aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Willi <martin@strongswan.org>2009-03-24 10:24:58 +0000
committerMartin Willi <martin@strongswan.org>2009-03-24 10:24:58 +0000
commit4a6b84a93461b5ef7a3ad491c1894418db2c555b (patch)
tree4b532a5b23d5d250158b44f00cd0f914359da5d2
parent623bca407be12418fbaccdca977546ed4853e36e (diff)
downloadstrongswan-4a6b84a93461b5ef7a3ad491c1894418db2c555b.tar.bz2
strongswan-4a6b84a93461b5ef7a3ad491c1894418db2c555b.tar.xz
reintegrated eap-radius branch into trunk
-rw-r--r--configure.in10
-rw-r--r--src/charon/Makefile.am5
-rw-r--r--src/charon/plugins/eap_radius/Makefile.am14
-rw-r--r--src/charon/plugins/eap_radius/eap_radius.c276
-rw-r--r--src/charon/plugins/eap_radius/eap_radius.h50
-rw-r--r--src/charon/plugins/eap_radius/eap_radius_plugin.c56
-rw-r--r--src/charon/plugins/eap_radius/eap_radius_plugin.h52
-rw-r--r--src/charon/plugins/eap_radius/radius_client.c493
-rw-r--r--src/charon/plugins/eap_radius/radius_client.h92
-rw-r--r--src/charon/plugins/eap_radius/radius_message.c465
-rw-r--r--src/charon/plugins/eap_radius/radius_message.h279
-rw-r--r--src/charon/sa/authenticators/eap/eap_method.c3
-rw-r--r--src/charon/sa/authenticators/eap/eap_method.h2
-rw-r--r--src/charon/sa/authenticators/eap_authenticator.c5
-rw-r--r--src/libstrongswan/crypto/signers/signer.h2
-rw-r--r--src/libstrongswan/plugins/hmac/hmac_plugin.c2
-rw-r--r--src/libstrongswan/plugins/hmac/hmac_signer.c4
-rw-r--r--src/starter/confread.c4
-rw-r--r--src/starter/ipsec.conf.54
19 files changed, 1815 insertions, 3 deletions
diff --git a/configure.in b/configure.in
index fc2149417..5d9c256ca 100644
--- a/configure.in
+++ b/configure.in
@@ -466,6 +466,14 @@ AC_ARG_ENABLE(
)
AC_ARG_ENABLE(
+ [eap-radius],
+ AS_HELP_STRING([--enable-eap-radius],[build RADIUS proxy authenication module for EAP (default is NO).]),
+ [if test x$enableval = xyes; then
+ eap_radius=true
+ fi]
+)
+
+AC_ARG_ENABLE(
[kernel-netlink],
AS_HELP_STRING([--disable-kernel-netlink],[disable the netlink kernel interface. (default is NO).]),
[if test x$enableval = xyes; then
@@ -963,6 +971,7 @@ AM_CONDITIONAL(USE_EAP_MD5, test x$eap_md5 = xtrue)
AM_CONDITIONAL(USE_EAP_GTC, test x$eap_gtc = xtrue)
AM_CONDITIONAL(USE_EAP_AKA, test x$eap_aka = xtrue)
AM_CONDITIONAL(USE_EAP_MSCHAPV2, test x$eap_mschapv2 = xtrue)
+AM_CONDITIONAL(USE_EAP_RADIUS, test x$eap_radius = xtrue)
AM_CONDITIONAL(USE_KERNEL_NETLINK, test x$kernel_netlink = xtrue)
AM_CONDITIONAL(USE_KERNEL_PFKEY, test x$kernel_pfkey = xtrue)
AM_CONDITIONAL(USE_KERNEL_KLIPS, test x$kernel_klips = xtrue)
@@ -1044,6 +1053,7 @@ AC_OUTPUT(
src/charon/plugins/eap_sim/Makefile
src/charon/plugins/eap_sim_file/Makefile
src/charon/plugins/eap_mschapv2/Makefile
+ src/charon/plugins/eap_radius/Makefile
src/charon/plugins/kernel_netlink/Makefile
src/charon/plugins/kernel_pfkey/Makefile
src/charon/plugins/kernel_klips/Makefile
diff --git a/src/charon/Makefile.am b/src/charon/Makefile.am
index 3393b4516..9da2b238a 100644
--- a/src/charon/Makefile.am
+++ b/src/charon/Makefile.am
@@ -221,6 +221,11 @@ if USE_EAP_MSCHAPV2
PLUGINS += eapmschapv2
endif
+if USE_EAP_RADIUS
+ SUBDIRS += plugins/eap_radius
+ PLUGINS += eapradius
+endif
+
if USE_MEDSRV
SUBDIRS += plugins/medsrv
PLUGINS += medsrv
diff --git a/src/charon/plugins/eap_radius/Makefile.am b/src/charon/plugins/eap_radius/Makefile.am
new file mode 100644
index 000000000..f7de2f14f
--- /dev/null
+++ b/src/charon/plugins/eap_radius/Makefile.am
@@ -0,0 +1,14 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
+
+AM_CFLAGS = -rdynamic
+
+plugin_LTLIBRARIES = libstrongswan-eapradius.la
+
+libstrongswan_eapradius_la_SOURCES = \
+ eap_radius_plugin.h eap_radius_plugin.c \
+ eap_radius.h eap_radius.c \
+ radius_client.h radius_client.c \
+ radius_message.h radius_message.c
+libstrongswan_eapradius_la_LDFLAGS = -module
+
diff --git a/src/charon/plugins/eap_radius/eap_radius.c b/src/charon/plugins/eap_radius/eap_radius.c
new file mode 100644
index 000000000..1a02c5acf
--- /dev/null
+++ b/src/charon/plugins/eap_radius/eap_radius.c
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#include "eap_radius.h"
+
+#include "radius_message.h"
+#include "radius_client.h"
+
+#include <daemon.h>
+
+
+typedef struct private_eap_radius_t private_eap_radius_t;
+
+/**
+ * Private data of an eap_radius_t object.
+ */
+struct private_eap_radius_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ eap_radius_t public;
+
+ /**
+ * ID of the server
+ */
+ identification_t *server;
+
+ /**
+ * ID of the peer
+ */
+ identification_t *peer;
+
+ /**
+ * EAP method type we are proxying
+ */
+ eap_type_t type;
+
+ /**
+ * EAP vendor, if any
+ */
+ u_int32_t vendor;
+
+ /**
+ * EAP MSK, if method established one
+ */
+ chunk_t msk;
+
+ /**
+ * RADIUS client instance
+ */
+ radius_client_t *client;
+};
+
+/**
+ * Add EAP-Identity to RADIUS message
+ */
+static void add_eap_identity(private_eap_radius_t *this,
+ radius_message_t *request)
+{
+ struct {
+ /** EAP code (REQUEST/RESPONSE) */
+ u_int8_t code;
+ /** unique message identifier */
+ u_int8_t identifier;
+ /** length of whole message */
+ u_int16_t length;
+ /** EAP type */
+ u_int8_t type;
+ /** identity data */
+ u_int8_t data[];
+ } __attribute__((__packed__)) *hdr;
+ chunk_t id;
+ size_t len;
+
+ id = this->peer->get_encoding(this->peer);
+ len = sizeof(*hdr) + id.len;
+
+ hdr = alloca(len);
+ hdr->code = EAP_RESPONSE;
+ hdr->identifier = 0;
+ hdr->length = htons(len);
+ hdr->type = EAP_IDENTITY;
+ memcpy(hdr->data, id.ptr, id.len);
+
+ request->add(request, RAT_EAP_MESSAGE, chunk_create((u_char*)hdr, len));
+}
+
+/**
+ * Copy EAP-Message attribute from RADIUS message to an new EAP payload
+ */
+static bool radius2ike(private_eap_radius_t *this,
+ radius_message_t *msg, eap_payload_t **out)
+{
+ enumerator_t *enumerator;
+ eap_payload_t *payload;
+ chunk_t data;
+ int type;
+
+ enumerator = msg->create_enumerator(msg);
+ while (enumerator->enumerate(enumerator, &type, &data))
+ {
+ if (type == RAT_EAP_MESSAGE)
+ {
+ *out = payload = eap_payload_create_data(data);
+ /* apply EAP method selected by RADIUS server */
+ this->type = payload->get_type(payload, &this->vendor);
+ enumerator->destroy(enumerator);
+ return TRUE;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return FALSE;
+}
+
+/**
+ * Implementation of eap_method_t.initiate
+ */
+static status_t initiate(private_eap_radius_t *this, eap_payload_t **out)
+{
+ radius_message_t *request, *response;
+ status_t status = FAILED;
+
+ request = radius_message_create_request();
+ request->add(request, RAT_USER_NAME, this->peer->get_encoding(this->peer));
+ add_eap_identity(this, request);
+ response = this->client->request(this->client, request);
+ if (response)
+ {
+ if (radius2ike(this, response, out))
+ {
+ status = NEED_MORE;
+ }
+ response->destroy(response);
+ }
+ request->destroy(request);
+ return status;
+}
+
+/**
+ * Implementation of eap_method_t.process
+ */
+static status_t process(private_eap_radius_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ radius_message_t *request, *response;
+ status_t status = FAILED;
+
+ request = radius_message_create_request();
+ request->add(request, RAT_USER_NAME, this->peer->get_encoding(this->peer));
+ request->add(request, RAT_EAP_MESSAGE, in->get_data(in));
+
+ response = this->client->request(this->client, request);
+ if (response)
+ {
+ switch (response->get_code(response))
+ {
+ case RMC_ACCESS_CHALLENGE:
+ if (radius2ike(this, response, out))
+ {
+ status = NEED_MORE;
+ break;
+ }
+ status = FAILED;
+ break;
+ case RMC_ACCESS_ACCEPT:
+ this->msk = this->client->decrypt_msk(this->client,
+ response, request);
+ status = SUCCESS;
+ break;
+ case RMC_ACCESS_REJECT:
+ default:
+ DBG1(DBG_CFG, "received %N from RADIUS server",
+ radius_message_code_names, response->get_code(response));
+ status = FAILED;
+ break;
+ }
+ response->destroy(response);
+ }
+ request->destroy(request);
+ return status;
+}
+
+/**
+ * Implementation of eap_method_t.get_type.
+ */
+static eap_type_t get_type(private_eap_radius_t *this, u_int32_t *vendor)
+{
+ *vendor = this->vendor;
+ return this->type;
+}
+
+/**
+ * Implementation of eap_method_t.get_msk.
+ */
+static status_t get_msk(private_eap_radius_t *this, chunk_t *msk)
+{
+ if (this->msk.ptr)
+ {
+ *msk = this->msk;
+ return SUCCESS;
+ }
+ return FAILED;
+}
+
+/**
+ * Implementation of eap_method_t.is_mutual.
+ */
+static bool is_mutual(private_eap_radius_t *this)
+{
+ switch (this->type)
+ {
+ case EAP_AKA:
+ case EAP_SIM:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/**
+ * Implementation of eap_method_t.destroy.
+ */
+static void destroy(private_eap_radius_t *this)
+{
+ this->peer->destroy(this->peer);
+ this->server->destroy(this->server);
+ this->client->destroy(this->client);
+ chunk_clear(&this->msk);
+ free(this);
+}
+
+/**
+ * Generic constructor
+ */
+eap_radius_t *eap_radius_create(identification_t *server, identification_t *peer)
+{
+ private_eap_radius_t *this = malloc_thing(private_eap_radius_t);
+
+ this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
+ this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
+ this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
+ this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
+ this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
+ this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
+
+ this->client = radius_client_create();
+ if (!this->client)
+ {
+ free(this);
+ return NULL;
+ }
+ this->peer = peer->clone(peer);
+ this->server = server->clone(server);
+ /* initially EAP_RADIUS, but is set to the method selected by RADIUS */
+ this->type = EAP_RADIUS;
+ this->vendor = 0;
+ this->msk = chunk_empty;
+
+ return &this->public;
+}
+
diff --git a/src/charon/plugins/eap_radius/eap_radius.h b/src/charon/plugins/eap_radius/eap_radius.h
new file mode 100644
index 000000000..5af72eb2e
--- /dev/null
+++ b/src/charon/plugins/eap_radius/eap_radius.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup eap_radius_i eap_radius
+ * @{ @ingroup eap_radius
+ */
+
+#ifndef EAP_RADIUS_H_
+#define EAP_RADIUS_H_
+
+typedef struct eap_radius_t eap_radius_t;
+
+#include <sa/authenticators/eap/eap_method.h>
+
+/**
+ * Implementation of the eap_method_t interface using a RADIUS server.
+ */
+struct eap_radius_t {
+
+ /**
+ * Implemented eap_method_t interface.
+ */
+ eap_method_t eap_method_interface;
+};
+
+/**
+ * Create a EAP RADIUS proxy.
+ *
+ * @param server ID of the EAP server
+ * @param peer ID of the EAP client
+ * @return eap_radius_t object
+ */
+eap_radius_t *eap_radius_create(identification_t *server, identification_t *peer);
+
+#endif /* EAP_RADIUS_H_ @}*/
diff --git a/src/charon/plugins/eap_radius/eap_radius_plugin.c b/src/charon/plugins/eap_radius/eap_radius_plugin.c
new file mode 100644
index 000000000..a429859a7
--- /dev/null
+++ b/src/charon/plugins/eap_radius/eap_radius_plugin.c
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#include "eap_radius_plugin.h"
+
+#include "eap_radius.h"
+#include "radius_client.h"
+
+#include <daemon.h>
+
+/**
+ * Implementation of plugin_t.destroy
+ */
+static void destroy(eap_radius_plugin_t *this)
+{
+ charon->eap->remove_method(charon->eap, (eap_constructor_t)eap_radius_create);
+ radius_client_cleanup();
+ free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *plugin_create()
+{
+ eap_radius_plugin_t *this;
+
+ if (!radius_client_init())
+ {
+ DBG1(DBG_CFG, "RADIUS plugin initialization failed");
+ return NULL;
+ }
+
+ this = malloc_thing(eap_radius_plugin_t);
+ this->plugin.destroy = (void(*)(plugin_t*))destroy;
+
+ charon->eap->add_method(charon->eap, EAP_RADIUS, 0,
+ EAP_SERVER, (eap_constructor_t)eap_radius_create);
+
+ return &this->plugin;
+}
+
diff --git a/src/charon/plugins/eap_radius/eap_radius_plugin.h b/src/charon/plugins/eap_radius/eap_radius_plugin.h
new file mode 100644
index 000000000..2c3978f42
--- /dev/null
+++ b/src/charon/plugins/eap_radius/eap_radius_plugin.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup eap_radius eap_radius
+ * @ingroup cplugins
+ *
+ * @defgroup eap_radius_plugin eap_radius_plugin
+ * @{ @ingroup eap_radius
+ */
+
+#ifndef EAP_RADIUS_PLUGIN_H_
+#define EAP_RADIUS_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct eap_radius_plugin_t eap_radius_plugin_t;
+
+/**
+ * EAP RADIUS proxy plugin.
+ *
+ * This plugin provides not a single EAP method, but a proxy to forwared
+ * EAP packets to a RADIUS server. It only provides server implementations.
+ */
+struct eap_radius_plugin_t {
+
+ /**
+ * implements plugin interface
+ */
+ plugin_t plugin;
+};
+
+/**
+ * Create a eap_radius_plugin instance.
+ */
+plugin_t *plugin_create();
+
+#endif /* EAP_RADIUS_PLUGIN_H_ @}*/
diff --git a/src/charon/plugins/eap_radius/radius_client.c b/src/charon/plugins/eap_radius/radius_client.c
new file mode 100644
index 000000000..7c718e8ff
--- /dev/null
+++ b/src/charon/plugins/eap_radius/radius_client.c
@@ -0,0 +1,493 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#include "radius_client.h"
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <daemon.h>
+#include <utils/host.h>
+#include <utils/linked_list.h>
+#include <utils/mutex.h>
+
+/**
+ * Default RADIUS server port, when not configured
+ */
+#define RADIUS_PORT 1812
+
+/**
+ * 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_client_t private_radius_client_t;
+
+typedef struct entry_t entry_t;
+
+/**
+ * A socket pool entry.
+ */
+struct entry_t {
+ /** socket file descriptor */
+ int fd;
+ /** 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;
+};
+
+/**
+ * Private data of an radius_client_t object.
+ */
+struct private_radius_client_t {
+
+ /**
+ * Public radius_client_t interface.
+ */
+ radius_client_t public;
+
+ /**
+ * The clients socket from the pool
+ */
+ entry_t *socket;
+
+ /**
+ * RADIUS servers State attribute
+ */
+ chunk_t state;
+};
+
+/**
+ * Global list of radius sockets, contains entry_t's
+ */
+static linked_list_t *sockets;
+
+/**
+ * mutex to lock sockets list
+ */
+static mutex_t *mutex;
+
+/**
+ * condvar to wait for sockets
+ */
+static condvar_t *condvar;
+
+/**
+ * RADIUS secret
+ */
+static chunk_t secret;
+
+/**
+ * NAS-Identifier
+ */
+static chunk_t nas_identifier;
+
+/**
+ * Clean up socket list
+ */
+void radius_client_cleanup()
+{
+ entry_t *entry;
+
+ mutex->destroy(mutex);
+ condvar->destroy(condvar);
+ while (sockets->remove_last(sockets, (void**)&entry) == SUCCESS)
+ {
+ entry->rng->destroy(entry->rng);
+ entry->hasher->destroy(entry->hasher);
+ entry->signer->destroy(entry->signer);
+ close(entry->fd);
+ free(entry);
+ }
+ sockets->destroy(sockets);
+}
+
+/**
+ * Initialize the socket list
+ */
+bool radius_client_init()
+{
+ int i, count, fd;
+ u_int16_t port;
+ entry_t *entry;
+ host_t *host;
+ char *server;
+
+ nas_identifier.ptr = lib->settings->get_str(lib->settings,
+ "charon.plugins.eap_radius.nas_identifier", "strongSwan");
+ nas_identifier.len = strlen(nas_identifier.ptr);
+
+ secret.ptr = lib->settings->get_str(lib->settings,
+ "charon.plugins.eap_radius.secret", NULL);
+ if (!secret.ptr)
+ {
+ DBG1(DBG_CFG, "no RADUIS secret defined");
+ return FALSE;
+ }
+ secret.len = strlen(secret.ptr);
+ server = lib->settings->get_str(lib->settings,
+ "charon.plugins.eap_radius.server", NULL);
+ if (!server)
+ {
+ DBG1(DBG_CFG, "no RADUIS server defined");
+ return FALSE;
+ }
+ port = lib->settings->get_int(lib->settings,
+ "charon.plugins.eap_radius.port", RADIUS_PORT);
+ host = host_create_from_dns(server, 0, port);
+ if (!host)
+ {
+ return FALSE;
+ }
+ count = lib->settings->get_int(lib->settings,
+ "charon.plugins.eap_radius.sockets", 5);
+
+ sockets = linked_list_create();
+ mutex = mutex_create(MUTEX_DEFAULT);
+ condvar = condvar_create(CONDVAR_DEFAULT);
+ for (i = 0; i < count; i++)
+ {
+ fd = socket(host->get_family(host), SOCK_DGRAM, IPPROTO_UDP);
+ if (fd < 0)
+ {
+ DBG1(DBG_CFG, "opening RADIUS socket failed");
+ host->destroy(host);
+ radius_client_cleanup();
+ return FALSE;
+ }
+ if (connect(fd, host->get_sockaddr(host),
+ *host->get_sockaddr_len(host)) < 0)
+ {
+ DBG1(DBG_CFG, "connecting RADIUS socket failed");
+ host->destroy(host);
+ radius_client_cleanup();
+ return FALSE;
+ }
+ entry = malloc_thing(entry_t);
+ entry->fd = fd;
+ /* we use per-socket crypto elements: this reduces overhead, but
+ * is still thread-save. */
+ entry->hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
+ entry->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128);
+ entry->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+ if (!entry->hasher || !entry->signer || !entry->rng)
+ {
+ DBG1(DBG_CFG, "RADIUS initialization failed, HMAC/MD5/RNG required");
+ DESTROY_IF(entry->hasher);
+ DESTROY_IF(entry->signer);
+ DESTROY_IF(entry->rng);
+ free(entry);
+ host->destroy(host);
+ radius_client_cleanup();
+ return FALSE;
+ }
+ entry->signer->set_key(entry->signer, secret);
+ /* we use a random identifier, helps if we restart often (testing) */
+ entry->identifier = random();
+ sockets->insert_last(sockets, entry);
+ }
+ host->destroy(host);
+ return TRUE;
+}
+
+/**
+ * Get a socket from the pool, block if none available
+ */
+static entry_t* get_socket()
+{
+ entry_t *entry;
+
+ mutex->lock(mutex);
+ while (sockets->remove_first(sockets, (void**)&entry) != SUCCESS)
+ {
+ condvar->wait(condvar, mutex);
+ }
+ mutex->unlock(mutex);
+ return entry;
+}
+
+/**
+ * Release a socket to the pool
+ */
+static void put_socket(entry_t *entry)
+{
+ mutex->lock(mutex);
+ sockets->insert_last(sockets, entry);
+ mutex->unlock(mutex);
+ condvar->signal(condvar);
+}
+
+/**
+ * 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);
+}
+
+/**
+ * Implementation of radius_client_t.request
+ */
+static radius_message_t* request(private_radius_client_t *this,
+ radius_message_t *req)
+{
+ char virtual[] = {0x00,0x00,0x00,0x05};
+ radius_message_t *response;
+ chunk_t data;
+ fd_set fds;
+ int i;
+
+ /* set Message Identifier */
+ req->set_identifier(req, this->socket->identifier++);
+ /* 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, nas_identifier);
+ /* add State attribute, if server sent one */
+ if (this->state.ptr)
+ {
+ req->add(req, RAT_STATE, this->state);
+ }
+ /* sign the request */
+ req->sign(req, this->socket->rng, this->socket->signer);
+
+ data = req->get_encoding(req);
+ FD_ZERO(&fds);
+ FD_SET(this->socket->fd, &fds);
+ /* timeout after 2, 3, 4, 5 seconds */
+ for (i = 2; i <= 5; i++)
+ {
+ bool retransmit = FALSE;
+ struct timeval tv;
+ char buf[1024];
+ int res, retry = 0;
+
+ if (send(this->socket->fd, data.ptr, data.len, 0) != data.len)
+ {
+ DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno));
+ return NULL;
+ }
+ while (TRUE)
+ {
+ tv.tv_sec = i;
+ tv.tv_usec = 0;
+
+ res = select(this->socket->fd + 1, &fds, NULL, NULL, &tv);
+ 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->socket->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, req->get_authenticator(req),
+ secret, this->socket->hasher, this->socket->signer))
+ {
+ save_state(this, response);
+ return response;
+ }
+ response->destroy(response);
+ }
+ /* might be a maliciously injected packet, read onother one.
+ * Limit the number of retries, an attacker could us trick into
+ * a loop otherwise. */
+ if (retry++ > 5)
+ {
+ break;
+ }
+ }
+ 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_client_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->socket->hasher->get_hash(this->socket->hasher, secret, NULL);
+ this->socket->hasher->get_hash(this->socket->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));
+}
+
+/**
+ * Implementation of radius_client_t.decrypt_msk
+ */
+static chunk_t decrypt_msk(private_radius_client_t *this,
+ radius_message_t *response, radius_message_t *request)
+{
+ 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;
+}
+
+/**
+ * Implementation of radius_client_t.destroy.
+ */
+static void destroy(private_radius_client_t *this)
+{
+ put_socket(this->socket);
+ free(this->state.ptr);
+ free(this);
+}
+
+/**
+ * See header
+ */
+radius_client_t *radius_client_create()
+{
+ private_radius_client_t *this = malloc_thing(private_radius_client_t);
+
+ this->public.request = (radius_message_t*(*)(radius_client_t*, radius_message_t *msg))request;
+ this->public.decrypt_msk = (chunk_t(*)(radius_client_t*, radius_message_t *, radius_message_t *))decrypt_msk;
+ this->public.destroy = (void(*)(radius_client_t*))destroy;
+
+ this->socket = get_socket();
+ this->state = chunk_empty;
+
+ return &this->public;
+}
+
diff --git a/src/charon/plugins/eap_radius/radius_client.h b/src/charon/plugins/eap_radius/radius_client.h
new file mode 100644
index 000000000..5b8a88b12
--- /dev/null
+++ b/src/charon/plugins/eap_radius/radius_client.h
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+/**
+ * @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. All instances share a fixed size pool of sockets. During construction,
+ * one sockets gets reserved for the client, so each client uses a different
+ * but fixed port during its lifetime. On destruction, the socket is restored
+ * to the pool.
+ */
+struct radius_client_t {
+
+ /**
+ * Send a RADIUS request and wait for the response.
+ *
+ * The client fills in RADIUS Message identifier, NAS-Identifier,
+ * NAS-Port-Type, 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 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);
+
+ /**
+ * Decrypt the MSK encoded in a messages MS-MPPE-Send/Recv-Key.
+ *
+ * @param response RADIUS response message containing attributes
+ * @param request associated RADIUS request message
+ * @return allocated MSK, empty chunk if none found
+ */
+ chunk_t (*decrypt_msk)(radius_client_t *this, radius_message_t *response,
+ radius_message_t *request);
+
+ /**
+ * Destroy the client, release the socket.
+ */
+ void (*destroy)(radius_client_t *this);
+};
+
+/**
+ * Create a RADIUS client, acquire a socket.
+ *
+ * This call might block if the socket pool is empty.
+ *
+ * @return radius_client_t object
+ */
+radius_client_t *radius_client_create();
+
+/**
+ * Initialize the socket pool.
+ *
+ * @return TRUE if initialization successful
+ */
+bool radius_client_init();
+
+/**
+ * Cleanup the socket pool.
+ */
+void radius_client_cleanup();
+
+#endif /* radius_client_H_ @}*/
diff --git a/src/charon/plugins/eap_radius/radius_message.c b/src/charon/plugins/eap_radius/radius_message.c
new file mode 100644
index 000000000..7279c7957
--- /dev/null
+++ b/src/charon/plugins/eap_radius/radius_message.c
@@ -0,0 +1,465 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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;
+
+
+/**
+ * Implementation of attribute_enumerator_t.enumerate
+ */
+static bool attribute_enumerate(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;
+}
+
+/**
+ * Implementation of radius_message_t.create_enumerator
+ */
+static enumerator_t* create_enumerator(private_radius_message_t *this)
+{
+ attribute_enumerator_t *e;
+
+ if (ntohs(this->msg->length) < sizeof(rmsg_t) + sizeof(rattr_t))
+ {
+ return enumerator_create_empty();
+ }
+
+ e = malloc_thing(attribute_enumerator_t);
+ e->public.enumerate = (void*)attribute_enumerate;
+ e->public.destroy = (void*)free;
+ e->next = (rattr_t*)this->msg->attributes;
+ e->left = ntohs(this->msg->length) - sizeof(rmsg_t);
+ return &e->public;
+}
+
+/**
+ * Implementation of radius_message_t.add
+ */
+static void add(private_radius_message_t *this, radius_attribute_type_t type,
+ chunk_t data)
+{
+ rattr_t *attribute;
+
+ 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);
+}
+
+/**
+ * Implementation of radius_message_t.sign
+ */
+static void sign(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);
+}
+
+/**
+ * Implementation of radius_message_t.verify
+ */
+static bool verify(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;
+
+ /* 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))))
+ { /* good, restore Authenticators */
+ memcpy(this->msg->authenticator, res_auth, HASH_SIZE_MD5);
+ memcpy(data.ptr, buf, data.len);
+ enumerator->destroy(enumerator);
+ return TRUE;
+ }
+ else
+ {
+ DBG1(DBG_CFG, "RADIUS Message-Authenticator verification failed");
+ enumerator->destroy(enumerator);
+ return FALSE;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ DBG1(DBG_CFG, "RADIUS Message-Authenticator attribute missing");
+ return FALSE;
+}
+
+/**
+ * Implementation of radius_message_t.get_code
+ */
+static radius_message_code_t get_code(private_radius_message_t *this)
+{
+ return this->msg->code;
+}
+
+/**
+ * Implementation of radius_message_t.get_identifier
+ */
+static u_int8_t get_identifier(private_radius_message_t *this)
+{
+ return this->msg->identifier;
+}
+
+/**
+ * Implementation of radius_message_t.set_identifier
+ */
+static void set_identifier(private_radius_message_t *this, u_int8_t identifier)
+{
+ this->msg->identifier = identifier;
+}
+
+/**
+ * Implementation of radius_message_t.get_authenticator
+ */
+static u_int8_t* get_authenticator(private_radius_message_t *this)
+{
+ return this->msg->authenticator;
+}
+
+
+/**
+ * Implementation of radius_message_t.get_encoding
+ */
+static chunk_t get_encoding(private_radius_message_t *this)
+{
+ return chunk_create((u_char*)this->msg, ntohs(this->msg->length));
+}
+
+/**
+ * Implementation of radius_message_t.destroy.
+ */
+static void destroy(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 = malloc_thing(private_radius_message_t);
+
+ this->public.create_enumerator = (enumerator_t*(*)(radius_message_t*))create_enumerator;
+ this->public.add = (void(*)(radius_message_t*, radius_attribute_type_t,chunk_t))add;
+ this->public.get_code = (radius_message_code_t(*)(radius_message_t*))get_code;
+ this->public.get_identifier = (u_int8_t(*)(radius_message_t*))get_identifier;
+ this->public.set_identifier = (void(*)(radius_message_t*, u_int8_t identifier))set_identifier;
+ this->public.get_authenticator = (u_int8_t*(*)(radius_message_t*))get_authenticator;
+ this->public.get_encoding = (chunk_t(*)(radius_message_t*))get_encoding;
+ this->public.sign = (void(*)(radius_message_t*, rng_t *rng, signer_t *signer))sign;
+ this->public.verify = (bool(*)(radius_message_t*, u_int8_t *req_auth, chunk_t secret, hasher_t *hasher, signer_t *signer))verify;
+ this->public.destroy = (void(*)(radius_message_t*))destroy;
+
+ return this;
+}
+
+/**
+ * See header
+ */
+radius_message_t *radius_message_create_request()
+{
+ private_radius_message_t *this = radius_message_create();
+
+ this->msg = malloc_thing(rmsg_t);
+ this->msg->code = RMC_ACCESS_REQUEST;
+ this->msg->identifier = 0;
+ this->msg->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/charon/plugins/eap_radius/radius_message.h b/src/charon/plugins/eap_radius/radius_message.h
new file mode 100644
index 000000000..5cf49dceb
--- /dev/null
+++ b/src/charon/plugins/eap_radius/radius_message.h
@@ -0,0 +1,279 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+/**
+ * @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).
+ *
+ * @param identifier RADIUS message identifier
+ * @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/charon/sa/authenticators/eap/eap_method.c b/src/charon/sa/authenticators/eap/eap_method.c
index d6c2b867c..d27a8ac2f 100644
--- a/src/charon/sa/authenticators/eap/eap_method.c
+++ b/src/charon/sa/authenticators/eap/eap_method.c
@@ -30,7 +30,8 @@ ENUM_NEXT(eap_type_names, EAP_AKA, EAP_AKA, EAP_SIM,
"EAP_AKA");
ENUM_NEXT(eap_type_names, EAP_MSCHAPV2, EAP_MSCHAPV2, EAP_AKA,
"EAP_MSCHAPV2");
-ENUM_NEXT(eap_type_names, EAP_EXPANDED, EAP_EXPERIMENTAL, EAP_MSCHAPV2,
+ENUM_NEXT(eap_type_names, EAP_RADIUS, EAP_EXPERIMENTAL, EAP_MSCHAPV2,
+ "EAP_RADIUS",
"EAP_EXPANDED",
"EAP_EXPERIMENTAL");
ENUM_END(eap_type_names, EAP_EXPERIMENTAL);
diff --git a/src/charon/sa/authenticators/eap/eap_method.h b/src/charon/sa/authenticators/eap/eap_method.h
index edc939deb..c3f7c4862 100644
--- a/src/charon/sa/authenticators/eap/eap_method.h
+++ b/src/charon/sa/authenticators/eap/eap_method.h
@@ -57,6 +57,8 @@ enum eap_type_t {
EAP_SIM = 18,
EAP_AKA = 23,
EAP_MSCHAPV2 = 26,
+ /** not a method, but an implementation providing different methods */
+ EAP_RADIUS = 253,
EAP_EXPANDED = 254,
EAP_EXPERIMENTAL = 255,
};
diff --git a/src/charon/sa/authenticators/eap_authenticator.c b/src/charon/sa/authenticators/eap_authenticator.c
index 0bda6f242..3ef84c81b 100644
--- a/src/charon/sa/authenticators/eap_authenticator.c
+++ b/src/charon/sa/authenticators/eap_authenticator.c
@@ -141,7 +141,8 @@ static identification_t *get_peer_id(private_eap_authenticator_t *this)
{
config = this->ike_sa->get_peer_cfg(this->ike_sa);
auth = config->get_auth(config);
- if (!auth->get_item(auth, AUTHN_EAP_IDENTITY, (void**)&id))
+ if (!auth->get_item(auth, AUTHN_EAP_IDENTITY, (void**)&id) ||
+ id->get_type(id) == ID_ANY)
{
if (this->role == EAP_PEER)
{
@@ -252,7 +253,7 @@ static status_t initiate(private_eap_authenticator_t *this, eap_type_t type,
if (this->method->initiate(this->method, out) != NEED_MORE)
{
DBG1(DBG_IKE, "failed to initiate EAP exchange, sending %N",
- eap_type_names, type, eap_code_names, EAP_FAILURE);
+ eap_code_names, EAP_FAILURE);
*out = eap_payload_create_code(EAP_FAILURE, 0);
return FAILED;
}
diff --git a/src/libstrongswan/crypto/signers/signer.h b/src/libstrongswan/crypto/signers/signer.h
index cfc6652bc..bafcd6b8c 100644
--- a/src/libstrongswan/crypto/signers/signer.h
+++ b/src/libstrongswan/crypto/signers/signer.h
@@ -51,6 +51,8 @@ enum integrity_algorithm_t {
AUTH_HMAC_SHA2_512_256 = 14,
/** Implemented via hmac_signer_t */
AUTH_HMAC_SHA1_128 = 1025,
+ /** Implemented via hmac_signer_t */
+ AUTH_HMAC_MD5_128 = 1026,
};
/**
diff --git a/src/libstrongswan/plugins/hmac/hmac_plugin.c b/src/libstrongswan/plugins/hmac/hmac_plugin.c
index bf9626bd6..3da635859 100644
--- a/src/libstrongswan/plugins/hmac/hmac_plugin.c
+++ b/src/libstrongswan/plugins/hmac/hmac_plugin.c
@@ -74,6 +74,8 @@ plugin_t *plugin_create()
(signer_constructor_t)hmac_signer_create);
lib->crypto->add_signer(lib->crypto, AUTH_HMAC_MD5_96,
(signer_constructor_t)hmac_signer_create);
+ lib->crypto->add_signer(lib->crypto, AUTH_HMAC_MD5_128,
+ (signer_constructor_t)hmac_signer_create);
lib->crypto->add_signer(lib->crypto, AUTH_HMAC_SHA2_384_192,
(signer_constructor_t)hmac_signer_create);
lib->crypto->add_signer(lib->crypto, AUTH_HMAC_SHA2_512_256,
diff --git a/src/libstrongswan/plugins/hmac/hmac_signer.c b/src/libstrongswan/plugins/hmac/hmac_signer.c
index 1b6f80d7b..7678a3eeb 100644
--- a/src/libstrongswan/plugins/hmac/hmac_signer.c
+++ b/src/libstrongswan/plugins/hmac/hmac_signer.c
@@ -159,6 +159,10 @@ hmac_signer_t *hmac_signer_create(integrity_algorithm_t algo)
hash = HASH_MD5;
trunc = 12;
break;
+ case AUTH_HMAC_MD5_128:
+ hash = HASH_MD5;
+ trunc = 16;
+ break;
case AUTH_HMAC_SHA2_256_128:
hash = HASH_SHA256;
trunc = 16;
diff --git a/src/starter/confread.c b/src/starter/confread.c
index 8bfc6fe68..56d6dd146 100644
--- a/src/starter/confread.c
+++ b/src/starter/confread.c
@@ -625,6 +625,10 @@ load_conn(starter_conn_t *conn, kw_list_t *kw, starter_config_t *cfg)
{
conn->eap_type = 26;
}
+ else if (streq(kw->value, "radius"))
+ { /* pseudo-type */
+ conn->eap_type = 253;
+ }
else
{
conn->eap_type = atoi(kw->value);
diff --git a/src/starter/ipsec.conf.5 b/src/starter/ipsec.conf.5
index 84c503cea..bb6bb45a9 100644
--- a/src/starter/ipsec.conf.5
+++ b/src/starter/ipsec.conf.5
@@ -369,6 +369,10 @@ in the form
(e.g.
.B eap=7-12345
) can be used to specify vendor specific EAP types.
+
+To forward EAP authentication to a RADIUS server using the EAP-RADIUS plugin,
+set
+.B eap=radius
.TP
.B eap_identity
defines the identity the client uses to reply to a EAP Identity request.