Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / src / eapol_supp / eapol_supp_sm.c
index 77cd564..09cf4f6 100644 (file)
@@ -1,15 +1,9 @@
 /*
  * EAPOL supplicant state machines
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -22,6 +16,7 @@
 #include "crypto/md5.h"
 #include "common/eapol_common.h"
 #include "eap_peer/eap.h"
+#include "eap_peer/eap_proxy.h"
 #include "eapol_supp_sm.h"
 
 #define STATE_MACHINE_DATA struct eapol_sm
@@ -133,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;
@@ -142,47 +138,14 @@ struct eapol_sm {
        Boolean cached_pmk;
 
        Boolean unicast_key_received, broadcast_key_received;
-};
 
+       Boolean force_authorized_update;
 
-#define IEEE8021X_REPLAY_COUNTER_LEN 8
-#define IEEE8021X_KEY_SIGN_LEN 16
-#define IEEE8021X_KEY_IV_LEN 16
-
-#define IEEE8021X_KEY_INDEX_FLAG 0x80
-#define IEEE8021X_KEY_INDEX_MASK 0x03
-
-#ifdef _MSC_VER
-#pragma pack(push, 1)
-#endif /* _MSC_VER */
-
-struct ieee802_1x_eapol_key {
-       u8 type;
-       /* Note: key_length is unaligned */
-       u8 key_length[2];
-       /* does not repeat within the life of the keying material used to
-        * encrypt the Key field; 64-bit NTP timestamp MAY be used here */
-       u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN];
-       u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */
-       u8 key_index; /* key flag in the most significant bit:
-                      * 0 = broadcast (default key),
-                      * 1 = unicast (key mapping key); key index is in the
-                      * 7 least significant bits */
-       /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as
-        * the key */
-       u8 key_signature[IEEE8021X_KEY_SIGN_LEN];
-
-       /* followed by key: if packet body length = 44 + key length, then the
-        * key field (of key_length bytes) contains the key in encrypted form;
-        * if packet body length = 44, key field is absent and key_length
-        * represents the number of least significant octets from
-        * MS-MPPE-Send-Key attribute to be used as the keying material;
-        * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
-} STRUCT_PACKED;
-
-#ifdef _MSC_VER
-#pragma pack(pop)
-#endif /* _MSC_VER */
+#ifdef CONFIG_EAP_PROXY
+       Boolean use_eap_proxy;
+       struct eap_proxy_sm *eap_proxy;
+#endif /* CONFIG_EAP_PROXY */
+};
 
 
 static void eapol_sm_txLogoff(struct eapol_sm *sm);
@@ -251,7 +214,6 @@ SM_STATE(SUPP_PAE, LOGOFF)
        SM_ENTRY(SUPP_PAE, LOGOFF);
        eapol_sm_txLogoff(sm);
        sm->logoffSent = TRUE;
-       sm->suppPortStatus = Unauthorized;
        eapol_sm_set_port_unauthorized(sm);
 }
 
@@ -261,20 +223,35 @@ SM_STATE(SUPP_PAE, DISCONNECTED)
        SM_ENTRY(SUPP_PAE, DISCONNECTED);
        sm->sPortMode = Auto;
        sm->startCount = 0;
+       sm->eapTriggerStart = FALSE;
        sm->logoffSent = FALSE;
-       sm->suppPortStatus = Unauthorized;
        eapol_sm_set_port_unauthorized(sm);
        sm->suppAbort = TRUE;
 
        sm->unicast_key_received = FALSE;
        sm->broadcast_key_received = FALSE;
