Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / src / ap / wpa_auth.c
index 36cb0f4..2760a3f 100644 (file)
@@ -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-2015, 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"
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "utils/state_machine.h"
+#include "utils/bitfield.h"
 #include "common/ieee802_11_defs.h"
 #include "crypto/aes_wrap.h"
 #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"
 
 static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
 static int wpa_sm_step(struct wpa_state_machine *sm);
-static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len);
+static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
+                             size_t data_len);
 static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
 static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
                              struct wpa_group *group);
 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 int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+                         const u8 *pmk, struct wpa_ptk *ptk);
+static void wpa_group_free(struct wpa_authenticator *wpa_auth,
+                          struct wpa_group *group);
+static void wpa_group_get(struct wpa_authenticator *wpa_auth,
+                         struct wpa_group *group);
+static void wpa_group_put(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 +64,20 @@ 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;
+}
+
+
+static inline void wpa_auth_psk_failure_report(
+       struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+       if (wpa_auth->cb.psk_failure_report)
+               wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr);
 }
 
 
@@ -83,11 +100,14 @@ static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
 
 
 static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
-                                         const u8 *addr, const u8 *prev_psk)
+                                         const u8 *addr,
+                                         const u8 *p2p_dev_addr,
+                                         const u8 *prev_psk)
 {
        if (wpa_auth->cb.get_psk == NULL)
                return NULL;
-       return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, prev_psk);
+       return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr,
+                                   prev_psk);
 }
 
 
@@ -132,6 +152,17 @@ wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
 }
 
 
+#ifdef CONFIG_MESH
+static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth,
+                                     const u8 *addr)
+{
+       if (wpa_auth->cb.start_ampe == NULL)
+               return -1;
+       return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr);
+}
+#endif /* CONFIG_MESH */
+
+
 int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
                          int (*cb)(struct wpa_state_machine *sm, void *ctx),
                          void *cb_ctx)
@@ -191,6 +222,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);
 }
@@ -207,6 +239,8 @@ static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
        if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
                ret = 1;
 #endif /* CONFIG_IEEE80211W */
+       if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN)
+               ret = 1;
        return ret;
 }
 
@@ -215,11 +249,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) {
@@ -232,15 +268,22 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
 static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
 {
        struct wpa_authenticator *wpa_auth = eloop_ctx;
-       struct wpa_group *group;
+       struct wpa_group *group, *next;
 
        wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
-       for (group = wpa_auth->group; group; group = group->next) {
+       group = wpa_auth->group;
+       while (group) {
+               wpa_group_get(wpa_auth, group);
+
                group->GTKReKey = TRUE;
                do {
                        group->changed = FALSE;
                        wpa_group_sm_step(wpa_auth, group);
                } while (group->changed);
+
+               next = group->next;
+               wpa_group_put(wpa_auth, group);
+               group = next;
        }
 
        if (wpa_auth->conf.wpa_group_rekey) {
@@ -277,31 +320,42 @@ 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(unsigned long)];
+       u8 rkey[32];
+       unsigned long ptr;
+
+       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);
+       ptr = (unsigned long) group;
+       os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr));
+       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 +363,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 +425,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);
@@ -375,6 +436,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
                                                wpa_auth);
        if (wpa_auth->pmksa == NULL) {
                wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
+               os_free(wpa_auth->group);
                os_free(wpa_auth->wpa_ie);
                os_free(wpa_auth);
                return NULL;
@@ -384,6 +446,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
        wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
        if (wpa_auth->ft_pmk_cache == NULL) {
                wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
+               os_free(wpa_auth->group);
                os_free(wpa_auth->wpa_ie);
                pmksa_cache_auth_deinit(wpa_auth->pmksa);
                os_free(wpa_auth);
@@ -401,10 +464,36 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
                                       wpa_rekey_gtk, wpa_auth, NULL);
        }
 
+#ifdef CONFIG_P2P
+       if (WPA_GET_BE32(conf->ip_addr_start)) {
+               int count = WPA_GET_BE32(conf->ip_addr_end) -
+                       WPA_GET_BE32(conf->ip_addr_start) + 1;
+               if (count > 1000)
+                       count = 1000;
+               if (count > 0)
+                       wpa_auth->ip_pool = bitfield_alloc(count);
+       }
+#endif /* CONFIG_P2P */
+
        return wpa_auth;
 }
 
 
+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);
+       if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+               return -1;
+       return 0;
+}
+
+
 /**
  * wpa_deinit - Deinitialize WPA authenticator
  * @wpa_auth: Pointer to WPA authenticator data from wpa_init()
@@ -428,6 +517,11 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth)
        wpa_auth->ft_pmk_cache = NULL;
 #endif /* CONFIG_IEEE80211R */
 
+#ifdef CONFIG_P2P
+       bitfield_free(wpa_auth->ip_pool);
+#endif /* CONFIG_P2P */
+
+
        os_free(wpa_auth->wpa_ie);
 
        group = wpa_auth->group;
