PMKSA: Show AP/mesh PMKSA list in PMKSA command
[mech_eap.git] / src / ap / wpa_auth.c
index 3203d4f..57839fe 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11 RSN / WPA Authenticator
- * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,7 @@
 #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"
@@ -32,7 +33,8 @@
 
 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);
@@ -41,6 +43,15 @@ 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, unsigned int pmk_len,
+                         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;
@@ -54,11 +65,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);
 }
 
 
@@ -81,11 +101,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);
 }
 
 
@@ -130,6 +153,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)
@@ -206,6 +240,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;
 }
 
@@ -233,15 +269,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) {
@@ -278,33 +321,12 @@ static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
 }
 
 
-static void wpa_group_set_key_len(struct wpa_group *group, int cipher)
-{
-       switch (cipher) {
-       case WPA_CIPHER_CCMP:
-               group->GTK_len = 16;
-               break;
-       case WPA_CIPHER_GCMP:
-               group->GTK_len = 16;
-               break;
-       case WPA_CIPHER_TKIP:
-               group->GTK_len = 32;
-               break;
-       case WPA_CIPHER_WEP104:
-               group->GTK_len = 13;
-               break;
-       case WPA_CIPHER_WEP40:
-               group->GTK_len = 5;
-               break;
-       }
-}
-
-
 static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
                                          struct wpa_group *group)
 {
-       u8 buf[ETH_ALEN + 8 + sizeof(group)];
+       u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)];
        u8 rkey[32];
+       unsigned long ptr;
 
        if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0)
                return -1;
@@ -316,7 +338,8 @@ static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
         */
        os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
        wpa_get_ntp_timestamp(buf + ETH_ALEN);
-       os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group));
+       ptr = (unsigned long) group;
+       os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr));
        if (random_get_bytes(rkey, sizeof(rkey)) < 0)
                return -1;
 
@@ -341,8 +364,7 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
 
        group->GTKAuthenticator = TRUE;
        group->vlan_id = vlan_id;
-
-       wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
+       group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
 
        if (random_pool_ready() != 1) {
                wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
@@ -415,6 +437,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;
@@ -424,6 +447,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);
@@ -441,6 +465,17 @@ 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;
 }
 
@@ -454,6 +489,8 @@ int wpa_init_keys(struct wpa_authenticator *wpa_auth)
        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;
 }
 
@@ -481,6 +518,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;
@@ -517,7 +559,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;
@@ -528,17 +570,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;
 }
@@ -555,6 +604,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 */
@@ -592,15 +643,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);
 }
 
@@ -723,8 +789,8 @@ static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
 #endif /* CONFIG_IEEE80211R */
 
 
-static void wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
-                                    struct wpa_state_machine *sm, int group)
+static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
+                                   struct wpa_state_machine *sm, int group)
 {
        /* Supplicant reported a Michael MIC error */
        wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
@@ -741,7 +807,8 @@ static void wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
                                "ignore Michael MIC failure report since "
                                "pairwise cipher is not TKIP");
        } else {
-               wpa_auth_mic_failure_report(wpa_auth, sm->addr);
+               if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0)
+                       return 1; /* STA entry was removed */
                sm->dot11RSNAStatsTKIPRemoteMICFailures++;
                wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
        }
@@ -751,6 +818,56 @@ static void wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
         * Authenticator may do it, let's change the keys now anyway.
         */
        wpa_request_new_ptk(sm);
+       return 0;
+}
+
+
+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;
+       unsigned int pmk_len;
+
+       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;
+                       pmk_len = PMK_LEN;
+               } else {
+                       pmk = sm->PMK;
+                       pmk_len = sm->pmk_len;
+               }
+
+               wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &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;
 }
 
 
@@ -760,34 +877,45 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
 {
        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 (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) - sizeof(*key)) {
+       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;
        }
 
@@ -855,6 +983,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                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,
@@ -873,6 +1003,13 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                                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) {
@@ -908,8 +1045,25 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                                         "based on retransmitted EAPOL-Key "
                                         "1/4");
                        sm->update_snonce = 1;
