aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libcharon/plugins/eap_radius/Makefile.am1
-rw-r--r--src/libcharon/plugins/eap_radius/eap_radius.c5
-rw-r--r--src/libcharon/plugins/eap_radius/eap_radius_forward.c458
-rw-r--r--src/libcharon/plugins/eap_radius/eap_radius_forward.h65
-rw-r--r--src/libcharon/plugins/eap_radius/eap_radius_plugin.c16
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;
}