@@ -464,7 +558,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;
@@ -475,17 +569,24 @@ int wpa_reconfig(struct wpa_authenticator *wpa_auth,
 
 
 struct wpa_state_machine *
-wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr)
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
+                 const u8 *p2p_dev_addr)
 {
        struct wpa_state_machine *sm;
 
+       if (wpa_auth->group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+               return NULL;
+
        sm = os_zalloc(sizeof(struct wpa_state_machine));
        if (sm == NULL)
                return NULL;
        os_memcpy(sm->addr, addr, ETH_ALEN);
+       if (p2p_dev_addr)
+               os_memcpy(sm->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
 
        sm->wpa_auth = wpa_auth;
        sm->group = wpa_auth->group;
+       wpa_group_get(sm->wpa_auth, sm->group);
 
        return sm;
 }
@@ -502,6 +603,8 @@ int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
                wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
                                "FT authentication already completed - do not "
                                "start 4-way handshake");
+               /* Go to PTKINITDONE state to allow GTK rekeying */
+               sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
                return 0;
        }
 #endif /* CONFIG_IEEE80211R */
@@ -539,11 +642,30 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
 
 static void wpa_free_sta_sm(struct wpa_state_machine *sm)
 {
+#ifdef CONFIG_P2P
+       if (WPA_GET_BE32(sm->ip_addr)) {
+               u32 start;
+               wpa_printf(MSG_DEBUG, "P2P: Free assigned IP "
+                          "address %u.%u.%u.%u from " MACSTR,
+                          sm->ip_addr[0], sm->ip_addr[1],
+                          sm->ip_addr[2], sm->ip_addr[3],
+                          MAC2STR(sm->addr));
+               start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start);
+               bitfield_clear(sm->wpa_auth->ip_pool,
+                              WPA_GET_BE32(sm->ip_addr) - start);
+       }
+#endif /* CONFIG_P2P */
+       if (sm->GUpdateStationKeys) {
+               sm->group->GKeyDoneStations--;
+               sm->GUpdateStationKeys = FALSE;
+       }
 #ifdef CONFIG_IEEE80211R
        os_free(sm->assoc_resp_ftie);
+       wpabuf_free(sm->ft_pending_req_ies);
 #endif /* CONFIG_IEEE80211R */
        os_free(sm->last_rx_eapol_key);
        os_free(sm->wpa_ie);
+       wpa_group_put(sm->wpa_auth, sm->group);
        os_free(sm);
 }
 
@@ -563,6 +685,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 +709,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 +724,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,42 +788,141 @@ 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;
+}
+
+
+static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
+                             size_t data_len)
+{
+       struct wpa_ptk PTK;
+       int ok = 0;
+       const u8 *pmk = NULL;
+
+       for (;;) {
+               if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+                       pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+                                              sm->p2p_dev_addr, pmk);
+                       if (pmk == NULL)
+                               break;
+               } else
+                       pmk = sm->PMK;
+
+               wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK);
+
+               if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
+                   == 0) {
+                       ok = 1;
+                       break;
+               }
+
+               if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+                       break;
+       }
+
+       if (!ok) {
+               wpa_printf(MSG_DEBUG,
+                          "WPA: Earlier SNonce did not result in matching MIC");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,
+                  "WPA: Earlier SNonce resulted in matching MIC");
+       sm->alt_snonce_valid = 0;
+       os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
+       os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+       sm->PTK_valid = TRUE;
+
+       return 0;
+}
+
+
 void wpa_receive(struct wpa_authenticator *wpa_auth,
                 struct wpa_state_machine *sm,
                 u8 *data, size_t data_len)
 {
        struct ieee802_1x_hdr *hdr;
        struct wpa_eapol_key *key;
+       struct wpa_eapol_key_192 *key192;
        u16 key_info, key_data_length;
        enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
               SMK_M1, SMK_M3, SMK_ERROR } msg;
        char *msgtxt;
        struct wpa_eapol_ie_parse kde;
        int ft;
-       const u8 *eapol_key_ie;
-       size_t eapol_key_ie_len;
+       const u8 *eapol_key_ie, *key_data;
+       size_t eapol_key_ie_len, keyhdrlen, mic_len;
 
        if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
                return;
 
-       if (data_len < sizeof(*hdr) + sizeof(*key))
+       mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+       keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+       if (data_len < sizeof(*hdr) + keyhdrlen)
                return;
 
        hdr = (struct ieee802_1x_hdr *) data;
        key = (struct wpa_eapol_key *) (hdr + 1);
+       key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
        key_info = WPA_GET_BE16(key->key_info);
