Moved generic RADIUS protocol support to a dedicated libradius
authorMartin Willi <martin@revosec.ch>
Mon, 27 Feb 2012 14:18:58 +0000 (15:18 +0100)
committerMartin Willi <martin@revosec.ch>
Mon, 5 Mar 2012 17:08:04 +0000 (18:08 +0100)
29 files changed:
Doxyfile.in
configure.in
src/Makefile.am
src/checksum/Makefile.am
src/libcharon/Makefile.am
src/libcharon/plugins/eap_radius/Makefile.am
src/libcharon/plugins/eap_radius/eap_radius.c
src/libcharon/plugins/eap_radius/eap_radius_accounting.c
src/libcharon/plugins/eap_radius/eap_radius_dae.c
src/libcharon/plugins/eap_radius/eap_radius_forward.h
src/libcharon/plugins/eap_radius/eap_radius_plugin.c
src/libcharon/plugins/eap_radius/eap_radius_plugin.h
src/libcharon/plugins/eap_radius/radius_client.c [deleted file]
src/libcharon/plugins/eap_radius/radius_client.h [deleted file]
src/libcharon/plugins/eap_radius/radius_message.c [deleted file]
src/libcharon/plugins/eap_radius/radius_message.h [deleted file]
src/libcharon/plugins/eap_radius/radius_server.c [deleted file]
src/libcharon/plugins/eap_radius/radius_server.h [deleted file]
src/libcharon/plugins/eap_radius/radius_socket.c [deleted file]
src/libcharon/plugins/eap_radius/radius_socket.h [deleted file]
src/libradius/Makefile.am [new file with mode: 0644]
src/libradius/radius_client.c [new file with mode: 0644]
src/libradius/radius_client.h [new file with mode: 0644]
src/libradius/radius_message.c [new file with mode: 0644]
src/libradius/radius_message.h [new file with mode: 0644]
src/libradius/radius_server.c [new file with mode: 0644]
src/libradius/radius_server.h [new file with mode: 0644]
src/libradius/radius_socket.c [new file with mode: 0644]
src/libradius/radius_socket.h [new file with mode: 0644]

index 524d639..7fb5161 100644 (file)
@@ -532,6 +532,7 @@ INPUT                  = @SRC_DIR@/src/libstrongswan \
                          @SRC_DIR@/src/libcharon \
                          @SRC_DIR@/src/libsimaka \
                          @SRC_DIR@/src/libtls \
                          @SRC_DIR@/src/libcharon \
                          @SRC_DIR@/src/libsimaka \
                          @SRC_DIR@/src/libtls \
+                         @SRC_DIR@/src/libradius \
                          @SRC_DIR@/src/libtnccs \
                          @SRC_DIR@/src/libtncif \
                          @SRC_DIR@/src/libfast \
                          @SRC_DIR@/src/libtnccs \
                          @SRC_DIR@/src/libtncif \
                          @SRC_DIR@/src/libfast \
index 2b13a94..a463c48 100644 (file)
@@ -252,6 +252,10 @@ if test x$eap_tls = xtrue -o x$eap_ttls = xtrue -o x$eap_peap = xtrue; then
        tls=true;
 fi
 
        tls=true;
 fi
 
+if test x$eap_radius = xtrue; then
+       radius=true;
+fi
+
 if test x$tnc_imc = xtrue -o x$tnc_imv = xtrue -o x$tnccs_11 = xtrue -o x$tnccs_11 = xtrue -o x$tnccs_dynamic = xtrue -o x$eap_tnc = xtrue; then
        tnc_tnccs=true;
 fi
 if test x$tnc_imc = xtrue -o x$tnc_imv = xtrue -o x$tnccs_11 = xtrue -o x$tnccs_11 = xtrue -o x$tnccs_dynamic = xtrue -o x$eap_tnc = xtrue; then
        tnc_tnccs=true;
 fi
@@ -1052,6 +1056,7 @@ AM_CONDITIONAL(USE_LIBCAP, test x$capabilities = xlibcap)
 AM_CONDITIONAL(USE_VSTR, test x$vstr = xtrue)
 AM_CONDITIONAL(USE_SIMAKA, test x$simaka = xtrue)
 AM_CONDITIONAL(USE_TLS, test x$tls = xtrue)
 AM_CONDITIONAL(USE_VSTR, test x$vstr = xtrue)
 AM_CONDITIONAL(USE_SIMAKA, test x$simaka = xtrue)
 AM_CONDITIONAL(USE_TLS, test x$tls = xtrue)
+AM_CONDITIONAL(USE_RADIUS, test x$radius = xtrue)
 AM_CONDITIONAL(USE_IMCV, test x$imcv = xtrue)
 AM_CONDITIONAL(USE_PTS, test x$pts = xtrue)
 AM_CONDITIONAL(MONOLITHIC, test x$monolithic = xtrue)
 AM_CONDITIONAL(USE_IMCV, test x$imcv = xtrue)
 AM_CONDITIONAL(USE_PTS, test x$pts = xtrue)
 AM_CONDITIONAL(MONOLITHIC, test x$monolithic = xtrue)
@@ -1130,6 +1135,7 @@ AC_OUTPUT(
        src/libfreeswan/Makefile
        src/libsimaka/Makefile
        src/libtls/Makefile
        src/libfreeswan/Makefile
        src/libsimaka/Makefile
        src/libtls/Makefile
+       src/libradius/Makefile
        src/libtncif/Makefile
        src/libtnccs/Makefile
        src/libpts/Makefile
        src/libtncif/Makefile
        src/libtnccs/Makefile
        src/libpts/Makefile
index 041bd48..1440de2 100644 (file)
@@ -16,6 +16,10 @@ if USE_TLS
   SUBDIRS += libtls
 endif
 
   SUBDIRS += libtls
 endif
 
+if USE_RADIUS
+  SUBDIRS += libradius
+endif
+
 if USE_LIBTNCIF
   SUBDIRS += libtncif
 endif
 if USE_LIBTNCIF
   SUBDIRS += libtncif
 endif
index a55891a..58292a4 100644 (file)
@@ -45,6 +45,11 @@ if USE_TLS
   libs += $(DESTDIR)$(ipseclibdir)/libtls.so
 endif
 
   libs += $(DESTDIR)$(ipseclibdir)/libtls.so
 endif
 
+if USE_RADIUS
+  deps += $(top_builddir)/src/libradius/libradius.la
+  libs += $(DESTDIR)$(ipseclibdir)/libradius.so
+endif
+
 if USE_LIBTNCCS
   deps += $(top_builddir)/src/libtnccs/libtnccs.la
   libs += $(DESTDIR)$(ipseclibdir)/libtnccs.so
 if USE_LIBTNCCS
   deps += $(top_builddir)/src/libtnccs/libtnccs.la
   libs += $(DESTDIR)$(ipseclibdir)/libtnccs.so
index b50ea25..fba51c6 100644 (file)
@@ -326,6 +326,13 @@ if MONOLITHIC
 endif
 endif
 
 endif
 endif
 
+if USE_RADIUS
+if MONOLITHIC
+  # otherwise this library is linked to eap_radius
+  libcharon_la_LIBADD += $(top_builddir)/src/libradius/libradius.la
+endif
+endif
+
 if USE_TNC_IFMAP
   SUBDIRS += plugins/tnc_ifmap
 if MONOLITHIC
 if USE_TNC_IFMAP
   SUBDIRS += plugins/tnc_ifmap
 if MONOLITHIC
index 96bfa6f..181497a 100644 (file)
@@ -1,12 +1,13 @@
 
 INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra \
 
 INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra \
-       -I$(top_srcdir)/src/libcharon
+       -I$(top_srcdir)/src/libcharon -I$(top_srcdir)/src/libradius
 
 AM_CFLAGS = -rdynamic
 
 if MONOLITHIC
 noinst_LTLIBRARIES = libstrongswan-eap-radius.la
 else
 
 AM_CFLAGS = -rdynamic
 
 if MONOLITHIC
 noinst_LTLIBRARIES = libstrongswan-eap-radius.la
 else
+libstrongswan_eap_radius_la_LIBADD = $(top_builddir)/src/libradius/libradius.la
 plugin_LTLIBRARIES = libstrongswan-eap-radius.la
 endif
 
 plugin_LTLIBRARIES = libstrongswan-eap-radius.la
 endif
 
@@ -15,10 +16,6 @@ libstrongswan_eap_radius_la_SOURCES = \
        eap_radius.h eap_radius.c \
        eap_radius_accounting.h eap_radius_accounting.c \
        eap_radius_dae.h eap_radius_dae.c \
        eap_radius.h eap_radius.c \
        eap_radius_accounting.h eap_radius_accounting.c \
        eap_radius_dae.h eap_radius_dae.c \
-       eap_radius_forward.h eap_radius_forward.c \
-       radius_server.h radius_server.c \
-       radius_socket.h radius_socket.c \
-       radius_client.h radius_client.c \
-       radius_message.h radius_message.c
+       eap_radius_forward.h eap_radius_forward.c
 
 libstrongswan_eap_radius_la_LDFLAGS = -module -avoid-version
 
 libstrongswan_eap_radius_la_LDFLAGS = -module -avoid-version
index f2b47e3..051d46e 100644 (file)
  */
 
 #include "eap_radius.h"
  */
 
 #include "eap_radius.h"
+#include "eap_radius_plugin.h"
 #include "eap_radius_forward.h"
 
 #include "eap_radius_forward.h"
 
-#include "radius_message.h"
-#include "radius_client.h"
+#include <radius_message.h>
+#include <radius_client.h>
 
 #include <daemon.h>
 
 
 #include <daemon.h>
 
@@ -462,7 +463,7 @@ eap_radius_t *eap_radius_create(identification_t *server, identification_t *peer
                                                                "charon.plugins.eap-radius.filter_id", FALSE),
 
        );
                                                                "charon.plugins.eap-radius.filter_id", FALSE),
 
        );
