]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - src/ap/wpa_auth.c
Allow RSNE in EAPOL-Key msg 3/4 to be replaced for testing purposes
[thirdparty/hostap.git] / src / ap / wpa_auth.c
index 54ad2a4b892461d6c449fa3f16c4b7a5ef54a2e3..ab20705f0f3b0bb71723d25844944c5f9844043d 100644 (file)
@@ -56,13 +56,14 @@ 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);
+                         struct wpa_ptk *ptk, int force_sha256);
 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 int ieee80211w_kde_len(struct wpa_state_machine *sm);
 static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
 
 static const u32 eapol_key_timeout_first = 100; /* ms */
@@ -136,21 +137,46 @@ static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth,
 static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
                                   int vlan_id,
                                   enum wpa_alg alg, const u8 *addr, int idx,
-                                  u8 *key, size_t key_len)
+                                  u8 *key, size_t key_len,
+                                  enum key_flag key_flag)
 {
        if (wpa_auth->cb->set_key == NULL)
                return -1;
        return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
-                                    key, key_len);
+                                    key, key_len, key_flag);
 }
 
 
 static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
                                      const u8 *addr, int idx, u8 *seq)
 {
+       int res;
+
        if (wpa_auth->cb->get_seqnum == NULL)
                return -1;
-       return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
+       res = wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
+#ifdef CONFIG_TESTING_OPTIONS
+       if (!addr && idx < 4 && wpa_auth->conf.gtk_rsc_override_set) {
+               wpa_printf(MSG_DEBUG,
+                          "TESTING: Override GTK RSC %016llx --> %016llx",
+                          (long long unsigned) WPA_GET_LE64(seq),
+                          (long long unsigned)
+                          WPA_GET_LE64(wpa_auth->conf.gtk_rsc_override));
+               os_memcpy(seq, wpa_auth->conf.gtk_rsc_override,
+                         WPA_KEY_RSC_LEN);
+       }
+       if (!addr && idx >= 4 && idx <= 5 &&
+           wpa_auth->conf.igtk_rsc_override_set) {
+               wpa_printf(MSG_DEBUG,
+                          "TESTING: Override IGTK RSC %016llx --> %016llx",
+                          (long long unsigned) WPA_GET_LE64(seq),
+                          (long long unsigned)
+                          WPA_GET_LE64(wpa_auth->conf.igtk_rsc_override));
+               os_memcpy(seq, wpa_auth->conf.igtk_rsc_override,
+                         WPA_KEY_RSC_LEN);
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+       return res;
 }
 
 
@@ -708,6 +734,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
 #endif /* CONFIG_IEEE80211R_AP */
        os_free(sm->last_rx_eapol_key);
        os_free(sm->wpa_ie);
+       os_free(sm->rsnxe);
        wpa_group_put(sm->wpa_auth, sm->group);
 #ifdef CONFIG_DPP2
        wpabuf_clear_free(sm->dpp_z);
@@ -754,8 +781,18 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm)
        if (sm == NULL)
                return;
 
-       sm->PTKRequest = TRUE;
-       sm->PTK_valid = 0;
+       if (sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
+               wpa_printf(MSG_INFO,
+                          "WPA: PTK0 rekey not allowed, disconnect " MACSTR,
+                          MAC2STR(sm->addr));
+               sm->Disconnect = TRUE;
+               /* Try to encourage the STA to reconnect */
+               sm->disconnect_reason =
+                       WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
+       } else {
+               sm->PTKRequest = TRUE;
+               sm->PTK_valid = 0;
+       }
 }
 
 
@@ -900,7 +937,8 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
                        pmk_len = sm->pmk_len;
                }
 
-               if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK) < 0)
+               if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK, 0) <
+                   0)
                        break;
 
                if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
@@ -934,6 +972,7 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
 
        os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
        os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+       forced_memzero(&PTK, sizeof(PTK));
        sm->PTK_valid = TRUE;
 
        return 0;
@@ -1407,6 +1446,8 @@ static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
 #endif /* CONFIG_SHA256 */
 #endif /* CONFIG_SHA384 */
 
+       forced_memzero(data, sizeof(data));
+
        return ret;
 }
 