+
+       /*
+        * IEEE Std 802.1X-2004 does not clear heldWhile here, but doing so
+        * allows the timer tick to be stopped more quickly when the port is
+        * not enabled. Since this variable is used only within HELD state,
+        * clearing it on initialization does not change actual state machine
+        * behavior.
+        */
+       sm->heldWhile = 0;
 }
 
 
 SM_STATE(SUPP_PAE, CONNECTING)
 {
-       int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING;
+       int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING ||
+               sm->SUPP_PAE_state == SUPP_PAE_HELD;
        SM_ENTRY(SUPP_PAE, CONNECTING);
+
+       if (sm->eapTriggerStart)
+               send_start = 1;
+       sm->eapTriggerStart = FALSE;
+
        if (send_start) {
                sm->startWhen = sm->startPeriod;
                sm->startCount++;
@@ -286,12 +263,14 @@ SM_STATE(SUPP_PAE, CONNECTING)
                 * delay authentication. Use a short timeout to send the first
                 * EAPOL-Start if Authenticator does not start authentication.
                 */
-#ifdef CONFIG_WPS
-               /* Reduce latency on starting WPS negotiation. */
-               sm->startWhen = 1;
-#else /* CONFIG_WPS */
-               sm->startWhen = 3;
-#endif /* CONFIG_WPS */
+               if (sm->conf.wps && !(sm->conf.wps & EAPOL_PEER_IS_WPS20_AP)) {
+                       /* Reduce latency on starting WPS negotiation. */
+                       wpa_printf(MSG_DEBUG,
+                                  "EAPOL: Using shorter startWhen for WPS");
+                       sm->startWhen = 1;
+               } else {
+                       sm->startWhen = 2;
+               }
        }
        eapol_enable_timer_tick(sm);
        sm->eapolEap = FALSE;
@@ -318,7 +297,6 @@ SM_STATE(SUPP_PAE, HELD)
        SM_ENTRY(SUPP_PAE, HELD);
        sm->heldWhile = sm->heldPeriod;
        eapol_enable_timer_tick(sm);
-       sm->suppPortStatus = Unauthorized;
        eapol_sm_set_port_unauthorized(sm);
        sm->cb_status = EAPOL_CB_FAILURE;
 }
@@ -327,7 +305,6 @@ SM_STATE(SUPP_PAE, HELD)
 SM_STATE(SUPP_PAE, AUTHENTICATED)
 {
        SM_ENTRY(SUPP_PAE, AUTHENTICATED);
-       sm->suppPortStatus = Authorized;
        eapol_sm_set_port_authorized(sm);
        sm->cb_status = EAPOL_CB_SUCCESS;
 }
@@ -343,7 +320,6 @@ SM_STATE(SUPP_PAE, RESTART)
 SM_STATE(SUPP_PAE, S_FORCE_AUTH)
 {
        SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
-       sm->suppPortStatus = Authorized;
        eapol_sm_set_port_authorized(sm);
        sm->sPortMode = ForceAuthorized;
 }
@@ -352,7 +328,6 @@ SM_STATE(SUPP_PAE, S_FORCE_AUTH)
 SM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
 {
        SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
-       sm->suppPortStatus = Unauthorized;
        eapol_sm_set_port_unauthorized(sm);
        sm->sPortMode = ForceUnauthorized;
        eapol_sm_txLogoff(sm);
@@ -419,6 +394,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)
@@ -500,6 +477,17 @@ SM_STATE(SUPP_BE, SUCCESS)
        sm->keyRun = TRUE;
        sm->suppSuccess = TRUE;
 
+#ifdef CONFIG_EAP_PROXY
+       if (sm->use_eap_proxy) {
+               if (eap_proxy_key_available(sm->eap_proxy)) {
+                       /* New key received - clear IEEE 802.1X EAPOL-Key replay
+                        * counter */
+                       sm->replay_counter_valid = FALSE;
+               }
+               return;
+       }
+#endif /* CONFIG_EAP_PROXY */
+
        if (eap_key_available(sm->eap)) {
                /* New key received - clear IEEE 802.1X EAPOL-Key replay
                 * counter */
@@ -535,6 +523,15 @@ SM_STATE(SUPP_BE, INITIALIZE)
        SM_ENTRY(SUPP_BE, INITIALIZE);
        eapol_sm_abortSupp(sm);
        sm->suppAbort = FALSE;
+
+       /*
+        * IEEE Std 802.1X-2004 does not clear authWhile here, but doing so
+        * allows the timer tick to be stopped more quickly when the port is
+        * not enabled. Since this variable is used only within RECEIVE state,
+        * clearing it on initialization does not change actual state machine
+        * behavior.
+        */
+       sm->authWhile = 0;
 }
 
 
@@ -561,7 +558,7 @@ SM_STEP(SUPP_BE)
                 * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL
                 * and SUCCESS based on eapFail and eapSuccess, respectively.
                 * However, IEEE Std 802.1X-2004 is also specifying that
-                * eapNoResp should be set in conjuction with eapSuccess and
+                * eapNoResp should be set in conjunction with eapSuccess and
                 * eapFail which would mean that more than one of the
                 * transitions here would be activated at the same time.
                 * Skipping RESPONSE and/or RECEIVE states in these cases can
