reintegrated eap-radius branch into trunk
authorMartin Willi <martin@strongswan.org>
Tue, 24 Mar 2009 10:24:58 +0000 (10:24 -0000)
committerMartin Willi <martin@strongswan.org>
Tue, 24 Mar 2009 10:24:58 +0000 (10:24 -0000)
19 files changed:
configure.in
src/charon/Makefile.am
src/charon/plugins/eap_radius/Makefile.am [new file with mode: 0644]
src/charon/plugins/eap_radius/eap_radius.c [new file with mode: 0644]
src/charon/plugins/eap_radius/eap_radius.h [new file with mode: 0644]
src/charon/plugins/eap_radius/eap_radius_plugin.c [new file with mode: 0644]
src/charon/plugins/eap_radius/eap_radius_plugin.h [new file with mode: 0644]
src/charon/plugins/eap_radius/radius_client.c [new file with mode: 0644]
src/charon/plugins/eap_radius/radius_client.h [new file with mode: 0644]
src/charon/plugins/eap_radius/radius_message.c [new file with mode: 0644]
src/charon/plugins/eap_radius/radius_message.h [new file with mode: 0644]
src/charon/sa/authenticators/eap/eap_method.c
src/charon/sa/authenticators/eap/eap_method.h
src/charon/sa/authenticators/eap_authenticator.c
src/libstrongswan/crypto/signers/signer.h
src/libstrongswan/plugins/hmac/hmac_plugin.c
src/libstrongswan/plugins/hmac/hmac_signer.c
src/starter/confread.c
src/starter/ipsec.conf.5

index fc21494..5d9c256 100644 (file)
@@ -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
index 3393b45..9da2b23 100644 (file)
@@ -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 (file)
index 0000000..f7de2f1
--- /dev/null
@@ -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 (file)
index 0000000..1a02c5a
--- /dev/null
@@ -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 (file)
index 0000000..5af72eb
--- /dev/null
@@ -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 (file)
index 0000000..a429859
--- /dev/null
@@ -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 (file)
index 0000000..2c3978f
--- /dev/null
@@ -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 (file)
index 0000000..7c718e8
--- /dev/null
@@ -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 (file)
index 0000000..5b8a88b
--- /dev/null
@@ -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 (file)
index 0000000..7279c79
--- /dev/null
@@ -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 (file)
index 0000000..5cf49dc
--- /dev/null
@@ -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_ @}*/
index d6c2b86..d27a8ac 100644 (file)
@@ -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);
index edc939d..c3f7c48 100644 (file)
@@ -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,
 };
index 0bda6f2..3ef84c8 100644 (file)
@@ -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;  
        }
index cfc6652..bafcd6b 100644 (file)
@@ -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,
 };
 
 /**
index bf9626b..3da6358 100644 (file)
@@ -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, 
index 1b6f80d..7678a3e 100644 (file)
@@ -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;
index 8bfc6fe..56d6dd1 100644 (file)
@@ -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);
index 84c503c..bb6bb45 100644 (file)
@@ -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.