@@ -1710,7 +1751,7 @@ void wpa_remove_ptk(struct wpa_state_machine *sm)
        sm->PTK_valid = FALSE;
        os_memset(&sm->PTK, 0, sizeof(sm->PTK));
        if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL,
-                            0))
+                            0, KEY_FLAG_PAIRWISE))
                wpa_printf(MSG_DEBUG,
                           "RSN: PTK removal from the driver failed");
        sm->pairwise_set = FALSE;
@@ -1771,6 +1812,15 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
                        sm->Init = FALSE;
                        sm->AuthenticationRequest = TRUE;
                        break;
+               } else if (sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
+                       wpa_printf(MSG_INFO,
+                                  "WPA: PTK0 rekey not allowed, disconnect "
+                                  MACSTR, MAC2STR(sm->addr));
+                       sm->Disconnect = TRUE;
+                       /* Try to encourage the STA reconnect */
+                       sm->disconnect_reason =
+                               WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
+                       break;
                }
                if (sm->GUpdateStationKeys) {
                        /*
@@ -1815,10 +1865,8 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
        sm->ft_completed = 0;
 #endif /* CONFIG_IEEE80211R_AP */
 
-#ifdef CONFIG_IEEE80211W
        if (sm->mgmt_frame_prot && event == WPA_AUTH)
                remove_ptk = 0;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_FILS
        if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
            (event == WPA_AUTH || event == WPA_ASSOC))
@@ -2046,7 +2094,7 @@ SM_STATE(WPA_PTK, INITPMK)
                sm->Disconnect = TRUE;
                return;
        }
-       os_memset(msk, 0, sizeof(msk));
+       forced_memzero(msk, sizeof(msk));
 
        sm->req_replay_counter_used = 0;
        /* IEEE 802.11i does not set keyRun to FALSE, but not doing this
@@ -2167,7 +2215,6 @@ SM_STATE(WPA_PTK, PTKSTART)
                        wpa_printf(MSG_DEBUG,
                                   "FT: No PMKID in message 1/4 when using FT protocol");
                        pmkid = NULL;
-                       pmkid_len = 0;
 #endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_SAE
                } else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
@@ -2197,6 +2244,8 @@ SM_STATE(WPA_PTK, PTKSTART)
                                    &pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN);
                }
        }
+       if (!pmkid)
+               pmkid_len = 0;
        wpa_send_eapol(sm->wpa_auth, sm,
                       WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
                       sm->ANonce, pmkid, pmkid_len, 0, 0);
@@ -2205,14 +2254,27 @@ SM_STATE(WPA_PTK, PTKSTART)
 
 static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
                          const u8 *pmk, unsigned int pmk_len,
-                         struct wpa_ptk *ptk)
+                         struct wpa_ptk *ptk, int force_sha256)
 {
        const u8 *z = NULL;
        size_t z_len = 0;
+       int akmp;
 
 #ifdef CONFIG_IEEE80211R_AP
-       if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+       if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+               if (sm->ft_completed) {
+                       u8 ptk_name[WPA_PMK_NAME_LEN];
+
+                       return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len,
+                                                sm->SNonce, sm->ANonce,
+                                                sm->addr, sm->wpa_auth->addr,
+                                                sm->pmk_r1_name,
+                                                ptk, ptk_name,
+                                                sm->wpa_key_mgmt,
+                                                sm->pairwise);
+               }
                return wpa_auth_derive_ptk_ft(sm, ptk);
+       }
 #endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_DPP2
@@ -2222,9 +2284,12 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
        }
 #endif /* CONFIG_DPP2 */
 
+       akmp = sm->wpa_key_mgmt;
+       if (force_sha256)
+               akmp |= WPA_KEY_MGMT_PSK_SHA256;
        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, z, z_len);
+                             ptk, akmp, sm->pairwise, z, z_len);
 }
 
 
@@ -2273,12 +2338,12 @@ int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
                wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
                            pmk_r0_name, WPA_PMK_NAME_LEN);
                wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
-               os_memset(fils_ft, 0, sizeof(fils_ft));
+               forced_memzero(fils_ft, sizeof(fils_ft));
 
                res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder,
                                             sm->addr, sm->pmk_r1_name,
                                             use_sha384);
-               os_memset(pmk_r0, 0, PMK_LEN_MAX);
+               forced_memzero(pmk_r0, PMK_LEN_MAX);
                if (res < 0)
                        return -1;
                wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name,