@@ -652,13 +649,17 @@ struct eap_key_data {
 
 static void eapol_sm_processKey(struct eapol_sm *sm)
 {
+#ifndef CONFIG_FIPS
        struct ieee802_1x_hdr *hdr;
        struct ieee802_1x_eapol_key *key;
        struct eap_key_data keydata;
        u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];
+#ifndef CONFIG_NO_RC4
        u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
+#endif /* CONFIG_NO_RC4 */
        int key_len, res, sign_key_len, encr_key_len;
        u16 rx_key_length;
+       size_t plen;
 
        wpa_printf(MSG_DEBUG, "EAPOL: processKey");
        if (sm->last_rx_key == NULL)
@@ -671,9 +672,12 @@ static void eapol_sm_processKey(struct eapol_sm *sm)
                return;
        }
 
+       if (sm->last_rx_key_len < sizeof(*hdr) + sizeof(*key))
+               return;
        hdr = (struct ieee802_1x_hdr *) sm->last_rx_key;
        key = (struct ieee802_1x_eapol_key *) (hdr + 1);
-       if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) {
+       plen = be_to_host16(hdr->length);
+       if (sizeof(*hdr) + plen > sm->last_rx_key_len || plen < sizeof(*key)) {
                wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");
                return;
        }
@@ -729,8 +733,8 @@ static void eapol_sm_processKey(struct eapol_sm *sm)
        hmac_md5(keydata.sign_key, sign_key_len,
                 sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length),
                 key->key_signature);
-       if (os_memcmp(orig_key_sign, key->key_signature,
-                     IEEE8021X_KEY_SIGN_LEN) != 0) {
+       if (os_memcmp_const(orig_key_sign, key->key_signature,
+                           IEEE8021X_KEY_SIGN_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in "
                           "EAPOL-Key packet");
                os_memcpy(key->key_signature, orig_key_sign,
@@ -739,13 +743,20 @@ static void eapol_sm_processKey(struct eapol_sm *sm)
        }
        wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");
 
-       key_len = be_to_host16(hdr->length) - sizeof(*key);
+       key_len = plen - sizeof(*key);
        if (key_len > 32 || rx_key_length > 32) {
                wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",
                           key_len ? key_len : rx_key_length);
                return;
        }
        if (key_len == rx_key_length) {
+#ifdef CONFIG_NO_RC4
+               if (encr_key_len) {
+                       /* otherwise unused */
+               }
+               wpa_printf(MSG_ERROR, "EAPOL: RC4 not supported in the build");
+               return;
+#else /* CONFIG_NO_RC4 */
                os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);
                os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,
                          encr_key_len);
@@ -754,6 +765,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm)
                         datakey, key_len);
                wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",
                                datakey, key_len);
+#endif /* CONFIG_NO_RC4 */
        } else if (key_len == 0) {
                /*
                 * IEEE 802.1X-2004 specifies that least significant Key Length
@@ -810,6 +822,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm)
                                sm->ctx->eapol_done_cb(sm->ctx->ctx);
                }
        }
+#endif /* CONFIG_FIPS */
 }
 
 
@@ -828,6 +841,19 @@ static void eapol_sm_txSuppRsp(struct eapol_sm *sm)
        struct wpabuf *resp;
 
        wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp");
+
+#ifdef CONFIG_EAP_PROXY
+       if (sm->use_eap_proxy) {
+               /* Get EAP Response from EAP Proxy */
+               resp = eap_proxy_get_eapRespData(sm->eap_proxy);
+               if (resp == NULL) {
+                       wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP Proxy "
+                                  "response data not available");
+                       return;
+               }
+       } else
+#endif /* CONFIG_EAP_PROXY */
+
        resp = eap_get_eapRespData(sm->eap);
        if (resp == NULL) {
                wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data "
@@ -872,14 +898,24 @@ static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx)
 
 static void eapol_sm_set_port_authorized(struct eapol_sm *sm)
 {
-       if (sm->ctx->port_cb)
+       int cb;
+
+       cb = sm->suppPortStatus != Authorized || sm->force_authorized_update;
+       sm->force_authorized_update = FALSE;
+       sm->suppPortStatus = Authorized;
+       if (cb && sm->ctx->port_cb)
                sm->ctx->port_cb(sm->ctx->ctx, 1);
 }
 
 
 static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm)
 {
-       if (sm->ctx->port_cb)
+       int cb;
+
+       cb = sm->suppPortStatus != Unauthorized || sm->force_authorized_update;
+       sm->force_authorized_update = FALSE;
+       sm->suppPortStatus = Unauthorized;
+       if (cb && sm->ctx->port_cb)
                sm->ctx->port_cb(sm->ctx->ctx, 0);
 }
 