-       key_data_length = WPA_GET_BE16(key->key_data_length);
-       if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) {
+       if (mic_len == 24) {
+               key_data = (const u8 *) (key192 + 1);
+               key_data_length = WPA_GET_BE16(key192->key_data_length);
+       } else {
+               key_data = (const u8 *) (key + 1);
+               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) - keyhdrlen) {
                wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
                           "key_data overflow (%d > %lu)",
                           key_data_length,
                           (unsigned long) (data_len - sizeof(*hdr) -
-                                           sizeof(*key)));
+                                           keyhdrlen));
                return;
        }
 
        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 +937,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,8 +975,11 @@ 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) &&
+                           sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN &&
+                           !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
                            ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
                                wpa_auth_logger(wpa_auth, sm->addr,
                                                LOGGER_WARNING,
@@ -750,10 +994,17 @@ 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;
                        }
                }
+
+               if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+                   ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+                       wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
+                                       "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
+                       return;
+               }
        }
 
        if (key_info & WPA_KEY_INFO_REQUEST) {
@@ -768,11 +1019,61 @@ 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;
+                       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, sm->addr, 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, 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,18 +1086,38 @@ 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;
                }
-               if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length,
-                                     &kde) < 0) {
+               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(key_data, key_data_length, &kde) < 0) {
                        wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
                                         "received EAPOL-Key msg 2/4 with "
                                         "invalid Key Data contents");
@@ -805,6 +1126,9 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                if (kde.rsn_ie) {
                        eapol_key_ie = kde.rsn_ie;
                        eapol_key_ie_len = kde.rsn_ie_len;
+               } else if (kde.osen) {
+                       eapol_key_ie = kde.osen;
+                       eapol_key_ie_len = kde.osen_len;
                } else {
                        eapol_key_ie = kde.wpa_ie;
                        eapol_key_ie_len = kde.wpa_ie_len;
@@ -834,6 +1158,26 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                        return;
                }
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+               if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+                   wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+                       int idx;
+                       wpa_printf(MSG_DEBUG, "P2P: IP address requested in "
+                                  "EAPOL-Key exchange");
+                       idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+                       if (idx >= 0) {
+                               u32 start = WPA_GET_BE32(wpa_auth->conf.
+                                                        ip_addr_start);
+                               bitfield_set(wpa_auth->ip_pool, idx);
+                               WPA_PUT_BE32(sm->ip_addr, start + idx);
+                               wpa_printf(MSG_DEBUG, "P2P: Assigned IP "
+                                          "address %u.%u.%u.%u to " MACSTR,
+                                          sm->ip_addr[0], sm->ip_addr[1],
+                                          sm->ip_addr[2], sm->ip_addr[3],
+                                          MAC2STR(sm->addr));
+                       }
+               }
+#endif /* CONFIG_P2P */
                break;
        case PAIRWISE_4:
                if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
@@ -897,14 +1241,18 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
        }
 
        sm->MICVerified = FALSE;
-       if (sm->PTK_valid) {
-               if (wpa_verify_key_mic(&sm->PTK, data, data_len)) {
+       if (sm->PTK_valid && !sm->update_snonce) {
+               if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
+                                      data_len) &&
+                   (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
+                    wpa_try_alt_snonce(sm, data, data_len))) {
                        wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
                                        "received EAPOL-Key with invalid MIC");
                        return;
                }
                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) {
@@ -926,21 +1274,14 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                 */
                if (msg == SMK_ERROR) {
 #ifdef CONFIG_PEERKEY
-                       wpa_smk_error(wpa_auth, sm, key);
+                       wpa_smk_error(wpa_auth, sm, key_data, key_data_length);
 #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 "
@@ -948,34 +1289,50 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                        wpa_request_new_ptk(sm);
 #ifdef CONFIG_PEERKEY
                } else if (msg == SMK_M1) {
-                       wpa_smk_m1(wpa_auth, sm, key);
+                       wpa_smk_m1(wpa_auth, sm, key, key_data,
+                                  key_data_length);
 #endif /* CONFIG_PEERKEY */
                } else if (key_data_length > 0 &&
-                          wpa_parse_kde_ies((const u8 *) (key + 1),
-                                            key_data_length, &kde) == 0 &&
+                          wpa_parse_kde_ies(key_data, key_data_length,
+                                            &kde) == 0 &&
                           kde.mac_addr) {
                } else {
                        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
        if (msg == SMK_M3) {
-               wpa_smk_m3(wpa_auth, sm, key);
+               wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length);
                return;
        }
 #endif /* CONFIG_PEERKEY */
@@ -987,6 +1344,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 +1353,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 +1392,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);
@@ -1036,25 +1407,33 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
 {
        struct ieee802_1x_hdr *hdr;
        struct wpa_eapol_key *key;
-       size_t len;
+       struct wpa_eapol_key_192 *key192;
+       size_t len, mic_len, keyhdrlen;
        int alg;
        int key_data_len, pad_len = 0;
        u8 *buf, *pos;
        int version, pairwise;
        int i;
+       u8 *key_data;
 
-       len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key);
+       mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+       keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+       len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
 
        if (force_version)
                version = force_version;
