]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - src/ap/wpa_auth.c
WPA auth: Disconnect STA if MSK cannot be fetched
[thirdparty/hostap.git] / src / ap / wpa_auth.c
index 4d19bb0336a8d93426a4ab4e880c359731645c92..668cb429dd245915afaf443544a5dc700c3228e1 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.
@@ -33,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);
@@ -42,6 +43,8 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
                          struct wpa_group *group);
 static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
                                       struct wpa_group *group);
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+                         const u8 *pmk, struct wpa_ptk *ptk);
 
 static const u32 dot11RSNAConfigGroupUpdateCount = 4;
 static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
@@ -135,6 +138,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)
@@ -401,6 +415,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;
@@ -410,6 +425,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);
@@ -565,6 +581,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 */
@@ -621,6 +639,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
        }
 #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);
@@ -779,40 +798,96 @@ static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
 }
 
 
+static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
+                             size_t data_len)
+{
+       struct wpa_ptk PTK;
+       int ok = 0;
+       const u8 *pmk = NULL;
+
+       for (;;) {
+               if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+                       pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+                                              sm->p2p_dev_addr, pmk);
+                       if (pmk == NULL)
+                               break;
+               } else
+                       pmk = sm->PMK;
+
+               wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK);
+
+               if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
+                   == 0) {
+                       ok = 1;
+                       break;
+               }
+
+               if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+                       break;
+       }
+
+       if (!ok) {
+               wpa_printf(MSG_DEBUG,
+                          "WPA: Earlier SNonce did not result in matching MIC");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,
+                  "WPA: Earlier SNonce resulted in matching MIC");
+       sm->alt_snonce_valid = 0;
+       os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
+       os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+       sm->PTK_valid = TRUE;
+
+       return 0;
+}
+
+
 void wpa_receive(struct wpa_authenticator *wpa_auth,
                 struct wpa_state_machine *sm,
                 u8 *data, size_t data_len)
 {
        struct ieee802_1x_hdr *hdr;
        struct wpa_eapol_key *key;
+       struct wpa_eapol_key_192 *key192;
        u16 key_info, key_data_length;
        enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
               SMK_M1, SMK_M3, SMK_ERROR } msg;
        char *msgtxt;
        struct wpa_eapol_ie_parse kde;
        int ft;
-       const u8 *eapol_key_ie;
-       size_t eapol_key_ie_len;
+       const u8 *eapol_key_ie, *key_data;
+       size_t eapol_key_ie_len, keyhdrlen, mic_len;
 
        if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
                return;
 
-       if (data_len < sizeof(*hdr) + sizeof(*key))
+       mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+       keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+       if (data_len < sizeof(*hdr) + keyhdrlen)
                return;
 
        hdr = (struct ieee802_1x_hdr *) data;
        key = (struct wpa_eapol_key *) (hdr + 1);
+       key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
        key_info = WPA_GET_BE16(key->key_info);
-       key_data_length = WPA_GET_BE16(key->key_data_length);
+       if (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;
        }
 
@@ -881,6 +956,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                    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,
@@ -899,6 +975,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) {
@@ -934,8 +1017,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;
                }
 
@@ -994,8 +1094,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");
@@ -1120,7 +1219,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;
@@ -1149,7 +1251,7 @@ 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) {
@@ -1164,11 +1266,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,
@@ -1206,7 +1309,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 */
@@ -1281,18 +1384,24 @@ 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)
+       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;
@@ -1317,6 +1426,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
 
        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)
@@ -1333,6 +1443,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;
@@ -1358,6 +1470,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)
@@ -1367,8 +1481,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) {
@@ -1386,29 +1503,46 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
                                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);
+               } 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);
+               } 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 "
@@ -1416,16 +1550,19 @@ 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.0d &&
+                   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->key_mic[0]++;
+                       key_mic[0]++;
                }
 #endif /* CONFIG_TESTING_OPTIONS */
        }
@@ -1469,27 +1606,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;
 }
 
@@ -1516,6 +1658,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:
@@ -1729,8 +1879,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
@@ -1769,6 +1923,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) {
@@ -1791,10 +1946,13 @@ SM_STATE(WPA_PTK, PTKSTART)
                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.
@@ -1810,21 +1968,17 @@ SM_STATE(WPA_PTK, PTKSTART)
 }
 
 
-static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk,
-                         struct wpa_ptk *ptk)
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+                         const u8 *pmk, struct wpa_ptk *ptk)
 {
-       size_t ptk_len = 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, 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);
 }
 
 
@@ -1850,9 +2004,10 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
                } else
                        pmk = sm->PMK;
 
-               wpa_derive_ptk(sm, pmk, &PTK);
+               wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK);
 
-               if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key,
+               if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
+                                      sm->last_rx_eapol_key,
                                       sm->last_rx_eapol_key_len) == 0) {
                        ok = 1;
                        break;
@@ -1874,8 +2029,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");
@@ -2005,8 +2160,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,
@@ -2153,7 +2310,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;
                }
@@ -2327,7 +2484,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];
 
@@ -2363,28 +2521,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);
 }
 
 
@@ -2855,7 +3014,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;
 
@@ -2905,7 +3064,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;
 
@@ -2915,7 +3074,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;
 
@@ -2957,7 +3116,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;
 
@@ -2967,7 +3126,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,6 +3210,7 @@ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
                return -1;
 
        if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN,
+                                sm->PTK.kck, sm->PTK.kck_len,
                                 sm->wpa_auth->addr, sm->addr, session_timeout,
                                 eapol, sm->wpa_key_mgmt))
                return 0;
@@ -3067,7 +3227,9 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
        if (wpa_auth == NULL)
                return -1;
 
-       if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr,
+       if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len,
+                                NULL, 0,
+                                wpa_auth->addr,
                                 sta_addr, session_timeout, eapol,
                                 WPA_KEY_MGMT_IEEE8021X))
                return 0;
@@ -3076,6 +3238,22 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
 }
 
 
+int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+                          const u8 *pmk)
+{
+       if (wpa_auth->conf.disable_pmksa_caching)
+               return -1;
+
+       if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN,
+                                NULL, 0,
+                                wpa_auth->addr, addr, 0, NULL,
+                                WPA_KEY_MGMT_SAE))
+               return 0;
+
+       return -1;
+}
+
+
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
                           const u8 *sta_addr)
 {
@@ -3202,3 +3380,10 @@ int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr)
        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);
+}