Kea: gss_tsig_impl.cc Source File

| Kea 3.2.0-git

gss_tsig_impl.cc
Go to the documentation of this file.
1// Copyright (C) 2021-2025 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <cc/command_interpreter.h>
10#include <stats/stats_mgr.h>
11#include <gss_tsig_impl.h>
12#include <gss_tsig_log.h>
13#include <boost/foreach.hpp>
14#include <cstdlib>
15#include <list>
16#include <sstream>
17
18using namespace isc;
19using namespace isc::asiolink;
20using namespace isc::config;
21using namespace isc::d2;
22using namespace isc::data;
23using namespace isc::hooks;
24using namespace isc::log;
25using namespace isc::stats;
26using namespace std;
27using namespace std::chrono;
28
29namespace isc {
30namespace gss_tsig {
31
32GssTsigImpl::GssTsigImpl() : cfg_(), keys_(), io_service_(new IOService()),
33 krb5_client_ktname_prev_(), krb5ccname_prev_(), purge_timer_() {
34}
35
36GssTsigImpl::~GssTsigImpl() {
37 stop();
38 io_service_->stopAndPoll();
39}
40
41void
42GssTsigImpl::configure(ConstElementPtr config) {
43 cfg_.configure(config);
44 if (!cfg_.getClientKeyTab().empty()) {
45 char* krb5_client_ktname = getenv("KRB5_CLIENT_KTNAME");
46 if (krb5_client_ktname) {
47 krb5_client_ktname_prev_.reset(new string(krb5_client_ktname));
48 } else {
49 krb5_client_ktname_prev_.reset();
50 }
51 setenv("KRB5_CLIENT_KTNAME", cfg_.getClientKeyTab().c_str(), 1);
52 }
53 if (!cfg_.getCredsCache().empty()) {
54 char* krb5ccname = getenv("KRB5CCNAME");
55 if (krb5ccname) {
56 krb5ccname_prev_.reset(new string(krb5ccname));
57 } else {
58 krb5ccname_prev_.reset();
59 }
60 setenv("KRB5CCNAME", cfg_.getCredsCache().c_str(), 1);
61 }
62 StatsMgr& stats_mgr = StatsMgr::instance();
63 for (auto const& name : DnsServer::STAT_NAMES) {
64 stats_mgr.setValue(name, static_cast<int64_t>(0));
65 }
66}
67
68void
69GssTsigImpl::finishConfigure(D2CfgContextPtr d2_config) {
70 cfg_.buildServerRevMap(d2_config);
71}
72
73void
74GssTsigImpl::start() {
75 LOG_DEBUG(gss_tsig_logger, DBGLVL_TRACE_BASIC, GSS_TSIG_MANAGER_STARTED);
76 for (auto const& server : cfg_.getServerList()) {
77 if (!server) {
78 continue;
79 }
80 server->getTimer().reset(new IntervalTimer(io_service_));
81 }
82 processAllServersKeys();
83 uint32_t max_tkey_lifetime = cfg_.getMaxKeyLifetime();
84 if (max_tkey_lifetime > 0) {
85 purge_timer_.reset(new IntervalTimer(io_service_));
86 purge_timer_->setup([this]() { purgeKeys(); },
87 max_tkey_lifetime * 1000,
88 IntervalTimer::REPEATING);
89 }
90}
91
92void
93GssTsigImpl::stop() {
94 LOG_DEBUG(gss_tsig_logger, DBGLVL_TRACE_BASIC, GSS_TSIG_MANAGER_STOPPED);
95 if (purge_timer_) {
96 purge_timer_->cancel();
97 purge_timer_.reset();
98 }
99 for (auto const& server : cfg_.getServerList()) {
100 if (!server) {
101 continue;
102 }
103 if (server->getTimer()) {
104 server->getTimer()->cancel();
105 server->getTimer().reset();
106 }
107 }
108 for (auto const& key : keys_) {
109 key->getTKeyExchange().reset();
110 }
111 keys_.clear();
112 cfg_.clearServers();
113 // Run cancelled callbacks.
114 if (io_service_) {
115 try {
116 io_service_->poll();
117 } catch (const std::exception& ex) {
118 LOG_ERROR(gss_tsig_logger, GSS_TSIG_MANAGER_STOP_ERROR)
119 .arg(ex.what());
120 } catch (...) {
121 LOG_ERROR(gss_tsig_logger, GSS_TSIG_MANAGER_STOP_GENERAL_ERROR);
122 }
123 }
124 // Remove statistics.
125 StatsMgr& stats_mgr = StatsMgr::instance();
126 for (auto const& name : DnsServer::STAT_NAMES) {
127 stats_mgr.del(name);
128 }
129 // Restore environment variables.
130 if (!cfg_.getClientKeyTab().empty()) {
131 if (krb5_client_ktname_prev_) {
132 setenv("KRB5_CLIENT_KTNAME", krb5_client_ktname_prev_->c_str(), 1);
133 } else {
134 unsetenv("KRB5_CLIENT_KTNAME");
135 }
136 }
137 if (!cfg_.getCredsCache().empty()) {
138 if (krb5ccname_prev_) {
139 setenv("KRB5CCNAME", krb5ccname_prev_->c_str(), 1);
140 } else {
141 unsetenv("KRB5CCNAME");
142 }
143 }
144}
145
146void
147GssTsigImpl::createKey(DnsServerPtr server,
148 time_point<std::chrono::system_clock> now) {
149 StatsMgr& stats_mgr = StatsMgr::instance();
150 stats_mgr.addValue("gss-tsig-key-created",
151 static_cast<int64_t>(1));
152 stats_mgr.addValue(StatsMgr::generateName("server",
153 server->getID(),
154 "gss-tsig-key-created"),
155 static_cast<int64_t>(1));
156 string name;
157 for (;;) {
158 name = ManagedKey::genName(server->getKeyNameSuffix());
159 if (keys_.count(name) == 0) {
160 break;
161 }
162 // Very unlikely but possible name collision.
163 // Try another name.
164 }
165 LOG_INFO(gss_tsig_logger, GSS_TSIG_NEW_KEY).arg(name);
166 ManagedKeyPtr mkey(new ManagedKey(name));
167 mkey->setParentID(server->getID());
168 mkey->setInception(now);
169 mkey->setExpire(now + seconds(server->getKeyLifetime()));
170 static_cast<void>(keys_.insert(mkey));
171 OM_uint32 flags = TKeyExchange::TKEY_EXCHANGE_FLAGS;
172 if (!server->getGssReplayFlag()) {
173 flags &= ~GSS_C_REPLAY_FLAG;
174 }
175 if (server->getGssSequenceFlag()) {
176 flags |= GSS_C_SEQUENCE_FLAG;
177 }
178 mkey->getTKeyExchange().reset(new TKeyExchange(io_service_, server, mkey,
179 mkey.get(),
180 server->getExchangeTimeout(),
181 flags));
182
183 io_service_->post([mkey]() {
184 if (mkey->getTKeyExchange()) {
185 mkey->getTKeyExchange()->doExchange();
186 }
187 });
188}
189
190void
191GssTsigImpl::processAllServersKeys(bool rekey) {
192 for (auto const& server : cfg_.getServerList()) {
193 processServerKeys(server, rekey);
194 }
195}
196
197void
198GssTsigImpl::processServerKeys(DnsServerPtr server, bool rekey) {
199 if (!server) {
200 return;
201 }
202 bool retry = false;
203 ManagedKeyPtr newest;
204 auto const now = system_clock::now();
205 const std::chrono::seconds retry_dur(server->getRetryInterval());
206 const std::chrono::seconds rekey_dur(server->getRekeyInterval());
207 auto const& idx = keys_.get<GssTsigKeyServerTag>();
208 auto const& range = idx.equal_range(server->getID());
209 try {
210 BOOST_FOREACH(auto const& key, range) {
211 lock_guard<mutex> lock(*key->mutex_);
212 if (!newest || (newest->getInception() < key->getInception())) {
213 newest = key;
214 }
215 if (key->getStatus() == ManagedKey::USABLE) {
216 if (now >= key->getExpire()) {
217 key->setStatus(ManagedKey::EXPIRED);
218 }
219 }
220 }
221 // Rekey if no key or the newest key is not usable but ready.
222 if (!newest) {
223 rekey = true;
224 } else {
225 switch (newest->getStatus()) {
226 case ManagedKey::NOT_READY:
227 // Lost some race, check again later.
228 retry = true;
229 break;
230 case ManagedKey::USABLE:
231 break;
232 default:
233 rekey = true;
234 break;
235 }
236 }
237 // Rekey too if the newest key is usable but soon to expire.
238 if (!rekey &&
239 ((now + retry_dur >= newest->getExpire()) ||
240 (now >= newest->getInception() + rekey_dur))) {
241 rekey = true;
242 }
243 // Create and setup a new GSS-TSIG key.
244 if (rekey) {
245 createKey(server, now);
246 retry = true;
247 }
248 } catch (const std::exception& ex) {
249 retry = true;
250 LOG_ERROR(gss_tsig_logger, KEY_PROCESSING_FAILED)
251 .arg(server->getID())
252 .arg(ex.what());
253 } catch (...) {
254 retry = true;
255 LOG_ERROR(gss_tsig_logger, KEY_PROCESSING_FAILED_UNSPECIFIED_ERROR)
256 .arg(server->getID());
257 }
258 auto timer = server->getTimer();
259 if (!timer) {
260 return;
261 }
262 timer->cancel();
263 // Schedule a retry.
264 if (retry || !newest) {
265 timer->setup([this, server]() { processServerKeys(server); },
266 server->getRetryInterval() * 1000,
267 IntervalTimer::ONE_SHOT);
268 LOG_DEBUG(gss_tsig_logger, DBGLVL_TRACE_BASIC, START_RETRY_TIMER)
269 .arg(server->getID())
270 .arg(server->getRetryInterval());
271 return;
272 }
273 // Schedule a rekey.
274 auto const rekey_date = newest->getInception() + rekey_dur;
275 const std::chrono::system_clock::duration rekey_interval(rekey_date - now);
276 auto interval =
277 (duration_cast<nanoseconds>(rekey_interval).count() + 999999) / 1000000;
278 if (interval < server->getRetryInterval()) {
279 interval = server->getRetryInterval();
280 }
281 timer->setup([this, server]() { processServerKeys(server); },
282 interval, IntervalTimer::ONE_SHOT);
283 LOG_DEBUG(gss_tsig_logger, DBGLVL_TRACE_BASIC, START_REKEY_TIMER)
284 .arg(server->getID())
285 .arg(interval / 1000);
286}
287
288 DnsServerPtr
289GssTsigImpl::getServer(const std::string& id) const {
290 return (cfg_.getServer(id));
291}
292
293 ManagedKeyPtr
294GssTsigImpl::findKey(const d2::DnsServerInfoPtr& server_info,
295 bool& useGssTsig, bool& fallback) {
296 DnsServerPtr server = cfg_.getServer(server_info);
297 if (!server) {
298 LOG_DEBUG(gss_tsig_logger, DBGLVL_TRACE_BASIC, KEY_LOOKUP_DISABLED);
299 useGssTsig = false;
300 fallback = false;
301 return (ManagedKeyPtr());
302 }
303 useGssTsig = true;
304 fallback = server->getFallback();
305 auto now = system_clock::now();
306 auto const& idx = keys_.get<GssTsigKeyServerTag>();
307 auto const& range = idx.equal_range(server->getID());
308 ManagedKeyPtr candidate;
309 BOOST_FOREACH(auto const& key, range) {
310 lock_guard<mutex> lock(*key->mutex_);
311 if (key->getStatus() == ManagedKey::USABLE) {
312 if (now >= key->getExpire()) {
313 key->setStatus(ManagedKey::EXPIRED);
314 continue;
315 }
316 candidate = key;
317 }
318 }
319 if (candidate) {
320 LOG_DEBUG(gss_tsig_logger, DBGLVL_TRACE_BASIC, KEY_LOOKUP_FOUND)
321 .arg(candidate->getKeyNameStr());
322 return (candidate);
323 }
324 LOG_DEBUG(gss_tsig_logger, DBGLVL_TRACE_BASIC, KEY_LOOKUP_NONE);
325 return (ManagedKeyPtr());
326}
327
328 ManagedKeyPtr
329 GssTsigImpl::findKey(const std::string& name) const {
330 auto const& it = keys_.find(name);
331 if (it == keys_.cend()) {
332 return (ManagedKeyPtr());
333 }
334 return (*it);
335}
336
337void
338GssTsigImpl::purgeKeys() {
339 // Build the list of keys to remove.
340 auto now = system_clock::now();
341 std::chrono::seconds max_age(cfg_.getMaxKeyLifetime() * 3);
342 list<ManagedKeyPtr> to_purge;
343 for (auto const& key : keys_) {
344 lock_guard<mutex> lock(*key->mutex_);
345 if (key->getExpire() + max_age < now) {
346 to_purge.push_back(key);
347 }
348 }
349
350 // Return now if there is no key to remove.
351 if (to_purge.empty()) {
352 return;
353 }
354
355 // Remove all elements of the list.
356 for (auto const& key : to_purge) {
357 auto it = keys_.find(key->getKeyNameStr());
358 if (it != keys_.end()) {
359 key->getTKeyExchange().reset();
360 keys_.erase(it);
361 }
362 }
363
365 LOG_DEBUG(gss_tsig_logger, DBGLVL_TRACE_BASIC, GSS_TSIG_OLD_KEY_REMOVED)
366 .arg(to_purge.size());
367}
368
369void
370GssTsigImpl::getHandler(CalloutHandle& handle) const {
371 ConstElementPtr response;
372 string id;
373 DnsServerPtr server;
374 try {
375 // Command is always provided.
376 ConstElementPtr command;
377 handle.getArgument("command", command);
378
379 // Retrieve arguments.
380 ConstElementPtr args;
381 static_cast<void>(parseCommand(args, command));
382
383 // Arguments are required.
384 if (!args) {
385 isc_throw(BadValue, "arguments not found in the '"
386 << command->str() << "' command");
387 }
388
389 // Arguments must be a map.
390 if (args->getType() != Element::map) {
391 isc_throw(BadValue, "arguments in the '" << command->str()
392 << "' command are not a map");
393 }
394
395 // server-id is mandatory.
396 ConstElementPtr server_id = args->get("server-id");
397 if (!server_id) {
398 isc_throw(BadValue, "'server-id' is mandatory for the '"
399 << command->str() << "' command");
400 }
401
402 // server-id must be a string.
403 if (server_id->getType() != Element::string) {
404 isc_throw(BadValue, "'server-id' must be a string in the '"
405 << command->str() << "' command");
406 }
407
408 id = server_id->stringValue();
409 server = cfg_.getServer(id);
410 } catch (const std::exception& ex) {
411 // There was an error while parsing command arguments.
412 // Return an error status code to notify the user.
413 response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
414 handle.setArgument("response", response);
415 return;
416 }
417
418 ostringstream msg;
419 msg << "GSS-TSIG server[" << id << "] ";
420
421 // No server.
422 if (!server) {
423 msg << "not found";
424 response = createAnswer(CONTROL_RESULT_EMPTY, msg.str());
425 } else {
426 msg << "found";
427 ElementPtr desc = server->toElement();
428 ElementPtr keys = Element::createList();
429 auto const& idx = keys_.get<GssTsigKeyServerTag>();
430 auto const& range = idx.equal_range(server->getID());
431 BOOST_FOREACH(auto const& key, range) {
432 keys->add(key->toElement());
433 }
434 desc->set("keys", keys);
435 response = createAnswer(CONTROL_RESULT_SUCCESS, msg.str(), desc);
436 }
437 handle.setArgument("response", response);
438}
439
440void
441GssTsigImpl::getAllHandler(CalloutHandle& handle) const {
442 ConstElementPtr response;
443 // Create a list where we're going to store servers' information.
444 ElementPtr servers = Element::createList();
445 // Create arguments map and add server list.
446 ElementPtr args = Element::createMap();
447 args->set("gss-tsig-servers", servers);
448 auto const& idx = keys_.get<GssTsigKeyServerTag>();
449 size_t key_count(0);
450
451 // Iterate over servers.
452 for (auto const& server : cfg_.getServerList()) {
453 ElementPtr desc = server->toElement();
454 ElementPtr keys = Element::createList();
455 auto const& range = idx.equal_range(server->getID());
456 BOOST_FOREACH(auto const& key, range) {
457 keys->add(key->toElement());
458 }
459 desc->set("keys", keys);
460 key_count += keys->size();
461 servers->add(desc);
462 }
463
464 ostringstream msg;
465 msg << servers->size() << " GSS-TSIG servers";
466
467 if (servers->empty()) {
468 response = createAnswer(CONTROL_RESULT_EMPTY, msg.str(), args);
469 } else {
470 msg << " and " << key_count << " keys";
471 response = createAnswer(CONTROL_RESULT_SUCCESS, msg.str(), args);
472 }
473 handle.setArgument("response", response);
474}
475
476void
477GssTsigImpl::listHandler(CalloutHandle& handle) const {
478 ConstElementPtr response;
479 // Create a list where we're going to store servers' ID.
480 ElementPtr servers = Element::createList();
481 // Create a list where we're going to store keys' name.
482 ElementPtr keys = Element::createList();
483 // Create arguments map and add server and key lists.
484 ElementPtr args = Element::createMap();
485 args->set("gss-tsig-servers", servers);
486 args->set("gss-tsig-keys", keys);
487
488 // Iterate over servers.
489 for (auto const& server : cfg_.getServerList()) {
490 servers->add(Element::create(server->getID()));
491 }
492
493 // Iterate over keys.
494 for (auto const& key : keys_) {
495 keys->add(Element::create(key->getKeyNameStr()));
496 }
497
498 ostringstream msg;
499 msg << servers->size() << " GSS-TSIG servers and "
500 << keys->size() << " keys";
501
502 if (servers->empty() && keys->empty()) {
503 response = createAnswer(CONTROL_RESULT_EMPTY, msg.str(), args);
504 } else {
505 response = createAnswer(CONTROL_RESULT_SUCCESS, msg.str(), args);
506 }
507 handle.setArgument("response", response);
508}
509
510void
511GssTsigImpl::keyGetHandler(CalloutHandle& handle) const {
512 ConstElementPtr response;
513 string name;
514 ManagedKeyPtr key;
515 try {
516 // Command is always provided.
517 ConstElementPtr command;
518 handle.getArgument("command", command);
519
520 // Retrieve arguments.
521 ConstElementPtr args;
522 static_cast<void>(parseCommand(args, command));
523
524 // Arguments are required.
525 if (!args) {
526 isc_throw(BadValue, "arguments not found in the '"
527 << command->str() << "' command");
528 }
529
530 // Arguments must be a map.
531 if (args->getType() != Element::map) {
532 isc_throw(BadValue, "arguments in the '" << command->str()
533 << "' command are not a map");
534 }
535
536 // key-name is mandatory.
537 ConstElementPtr key_name = args->get("key-name");
538 if (!key_name) {
539 isc_throw(BadValue, "'key-name' is mandatory for the '"
540 << command->str() << "' command");
541 }
542
543 // key-name must be a string.
544 if (key_name->getType() != Element::string) {
545 isc_throw(BadValue, "'key-name' must be a string in the '"
546 << command->str() << "' command");
547 }
548
549 name = key_name->stringValue();
550 key = findKey(name);
551 } catch (const std::exception& ex) {
552 // There was an error while parsing command arguments.
553 // Return an error status code to notify the user.
554 response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
555 handle.setArgument("response", response);
556 return;
557 }
558
559 ostringstream msg;
560 msg << "GSS-TSIG key '" << name << "' ";
561
562 // No key.
563 if (!key) {
564 msg << "not found";
565 response = createAnswer(CONTROL_RESULT_EMPTY, msg.str());
566 } else {
567 msg << "found";
568 ConstElementPtr desc = key->toElement();
569 response = createAnswer(CONTROL_RESULT_SUCCESS, msg.str(), desc);
570 }
571 handle.setArgument("response", response);
572}
573
574void
575GssTsigImpl::keyExpireHandler(CalloutHandle& handle) {
576 ConstElementPtr response;
577 string name;
578 ManagedKeyPtr key;
579 try {
580 // Command is always provided.
581 ConstElementPtr command;
582 handle.getArgument("command", command);
583
584 // Retrieve arguments.
585 ConstElementPtr args;
586 static_cast<void>(parseCommand(args, command));
587
588 // Arguments are required.
589 if (!args) {
590 isc_throw(BadValue, "arguments not found in the '"
591 << command->str() << "' command");
592 }
593
594 // Arguments must be a map.
595 if (args->getType() != Element::map) {
596 isc_throw(BadValue, "arguments in the '" << command->str()
597 << "' command are not a map");
598 }
599
600 // key-name is mandatory.
601 ConstElementPtr key_name = args->get("key-name");
602 if (!key_name) {
603 isc_throw(BadValue, "'key-name' is mandatory for the '"
604 << command->str() << "' command");
605 }
606
607 // key-name must be a string.
608 if (key_name->getType() != Element::string) {
609 isc_throw(BadValue, "'key-name' must be a string in the '"
610 << command->str() << "' command");
611 }
612
613 name = key_name->stringValue();
614 key = findKey(name);
615 } catch (const std::exception& ex) {
616 // There was an error while parsing command arguments.
617 // Return an error status code to notify the user.
618 response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
619 handle.setArgument("response", response);
620 return;
621 }
622
623 ostringstream msg;
624 msg << "GSS-TSIG key '" << name << "' ";
625
626 // No key.
627 if (!key) {
628 msg << "not found";
629 response = createAnswer(CONTROL_RESULT_EMPTY, msg.str());
630 } else {
631 bool can_expire = true;
632 {
633 lock_guard<mutex> lock(*key->mutex_);
634 if (key->getStatus() >= ManagedKey::EXPIRED) {
635 can_expire = false;
636 } else {
637 key->setStatus(ManagedKey::EXPIRED);
638 }
639 }
640 if (!can_expire) {
641 msg << "can't be expired";
642 response = createAnswer(CONTROL_RESULT_EMPTY, msg.str());
643 } else {
644 msg << "expired";
645 response = createAnswer(CONTROL_RESULT_SUCCESS, msg.str());
646 }
647 }
648 handle.setArgument("response", response);
649}
650
651void
652GssTsigImpl::keyDelHandler(CalloutHandle& handle) {
653 ConstElementPtr response;
654 string name;
655 ManagedKeyPtr key;
656 try {
657 // Command is always provided.
658 ConstElementPtr command;
659 handle.getArgument("command", command);
660
661 // Retrieve arguments.
662 ConstElementPtr args;
663 static_cast<void>(parseCommand(args, command));
664
665 // Arguments are required.
666 if (!args) {
667 isc_throw(BadValue, "arguments not found in the '"
668 << command->str() << "' command");
669 }
670
671 // Arguments must be a map.
672 if (args->getType() != Element::map) {
673 isc_throw(BadValue, "arguments in the '" << command->str()
674 << "' command are not a map");
675 }
676
677 // key-name is mandatory.
678 ConstElementPtr key_name = args->get("key-name");
679 if (!key_name) {
680 isc_throw(BadValue, "'key-name' is mandatory for the '"
681 << command->str() << "' command");
682 }
683
684 // key-name must be a string.
685 if (key_name->getType() != Element::string) {
686 isc_throw(BadValue, "'key-name' must be a string in the '"
687 << command->str() << "' command");
688 }
689
690 name = key_name->stringValue();
691 key = findKey(name);
692 } catch (const std::exception& ex) {
693 // There was an error while parsing command arguments.
694 // Return an error status code to notify the user.
695 response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
696 handle.setArgument("response", response);
697 return;
698 }
699
700 ostringstream msg;
701 msg << "GSS-TSIG key '" << name << "' ";
702
703 // No key.
704 if (!key) {
705 msg << "not found";
706 response = createAnswer(CONTROL_RESULT_EMPTY, msg.str());
707 } else {
708 msg << "deleted";
709 auto it = keys_.find(name);
710 if (it != keys_.end()) {
711 key->getTKeyExchange().reset();
712 keys_.erase(it);
713 }
714 response = createAnswer(CONTROL_RESULT_SUCCESS, msg.str());
715 }
716 handle.setArgument("response", response);
717}
718
719void
720GssTsigImpl::purgeHandler(CalloutHandle& handle) {
721 ConstElementPtr response;
722 string id;
723 try {
724 // Command is always provided.
725 ConstElementPtr command;
726 handle.getArgument("command", command);
727
728 // Retrieve arguments.
729 ConstElementPtr args;
730 static_cast<void>(parseCommand(args, command));
731
732 // Arguments are required.
733 if (!args) {
734 isc_throw(BadValue, "arguments not found in the '"
735 << command->str() << "' command");
736 }
737
738 // Arguments must be a map.
739 if (args->getType() != Element::map) {
740 isc_throw(BadValue, "arguments in the '" << command->str()
741 << "' command are not a map");
742 }
743
744 // server-id is mandatory.
745 ConstElementPtr server_id = args->get("server-id");
746 if (!server_id) {
747 isc_throw(BadValue, "'server-id' is mandatory for the '"
748 << command->str() << "' command");
749 }
750
751 // server-id must be a string.
752 if (server_id->getType() != Element::string) {
753 isc_throw(BadValue, "'server-id' must be a string in the '"
754 << command->str() << "' command");
755 }
756
757 id = server_id->stringValue();
758 } catch (const std::exception& ex) {
759 // There was an error while parsing command arguments.
760 // Return an error status code to notify the user.
761 response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
762 handle.setArgument("response", response);
763 return;
764 }
765
766 // Build the list of keys to remove.
767 auto now = system_clock::now();
768 list<ManagedKeyPtr> to_purge;
769 auto const& idx = keys_.get<GssTsigKeyServerTag>();
770 auto const& range = idx.equal_range(id);
771 BOOST_FOREACH(auto const& key, range) {
772 lock_guard<mutex> lock(*key->mutex_);
773 auto status = key->getStatus();
774 switch (status) {
775 case ManagedKey::NOT_READY:
776 // skip
777 break;
778 case ManagedKey::USABLE:
779 if (now < key->getExpire()) {
780 // skip
781 break;
782 }
783 key->setStatus(ManagedKey::EXPIRED);
784 to_purge.push_back(key);
785 break;
786 default:
787 to_purge.push_back(key);
788 break;
789 }
790 }
791
792 // Remove all elements of the list.
793 for (auto const& key : to_purge) {
794 auto it = keys_.find(key->getKeyNameStr());
795 if (it != keys_.end()) {
796 key->getTKeyExchange().reset();
797 keys_.erase(it);
798 }
799 }
800
801 ostringstream msg;
802 msg << to_purge.size() << " purged keys for GSS-TSIG server[" << id << "]";
803
804 if (to_purge.empty()) {
805 response = createAnswer(CONTROL_RESULT_EMPTY, msg.str());
806 } else {
807 response = createAnswer(CONTROL_RESULT_SUCCESS, msg.str());
808 }
809 handle.setArgument("response", response);
810}
811
812void
813GssTsigImpl::purgeAllHandler(CalloutHandle& handle) {
814 ConstElementPtr response;
815
816 // Build the list of keys to remove.
817 auto now = system_clock::now();
818 list<ManagedKeyPtr> to_purge;
819 for (auto const& key : keys_) {
820 lock_guard<mutex> lock(*key->mutex_);
821 auto status = key->getStatus();
822 switch (status) {
823 case ManagedKey::NOT_READY:
824 // skip
825 break;
826 case ManagedKey::USABLE:
827 if (now < key->getExpire()) {
828 // skip
829 break;
830 }
831 key->setStatus(ManagedKey::EXPIRED);
832 to_purge.push_back(key);
833 break;
834 default:
835 to_purge.push_back(key);
836 break;
837 }
838 }
839
840 // Remove all elements of the list.
841 for (auto const& key : to_purge) {
842 auto it = keys_.find(key->getKeyNameStr());
843 if (it != keys_.end()) {
844 key->getTKeyExchange().reset();
845 keys_.erase(it);
846 }
847 }
848
849 ostringstream msg;
850 msg << to_purge.size() << " purged GSS-TSIG keys";
851
852 if (to_purge.empty()) {
853 response = createAnswer(CONTROL_RESULT_EMPTY, msg.str());
854 } else {
855 response = createAnswer(CONTROL_RESULT_SUCCESS, msg.str());
856 }
857 handle.setArgument("response", response);
858}
859
860void
861GssTsigImpl::rekeyHandler(CalloutHandle& handle) {
862 ConstElementPtr response;
863 string id;
864 DnsServerPtr server;
865 try {
866 // Command is always provided.
867 ConstElementPtr command;
868 handle.getArgument("command", command);
869
870 // Retrieve arguments.
871 ConstElementPtr args;
872 static_cast<void>(parseCommand(args, command));
873
874 // Arguments are required.
875 if (!args) {
876 isc_throw(BadValue, "arguments not found in the '"
877 << command->str() << "' command");
878 }
879
880 // Arguments must be a map.
881 if (args->getType() != Element::map) {
882 isc_throw(BadValue, "arguments in the '" << command->str()
883 << "' command are not a map");
884 }
885
886 // server-id is mandatory.
887 ConstElementPtr server_id = args->get("server-id");
888 if (!server_id) {
889 isc_throw(BadValue, "'server-id' is mandatory for the '"
890 << command->str() << "' command");
891 }
892
893 // server-id must be a string.
894 if (server_id->getType() != Element::string) {
895 isc_throw(BadValue, "'server-id' must be a string in the '"
896 << command->str() << "' command");
897 }
898
899 id = server_id->stringValue();
900 server = cfg_.getServer(id);
901 } catch (const std::exception& ex) {
902 // There was an error while parsing command arguments.
903 // Return an error status code to notify the user.
904 response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
905 handle.setArgument("response", response);
906 return;
907 }
908
909 ostringstream msg;
910 msg << "GSS-TSIG server[" << id << "] ";
911
912 // No server.
913 if (!server) {
914 msg << "not found";
915 response = createAnswer(CONTROL_RESULT_EMPTY, msg.str());
916 } else {
917 auto now = system_clock::now();
918 createKey(server, now);
919 msg << "rekeyed";
920 response = createAnswer(CONTROL_RESULT_SUCCESS, msg.str());
921 }
922 handle.setArgument("response", response);
923}
924
925void
926GssTsigImpl::rekeyAllHandler(CalloutHandle& handle) {
927 io_service_->post([this]() { processAllServersKeys(true); });
928 ConstElementPtr response = createAnswer(CONTROL_RESULT_SUCCESS, "rekeyed");
929 handle.setArgument("response", response);
930}
931
932void
933GssTsigImpl::commandProcessed(CalloutHandle& handle) {
934 // Filter command names.
935 string command_name;
936 handle.getArgument("name", command_name);
937 if (command_name != "status-get") {
938 return;
939 }
940 // Get the response.
941 ConstElementPtr response;
942 handle.getArgument("response", response);
943 if (!response || (response->getType() != Element::map)) {
944 return;
945 }
946 // Get the arguments item from the response.
947 ConstElementPtr resp_args = response->get("arguments");
948 if (!resp_args || (resp_args->getType() != Element::map)) {
949 return;
950 }
951 // Add the gss-tsig entry.
952 ElementPtr mutable_resp_args = boost::const_pointer_cast<Element>(resp_args);
953 mutable_resp_args->set("gss-tsig", Element::create(true));
954}
955
956} // end of namespace isc::gss_tsig
957} // end of namespace isc
Element::create
static ElementPtr create(const Position &pos=ZERO_POSITION())
Create a NullElement.
Definition data.cc:299
Element::map
@ map
Definition data.h:160
Element::string
@ string
Definition data.h:157
Element::createMap
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:354
Element::createList
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:349
isc::BadValue
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition exceptions/exceptions.h:132
isc::asiolink::IOService
The IOService class is a wrapper for the ASIO io_context class.
Definition io_service.h:39
isc::asiolink::IntervalTimer
The IntervalTimer class is a wrapper for the ASIO boost::asio::system_timer class.
Definition interval_timer.h:61
isc::asiolink::IntervalTimer::ONE_SHOT
@ ONE_SHOT
Definition interval_timer.h:72
isc::asiolink::IntervalTimer::REPEATING
@ REPEATING
Definition interval_timer.h:71
isc::gss_tsig::DnsServer::STAT_NAMES
static const std::list< std::string > STAT_NAMES
Server TKEY exchange statistics names.
Definition gss_tsig_cfg.h:45
isc::gss_tsig::GssTsigImpl::rekeyAllHandler
void rekeyAllHandler(isc::hooks::CalloutHandle &handle)
The gss-tsig-rekey-all command handler.
Definition gss_tsig_impl.cc:926
isc::gss_tsig::GssTsigImpl::krb5_client_ktname_prev_
std::unique_ptr< std::string > krb5_client_ktname_prev_
The previous value of client key table environment variable.
Definition gss_tsig_impl.h:220
isc::gss_tsig::GssTsigImpl::io_service_
isc::asiolink::IOServicePtr io_service_
The hook I/O service.
Definition gss_tsig_impl.h:217
isc::gss_tsig::GssTsigImpl::GssTsigImpl
GssTsigImpl()
Constructor.
Definition gss_tsig_impl.cc:32
isc::gss_tsig::GssTsigImpl::stop
void stop()
Stop method.
Definition gss_tsig_impl.cc:93
isc::gss_tsig::GssTsigImpl::purgeAllHandler
void purgeAllHandler(isc::hooks::CalloutHandle &handle)
The gss-tsig-purge-all command handler.
Definition gss_tsig_impl.cc:813
isc::gss_tsig::GssTsigImpl::finishConfigure
void finishConfigure(isc::d2::D2CfgContextPtr d2_config)
Finish configure.
Definition gss_tsig_impl.cc:69
isc::gss_tsig::GssTsigImpl::cfg_
GssTsigCfg cfg_
GSS-TSIG hook configuration.
Definition gss_tsig_impl.h:211
isc::gss_tsig::GssTsigImpl::commandProcessed
void commandProcessed(isc::hooks::CalloutHandle &handle)
The command_processed handler.
Definition gss_tsig_impl.cc:933
isc::gss_tsig::GssTsigImpl::start
void start()
Start method.
Definition gss_tsig_impl.cc:74
isc::gss_tsig::GssTsigImpl::keyExpireHandler
void keyExpireHandler(isc::hooks::CalloutHandle &handle)
The gss-tsig-key-expire command handler.
Definition gss_tsig_impl.cc:575
isc::gss_tsig::GssTsigImpl::purgeHandler
void purgeHandler(isc::hooks::CalloutHandle &handle)
The gss-tsig-purge command handler.
Definition gss_tsig_impl.cc:720
isc::gss_tsig::GssTsigImpl::rekeyHandler
void rekeyHandler(isc::hooks::CalloutHandle &handle)
The gss-tsig-rekey command handler.
Definition gss_tsig_impl.cc:861
isc::gss_tsig::GssTsigImpl::getAllHandler
void getAllHandler(isc::hooks::CalloutHandle &handle) const
The gss-tsig-get-all command handler.
Definition gss_tsig_impl.cc:441
isc::gss_tsig::GssTsigImpl::configure
void configure(isc::data::ConstElementPtr config)
Configure.
Definition gss_tsig_impl.cc:42
isc::gss_tsig::GssTsigImpl::keyDelHandler
void keyDelHandler(isc::hooks::CalloutHandle &handle)
The gss-tsig-key-del command handler.
Definition gss_tsig_impl.cc:652
isc::gss_tsig::GssTsigImpl::getServer
DnsServerPtr getServer(const std::string &id) const
Get the DNS server from its ID.
Definition gss_tsig_impl.cc:289
isc::gss_tsig::GssTsigImpl::processAllServersKeys
void processAllServersKeys(bool rekey=false)
Process GSS-TSIG keys for all servers.
Definition gss_tsig_impl.cc:191
isc::gss_tsig::GssTsigImpl::keys_
ManagedKeyList keys_
Map of GSS-TSIG keys by name.
Definition gss_tsig_impl.h:214
isc::gss_tsig::GssTsigImpl::findKey
ManagedKeyPtr findKey(const d2::DnsServerInfoPtr &server_info, bool &useGssTsig, bool &fallback)
Find a GSS-TSIG key by server info.
Definition gss_tsig_impl.cc:294
isc::gss_tsig::GssTsigImpl::purgeKeys
void purgeKeys()
Purge very old GSS-TSIG keys.
Definition gss_tsig_impl.cc:338
isc::gss_tsig::GssTsigImpl::processServerKeys
void processServerKeys(DnsServerPtr server, bool rekey=false)
Process GSS-TSIG keys for a specific server.
Definition gss_tsig_impl.cc:198
isc::gss_tsig::GssTsigImpl::krb5ccname_prev_
std::unique_ptr< std::string > krb5ccname_prev_
The previous value of credential cache environment variable.
Definition gss_tsig_impl.h:223
isc::gss_tsig::GssTsigImpl::keyGetHandler
void keyGetHandler(isc::hooks::CalloutHandle &handle) const
The gss-tsig-key-get command handler.
Definition gss_tsig_impl.cc:511
isc::gss_tsig::GssTsigImpl::purge_timer_
isc::asiolink::IntervalTimerPtr purge_timer_
The purge periodic timer.
Definition gss_tsig_impl.h:226
isc::gss_tsig::GssTsigImpl::~GssTsigImpl
virtual ~GssTsigImpl()
Destructor.
Definition gss_tsig_impl.cc:36
isc::gss_tsig::GssTsigImpl::listHandler
void listHandler(isc::hooks::CalloutHandle &handle) const
The gss-tsig-list command handler.
Definition gss_tsig_impl.cc:477
isc::gss_tsig::GssTsigImpl::getHandler
void getHandler(isc::hooks::CalloutHandle &handle) const
The gss-tsig-get command handler.
Definition gss_tsig_impl.cc:370
isc::gss_tsig::GssTsigImpl::createKey
void createKey(DnsServerPtr server, std::chrono::time_point< std::chrono::system_clock > now)
Create new GSS-TSIG key.
Definition gss_tsig_impl.cc:147
isc::gss_tsig::ManagedKey
Managed GSS-TSIG key.
Definition managed_key.h:24
isc::gss_tsig::ManagedKey::genName
static std::string genName(const std::string &server)
Create a random name from a suffix.
Definition managed_key.cc:41
isc::gss_tsig::ManagedKey::EXPIRED
@ EXPIRED
Expired (no longer usable).
Definition managed_key.h:35
isc::gss_tsig::ManagedKey::USABLE
@ USABLE
Usable.
Definition managed_key.h:34
isc::gss_tsig::ManagedKey::NOT_READY
@ NOT_READY
Not yet ready (not yet usable).
Definition managed_key.h:33
isc::gss_tsig::TKeyExchange
The TKeyExchange class handles communication with the DNS server.
Definition tkey_exchange.h:36
isc::gss_tsig::TKeyExchange::TKEY_EXCHANGE_FLAGS
static const OM_uint32 TKEY_EXCHANGE_FLAGS
The default TKEY exchange flags.
Definition tkey_exchange.h:117
isc::hooks::CalloutHandle
Per-packet callout handle.
Definition callout_handle.h:78
isc::hooks::CalloutHandle::getArgument
void getArgument(const std::string &name, T &value) const
Get argument.
Definition callout_handle.h:164
isc::hooks::CalloutHandle::setArgument
void setArgument(const std::string &name, T value)
Set argument.
Definition callout_handle.h:147
isc::stats::StatsMgr
Statistics Manager class.
Definition lib/stats/stats_mgr.h:65
isc::stats::StatsMgr::instance
static StatsMgr & instance()
Statistics Manager accessor method.
Definition lib/stats/stats_mgr.cc:30
isc::stats::StatsMgr::generateName
static std::string generateName(const std::string &context, Type index, const std::string &stat_name)
Generates statistic name in a given context.
Definition lib/stats/stats_mgr.h:303
command_interpreter.h
This file contains several functions and constants that are used for handling commands and responses ...
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition exceptions/exceptions.h:210
isc::stats::StatsMgr::del
bool del(const std::string &name)
Removes specified statistic.
Definition lib/stats/stats_mgr.cc:255
isc::stats::StatsMgr::setValue
void setValue(const std::string &name, const int64_t value)
Records absolute integer observation.
Definition lib/stats/stats_mgr.cc:40
isc::stats::StatsMgr::addValue
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
Definition lib/stats/stats_mgr.cc:70
rekey
int rekey(CalloutHandle &handle)
The gss-tsig-rekey command.
Definition gss_tsig_callouts.cc:129
gss_tsig_impl.h
gss_tsig_log.h
stats_mgr.h
LOG_ERROR
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
LOG_INFO
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition macros.h:20
LOG_DEBUG
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
isc::asiolink
Definition addr_utilities.cc:170
isc::config
Definition command_interpreter.cc:23
isc::config::CONTROL_RESULT_EMPTY
const int CONTROL_RESULT_EMPTY
Status code indicating that the specified command was completed correctly, but failed to produce any ...
Definition command_interpreter.h:53
isc::config::CONTROL_RESULT_ERROR
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
Definition command_interpreter.h:45
isc::config::parseCommand
std::string parseCommand(ConstElementPtr &arg, ConstElementPtr command)
Parses the given command into a string containing the actual command and an ElementPtr containing the...
Definition command_interpreter.cc:200
isc::config::createAnswer
ConstElementPtr createAnswer()
Creates a standard config/command level success answer message (i.e.
Definition command_interpreter.cc:54
isc::config::CONTROL_RESULT_SUCCESS
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
Definition command_interpreter.h:42
isc::d2
Definition check_exists_add.cc:19
isc::d2::DnsServerInfoPtr
boost::shared_ptr< DnsServerInfo > DnsServerInfoPtr
Defines a pointer for DnsServerInfo instances.
Definition d2_config.h:554
isc::d2::D2CfgContextPtr
boost::shared_ptr< D2CfgContext > D2CfgContextPtr
Pointer to a configuration context.
Definition d2_cfg_mgr.h:26
isc::data
Definition base_stamped_element.cc:12
isc::data::ConstElementPtr
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:30
isc::data::ElementPtr
boost::shared_ptr< Element > ElementPtr
Definition data.h:29
isc::dns::name
Definition name.cc:69
isc::gss_tsig
Definition gss_tsig_api.cc:19
isc::gss_tsig::GSS_TSIG_MANAGER_STOP_GENERAL_ERROR
const isc::log::MessageID GSS_TSIG_MANAGER_STOP_GENERAL_ERROR
Definition gss_tsig_messages.h:19
isc::gss_tsig::START_REKEY_TIMER
const isc::log::MessageID START_REKEY_TIMER
Definition gss_tsig_messages.h:32
isc::gss_tsig::KEY_LOOKUP_DISABLED
const isc::log::MessageID KEY_LOOKUP_DISABLED
Definition gss_tsig_messages.h:27
isc::gss_tsig::GSS_TSIG_NEW_KEY
const isc::log::MessageID GSS_TSIG_NEW_KEY
Definition gss_tsig_messages.h:20
isc::gss_tsig::DnsServerPtr
boost::shared_ptr< DnsServer > DnsServerPtr
A pointer to a DNS server.
Definition gss_tsig_cfg.h:399
isc::gss_tsig::GSS_TSIG_MANAGER_STOP_ERROR
const isc::log::MessageID GSS_TSIG_MANAGER_STOP_ERROR
Definition gss_tsig_messages.h:18
isc::gss_tsig::GSS_TSIG_OLD_KEY_REMOVED
const isc::log::MessageID GSS_TSIG_OLD_KEY_REMOVED
Definition gss_tsig_messages.h:23
isc::gss_tsig::KEY_LOOKUP_NONE
const isc::log::MessageID KEY_LOOKUP_NONE
Definition gss_tsig_messages.h:29
isc::gss_tsig::KEY_LOOKUP_FOUND
const isc::log::MessageID KEY_LOOKUP_FOUND
Definition gss_tsig_messages.h:28
isc::gss_tsig::GSS_TSIG_MANAGER_STARTED
const isc::log::MessageID GSS_TSIG_MANAGER_STARTED
Definition gss_tsig_messages.h:16
isc::gss_tsig::KEY_PROCESSING_FAILED_UNSPECIFIED_ERROR
const isc::log::MessageID KEY_PROCESSING_FAILED_UNSPECIFIED_ERROR
Definition gss_tsig_messages.h:31
isc::gss_tsig::START_RETRY_TIMER
const isc::log::MessageID START_RETRY_TIMER
Definition gss_tsig_messages.h:33
isc::gss_tsig::ManagedKeyPtr
boost::shared_ptr< ManagedKey > ManagedKeyPtr
Type of pointer to a Managed GSS-TSIG key.
Definition managed_key.h:156
isc::gss_tsig::KEY_PROCESSING_FAILED
const isc::log::MessageID KEY_PROCESSING_FAILED
Definition gss_tsig_messages.h:30
isc::gss_tsig::gss_tsig_logger
isc::log::Logger gss_tsig_logger("gss-tsig-hooks")
Definition gss_tsig_log.h:17
isc::gss_tsig::GSS_TSIG_MANAGER_STOPPED
const isc::log::MessageID GSS_TSIG_MANAGER_STOPPED
Definition gss_tsig_messages.h:17
isc::hooks
Definition callout_handle.cc:21
isc::log
Definition buffer_appender_impl.cc:17
isc::log::DBGLVL_TRACE_BASIC
const int DBGLVL_TRACE_BASIC
Trace basic operations.
Definition log_dbglevels.h:69
isc::stats
Definition context.cc:18
isc
Defines the logger used by the top-level component of kea-lfc.
Definition check_exists_add.cc:18
isc::gss_tsig::GssTsigKeyServerTag
Tag for the server ID index for searching GSS-TSIG key.
Definition gss_tsig_impl.h:27