+       else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+                wpa_key_mgmt_suite_b(sm->wpa_key_mgmt))
+               version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
        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;
 
-       pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
+       pairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
 
        wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d "
                   "ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d "
@@ -1069,6 +1448,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
        key_data_len = kde_len;
 
        if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+            sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+            wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
             version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
                pad_len = key_data_len % 8;
                if (pad_len)
@@ -1085,6 +1466,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
        hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
        hdr->length = host_to_be16(len  - sizeof(*hdr));
        key = (struct wpa_eapol_key *) (hdr + 1);
+       key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+       key_data = ((u8 *) (hdr + 1)) + keyhdrlen;
 
        key->type = sm->wpa == WPA_VERSION_WPA2 ?
                EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
@@ -1096,20 +1479,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);
 
@@ -1123,6 +1493,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
        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);
+       wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter",
+                   key->replay_counter, WPA_REPLAY_COUNTER_LEN);
        sm->key_replay[0].valid = TRUE;
 
        if (nonce)
@@ -1132,8 +1504,11 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
                os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN);
 
        if (kde && !encr) {
-               os_memcpy(key + 1, kde, kde_len);
-               WPA_PUT_BE16(key->key_data_length, kde_len);
+               os_memcpy(key_data, kde, kde_len);
+               if (mic_len == 24)
+                       WPA_PUT_BE16(key192->key_data_length, kde_len);
+               else
+                       WPA_PUT_BE16(key->key_data_length, kde_len);
        } else if (encr && kde) {
                buf = os_zalloc(key_data_len);
                if (buf == NULL) {
@@ -1150,29 +1525,49 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
                wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
                                buf, key_data_len);
                if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+                   sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+                   wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
                    version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
-                       if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf,
-                                    (u8 *) (key + 1))) {
+                       if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
+                                    (key_data_len - 8) / 8, buf, key_data)) {
                                os_free(hdr);
                                os_free(buf);
                                return;
                        }
-                       WPA_PUT_BE16(key->key_data_length, key_data_len);
-               } else {
+                       if (mic_len == 24)
+                               WPA_PUT_BE16(key192->key_data_length,
+                                            key_data_len);
+                       else
+                               WPA_PUT_BE16(key->key_data_length,
+                                            key_data_len);
+#ifndef CONFIG_NO_RC4
+               } else if (sm->PTK.kek_len == 16) {
                        u8 ek[32];
                        os_memcpy(key->key_iv,
                                  sm->group->Counter + WPA_NONCE_LEN - 16, 16);
                        inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
                        os_memcpy(ek, key->key_iv, 16);
-                       os_memcpy(ek + 16, sm->PTK.kek, 16);
-                       os_memcpy(key + 1, buf, key_data_len);
-                       rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len);
-                       WPA_PUT_BE16(key->key_data_length, key_data_len);
+                       os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len);
+                       os_memcpy(key_data, buf, key_data_len);
+                       rc4_skip(ek, 32, 256, key_data, key_data_len);
+                       if (mic_len == 24)
+                               WPA_PUT_BE16(key192->key_data_length,
+                                            key_data_len);
+                       else
+                               WPA_PUT_BE16(key->key_data_length,
+                                            key_data_len);
+#endif /* CONFIG_NO_RC4 */
+               } else {
+                       os_free(hdr);
+                       os_free(buf);
+                       return;
                }
                os_free(buf);
        }
 
        if (key_info & WPA_KEY_INFO_MIC) {
+               u8 *key_mic;
+
                if (!sm->PTK_valid) {
                        wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
                                        "PTK not valid when sending EAPOL-Key "
@@ -1180,8 +1575,21 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
                        os_free(hdr);
                        return;
                }
-               wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len,
-                                 key->key_mic);
+
+               key_mic = key192->key_mic; /* same offset for key and key192 */
+               wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
+                                 sm->wpa_key_mgmt, version,
+                                 (u8 *) hdr, len, key_mic);
+#ifdef CONFIG_TESTING_OPTIONS
+               if (!pairwise &&
+                   wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 &&
+                   drand48() <
+                   wpa_auth->conf.corrupt_gtk_rekey_mic_probability) {
+                       wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+                                       "Corrupting group EAPOL-Key Key MIC");
+                       key_mic[0]++;
+               }
+#endif /* CONFIG_TESTING_OPTIONS */
        }
 
        wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx,
@@ -1209,36 +1617,46 @@ 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);
 }
 
 
