Moved generic RADIUS protocol support to a dedicated libradius

This commit is contained in:
Martin Willi
2012-03-05 18:08:04 +01:00
parent 990fda9d88
commit f0f94e2ce6
21 changed files with 101 additions and 67 deletions
+3 -6
View File
@@ -1,12 +1,13 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra \
-I$(top_srcdir)/src/libcharon
-I$(top_srcdir)/src/libcharon -I$(top_srcdir)/src/libradius
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-radius.la
else
libstrongswan_eap_radius_la_LIBADD = $(top_builddir)/src/libradius/libradius.la
plugin_LTLIBRARIES = libstrongswan-eap-radius.la
endif
@@ -15,10 +16,6 @@ libstrongswan_eap_radius_la_SOURCES = \
eap_radius.h eap_radius.c \
eap_radius_accounting.h eap_radius_accounting.c \
eap_radius_dae.h eap_radius_dae.c \
eap_radius_forward.h eap_radius_forward.c \
radius_server.h radius_server.c \
radius_socket.h radius_socket.c \
radius_client.h radius_client.c \
radius_message.h radius_message.c
eap_radius_forward.h eap_radius_forward.c
libstrongswan_eap_radius_la_LDFLAGS = -module -avoid-version
@@ -14,10 +14,11 @@
*/
#include "eap_radius.h"
#include "eap_radius_plugin.h"
#include "eap_radius_forward.h"
#include "radius_message.h"
#include "radius_client.h"
#include <radius_message.h>
#include <radius_client.h>
#include <daemon.h>
@@ -462,7 +463,7 @@ eap_radius_t *eap_radius_create(identification_t *server, identification_t *peer
"charon.plugins.eap-radius.filter_id", FALSE),
);
this->client = radius_client_create();
this->client = eap_radius_create_client();
if (!this->client)
{
free(this);
@@ -14,11 +14,12 @@
*/
#include "eap_radius_accounting.h"
#include "eap_radius_plugin.h"
#include <time.h>
#include "radius_message.h"
#include "radius_client.h"
#include <radius_message.h>
#include <radius_client.h>
#include <daemon.h>
#include <utils/hashtable.h>
#include <threading/mutex.h>
@@ -125,7 +126,7 @@ static bool send_message(private_eap_radius_accounting_t *this,
radius_client_t *client;
bool ack = FALSE;
client = radius_client_create();
client = eap_radius_create_client();
if (client)
{
response = client->request(client, request);
@@ -15,7 +15,7 @@
#include "eap_radius_dae.h"
#include "radius_message.h"
#include <radius_message.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -15,13 +15,13 @@
/**
* @defgroup eap_radius_forward eap_radius_forward
* @{ @ingroup
* @{ @ingroup eap_radius
*/
#ifndef EAP_RADIUS_FORWARD_H_
#define EAP_RADIUS_FORWARD_H_
#include "radius_message.h"
#include <radius_message.h>
#include <bus/listeners/listener.h>
@@ -19,8 +19,9 @@
#include "eap_radius_accounting.h"
#include "eap_radius_dae.h"
#include "eap_radius_forward.h"
#include "radius_client.h"
#include "radius_server.h"
#include <radius_client.h>
#include <radius_server.h>
#include <daemon.h>
#include <threading/rwlock.h>
@@ -261,15 +262,43 @@ plugin_t *eap_radius_plugin_create()
/**
* See header
*/
enumerator_t *eap_radius_create_server_enumerator()
radius_client_t *eap_radius_create_client()
{
if (instance)
{
enumerator_t *enumerator;
radius_server_t *server, *selected = NULL;
int current, best = -1;
instance->lock->read_lock(instance->lock);
return enumerator_create_cleaner(
instance->servers->create_enumerator(instance->servers),
(void*)instance->lock->unlock, instance->lock);
enumerator = instance->servers->create_enumerator(instance->servers);
while (enumerator->enumerate(enumerator, &server))
{
current = server->get_preference(server);
if (current > best ||
/* for two with equal preference, 50-50 chance */
(current == best && random() % 2 == 0))
{
DBG2(DBG_CFG, "RADIUS server '%s' is candidate: %d",
server->get_name(server), current);
best = current;
DESTROY_IF(selected);
selected = server->get_ref(server);
}
else
{
DBG2(DBG_CFG, "RADIUS server '%s' skipped: %d",
server->get_name(server), current);
}
}
enumerator->destroy(enumerator);
instance->lock->unlock(instance->lock);
if (selected)
{
return radius_client_create(selected);
}
}
return enumerator_create_empty();
return NULL;
}
@@ -25,7 +25,8 @@
#define EAP_RADIUS_PLUGIN_H_
#include <plugins/plugin.h>
#include <utils/enumerator.h>
#include <radius_client.h>
typedef struct eap_radius_plugin_t eap_radius_plugin_t;
@@ -44,10 +45,10 @@ struct eap_radius_plugin_t {
};
/**
* Create an enumerator over all loaded RADIUS servers.
* Get a RADIUS client instance to connect to servers.
*
* @return enumerator over radius_server_t
* @return RADIUS client
*/
enumerator_t *eap_radius_create_server_enumerator();
radius_client_t *eap_radius_create_client();
#endif /** EAP_RADIUS_PLUGIN_H_ @}*/
@@ -1,185 +0,0 @@
/*
* Copyright (C) 2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "radius_client.h"
#include "eap_radius_plugin.h"
#include "radius_server.h"
#include <unistd.h>
#include <errno.h>
#include <debug.h>
#include <utils/host.h>
#include <utils/linked_list.h>
#include <threading/condvar.h>
#include <threading/mutex.h>
typedef struct private_radius_client_t private_radius_client_t;
/**
* Private data of an radius_client_t object.
*/
struct private_radius_client_t {
/**
* Public radius_client_t interface.
*/
radius_client_t public;
/**
* Selected RADIUS server
*/
radius_server_t *server;
/**
* RADIUS servers State attribute
*/
chunk_t state;
/**
* EAP MSK, from MPPE keys
*/
chunk_t msk;
};
/**
* Save the state attribute to include in further request
*/
static void save_state(private_radius_client_t *this, radius_message_t *msg)
{
enumerator_t *enumerator;
int type;
chunk_t data;
enumerator = msg->create_enumerator(msg);
while (enumerator->enumerate(enumerator, &type, &data))
{
if (type == RAT_STATE)
{
free(this->state.ptr);
this->state = chunk_clone(data);
enumerator->destroy(enumerator);
return;
}
}
enumerator->destroy(enumerator);
/* no state attribute found, remove state */
chunk_free(&this->state);
}
METHOD(radius_client_t, request, radius_message_t*,
private_radius_client_t *this, radius_message_t *req)
{
char virtual[] = {0x00,0x00,0x00,0x05};
radius_socket_t *socket;
radius_message_t *res;
/* we add the "Virtual" NAS-Port-Type, as we SHOULD include one */
req->add(req, RAT_NAS_PORT_TYPE, chunk_create(virtual, sizeof(virtual)));
/* add our NAS-Identifier */
req->add(req, RAT_NAS_IDENTIFIER,
this->server->get_nas_identifier(this->server));
/* add State attribute, if server sent one */
if (this->state.ptr)
{
req->add(req, RAT_STATE, this->state);
}
socket = this->server->get_socket(this->server);
DBG1(DBG_CFG, "sending RADIUS %N to server '%s'", radius_message_code_names,
req->get_code(req), this->server->get_name(this->server));
res = socket->request(socket, req);
if (res)
{
DBG1(DBG_CFG, "received RADIUS %N from server '%s'",
radius_message_code_names, res->get_code(res),
this->server->get_name(this->server));
save_state(this, res);
if (res->get_code(res) == RMC_ACCESS_ACCEPT)
{
chunk_clear(&this->msk);
this->msk = socket->decrypt_msk(socket, req, res);
}
this->server->put_socket(this->server, socket, TRUE);
return res;
}
this->server->put_socket(this->server, socket, FALSE);
return NULL;
}
METHOD(radius_client_t, get_msk, chunk_t,
private_radius_client_t *this)
{
return this->msk;
}
METHOD(radius_client_t, destroy, void,
private_radius_client_t *this)
{
this->server->destroy(this->server);
chunk_clear(&this->msk);
free(this->state.ptr);
free(this);
}
/**
* See header
*/
radius_client_t *radius_client_create()
{
private_radius_client_t *this;
enumerator_t *enumerator;
radius_server_t *server;
int current, best = -1;
INIT(this,
.public = {
.request = _request,
.get_msk = _get_msk,
.destroy = _destroy,
},
);
enumerator = eap_radius_create_server_enumerator();
while (enumerator->enumerate(enumerator, &server))
{
current = server->get_preference(server);
if (current > best ||
/* for two with equal preference, 50-50 chance */
(current == best && random() % 2 == 0))
{
DBG2(DBG_CFG, "RADIUS server '%s' is candidate: %d",
server->get_name(server), current);
best = current;
DESTROY_IF(this->server);
this->server = server->get_ref(server);
}
else
{
DBG2(DBG_CFG, "RADIUS server '%s' skipped: %d",
server->get_name(server), current);
}
}
enumerator->destroy(enumerator);
if (!this->server)
{
free(this);
return NULL;
}
return &this->public;
}
@@ -1,66 +0,0 @@
/*
* Copyright (C) 2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup radius_client radius_client
* @{ @ingroup eap_radius
*/
#ifndef RADIUS_CLIENT_H_
#define RADIUS_CLIENT_H_
#include "radius_message.h"
typedef struct radius_client_t radius_client_t;
/**
* RADIUS client functionality.
*
* To communicate with a RADIUS server, create a client and send messages over
* it. The client allocates a socket from the best RADIUS server abailable.
*/
struct radius_client_t {
/**
* Send a RADIUS request and wait for the response.
*
* The client fills in NAS-Identifier nad NAS-Port-Type
*
* @param msg RADIUS request message to send
* @return response, NULL if timed out/verification failed
*/
radius_message_t* (*request)(radius_client_t *this, radius_message_t *msg);
/**
* Get the EAP MSK after successful RADIUS authentication.
*
* @return MSK, allocated
*/
chunk_t (*get_msk)(radius_client_t *this);
/**
* Destroy the client, release the socket.
*/
void (*destroy)(radius_client_t *this);
};
/**
* Create a RADIUS client.
*
* @return radius_client_t object
*/
radius_client_t *radius_client_create();
#endif /** RADIUS_CLIENT_H_ @}*/
@@ -1,491 +0,0 @@
/*
* Copyright (C) 2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "radius_message.h"
#include <debug.h>
#include <crypto/hashers/hasher.h>
typedef struct private_radius_message_t private_radius_message_t;
typedef struct rmsg_t rmsg_t;
typedef struct rattr_t rattr_t;
/**
* RADIUS message header
*/
struct rmsg_t {
/** message code, radius_message_code_t */
u_int8_t code;
/** message identifier */
u_int8_t identifier;
/** length of Code, Identifier, Length, Authenticator and Attributes */
u_int16_t length;
/** message authenticator, MD5 hash */
u_int8_t authenticator[HASH_SIZE_MD5];
/** variable list of packed attributes */
u_int8_t attributes[];
} __attribute__((packed));
/**
* RADIUS message attribute.
*/
struct rattr_t {
/** attribute type, radius_attribute_type_t */
u_int8_t type;
/** length of the attriubte, including the Type, Length and Value fields */
u_int8_t length;
/** variable length attribute value */
u_int8_t value[];
} __attribute__((packed));
/**
* Private data of an radius_message_t object.
*/
struct private_radius_message_t {
/**
* Public radius_message_t interface.
*/
radius_message_t public;
/**
* message data, allocated
*/
rmsg_t *msg;
};
ENUM_BEGIN(radius_message_code_names, RMC_ACCESS_REQUEST, RMC_ACCOUNTING_RESPONSE,
"Access-Request",
"Access-Accept",
"Access-Reject",
"Accounting-Request",
"Accounting-Response");
ENUM_NEXT(radius_message_code_names, RMC_ACCESS_CHALLENGE, RMC_ACCESS_CHALLENGE, RMC_ACCOUNTING_RESPONSE,
"Access-Challenge");
ENUM_NEXT(radius_message_code_names, RMC_DISCONNECT_REQUEST, RMC_COA_NAK, RMC_ACCESS_CHALLENGE,
"Disconnect-Request",
"Disconnect-ACK",
"Disconnect-NAK",
"CoA-Request",
"CoA-ACK",
"CoA-NAK");
ENUM_END(radius_message_code_names, RMC_COA_NAK);
ENUM(radius_attribute_type_names, RAT_USER_NAME, RAT_MIP6_HOME_LINK_PREFIX,
"User-Name",
"User-Password",
"CHAP-Password",
"NAS-IP-Address",
"NAS-Port",
"Service-Type",
"Framed-Protocol",
"Framed-IP-Address",
"Framed-IP-Netmask",
"Framed-Routing",
"Filter-Id",
"Framed-MTU",
"Framed-Compression",
"Login-IP-Host",
"Login-Service",
"Login-TCP-Port",
"Unassigned",
"Reply-Message",
"Callback-Number",
"Callback-Id",
"Unassigned",
"Framed-Route",
"Framed-IPX-Network",
"State",
"Class",
"Vendor-Specific",
"Session-Timeout",
"Idle-Timeout",
"Termination-Action",
"Called-Station-Id",
"Calling-Station-Id",
"NAS-Identifier",
"Proxy-State",
"Login-LAT-Service",
"Login-LAT-Node",
"Login-LAT-Group",
"Framed-AppleTalk-Link",
"Framed-AppleTalk-Network",
"Framed-AppleTalk-Zone",
"Acct-Status-Type",
"Acct-Delay-Time",
"Acct-Input-Octets",
"Acct-Output-Octets",
"Acct-Session-Id",
"Acct-Authentic",
"Acct-Session-Time",
"Acct-Input-Packets",
"Acct-Output-Packets",
"Acct-Terminate-Cause",
"Acct-Multi-Session-Id",
"Acct-Link-Count",
"Acct-Input-Gigawords",
"Acct-Output-Gigawords",
"Unassigned",
"Event-Timestamp",
"Egress-VLANID",
"Ingress-Filters",
"Egress-VLAN-Name",
"User-Priority-Table",
"CHAP-Challenge",
"NAS-Port-Type",
"Port-Limit",
"Login-LAT-Port",
"Tunnel-Type",
"Tunnel-Medium-Type",
"Tunnel-Client-Endpoint",
"Tunnel-Server-Endpoint",
"Acct-Tunnel-Connection",
"Tunnel-Password",
"ARAP-Password",
"ARAP-Features",
"ARAP-Zone-Access",
"ARAP-Security",
"ARAP-Security-Data",
"Password-Retry",
"Prompt",
"Connect-Info",
"Configuration-Token",
"EAP-Message",
"Message-Authenticator",
"Tunnel-Private-Group-ID",
"Tunnel-Assignment-ID",
"Tunnel-Preference",
"ARAP-Challenge-Response",
"Acct-Interim-Interval",
"Acct-Tunnel-Packets-Lost",
"NAS-Port-Id",
"Framed-Pool",
"CUI",
"Tunnel-Client-Auth-ID",
"Tunnel-Server-Auth-ID",
"NAS-Filter-Rule",
"Unassigned",
"Originating-Line-Info",
"NAS-IPv6-Address",
"Framed-Interface-Id",
"Framed-IPv6-Prefix",
"Login-IPv6-Host",
"Framed-IPv6-Route",
"Framed-IPv6-Pool",
"Error-Cause",
"EAP-Key-Name",
"Digest-Response",
"Digest-Realm",
"Digest-Nonce",
"Digest-Response-Auth",
"Digest-Nextnonce",
"Digest-Method",
"Digest-URI",
"Digest-Qop",
"Digest-Algorithm",
"Digest-Entity-Body-Hash",
"Digest-CNonce",
"Digest-Nonce-Count",
"Digest-Username",
"Digest-Opaque",
"Digest-Auth-Param",
"Digest-AKA-Auts",
"Digest-Domain",
"Digest-Stale",
"Digest-HA1",
"SIP-AOR",
"Delegated-IPv6-Prefix",
"MIP6-Feature-Vector",
"MIP6-Home-Link-Prefix");
/**
* Attribute enumerator implementation
*/
typedef struct {
/** implements enumerator interface */
enumerator_t public;
/** currently pointing attribute */
rattr_t *next;
/** bytes left */
int left;
} attribute_enumerator_t;
METHOD(enumerator_t, attribute_enumerate, bool,
attribute_enumerator_t *this, int *type, chunk_t *data)
{
if (this->left == 0)
{
return FALSE;
}
if (this->left < sizeof(rattr_t) ||
this->left < this->next->length)
{
DBG1(DBG_IKE, "RADIUS message truncated");
return FALSE;
}
*type = this->next->type;
data->ptr = this->next->value;
data->len = this->next->length - sizeof(rattr_t);
this->left -= this->next->length;
this->next = ((void*)this->next) + this->next->length;
return TRUE;
}
METHOD(radius_message_t, create_enumerator, enumerator_t*,
private_radius_message_t *this)
{
attribute_enumerator_t *e;
if (ntohs(this->msg->length) < sizeof(rmsg_t) + sizeof(rattr_t))
{
return enumerator_create_empty();
}
INIT(e,
.public = {
.enumerate = (void*)_attribute_enumerate,
.destroy = (void*)free,
},
.next = (rattr_t*)this->msg->attributes,
.left = ntohs(this->msg->length) - sizeof(rmsg_t),
);
return &e->public;
}
METHOD(radius_message_t, add, void,
private_radius_message_t *this, radius_attribute_type_t type, chunk_t data)
{
rattr_t *attribute;
data.len = min(data.len, 253);
this->msg = realloc(this->msg,
ntohs(this->msg->length) + sizeof(rattr_t) + data.len);
attribute = ((void*)this->msg) + ntohs(this->msg->length);
attribute->type = type;
attribute->length = data.len + sizeof(rattr_t);
memcpy(attribute->value, data.ptr, data.len);
this->msg->length = htons(ntohs(this->msg->length) + attribute->length);
}
METHOD(radius_message_t, sign, void,
private_radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
hasher_t *hasher, signer_t *signer, rng_t *rng)
{
if (rng == NULL)
{
chunk_t msg;
if (req_auth)
{
memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5);
}
else
{
memset(this->msg->authenticator, 0, sizeof(this->msg->authenticator));
}
msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length));
hasher->get_hash(hasher, msg, NULL);
hasher->get_hash(hasher, secret, this->msg->authenticator);
}
else
{
char buf[HASH_SIZE_MD5];
/* build Request-Authenticator */
rng->get_bytes(rng, HASH_SIZE_MD5, this->msg->authenticator);
/* build Message-Authenticator attribute, using 16 null bytes */
memset(buf, 0, sizeof(buf));
add(this, RAT_MESSAGE_AUTHENTICATOR, chunk_create(buf, sizeof(buf)));
signer->get_signature(signer,
chunk_create((u_char*)this->msg, ntohs(this->msg->length)),
((u_char*)this->msg) + ntohs(this->msg->length) - HASH_SIZE_MD5);
}
}
METHOD(radius_message_t, verify, bool,
private_radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
hasher_t *hasher, signer_t *signer)
{
char buf[HASH_SIZE_MD5], res_auth[HASH_SIZE_MD5];
enumerator_t *enumerator;
int type;
chunk_t data, msg;
bool has_eap = FALSE, has_auth = FALSE;
/* replace Response by Request Authenticator for verification */
memcpy(res_auth, this->msg->authenticator, HASH_SIZE_MD5);
if (req_auth)
{
memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5);
}
else
{
memset(this->msg->authenticator, 0, HASH_SIZE_MD5);
}
msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length));
/* verify Response-Authenticator */
hasher->get_hash(hasher, msg, NULL);
hasher->get_hash(hasher, secret, buf);
if (!memeq(buf, res_auth, HASH_SIZE_MD5))
{
DBG1(DBG_CFG, "RADIUS Response-Authenticator verification failed");
return FALSE;
}
/* verify Message-Authenticator attribute */
enumerator = create_enumerator(this);
while (enumerator->enumerate(enumerator, &type, &data))
{
if (type == RAT_MESSAGE_AUTHENTICATOR)
{
if (data.len != HASH_SIZE_MD5)
{
DBG1(DBG_CFG, "RADIUS Message-Authenticator invalid length");
enumerator->destroy(enumerator);
return FALSE;
}
memcpy(buf, data.ptr, data.len);
memset(data.ptr, 0, data.len);
if (signer->verify_signature(signer, msg,
chunk_create(buf, sizeof(buf))))
{
/* restore Message-Authenticator */
memcpy(data.ptr, buf, data.len);
has_auth = TRUE;
break;
}
else
{
DBG1(DBG_CFG, "RADIUS Message-Authenticator verification failed");
enumerator->destroy(enumerator);
return FALSE;
}
}
else if (type == RAT_EAP_MESSAGE)
{
has_eap = TRUE;
}
}
enumerator->destroy(enumerator);
/* restore Response-Authenticator */
memcpy(this->msg->authenticator, res_auth, HASH_SIZE_MD5);
if (has_eap && !has_auth)
{ /* Message-Authenticator is required if we have an EAP-Message */
DBG1(DBG_CFG, "RADIUS Message-Authenticator attribute missing");
return FALSE;
}
return TRUE;
}
METHOD(radius_message_t, get_code, radius_message_code_t,
private_radius_message_t *this)
{
return this->msg->code;
}
METHOD(radius_message_t, get_identifier, u_int8_t,
private_radius_message_t *this)
{
return this->msg->identifier;
}
METHOD(radius_message_t, set_identifier, void,
private_radius_message_t *this, u_int8_t identifier)
{
this->msg->identifier = identifier;
}
METHOD(radius_message_t, get_authenticator, u_int8_t*,
private_radius_message_t *this)
{
return this->msg->authenticator;
}
METHOD(radius_message_t, get_encoding, chunk_t,
private_radius_message_t *this)
{
return chunk_create((u_char*)this->msg, ntohs(this->msg->length));
}
METHOD(radius_message_t, destroy, void,
private_radius_message_t *this)
{
free(this->msg);
free(this);
}
/**
* Generic constructor
*/
static private_radius_message_t *radius_message_create_empty()
{
private_radius_message_t *this;
INIT(this,
.public = {
.create_enumerator = _create_enumerator,
.add = _add,
.get_code = _get_code,
.get_identifier = _get_identifier,
.set_identifier = _set_identifier,
.get_authenticator = _get_authenticator,
.get_encoding = _get_encoding,
.sign = _sign,
.verify = _verify,
.destroy = _destroy,
},
);
return this;
}
/**
* See header
*/
radius_message_t *radius_message_create(radius_message_code_t code)
{
private_radius_message_t *this = radius_message_create_empty();
INIT(this->msg,
.code = code,
.identifier = 0,
.length = htons(sizeof(rmsg_t)),
);
return &this->public;
}
/**
* See header
*/
radius_message_t *radius_message_parse(chunk_t data)
{
private_radius_message_t *this = radius_message_create_empty();
this->msg = malloc(data.len);
memcpy(this->msg, data.ptr, data.len);
if (data.len < sizeof(rmsg_t) ||
ntohs(this->msg->length) != data.len)
{
DBG1(DBG_IKE, "RADIUS message has invalid length");
destroy(this);
return NULL;
}
return &this->public;
}
@@ -1,287 +0,0 @@
/*
* Copyright (C) 2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup radius_message radius_message
* @{ @ingroup eap_radius
*/
#ifndef RADIUS_MESSAGE_H_
#define RADIUS_MESSAGE_H_
#include <library.h>
typedef struct radius_message_t radius_message_t;
typedef enum radius_message_code_t radius_message_code_t;
typedef enum radius_attribute_type_t radius_attribute_type_t;
/**
* RADIUS Message Codes.
*/
enum radius_message_code_t {
RMC_ACCESS_REQUEST = 1,
RMC_ACCESS_ACCEPT = 2,
RMC_ACCESS_REJECT = 3,
RMC_ACCOUNTING_REQUEST = 4,
RMC_ACCOUNTING_RESPONSE = 5,
RMC_ACCESS_CHALLENGE = 11,
RMC_DISCONNECT_REQUEST = 40,
RMC_DISCONNECT_ACK = 41,
RMC_DISCONNECT_NAK = 42,
RMC_COA_REQUEST = 43,
RMC_COA_ACK = 44,
RMC_COA_NAK = 45,
};
/**
* Enum names for radius_attribute_type_t.
*/
extern enum_name_t *radius_message_code_names;
/**
* RADIUS Attribute Types.
*/
enum radius_attribute_type_t {
RAT_USER_NAME = 1,
RAT_USER_PASSWORD = 2,
RAT_CHAP_PASSWORD = 3,
RAT_NAS_IP_ADDRESS = 4,
RAT_NAS_PORT = 5,
RAT_SERVICE_TYPE = 6,
RAT_FRAMED_PROTOCOL = 7,
RAT_FRAMED_IP_ADDRESS = 8,
RAT_FRAMED_IP_NETMASK = 9,
RAT_FRAMED_ROUTING = 10,
RAT_FILTER_ID = 11,
RAT_FRAMED_MTU = 12,
RAT_FRAMED_COMPRESSION = 13,
RAT_LOGIN_IP_HOST = 14,
RAT_LOGIN_SERVICE = 15,
RAT_LOGIN_TCP_PORT = 16,
RAT_REPLY_MESSAGE = 18,
RAT_CALLBACK_NUMBER = 19,
RAT_CALLBACK_ID = 20,
RAT_FRAMED_ROUTE = 22,
RAT_FRAMED_IPX_NETWORK = 23,
RAT_STATE = 24,
RAT_CLASS = 25,
RAT_VENDOR_SPECIFIC = 26,
RAT_SESSION_TIMEOUT = 27,
RAT_IDLE_TIMEOUT = 28,
RAT_TERMINATION_ACTION = 29,
RAT_CALLED_STATION_ID = 30,
RAT_CALLING_STATION_ID = 31,
RAT_NAS_IDENTIFIER = 32,
RAT_PROXY_STATE = 33,
RAT_LOGIN_LAT_SERVICE = 34,
RAT_LOGIN_LAT_NODE = 35,
RAT_LOGIN_LAT_GROUP = 36,
RAT_FRAMED_APPLETALK_LINK = 37,
RAT_FRAMED_APPLETALK_NETWORK = 38,
RAT_FRAMED_APPLETALK_ZONE = 39,
RAT_ACCT_STATUS_TYPE = 40,
RAT_ACCT_DELAY_TIME = 41,
RAT_ACCT_INPUT_OCTETS = 42,
RAT_ACCT_OUTPUT_OCTETS = 43,
RAT_ACCT_SESSION_ID = 44,
RAT_ACCT_AUTHENTIC = 45,
RAT_ACCT_SESSION_TIME = 46,
RAT_ACCT_INPUT_PACKETS = 47,
RAT_ACCT_OUTPUT_PACKETS = 48,
RAT_ACCT_TERMINATE_CAUSE = 49,
RAT_ACCT_MULTI_SESSION_ID = 50,
RAT_ACCT_LINK_COUNT = 51,
RAT_ACCT_INPUT_GIGAWORDS = 52,
RAT_ACCT_OUTPUT_GIGAWORDS = 53,
RAT_EVENT_TIMESTAMP = 55,
RAT_EGRESS_VLANID = 56,
RAT_INGRESS_FILTERS = 57,
RAT_EGRESS_VLAN_NAME = 58,
RAT_USER_PRIORITY_TABLE = 59,
RAT_CHAP_CHALLENGE = 60,
RAT_NAS_PORT_TYPE = 61,
RAT_PORT_LIMIT = 62,
RAT_LOGIN_LAT_PORT = 63,
RAT_TUNNEL_TYPE = 64,
RAT_TUNNEL_MEDIUM_TYPE = 65,
RAT_TUNNEL_CLIENT_ENDPOINT = 66,
RAT_TUNNEL_SERVER_ENDPOINT = 67,
RAT_ACCT_TUNNEL_CONNECTION = 68,
RAT_TUNNEL_PASSWORD = 69,
RAT_ARAP_PASSWORD = 70,
RAT_ARAP_FEATURES = 71,
RAT_ARAP_ZONE_ACCESS = 72,
RAT_ARAP_SECURITY = 73,
RAT_ARAP_SECURITY_DATA = 74,
RAT_PASSWORD_RETRY = 75,
RAT_PROMPT = 76,
RAT_CONNECT_INFO = 77,
RAT_CONFIGURATION_TOKEN = 78,
RAT_EAP_MESSAGE = 79,
RAT_MESSAGE_AUTHENTICATOR = 80,
RAT_TUNNEL_PRIVATE_GROUP_ID = 81,
RAT_TUNNEL_ASSIGNMENT_ID = 82,
RAT_TUNNEL_PREFERENCE = 83,
RAT_ARAP_CHALLENGE_RESPONSE = 84,
RAT_ACCT_INTERIM_INTERVAL = 85,
RAT_ACCT_TUNNEL_PACKETS_LOST = 86,
RAT_NAS_PORT_ID = 87,
RAT_FRAMED_POOL = 88,
RAT_CUI = 89,
RAT_TUNNEL_CLIENT_AUTH_ID = 90,
RAT_TUNNEL_SERVER_AUTH_ID = 91,
RAT_NAS_FILTER_RULE = 92,
RAT_UNASSIGNED = 93,
RAT_ORIGINATING_LINE_INFO = 94,
RAT_NAS_IPV6_ADDRESS = 95,
RAT_FRAMED_INTERFACE_ID = 96,
RAT_FRAMED_IPV6_PREFIX = 97,
RAT_LOGIN_IPV6_HOST = 98,
RAT_FRAMED_IPV6_ROUTE = 99,
RAT_FRAMED_IPV6_POOL = 100,
RAT_ERROR_CAUSE = 101,
RAT_EAP_KEY_NAME = 102,
RAT_DIGEST_RESPONSE = 103,
RAT_DIGEST_REALM = 104,
RAT_DIGEST_NONCE = 105,
RAT_DIGEST_RESPONSE_AUTH = 106,
RAT_DIGEST_NEXTNONCE = 107,
RAT_DIGEST_METHOD = 108,
RAT_DIGEST_URI = 109,
RAT_DIGEST_QOP = 110,
RAT_DIGEST_ALGORITHM = 111,
RAT_DIGEST_ENTITY_BODY_HASH = 112,
RAT_DIGEST_CNONCE = 113,
RAT_DIGEST_NONCE_COUNT = 114,
RAT_DIGEST_USERNAME = 115,
RAT_DIGEST_OPAQUE = 116,
RAT_DIGEST_AUTH_PARAM = 117,
RAT_DIGEST_AKA_AUTS = 118,
RAT_DIGEST_DOMAIN = 119,
RAT_DIGEST_STALE = 120,
RAT_DIGEST_HA1 = 121,
RAT_SIP_AOR = 122,
RAT_DELEGATED_IPV6_PREFIX = 123,
RAT_MIP6_FEATURE_VECTOR = 124,
RAT_MIP6_HOME_LINK_PREFIX = 125,
};
/**
* Enum names for radius_attribute_type_t.
*/
extern enum_name_t *radius_attribute_type_names;
/**
* A RADIUS message, contains attributes.
*/
struct radius_message_t {
/**
* Create an enumerator over contained RADIUS attributes.
*
* @return enumerator over (int type, chunk_t data)
*/
enumerator_t* (*create_enumerator)(radius_message_t *this);
/**
* Add a RADIUS attribute to the message.
*
* @param type type of attribute to add
* @param attribute data, gets cloned
*/
void (*add)(radius_message_t *this, radius_attribute_type_t type,
chunk_t data);
/**
* Get the message type (code).
*
* @return message code
*/
radius_message_code_t (*get_code)(radius_message_t *this);
/**
* Get the message identifier.
*
* @return message identifier
*/
u_int8_t (*get_identifier)(radius_message_t *this);
/**
* Set the message identifier.
*
* @param identifier message identifier
*/
void (*set_identifier)(radius_message_t *this, u_int8_t identifier);
/**
* Get the 16 byte authenticator.
*
* @return pointer to the Authenticator field
*/
u_int8_t* (*get_authenticator)(radius_message_t *this);
/**
* Get the RADIUS message in its encoded form.
*
* @return chunk pointing to internal RADIUS message.
*/
chunk_t (*get_encoding)(radius_message_t *this);
/**
* Calculate and add the Message-Authenticator attribute to the message.
*
* @param req_auth 16 byte Authenticator of request, or NULL
* @param secret shared RADIUS secret
* @param signer HMAC-MD5 signer with secret set
* @param hasher MD5 hasher
* @param rng RNG to create Message-Authenticator, NULL to omit
*/
void (*sign)(radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
hasher_t *hasher, signer_t *signer, rng_t *rng);
/**
* Verify the integrity of a received RADIUS message.
*
* @param req_auth 16 byte Authenticator of request, or NULL
* @param secret shared RADIUS secret
* @param signer HMAC-MD5 signer with secret set
* @param hasher MD5 hasher
*/
bool (*verify)(radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
hasher_t *hasher, signer_t *signer);
/**
* Destroy the message.
*/
void (*destroy)(radius_message_t *this);
};
/**
* Create an empty RADIUS message.
*
* @param code request type
* @return radius_message_t object
*/
radius_message_t *radius_message_create(radius_message_code_t code);
/**
* Parse and verify a recevied RADIUS message.
*
* @param data received message data
* @return radius_message_t object, NULL if length invalid
*/
radius_message_t *radius_message_parse(chunk_t data);
#endif /** RADIUS_MESSAGE_H_ @}*/
@@ -1,221 +0,0 @@
/*
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "radius_server.h"
#include <threading/mutex.h>
#include <threading/condvar.h>
#include <utils/linked_list.h>
typedef struct private_radius_server_t private_radius_server_t;
/**
* Private data of an radius_server_t object.
*/
struct private_radius_server_t {
/**
* Public radius_server_t interface.
*/
radius_server_t public;
/**
* list of radius sockets, as radius_socket_t
*/
linked_list_t *sockets;
/**
* Total number of sockets, in list + currently in use
*/
int socket_count;
/**
* mutex to lock sockets list
*/
mutex_t *mutex;
/**
* condvar to wait for sockets
*/
condvar_t *condvar;
/**
* Server name
*/
char *name;
/**
* NAS-Identifier
*/
chunk_t nas_identifier;
/**
* Preference boost for this server
*/
int preference;
/**
* Is the server currently reachable
*/
bool reachable;
/**
* Retry counter for unreachable servers
*/
int retry;
/**
* reference count
*/
refcount_t ref;
};
METHOD(radius_server_t, get_socket, radius_socket_t*,
private_radius_server_t *this)
{
radius_socket_t *skt;
this->mutex->lock(this->mutex);
while (this->sockets->remove_first(this->sockets, (void**)&skt) != SUCCESS)
{
this->condvar->wait(this->condvar, this->mutex);
}
this->mutex->unlock(this->mutex);
return skt;
}
METHOD(radius_server_t, put_socket, void,
private_radius_server_t *this, radius_socket_t *skt, bool result)
{
this->mutex->lock(this->mutex);
this->sockets->insert_last(this->sockets, skt);
this->mutex->unlock(this->mutex);
this->condvar->signal(this->condvar);
this->reachable = result;
}
METHOD(radius_server_t, get_nas_identifier, chunk_t,
private_radius_server_t *this)
{
return this->nas_identifier;
}
METHOD(radius_server_t, get_preference, int,
private_radius_server_t *this)
{
int pref;
if (this->socket_count == 0)
{ /* don't have sockets, huh? */
return -1;
}
/* calculate preference between 0-100 + boost */
pref = this->preference;
pref += this->sockets->get_count(this->sockets) * 100 / this->socket_count;
if (this->reachable)
{ /* reachable server get a boost: pref = 110-210 + boost */
return pref + 110;
}
/* Not reachable. Increase preference randomly to let it retry from
* time to time, especially if other servers have high load. */
this->retry++;
if (this->retry % 128 == 0)
{ /* every 64th request gets 210, same as unloaded reachable */
return pref + 110;
}
if (this->retry % 32 == 0)
{ /* every 32th request gets 190, wins against average loaded */
return pref + 90;
}
if (this->retry % 8 == 0)
{ /* every 8th request gets 110, same as server under load */
return pref + 10;
}
/* other get ~100, less than fully loaded */
return pref;
}
METHOD(radius_server_t, get_name, char*,
private_radius_server_t *this)
{
return this->name;
}
METHOD(radius_server_t, get_ref, radius_server_t*,
private_radius_server_t *this)
{
ref_get(&this->ref);
return &this->public;
}
METHOD(radius_server_t, destroy, void,
private_radius_server_t *this)
{
if (ref_put(&this->ref))
{
this->mutex->destroy(this->mutex);
this->condvar->destroy(this->condvar);
this->sockets->destroy_offset(this->sockets,
offsetof(radius_socket_t, destroy));
free(this);
}
}
/**
* See header
*/
radius_server_t *radius_server_create(char *name, char *address,
u_int16_t auth_port, u_int16_t acct_port,
char *nas_identifier, char *secret,
int sockets, int preference)
{
private_radius_server_t *this;
radius_socket_t *socket;
INIT(this,
.public = {
.get_socket = _get_socket,
.put_socket = _put_socket,
.get_nas_identifier = _get_nas_identifier,
.get_preference = _get_preference,
.get_name = _get_name,
.get_ref = _get_ref,
.destroy = _destroy,
},
.reachable = TRUE,
.nas_identifier = chunk_create(nas_identifier, strlen(nas_identifier)),
.socket_count = sockets,
.sockets = linked_list_create(),
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
.condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
.name = name,
.preference = preference,
.ref = 1,
);
while (sockets--)
{
socket = radius_socket_create(address, auth_port, acct_port,
chunk_create(secret, strlen(secret)));
if (!socket)
{
destroy(this);
return NULL;
}
this->sockets->insert_last(this->sockets, socket);
}
return &this->public;
}
@@ -1,100 +0,0 @@
/*
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup radius_server radius_server
* @{ @ingroup eap_radius
*/
#ifndef RADIUS_SERVER_H_
#define RADIUS_SERVER_H_
typedef struct radius_server_t radius_server_t;
#include "radius_socket.h"
/**
* RADIUS server configuration.
*/
struct radius_server_t {
/**
* Get a RADIUS socket from the pool to communicate with this server.
*
* @return RADIUS socket
*/
radius_socket_t* (*get_socket)(radius_server_t *this);
/**
* Release a socket to the pool after use.
*
* @param skt RADIUS socket to release
* @param result result of the socket use, TRUE for success
*/
void (*put_socket)(radius_server_t *this, radius_socket_t *skt, bool result);
/**
* Get the NAS-Identifier to use with this server.
*
* @return NAS-Identifier, internal data
*/
chunk_t (*get_nas_identifier)(radius_server_t *this);
/**
* Get the preference of this server.
*
* Based on the available sockets and the server reachability a preference
* value is calculated: better servers return a higher value.
*/
int (*get_preference)(radius_server_t *this);
/**
* Get the name of the RADIUS server.
*
* @return server name
*/
char* (*get_name)(radius_server_t *this);
/**
* Increase reference count of this server.
*
* @return this
*/
radius_server_t* (*get_ref)(radius_server_t *this);
/**
* Destroy a radius_server_t.
*/
void (*destroy)(radius_server_t *this);
};
/**
* Create a radius_server instance.
*
* @param name server name
* @param address server address
* @param auth_port server port for authentication
* @param acct_port server port for accounting
* @param nas_identifier NAS-Identifier to use with this server
* @param secret secret to use with this server
* @param sockets number of sockets to create in pool
* @param preference preference boost for this server
*/
radius_server_t *radius_server_create(char *name, char *address,
u_int16_t auth_port, u_int16_t acct_port,
char *nas_identifier, char *secret,
int sockets, int preference);
#endif /** RADIUS_SERVER_H_ @}*/
@@ -1,386 +0,0 @@
/*
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "radius_socket.h"
#include <errno.h>
#include <unistd.h>
#include <debug.h>
/**
* Vendor-Id of Microsoft specific attributes
*/
#define VENDOR_ID_MICROSOFT 311
/**
* Microsoft specific vendor attributes
*/
#define MS_MPPE_SEND_KEY 16
#define MS_MPPE_RECV_KEY 17
typedef struct private_radius_socket_t private_radius_socket_t;
/**
* Private data of an radius_socket_t object.
*/
struct private_radius_socket_t {
/**
* Public radius_socket_t interface.
*/
radius_socket_t public;
/**
* Server port for authentication
*/
u_int16_t auth_port;
/**
* socket file descriptor for authentication
*/
int auth_fd;
/**
* Server port for accounting
*/
u_int16_t acct_port;
/**
* socket file descriptor for accounting
*/
int acct_fd;
/**
* Server address
*/
char *address;
/**
* current RADIUS identifier
*/
u_int8_t identifier;
/**
* hasher to use for response verification
*/
hasher_t *hasher;
/**
* HMAC-MD5 signer to build Message-Authenticator attribute
*/
signer_t *signer;
/**
* random number generator for RADIUS request authenticator
*/
rng_t *rng;
/**
* RADIUS secret
*/
chunk_t secret;
};
/**
* Check or establish RADIUS connection
*/
static bool check_connection(private_radius_socket_t *this,
int *fd, u_int16_t port)
{
if (*fd == -1)
{
host_t *server;
server = host_create_from_dns(this->address, AF_UNSPEC, port);
if (!server)
{
DBG1(DBG_CFG, "resolving RADIUS server address '%s' failed",
this->address);
return FALSE;
}
*fd = socket(server->get_family(server), SOCK_DGRAM, IPPROTO_UDP);
if (*fd == -1)
{
DBG1(DBG_CFG, "opening RADIUS socket for %#H failed: %s",
server, strerror(errno));
server->destroy(server);
return FALSE;
}
if (connect(*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(*fd);
*fd = -1;
return FALSE;
}
server->destroy(server);
}
return TRUE;
}
METHOD(radius_socket_t, request, radius_message_t*,
private_radius_socket_t *this, radius_message_t *request)
{
chunk_t data;
int i, *fd;
u_int16_t port;
rng_t *rng = NULL;
if (request->get_code(request) == RMC_ACCOUNTING_REQUEST)
{
fd = &this->acct_fd;
port = this->acct_port;
}
else
{
fd = &this->auth_fd;
port = this->auth_port;
rng = this->rng;
}
/* set Message Identifier */
request->set_identifier(request, this->identifier++);
/* sign the request */
request->sign(request, NULL, this->secret, this->hasher, this->signer, rng);
if (!check_connection(this, fd, port))
{
return NULL;
}
data = request->get_encoding(request);
/* timeout after 2, 3, 4, 5 seconds */
for (i = 2; i <= 5; i++)
{
radius_message_t *response;
bool retransmit = FALSE;
struct timeval tv;
char buf[4096];
fd_set fds;
int res;
if (send(*fd, data.ptr, data.len, 0) != data.len)
{
DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno));
return NULL;
}
tv.tv_sec = i;
tv.tv_usec = 0;
while (TRUE)
{
FD_ZERO(&fds);
FD_SET(*fd, &fds);
res = select((*fd) + 1, &fds, NULL, NULL, &tv);
/* TODO: updated tv to time not waited. Linux does this for us. */
if (res < 0)
{ /* failed */
DBG1(DBG_CFG, "waiting for RADIUS message failed: %s",
strerror(errno));
break;
}
if (res == 0)
{ /* timeout */
DBG1(DBG_CFG, "retransmitting RADIUS message");
retransmit = TRUE;
break;
}
res = recv(*fd, buf, sizeof(buf), MSG_DONTWAIT);
if (res <= 0)
{
DBG1(DBG_CFG, "receiving RADIUS message failed: %s",
strerror(errno));
break;
}
response = radius_message_parse(chunk_create(buf, res));
if (response)
{
if (response->verify(response,
request->get_authenticator(request), this->secret,
this->hasher, this->signer))
{
return response;
}
response->destroy(response);
}
DBG1(DBG_CFG, "received invalid RADIUS message, ignored");
}
if (!retransmit)
{
break;
}
}
DBG1(DBG_CFG, "RADIUS server is not responding");
return NULL;
}
/**
* Decrypt a MS-MPPE-Send/Recv-Key
*/
static chunk_t decrypt_mppe_key(private_radius_socket_t *this, u_int16_t salt,
chunk_t C, radius_message_t *request)
{
chunk_t A, R, P, seed;
u_char *c, *p;
/**
* From RFC2548 (encryption):
* b(1) = MD5(S + R + A) c(1) = p(1) xor b(1) C = c(1)
* b(2) = MD5(S + c(1)) c(2) = p(2) xor b(2) C = C + c(2)
* . . .
* b(i) = MD5(S + c(i-1)) c(i) = p(i) xor b(i) C = C + c(i)
*/
if (C.len % HASH_SIZE_MD5 || C.len < HASH_SIZE_MD5)
{
return chunk_empty;
}
A = chunk_create((u_char*)&salt, sizeof(salt));
R = chunk_create(request->get_authenticator(request), HASH_SIZE_MD5);
P = chunk_alloca(C.len);
p = P.ptr;
c = C.ptr;
seed = chunk_cata("cc", R, A);
while (c < C.ptr + C.len)
{
/* b(i) = MD5(S + c(i-1)) */
this->hasher->get_hash(this->hasher, this->secret, NULL);
this->hasher->get_hash(this->hasher, seed, p);
/* p(i) = b(i) xor c(1) */
memxor(p, c, HASH_SIZE_MD5);
/* prepare next round */
seed = chunk_create(c, HASH_SIZE_MD5);
c += HASH_SIZE_MD5;
p += HASH_SIZE_MD5;
}
/* remove truncation, first byte is key length */
if (*P.ptr >= P.len)
{ /* decryption failed? */
return chunk_empty;
}
return chunk_clone(chunk_create(P.ptr + 1, *P.ptr));
}
METHOD(radius_socket_t, decrypt_msk, chunk_t,
private_radius_socket_t *this, radius_message_t *request,
radius_message_t *response)
{
struct {
u_int32_t id;
u_int8_t type;
u_int8_t length;
u_int16_t salt;
u_int8_t key[];
} __attribute__((packed)) *mppe_key;
enumerator_t *enumerator;
chunk_t data, send = chunk_empty, recv = chunk_empty;
int type;
enumerator = response->create_enumerator(response);
while (enumerator->enumerate(enumerator, &type, &data))
{
if (type == RAT_VENDOR_SPECIFIC &&
data.len > sizeof(*mppe_key))
{
mppe_key = (void*)data.ptr;
if (ntohl(mppe_key->id) == VENDOR_ID_MICROSOFT &&
mppe_key->length == data.len - sizeof(mppe_key->id))
{
data = chunk_create(mppe_key->key, data.len - sizeof(*mppe_key));
if (mppe_key->type == MS_MPPE_SEND_KEY)
{
send = decrypt_mppe_key(this, mppe_key->salt, data, request);
}
if (mppe_key->type == MS_MPPE_RECV_KEY)
{
recv = decrypt_mppe_key(this, mppe_key->salt, data, request);
}
}
}
}
enumerator->destroy(enumerator);
if (send.ptr && recv.ptr)
{
return chunk_cat("mm", recv, send);
}
chunk_clear(&send);
chunk_clear(&recv);
return chunk_empty;
}
METHOD(radius_socket_t, destroy, void,
private_radius_socket_t *this)
{
DESTROY_IF(this->hasher);
DESTROY_IF(this->signer);
DESTROY_IF(this->rng);
if (this->auth_fd != -1)
{
close(this->auth_fd);
};
if (this->acct_fd != -1)
{
close(this->acct_fd);
}
free(this);
}
/**
* See header
*/
radius_socket_t *radius_socket_create(char *address, u_int16_t auth_port,
u_int16_t acct_port, chunk_t secret)
{
private_radius_socket_t *this;
INIT(this,
.public = {
.request = _request,
.decrypt_msk = _decrypt_msk,
.destroy = _destroy,
},
.address = address,
.auth_port = auth_port,
.auth_fd = -1,
.acct_port = acct_port,
.acct_fd = -1,
);
this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128);
this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
if (!this->hasher || !this->signer || !this->rng)
{
DBG1(DBG_CFG, "RADIUS initialization failed, HMAC/MD5/RNG required");
destroy(this);
return NULL;
}
this->secret = secret;
this->signer->set_key(this->signer, secret);
/* we use a random identifier, helps if we restart often */
this->identifier = random();
return &this->public;
}
@@ -1,77 +0,0 @@
/*
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup radius_socket radius_socket
* @{ @ingroup eap_radius
*/
#ifndef RADIUS_SOCKET_H_
#define RADIUS_SOCKET_H_
typedef struct radius_socket_t radius_socket_t;
#include "radius_message.h"
#include <utils/host.h>
/**
* RADIUS socket to a server.
*/
struct radius_socket_t {
/**
* Send a RADIUS request, wait for response.
*
* The socket fills in RADIUS Message identifier, builds a
* Request-Authenticator and calculates the Message-Authenticator
* attribute.
* The received response gets verified using the Response-Identifier
* and the Message-Authenticator attribute.
*
* @param request request message
* @return response message, NULL if timed out
*/
radius_message_t* (*request)(radius_socket_t *this,
radius_message_t *request);
/**
* Decrypt the MSK encoded in a messages MS-MPPE-Send/Recv-Key.
*
* @param request associated RADIUS request message
* @param response RADIUS response message containing attributes
* @return allocated MSK, empty chunk if none found
*/
chunk_t (*decrypt_msk)(radius_socket_t *this, radius_message_t *request,
radius_message_t *response);
/**
* Destroy a radius_socket_t.
*/
void (*destroy)(radius_socket_t *this);
};
/**
* Create a radius_socket instance.
*
* @param address server name
* @param auth_port server port for authentication
* @param acct_port server port for accounting
* @param secret RADIUS secret
*/
radius_socket_t *radius_socket_create(char *address, u_int16_t auth_port,
u_int16_t acct_port, chunk_t secret);
#endif /** RADIUS_SOCKET_H_ @}*/