-                       wpa_replay_counter_mark_invalid(sm->prev_key_replay,
-                                                       key->replay_counter);
+                       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;
                }
 
@@ -968,8 +1122,7 @@ continue_processing:
                        wpa_sta_disconnect(wpa_auth, sm->addr);
                        return;
                }
-               if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length,
-                                     &kde) < 0) {
+               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");
@@ -978,6 +1131,9 @@ continue_processing:
                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;
@@ -1007,6 +1163,26 @@ continue_processing:
                        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 ||
@@ -1071,7 +1247,10 @@ continue_processing:
 
        sm->MICVerified = FALSE;
        if (sm->PTK_valid && !sm->update_snonce) {
-               if (wpa_verify_key_mic(&sm->PTK, data, data_len)) {
+               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;
@@ -1100,13 +1279,14 @@ continue_processing:
                 */
                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) {
-                       wpa_receive_error_report(
-                               wpa_auth, sm,
-                               !(key_info & WPA_KEY_INFO_KEY_TYPE));
+                       if (wpa_receive_error_report(
+                                   wpa_auth, sm,
+                                   !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
+                               return; /* STA entry was removed */
                } else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
                        wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
                                        "received EAPOL-Key Request for new "
@@ -1114,11 +1294,12 @@ continue_processing:
                        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,
@@ -1156,7 +1337,7 @@ continue_processing:
 
 #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 */
@@ -1231,17 +1412,25 @@ 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;
+
+       mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+       keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
 
-       len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_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_TKIP)
@@ -1249,7 +1438,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
        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 "
@@ -1264,6 +1453,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)
@@ -1280,6 +1471,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;
@@ -1291,23 +1484,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_GCMP:
-               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);
 
@@ -1321,6 +1498,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)
@@ -1330,8 +1509,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) {
@@ -1348,29 +1530,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 "
@@ -1378,8 +1580,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,
@@ -1421,27 +1636,32 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
 }
 
 
-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;
 }
 
@@ -1456,7 +1676,7 @@ void wpa_remove_ptk(struct wpa_state_machine *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;
 
@@ -1468,6 +1688,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:
@@ -1536,25 +1764,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_GCMP:
-               return WPA_ALG_GCMP;
-       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);
 }
 
 
@@ -1638,9 +1856,13 @@ static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth,
                group->reject_4way_hs_for_entropy = FALSE;
        }
 
-       wpa_group_init_gmk_and_counter(wpa_auth, group);
-       wpa_gtk_update(wpa_auth, group);
-       wpa_group_config_group_keys(wpa_auth, group);
+       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;
+       }
 }
 
 
@@ -1649,6 +1871,7 @@ SM_STATE(WPA_PTK, AUTHENTICATION2)
        SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk);
 
        wpa_group_ensure_init(sm->wpa_auth, sm->group);
+       sm->ReAuthenticationRequest = FALSE;
 
        /*
         * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat
@@ -1662,12 +1885,11 @@ SM_STATE(WPA_PTK, AUTHENTICATION2)
        if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
                wpa_printf(MSG_ERROR, "WPA: Failed to get random data for "
                           "ANonce.");
-               wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+               sm->Disconnect = TRUE;
                return;
        }
        wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
                    WPA_NONCE_LEN);
-       sm->ReAuthenticationRequest = FALSE;
        /* IEEE 802.11i does not clear TimeoutCtr here, but this is more
         * logical place than INITIALIZE since AUTHENTICATION2 can be
         * re-entered on ReAuthenticationRequest without going through
@@ -1687,11 +1909,27 @@ SM_STATE(WPA_PTK, INITPMK)
 #endif /* CONFIG_IEEE80211R */
        if (sm->pmksa) {
                wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
-               os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN);
+               os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
+               sm->pmk_len = sm->pmksa->pmk_len;
        } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
+               unsigned int pmk_len;
+
+               if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+                       pmk_len = PMK_LEN_SUITE_B_192;
+               else
+                       pmk_len = PMK_LEN;
                wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine "
-                          "(len=%lu)", (unsigned long) len);
-               os_memcpy(sm->PMK, msk, PMK_LEN);
+                          "(MSK len=%lu PMK len=%u)", (unsigned long) len,
+                          pmk_len);
+               if (len < pmk_len) {
+                       wpa_printf(MSG_DEBUG,
+                                  "WPA: MSK not long enough (%u) to create PMK (%u)",
+                                  (unsigned int) len, (unsigned int) pmk_len);
+                       sm->Disconnect = TRUE;
+                       return;
+               }
+               os_memcpy(sm->PMK, msk, pmk_len);
+               sm->pmk_len = pmk_len;
 #ifdef CONFIG_IEEE80211R
                if (len >= 2 * PMK_LEN) {
                        os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
@@ -1699,8 +1937,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
@@ -1719,9 +1961,10 @@ 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);
+               sm->pmk_len = PMK_LEN;
 #ifdef CONFIG_IEEE80211R
                os_memcpy(sm->xxkey, psk, PMK_LEN);
                sm->xxkey_len = PMK_LEN;
@@ -1739,6 +1982,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) {
@@ -1754,21 +1998,25 @@ 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.
                         */
-                       rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr,
+                       rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr,
                                  sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
                                  wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
                }
@@ -1779,29 +2027,27 @@ SM_STATE(WPA_PTK, PTKSTART)
 }
 
 
-static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk,
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+                         const u8 *pmk, unsigned int pmk_len,
                          struct wpa_ptk *ptk)
 {
-       size_t ptk_len = sm->pairwise != WPA_CIPHER_TKIP ? 48 : 64;
 #ifdef CONFIG_IEEE80211R
        if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
-               return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len);
+               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;
+       unsigned int pmk_len;
 
        SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
        sm->EAPOLKeyReceived = FALSE;
@@ -1812,15 +2058,21 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
         * 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;
-               } else
+                       psk_found = 1;
+                       pmk_len = PMK_LEN;
+               } else {
                        pmk = sm->PMK;
+                       pmk_len = sm->pmk_len;
+               }
 
-               wpa_derive_ptk(sm, pmk, &PTK);
+               wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &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;
@@ -1833,6 +2085,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;
        }
 
@@ -1842,8 +2096,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");
@@ -1865,6 +2119,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
                 * state machine data based on whatever PSK was selected here.
                 */
                os_memcpy(sm->PMK, pmk, PMK_LEN);
+               sm->pmk_len = PMK_LEN;
        }
 
        sm->MICVerified = TRUE;
@@ -1886,7 +2141,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;
@@ -1897,6 +2154,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;
@@ -1904,19 +2163,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, WPA_IGTK_LEN) < 0)
+               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;
 }
@@ -1966,8 +2228,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,
@@ -2021,6 +2285,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;
@@ -2030,14 +2298,19 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
        pos += wpa_ie_len;
 #ifdef CONFIG_IEEE80211R
        if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
-               int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name);
+               int res;
+               size_t elen;
+
+               elen = pos - kde;
+               res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
                if (res < 0) {
                        wpa_printf(MSG_ERROR, "FT: Failed to insert "
                                   "PMKR1Name into RSN IE in EAPOL-Key data");
                        os_free(kde);
                        return;
                }
-               pos += res;
+               pos -= wpa_ie_len;
+               pos += elen;
        }
 #endif /* CONFIG_IEEE80211R */
        if (gtk) {
@@ -2055,10 +2328,18 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
                struct wpa_auth_config *conf;
 
                conf = &sm->wpa_auth->conf;
-               res = wpa_write_ftie(conf, conf->r0_key_holder,
-                                    conf->r0_key_holder_len,
-                                    NULL, NULL, pos, kde + kde_len - pos,
-                                    NULL, 0);
+               if (sm->assoc_resp_ftie &&
+                   kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
+                       os_memcpy(pos, sm->assoc_resp_ftie,
+                                 2 + sm->assoc_resp_ftie[1]);
+                       res = 2 + sm->assoc_resp_ftie[1];
+               } else {
+                       res = wpa_write_ftie(conf, conf->r0_key_holder,
+                                            conf->r0_key_holder_len,
+                                            NULL, NULL, pos,
+                                            kde + kde_len - pos,
+                                            NULL, 0);
+               }
                if (res < 0) {
                        wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
                                   "into EAPOL-Key Key Data");
@@ -2082,6 +2363,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 |
@@ -2097,20 +2388,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 if (sm->pairwise == WPA_CIPHER_GCMP) {
-                       alg = WPA_ALG_GCMP;
-                       klen = 16;
-               } 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;
                }