-static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len)
+static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
+                             size_t data_len)
 {
        struct ieee802_1x_hdr *hdr;
        struct wpa_eapol_key *key;
+       struct wpa_eapol_key_192 *key192;
        u16 key_info;
        int ret = 0;
-       u8 mic[16];
+       u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+       size_t mic_len = wpa_mic_len(akmp);
 
        if (data_len < sizeof(*hdr) + sizeof(*key))
                return -1;
 
        hdr = (struct ieee802_1x_hdr *) data;
        key = (struct wpa_eapol_key *) (hdr + 1);
+       key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
        key_info = WPA_GET_BE16(key->key_info);
-       os_memcpy(mic, key->key_mic, 16);
-       os_memset(key->key_mic, 0, 16);
-       if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK,
-                             data, data_len, key->key_mic) ||
-           os_memcmp(mic, key->key_mic, 16) != 0)
+       os_memcpy(mic, key192->key_mic, mic_len);
+       os_memset(key192->key_mic, 0, mic_len);
+       if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
+                             key_info & WPA_KEY_INFO_TYPE_MASK,
+                             data, data_len, key192->key_mic) ||
+           os_memcmp_const(mic, key192->key_mic, mic_len) != 0)
                ret = -1;
-       os_memcpy(key->key_mic, mic, 16);
+       os_memcpy(key192->key_mic, mic, mic_len);
        return ret;
 }
 
@@ -1247,14 +1665,13 @@ 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);
 }
 
 
-int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
+int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
 {
        int remove_ptk = 1;
 
@@ -1266,6 +1683,14 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
 
        switch (event) {
        case WPA_AUTH:
+#ifdef CONFIG_MESH
+               /* PTKs are derived through AMPE */
+               if (wpa_auth_start_ampe(sm->wpa_auth, sm->addr)) {
+                       /* not mesh */
+                       break;
+               }
+               return 0;
+#endif /* CONFIG_MESH */
        case WPA_ASSOC:
                break;
        case WPA_DEAUTH:
@@ -1334,23 +1759,15 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
                        wpa_remove_ptk(sm);
        }
 
-       return wpa_sm_step(sm);
-}
-
-
-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;
+       if (sm->in_step_loop) {
+               /*
+                * wpa_sm_step() is already running - avoid recursive call to
+                * it by making the existing loop process the new update.
+                */
+               sm->changed = TRUE;
+               return 0;
        }
+       return wpa_sm_step(sm);
 }
 
 
@@ -1411,12 +1828,63 @@ 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;
+       }
+
+       if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 ||
+           wpa_gtk_update(wpa_auth, group) < 0 ||
+           wpa_group_config_group_keys(wpa_auth, group) < 0) {
+               wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed");
+               group->first_sta_seen = FALSE;
+               group->reject_4way_hs_for_entropy = TRUE;
+       }
+}
+
+
 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);
        sm->ReAuthenticationRequest = FALSE;
+
+       /*
+        * 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.");
+               sm->Disconnect = TRUE;
+               return;
+       }
+       wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
+                   WPA_NONCE_LEN);
        /* IEEE 802.11i does not clear TimeoutCtr here, but this is more
         * logical place than INITIALIZE since AUTHENTICATION2 can be
         * re-entered on ReAuthenticationRequest without going through
@@ -1448,8 +1916,12 @@ SM_STATE(WPA_PTK, INITPMK)
                }
 #endif /* CONFIG_IEEE80211R */
        } else {
-               wpa_printf(MSG_DEBUG, "WPA: Could not get PMK");
+               wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p",
+                          sm->wpa_auth->cb.get_msk);
+               sm->Disconnect = TRUE;
+               return;
        }
+       os_memset(msk, 0, sizeof(msk));
 
        sm->req_replay_counter_used = 0;
        /* IEEE 802.11i does not set keyRun to FALSE, but not doing this
@@ -1468,7 +1940,7 @@ SM_STATE(WPA_PTK, INITPSK)
 {
        const u8 *psk;
        SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
-       psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL);
+       psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL);
        if (psk) {
                os_memcpy(sm->PMK, psk, PMK_LEN);
 #ifdef CONFIG_IEEE80211R
@@ -1488,6 +1960,7 @@ SM_STATE(WPA_PTK, PTKSTART)
        SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
        sm->PTKRequest = FALSE;
        sm->TimeoutEvt = FALSE;
+       sm->alt_snonce_valid = FALSE;
 
        sm->TimeoutCtr++;
        if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
@@ -1503,16 +1976,20 @@ SM_STATE(WPA_PTK, PTKSTART)
         * one possible PSK for this STA.
         */
        if (sm->wpa == WPA_VERSION_WPA2 &&
-           wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) {
+           wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+           sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
                pmkid = buf;
                pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
                pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
                pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
                RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
-               if (sm->pmksa)
+               if (sm->pmksa) {
                        os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
                                  sm->pmksa->pmkid, PMKID_LEN);
-               else {
+               } else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) {
+                       /* No KCK available to derive PMKID */
+                       pmkid = NULL;
+               } else {
                        /*
                         * Calculate PMKID since no PMKSA cache entry was
                         * available with pre-calculated PMKID.
@@ -1528,47 +2005,47 @@ SM_STATE(WPA_PTK, PTKSTART)
 }
 
 
-static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk,
-                         struct wpa_ptk *ptk)
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+                         const u8 *pmk, struct wpa_ptk *ptk)
 {
-       size_t ptk_len = sm->pairwise == WPA_CIPHER_CCMP ? 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);
+               return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
 #endif /* CONFIG_IEEE80211R */
 
-       wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
-                      sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce,
-                      (u8 *) ptk, ptk_len,
-                      wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
-
-       return 0;
+       return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
+                             sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
+                             ptk, sm->wpa_key_mgmt, sm->pairwise);
 }
 
 
 SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
 {
        struct wpa_ptk PTK;
-       int ok = 0;
+       int ok = 0, psk_found = 0;
        const u8 *pmk = NULL;
 
        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
         * the packet */
        for (;;) {
                if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
-                       pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk);
+                       pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+                                              sm->p2p_dev_addr, pmk);
                        if (pmk == NULL)
                                break;
