Merge branch 'eap-client-select'
authorTobias Brunner <tobias@strongswan.org>
Fri, 31 Aug 2012 10:23:38 +0000 (12:23 +0200)
committerTobias Brunner <tobias@strongswan.org>
Fri, 31 Aug 2012 10:25:41 +0000 (12:25 +0200)
This brings support for EAP-Nak payloads on the client (to select a
specific or supported method), and the server (via the eap-dynamic
plugin which selects a method supported/requested by the client).

20 files changed:
NEWS
configure.in
man/ipsec.conf.5.in
man/strongswan.conf.5.in
src/libcharon/Makefile.am
src/libcharon/encoding/payloads/eap_payload.c
src/libcharon/encoding/payloads/eap_payload.h
src/libcharon/plugins/eap_dynamic/Makefile.am [new file with mode: 0644]
src/libcharon/plugins/eap_dynamic/eap_dynamic.c [new file with mode: 0644]
src/libcharon/plugins/eap_dynamic/eap_dynamic.h [new file with mode: 0644]
src/libcharon/plugins/eap_dynamic/eap_dynamic_plugin.c [new file with mode: 0644]
src/libcharon/plugins/eap_dynamic/eap_dynamic_plugin.h [new file with mode: 0644]
src/libcharon/plugins/eap_peap/eap_peap_peer.c
src/libcharon/plugins/eap_ttls/eap_ttls_peer.c
src/libcharon/plugins/stroke/stroke_config.c
src/libcharon/sa/eap/eap_manager.c
src/libcharon/sa/eap/eap_manager.h
src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
src/libstrongswan/eap/eap.c
src/libstrongswan/eap/eap.h

diff --git a/NEWS b/NEWS
index 3a30817..fb963fc 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,15 @@ strongswan-5.0.1
   PAM directly anymore, but can use any XAuth backend to verify credentials,
   including xauth-pam.
 
+- An EAP-Nak payload is returned by clients if the gateway requests an EAP
+  method that the client does not support.  Clients can also request a specific
+  EAP method by configuring that method with leftauth.
+
+- The eap-dynamic plugin handles EAP-Nak payloads returned by clients and uses
+  these to select a different EAP method supported/requested by the client.
+  The plugin initially requests the first registered method or the first method
+  configured with charon.plugins.eap-dynamic.preferred.
+
 - The autotools build has been migrated to use a config.h header. strongSwan
   development headers will get installed during "make install" if
   --with-dev-headers has been passed to ./configure.
index 43fd093..f233b0a 100644 (file)
@@ -154,6 +154,7 @@ ARG_ENABL_SET([eap-tls],        [enable EAP TLS authentication module.])
 ARG_ENABL_SET([eap-ttls],       [enable EAP TTLS authentication module.])
 ARG_ENABL_SET([eap-peap],       [enable EAP PEAP authentication module.])
 ARG_ENABL_SET([eap-tnc],        [enable EAP TNC trusted network connect module.])
+ARG_ENABL_SET([eap-dynamic],    [enable dynamic EAP proxy module.])
 ARG_ENABL_SET([eap-radius],     [enable RADIUS proxy authentication module.])
 ARG_DISBL_SET([xauth-generic],  [disable generic XAuth backend.])
 ARG_ENABL_SET([xauth-eap],      [enable XAuth backend using EAP methods to verify passwords.])
@@ -922,6 +923,7 @@ ADD_PLUGIN([eap-simaka-reauth],    [c charon])
 ADD_PLUGIN([eap-md5],              [c charon nm])
 ADD_PLUGIN([eap-gtc],              [c charon nm])
 ADD_PLUGIN([eap-mschapv2],         [c charon nm])
+ADD_PLUGIN([eap-dynamic],          [c charon])
 ADD_PLUGIN([eap-radius],           [c charon])
 ADD_PLUGIN([eap-tls],              [c charon nm])
 ADD_PLUGIN([eap-ttls],             [c charon nm])
@@ -1055,6 +1057,7 @@ AM_CONDITIONAL(USE_EAP_TLS, test x$eap_tls = xtrue)
 AM_CONDITIONAL(USE_EAP_TTLS, test x$eap_ttls = xtrue)
 AM_CONDITIONAL(USE_EAP_PEAP, test x$eap_peap = xtrue)
 AM_CONDITIONAL(USE_EAP_TNC, test x$eap_tnc = xtrue)
+AM_CONDITIONAL(USE_EAP_DYNAMIC, test x$eap_dynamic = xtrue)
 AM_CONDITIONAL(USE_EAP_RADIUS, test x$eap_radius = xtrue)
 AM_CONDITIONAL(USE_XAUTH_GENERIC, test x$xauth_generic = xtrue)
 AM_CONDITIONAL(USE_XAUTH_EAP, test x$xauth_eap = xtrue)