@@ -905,6 +941,13 @@ void eapol_sm_step(struct eapol_sm *sm)
                SM_STEP_RUN(SUPP_PAE);
                SM_STEP_RUN(KEY_RX);
                SM_STEP_RUN(SUPP_BE);
+#ifdef CONFIG_EAP_PROXY
+               if (sm->use_eap_proxy) {
+                       /* Drive the EAP proxy state machine */
+                       if (eap_proxy_sm_step(sm->eap_proxy, sm->eap))
+                               sm->changed = TRUE;
+               } else
+#endif /* CONFIG_EAP_PROXY */
                if (eap_peer_sm_step(sm->eap))
                        sm->changed = TRUE;
                if (!sm->changed)
@@ -919,9 +962,15 @@ void eapol_sm_step(struct eapol_sm *sm)
        }
 
        if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
-               int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0;
+               enum eapol_supp_result result;
+               if (sm->cb_status == EAPOL_CB_SUCCESS)
+                       result = EAPOL_SUPP_RESULT_SUCCESS;
+               else if (eap_peer_was_failure_expected(sm->eap))
+                       result = EAPOL_SUPP_RESULT_EXPECTED_FAILURE;
+               else
+                       result = EAPOL_SUPP_RESULT_FAILURE;
                sm->cb_status = EAPOL_CB_IN_PROGRESS;
-               sm->ctx->cb(sm, success, sm->ctx->cb_ctx);
+               sm->ctx->cb(sm, result, sm->ctx->cb_ctx);
        }
 }
 
@@ -1029,6 +1078,21 @@ void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
 }
 
 
+/**
+ * eapol_sm_get_method_name - Get EAPOL method name
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * Returns: Static string containing name of current eap method or NULL
+ */
+const char * eapol_sm_get_method_name(struct eapol_sm *sm)
+{
+       if (sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED ||
+           sm->suppPortStatus != Authorized)
+               return NULL;
+
+       return eap_sm_get_method_name(sm->eap);
+}
+
+
 #ifdef CONFIG_CTRL_IFACE
 /**
  * eapol_sm_get_status - Get EAPOL state machine status
@@ -1055,7 +1119,7 @@ int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
                          "suppPortStatus=%s\n",
                          eapol_supp_pae_state(sm->SUPP_PAE_state),
                          eapol_port_status(sm->suppPortStatus));
-       if (len < 0 || (size_t) len >= buflen)
+       if (os_snprintf_error(buflen, len))
                return 0;
 
        if (verbose) {
@@ -1072,11 +1136,18 @@ int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
                                  sm->maxStart,
                                  eapol_port_control(sm->portControl),
                                  eapol_supp_be_state(sm->SUPP_BE_state));
-               if (ret < 0 || (size_t) ret >= buflen - len)
+               if (os_snprintf_error(buflen - len, ret))
                        return len;
                len += ret;
        }
 
+#ifdef CONFIG_EAP_PROXY
+       if (sm->use_eap_proxy)
+               len += eap_proxy_sm_get_status(sm->eap_proxy,
+                                              buf + len, buflen - len,
+                                              verbose);
+       else
+#endif /* CONFIG_EAP_PROXY */
        len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose);
 
        return len;
@@ -1119,7 +1190,7 @@ int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
                          "Authorized" : "Unauthorized",
                          sm->SUPP_BE_state);
 
-       if (ret < 0 || (size_t) ret >= buflen)
+       if (os_snprintf_error(buflen, ret))
                return 0;
        len = ret;
 
@@ -1147,7 +1218,7 @@ int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
                          sm->dot1xSuppLastEapolFrameVersion,
                          MAC2STR(sm->dot1xSuppLastEapolFrameSource));
 
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -1193,7 +1264,7 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
                return 0;
        }
 #ifdef CONFIG_WPS
