OSDN Git Service

Accumulative patch from commit dc013f1e37df3462085cf01a13f0c432f146ad7a
[android-x86/external-wpa_supplicant_8.git] / src / ap / wpa_auth.c
index adc69e2..fa4b1cb 100644 (file)
@@ -2,14 +2,8 @@
  * 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"
@@ -60,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;
 }
 
 
@@ -284,30 +279,12 @@ 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)
-{
-       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;
-       }
-}
-
-
 static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
                                          struct wpa_group *group)
 {
-       u8 buf[ETH_ALEN + 8 + sizeof(group)];
+       u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)];
        u8 rkey[32];
+       unsigned long ptr;
 
        if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0)
                return -1;
@@ -319,7 +296,8 @@ static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
         */
        os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
        wpa_get_ntp_timestamp(buf + ETH_ALEN);
-       os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group));
+       ptr = (unsigned long) group;
+       os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr));
        if (random_get_bytes(rkey, sizeof(rkey)) < 0)
                return -1;
 
@@ -344,8 +322,7 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
 
        group->GTKAuthenticator = TRUE;
        group->vlan_id = vlan_id;
-
-       wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
+       group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
 
        if (random_pool_ready() != 1) {
                wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
@@ -520,7 +497,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;
@@ -647,14 +624,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;
        }
@@ -662,6 +639,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,
@@ -712,8 +703,8 @@ static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
 #endif /* CONFIG_IEEE80211R */
 
 
-static void wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
-                                    struct wpa_state_machine *sm, int group)
+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,
@@ -730,7 +721,8 @@ static void wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
                                "ignore Michael MIC failure report since "
                                "pairwise cipher is not TKIP");
        } else {
-               wpa_auth_mic_failure_report(wpa_auth, sm->addr);
+               if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0)
+                       return 1; /* STA entry was removed */
                sm->dot11RSNAStatsTKIPRemoteMICFailures++;
                wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
        }
@@ -740,6 +732,7 @@ static void wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
         * Authenticator may do it, let's change the keys now anyway.
         */
        wpa_request_new_ptk(sm);
+       return 0;
 }
 
 
@@ -781,7 +774,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);
@@ -834,7 +834,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,
@@ -850,7 +851,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;
                        }
                }
@@ -868,11 +869,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_DEBUG,
-                                "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;
@@ -885,10 +919,13 @@ 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",
@@ -909,9 +946,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                        wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to "
                                   "collect more entropy for random number "
                                   "generation");
-                       sm->group->reject_4way_hs_for_entropy = FALSE;
                        random_mark_pool_ready();
-                       sm->group->first_sta_seen = FALSE;
                        wpa_sta_disconnect(wpa_auth, sm->addr);
                        return;
                }
@@ -1017,7 +1052,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");
@@ -1051,9 +1086,10 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
 #endif /* CONFIG_PEERKEY */
                        return;
                } else if (key_info & WPA_KEY_INFO_ERROR) {
-                       wpa_receive_error_report(
-                               wpa_auth, sm,
-                               !(key_info & WPA_KEY_INFO_KEY_TYPE));
+                       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 "
@@ -1075,12 +1111,30 @@ 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. 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
@@ -1173,7 +1227,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;
@@ -1220,20 +1274,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);
 
@@ -1466,22 +1507,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);
@@ -1539,9 +1564,11 @@ SM_STATE(WPA_PTK, AUTHENTICATION)
 }
 
 
-static void wpa_group_first_station(struct wpa_authenticator *wpa_auth,
-                                   struct wpa_group *group)
+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
@@ -1555,7 +1582,11 @@ static void wpa_group_first_station(struct wpa_authenticator *wpa_auth,
                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);
@@ -1566,15 +1597,25 @@ SM_STATE(WPA_PTK, AUTHENTICATION2)
 {
        SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk);
 
-       if (!sm->group->first_sta_seen) {
-               wpa_group_first_station(sm->wpa_auth, sm->group);
-               sm->group->first_sta_seen = TRUE;
-       }
+       wpa_group_ensure_init(sm->wpa_auth, sm->group);
 
-       os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN);
+       /*
+        * 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);
-       inc_byte_array(sm->group->Counter, 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
@@ -1690,7 +1731,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);
@@ -1713,6 +1754,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
@@ -1814,6 +1856,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);
 
@@ -1838,7 +1888,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;
@@ -1876,6 +1926,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;
@@ -1987,15 +2046,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);
@@ -2132,8 +2184,10 @@ 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) {
@@ -2170,6 +2224,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);
 
@@ -2190,6 +2245,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);
@@ -2201,10 +2266,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;
        }
 
@@ -2330,6 +2395,9 @@ 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");
@@ -2347,6 +2415,10 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
                                "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;
 
@@ -2355,6 +2427,86 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
 }
 
 
+#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)
 {
@@ -2384,7 +2536,7 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
                           group->GKeyDoneStations);
                group->GKeyDoneStations = 0;
        }
-       wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL);
+       wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group);
        wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
                   group->GKeyDoneStations);
 }
@@ -2396,7 +2548,7 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
        int ret = 0;
 
        if (wpa_auth_set_key(wpa_auth, group->vlan_id,
-                            wpa_alg_enum(wpa_auth->conf.wpa_group),
+                            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;
@@ -2536,23 +2688,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
@@ -2615,7 +2750,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,
@@ -2658,29 +2793,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(
@@ -2898,3 +3014,11 @@ void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
                                       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);
+}