+                       psk_found = 1;
                } else
                        pmk = sm->PMK;
 
-               wpa_derive_ptk(sm, pmk, &PTK);
+               wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK);
 
-               if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key,
+               if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
+                                      sm->last_rx_eapol_key,
                                       sm->last_rx_eapol_key_len) == 0) {
                        ok = 1;
                        break;
@@ -1581,6 +2058,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
        if (!ok) {
                wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
                                "invalid MIC in msg 2/4 of 4-Way Handshake");
+               if (psk_found)
+                       wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr);
                return;
        }
 
@@ -1590,8 +2069,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
                 * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
                 * with the value we derived.
                 */
-               if (os_memcmp(sm->sup_pmk_r1_name, sm->pmk_r1_name,
-                             WPA_PMK_NAME_LEN) != 0) {
+               if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name,
+                                   WPA_PMK_NAME_LEN) != 0) {
                        wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
                                        "PMKR1Name mismatch in FT 4-way "
                                        "handshake");
@@ -1605,6 +2084,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)) {
@@ -1633,7 +2113,9 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2)
 static int ieee80211w_kde_len(struct wpa_state_machine *sm)
 {
        if (sm->mgmt_frame_prot) {
-               return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde);
+               size_t len;
+               len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+               return 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN + len;
        }
 
        return 0;
@@ -1644,6 +2126,8 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
 {
        struct wpa_igtk_kde igtk;
        struct wpa_group *gsm = sm->group;
+       u8 rsc[WPA_KEY_RSC_LEN];
+       size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
 
        if (!sm->mgmt_frame_prot)
                return pos;
@@ -1651,11 +2135,22 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
        igtk.keyid[0] = gsm->GN_igtk;
        igtk.keyid[1] = 0;
        if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
-           wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0)
+           wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, rsc) < 0)
                os_memset(igtk.pn, 0, sizeof(igtk.pn));
-       os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+       else
+               os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
+       os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], 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, len) < 0)
+                       return pos;
+       }
        pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
-                         (const u8 *) &igtk, sizeof(igtk), NULL, 0);
+                         (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
+                         NULL, 0);
 
        return pos;
 }
@@ -1678,7 +2173,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;
@@ -1705,8 +2200,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
        if (sm->wpa == WPA_VERSION_WPA &&
            (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
            wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
-               /* WPA-only STA, remove RSN IE */
+               /* WPA-only STA, remove RSN IE and possible MDIE */
                wpa_ie = wpa_ie + wpa_ie[1] + 2;
+               if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
+                       wpa_ie = wpa_ie + wpa_ie[1] + 2;
                wpa_ie_len = wpa_ie[1] + 2;
        }
        wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
@@ -1716,6 +2213,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 +2232,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);
@@ -1737,6 +2257,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
                kde_len += 300; /* FTIE + 2 * TIE */
        }
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+       if (WPA_GET_BE32(sm->ip_addr) > 0)
+               kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
+#endif /* CONFIG_P2P */
        kde = os_malloc(kde_len);
        if (kde == NULL)
                return;
@@ -1798,6 +2322,16 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
                pos += 4;
        }
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+       if (WPA_GET_BE32(sm->ip_addr) > 0) {
+               u8 addr[3 * 4];
+               os_memcpy(addr, sm->ip_addr, 4);
+               os_memcpy(addr + 4, sm->wpa_auth->conf.ip_addr_mask, 4);
+               os_memcpy(addr + 8, sm->wpa_auth->conf.ip_addr_go, 4);
+               pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC,
+                                 addr, sizeof(addr), NULL, 0);
+       }
+#endif /* CONFIG_P2P */
 
        wpa_send_eapol(sm->wpa_auth, sm,
                       (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
@@ -1813,17 +2347,10 @@ 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)) {
+                                    sm->PTK.tk, klen)) {
                        wpa_sta_disconnect(sm->wpa_auth, sm->addr);
                        return;
                }