@@ -2296,7 +2361,7 @@ int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
                               sm->wpa_key_mgmt, sm->fils_key_auth_sta,
                               sm->fils_key_auth_ap,
                               &sm->fils_key_auth_len);
-       os_memset(ick, 0, sizeof(ick));
+       forced_memzero(ick, sizeof(ick));
 
        /* Store nonces for (Re)Association Request/Response frame processing */
        os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN);
@@ -2598,7 +2663,7 @@ int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
        if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) {
                wpa_printf(MSG_DEBUG,
                           "FILS: Not enough room for FILS elements");
-               wpabuf_free(plain);
+               wpabuf_clear_free(plain);
                return -1;
        }
 
@@ -2608,7 +2673,7 @@ int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
        if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len,
                            wpabuf_head(plain), wpabuf_len(plain),
                            5, aad, aad_len, pos) < 0) {
-               wpabuf_free(plain);
+               wpabuf_clear_free(plain);
                return -1;
        }
 
@@ -2616,7 +2681,7 @@ int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
                    "FILS: Encrypted Association Response elements",
                    pos, AES_BLOCK_SIZE + wpabuf_len(plain));
        current_len += wpabuf_len(plain) + AES_BLOCK_SIZE;
-       wpabuf_free(plain);
+       wpabuf_clear_free(plain);
 
        sm->fils_completed = 1;
 
@@ -2634,7 +2699,7 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
        size_t gtk_len;
        struct wpa_group *gsm;
 
-       plain = wpabuf_alloc(1000);
+       plain = wpabuf_alloc(1000 + ieee80211w_kde_len(sm));
        if (!plain)
                return NULL;
 
@@ -2670,7 +2735,7 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
                 * of GTK in the BSS.
                 */
                if (random_get_bytes(dummy_gtk, gtk_len) < 0) {
-                       wpabuf_free(plain);
+                       wpabuf_clear_free(plain);
                        return NULL;
                }
                gtk = dummy_gtk;
@@ -2682,7 +2747,7 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
                           gtk, gtk_len);
        wpabuf_put(plain, tmp2 - tmp);
 
-       /* IGTK KDE */
+       /* IGTK KDE and BIGTK KDE */
        tmp = wpabuf_put(plain, 0);
        tmp2 = ieee80211w_kde_add(sm, tmp);
        wpabuf_put(plain, tmp2 - tmp);
@@ -2697,13 +2762,13 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
                if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
                        wpa_printf(MSG_WARNING,
                                   "FILS: Failed to get channel info for OCI element");
-                       wpabuf_free(plain);
+                       wpabuf_clear_free(plain);
                        return NULL;
                }
 
                pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN);
                if (ocv_insert_extended_oci(&ci, pos) < 0) {
-                       wpabuf_free(plain);
+                       wpabuf_clear_free(plain);
                        return NULL;
                }
        }
@@ -2732,7 +2797,7 @@ int fils_set_tk(struct wpa_state_machine *sm)
 
        wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver");
        if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
-                            sm->PTK.tk, klen)) {
+                            sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX)) {
                wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
                return -1;
        }
@@ -2766,7 +2831,7 @@ u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf,
 
        wpa_printf(MSG_DEBUG, "%s: plain buf_len: %u", __func__,
                   (unsigned int) wpabuf_len(plain));
-       wpabuf_free(plain);
+       wpabuf_clear_free(plain);
        sm->fils_completed = 1;
        return pos;
 }
@@ -2804,6 +2869,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
        struct wpa_eapol_key *key;
        struct wpa_eapol_ie_parse kde;
        int vlan_id = 0;
+       int owe_ptk_workaround = !!wpa_auth->conf.owe_ptk_workaround;
 
        SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
        sm->EAPOLKeyReceived = FALSE;
@@ -2841,7 +2907,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
                        pmk_len = sm->pmksa->pmk_len;
                }
 
-               if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0)
+               if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK,
+                                  owe_ptk_workaround == 2) < 0)
                        break;
 
                if (mic_len &&
@@ -2865,6 +2932,16 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
                }
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_OWE
+               if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && pmk_len > 32 &&
+                   owe_ptk_workaround == 1) {
+                       wpa_printf(MSG_DEBUG,
+                                  "OWE: Try PTK derivation workaround with SHA256");
+                       owe_ptk_workaround = 2;
+                       continue;
+               }
+#endif /* CONFIG_OWE */
+
                if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
                    wpa_key_mgmt_sae(sm->wpa_key_mgmt))
                        break;
@@ -2923,6 +3000,22 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
                                   WLAN_REASON_PREV_AUTH_NOT_VALID);
                return;
        }
+       if ((!sm->rsnxe && kde.rsnxe) ||
+           (sm->rsnxe && !kde.rsnxe) ||
+           (sm->rsnxe && kde.rsnxe &&
+            (sm->rsnxe_len != kde.rsnxe_len ||
+             os_memcmp(sm->rsnxe, kde.rsnxe, sm->rsnxe_len) != 0))) {
+               wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+                               "RSNXE from (Re)AssocReq did not match the one in EAPOL-Key msg 2/4");
+               wpa_hexdump(MSG_DEBUG, "RSNXE in AssocReq",
+                           sm->rsnxe, sm->rsnxe_len);
+               wpa_hexdump(MSG_DEBUG, "RSNXE in EAPOL-Key msg 2/4",
+                           kde.rsnxe, kde.rsnxe_len);
+               /* MLME-DEAUTHENTICATE.request */
+               wpa_sta_disconnect(wpa_auth, sm->addr,
+                                  WLAN_REASON_PREV_AUTH_NOT_VALID);
+               return;
+       }
 #ifdef CONFIG_OCV
        if (wpa_auth_uses_ocv(sm)) {
                struct wpa_channel_info ci;
@@ -3018,6 +3111,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
        sm->MICVerified = TRUE;
 
        os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+       forced_memzero(&PTK, sizeof(PTK));
        sm->PTK_valid = TRUE;
 }
 
@@ -3029,23 +3123,27 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2)
 }
 
 
-#ifdef CONFIG_IEEE80211W
-
 static int ieee80211w_kde_len(struct wpa_state_machine *sm)
 {
+       size_t len = 0;
+
        if (sm->mgmt_frame_prot) {
-               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;
+               len += 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN;
+               len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+       }
+       if (sm->mgmt_frame_prot && sm->wpa_auth->conf.beacon_prot) {
+               len += 2 + RSN_SELECTOR_LEN + WPA_BIGTK_KDE_PREFIX_LEN;
+               len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
        }
 
-       return 0;
+       return len;
 }
 
 
 static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
 {
        struct wpa_igtk_kde igtk;
+       struct wpa_bigtk_kde bigtk;
        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);
@@ -3074,24 +3172,24 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
                          (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
                          NULL, 0);
 
-       return pos;
-}
-
-#else /* CONFIG_IEEE80211W */
-
-static int ieee80211w_kde_len(struct wpa_state_machine *sm)
-{
-       return 0;
-}
+       if (!sm->wpa_auth->conf.beacon_prot)
+               return pos;
 
+       bigtk.keyid[0] = gsm->GN_bigtk;
+       bigtk.keyid[1] = 0;
+       if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
+           wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, rsc) < 0)
+               os_memset(bigtk.pn, 0, sizeof(bigtk.pn));
+       else
+               os_memcpy(bigtk.pn, rsc, sizeof(bigtk.pn));
+       os_memcpy(bigtk.bigtk, gsm->BIGTK[gsm->GN_bigtk - 6], len);
+       pos = wpa_add_kde(pos, RSN_KEY_DATA_BIGTK,
+                         (const u8 *) &bigtk, WPA_BIGTK_KDE_PREFIX_LEN + len,
+                         NULL, 0);
 
-static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
-{
        return pos;
 }
 
