ERP: Add support for ERP on EAP peer
authorJouni Malinen <j@w1.fi>
Sat, 29 Nov 2014 21:14:40 +0000 (23:14 +0200)
committerJouni Malinen <j@w1.fi>
Thu, 4 Dec 2014 10:16:29 +0000 (12:16 +0200)
Derive rRK and rIK on EAP peer if ERP is enabled. The new wpa_supplicant
network configuration parameter erp=1 can now be used to configure the
EAP peer to derive EMSK, rRK, and rIK at the successful completion of an
EAP authentication method. This functionality is not included in the
default build and can be enabled with CONFIG_ERP=y.

If EAP authenticator indicates support for re-authentication protocol,
initiate this with EAP-Initiate/Re-auth and complete protocol when
receiving EAP-Finish/Re-auth.

Signed-off-by: Jouni Malinen <j@w1.fi>
src/eap_peer/eap.c
src/eap_peer/eap.h
src/eap_peer/eap_config.h
src/eap_peer/eap_i.h
src/eapol_supp/eapol_supp_sm.c
wpa_supplicant/Android.mk
wpa_supplicant/Makefile
wpa_supplicant/config.c
wpa_supplicant/config_file.c
wpa_supplicant/ctrl_iface.c
wpa_supplicant/wpa_supplicant.conf

index 148d515..d46b5c4 100644 (file)
@@ -23,6 +23,7 @@
 #include "ext_password.h"
 #include "crypto/crypto.h"
 #include "crypto/tls.h"
+#include "crypto/sha256.h"
 #include "common/wpa_ctrl.h"
 #include "eap_common/eap_wsc_common.h"
 #include "eap_i.h"
@@ -190,6 +191,8 @@ SM_STATE(EAP, INITIALIZE)
        sm->num_rounds = 0;
        sm->prev_failure = 0;
        sm->expected_failure = 0;
+       sm->reauthInit = FALSE;
+       sm->erp_seq = (u32) -1;
 }
 
 
@@ -353,6 +356,267 @@ nak:
 }
 
 
