keep a list of RADIUS connections with EAP method states
authorAndreas Steffen <andreas.steffen@strongswan.org>
Fri, 9 Mar 2012 16:38:06 +0000 (17:38 +0100)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Tue, 13 Mar 2012 15:27:17 +0000 (16:27 +0100)
src/libcharon/plugins/tnc_pdp/Makefile.am
src/libcharon/plugins/tnc_pdp/tnc_pdp.c
src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.c [new file with mode: 0644]
src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.h [new file with mode: 0644]

index 170a6f9..2d4c4d5 100644 (file)
@@ -18,6 +18,7 @@ libstrongswan_tnc_pdp_la_LIBADD = \
 endif
 
 libstrongswan_tnc_pdp_la_SOURCES = \
-       tnc_pdp_plugin.h tnc_pdp_plugin.c tnc_pdp.h tnc_pdp.c
+       tnc_pdp_plugin.h tnc_pdp_plugin.c \
+       tnc_pdp.h tnc_pdp.c tnc_pdp_connections.h tnc_pdp_connections.c 
 
 libstrongswan_tnc_pdp_la_LDFLAGS = -module -avoid-version
index 1ef87f2..db1f4c7 100644 (file)
@@ -14,6 +14,7 @@
  */
 
 #include "tnc_pdp.h"
+#include "tnc_pdp_connections.h"
 
 #include <errno.h>
 #include <unistd.h>
@@ -84,9 +85,9 @@ struct private_tnc_pdp_t {
        signer_t *signer;
 
        /**
-        * EAP method
+        * List of registered TNC-PDP connections
         */
-       eap_method_t *method;
+       tnc_pdp_connections_t *connections;
 };
 
 
@@ -195,7 +196,7 @@ static void send_response(private_tnc_pdp_t *this,
                while (data.len > MAX_RADIUS_ATTRIBUTE_SIZE)
                {
                        response->add(response, RAT_EAP_MESSAGE,
-                                                 chunk_create(data.ptr,MAX_RADIUS_ATTRIBUTE_SIZE));
+                                                 chunk_create(data.ptr, MAX_RADIUS_ATTRIBUTE_SIZE));
                        data = chunk_skip(data, MAX_RADIUS_ATTRIBUTE_SIZE);
                }
                response->add(response, RAT_EAP_MESSAGE, data);
@@ -207,6 +208,7 @@ static void send_response(private_tnc_pdp_t *this,
        DBG1(DBG_CFG, "sending RADIUS %N to client '%H'", radius_message_code_names,
                 code, client);
        send_message(this, response, client);
+       response->destroy(response);
 }
 
 /**
@@ -217,8 +219,10 @@ static void process_eap(private_tnc_pdp_t *this, radius_message_t *request,
 {
        enumerator_t *enumerator;
        eap_payload_t *in, *out = NULL;
+       eap_method_t *method;
        eap_type_t eap_type;
        chunk_t data, message = chunk_empty;
+       chunk_t user_name = chunk_empty, nas_id = chunk_empty;
        radius_message_code_t code = RMC_ACCESS_CHALLENGE;
        u_int32_t eap_vendor;
        int type;
@@ -226,9 +230,22 @@ static void process_eap(private_tnc_pdp_t *this, radius_message_t *request,
        enumerator = request->create_enumerator(request);
        while (enumerator->enumerate(enumerator, &type, &data))
        {
-               if (type == RAT_EAP_MESSAGE && data.len)
+               switch (type)
                {
-                       message = chunk_cat("mc", message, data);
+                       case RAT_USER_NAME:
+                               user_name = data;
+                               break;
+                       case RAT_NAS_IDENTIFIER:
+                               nas_id = data;
+                               break;
+                       case RAT_EAP_MESSAGE:
+                               if (data.len)
+                               {
+                                       message = chunk_cat("mc", message, data);
+                               }
+                               break;
+                       default:
+                               break;
                }
        }
        enumerator->destroy(enumerator);
@@ -255,19 +272,27 @@ static void process_eap(private_tnc_pdp_t *this, radius_message_t *request,
                        eap_identity = chunk_create(message.ptr + 5, message.len - 5);
                        peer = identification_create_from_data(eap_identity);
 
-                       this->method = charon->eap->create_instance(charon->eap, this->type,
-                                                                                               0, EAP_SERVER, this->server, peer); 
+                       method = charon->eap->create_instance(charon->eap, this->type,
+                                                                               0, EAP_SERVER, this->server, peer); 
                        peer->destroy(peer);
-                       if (!this->method)
+                       if (!method)
                        {
                                in->destroy(in);
                                return;
                        }
-                       this->method->initiate(this->method, &out);
+                       this->connections->add(this->connections, nas_id, user_name, method);
+                       method->initiate(method, &out);
                }
                else
                {
-                       switch (this->method->process(this->method, in, &out))
+                       method = this->connections->get_method(this->connections, nas_id,
+                                                                                                  user_name);
+                       if (!method)
+                       {
+                               return;
+                       }
+
+                       switch (method->process(method, in, &out))
                        {
                                case NEED_MORE:
                                        code = RMC_ACCESS_CHALLENGE;
@@ -287,6 +312,11 @@ static void process_eap(private_tnc_pdp_t *this, radius_message_t *request,
                        }
                }
 
+               if (code == RMC_ACCESS_ACCEPT || code == RMC_ACCESS_REJECT)
+               {
+                       this->connections->remove(this->connections, nas_id, user_name);
+               }
+
                send_response(this, request, code, out, source);
                in->destroy(in);
                out->destroy(out);
@@ -412,7 +442,7 @@ METHOD(tnc_pdp_t, destroy, void,
        DESTROY_IF(this->server);
        DESTROY_IF(this->signer);
        DESTROY_IF(this->hasher);
-       DESTROY_IF(this->method);
+       DESTROY_IF(this->connections);
        free(this);
 }
 
@@ -433,6 +463,7 @@ tnc_pdp_t *tnc_pdp_create(u_int16_t port)
                .ipv6 = open_socket(this, AF_INET6, port),
                .hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5),
                .signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128),
+               .connections = tnc_pdp_connections_create(),
        );
 
        if (!this->ipv4 && !this->ipv6)
diff --git a/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.c b/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.c
new file mode 100644 (file)
index 0000000..b57a15c
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2012 Andreas Steffen
+ * HSR 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 "tnc_pdp_connections.h"
+
+#include <utils/linked_list.h>
+#include <debug.h>
+
+typedef struct private_tnc_pdp_connections_t private_tnc_pdp_connections_t;
+typedef struct entry_t entry_t;
+
+/**
+ * Private data of tnc_pdp_connections_t
+ */
+struct private_tnc_pdp_connections_t {
+
+       /**
+        * Implements tnc_pdp_connections_t interface
+        */
+       tnc_pdp_connections_t public;
+
+       /**
+        * List of TNC PEP RADIUS Connections
+        */ 
+       linked_list_t *list;
+};
+
+/**
+ * Data entry for a TNC PEP RADIUS connection
+ */
+struct entry_t {
+
+       /**
+        * NAS identifier of PEP
+        */
+       chunk_t nas_id;
+
+       /**
+        * User name of TNC Client
+        */
+       chunk_t user_name;
+
+       /**
+        * EAP method state
+        */
+       eap_method_t *method;
+};
+
+/**
+ * Free the memory allocated to a data entry
+ */
+static void free_entry(entry_t *this)
+{
+       this->method->destroy(this->method);
+       free(this->nas_id.ptr);
+       free(this->user_name.ptr);
+       free(this);
+}
+
+/**
+ * Find a matching data entry
+ */
+static bool equals_entry( entry_t *this, chunk_t nas_id, chunk_t user_name)
+{
+       bool no_nas_id = !this->nas_id.ptr && !nas_id.ptr;
+
+       return (chunk_equals(this->nas_id, nas_id) || no_nas_id) &&
+                       chunk_equals(this->user_name, user_name);
+}
+
+/**
+ * Find a matching data entry
+ */
+static void dbg_nas_user(chunk_t nas_id, chunk_t user_name, bool not, char *op)
+{
+       if (nas_id.len)
+       {
+               DBG1(DBG_CFG, "%s RADIUS connection for user '%.*s' NAS '%.*s'",
+                                          not ? "could not find" : op, user_name.len, user_name.ptr,
+                                          nas_id.len, nas_id.ptr);
+       }
+       else
+       {
+               DBG1(DBG_CFG, "%s RADIUS connection for user '%.*s'", 
+                                          not ? "could not find" : op, user_name.len, user_name.ptr);
+       }
+}
+
+METHOD(tnc_pdp_connections_t, add, void,
+       private_tnc_pdp_connections_t *this, chunk_t nas_id, chunk_t user_name,
+       eap_method_t *method)
+{
+       enumerator_t *enumerator;
+       entry_t *entry;
+       bool found = FALSE;
+
+       enumerator = this->list->create_enumerator(this->list);
+       while (enumerator->enumerate(enumerator, &entry))
+       {
+               if (equals_entry(entry, nas_id, user_name))
+               {
+                       found = TRUE;
+                       entry->method->destroy(entry->method);
+                       DBG1(DBG_CFG, "removed stale TNC PEP RADIUS connection");
+                       entry->method = method;
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       
+       if (!found)
+       {
+               entry = malloc_thing(entry_t);
+               entry->nas_id = chunk_clone(nas_id);
+               entry->user_name = chunk_clone(user_name);
+               entry->method = method;
+               this->list->insert_last(this->list, entry);
+       }
+       dbg_nas_user(nas_id, user_name, FALSE, "created");
+}
+
+METHOD(tnc_pdp_connections_t, remove_, void,
+       private_tnc_pdp_connections_t *this, chunk_t nas_id, chunk_t user_name)
+{
+       enumerator_t *enumerator;
+       entry_t *entry;
+
+       enumerator = this->list->create_enumerator(this->list);
+       while (enumerator->enumerate(enumerator, &entry))
+       {
+               if (equals_entry(entry, nas_id, user_name))
+               {
+                       free_entry(entry);
+                       this->list->remove_at(this->list, enumerator);
+                       dbg_nas_user(nas_id, user_name, FALSE, "removed");
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+}
+
+METHOD(tnc_pdp_connections_t, get_method, eap_method_t*,
+       private_tnc_pdp_connections_t *this, chunk_t nas_id, chunk_t user_name)
+{
+       enumerator_t *enumerator;
+       entry_t *entry;
+       eap_method_t *found = NULL;
+
+       enumerator = this->list->create_enumerator(this->list);
+       while (enumerator->enumerate(enumerator, &entry))
+       {
+               if (equals_entry(entry, nas_id, user_name))
+               {
+                       found = entry->method;
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+
+       dbg_nas_user(nas_id, user_name, !found, "found");
+       return found;
+}
+
+METHOD(tnc_pdp_connections_t, destroy, void,
+       private_tnc_pdp_connections_t *this)
+{
+       this->list->destroy_function(this->list, (void*)free_entry);
+       free(this);
+}
+
+/*
+ * see header file
+ */
+tnc_pdp_connections_t *tnc_pdp_connections_create(void)
+{
+       private_tnc_pdp_connections_t *this;
+
+       INIT(this,
+               .public = {
+                       .add = _add,
+                       .remove = _remove_,
+                       .get_method = _get_method,
+                       .destroy = _destroy,
+               },
+               .list = linked_list_create(),
+       );
+
+       return &this->public;
+}
+
diff --git a/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.h b/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.h
new file mode 100644 (file)
index 0000000..6526897
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 Andreas Steffen
+ * HSR 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 tnc_pdp_connections tnc_pdp_connections
+ * @{ @ingroup tnc_pdp
+ */
+
+#ifndef TNC_PDP_CONNECTIONS_H_
+#define TNC_PDP_CONNECTIONS_H_
+
+typedef struct tnc_pdp_connections_t tnc_pdp_connections_t;
+
+#include <library.h>
+#include <sa/authenticators/eap/eap_method.h>
+
+/**
+ * Public interface of a tnc_pdp_connections object
+ */
+struct tnc_pdp_connections_t {
+
+       /**
+        * Register a new TNC PEP RADIUS Connection
+        *
+        * @param nas_id                NAS identifier of Policy Enforcement Point
+        * @param user_name             User name of TNC Client
+        * @param method                EAP method state for this TNC PEP Connection
+        */
+       void (*add)(tnc_pdp_connections_t *this, chunk_t nas_id, chunk_t user_name,
+                               eap_method_t *method);
+
+       /**
+        * Remove a TNC PEP RADIUS Connection
+        *
+        * @param nas_id                NAS identifier of Policy Enforcement Point
+        * @param user_name             User name of TNC Client
+        */
+       void (*remove)(tnc_pdp_connections_t *this, chunk_t nas_id,
+                                  chunk_t user_name);
+
+       /**
+        * Get the EAP method of a registered TNC PEP RADIUS Connection
+        *
+        * @param nas_id                NAS identifier of Policy Enforcement Point
+        * @param user_name             User name of TNC Client
+        * @return                              EAP method for this connection or NULL if not found
+        */
+       eap_method_t* (*get_method)(tnc_pdp_connections_t *this, chunk_t nas_id,
+                                                               chunk_t user_name);
+
+       /**
+        * Destroys a tnc_pdp_connections_t object.
+        */
+       void (*destroy)(tnc_pdp_connections_t *this);
+};
+
+/**
+ * Create a tnc_pdp_connections_t instance
+ */
+tnc_pdp_connections_t* tnc_pdp_connections_create(void);
+
+#endif /** TNC_PDP_CONNECTIONS_PLUGIN_H_ @}*/