-#endif /* CONFIG_IEEE80211W */
-
 
 static int ocv_oci_len(struct wpa_state_machine *sm)
 {
@@ -3123,13 +3221,46 @@ static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos)
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+static u8 * replace_ie(const char *name, const u8 *old_buf, size_t *len, u8 eid,
+                      const u8 *ie, size_t ie_len)
+{
+       const u8 *elem;
+       u8 *buf;
+
+       wpa_printf(MSG_DEBUG, "TESTING: %s EAPOL override", name);
+       wpa_hexdump(MSG_DEBUG, "TESTING: wpa_ie before override",
+                   old_buf, *len);
+       buf = os_malloc(*len + ie_len);
+       if (!buf)
+               return NULL;
+       os_memcpy(buf, old_buf, *len);
+       elem = get_ie(buf, *len, eid);
+       if (elem) {
+               u8 elem_len = 2 + elem[1];
+
+               os_memmove((void *) elem, elem + elem_len,
+                          *len - (elem - buf) - elem_len);
+               *len -= elem_len;
+       }
+       os_memcpy(buf + *len, ie, ie_len);
+       *len += ie_len;
+       wpa_hexdump(MSG_DEBUG, "TESTING: wpa_ie after EAPOL override",
+                   buf, *len);
+
+       return buf;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
 SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 {
-       u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32];
-       size_t gtk_len, kde_len;
+       u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde = NULL, *pos, dummy_gtk[32];
+       size_t gtk_len, kde_len, wpa_ie_len;
        struct wpa_group *gsm = sm->group;
        u8 *wpa_ie;
-       int wpa_ie_len, secure, keyidx, encr = 0;
+       int secure, gtkidx, encr = 0;
+       u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL;
 
        SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
        sm->TimeoutEvt = FALSE;
@@ -3147,7 +3278,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
        }
 
        /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
-          GTK[GN], IGTK, [FTIE], [TIE * 2])
+          GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
         */
        os_memset(rsc, 0, WPA_KEY_RSC_LEN);
        wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
@@ -3156,13 +3287,33 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
        wpa_ie_len = sm->wpa_auth->wpa_ie_len;
        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_ie_len > wpa_ie[1] + 2U && wpa_ie[0] == WLAN_EID_RSN) {
                /* 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;
        }
+#ifdef CONFIG_TESTING_OPTIONS
+       if (sm->wpa_auth->conf.rsne_override_eapol_set) {
+               wpa_ie_buf2 = replace_ie(
+                       "RSNE", wpa_ie, &wpa_ie_len, WLAN_EID_RSN,
+                       sm->wpa_auth->conf.rsne_override_eapol,
+                       sm->wpa_auth->conf.rsne_override_eapol_len);
+               if (!wpa_ie_buf2)
+                       goto done;
+               wpa_ie = wpa_ie_buf2;
+       }
+       if (sm->wpa_auth->conf.rsnxe_override_eapol_set) {
+               wpa_ie_buf = replace_ie(
+                       "RSNXE", wpa_ie, &wpa_ie_len, WLAN_EID_RSNX,
+                       sm->wpa_auth->conf.rsnxe_override_eapol,
+                       sm->wpa_auth->conf.rsnxe_override_eapol_len);
+               if (!wpa_ie_buf)
+                       goto done;
+               wpa_ie = wpa_ie_buf;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
        wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
                        "sending 3/4 msg of 4-Way Handshake");
        if (sm->wpa == WPA_VERSION_WPA2) {
@@ -3177,10 +3328,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
                         * of GTK in the BSS.
                         */
                        if (random_get_bytes(dummy_gtk, gtk_len) < 0)
-                               return;
+                               goto done;
                        gtk = dummy_gtk;
                }
-               keyidx = gsm->GN;
+               gtkidx = gsm->GN;
                _rsc = rsc;
                encr = 1;
        } else {
@@ -3188,7 +3339,6 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
                secure = 0;
                gtk = NULL;
                gtk_len = 0;
-               keyidx = 0;
                _rsc = NULL;
                if (sm->rx_eapol_key_secure) {
                        /*
@@ -3221,7 +3371,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 #endif /* CONFIG_P2P */
        kde = os_malloc(kde_len);
        if (kde == NULL)
-               return;
+               goto done;
 
        pos = kde;
        os_memcpy(pos, wpa_ie, wpa_ie_len);
@@ -3236,8 +3386,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
                if (res < 0) {
                        wpa_printf(MSG_ERROR, "FT: Failed to insert "
                                   "PMKR1Name into RSN IE in EAPOL-Key data");
-                       os_free(kde);
-                       return;
+                       goto done;
                }
                pos -= wpa_ie_len;
                pos += elen;
@@ -3245,16 +3394,14 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 #endif /* CONFIG_IEEE80211R_AP */
        if (gtk) {
                u8 hdr[2];
-               hdr[0] = keyidx & 0x03;
+               hdr[0] = gtkidx & 0x03;
                hdr[1] = 0;
                pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
                                  gtk, gtk_len);
        }
        pos = ieee80211w_kde_add(sm, pos);
-       if (ocv_oci_add(sm, &pos) < 0) {
-               os_free(kde);
-               return;
-       }
+       if (ocv_oci_add(sm, &pos) < 0)
+               goto done;
 
 #ifdef CONFIG_IEEE80211R_AP
        if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
@@ -3280,8 +3427,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
                if (res < 0) {
                        wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
                                   "into EAPOL-Key Key Data");
-                       os_free(kde);
-                       return;
+                       goto done;
                }
                pos += res;
 