@@ -2209,7 +2490,8 @@ SM_STEP(WPA_PTK)
                }
                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,
@@ -2283,7 +2565,8 @@ 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];
 
@@ -2319,28 +2602,29 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
        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,
                                  gtk, gsm->GTK_len);
                pos = ieee80211w_kde_add(sm, pos);
+               kde_len = pos - kde;
        } else {
                kde = gtk;
-               pos = kde + gsm->GTK_len;
+               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);
 }
 
 
@@ -2417,15 +2701,16 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
 
 #ifdef CONFIG_IEEE80211W
        if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+               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],
-                                  WPA_IGTK_LEN) < 0)
+                                  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 */
 
@@ -2476,11 +2761,9 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
                                "marking station for GTK rekeying");
        }
 
-#ifdef CONFIG_IEEE80211V
-       /* Do not rekey GTK/IGTK when STA is in wnmsleep */
+       /* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */
        if (sm->is_wnmsleep)
                return 0;
-#endif /* CONFIG_IEEE80211V */
 
        sm->group->GKeyDoneStations++;
        sm->GUpdateStationKeys = TRUE;
@@ -2490,11 +2773,11 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
 }
 
 
-#ifdef CONFIG_IEEE80211V
-/* update GTK when exiting wnmsleep mode */
+#ifdef CONFIG_WNM
+/* update GTK when exiting WNM-Sleep Mode */
 void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
 {
-       if (sm->is_wnmsleep)
+       if (sm == NULL || sm->is_wnmsleep)
                return;
 
        wpa_group_update_sta(sm, NULL);
@@ -2503,117 +2786,73 @@ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
 
 void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag)
 {
-       sm->is_wnmsleep = !!flag;
+       if (sm)
+               sm->is_wnmsleep = !!flag;
 }
 
 
 int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos)
 {
-       u8 *subelem;
        struct wpa_group *gsm = sm->group;
-       size_t subelem_len, pad_len;
-       const u8 *key;
-       size_t key_len;
-       u8 keybuf[32];
-
-       /* GTK subslement */
-       key_len = gsm->GTK_len;
-       if (key_len > sizeof(keybuf))
-               return 0;
-
-       /*
-        * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less
-        * than 16 bytes.
-        */
-       pad_len = key_len % 8;
-       if (pad_len)
-               pad_len = 8 - pad_len;
-       if (key_len + pad_len < 16)
-               pad_len += 8;
-       if (pad_len) {
-               os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
-               os_memset(keybuf + key_len, 0, pad_len);
-               keybuf[key_len] = 0xdd;
-               key_len += pad_len;
-               key = keybuf;
-       } else
-               key = gsm->GTK[gsm->GN - 1];
+       u8 *start = pos;
 
        /*
+        * GTK subelement:
         * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
-        * Key[5..32] | 8 padding.
+        * Key[5..32]
         */
-       subelem_len = 13 + key_len + 8;
-       subelem = os_zalloc(subelem_len);
-       if (subelem == NULL)
-               return 0;
-
-       subelem[0] = WNM_SLEEP_SUBELEM_GTK;
-       subelem[1] = 11 + key_len + 8;
+       *pos++ = WNM_SLEEP_SUBELEM_GTK;
+       *pos++ = 11 + gsm->GTK_len;
        /* Key ID in B0-B1 of Key Info */
-       WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
-       subelem[4] = gsm->GTK_len;
-       if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5) != 0)
-       {
-               os_free(subelem);
+       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;
-       }
-       if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) {
-               os_free(subelem);
-               return 0;
-       }
-
-       os_memcpy(pos, subelem, subelem_len);
+       pos += 8;
+       os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+       pos += gsm->GTK_len;
 
