123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- // Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC")
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this
- // file, You can obtain one at http://mozilla.org/MPL/2.0/.
- #include <config.h>
- #include <dhcp/libdhcp++.h>
- #include <dhcp/option.h>
- #include <dhcp/option_definition.h>
- #include <dhcp/option_space.h>
- #include <dhcpsrv/cfg_option.h>
- #include <dhcpsrv/db_exceptions.h>
- #include <dhcpsrv/dhcpsrv_log.h>
- #include <dhcpsrv/radius_host_data_source.h>
- #include <dhcpsrv/db_exceptions.h>
- #include <util/buffer.h>
- #include <util/optional_value.h>
- #include <boost/algorithm/string/split.hpp>
- #include <boost/algorithm/string/classification.hpp>
- #include <boost/array.hpp>
- #include <boost/pointer_cast.hpp>
- #include <boost/static_assert.hpp>
- #include <radcli/radcli.h>
- #include <stdint.h>
- #include <string>
- using namespace isc;
- using namespace isc::asiolink;
- using namespace isc::dhcp;
- using namespace isc::util;
- using namespace std;
- /// @brief Maximum size of an IPv6 address represented as a text string.
- ///
- /// This is 32 hexadecimal characters written in 8 groups of four, plus seven
- /// colon separators.
- const size_t ADDRESS6_TEXT_MAX_LEN = 39;
- /// @brief Maximum length of classes stored in a dhcp4/6_client_classes
- /// columns.
- const size_t CLIENT_CLASSES_MAX_LEN = 255;
- /// @brief Maximum length of the hostname stored in DNS.
- ///
- /// This length is restricted by the length of the domain-name carried
- /// in the Client FQDN %Option (see RFC4702 and RFC4704).
- const size_t HOSTNAME_MAX_LEN = 255;
- /// @brief Maximum length of option value.
- const size_t OPTION_VALUE_MAX_LEN = 4096;
- /// @brief Maximum length of option value specified in textual format.
- const size_t OPTION_FORMATTED_VALUE_MAX_LEN = 8192;
- /// @brief Maximum length of option space name.
- const size_t OPTION_SPACE_MAX_LEN = 128;
- /// @brief Maximum length of the server hostname.
- const size_t SERVER_HOSTNAME_MAX_LEN = 64;
- /// @brief Maximum length of the boot file name.
- const size_t BOOT_FILE_NAME_MAX_LEN = 128;
- /// @brief Numeric value representing last supported identifier.
- ///
- /// This value is used to validate whether the identifier type stored in
- /// a database is within bounds. of supported identifiers.
- const uint8_t MAX_IDENTIFIER_TYPE = static_cast<uint8_t>(Host::LAST_IDENTIFIER_TYPE);
- namespace isc {
- namespace dhcp {
- static std::string getParameter(const DatabaseConnection::ParameterMap& parameters, const std::string& name) {
- DatabaseConnection::ParameterMap::const_iterator param = parameters.find(name);
- if (param == parameters.end()) {
- isc_throw(BadValue, "Parameter " << name << " not found");
- }
- return (param->second);
- }
- RadiusHostDataSource::
- RadiusHostDataSource(const DatabaseConnection::ParameterMap& parameters) {
- int res;
- rh = rc_new();
- if (rh == NULL) {
- isc_throw(DbOpenError, "Failed to initialize Radius client");
- }
- rh = rc_config_init(rh);
- if (rh == NULL) {
- isc_throw(DbOpenError, "Failed to initialize Radius client");
- }
- res = rc_add_config(rh, "auth_order", "radius", NULL, 0);
- if (res != 0) {
- isc_throw(DbOpenError, "Failed to configure Radius auth_order");
- }
- /* TODO: just define manually the few attributes we need */
- res = rc_add_config(rh, "dictionary", "/usr/share/radcli/dictionary", NULL, 0);
- if (res != 0) {
- isc_throw(DbOpenError, "Failed to configure Radius dictionary");
- }
- res = rc_add_config(rh, "radius_timeout", "1", NULL, 0);
- if (res != 0) {
- isc_throw(DbOpenError, "Failed to configure Radius timeout");
- }
- res = rc_add_config(rh, "radius_retries", "1", NULL, 0);
- if (res != 0) {
- isc_throw(DbOpenError, "Failed to configure Radius retries");
- }
- try {
- realm_ = getParameter(parameters, "name");
- } catch (...) {
- // No realm. Throw an exception.
- isc_throw(NoDatabaseName, "must specify a database name (realm) for Radius connection");
- }
- const char* host = "localhost";
- string shost;
- try {
- shost = getParameter(parameters, "host");
- host = shost.c_str();
- } catch (...) {
- // No host. Fine, we'll use "localhost"
- }
- unsigned int port = 0;
- string sport;
- try {
- sport = getParameter(parameters, "port");
- } catch (...) {
- // No port parameter, we are going to use the default port.
- sport = "";
- }
- if (sport.size() > 0) {
- // Port was given, so try to convert it to an integer.
- try {
- port = boost::lexical_cast<unsigned int>(sport);
- } catch (...) {
- // Port given but could not be converted to an unsigned int.
- // Just fall back to the default value.
- port = 0;
- }
- // The port is only valid when it is in the 0..65535 range.
- // Again fall back to the default when the given value is invalid.
- if (port > numeric_limits<uint16_t>::max()) {
- port = 0;
- }
- }
- const char* password = NULL;
- string spassword;
- try {
- spassword = getParameter(parameters, "password");
- password = spassword.c_str();
- } catch (...) {
- // No secret. Throw an exception
- isc_throw(NoPassword, "must specify a secret (password) for Radius connection");
- }
- char authserver[512];
- snprintf(authserver, sizeof(authserver), "%s:%u:%s", host, port, password);
- res = rc_add_config(rh, "authserver", authserver, NULL, 0);
- if (res != 0) {
- isc_throw(DbOpenError, "Failed to configure Radius authserver");
- }
- // Test and apply config (this also setups the necessary structures to
- // send requests to the radius server)
- res = rc_test_config(rh, "kea");
- if (res != 0) {
- isc_throw(DbOpenError, "Failed to apply radcli configuration");
- }
- // Load dictionary
- res = rc_read_dictionary(rh, rc_conf_str(rh, "dictionary"));
- if (res != 0) {
- isc_throw(DbOpenError, "Failed to read Radius dictionary");
- }
- }
- RadiusHostDataSource::~RadiusHostDataSource() {
- }
- void
- RadiusHostDataSource::add(const HostPtr& host) {
- // cannot add a host with radius
- isc_throw(NotImplemented, "RadiusHostDataSource::add not implemented");
- }
- bool
- RadiusHostDataSource::del(const SubnetID& subnet_id, const asiolink::IOAddress& addr) {
- // cannot delete hosts with radius
- isc_throw(NotImplemented, "RadiusHostDataSource::del not implemented");
- return false;
- }
- bool
- RadiusHostDataSource::del4(const SubnetID& subnet_id,
- const Host::IdentifierType& identifier_type,
- const uint8_t* identifier_begin, const size_t identifier_len) {
- // cannot delete hosts with radius
- isc_throw(NotImplemented, "RadiusHostDataSource::del4 not implemented");
- return false;
- }
- bool
- RadiusHostDataSource::del6(const SubnetID& subnet_id,
- const Host::IdentifierType& identifier_type,
- const uint8_t* identifier_begin, const size_t identifier_len) {
- // cannot delete hosts with radius
- isc_throw(NotImplemented, "RadiusHostDataSource::del6 not implemented");
- return false;
- }
- ConstHostCollection
- RadiusHostDataSource::getAll(const HWAddrPtr& hwaddr,
- const DuidPtr& duid) const {
- if (duid){
- return (getAll(Host::IDENT_DUID, &duid->getDuid()[0],
- duid->getDuid().size()));
- } else if (hwaddr) {
- return (getAll(Host::IDENT_HWADDR,
- &hwaddr->hwaddr_[0],
- hwaddr->hwaddr_.size()));
- }
- return (ConstHostCollection());
- }
- ConstHostCollection
- RadiusHostDataSource::getAll(const Host::IdentifierType& identifier_type,
- const uint8_t* identifier_begin,
- const size_t identifier_len) const {
- ConstHostCollection result;
- HostPtr host;
- int res;
- VALUE_PAIR *send = NULL, *received;
- // Convert binary identifier (DUID or MAC address) to an hexadecimal
- // string, with each byte separated by a colon.
- std::stringstream tmp;
- tmp << std::hex;
- bool delim = false;
- for (int i = 0; i < identifier_len; ++i) {
- if (delim) {
- tmp << ":";
- }
- tmp << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(identifier_begin[i]);
- delim = true;
- }
- // Add realm
- tmp << "@" << realm_;
- // Necessary because of variable lifetime, see https://stackoverflow.com/a/1374485/4113356
- const std::string tmp2 = tmp.str();
- const char* identifier_hex = tmp2.c_str();
- // Build radius request
- if (rc_avpair_add(rh, &send, PW_USER_NAME, identifier_hex, -1, 0) == NULL)
- isc_throw(DbOperationError, "Failed to set username");
- res = rc_auth(rh, 0, send, &received, NULL);
- if (res == OK_RC) {
- VALUE_PAIR *vp = received;
- char name[128];
- char value[128];
- HostPtr host(new Host(identifier_begin, identifier_len,
- identifier_type, SubnetID(),
- SubnetID(), asiolink::IOAddress::IPV4_ZERO_ADDRESS()));
- fprintf(stderr, "\"%s\" RADIUS Authentication OK\n", identifier_hex);
- /* parse the known attributes in the reply */
- while(vp != NULL) {
- if (rc_avpair_tostr(rh, vp, name, sizeof(name), value, sizeof(value)) == 0) {
- if (std::string(name) == "Framed-IP-Address") {
- host->setIPv4Reservation(asiolink::IOAddress(value));
- }
- if (std::string(name) == "Framed-IPv6-Address") {
- IPv6Resrv ipv6_addr(IPv6Resrv::TYPE_NA, asiolink::IOAddress(value), 128);
- host->addReservation(ipv6_addr);
- }
- if (std::string(name) == "Delegated-IPv6-Prefix") {
- /* Split "prefix/prefixlen" appropriately */
- std::string value_str(value);
- size_t pos = value_str.find('/');
- asiolink::IOAddress prefix(value_str.substr(0, pos));
- uint8_t prefixlen = std::stoi(value_str.substr(pos + 1));
- IPv6Resrv ipv6_prefix(IPv6Resrv::TYPE_PD, prefix, prefixlen);
- host->addReservation(ipv6_prefix);
- }
- }
- vp = vp->next;
- }
- result.push_back(host);
- } else {
- fprintf(stderr, "\"%s\" RADIUS Authentication failure (RC=%i)\n", identifier_hex, res);
- }
- return (result);
- }
- ConstHostCollection
- RadiusHostDataSource::getAll4(const asiolink::IOAddress& address) const {
- return (ConstHostCollection());
- }
- ConstHostPtr
- RadiusHostDataSource::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
- const DuidPtr& duid) const {
- if (hwaddr && duid) {
- isc_throw(BadValue, "Radius host data source get4() called with both"
- " hwaddr and duid, only one of them is allowed");
- }
- if (!hwaddr && !duid) {
- isc_throw(BadValue, "Radius host data source get4() called with "
- "neither hwaddr or duid specified, one of them is required");
- }
- // Choosing one of the identifiers
- if (hwaddr) {
- return (get4(subnet_id, Host::IDENT_HWADDR, &hwaddr->hwaddr_[0],
- hwaddr->hwaddr_.size()));
- } else if (duid) {
- return (get4(subnet_id, Host::IDENT_DUID, &duid->getDuid()[0],
- duid->getDuid().size()));
- }
- return (ConstHostPtr());
- }
- ConstHostPtr
- RadiusHostDataSource::get4(const SubnetID& subnet_id,
- const Host::IdentifierType& identifier_type,
- const uint8_t* identifier_begin,
- const size_t identifier_len) const {
- ConstHostCollection collection = getAll(identifier_type, identifier_begin, identifier_len);
- ConstHostPtr result;
- if (!collection.empty())
- result = *collection.begin();
- return (result);
- }
- ConstHostPtr
- RadiusHostDataSource::get4(const SubnetID& subnet_id,
- const asiolink::IOAddress& address) const {
- // We always assume that there is no conflict between reserved
- // addresses and dynamic addresses, so just return nothing here.
- return (ConstHostPtr());
- }
- ConstHostPtr
- RadiusHostDataSource::get6(const SubnetID& subnet_id, const DuidPtr& duid,
- const HWAddrPtr& hwaddr) const {
- if (hwaddr && duid) {
- isc_throw(BadValue, "Radius host data source get6() called with both"
- " hwaddr and duid, only one of them is allowed");
- }
- if (!hwaddr && !duid) {
- isc_throw(BadValue, "Radius host data source get6() called with "
- "neither hwaddr or duid specified, one of them is required");
- }
- // Choosing one of the identifiers
- if (hwaddr) {
- return (get6(subnet_id, Host::IDENT_HWADDR, &hwaddr->hwaddr_[0],
- hwaddr->hwaddr_.size()));
- } else if (duid) {
- return (get6(subnet_id, Host::IDENT_DUID, &duid->getDuid()[0],
- duid->getDuid().size()));
- }
- return (ConstHostPtr());
- }
- ConstHostPtr
- RadiusHostDataSource::get6(const SubnetID& subnet_id,
- const Host::IdentifierType& identifier_type,
- const uint8_t* identifier_begin,
- const size_t identifier_len) const {
- ConstHostCollection collection = getAll(identifier_type, identifier_begin, identifier_len);
- ConstHostPtr result;
- if (!collection.empty())
- result = *collection.begin();
- return (result);
- }
- ConstHostPtr
- RadiusHostDataSource::get6(const asiolink::IOAddress& prefix,
- const uint8_t prefix_len) const {
- // We always assume that there is no conflict between reserved
- // prefixes and dynamic prefixes, so just return nothing here.
- return (ConstHostPtr());
- }
- ConstHostPtr
- RadiusHostDataSource::get6(const SubnetID& subnet_id,
- const asiolink::IOAddress& address) const {
- // We always assume that there is no conflict between reserved
- // addresses and dynamic addresses, so just return nothing here.
- return (ConstHostPtr());
- }
- // Miscellaneous database methods.
- std::string RadiusHostDataSource::getName() const {
- std::string name = "";
- return (name);
- }
- std::string RadiusHostDataSource::getDescription() const {
- return (std::string("Host data source that retrieves host information"
- "in radius server"));
- }
- std::pair<uint32_t, uint32_t> RadiusHostDataSource::getVersion() const {
- // TODO: Not relevant for libradcli ?
- return std::make_pair(0,0);
- }
- void
- RadiusHostDataSource::commit() {
- // Not relevant for radius.
- }
- void
- RadiusHostDataSource::rollback() {
- // Not relevant for radius.
- }
- }; // end of isc::dhcp namespace
- }; // end of isc namespace
|