@@ -3317,8 +3463,11 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
                        WPA_KEY_INFO_MIC : 0) |
                       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
                       WPA_KEY_INFO_KEY_TYPE,
-                      _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
+                      _rsc, sm->ANonce, kde, pos - kde, 0, encr);
+done:
        os_free(kde);
+       os_free(wpa_ie_buf);
+       os_free(wpa_ie_buf2);
 }
 
 
@@ -3330,7 +3479,8 @@ 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.tk, klen)) {
+                                    sm->PTK.tk, klen,
+                                    KEY_FLAG_PAIRWISE_RX_TX)) {
                        wpa_sta_disconnect(sm->wpa_auth, sm->addr,
                                           WLAN_REASON_PREV_AUTH_NOT_VALID);
                        return;
@@ -3720,6 +3870,7 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
                          struct wpa_group *group)
 {
        int ret = 0;
+       size_t len;
 
        os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
        inc_byte_array(group->Counter, WPA_NONCE_LEN);
@@ -3730,9 +3881,7 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
        wpa_hexdump_key(MSG_DEBUG, "GTK",
                        group->GTK[group->GN - 1], group->GTK_len);
 
-#ifdef CONFIG_IEEE80211W
        if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
-               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);
@@ -3743,7 +3892,19 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
                wpa_hexdump_key(MSG_DEBUG, "IGTK",
                                group->IGTK[group->GN_igtk - 4], len);
        }
-#endif /* CONFIG_IEEE80211W */
+
+       if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+           wpa_auth->conf.beacon_prot) {
+               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, "BIGTK key expansion",
+                                  wpa_auth->addr, group->GNonce,
+                                  group->BIGTK[group->GN_bigtk - 6], len) < 0)
+                       ret = -1;
+               wpa_hexdump_key(MSG_DEBUG, "BIGTK",
+                               group->BIGTK[group->GN_bigtk - 6], len);
+       }
 
        return ret;
 }
@@ -3761,10 +3922,10 @@ static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth,
        os_memset(group->GTK, 0, sizeof(group->GTK));
        group->GN = 1;
        group->GM = 2;
-#ifdef CONFIG_IEEE80211W
        group->GN_igtk = 4;
        group->GM_igtk = 5;
-#endif /* CONFIG_IEEE80211W */
+       group->GN_bigtk = 6;
+       group->GM_bigtk = 7;
        /* GTK[GN] = CalcGTK() */
        wpa_gtk_update(wpa_auth, group);
 }
@@ -3853,7 +4014,6 @@ int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos)
 }
 
 
-#ifdef CONFIG_IEEE80211W
 int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
 {
        struct wpa_group *gsm = sm->group;
@@ -3882,7 +4042,37 @@ int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
 
        return pos - start;
 }
-#endif /* CONFIG_IEEE80211W */
+
+
+int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+       struct wpa_group *gsm = sm->group;
+       u8 *start = pos;
+       size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+
+       /*
+        * BIGTK subelement:
+        * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
+        */
+       *pos++ = WNM_SLEEP_SUBELEM_BIGTK;
+       *pos++ = 2 + 6 + len;
+       WPA_PUT_LE16(pos, gsm->GN_bigtk);
+       pos += 2;
+       if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos) != 0)
+               return 0;
+       pos += 6;
+
+       os_memcpy(pos, gsm->BIGTK[gsm->GN_bigtk - 6], len);
+       pos += len;
+
+       wpa_printf(MSG_DEBUG, "WNM: BIGTK Key ID %u in WNM-Sleep Mode exit",
+                  gsm->GN_bigtk);
+       wpa_hexdump_key(MSG_DEBUG, "WNM: BIGTK in WNM-Sleep Mode exit",
+                       gsm->IGTK[gsm->GN_bigtk - 6], len);
+
+       return pos - start;
+}
+
 #endif /* CONFIG_WNM_AP */
 
 