+#ifdef CONFIG_ERP
+
+static char * eap_home_realm(struct eap_sm *sm)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       char *realm;
+       size_t i, realm_len;
+
+       if (!config)
+               return NULL;
+
+       if (config->identity) {
+               for (i = 0; i < config->identity_len; i++) {
+                       if (config->identity[i] == '@')
+                               break;
+               }
+               if (i < config->identity_len) {
+                       realm_len = config->identity_len - i - 1;
+                       realm = os_malloc(realm_len + 1);
+                       if (realm == NULL)
+                               return NULL;
+                       os_memcpy(realm, &config->identity[i + 1], realm_len);
+                       realm[realm_len] = '\0';
+                       return realm;
+               }
+       }
+
+       if (config->anonymous_identity) {
+               for (i = 0; i < config->anonymous_identity_len; i++) {
+                       if (config->anonymous_identity[i] == '@')
+                               break;
+               }
+               if (i < config->anonymous_identity_len) {
+                       realm_len = config->anonymous_identity_len - i - 1;
+                       realm = os_malloc(realm_len + 1);
+                       if (realm == NULL)
+                               return NULL;
+                       os_memcpy(realm, &config->anonymous_identity[i + 1],
+                                 realm_len);
+                       realm[realm_len] = '\0';
+                       return realm;
+               }
+       }
+
+       return os_strdup("");
+}
+
+
+static struct eap_erp_key *
+eap_erp_get_key(struct eap_sm *sm, const char *realm)
+{
+       struct eap_erp_key *erp;
+
+       dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) {
+               char *pos;
+
+               pos = os_strchr(erp->keyname_nai, '@');
+               if (!pos)
+                       continue;
+               pos++;
+               if (os_strcmp(pos, realm) == 0)
+                       return erp;
+       }
+
+       return NULL;
+}
+
+
+static struct eap_erp_key *
+eap_erp_get_key_nai(struct eap_sm *sm, const char *nai)
+{
+       struct eap_erp_key *erp;
+
+       dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) {
+               if (os_strcmp(erp->keyname_nai, nai) == 0)
+                       return erp;
+       }
+
+       return NULL;
+}
+
+
+static void eap_peer_erp_free_key(struct eap_erp_key *erp)
+{
+       dl_list_del(&erp->list);
+       bin_clear_free(erp, sizeof(*erp));
+}
+
+
+static void eap_erp_remove_keys_realm(struct eap_sm *sm, const char *realm)
+{
+       struct eap_erp_key *erp;
+
+       while ((erp = eap_erp_get_key(sm, realm)) != NULL) {
+               wpa_printf(MSG_DEBUG, "EAP: Delete old ERP key %s",
+                          erp->keyname_nai);
+               eap_peer_erp_free_key(erp);
+       }
+}
+
+#endif /* CONFIG_ERP */
+
+
+static void eap_peer_erp_free_keys(struct eap_sm *sm)
+{
+#ifdef CONFIG_ERP
+       struct eap_erp_key *erp, *tmp;
+
+       dl_list_for_each_safe(erp, tmp, &sm->erp_keys, struct eap_erp_key, list)
+               eap_peer_erp_free_key(erp);
+#endif /* CONFIG_ERP */
+}
+
+
+static void eap_peer_erp_init(struct eap_sm *sm)
+{
+#ifdef CONFIG_ERP
+       u8 *emsk = NULL;
+       size_t emsk_len;
+       u8 EMSKname[EAP_EMSK_NAME_LEN];
+       u8 len[2];
+       char *realm;
+       size_t realm_len, nai_buf_len;
+       struct eap_erp_key *erp = NULL;
+       int pos;
+
+       realm = eap_home_realm(sm);
+       if (!realm)
+               return;
+       realm_len = os_strlen(realm);
+       wpa_printf(MSG_DEBUG, "EAP: Realm for ERP keyName-NAI: %s", realm);
+       eap_erp_remove_keys_realm(sm, realm);
+
+       nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + realm_len;
+       if (nai_buf_len > 253) {
+               /*
+                * keyName-NAI has a maximum length of 253 octet to fit in
+                * RADIUS attributes.
+                */
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Too long realm for ERP keyName-NAI maximum length");
+               goto fail;
+       }
+       nai_buf_len++; /* null termination */
+       erp = os_zalloc(sizeof(*erp) + nai_buf_len);
+       if (erp == NULL)
+               goto fail;
+
+       emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
+       if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: No suitable EMSK available for ERP");
+               goto fail;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
+
+       WPA_PUT_BE16(len, 8);
+       if (hmac_sha256_kdf(sm->eapSessionId, sm->eapSessionIdLen, "EMSK",
+                           len, sizeof(len),
+                           EMSKname, EAP_EMSK_NAME_LEN) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname");
+               goto fail;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN);
+
+       pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len,
+                              EMSKname, EAP_EMSK_NAME_LEN);
+       erp->keyname_nai[pos] = '@';
+       os_memcpy(&erp->keyname_nai[pos + 1], realm, realm_len);
+
+       WPA_PUT_BE16(len, emsk_len);
+       if (hmac_sha256_kdf(emsk, emsk_len,
+                           "EAP Re-authentication Root Key@ietf.org",
+                           len, sizeof(len), erp->rRK, emsk_len) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP");
+               goto fail;
+       }
+       erp->rRK_len = emsk_len;
+       wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len);
+
+       if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+                           "EAP Re-authentication Integrity Key@ietf.org",
+                           len, sizeof(len), erp->rIK, erp->rRK_len) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP");
+               goto fail;
+       }
+       erp->rIK_len = erp->rRK_len;
+       wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len);
+
+       wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s", erp->keyname_nai);
+       dl_list_add(&sm->erp_keys, &erp->list);
+       erp = NULL;
+fail:
+       bin_clear_free(emsk, emsk_len);
+       bin_clear_free(erp, sizeof(*erp));
+       os_free(realm);
+#endif /* CONFIG_ERP */
+}
+
+
+#ifdef CONFIG_ERP
+static int eap_peer_erp_reauth_start(struct eap_sm *sm,
+                                    const struct eap_hdr *hdr, size_t len)
+{
+       char *realm;
+       struct eap_erp_key *erp;
+       struct wpabuf *msg;
+       u8 hash[SHA256_MAC_LEN];
+
+       realm = eap_home_realm(sm);
+       if (!realm)
+               return -1;
+
+       erp = eap_erp_get_key(sm, realm);
+       os_free(realm);
+       realm = NULL;
+       if (!erp)
+               return -1;
+
+       if (erp->next_seq >= 65536)
+               return -1; /* SEQ has range of 0..65535 */
+
+       /* TODO: check rRK lifetime expiration */
+
+       wpa_printf(MSG_DEBUG, "EAP: Valid ERP key found %s (SEQ=%u)",
+                  erp->keyname_nai, erp->next_seq);
+
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH,
+                           1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16,
+                           EAP_CODE_INITIATE, hdr->identifier);
+       if (msg == NULL)
+               return -1;
+
+       wpabuf_put_u8(msg, 0x20); /* Flags: R=0 B=0 L=1 */
+       wpabuf_put_be16(msg, erp->next_seq);
+
+       wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI);
+       wpabuf_put_u8(msg, os_strlen(erp->keyname_nai));
+       wpabuf_put_str(msg, erp->keyname_nai);
+
+       wpabuf_put_u8(msg, EAP_ERP_CS_HMAC_SHA256_128); /* Cryptosuite */
+
+       if (hmac_sha256(erp->rIK, erp->rIK_len,
+                       wpabuf_head(msg), wpabuf_len(msg), hash) < 0) {
+               wpabuf_free(msg);
+               return -1;
+       }
+       wpabuf_put_data(msg, hash, 16);
+
+       wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth");
+       sm->erp_seq = erp->next_seq;
+       erp->next_seq++;
+       wpabuf_free(sm->eapRespData);
+       sm->eapRespData = msg;
+       sm->reauthInit = TRUE;
+       return 0;
+}
+#endif /* CONFIG_ERP */
+
+
 /*
  * The method processing happens here. The request from the authenticator is
  * processed, and an appropriate response packet is built.
@@ -414,6 +678,8 @@ SM_STATE(EAP, METHOD)
 
        if (sm->m->isKeyAvailable && sm->m->getKey &&
            sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
+               struct eap_peer_config *config = eap_get_config(sm);
+
                eap_sm_free_key(sm);
                sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
                                               &sm->eapKeyDataLen);
@@ -426,6 +692,8 @@ SM_STATE(EAP, METHOD)
                        wpa_hexdump(MSG_DEBUG, "EAP: Session-Id",
                                    sm->eapSessionId, sm->eapSessionIdLen);
                }
+               if (config->erp && sm->m->get_emsk && sm->eapSessionId)
+                       eap_peer_erp_init(sm);
        }
 }
 
@@ -450,6 +718,7 @@ SM_STATE(EAP, SEND_RESPONSE)
        }
        eapol_set_bool(sm, EAPOL_eapReq, FALSE);
        eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
+       sm->reauthInit = FALSE;
 }
 
 
@@ -709,6 +978,8 @@ static void eap_peer_sm_step_received(struct eap_sm *sm)
        else if (sm->selectedMethod == EAP_TYPE_LEAP &&
                 (sm->rxSuccess || sm->rxResp))
                SM_ENTER(EAP, METHOD);
+       else if (sm->reauthInit)
+               SM_ENTER(EAP, SEND_RESPONSE);
        else
                SM_ENTER(EAP, DISCARD);
 }
@@ -1231,6 +1502,209 @@ static struct wpabuf * eap_sm_buildNotify(int id)
 }
 
 
+static void eap_peer_initiate(struct eap_sm *sm, const struct eap_hdr *hdr,
+                             size_t len)
+{
+#ifdef CONFIG_ERP
+       const u8 *pos = (const u8 *) (hdr + 1);
+       const u8 *end = ((const u8 *) hdr) + len;
+       struct erp_tlvs parse;
+
+       if (len < sizeof(*hdr) + 1) {
+               wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Initiate");
+               return;
+       }
+
+       if (*pos != EAP_ERP_TYPE_REAUTH_START) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Ignored unexpected EAP-Initiate Type=%u",
+                          *pos);
+               return;
+       }
+
+       pos++;
+       if (pos >= end) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Too short EAP-Initiate/Re-auth-Start");
+               return;
+       }
+       pos++; /* Reserved */
+       wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth-Start TVs/TLVs",
+                   pos, end - pos);
+
+       if (erp_parse_tlvs(pos, end, &parse, 0) < 0)
+               goto invalid;
+
+       if (parse.domain) {
+               wpa_hexdump_ascii(MSG_DEBUG,
+                                 "EAP: EAP-Initiate/Re-auth-Start - Domain name",
+                                 parse.domain, parse.domain_len);
+               /* TODO: Derivation of domain specific keys for local ER */
+       }
+
+       if (eap_peer_erp_reauth_start(sm, hdr, len) == 0)
+               return;
+
+invalid:
+#endif /* CONFIG_ERP */
+       wpa_printf(MSG_DEBUG,
+                  "EAP: EAP-Initiate/Re-auth-Start - No suitable ERP keys available - try to start full EAP authentication");
+       eapol_set_bool(sm, EAPOL_eapTriggerStart, TRUE);
+}
+
+
+static void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr,
+                           size_t len)
+{
+#ifdef CONFIG_ERP
+       const u8 *pos = (const u8 *) (hdr + 1);
+       const u8 *end = ((const u8 *) hdr) + len;
+       const u8 *start;
+       struct erp_tlvs parse;
+       u8 flags;
+       u16 seq;
+       u8 hash[SHA256_MAC_LEN];
+       size_t hash_len;
+       struct eap_erp_key *erp;
+       int max_len;
+       char nai[254];
+       u8 seed[4];
+
+       if (len < sizeof(*hdr) + 1) {
+               wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Finish");
+               return;
+       }
+
+       if (*pos != EAP_ERP_TYPE_REAUTH) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Ignored unexpected EAP-Finish Type=%u", *pos);
+               return;
+       }
+
+       if (len < sizeof(*hdr) + 4) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Ignored too short EAP-Finish/Re-auth");
+               return;
+       }
+
+       pos++;
+       flags = *pos++;
+       seq = WPA_GET_BE16(pos);
+       pos += 2;
+       wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq);
+
+       if (seq != sm->erp_seq) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Unexpected EAP-Finish/Re-auth SEQ=%u", seq);
+               return;
+       }
+
+       /*
+        * Parse TVs/TLVs. Since we do not yet know the length of the
+        * Authentication Tag, stop parsing if an unknown TV/TLV is seen and
+        * just try to find the keyName-NAI first so that we can check the
+        * Authentication Tag.
+        */
+       if (erp_parse_tlvs(pos, end, &parse, 1) < 0)
+               return;
+
+       if (!parse.keyname) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: No keyName-NAI in EAP-Finish/Re-auth Packet");
+               return;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Finish/Re-auth - keyName-NAI",
+                         parse.keyname, parse.keyname_len);
+       if (parse.keyname_len > 253) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Too long keyName-NAI in EAP-Finish/Re-auth");
+               return;
+       }
+       os_memcpy(nai, parse.keyname, parse.keyname_len);
+       nai[parse.keyname_len] = '\0';
+
+       erp = eap_erp_get_key_nai(sm, nai);
+       if (!erp) {
+               wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s",
+                          nai);
+               return;
+       }
+
+       /* Is there enough room for Cryptosuite and Authentication Tag? */
+       start = parse.keyname + parse.keyname_len;
+       max_len = end - start;
+       hash_len = 16;
+       if (max_len < 1 + (int) hash_len) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Not enough room for Authentication Tag");
+               return;
+       }
+       if (end[-17] != EAP_ERP_CS_HMAC_SHA256_128) {
+               wpa_printf(MSG_DEBUG, "EAP: Different Cryptosuite used");
+               return;
+       }
+
+       if (hmac_sha256(erp->rIK, erp->rIK_len, (const u8 *) hdr,
+                       end - ((const u8 *) hdr) - hash_len, hash) < 0)
+               return;
+       if (os_memcmp(end - hash_len, hash, hash_len) != 0) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Authentication Tag mismatch");
+               return;
+       }
+       end -= 1 + hash_len;
+
+       /*
+        * Parse TVs/TLVs again now that we know the exact part of the buffer
+        * that contains them.
+        */
+       wpa_hexdump(MSG_DEBUG, "EAP: EAP-Finish/Re-Auth TVs/TLVs",
+                   pos, end - pos);
+       if (erp_parse_tlvs(pos, end, &parse, 0) < 0)
+               return;
+
+       if (flags & 0x80) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: EAP-Finish/Re-auth indicated failure");
+               eapol_set_bool(sm, EAPOL_eapFail, TRUE);
+               eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+               eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+               wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+                       "EAP authentication failed");
+               sm->prev_failure = 1;
+               return;
+       }
+
+       eap_sm_free_key(sm);
+       sm->eapKeyDataLen = 0;
+       sm->eapKeyData = os_malloc(erp->rRK_len);
+       if (!sm->eapKeyData)
+               return;
+       sm->eapKeyDataLen = erp->rRK_len;
+
+       WPA_PUT_BE16(seed, seq);
+       WPA_PUT_BE16(&seed[2], erp->rRK_len);
+       if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+                           "Re-authentication Master Session Key@ietf.org",
+                           seed, sizeof(seed),
+                           sm->eapKeyData, erp->rRK_len) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP");
+               eap_sm_free_key(sm);
+               return;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK",
+                       sm->eapKeyData, sm->eapKeyDataLen);
+       sm->eapKeyAvailable = TRUE;
+       eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+       eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+       eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+       wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+               "EAP re-authentication completed successfully");
+#endif /* CONFIG_ERP */
+}
+
+
 static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req)
 {
        const struct eap_hdr *hdr;
@@ -1322,6 +1796,12 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req)
                eap_notify_status(sm, "completion", "failure");
                sm->rxFailure = TRUE;
                break;