-       if (sm->conf.workaround &&
+       if (sm->conf.wps && sm->conf.workaround &&
            plen < len - sizeof(*hdr) &&
            hdr->type == IEEE802_1X_TYPE_EAP_PACKET &&
            len - sizeof(*hdr) > sizeof(struct eap_hdr)) {
@@ -1221,6 +1292,24 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
 
        switch (hdr->type) {
        case IEEE802_1X_TYPE_EAP_PACKET:
+               if (sm->conf.workaround) {
+                       /*
+                        * An AP has been reported to send out EAP message with
+                        * undocumented code 10 at some point near the
+                        * completion of EAP authentication. This can result in
+                        * issues with the unexpected EAP message triggering
+                        * restart of EAPOL authentication. Avoid this by
+                        * skipping the message without advancing the state
+                        * machine.
+                        */
+                       const struct eap_hdr *ehdr =
+                               (const struct eap_hdr *) (hdr + 1);
+                       if (plen >= sizeof(*ehdr) && ehdr->code == 10) {
+                               wpa_printf(MSG_DEBUG, "EAPOL: Ignore EAP packet with unknown code 10");
+                               break;
+                       }
+               }
+
                if (sm->cached_pmk) {
                        /* Trying to use PMKSA caching, but Authenticator did
                         * not seem to have a matching entry. Need to restart
@@ -1234,6 +1323,16 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
                        wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet "
                                   "frame");
                        sm->eapolEap = TRUE;
+#ifdef CONFIG_EAP_PROXY
+                       if (sm->use_eap_proxy) {
+                               eap_proxy_packet_update(
+                                       sm->eap_proxy,
+                                       wpabuf_mhead_u8(sm->eapReqData),
+                                       wpabuf_len(sm->eapReqData));
+                               wpa_printf(MSG_DEBUG, "EAPOL: eap_proxy "
+                                          "EAP Req updated");
+                       }
+#endif /* CONFIG_EAP_PROXY */
                        eapol_sm_step(sm);
                }
                break;
@@ -1268,6 +1367,13 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
                        eapol_sm_step(sm);
                }
                break;
+#ifdef CONFIG_MACSEC
+       case IEEE802_1X_TYPE_EAPOL_MKA:
+               wpa_printf(MSG_EXCESSIVE,
+                          "EAPOL type %d will be handled by MKA",
+                          hdr->type);
+               break;
+#endif /* CONFIG_MACSEC */
        default:
                wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d",
                           hdr->type);
@@ -1306,6 +1412,8 @@ void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled)
                return;
        wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
                   "portEnabled=%d", enabled);
+       if (sm->portEnabled != enabled)
+               sm->force_authorized_update = TRUE;
        sm->portEnabled = enabled;
        eapol_sm_step(sm);
 }
@@ -1394,6 +1502,9 @@ void eapol_sm_notify_config(struct eapol_sm *sm,
                return;
 
        sm->config = config;
+#ifdef CONFIG_EAP_PROXY
+       sm->use_eap_proxy = eap_proxy_notify_config(sm->eap_proxy, config) > 0;
+#endif /* CONFIG_EAP_PROXY */
 
        if (conf == NULL)
                return;
@@ -1402,10 +1513,18 @@ void eapol_sm_notify_config(struct eapol_sm *sm,
        sm->conf.required_keys = conf->required_keys;
        sm->conf.fast_reauth = conf->fast_reauth;
        sm->conf.workaround = conf->workaround;
+       sm->conf.wps = conf->wps;
+#ifdef CONFIG_EAP_PROXY
+       if (sm->use_eap_proxy) {
+               /* Using EAP Proxy, so skip EAP state machine update */
+               return;
+       }
+#endif /* CONFIG_EAP_PROXY */
        if (sm->eap) {
                eap_set_fast_reauth(sm->eap, conf->fast_reauth);
                eap_set_workaround(sm->eap, conf->workaround);
                eap_set_force_disabled(sm->eap, conf->eap_disabled);
+               eap_set_external_sim(sm->eap, conf->external_sim);
        }
 }
 
@@ -1426,6 +1545,22 @@ int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
        const u8 *eap_key;
        size_t eap_len;
 
+#ifdef CONFIG_EAP_PROXY
+       if (sm && sm->use_eap_proxy) {
+               /* Get key from EAP proxy */
+               if (sm == NULL || !eap_proxy_key_available(sm->eap_proxy)) {
+                       wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
+                       return -1;
+               }
+               eap_key = eap_proxy_get_eapKeyData(sm->eap_proxy, &eap_len);
+               if (eap_key == NULL) {
+                       wpa_printf(MSG_DEBUG, "EAPOL: Failed to get "
+                                  "eapKeyData");
+                       return -1;
+               }
+               goto key_fetched;
+       }
+#endif /* CONFIG_EAP_PROXY */
        if (sm == NULL || !eap_key_available(sm->eap)) {
                wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
                return -1;
@@ -1435,6 +1570,9 @@ int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
                wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData");
                return -1;
        }
+#ifdef CONFIG_EAP_PROXY
+key_fetched:
+#endif /* CONFIG_EAP_PROXY */
        if (len > eap_len) {
                wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not "
                           "available (len=%lu)",
@@ -1449,6 +1587,24 @@ int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
 
 
 /**
+ * eapol_sm_get_session_id - Get EAP Session-Id
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @len: Pointer to variable that will be set to number of bytes in the session
+ * Returns: Pointer to the EAP Session-Id or %NULL on failure
+ *
+ * The Session-Id is available only after a successful authentication.
+ */
+const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len)
+{
+       if (sm == NULL || !eap_key_available(sm->eap)) {
+               wpa_printf(MSG_DEBUG, "EAPOL: EAP Session-Id not available");
+               return NULL;
+       }
+       return eap_get_eapSessionId(sm->eap, len);
+}
+
+
+/**
  * eapol_sm_notify_logoff - Notification of logon/logoff commands
  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
  * @logoff: Whether command was logoff
@@ -1459,6 +1615,10 @@ void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
 {
        if (sm) {
                sm->userLogoff = logoff;
+               if (!logoff) {
+                       /* If there is a delayed txStart queued, start now. */
+                       sm->startWhen = 0;
+               }
                eapol_sm_step(sm);
        }
 }
