]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
Work around SNonce updates on EAPOL-Key 1/4 retransmission
authorJouni Malinen <jouni.malinen@atheros.com>
Tue, 29 Mar 2011 14:39:12 +0000 (17:39 +0300)
committerJouni Malinen <j@w1.fi>
Tue, 29 Mar 2011 14:39:12 +0000 (17:39 +0300)
Some deployed supplicants update their SNonce for every receive
EAPOL-Key message 1/4 even when these messages happen during the
same 4-way handshake. Furthermore, some of these supplicants fail
to use the first SNonce that they sent and derive an incorrect PTK
using another SNonce that does not match with what the authenticator
is using from the first received message 2/4. This results in
failed 4-way handshake whenever the EAPOL-Key 1/4 retransmission
timeout is reached. The timeout for the first retry is fixed to
100 ms in the IEEE 802.11 standard and that seems to be short
enough to make it difficult for some stations to get the response
out before retransmission.

Work around this issue by increasing the initial EAPOL-Key 1/4
timeout by 1000 ms (i.e., total timeout of 1100 ms) if the station
acknowledges reception of the EAPOL-Key frame. If the driver does
not indicate TX status for EAPOL frames, use longer initial
timeout (1000 ms) unconditionally.

src/ap/ieee802_1x.c
src/ap/wpa_auth.c
src/ap/wpa_auth.h
src/ap/wpa_auth_glue.c
src/ap/wpa_auth_i.h

index 3c370f7e93be842e99dd97e1650aa1127c9f1c1b..676a3e15c744c4cc7510c93c0b885b06a83e85d2 100644 (file)
@@ -1770,6 +1770,16 @@ int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
                   MAC2STR(sta->addr), xhdr->version, xhdr->type,
                   be_to_host16(xhdr->length), ack);
 
+       if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY &&
+           pos + sizeof(struct wpa_eapol_key) <= buf + len) {
+               const struct wpa_eapol_key *wpa;
+               wpa = (const struct wpa_eapol_key *) pos;
+               if (wpa->type == EAPOL_KEY_TYPE_RSN ||
+                   wpa->type == EAPOL_KEY_TYPE_WPA)
+                       wpa_auth_eapol_key_tx_status(hapd->wpa_auth,
+                                                    sta->wpa_sm, ack);
+       }
+
        /* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant
         * or Authenticator state machines, but EAPOL-Key packets are not
         * retransmitted in case of failure. Try to re-sent failed EAPOL-Key
index e64ef1d8dc8eb125591b5bb8a8800509d1baf150..510d64fca5aa03482aeb5ec2cecfc80b0f30597e 100644 (file)
@@ -602,6 +602,7 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
        }
 
        eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
+       sm->pending_1_of_4_timeout = 0;
        eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
        eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
        if (sm->in_step_loop) {
@@ -969,6 +970,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                }
                sm->MICVerified = TRUE;
                eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+               sm->pending_1_of_4_timeout = 0;
        }
 
        if (key_info & WPA_KEY_INFO_REQUEST) {
@@ -1098,6 +1100,7 @@ static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx)
        struct wpa_authenticator *wpa_auth = eloop_ctx;
        struct wpa_state_machine *sm = timeout_ctx;
 
+       sm->pending_1_of_4_timeout = 0;
        wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout");
        sm->TimeoutEvt = TRUE;
        wpa_sm_step(sm);
@@ -1285,10 +1288,14 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
                         keyidx, encr, 0);
 
        ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
-       if (ctr == 1)
+       if (ctr == 1 && wpa_auth->conf.tx_status)
                timeout_ms = eapol_key_timeout_first;
        else
                timeout_ms = eapol_key_timeout_subseq;
+       if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
+               sm->pending_1_of_4_timeout = 1;
+       wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry "
+                  "counter %d)", timeout_ms, ctr);
        eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
                               wpa_send_eapol_timeout, wpa_auth, sm);
 }
@@ -1711,6 +1718,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
        }
 #endif /* CONFIG_IEEE80211R */
 
+       sm->pending_1_of_4_timeout = 0;
        eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
 
        if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
@@ -2798,3 +2806,33 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
        sm->group = group;
        return 0;
 }
+
+
+void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
+                                 struct wpa_state_machine *sm, int ack)
+{
+       if (wpa_auth == NULL || sm == NULL)
+               return;
+       wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR
+                  " ack=%d", MAC2STR(sm->addr), ack);
+       if (sm->pending_1_of_4_timeout && ack) {
+               /*
+                * Some deployed supplicant implementations update their SNonce
+                * for each EAPOL-Key 2/4 message even within the same 4-way
+                * handshake and then fail to use the first SNonce when
+                * deriving the PTK. This results in unsuccessful 4-way
+                * handshake whenever the relatively short initial timeout is
+                * reached and EAPOL-Key 1/4 is retransmitted. Try to work
+                * around this by increasing the timeout now that we know that
+                * the station has received the frame.
+                */
+               int timeout_ms = eapol_key_timeout_subseq;
+               wpa_printf(MSG_DEBUG, "WPA: Increase initial EAPOL-Key 1/4 "
+                          "timeout by %u ms because of acknowledged frame",
+                          timeout_ms);
+               eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+               eloop_register_timeout(timeout_ms / 1000,
+                                      (timeout_ms % 1000) * 1000,
+                                      wpa_send_eapol_timeout, wpa_auth, sm);
+       }
+}
index 52ef2573cf40c574cfd1b51c4e7add281e8e38ac..b3e1ff027e06284b623a72eda0cf1d08aaae9cd7 100644 (file)
@@ -144,6 +144,7 @@ struct wpa_auth_config {
        int wmm_enabled;
        int wmm_uapsd;
        int okc;
+       int tx_status;
 #ifdef CONFIG_IEEE80211W
        enum mfp_options ieee80211w;
 #endif /* CONFIG_IEEE80211W */
@@ -260,6 +261,8 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
                               int session_timeout,
                               struct eapol_state_machine *eapol);
 int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
+void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
+                                 struct wpa_state_machine *sm, int ack);
 
 #ifdef CONFIG_IEEE80211R
 u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
index a6ce4c8435ca40682a91a847f4c2d63381ea8045..f7999b9f02ecab310f8db66e03492de268510b3d 100644 (file)
@@ -462,6 +462,8 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
        size_t wpa_ie_len;
 
        hostapd_wpa_auth_conf(hapd->conf, &_conf);
+       if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
+               _conf.tx_status = 1;
        os_memset(&cb, 0, sizeof(cb));
        cb.ctx = hapd;
        cb.logger = hostapd_wpa_auth_logger;
index 3173144abfff761069a850b646a213e4d8fc7f57..67a5c3bf45ae6a82dc16a0b6fa0aaa467154cabe 100644 (file)
@@ -120,6 +120,8 @@ struct wpa_state_machine {
                                               * message 2/4 */
        u8 *assoc_resp_ftie;
 #endif /* CONFIG_IEEE80211R */
+
+       int pending_1_of_4_timeout;
 };