From: Jouni Malinen Date: Sun, 28 Jan 2024 09:38:45 +0000 (+0200) Subject: Move Key Replay Counter checks for EAPOL-Key frames to helper functions X-Git-Tag: hostap_2_11~403 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8037c1ad6198090f541a34dda2990e97a748ac67;p=thirdparty%2Fhostap.git Move Key Replay Counter checks for EAPOL-Key frames to helper functions This simplifies wpa_receive(). Signed-off-by: Jouni Malinen --- diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 1c49213d5..cc2f1393d 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1097,6 +1097,8 @@ static bool wpa_auth_gtk_rekey_in_process(struct wpa_authenticator *wpa_auth) } +enum eapol_key_msg { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST }; + static bool wpa_auth_valid_key_desc_ver(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u16 ver) { @@ -1138,6 +1140,105 @@ static bool wpa_auth_valid_key_desc_ver(struct wpa_authenticator *wpa_auth, } +static bool wpa_auth_valid_request_counter(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *replay_counter) +{ + + if (sm->req_replay_counter_used && + os_memcmp(replay_counter, sm->req_replay_counter, + WPA_REPLAY_COUNTER_LEN) <= 0) { + wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), + LOGGER_WARNING, + "received EAPOL-Key request with replayed counter"); + return false; + } + + return true; +} + + +static bool wpa_auth_valid_counter(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const struct wpa_eapol_key *key, + enum eapol_key_msg msg, + const char *msgtxt) +{ + int i; + + if (msg == REQUEST) + return wpa_auth_valid_request_counter(wpa_auth, sm, + key->replay_counter); + + if (wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) + return true; + + if (msg == PAIRWISE_2 && + wpa_replay_counter_valid(sm->prev_key_replay, + key->replay_counter) && + sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && + os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0) { + /* + * Some supplicant implementations (e.g., Windows XP + * WZC) update SNonce for each EAPOL-Key 2/4. This + * breaks the workaround on accepting any of the + * pending requests, so allow the SNonce to be updated + * even if we have already sent out EAPOL-Key 3/4. + */ + wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), + LOGGER_DEBUG, + "Process SNonce update from STA based on retransmitted EAPOL-Key 1/4"); + sm->update_snonce = 1; + os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN); + sm->alt_snonce_valid = true; + os_memcpy(sm->alt_replay_counter, + sm->key_replay[0].counter, + WPA_REPLAY_COUNTER_LEN); + return true; + } + + if (msg == PAIRWISE_4 && sm->alt_snonce_valid && + sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && + os_memcmp(key->replay_counter, sm->alt_replay_counter, + WPA_REPLAY_COUNTER_LEN) == 0) { + /* + * Supplicant may still be using the old SNonce since + * there was two EAPOL-Key 2/4 messages and they had + * different SNonce values. + */ + wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), + LOGGER_DEBUG, + "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4"); + return true; + } + + if (msg == PAIRWISE_2 && + wpa_replay_counter_valid(sm->prev_key_replay, + key->replay_counter) && + sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) { + wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), + LOGGER_DEBUG, + "ignore retransmitted EAPOL-Key %s - SNonce did not change", + msgtxt); + } else { + wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), + LOGGER_DEBUG, + "received EAPOL-Key %s with unexpected replay counter", + msgtxt); + } + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (!sm->key_replay[i].valid) + break; + wpa_hexdump(MSG_DEBUG, "pending replay counter", + sm->key_replay[i].counter, + WPA_REPLAY_COUNTER_LEN); + } + wpa_hexdump(MSG_DEBUG, "received replay counter", + key->replay_counter, WPA_REPLAY_COUNTER_LEN); + return false; +} + + void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len) @@ -1145,8 +1246,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; u16 key_info, ver, key_data_length; - enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg; - char *msgtxt; + enum eapol_key_msg msg; + const char *msgtxt; struct wpa_eapol_ie_parse kde; const u8 *key_data; size_t keyhdrlen, mic_len; @@ -1272,88 +1373,9 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, msgtxt = "2/4 Pairwise"; } - if (key_info & WPA_KEY_INFO_REQUEST) { - if (sm->req_replay_counter_used && - os_memcmp(key->replay_counter, sm->req_replay_counter, - WPA_REPLAY_COUNTER_LEN) <= 0) { - wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), - LOGGER_WARNING, - "received EAPOL-Key request with replayed counter"); - goto out; - } - } - - if (!(key_info & WPA_KEY_INFO_REQUEST) && - !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) { - int i; - - if (msg == PAIRWISE_2 && - wpa_replay_counter_valid(sm->prev_key_replay, - key->replay_counter) && - sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && - os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0) - { - /* - * Some supplicant implementations (e.g., Windows XP - * WZC) update SNonce for each EAPOL-Key 2/4. This - * breaks the workaround on accepting any of the - * pending requests, so allow the SNonce to be updated - * even if we have already sent out EAPOL-Key 3/4. - */ - wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), - LOGGER_DEBUG, - "Process SNonce update from STA based on retransmitted EAPOL-Key 1/4"); - sm->update_snonce = 1; - os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN); - sm->alt_snonce_valid = true; - os_memcpy(sm->alt_replay_counter, - sm->key_replay[0].counter, - WPA_REPLAY_COUNTER_LEN); - goto continue_processing; - } - - if (msg == PAIRWISE_4 && sm->alt_snonce_valid && - sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && - os_memcmp(key->replay_counter, sm->alt_replay_counter, - WPA_REPLAY_COUNTER_LEN) == 0) { - /* - * Supplicant may still be using the old SNonce since - * there was two EAPOL-Key 2/4 messages and they had - * different SNonce values. - */ - wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), - LOGGER_DEBUG, - "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4"); - goto continue_processing; - } - - if (msg == PAIRWISE_2 && - wpa_replay_counter_valid(sm->prev_key_replay, - key->replay_counter) && - sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) { - wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), - LOGGER_DEBUG, - "ignore retransmitted EAPOL-Key %s - SNonce did not change", - msgtxt); - } else { - wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), - LOGGER_DEBUG, - "received EAPOL-Key %s with unexpected replay counter", - msgtxt); - } - for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { - if (!sm->key_replay[i].valid) - break; - wpa_hexdump(MSG_DEBUG, "pending replay counter", - sm->key_replay[i].counter, - WPA_REPLAY_COUNTER_LEN); - } - wpa_hexdump(MSG_DEBUG, "received replay counter", - key->replay_counter, WPA_REPLAY_COUNTER_LEN); + if (!wpa_auth_valid_counter(wpa_auth, sm, key, msg, msgtxt)) goto out; - } -continue_processing: #ifdef CONFIG_FILS if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {