|
@@ -75,6 +75,14 @@ const uint8_t MAX_IDENTIFIER_TYPE = static_cast<uint8_t>(Host::LAST_IDENTIFIER_T
|
|
namespace isc {
|
|
namespace isc {
|
|
namespace dhcp {
|
|
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::
|
|
RadiusHostDataSource(const DatabaseConnection::ParameterMap& parameters) {
|
|
RadiusHostDataSource(const DatabaseConnection::ParameterMap& parameters) {
|
|
int res;
|
|
int res;
|
|
@@ -82,13 +90,91 @@ RadiusHostDataSource(const DatabaseConnection::ParameterMap& parameters) {
|
|
if (rh == NULL) {
|
|
if (rh == NULL) {
|
|
isc_throw(isc::Exception, "Failed to initialize Radius client");
|
|
isc_throw(isc::Exception, "Failed to initialize Radius client");
|
|
}
|
|
}
|
|
- res = rc_add_config(rh, "authserver", "127.0.0.1", NULL, 0);
|
|
|
|
- if (res != 0) {
|
|
|
|
|
|
+ rh = rc_config_init(rh);
|
|
|
|
+ if (rh == NULL) {
|
|
isc_throw(isc::Exception, "Failed to initialize Radius client");
|
|
isc_throw(isc::Exception, "Failed to initialize Radius client");
|
|
}
|
|
}
|
|
|
|
+ res = rc_add_config(rh, "auth_order", "radius", NULL, 0);
|
|
|
|
+ if (res != 0) {
|
|
|
|
+ isc_throw(isc::Exception, "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(isc::Exception, "Failed to configure Radius dictionary");
|
|
|
|
+ }
|
|
res = rc_add_config(rh, "radius_timeout", "1", NULL, 0);
|
|
res = rc_add_config(rh, "radius_timeout", "1", NULL, 0);
|
|
- res = rc_add_config(rh, "radius_retries", "2", NULL, 0);
|
|
|
|
- res = rc_add_config(rh, "serv-type", "tcp", NULL, 0);
|
|
|
|
|
|
+ if (res != 0) {
|
|
|
|
+ isc_throw(isc::Exception, "Failed to configure Radius timeout");
|
|
|
|
+ }
|
|
|
|
+ res = rc_add_config(rh, "radius_retries", "1", NULL, 0);
|
|
|
|
+ if (res != 0) {
|
|
|
|
+ isc_throw(isc::Exception, "Failed to configure Radius retries");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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(isc::Exception, "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(isc::Exception, "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(isc::Exception, "Failed to apply radcli configuration");
|
|
|
|
+ }
|
|
|
|
+ // Load dictionary
|
|
|
|
+ res = rc_read_dictionary(rh, rc_conf_str(rh, "dictionary"));
|
|
|
|
+ if (res != 0) {
|
|
|
|
+ isc_throw(isc::Exception, "Failed to read Radius dictionary");
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
RadiusHostDataSource::~RadiusHostDataSource() {
|
|
RadiusHostDataSource::~RadiusHostDataSource() {
|
|
@@ -128,7 +214,16 @@ RadiusHostDataSource::del6(const SubnetID& subnet_id,
|
|
ConstHostCollection
|
|
ConstHostCollection
|
|
RadiusHostDataSource::getAll(const HWAddrPtr& hwaddr,
|
|
RadiusHostDataSource::getAll(const HWAddrPtr& hwaddr,
|
|
const DuidPtr& duid) const {
|
|
const DuidPtr& duid) const {
|
|
- // TODO: libradcli call
|
|
|
|
|
|
+ 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());
|
|
return (ConstHostCollection());
|
|
}
|
|
}
|
|
|
|
|
|
@@ -136,8 +231,56 @@ ConstHostCollection
|
|
RadiusHostDataSource::getAll(const Host::IdentifierType& identifier_type,
|
|
RadiusHostDataSource::getAll(const Host::IdentifierType& identifier_type,
|
|
const uint8_t* identifier_begin,
|
|
const uint8_t* identifier_begin,
|
|
const size_t identifier_len) const {
|
|
const size_t identifier_len) const {
|
|
- // TODO: libradcli call
|
|
|
|
- return (ConstHostCollection());
|
|
|
|
|
|
+ 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 << "@radio.rezine.org";
|
|
|
|
+ // 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(isc::Exception, "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];
|
|
|
|
+
|
|
|
|
+ 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.reset(new Host(identifier_begin, identifier_len,
|
|
|
|
+ identifier_type, SubnetID(),
|
|
|
|
+ SubnetID(), asiolink::IOAddress(value)));
|
|
|
|
+ result.push_back(host);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ vp = vp->next;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ fprintf(stderr, "\"%s\" RADIUS Authentication failure (RC=%i)\n", identifier_hex, res);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return (result);
|
|
}
|
|
}
|
|
|
|
|
|
ConstHostCollection
|
|
ConstHostCollection
|
|
@@ -147,10 +290,27 @@ RadiusHostDataSource::getAll4(const asiolink::IOAddress& address) const {
|
|
|
|
|
|
ConstHostPtr
|
|
ConstHostPtr
|
|
RadiusHostDataSource::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
|
|
RadiusHostDataSource::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
|
|
- const DuidPtr& duid) const {
|
|
|
|
- // TODO: libradcli call
|
|
|
|
- ConstHostPtr result = NULL;
|
|
|
|
- return (result);
|
|
|
|
|
|
+ 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
|
|
ConstHostPtr
|
|
@@ -158,17 +318,19 @@ RadiusHostDataSource::get4(const SubnetID& subnet_id,
|
|
const Host::IdentifierType& identifier_type,
|
|
const Host::IdentifierType& identifier_type,
|
|
const uint8_t* identifier_begin,
|
|
const uint8_t* identifier_begin,
|
|
const size_t identifier_len) const {
|
|
const size_t identifier_len) const {
|
|
- // TODO: libradcli call
|
|
|
|
- ConstHostPtr result = NULL;
|
|
|
|
|
|
+ ConstHostCollection collection = getAll(identifier_type, identifier_begin, identifier_len);
|
|
|
|
+ ConstHostPtr result;
|
|
|
|
+ if (!collection.empty())
|
|
|
|
+ result = *collection.begin();
|
|
return (result);
|
|
return (result);
|
|
}
|
|
}
|
|
|
|
|
|
ConstHostPtr
|
|
ConstHostPtr
|
|
RadiusHostDataSource::get4(const SubnetID& subnet_id,
|
|
RadiusHostDataSource::get4(const SubnetID& subnet_id,
|
|
const asiolink::IOAddress& address) const {
|
|
const asiolink::IOAddress& address) const {
|
|
- // TODO: libradcli call
|
|
|
|
- ConstHostPtr result = NULL;
|
|
|
|
- return (result);
|
|
|
|
|
|
+ // We always assume that there is no conflict between reserved
|
|
|
|
+ // addresses and dynamic addresses, so just return nothing here.
|
|
|
|
+ return (ConstHostPtr());
|
|
}
|
|
}
|
|
|
|
|
|
ConstHostPtr
|
|
ConstHostPtr
|