diff options
Diffstat (limited to 'src/libcharon/plugins/eap_radius')
19 files changed, 1710 insertions, 1808 deletions
diff --git a/src/libcharon/plugins/eap_radius/Makefile.am b/src/libcharon/plugins/eap_radius/Makefile.am index afc50bced..181497ab5 100644 --- a/src/libcharon/plugins/eap_radius/Makefile.am +++ b/src/libcharon/plugins/eap_radius/Makefile.am @@ -1,21 +1,21 @@ 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 libstrongswan_eap_radius_la_SOURCES = \ eap_radius_plugin.h eap_radius_plugin.c \ eap_radius.h eap_radius.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_accounting.h eap_radius_accounting.c \ + eap_radius_dae.h eap_radius_dae.c \ + eap_radius_forward.h eap_radius_forward.c libstrongswan_eap_radius_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/eap_radius/Makefile.in b/src/libcharon/plugins/eap_radius/Makefile.in index 740c64055..0bef44042 100644 --- a/src/libcharon/plugins/eap_radius/Makefile.in +++ b/src/libcharon/plugins/eap_radius/Makefile.in @@ -74,10 +74,11 @@ am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__installdirs = "$(DESTDIR)$(plugindir)" LTLIBRARIES = $(noinst_LTLIBRARIES) $(plugin_LTLIBRARIES) -libstrongswan_eap_radius_la_LIBADD = +@MONOLITHIC_FALSE@libstrongswan_eap_radius_la_DEPENDENCIES = \ +@MONOLITHIC_FALSE@ $(top_builddir)/src/libradius/libradius.la am_libstrongswan_eap_radius_la_OBJECTS = eap_radius_plugin.lo \ - eap_radius.lo radius_server.lo radius_socket.lo \ - radius_client.lo radius_message.lo + eap_radius.lo eap_radius_accounting.lo eap_radius_dae.lo \ + eap_radius_forward.lo libstrongswan_eap_radius_la_OBJECTS = \ $(am_libstrongswan_eap_radius_la_OBJECTS) libstrongswan_eap_radius_la_LINK = $(LIBTOOL) --tag=CC \ @@ -196,6 +197,9 @@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +attest_plugins = @attest_plugins@ +axis2c_CFLAGS = @axis2c_CFLAGS@ +axis2c_LIBS = @axis2c_LIBS@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -204,6 +208,7 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ @@ -220,11 +225,13 @@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ +imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ +ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ @@ -268,6 +275,7 @@ sharedstatedir = @sharedstatedir@ soup_CFLAGS = @soup_CFLAGS@ soup_LIBS = @soup_LIBS@ srcdir = @srcdir@ +starter_plugins = @starter_plugins@ strongswan_conf = @strongswan_conf@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ @@ -279,18 +287,18 @@ urandom_device = @urandom_device@ xml_CFLAGS = @xml_CFLAGS@ xml_LIBS = @xml_LIBS@ 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 @MONOLITHIC_TRUE@noinst_LTLIBRARIES = libstrongswan-eap-radius.la +@MONOLITHIC_FALSE@libstrongswan_eap_radius_la_LIBADD = $(top_builddir)/src/libradius/libradius.la @MONOLITHIC_FALSE@plugin_LTLIBRARIES = libstrongswan-eap-radius.la libstrongswan_eap_radius_la_SOURCES = \ eap_radius_plugin.h eap_radius_plugin.c \ eap_radius.h eap_radius.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_accounting.h eap_radius_accounting.c \ + eap_radius_dae.h eap_radius_dae.c \ + eap_radius_forward.h eap_radius_forward.c libstrongswan_eap_radius_la_LDFLAGS = -module -avoid-version all: all-am @@ -377,11 +385,10 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_radius.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_radius_accounting.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_radius_dae.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_radius_forward.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_radius_plugin.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/radius_client.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/radius_message.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/radius_server.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/radius_socket.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< diff --git a/src/libcharon/plugins/eap_radius/eap_radius.c b/src/libcharon/plugins/eap_radius/eap_radius.c index dfe0e2e09..c0a3703b6 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius.c +++ b/src/libcharon/plugins/eap_radius/eap_radius.c @@ -14,14 +14,14 @@ */ #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> -#define TUNNEL_TYPE_ESP 9 - typedef struct private_eap_radius_t private_eap_radius_t; /** @@ -162,7 +162,7 @@ METHOD(eap_method_t, initiate, status_t, status_t status = FAILED; chunk_t username; - request = radius_message_create_request(); + request = radius_message_create(RMC_ACCESS_REQUEST); username = chunk_create(this->id_prefix, strlen(this->id_prefix)); username = chunk_cata("cc", username, this->peer->get_encoding(this->peer)); request->add(request, RAT_USER_NAME, username); @@ -175,16 +175,22 @@ METHOD(eap_method_t, initiate, status_t, { add_eap_identity(this, request); } + eap_radius_forward_from_ike(request); response = this->client->request(this->client, request); if (response) { + eap_radius_forward_to_ike(response); if (radius2ike(this, response, out)) { status = NEED_MORE; } response->destroy(response); } + else + { + charon->bus->alert(charon->bus, ALERT_RADIUS_NOT_RESPONDING); + } request->destroy(request); return status; } @@ -253,7 +259,7 @@ static void process_filter_id(private_eap_radius_t *this, radius_message_t *msg) tunnel_type = untoh32(data.ptr); DBG1(DBG_IKE, "received RADIUS attribute Tunnel-Type: " "tag = %u, value = %u", tunnel_tag, tunnel_type); - is_esp_tunnel = (tunnel_type == TUNNEL_TYPE_ESP); + is_esp_tunnel = (tunnel_type == RADIUS_TUNNEL_TYPE_ESP); break; case RAT_FILTER_ID: filter_id = data; @@ -282,6 +288,31 @@ static void process_filter_id(private_eap_radius_t *this, radius_message_t *msg) } } +/** + * Handle Session-Timeout attribte + */ +static void process_timeout(private_eap_radius_t *this, radius_message_t *msg) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + chunk_t data; + int type; + + enumerator = msg->create_enumerator(msg); + while (enumerator->enumerate(enumerator, &type, &data)) + { + if (type == RAT_SESSION_TIMEOUT && data.len == 4) + { + ike_sa = charon->bus->get_sa(charon->bus); + if (ike_sa) + { + ike_sa->set_auth_lifetime(ike_sa, untoh32(data.ptr)); + } + } + } + enumerator->destroy(enumerator); +} + METHOD(eap_method_t, process, status_t, private_eap_radius_t *this, eap_payload_t *in, eap_payload_t **out) { @@ -289,22 +320,25 @@ METHOD(eap_method_t, process, status_t, status_t status = FAILED; chunk_t data; - request = radius_message_create_request(); + request = radius_message_create(RMC_ACCESS_REQUEST); request->add(request, RAT_USER_NAME, this->peer->get_encoding(this->peer)); data = in->get_data(in); DBG3(DBG_IKE, "%N payload %B", eap_type_names, this->type, &data); - - /* fragment data suitable for RADIUS (not more than 253 bytes) */ - while (data.len > 253) + + /* fragment data suitable for RADIUS */ + while (data.len > MAX_RADIUS_ATTRIBUTE_SIZE) { - request->add(request, RAT_EAP_MESSAGE, chunk_create(data.ptr, 253)); - data = chunk_skip(data, 253); + request->add(request, RAT_EAP_MESSAGE, + chunk_create(data.ptr,MAX_RADIUS_ATTRIBUTE_SIZE)); + data = chunk_skip(data, MAX_RADIUS_ATTRIBUTE_SIZE); } request->add(request, RAT_EAP_MESSAGE, data); + eap_radius_forward_from_ike(request); response = this->client->request(this->client, request); if (response) { + eap_radius_forward_to_ike(response); switch (response->get_code(response)) { case RMC_ACCESS_CHALLENGE: @@ -324,6 +358,7 @@ METHOD(eap_method_t, process, status_t, { process_filter_id(this, response); } + process_timeout(this, response); DBG1(DBG_IKE, "RADIUS authentication of '%Y' successful", this->peer); status = SUCCESS; @@ -427,7 +462,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); diff --git a/src/libcharon/plugins/eap_radius/eap_radius_accounting.c b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c new file mode 100644 index 000000000..45be22704 --- /dev/null +++ b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 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 "eap_radius_accounting.h" +#include "eap_radius_plugin.h" + +#include <time.h> + +#include <radius_message.h> +#include <radius_client.h> +#include <daemon.h> +#include <utils/hashtable.h> +#include <threading/mutex.h> + +typedef struct private_eap_radius_accounting_t private_eap_radius_accounting_t; + +/** + * Private data of an eap_radius_accounting_t object. + */ +struct private_eap_radius_accounting_t { + + /** + * Public eap_radius_accounting_t interface. + */ + eap_radius_accounting_t public; + + /** + * Hashtable with sessions, IKE_SA unique id => entry_t + */ + hashtable_t *sessions; + + /** + * Mutex to lock sessions + */ + mutex_t *mutex; + + /** + * Session ID prefix + */ + u_int32_t prefix; +}; + +/** + * Hashtable entry with usage stats + */ +typedef struct { + /** RADIUS accounting session ID */ + char sid[16]; + /** number of octets sent */ + u_int64_t sent; + /** number of octets received */ + u_int64_t received; + /** session creation time */ + time_t created; +} entry_t; + +/** + * Accounting message status types + */ +typedef enum { + ACCT_STATUS_START = 1, + ACCT_STATUS_STOP = 2, + ACCT_STATUS_INTERIM_UPDATE = 3, + ACCT_STATUS_ACCOUNTING_ON = 7, + ACCT_STATUS_ACCOUNTING_OFF = 8, +} radius_acct_status_t; + +/** + * Hashtable hash function + */ +static u_int hash(uintptr_t key) +{ + return key; +} + +/** + * Hashtable equals function + */ +static bool equals(uintptr_t a, uintptr_t b) +{ + return a == b; +} + +/** + * Update usage counter when a CHILD_SA rekeys/goes down + */ +static void update_usage(private_eap_radius_accounting_t *this, + ike_sa_t *ike_sa, child_sa_t *child_sa) +{ + u_int64_t sent, received; + entry_t *entry; + + child_sa->get_usestats(child_sa, FALSE, NULL, &sent); + child_sa->get_usestats(child_sa, TRUE, NULL, &received); + + this->mutex->lock(this->mutex); + entry = this->sessions->get(this->sessions, + (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa)); + if (entry) + { + entry->sent += sent; + entry->received += received; + } + this->mutex->unlock(this->mutex); +} + +/** + * Send a RADIUS message, wait for response + */ +static bool send_message(private_eap_radius_accounting_t *this, + radius_message_t *request) +{ + radius_message_t *response; + radius_client_t *client; + bool ack = FALSE; + + client = eap_radius_create_client(); + if (client) + { + response = client->request(client, request); + if (response) + { + ack = response->get_code(response) == RMC_ACCOUNTING_RESPONSE; + response->destroy(response); + } + else + { + charon->bus->alert(charon->bus, ALERT_RADIUS_NOT_RESPONDING); + } + client->destroy(client); + } + return ack; +} + +/** + * Add common IKE_SA parameters to RADIUS account message + */ +static void add_ike_sa_parameters(radius_message_t *message, ike_sa_t *ike_sa) +{ + host_t *vip; + char buf[64]; + chunk_t data; + + snprintf(buf, sizeof(buf), "%Y", ike_sa->get_other_eap_id(ike_sa)); + message->add(message, RAT_USER_NAME, chunk_create(buf, strlen(buf))); + snprintf(buf, sizeof(buf), "%#H", ike_sa->get_other_host(ike_sa)); + message->add(message, RAT_CALLING_STATION_ID, chunk_create(buf, strlen(buf))); + vip = ike_sa->get_virtual_ip(ike_sa, FALSE); + if (vip && vip->get_family(vip) == AF_INET) + { + message->add(message, RAT_FRAMED_IP_ADDRESS, vip->get_address(vip)); + } + if (vip && vip->get_family(vip) == AF_INET6) + { + /* we currently assign /128 prefixes, only (reserved, length) */ + data = chunk_from_chars(0, 128); + data = chunk_cata("cc", data, vip->get_address(vip)); + message->add(message, RAT_FRAMED_IPV6_PREFIX, data); + } +} + +/** + * Send an accounting start message + */ +static void send_start(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa) +{ + radius_message_t *message; + entry_t *entry; + u_int32_t id, value; + + id = ike_sa->get_unique_id(ike_sa); + INIT(entry, + .created = time_monotonic(NULL), + ); + snprintf(entry->sid, sizeof(entry->sid), "%u-%u", this->prefix, id); + + message = radius_message_create(RMC_ACCOUNTING_REQUEST); + value = htonl(ACCT_STATUS_START); + message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value)); + message->add(message, RAT_ACCT_SESSION_ID, + chunk_create(entry->sid, strlen(entry->sid))); + add_ike_sa_parameters(message, ike_sa); + if (send_message(this, message)) + { + this->mutex->lock(this->mutex); + entry = this->sessions->put(this->sessions, (void*)(uintptr_t)id, entry); + this->mutex->unlock(this->mutex); + free(entry); + } + message->destroy(message); +} + +/** + * Send an account stop message + */ +static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa) +{ + radius_message_t *message; + entry_t *entry; + u_int32_t id, value; + + id = ike_sa->get_unique_id(ike_sa); + this->mutex->lock(this->mutex); + entry = this->sessions->remove(this->sessions, (void*)(uintptr_t)id); + this->mutex->unlock(this->mutex); + if (entry) + { + message = radius_message_create(RMC_ACCOUNTING_REQUEST); + value = htonl(ACCT_STATUS_STOP); + message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value)); + message->add(message, RAT_ACCT_SESSION_ID, + chunk_create(entry->sid, strlen(entry->sid))); + add_ike_sa_parameters(message, ike_sa); + value = htonl(entry->sent); + message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value)); + value = htonl(entry->sent >> 32); + if (value) + { + message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS, + chunk_from_thing(value)); + } + value = htonl(entry->received); + message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value)); + value = htonl(entry->received >> 32); + if (value) + { + message->add(message, RAT_ACCT_INPUT_GIGAWORDS, + chunk_from_thing(value)); + } + value = htonl(time_monotonic(NULL) - entry->created); + message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value)); + + send_message(this, message); + message->destroy(message); + free(entry); + } +} + +METHOD(listener_t, ike_updown, bool, + private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, bool up) +{ + if (!up) + { + enumerator_t *enumerator; + child_sa_t *child_sa; + + /* update usage for all children just before sending stop */ + enumerator = ike_sa->create_child_sa_enumerator(ike_sa); + while (enumerator->enumerate(enumerator, &child_sa)) + { + update_usage(this, ike_sa, child_sa); + } + enumerator->destroy(enumerator); + + send_stop(this, ike_sa); + } + return TRUE; +} + +METHOD(listener_t, message_hook, bool, + private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, + message_t *message, bool incoming) +{ + /* start accounting here, virtual IP now is set */ + if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED && + message->get_exchange_type(message) == IKE_AUTH && + !incoming && !message->get_request(message)) + { + send_start(this, ike_sa); + } + return TRUE; +} + +METHOD(listener_t, child_rekey, bool, + private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, + child_sa_t *old, child_sa_t *new) +{ + update_usage(this, ike_sa, old); + + return TRUE; +} + +METHOD(listener_t, child_updown, bool, + private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, + child_sa_t *child_sa, bool up) +{ + if (!up && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED) + { + update_usage(this, ike_sa, child_sa); + } + return TRUE; +} + +METHOD(eap_radius_accounting_t, destroy, void, + private_eap_radius_accounting_t *this) +{ + this->mutex->destroy(this->mutex); + this->sessions->destroy(this->sessions); + free(this); +} + +/** + * See header + */ +eap_radius_accounting_t *eap_radius_accounting_create() +{ + private_eap_radius_accounting_t *this; + + INIT(this, + .public = { + .listener = { + .ike_updown = _ike_updown, + .message = _message_hook, + .child_updown = _child_updown, + .child_rekey = _child_rekey, + }, + .destroy = _destroy, + }, + /* use system time as Session ID prefix */ + .prefix = (u_int32_t)time(NULL), + .sessions = hashtable_create((hashtable_hash_t)hash, + (hashtable_equals_t)equals, 32), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + ); + + return &this->public; +} diff --git a/src/libcharon/plugins/eap_radius/eap_radius_accounting.h b/src/libcharon/plugins/eap_radius/eap_radius_accounting.h new file mode 100644 index 000000000..811a5bb90 --- /dev/null +++ b/src/libcharon/plugins/eap_radius/eap_radius_accounting.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 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 eap_radius_accounting eap_radius_accounting + * @{ @ingroup eap_radius + */ + +#ifndef EAP_RADIUS_ACCOUNTING_H_ +#define EAP_RADIUS_ACCOUNTING_H_ + +#include <bus/listeners/listener.h> + +typedef struct eap_radius_accounting_t eap_radius_accounting_t; + +/** + * RADIUS accounting for IKE/IPsec. + */ +struct eap_radius_accounting_t { + + /** + * Implements listener_t. + */ + listener_t listener; + + /** + * Destroy a eap_radius_accounting_t. + */ + void (*destroy)(eap_radius_accounting_t *this); +}; + +/** + * Create a eap_radius_accounting instance. + */ +eap_radius_accounting_t *eap_radius_accounting_create(); + +#endif /** EAP_RADIUS_ACCOUNTING_H_ @}*/ diff --git a/src/libcharon/plugins/eap_radius/eap_radius_dae.c b/src/libcharon/plugins/eap_radius/eap_radius_dae.c new file mode 100644 index 000000000..e84fe5b9c --- /dev/null +++ b/src/libcharon/plugins/eap_radius/eap_radius_dae.c @@ -0,0 +1,543 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 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 "eap_radius_dae.h" + +#include <radius_message.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <unistd.h> +#include <errno.h> + +#include <daemon.h> +#include <threading/thread.h> +#include <processing/jobs/callback_job.h> +#include <processing/jobs/delete_ike_sa_job.h> + +#define RADIUS_DAE_PORT 3799 + +typedef struct private_eap_radius_dae_t private_eap_radius_dae_t; + +/** + * Private data of an eap_radius_dae_t object. + */ +struct private_eap_radius_dae_t { + + /** + * Public eap_radius_dae_t interface. + */ + eap_radius_dae_t public; + + /** + * RADIUS session state + */ + eap_radius_accounting_t *accounting; + + /** + * Socket to listen on authorization extension port + */ + int fd; + + /** + * Listen job + */ + callback_job_t *job; + + /** + * RADIUS shared secret for DAE exchanges + */ + chunk_t secret; + + /** + * MD5 hasher + */ + hasher_t *hasher; + + /** + * HMAC MD5 signer, with secret set + */ + signer_t *signer; + + /** + * List of responses for retransmission, as entry_t + */ + linked_list_t *responses; +}; + +/** + * Entry to store responses for retransmit + */ +typedef struct { + /** stored response */ + radius_message_t *response; + /** client that sent the request */ + host_t *client; +} entry_t; + +/** + * Clean up an entry + */ +static void entry_destroy(entry_t *entry) +{ + entry->response->destroy(entry->response); + entry->client->destroy(entry->client); + free(entry); +} + +/** + * Save/Replace response for retransmission + */ +static void save_retransmit(private_eap_radius_dae_t *this, + radius_message_t *response, host_t *client) +{ + enumerator_t *enumerator; + entry_t *entry; + bool found = FALSE; + + enumerator = this->responses->create_enumerator(this->responses); + while (enumerator->enumerate(enumerator, &entry)) + { + if (client->equals(client, entry->client)) + { + entry->response->destroy(entry->response); + entry->response = response; + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + if (!found) + { + INIT(entry, + .response = response, + .client = client->clone(client), + ); + this->responses->insert_first(this->responses, entry); + } +} + +/** + * Send a RADIUS message to client + */ +static void send_message(private_eap_radius_dae_t *this, + radius_message_t *message, host_t *client) +{ + chunk_t data; + + data = message->get_encoding(message); + if (sendto(this->fd, data.ptr, data.len, 0, client->get_sockaddr(client), + *client->get_sockaddr_len(client)) != data.len) + { + DBG1(DBG_CFG, "sending RADIUS DAE response failed: %s", strerror(errno)); + } +} + +/** + * Check if we request is a retransmit, retransmit stored response + */ +static bool send_retransmit(private_eap_radius_dae_t *this, + radius_message_t *request, host_t *client) +{ + enumerator_t *enumerator; + entry_t *entry; + bool found = FALSE; + + enumerator = this->responses->create_enumerator(this->responses); + while (enumerator->enumerate(enumerator, &entry)) + { + if (client->equals(client, entry->client) && + request->get_identifier(request) == + entry->response->get_identifier(entry->response)) + { + DBG1(DBG_CFG, "received retransmit of RADIUS %N, retransmitting %N " + "to %H", radius_message_code_names, request->get_code(request), + radius_message_code_names, + entry->response->get_code(entry->response), client); + send_message(this, entry->response, client); + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + return found; +} + +/** + * Send an ACK/NAK response for a request + */ +static void send_response(private_eap_radius_dae_t *this, + radius_message_t *request, radius_message_code_t code, + host_t *client) +{ + radius_message_t *response; + + response = radius_message_create(code); + response->set_identifier(response, request->get_identifier(request)); + response->sign(response, request->get_authenticator(request), + this->secret, this->hasher, this->signer, NULL, FALSE); + + send_message(this, response, client); + save_retransmit(this, response, client); +} + +/** + * Add all IKE_SAs matching to user to a list + */ +static void add_matching_ike_sas(linked_list_t *list, identification_t *user) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + ike_sa_id_t *id; + + enumerator = charon->ike_sa_manager->create_enumerator( + charon->ike_sa_manager, FALSE); + while (enumerator->enumerate(enumerator, &ike_sa)) + { + if (user->matches(user, ike_sa->get_other_eap_id(ike_sa))) + { + id = ike_sa->get_id(ike_sa); + list->insert_last(list, id->clone(id)); + } + } + enumerator->destroy(enumerator); +} + +/** + * Get list of IKE_SAs matching a Disconnect/CoA request + */ +static linked_list_t *get_matching_ike_sas(private_eap_radius_dae_t *this, + radius_message_t *request, host_t *client) +{ + enumerator_t *enumerator; + identification_t *user; + linked_list_t *ids; + chunk_t data; + int type; + + ids = linked_list_create(); + + enumerator = request->create_enumerator(request); + while (enumerator->enumerate(enumerator, &type, &data)) + { + if (type == RAT_USER_NAME && data.len) + { + user = identification_create_from_data(data); + DBG1(DBG_CFG, "received RADIUS DAE %N for %Y from %H", + radius_message_code_names, request->get_code(request), + user, client); + add_matching_ike_sas(ids, user); + user->destroy(user); + } + } + enumerator->destroy(enumerator); + + return ids; +} + +/** + * Process a DAE disconnect request, send response + */ +static void process_disconnect(private_eap_radius_dae_t *this, + radius_message_t *request, host_t *client) +{ + enumerator_t *enumerator; + linked_list_t *ids; + ike_sa_id_t *id; + + ids = get_matching_ike_sas(this, request, client); + + if (ids->get_count(ids)) + { + DBG1(DBG_CFG, "closing %d IKE_SA%s matching %N, sending %N", + ids->get_count(ids), ids->get_count(ids) > 1 ? "s" : "", + radius_message_code_names, RMC_DISCONNECT_REQUEST, + radius_message_code_names, RMC_DISCONNECT_ACK); + + enumerator = ids->create_enumerator(ids); + while (enumerator->enumerate(enumerator, &id)) + { + lib->processor->queue_job(lib->processor, (job_t*) + delete_ike_sa_job_create(id, TRUE)); + } + enumerator->destroy(enumerator); + + send_response(this, request, RMC_DISCONNECT_ACK, client); + } + else + { + DBG1(DBG_CFG, "no IKE_SA matches %N, sending %N", + radius_message_code_names, RMC_DISCONNECT_REQUEST, + radius_message_code_names, RMC_DISCONNECT_NAK); + send_response(this, request, RMC_DISCONNECT_NAK, client); + } + ids->destroy_offset(ids, offsetof(ike_sa_id_t, destroy)); +} + +/** + * Apply a new lifetime to an IKE_SA + */ +static void apply_lifetime(private_eap_radius_dae_t *this, ike_sa_id_t *id, + u_int32_t lifetime) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id); + if (ike_sa) + { + if (ike_sa->set_auth_lifetime(ike_sa, lifetime) == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + } +} + +/** + * Process a DAE CoA request, send response + */ +static void process_coa(private_eap_radius_dae_t *this, + radius_message_t *request, host_t *client) +{ + enumerator_t *enumerator; + linked_list_t *ids; + ike_sa_id_t *id; + chunk_t data; + int type; + u_int32_t lifetime = 0; + bool lifetime_seen = FALSE; + + ids = get_matching_ike_sas(this, request, client); + + if (ids->get_count(ids)) + { + enumerator = request->create_enumerator(request); + while (enumerator->enumerate(enumerator, &type, &data)) + { + if (type == RAT_SESSION_TIMEOUT && data.len == 4) + { + lifetime = untoh32(data.ptr); + lifetime_seen = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + if (lifetime_seen) + { + DBG1(DBG_CFG, "applying %us lifetime to %d IKE_SA%s matching %N, " + "sending %N", lifetime, ids->get_count(ids), + ids->get_count(ids) > 1 ? "s" : "", + radius_message_code_names, RMC_COA_REQUEST, + radius_message_code_names, RMC_COA_ACK); + + enumerator = ids->create_enumerator(ids); + while (enumerator->enumerate(enumerator, &id)) + { + apply_lifetime(this, id, lifetime); + } + enumerator->destroy(enumerator); + send_response(this, request, RMC_COA_ACK, client); + } + else + { + DBG1(DBG_CFG, "no Session-Timeout attribute found in %N, sending %N", + radius_message_code_names, RMC_COA_REQUEST, + radius_message_code_names, RMC_COA_NAK); + send_response(this, request, RMC_COA_NAK, client); + } + } + else + { + DBG1(DBG_CFG, "no IKE_SA matches %N, sending %N", + radius_message_code_names, RMC_COA_REQUEST, + radius_message_code_names, RMC_COA_NAK); + send_response(this, request, RMC_COA_NAK, client); + } + ids->destroy_offset(ids, offsetof(ike_sa_id_t, destroy)); +} + +/** + * Receive RADIUS DAE requests + */ +static job_requeue_t receive(private_eap_radius_dae_t *this) +{ + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + radius_message_t *request; + char buf[2048]; + ssize_t len; + bool oldstate; + host_t *client; + + oldstate = thread_cancelability(TRUE); + len = recvfrom(this->fd, buf, sizeof(buf), 0, + (struct sockaddr*)&addr, &addr_len); + thread_cancelability(oldstate); + + if (len > 0) + { + request = radius_message_parse(chunk_create(buf, len)); + if (request) + { + client = host_create_from_sockaddr((struct sockaddr*)&addr); + if (client) + { + if (!send_retransmit(this, request, client)) + { + if (request->verify(request, NULL, this->secret, + this->hasher, this->signer)) + { + switch (request->get_code(request)) + { + case RMC_DISCONNECT_REQUEST: + process_disconnect(this, request, client); + break; + case RMC_COA_REQUEST: + process_coa(this, request, client); + break; + default: + DBG1(DBG_CFG, "ignoring unsupported RADIUS DAE " + "%N message from %H", + radius_message_code_names, + request->get_code(request), client); + break; + } + } + } + client->destroy(client); + } + request->destroy(request); + } + else + { + DBG1(DBG_NET, "ignoring invalid RADIUS DAE request"); + } + } + else + { + DBG1(DBG_NET, "receiving RADIUS DAE request failed: %s", strerror(errno)); + } + return JOB_REQUEUE_DIRECT; +} + +/** + * Open DAE socket + */ +static bool open_socket(private_eap_radius_dae_t *this) +{ + host_t *host; + + this->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (this->fd == -1) + { + DBG1(DBG_CFG, "unable to open RADIUS DAE socket: %s", strerror(errno)); + return FALSE; + } + + host = host_create_from_string( + lib->settings->get_str(lib->settings, + "charon.plugins.eap-radius.dae.listen", "0.0.0.0"), + lib->settings->get_int(lib->settings, + "charon.plugins.eap-radius.dae.port", RADIUS_DAE_PORT)); + if (!host) + { + DBG1(DBG_CFG, "invalid RADIUS DAE listen address"); + return FALSE; + } + + if (bind(this->fd, host->get_sockaddr(host), + *host->get_sockaddr_len(host)) == -1) + { + DBG1(DBG_CFG, "unable to bind RADIUS DAE socket: %s", strerror(errno)); + host->destroy(host); + return FALSE; + } + host->destroy(host); + return TRUE; +} + +METHOD(eap_radius_dae_t, destroy, void, + private_eap_radius_dae_t *this) +{ + if (this->job) + { + this->job->cancel(this->job); + } + if (this->fd != -1) + { + close(this->fd); + } + DESTROY_IF(this->signer); + DESTROY_IF(this->hasher); + this->responses->destroy_function(this->responses, (void*)entry_destroy); + free(this); +} + +/** + * See header + */ +eap_radius_dae_t *eap_radius_dae_create(eap_radius_accounting_t *accounting) +{ + private_eap_radius_dae_t *this; + + INIT(this, + .public = { + .destroy = _destroy, + }, + .accounting = accounting, + .fd = -1, + .secret = { + .ptr = lib->settings->get_str(lib->settings, + "charon.plugins.eap-radius.dae.secret", NULL), + }, + .hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5), + .signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128), + .responses = linked_list_create(), + ); + + if (!this->hasher || !this->signer) + { + destroy(this); + return NULL; + } + if (!this->secret.ptr) + { + DBG1(DBG_CFG, "missing RADIUS DAE secret, disabled"); + destroy(this); + return NULL; + } + this->secret.len = strlen(this->secret.ptr); + this->signer->set_key(this->signer, this->secret); + + if (!open_socket(this)) + { + destroy(this); + return NULL; + } + + this->job = callback_job_create_with_prio((callback_job_cb_t)receive, + this, NULL, NULL, JOB_PRIO_CRITICAL); + lib->processor->queue_job(lib->processor, (job_t*)this->job); + + return &this->public; +} diff --git a/src/libcharon/plugins/eap_radius/eap_radius_dae.h b/src/libcharon/plugins/eap_radius/eap_radius_dae.h new file mode 100644 index 000000000..759eadb49 --- /dev/null +++ b/src/libcharon/plugins/eap_radius/eap_radius_dae.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 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 eap_radius_dae eap_radius_dae + * @{ @ingroup eap_radius + */ + +#ifndef EAP_RADIUS_DAE_H_ +#define EAP_RADIUS_DAE_H_ + +#include "eap_radius_accounting.h" + +typedef struct eap_radius_dae_t eap_radius_dae_t; + +/** + * Dynamic Authorization Extensions (RFC 5176) for EAP-RADIUS. + */ +struct eap_radius_dae_t { + + /** + * Destroy a eap_radius_dae_t. + */ + void (*destroy)(eap_radius_dae_t *this); +}; + +/** + * Create a eap_radius_dae instance. + */ +eap_radius_dae_t *eap_radius_dae_create(eap_radius_accounting_t *accounting); + +#endif /** EAP_RADIUS_DAE_H_ @}*/ diff --git a/src/libcharon/plugins/eap_radius/eap_radius_forward.c b/src/libcharon/plugins/eap_radius/eap_radius_forward.c new file mode 100644 index 000000000..cb4ca74e3 --- /dev/null +++ b/src/libcharon/plugins/eap_radius/eap_radius_forward.c @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 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 "eap_radius_forward.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <utils/hashtable.h> +#include <threading/mutex.h> + +typedef struct private_eap_radius_forward_t private_eap_radius_forward_t; + +/** + * Private data of an eap_radius_forward_t object. + */ +struct private_eap_radius_forward_t { + + /** + * Public eap_radius_forward_t interface. + */ + eap_radius_forward_t public; + + /** + * List of attribute types to copy from IKE, as attr_t + */ + linked_list_t *from_attr; + + /** + * List of attribute types to copy to IKE, as attr_t + */ + linked_list_t *to_attr; + + /** + * Queued to forward from IKE, unique_id => linked_list_t of chunk_t + */ + hashtable_t *from; + + /** + * Queued to forward to IKE, unique_id => linked_list_t of chunk_t + */ + hashtable_t *to; + + /** + * Mutex to lock concurrent access to hashtables + */ + mutex_t *mutex; +}; + +/** + * RADIUS attribute selector + */ +typedef struct { + /** vendor ID, 0 for standard attributes */ + u_int32_t vendor; + /** attribute type */ + u_int8_t type; +} attr_t; + +/** + * Single instance of this + */ +static private_eap_radius_forward_t *singleton = NULL; + +/** + * Hashtable hash function + */ +static u_int hash(uintptr_t key) +{ + return key; +} + +/** + * Hashtable equals function + */ +static bool equals(uintptr_t a, uintptr_t b) +{ + return a == b; +} + +/** + * Free a queue entry + */ +static void free_attribute(chunk_t *chunk) +{ + free(chunk->ptr); + free(chunk); +} + +/** + * Lookup/create an attribute queue from a table + */ +static linked_list_t *lookup_queue(private_eap_radius_forward_t *this, + hashtable_t *table) +{ + linked_list_t *queue = NULL; + ike_sa_t *ike_sa; + uintptr_t id; + + ike_sa = charon->bus->get_sa(charon->bus); + if (ike_sa && ike_sa->supports_extension(ike_sa, EXT_STRONGSWAN)) + { + id = ike_sa->get_unique_id(ike_sa); + this->mutex->lock(this->mutex); + queue = table->get(table, (void*)id); + if (!queue) + { + queue = linked_list_create(); + table->put(table, (void*)id, queue); + } + this->mutex->unlock(this->mutex); + } + return queue; +} + +/** + * Remove attribute queue from table + */ +static void remove_queue(private_eap_radius_forward_t *this, + hashtable_t *table, ike_sa_t *ike_sa) +{ + linked_list_t *queue; + + this->mutex->lock(this->mutex); + queue = table->remove(table, (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa)); + this->mutex->unlock(this->mutex); + if (queue) + { + queue->destroy_function(queue, (void*)free_attribute); + } +} + +/** + * Check if RADIUS attribute is contained in selector + */ +static bool is_attribute_selected(linked_list_t *selector, + radius_attribute_type_t type, chunk_t data) +{ + enumerator_t *enumerator; + u_int32_t vendor = 0; + attr_t *sel; + bool found = FALSE; + + if (type == RAT_VENDOR_SPECIFIC) + { + if (data.len < 4) + { + return FALSE; + } + vendor = untoh32(data.ptr); + } + enumerator = selector->create_enumerator(selector); + while (!found && enumerator->enumerate(enumerator, &sel)) + { + if (sel->vendor == vendor) + { + if (vendor) + { + if (sel->type == 0) + { /* any of that vendor is fine */ + found = TRUE; + } + else if (data.len > 4 && data.ptr[4] == sel->type) + { /* vendor specific type field, as defined in RFC 2865 */ + found = TRUE; + } + } + else + { + if (sel->type == type) + { + found = TRUE; + } + } + } + } + enumerator->destroy(enumerator); + + return found; +} + +/** + * Copy RADIUS attributes from queue to a RADIUS message + */ +static void queue2radius(linked_list_t *queue, radius_message_t *message) +{ + chunk_t *data; + + while (queue->remove_last(queue, (void**)&data) == SUCCESS) + { + if (data->len >= 2) + { + message->add(message, data->ptr[0], chunk_skip(*data, 2)); + } + free_attribute(data); + } +} + +/** + * Copy RADIUS attributes from a RADIUS message to the queue + */ +static void radius2queue(radius_message_t *message, linked_list_t *queue, + linked_list_t *selector) +{ + enumerator_t *enumerator; + int type; + chunk_t data, hdr, *ptr; + + enumerator = message->create_enumerator(message); + while (enumerator->enumerate(enumerator, &type, &data)) + { + if (is_attribute_selected(selector, type, data)) + { + hdr = chunk_alloc(2); + hdr.ptr[0] = type; + hdr.ptr[1] = data.len + 2; + + INIT(ptr); + *ptr = chunk_cat("mc", hdr, data); + queue->insert_last(queue, ptr); + } + } + enumerator->destroy(enumerator); +} + +/** + * Copy RADIUS attribute nofifies from IKE message to queue + */ +static void ike2queue(message_t *message, linked_list_t *queue, + linked_list_t *selector) +{ + enumerator_t *enumerator; + payload_t *payload; + notify_payload_t *notify; + chunk_t data, *ptr; + + enumerator = message->create_payload_enumerator(message); + while (enumerator->enumerate(enumerator, &payload)) + { + if (payload->get_type(payload) == NOTIFY) + { + notify = (notify_payload_t*)payload; + if (notify->get_notify_type(notify) == RADIUS_ATTRIBUTE) + { + data = notify->get_notification_data(notify); + if (data.len >= 2 && is_attribute_selected(selector, + data.ptr[0], chunk_skip(data, 2))) + { + INIT(ptr); + *ptr = chunk_clone(data); + queue->insert_last(queue, ptr); + } + } + } + } + enumerator->destroy(enumerator); +} + +/** + * Copy RADUIS attributes from queue to IKE message notifies + */ +static void queue2ike(linked_list_t *queue, message_t *message) +{ + chunk_t *data; + + while (queue->remove_last(queue, (void**)&data) == SUCCESS) + { + message->add_notify(message, FALSE, RADIUS_ATTRIBUTE, *data); + free_attribute(data); + } +} + +/** + * See header. + */ +void eap_radius_forward_from_ike(radius_message_t *request) +{ + private_eap_radius_forward_t *this = singleton; + linked_list_t *queue; + + if (this) + { + queue = lookup_queue(this, this->from); + if (queue) + { + queue2radius(queue, request); + } + } +} + +/** + * See header. + */ +void eap_radius_forward_to_ike(radius_message_t *response) +{ + private_eap_radius_forward_t *this = singleton; + linked_list_t *queue; + + if (this) + { + queue = lookup_queue(this, this->to); + if (queue) + { + radius2queue(response, queue, this->to_attr); + } + } +} + +METHOD(listener_t, message, bool, + private_eap_radius_forward_t *this, + ike_sa_t *ike_sa, message_t *message, bool incoming) +{ + linked_list_t *queue; + + if (message->get_exchange_type(message) == IKE_AUTH) + { + if (incoming) + { + queue = lookup_queue(this, this->from); + if (queue) + { + ike2queue(message, queue, this->from_attr); + } + } + else + { + queue = lookup_queue(this, this->to); + if (queue) + { + queue2ike(queue, message); + } + } + } + return TRUE; +} + +METHOD(listener_t, ike_updown, bool, + private_eap_radius_forward_t *this, ike_sa_t *ike_sa, bool up) +{ + /* up or down, we don't need the state anymore */ + remove_queue(this, this->from, ike_sa); + remove_queue(this, this->to, ike_sa); + return TRUE; +} + +/** + * Parse a selector string to a list of attr_t selectors + */ +static linked_list_t* parse_selector(char *selector) +{ + enumerator_t *enumerator; + linked_list_t *list; + char *token, *pos; + + list = linked_list_create(); + enumerator = enumerator_create_token(selector, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + int type, vendor = 0; + attr_t *attr; + + pos = strchr(token, ':'); + if (pos) + { + *(pos++) = 0; + vendor = atoi(token); + token = pos; + } + type = enum_from_name(radius_attribute_type_names, token); + if (type == -1) + { + type = atoi(token); + } + if (vendor == 0 && type == 0) + { + DBG1(DBG_CFG, "ignoring unknown RADIUS attribute type '%s'", token); + } + else + { + INIT(attr, + .type = type, + .vendor = vendor, + ); + list->insert_last(list, attr); + if (!vendor) + { + DBG1(DBG_IKE, "forwarding RADIUS attribute %N", + radius_attribute_type_names, type); + } + else + { + DBG1(DBG_IKE, "forwarding RADIUS VSA %d-%d", vendor, type); + } + } + } + enumerator->destroy(enumerator); + return list; +} + +METHOD(eap_radius_forward_t, destroy, void, + private_eap_radius_forward_t *this) +{ + this->from_attr->destroy_function(this->from_attr, free); + this->to_attr->destroy_function(this->to_attr, free); + this->from->destroy(this->from); + this->to->destroy(this->to); + this->mutex->destroy(this->mutex); + free(this); + singleton = NULL; +} + +/** + * See header + */ +eap_radius_forward_t *eap_radius_forward_create() +{ + private_eap_radius_forward_t *this; + + INIT(this, + .public = { + .listener = { + .message = _message, + .ike_updown = _ike_updown, + }, + .destroy = _destroy, + }, + .from_attr = parse_selector(lib->settings->get_str(lib->settings, + "charon.plugins.eap-radius.forward.ike_to_radius", "")), + .to_attr = parse_selector(lib->settings->get_str(lib->settings, + "charon.plugins.eap-radius.forward.radius_to_ike", "")), + .from = hashtable_create((hashtable_hash_t)hash, + (hashtable_equals_t)equals, 8), + .to = hashtable_create((hashtable_hash_t)hash, + (hashtable_equals_t)equals, 8), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + ); + + if (this->from_attr->get_count(this->from_attr) == 0 && + this->to_attr->get_count(this->to_attr) == 0) + { + destroy(this); + return NULL; + } + + singleton = this; + return &this->public; +} diff --git a/src/libcharon/plugins/eap_radius/eap_radius_forward.h b/src/libcharon/plugins/eap_radius/eap_radius_forward.h new file mode 100644 index 000000000..2c1dbf7a8 --- /dev/null +++ b/src/libcharon/plugins/eap_radius/eap_radius_forward.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 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 eap_radius_forward eap_radius_forward + * @{ @ingroup eap_radius + */ + +#ifndef EAP_RADIUS_FORWARD_H_ +#define EAP_RADIUS_FORWARD_H_ + +#include <radius_message.h> + +#include <bus/listeners/listener.h> + +typedef struct eap_radius_forward_t eap_radius_forward_t; + +/** + * Forward RADIUS attributes in Notifies between client and AAA backend. + */ +struct eap_radius_forward_t { + + /** + * Implements a listener. + */ + listener_t listener; + + /** + * Destroy a eap_radius_forward_t. + */ + void (*destroy)(eap_radius_forward_t *this); +}; + +/** + * Create a eap_radius_forward instance. + */ +eap_radius_forward_t *eap_radius_forward_create(); + +/** + * Forward RADIUS attributes from IKE notifies to a RADIUS request. + * + * @param request RADIUS request message to add attributes to + */ +void eap_radius_forward_from_ike(radius_message_t *request); + +/** + * Forward RADIUS attributes from a RADIUS response to IKE notifies. + * + * @param response RADIUS respose to read notifies from + */ +void eap_radius_forward_to_ike(radius_message_t *response); + +#endif /** EAP_RADIUS_FORWARD_H_ @}*/ diff --git a/src/libcharon/plugins/eap_radius/eap_radius_plugin.c b/src/libcharon/plugins/eap_radius/eap_radius_plugin.c index c218bd48b..8ee0ab81a 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_plugin.c +++ b/src/libcharon/plugins/eap_radius/eap_radius_plugin.c @@ -16,16 +16,25 @@ #include "eap_radius_plugin.h" #include "eap_radius.h" -#include "radius_client.h" -#include "radius_server.h" +#include "eap_radius_accounting.h" +#include "eap_radius_dae.h" +#include "eap_radius_forward.h" + +#include <radius_client.h> +#include <radius_config.h> #include <daemon.h> #include <threading/rwlock.h> /** - * Default RADIUS server port, when not configured + * Default RADIUS server port for authentication */ -#define RADIUS_PORT 1812 +#define AUTH_PORT 1812 + +/** + * Default RADIUS server port for accounting + */ +#define ACCT_PORT 1813 typedef struct private_eap_radius_plugin_t private_eap_radius_plugin_t; @@ -40,14 +49,29 @@ struct private_eap_radius_plugin_t { eap_radius_plugin_t public; /** - * List of RADIUS servers + * List of RADIUS server configurations */ - linked_list_t *servers; + linked_list_t *configs; /** - * Lock for server list + * Lock for configs list */ rwlock_t *lock; + + /** + * RADIUS sessions for accounting + */ + eap_radius_accounting_t *accounting; + + /** + * Dynamic authorization extensions + */ + eap_radius_dae_t *dae; + + /** + * RADIUS <-> IKE attribute forwarding + */ + eap_radius_forward_t *forward; }; /** @@ -58,12 +82,12 @@ static private_eap_radius_plugin_t *instance = NULL; /** * Load RADIUS servers from configuration */ -static void load_servers(private_eap_radius_plugin_t *this) +static void load_configs(private_eap_radius_plugin_t *this) { enumerator_t *enumerator; - radius_server_t *server; + radius_config_t *config; char *nas_identifier, *secret, *address, *section; - int port, sockets, preference; + int auth_port, acct_port, sockets, preference; address = lib->settings->get_str(lib->settings, "charon.plugins.eap-radius.server", NULL); @@ -78,18 +102,18 @@ static void load_servers(private_eap_radius_plugin_t *this) } nas_identifier = lib->settings->get_str(lib->settings, "charon.plugins.eap-radius.nas_identifier", "strongSwan"); - port = lib->settings->get_int(lib->settings, - "charon.plugins.eap-radius.port", RADIUS_PORT); + auth_port = lib->settings->get_int(lib->settings, + "charon.plugins.eap-radius.port", AUTH_PORT); sockets = lib->settings->get_int(lib->settings, "charon.plugins.eap-radius.sockets", 1); - server = radius_server_create(address, address, port, nas_identifier, - secret, sockets, 0); - if (!server) + config = radius_config_create(address, address, auth_port, ACCT_PORT, + nas_identifier, secret, sockets, 0); + if (!config) { DBG1(DBG_CFG, "no RADUIS server defined"); return; } - this->servers->insert_last(this->servers, server); + this->configs->insert_last(this->configs, config); return; } @@ -114,26 +138,32 @@ static void load_servers(private_eap_radius_plugin_t *this) nas_identifier = lib->settings->get_str(lib->settings, "charon.plugins.eap-radius.servers.%s.nas_identifier", "strongSwan", section); - port = lib->settings->get_int(lib->settings, - "charon.plugins.eap-radius.servers.%s.port", RADIUS_PORT, section); + auth_port = lib->settings->get_int(lib->settings, + "charon.plugins.eap-radius.servers.%s.auth_port", + lib->settings->get_int(lib->settings, + "charon.plugins.eap-radius.servers.%s.port", + AUTH_PORT, section), + section); + acct_port = lib->settings->get_int(lib->settings, + "charon.plugins.eap-radius.servers.%s.acct_port", ACCT_PORT, section); sockets = lib->settings->get_int(lib->settings, "charon.plugins.eap-radius.servers.%s.sockets", 1, section); preference = lib->settings->get_int(lib->settings, "charon.plugins.eap-radius.servers.%s.preference", 0, section); - server = radius_server_create(section, address, port, nas_identifier, - secret, sockets, preference); - if (!server) + config = radius_config_create(section, address, auth_port, acct_port, + nas_identifier, secret, sockets, preference); + if (!config) { DBG1(DBG_CFG, "loading RADIUS server '%s' failed, skipped", section); continue; } - this->servers->insert_last(this->servers, server); + this->configs->insert_last(this->configs, config); } enumerator->destroy(enumerator); DBG1(DBG_CFG, "loaded %d RADIUS server configuration%s", - this->servers->get_count(this->servers), - this->servers->get_count(this->servers) == 1 ? "" : "s"); + this->configs->get_count(this->configs), + this->configs->get_count(this->configs) == 1 ? "" : "s"); } METHOD(plugin_t, get_name, char*, @@ -142,14 +172,28 @@ METHOD(plugin_t, get_name, char*, return "eap-radius"; } +METHOD(plugin_t, get_features, int, + eap_radius_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_CALLBACK(eap_method_register, eap_radius_create), + PLUGIN_PROVIDE(EAP_SERVER, EAP_RADIUS), + PLUGIN_DEPENDS(HASHER, HASH_MD5), + PLUGIN_DEPENDS(SIGNER, AUTH_HMAC_MD5_128), + PLUGIN_DEPENDS(RNG, RNG_WEAK), + }; + *features = f; + return countof(f); +} + METHOD(plugin_t, reload, bool, private_eap_radius_plugin_t *this) { this->lock->write_lock(this->lock); - this->servers->destroy_offset(this->servers, - offsetof(radius_server_t, destroy)); - this->servers = linked_list_create(); - load_servers(this); + this->configs->destroy_offset(this->configs, + offsetof(radius_config_t, destroy)); + this->configs = linked_list_create(); + load_configs(this); this->lock->unlock(this->lock); return TRUE; } @@ -157,10 +201,17 @@ METHOD(plugin_t, reload, bool, METHOD(plugin_t, destroy, void, private_eap_radius_plugin_t *this) { - charon->eap->remove_method(charon->eap, (eap_constructor_t)eap_radius_create); - this->servers->destroy_offset(this->servers, - offsetof(radius_server_t, destroy)); + if (this->forward) + { + charon->bus->remove_listener(charon->bus, &this->forward->listener); + this->forward->destroy(this->forward); + } + DESTROY_IF(this->dae); + this->configs->destroy_offset(this->configs, + offsetof(radius_config_t, destroy)); this->lock->destroy(this->lock); + charon->bus->remove_listener(charon->bus, &this->accounting->listener); + this->accounting->destroy(this->accounting); free(this); instance = NULL; } @@ -176,36 +227,78 @@ plugin_t *eap_radius_plugin_create() .public = { .plugin = { .get_name = _get_name, + .get_features = _get_features, .reload = _reload, .destroy = _destroy, }, }, - .servers = linked_list_create(), + .configs = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .accounting = eap_radius_accounting_create(), + .forward = eap_radius_forward_create(), ); - load_servers(this); - - charon->eap->add_method(charon->eap, EAP_RADIUS, 0, - EAP_SERVER, (eap_constructor_t)eap_radius_create); - + load_configs(this); instance = this; + if (lib->settings->get_bool(lib->settings, + "charon.plugins.eap-radius.accounting", FALSE)) + { + charon->bus->add_listener(charon->bus, &this->accounting->listener); + } + if (lib->settings->get_bool(lib->settings, + "charon.plugins.eap-radius.dae.enable", FALSE)) + { + this->dae = eap_radius_dae_create(this->accounting); + } + if (this->forward) + { + charon->bus->add_listener(charon->bus, &this->forward->listener); + } + return &this->public.plugin; } /** * See header */ -enumerator_t *eap_radius_create_server_enumerator() +radius_client_t *eap_radius_create_client() { if (instance) { + enumerator_t *enumerator; + radius_config_t *config, *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->configs->create_enumerator(instance->configs); + while (enumerator->enumerate(enumerator, &config)) + { + current = config->get_preference(config); + 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", + config->get_name(config), current); + best = current; + DESTROY_IF(selected); + selected = config->get_ref(config); + } + else + { + DBG2(DBG_CFG, "RADIUS server '%s' skipped: %d", + config->get_name(config), current); + } + } + enumerator->destroy(enumerator); + instance->lock->unlock(instance->lock); + + if (selected) + { + return radius_client_create(selected); + } } - return enumerator_create_empty(); + return NULL; } diff --git a/src/libcharon/plugins/eap_radius/eap_radius_plugin.h b/src/libcharon/plugins/eap_radius/eap_radius_plugin.h index cb724364a..1570bd566 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_plugin.h +++ b/src/libcharon/plugins/eap_radius/eap_radius_plugin.h @@ -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_ @}*/ diff --git a/src/libcharon/plugins/eap_radius/radius_client.c b/src/libcharon/plugins/eap_radius/radius_client.c deleted file mode 100644 index 245308e59..000000000 --- a/src/libcharon/plugins/eap_radius/radius_client.c +++ /dev/null @@ -1,186 +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 <daemon.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); - charon->bus->alert(charon->bus, ALERT_RADIUS_NOT_RESPONDING); - 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; -} - diff --git a/src/libcharon/plugins/eap_radius/radius_client.h b/src/libcharon/plugins/eap_radius/radius_client.h deleted file mode 100644 index e4f3a7222..000000000 --- a/src/libcharon/plugins/eap_radius/radius_client.h +++ /dev/null @@ -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_ @}*/ diff --git a/src/libcharon/plugins/eap_radius/radius_message.c b/src/libcharon/plugins/eap_radius/radius_message.c deleted file mode 100644 index 23a29b772..000000000 --- a/src/libcharon/plugins/eap_radius/radius_message.c +++ /dev/null @@ -1,457 +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 <daemon.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_END(radius_message_code_names, RMC_ACCESS_CHALLENGE); - -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, rng_t *rng, signer_t *signer) -{ - 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); - memcpy(this->msg->authenticator, req_auth, 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() -{ - 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_request() -{ - private_radius_message_t *this = radius_message_create(); - - INIT(this->msg, - .code = RMC_ACCESS_REQUEST, - .identifier = 0, - .length = htons(sizeof(rmsg_t)), - ); - - return &this->public; -} - -/** - * See header - */ -radius_message_t *radius_message_parse_response(chunk_t data) -{ - private_radius_message_t *this = radius_message_create(); - - 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; -} - diff --git a/src/libcharon/plugins/eap_radius/radius_message.h b/src/libcharon/plugins/eap_radius/radius_message.h deleted file mode 100644 index 266839d3b..000000000 --- a/src/libcharon/plugins/eap_radius/radius_message.h +++ /dev/null @@ -1,276 +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, -}; - -/** - * 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 rng RNG to create Request-Authenticator - * @param signer HMAC-MD5 signer with secret set - */ - void (*sign)(radius_message_t *this, rng_t *rng, signer_t *signer); - - /** - * Verify the integrity of a received RADIUS response. - * - * @param req_auth 16 byte Authenticator of the corresponding request - * @param secret shared RADIUS secret - * @param hasher hasher to verify Response-Authenticator - * @param signer signer to verify Message-Authenticator attribute - */ - 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 request message (RMT_ACCESS_REQUEST). - * - * @return radius_message_t object - */ -radius_message_t *radius_message_create_request(); - -/** - * Parse and verify a recevied RADIUS response. - * - * @param data received message data - * @return radius_message_t object, NULL if length invalid - */ -radius_message_t *radius_message_parse_response(chunk_t data); - -#endif /** RADIUS_MESSAGE_H_ @}*/ diff --git a/src/libcharon/plugins/eap_radius/radius_server.c b/src/libcharon/plugins/eap_radius/radius_server.c deleted file mode 100644 index 3baf39807..000000000 --- a/src/libcharon/plugins/eap_radius/radius_server.c +++ /dev/null @@ -1,219 +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 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, port, - chunk_create(secret, strlen(secret))); - if (!socket) - { - destroy(this); - return NULL; - } - this->sockets->insert_last(this->sockets, socket); - } - return &this->public; -} diff --git a/src/libcharon/plugins/eap_radius/radius_server.h b/src/libcharon/plugins/eap_radius/radius_server.h deleted file mode 100644 index c59361c49..000000000 --- a/src/libcharon/plugins/eap_radius/radius_server.h +++ /dev/null @@ -1,97 +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 port server port - * @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 port, - char *nas_identifier, char *secret, int sockets, int preference); - -#endif /** RADIUS_SERVER_H_ @}*/ diff --git a/src/libcharon/plugins/eap_radius/radius_socket.c b/src/libcharon/plugins/eap_radius/radius_socket.c deleted file mode 100644 index b3229c288..000000000 --- a/src/libcharon/plugins/eap_radius/radius_socket.c +++ /dev/null @@ -1,355 +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; - - /** - * socket file descriptor - */ - int fd; - - /** - * Server address - */ - char *address; - - /** - * Server port - */ - u_int16_t port; - - /** - * 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) -{ - 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*, - private_radius_socket_t *this, radius_message_t *request) -{ - chunk_t data; - int i; - - /* set Message Identifier */ - request->set_identifier(request, this->identifier++); - /* sign the request */ - request->sign(request, this->rng, this->signer); - - if (!check_connection(this)) - { - 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(this->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(this->fd, &fds); - res = select(this->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(this->fd, buf, sizeof(buf), MSG_DONTWAIT); - if (res <= 0) - { - DBG1(DBG_CFG, "receiving RADIUS message failed: %s", - strerror(errno)); - break; - } - response = radius_message_parse_response(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->fd != -1) - { - close(this->fd); - } - free(this); -} - -/** - * See header - */ -radius_socket_t *radius_socket_create(char *address, u_int16_t port, - chunk_t secret) -{ - private_radius_socket_t *this; - - INIT(this, - .public = { - .request = _request, - .decrypt_msk = _decrypt_msk, - .destroy = _destroy, - }, - .address = address, - .port = port, - .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; -} diff --git a/src/libcharon/plugins/eap_radius/radius_socket.h b/src/libcharon/plugins/eap_radius/radius_socket.h deleted file mode 100644 index 2875008eb..000000000 --- a/src/libcharon/plugins/eap_radius/radius_socket.h +++ /dev/null @@ -1,76 +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 port server port - * @param secret RADIUS secret - */ -radius_socket_t *radius_socket_create(char *address, u_int16_t port, - chunk_t secret); - -#endif /** RADIUS_SOCKET_H_ @}*/ |