@@ -1876,8 +2403,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,11 +2443,14 @@ 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;
        case WPA_PTK_INITPSK:
-               if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL))
+               if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr,
+                                    NULL))
                        SM_ENTER(WPA_PTK, PTKSTART);
                else {
                        wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
@@ -1933,6 +2466,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 +2486,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);
@@ -1982,8 +2524,10 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
 {
        u8 rsc[WPA_KEY_RSC_LEN];
        struct wpa_group *gsm = sm->group;
-       u8 *kde, *pos, hdr[2];
+       const u8 *kde;
+       u8 *kde_buf = NULL, *pos, hdr[2];
        size_t kde_len;
+       u8 *gtk, dummy_gtk[32];
 
        SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
 
@@ -2004,31 +2548,42 @@ 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);
-               kde = os_malloc(kde_len);
-               if (kde == NULL)
+               kde_buf = os_malloc(kde_len);
+               if (kde_buf == NULL)
                        return;
 
-               pos = kde;
+               kde = pos = kde_buf;
                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);
+               kde_len = pos - kde;
        } else {
-               kde = gsm->GTK[gsm->GN - 1];
-               pos = kde + gsm->GTK_len;
+               kde = gtk;
+               kde_len = gsm->GTK_len;
        }
 
        wpa_send_eapol(sm->wpa_auth, sm,
                       WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
                       WPA_KEY_INFO_ACK |
                       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
-                      rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1);
-       if (sm->wpa == WPA_VERSION_WPA2)
-               os_free(kde);
+                      rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1);
+
+       os_free(kde_buf);
 }
 
 
@@ -2094,22 +2649,27 @@ 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");
+               size_t len;
+               len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+               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], len) < 0)
                        ret = -1;
-               }
                wpa_hexdump_key(MSG_DEBUG, "IGTK",
-                               group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN);
+                               group->IGTK[group->GN_igtk - 4], len);
        }
 #endif /* CONFIG_IEEE80211W */
 
@@ -2140,28 +2700,120 @@ 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 == NULL || sm->is_wnmsleep)
+               return;
+
+       wpa_group_update_sta(sm, NULL);
+}
+
+
+void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag)
+{
+       if (sm)
+               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;
+       size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+
+       /*
+        * IGTK subelement:
+        * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
+        */
+       *pos++ = WNM_SLEEP_SUBELEM_IGTK;
+       *pos++ = 2 + 6 + 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], len);
+       pos += 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], 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 +2837,86 @@ 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) {
+               enum wpa_alg alg;
+               size_t len;
+
+               alg = wpa_cipher_to_alg(wpa_auth->conf.group_mgmt_cipher);
+               len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+
+               if (ret == 0 &&
+                   wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+                                    broadcast_ether_addr, group->GN_igtk,
+                                    group->IGTK[group->GN_igtk - 4], len) < 0)
+                       ret = -1;
+       }
+#endif /* CONFIG_IEEE80211W */
+
+       return ret;
+}
+
+
+static int wpa_group_disconnect_cb(struct wpa_state_machine *sm, void *ctx)
+{
+       if (sm->group == ctx) {
+               wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR
+                          " for discconnection due to fatal failure",
+                          MAC2STR(sm->addr));
+               sm->Disconnect = TRUE;
+       }
+
+       return 0;
+}
+
+
+static void wpa_group_fatal_failure(struct wpa_authenticator *wpa_auth,
+                                   struct wpa_group *group)
+{
+       wpa_printf(MSG_DEBUG, "WPA: group state machine entering state FATAL_FAILURE");
+       group->changed = TRUE;
+       group->wpa_group_state = WPA_GROUP_FATAL_FAILURE;
+       wpa_auth_for_each_sta(wpa_auth, wpa_group_disconnect_cb, group);
+}
+
+
+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);
+       if (wpa_group_config_group_keys(wpa_auth, group) < 0) {
+               wpa_group_fatal_failure(wpa_auth, group);
+               return -1;
        }
-#endif /* CONFIG_IEEE80211W */
+
+       return 0;
 }
 
 
@@ -2219,6 +2925,8 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
 {
        if (group->GInit) {
                wpa_group_gtk_init(wpa_auth, group);
+       } else if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) {
+               /* Do not allow group operations */
        } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT &&
                   group->GTKAuthenticator) {
                wpa_group_setkeysdone(wpa_auth, group);
@@ -2310,30 +3018,14 @@ 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);
        }
 }
 
 
-static const char * wpa_bool_txt(int bool)
+static const char * wpa_bool_txt(int val)
 {
-       return bool ? "TRUE" : "FALSE";
-}
-
-
-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;
-       }
+       return val ? "TRUE" : "FALSE";
 }
 
 