@@ -1222,6 +1225,7 @@ AC_OUTPUT(
        src/libcharon/Makefile
        src/libcharon/plugins/eap_aka/Makefile
        src/libcharon/plugins/eap_aka_3gpp2/Makefile
+       src/libcharon/plugins/eap_dynamic/Makefile
        src/libcharon/plugins/eap_identity/Makefile
        src/libcharon/plugins/eap_md5/Makefile
        src/libcharon/plugins/eap_gtc/Makefile
index ca77ee7..837a205 100644 (file)
@@ -523,6 +523,7 @@ an optional EAP method can be appended. Currently defined methods are
 .BR eap-sim ,
 .BR eap-tls ,
 .BR eap-ttls ,
+.BR eap-dynamic ,
 and
 .BR eap-radius .
 Alternatively, IANA assigned EAP method numbers are accepted. Vendor specific
index 4fba234..8df6cf1 100644 (file)
@@ -303,6 +303,16 @@ enable loaded duplicheck plugin
 .BR charon.plugins.eap-aka-3ggp2.seq_check
 
 .TP
+.BR charon.plugins.eap-dynamic.preferred
+The preferred EAP method(s) to be used.  If it is not given the first
+registered method will be used initially.  If a comma separated list is given
+the methods are tried in the given order before trying the rest of the
+registered methods.
+.TP
+.BR charon.plugins.eap-dynamic.prefer_user " [no]"
+If enabled the EAP methods proposed in an EAP-Nak message sent by the peer are
+preferred over the methods registered locally.
+.TP
 .BR charon.plugins.eap-gtc.backend " [pam]"
 XAuth backend to be used for credential verification
 .TP
index eada68b..3869066 100644 (file)
@@ -316,6 +316,13 @@ if MONOLITHIC
 endif
 endif
 
+if USE_EAP_DYNAMIC
+  SUBDIRS += plugins/eap_dynamic
+if MONOLITHIC
+  libcharon_la_LIBADD += plugins/eap_dynamic/libstrongswan-eap-dynamic.la
+endif
+endif
+
 if USE_EAP_RADIUS
   SUBDIRS += plugins/eap_radius
 if MONOLITHIC
index 1b9a5c8..dd2e257 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2012 Tobias Brunner
  * Copyright (C) 2005-2010 Martin Willi
  * Copyright (C) 2005 Jan Hutter
  * Hochschule fuer Technik Rapperswil
@@ -20,6 +21,7 @@
 
 #include <daemon.h>
 #include <eap/eap.h>
+#include <bio/bio_writer.h>
 
 typedef struct private_eap_payload_t private_eap_payload_t;
 
@@ -217,28 +219,93 @@ METHOD(eap_payload_t, get_identifier, u_int8_t,
        return 0;
 }
 
+/**
+ * Get the current type at the given offset into this->data.
+ * @return     the new offset or 0 if failed
+ */
+static size_t extract_type(private_eap_payload_t *this, size_t offset,
+                                              eap_type_t *type, u_int32_t *vendor)
+{
+       if (this->data.len > offset)
+       {
+               *vendor = 0;
+               *type = this->data.ptr[offset];
+               if (*type != EAP_EXPANDED)
+               {
+                       return offset + 1;
+               }
+               if (this->data.len >= offset + 8)
+               {
+                       *vendor = untoh32(this->data.ptr + offset) & 0x00FFFFFF;
+                       *type = untoh32(this->data.ptr + offset + 4);
+                       return offset + 8;
+               }
+       }
+       return 0;
+}
+
 METHOD(eap_payload_t, get_type, eap_type_t,
        private_eap_payload_t *this, u_int32_t *vendor)
 {
        eap_type_t type;
 
        *vendor = 0;
-       if (this->data.len > 4)
+       if (extract_type(this, 4, &type, vendor))
        {
-               type = this->data.ptr[4];
-               if (type != EAP_EXPANDED)
-               {
-                       return type;
-               }
-               if (this->data.len >= 12)
-               {
-                       *vendor = untoh32(this->data.ptr + 4) & 0x00FFFFFF;
-                       return untoh32(this->data.ptr + 8);
-               }
+               return type;
        }
        return 0;
 }
 
+/**
+ * Type enumerator
+ */
+typedef struct {
+       /** public interface */
+       enumerator_t public;
+       /** payload */
+       private_eap_payload_t *payload;
+       /** current offset in the data */
+       size_t offset;
+} type_enumerator_t;
+
+METHOD(enumerator_t, enumerate_types, bool,
+       type_enumerator_t *this, eap_type_t *type, u_int32_t *vendor)
+{
+       this->offset = extract_type(this->payload, this->offset, type, vendor);
+       return this->offset;
+}
+
+METHOD(eap_payload_t, get_types, enumerator_t*,
+       private_eap_payload_t *this)
+{
+       type_enumerator_t *enumerator;
+       eap_type_t type;
+       u_int32_t vendor;
+       size_t offset;
+
+       offset = extract_type(this, 4, &type, &vendor);
+       if (offset && type == EAP_NAK)
+       {
+               INIT(enumerator,
+                       .public = {
+                               .enumerate = (void*)_enumerate_types,
+                               .destroy = (void*)free,
+                       },
+                       .payload = this,
+                       .offset = offset,
+               );
+               return &enumerator->public;
+       }
+       return enumerator_create_empty();
+}
+
+METHOD(eap_payload_t, is_expanded, bool,
+       private_eap_payload_t *this)
+{
+       return this->data.len > 4 ? this->data.ptr[4] == EAP_EXPANDED : FALSE;
+}
+
 METHOD2(payload_t, eap_payload_t, destroy, void,
        private_eap_payload_t *this)
 {
@@ -270,6 +337,8 @@ eap_payload_t *eap_payload_create()
                        .get_code = _get_code,
                        .get_identifier = _get_identifier,
                        .get_type = _get_type,
+                       .get_types = _get_types,
+                       .is_expanded = _is_expanded,
                        .destroy = _destroy,
                },
                .next_payload = NO_PAYLOAD,
@@ -313,15 +382,81 @@ eap_payload_t *eap_payload_create_code(eap_code_t code, u_int8_t identifier)
        return eap_payload_create_data(data);
 }
 
+/**
+ * Write the given type either expanded or not
+ */
+static void write_type(bio_writer_t *writer, eap_type_t type, u_int32_t vendor,
+                                          bool expanded)
+{
+       if (expanded)
+       {
+               writer->write_uint8(writer, EAP_EXPANDED);
+               writer->write_uint24(writer, vendor);
+               writer->write_uint32(writer, type);
+       }
+       else
+       {
+               writer->write_uint8(writer, type);
+       }
+}
+
 /*
  * Described in header
  */