+       case EAP_CODE_INITIATE:
+               eap_peer_initiate(sm, hdr, plen);
+               break;
+       case EAP_CODE_FINISH:
+               eap_peer_finish(sm, hdr, plen);
+               break;
        default:
                wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown "
                           "code %d", hdr->code);
@@ -1413,6 +1893,7 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
        sm->msg_ctx = msg_ctx;
        sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
        sm->wps = conf->wps;
+       dl_list_init(&sm->erp_keys);
 
        os_memset(&tlsconf, 0, sizeof(tlsconf));
        tlsconf.opensc_engine_path = conf->opensc_engine_path;
@@ -1460,6 +1941,7 @@ void eap_peer_sm_deinit(struct eap_sm *sm)
        if (sm->ssl_ctx2)
                tls_deinit(sm->ssl_ctx2);
        tls_deinit(sm->ssl_ctx);
+       eap_peer_erp_free_keys(sm);
        os_free(sm);
 }
 
index 28b6f8d..0e3569c 100644 (file)
@@ -94,7 +94,14 @@ enum eapol_bool_var {
         *
         * EAP state machines reads this value.
         */
-       EAPOL_altReject
+       EAPOL_altReject,
+
+       /**
+        * EAPOL_eapTriggerStart - EAP-based trigger to send EAPOL-Start
+        *
+        * EAP state machine writes this value.
+        */
+       EAPOL_eapTriggerStart
 };
 
 /**
index 106435f..3584bdb 100644 (file)
@@ -695,6 +695,11 @@ struct eap_peer_config {
         * list is used.
         */
        char *openssl_ciphers;