-       this->client = radius_client_create();
+       this->client = eap_radius_create_client();
        if (!this->client)
        {
                free(this);
        if (!this->client)
        {
                free(this);
index 25013f6..45be227 100644 (file)
  */
 
 #include "eap_radius_accounting.h"
  */
 
 #include "eap_radius_accounting.h"
+#include "eap_radius_plugin.h"
 
 #include <time.h>
 
 
 #include <time.h>
 
-#include "radius_message.h"
-#include "radius_client.h"
+#include <radius_message.h>
+#include <radius_client.h>
 #include <daemon.h>
 #include <utils/hashtable.h>
 #include <threading/mutex.h>
 #include <daemon.h>
 #include <utils/hashtable.h>
 #include <threading/mutex.h>
@@ -125,7 +126,7 @@ static bool send_message(private_eap_radius_accounting_t *this,
        radius_client_t *client;
        bool ack = FALSE;
 
        radius_client_t *client;
        bool ack = FALSE;
 
-       client = radius_client_create();
+       client = eap_radius_create_client();
        if (client)
        {
                response = client->request(client, request);
        if (client)
        {
                response = client->request(client, request);
index 3ea5873..1cc19af 100644 (file)
@@ -15,7 +15,7 @@
 
 #include "eap_radius_dae.h"
 
 
 #include "eap_radius_dae.h"
 
-#include "radius_message.h"
+#include <radius_message.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
index e1a8c41..2c1dbf7 100644 (file)
 
 /**
  * @defgroup eap_radius_forward eap_radius_forward
 
 /**
  * @defgroup eap_radius_forward eap_radius_forward
- * @{ @ingroup
+ * @{ @ingroup eap_radius
  */
 
 #ifndef EAP_RADIUS_FORWARD_H_
 #define EAP_RADIUS_FORWARD_H_
 
  */
 
 #ifndef EAP_RADIUS_FORWARD_H_
 #define EAP_RADIUS_FORWARD_H_
 
-#include "radius_message.h"
+#include <radius_message.h>
 
 #include <bus/listeners/listener.h>
 
 
 #include <bus/listeners/listener.h>
 
index e544aaf..0caecc1 100644 (file)
@@ -19,8 +19,9 @@
 #include "eap_radius_accounting.h"
 #include "eap_radius_dae.h"
 #include "eap_radius_forward.h"
 #include "eap_radius_accounting.h"
 #include "eap_radius_dae.h"
 #include "eap_radius_forward.h"
-#include "radius_client.h"
-#include "radius_server.h"
+
+#include <radius_client.h>
+#include <radius_server.h>
 
 #include <daemon.h>
 #include <threading/rwlock.h>
 
 #include <daemon.h>
 #include <threading/rwlock.h>
@@ -261,15 +262,43 @@ plugin_t *eap_radius_plugin_create()
 /**
  * See header
  */
 /**
  * See header
  */
-enumerator_t *eap_radius_create_server_enumerator()
+radius_client_t *eap_radius_create_client()
 {
        if (instance)
        {
 {
        if (instance)
        {
+               enumerator_t *enumerator;
+               radius_server_t *server, *selected = NULL;
+               int current, best = -1;
+
                instance->lock->read_lock(instance->lock);
                instance->lock->read_lock(instance->lock);
-               return enumerator_create_cleaner(
-                                       instance->servers->create_enumerator(instance->servers),
-                                       (void*)instance->lock->unlock, instance->lock);
+               enumerator = instance->servers->create_enumerator(instance->servers);
+               while (enumerator->enumerate(enumerator, &server))
+               {
+                       current = server->get_preference(server);
+                       if (current > best ||
+                               /* for two with equal preference, 50-50 chance */
+                               (current == best && random() % 2 == 0))
+                       {
+                               DBG2(DBG_CFG, "RADIUS server '%s' is candidate: %d",
+                                        server->get_name(server), current);
+                               best = current;
+                               DESTROY_IF(selected);
+                               selected = server->get_ref(server);
+                       }
+                       else
+                       {
+                               DBG2(DBG_CFG, "RADIUS server '%s' skipped: %d",
+                                        server->get_name(server), current);
+                       }
+               }
+               enumerator->destroy(enumerator);
+               instance->lock->unlock(instance->lock);
+
+               if (selected)
+               {
+                       return radius_client_create(selected);
+               }
        }
        }
-       return enumerator_create_empty();
+       return NULL;
 }
 
 }
 
index cb72436..1570bd5 100644 (file)
@@ -25,7 +25,8 @@
 #define EAP_RADIUS_PLUGIN_H_
 
 #include <plugins/plugin.h>
 #define EAP_RADIUS_PLUGIN_H_
 
 #include <plugins/plugin.h>
-#include <utils/enumerator.h>
+
+#include <radius_client.h>
 
 typedef struct eap_radius_plugin_t eap_radius_plugin_t;
 
 
 typedef struct eap_radius_plugin_t eap_radius_plugin_t;
 
@@ -44,10 +45,10 @@ struct eap_radius_plugin_t {
 };
 
 /**
 };
 
 /**
- * Create an enumerator over all loaded RADIUS servers.
+ * Get a RADIUS client instance to connect to servers.
  *
  *
- * @return                     enumerator over radius_server_t
+ * @return                     RADIUS client
  */
  */
-enumerator_t *eap_radius_create_server_enumerator();
+radius_client_t *eap_radius_create_client();
 
 #endif /** EAP_RADIUS_PLUGIN_H_ @}*/
 
 #endif /** EAP_RADIUS_PLUGIN_H_ @}*/
diff --git a/src/libcharon/plugins/eap_radius/radius_client.c b/src/libcharon/plugins/eap_radius/radius_client.c
deleted file mode 100644 (file)
index 1a67096..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2009 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#include "radius_client.h"
-
-#include "eap_radius_plugin.h"
-#include "radius_server.h"
-
-#include <unistd.h>
-#include <errno.h>
-
-#include <debug.h>
-#include <utils/host.h>
-#include <utils/linked_list.h>
-#include <threading/condvar.h>
-#include <threading/mutex.h>
-
-typedef struct private_radius_client_t private_radius_client_t;
-
-/**
- * Private data of an radius_client_t object.
- */
-struct private_radius_client_t {
-
-       /**
-        * Public radius_client_t interface.
-        */
-       radius_client_t public;
-
-       /**
-        * Selected RADIUS server
-        */
-       radius_server_t *server;
-
-       /**
-        * RADIUS servers State attribute
-        */
-       chunk_t state;
-
-       /**
-        * EAP MSK, from MPPE keys
-        */
-       chunk_t msk;
-};
-
-/**
- * Save the state attribute to include in further request
- */
-static void save_state(private_radius_client_t *this, radius_message_t *msg)
-{
-       enumerator_t *enumerator;
-       int type;
-       chunk_t data;
-
-       enumerator = msg->create_enumerator(msg);
-       while (enumerator->enumerate(enumerator, &type, &data))
-       {
-               if (type == RAT_STATE)
-               {
-                       free(this->state.ptr);
-                       this->state = chunk_clone(data);
-                       enumerator->destroy(enumerator);
-                       return;
-               }
-       }
-       enumerator->destroy(enumerator);
-       /* no state attribute found, remove state */
-       chunk_free(&this->state);
-}
-
-METHOD(radius_client_t, request, radius_message_t*,
-       private_radius_client_t *this, radius_message_t *req)
-{
-       char virtual[] = {0x00,0x00,0x00,0x05};
-       radius_socket_t *socket;
-       radius_message_t *res;
-
-       /* we add the "Virtual" NAS-Port-Type, as we SHOULD include one */
-       req->add(req, RAT_NAS_PORT_TYPE, chunk_create(virtual, sizeof(virtual)));
-       /* add our NAS-Identifier */
-       req->add(req, RAT_NAS_IDENTIFIER,
-                        this->server->get_nas_identifier(this->server));
-       /* add State attribute, if server sent one */
-       if (this->state.ptr)
-       {
-               req->add(req, RAT_STATE, this->state);
-       }
-       socket = this->server->get_socket(this->server);
-       DBG1(DBG_CFG, "sending RADIUS %N to server '%s'", radius_message_code_names,
-                req->get_code(req), this->server->get_name(this->server));
-       res = socket->request(socket, req);
-       if (res)
-       {
-               DBG1(DBG_CFG, "received RADIUS %N from server '%s'",
-                        radius_message_code_names, res->get_code(res),
-                        this->server->get_name(this->server));
-               save_state(this, res);
-               if (res->get_code(res) == RMC_ACCESS_ACCEPT)
-               {
-                       chunk_clear(&this->msk);
-                       this->msk = socket->decrypt_msk(socket, req, res);
-               }
-               this->server->put_socket(this->server, socket, TRUE);
-               return res;
-       }
-       this->server->put_socket(this->server, socket, FALSE);
-       return NULL;
-}
-
-METHOD(radius_client_t, get_msk, chunk_t,
-       private_radius_client_t *this)
-{
-       return this->msk;
-}
-
-METHOD(radius_client_t, destroy, void,
-       private_radius_client_t *this)
-{
-       this->server->destroy(this->server);
-       chunk_clear(&this->msk);
-       free(this->state.ptr);
-       free(this);
-}
-
-/**
- * See header
- */
-radius_client_t *radius_client_create()
-{
-       private_radius_client_t *this;
-       enumerator_t *enumerator;
-       radius_server_t *server;
-       int current, best = -1;
-
-       INIT(this,
-               .public = {
-                       .request = _request,
-                       .get_msk = _get_msk,
-                       .destroy = _destroy,
-               },
-       );
-
-       enumerator = eap_radius_create_server_enumerator();
-       while (enumerator->enumerate(enumerator, &server))
-       {
-               current = server->get_preference(server);
-               if (current > best ||
-                       /* for two with equal preference, 50-50 chance */
-                       (current == best && random() % 2 == 0))
-               {
-                       DBG2(DBG_CFG, "RADIUS server '%s' is candidate: %d",
-                                server->get_name(server), current);
-                       best = current;
-                       DESTROY_IF(this->server);
-                       this->server = server->get_ref(server);
-               }
-               else
-               {
-                       DBG2(DBG_CFG, "RADIUS server '%s' skipped: %d",
-                                server->get_name(server), current);
-               }
-       }
-       enumerator->destroy(enumerator);
-
-       if (!this->server)
-       {
-               free(this);
-               return NULL;
-       }
-
-       return &this->public;
-}
-
diff --git a/src/libcharon/plugins/eap_radius/radius_client.h b/src/libcharon/plugins/eap_radius/radius_client.h
deleted file mode 100644 (file)
index e4f3a72..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2009 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-/**
- * @defgroup radius_client radius_client
- * @{ @ingroup eap_radius
- */
-
-#ifndef RADIUS_CLIENT_H_
-#define RADIUS_CLIENT_H_
-
-#include "radius_message.h"
-
-typedef struct radius_client_t radius_client_t;
-
-/**
- * RADIUS client functionality.
- *
- * To communicate with a RADIUS server, create a client and send messages over
- * it. The client allocates a socket from the best RADIUS server abailable.
- */
-struct radius_client_t {
-
-       /**
-        * Send a RADIUS request and wait for the response.
-        *
-        * The client fills in NAS-Identifier nad NAS-Port-Type
-        *
-        * @param msg                   RADIUS request message to send
-        * @return                              response, NULL if timed out/verification failed
-        */
-       radius_message_t* (*request)(radius_client_t *this, radius_message_t *msg);
-
-       /**
-        * Get the EAP MSK after successful RADIUS authentication.
-        *
-        * @return                              MSK, allocated
-        */
-       chunk_t (*get_msk)(radius_client_t *this);
-
-       /**
-        * Destroy the client, release the socket.
-        */
-       void (*destroy)(radius_client_t *this);
-};
-
-/**
- * Create a RADIUS client.
- *
- * @return                     radius_client_t object
- */
-radius_client_t *radius_client_create();
-
-#endif /** RADIUS_CLIENT_H_ @}*/
diff --git a/src/libcharon/plugins/eap_radius/radius_message.c b/src/libcharon/plugins/eap_radius/radius_message.c
deleted file mode 100644 (file)
index ad88754..0000000
+++ /dev/null
@@ -1,491 +0,0 @@
-/*
- * Copyright (C) 2009 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#include "radius_message.h"
-
-#include <debug.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_NEXT(radius_message_code_names, RMC_DISCONNECT_REQUEST, RMC_COA_NAK, RMC_ACCESS_CHALLENGE,
-       "Disconnect-Request",
-       "Disconnect-ACK",
-       "Disconnect-NAK",
-       "CoA-Request",
-       "CoA-ACK",
-       "CoA-NAK");
-ENUM_END(radius_message_code_names, RMC_COA_NAK);
-
-ENUM(radius_attribute_type_names, RAT_USER_NAME, RAT_MIP6_HOME_LINK_PREFIX,
-       "User-Name",
-       "User-Password",
-       "CHAP-Password",
-       "NAS-IP-Address",
-       "NAS-Port",
-       "Service-Type",
-       "Framed-Protocol",
-       "Framed-IP-Address",
-       "Framed-IP-Netmask",
-       "Framed-Routing",
-       "Filter-Id",
-       "Framed-MTU",
-       "Framed-Compression",
-       "Login-IP-Host",
-       "Login-Service",
-       "Login-TCP-Port",
-       "Unassigned",
-       "Reply-Message",
-       "Callback-Number",
-       "Callback-Id",
-       "Unassigned",
-       "Framed-Route",
-       "Framed-IPX-Network",
-       "State",
-       "Class",
-       "Vendor-Specific",
-       "Session-Timeout",
-       "Idle-Timeout",
-       "Termination-Action",
-       "Called-Station-Id",
-       "Calling-Station-Id",
-       "NAS-Identifier",
-       "Proxy-State",
-       "Login-LAT-Service",
-       "Login-LAT-Node",
-       "Login-LAT-Group",
-       "Framed-AppleTalk-Link",
-       "Framed-AppleTalk-Network",
-       "Framed-AppleTalk-Zone",
-       "Acct-Status-Type",
-       "Acct-Delay-Time",
-       "Acct-Input-Octets",
-       "Acct-Output-Octets",
-       "Acct-Session-Id",
-       "Acct-Authentic",
-       "Acct-Session-Time",
-       "Acct-Input-Packets",
-       "Acct-Output-Packets",
-       "Acct-Terminate-Cause",
-       "Acct-Multi-Session-Id",
-       "Acct-Link-Count",
-       "Acct-Input-Gigawords",
-       "Acct-Output-Gigawords",
-       "Unassigned",
-       "Event-Timestamp",
-       "Egress-VLANID",
-       "Ingress-Filters",
-       "Egress-VLAN-Name",
-       "User-Priority-Table",
-       "CHAP-Challenge",
-       "NAS-Port-Type",
-       "Port-Limit",
-       "Login-LAT-Port",
-       "Tunnel-Type",
-       "Tunnel-Medium-Type",
-       "Tunnel-Client-Endpoint",
-       "Tunnel-Server-Endpoint",
-       "Acct-Tunnel-Connection",
-       "Tunnel-Password",
-       "ARAP-Password",
-       "ARAP-Features",
-       "ARAP-Zone-Access",
-       "ARAP-Security",
-       "ARAP-Security-Data",
-       "Password-Retry",
-       "Prompt",
-       "Connect-Info",
-       "Configuration-Token",
-       "EAP-Message",
-       "Message-Authenticator",
-       "Tunnel-Private-Group-ID",
-       "Tunnel-Assignment-ID",
-       "Tunnel-Preference",
-       "ARAP-Challenge-Response",
-       "Acct-Interim-Interval",
-       "Acct-Tunnel-Packets-Lost",
-       "NAS-Port-Id",
-       "Framed-Pool",
-       "CUI",
-       "Tunnel-Client-Auth-ID",
-       "Tunnel-Server-Auth-ID",
-       "NAS-Filter-Rule",
-       "Unassigned",
-       "Originating-Line-Info",
-       "NAS-IPv6-Address",
-       "Framed-Interface-Id",
-       "Framed-IPv6-Prefix",
-       "Login-IPv6-Host",
-       "Framed-IPv6-Route",
-       "Framed-IPv6-Pool",
-       "Error-Cause",
-       "EAP-Key-Name",
-       "Digest-Response",
-       "Digest-Realm",
-       "Digest-Nonce",
-       "Digest-Response-Auth",
-       "Digest-Nextnonce",
-       "Digest-Method",
-       "Digest-URI",
-       "Digest-Qop",
-       "Digest-Algorithm",
-       "Digest-Entity-Body-Hash",
-       "Digest-CNonce",
-       "Digest-Nonce-Count",
-       "Digest-Username",
-       "Digest-Opaque",
-       "Digest-Auth-Param",
-       "Digest-AKA-Auts",
-       "Digest-Domain",
-       "Digest-Stale",
-       "Digest-HA1",
-       "SIP-AOR",
-       "Delegated-IPv6-Prefix",
-       "MIP6-Feature-Vector",
-       "MIP6-Home-Link-Prefix");
-
-/**
- * Attribute enumerator implementation
- */
-typedef struct {
-       /** implements enumerator interface */
-       enumerator_t public;
-       /** currently pointing attribute */
-       rattr_t *next;
-       /** bytes left */
-       int left;
-} attribute_enumerator_t;
-
-METHOD(enumerator_t, attribute_enumerate, bool,
-       attribute_enumerator_t *this, int *type, chunk_t *data)
-{
-       if (this->left == 0)
-       {
-               return FALSE;
-       }
-       if (this->left < sizeof(rattr_t) ||
-               this->left < this->next->length)
-       {
-               DBG1(DBG_IKE, "RADIUS message truncated");
-               return FALSE;
-       }
-       *type = this->next->type;
-       data->ptr = this->next->value;
-       data->len = this->next->length - sizeof(rattr_t);
-       this->left -= this->next->length;
-       this->next = ((void*)this->next) + this->next->length;
-       return TRUE;
-}
-
-METHOD(radius_message_t, create_enumerator, enumerator_t*,
-       private_radius_message_t *this)
-{
-       attribute_enumerator_t *e;
-
-       if (ntohs(this->msg->length) < sizeof(rmsg_t) + sizeof(rattr_t))
-       {
-               return enumerator_create_empty();
-       }
-       INIT(e,
-               .public = {
-                       .enumerate = (void*)_attribute_enumerate,
-                       .destroy = (void*)free,
-               },
-               .next = (rattr_t*)this->msg->attributes,
-               .left = ntohs(this->msg->length) - sizeof(rmsg_t),
-       );
-       return &e->public;
-}
-
-METHOD(radius_message_t, add, void,
-       private_radius_message_t *this, radius_attribute_type_t type, chunk_t data)
-{
-       rattr_t *attribute;
-
-       data.len = min(data.len, 253);
-       this->msg = realloc(this->msg,
-                                               ntohs(this->msg->length) + sizeof(rattr_t) + data.len);
-       attribute = ((void*)this->msg) + ntohs(this->msg->length);
-       attribute->type = type;
-       attribute->length = data.len + sizeof(rattr_t);
-       memcpy(attribute->value, data.ptr, data.len);
-       this->msg->length = htons(ntohs(this->msg->length) + attribute->length);
-}
-
-METHOD(radius_message_t, sign, void,
-       private_radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
-       hasher_t *hasher, signer_t *signer, rng_t *rng)
-{
-       if (rng == NULL)
-       {
-               chunk_t msg;
-
-               if (req_auth)
-               {
-                       memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5);
-               }
-               else
-               {
-                       memset(this->msg->authenticator, 0, sizeof(this->msg->authenticator));
-               }
-               msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length));
-               hasher->get_hash(hasher, msg, NULL);
-               hasher->get_hash(hasher, secret, this->msg->authenticator);
-       }
-       else
-       {
-               char buf[HASH_SIZE_MD5];
-
-               /* build Request-Authenticator */
-               rng->get_bytes(rng, HASH_SIZE_MD5, this->msg->authenticator);
-
-               /* build Message-Authenticator attribute, using 16 null bytes */
-               memset(buf, 0, sizeof(buf));
-               add(this, RAT_MESSAGE_AUTHENTICATOR, chunk_create(buf, sizeof(buf)));
-               signer->get_signature(signer,
-                               chunk_create((u_char*)this->msg, ntohs(this->msg->length)),
-                               ((u_char*)this->msg) + ntohs(this->msg->length) - HASH_SIZE_MD5);
-       }
-}
-
-METHOD(radius_message_t, verify, bool,
-       private_radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
-       hasher_t *hasher, signer_t *signer)
-{
-       char buf[HASH_SIZE_MD5], res_auth[HASH_SIZE_MD5];
-       enumerator_t *enumerator;
-       int type;
-       chunk_t data, msg;
-       bool has_eap = FALSE, has_auth = FALSE;
-
-       /* replace Response by Request Authenticator for verification */
-       memcpy(res_auth, this->msg->authenticator, HASH_SIZE_MD5);
-       if (req_auth)
-       {
-               memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5);
-       }
-       else
-       {
-               memset(this->msg->authenticator, 0, HASH_SIZE_MD5);
-       }
-       msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length));
-
-       /* verify Response-Authenticator */
-       hasher->get_hash(hasher, msg, NULL);
-       hasher->get_hash(hasher, secret, buf);
-       if (!memeq(buf, res_auth, HASH_SIZE_MD5))
-       {
-               DBG1(DBG_CFG, "RADIUS Response-Authenticator verification failed");
-               return FALSE;
-       }
-
-       /* verify Message-Authenticator attribute */
-       enumerator = create_enumerator(this);
-       while (enumerator->enumerate(enumerator, &type, &data))
-       {
-               if (type == RAT_MESSAGE_AUTHENTICATOR)
-               {
-                       if (data.len != HASH_SIZE_MD5)
-                       {
-                               DBG1(DBG_CFG, "RADIUS Message-Authenticator invalid length");
-                               enumerator->destroy(enumerator);
-                               return FALSE;
-                       }
-                       memcpy(buf, data.ptr, data.len);
-                       memset(data.ptr, 0, data.len);
-                       if (signer->verify_signature(signer, msg,
-                                                                                chunk_create(buf, sizeof(buf))))
-                       {
-                               /* restore Message-Authenticator */
-                               memcpy(data.ptr, buf, data.len);
-                               has_auth = TRUE;
-                               break;
-                       }
-                       else
-                       {
-                               DBG1(DBG_CFG, "RADIUS Message-Authenticator verification failed");
-                               enumerator->destroy(enumerator);
-                               return FALSE;
-                       }
-               }
-               else if (type == RAT_EAP_MESSAGE)
-               {
-                       has_eap = TRUE;
-               }
-       }
-       enumerator->destroy(enumerator);
-       /* restore Response-Authenticator */
-       memcpy(this->msg->authenticator, res_auth, HASH_SIZE_MD5);
-
-       if (has_eap && !has_auth)
-       {       /* Message-Authenticator is required if we have an EAP-Message */
-               DBG1(DBG_CFG, "RADIUS Message-Authenticator attribute missing");
-               return FALSE;
-       }
-       return TRUE;
-}
-
-METHOD(radius_message_t, get_code, radius_message_code_t,
-       private_radius_message_t *this)
-{
-       return this->msg->code;
-}
-
-METHOD(radius_message_t, get_identifier, u_int8_t,
-       private_radius_message_t *this)
-{
-       return this->msg->identifier;
-}
-
-METHOD(radius_message_t, set_identifier, void,
-       private_radius_message_t *this, u_int8_t identifier)
-{
-       this->msg->identifier = identifier;
-}
-
-METHOD(radius_message_t, get_authenticator, u_int8_t*,
-       private_radius_message_t *this)
-{
-       return this->msg->authenticator;
-}
-
-
-METHOD(radius_message_t, get_encoding, chunk_t,
-       private_radius_message_t *this)
-{
-       return chunk_create((u_char*)this->msg, ntohs(this->msg->length));
-}
-
-METHOD(radius_message_t, destroy, void,
-       private_radius_message_t *this)
-{
-       free(this->msg);
-       free(this);
-}
-
-/**
- * Generic constructor
- */
-static private_radius_message_t *radius_message_create_empty()
-{
-       private_radius_message_t *this;
-
-       INIT(this,
-               .public = {
-                       .create_enumerator = _create_enumerator,
-                       .add = _add,
-                       .get_code = _get_code,
-                       .get_identifier = _get_identifier,
-                       .set_identifier = _set_identifier,
-                       .get_authenticator = _get_authenticator,
-                       .get_encoding = _get_encoding,
-                       .sign = _sign,
-                       .verify = _verify,
-                       .destroy = _destroy,
-               },
-       );
-
-       return this;
-}
-
-/**
- * See header
- */
-radius_message_t *radius_message_create(radius_message_code_t code)
-{
-       private_radius_message_t *this = radius_message_create_empty();
-
-       INIT(this->msg,
-               .code = code,
-               .identifier = 0,
-               .length = htons(sizeof(rmsg_t)),
-       );
-
-       return &this->public;
-}
-
-/**
- * See header
- */
-radius_message_t *radius_message_parse(chunk_t data)
-{
-       private_radius_message_t *this = radius_message_create_empty();
-
-       this->msg = malloc(data.len);
-       memcpy(this->msg, data.ptr, data.len);
-       if (data.len < sizeof(rmsg_t) ||
-               ntohs(this->msg->length) != data.len)
-       {
-               DBG1(DBG_IKE, "RADIUS message has invalid length");
-               destroy(this);
-               return NULL;
-       }
-       return &this->public;
-}
-
diff --git a/src/libcharon/plugins/eap_radius/radius_message.h b/src/libcharon/plugins/eap_radius/radius_message.h
deleted file mode 100644 (file)
index 3557145..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (C) 2009 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-/**
- * @defgroup radius_message radius_message
- * @{ @ingroup eap_radius
- */
-
-#ifndef RADIUS_MESSAGE_H_
-#define RADIUS_MESSAGE_H_
-
-#include <library.h>
-
-typedef struct radius_message_t radius_message_t;
-typedef enum radius_message_code_t radius_message_code_t;
-typedef enum radius_attribute_type_t radius_attribute_type_t;
-
-/**
- * RADIUS Message Codes.
- */
-enum radius_message_code_t {
-       RMC_ACCESS_REQUEST = 1,
-       RMC_ACCESS_ACCEPT = 2,
-       RMC_ACCESS_REJECT = 3,
-       RMC_ACCOUNTING_REQUEST = 4,
-       RMC_ACCOUNTING_RESPONSE = 5,
-       RMC_ACCESS_CHALLENGE = 11,
-       RMC_DISCONNECT_REQUEST = 40,
-       RMC_DISCONNECT_ACK = 41,
-       RMC_DISCONNECT_NAK = 42,
-       RMC_COA_REQUEST = 43,
-       RMC_COA_ACK = 44,
-       RMC_COA_NAK = 45,
-};
-
-/**
- * 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 req_auth              16 byte Authenticator of request, or NULL
-        * @param secret                shared RADIUS secret
-        * @param signer                HMAC-MD5 signer with secret set
-        * @param hasher                MD5 hasher
-        * @param rng                   RNG to create Message-Authenticator, NULL to omit
-        */
-       void (*sign)(radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
-                                hasher_t *hasher, signer_t *signer, rng_t *rng);
-
-       /**
-        * Verify the integrity of a received RADIUS message.
-        *
-        * @param req_auth              16 byte Authenticator of request, or NULL
-        * @param secret                shared RADIUS secret
-        * @param signer                HMAC-MD5 signer with secret set
-        * @param hasher                MD5 hasher
-        */
-       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 message.
- *
- * @param code                 request type
- * @return                             radius_message_t object
- */
-radius_message_t *radius_message_create(radius_message_code_t code);
-
-/**
- * Parse and verify a recevied RADIUS message.
- *
- * @param data                 received message data
- * @return                             radius_message_t object, NULL if length invalid
- */
-radius_message_t *radius_message_parse(chunk_t data);
-
-#endif /** RADIUS_MESSAGE_H_ @}*/
diff --git a/src/libcharon/plugins/eap_radius/radius_server.c b/src/libcharon/plugins/eap_radius/radius_server.c
deleted file mode 100644 (file)
index 282f508..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright (C) 2010 Martin Willi
- * Copyright (C) 2010 revosec AG
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#include "radius_server.h"
-
-#include <threading/mutex.h>
-#include <threading/condvar.h>
-#include <utils/linked_list.h>
-
-typedef struct private_radius_server_t private_radius_server_t;
-
-/**
- * Private data of an radius_server_t object.
- */
-struct private_radius_server_t {
-
-       /**
-        * Public radius_server_t interface.
-        */
-       radius_server_t public;
-
-       /**
-        * list of radius sockets, as radius_socket_t
-        */
-       linked_list_t *sockets;
-
-       /**
-        * Total number of sockets, in list + currently in use
-        */
-       int socket_count;
-
-       /**
-        * mutex to lock sockets list
-        */
-       mutex_t *mutex;
-
-       /**
-        * condvar to wait for sockets
-        */
-       condvar_t *condvar;
-
-       /**
-        * Server name
-        */
-       char *name;
-
-       /**
-        * NAS-Identifier
-        */
-       chunk_t nas_identifier;
-
-       /**
-        * Preference boost for this server
-        */
-       int preference;
-
-       /**
-        * Is the server currently reachable
-        */
-       bool reachable;
-
-       /**
-        * Retry counter for unreachable servers
-        */
-       int retry;
-
-       /**
-        * reference count
-        */
-       refcount_t ref;
-};
-
-METHOD(radius_server_t, get_socket, radius_socket_t*,
-       private_radius_server_t *this)
-{
-       radius_socket_t *skt;
-
-       this->mutex->lock(this->mutex);
-       while (this->sockets->remove_first(this->sockets, (void**)&skt) != SUCCESS)
-       {
-               this->condvar->wait(this->condvar, this->mutex);
-       }
-       this->mutex->unlock(this->mutex);
-       return skt;
-}
-
-METHOD(radius_server_t, put_socket, void,
-       private_radius_server_t *this, radius_socket_t *skt, bool result)
-{
-       this->mutex->lock(this->mutex);
-       this->sockets->insert_last(this->sockets, skt);
-       this->mutex->unlock(this->mutex);
-       this->condvar->signal(this->condvar);
-       this->reachable = result;
-}
-
-METHOD(radius_server_t, get_nas_identifier, chunk_t,
-       private_radius_server_t *this)
-{
-       return this->nas_identifier;
-}
-
-METHOD(radius_server_t, get_preference, int,
-       private_radius_server_t *this)
-{
-       int pref;
-
-       if (this->socket_count == 0)
-       {       /* don't have sockets, huh? */
-               return -1;
-       }
-       /* calculate preference between 0-100 + boost */
-       pref = this->preference;
-       pref += this->sockets->get_count(this->sockets) * 100 / this->socket_count;
-       if (this->reachable)
-       {       /* reachable server get a boost: pref = 110-210 + boost */
-               return pref + 110;
-       }
-       /* Not reachable. Increase preference randomly to let it retry from
-        * time to time, especially if other servers have high load. */
-       this->retry++;
-       if (this->retry % 128 == 0)
-       {       /* every 64th request gets 210, same as unloaded reachable */
-               return pref + 110;
-       }
-       if (this->retry % 32 == 0)
-       {       /* every 32th request gets 190, wins against average loaded */
-               return pref + 90;
-       }
-       if (this->retry % 8 == 0)
-       {       /* every 8th request gets 110, same as server under load */
-               return pref + 10;
-       }
-       /* other get ~100, less than fully loaded */
-       return pref;
-}
-
-METHOD(radius_server_t, get_name, char*,
-       private_radius_server_t *this)
-{
-       return this->name;
-}
-
-METHOD(radius_server_t, get_ref, radius_server_t*,
-       private_radius_server_t *this)
-{
-       ref_get(&this->ref);
-       return &this->public;
-}
-
-
-METHOD(radius_server_t, destroy, void,
-       private_radius_server_t *this)
-{
-       if (ref_put(&this->ref))
-       {
-               this->mutex->destroy(this->mutex);
-               this->condvar->destroy(this->condvar);
-               this->sockets->destroy_offset(this->sockets,
-                                                                         offsetof(radius_socket_t, destroy));
-               free(this);
-       }
-}
-
-/**
- * See header
- */
-radius_server_t *radius_server_create(char *name, char *address,
-                                                                         u_int16_t auth_port, u_int16_t acct_port,
-                                                                         char *nas_identifier, char *secret,
-                                                                         int sockets, int preference)
-{
-       private_radius_server_t *this;
-       radius_socket_t *socket;
-
-       INIT(this,
-               .public = {
-                       .get_socket = _get_socket,
-                       .put_socket = _put_socket,
-                       .get_nas_identifier = _get_nas_identifier,
-                       .get_preference = _get_preference,
-                       .get_name = _get_name,
-                       .get_ref = _get_ref,
-                       .destroy = _destroy,
-               },
-               .reachable = TRUE,
-               .nas_identifier = chunk_create(nas_identifier, strlen(nas_identifier)),
-               .socket_count = sockets,
-               .sockets = linked_list_create(),
-               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
-               .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
-               .name = name,
-               .preference = preference,
-               .ref = 1,
-       );
-
-       while (sockets--)
-       {
-               socket = radius_socket_create(address, auth_port, acct_port,
-                                                                         chunk_create(secret, strlen(secret)));
-               if (!socket)
-               {
-                       destroy(this);
-                       return NULL;
-               }
-               this->sockets->insert_last(this->sockets, socket);
-       }
-       return &this->public;
-}
diff --git a/src/libcharon/plugins/eap_radius/radius_server.h b/src/libcharon/plugins/eap_radius/radius_server.h
deleted file mode 100644 (file)
index 93b0e5d..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2010 Martin Willi
- * Copyright (C) 2010 revosec AG
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-/**
- * @defgroup radius_server radius_server
- * @{ @ingroup eap_radius
- */
-
-#ifndef RADIUS_SERVER_H_
-#define RADIUS_SERVER_H_
-
-typedef struct radius_server_t radius_server_t;
-
-#include "radius_socket.h"
-
-/**
- * RADIUS server configuration.
- */
-struct radius_server_t {
-
-       /**
-        * Get a RADIUS socket from the pool to communicate with this server.
-        *
-        * @return                      RADIUS socket
-        */
-       radius_socket_t* (*get_socket)(radius_server_t *this);
-
-       /**
-        * Release a socket to the pool after use.
-        *
-        * @param skt           RADIUS socket to release
-        * @param result        result of the socket use, TRUE for success
-        */
-       void (*put_socket)(radius_server_t *this, radius_socket_t *skt, bool result);
-
-       /**
-        * Get the NAS-Identifier to use with this server.
-        *
-        * @return                      NAS-Identifier, internal data
-        */
-       chunk_t (*get_nas_identifier)(radius_server_t *this);
-
-       /**
-        * Get the preference of this server.
-        *
-        * Based on the available sockets and the server reachability a preference
-        * value is calculated: better servers return a higher value.
-        */
-       int (*get_preference)(radius_server_t *this);
-
-       /**
-        * Get the name of the RADIUS server.
-        *
-        * @return                      server name
-        */
-       char* (*get_name)(radius_server_t *this);
-
-       /**
-        * Increase reference count of this server.
-        *
-        * @return                      this
-        */
-       radius_server_t* (*get_ref)(radius_server_t *this);
-
-       /**
-        * Destroy a radius_server_t.
-        */
-       void (*destroy)(radius_server_t *this);
-};
-
-/**
- * Create a radius_server instance.
- *
- * @param name                         server name
- * @param address                      server address
- * @param auth_port                    server port for authentication
- * @param acct_port                    server port for accounting
- * @param nas_identifier       NAS-Identifier to use with this server
- * @param secret                       secret to use with this server
- * @param sockets                      number of sockets to create in pool
- * @param preference           preference boost for this server
- */
-radius_server_t *radius_server_create(char *name, char *address,
-                                                                         u_int16_t auth_port, u_int16_t acct_port,
-                                                                         char *nas_identifier, char *secret,
-                                                                         int sockets, int preference);
-
-#endif /** RADIUS_SERVER_H_ @}*/
diff --git a/src/libcharon/plugins/eap_radius/radius_socket.c b/src/libcharon/plugins/eap_radius/radius_socket.c
deleted file mode 100644 (file)
index 875bd61..0000000
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * Copyright (C) 2010 Martin Willi
- * Copyright (C) 2010 revosec AG
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#include "radius_socket.h"
-
-#include <errno.h>
-#include <unistd.h>
-
-#include <debug.h>
-
-/**
- * Vendor-Id of Microsoft specific attributes
- */
-#define VENDOR_ID_MICROSOFT 311
-
-/**
- * Microsoft specific vendor attributes
- */
-#define MS_MPPE_SEND_KEY 16
-#define MS_MPPE_RECV_KEY 17
-
-typedef struct private_radius_socket_t private_radius_socket_t;
-
-/**
- * Private data of an radius_socket_t object.
- */
-struct private_radius_socket_t {
-
-       /**
-        * Public radius_socket_t interface.
-        */
-       radius_socket_t public;
-
-       /**
-        * Server port for authentication
-        */
-       u_int16_t auth_port;
-
-       /**
-        * socket file descriptor for authentication
-        */
-       int auth_fd;
-
-       /**
-        * Server port for accounting
-        */
-       u_int16_t acct_port;
-
-       /**
-        * socket file descriptor for accounting
-        */
-       int acct_fd;
-
-       /**
-        * Server address
-        */
-       char *address;
-
-       /**
-        * current RADIUS identifier
-        */
-       u_int8_t identifier;
-
-       /**
-        * hasher to use for response verification
-        */
-       hasher_t *hasher;
-
-       /**
-        * HMAC-MD5 signer to build Message-Authenticator attribute
-        */
-       signer_t *signer;
-
-       /**
-        * random number generator for RADIUS request authenticator
-        */
-       rng_t *rng;
-
-       /**
-        * RADIUS secret
-        */
-       chunk_t secret;
-};
-
-/**
- * Check or establish RADIUS connection
- */
-static bool check_connection(private_radius_socket_t *this,
-                                                        int *fd, u_int16_t port)
-{
-       if (*fd == -1)
-       {
-               host_t *server;
-
-               server = host_create_from_dns(this->address, AF_UNSPEC, port);
-               if (!server)
-               {
-                       DBG1(DBG_CFG, "resolving RADIUS server address '%s' failed",
-                                this->address);
-                       return FALSE;
-               }
-               *fd = socket(server->get_family(server), SOCK_DGRAM, IPPROTO_UDP);
-               if (*fd == -1)
-               {
-                       DBG1(DBG_CFG, "opening RADIUS socket for %#H failed: %s",
-                                server, strerror(errno));
-                       server->destroy(server);
-                       return FALSE;
-               }
-               if (connect(*fd, server->get_sockaddr(server),
-                                       *server->get_sockaddr_len(server)) < 0)
-               {
-                       DBG1(DBG_CFG, "connecting RADIUS socket to %#H failed: %s",
-                                server, strerror(errno));
-                       server->destroy(server);
-                       close(*fd);
-                       *fd = -1;
-                       return FALSE;
-               }
-               server->destroy(server);
-       }
-       return TRUE;
-}
-
-METHOD(radius_socket_t, request, radius_message_t*,
-       private_radius_socket_t *this, radius_message_t *request)
-{
-       chunk_t data;
-       int i, *fd;
-       u_int16_t port;
-       rng_t *rng = NULL;
-
-       if (request->get_code(request) == RMC_ACCOUNTING_REQUEST)
-       {
-               fd = &this->acct_fd;
-               port = this->acct_port;
-       }
-       else
-       {
-               fd = &this->auth_fd;
-               port = this->auth_port;
-               rng = this->rng;
-       }
-
-       /* set Message Identifier */
-       request->set_identifier(request, this->identifier++);
-       /* sign the request */
-       request->sign(request, NULL, this->secret, this->hasher, this->signer, rng);
-
-       if (!check_connection(this, fd, port))
-       {
-               return NULL;
-       }
-
-       data = request->get_encoding(request);
-       /* timeout after 2, 3, 4, 5 seconds */
-       for (i = 2; i <= 5; i++)
-       {
-               radius_message_t *response;
-               bool retransmit = FALSE;
-               struct timeval tv;
-               char buf[4096];
-               fd_set fds;
-               int res;
-
-               if (send(*fd, data.ptr, data.len, 0) != data.len)
-               {
-                       DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno));
-                       return NULL;
-               }
-               tv.tv_sec = i;
-               tv.tv_usec = 0;
-
-               while (TRUE)
-               {
-                       FD_ZERO(&fds);
-                       FD_SET(*fd, &fds);
-                       res = select((*fd) + 1, &fds, NULL, NULL, &tv);
-                       /* TODO: updated tv to time not waited. Linux does this for us. */
-                       if (res < 0)
-                       {       /* failed */
-                               DBG1(DBG_CFG, "waiting for RADIUS message failed: %s",
-                                        strerror(errno));
-                               break;
-                       }
-                       if (res == 0)
-                       {       /* timeout */
-                               DBG1(DBG_CFG, "retransmitting RADIUS message");
-                               retransmit = TRUE;
-                               break;
-                       }
-                       res = recv(*fd, buf, sizeof(buf), MSG_DONTWAIT);
-                       if (res <= 0)
-                       {
-                               DBG1(DBG_CFG, "receiving RADIUS message failed: %s",
-                                        strerror(errno));
-                               break;
-                       }
-                       response = radius_message_parse(chunk_create(buf, res));
-                       if (response)
-                       {
-                               if (response->verify(response,
-                                                       request->get_authenticator(request), this->secret,
-                                                       this->hasher, this->signer))
-                               {
-                                       return response;
-                               }
-                               response->destroy(response);
-                       }
-                       DBG1(DBG_CFG, "received invalid RADIUS message, ignored");
-               }
-               if (!retransmit)
-               {
-                       break;
-               }
-       }
-       DBG1(DBG_CFG, "RADIUS server is not responding");
-       return NULL;
-}
-
-/**
- * Decrypt a MS-MPPE-Send/Recv-Key
- */
-static chunk_t decrypt_mppe_key(private_radius_socket_t *this, u_int16_t salt,
-                                                               chunk_t C, radius_message_t *request)
-{
-       chunk_t A, R, P, seed;
-       u_char *c, *p;
-
-       /**
-        * From RFC2548 (encryption):
-        * b(1) = MD5(S + R + A)    c(1) = p(1) xor b(1)   C = c(1)
-        * b(2) = MD5(S + c(1))     c(2) = p(2) xor b(2)   C = C + c(2)
-        *      . . .
-        * b(i) = MD5(S + c(i-1))   c(i) = p(i) xor b(i)   C = C + c(i)
-        */
-
-       if (C.len % HASH_SIZE_MD5 || C.len < HASH_SIZE_MD5)
-       {
-               return chunk_empty;
-       }
-
-       A = chunk_create((u_char*)&salt, sizeof(salt));
-       R = chunk_create(request->get_authenticator(request), HASH_SIZE_MD5);
-       P = chunk_alloca(C.len);
-       p = P.ptr;
-       c = C.ptr;
-
-       seed = chunk_cata("cc", R, A);
-
-       while (c < C.ptr + C.len)
-       {
-               /* b(i) = MD5(S + c(i-1)) */
-               this->hasher->get_hash(this->hasher, this->secret, NULL);
-               this->hasher->get_hash(this->hasher, seed, p);
-
-               /* p(i) = b(i) xor c(1) */
-               memxor(p, c, HASH_SIZE_MD5);
-
-               /* prepare next round */
-               seed = chunk_create(c, HASH_SIZE_MD5);
-               c += HASH_SIZE_MD5;
-               p += HASH_SIZE_MD5;
-       }
-
-       /* remove truncation, first byte is key length */
-       if (*P.ptr >= P.len)
-       {       /* decryption failed? */
-               return chunk_empty;
-       }
-       return chunk_clone(chunk_create(P.ptr + 1, *P.ptr));
-}
-
-METHOD(radius_socket_t, decrypt_msk, chunk_t,
-       private_radius_socket_t *this, radius_message_t *request,
-       radius_message_t *response)
-{
-       struct {
-               u_int32_t id;
-               u_int8_t type;
-               u_int8_t length;
-               u_int16_t salt;
-               u_int8_t key[];
-       } __attribute__((packed)) *mppe_key;
-       enumerator_t *enumerator;
-       chunk_t data, send = chunk_empty, recv = chunk_empty;
-       int type;
-
-       enumerator = response->create_enumerator(response);
-       while (enumerator->enumerate(enumerator, &type, &data))
-       {
-               if (type == RAT_VENDOR_SPECIFIC &&
-                       data.len > sizeof(*mppe_key))
-               {
-                       mppe_key = (void*)data.ptr;
-                       if (ntohl(mppe_key->id) == VENDOR_ID_MICROSOFT &&
-                               mppe_key->length == data.len - sizeof(mppe_key->id))
-                       {
-                               data = chunk_create(mppe_key->key, data.len - sizeof(*mppe_key));
-                               if (mppe_key->type == MS_MPPE_SEND_KEY)
-                               {
-                                       send = decrypt_mppe_key(this, mppe_key->salt, data, request);
-                               }
-                               if (mppe_key->type == MS_MPPE_RECV_KEY)
-                               {
-                                       recv = decrypt_mppe_key(this, mppe_key->salt, data, request);
-                               }
-                       }
-               }
-       }
-       enumerator->destroy(enumerator);
-       if (send.ptr && recv.ptr)
-       {
-               return chunk_cat("mm", recv, send);
-       }
-       chunk_clear(&send);
-       chunk_clear(&recv);
-       return chunk_empty;
-}
-
-METHOD(radius_socket_t, destroy, void,
-       private_radius_socket_t *this)
-{
-       DESTROY_IF(this->hasher);
-       DESTROY_IF(this->signer);
-       DESTROY_IF(this->rng);
-       if (this->auth_fd != -1)
-       {
-               close(this->auth_fd);
-       };
-       if (this->acct_fd != -1)
-       {
-               close(this->acct_fd);
-       }
-       free(this);
-}
-
-/**
- * See header
- */
-radius_socket_t *radius_socket_create(char *address, u_int16_t auth_port,
-                                                                         u_int16_t acct_port, chunk_t secret)
-{
-       private_radius_socket_t *this;
-
-       INIT(this,
-               .public = {
-                       .request = _request,
-                       .decrypt_msk = _decrypt_msk,
-                       .destroy = _destroy,
-               },
-               .address = address,
-               .auth_port = auth_port,
-               .auth_fd = -1,
-               .acct_port = acct_port,
-               .acct_fd = -1,
-       );
-
-       this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
-       this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128);
-       this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
-       if (!this->hasher || !this->signer || !this->rng)
-       {
-               DBG1(DBG_CFG, "RADIUS initialization failed, HMAC/MD5/RNG required");
-               destroy(this);
-               return NULL;
-       }
-       this->secret = secret;
-       this->signer->set_key(this->signer, secret);
-       /* we use a random identifier, helps if we restart often */
-       this->identifier = random();
-
-       return &this->public;
-}
diff --git a/src/libcharon/plugins/eap_radius/radius_socket.h b/src/libcharon/plugins/eap_radius/radius_socket.h
deleted file mode 100644 (file)
index 0301ec6..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2010 Martin Willi
- * Copyright (C) 2010 revosec AG
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-/**
- * @defgroup radius_socket radius_socket
- * @{ @ingroup eap_radius
- */
-
-#ifndef RADIUS_SOCKET_H_
-#define RADIUS_SOCKET_H_
-
-typedef struct radius_socket_t radius_socket_t;
-
-#include "radius_message.h"
-
-#include <utils/host.h>
-
-/**
- * RADIUS socket to a server.
- */
-struct radius_socket_t {
-
-       /**
-        * Send a RADIUS request, wait for response.
-        *
-        * The socket fills in RADIUS Message identifier, builds a
-        * Request-Authenticator and calculates the Message-Authenticator
-        * attribute.
-        * The received response gets verified using the Response-Identifier
-        * and the Message-Authenticator attribute.
-        *
-        * @param request               request message
-        * @return                              response message, NULL if timed out
-        */
-       radius_message_t* (*request)(radius_socket_t *this,
-                                                                radius_message_t *request);
-
-       /**
-        * Decrypt the MSK encoded in a messages MS-MPPE-Send/Recv-Key.
-        *
-        * @param request               associated RADIUS request message
-        * @param response              RADIUS response message containing attributes
-        * @return                              allocated MSK, empty chunk if none found
-        */
-       chunk_t (*decrypt_msk)(radius_socket_t *this, radius_message_t *request,
-                                                  radius_message_t *response);
-
-       /**
-        * Destroy a radius_socket_t.
-        */
-       void (*destroy)(radius_socket_t *this);
-};
-
-/**
- * Create a radius_socket instance.
- *
- * @param address      server name
- * @param auth_port    server port for authentication
- * @param acct_port    server port for accounting
- * @param secret       RADIUS secret
- */
-radius_socket_t *radius_socket_create(char *address, u_int16_t auth_port,
-                                                                         u_int16_t acct_port, chunk_t secret);
-
-#endif /** RADIUS_SOCKET_H_ @}*/
diff --git a/src/libradius/Makefile.am b/src/libradius/Makefile.am
new file mode 100644 (file)
index 0000000..8723a78
--- /dev/null
@@ -0,0 +1,9 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan
+
+ipseclib_LTLIBRARIES = libradius.la
+libradius_la_SOURCES = \
+       radius_message.h radius_message.c \
+       radius_socket.h radius_socket.c \
+       radius_client.h radius_client.c \
+       radius_server.h radius_server.c
diff --git a/src/libradius/radius_client.c b/src/libradius/radius_client.c
new file mode 100644 (file)
index 0000000..9c12be4
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2009 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "radius_client.h"
+#include "radius_server.h"
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <debug.h>
+#include <utils/host.h>
+#include <utils/linked_list.h>
+#include <threading/condvar.h>
+#include <threading/mutex.h>
+
+typedef struct private_radius_client_t private_radius_client_t;
+
+/**
+ * Private data of an radius_client_t object.
+ */
+struct private_radius_client_t {
+
+       /**
+        * Public radius_client_t interface.
+        */
+       radius_client_t public;
+
+       /**
+        * Selected RADIUS server
+        */
+       radius_server_t *server;
+
+       /**
+        * RADIUS servers State attribute
+        */
+       chunk_t state;
+
+       /**
+        * EAP MSK, from MPPE keys
+        */
+       chunk_t msk;
+};
+
+/**
+ * Save the state attribute to include in further request
+ */
+static void save_state(private_radius_client_t *this, radius_message_t *msg)
+{
+       enumerator_t *enumerator;
+       int type;
+       chunk_t data;
+
+       enumerator = msg->create_enumerator(msg);
+       while (enumerator->enumerate(enumerator, &type, &data))
+       {
+               if (type == RAT_STATE)
+               {
+                       free(this->state.ptr);
+                       this->state = chunk_clone(data);
+                       enumerator->destroy(enumerator);
+                       return;
+               }
+       }
+       enumerator->destroy(enumerator);
+       /* no state attribute found, remove state */
+       chunk_free(&this->state);
+}
+
+METHOD(radius_client_t, request, radius_message_t*,
+       private_radius_client_t *this, radius_message_t *req)
+{
+       char virtual[] = {0x00,0x00,0x00,0x05};
+       radius_socket_t *socket;
+       radius_message_t *res;
+
+       /* we add the "Virtual" NAS-Port-Type, as we SHOULD include one */
+       req->add(req, RAT_NAS_PORT_TYPE, chunk_create(virtual, sizeof(virtual)));
+       /* add our NAS-Identifier */
+       req->add(req, RAT_NAS_IDENTIFIER,
+                        this->server->get_nas_identifier(this->server));
+       /* add State attribute, if server sent one */
+       if (this->state.ptr)
+       {
+               req->add(req, RAT_STATE, this->state);
+       }
+       socket = this->server->get_socket(this->server);
+       DBG1(DBG_CFG, "sending RADIUS %N to server '%s'", radius_message_code_names,
+                req->get_code(req), this->server->get_name(this->server));
+       res = socket->request(socket, req);
+       if (res)
+       {
+               DBG1(DBG_CFG, "received RADIUS %N from server '%s'",
+                        radius_message_code_names, res->get_code(res),
+                        this->server->get_name(this->server));
+               save_state(this, res);
+               if (res->get_code(res) == RMC_ACCESS_ACCEPT)
+               {
+                       chunk_clear(&this->msk);
+                       this->msk = socket->decrypt_msk(socket, req, res);
+               }
+               this->server->put_socket(this->server, socket, TRUE);
+               return res;
+       }
+       this->server->put_socket(this->server, socket, FALSE);
+       return NULL;
+}
+
+METHOD(radius_client_t, get_msk, chunk_t,
+       private_radius_client_t *this)
+{
+       return this->msk;
+}
+
+METHOD(radius_client_t, destroy, void,
+       private_radius_client_t *this)
+{
+       this->server->destroy(this->server);
+       chunk_clear(&this->msk);
+       free(this->state.ptr);
+       free(this);
+}
+
+/**
+ * See header
+ */
+radius_client_t *radius_client_create(radius_server_t *server)
+{
+       private_radius_client_t *this;
+
+       INIT(this,
+               .public = {
+                       .request = _request,
+                       .get_msk = _get_msk,
+                       .destroy = _destroy,
+               },
+               .server = server,
+       );
+
+       return &this->public;
+}
diff --git a/src/libradius/radius_client.h b/src/libradius/radius_client.h
new file mode 100644 (file)
index 0000000..4ec344b
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup radius_client radius_client
+ * @{ @ingroup libradius
+ */
+
+#ifndef RADIUS_CLIENT_H_
+#define RADIUS_CLIENT_H_
+
+#include "radius_message.h"
+#include "radius_server.h"
+
+typedef struct radius_client_t radius_client_t;
+
+/**
+ * RADIUS client functionality.
+ *
+ * To communicate with a RADIUS server, create a client and send messages over
+ * it. The client allocates a socket from the best RADIUS server abailable.
+ */
+struct radius_client_t {
+
+       /**
+        * Send a RADIUS request and wait for the response.
+        *
+        * The client fills in NAS-Identifier nad NAS-Port-Type
+        *
+        * @param msg                   RADIUS request message to send
+        * @return                              response, NULL if timed out/verification failed
+        */
+       radius_message_t* (*request)(radius_client_t *this, radius_message_t *msg);
+
+       /**
+        * Get the EAP MSK after successful RADIUS authentication.
+        *
+        * @return                              MSK, allocated
+        */
+       chunk_t (*get_msk)(radius_client_t *this);
+
+       /**
+        * Destroy the client, release the socket.
+        */
+       void (*destroy)(radius_client_t *this);
+};
+
+/**
+ * Create a RADIUS client.
+ *
+ * @param server       reference to a server configuration, gets owned
+ * @return                     radius_client_t object
+ */
+radius_client_t *radius_client_create(radius_server_t *server);
+
+#endif /** RADIUS_CLIENT_H_ @}*/
diff --git a/src/libradius/radius_message.c b/src/libradius/radius_message.c
new file mode 100644 (file)
index 0000000..bd3a32f
--- /dev/null
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2009 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "radius_message.h"
+
+#include <debug.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_NEXT(radius_message_code_names, RMC_DISCONNECT_REQUEST, RMC_COA_NAK, RMC_ACCESS_CHALLENGE,
+       "Disconnect-Request",
+       "Disconnect-ACK",
+       "Disconnect-NAK",
+       "CoA-Request",
+       "CoA-ACK",
+       "CoA-NAK");
+ENUM_END(radius_message_code_names, RMC_COA_NAK);
+
+ENUM(radius_attribute_type_names, RAT_USER_NAME, RAT_MIP6_HOME_LINK_PREFIX,
+       "User-Name",
+       "User-Password",
+       "CHAP-Password",
+       "NAS-IP-Address",
+       "NAS-Port",
+       "Service-Type",
+       "Framed-Protocol",
+       "Framed-IP-Address",
+       "Framed-IP-Netmask",
+       "Framed-Routing",
+       "Filter-Id",
+       "Framed-MTU",
+       "Framed-Compression",
+       "Login-IP-Host",
+       "Login-Service",
+       "Login-TCP-Port",
+       "Unassigned",
+       "Reply-Message",
+       "Callback-Number",
+       "Callback-Id",
+       "Unassigned",
+       "Framed-Route",
+       "Framed-IPX-Network",
+       "State",
+       "Class",
+       "Vendor-Specific",
+       "Session-Timeout",
+       "Idle-Timeout",
+       "Termination-Action",
+       "Called-Station-Id",
+       "Calling-Station-Id",
+       "NAS-Identifier",
+       "Proxy-State",
+       "Login-LAT-Service",
+       "Login-LAT-Node",
+       "Login-LAT-Group",
+       "Framed-AppleTalk-Link",
+       "Framed-AppleTalk-Network",
+       "Framed-AppleTalk-Zone",
+       "Acct-Status-Type",
+       "Acct-Delay-Time",
+       "Acct-Input-Octets",
+       "Acct-Output-Octets",
+       "Acct-Session-Id",
+       "Acct-Authentic",
+       "Acct-Session-Time",
+       "Acct-Input-Packets",
+       "Acct-Output-Packets",
+       "Acct-Terminate-Cause",
+       "Acct-Multi-Session-Id",
+       "Acct-Link-Count",
+       "Acct-Input-Gigawords",
+       "Acct-Output-Gigawords",
+       "Unassigned",
+       "Event-Timestamp",
+       "Egress-VLANID",
+       "Ingress-Filters",
+       "Egress-VLAN-Name",
+       "User-Priority-Table",
+       "CHAP-Challenge",
+       "NAS-Port-Type",
+       "Port-Limit",
+       "Login-LAT-Port",
+       "Tunnel-Type",
+       "Tunnel-Medium-Type",
+       "Tunnel-Client-Endpoint",
+       "Tunnel-Server-Endpoint",
+       "Acct-Tunnel-Connection",
+       "Tunnel-Password",
+       "ARAP-Password",
+       "ARAP-Features",
+       "ARAP-Zone-Access",
+       "ARAP-Security",
+       "ARAP-Security-Data",
+       "Password-Retry",
+       "Prompt",
+       "Connect-Info",
+       "Configuration-Token",
+       "EAP-Message",
+       "Message-Authenticator",
+       "Tunnel-Private-Group-ID",
+       "Tunnel-Assignment-ID",
+       "Tunnel-Preference",
+       "ARAP-Challenge-Response",
+       "Acct-Interim-Interval",
+       "Acct-Tunnel-Packets-Lost",
+       "NAS-Port-Id",
+       "Framed-Pool",
+       "CUI",
+       "Tunnel-Client-Auth-ID",
+       "Tunnel-Server-Auth-ID",
+       "NAS-Filter-Rule",
+       "Unassigned",
+       "Originating-Line-Info",
+       "NAS-IPv6-Address",
+       "Framed-Interface-Id",
+       "Framed-IPv6-Prefix",
+       "Login-IPv6-Host",
+       "Framed-IPv6-Route",
+       "Framed-IPv6-Pool",
+       "Error-Cause",
+       "EAP-Key-Name",
+       "Digest-Response",
+       "Digest-Realm",
+       "Digest-Nonce",
+       "Digest-Response-Auth",
+       "Digest-Nextnonce",
+       "Digest-Method",
+       "Digest-URI",
+       "Digest-Qop",
+       "Digest-Algorithm",
+       "Digest-Entity-Body-Hash",
+       "Digest-CNonce",
+       "Digest-Nonce-Count",
+       "Digest-Username",
+       "Digest-Opaque",
+       "Digest-Auth-Param",
+       "Digest-AKA-Auts",
+       "Digest-Domain",
+       "Digest-Stale",
+       "Digest-HA1",
+       "SIP-AOR",
+       "Delegated-IPv6-Prefix",
+       "MIP6-Feature-Vector",
+       "MIP6-Home-Link-Prefix");
+
+/**
+ * Attribute enumerator implementation
+ */
+typedef struct {
+       /** implements enumerator interface */
+       enumerator_t public;
+       /** currently pointing attribute */
+       rattr_t *next;
+       /** bytes left */
+       int left;
+} attribute_enumerator_t;
+
+METHOD(enumerator_t, attribute_enumerate, bool,
+       attribute_enumerator_t *this, int *type, chunk_t *data)
+{
+       if (this->left == 0)
+       {
+               return FALSE;
+       }
+       if (this->left < sizeof(rattr_t) ||
+               this->left < this->next->length)
+       {
+               DBG1(DBG_IKE, "RADIUS message truncated");
+               return FALSE;
+       }
+       *type = this->next->type;
+       data->ptr = this->next->value;
+       data->len = this->next->length - sizeof(rattr_t);
+       this->left -= this->next->length;
+       this->next = ((void*)this->next) + this->next->length;
+       return TRUE;
+}
+
+METHOD(radius_message_t, create_enumerator, enumerator_t*,
+       private_radius_message_t *this)
+{
+       attribute_enumerator_t *e;
+
+       if (ntohs(this->msg->length) < sizeof(rmsg_t) + sizeof(rattr_t))
+       {
+               return enumerator_create_empty();
+       }
+       INIT(e,
+               .public = {
+                       .enumerate = (void*)_attribute_enumerate,
+                       .destroy = (void*)free,
+               },
+               .next = (rattr_t*)this->msg->attributes,
+               .left = ntohs(this->msg->length) - sizeof(rmsg_t),
+       );
+       return &e->public;
+}
+
+METHOD(radius_message_t, add, void,
+       private_radius_message_t *this, radius_attribute_type_t type, chunk_t data)
+{
+       rattr_t *attribute;
+
+       data.len = min(data.len, 253);
+       this->msg = realloc(this->msg,
+                                               ntohs(this->msg->length) + sizeof(rattr_t) + data.len);
+       attribute = ((void*)this->msg) + ntohs(this->msg->length);
+       attribute->type = type;
+       attribute->length = data.len + sizeof(rattr_t);
+       memcpy(attribute->value, data.ptr, data.len);
+       this->msg->length = htons(ntohs(this->msg->length) + attribute->length);
+}
+
+METHOD(radius_message_t, sign, void,
+       private_radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
+       hasher_t *hasher, signer_t *signer, rng_t *rng)
+{
+       if (rng == NULL)
+       {
+               chunk_t msg;
+
+               if (req_auth)
+               {
+                       memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5);
+               }
+               else
+               {
+                       memset(this->msg->authenticator, 0, sizeof(this->msg->authenticator));
+               }
+               msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length));
+               hasher->get_hash(hasher, msg, NULL);
+               hasher->get_hash(hasher, secret, this->msg->authenticator);
+       }
+       else
+       {
+               char buf[HASH_SIZE_MD5];
+
+               /* build Request-Authenticator */
+               rng->get_bytes(rng, HASH_SIZE_MD5, this->msg->authenticator);
+
+               /* build Message-Authenticator attribute, using 16 null bytes */
+               memset(buf, 0, sizeof(buf));
+               add(this, RAT_MESSAGE_AUTHENTICATOR, chunk_create(buf, sizeof(buf)));
+               signer->get_signature(signer,
+                               chunk_create((u_char*)this->msg, ntohs(this->msg->length)),
+                               ((u_char*)this->msg) + ntohs(this->msg->length) - HASH_SIZE_MD5);
+       }
+}
+
+METHOD(radius_message_t, verify, bool,
+       private_radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
+       hasher_t *hasher, signer_t *signer)
+{
+       char buf[HASH_SIZE_MD5], res_auth[HASH_SIZE_MD5];
+       enumerator_t *enumerator;
+       int type;
+       chunk_t data, msg;
+       bool has_eap = FALSE, has_auth = FALSE;
+
+       /* replace Response by Request Authenticator for verification */
+       memcpy(res_auth, this->msg->authenticator, HASH_SIZE_MD5);
+       if (req_auth)
+       {
+               memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5);
+       }
+       else
+       {
+               memset(this->msg->authenticator, 0, HASH_SIZE_MD5);
+       }
+       msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length));
+
+       /* verify Response-Authenticator */
+       hasher->get_hash(hasher, msg, NULL);
+       hasher->get_hash(hasher, secret, buf);
+       if (!memeq(buf, res_auth, HASH_SIZE_MD5))
+       {
+               DBG1(DBG_CFG, "RADIUS Response-Authenticator verification failed");
+               return FALSE;
+       }
+
+       /* verify Message-Authenticator attribute */
+       enumerator = create_enumerator(this);
+       while (enumerator->enumerate(enumerator, &type, &data))
+       {
+               if (type == RAT_MESSAGE_AUTHENTICATOR)
+               {
+                       if (data.len != HASH_SIZE_MD5)
+                       {
+                               DBG1(DBG_CFG, "RADIUS Message-Authenticator invalid length");
+                               enumerator->destroy(enumerator);
+                               return FALSE;
+                       }
+                       memcpy(buf, data.ptr, data.len);
+                       memset(data.ptr, 0, data.len);
+                       if (signer->verify_signature(signer, msg,
+                                                                                chunk_create(buf, sizeof(buf))))
+                       {
+                               /* restore Message-Authenticator */
+                               memcpy(data.ptr, buf, data.len);
+                               has_auth = TRUE;
+                               break;
+                       }
+                       else
+                       {
+                               DBG1(DBG_CFG, "RADIUS Message-Authenticator verification failed");
+                               enumerator->destroy(enumerator);
+                               return FALSE;
+                       }
+               }
+               else if (type == RAT_EAP_MESSAGE)
+               {
+                       has_eap = TRUE;
+               }
+       }
+       enumerator->destroy(enumerator);
+       /* restore Response-Authenticator */
+       memcpy(this->msg->authenticator, res_auth, HASH_SIZE_MD5);
+
+       if (has_eap && !has_auth)
+       {       /* Message-Authenticator is required if we have an EAP-Message */
+               DBG1(DBG_CFG, "RADIUS Message-Authenticator attribute missing");
+               return FALSE;
+       }
+       return TRUE;
+}
+
+METHOD(radius_message_t, get_code, radius_message_code_t,
+       private_radius_message_t *this)
+{
+       return this->msg->code;
+}
+
+METHOD(radius_message_t, get_identifier, u_int8_t,
+       private_radius_message_t *this)
+{
+       return this->msg->identifier;
+}
+
+METHOD(radius_message_t, set_identifier, void,
+       private_radius_message_t *this, u_int8_t identifier)
+{
+       this->msg->identifier = identifier;
+}
+
+METHOD(radius_message_t, get_authenticator, u_int8_t*,
+       private_radius_message_t *this)
+{
+       return this->msg->authenticator;
+}
+
+
+METHOD(radius_message_t, get_encoding, chunk_t,
+       private_radius_message_t *this)
+{
+       return chunk_create((u_char*)this->msg, ntohs(this->msg->length));
+}
+
+METHOD(radius_message_t, destroy, void,
+       private_radius_message_t *this)
+{
+       free(this->msg);
+       free(this);
+}
+
+/**
+ * Generic constructor
+ */
+static private_radius_message_t *radius_message_create_empty()
+{
+       private_radius_message_t *this;
+
+       INIT(this,
+               .public = {
+                       .create_enumerator = _create_enumerator,
+                       .add = _add,
+                       .get_code = _get_code,
+                       .get_identifier = _get_identifier,
+                       .set_identifier = _set_identifier,
+                       .get_authenticator = _get_authenticator,
+                       .get_encoding = _get_encoding,
+                       .sign = _sign,
+                       .verify = _verify,
+                       .destroy = _destroy,
+               },
+       );
+
+       return this;
+}
+
+/**
+ * See header
+ */
+radius_message_t *radius_message_create(radius_message_code_t code)
+{
+       private_radius_message_t *this = radius_message_create_empty();
+
+       INIT(this->msg,
+               .code = code,
+               .identifier = 0,
+               .length = htons(sizeof(rmsg_t)),
+       );
+
+       return &this->public;
+}
+
+/**
+ * See header
+ */
+radius_message_t *radius_message_parse(chunk_t data)
+{
+       private_radius_message_t *this = radius_message_create_empty();
+
+       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/libradius/radius_message.h b/src/libradius/radius_message.h
new file mode 100644 (file)
index 0000000..41cfb51
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2009 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup libradius libradius
+ *
+ * @addtogroup libradius
+ * RADIUS protocol support library.
+ *
+ * @defgroup radius_message radius_message
+ * @{ @ingroup libradius
+ */
+
+#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,
+       RMC_DISCONNECT_REQUEST = 40,
+       RMC_DISCONNECT_ACK = 41,
+       RMC_DISCONNECT_NAK = 42,
+       RMC_COA_REQUEST = 43,
+       RMC_COA_ACK = 44,
+       RMC_COA_NAK = 45,
+};
+
+/**
+ * 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 req_auth              16 byte Authenticator of request, or NULL
+        * @param secret                shared RADIUS secret
+        * @param signer                HMAC-MD5 signer with secret set
+        * @param hasher                MD5 hasher
+        * @param rng                   RNG to create Message-Authenticator, NULL to omit
+        */
+       void (*sign)(radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
+                                hasher_t *hasher, signer_t *signer, rng_t *rng);
+
+       /**
+        * Verify the integrity of a received RADIUS message.
+        *
+        * @param req_auth              16 byte Authenticator of request, or NULL
+        * @param secret                shared RADIUS secret
+        * @param signer                HMAC-MD5 signer with secret set
+        * @param hasher                MD5 hasher
+        */
+       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 message.
+ *
+ * @param code                 request type
+ * @return                             radius_message_t object
+ */
+radius_message_t *radius_message_create(radius_message_code_t code);
+
+/**
+ * Parse and verify a recevied RADIUS message.
+ *
+ * @param data                 received message data
+ * @return                             radius_message_t object, NULL if length invalid
+ */
+radius_message_t *radius_message_parse(chunk_t data);
+
+#endif /** RADIUS_MESSAGE_H_ @}*/
diff --git a/src/libradius/radius_server.c b/src/libradius/radius_server.c
new file mode 100644 (file)
index 0000000..282f508
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "radius_server.h"
+
+#include <threading/mutex.h>
+#include <threading/condvar.h>
+#include <utils/linked_list.h>
+
+typedef struct private_radius_server_t private_radius_server_t;
+
+/**
+ * Private data of an radius_server_t object.
+ */
+struct private_radius_server_t {
+
+       /**
+        * Public radius_server_t interface.
+        */
+       radius_server_t public;
+
+       /**
+        * list of radius sockets, as radius_socket_t
+        */
+       linked_list_t *sockets;
+
+       /**
+        * Total number of sockets, in list + currently in use
+        */
+       int socket_count;
+
+       /**
+        * mutex to lock sockets list
+        */
+       mutex_t *mutex;
+
+       /**
+        * condvar to wait for sockets
+        */
+       condvar_t *condvar;
+
+       /**
+        * Server name
+        */
+       char *name;
+
+       /**
+        * NAS-Identifier
+        */
+       chunk_t nas_identifier;
+
+       /**
+        * Preference boost for this server
+        */
+       int preference;
+
+       /**
+        * Is the server currently reachable
+        */
+       bool reachable;
+
+       /**
+        * Retry counter for unreachable servers
+        */
+       int retry;
+
+       /**
+        * reference count
+        */
+       refcount_t ref;
+};
+
+METHOD(radius_server_t, get_socket, radius_socket_t*,
+       private_radius_server_t *this)
+{
+       radius_socket_t *skt;
+
+       this->mutex->lock(this->mutex);
+       while (this->sockets->remove_first(this->sockets, (void**)&skt) != SUCCESS)
+       {
+               this->condvar->wait(this->condvar, this->mutex);
+       }
+       this->mutex->unlock(this->mutex);
+       return skt;
+}
+
+METHOD(radius_server_t, put_socket, void,
+       private_radius_server_t *this, radius_socket_t *skt, bool result)
+{
+       this->mutex->lock(this->mutex);
+       this->sockets->insert_last(this->sockets, skt);
+       this->mutex->unlock(this->mutex);
+       this->condvar->signal(this->condvar);
+       this->reachable = result;
+}
+
+METHOD(radius_server_t, get_nas_identifier, chunk_t,
+       private_radius_server_t *this)
+{
+       return this->nas_identifier;
+}
+
+METHOD(radius_server_t, get_preference, int,
+       private_radius_server_t *this)
+{
+       int pref;
+
+       if (this->socket_count == 0)
+       {       /* don't have sockets, huh? */
+               return -1;
+       }
+       /* calculate preference between 0-100 + boost */
+       pref = this->preference;
+       pref += this->sockets->get_count(this->sockets) * 100 / this->socket_count;
+       if (this->reachable)
+       {       /* reachable server get a boost: pref = 110-210 + boost */
+               return pref + 110;
+       }
+       /* Not reachable. Increase preference randomly to let it retry from
+        * time to time, especially if other servers have high load. */
+       this->retry++;
+       if (this->retry % 128 == 0)
+       {       /* every 64th request gets 210, same as unloaded reachable */
+               return pref + 110;
+       }
+       if (this->retry % 32 == 0)
+       {       /* every 32th request gets 190, wins against average loaded */
+               return pref + 90;
+       }
+       if (this->retry % 8 == 0)
+       {       /* every 8th request gets 110, same as server under load */
+               return pref + 10;
+       }
+       /* other get ~100, less than fully loaded */
+       return pref;
+}
+
+METHOD(radius_server_t, get_name, char*,
+       private_radius_server_t *this)
+{
+       return this->name;
+}
+
+METHOD(radius_server_t, get_ref, radius_server_t*,
+       private_radius_server_t *this)
+{
+       ref_get(&this->ref);
+       return &this->public;
+}
+
+
+METHOD(radius_server_t, destroy, void,
+       private_radius_server_t *this)
+{
+       if (ref_put(&this->ref))
+       {
+               this->mutex->destroy(this->mutex);
+               this->condvar->destroy(this->condvar);
+               this->sockets->destroy_offset(this->sockets,
+                                                                         offsetof(radius_socket_t, destroy));
+               free(this);
+       }
+}
+
+/**
+ * See header
+ */
+radius_server_t *radius_server_create(char *name, char *address,
+                                                                         u_int16_t auth_port, u_int16_t acct_port,
+                                                                         char *nas_identifier, char *secret,
+                                                                         int sockets, int preference)
+{
+       private_radius_server_t *this;
+       radius_socket_t *socket;
+
+       INIT(this,
+               .public = {
+                       .get_socket = _get_socket,
+                       .put_socket = _put_socket,
+                       .get_nas_identifier = _get_nas_identifier,
+                       .get_preference = _get_preference,
+                       .get_name = _get_name,
+                       .get_ref = _get_ref,
+                       .destroy = _destroy,
+               },
+               .reachable = TRUE,
+               .nas_identifier = chunk_create(nas_identifier, strlen(nas_identifier)),
+               .socket_count = sockets,
+               .sockets = linked_list_create(),
+               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+               .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
+               .name = name,
+               .preference = preference,
+               .ref = 1,
+       );
+
+       while (sockets--)
+       {
+               socket = radius_socket_create(address, auth_port, acct_port,
+                                                                         chunk_create(secret, strlen(secret)));
+               if (!socket)
+               {
+                       destroy(this);
+                       return NULL;
+               }
+               this->sockets->insert_last(this->sockets, socket);
+       }
+       return &this->public;
+}
diff --git a/src/libradius/radius_server.h b/src/libradius/radius_server.h
new file mode 100644 (file)
index 0000000..2cc281c
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup radius_server radius_server
+ * @{ @ingroup libradius
+ */
+
+#ifndef RADIUS_SERVER_H_
+#define RADIUS_SERVER_H_
+
+typedef struct radius_server_t radius_server_t;
+
+#include "radius_socket.h"
+
+/**
+ * RADIUS server configuration.
+ */
+struct radius_server_t {
+
+       /**
+        * Get a RADIUS socket from the pool to communicate with this server.
+        *
+        * @return                      RADIUS socket
+        */
+       radius_socket_t* (*get_socket)(radius_server_t *this);
+
+       /**
+        * Release a socket to the pool after use.
+        *
+        * @param skt           RADIUS socket to release
+        * @param result        result of the socket use, TRUE for success
+        */
+       void (*put_socket)(radius_server_t *this, radius_socket_t *skt, bool result);
+
+       /**
+        * Get the NAS-Identifier to use with this server.
+        *
+        * @return                      NAS-Identifier, internal data
+        */
+       chunk_t (*get_nas_identifier)(radius_server_t *this);
+
+       /**
+        * Get the preference of this server.
+        *
+        * Based on the available sockets and the server reachability a preference
+        * value is calculated: better servers return a higher value.
+        */
+       int (*get_preference)(radius_server_t *this);
+
+       /**
+        * Get the name of the RADIUS server.
+        *
+        * @return                      server name
+        */
+       char* (*get_name)(radius_server_t *this);
+
+       /**
+        * Increase reference count of this server.
+        *
+        * @return                      this
+        */
+       radius_server_t* (*get_ref)(radius_server_t *this);
+
+       /**
+        * Destroy a radius_server_t.
+        */
+       void (*destroy)(radius_server_t *this);
+};
+
+/**
+ * Create a radius_server instance.
+ *
+ * @param name                         server name
+ * @param address                      server address
+ * @param auth_port                    server port for authentication
+ * @param acct_port                    server port for accounting
+ * @param nas_identifier       NAS-Identifier to use with this server
+ * @param secret                       secret to use with this server
+ * @param sockets                      number of sockets to create in pool
+ * @param preference           preference boost for this server
+ */
+radius_server_t *radius_server_create(char *name, char *address,
+                                                                         u_int16_t auth_port, u_int16_t acct_port,
+                                                                         char *nas_identifier, char *secret,
+                                                                         int sockets, int preference);
+
+#endif /** RADIUS_SERVER_H_ @}*/
diff --git a/src/libradius/radius_socket.c b/src/libradius/radius_socket.c
new file mode 100644 (file)
index 0000000..875bd61
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "radius_socket.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <debug.h>
+
+/**
+ * Vendor-Id of Microsoft specific attributes
+ */
+#define VENDOR_ID_MICROSOFT 311
+
+/**
+ * Microsoft specific vendor attributes
+ */
+#define MS_MPPE_SEND_KEY 16
+#define MS_MPPE_RECV_KEY 17
+
+typedef struct private_radius_socket_t private_radius_socket_t;
+
+/**
+ * Private data of an radius_socket_t object.
+ */
+struct private_radius_socket_t {
+
+       /**
+        * Public radius_socket_t interface.
+        */
+       radius_socket_t public;
+
+       /**
+        * Server port for authentication
+        */
+       u_int16_t auth_port;
+
+       /**
+        * socket file descriptor for authentication
+        */
+       int auth_fd;
+
+       /**
+        * Server port for accounting
+        */
+       u_int16_t acct_port;
+
+       /**
+        * socket file descriptor for accounting
+        */
+       int acct_fd;
+
+       /**
+        * Server address
+        */
+       char *address;
+
+       /**
+        * current RADIUS identifier
+        */
+       u_int8_t identifier;
+
+       /**
+        * hasher to use for response verification
+        */
+       hasher_t *hasher;
+
+       /**
+        * HMAC-MD5 signer to build Message-Authenticator attribute
+        */
+       signer_t *signer;
+
+       /**
+        * random number generator for RADIUS request authenticator
+        */
+       rng_t *rng;
+
+       /**
+        * RADIUS secret
+        */
+       chunk_t secret;
+};
+
+/**
+ * Check or establish RADIUS connection
+ */
+static bool check_connection(private_radius_socket_t *this,
+                                                        int *fd, u_int16_t port)
+{
+       if (*fd == -1)
+       {
+               host_t *server;
+
+               server = host_create_from_dns(this->address, AF_UNSPEC, port);
+               if (!server)
+               {
+                       DBG1(DBG_CFG, "resolving RADIUS server address '%s' failed",
+                                this->address);
+                       return FALSE;
+               }
+               *fd = socket(server->get_family(server), SOCK_DGRAM, IPPROTO_UDP);
+               if (*fd == -1)
+               {
+                       DBG1(DBG_CFG, "opening RADIUS socket for %#H failed: %s",
+                                server, strerror(errno));
+                       server->destroy(server);
+                       return FALSE;
+               }
+               if (connect(*fd, server->get_sockaddr(server),
+                                       *server->get_sockaddr_len(server)) < 0)
+               {
+                       DBG1(DBG_CFG, "connecting RADIUS socket to %#H failed: %s",
+                                server, strerror(errno));
+                       server->destroy(server);
+                       close(*fd);
+                       *fd = -1;
+                       return FALSE;
+               }
+               server->destroy(server);
+       }
+       return TRUE;
+}
+
+METHOD(radius_socket_t, request, radius_message_t*,
+       private_radius_socket_t *this, radius_message_t *request)
+{
+       chunk_t data;
+       int i, *fd;
+       u_int16_t port;
+       rng_t *rng = NULL;
+
+       if (request->get_code(request) == RMC_ACCOUNTING_REQUEST)
+       {
+               fd = &this->acct_fd;
+               port = this->acct_port;
+       }
+       else
+       {
+               fd = &this->auth_fd;
+               port = this->auth_port;
+               rng = this->rng;
+       }
+
+       /* set Message Identifier */
+       request->set_identifier(request, this->identifier++);
+       /* sign the request */
+       request->sign(request, NULL, this->secret, this->hasher, this->signer, rng);
+
+       if (!check_connection(this, fd, port))
+       {
+               return NULL;
+       }
+
+       data = request->get_encoding(request);
+       /* timeout after 2, 3, 4, 5 seconds */
+       for (i = 2; i <= 5; i++)
+       {
+               radius_message_t *response;
+               bool retransmit = FALSE;
+               struct timeval tv;
+               char buf[4096];
+               fd_set fds;
+               int res;
+
+               if (send(*fd, data.ptr, data.len, 0) != data.len)
+               {
+                       DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno));
+                       return NULL;
+               }
+               tv.tv_sec = i;
+               tv.tv_usec = 0;
+
+               while (TRUE)
+               {
+                       FD_ZERO(&fds);
+                       FD_SET(*fd, &fds);
+                       res = select((*fd) + 1, &fds, NULL, NULL, &tv);
+                       /* TODO: updated tv to time not waited. Linux does this for us. */
+                       if (res < 0)
+                       {       /* failed */
+                               DBG1(DBG_CFG, "waiting for RADIUS message failed: %s",
+                                        strerror(errno));
+                               break;
+                       }
+                       if (res == 0)
+                       {       /* timeout */
+                               DBG1(DBG_CFG, "retransmitting RADIUS message");
+                               retransmit = TRUE;
+                               break;
+                       }
+                       res = recv(*fd, buf, sizeof(buf), MSG_DONTWAIT);
+                       if (res <= 0)
+                       {
+                               DBG1(DBG_CFG, "receiving RADIUS message failed: %s",
+                                        strerror(errno));
+                               break;
+                       }
+                       response = radius_message_parse(chunk_create(buf, res));
+                       if (response)
+                       {
+                               if (response->verify(response,
+                                                       request->get_authenticator(request), this->secret,
+                                                       this->hasher, this->signer))
+                               {
+                                       return response;
+                               }
+                               response->destroy(response);
+                       }
+                       DBG1(DBG_CFG, "received invalid RADIUS message, ignored");
+               }
+               if (!retransmit)
+               {
+                       break;
+               }
+       }
+       DBG1(DBG_CFG, "RADIUS server is not responding");
+       return NULL;
+}
+
+/**
+ * Decrypt a MS-MPPE-Send/Recv-Key
+ */
+static chunk_t decrypt_mppe_key(private_radius_socket_t *this, u_int16_t salt,
+                                                               chunk_t C, radius_message_t *request)
+{
+       chunk_t A, R, P, seed;
+       u_char *c, *p;
+
+       /**
+        * From RFC2548 (encryption):
+        * b(1) = MD5(S + R + A)    c(1) = p(1) xor b(1)   C = c(1)
+        * b(2) = MD5(S + c(1))     c(2) = p(2) xor b(2)   C = C + c(2)
+        *      . . .
+        * b(i) = MD5(S + c(i-1))   c(i) = p(i) xor b(i)   C = C + c(i)
+        */
+
+       if (C.len % HASH_SIZE_MD5 || C.len < HASH_SIZE_MD5)
+       {
+               return chunk_empty;
+       }
+
+       A = chunk_create((u_char*)&salt, sizeof(salt));
+       R = chunk_create(request->get_authenticator(request), HASH_SIZE_MD5);
+       P = chunk_alloca(C.len);
+       p = P.ptr;
+       c = C.ptr;
+
+       seed = chunk_cata("cc", R, A);
+
+       while (c < C.ptr + C.len)
+       {
+               /* b(i) = MD5(S + c(i-1)) */
+               this->hasher->get_hash(this->hasher, this->secret, NULL);
+               this->hasher->get_hash(this->hasher, seed, p);
+
+               /* p(i) = b(i) xor c(1) */
+               memxor(p, c, HASH_SIZE_MD5);
+
+               /* prepare next round */
+               seed = chunk_create(c, HASH_SIZE_MD5);
+               c += HASH_SIZE_MD5;
+               p += HASH_SIZE_MD5;
+       }
+
+       /* remove truncation, first byte is key length */
+       if (*P.ptr >= P.len)
+       {       /* decryption failed? */
+               return chunk_empty;
+       }
+       return chunk_clone(chunk_create(P.ptr + 1, *P.ptr));
+}
+
+METHOD(radius_socket_t, decrypt_msk, chunk_t,
+       private_radius_socket_t *this, radius_message_t *request,
+       radius_message_t *response)
+{
+       struct {
+               u_int32_t id;
+               u_int8_t type;
+               u_int8_t length;
+               u_int16_t salt;
+               u_int8_t key[];
+       } __attribute__((packed)) *mppe_key;
+       enumerator_t *enumerator;
+       chunk_t data, send = chunk_empty, recv = chunk_empty;
+       int type;
+
+       enumerator = response->create_enumerator(response);
+       while (enumerator->enumerate(enumerator, &type, &data))
+       {
+               if (type == RAT_VENDOR_SPECIFIC &&
+                       data.len > sizeof(*mppe_key))
+               {
+                       mppe_key = (void*)data.ptr;
+                       if (ntohl(mppe_key->id) == VENDOR_ID_MICROSOFT &&
+                               mppe_key->length == data.len - sizeof(mppe_key->id))
+                       {
+                               data = chunk_create(mppe_key->key, data.len - sizeof(*mppe_key));
+                               if (mppe_key->type == MS_MPPE_SEND_KEY)
+                               {
+                                       send = decrypt_mppe_key(this, mppe_key->salt, data, request);
+                               }
+                               if (mppe_key->type == MS_MPPE_RECV_KEY)
+                               {
+                                       recv = decrypt_mppe_key(this, mppe_key->salt, data, request);
+                               }
+                       }
+               }
+       }
+       enumerator->destroy(enumerator);
+       if (send.ptr && recv.ptr)
+       {
+               return chunk_cat("mm", recv, send);
+       }
+       chunk_clear(&send);
+       chunk_clear(&recv);
+       return chunk_empty;
+}
+
+METHOD(radius_socket_t, destroy, void,
+       private_radius_socket_t *this)
+{
+       DESTROY_IF(this->hasher);
+       DESTROY_IF(this->signer);
+       DESTROY_IF(this->rng);
+       if (this->auth_fd != -1)
+       {
+               close(this->auth_fd);
+       };
+       if (this->acct_fd != -1)
+       {
+               close(this->acct_fd);
+       }
+       free(this);
+}
+
+/**
+ * See header
+ */
+radius_socket_t *radius_socket_create(char *address, u_int16_t auth_port,
+                                                                         u_int16_t acct_port, chunk_t secret)
+{
+       private_radius_socket_t *this;
+
+       INIT(this,
+               .public = {
+                       .request = _request,
+                       .decrypt_msk = _decrypt_msk,
+                       .destroy = _destroy,
+               },
+               .address = address,
+               .auth_port = auth_port,
+               .auth_fd = -1,
+               .acct_port = acct_port,
+               .acct_fd = -1,
+       );
+
+       this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
+       this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128);
+       this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+       if (!this->hasher || !this->signer || !this->rng)
+       {
+               DBG1(DBG_CFG, "RADIUS initialization failed, HMAC/MD5/RNG required");
+               destroy(this);
+               return NULL;
+       }
+       this->secret = secret;
+       this->signer->set_key(this->signer, secret);
+       /* we use a random identifier, helps if we restart often */
+       this->identifier = random();
+
+       return &this->public;
+}
diff --git a/src/libradius/radius_socket.h b/src/libradius/radius_socket.h
new file mode 100644 (file)
index 0000000..07d642c
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup radius_socket radius_socket
+ * @{ @ingroup libradius
+ */
+
+#ifndef RADIUS_SOCKET_H_
+#define RADIUS_SOCKET_H_
+
+typedef struct radius_socket_t radius_socket_t;
+
+#include "radius_message.h"
+
+#include <utils/host.h>
+
+/**
+ * RADIUS socket to a server.
+ */
+struct radius_socket_t {
+
+       /**
+        * Send a RADIUS request, wait for response.
+        *
+        * The socket fills in RADIUS Message identifier, builds a
+        * Request-Authenticator and calculates the Message-Authenticator
+        * attribute.
+        * The received response gets verified using the Response-Identifier
+        * and the Message-Authenticator attribute.
+        *
+        * @param request               request message
+        * @return                              response message, NULL if timed out
+        */
+       radius_message_t* (*request)(radius_socket_t *this,
+                                                                radius_message_t *request);
+
+       /**
+        * Decrypt the MSK encoded in a messages MS-MPPE-Send/Recv-Key.
+        *
+        * @param request               associated RADIUS request message
+        * @param response              RADIUS response message containing attributes
+        * @return                              allocated MSK, empty chunk if none found
+        */
+       chunk_t (*decrypt_msk)(radius_socket_t *this, radius_message_t *request,
+                                                  radius_message_t *response);
+
+       /**
+        * Destroy a radius_socket_t.
+        */
+       void (*destroy)(radius_socket_t *this);
+};
+
+/**
+ * Create a radius_socket instance.
+ *
+ * @param address      server name
+ * @param auth_port    server port for authentication
+ * @param acct_port    server port for accounting
+ * @param secret       RADIUS secret
+ */
+radius_socket_t *radius_socket_create(char *address, u_int16_t auth_port,
+                                                                         u_int16_t acct_port, chunk_t secret);
+
+#endif /** RADIUS_SOCKET_H_ @}*/