-eap_payload_t *eap_payload_create_nak(u_int8_t identifier)
+eap_payload_t *eap_payload_create_nak(u_int8_t identifier, eap_type_t type,
+                                                                         u_int32_t vendor, bool expanded)
 {
-       chunk_t data;
+       enumerator_t *enumerator;
+       eap_type_t reg_type;
+       u_int32_t reg_vendor;
+       bio_writer_t *writer;
+       chunk_t length, data;
+       bool added_any = FALSE, found_vendor = FALSE;
+       eap_payload_t *payload;
+
+       writer = bio_writer_create(12);
+       writer->write_uint8(writer, EAP_RESPONSE);
+       writer->write_uint8(writer, identifier);
+       length = writer->skip(writer, 2);
+
+       write_type(writer, EAP_NAK, 0, expanded);
+
+       enumerator = charon->eap->create_enumerator(charon->eap, EAP_PEER);
+       while (enumerator->enumerate(enumerator, &reg_type, &reg_vendor))
+       {
+               if ((type && type != reg_type) ||
+                       (type && vendor && vendor != reg_vendor))
+               {       /* the preferred type is only sent if we actually find it */
+                       continue;
+               }
+               if (!reg_vendor || expanded)
+               {
+                       write_type(writer, reg_type, reg_vendor, expanded);
+                       added_any = TRUE;
+               }
+               else if (reg_vendor)
+               {       /* found vendor specifc method, but this is not an expanded Nak */
+                       found_vendor = TRUE;
+               }
+       }
+       enumerator->destroy(enumerator);
 
-       data = chunk_from_chars(EAP_RESPONSE, identifier, 0, 0, EAP_NAK);
-       htoun16(data.ptr + 2, data.len);
-       return eap_payload_create_data(data);
+       if (found_vendor)
+       {       /* request an expanded authentication type */
+               write_type(writer, EAP_EXPANDED, 0, expanded);
+               added_any = TRUE;
+       }
+       if (!added_any)
+       {       /* no methods added */
+               write_type(writer, 0, 0, expanded);
+       }
+
+       /* set length */
+       data = writer->get_buf(writer);
+       htoun16(length.ptr, data.len);
+
+       payload = eap_payload_create_data(data);
+       writer->destroy(writer);
+       return payload;
 }
 
index 52bc7ac..e8ed1c5 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2012 Tobias Brunner
  * Copyright (C) 2005-2006 Martin Willi
  * Copyright (C) 2005 Jan Hutter
  * Hochschule fuer Technik Rapperswil
