implemented IKEV2 EAP-SIM server and client test module that use triplets stored...
authorAndreas Steffen <andreas.steffen@strongswan.org>
Mon, 4 Feb 2008 14:52:06 +0000 (14:52 -0000)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Mon, 4 Feb 2008 14:52:06 +0000 (14:52 -0000)
13 files changed:
src/charon/Makefile.am
src/charon/sa/authenticators/eap/eap_sim.c
src/charon/sa/authenticators/eap/eap_sim.h
src/charon/sa/authenticators/eap/sim/eap_sim_file.c [new file with mode: 0644]
testing/tests/ikev2/rw-eap-sim-rsa/description.txt [new file with mode: 0644]
testing/tests/ikev2/rw-eap-sim-rsa/evaltest.dat [new file with mode: 0644]
testing/tests/ikev2/rw-eap-sim-rsa/hosts/carol/etc/ipsec.conf [new file with mode: 0755]
testing/tests/ikev2/rw-eap-sim-rsa/hosts/carol/etc/ipsec.d/triplets.dat [new file with mode: 0644]
testing/tests/ikev2/rw-eap-sim-rsa/hosts/moon/etc/ipsec.conf [new file with mode: 0755]
testing/tests/ikev2/rw-eap-sim-rsa/hosts/moon/etc/ipsec.d/triplets.dat [new file with mode: 0644]
testing/tests/ikev2/rw-eap-sim-rsa/posttest.dat [new file with mode: 0644]
testing/tests/ikev2/rw-eap-sim-rsa/pretest.dat [new file with mode: 0644]
testing/tests/ikev2/rw-eap-sim-rsa/test.conf [new file with mode: 0644]

index 9111191..1d70223 100644 (file)
@@ -104,7 +104,8 @@ endif
 
 INCLUDES = -I${linuxdir} -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon -I$(top_srcdir)/src/stroke
 AM_CFLAGS = -rdynamic -DIPSEC_CONFDIR=\"${confdir}\" -DIPSEC_DIR=\"${ipsecdir}\" -DIPSEC_PIDDIR=\"${piddir}\" \
-       -DIPSEC_EAPDIR=\"${eapdir}\" -DIPSEC_BACKENDDIR=\"${backenddir}\" -DIPSEC_INTERFACEDIR=\"${interfacedir}\"
+       -DIPSEC_EAPDIR=\"${eapdir}\" -DIPSEC_BACKENDDIR=\"${backenddir}\" -DIPSEC_INTERFACEDIR=\"${interfacedir}\" \
+       -DSIM_READER_LIB=\"${simreader}\"
 charon_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la -lgmp -lpthread -lm -ldl
 
 if USE_LIBCURL
@@ -126,6 +127,10 @@ if USE_EAP_SIM
   eap_LTLIBRARIES += libcharon-eapsim.la
   libcharon_eapsim_la_SOURCES = sa/authenticators/eap/eap_sim.h sa/authenticators/eap/eap_sim.c
   libcharon_eapsim_la_LDFLAGS = -module
+
+  plugin_LTLIBRARIES = libcharon-eapsim-file.la
+  libcharon_eapsim_file_la_SOURCES = sa/authenticators/eap/sim/eap_sim_file.c
+  libcharon_eapsim_file_la_LDFLAGS = -module
 endif
 
 if USE_EAP_MD5
index c9eb5ce..90898fb 100644 (file)
 
 #define MAX_TRIES 3
 