+
+       /**
+        * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
+        */
+       int erp;
 };
 
 
index fde809c..2d7fdea 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * EAP peer state machines internal structures (RFC 4137)
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 #define EAP_I_H
 
 #include "wpabuf.h"
+#include "utils/list.h"
 #include "eap_peer/eap.h"
 #include "eap_common/eap_common.h"
 
@@ -277,6 +278,16 @@ struct eap_method {
 };
 
 
+struct eap_erp_key {
+       struct dl_list list;
+       size_t rRK_len;
+       size_t rIK_len;
+       u8 rRK[ERP_MAX_KEY_LEN];
+       u8 rIK[ERP_MAX_KEY_LEN];
+       u32 next_seq;
+       char keyname_nai[];
+};
+
 /**
  * struct eap_sm - EAP state machine data
  */
@@ -321,6 +332,8 @@ struct eap_sm {
        void *eap_method_priv;
        int init_phase2;
        int fast_reauth;
+       Boolean reauthInit; /* send EAP-Identity/Re-auth */
+       u32 erp_seq;
 
        Boolean rxResp /* LEAP only */;
        Boolean leap_done;
@@ -353,6 +366,8 @@ struct eap_sm {
        int external_sim;
 
        unsigned int expected_failure:1;
+
+       struct dl_list erp_keys; /* struct eap_erp_key */
 };
 
 const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
index 5cc0508..d375114 100644 (file)
@@ -128,6 +128,7 @@ struct eapol_sm {
        struct wpabuf *eapReqData; /* for EAP */
        Boolean altAccept; /* for EAP */
        Boolean altReject; /* for EAP */
+       Boolean eapTriggerStart;
        Boolean replay_counter_valid;
        u8 last_replay_counter[16];
        struct eapol_config conf;
@@ -222,6 +223,7 @@ SM_STATE(SUPP_PAE, DISCONNECTED)
        SM_ENTRY(SUPP_PAE, DISCONNECTED);
        sm->sPortMode = Auto;
        sm->startCount = 0;
+       sm->eapTriggerStart = FALSE;
        sm->logoffSent = FALSE;
        eapol_sm_set_port_unauthorized(sm);
        sm->suppAbort = TRUE;
@@ -244,6 +246,11 @@ SM_STATE(SUPP_PAE, CONNECTING)
 {
        int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING;
        SM_ENTRY(SUPP_PAE, CONNECTING);
+
+       if (sm->eapTriggerStart)
+               send_start = 1;
+       sm->eapTriggerStart = FALSE;
+
        if (send_start) {
                sm->startWhen = sm->startPeriod;
                sm->startCount++;
@@ -386,6 +393,8 @@ SM_STEP(SUPP_PAE)
                        SM_ENTER(SUPP_PAE, HELD);
                else if (sm->suppTimeout)
                        SM_ENTER(SUPP_PAE, CONNECTING);
+               else if (sm->eapTriggerStart)
+                       SM_ENTER(SUPP_PAE, CONNECTING);
                break;
        case SUPP_PAE_HELD:
                if (sm->heldWhile == 0)
@@ -1822,6 +1831,8 @@ static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable)
                return sm->altAccept;
        case EAPOL_altReject:
                return sm->altReject;
+       case EAPOL_eapTriggerStart:
+               return sm->eapTriggerStart;
        }
        return FALSE;
 }
@@ -1861,6 +1872,9 @@ static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable,
        case EAPOL_altReject:
                sm->altReject = value;
                break;
+       case EAPOL_eapTriggerStart:
+               sm->eapTriggerStart = value;
+               break;
        }
 }
 
