Forward Cisco Banner received from RADIUS to Unity capable clients
authorMartin Willi <martin@revosec.ch>
Tue, 12 Mar 2013 19:33:08 +0000 (20:33 +0100)
committerMartin Willi <martin@revosec.ch>
Tue, 12 Mar 2013 19:37:35 +0000 (20:37 +0100)
src/libcharon/plugins/eap_radius/eap_radius.c
src/libcharon/plugins/eap_radius/eap_radius_provider.c
src/libcharon/plugins/eap_radius/eap_radius_provider.h

index 28cd17e..62aa1fb 100644 (file)
@@ -336,12 +336,14 @@ static void process_cfg_attributes(private_eap_radius_t *this,
 {
        eap_radius_provider_t *provider;
        enumerator_t *enumerator;
+       ike_sa_t *ike_sa;
        host_t *host;
        chunk_t data;
-       int type;
+       int type, vendor;
 
+       ike_sa = charon->bus->get_sa(charon->bus);
        provider = eap_radius_provider_get();
-       if (provider)
+       if (provider && ike_sa)
        {
                enumerator = msg->create_enumerator(msg);
                while (enumerator->enumerate(enumerator, &type, &data))
@@ -356,6 +358,28 @@ static void process_cfg_attributes(private_eap_radius_t *this,
                        }
                }
                enumerator->destroy(enumerator);
+
+               enumerator = msg->create_vendor_enumerator(msg);
+               while (enumerator->enumerate(enumerator, &vendor, &type, &data))
+               {
+                       if (vendor == PEN_ALTIGA /* aka Cisco VPN3000 */)
+                       {
+                               switch (type)
+                               {
+                                       case 15: /* CVPN3000-IPSec-Banner1 */
+                                       case 36: /* CVPN3000-IPSec-Banner2 */
+                                               if (ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY))
+                                               {
+                                                       provider->add_attribute(provider, this->peer,
+                                                                                                       UNITY_BANNER, data);
+                                               }
+                                               break;
+                                       default:
+                                               break;
+                               }
+                       }
+               }
+               enumerator->destroy(enumerator);
        }
 }
 
