diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/libcharon/plugins/eap_radius/Makefile.am | 1 | ||||
-rw-r--r-- | src/libcharon/plugins/eap_radius/eap_radius.c | 5 | ||||
-rw-r--r-- | src/libcharon/plugins/eap_radius/eap_radius_forward.c | 458 | ||||
-rw-r--r-- | src/libcharon/plugins/eap_radius/eap_radius_forward.h | 65 | ||||
-rw-r--r-- | src/libcharon/plugins/eap_radius/eap_radius_plugin.c | 16 |
5 files changed, 545 insertions, 0 deletions
diff --git a/src/libcharon/plugins/eap_radius/Makefile.am b/src/libcharon/plugins/eap_radius/Makefile.am index 403f85806..96bfa6fcb 100644 --- a/src/libcharon/plugins/eap_radius/Makefile.am +++ b/src/libcharon/plugins/eap_radius/Makefile.am @@ -15,6 +15,7 @@ libstrongswan_eap_radius_la_SOURCES = \ eap_radius.h eap_radius.c \ eap_radius_accounting.h eap_radius_accounting.c \ eap_radius_dae.h eap_radius_dae.c \ + eap_radius_forward.h eap_radius_forward.c \ radius_server.h radius_server.c \ radius_socket.h radius_socket.c \ radius_client.h radius_client.c \ diff --git a/src/libcharon/plugins/eap_radius/eap_radius.c b/src/libcharon/plugins/eap_radius/eap_radius.c index 3d8c823b8..d62bcd7d0 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius.c +++ b/src/libcharon/plugins/eap_radius/eap_radius.c @@ -14,6 +14,7 @@ */ #include "eap_radius.h" +#include "eap_radius_forward.h" #include "radius_message.h" #include "radius_client.h" @@ -175,10 +176,12 @@ 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; @@ -327,9 +330,11 @@ METHOD(eap_method_t, process, status_t, } 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: 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..e1a8c4170 --- /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 + */ + +#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 62b53e5ac..e544aaf3e 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_plugin.c +++ b/src/libcharon/plugins/eap_radius/eap_radius_plugin.c @@ -18,6 +18,7 @@ #include "eap_radius.h" #include "eap_radius_accounting.h" #include "eap_radius_dae.h" +#include "eap_radius_forward.h" #include "radius_client.h" #include "radius_server.h" @@ -65,6 +66,11 @@ struct private_eap_radius_plugin_t { * Dynamic authorization extensions */ eap_radius_dae_t *dae; + + /** + * RADIUS <-> IKE attribute forwarding + */ + eap_radius_forward_t *forward; }; /** @@ -194,6 +200,11 @@ METHOD(plugin_t, reload, bool, METHOD(plugin_t, destroy, void, private_eap_radius_plugin_t *this) { + if (this->forward) + { + charon->bus->remove_listener(charon->bus, &this->forward->listener); + this->forward->destroy(this->forward); + } DESTROY_IF(this->dae); this->servers->destroy_offset(this->servers, offsetof(radius_server_t, destroy)); @@ -223,6 +234,7 @@ plugin_t *eap_radius_plugin_create() .servers = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), .accounting = eap_radius_accounting_create(), + .forward = eap_radius_forward_create(), ); load_servers(this); @@ -238,6 +250,10 @@ plugin_t *eap_radius_plugin_create() { this->dae = eap_radius_dae_create(this->accounting); } + if (this->forward) + { + charon->bus->add_listener(charon->bus, &this->forward->listener); + } return &this->public.plugin; } |