index 5ab2996..7d7f1b6 100644 (file)
@@ -346,6 +346,12 @@ ifeq ($(CONFIG_L2_PACKET), freebsd)
 LIBS += -lpcap
 endif
 
+ifdef CONFIG_ERP
+L_CFLAGS += -DCONFIG_ERP
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
 ifdef CONFIG_EAP_TLS
 # EAP-TLS
 ifeq ($(CONFIG_EAP_TLS), dyn)
@@ -1243,6 +1249,9 @@ endif
 ifdef NEED_TLS_PRF_SHA256
 SHA256OBJS += src/crypto/sha256-tlsprf.c
 endif
+ifdef NEED_HMAC_SHA256_KDF
+SHA256OBJS += src/crypto/sha256-kdf.c
+endif
 OBJS += $(SHA256OBJS)
 endif
 
index 470a85d..c2f8d01 100644 (file)
@@ -348,6 +348,12 @@ ifeq ($(CONFIG_L2_PACKET), freebsd)
 LIBS += -lpcap
 endif
 
+ifdef CONFIG_ERP
+CFLAGS += -DCONFIG_ERP
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
 ifdef CONFIG_EAP_TLS
 # EAP-TLS
 ifeq ($(CONFIG_EAP_TLS), dyn)
@@ -1256,6 +1262,9 @@ endif
 ifdef NEED_TLS_PRF_SHA256
 SHA256OBJS += ../src/crypto/sha256-tlsprf.o
 endif
