diff options
Diffstat (limited to 'src/ap/wpa_auth.c')
-rw-r--r-- | src/ap/wpa_auth.c | 813 |
1 files changed, 592 insertions, 221 deletions
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 36cb0f4783c34..2c70149a18189 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1,15 +1,9 @@ /* - * hostapd - IEEE 802.11i-2004 / WPA Authenticator - * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * IEEE 802.11 RSN / WPA Authenticator + * Copyright (c) 2004-2011, 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 "utils/includes.h" @@ -22,6 +16,7 @@ #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/random.h" #include "eapol_auth/eapol_auth_sm.h" #include "ap_config.h" #include "ieee802_11.h" @@ -44,11 +39,14 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, static void wpa_request_new_ptk(struct wpa_state_machine *sm); static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, struct wpa_group *group); +static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); static const u32 dot11RSNAConfigGroupUpdateCount = 4; static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; static const u32 eapol_key_timeout_first = 100; /* ms */ static const u32 eapol_key_timeout_subseq = 1000; /* ms */ +static const u32 eapol_key_timeout_first_group = 500; /* ms */ /* TODO: make these configurable */ static const int dot11RSNAConfigPMKLifetime = 43200; @@ -56,11 +54,12 @@ static const int dot11RSNAConfigPMKReauthThreshold = 70; static const int dot11RSNAConfigSATimeout = 60; -static inline void wpa_auth_mic_failure_report( +static inline int wpa_auth_mic_failure_report( struct wpa_authenticator *wpa_auth, const u8 *addr) { if (wpa_auth->cb.mic_failure_report) - wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); + return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); + return 0; } @@ -191,6 +190,7 @@ static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, { if (wpa_auth->cb.disconnect == NULL) return; + wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr)); wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } @@ -215,11 +215,13 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; - if (os_get_random(wpa_auth->group->GMK, WPA_GMK_LEN)) { + if (random_get_bytes(wpa_auth->group->GMK, WPA_GMK_LEN)) { wpa_printf(MSG_ERROR, "Failed to get random data for WPA " "initialization."); } else { wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd"); + wpa_hexdump_key(MSG_DEBUG, "GMK", + wpa_auth->group->GMK, WPA_GMK_LEN); } if (wpa_auth->conf.wpa_gmk_rekey) { @@ -277,31 +279,40 @@ static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, } -static void wpa_group_set_key_len(struct wpa_group *group, int cipher) +static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - switch (cipher) { - case WPA_CIPHER_CCMP: - group->GTK_len = 16; - break; - case WPA_CIPHER_TKIP: - group->GTK_len = 32; - break; - case WPA_CIPHER_WEP104: - group->GTK_len = 13; - break; - case WPA_CIPHER_WEP40: - group->GTK_len = 5; - break; - } + u8 buf[ETH_ALEN + 8 + sizeof(group)]; + u8 rkey[32]; + + if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN); + + /* + * Counter = PRF-256(Random number, "Init Counter", + * Local MAC Address || Time) + */ + os_memcpy(buf, wpa_auth->addr, ETH_ALEN); + wpa_get_ntp_timestamp(buf + ETH_ALEN); + os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); + if (random_get_bytes(rkey, sizeof(rkey)) < 0) + return -1; + + if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), + group->Counter, WPA_NONCE_LEN) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "Key Counter", + group->Counter, WPA_NONCE_LEN); + + return 0; } static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, - int vlan_id) + int vlan_id, int delay_init) { struct wpa_group *group; - u8 buf[ETH_ALEN + 8 + sizeof(group)]; - u8 rkey[32]; group = os_zalloc(sizeof(struct wpa_group)); if (group == NULL) @@ -309,30 +320,37 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, group->GTKAuthenticator = TRUE; group->vlan_id = vlan_id; + group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); - wpa_group_set_key_len(group, wpa_auth->conf.wpa_group); + if (random_pool_ready() != 1) { + wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " + "for secure operations - update keys later when " + "the first station connects"); + } - /* Counter = PRF-256(Random number, "Init Counter", - * Local MAC Address || Time) + /* + * Set initial GMK/Counter value here. The actual values that will be + * used in negotiations will be set once the first station tries to + * connect. This allows more time for collecting additional randomness + * on embedded devices. */ - os_memcpy(buf, wpa_auth->addr, ETH_ALEN); - wpa_get_ntp_timestamp(buf + ETH_ALEN); - os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); - if (os_get_random(rkey, sizeof(rkey)) || - os_get_random(group->GMK, WPA_GMK_LEN)) { + if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) { wpa_printf(MSG_ERROR, "Failed to get random data for WPA " "initialization."); os_free(group); return NULL; } - sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), - group->Counter, WPA_NONCE_LEN); - group->GInit = TRUE; - wpa_group_sm_step(wpa_auth, group); - group->GInit = FALSE; - wpa_group_sm_step(wpa_auth, group); + if (delay_init) { + wpa_printf(MSG_DEBUG, "WPA: Delay group state machine start " + "until Beacon frames have been configured"); + /* Initialization is completed in wpa_init_keys(). */ + } else { + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + } return group; } @@ -364,7 +382,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr, return NULL; } - wpa_auth->group = wpa_group_init(wpa_auth, 0); + wpa_auth->group = wpa_group_init(wpa_auth, 0, 1); if (wpa_auth->group == NULL) { os_free(wpa_auth->wpa_ie); os_free(wpa_auth); @@ -405,6 +423,19 @@ struct wpa_authenticator * wpa_init(const u8 *addr, } +int wpa_init_keys(struct wpa_authenticator *wpa_auth) +{ + struct wpa_group *group = wpa_auth->group; + + wpa_printf(MSG_DEBUG, "WPA: Start group state machine to set initial " + "keys"); + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + return 0; +} + + /** * wpa_deinit - Deinitialize WPA authenticator * @wpa_auth: Pointer to WPA authenticator data from wpa_init() @@ -464,7 +495,7 @@ int wpa_reconfig(struct wpa_authenticator *wpa_auth, * configuration. */ group = wpa_auth->group; - wpa_group_set_key_len(group, wpa_auth->conf.wpa_group); + group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); group->GInit = TRUE; wpa_group_sm_step(wpa_auth, group); group->GInit = FALSE; @@ -539,6 +570,10 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm) static void wpa_free_sta_sm(struct wpa_state_machine *sm) { + if (sm->GUpdateStationKeys) { + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + } #ifdef CONFIG_IEEE80211R os_free(sm->assoc_resp_ftie); #endif /* CONFIG_IEEE80211R */ @@ -563,6 +598,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) { @@ -586,14 +622,14 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm) } -static int wpa_replay_counter_valid(struct wpa_state_machine *sm, +static int wpa_replay_counter_valid(struct wpa_key_replay_counter *ctr, const u8 *replay_counter) { int i; for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { - if (!sm->key_replay[i].valid) + if (!ctr[i].valid) break; - if (os_memcmp(replay_counter, sm->key_replay[i].counter, + if (os_memcmp(replay_counter, ctr[i].counter, WPA_REPLAY_COUNTER_LEN) == 0) return 1; } @@ -601,6 +637,20 @@ static int wpa_replay_counter_valid(struct wpa_state_machine *sm, } +static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr, + const u8 *replay_counter) +{ + int i; + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (ctr[i].valid && + (replay_counter == NULL || + os_memcmp(replay_counter, ctr[i].counter, + WPA_REPLAY_COUNTER_LEN) == 0)) + ctr[i].valid = FALSE; + } +} + + #ifdef CONFIG_IEEE80211R static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, @@ -651,6 +701,39 @@ static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, #endif /* CONFIG_IEEE80211R */ +static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int group) +{ + /* Supplicant reported a Michael MIC error */ + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Error Request " + "(STA detected Michael MIC failure (group=%d))", + group); + + if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "ignore Michael MIC failure report since " + "group cipher is not TKIP"); + } else if (!group && sm->pairwise != WPA_CIPHER_TKIP) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "ignore Michael MIC failure report since " + "pairwise cipher is not TKIP"); + } else { + if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0) + return 1; /* STA entry was removed */ + sm->dot11RSNAStatsTKIPRemoteMICFailures++; + wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; + } + + /* + * Error report is not a request for a new key handshake, but since + * Authenticator may do it, let's change the keys now anyway. + */ + wpa_request_new_ptk(sm); + return 0; +} + + void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len) @@ -676,6 +759,9 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, key = (struct wpa_eapol_key *) (hdr + 1); key_info = WPA_GET_BE16(key->key_info); key_data_length = WPA_GET_BE16(key->key_data_length); + wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR + " key_info=0x%x type=%u key_data_length=%u", + MAC2STR(sm->addr), key_info, key->type, key_data_length); if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) { wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " "key_data overflow (%d > %lu)", @@ -686,7 +772,14 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } if (sm->wpa == WPA_VERSION_WPA2) { - if (key->type != EAPOL_KEY_TYPE_RSN) { + if (key->type == EAPOL_KEY_TYPE_WPA) { + /* + * Some deployed station implementations seem to send + * msg 4/4 with incorrect type value in WPA2 mode. + */ + wpa_printf(MSG_DEBUG, "Workaround: Allow EAPOL-Key " + "with unexpected WPA type in RSN mode"); + } else if (key->type != EAPOL_KEY_TYPE_RSN) { wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " "unexpected type %d in RSN mode", key->type); @@ -701,6 +794,11 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } } + wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce, + WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter", + key->replay_counter, WPA_REPLAY_COUNTER_LEN); + /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys * are set */ @@ -734,7 +832,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 || msg == GROUP_2) { u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK; - if (sm->pairwise == WPA_CIPHER_CCMP) { + if (sm->pairwise == WPA_CIPHER_CCMP || + sm->pairwise == WPA_CIPHER_GCMP) { if (wpa_use_aes_cmac(sm) && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_auth_logger(wpa_auth, sm->addr, @@ -750,7 +849,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "did not use HMAC-SHA1-AES " - "with CCMP"); + "with CCMP/GCMP"); return; } } @@ -768,11 +867,44 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } if (!(key_info & WPA_KEY_INFO_REQUEST) && - !wpa_replay_counter_valid(sm, key->replay_counter)) { + !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) { int i; - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key %s with unexpected " - "replay counter", msgtxt); + + 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, sm->addr, LOGGER_DEBUG, + "Process SNonce update from STA " + "based on retransmitted EAPOL-Key " + "1/4"); + sm->update_snonce = 1; + wpa_replay_counter_mark_invalid(sm->prev_key_replay, + key->replay_counter); + 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, sm->addr, LOGGER_DEBUG, + "ignore retransmitted EAPOL-Key %s - " + "SNonce did not change", msgtxt); + } else { + wpa_auth_vlogger(wpa_auth, sm->addr, 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; @@ -785,16 +917,37 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, return; } +continue_processing: switch (msg) { case PAIRWISE_2: if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && - sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) { + sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING && + (!sm->update_snonce || + sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 2/4 in " "invalid state (%d) - dropped", sm->wpa_ptk_state); return; } + random_add_randomness(key->key_nonce, WPA_NONCE_LEN); + if (sm->group->reject_4way_hs_for_entropy) { + /* + * The system did not have enough entropy to generate + * strong random numbers. Reject the first 4-way + * handshake(s) and collect some entropy based on the + * information from it. Once enough entropy is + * available, the next atempt will trigger GMK/Key + * Counter update and the station will be allowed to + * continue. + */ + wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to " + "collect more entropy for random number " + "generation"); + random_mark_pool_ready(); + wpa_sta_disconnect(wpa_auth, sm->addr); + return; + } if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length, &kde) < 0) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, @@ -897,7 +1050,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } sm->MICVerified = FALSE; - if (sm->PTK_valid) { + if (sm->PTK_valid && !sm->update_snonce) { if (wpa_verify_key_mic(&sm->PTK, data, data_len)) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key with invalid MIC"); @@ -905,6 +1058,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) { @@ -930,17 +1084,10 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, #endif /* CONFIG_PEERKEY */ return; } else if (key_info & WPA_KEY_INFO_ERROR) { - /* Supplicant reported a Michael MIC error */ - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key Error Request " - "(STA detected Michael MIC failure)"); - wpa_auth_mic_failure_report(wpa_auth, sm->addr); - sm->dot11RSNAStatsTKIPRemoteMICFailures++; - wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; - /* Error report is not a request for a new key - * handshake, but since Authenticator may do it, let's - * change the keys now anyway. */ - wpa_request_new_ptk(sm); + if (wpa_receive_error_report( + wpa_auth, sm, + !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0) + return; /* STA entry was removed */ } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Request for new " @@ -958,19 +1105,34 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Request for GTK " "rekeying"); - /* FIX: why was this triggering PTK rekeying for the - * STA that requested Group Key rekeying?? */ - /* wpa_request_new_ptk(sta->wpa_sm); */ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); wpa_rekey_gtk(wpa_auth, NULL); } } else { - /* 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; + /* Do not allow the same key replay counter to be reused. */ + wpa_replay_counter_mark_invalid(sm->key_replay, + key->replay_counter); + + if (msg == PAIRWISE_2) { + /* + * Maintain a copy of the pending EAPOL-Key frames in + * case the EAPOL-Key frame was retransmitted. This is + * needed to allow EAPOL-Key msg 2/4 reply to another + * pending msg 1/4 to update the SNonce to work around + * unexpected supplicant behavior. + */ + os_memcpy(sm->prev_key_replay, sm->key_replay, + sizeof(sm->key_replay)); + } else { + os_memset(sm->prev_key_replay, 0, + sizeof(sm->prev_key_replay)); + } + + /* + * Make sure old valid counters are not accepted anymore and + * do not get copied again. + */ + wpa_replay_counter_mark_invalid(sm->key_replay, NULL); } #ifdef CONFIG_PEERKEY @@ -987,6 +1149,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, os_memcpy(sm->last_rx_eapol_key, data, data_len); sm->last_rx_eapol_key_len = data_len; + sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE); sm->EAPOLKeyReceived = TRUE; sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST); @@ -995,25 +1158,37 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } -static void wpa_gmk_to_gtk(const u8 *gmk, const u8 *addr, const u8 *gnonce, - u8 *gtk, size_t gtk_len) +static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr, + const u8 *gnonce, u8 *gtk, size_t gtk_len) { - u8 data[ETH_ALEN + WPA_NONCE_LEN]; + u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16]; + u8 *pos; + int ret = 0; - /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */ + /* GTK = PRF-X(GMK, "Group key expansion", + * AA || GNonce || Time || random data) + * The example described in the IEEE 802.11 standard uses only AA and + * GNonce as inputs here. Add some more entropy since this derivation + * is done only at the Authenticator and as such, does not need to be + * exactly same. + */ os_memcpy(data, addr, ETH_ALEN); os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); + pos = data + ETH_ALEN + WPA_NONCE_LEN; + wpa_get_ntp_timestamp(pos); + pos += 8; + if (random_get_bytes(pos, 16) < 0) + ret = -1; #ifdef CONFIG_IEEE80211W - sha256_prf(gmk, WPA_GMK_LEN, "Group key expansion", - data, sizeof(data), gtk, gtk_len); + sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len); #else /* CONFIG_IEEE80211W */ - sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion", - data, sizeof(data), gtk, gtk_len); + if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len) + < 0) + ret = -1; #endif /* CONFIG_IEEE80211W */ - wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN); - wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len); + return ret; } @@ -1022,6 +1197,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); @@ -1049,7 +1225,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, version = force_version; else if (wpa_use_aes_cmac(sm)) version = WPA_KEY_INFO_TYPE_AES_128_CMAC; - else if (sm->pairwise == WPA_CIPHER_CCMP) + else if (sm->pairwise != WPA_CIPHER_TKIP) version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -1096,20 +1272,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, WPA_PUT_BE16(key->key_info, key_info); alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; - switch (alg) { - case WPA_CIPHER_CCMP: - WPA_PUT_BE16(key->key_length, 16); - break; - case WPA_CIPHER_TKIP: - WPA_PUT_BE16(key->key_length, 32); - break; - case WPA_CIPHER_WEP40: - WPA_PUT_BE16(key->key_length, 5); - break; - case WPA_CIPHER_WEP104: - WPA_PUT_BE16(key->key_length, 13); - break; - } + WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg)); if (key_info & WPA_KEY_INFO_SMK_MESSAGE) WPA_PUT_BE16(key->key_length, 0); @@ -1209,10 +1372,15 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, keyidx, encr, 0); ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr; - if (ctr == 1) - timeout_ms = eapol_key_timeout_first; + if (ctr == 1 && wpa_auth->conf.tx_status) + timeout_ms = pairwise ? eapol_key_timeout_first : + eapol_key_timeout_first_group; 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); } @@ -1247,8 +1415,7 @@ void wpa_remove_ptk(struct wpa_state_machine *sm) { sm->PTK_valid = FALSE; os_memset(&sm->PTK, 0, sizeof(sm->PTK)); - wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, (u8 *) "", - 0); + wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0); sm->pairwise_set = FALSE; eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); } @@ -1338,22 +1505,6 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) } -static enum wpa_alg wpa_alg_enum(int alg) -{ - switch (alg) { - case WPA_CIPHER_CCMP: - return WPA_ALG_CCMP; - case WPA_CIPHER_TKIP: - return WPA_ALG_TKIP; - case WPA_CIPHER_WEP104: - case WPA_CIPHER_WEP40: - return WPA_ALG_WEP; - default: - return WPA_ALG_NONE; - } -} - - SM_STATE(WPA_PTK, INITIALIZE) { SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk); @@ -1411,11 +1562,58 @@ SM_STATE(WPA_PTK, AUTHENTICATION) } +static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + if (group->first_sta_seen) + return; + /* + * System has run bit further than at the time hostapd was started + * potentially very early during boot up. This provides better chances + * of collecting more randomness on embedded systems. Re-initialize the + * GMK and Counter here to improve their strength if there was not + * enough entropy available immediately after system startup. + */ + wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first " + "station"); + if (random_pool_ready() != 1) { + wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " + "to proceed - reject first 4-way handshake"); + group->reject_4way_hs_for_entropy = TRUE; + } else { + group->first_sta_seen = TRUE; + group->reject_4way_hs_for_entropy = FALSE; + } + + wpa_group_init_gmk_and_counter(wpa_auth, group); + wpa_gtk_update(wpa_auth, group); + wpa_group_config_group_keys(wpa_auth, group); +} + + SM_STATE(WPA_PTK, AUTHENTICATION2) { SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); - os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); + + wpa_group_ensure_init(sm->wpa_auth, sm->group); + + /* + * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat + * ambiguous. The Authenticator state machine uses a counter that is + * incremented by one for each 4-way handshake. However, the security + * analysis of 4-way handshake points out that unpredictable nonces + * help in preventing precomputation attacks. Instead of the state + * machine definition, use an unpredictable nonce value here to provide + * stronger protection against potential precomputation attacks. + */ + if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { + wpa_printf(MSG_ERROR, "WPA: Failed to get random data for " + "ANonce."); + wpa_sta_disconnect(sm->wpa_auth, sm->addr); + return; + } + wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce, + WPA_NONCE_LEN); sm->ReAuthenticationRequest = FALSE; /* IEEE 802.11i does not clear TimeoutCtr here, but this is more * logical place than INITIALIZE since AUTHENTICATION2 can be @@ -1531,7 +1729,7 @@ SM_STATE(WPA_PTK, PTKSTART) static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk, struct wpa_ptk *ptk) { - size_t ptk_len = sm->pairwise == WPA_CIPHER_CCMP ? 48 : 64; + size_t ptk_len = sm->pairwise != WPA_CIPHER_TKIP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len); @@ -1554,6 +1752,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; + sm->update_snonce = FALSE; /* WPA with IEEE 802.1X: use the derived PMK from EAP * WPA-PSK: iterate through possible PSKs and select the one matching @@ -1605,6 +1804,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)) { @@ -1654,6 +1854,14 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0) os_memset(igtk.pn, 0, sizeof(igtk.pn)); os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random IGTK to each STA to prevent use of + * IGTK in the BSS. + */ + if (random_get_bytes(igtk.igtk, WPA_IGTK_LEN) < 0) + return pos; + } pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, (const u8 *) &igtk, sizeof(igtk), NULL, 0); @@ -1678,7 +1886,7 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) SM_STATE(WPA_PTK, PTKINITNEGOTIATING) { - u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos; + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32]; size_t gtk_len, kde_len; struct wpa_group *gsm = sm->group; u8 *wpa_ie; @@ -1716,6 +1924,15 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) secure = 1; gtk = gsm->GTK[gsm->GN - 1]; gtk_len = gsm->GTK_len; + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. + */ + if (random_get_bytes(dummy_gtk, gtk_len) < 0) + return; + gtk = dummy_gtk; + } keyidx = gsm->GN; _rsc = rsc; encr = 1; @@ -1726,6 +1943,20 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) gtk_len = 0; keyidx = 0; _rsc = NULL; + if (sm->rx_eapol_key_secure) { + /* + * It looks like Windows 7 supplicant tries to use + * Secure bit in msg 2/4 after having reported Michael + * MIC failure and it then rejects the 4-way handshake + * if msg 3/4 does not set Secure bit. Work around this + * by setting the Secure bit here even in the case of + * WPA if the supplicant used it first. + */ + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "STA used Secure bit in WPA msg 2/4 - " + "set Secure for 3/4 as workaround"); + secure = 1; + } } kde_len = wpa_ie_len + ieee80211w_kde_len(sm); @@ -1813,15 +2044,8 @@ SM_STATE(WPA_PTK, PTKINITDONE) SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); sm->EAPOLKeyReceived = FALSE; if (sm->Pair) { - enum wpa_alg alg; - int klen; - if (sm->pairwise == WPA_CIPHER_TKIP) { - alg = WPA_ALG_TKIP; - klen = 32; - } else { - alg = WPA_ALG_CCMP; - klen = 16; - } + enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise); + int klen = wpa_cipher_key_len(sm->pairwise); if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, sm->PTK.tk1, klen)) { wpa_sta_disconnect(sm->wpa_auth, sm->addr); @@ -1876,8 +2100,11 @@ SM_STEP(WPA_PTK) if (sm->Init) SM_ENTER(WPA_PTK, INITIALIZE); else if (sm->Disconnect - /* || FIX: dot11RSNAConfigSALifetime timeout */) + /* || FIX: dot11RSNAConfigSALifetime timeout */) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "WPA_PTK: sm->Disconnect"); SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->DeauthenticationRequest) SM_ENTER(WPA_PTK, DISCONNECTED); else if (sm->AuthenticationRequest) @@ -1913,6 +2140,8 @@ SM_STEP(WPA_PTK) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "INITPMK - keyAvailable = false"); SM_ENTER(WPA_PTK, DISCONNECT); } break; @@ -1933,6 +2162,9 @@ SM_STEP(WPA_PTK) else if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKSTART: Retry limit %d reached", + dot11RSNAConfigPairwiseUpdateCount); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKSTART); @@ -1950,12 +2182,18 @@ SM_STEP(WPA_PTK) SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); break; case WPA_PTK_PTKINITNEGOTIATING: - if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && - sm->EAPOLKeyPairwise && sm->MICVerified) + if (sm->update_snonce) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK, PTKINITDONE); else if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKINITNEGOTIATING: Retry limit %d " + "reached", + dot11RSNAConfigPairwiseUpdateCount); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); @@ -1984,6 +2222,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) struct wpa_group *gsm = sm->group; u8 *kde, *pos, hdr[2]; size_t kde_len; + u8 *gtk, dummy_gtk[32]; SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); @@ -2004,6 +2243,16 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 1/2 msg of Group Key Handshake"); + gtk = gsm->GTK[gsm->GN - 1]; + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. + */ + if (random_get_bytes(dummy_gtk, gsm->GTK_len) < 0) + return; + gtk = dummy_gtk; + } if (sm->wpa == WPA_VERSION_WPA2) { kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + ieee80211w_kde_len(sm); @@ -2015,10 +2264,10 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) hdr[0] = gsm->GN & 0x03; hdr[1] = 0; pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, - gsm->GTK[gsm->GN - 1], gsm->GTK_len); + gtk, gsm->GTK_len); pos = ieee80211w_kde_add(sm, pos); } else { - kde = gsm->GTK[gsm->GN - 1]; + kde = gtk; pos = kde + gsm->GTK_len; } @@ -2094,20 +2343,24 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, { int ret = 0; - /* FIX: is this the correct way of getting GNonce? */ os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); inc_byte_array(group->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(group->GMK, wpa_auth->addr, group->GNonce, - group->GTK[group->GN - 1], group->GTK_len); + if (wpa_gmk_to_gtk(group->GMK, "Group key expansion", + wpa_auth->addr, group->GNonce, + group->GTK[group->GN - 1], group->GTK_len) < 0) + ret = -1; + wpa_hexdump_key(MSG_DEBUG, "GTK", + group->GTK[group->GN - 1], group->GTK_len); #ifdef CONFIG_IEEE80211W if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { - if (os_get_random(group->IGTK[group->GN_igtk - 4], - WPA_IGTK_LEN) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to get new random " - "IGTK"); + os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); + inc_byte_array(group->Counter, WPA_NONCE_LEN); + if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion", + wpa_auth->addr, group->GNonce, + group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN) < 0) ret = -1; - } wpa_hexdump_key(MSG_DEBUG, "IGTK", group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN); } @@ -2140,28 +2393,118 @@ static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth, static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) { + if (ctx != NULL && ctx != sm->group) + return 0; + if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "Not in PTKINITDONE; skip Group Key update"); + sm->GUpdateStationKeys = FALSE; return 0; } if (sm->GUpdateStationKeys) { /* - * This should not really happen, but just in case, make sure - * we do not count the same STA twice in GKeyDoneStations. + * This should not really happen, so add a debug log entry. + * Since we clear the GKeyDoneStations before the loop, the + * station needs to be counted here anyway. */ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "GUpdateStationKeys already set - do not " - "increment GKeyDoneStations"); - } else { - sm->group->GKeyDoneStations++; - sm->GUpdateStationKeys = TRUE; + "GUpdateStationKeys was already set when " + "marking station for GTK rekeying"); } + + /* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */ + if (sm->is_wnmsleep) + return 0; + + sm->group->GKeyDoneStations++; + sm->GUpdateStationKeys = TRUE; + wpa_sm_step(sm); return 0; } +#ifdef CONFIG_WNM +/* update GTK when exiting WNM-Sleep Mode */ +void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm) +{ + if (sm->is_wnmsleep) + return; + + wpa_group_update_sta(sm, NULL); +} + + +void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag) +{ + sm->is_wnmsleep = !!flag; +} + + +int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos) +{ + struct wpa_group *gsm = sm->group; + u8 *start = pos; + + /* + * GTK subelement: + * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | + * Key[5..32] + */ + *pos++ = WNM_SLEEP_SUBELEM_GTK; + *pos++ = 11 + gsm->GTK_len; + /* Key ID in B0-B1 of Key Info */ + WPA_PUT_LE16(pos, gsm->GN & 0x03); + pos += 2; + *pos++ = gsm->GTK_len; + if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0) + return 0; + pos += 8; + os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len); + pos += gsm->GTK_len; + + wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit", + gsm->GN); + wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit", + gsm->GTK[gsm->GN - 1], gsm->GTK_len); + + return pos - start; +} + + +#ifdef CONFIG_IEEE80211W +int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos) +{ + struct wpa_group *gsm = sm->group; + u8 *start = pos; + + /* + * IGTK subelement: + * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16] + */ + *pos++ = WNM_SLEEP_SUBELEM_IGTK; + *pos++ = 2 + 6 + WPA_IGTK_LEN; + WPA_PUT_LE16(pos, gsm->GN_igtk); + pos += 2; + if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0) + return 0; + pos += 6; + + os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + pos += WPA_IGTK_LEN; + + wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit", + gsm->GN_igtk); + wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit", + gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + + return pos - start; +} +#endif /* CONFIG_IEEE80211W */ +#endif /* CONFIG_WNM */ + + static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { @@ -2185,32 +2528,54 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, * including all STAs that could be in not-yet-completed state. */ wpa_gtk_update(wpa_auth, group); - wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL); + if (group->GKeyDoneStations) { + wpa_printf(MSG_DEBUG, "wpa_group_setkeys: Unexpected " + "GKeyDoneStations=%d when starting new GTK rekey", + group->GKeyDoneStations); + group->GKeyDoneStations = 0; + } + wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group); wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d", group->GKeyDoneStations); } -static void wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, - struct wpa_group *group) +static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + int ret = 0; + + if (wpa_auth_set_key(wpa_auth, group->vlan_id, + wpa_cipher_to_alg(wpa_auth->conf.wpa_group), + broadcast_ether_addr, group->GN, + group->GTK[group->GN - 1], group->GTK_len) < 0) + ret = -1; + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION && + wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK, + broadcast_ether_addr, group->GN_igtk, + group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN) < 0) + ret = -1; +#endif /* CONFIG_IEEE80211W */ + + return ret; +} + + +static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " "SETKEYSDONE (VLAN-ID %d)", group->vlan_id); group->changed = TRUE; group->wpa_group_state = WPA_GROUP_SETKEYSDONE; - wpa_auth_set_key(wpa_auth, group->vlan_id, - wpa_alg_enum(wpa_auth->conf.wpa_group), - NULL, group->GN, group->GTK[group->GN - 1], - group->GTK_len); -#ifdef CONFIG_IEEE80211W - if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { - wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK, - NULL, group->GN_igtk, - group->IGTK[group->GN_igtk - 4], - WPA_IGTK_LEN); - } -#endif /* CONFIG_IEEE80211W */ + if (wpa_group_config_group_keys(wpa_auth, group) < 0) + return -1; + + return 0; } @@ -2310,6 +2675,7 @@ void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) group->GN_igtk = tmp; #endif /* CONFIG_IEEE80211W */ wpa_gtk_update(wpa_auth, group); + wpa_group_config_group_keys(wpa_auth, group); } } @@ -2320,23 +2686,6 @@ static const char * wpa_bool_txt(int bool) } -static int wpa_cipher_bits(int cipher) -{ - switch (cipher) { - case WPA_CIPHER_CCMP: - return 128; - case WPA_CIPHER_TKIP: - return 256; - case WPA_CIPHER_WEP104: - return 104; - case WPA_CIPHER_WEP40: - return 40; - default: - return 0; - } -} - - #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) \ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff @@ -2345,19 +2694,21 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) { int len = 0, ret; char pmkid_txt[PMKID_LEN * 2 + 1]; +#ifdef CONFIG_RSN_PREAUTH + const int preauth = 1; +#else /* CONFIG_RSN_PREAUTH */ + const int preauth = 0; +#endif /* CONFIG_RSN_PREAUTH */ if (wpa_auth == NULL) return len; ret = os_snprintf(buf + len, buflen - len, "dot11RSNAOptionImplemented=TRUE\n" -#ifdef CONFIG_RSN_PREAUTH - "dot11RSNAPreauthenticationImplemented=TRUE\n" -#else /* CONFIG_RSN_PREAUTH */ - "dot11RSNAPreauthenticationImplemented=FALSE\n" -#endif /* CONFIG_RSN_PREAUTH */ + "dot11RSNAPreauthenticationImplemented=%s\n" "dot11RSNAEnabled=%s\n" "dot11RSNAPreauthenticationEnabled=%s\n", + wpa_bool_txt(preauth), wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN), wpa_bool_txt(wpa_auth->conf.rsn_preauth)); if (ret < 0 || (size_t) ret >= buflen - len) @@ -2397,7 +2748,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) !!wpa_auth->conf.wpa_strict_rekey, dot11RSNAConfigGroupUpdateCount, dot11RSNAConfigPairwiseUpdateCount, - wpa_cipher_bits(wpa_auth->conf.wpa_group), + wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8, dot11RSNAConfigPMKLifetime, dot11RSNAConfigPMKReauthThreshold, dot11RSNAConfigSATimeout, @@ -2440,29 +2791,10 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) /* dot11RSNAStatsEntry */ - if (sm->wpa == WPA_VERSION_WPA) { - if (sm->pairwise == WPA_CIPHER_CCMP) - pairwise = WPA_CIPHER_SUITE_CCMP; - else if (sm->pairwise == WPA_CIPHER_TKIP) - pairwise = WPA_CIPHER_SUITE_TKIP; - else if (sm->pairwise == WPA_CIPHER_WEP104) - pairwise = WPA_CIPHER_SUITE_WEP104; - else if (sm->pairwise == WPA_CIPHER_WEP40) - pairwise = WPA_CIPHER_SUITE_WEP40; - else if (sm->pairwise == WPA_CIPHER_NONE) - pairwise = WPA_CIPHER_SUITE_NONE; - } else if (sm->wpa == WPA_VERSION_WPA2) { - if (sm->pairwise == WPA_CIPHER_CCMP) - pairwise = RSN_CIPHER_SUITE_CCMP; - else if (sm->pairwise == WPA_CIPHER_TKIP) - pairwise = RSN_CIPHER_SUITE_TKIP; - else if (sm->pairwise == WPA_CIPHER_WEP104) - pairwise = RSN_CIPHER_SUITE_WEP104; - else if (sm->pairwise == WPA_CIPHER_WEP40) - pairwise = RSN_CIPHER_SUITE_WEP40; - else if (sm->pairwise == WPA_CIPHER_NONE) - pairwise = RSN_CIPHER_SUITE_NONE; - } else + pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ? + WPA_PROTO_RSN : WPA_PROTO_WPA, + sm->pairwise); + if (pairwise == 0) return 0; ret = os_snprintf( @@ -2473,7 +2805,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" /* TODO: dot11RSNAStatsTKIPICVErrors */ "dot11RSNAStatsTKIPLocalMICFailures=%u\n" - "dot11RSNAStatsTKIPRemoveMICFailures=%u\n" + "dot11RSNAStatsTKIPRemoteMICFailures=%u\n" /* TODO: dot11RSNAStatsCCMPReplays */ /* TODO: dot11RSNAStatsCCMPDecryptErrors */ /* TODO: dot11RSNAStatsTKIPReplays */, @@ -2570,7 +2902,8 @@ const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len) int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, int session_timeout, struct eapol_state_machine *eapol) { - if (sm == NULL || sm->wpa != WPA_VERSION_WPA2) + if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 || + sm->wpa_auth->conf.disable_pmksa_caching) return -1; if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, @@ -2609,7 +2942,7 @@ wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d", vlan_id); - group = wpa_group_init(wpa_auth, vlan_id); + group = wpa_group_init(wpa_auth, vlan_id, 0); if (group == NULL) return NULL; @@ -2649,3 +2982,41 @@ 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); + } +} + + +int wpa_auth_uses_sae(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return 0; + return wpa_key_mgmt_sae(sm->wpa_key_mgmt); +} |