index 6cdbb3c..83fa838 100644 (file)
@@ -70,13 +70,34 @@ struct private_eap_radius_provider_t {
 static eap_radius_provider_t *singleton = NULL;
 
 /**
- * Hashtable entry with leases
+ * Configuration attribute in an entry
+ */
+typedef struct {
+       /** type of attribute */
+       configuration_attribute_type_t type;
+       /** attribute data */
+       chunk_t data;
+} attr_t;
+
+/**
+ * Destroy an attr_t
+ */
+static void destroy_attr(attr_t *this)
+{
+       free(this->data.ptr);
+       free(this);
+}
+
+/**
+ * Hashtable entry with leases and attributes
  */
 typedef struct {
        /** identity we assigned the IP lease */
        identification_t *id;
        /** list of IP leases received from AAA, as host_t */
        linked_list_t *addrs;
+       /** list of configuration attributes, as attr_t */
+       linked_list_t *attrs;
 } entry_t;
 
 /**
@@ -86,6 +107,7 @@ static void destroy_entry(entry_t *this)
 {
        this->id->destroy(this->id);
        this->addrs->destroy_offset(this->addrs, offsetof(host_t, destroy));
+       this->attrs->destroy_function(this->attrs, (void*)destroy_attr);
        free(this);
 }
 
@@ -102,6 +124,7 @@ static entry_t* get_or_create_entry(hashtable_t *hashtable, identification_t *id
                INIT(entry,
                        .id = id->clone(id),
                        .addrs = linked_list_create(),
+                       .attrs = linked_list_create(),
                );
                hashtable->put(hashtable, entry->id, entry);
        }
@@ -113,7 +136,8 @@ static entry_t* get_or_create_entry(hashtable_t *hashtable, identification_t *id
  */
 static void put_or_destroy_entry(hashtable_t *hashtable, entry_t *entry)
 {
-       if (entry->addrs->get_count(entry->addrs) > 0)
+       if (entry->addrs->get_count(entry->addrs) > 0 ||
+               entry->attrs->get_count(entry->attrs) > 0)
        {
                hashtable->put(hashtable, entry->id, entry);
        }
@@ -170,6 +194,36 @@ static host_t* remove_addr(private_eap_radius_provider_t *this,
 }
 
 /**
+ * Insert an attribute entry to a locked claimed/unclaimed hashtable
+ */
+static void add_attr(private_eap_radius_provider_t *this,
+                                        hashtable_t *hashtable, identification_t *id, attr_t *attr)
+{
+       entry_t *entry;
+
+       entry = get_or_create_entry(hashtable, id);
+       entry->attrs->insert_last(entry->attrs, attr);
+}
+
+/**
+ * Remove the next attribute from the locked hashtable stored for given id
+ */
+static attr_t* remove_attr(private_eap_radius_provider_t *this,
+                                                  hashtable_t *hashtable, identification_t *id)
+{
+       entry_t *entry;
+       attr_t *attr = NULL;
+
+       entry = hashtable->remove(hashtable, id);
+       if (entry)
+       {
+               entry->attrs->remove_first(entry->attrs, (void**)&attr);
+               put_or_destroy_entry(hashtable, entry);
+       }
+       return attr;
+}
+
+/**
  * Clean up unclaimed leases assigned for an IKE_SA
  */
 static void release_unclaimed(private_listener_t *this, ike_sa_t *ike_sa)
@@ -276,11 +330,77 @@ METHOD(attribute_provider_t, release_address, bool,
        return FALSE;
 }
 
+/**
+ * Enumerator implementation over attributes
+ */
+typedef struct {
+       /** implements enumerator_t */
+       enumerator_t public;
+       /** list of attributes to enumerate */
+       linked_list_t *list;
+       /** currently enumerating attribute */
+       attr_t *current;
+} attribute_enumerator_t;
+
+
+METHOD(enumerator_t, attribute_enumerate, bool,
+       attribute_enumerator_t *this, configuration_attribute_type_t *type,
+       chunk_t *data)
+{
+       if (this->current)
+       {
+               destroy_attr(this->current);
+               this->current = NULL;
+       }
+       if (this->list->remove_first(this->list, (void**)&this->current) == SUCCESS)
+       {
+               *type = this->current->type;
+               *data = this->current->data;
+               return TRUE;
+       }
+       return FALSE;
+}
+
+METHOD(enumerator_t, attribute_destroy, void,
+       attribute_enumerator_t *this)
+{
+       if (this->current)
+       {
+               destroy_attr(this->current);
+       }
+       this->list->destroy_function(this->list, (void*)destroy_attr);
+       free(this);
+}
+
 METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*,
        private_eap_radius_provider_t *this, linked_list_t *pools,
        identification_t *id, linked_list_t *vips)
 {
-       return enumerator_create_empty();
+       attribute_enumerator_t *enumerator;
+       attr_t *attr;
+
+       INIT(enumerator,
+               .public = {
+                       .enumerate = (void*)_attribute_enumerate,
+                       .destroy = _attribute_destroy,
+               },
+               .list = linked_list_create(),
+       );
+
+       /* we forward attributes regardless of pool configurations */
+       this->listener.mutex->lock(this->listener.mutex);
+       while (TRUE)
+       {
+               attr = remove_attr(this, this->listener.unclaimed, id);
+               if (!attr)
+               {
+                       break;
+               }
+               enumerator->list->insert_last(enumerator->list, attr);
+       }
+       this->listener.mutex->unlock(this->listener.mutex);
+
+       return &enumerator->public;
 }
 
 METHOD(eap_radius_provider_t, add_framed_ip, void,
@@ -291,6 +411,21 @@ METHOD(eap_radius_provider_t, add_framed_ip, void,
        this->listener.mutex->unlock(this->listener.mutex);
 }
 
+METHOD(eap_radius_provider_t, add_attribute, void,
+       private_eap_radius_provider_t *this, identification_t *id,
+       configuration_attribute_type_t type, chunk_t data)
+{
+       attr_t *attr;
+
+       INIT(attr,
+               .type = type,
+               .data = chunk_clone(data),
+       );
+       this->listener.mutex->lock(this->listener.mutex);
+       add_attr(this, this->listener.unclaimed, id, attr);
+       this->listener.mutex->unlock(this->listener.mutex);
+}
+
 METHOD(eap_radius_provider_t, destroy, void,
        private_eap_radius_provider_t *this)
 {
@@ -319,6 +454,7 @@ eap_radius_provider_t *eap_radius_provider_create()
                                        .create_attribute_enumerator = _create_attribute_enumerator,
                                },
                                .add_framed_ip = _add_framed_ip,
+                               .add_attribute = _add_attribute,
                                .destroy = _destroy,
                        },
                        .listener = {
index 2be9fef..a0b4a6b 100644 (file)
@@ -21,6 +21,7 @@
 #ifndef EAP_RADIUS_PROVIDER_H_
 #define EAP_RADIUS_PROVIDER_H_
 
+#include <attributes/attributes.h>
 #include <attributes/attribute_provider.h>
 
 typedef struct eap_radius_provider_t eap_radius_provider_t;
@@ -45,6 +46,16 @@ struct eap_radius_provider_t {
                                                  host_t *ip);
 
        /**
+        * Add a configuration attribute received from RADIUS to forward.
+        *
+        * @param id                    client identity
+        * @param type                  attribute type
+        * @param data                  attribute data
+        */
+       void (*add_attribute)(eap_radius_provider_t *this, identification_t *id,
+                                                 configuration_attribute_type_t type, chunk_t data);
+
+       /**
         * Destroy a eap_radius_provider_t.
         */
        void (*destroy)(eap_radius_provider_t *this);