diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index 3c370f7e9..676a3e15c 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -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 diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index e64ef1d8d..510d64fca 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -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); + } +} diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 52ef2573c..b3e1ff027 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -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, diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index a6ce4c843..f7999b9f0 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -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; diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index 3173144ab..67a5c3bf4 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -120,6 +120,8 @@ struct wpa_state_machine { * message 2/4 */ u8 *assoc_resp_ftie; #endif /* CONFIG_IEEE80211R */ + + int pending_1_of_4_timeout; };