@@ -3899,11 +4089,12 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
        tmp = group->GM;
        group->GM = group->GN;
        group->GN = tmp;
-#ifdef CONFIG_IEEE80211W
        tmp = group->GM_igtk;
        group->GM_igtk = group->GN_igtk;
        group->GN_igtk = tmp;
-#endif /* CONFIG_IEEE80211W */
+       tmp = group->GM_bigtk;
+       group->GM_bigtk = group->GN_bigtk;
+       group->GN_bigtk = tmp;
        /* "GKeyDoneStations = GNoStations" is done in more robust way by
         * counting the STAs that are marked with GUpdateStationKeys instead of
         * including all STAs that could be in not-yet-completed state. */
@@ -3929,10 +4120,10 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
        if (wpa_auth_set_key(wpa_auth, group->vlan_id,
                             wpa_cipher_to_alg(wpa_auth->conf.wpa_group),
                             broadcast_ether_addr, group->GN,
-                            group->GTK[group->GN - 1], group->GTK_len) < 0)
+                            group->GTK[group->GN - 1], group->GTK_len,
+                            KEY_FLAG_GROUP_TX_DEFAULT) < 0)
                ret = -1;
 
-#ifdef CONFIG_IEEE80211W
        if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
                enum wpa_alg alg;
                size_t len;
@@ -3943,10 +4134,17 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
                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)
+                                    group->IGTK[group->GN_igtk - 4], len,
+                                    KEY_FLAG_GROUP_TX_DEFAULT) < 0)
+                       ret = -1;
+
+               if (ret == 0 && wpa_auth->conf.beacon_prot &&
+                   wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+                                    broadcast_ether_addr, group->GN_bigtk,
+                                    group->BIGTK[group->GN_bigtk - 6], len,
+                                    KEY_FLAG_GROUP_TX_DEFAULT) < 0)
                        ret = -1;
        }
-#endif /* CONFIG_IEEE80211W */
 
        return ret;
 }
@@ -4084,11 +4282,12 @@ void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth)
                tmp = group->GM;
                group->GM = group->GN;
                group->GN = tmp;
-#ifdef CONFIG_IEEE80211W
                tmp = group->GM_igtk;
                group->GM_igtk = group->GN_igtk;
                group->GN_igtk = tmp;
-#endif /* CONFIG_IEEE80211W */
+               tmp = group->GM_bigtk;
+               group->GM_bigtk = group->GN_bigtk;
+               group->GN_bigtk = tmp;
                wpa_gtk_update(wpa_auth, group);
                wpa_group_config_group_keys(wpa_auth, group);
        }
@@ -4234,8 +4433,12 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
 
        /* Private MIB */
        ret = os_snprintf(buf + len, buflen - len,
+                         "wpa=%d\n"
+                         "AKMSuiteSelector=" RSN_SUITE "\n"
                          "hostapdWPAPTKState=%d\n"
                          "hostapdWPAPTKGroupState=%d\n",
+                         sm->wpa,
+                         RSN_SUITE_ARG(wpa_akm_to_suite(sm->wpa_key_mgmt)),
                          sm->wpa_ptk_state,
                          sm->wpa_ptk_group_state);
        if (os_snprintf_error(buflen - len, ret))
@@ -4347,6 +4550,15 @@ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
            sm->wpa_auth->conf.disable_pmksa_caching)
                return -1;
 
+#ifdef CONFIG_IEEE80211R_AP
+       if (pmk_len >= 2 * PMK_LEN && wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
+           wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+           !wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
+               /* Cache MPMK/XXKey instead of initial part from MSK */
+               pmk = pmk + PMK_LEN;
+               pmk_len = PMK_LEN;
+       } else
+#endif /* CONFIG_IEEE80211R_AP */
        if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
                if (pmk_len > PMK_LEN_SUITE_B_192)
                        pmk_len = PMK_LEN_SUITE_B_192;