-       wpa_hexdump_key(MSG_DEBUG, "Plaintext GTK",
+       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);
-       os_free(subelem);
 
-       return subelem_len;
+       return pos - start;
 }
 
 
 #ifdef CONFIG_IEEE80211W
 int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
 {
-       u8 *subelem, *ptr;
        struct wpa_group *gsm = sm->group;
-       size_t subelem_len;
-
-       /* IGTK subelement
-        * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] |
-        * Key[16] | 8 padding */
-       subelem_len = 1 + 1 + 2 + 6 + WPA_IGTK_LEN + 8;
-       subelem = os_zalloc(subelem_len);
-       if (subelem == NULL)
-               return 0;
+       u8 *start = pos;
+       size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
 
-       ptr = subelem;
-       *ptr++ = WNM_SLEEP_SUBELEM_IGTK;
-       *ptr++ = subelem_len - 2;
-       WPA_PUT_LE16(ptr, gsm->GN_igtk);
-       ptr += 2;
-       if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, ptr) != 0) {
-               os_free(subelem);
+       /*
+        * 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;
-       }
-       ptr += 6;
-       if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8,
-                    gsm->IGTK[gsm->GN_igtk - 4], ptr)) {
-               os_free(subelem);
-               return -1;
-       }
+       pos += 6;
 
-       os_memcpy(pos, subelem, subelem_len);
+       os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len);
+       pos += len;
 
-       wpa_hexdump_key(MSG_DEBUG, "Plaintext IGTK",
-                       gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
-       os_free(subelem);
+       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 subelem_len;
+       return pos - start;
 }
 #endif /* CONFIG_IEEE80211W */
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 
 
 static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
@@ -2657,24 +2896,54 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
        int ret = 0;
 
        if (wpa_auth_set_key(wpa_auth, group->vlan_id,
-                            wpa_alg_enum(wpa_auth->conf.wpa_group),
+                            wpa_cipher_to_alg(wpa_auth->conf.wpa_group),
                             broadcast_ether_addr, group->GN,
                             group->GTK[group->GN - 1], group->GTK_len) < 0)
                ret = -1;
 
 #ifdef CONFIG_IEEE80211W
-       if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION &&
-           wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK,
-                            broadcast_ether_addr, group->GN_igtk,
-                            group->IGTK[group->GN_igtk - 4],
-                            WPA_IGTK_LEN) < 0)
-               ret = -1;
+       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)
 {
@@ -2683,8 +2952,10 @@ static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
        group->changed = TRUE;
        group->wpa_group_state = WPA_GROUP_SETKEYSDONE;
 
-       if (wpa_group_config_group_keys(wpa_auth, group) < 0)
+       if (wpa_group_config_group_keys(wpa_auth, group) < 0) {
+               wpa_group_fatal_failure(wpa_auth, group);
                return -1;
+       }
 
        return 0;
 }
@@ -2695,6 +2966,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);
@@ -2791,28 +3064,9 @@ void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth)
 }
 
 
-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_GCMP:
-               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";
 }
 
 
@@ -2841,7 +3095,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
                          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;
 
@@ -2878,7 +3132,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,
@@ -2891,7 +3145,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;
 
@@ -2901,7 +3155,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;
 