@@ -82,6 +83,21 @@ struct eap_payload_t {
        eap_type_t (*get_type) (eap_payload_t *this, u_int32_t *vendor);
 
        /**
+        * Enumerate the EAP method types contained in an EAP-Nak (i.e. get_type()
+        * returns EAP_NAK).
+        *
+        * @return                      enumerator over (eap_type_t type, u_int32_t vendor)
+        */
+       enumerator_t* (*get_types) (eap_payload_t *this);
+
+       /**
+        * Check if the EAP method type is encoded in the Expanded Type format.
+        *
+        * @return                      TRUE if in Expanded Type format
+        */
+       bool (*is_expanded) (eap_payload_t *this);
+
+       /**
         * Destroys an eap_payload_t object.
         */
        void (*destroy) (eap_payload_t *this);
@@ -126,8 +142,12 @@ eap_payload_t *eap_payload_create_code(eap_code_t code, u_int8_t identifier);
  * Creates an eap_payload_t EAP_RESPONSE containing an EAP_NAK.
  *
  * @param identifier   EAP identifier to use in payload
+ * @param type                 preferred auth type, 0 to send all supported types
+ * @param vendor               vendor identifier for auth type, 0 for default
+ * @param expanded             TRUE to send an expanded Nak
  * @return                             eap_payload_t object
  */
-eap_payload_t *eap_payload_create_nak(u_int8_t identifier);
+eap_payload_t *eap_payload_create_nak(u_int8_t identifier, eap_type_t type,
+                                                                         u_int32_t vendor, bool expanded);
 
 #endif /** EAP_PAYLOAD_H_ @}*/
diff --git a/src/libcharon/plugins/eap_dynamic/Makefile.am b/src/libcharon/plugins/eap_dynamic/Makefile.am
new file mode 100644 (file)
index 0000000..0d07fbf
--- /dev/null
@@ -0,0 +1,16 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra \
+       -I$(top_srcdir)/src/libcharon
+
+AM_CFLAGS = -rdynamic
+
+if MONOLITHIC
+noinst_LTLIBRARIES = libstrongswan-eap-dynamic.la
+else
+plugin_LTLIBRARIES = libstrongswan-eap-dynamic.la
+endif
+
+libstrongswan_eap_dynamic_la_SOURCES = \
+       eap_dynamic_plugin.h eap_dynamic_plugin.c eap_dynamic.h eap_dynamic.c
+
+libstrongswan_eap_dynamic_la_LDFLAGS = -module -avoid-version
diff --git a/src/libcharon/plugins/eap_dynamic/eap_dynamic.c b/src/libcharon/plugins/eap_dynamic/eap_dynamic.c
new file mode 100644 (file)
index 0000000..d24cbd1
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * 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 "eap_dynamic.h"
+
+#include <daemon.h>
+#include <library.h>
+
+typedef struct private_eap_dynamic_t private_eap_dynamic_t;
+
+/**
+ * Private data of an eap_dynamic_t object.
+ */
+struct private_eap_dynamic_t {
+
+       /**
+        * Public authenticator_t interface.
+        */
+       eap_dynamic_t public;
+
+       /**
+        * ID of the server
+        */
+       identification_t *server;
+
+       /**
+        * ID of the peer
+        */
+       identification_t *peer;
+
+       /**
+        * Our supported EAP types (as eap_vendor_type_t*)
+        */
+       linked_list_t *types;
+
+       /**
+        * EAP types supported by peer, if any
+        */
+       linked_list_t *other_types;
+
+       /**
+        * Prefer types sent by peer
+        */
+       bool prefer_peer;
+
+       /**
+        * The proxied EAP method
+        */
+       eap_method_t *method;
+};
+
+/**
+ * Compare two eap_vendor_type_t objects
+ */
+static bool entry_matches(eap_vendor_type_t *item, eap_vendor_type_t *other)
+{
+       return item->type == other->type && item->vendor == other->vendor;
+}
+
+/**
+ * Load the given EAP method
+ */
+static eap_method_t *load_method(private_eap_dynamic_t *this,
+                                                                eap_type_t type, u_int32_t vendor)
+{
+       eap_method_t *method;
+
+       method = charon->eap->create_instance(charon->eap, type, vendor, EAP_SERVER,
+                                                                                 this->server, this->peer);
+       if (!method)
+       {
+               if (vendor)
+               {
+                       DBG1(DBG_IKE, "loading vendor specific EAP method %d-%d failed",
+                                type, vendor);
+               }
+               else
+               {
+                       DBG1(DBG_IKE, "loading %N method failed", eap_type_names, type);
+               }
+       }
+       return method;
+}
+
+/**
+ * Select the first method we can instantiate and is supported by both peers.
+ */
+static void select_method(private_eap_dynamic_t *this)
+{
+       eap_vendor_type_t *entry;
+       linked_list_t *outer = this->types, *inner = this->other_types;
+       char *who = "peer";
+
+       if (this->other_types && this->prefer_peer)
+       {
+               outer = this->other_types;
+               inner = this->types;
+               who = "us";
+       }
+
+       while (outer->remove_first(outer, (void*)&entry) == SUCCESS)
+       {
+               if (inner)
+               {
+                       if (inner->find_first(inner, (void*)entry_matches,
+                                                                 NULL, entry) != SUCCESS)
+                       {
+                               if (entry->vendor)
+                               {
+                                       DBG2(DBG_IKE, "proposed vendor specific EAP method %d-%d "
+                                                "not supported by %s, skipped", entry->type,
+                                                 entry->vendor, who);
+                               }
+                               else
+                               {
+                                       DBG2(DBG_IKE, "proposed %N method not supported by %s, "
+                                                "skipped", eap_type_names, entry->type, who);
+                               }
+                               free(entry);
+                               continue;
+                       }
+               }
+               this->method = load_method(this, entry->type, entry->vendor);
+               if (this->method)
+               {
+                       if (entry->vendor)
+                       {
+                               DBG1(DBG_IKE, "vendor specific EAP method %d-%d selected",
+                                        entry->type, entry->vendor);
+                       }
+                       else
+                       {
+                               DBG1(DBG_IKE, "%N method selected", eap_type_names,
+                                        entry->type);
+                       }
+                       free(entry);
+                       break;
+               }
+               free(entry);
+       }
+}
+
+METHOD(eap_method_t, initiate, status_t,
+       private_eap_dynamic_t *this, eap_payload_t **out)
+{
+       if (!this->method)
+       {
+               select_method(this);
+               if (!this->method)
+               {
+                       DBG1(DBG_IKE, "no supported EAP method found");
+                       return FAILED;
+               }
+       }
+       return this->method->initiate(this->method, out);
+}
+
+METHOD(eap_method_t, process, status_t,
+       private_eap_dynamic_t *this, eap_payload_t *in, eap_payload_t **out)
+{
+       eap_type_t received_type, type;
+       u_int32_t received_vendor, vendor;
+
+       received_type = in->get_type(in, &received_vendor);
+       if (received_vendor == 0 && received_type == EAP_NAK)
+       {
+               enumerator_t *enumerator;
+
+               DBG1(DBG_IKE, "received %N, selecting a different EAP method",
+                        eap_type_names, EAP_NAK);
+
+               if (this->other_types)
+               {       /* we already received a Nak or a proper response before */
+                       DBG1(DBG_IKE, "%N is not supported in this state", eap_type_names,
+                                EAP_NAK);
+                       return FAILED;
+               }
+
+               this->other_types = linked_list_create();
+               enumerator = in->get_types(in);
+               while (enumerator->enumerate(enumerator, &type, &vendor))
+               {
+                       eap_vendor_type_t *entry;
+
+                       if (!type)
+                       {
+                               DBG1(DBG_IKE, "peer does not support any other EAP methods");
+                               enumerator->destroy(enumerator);
+                               return FAILED;
+                       }
+                       INIT(entry,
+                               .type = type,
+                               .vendor = vendor,
+                       );
+                       this->other_types->insert_last(this->other_types, entry);
+               }
+               enumerator->destroy(enumerator);
+
+               /* restart with a different method */
+               this->method->destroy(this->method);
+               this->method = NULL;
+               return initiate(this, out);
+       }
+       if (!this->other_types)
+       {       /* so we don't handle EAP-Naks later */
+               this->other_types = linked_list_create();
+       }
+       if (this->method)
+       {
+               return this->method->process(this->method, in, out);
+       }
+       return FAILED;
+}
+
+METHOD(eap_method_t, get_type, eap_type_t,
+       private_eap_dynamic_t *this, u_int32_t *vendor)
+{
+       if (this->method)
+       {
+               return this->method->get_type(this->method, vendor);
+       }
+       *vendor = 0;
+       return EAP_DYNAMIC;
+}
+
+METHOD(eap_method_t, get_msk, status_t,
+       private_eap_dynamic_t *this, chunk_t *msk)
+{
+       if (this->method)
+       {
+               return this->method->get_msk(this->method, msk);
+       }
+       return FAILED;
+}
+
+METHOD(eap_method_t, get_identifier, u_int8_t,
+       private_eap_dynamic_t *this)
+{
+       if (this->method)
+       {
+               return this->method->get_identifier(this->method);
+       }
+       return 0;
+}
+
+METHOD(eap_method_t, set_identifier, void,
+       private_eap_dynamic_t *this, u_int8_t identifier)
+{
+       if (this->method)
+       {
+               this->method->set_identifier(this->method, identifier);
+       }
+}
+
+METHOD(eap_method_t, is_mutual, bool,
+       private_eap_dynamic_t *this)
+{
+       if (this->method)
+       {
+               return this->method->is_mutual(this->method);
+       }
+       return FALSE;
+}
+
+METHOD(eap_method_t, destroy, void,
+       private_eap_dynamic_t *this)
+{
+       DESTROY_IF(this->method);
+       this->types->destroy_function(this->types, (void*)free);
+       DESTROY_FUNCTION_IF(this->other_types, (void*)free);
+       this->server->destroy(this->server);
+       this->peer->destroy(this->peer);
+       free(this);
+}
+
+/**
+ * Parse preferred EAP types
+ */
+static void handle_preferred_eap_types(private_eap_dynamic_t *this,
+                                                                          char *methods)
+{
+       enumerator_t *enumerator;
+       eap_vendor_type_t *type, *entry;
+       linked_list_t *preferred;
+       char *method;
+
+       /* parse preferred EAP methods, format: type[-vendor], ... */
+       preferred = linked_list_create();
+       enumerator = enumerator_create_token(methods, ",", " ");
+       while (enumerator->enumerate(enumerator, &method))
+       {
+               type = eap_vendor_type_from_string(method);
+               if (type)
+               {
+                       preferred->insert_last(preferred, type);
+               }
+       }
+       enumerator->destroy(enumerator);
+
+       enumerator = this->types->create_enumerator(this->types);
+       while (preferred->remove_last(preferred, (void**)&type) == SUCCESS)
+       {       /* move (supported) types to the front, maintain the preferred order */
+               this->types->reset_enumerator(this->types, enumerator);
+               while (enumerator->enumerate(enumerator, &entry))
+               {
+                       if (entry_matches(entry, type))
+                       {
+                               this->types->remove_at(this->types, enumerator);
+                               this->types->insert_first(this->types, entry);
+                               break;
+                       }
+               }
+               free(type);
+       }
+       enumerator->destroy(enumerator);
+       preferred->destroy(preferred);
+}
+
+/**
+ * Get all supported EAP methods
+ */
+static void get_supported_eap_types(private_eap_dynamic_t *this)
+{
+       enumerator_t *enumerator;
+       eap_type_t type;
+       u_int32_t vendor;
+
+       enumerator = charon->eap->create_enumerator(charon->eap, EAP_SERVER);
+       while (enumerator->enumerate(enumerator, &type, &vendor))
+       {
+               eap_vendor_type_t *entry;
+
+               INIT(entry,
+                       .type = type,
+                       .vendor = vendor,
+               );
+               this->types->insert_last(this->types, entry);
+       }
+       enumerator->destroy(enumerator);
+}
+
+/*
+ * Defined in header
+ */
+eap_dynamic_t *eap_dynamic_create(identification_t *server,
+                                                                 identification_t *peer)
+{
+       private_eap_dynamic_t *this;
+       char *preferred;
+
+       INIT(this,
+               .public = {
+                       .interface = {
+                               .initiate = _initiate,
+                               .process = _process,
+                               .get_type = _get_type,
+                               .is_mutual = _is_mutual,
+                               .get_msk = _get_msk,
+                               .get_identifier = _get_identifier,
+                               .set_identifier = _set_identifier,
+                               .destroy = _destroy,
+                       },
+               },
+               .peer = peer->clone(peer),
+               .server = server->clone(server),
+               .types = linked_list_create(),
+               .prefer_peer = lib->settings->get_bool(lib->settings,
+                                       "%s.plugins.eap-dynamic.prefer_peer", FALSE, charon->name),
+       );
+
+       /* get all supported EAP methods */
+       get_supported_eap_types(this);
+       /* move preferred methods to the front */
+       preferred = lib->settings->get_str(lib->settings,
+                                       "%s.plugins.eap-dynamic.preferred", NULL, charon->name);
+       if (preferred)
+       {
+               handle_preferred_eap_types(this, preferred);
+       }
+       return &this->public;
+}
diff --git a/src/libcharon/plugins/eap_dynamic/eap_dynamic.h b/src/libcharon/plugins/eap_dynamic/eap_dynamic.h
new file mode 100644 (file)
index 0000000..35db4fa
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * 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 eap_dynamic_i eap_dynamic
+ * @{ @ingroup eap_dynamic
+ */
+
+#ifndef EAP_DYNAMIC_H_
+#define EAP_DYNAMIC_H_
+
+typedef struct eap_dynamic_t eap_dynamic_t;
+
+#include <sa/eap/eap_method.h>
+
+/**
+ * Implementation of the eap_method_t interface for a virtual EAP method that
+ * proxies other EAP methods and supports the selection of the actual method
+ * by the client.
+ */
+struct eap_dynamic_t {
+
+       /**
+        * Implemented eap_method_t interface
+        */
+       eap_method_t interface;
+};
+
+/**
+ * Create a dynamic EAP proxy serving any supported real method which is also
+ * supported (or selected) by the client.
+ *
+ * @param server       ID of the EAP server
+ * @param peer         ID of the EAP client
+ * @return                     eap_dynamic_t object
+ */
+eap_dynamic_t *eap_dynamic_create(identification_t *server,
+                                                                 identification_t *peer);
+
+#endif /** EAP_DYNAMIC_H_ @}*/
diff --git a/src/libcharon/plugins/eap_dynamic/eap_dynamic_plugin.c b/src/libcharon/plugins/eap_dynamic/eap_dynamic_plugin.c
new file mode 100644 (file)
index 0000000..d6f38b6
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * 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 "eap_dynamic_plugin.h"
+
+#include "eap_dynamic.h"
+
+#include <daemon.h>
+
+METHOD(plugin_t, get_name, char*,
+       eap_dynamic_plugin_t *this)
+{
+       return "eap-dynamic";
+}
+
+METHOD(plugin_t, get_features, int,
+       eap_dynamic_plugin_t *this, plugin_feature_t *features[])
+{
+       static plugin_feature_t f[] = {
+               PLUGIN_CALLBACK(eap_method_register, eap_dynamic_create),
+                       PLUGIN_PROVIDE(EAP_SERVER, EAP_DYNAMIC),
+       };
+       *features = f;
+       return countof(f);
+}
+
+METHOD(plugin_t, destroy, void,
+       eap_dynamic_plugin_t *this)
+{
+       free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *eap_dynamic_plugin_create()
+{
+       eap_dynamic_plugin_t *this;
+
+       INIT(this,
+               .plugin = {
+                       .get_name = _get_name,
+                       .get_features = _get_features,
+                       .destroy = _destroy,
+               },
+       );
+
+       return &this->plugin;
+}
+
diff --git a/src/libcharon/plugins/eap_dynamic/eap_dynamic_plugin.h b/src/libcharon/plugins/eap_dynamic/eap_dynamic_plugin.h
new file mode 100644 (file)
index 0000000..9b124d8
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * 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 eap_dynamic eap_dynamic
+ * @ingroup cplugins
+ *
+ * @defgroup eap_dynamic_plugin eap_dynamic_plugin
+ * @{ @ingroup eap_dynamic
+ */
+
+#ifndef EAP_DYNAMIC_PLUGIN_H_
+#define EAP_DYNAMIC_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct eap_dynamic_plugin_t eap_dynamic_plugin_t;
+
+/**
+ * EAP plugin that can use any supported EAP method the client supports or
+ * prefers to use.
+ */
+struct eap_dynamic_plugin_t {
+
+       /**
+        * implements plugin interface
+        */
+       plugin_t plugin;
+};
+
+#endif /** EAP_DYNAMIC_PLUGIN_H_ @}*/
index 72e201f..79fd667 100644 (file)
@@ -85,7 +85,7 @@ METHOD(tls_application_t, process, status_t,
                default:
                        return FAILED;
        }
-               
+
        in = eap_payload_create_data(data);
        DBG3(DBG_IKE, "%B", &data);
        chunk_free(&data);
@@ -151,7 +151,8 @@ METHOD(tls_application_t, process, status_t,
                if (!this->ph2_method)
                {
                        DBG1(DBG_IKE, "EAP method not supported");
-                       this->out = eap_payload_create_nak(in->get_identifier(in));
+                       this->out = eap_payload_create_nak(in->get_identifier(in), 0, 0,
+                                                                                          in->is_expanded(in));
                        in->destroy(in);
                        return NEED_MORE;
                }
index 767111b..00a4da3 100644 (file)
@@ -138,7 +138,7 @@ METHOD(tls_application_t, process, status_t,
                chunk_free(&avp_data);
        }
        while (eap_pos < eap_data.len);
-               
+
        in = eap_payload_create_data(eap_data);
        chunk_free(&eap_data);
        payload = (payload_t*)in;
@@ -192,7 +192,8 @@ METHOD(tls_application_t, process, status_t,
                if (!this->method)
                {
                        DBG1(DBG_IKE, "EAP method not supported");
-                       this->out = eap_payload_create_nak(in->get_identifier(in));
+                       this->out = eap_payload_create_nak(in->get_identifier(in), 0, 0,
+                                                                                          in->is_expanded(in));
                        in->destroy(in);
                        return NEED_MORE;
                }
index bf93f2c..9dcb9ba 100644 (file)
@@ -563,51 +563,20 @@ static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this,
        }
        else if (strneq(auth, "eap", 3))
        {
-               enumerator_t *enumerator;
-               char *str;
-               int i = 0, type = 0, vendor;
+               eap_vendor_type_t *type;
 
                cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
 
-               /* parse EAP string, format: eap[-type[-vendor]] */
-               enumerator = enumerator_create_token(auth, "-", " ");
-               while (enumerator->enumerate(enumerator, &str))
+               type = eap_vendor_type_from_string(auth);
+               if (type)
                {
-                       switch (i)
+                       cfg->add(cfg, AUTH_RULE_EAP_TYPE, type->type);
+                       if (type->vendor)
                        {
-                               case 1:
-                                       type = eap_type_from_string(str);
-                                       if (!type)
-                                       {
-                                               type = atoi(str);
-                                               if (!type)
-                                               {
-                                                       DBG1(DBG_CFG, "unknown EAP method: %s", str);
-                                                       break;
-                                               }
-                                       }
-                                       cfg->add(cfg, AUTH_RULE_EAP_TYPE, type);
-                                       break;
-                               case 2:
-                                       if (type)
-                                       {
-                                               vendor = atoi(str);
-                                               if (vendor)
-                                               {
-                                                       cfg->add(cfg, AUTH_RULE_EAP_VENDOR, vendor);
-                                               }
-                                               else
-                                               {
-                                                       DBG1(DBG_CFG, "unknown EAP vendor: %s", str);
-                                               }
-                                       }
-                                       break;
-                               default:
-                                       break;
+                               cfg->add(cfg, AUTH_RULE_EAP_VENDOR, type->vendor);
                        }
-                       i++;
+                       free(type);
                }
-               enumerator->destroy(enumerator);
 
                if (msg->add_conn.eap_identity)
                {
index d38754e..520c0ce 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2012 Tobias Brunner
  * Copyright (C) 2008 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
@@ -104,6 +105,44 @@ METHOD(eap_manager_t, remove_method, void,
        this->lock->unlock(this->lock);
 }
 
+/**
+ * filter the registered methods
+ */
+static bool filter_methods(uintptr_t role, eap_entry_t **entry,
+                                                  eap_type_t *type, void *in, u_int32_t *vendor)
+{
+       if ((*entry)->role != (eap_role_t)role)
+       {
+               return FALSE;
+       }
+       if ((*entry)->vendor == 0 &&
+          ((*entry)->type < 4 || (*entry)->type == EAP_EXPANDED ||
+           (*entry)->type > EAP_EXPERIMENTAL))
+       {       /* filter invalid types */
+               return FALSE;
+       }
+       if (type)
+       {
+               *type = (*entry)->type;
+       }
+       if (vendor)
+       {
+               *vendor = (*entry)->vendor;
+       }
+       return TRUE;
+}
+
+METHOD(eap_manager_t, create_enumerator, enumerator_t*,
+       private_eap_manager_t *this, eap_role_t role)
+{
+       this->lock->read_lock(this->lock);
+       return enumerator_create_cleaner(
+                               enumerator_create_filter(
+                                       this->methods->create_enumerator(this->methods),
+                                       (void*)filter_methods, (void*)(uintptr_t)role, NULL),
+                               (void*)this->lock->unlock, this->lock);
+}
+
 METHOD(eap_manager_t, create_instance, eap_method_t*,
        private_eap_manager_t *this, eap_type_t type, u_int32_t vendor,
        eap_role_t role, identification_t *server, identification_t *peer)
@@ -150,6 +189,7 @@ eap_manager_t *eap_manager_create()
                        .public = {
                                .add_method = _add_method,
                                .remove_method = _remove_method,
+                               .create_enumerator = _create_enumerator,
                                .create_instance = _create_instance,
                                .destroy = _destroy,
                        },
index 868eaef..e318ef5 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2012 Tobias Brunner
  * Copyright (C) 2008 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
@@ -54,6 +55,17 @@ struct eap_manager_t {
        void (*remove_method)(eap_manager_t *this, eap_constructor_t constructor);
 
        /**
+        * Enumerate the registered EAP authentication methods for the given role.
+        *
+        * @note Only authentication types are enumerated (e.g. EAP-Identity is not
+        * even though it is registered as method with this manager).
+        *
+        * @param role                  EAP role of methods to enumerate
+        * @return                              enumerator over (eap_type_t type, u_int32_t vendor)
+        */
+       enumerator_t* (*create_enumerator)(eap_manager_t *this, eap_role_t role);
+
+       /**
         * Create a new EAP method instance.
         *
         * @param type                  type of the EAP method
index 2c282dc..aa06440 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2012 Tobias Brunner
  * Copyright (C) 2006-2009 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
@@ -186,9 +187,9 @@ static eap_payload_t* server_initiate_eap(private_eap_authenticator_t *this,
        if (this->method)
        {
                action = "initiating";
-               type = this->method->get_type(this->method, &vendor);
                if (this->method->initiate(this->method, &out) == NEED_MORE)
                {
+                       type = this->method->get_type(this->method, &vendor);
                        if (vendor)
                        {
                                DBG1(DBG_IKE, "initiating EAP vendor type %d-%d method (id 0x%02X)",
@@ -201,6 +202,8 @@ static eap_payload_t* server_initiate_eap(private_eap_authenticator_t *this,
                        }
                        return out;
                }
+               /* type might have changed for virtual methods */
+               type = this->method->get_type(this->method, &vendor);
        }
        if (vendor)
        {
@@ -233,9 +236,10 @@ static void replace_eap_identity(private_eap_authenticator_t *this)
 static eap_payload_t* server_process_eap(private_eap_authenticator_t *this,
                                                                                 eap_payload_t *in)
 {
-       eap_type_t type, received_type;
-       u_int32_t vendor, received_vendor;
+       eap_type_t type, received_type, conf_type;
+       u_int32_t vendor, received_vendor, conf_vendor;
        eap_payload_t *out;
+       auth_cfg_t *auth;
 
        if (in->get_code(in) != EAP_RESPONSE)
        {
@@ -250,15 +254,25 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this,
        {
                if (received_vendor == 0 && received_type == EAP_NAK)
                {
-                       DBG1(DBG_IKE, "received %N, sending %N",
-                                eap_type_names, EAP_NAK, eap_code_names, EAP_FAILURE);
+                       auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
+                       conf_type = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_TYPE);
+                       conf_vendor = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_VENDOR);
+                       if ((type == EAP_IDENTITY && !vendor) ||
+                               (type == conf_type && vendor == conf_vendor))
+                       {
+                               DBG1(DBG_IKE, "received %N, sending %N",
+                                        eap_type_names, EAP_NAK, eap_code_names, EAP_FAILURE);
+                               return eap_payload_create_code(EAP_FAILURE,
+                                                                                          in->get_identifier(in));
+                       }
+                       /* virtual methods handle NAKs in process() */
                }
                else
                {
                        DBG1(DBG_IKE, "received invalid EAP response, sending %N",
                                 eap_code_names, EAP_FAILURE);
+                       return eap_payload_create_code(EAP_FAILURE, in->get_identifier(in));
                }
-               return eap_payload_create_code(EAP_FAILURE, in->get_identifier(in));
        }
 
        switch (this->method->process(this->method, in, &out))
@@ -302,6 +316,8 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this,
                        return eap_payload_create_code(EAP_SUCCESS, in->get_identifier(in));
                case FAILED:
                default:
+                       /* type might have changed for virtual methods */
+                       type = this->method->get_type(this->method, &vendor);
                        if (vendor)
                        {
                                DBG1(DBG_IKE, "EAP vendor specific method %d-%d failed for "
@@ -324,8 +340,8 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this,
 static eap_payload_t* client_process_eap(private_eap_authenticator_t *this,
                                                                                 eap_payload_t *in)
 {
-       eap_type_t type;
-       u_int32_t vendor;
+       eap_type_t type, conf_type;
+       u_int32_t vendor, conf_vendor;
        auth_cfg_t *auth;
        eap_payload_t *out;
        identification_t *id;
@@ -357,9 +373,11 @@ static eap_payload_t* client_process_eap(private_eap_authenticator_t *this,
                        this->method->destroy(this->method);
                        this->method = NULL;
                }
+               /* FIXME: sending a Nak is not correct here as EAP_IDENTITY (1) is no
+                * EAP method (types 3-253, 255) */
                DBG1(DBG_IKE, "%N not supported, sending EAP_NAK",
                         eap_type_names, type);
-               return eap_payload_create_nak(in->get_identifier(in));
+               return eap_payload_create_nak(in->get_identifier(in), 0, 0, FALSE);
        }
        if (this->method == NULL)
        {
@@ -373,11 +391,31 @@ static eap_payload_t* client_process_eap(private_eap_authenticator_t *this,
                        DBG1(DBG_IKE, "server requested %N authentication (id 0x%02X)",
                                 eap_type_names, type, in->get_identifier(in));
                }
+               auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+               conf_type = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_TYPE);
+               conf_vendor = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_VENDOR);
+               if (conf_type != EAP_NAK &&
+                  (conf_type != type || conf_vendor != vendor))
+               {
+                       if (conf_vendor)
+                       {
+                               DBG1(DBG_IKE, "requesting EAP method %d-%d, sending EAP_NAK",
+                                        conf_type, conf_vendor);
+                       }
+                       else
+                       {
+                               DBG1(DBG_IKE, "requesting %N authentication, sending EAP_NAK",
+                                        eap_type_names, conf_type);
+                       }
+                       return eap_payload_create_nak(in->get_identifier(in), conf_type,
+                                                                                 conf_vendor, in->is_expanded(in));
+               }
                this->method = load_method(this, type, vendor, EAP_PEER);
                if (!this->method)
                {
                        DBG1(DBG_IKE, "EAP method not supported, sending EAP_NAK");
-                       return eap_payload_create_nak(in->get_identifier(in));
+                       return eap_payload_create_nak(in->get_identifier(in), 0, 0,
+                                                                                 in->is_expanded(in));
                }
        }
 
index efd3ee9..1e4cf11 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2012 Tobias Brunner
  * Copyright (C) 2006 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
  * for more details.
  */
 
+#include <stdlib.h>
+#include <errno.h>
+
 #include "eap.h"
 
+#include <debug.h>
+
 ENUM(eap_code_names, EAP_REQUEST, EAP_FAILURE,
        "EAP_REQUEST",
        "EAP_RESPONSE",
@@ -51,12 +57,12 @@ ENUM_NEXT(eap_type_names, EAP_MSTLV, EAP_MSTLV, EAP_MSCHAPV2,
        "EAP_MSTLV");
 ENUM_NEXT(eap_type_names, EAP_TNC, EAP_TNC, EAP_MSTLV,
        "EAP_TNC");
-ENUM_NEXT(eap_type_names, EAP_DYNAMIC, EAP_EXPERIMENTAL, EAP_TNC,
-       "EAP_DYNAMIC",
-       "EAP_RADIUS",
+ENUM_NEXT(eap_type_names, EAP_EXPANDED, EAP_DYNAMIC, EAP_TNC,
        "EAP_EXPANDED",
-       "EAP_EXPERIMENTAL");
-ENUM_END(eap_type_names, EAP_EXPERIMENTAL);
+       "EAP_EXPERIMENTAL",
+       "EAP_RADIUS",
+       "EAP_DYNAMIC");
+ENUM_END(eap_type_names, EAP_DYNAMIC);
 
 ENUM_BEGIN(eap_type_short_names, EAP_IDENTITY, EAP_GTC,
        "ID",
@@ -80,12 +86,12 @@ ENUM_NEXT(eap_type_short_names, EAP_MSTLV, EAP_MSTLV, EAP_MSCHAPV2,
        "MSTLV");
 ENUM_NEXT(eap_type_short_names, EAP_TNC, EAP_TNC, EAP_MSTLV,
        "TNC");
-ENUM_NEXT(eap_type_short_names, EAP_DYNAMIC, EAP_EXPERIMENTAL, EAP_TNC,
-       "DYN",
-       "RAD",
+ENUM_NEXT(eap_type_short_names, EAP_EXPANDED, EAP_DYNAMIC, EAP_TNC,
        "EXP",
-       "XP");
-ENUM_END(eap_type_short_names, EAP_EXPERIMENTAL);
+       "XP",
+       "RAD",
+       "DYN");
+ENUM_END(eap_type_short_names, EAP_DYNAMIC);
 
 /*
  * See header
@@ -108,6 +114,7 @@ eap_type_t eap_type_from_string(char *name)
                {"peap",                EAP_PEAP},
                {"mschapv2",    EAP_MSCHAPV2},
                {"tnc",                 EAP_TNC},
+               {"dynamic",             EAP_DYNAMIC},
                {"radius",              EAP_RADIUS},
        };
 
@@ -120,3 +127,56 @@ eap_type_t eap_type_from_string(char *name)
        }
        return 0;
 }
+
+/*
+ * See header
+ */
+eap_vendor_type_t *eap_vendor_type_from_string(char *str)
+{
+       enumerator_t *enumerator;
+       eap_vendor_type_t *result = NULL;
+       eap_type_t type = 0;
+       u_int32_t vendor = 0;
+       char *part, *end;
+
+       /* parse EAP method string of the form: [eap-]type[-vendor] */
+       enumerator = enumerator_create_token(str, "-", " ");
+       while (enumerator->enumerate(enumerator, &part))
+       {
+               if (!type)
+               {
+                       if (streq(part, "eap"))
+                       {       /* skip 'eap' at the beginning */
+                               continue;
+                       }
+                       type = eap_type_from_string(part);
+                       if (!type)
+                       {
+                               type = strtoul(part, &end, 0);
+                               if (*end != '\0' || errno)
+                               {
+                                       DBG1(DBG_LIB, "unknown or invalid EAP method: %s", part);
+                                       break;
+                               }
+                       }
+                       continue;
+               }
+               vendor = strtoul(part, &end, 0);
+               if (*end != '\0' || errno)
+               {
+                       DBG1(DBG_LIB, "invalid EAP vendor: %s", part);
+                       type = 0;
+               }
+               break;
+       }
+       enumerator->destroy(enumerator);
+
+       if (type)
+       {
+               INIT(result,
+                       .type = type,
+                       .vendor = vendor,
+               );
+       }
+       return result;
+}
index 143232e..0e144b1 100644 (file)
@@ -1,6 +1,8 @@
 /*
+ * Copyright (C) 2012 Tobias Brunner
  * Copyright (C) 2010 Martin Willi
  * Copyright (C) 2010 revosec AG
+ * 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
@@ -23,6 +25,7 @@
 
 typedef enum eap_code_t eap_code_t;
 typedef enum eap_type_t eap_type_t;
+typedef struct eap_vendor_type_t eap_vendor_type_t;
 
 #include <library.h>
 
@@ -64,12 +67,12 @@ enum eap_type_t {
        EAP_MSCHAPV2 = 26,
        EAP_MSTLV = 33,
        EAP_TNC = 38,
-       /** select EAP method dynamically based on i.e. EAP-Identity */
-       EAP_DYNAMIC = 252,
-       /** not a method, but an implementation providing different methods */
-       EAP_RADIUS = 253,
        EAP_EXPANDED = 254,
        EAP_EXPERIMENTAL = 255,
+       /** not a method, but an implementation providing different methods */
+       EAP_RADIUS = 256,
+       /** not a method, select method dynamically based on client selection */
+       EAP_DYNAMIC = 257,
 };
 
 /**
@@ -83,6 +86,22 @@ extern enum_name_t *eap_type_names;
 extern enum_name_t *eap_type_short_names;
 
 /**
+ * Struct that stores EAP type and vendor ID
+ */
+struct eap_vendor_type_t {
+
+       /**
+        * EAP type
+        */
+       eap_type_t type;
+
+       /**
+        * Vendor Id
+        */
+       u_int32_t vendor;
+};
+
+/**
  * EAP packet format
  */
 typedef struct __attribute__((packed)) {
@@ -101,4 +120,12 @@ typedef struct __attribute__((packed)) {
  */
 eap_type_t eap_type_from_string(char *name);
 
+/**
+ * Parse a string of the form [eap-]type[-vendor].
+ *
+ * @param str          EAP method string
+ * @return                     parsed type (gets allocated), NULL if unknown or failed
+ */
+eap_vendor_type_t *eap_vendor_type_from_string(char *str);
+
 #endif /** EAP_H_ @}*/