@@ -4354,6 +4566,7 @@ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
                pmk_len = PMK_LEN;
        }
 
+       wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK", pmk, 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,
@@ -4372,6 +4585,7 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
        if (wpa_auth == NULL)
                return -1;
 
+       wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from preauth", pmk, len);
        if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL,
                                 NULL, 0,
                                 wpa_auth->addr,
@@ -4389,6 +4603,7 @@ int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
        if (wpa_auth->conf.disable_pmksa_caching)
                return -1;
 
+       wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, PMK_LEN);
        if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid,
                                 NULL, 0,
                                 wpa_auth->addr, addr, 0, NULL,
@@ -4413,6 +4628,7 @@ int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
        if (wpa_auth->conf.disable_pmksa_caching)
                return -1;
 
+       wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (2)", pmk, PMK_LEN);
        if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
                                 NULL, 0, wpa_auth->addr, addr, session_timeout,
                                 NULL, akmp))
@@ -4946,16 +5162,14 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
                       void *ctx1, void *ctx2)
 {
        u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
-#ifdef CONFIG_IEEE80211W
        u8 *opos;
-#endif /* CONFIG_IEEE80211W */
        size_t gtk_len, kde_len;
        struct wpa_group *gsm = sm->group;
        u8 *wpa_ie;
-       int wpa_ie_len, secure, keyidx, encr = 0;
+       int wpa_ie_len, secure, gtkidx, encr = 0;
 
        /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
-          GTK[GN], IGTK, [FTIE], [TIE * 2])
+          GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
         */
 
        /* Use 0 RSC */
@@ -4979,7 +5193,7 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
                secure = 1;
                gtk = gsm->GTK[gsm->GN - 1];
                gtk_len = gsm->GTK_len;
-               keyidx = gsm->GN;
+               gtkidx = gsm->GN;
                _rsc = rsc;
                encr = 1;
        } else {
@@ -4987,7 +5201,6 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
                secure = 0;
                gtk = NULL;
                gtk_len = 0;
-               keyidx = 0;
                _rsc = NULL;
                if (sm->rx_eapol_key_secure) {
                        /*
@@ -5040,12 +5253,11 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
 #endif /* CONFIG_IEEE80211R_AP */
        if (gtk) {
                u8 hdr[2];
-               hdr[0] = keyidx & 0x03;
+               hdr[0] = gtkidx & 0x03;
                hdr[1] = 0;
                pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
                                  gtk, gtk_len);
        }
-#ifdef CONFIG_IEEE80211W
        opos = pos;
        pos = ieee80211w_kde_add(sm, pos);
        if (pos - opos >= 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
@@ -5053,7 +5265,6 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
                opos += 2 + RSN_SELECTOR_LEN + 2;
                os_memset(opos, 0, 6); /* clear PN */
        }
-#endif /* CONFIG_IEEE80211W */
        if (ocv_oci_add(sm, &pos) < 0) {
                os_free(kde);
                return -1;
@@ -5110,7 +5321,7 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
                        WPA_KEY_INFO_MIC : 0) |
                       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
                       WPA_KEY_INFO_KEY_TYPE,
-                      _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
+                      _rsc, sm->ANonce, kde, pos - kde, 0, encr);
        os_free(kde);
        return 0;
 }
@@ -5124,9 +5335,7 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
        struct wpa_group *gsm = sm->group;
        const u8 *kde;
        u8 *kde_buf = NULL, *pos, hdr[2];
-#ifdef CONFIG_IEEE80211W
        u8 *opos;
-#endif /* CONFIG_IEEE80211W */
        size_t kde_len;
        u8 *gtk;
 
@@ -5149,7 +5358,6 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
                hdr[1] = 0;
                pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
                                  gtk, gsm->GTK_len);
-#ifdef CONFIG_IEEE80211W
                opos = pos;
                pos = ieee80211w_kde_add(sm, pos);
                if (pos - opos >=
@@ -5158,7 +5366,6 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
                        opos += 2 + RSN_SELECTOR_LEN + 2;
                        os_memset(opos, 0, 6); /* clear PN */
                }
-#endif /* CONFIG_IEEE80211W */
                if (ocv_oci_add(sm, &pos) < 0) {
                        os_free(kde_buf);
                        return -1;