+/* number of triplets for one authentication */
+#define TRIPLET_COUNT 3
+
+typedef enum sim_subtype_t sim_subtype_t;
+
+/**
+ * Subtypes of SIM messages
+ */
+enum sim_subtype_t {
+       SIM_START = 10,
+       SIM_CHALLENGE = 11,
+       SIM_NOTIFICATION = 12,
+       SIM_CLIENT_ERROR = 14,
+};
+
 ENUM(sim_subtype_names, SIM_START, SIM_CLIENT_ERROR,
        "SIM_START",
        "SIM_CHALLENGE",
@@ -37,6 +52,40 @@ ENUM(sim_subtype_names, SIM_START, SIM_CLIENT_ERROR,
        "SIM_CLIENT_ERROR",
 );
 
+typedef enum sim_attribute_t sim_attribute_t;
+
+/**
+ * Attributes in SIM messages
+ */
+enum sim_attribute_t {
+       /** defines the end of attribute list */
+       AT_END = -1,
+       AT_RAND = 1,
+       AT_AUTN = 2,
+       AT_RES = 3,
+       AT_AUTS = 4,
+       AT_PADDING = 6,
+       AT_NONCE_MT = 7,
+       AT_PERMANENT_ID_REQ = 10,
+       AT_MAC = 11,
+       AT_NOTIFICATION = 12,
+       AT_ANY_ID_REQ = 13,
+       AT_IDENTITY = 14,
+       AT_VERSION_LIST = 15,
+       AT_SELECTED_VERSION = 16,
+       AT_FULLAUTH_ID_REQ = 17,
+       AT_COUNTER = 19,
+       AT_COUNTER_TOO_SMALL = 20,
+       AT_NONCE_S = 21,
+       AT_CLIENT_ERROR_CODE = 22,
+       AT_IV = 129,
+       AT_ENCR_DATA = 130,
+       AT_NEXT_PSEUDONYM = 132,
+       AT_NEXT_REAUTH_ID = 133,
+       AT_CHECKCODE = 134,
+       AT_RESULT_IND = 135,
+};
+
 ENUM_BEGIN(sim_attribute_names, AT_END, AT_CLIENT_ERROR_CODE,
        "AT_END",
        "AT_0",
@@ -96,6 +145,11 @@ struct private_eap_sim_t {
        sim_algo_t alg;
        
        /**
+        * libraries get_triplet() function returning a triplet
+        */
+       sim_get_triplet_t get_triplet;
+       
+       /**
         * handle of the loaded library
         */
        void *handle;
@@ -106,6 +160,16 @@ struct private_eap_sim_t {
        int tries;
        
        /**
+        * unique EAP identifier
+        */
+       u_int8_t identifier;
+       
+       /**
+        * EAP message type this role sends
+        */
+       u_int8_t type;
+       
+       /**
         * version this implementation uses
         */
        chunk_t version;
@@ -121,6 +185,11 @@ struct private_eap_sim_t {
        chunk_t nonce;
        
        /**
+        * concatenated SRES values
+        */
+       chunk_t sreses;
+       
+       /**
         * k_encr key derived from MK
         */
        chunk_t k_encr;
@@ -147,6 +216,10 @@ struct private_eap_sim_t {
 #define MAC_LEN 16
 /** length of the AT_RAND value */
 #define RAND_LEN 16
+/** length of Kc */
+#define KC_LEN 8
+/** length of SRES */
+#define SRES_LEN 4
 /** length of the k_encr key */
 #define KENCR_LEN 16
 /** length of the k_auth key */
@@ -156,6 +229,7 @@ struct private_eap_sim_t {
 /** length of the EMSK */
 #define EMSK_LEN 64
 
+static char version[] = {0x00,0x01};
 /* client error codes used in AT_CLIENT_ERROR_CODE */
 char client_error_general_buf[] = {0x00, 0x01};
 char client_error_unsupported_buf[] = {0x00, 0x02};
@@ -229,7 +303,7 @@ static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier
        chunk_t mac_data = chunk_empty;
        
        /* write EAP header, skip length bytes */
-       *pos.ptr++ = EAP_RESPONSE;
+       *pos.ptr++ = this->type;
        *pos.ptr++ = identifier;
        pos.ptr += 2;
        pos.len -= 4;
@@ -263,6 +337,7 @@ static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier
                                break;
                        }
                        case AT_IDENTITY:
+                       case AT_VERSION_LIST:
                        {
                                u_int16_t act_len = data.len;
                                /* align up to four byte */
@@ -346,8 +421,8 @@ static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier
 /**
  * process an EAP-SIM/Request/Start message
  */
-static status_t process_start(private_eap_sim_t *this, eap_payload_t *in,
-                                                         eap_payload_t **out)
+static status_t peer_process_start(private_eap_sim_t *this, eap_payload_t *in,
+                                                                  eap_payload_t **out)
 {
        chunk_t message, data;
        sim_attribute_t attribute, include_id = AT_END;
@@ -440,18 +515,56 @@ static status_t process_start(private_eap_sim_t *this, eap_payload_t *in,
 }
 
 /**
+ * derive EAP keys from kc
+ */
+static void derive_keys(private_eap_sim_t *this, chunk_t kcs)
+{
+       chunk_t tmp, mk;
+       hasher_t *hasher;
+       prf_t *prf;
+       int i;
+
+       /* build MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
+       tmp = chunk_cata("ccccc", this->peer->get_encoding(this->peer), kcs,
+                                        this->nonce, this->version_list, this->version);
+       hasher = hasher_create(HASH_SHA1);
+       mk = chunk_alloca(hasher->get_hash_size(hasher));
+       hasher->get_hash(hasher, tmp, mk.ptr);
+       hasher->destroy(hasher);
+       DBG3(DBG_IKE, "MK = SHA1(%B\n) = %B", &tmp, &mk);
+       
+       /* K_encr | K_auth | MSK | EMSK = prf() | prf() | prf() | prf()
+        * FIPS PRF has 320 bit block size, we need 160 byte for keys
+        *  => run prf four times */
+       prf = prf_create(PRF_FIPS_SHA1_160);
+       prf->set_key(prf, mk);
+       tmp = chunk_alloca(prf->get_block_size(prf) * 4);
+       for (i = 0; i < 4; i++)
+       {
+               prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * i);
+       }
+       prf->destroy(prf);
+       chunk_free(&this->k_encr);
+       chunk_free(&this->k_auth);
+       chunk_free(&this->msk);
+       chunk_free(&this->emsk);
+       chunk_split(tmp, "aaaa", KENCR_LEN, &this->k_encr, KAUTH_LEN, &this->k_auth,
+                               MSK_LEN, &this->msk, EMSK_LEN, &this->emsk);
+       DBG3(DBG_IKE, "K_encr %B\nK_auth %B\nMSK %B\nEMSK %B",
+                &this->k_encr, &this->k_auth, &this->msk, &this->emsk);
+}
+
+/**
  * process an EAP-SIM/Request/Challenge message
  */
-static status_t process_challenge(private_eap_sim_t *this, eap_payload_t *in,
-                                                                 eap_payload_t **out)
+static status_t peer_process_challenge(private_eap_sim_t *this,
+                                                                          eap_payload_t *in, eap_payload_t **out)
 {
-       chunk_t message, data, tmp, kcs, kc, sreses, sres, mk;
+       chunk_t message, data, tmp, kcs, kc, sreses, sres;
        sim_attribute_t attribute;
-       u_int8_t identifier, i;
+       u_int8_t identifier;
        chunk_t mac = chunk_empty, rands = chunk_empty;
        signer_t *signer;
-       hasher_t *hasher;
-       prf_t *prf;
        
        if (this->tries-- <= 0)
        {
@@ -541,7 +654,7 @@ static status_t process_challenge(private_eap_sim_t *this, eap_payload_t *in,
                
                if (this->alg(rands.ptr, RAND_LEN, sres.ptr, &sres_len, kc.ptr, &kc_len))
                {
-                       DBG1(DBG_IKE, "unable to get triplets from SIM");
+                       DBG1(DBG_IKE, "unable to get EAP-SIM triplet");
                        *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
                                                                 AT_CLIENT_ERROR_CODE, client_error_general,
                                                                 AT_END);
@@ -554,34 +667,7 @@ static status_t process_challenge(private_eap_sim_t *this, eap_payload_t *in,
                rands = chunk_skip(rands, RAND_LEN);
        }
        
-       /* build MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
-       tmp = chunk_cata("ccccc", this->peer->get_encoding(this->peer), kcs,
-                                        this->nonce, this->version_list, this->version);
-       hasher = hasher_create(HASH_SHA1);
-       mk = chunk_alloca(hasher->get_hash_size(hasher));
-       hasher->get_hash(hasher, tmp, mk.ptr);
-       hasher->destroy(hasher);
-       DBG3(DBG_IKE, "MK = SHA1(%B\n) = %B", &tmp, &mk);
-       
-       /* K_encr | K_auth | MSK | EMSK = prf() | prf() | prf() | prf()
-        * FIPS PRF has 320 bit block size, we need 160 byte for keys
-        *  => run prf four times */
-       prf = prf_create(PRF_FIPS_SHA1_160);
-       prf->set_key(prf, mk);
-       tmp = chunk_alloca(prf->get_block_size(prf) * 4);
-       for (i = 0; i < 4; i++)
-       {
-               prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * i);
-       }
-       prf->destroy(prf);
-       chunk_free(&this->k_encr);
-       chunk_free(&this->k_auth);
-       chunk_free(&this->msk);
-       chunk_free(&this->emsk);
-       chunk_split(tmp, "aaaa", KENCR_LEN, &this->k_encr, KAUTH_LEN, &this->k_auth,
-                               MSK_LEN, &this->msk, EMSK_LEN, &this->emsk);
-       DBG3(DBG_IKE, "K_encr %B\nK_auth %B\nMSK %B\nEMSK %B",
-                &this->k_encr, &this->k_auth, &this->msk, &this->emsk);
+       derive_keys(this, kcs);
        
        /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT"  */
        signer = signer_create(AUTH_HMAC_SHA1_128);
@@ -606,10 +692,144 @@ static status_t process_challenge(private_eap_sim_t *this, eap_payload_t *in,
 }
 
 /**
+ * process an EAP-SIM/Response/Challenge message
+ */
+static status_t server_process_challenge(private_eap_sim_t *this,
+                                                                            eap_payload_t *in, eap_payload_t **out)
+{
+       chunk_t message, data;
+       sim_attribute_t attribute;
+       chunk_t mac = chunk_empty, tmp;
+       signer_t *signer;
+       
+       message = in->get_data(in);
+       read_header(&message);
+
+       while ((attribute = read_attribute(&message, &data)) != AT_END)
+       {
+               switch (attribute)
+               {
+                       case AT_MAC:
+                               /* MAC has two reserved bytes */
+                               if (data.len == MAC_LEN + 2)
+                               {       /* clone and zero MAC for verification */
+                                       mac = chunk_clonea(chunk_skip(data, 2));
+                                       memset(data.ptr, 0, data.len);
+                               }
+                               break;
+                       default:
+                               DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
+                                        sim_attribute_names, attribute);
+                               break;
+               }
+       }
+       if (!mac.ptr)
+       {
+               DBG1(DBG_IKE, "no valid AT_MAC attribute received");
+               return FAILED;
+       }
+       /* verify AT_MAC attribute, signature is over "EAP packet | n*SRES"  */
+       signer = signer_create(AUTH_HMAC_SHA1_128);
+       signer->set_key(signer, this->k_auth);
+       tmp = chunk_cata("cc", in->get_data(in), this->sreses);
+       if (!signer->verify_signature(signer, tmp, mac))
+       {
+               DBG1(DBG_IKE, "AT_MAC verification failed");
+               signer->destroy(signer);
+               return FAILED;
+       }
+       signer->destroy(signer);
+       return SUCCESS;
+}
+
+/**
+ * process an EAP-SIM/Response/Start message
+ */
+static status_t server_process_start(private_eap_sim_t *this,
+                                                                        eap_payload_t *in, eap_payload_t **out)
+{
+       chunk_t message, data;
+       sim_attribute_t attribute;
+       bool supported = FALSE;
+       chunk_t rands, rand, kcs, kc, sreses, sres;
+       char id[64];
+       int len, i, rand_len, kc_len, sres_len;
+       
+       message = in->get_data(in);
+       read_header(&message);
+
+       while ((attribute = read_attribute(&message, &data)) != AT_END)
+       {
+               switch (attribute)
+               {
+                       case AT_NONCE_MT:
+                               if (data.len == NONCE_LEN + 2)
+                               {
+                                       this->nonce = chunk_clone(chunk_skip(data, 2));
+                               }
+                               break;
+                       case AT_SELECTED_VERSION:
+                               if (chunk_equals(data, this->version))
+                               {
+                                       supported = TRUE;
+                               }
+                               break;
+                       default:
+                               DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
+                                        sim_attribute_names, attribute);
+                               break;
+               }
+       }
+       if (!supported || !this->nonce.ptr)
+       {
+               DBG1(DBG_IKE, "received incomplete EAP-SIM/Response/Start");
+               return FAILED;
+       }
+       len = snprintf(id, sizeof(id), "%D", this->peer);
+       if (len > sizeof(id) || len < 0)
+       {
+               return FAILED;
+       }
+       
+       /* read triplets from provider */
+       rand = rands = chunk_alloca(RAND_LEN * TRIPLET_COUNT);
+       kc = kcs = chunk_alloca(KC_LEN * TRIPLET_COUNT);
+       sres = sreses = chunk_alloca(SRES_LEN * TRIPLET_COUNT);
+       rands.len = 0;
+       kcs.len = 0;
+       sreses.len = 0;
+       for (i = 0; i < TRIPLET_COUNT; i++)
+       {
+               rand_len = RAND_LEN;
+               kc_len = KC_LEN;
+               sres_len = SRES_LEN;
+               if (this->get_triplet(id, rand.ptr, &rand_len, sres.ptr, &sres_len,
+                                                         kc.ptr, &kc_len))
+               {
+                       DBG1(DBG_IKE, "getting EAP-SIM triplet %d failed", i);
+                       return FAILED;
+               }
+               rands.len += rand_len;
+               kcs.len += kc_len;
+               sreses.len += sres_len;
+               rand = chunk_skip(rand, rand_len);
+               kc = chunk_skip(kc, kc_len);
+               sres = chunk_skip(sres, sres_len);
+       }
+       derive_keys(this, kcs);
+       
+       /* build MAC over "EAP packet | NONCE_MT" */
+       *out = build_payload(this, this->identifier++, SIM_CHALLENGE, AT_RAND,
+                                                rands, AT_MAC, this->nonce, AT_END);
+       this->sreses = chunk_clone(sreses);
+       return NEED_MORE;
+}
+
+/**
  * process an EAP-SIM/Request/Notification message
  */
-static status_t process_notification(private_eap_sim_t *this, eap_payload_t *in,
-                                                                        eap_payload_t **out)
+static status_t peer_process_notification(private_eap_sim_t *this,
+                                                                                 eap_payload_t *in, eap_payload_t **out)
 {
        chunk_t message, data;
        sim_attribute_t attribute;
@@ -656,12 +876,44 @@ static status_t process_notification(private_eap_sim_t *this, eap_payload_t *in,
        return NEED_MORE;
 }
 
+/**
+ * Process a client error
+ */
+static status_t server_process_client_error(private_eap_sim_t *this,
+                                                                                       eap_payload_t *in, eap_payload_t **out)
+{
+       chunk_t message, data;
+       sim_attribute_t attribute;
+       
+       message = in->get_data(in);
+       read_header(&message);
+
+       while ((attribute = read_attribute(&message, &data)) != AT_END)
+       {
+               if (attribute == AT_CLIENT_ERROR_CODE)
+               {
+                       u_int16_t code = 0;
+                       if (data.len == 2)
+                       {
+                               code = ntohs(*(u_int16_t*)data.ptr);
+                       }
+                       DBG1(DBG_IKE, "received %N error %d",
+                                sim_attribute_names, attribute, code);
+               }
+               else
+               {
+                       DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
+                                sim_attribute_names, attribute);
+               }
+       }
+       return FAILED;
+}
 
 /**
  * Implementation of eap_method_t.process for the peer
  */
-static status_t process(private_eap_sim_t *this,
-                                               eap_payload_t *in, eap_payload_t **out)
+static status_t peer_process(private_eap_sim_t *this,
+                                                        eap_payload_t *in, eap_payload_t **out)
 {
        sim_subtype_t type;
        chunk_t message;
@@ -672,11 +924,11 @@ static status_t process(private_eap_sim_t *this,
        switch (type)
        {
                case SIM_START:
-                       return process_start(this, in, out);
+                       return peer_process_start(this, in, out);
                case SIM_CHALLENGE:
-                       return process_challenge(this, in, out);
+                       return peer_process_challenge(this, in, out);
                case SIM_NOTIFICATION:
-                       return process_notification(this, in, out);
+                       return peer_process_notification(this, in, out);
                default:
                        DBG1(DBG_IKE, "unable to process EAP_SIM subtype %N",
                                 sim_subtype_names, type);
@@ -687,15 +939,55 @@ static status_t process(private_eap_sim_t *this,
 }
 
 /**
+ * Implementation of eap_method_t.process for the server
+ */
+static status_t server_process(private_eap_sim_t *this,
+                                                        eap_payload_t *in, eap_payload_t **out)
+{
+       sim_subtype_t type;
+       chunk_t message;
+       
+       message = in->get_data(in);
+       type = read_header(&message);
+       
+       switch (type)
+       {
+               case SIM_START:
+                       return server_process_start(this, in, out);
+               case SIM_CHALLENGE:
+                       return server_process_challenge(this, in, out);
+               case SIM_CLIENT_ERROR:
+                       return server_process_client_error(this, in, out);
+               default:
+                       DBG1(DBG_IKE, "unable to process EAP_SIM subtype %N",
+                                sim_subtype_names, type);
+                       return FAILED;
+       }
+}
+
+/**
  * Implementation of eap_method_t.initiate for the peer
  */
-static status_t initiate(private_eap_sim_t *this, eap_payload_t **out)
+static status_t peer_initiate(private_eap_sim_t *this, eap_payload_t **out)
 {
        /* peer never initiates */
        return FAILED;
 }
 
 /**
+ * Implementation of eap_method_t.initiate for the server
+ */
+static status_t server_initiate(private_eap_sim_t *this, eap_payload_t **out)
+{
+       /* version_list to derive MK, no padding */
+       this->version_list = chunk_clone(this->version);
+       /* build_payloads adds padding itself */
+       *out = build_payload(this, this->identifier++, SIM_START,
+                                                AT_VERSION_LIST, this->version, AT_END);
+       return NEED_MORE;
+}
+
+/**
  * Implementation of eap_method_t.get_type.
  */
 static eap_type_t get_type(private_eap_sim_t *this, u_int32_t *vendor)
@@ -732,6 +1024,7 @@ static void destroy(private_eap_sim_t *this)
 {
        dlclose(this->handle);
        chunk_free(&this->nonce);
+       chunk_free(&this->sreses);
        chunk_free(&this->version_list);
        chunk_free(&this->k_auth);
        chunk_free(&this->k_encr);
@@ -748,60 +1041,85 @@ eap_sim_t *eap_create(eap_role_t role,
 {
        private_eap_sim_t *this;
        randomizer_t *randomizer;
-       static char version[] = {0x00,0x01};
-       
-       if (role != EAP_PEER)
-       {
-               return NULL;
-       }       
-       this = malloc_thing(private_eap_sim_t);
+       void *symbol;
+       char *name;
+       
+       this = malloc_thing(private_eap_sim_t);
+       this->alg = NULL;
+       this->get_triplet = NULL;
+       this->nonce = chunk_empty;
+       this->sreses = chunk_empty;
+       this->peer = peer;
+       this->tries = MAX_TRIES;
+       this->version.ptr = version;
+       this->version.len = sizeof(version);
+       this->version_list = chunk_empty;
+       this->k_auth = chunk_empty;
+       this->k_encr = chunk_empty;
+       this->msk = chunk_empty;
+       this->emsk = chunk_empty;
+       this->identifier = random();
        
        this->handle = dlopen(SIM_READER_LIB, RTLD_LAZY);
        if (this->handle == NULL)
        {
-               DBG1(DBG_IKE, "unable to open SIM reader '%s'", SIM_READER_LIB);                
+               DBG1(DBG_IKE, "unable to open SIM reader '%s'", SIM_READER_LIB);
                free(this);
                return NULL;
        }
-       this->alg = dlsym(this->handle, SIM_READER_ALG);
-       if (this->alg == NULL)
+       switch (role)
        {
-               DBG1(DBG_IKE, "unable to open SIM reader function '%s' in '%s'",
-                        SIM_READER_ALG, SIM_READER_LIB);
+               case EAP_PEER:
+                       name = SIM_READER_ALG;
+                       break;
+               case EAP_SERVER:
+                       name = SIM_READER_GET_TRIPLET;
+                       break;
+               default:        
+                       free(this);
+                       return NULL;
+       }
+       symbol = dlsym(this->handle, name);
+       if (symbol == NULL)
+       {
+               DBG1(DBG_IKE, "unable to open SIM function '%s' in '%s'",
+                        name, SIM_READER_LIB);
                dlclose(this->handle);
                free(this);
                return NULL;
        }
-       
-       randomizer = randomizer_create();
-       if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_LEN,
-                                                                                                &this->nonce))
+       switch (role)
        {
-               DBG1(DBG_IKE, "unable to generate NONCE for EAP_SIM");          
-               randomizer->destroy(randomizer);
-               free(this);
-               return NULL;
+               case EAP_SERVER:
+                       this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))server_initiate;
+                       this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))server_process;
+                       this->get_triplet = symbol;
+                       this->type = EAP_REQUEST;
+                       break;
+               case EAP_PEER:
+                       this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))peer_initiate;
+                       this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))peer_process;
+                       this->alg = symbol;
+                       this->type = EAP_RESPONSE;
+                       randomizer = randomizer_create();
+                       if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_LEN,
+                                                                                                                &this->nonce))
+                       {
+                               DBG1(DBG_IKE, "unable to generate NONCE for EAP_SIM");          
+                               randomizer->destroy(randomizer);
+                               free(this);
+                               return NULL;
+                       }
+                       randomizer->destroy(randomizer);
+                       break;
+               default:
+                       free(this);
+                       return NULL;
        }
-       randomizer->destroy(randomizer);
-       
-       /* public functions */
-       this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
-       this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
        this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
        this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
        this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
        this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
        
-       /* private data */
-       this->peer = peer;
-       this->tries = MAX_TRIES;
-       this->version.ptr = version;
-       this->version.len = sizeof(version);
-       this->version_list = chunk_empty;
-       this->k_auth = chunk_empty;
-       this->k_encr = chunk_empty;
-       this->msk = chunk_empty;
-       this->emsk = chunk_empty;
-       
        return &this->public;
 }
