EAP-SIM/AKA crypto helper supports key derivation for fast reauthentication
[strongswan.git] / src / charon / plugins / eap_aka / eap_aka_server.c
index 6a2f970..9f6e71a 100644 (file)
@@ -62,6 +62,16 @@ struct private_eap_aka_server_t {
         * Random value RAND
         */
        chunk_t rand;
+
+       /**
+        * EAP-AKA message we have initiated
+        */
+       simaka_subtype_t pending;
+
+       /**
+        * Did the client send a synchronize request?
+        */
+       bool synchronized;
 };
 
 /**
@@ -88,7 +98,7 @@ static status_t initiate(private_eap_aka_server_t *this, eap_payload_t **out)
        sim_provider_t *provider;
        char rand[AKA_RAND_LEN], xres[AKA_RES_LEN];
        char ck[AKA_CK_LEN], ik[AKA_IK_LEN], autn[AKA_AUTN_LEN];
-       chunk_t data;
+       chunk_t data, mk;
        bool found = FALSE;
 
        enumerator = charon->sim->create_provider_enumerator(charon->sim);
@@ -112,16 +122,20 @@ static status_t initiate(private_eap_aka_server_t *this, eap_payload_t **out)
        data = chunk_cata("cc", chunk_create(ik, AKA_IK_LEN),
                                          chunk_create(ck, AKA_CK_LEN));
        free(this->msk.ptr);
-       this->msk = this->crypto->derive_keys_full(this->crypto, this->peer, data);
+       this->msk = this->crypto->derive_keys_full(this->crypto, this->peer,
+                                                                                          data, &mk);
+       free(mk.ptr);
        this->rand = chunk_clone(chunk_create(rand, AKA_RAND_LEN));
        this->xres = chunk_clone(chunk_create(xres, AKA_RES_LEN));
 
-       message = simaka_message_create(TRUE, this->identifier++,
-                                                                       EAP_AKA, AKA_CHALLENGE);
+       message = simaka_message_create(TRUE, this->identifier++, EAP_AKA,
+                                                                       AKA_CHALLENGE, this->crypto);
        message->add_attribute(message, AT_RAND, this->rand);
        message->add_attribute(message, AT_AUTN, chunk_create(autn, AKA_AUTN_LEN));
-       *out = message->generate(message, this->crypto, chunk_empty);
+       *out = message->generate(message, chunk_empty);
        message->destroy(message);
+
+       this->pending = AKA_CHALLENGE;
        return NEED_MORE;
 }
 
@@ -135,6 +149,12 @@ static status_t process_challenge(private_eap_aka_server_t *this,
        simaka_attribute_t type;
        chunk_t data, res = chunk_empty;
 
+       if (this->pending != AKA_CHALLENGE)
+       {
+               DBG1(DBG_IKE, "received %N, but not expected",
+                        simaka_subtype_names, AKA_CHALLENGE);
+               return FAILED;
+       }
        enumerator = in->create_attribute_enumerator(in);
        while (enumerator->enumerate(enumerator, &type, &data))
        {
@@ -157,7 +177,7 @@ static status_t process_challenge(private_eap_aka_server_t *this,
        enumerator->destroy(enumerator);
 
        /* verify MAC of EAP message, AT_MAC */
-       if (!in->verify(in, this->crypto, chunk_empty))
+       if (!in->verify(in, chunk_empty))
        {
                DBG1(DBG_IKE, "AT_MAC verification failed");
                return FAILED;
@@ -183,6 +203,13 @@ static status_t process_synchronize(private_eap_aka_server_t *this,
        chunk_t data, auts = chunk_empty;
        bool found = FALSE;
 
+       if (this->synchronized)
+       {
+               DBG1(DBG_IKE, "received %N, but peer did already resynchronize",
+                        simaka_subtype_names, AKA_SYNCHRONIZATION_FAILURE);
+               return FAILED;
+       }
+
        DBG1(DBG_IKE, "received synchronization request, retrying...");
 
        enumerator = in->create_attribute_enumerator(in);
@@ -229,6 +256,7 @@ static status_t process_synchronize(private_eap_aka_server_t *this,
                         "resynchronization for '%Y'", this->peer);
                return FAILED;
        }
+       this->synchronized = TRUE;
        return initiate(this, out);
 }
 
@@ -282,12 +310,12 @@ static status_t process(private_eap_aka_server_t *this,
        simaka_message_t *message;
        status_t status;
 
-       message = simaka_message_create_from_payload(in);
+       message = simaka_message_create_from_payload(in, this->crypto);
        if (!message)
        {
                return FAILED;
        }
-       if (!message->parse(message, this->crypto))
+       if (!message->parse(message))
        {
                message->destroy(message);
                return FAILED;
@@ -384,6 +412,8 @@ eap_aka_server_t *eap_aka_server_create(identification_t *server,
        this->msk = chunk_empty;
        this->xres = chunk_empty;
        this->rand = chunk_empty;
+       this->pending = 0;
+       this->synchronized = FALSE;
        /* generate a non-zero identifier */
        do {
                this->identifier = random();