Kea 3.1.7
radius_parsers.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2026 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 <radius_parsers.h>
10#include <radius_log.h>
11#include <cc/data.h>
13#include <dhcpsrv/cfgmgr.h>
14#include <eval/eval_context.h>
15#include <util/encode/encode.h>
16
17#include <limits>
18#include <string>
19#include <cstdlib>
20#include <cstring>
21#include <sstream>
22
23using namespace std;
24using namespace isc;
25using namespace isc::asiolink;
26using namespace isc::data;
27using namespace isc::dhcp;
28using namespace isc::util;
29
30namespace isc {
31namespace radius {
32
34const set<string>
36 "access", "accounting", "tls", // services
37 "bindaddr", "canonical-mac-address", "client-id-pop0",
38 "client-id-printable", "deadtime", "dictionary",
39 "extract-duid", "identifier-type4", "identifier-type6",
40 "nas-ports", "protocol",
41 "reselect-subnet-address", "reselect-subnet-pool",
42 "retries", "session-history", "thread-pool-size", "timeout",
43 "use-message-authenticator",
44 "comment" // not saved for toElement
45};
46
49 { "bindaddr", Element::string, "*" },
50 { "canonical-mac-address", Element::boolean, "false" },
51 { "client-id-pop0", Element::boolean, "false" },
52 { "client-id-printable", Element::boolean, "false" },
53 { "deadtime", Element::integer, "0" },
54 { "dictionary", Element::string, DICTIONARY },
55 { "extract-duid", Element::boolean, "true" },
56 { "identifier-type4", Element::string, "client-id" },
57 { "identifier-type6", Element::string, "duid" },
58 { "protocol", Element::string, "UDP" },
59 { "reselect-subnet-address", Element::boolean, "false" },
60 { "reselect-subnet-pool", Element::boolean, "false" },
61 { "retries", Element::integer, "3" },
62 { "session-history", Element::string, "" },
63 { "thread-pool-size", Element::integer, "0" },
64 { "timeout", Element::integer, "10" }
65};
66
69 { PW_USER_NAME, "User-Name", PW_TYPE_STRING },
70 { PW_USER_PASSWORD, "User-Password", PW_TYPE_STRING },
71 { PW_NAS_IP_ADDRESS, "NAS-IP-Address", PW_TYPE_IPADDR },
72 { PW_NAS_PORT, "NAS-Port", PW_TYPE_INTEGER },
73 { PW_SERVICE_TYPE, "Service-Type", PW_TYPE_INTEGER },
74 { PW_FRAMED_IP_ADDRESS, "Framed-IP-Address", PW_TYPE_IPADDR },
75 { PW_REPLY_MESSAGE, "Reply-Message", PW_TYPE_STRING },
76 { PW_CLASS, "Class", PW_TYPE_STRING },
77 { PW_VENDOR_SPECIFIC, "Vendor-Specific", PW_TYPE_VSA },
78 { PW_CALLING_STATION_ID, "Calling-Station-Id", PW_TYPE_STRING },
79 { PW_ACCT_STATUS_TYPE, "Acct-Status-Type", PW_TYPE_INTEGER },
80 { PW_ACCT_DELAY_TIME, "Acct-Delay-Time", PW_TYPE_INTEGER },
81 { PW_ACCT_SESSION_ID, "Acct-Session-Id", PW_TYPE_STRING },
82 { PW_MESSAGE_AUTHENTICATOR, "Message-Authenticator", PW_TYPE_STRING },
83 { PW_FRAMED_POOL, "Framed-Pool", PW_TYPE_STRING },
84 { PW_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", PW_TYPE_IPV6ADDR },
85 { PW_DELEGATED_IPV6_PREFIX, "Delegated-IPv6-Prefix", PW_TYPE_IPV6PREFIX },
86 { PW_FRAMED_IPV6_ADDRESS, "Framed-IPv6-Address", PW_TYPE_IPV6ADDR }
87};
88
91 { "data", Element::string, "" },
92 { "expr", Element::string, "" },
93 { "raw", Element::string, "" },
94 { "vendor", Element::string, "" }
95};
96
97void
99 try {
101
102 // Set defaults.
104
105 // dictionary (do it first).
106 const ConstElementPtr& dictionary = config->get("dictionary");
107 riref.dictionary_ = dictionary->stringValue();
108
109 // Read the dictionary
110 if (!AttrDefs::instance().getByType(1)) {
111 uint32_t vendor = 0;
112 try {
114 } catch (const exception& ex) {
115 isc_throw(BadValue, "can't read radius dictionary: "
116 << ex.what());
117 }
118 if (vendor != 0) {
119 isc_throw(BadValue, "vendor definitions were not properly "
120 << "closed: vendor " << vendor << " is still open");
121 }
122 }
123
124 // Check it.
126
127 // Protocol.
128 const ConstElementPtr& protocol = config->get("protocol");
129 string proto = protocol->stringValue();
130 if (proto == "UDP") {
131 riref.proto_ = PW_PROTO_UDP;
132 } else if (proto == "TCP") {
133 riref.proto_ = PW_PROTO_TCP;
134 } else if (proto == "TLS") {
135 riref.proto_ = PW_PROTO_TLS;
136 } else {
137 isc_throw(BadValue, "unknown protocol " << proto);
138 }
139 if (riref.proto_ == PW_PROTO_TCP) {
140 isc_throw(NotImplemented, "protocol 'TCP' is not supported");
141 }
142
143 // bindaddr.
144 const ConstElementPtr& bindaddr = config->get("bindaddr");
145 riref.bindaddr_ = bindaddr->stringValue();
146
147 // canonical-mac-address.
148 const ConstElementPtr& canonical = config->get("canonical-mac-address");
149 riref.canonical_mac_address_ = canonical->boolValue();
150
151 // client-id-pop0.
152 const ConstElementPtr& pop0 = config->get("client-id-pop0");
153 riref.clientid_pop0_ = pop0->boolValue();
154
155 // client-id-printable.
156 const ConstElementPtr& try_printable = config->get("client-id-printable");
157 riref.clientid_printable_ = try_printable->boolValue();
158
159 // deadtime.
160 const ConstElementPtr& deadtime = config->get("deadtime");
161 int64_t deadtime64 = deadtime->intValue();
162 if ((deadtime64 < 0) ||
163 (deadtime64 > numeric_limits<unsigned>::max())) {
164 isc_throw(OutOfRange, "bad deadtime " << deadtime64
165 << " not in [0.."
166 << numeric_limits<unsigned>::max() << "]");
167 }
168 riref.deadtime_ = static_cast<unsigned>(deadtime64);
169
170 // extract-duid.
171 const ConstElementPtr& rfc4361 = config->get("extract-duid");
172 riref.extract_duid_ = rfc4361->boolValue();
173
174 // identifier-type4.
175 const ConstElementPtr& id_type4 = config->get("identifier-type4");
176 riref.id_type4_ = Host::getIdentifierType(id_type4->stringValue());
177
178 // identifier-type6.
179 const ConstElementPtr& id_type6 = config->get("identifier-type6");
180 riref.id_type6_ = Host::getIdentifierType(id_type6->stringValue());
181
182 // reselect-subnet-address.
183 const ConstElementPtr& resel_addr =
184 config->get("reselect-subnet-address");
185 riref.reselect_subnet_address_ = resel_addr->boolValue();
186
187 // reselect-subnet-pool.
188 const ConstElementPtr& resel_pool =
189 config->get("reselect-subnet-pool");
190 riref.reselect_subnet_pool_ = resel_pool->boolValue();
191
192 // retries.
193 const ConstElementPtr& retries = config->get("retries");
194 int64_t retries64 = retries->intValue();
195 if ((retries64 < 0) ||
196 (retries64 > numeric_limits<unsigned>::max())) {
197 isc_throw(OutOfRange, "bad retries " << retries64
198 << " not in [0.."
199 << numeric_limits<unsigned>::max() << "]");
200 }
201 riref.retries_ = static_cast<unsigned>(retries64);
202
203 // session-history.
204 const ConstElementPtr& session_history = config->get("session-history");
205 riref.session_history_filename_ = session_history->stringValue();
206
207 // thread-pool-size.
208 const ConstElementPtr& thread_pool_size = config->get("thread-pool-size");
209 riref.thread_pool_size_ = thread_pool_size->intValue();
210
211 // timeout.
212 const ConstElementPtr& timeout = config->get("timeout");
213 int64_t timeout64 = timeout->intValue();
214 if ((timeout64 < 0) ||
215 (timeout64 > numeric_limits<long>::max() / 1000)) {
216 isc_throw(OutOfRange, "bad timeout " << timeout64
217 << " not in [0.."
218 << (numeric_limits<long>::max() / 1000) << "]");
219 }
220 riref.timeout_ = static_cast<unsigned>(timeout64);
221
222 // use-message-authenticator.
223 const ConstElementPtr use_ma = config->get("use-message-authenticator");
224 if (!use_ma) {
225 if (riref.proto_ != PW_PROTO_TLS) {
226 riref.use_message_authenticator_ = true;
227 } else {
228 riref.use_message_authenticator_ = false;
229 }
230 } else {
231 riref.use_message_authenticator_ = use_ma->boolValue();
232 }
233
234 // TLS service.
235 const ConstElementPtr& tls = config->get("tls");
236 if (tls) {
237 if (riref.proto_ != PW_PROTO_TLS) {
238 isc_throw(BadValue, "'tls' service can't be configured "
239 << "when protocol is not 'TLS'");
240 }
241 RadiusServiceParser parser;
242 parser.parse(riref.tls_, tls);
243 parser.checkAttributes(riref.tls_);
244 }
245
246 // Access service.
247 const ConstElementPtr& access = config->get("access");
248 if (access) {
249 RadiusServiceParser parser;
250 parser.parse(riref.auth_, access);
251 parser.checkAttributes(riref.auth_);
252 }
253
254 // Accounting service.
255 const ConstElementPtr& accounting = config->get("accounting");
256 if (accounting) {
257 RadiusServiceParser parser;
258 parser.parse(riref.acct_, accounting);
259 parser.checkAttributes(riref.acct_);
260 }
261
262 // nas-ports (last so we can return when it is not present.
263 const ConstElementPtr& nas_ports = config->get("nas-ports");
264 if (!nas_ports) {
265 return;
266 }
267 for (auto const& entry : nas_ports->listValue()) {
268 // port is mandatory.
269 const ConstElementPtr& port = entry->get("port");
270 if (!port) {
271 isc_throw(BadValue, "missing port in nas-ports entry: "
272 << entry->str());
273 }
274
275 // By subnet-id.
276 const ConstElementPtr& id = entry->get("subnet-id");
277 if (id) {
278 riref.remap_[id->intValue()] = port->intValue();
279 continue;
280 }
281
282 // By subnet-prefix (to be resolved into an ID).
283 const ConstElementPtr& prefix = entry->get("subnet-prefix");
284 if (prefix) {
285 if (CfgMgr::instance().getFamily() == AF_INET) {
286 auto subnet = CfgMgr::instance().getStagingCfg()->
287 getCfgSubnets4()->getByPrefix(prefix->stringValue());
288 if (!subnet) {
289 isc_throw(BadValue, "can't find subnet for "
290 << entry->str());
291 }
292 riref.remap_[subnet->getID()] = port->intValue();
293 continue;
294 } else {
295 auto subnet = CfgMgr::instance().getStagingCfg()->
296 getCfgSubnets6()->getByPrefix(prefix->stringValue());
297 if (!subnet) {
298 isc_throw(BadValue, "can't find subnet for "
299 << entry->str());
300 }
301 riref.remap_[subnet->getID()] = port->intValue();
302 continue;
303 }
304 }
305
306 // By shared-network-name (to be resolved, add all subnets).
307 const ConstElementPtr& name = entry->get("shared-network-name");
308 if (name) {
309 if (CfgMgr::instance().getFamily() == AF_INET) {
310 auto network = CfgMgr::instance().getStagingCfg()->
311 getCfgSharedNetworks4()->getByName(name->stringValue());
312 if (!network) {
313 isc_throw(BadValue, "can't find shared network for "
314 << entry->str());
315 }
316 for (auto const& subnet : *network->getAllSubnets()) {
317 riref.remap_[subnet->getID()] = port->intValue();
318 }
319 continue;
320 } else {
321 auto network = CfgMgr::instance().getStagingCfg()->
322 getCfgSharedNetworks6()->getByName(name->stringValue());
323 if (!network) {
324 isc_throw(BadValue, "can't find shared network for "
325 << entry->str());
326 }
327 for (auto const& subnet : *network->getAllSubnets()) {
328 riref.remap_[subnet->getID()] = port->intValue();
329 }
330 continue;
331 }
332 }
333
334 // Unknown selector.
335 if (entry->size() > 1) {
336 isc_throw(BadValue, "unknown selector in " << entry->str());
337 }
338
339 // Default is in subnet 0 (SUBNET_ID_DEFAULT).
340 riref.remap_[SUBNET_ID_DEFAULT] = port->intValue();
341 }
342
343 } catch (const ConfigError&) {
344 throw;
345 } catch (const std::exception& ex) {
346 isc_throw(ConfigError, ex.what());
347 }
348}
349
351const set<string>
353 "enabled", "servers", "attributes", "peer-updates", "max-pending-requests",
354 "idle-timer-interval"
355};
356
357void
359 const ConstElementPtr& srv_cfg) {
360 try {
362
363 // map type.
364 if (srv_cfg->getType() != Element::map) {
365 isc_throw(BadValue, "expected service to be map, but got "
366 << Element::typeToName(srv_cfg->getType())
367 << " instead");
368 }
369
370 // keywords.
371 const set<string> keywords = RadiusServiceParser::SERVICE_KEYWORDS;
372 for (auto const& entry : srv_cfg->mapValue()) {
373 if (keywords.count(entry.first) == 0) {
374 isc_throw(BadValue, "unknown service parameter: "
375 << entry.first);
376 }
377 }
378
379 // Enabled.
380 const ConstElementPtr& enabled = srv_cfg->get("enabled");
381 if (enabled) {
382 if (riref.proto_ != PW_PROTO_TLS) {
383 isc_throw(BadValue, "'enabled' makes sense only with TLS");
384 }
385 if (enabled->getType() != Element::boolean) {
386 isc_throw(BadValue, "expected enabled to be boolean, "
387 << "but got "
388 << Element::typeToName(enabled->getType())
389 << " instead");
390 }
391 if (service->name_ == "tls") {
392 isc_throw(BadValue, "can't set enabled in 'tls'");
393 }
394 service->enabled_ = enabled->boolValue();
395 } else {
396 if ((riref.proto_ == PW_PROTO_TLS) &&
397 (service->name_ != "tls")) {
398 service->enabled_ = true;
399 }
400 }
401
402 // servers.
403 const ConstElementPtr& servers = srv_cfg->get("servers");
404 if (servers) {
405 if ((riref.proto_ == PW_PROTO_TLS) &&
406 (service->name_ != "tls")) {
407 isc_throw(BadValue, "can't have servers entry in '"
408 << service->name_ << "' with TLS");
409 }
411 parser.parse(service, servers);
412 if (!service->servers_.empty()) {
413 service->enabled_ = true;
414 }
415 }
416
417 // attributes.
418 const ConstElementPtr& attributes = srv_cfg->get("attributes");
419 if (attributes) {
420 if (service->name_ == "tls") {
421 isc_throw(BadValue, "can't define attributes in 'tls'");
422 }
424 parser.parse(service, attributes);
425 }
426
427 // peer-updates.
428 const ConstElementPtr& peer_updates = srv_cfg->get("peer-updates");
429 if (peer_updates) {
430 if (service->name_ != "accounting") {
431 isc_throw(BadValue, "peer-updates configured for the "
432 << service->name_ << " service, but it is "
433 << "only supported for the accounting service");
434 }
435 if (peer_updates->getType() != Element::boolean) {
436 isc_throw(BadValue, "expected peer-updates to be boolean, "
437 << "but got "
438 << Element::typeToName(peer_updates->getType())
439 << " instead");
440 }
441 service->peer_updates_ = peer_updates->boolValue();
442 }
443
444 // max-pending-requests.
445 const ConstElementPtr& max_pending_requests =
446 srv_cfg->get("max-pending-requests");
447 if (max_pending_requests) {
448 if (service->name_ != "access") {
449 isc_throw(BadValue, "max-pending-requests configured for the "
450 << service->name_ << " service, but it is only "
451 << "supported for the access service");
452 }
453 if (max_pending_requests->getType() != Element::integer) {
454 isc_throw(BadValue, "expected max-pending-requests to be "
455 << "integer, but got "
456 << Element::typeToName(max_pending_requests->getType())
457 << " instead");
458 }
459 if (max_pending_requests->intValue() < 0) {
460 isc_throw(BadValue, "expected max-pending-requests to be "
461 << "positive, but got "
462 << max_pending_requests->intValue()
463 << " instead");
464 }
465 service->max_pending_requests_ = max_pending_requests->intValue();
466 }
467
468 // idle-timer-interval.
469 const ConstElementPtr& idle_timer_interval =
470 srv_cfg->get("idle-timer-interval");
471 if (idle_timer_interval) {
472 if ((riref.proto_ == PW_PROTO_TLS) &&
473 (service->name_ != "tls")) {
474 isc_throw(BadValue, "can't have idle-timer-interval entry in '"
475 << service->name_ << "' with TLS");
476 }
477 if (idle_timer_interval->getType() != Element::integer) {
478 isc_throw(BadValue, "expected idle-timer-interval to be "
479 << "integer, but got "
480 << Element::typeToName(idle_timer_interval->getType())
481 << " instead");
482 }
483 if (idle_timer_interval->intValue() < 0) {
484 isc_throw(BadValue, "expected idle-timer-interval to be "
485 << "positive, but got "
486 << idle_timer_interval->intValue()
487 << " instead");
488 }
489 service->idle_timer_interval_ = idle_timer_interval->intValue();
490 }
491 } catch (const std::exception& ex) {
492 isc_throw(ConfigError, ex.what() << " (parsing "
493 << service->name_ << ")");
494 }
495}
496
497void
499 if (!service->enabled_) {
500 return;
501 }
502
503 const CfgAttributes& cfg_attrs = service->attributes_;
504 const Attributes& attrs = cfg_attrs.getAll();
505 if (service->name_ == "access") {
506 // Nothing yet.
507 } else if (service->name_ == "accounting") {
508 // Expressions have no associated attributes.
509 if (cfg_attrs.size() > attrs.size()) {
511 "Expressions are not yet supported in accounting");
512 }
513 }
514}
515
516void
518 const ConstElementPtr& srv_list) {
519 for (auto const& srv : srv_list->listValue()) {
520 RadiusServerParser parser;
521 parser.parse(service, srv);
522 }
523}
524
525void
527 const ElementPtr& server) {
529
530 // Details will be logged.
531 ostringstream msg;
532
533 // Peer address (was name).
534 IOAddress peer_addr("::");
535 const string& name = getString(server, "name");
536 try {
537 peer_addr = IOAddress(name);
538 } catch (const Exception&) {
539 try {
540 peer_addr = Server::getAddress(name);
541 } catch (const Exception& ex) {
542 isc_throw(ConfigError, "can't resolve '" << name << "': "
543 << ex.what());
544 }
545 }
546 msg << "peer-addr=" << peer_addr.toText();
547
548 // port.
549 uint16_t port;
550 if (server->contains("port")) {
551 port = getUint16(server, "port");
552 } else if (service->name_ == "tls") {
553 port = PW_TLS_PORT;
554 } else if (service->name_ == "access") {
555 port = PW_AUTH_PORT;
556 } else {
557 port = PW_ACCT_PORT;
558 }
559 msg << " port=" << port;
560
561 // Local address.
562 IOAddress local_addr("::");
563 const string& local = riref.bindaddr_;
564 if (local != "*") {
565 try {
566 local_addr = IOAddress(local);
567 } catch (const Exception& ex) {
568 isc_throw(ConfigError, "bad local address '" << local << "': "
569 << ex.what());
570 }
571 } else {
572 try {
573 local_addr = Server::getSrcAddress(peer_addr);
574 } catch (const Exception& ex) {
575 isc_throw(ConfigError, "can't get local address: " << ex.what());
576 }
577 }
578 msg << " local_addr=" << local_addr;
579
580 // secret.
581 string secret;
582 if (!server->contains("secret") && (service->name_ == "tls")) {
583 secret = "radsec";
584 } else {
585 secret = getString(server, "secret");
586 try {
588 } catch (const DefaultCredential& ex) {
589 isc_throw(ConfigError, "illegal use of a default secret");
590 }
591 }
592 msg << " secret=*****";
593
594 // TLS parameters.
595 TlsContextPtr tls_context;
596 if (service->name_ == "tls") {
597 string trust_anchor = getString(server, "trust-anchor");
598 string cert_file = getString(server, "cert-file");
599 string key_file = getString(server, "key-file");
600 TlsContext::configure(tls_context, TlsRole::CLIENT,
601 trust_anchor, cert_file, key_file);
602 }
603
604 try {
605 ServerPtr srv(new Server(peer_addr, port, local_addr, tls_context,
606 secret, riref.timeout_, riref.deadtime_));
607 service->servers_.push_back(srv);
608 } catch (const Exception& ex) {
609 isc_throw(ConfigError, "can't create " << service->name_
610 << " server '" << msg.str() << "': " << ex.what());
611 }
612
613 // Done.
615 .arg(service->name_)
616 .arg(msg.str());
617}
618
619void
621 const ConstElementPtr& attr_list) {
622 for (auto const& attr : attr_list->listValue()) {
624 parser.parse(service, attr);
625 }
626}
627
628void
630 const ElementPtr& attr) {
631 AttrDefPtr def;
632
633 // Set defaults.
635
636 // vendor.
637 uint32_t vendor = 0;
638 const ConstElementPtr& vendor_elem = attr->get("vendor");
639 if (!vendor_elem) {
640 // Should not happen as it is added by setDefaults.
641 isc_throw(Unexpected, "no vendor parameter");
642 } else if (vendor_elem->getType() != Element::string) {
643 // Expected to be a common error.
644 isc_throw(TypeError, "vendor parameter must be a string");
645 }
646 const string& vendor_txt = vendor_elem->stringValue();
647 if (!vendor_txt.empty()) {
648 IntCstDefPtr vendor_cst =
650 if (vendor_cst) {
651 vendor = vendor_cst->value_;
652 } else {
653 try {
654 int64_t val = boost::lexical_cast<int64_t>(vendor_txt);
655 if ((val < numeric_limits<int32_t>::min()) ||
656 (val > numeric_limits<uint32_t>::max())) {
657 isc_throw(Unexpected, "not 32 bit " << vendor_txt);
658 }
659 vendor = static_cast<uint32_t>(val);
660 } catch (...) {
661 isc_throw(ConfigError, "can't parse vendor '"
662 << vendor_txt << "'");
663 }
664 }
665 }
666
667 // name.
668 const ConstElementPtr& name = attr->get("name");
669 if (name) {
670 if (name->stringValue().empty()) {
671 isc_throw(ConfigError, "attribute name is empty");
672 }
673 def = AttrDefs::instance().getByName(name->stringValue(), vendor);
674 if (!def) {
675 ostringstream msg;
676 msg << "attribute '" << name->stringValue() << "'";
677 if (vendor != 0) {
678 msg << " in vendor '" << vendor_txt << "'";
679 }
680 msg << " is unknown";
681 isc_throw(ConfigError, msg.str());
682 }
683 }
684
685 // type.
686 const ConstElementPtr& type = attr->get("type");
687 if (type) {
688 if ((type->intValue() < 0) || (type->intValue() > 255)) {
689 isc_throw(ConfigError, "out of range attribute type "
690 << type->intValue());
691 }
692 uint8_t attrib = static_cast<uint8_t>(type->intValue());
693 if (def && (def->type_ != attrib)) {
694 ostringstream msg;
695 msg << "'" << name->stringValue() << "' attribute";
696 if (vendor != 0) {
697 msg << " in vendor '" << vendor_txt << "'";
698 }
699 msg << " has type " << static_cast<unsigned>(def->type_)
700 << ", not " << static_cast<unsigned>(attrib);
701 isc_throw(ConfigError, msg.str());
702 }
703 if (!def) {
704 def = AttrDefs::instance().getByType(attrib, vendor);
705 }
706 if (!def) {
707 ostringstream msg;
708 msg << "attribute type " << static_cast<unsigned>(attrib);
709 if (vendor != 0) {
710 msg << " in vendor '" << vendor_txt << "'";
711 }
712 msg << " is unknown";
713 isc_throw(ConfigError, msg.str());
714 }
715 }
716
717 // name or type are required.
718 if (!def) {
719 isc_throw(ConfigError, "name or type are required");
720 }
721
722 // data.
723 const string& data_txt = getString(attr, "data");
724
725 // raw.
726 const string& raw_txt = getString(attr, "raw");
727
728 // expr.
729 const string& expr_txt = getString(attr, "expr");
730
732
733 ExpressionPtr expression;
734 if (!expr_txt.empty()) {
735 if (!data_txt.empty() || !raw_txt.empty()) {
736 isc_throw(ConfigError, "data, raw and expr are exclusive");
737 }
738 Option::Universe universe;
739 if (CfgMgr::instance().getFamily() == AF_INET) {
740 universe = Option::V4;
741 } else {
742 universe = Option::V6;
743 }
744 try {
745 EvalContext eval_ctx(universe);
746 eval_ctx.parseString(expr_txt, EvalContext::PARSER_STRING);
747 expression.reset(new Expression());
748 *expression = eval_ctx.expression_;
749 } catch (const std::exception& ex) {
750 isc_throw(ConfigError, "expression: [" << expr_txt
751 << "] error: " << ex.what() << " for "
752 << def->name_ << " attribute");
753 }
754
755 service->attributes_.add(def, AttributePtr(), expression, expr_txt);
756 } else if (!raw_txt.empty()) {
757 if (!data_txt.empty()) {
758 isc_throw(ConfigError, "data and raw are exclusive");
759 }
760 // The decodeHex function expects that the string contains an
761 // even number of digits. If we don't meet this requirement,
762 // we have to insert a leading 0.
763 string padded = raw_txt;
764 if ((padded.size() % 2) != 0) {
765 padded = padded.insert(0, "0");
766 }
767 vector<uint8_t> binary;
768 try {
769 encode::decodeHex(padded, binary);
770 } catch (...) {
771 isc_throw(ConfigError, "can't decode raw: [" << raw_txt
772 << "] for " << def->name_ << " attribute");
773 }
774 try {
775 AttributePtr attribute = Attribute::fromBytes(def, binary);
776 service->attributes_.add(def, attribute);
777 } catch (const Exception& ex) {
778 isc_throw(ConfigError, "can't create " << def->name_
779 << " attribute from raw: [" << raw_txt << "]: "
780 << ex.what());
781 }
782 } else {
783 try {
784 AttributePtr attribute = Attribute::fromText(def, data_txt);
785 service->attributes_.add(def, attribute);
786 } catch (const Exception& ex) {
787 isc_throw(ConfigError, "can't create " << def->name_
788 << " attribute from [" << data_txt << "]: "
789 << ex.what());
790 }
791 }
792}
793
794} // end of namespace isc::radius
795} // end of namespace isc
static std::string typeToName(Element::types type)
Returns the name of the given type as a string.
Definition data.cc:709
@ map
Definition data.h:160
@ integer
Definition data.h:153
@ boolean
Definition data.h:155
@ string
Definition data.h:157
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
An exception that is thrown if an error occurs while configuring any server.
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown when a function is not implemented.
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
A generic exception that is thrown when an unexpected error condition occurs.
Exception thrown on attempt to use a default credential.
static std::string getString(isc::data::ConstElementPtr scope, const std::string &name)
Returns a string parameter from a scope.
uint16_t getUint16(isc::data::ConstElementPtr scope, const std::string &name)
Returns a value converted to uint16_t.
static size_t setDefaults(isc::data::ElementPtr scope, const SimpleDefaults &default_values)
Sets the default values.
A standard Data module exception that is thrown if a function is called for an Element that has a wro...
Definition data.h:37
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:29
SrvConfigPtr getStagingCfg()
Returns a pointer to the staging configuration.
Definition cfgmgr.cc:121
IdentifierType getIdentifierType() const
Returns the identifier type.
Definition host.cc:273
Universe
defines option universe DHCPv4 or DHCPv6
Definition option.h:90
Evaluation context, an interface to the expression evaluation.
bool parseString(const std::string &str, ParserType type=PARSER_BOOL)
Run the parser on the string specified.
@ PARSER_STRING
expression is expected to evaluate to string
isc::dhcp::Expression expression_
Parsed expression (output tokens are stored here)
static AttrDefs & instance()
Returns a single instance.
void readDictionary(const std::string &path, uint32_t &vendor, unsigned int depth=0)
Read a dictionary from a file.
AttrDefPtr getByName(const std::string &name, const uint32_t vendor=0) const
Get attribute definition by name and vendor.
void checkStandardDefs(const AttrDefList &defs) const
Check if a list of standard attribute definitions are available and correct.
AttrDefPtr getByType(const uint8_t type, const uint32_t vendor=0) const
Get attribute definition by type and vendor.
static AttributePtr fromBytes(const std::vector< uint8_t > &bytes)
Generic factories.
static AttributePtr fromText(const AttrDefPtr &def, const std::string &value)
From definition generic factories.
Collection of attributes.
size_t size() const
Returns the number of elements.
Attribute data configuration.
Attributes getAll() const
Get all attributes in the configuration.
size_t size() const
Returns the number of elements.
Attribute list parser for Radius.
void parse(const RadiusServicePtr &service, const data::ConstElementPtr &attr_list)
Parses Radius list of attribute configurations.
Attribute configuration parser for Radius.
static const data::SimpleDefaults ATTRIBUTE_DEFAULTS
Defaults for Radius attribute configuration.
void parse(const RadiusServicePtr &service, const data::ElementPtr &attr)
Parses Radius attribute configuration.
static const std::set< std::string > RADIUS_KEYWORDS
Keywords (aka global configuration entry names).
void parse(data::ElementPtr &config)
Parses Radius configuration.
static const data::SimpleDefaults RADIUS_DEFAULTS
Defaults for Radius configuration.
static const AttrDefList USED_STANDARD_ATTR_DEFS
Needed standard attributes definitions.
Radius hooks library implementation.
Definition radius.h:142
unsigned thread_pool_size_
Thread pool size.
Definition radius.h:326
std::string dictionary_
Dictionary path.
Definition radius.h:266
boost::shared_ptr< RadiusTls > tls_
Pointer to tls (never null).
Definition radius.h:281
std::string bindaddr_
bindaddr.
Definition radius.h:296
bool clientid_pop0_
Client Id pop leading zero(s).
Definition radius.h:302
dhcp::Host::IdentifierType id_type4_
Identifier type for IPv4.
Definition radius.h:332
bool reselect_subnet_address_
Reselect subnet using address.
Definition radius.h:317
boost::shared_ptr< RadiusAccess > auth_
Pointer to access (never null).
Definition radius.h:284
bool extract_duid_
Extract Duid from Client Id.
Definition radius.h:311
unsigned timeout_
Timeout.
Definition radius.h:329
dhcp::Host::IdentifierType id_type6_
Identifier type for IPv6.
Definition radius.h:335
bool canonical_mac_address_
Canonical MAC address.
Definition radius.h:299
unsigned deadtime_
Deadtime.
Definition radius.h:308
boost::shared_ptr< RadiusAccounting > acct_
Pointer to accounting (never null).
Definition radius.h:287
unsigned retries_
Retries.
Definition radius.h:320
std::map< uint32_t, uint32_t > remap_
Subnet ID to NAS port map.
Definition radius.h:278
std::string session_history_filename_
Session history filename.
Definition radius.h:323
bool reselect_subnet_pool_
Reselect subnet using pool.
Definition radius.h:314
bool clientid_printable_
Client Id try printable.
Definition radius.h:305
RadiusProtocol proto_
Transport protocol.
Definition radius.h:269
static RadiusImpl & instance()
RadiusImpl is a singleton class.
Definition radius.cc:163
bool use_message_authenticator_
Use Message-Authenticator attribute.
Definition radius.h:338
Server list parser for Radius.
void parse(const RadiusServicePtr &service, const data::ConstElementPtr &srv_list)
Parses Radius server list.
Server parser for Radius.
void parse(const RadiusServicePtr &service, const data::ElementPtr &server)
Parses Radius server.
Service parser for Radius.
static const std::set< std::string > SERVICE_KEYWORDS
Keywords (aka service configuration entry names).
void checkAttributes(const RadiusServicePtr &service)
Check Radius attributes.
void parse(const RadiusServicePtr &service, const data::ConstElementPtr &srv_cfg)
Parses Radius service.
RADIUS server class.
static asiolink::IOAddress getSrcAddress(const asiolink::IOAddress &dest)
Get the source address from a destination address.
static asiolink::IOAddress getAddress(const std::string &name)
Get an address from a name.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition macros.h:20
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:30
std::vector< SimpleDefault > SimpleDefaults
This specifies all default values in a given scope (e.g. a subnet).
boost::shared_ptr< Element > ElementPtr
Definition data.h:29
boost::shared_ptr< Expression > ExpressionPtr
Definition token.h:31
std::vector< TokenPtr > Expression
This is a structure that holds an expression converted to RPN.
Definition token.h:29
boost::shared_ptr< IntCstDef > IntCstDefPtr
Shared pointers to Integer constant definition.
const isc::log::MessageID RADIUS_SERVER_CONFIGURED
@ PW_DELEGATED_IPV6_PREFIX
ipv6prefix.
@ PW_MESSAGE_AUTHENTICATOR
string.
@ PW_FRAMED_IPV6_ADDRESS
ipv6addr.
std::list< AttrDef > AttrDefList
List of Attribute definitions.
boost::shared_ptr< AttrDef > AttrDefPtr
Shared pointers to Attribute definition.
boost::shared_ptr< Server > ServerPtr
Type of shared pointers to a RADIUS server object.
boost::shared_ptr< RadiusService > RadiusServicePtr
Type of pointers to Radius service.
boost::shared_ptr< Attribute > AttributePtr
isc::log::Logger radius_logger("radius-hooks")
Radius Logger.
Definition radius_log.h:35
vector< uint8_t > pop0(const ClientIdPtr &client_id)
Pop leading zero in a DHCPv4 client-id.
void decodeHex(const string &encoded_str, vector< uint8_t > &output)
Decode a base16 encoded string into binary data.
Definition encode.cc:367
Defines the logger used by the top-level component of kea-lfc.
static void check(const std::string &value)
Check if the value is a default credential.