index 10640ba..d50cf73 100644 (file)
 #define EAP_SIM_H_
 
 typedef struct eap_sim_t eap_sim_t;
-typedef enum sim_subtype_t sim_subtype_t;
-typedef enum sim_attribute_t sim_attribute_t;
 
 #include <sa/authenticators/eap/eap_method.h>
 
-/**
- * Subtypes of SIM messages
- */
-enum sim_subtype_t {
-       SIM_START = 10,
-       SIM_CHALLENGE = 11,
-       SIM_NOTIFICATION = 12,
-       SIM_CLIENT_ERROR = 14,
-};
-
-/**
- * enum names for sim_subtype_t
- */
-extern enum_name_t *sim_subtype_names;
-
-enum sim_attribute_t {
-       /** defines the end of attribute list */
-       AT_END = -1,
-       AT_RAND = 1,
-       AT_AUTN = 2,
-       AT_RES = 3,
-       AT_AUTS = 4,
-       AT_PADDING = 6,
-       AT_NONCE_MT = 7,
-       AT_PERMANENT_ID_REQ = 10,
-       AT_MAC = 11,
-       AT_NOTIFICATION = 12,
-       AT_ANY_ID_REQ = 13,
-       AT_IDENTITY = 14,
-       AT_VERSION_LIST = 15,
-       AT_SELECTED_VERSION = 16,
-       AT_FULLAUTH_ID_REQ = 17,
-       AT_COUNTER = 19,
-       AT_COUNTER_TOO_SMALL = 20,
-       AT_NONCE_S = 21,
-       AT_CLIENT_ERROR_CODE = 22,
-       AT_IV = 129,
-       AT_ENCR_DATA = 130,
-       AT_NEXT_PSEUDONYM = 132,
-       AT_NEXT_REAUTH_ID = 133,
-       AT_CHECKCODE = 134,
-       AT_RESULT_IND = 135,
-};
-
-/**
- * enum names for sim_subtype_t
- */
-extern enum_name_t *sim_attribute_names;
+/** the library containing with the triplet functions */
+#ifndef SIM_READER_LIB
+#error SIM_READER_LIB not specified, use --with-sim-reader option
+#endif /* SIM_READER_LIB */
 
 /** 
  * @brief Cardreaders SIM function.
@@ -93,24 +47,42 @@ typedef int (*sim_algo_t)(const unsigned char *rand, int rand_length,
                                                  unsigned char *sres, int *sres_length, 
                                                  unsigned char *kc, int *kc_length);
 
-#ifndef SIM_READER_LIB
-/** the library containing the cardreader with the SIM function */
-#error SIM_READER_LIB not specified, use --with-sim-reader option
-#endif /* SIM_READER_LIB */
-
 #ifndef SIM_READER_ALG
 /** the SIM_READER_LIB's algorithm, uses sim_algo_t signature */
 #define SIM_READER_ALG "sim_run_alg"
 #endif /* SIM_READER_ALG */
 
