diff --git a/hostapd/wpa.c b/hostapd/wpa.c index 946552c94..4f563542e 100644 --- a/hostapd/wpa.c +++ b/hostapd/wpa.c @@ -498,7 +498,7 @@ void wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, #endif /* CONFIG_IEEE80211R */ if (sm->started) { - os_memset(sm->key_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); + os_memset(&sm->key_replay, 0, sizeof(sm->key_replay)); sm->ReAuthenticationRequest = TRUE; wpa_sm_step(sm); return; @@ -574,6 +574,21 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm) } +static int wpa_replay_counter_valid(struct wpa_state_machine *sm, + const u8 *replay_counter) +{ + int i; + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (!sm->key_replay[i].valid) + break; + if (os_memcmp(replay_counter, sm->key_replay[i].counter, + WPA_REPLAY_COUNTER_LEN) == 0) + return 1; + } + return 0; +} + + void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len) @@ -672,14 +687,18 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } if (!(key_info & WPA_KEY_INFO_REQUEST) && - (!sm->key_replay_counter_valid || - os_memcmp(key->replay_counter, sm->key_replay_counter, - WPA_REPLAY_COUNTER_LEN) != 0)) { + !wpa_replay_counter_valid(sm, key->replay_counter)) { + int i; wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key %s with unexpected " "replay counter", msgtxt); - wpa_hexdump(MSG_DEBUG, "expected replay counter", - sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN); + 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; @@ -842,8 +861,12 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, wpa_rekey_gtk(wpa_auth, NULL); } } else { - /* Do not allow the same key replay counter to be reused. */ - sm->key_replay_counter_valid = FALSE; + /* Do not allow the same key replay counter to be reused. This + * does also invalidate all other pending replay counters if + * retransmissions were used, i.e., we will only process one of + * the pending replies and ignore rest if more than one is + * received. */ + sm->key_replay[0].valid = FALSE; } #ifdef CONFIG_PEERKEY @@ -914,6 +937,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, int key_data_len, pad_len = 0; u8 *buf, *pos; int version, pairwise; + int i; len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); @@ -986,10 +1010,16 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, WPA_PUT_BE16(key->key_length, 0); /* FIX: STSL: what to use as key_replay_counter? */ - inc_byte_array(sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN); - os_memcpy(key->replay_counter, sm->key_replay_counter, + for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) { + sm->key_replay[i].valid = sm->key_replay[i - 1].valid; + os_memcpy(sm->key_replay[i].counter, + sm->key_replay[i - 1].counter, + WPA_REPLAY_COUNTER_LEN); + } + inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); + os_memcpy(key->replay_counter, sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); - sm->key_replay_counter_valid = TRUE; + sm->key_replay[0].valid = TRUE; if (nonce) os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN); diff --git a/hostapd/wpa_auth_i.h b/hostapd/wpa_auth_i.h index 7770d179d..3ee65488b 100644 --- a/hostapd/wpa_auth_i.h +++ b/hostapd/wpa_auth_i.h @@ -15,6 +15,9 @@ #ifndef WPA_AUTH_I_H #define WPA_AUTH_I_H +/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */ +#define RSNA_MAX_EAPOL_RETRIES 3 + struct wpa_group; struct wpa_stsl_negotiation { @@ -66,8 +69,10 @@ struct wpa_state_machine { Boolean pairwise_set; int keycount; Boolean Pair; - u8 key_replay_counter[WPA_REPLAY_COUNTER_LEN]; - Boolean key_replay_counter_valid; + struct { + u8 counter[WPA_REPLAY_COUNTER_LEN]; + Boolean valid; + } key_replay[RSNA_MAX_EAPOL_RETRIES]; Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */ Boolean PTKRequest; /* not in IEEE 802.11i state machine */ Boolean has_GTK;