@@ -1476,10 +1636,7 @@ void eapol_sm_notify_cached(struct eapol_sm *sm)
        if (sm == NULL)
                return;
        wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL");
-       sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED;
-       sm->suppPortStatus = Authorized;
-       eapol_sm_set_port_authorized(sm);
-       sm->portValid = TRUE;
+       sm->eapSuccess = TRUE;
        eap_notify_success(sm->eap);
        eapol_sm_step(sm);
 }
@@ -1488,21 +1645,15 @@ void eapol_sm_notify_cached(struct eapol_sm *sm)
 /**
  * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching
  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
- * @attempt: Whether PMKSA caching is tried
  *
- * Notify EAPOL state machines whether PMKSA caching is used.
+ * Notify EAPOL state machines if PMKSA caching is used.
  */
-void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt)
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm)
 {
        if (sm == NULL)
                return;
-       if (attempt) {
-               wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
-               sm->cached_pmk = TRUE;
-       } else {
-               wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA");
-               sm->cached_pmk = FALSE;
-       }
+       wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
+       sm->cached_pmk = TRUE;
 }
 
 
@@ -1514,7 +1665,6 @@ static void eapol_sm_abort_cached(struct eapol_sm *sm)
                return;
        sm->cached_pmk = FALSE;
        sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
-       sm->suppPortStatus = Unauthorized;
        eapol_sm_set_port_unauthorized(sm);
 
        /* Make sure we do not start sending EAPOL-Start frames first, but
@@ -1686,6 +1836,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;
 }
@@ -1725,6 +1877,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;
        }
 }
 
@@ -1751,7 +1906,8 @@ static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable,
        switch (variable) {
        case EAPOL_idleWhile:
                sm->idleWhile = value;
-               eapol_enable_timer_tick(sm);
+               if (sm->idleWhile > 0)
+                       eapol_enable_timer_tick(sm);
                break;
        }
 }
@@ -1798,7 +1954,7 @@ static void eapol_sm_notify_pending(void *ctx)
 
 
 #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
-static void eapol_sm_eap_param_needed(void *ctx, const char *field,
+static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field,
                                      const char *txt)
 {
        struct eapol_sm *sm = ctx;
@@ -1810,8 +1966,49 @@ static void eapol_sm_eap_param_needed(void *ctx, const char *field,
 #define eapol_sm_eap_param_needed NULL
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 
+static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject,
+                                const char *altsubject[],
+                                int num_altsubject, const char *cert_hash,
+                                const struct wpabuf *cert)
+{
+       struct eapol_sm *sm = ctx;
+       if (sm->ctx->cert_cb)
+               sm->ctx->cert_cb(sm->ctx->ctx, depth, subject, altsubject,
+                                num_altsubject, cert_hash, cert);
+}
+
+
+static void eapol_sm_notify_status(void *ctx, const char *status,
+                                  const char *parameter)
+{
+       struct eapol_sm *sm = ctx;
+
+       if (sm->ctx->status_cb)
+               sm->ctx->status_cb(sm->ctx->ctx, status, parameter);
+}
+
+
+#ifdef CONFIG_EAP_PROXY
+static void eapol_sm_eap_proxy_cb(void *ctx)
+{
+       struct eapol_sm *sm = ctx;
+
+       if (sm->ctx->eap_proxy_cb)
+               sm->ctx->eap_proxy_cb(sm->ctx->ctx);
+}
+#endif /* CONFIG_EAP_PROXY */
+
+
+static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len)
+{
+       struct eapol_sm *sm = ctx;
+
+       if (sm->ctx->set_anon_id)
+               sm->ctx->set_anon_id(sm->ctx->ctx, id, len);
+}
+
 
-static struct eapol_callbacks eapol_cb =
+static const struct eapol_callbacks eapol_cb =
 {
        eapol_sm_get_config,
        eapol_sm_get_bool,
@@ -1822,7 +2019,13 @@ static struct eapol_callbacks eapol_cb =
        eapol_sm_set_config_blob,
        eapol_sm_get_config_blob,
        eapol_sm_notify_pending,
-       eapol_sm_eap_param_needed
+       eapol_sm_eap_param_needed,
+       eapol_sm_notify_cert,
+       eapol_sm_notify_status,
+#ifdef CONFIG_EAP_PROXY
+       eapol_sm_eap_proxy_cb,
+#endif /* CONFIG_EAP_PROXY */
+       eapol_sm_set_anon_id
 };
 
 
@@ -1857,7 +2060,9 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
        conf.opensc_engine_path = ctx->opensc_engine_path;
        conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
        conf.pkcs11_module_path = ctx->pkcs11_module_path;
+       conf.openssl_ciphers = ctx->openssl_ciphers;
        conf.wps = ctx->wps;
+       conf.cert_in_cb = ctx->cert_in_cb;
 
        sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf);
        if (sm->eap == NULL) {
@@ -1865,7 +2070,16 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
                return NULL;
        }
 
+#ifdef CONFIG_EAP_PROXY
+       sm->use_eap_proxy = FALSE;
+       sm->eap_proxy = eap_proxy_init(sm, &eapol_cb, sm->ctx->msg_ctx);
+       if (sm->eap_proxy == NULL) {
+               wpa_printf(MSG_ERROR, "Unable to initialize EAP Proxy");
+       }
+#endif /* CONFIG_EAP_PROXY */
+
        /* Initialize EAPOL state machines */
+       sm->force_authorized_update = TRUE;
        sm->initialize = TRUE;
        eapol_sm_step(sm);
        sm->initialize = FALSE;
@@ -1891,8 +2105,46 @@ void eapol_sm_deinit(struct eapol_sm *sm)
        eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
        eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
        eap_peer_sm_deinit(sm->eap);
+#ifdef CONFIG_EAP_PROXY
+       eap_proxy_deinit(sm->eap_proxy);
+#endif /* CONFIG_EAP_PROXY */
        os_free(sm->last_rx_key);
        wpabuf_free(sm->eapReqData);
        os_free(sm->ctx);
        os_free(sm);
 }
+
+
+void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
+                            struct ext_password_data *ext)
+{
+       if (sm && sm->eap)
+               eap_sm_set_ext_pw_ctx(sm->eap, ext);
+}
+
+
+int eapol_sm_failed(struct eapol_sm *sm)
+{
+       if (sm == NULL)
+               return 0;
+       return !sm->eapSuccess && sm->eapFail;
+}
+
+
+int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len)
+{
+#ifdef CONFIG_EAP_PROXY
+       if (sm->eap_proxy == NULL)
+               return -1;
+       return eap_proxy_get_imsi(sm->eap_proxy, imsi, len);
+#else /* CONFIG_EAP_PROXY */
+       return -1;
+#endif /* CONFIG_EAP_PROXY */
+}
+
+
+void eapol_sm_erp_flush(struct eapol_sm *sm)
+{
+       if (sm)
+               eap_peer_erp_free_keys(sm->eap);
+}