@@ -2921,31 +3175,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_GCMP)
-                       pairwise = RSN_CIPHER_SUITE_GCMP;
-               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(
@@ -2964,7 +3197,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;
 
@@ -2974,7 +3207,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;
 
@@ -3051,13 +3284,22 @@ 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,
+                      unsigned int pmk_len,
                       int session_timeout, struct eapol_state_machine *eapol)
 {
        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,
+       if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+               if (pmk_len > PMK_LEN_SUITE_B_192)
+                       pmk_len = PMK_LEN_SUITE_B_192;
+       } else if (pmk_len > PMK_LEN) {
+               pmk_len = PMK_LEN;
+       }
+
+       if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len, NULL,
+                                sm->PTK.kck, sm->PTK.kck_len,
                                 sm->wpa_auth->addr, sm->addr, session_timeout,
                                 eapol, sm->wpa_key_mgmt))
                return 0;
@@ -3074,7 +3316,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,
+                                NULL, 0,
+                                wpa_auth->addr,
                                 sta_addr, session_timeout, eapol,
                                 WPA_KEY_MGMT_IEEE8021X))
                return 0;
@@ -3083,6 +3327,104 @@ 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, const u8 *pmkid)
+{
+       if (wpa_auth->conf.disable_pmksa_caching)
+               return -1;
+
+       if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid,
+                                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);
+       }
+}
+
+
+int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
+                       size_t len)
+{
+       if (!wpa_auth || !wpa_auth->pmksa)
+               return 0;
+       return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len);
+}
+
+
+/*
+ * 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)
 {
@@ -3104,6 +3446,98 @@ wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id)
 }
 
 
+/*
+ * Enforce that the group state machine for the VLAN is running, increase
+ * reference counter as interface is up. References might have been increased
+ * even if a negative value is returned.
+ * Returns: -1 on error (group missing, group already failed); otherwise, 0
+ */
+int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+       struct wpa_group *group;
+
+       if (wpa_auth == NULL)
+               return 0;
+
+       group = wpa_auth->group;
+       while (group) {
+               if (group->vlan_id == vlan_id)
+                       break;
+               group = group->next;
+       }
+
+       if (group == NULL) {
+               group = wpa_auth_add_group(wpa_auth, vlan_id);
+               if (group == NULL)
+                       return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,
+                  "WPA: Ensure group state machine running for VLAN ID %d",
+                  vlan_id);
+
+       wpa_group_get(wpa_auth, group);
+       group->num_setup_iface++;
+
+       if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+               return -1;
+
+       return 0;
+}
+
+
+/*
+ * Decrease reference counter, expected to be zero afterwards.
+ * returns: -1 on error (group not found, group in fail state)
+ *          -2 if wpa_group is still referenced
+ *           0 else
+ */
+int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+       struct wpa_group *group;
+       int ret = 0;
+
+       if (wpa_auth == NULL)
+               return 0;
+
+       group = wpa_auth->group;
+       while (group) {
+               if (group->vlan_id == vlan_id)
+                       break;
+               group = group->next;
+       }
+
+       if (group == NULL)
+               return -1;
+
+       wpa_printf(MSG_DEBUG,
+                  "WPA: Try stopping group state machine for VLAN ID %d",
+                  vlan_id);
+
+       if (group->num_setup_iface <= 0) {
+               wpa_printf(MSG_ERROR,
+                          "WPA: wpa_auth_release_group called more often than wpa_auth_ensure_group for VLAN ID %d, skipping.",
+                          vlan_id);
+               return -1;
+       }
+       group->num_setup_iface--;
+
+       if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+               ret = -1;
+
+       if (group->references > 1) {
+               wpa_printf(MSG_DEBUG,
+                          "WPA: Cannot stop group state machine for VLAN ID %d as references are still hold",
+                          vlan_id);
+               ret = -2;
+       }
+
+       wpa_group_put(wpa_auth, group);
+
+       return ret;
+}
+
+
 int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
 {
        struct wpa_group *group;
@@ -3127,10 +3561,16 @@ 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;
 }
 
@@ -3163,3 +3603,48 @@ void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
                                       wpa_send_eapol_timeout, wpa_auth, sm);
        }
 }
+
+
+int wpa_auth_uses_sae(struct wpa_state_machine *sm)
+{
+       if (sm == NULL)
+               return 0;
+       return wpa_key_mgmt_sae(sm->wpa_key_mgmt);
+}
+
+
+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);
+}