+/** 
+ * @brief Function to get a SIM triplet.
+ *
+ * @param identity             identity (imsi) to get a triplet for                    
+ * @param rand                 buffer to get RAND
+ * @param rand_length  size of buffer in rand, returns bytes written to RAND
+ * @param sres                 buffer to get SRES
+ * @param sres_length  size of buffer in sres, returns bytes written to SRES
+ * @param kc                   buffer to get Kc
+ * @param kc_length            size of buffer in Kc, returns bytes written to Kc
+ * @return                             zero on success
+ */
+typedef int (*sim_get_triplet_t)(char *identity,
+                                                                unsigned char *rand, int *rand_length,
+                                                                unsigned char *sres, int *sres_length, 
+                                                                unsigned char *kc, int *kc_length);
+                                                 
+#ifndef SIM_READER_GET_TRIPLET
+/** the SIM_READER_LIB's get-triplet function, uses sim_get_triplet_t signature */
+#define SIM_READER_GET_TRIPLET "sim_get_triplet"
+#endif /* SIM_READER_GET_TRIPLET */
 
 /**
  * @brief Implementation of the eap_method_t interface using EAP-SIM.
  *
  * This EAP-SIM client implementation uses another pluggable library to
- * access the SIM card. This module is specified using the SIM_READER_LIB
- * definition. The function to run the algorithm has the sim_algo_t type and
- * is named as SIM_READER_ALG is defined.
+ * access the SIM card/triplet provider. This module is specified using the
+ * SIM_READER_LIB definition. It has to privde a sim_run_alg() function to
+ * calculate a triplet (client), and/or a sim_get_triplet() function to get
+ * a triplet (server). These functions are named to the SIM_READER_ALG and
+ * the SIM_READER_GET_TRIPLET definitions.
  *
  * @b Constructors:
  *  - eap_create() of this module
@@ -129,6 +101,7 @@ struct eap_sim_t {
 /**
  * @brief Creates the EAP method EAP-SIM.
  *
+ * @param role         role of the module, client/server
  * @param server       ID of the EAP server
  * @param peer         ID of the EAP client
  * @return                     eap_sim_t object
diff --git a/src/charon/sa/authenticators/eap/sim/eap_sim_file.c b/src/charon/sa/authenticators/eap/sim/eap_sim_file.c
new file mode 100644 (file)
index 0000000..2ab45a5
--- /dev/null
@@ -0,0 +1,288 @@
+/**\r
+ * @file eap_sim.h\r
+ *\r
+ * @brief Interface of eap_sim_t.\r
+ *\r
+ */\r
+\r
+/*\r
+ * Copyright (C) 2007 Martin Willi\r
+ * Hochschule fuer Technik Rapperswil\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify it\r
+ * under the terms of the GNU General Public License as published by the\r
+ * Free Software Foundation; either version 2 of the License, or (at your\r
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.\r
+ *\r
+ * This program is distributed in the hope that it will be useful, but\r
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\r
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r
+ * for more details.\r
+ */\r
+\r
+#include <string.h>\r
+#include <stdio.h>\r
+#include <errno.h>\r
+\r
+#include <daemon.h>\r
+\r
+#define IMSI_LEN 64\r
+#define RAND_LEN 16\r
+#define SRES_LEN 4\r
+#define KC_LEN 8\r
+\r
+typedef struct triplet_t triplet_t;\r
+\r
+struct triplet_t {\r
+       unsigned char imsi[IMSI_LEN];\r
+       unsigned char rand[RAND_LEN];\r
+       unsigned char sres[SRES_LEN];\r
+       unsigned char kc[KC_LEN];\r
+};\r
+\r
+static triplet_t *triplets = NULL;\r
+static int triplet_count = 0;\r
+\r
+#define TRIPLET_FILE IPSEC_CONFDIR "/ipsec.d/triplets.dat"\r
+\r
+/**\r
+ * convert a single HEX char to its integer value\r
+ */\r
+static int hexchr(char chr)\r
+{\r
+       switch (chr)\r
+       {\r
+               case '0'...'9':\r
+                       return chr - '0';\r
+               case 'A'...'F':\r
+                       return 10 + chr - 'A';\r
+               case 'a'...'f':\r
+                       return 10 + chr - 'a';\r
+       }\r
+       return 0;\r
+}\r
+\r
+/**\r
+ * convert a HEX string into a char array bin, limited by array length len\r
+ */\r
+static void hex2bin(char *hex, unsigned char *bin, size_t len)\r
+{\r
+       char *pos;\r
+       int i, even = 1;\r
+       \r
+       pos = hex - 1;\r
+       /* find the end, as we convert bottom up */\r
+       while (TRUE)\r
+       {\r
+               switch (*(pos+1))\r
+               {\r
+                       case '0'...'9':\r
+                       case 'A'...'F':\r
+                       case 'a'...'f':\r
+                               pos++;\r
+                               continue;\r
+               }\r
+               break;\r
+       }\r
+       /* convert two hex chars into a single bin byte */\r
+       for (i = 0; pos >= hex && i < len; pos--)\r
+       {\r
+               if (even)\r
+               {\r
+                       bin[len - 1 - i] = hexchr(*pos);\r
+               }\r
+               else\r
+               {\r
+                       bin[len - 1 - i] |= 16 * hexchr(*pos);\r
+                       i++;\r
+               }\r
+               even = !even;\r
+       }\r
+}\r
+\r
+/**\r
+ * free up allocated triplets\r
+ */\r
+static void __attribute__ ((destructor)) free_triplets()\r
+{\r
+       free(triplets);\r
+}\r
+\r
+/**\r
+ * read the triplets from the file, using freeradius triplet file syntax:\r
+ * http://www.freeradius.org/radiusd/doc/rlm_sim_triplets\r
+ */\r
+static void __attribute__ ((constructor)) read_triplets()\r
+{\r
+       char line[512], *data[4], *pos;\r
+       FILE *file;\r
+       int i, nr = 0;\r
+       triplet_t *triplet;\r
+       \r
+       file = fopen(TRIPLET_FILE, "r");\r
+       if (file == NULL)\r
+       {\r
+               DBG1(DBG_CFG, "opening triplet file %s failed: %s",\r
+                        TRIPLET_FILE, strerror(errno));\r
+               return;\r
+       }\r
+       \r
+       if (triplets)\r
+       {\r
+               free(triplets);\r
+               triplets = NULL;\r
+               triplet_count = 0;\r
+       }\r
+       \r
+       /* read line by line */\r
+       while (fgets(line, sizeof(line), file))\r
+       {\r
+               nr++;\r
+               /* skip comments, empty lines */\r
+               switch (line[0])\r
+               {\r
+                       case '\n':\r
+                       case '\r':\r
+                       case '#':\r
+                       case '\0':\r
+                               continue;\r
+                       default:\r
+                               break;\r
+               }\r
+               /* read comma separated values */\r
+               pos = line;\r
+               for (i = 0; i < 4; i++)\r
+               {\r
+                       data[i] = pos;\r
+                       pos = strchr(pos, ',');\r
+                       if (pos)\r
+                       {\r
+                               *pos = '\0';\r
+                               pos++;\r
+                       }\r
+                       else if (i != 3)\r
+                       {\r
+                               DBG1(DBG_CFG, "error in triplet file, line %d", nr);\r
+                               fclose(file);\r
+                               return;\r
+                       }\r
+               }\r
+               /* allocate new triplet */\r
+               triplet_count++;\r
+               triplets = realloc(triplets, triplet_count * sizeof(triplet_t));\r
+               triplet = &triplets[triplet_count - 1];\r
+               memset(triplet, 0, sizeof(triplet_t));\r
+               \r
+               /* convert/copy triplet data */\r
+               for (i = 0; i < IMSI_LEN - 1; i++)\r
+               {\r
+                       switch (data[0][i])\r
+                       {\r
+                               case '\n':\r
+                               case '\r':\r
+                               case '\0':\r
+                                       break;\r
+                               default:\r
+                                       triplet->imsi[i] = data[0][i];\r
+                                       continue;\r
+                       }\r
+                       break;\r
+               }\r
+               hex2bin(data[1], triplet->rand, RAND_LEN);\r
+               hex2bin(data[2], triplet->sres, SRES_LEN);\r
+               hex2bin(data[3], triplet->kc, KC_LEN);\r
+               \r
+               DBG4(DBG_CFG, "triplet: imsi %b\nrand %b\nsres %b\nkc %b",\r
+                        triplet->imsi, IMSI_LEN, triplet->rand, RAND_LEN,\r
+                        triplet->sres, SRES_LEN, triplet->kc, KC_LEN);\r
+       }\r
+       fclose(file);\r
+       DBG2(DBG_CFG, "read %d triplets from %s", triplet_count, TRIPLET_FILE);\r
+}\r
+\r
+/**\r
+ * Run the sim algorithm, see eap_sim.h\r
+ */\r
+int sim_run_alg(const unsigned char *rand, int rand_length,\r
+                               unsigned char *sres, int *sres_length, \r
+                               unsigned char *kc, int *kc_length)\r
+{\r
+       int current;\r
+       \r
+       if (rand_length != RAND_LEN ||\r
+               *sres_length < SRES_LEN ||\r
+               *kc_length < KC_LEN)\r
+       {\r
+               return 1;\r
+       }\r
+       \r
+       for (current = 0; current < triplet_count; current++)\r
+       {\r
+               if (memcmp(triplets[current].rand, rand, RAND_LEN) == 0)\r
+               {\r
+                       memcpy(sres, triplets[current].sres, SRES_LEN);\r
+                       memcpy(kc, triplets[current].kc, KC_LEN);\r
+                       *sres_length = SRES_LEN;\r
+                       *kc_length = KC_LEN;\r
+                       return 0;\r
+               }\r
+       }\r
+       return 2;\r
+}\r
+\r
+/**\r
+ * Get a single triplet, see_eap_sim.h\r
+ */\r
+int sim_get_triplet(char *imsi,\r
+                                       unsigned char *rand, int *rand_length,\r
+                                       unsigned char *sres, int *sres_length, \r
+                                       unsigned char *kc, int *kc_length)\r
+{\r
+       int current;\r
+       triplet_t *triplet;\r
+       static int skip = -1;\r
+       \r
+       DBG2(DBG_CFG, "getting triplet for %s", imsi);\r
+       \r
+       if (*rand_length < RAND_LEN ||\r
+               *sres_length < SRES_LEN ||\r
+               *kc_length < KC_LEN)\r
+       {\r
+               return 1;\r
+       }\r
+       if (triplet_count == 0)\r
+       {\r
+               return 2;\r
+       }\r
+       for (current = 0; current < triplet_count; current++)\r
+       {\r
+               triplet = &triplets[current];\r
+       \r
+               if (streq(imsi, triplet->imsi))\r
+               {\r
+                       /* skip triplet if already used */\r
+                       if (skip >= current)\r
+                       {\r
+                               continue;\r
+                       }\r
+                       *rand_length = RAND_LEN;\r
+                       *sres_length = SRES_LEN;\r
+                       *kc_length = KC_LEN;\r
+                       memcpy(rand, triplet->rand, RAND_LEN);\r
+                       memcpy(sres, triplet->sres, SRES_LEN);\r
+                       memcpy(kc, triplet->kc, KC_LEN);\r
+                       /* remember used triplet */\r
+                       skip = current;\r
+                       return 0;\r
+               }\r
+       }\r
+       if (skip > -1)\r
+       {\r
+               /* no triplet left, reuse triplets */\r
+               skip = -1;\r
+               return sim_get_triplet(imsi, rand, rand_length,\r
+                                                          sres, sres_length, kc, kc_length);\r
+       }\r
+       return 2;\r
+}\r
+\r
diff --git a/testing/tests/ikev2/rw-eap-sim-rsa/description.txt b/testing/tests/ikev2/rw-eap-sim-rsa/description.txt
new file mode 100644 (file)
index 0000000..5fc75e1
--- /dev/null
@@ -0,0 +1,7 @@
+The roadwarrior <b>carol</b> sets up a connection to gateway <b>moon</b>.
+<b>carol</b> uses the <i>Extensible Authentication Protocol</i>
+in association with a GSM <i>Subscriber Identity Module</i> (<b>EAP-SIM</b>)
+to authenticate against the gateway. In this scenario triplets from the file
+<b>/etc/ipsec.d/triplets.dat</b> are used instead of a physical SIM card.
+Gateway <b>moon</b> additionaly uses an <b>RSA signature</b> to authenticate
+itself against <b>carol</b>.
diff --git a/testing/tests/ikev2/rw-eap-sim-rsa/evaltest.dat b/testing/tests/ikev2/rw-eap-sim-rsa/evaltest.dat
new file mode 100644 (file)
index 0000000..194434a
--- /dev/null
@@ -0,0 +1,10 @@
+carol::cat /var/log/daemon.log::authentication of 'moon.strongswan.org' with RSA signature successful::YES
+carol::cat /var/log/daemon.log::authentication of 'moon.strongswan.org' with EAP successful::YES
+moon::cat /var/log/daemon.log::authentication of 'carol@strongswan.org' with EAP successful::YES
+moon::ipsec statusall::rw-eapsim.*ESTABLISHED::YES
+carol::ipsec statusall::home.*ESTABLISHED::YES
+carol::ping -c 1 PH_IP_ALICE::64 bytes from PH_IP_ALICE: icmp_seq=1::YES
+moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES
+moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES
+
+
diff --git a/testing/tests/ikev2/rw-eap-sim-rsa/hosts/carol/etc/ipsec.conf b/testing/tests/ikev2/rw-eap-sim-rsa/hosts/carol/etc/ipsec.conf
new file mode 100755 (executable)
index 0000000..c2fe026
--- /dev/null
@@ -0,0 +1,23 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+config setup
+       plutostart=no
+
+conn %default
+       ikelifetime=60m
+       keylife=20m
+       rekeymargin=3m
+       keyingtries=1
+       keyexchange=ikev2
+       authby=eap
+
+conn home
+       left=PH_IP_CAROL
+       leftnexthop=%direct
+       leftid=carol@strongswan.org
+       leftfirewall=yes
+       right=PH_IP_MOON
+       rightid=@moon.strongswan.org
+       rightsubnet=10.1.0.0/16
+       rightsendcert=never
+       auto=add
diff --git a/testing/tests/ikev2/rw-eap-sim-rsa/hosts/carol/etc/ipsec.d/triplets.dat b/testing/tests/ikev2/rw-eap-sim-rsa/hosts/carol/etc/ipsec.d/triplets.dat
new file mode 100644 (file)
index 0000000..7595854
--- /dev/null
@@ -0,0 +1,3 @@
+moon.strongswan.org,100,210,310
+moon.strongswan.org,200,220,320
+moon.strongswan.org,300,230,330
diff --git a/testing/tests/ikev2/rw-eap-sim-rsa/hosts/moon/etc/ipsec.conf b/testing/tests/ikev2/rw-eap-sim-rsa/hosts/moon/etc/ipsec.conf
new file mode 100755 (executable)
index 0000000..3f88b2a
--- /dev/null
@@ -0,0 +1,24 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+config setup
+       strictcrlpolicy=no
+       plutostart=no
+
+conn %default
+       ikelifetime=60m
+       keylife=20m
+       rekeymargin=3m
+       keyingtries=1
+       keyexchange=ikev2
+
+conn rw-eapsim
+       authby=rsasig
+       eap=sim
+       left=PH_IP_MOON
+       leftsubnet=10.1.0.0/16
+       leftid=@moon.strongswan.org
+       leftcert=moonCert.pem
+       leftfirewall=yes
+       rightid=*@strongswan.org
+       right=%any
+       auto=add
diff --git a/testing/tests/ikev2/rw-eap-sim-rsa/hosts/moon/etc/ipsec.d/triplets.dat b/testing/tests/ikev2/rw-eap-sim-rsa/hosts/moon/etc/ipsec.d/triplets.dat
new file mode 100644 (file)
index 0000000..b15a1dd
--- /dev/null
@@ -0,0 +1,3 @@
+carol@strongswan.org,100,210,310
+carol@strongswan.org,200,220,320
+carol@strongswan.org,300,230,330
diff --git a/testing/tests/ikev2/rw-eap-sim-rsa/posttest.dat b/testing/tests/ikev2/rw-eap-sim-rsa/posttest.dat
new file mode 100644 (file)
index 0000000..94a4006
--- /dev/null
@@ -0,0 +1,4 @@
+moon::ipsec stop
+carol::ipsec stop
+moon::/etc/init.d/iptables stop 2> /dev/null
+carol::/etc/init.d/iptables stop 2> /dev/null
diff --git a/testing/tests/ikev2/rw-eap-sim-rsa/pretest.dat b/testing/tests/ikev2/rw-eap-sim-rsa/pretest.dat
new file mode 100644 (file)
index 0000000..23c7a62
--- /dev/null
@@ -0,0 +1,9 @@
+moon::/etc/init.d/iptables start 2> /dev/null
+carol::/etc/init.d/iptables start 2> /dev/null
+moon::cat /etc/ipsec.d/triplets.dat
+carol::cat /etc/ipsec.d/triplets.dat
+moon::ipsec start
+carol::ipsec start
+carol::sleep 1
+carol::ipsec up home
+carol::sleep 1
diff --git a/testing/tests/ikev2/rw-eap-sim-rsa/test.conf b/testing/tests/ikev2/rw-eap-sim-rsa/test.conf
new file mode 100644 (file)
index 0000000..2bd2149
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# This configuration file provides information on the
+# UML instances used for this test
+
+# All UML instances that are required for this test
+#
+UMLHOSTS="alice carol moon"
+
+# Corresponding block diagram
+#
+DIAGRAM="a-m-c.png"
+
+# UML instances on which tcpdump is to be started
+#
+TCPDUMPHOSTS="moon"
+
+# UML instances on which IPsec is started
+# Used for IPsec logging purposes
+#
+IPSECHOSTS="moon carol"