Resolve and connect to RADIUS servers not before required

This commit is contained in:
Martin Willi
2011-04-21 14:01:25 +02:00
parent 5b0bcfb1fc
commit f9a552f011
7 changed files with 94 additions and 56 deletions
@@ -82,7 +82,7 @@ static void load_servers(private_eap_radius_plugin_t *this)
"charon.plugins.eap-radius.port", RADIUS_PORT); "charon.plugins.eap-radius.port", RADIUS_PORT);
sockets = lib->settings->get_int(lib->settings, sockets = lib->settings->get_int(lib->settings,
"charon.plugins.eap-radius.sockets", 1); "charon.plugins.eap-radius.sockets", 1);
server = radius_server_create(address, port, nas_identifier, server = radius_server_create(address, address, port, nas_identifier,
secret, sockets, 0); secret, sockets, 0);
if (!server) if (!server)
{ {
@@ -120,7 +120,7 @@ static void load_servers(private_eap_radius_plugin_t *this)
"charon.plugins.eap-radius.servers.%s.sockets", 1, section); "charon.plugins.eap-radius.servers.%s.sockets", 1, section);
preference = lib->settings->get_int(lib->settings, preference = lib->settings->get_int(lib->settings,
"charon.plugins.eap-radius.servers.%s.preference", 0, section); "charon.plugins.eap-radius.servers.%s.preference", 0, section);
server = radius_server_create(address, port, nas_identifier, server = radius_server_create(section, address, port, nas_identifier,
secret, sockets, preference); secret, sockets, preference);
if (!server) if (!server)
{ {
@@ -98,13 +98,14 @@ METHOD(radius_client_t, request, radius_message_t*,
req->add(req, RAT_STATE, this->state); req->add(req, RAT_STATE, this->state);
} }
socket = this->server->get_socket(this->server); socket = this->server->get_socket(this->server);
DBG1(DBG_CFG, "sending RADIUS %N to %#H", radius_message_code_names, DBG1(DBG_CFG, "sending RADIUS %N to server '%s'", radius_message_code_names,
req->get_code(req), this->server->get_address(this->server)); req->get_code(req), this->server->get_name(this->server));
res = socket->request(socket, req); res = socket->request(socket, req);
if (res) if (res)
{ {
DBG1(DBG_CFG, "received RADIUS %N from %#H", radius_message_code_names, DBG1(DBG_CFG, "received RADIUS %N from server '%s'",
res->get_code(res), this->server->get_address(this->server)); radius_message_code_names, res->get_code(res),
this->server->get_name(this->server));
save_state(this, res); save_state(this, res);
if (res->get_code(res) == RMC_ACCESS_ACCEPT) if (res->get_code(res) == RMC_ACCESS_ACCEPT)
{ {
@@ -160,16 +161,16 @@ radius_client_t *radius_client_create()
/* for two with equal preference, 50-50 chance */ /* for two with equal preference, 50-50 chance */
(current == best && random() % 2 == 0)) (current == best && random() % 2 == 0))
{ {
DBG2(DBG_CFG, "RADIUS server %H is candidate: %d", DBG2(DBG_CFG, "RADIUS server '%s' is candidate: %d",
server->get_address(server), current); server->get_name(server), current);
best = current; best = current;
DESTROY_IF(this->server); DESTROY_IF(this->server);
this->server = server->get_ref(server); this->server = server->get_ref(server);
} }
else else
{ {
DBG2(DBG_CFG, "RADIUS server %H skipped: %d", DBG2(DBG_CFG, "RADIUS server '%s' skipped: %d",
server->get_address(server), current); server->get_name(server), current);
} }
} }
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
@@ -31,11 +31,6 @@ struct private_radius_server_t {
*/ */
radius_server_t public; radius_server_t public;
/**
* RADIUS server address
*/
host_t *host;
/** /**
* list of radius sockets, as radius_socket_t * list of radius sockets, as radius_socket_t
*/ */
@@ -57,9 +52,9 @@ struct private_radius_server_t {
condvar_t *condvar; condvar_t *condvar;
/** /**
* RADIUS secret * Server name
*/ */
chunk_t secret; char *name;
/** /**
* NAS-Identifier * NAS-Identifier
@@ -152,10 +147,10 @@ METHOD(radius_server_t, get_preference, int,
return pref; return pref;
} }
METHOD(radius_server_t, get_address, host_t*, METHOD(radius_server_t, get_name, char*,
private_radius_server_t *this) private_radius_server_t *this)
{ {
return this->host; return this->name;
} }
METHOD(radius_server_t, get_ref, radius_server_t*, METHOD(radius_server_t, get_ref, radius_server_t*,
@@ -171,12 +166,10 @@ METHOD(radius_server_t, destroy, void,
{ {
if (ref_put(&this->ref)) if (ref_put(&this->ref))
{ {
DESTROY_IF(this->host);
this->mutex->destroy(this->mutex); this->mutex->destroy(this->mutex);
this->condvar->destroy(this->condvar); this->condvar->destroy(this->condvar);
this->sockets->destroy_offset(this->sockets, this->sockets->destroy_offset(this->sockets,
offsetof(radius_socket_t, destroy)); offsetof(radius_socket_t, destroy));
free(this->nas_identifier.ptr);
free(this); free(this);
} }
} }
@@ -184,7 +177,7 @@ METHOD(radius_server_t, destroy, void,
/** /**
* See header * See header
*/ */
radius_server_t *radius_server_create(char *server, u_int16_t port, radius_server_t *radius_server_create(char *name, char *address, u_int16_t port,
char *nas_identifier, char *secret, int sockets, int preference) char *nas_identifier, char *secret, int sockets, int preference)
{ {
private_radius_server_t *this; private_radius_server_t *this;
@@ -196,7 +189,7 @@ radius_server_t *radius_server_create(char *server, u_int16_t port,
.put_socket = _put_socket, .put_socket = _put_socket,
.get_nas_identifier = _get_nas_identifier, .get_nas_identifier = _get_nas_identifier,
.get_preference = _get_preference, .get_preference = _get_preference,
.get_address = _get_address, .get_name = _get_name,
.get_ref = _get_ref, .get_ref = _get_ref,
.destroy = _destroy, .destroy = _destroy,
}, },
@@ -206,19 +199,14 @@ radius_server_t *radius_server_create(char *server, u_int16_t port,
.sockets = linked_list_create(), .sockets = linked_list_create(),
.mutex = mutex_create(MUTEX_TYPE_DEFAULT), .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
.condvar = condvar_create(CONDVAR_TYPE_DEFAULT), .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
.host = host_create_from_dns(server, 0, port), .name = name,
.preference = preference, .preference = preference,
.ref = 1, .ref = 1,
); );
if (!this->host)
{
destroy(this);
return NULL;
}
while (sockets--) while (sockets--)
{ {
socket = radius_socket_create(this->host, socket = radius_socket_create(address, port,
chunk_create(secret, strlen(secret))); chunk_create(secret, strlen(secret)));
if (!socket) if (!socket)
{ {
@@ -61,11 +61,11 @@ struct radius_server_t {
int (*get_preference)(radius_server_t *this); int (*get_preference)(radius_server_t *this);
/** /**
* Get the address of the RADIUS server. * Get the name of the RADIUS server.
* *
* @return address, internal data * @return server name
*/ */
host_t* (*get_address)(radius_server_t *this); char* (*get_name)(radius_server_t *this);
/** /**
* Increase reference count of this server. * Increase reference count of this server.
@@ -83,14 +83,15 @@ struct radius_server_t {
/** /**
* Create a radius_server instance. * Create a radius_server instance.
* *
* @param server server address * @param name server name
* @param address server address
* @param port server port * @param port server port
* @param nas_identifier NAS-Identifier to use with this server * @param nas_identifier NAS-Identifier to use with this server
* @param secret secret to use with this server * @param secret secret to use with this server
* @param sockets number of sockets to create in pool * @param sockets number of sockets to create in pool
* @param preference preference boost for this server * @param preference preference boost for this server
*/ */
radius_server_t *radius_server_create(char *server, u_int16_t port, radius_server_t *radius_server_create(char *name, char *address, u_int16_t port,
char *nas_identifier, char *secret, int sockets, int preference); char *nas_identifier, char *secret, int sockets, int preference);
#endif /** RADIUS_SERVER_H_ @}*/ #endif /** RADIUS_SERVER_H_ @}*/
@@ -48,6 +48,16 @@ struct private_radius_socket_t {
*/ */
int fd; int fd;
/**
* Server address
*/
char *address;
/**
* Server port
*/
u_int16_t port;
/** /**
* current RADIUS identifier * current RADIUS identifier
*/ */
@@ -74,6 +84,45 @@ struct private_radius_socket_t {
chunk_t secret; chunk_t secret;
}; };
/**
* Check or establish RADIUS connection
*/
static bool check_connection(private_radius_socket_t *this)
{
if (this->fd == -1)
{
host_t *server;
server = host_create_from_dns(this->address, AF_UNSPEC, this->port);
if (!server)
{
DBG1(DBG_CFG, "resolving RADIUS server address '%s' failed",
this->address);
return FALSE;
}
this->fd = socket(server->get_family(server), SOCK_DGRAM, IPPROTO_UDP);
if (this->fd == -1)
{
DBG1(DBG_CFG, "opening RADIUS socket for %#H failed: %s",
server, strerror(errno));
server->destroy(server);
return FALSE;
}
if (connect(this->fd, server->get_sockaddr(server),
*server->get_sockaddr_len(server)) < 0)
{
DBG1(DBG_CFG, "connecting RADIUS socket to %#H failed: %s",
server, strerror(errno));
server->destroy(server);
close(this->fd);
this->fd = -1;
return FALSE;
}
server->destroy(server);
}
return TRUE;
}
METHOD(radius_socket_t, request, radius_message_t*, METHOD(radius_socket_t, request, radius_message_t*,
private_radius_socket_t *this, radius_message_t *request) private_radius_socket_t *this, radius_message_t *request)
{ {
@@ -85,6 +134,11 @@ METHOD(radius_socket_t, request, radius_message_t*,
/* sign the request */ /* sign the request */
request->sign(request, this->rng, this->signer); request->sign(request, this->rng, this->signer);
if (!check_connection(this))
{
return NULL;
}
data = request->get_encoding(request); data = request->get_encoding(request);
/* timeout after 2, 3, 4, 5 seconds */ /* timeout after 2, 3, 4, 5 seconds */
for (i = 2; i <= 5; i++) for (i = 2; i <= 5; i++)
@@ -257,15 +311,18 @@ METHOD(radius_socket_t, destroy, void,
DESTROY_IF(this->hasher); DESTROY_IF(this->hasher);
DESTROY_IF(this->signer); DESTROY_IF(this->signer);
DESTROY_IF(this->rng); DESTROY_IF(this->rng);
chunk_clear(&this->secret); if (this->fd != -1)
close(this->fd); {
close(this->fd);
}
free(this); free(this);
} }
/** /**
* See header * See header
*/ */
radius_socket_t *radius_socket_create(host_t *host, chunk_t secret) radius_socket_t *radius_socket_create(char *address, u_int16_t port,
chunk_t secret)
{ {
private_radius_socket_t *this; private_radius_socket_t *this;
@@ -275,23 +332,11 @@ radius_socket_t *radius_socket_create(host_t *host, chunk_t secret)
.decrypt_msk = _decrypt_msk, .decrypt_msk = _decrypt_msk,
.destroy = _destroy, .destroy = _destroy,
}, },
.address = address,
.port = port,
.fd = -1,
); );
this->fd = socket(host->get_family(host), SOCK_DGRAM, IPPROTO_UDP);
if (this->fd < 0)
{
DBG1(DBG_CFG, "opening RADIUS socket failed: %s", strerror(errno));
free(this);
return NULL;
}
if (connect(this->fd, host->get_sockaddr(host),
*host->get_sockaddr_len(host)) < 0)
{
DBG1(DBG_CFG, "connecting RADIUS socket failed");
close(this->fd);
free(this);
return NULL;
}
this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5); this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128); this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128);
this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
@@ -34,7 +34,7 @@ struct radius_socket_t {
/** /**
* Send a RADIUS request, wait for response. * Send a RADIUS request, wait for response.
*
* The socket fills in RADIUS Message identifier, builds a * The socket fills in RADIUS Message identifier, builds a
* Request-Authenticator and calculates the Message-Authenticator * Request-Authenticator and calculates the Message-Authenticator
* attribute. * attribute.
@@ -66,9 +66,11 @@ struct radius_socket_t {
/** /**
* Create a radius_socket instance. * Create a radius_socket instance.
* *
* @param host RADIUS server address to connect to * @param address server name
* @param port server port
* @param secret RADIUS secret * @param secret RADIUS secret
*/ */
radius_socket_t *radius_socket_create(host_t *host, chunk_t secret); radius_socket_t *radius_socket_create(char *address, u_int16_t port,
chunk_t secret);
#endif /** RADIUS_SOCKET_H_ @}*/ #endif /** RADIUS_SOCKET_H_ @}*/
@@ -183,6 +183,7 @@ static eap_payload_t* server_initiate_eap(private_eap_authenticator_t *this,
if (this->method) if (this->method)
{ {
action = "initiating"; action = "initiating";
type = this->method->get_type(this->method, &vendor);
if (this->method->initiate(this->method, &out) == NEED_MORE) if (this->method->initiate(this->method, &out) == NEED_MORE)
{ {
if (vendor) if (vendor)