+ifdef NEED_HMAC_SHA256_KDF
+OBJS += ../src/crypto/sha256-kdf.o
+endif
 OBJS += $(SHA256OBJS)
 endif
 
index 30566f8..5ebc1a8 100644 (file)
@@ -1820,6 +1820,7 @@ static const struct parse_data ssid_fields[] = {
        { INT(eapol_flags) },
        { INTe(sim_num) },
        { STRe(openssl_ciphers) },
+       { INTe(erp) },
 #endif /* IEEE8021X_EAPOL */
        { FUNC_KEY(wep_key0) },
        { FUNC_KEY(wep_key1) },
index 4f2b146..b10d236 100644 (file)
@@ -716,6 +716,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
        INTe(engine);
        INTe(engine2);
        INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS);
+       INTe(erp);
 #endif /* IEEE8021X_EAPOL */
        for (i = 0; i < 4; i++)
                write_wep_key(f, i, ssid);
index 462460e..02d429f 100644 (file)
@@ -3619,6 +3619,15 @@ static int wpa_supplicant_ctrl_iface_get_capability(
                return ctrl_iface_get_capability_tdls(wpa_s, buf, buflen);
 #endif /* CONFIG_TDLS */
 
+#ifdef CONFIG_ERP
+       if (os_strcmp(field, "erp") == 0) {
+               res = os_snprintf(buf, buflen, "ERP");
+               if (res < 0 || (unsigned int) res >= buflen)
+                       return -1;
+               return res;
+       }
+#endif /* CONFIG_EPR */
+
        wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
                   field);
 
index 91b2da8..95a212d 100644 (file)
@@ -955,6 +955,8 @@ fast_reauth=1
 #      This can be used to override the global openssl_ciphers configuration
 #      parameter (see above).
 #
+# erp: Whether EAP Re-authentication Protocol (ERP) is enabled
+#
 # EAP-FAST variables:
 # pac_file: File path for the PAC entries. wpa_supplicant will need to be able
 #      to create this file and write updates to it when PAC is being