PMKSA: Show AP/mesh PMKSA list in PMKSA command
[mech_eap.git] / src / ap / wpa_auth.c
index b46b243..57839fe 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11 RSN / WPA Authenticator
- * Copyright (c) 2004-2013, 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.
@@ -44,7 +44,14 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
 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);
+                         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;
@@ -67,6 +74,14 @@ static inline int wpa_auth_mic_failure_report(
 }
 
 
+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);
+}
+
+
 static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth,
                                      const u8 *addr, wpa_eapol_variable var,
                                      int value)
@@ -254,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) {
@@ -565,6 +587,7 @@ wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
 
        sm->wpa_auth = wpa_auth;
        sm->group = wpa_auth->group;
+       wpa_group_get(sm->wpa_auth, sm->group);
 
        return sm;
 }
@@ -643,6 +666,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
 #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);
 }
 
@@ -804,6 +828,7 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
        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)) {
@@ -811,10 +836,13 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
                                               sm->p2p_dev_addr, pmk);
                        if (pmk == NULL)
                                break;
-               } else
+                       pmk_len = PMK_LEN;
+               } else {
                        pmk = sm->PMK;
+                       pmk_len = sm->pmk_len;
+               }
 
-               wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK);
+               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) {
@@ -849,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;
        }
 
@@ -1083,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");
@@ -1241,8 +1279,7 @@ continue_processing:
                 */
                if (msg == SMK_ERROR) {
 #ifdef CONFIG_PEERKEY
-                       wpa_smk_error(wpa_auth, sm, (const u8 *) (key + 1),
-                                     key_data_length);
+                       wpa_smk_error(wpa_auth, sm, key_data, key_data_length);
 #endif /* CONFIG_PEERKEY */
                        return;
                } else if (key_info & WPA_KEY_INFO_ERROR) {
@@ -1257,12 +1294,12 @@ continue_processing:
                        wpa_request_new_ptk(sm);
 #ifdef CONFIG_PEERKEY
                } else if (msg == SMK_M1) {
-                       wpa_smk_m1(wpa_auth, sm, key, (const u8 *) (key + 1),
+                       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,
@@ -1300,8 +1337,7 @@ continue_processing:
 
 #ifdef CONFIG_PEERKEY
        if (msg == SMK_M3) {
-               wpa_smk_m3(wpa_auth, sm, key, (const u8 *) (key + 1),
-                          key_data_length);
+               wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length);
                return;
        }
 #endif /* CONFIG_PEERKEY */
@@ -1376,14 +1412,19 @@ 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;
@@ -1430,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;
@@ -1466,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) {
@@ -1487,29 +1533,46 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
                    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, 16,
-                                    (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 "
@@ -1517,8 +1580,11 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
                        os_free(hdr);
                        return;
                }
-               wpa_eapol_key_mic(sm->PTK.kck, sm->wpa_key_mgmt, 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 &&
@@ -1526,7 +1592,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
                    wpa_auth->conf.corrupt_gtk_rekey_mic_probability) {
                        wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
                                        "Corrupting group EAPOL-Key Key MIC");
-                       key->key_mic[0]++;
+                       key_mic[0]++;
                }
 #endif /* CONFIG_TESTING_OPTIONS */
        }
@@ -1575,23 +1641,27 @@ static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
 {
        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, akmp, key_info & WPA_KEY_INFO_TYPE_MASK,
-                             data, data_len, key->key_mic) ||
-           os_memcmp_const(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;
 }
 
@@ -1606,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;
 
@@ -1694,6 +1764,14 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
                        wpa_remove_ptk(sm);
        }
 
+       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);
 }
 
@@ -1778,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;
+       }
 }
 
 
@@ -1827,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);
@@ -1841,7 +1939,10 @@ SM_STATE(WPA_PTK, INITPMK)
        } else {
                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
@@ -1863,6 +1964,7 @@ SM_STATE(WPA_PTK, INITPSK)
        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;
@@ -1914,7 +2016,7 @@ SM_STATE(WPA_PTK, PTKSTART)
                         * 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));
                }
@@ -1926,28 +2028,26 @@ SM_STATE(WPA_PTK, PTKSTART)
 
 
 static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
-                         const u8 *pmk, struct wpa_ptk *ptk)
+                         const u8 *pmk, unsigned int pmk_len,
+                         struct wpa_ptk *ptk)
 {
-       size_t ptk_len = wpa_cipher_key_len(sm->pairwise) + 32;
 #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, 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;
@@ -1962,10 +2062,14 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
                                               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, sm->SNonce, pmk, &PTK);
+               wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK);
 
                if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
                                       sm->last_rx_eapol_key,
@@ -1981,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;
        }
 
@@ -2013,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;
@@ -2191,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) {
@@ -2216,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");
@@ -2271,7 +2391,7 @@ SM_STATE(WPA_PTK, PTKINITDONE)
                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;
                }
@@ -2944,9 +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";
+       return val ? "TRUE" : "FALSE";
 }
 
 
@@ -3164,14 +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,
-                                sm->PTK.kck, sizeof(sm->PTK.kck),
+       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;
@@ -3188,7 +3316,7 @@ 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,
+       if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL,
                                 NULL, 0,
                                 wpa_auth->addr,
                                 sta_addr, session_timeout, eapol,
@@ -3200,12 +3328,12 @@ 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 *pmk, const u8 *pmkid)
 {
        if (wpa_auth->conf.disable_pmksa_caching)
                return -1;
 
-       if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN,
+       if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid,
                                 NULL, 0,
                                 wpa_auth->addr, addr, 0, NULL,
                                 WPA_KEY_MGMT_SAE))
@@ -3231,6 +3359,72 @@ void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 }
 
 
+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)
 {
@@ -3252,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;
@@ -3281,7 +3567,10 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
        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;
 }
 
@@ -3348,3 +3637,14 @@ int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
 {
        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);
+}