@@ -2345,22 +3037,24 @@ 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)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -2397,7 +3091,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,
@@ -2410,7 +3104,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
                RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested),
                wpa_auth->dot11RSNATKIPCounterMeasuresInvoked,
                wpa_auth->dot11RSNA4WayHandshakeFailures);
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -2420,7 +3114,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
        /* Private MIB */
        ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n",
                          wpa_auth->group->wpa_group_state);
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -2440,29 +3134,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 +3148,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 */,
@@ -2481,7 +3156,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
                RSN_SUITE_ARG(pairwise),
                sm->dot11RSNAStatsTKIPLocalMICFailures,
                sm->dot11RSNAStatsTKIPRemoteMICFailures);
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -2491,7 +3166,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
                          "hostapdWPAPTKGroupState=%d\n",
                          sm->wpa_ptk_state,
                          sm->wpa_ptk_group_state);
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -2570,10 +3245,12 @@ 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,
+                                sm->PTK.kck, sm->PTK.kck_len,
                                 sm->wpa_auth->addr, sm->addr, session_timeout,
                                 eapol, sm->wpa_key_mgmt))
                return 0;
@@ -2590,7 +3267,9 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
        if (wpa_auth == NULL)
                return -1;
 
-       if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr,
+       if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len,
+                                NULL, 0,
+                                wpa_auth->addr,
                                 sta_addr, session_timeout, eapol,
                                 WPA_KEY_MGMT_IEEE8021X))
                return 0;
@@ -2599,6 +3278,95 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
 }
 
 
+int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+                          const u8 *pmk)
+{
+       if (wpa_auth->conf.disable_pmksa_caching)
+               return -1;
+
+       if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN,
+                                NULL, 0,
+                                wpa_auth->addr, addr, 0, NULL,
+                                WPA_KEY_MGMT_SAE))
+               return 0;
+
+       return -1;
+}
+
+
+void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
+                          const u8 *sta_addr)
+{
+       struct rsn_pmksa_cache_entry *pmksa;
+
+       if (wpa_auth == NULL || wpa_auth->pmksa == NULL)
+               return;
+       pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
+       if (pmksa) {
+               wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for "
+                          MACSTR " based on request", MAC2STR(sta_addr));
+               pmksa_cache_free_entry(wpa_auth->pmksa, pmksa);
+       }
+}
+
+
+/*
+ * Remove and free the group from wpa_authenticator. This is triggered by a
+ * callback to make sure nobody is currently iterating the group list while it
+ * gets modified.
+ */
+static void wpa_group_free(struct wpa_authenticator *wpa_auth,
+                          struct wpa_group *group)
+{
+       struct wpa_group *prev = wpa_auth->group;
+
+       wpa_printf(MSG_DEBUG, "WPA: Remove group state machine for VLAN-ID %d",
+                  group->vlan_id);
+
+       while (prev) {
+               if (prev->next == group) {
+                       /* This never frees the special first group as needed */
+                       prev->next = group->next;
+                       os_free(group);
+                       break;
+               }
+               prev = prev->next;
+       }
+
+}
+
+
+/* Increase the reference counter for group */
+static void wpa_group_get(struct wpa_authenticator *wpa_auth,
+                         struct wpa_group *group)
+{
+       /* Skip the special first group */
+       if (wpa_auth->group == group)
+               return;
+
+       group->references++;
+}
+
+
+/* Decrease the reference counter and maybe free the group */
+static void wpa_group_put(struct wpa_authenticator *wpa_auth,
+                         struct wpa_group *group)
+{
+       /* Skip the special first group */
+       if (wpa_auth->group == group)
+               return;
+
+       group->references--;
+       if (group->references)
+               return;
+       wpa_group_free(wpa_auth, group);
+}
+
+
+/*
+ * Add a group that has its references counter set to zero. Caller needs to
+ * call wpa_group_get() on the return value to mark the entry in use.
+ */
 static struct wpa_group *
 wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id)
 {
@@ -2609,7 +3377,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;
 
@@ -2643,9 +3411,90 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
        if (sm->group == group)
                return 0;
 
+       if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+               return -1;
+
        wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state "
                   "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id);
 
+       wpa_group_get(sm->wpa_auth, group);
+       wpa_group_put(sm->wpa_auth, sm->group);
        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);
+}
+
+
+int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm)
+{
+       if (sm == NULL)
+               return 0;
+       return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE;
+}
+
+
+#ifdef CONFIG_P2P
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr)
+{
+       if (sm == NULL || WPA_GET_BE32(sm->ip_addr) == 0)
+               return -1;
+       os_memcpy(addr, sm->ip_addr, 4);
+       return 0;
+}
+#endif /* CONFIG_P2P */
+
+
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+                                        struct radius_das_attrs *attr)
+{
+       return pmksa_cache_auth_radius_das_disconnect(wpa_auth->pmksa, attr);
+}
+
+
+void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth)
+{
+       struct wpa_group *group;
+
+       if (!wpa_auth)
+               return;
+       for (group = wpa_auth->group; group; group = group->next)
+               wpa_group_config_group_keys(wpa_auth, group);
+}