]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
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 148d5156c1ec92c198142994bbf2dfdfc81d5d00..d46b5c43c371e4ab521e04912cfc9bd3a428d61d 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 28b6f8d8c169c87dee36dcf5450b398ae6f67b98..0e3569c6b9eeeafcf2b59129c369ce77581fa237 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 106435fe49f57cabe0c14d23567492564a4aa8b5..3584bdbcafd6c627d61c8d83b077abcac18b9914 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 fde809c3181c58b1f3bb089e34f1e643ee73c558..2d7fdea227747ed135648de0fcfcff86314eb698 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 5cc0508814a297da38defdccf6e71e2a15d685f3..d375114655b16d11f315b71e892a701280892382 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 5ab2996254728553b74a0107a8b51ab6f3842d81..7d7f1b6c978b54213fc67e08ce59575e786919db 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 470a85d1c8e07a23360dbba08266c7656fcb4a66..c2f8d01dfb457adc51a817887a6e9b5171e1f84b 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 30566f890fa2272c8c22d37ebc20841b30d8410b..5ebc1a8fd125c1f14555086aae2997dbe057a087 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 4f2b14671da8d65da3e15b43154b8c0975804c80..b10d2369f8beff553c4c3f7447da55a83f4671f0 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 462460eadd76285398a692d95cc3ac94b13ca7c6..02d429f239d7015194e01424920bc5fe726cee6a 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 91b2da8300a3f74beb95f63e9e07df014d146ecd..95a212df51f6fc584c23da096d2c08df1d284171 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