]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - wpa_supplicant/sme.c
SAE: Special test mode sae_pwe=3 for looping with password identifier
[thirdparty/hostap.git] / wpa_supplicant / sme.c
index 068ded7d2b79f2f3dc9d2114222da580c1234be4..81151a7fb710a0b0a63f281ed954d58e3bbf4377 100644 (file)
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/ocv.h"
+#include "common/hw_features_common.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "common/wpa_common.h"
 #include "common/sae.h"
+#include "common/dpp.h"
 #include "rsn_supp/wpa.h"
 #include "rsn_supp/pmksa_cache.h"
 #include "config.h"
@@ -35,9 +38,7 @@
 static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx);
 static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx);
 static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx);
-#ifdef CONFIG_IEEE80211W
 static void sme_stop_sa_query(struct wpa_supplicant *wpa_s);
-#endif /* CONFIG_IEEE80211W */
 
 
 #ifdef CONFIG_SAE
@@ -56,7 +57,7 @@ static int index_within_array(const int *array, int idx)
 static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
 {
        int *groups = wpa_s->conf->sae_groups;
-       int default_groups[] = { 19, 20, 21, 25, 26, 0 };
+       int default_groups[] = { 19, 20, 21, 0 };
 
        if (!groups || groups[0] <= 0)
                groups = default_groups;
@@ -72,7 +73,7 @@ static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
                if (sae_set_group(&wpa_s->sme.sae, group) == 0) {
                        wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
                                wpa_s->sme.sae.group);
-                      return 0;
+                       return 0;
                }
                wpa_s->sme.sae_group_index++;
        }
@@ -83,43 +84,115 @@ static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
 
 static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
                                                 struct wpa_ssid *ssid,
-                                                const u8 *bssid)
+                                                const u8 *bssid, int external,
+                                                int reuse, int *ret_use_pt)
 {
        struct wpabuf *buf;
        size_t len;
+       const char *password;
+       struct wpa_bss *bss;
+       int use_pt = 0;
+
+       if (ret_use_pt)
+               *ret_use_pt = 0;
+
+#ifdef CONFIG_TESTING_OPTIONS
+       if (wpa_s->sae_commit_override) {
+               wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
+               buf = wpabuf_alloc(4 + wpabuf_len(wpa_s->sae_commit_override));
+               if (!buf)
+                       return NULL;
+               if (!external) {
+                       wpabuf_put_le16(buf, 1); /* Transaction seq# */
+                       wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+               }
+               wpabuf_put_buf(buf, wpa_s->sae_commit_override);
+               return buf;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
 
-       if (ssid->passphrase == NULL) {
+       password = ssid->sae_password;
+       if (!password)
+               password = ssid->passphrase;
+       if (!password) {
                wpa_printf(MSG_DEBUG, "SAE: No password available");
                return NULL;
        }
 
+       if (reuse && wpa_s->sme.sae.tmp &&
+           os_memcmp(bssid, wpa_s->sme.sae.tmp->bssid, ETH_ALEN) == 0) {
+               wpa_printf(MSG_DEBUG,
+                          "SAE: Reuse previously generated PWE on a retry with the same AP");
+               use_pt = wpa_s->sme.sae.tmp->h2e;
+               goto reuse_data;
+       }
        if (sme_set_sae_group(wpa_s) < 0) {
                wpa_printf(MSG_DEBUG, "SAE: Failed to select group");
                return NULL;
        }
 
-       if (sae_prepare_commit(wpa_s->own_addr, bssid,
-                              (u8 *) ssid->passphrase,
-                              os_strlen(ssid->passphrase),
+       if (ssid->sae_password_id && wpa_s->conf->sae_pwe != 3)
+               use_pt = 1;
+
+       if (use_pt || wpa_s->conf->sae_pwe == 1 || wpa_s->conf->sae_pwe == 2) {
+               bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+               if (bss) {
+                       const u8 *rsnxe;
+
+                       rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+                       if (rsnxe && rsnxe[1] >= 1)
+                               use_pt = !!(rsnxe[2] &
+                                           BIT(WLAN_RSNX_CAPAB_SAE_H2E));
+               }
+
+               if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id) &&
+                   wpa_s->conf->sae_pwe != 3 &&
+                   !use_pt) {
+                       wpa_printf(MSG_DEBUG,
+                                  "SAE: Cannot use H2E with the selected AP");
+                       return NULL;
+               }
+       }
+
+       if (use_pt &&
+           sae_prepare_commit_pt(&wpa_s->sme.sae, ssid->pt,
+                                 wpa_s->own_addr, bssid,
+                                 wpa_s->sme.sae_rejected_groups) < 0)
+               return NULL;
+       if (!use_pt &&
+           sae_prepare_commit(wpa_s->own_addr, bssid,
+                              (u8 *) password, os_strlen(password),
+                              ssid->sae_password_id,
                               &wpa_s->sme.sae) < 0) {
                wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
                return NULL;
        }
+       if (wpa_s->sme.sae.tmp)
+               os_memcpy(wpa_s->sme.sae.tmp->bssid, bssid, ETH_ALEN);
 
-       len = wpa_s->sme.sae_token ? wpabuf_len(wpa_s->sme.sae_token) : 0;
+reuse_data:
+       len = wpa_s->sme.sae_token ? 3 + wpabuf_len(wpa_s->sme.sae_token) : 0;
+       if (ssid->sae_password_id)
+               len += 4 + os_strlen(ssid->sae_password_id);
        buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len);
        if (buf == NULL)
                return NULL;
-
-       wpabuf_put_le16(buf, 1); /* Transaction seq# */
-       wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-       sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token);
+       if (!external) {
+               wpabuf_put_le16(buf, 1); /* Transaction seq# */
+               wpabuf_put_le16(buf, use_pt ? WLAN_STATUS_SAE_HASH_TO_ELEMENT :
+                               WLAN_STATUS_SUCCESS);
+       }
+       sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token,
+                        ssid->sae_password_id);
+       if (ret_use_pt)
+               *ret_use_pt = use_pt;
 
        return buf;
 }
 
 
-static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s)
+static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s,
+                                                 int external)
 {
        struct wpabuf *buf;
 
@@ -127,8 +200,10 @@ static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s)
        if (buf == NULL)
                return NULL;
 
-       wpabuf_put_le16(buf, 2); /* Transaction seq# */
-       wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+       if (!external) {
+               wpabuf_put_le16(buf, 2); /* Transaction seq# */
+               wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+       }
        sae_write_confirm(&wpa_s->sme.sae, buf);
 
        return buf;
@@ -161,9 +236,10 @@ static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s,
                return;
        }
 
-       if (!(wpa_s->drv_rrm_flags &
-             WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) ||
-           !(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) {
+       if (!((wpa_s->drv_rrm_flags &
+              WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) &&
+             (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) &&
+           !(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_RRM)) {
                wpa_printf(MSG_DEBUG,
                           "RRM: Insufficient RRM support in driver - do not use RRM");
                return;
@@ -186,6 +262,13 @@ static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s,
        if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)
                *pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT;
 
+       *pos |= WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
+               WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
+               WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
+
+       if (wpa_s->lci)
+               pos[1] |= WLAN_RRM_CAPS_LCI_MEASUREMENT;
+
        wpa_s->sme.assoc_req_ie_len += rrm_ie_len + 2;
        wpa_s->rrm.rrm_used = 1;
 }
@@ -200,16 +283,18 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 #ifdef CONFIG_IEEE80211R
        const u8 *ie;
 #endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
        const u8 *md = NULL;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R || CONFIG_FILS */
        int i, bssid_changed;
        struct wpabuf *resp = NULL;
        u8 ext_capab[18];
        int ext_capab_len;
        int skip_auth;
+       u8 *wpa_ie;
+       size_t wpa_ie_len;
 #ifdef CONFIG_MBO
-       const u8 *mbo;
+       const u8 *mbo_ie;
 #endif /* CONFIG_MBO */
 
        if (bss == NULL) {
@@ -268,6 +353,12 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                if (!rsn) {
                        wpa_dbg(wpa_s, MSG_DEBUG,
                                "SAE enabled, but target BSS does not advertise RSN");
+#ifdef CONFIG_DPP
+               } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 &&
+                          (ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
+                          (ied.key_mgmt & WPA_KEY_MGMT_DPP)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "Prefer DPP over SAE when both are enabled");
+#endif /* CONFIG_DPP */
                } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 &&
                           wpa_key_mgmt_sae(ied.key_mgmt)) {
                        wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
@@ -286,23 +377,24 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        }
        params.wep_tx_keyidx = ssid->wep_tx_keyidx;
 
-       bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
-       os_memset(wpa_s->bssid, 0, ETH_ALEN);
-       os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
-       if (bssid_changed)
-               wpas_notify_bssid_changed(wpa_s);
-
        if ((wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
             wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
            wpa_key_mgmt_wpa(ssid->key_mgmt)) {
                int try_opportunistic;
+               const u8 *cache_id = NULL;
+
                try_opportunistic = (ssid->proactive_key_caching < 0 ?
                                     wpa_s->conf->okc :
                                     ssid->proactive_key_caching) &&
                        (ssid->proto & WPA_PROTO_RSN);
+#ifdef CONFIG_FILS
+               if (wpa_key_mgmt_fils(ssid->key_mgmt))
+                       cache_id = wpa_bss_get_fils_cache_id(bss);
+#endif /* CONFIG_FILS */
                if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
                                            wpa_s->current_ssid,
-                                           try_opportunistic) == 0)
+                                           try_opportunistic, cache_id,
+                                           0) == 0)
                        eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
                wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
                if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
@@ -313,6 +405,20 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                        wpas_connect_work_done(wpa_s);
                        return;
                }
+#ifdef CONFIG_HS20
+       } else if (wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE) &&
+                  (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)) {
+               /* No PMKSA caching, but otherwise similar to RSN/WPA */
+               wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
+               if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
+                                             wpa_s->sme.assoc_req_ie,
+                                             &wpa_s->sme.assoc_req_ie_len)) {
+                       wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
+                               "key management and encryption suites");
+                       wpas_connect_work_done(wpa_s);
+                       return;
+               }
+#endif /* CONFIG_HS20 */
        } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
                   wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
                /*
@@ -352,17 +458,45 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                wpa_s->sme.assoc_req_ie_len = 0;
        }
 
+       /* In case the WPA vendor IE is used, it should be placed after all the
+        * non-vendor IEs, as the lower layer expects the IEs to be ordered as
+        * defined in the standard. Store the WPA IE so it can later be
+        * inserted at the correct location.
+        */
+       wpa_ie = NULL;
+       wpa_ie_len = 0;
+       if (wpa_s->wpa_proto == WPA_PROTO_WPA) {
+               wpa_ie = os_memdup(wpa_s->sme.assoc_req_ie,
+                                  wpa_s->sme.assoc_req_ie_len);
+               if (wpa_ie) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Storing WPA IE");
+
+                       wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
+                       wpa_s->sme.assoc_req_ie_len = 0;
+               } else {
+                       wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed copy WPA IE");
+                       wpas_connect_work_done(wpa_s);
+                       return;
+               }
+       }
+
 #ifdef CONFIG_IEEE80211R
        ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
        if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
                md = ie + 2;
        wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
+       if (md && (!wpa_key_mgmt_ft(ssid->key_mgmt) ||
+                  !wpa_key_mgmt_ft(wpa_s->key_mgmt)))
+               md = NULL;
        if (md) {
                /* Prepare for the next transition */
                wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
        }
 
-       if (md && wpa_key_mgmt_ft(ssid->key_mgmt)) {
+       if (md) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT mobility domain %02x%02x",
+                       md[0], md[1]);
+
                if (wpa_s->sme.assoc_req_ie_len + 5 <
                    sizeof(wpa_s->sme.assoc_req_ie)) {
                        struct rsn_mdie *mdie;
@@ -377,7 +511,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                        wpa_s->sme.assoc_req_ie_len += 5;
                }
 
-               if (wpa_s->sme.ft_used &&
+               if (wpa_s->sme.prev_bssid_set && wpa_s->sme.ft_used &&
                    os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 &&
                    wpa_sm_has_ptk(wpa_s->wpa)) {
                        wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT "
@@ -389,7 +523,6 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        }
 #endif /* CONFIG_IEEE80211R */
 
-#ifdef CONFIG_IEEE80211W
        wpa_s->sme.mfp = wpas_get_ssid_pmf(wpa_s, ssid);
        if (wpa_s->sme.mfp != NO_MGMT_FRAME_PROTECTION) {
                const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
@@ -402,7 +535,6 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                        wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED;
                }
        }
-#endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_P2P
        if (wpa_s->global->p2p) {
@@ -419,28 +551,6 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        }
 #endif /* CONFIG_P2P */
 
-#ifdef CONFIG_HS20
-       if (is_hs20_network(wpa_s, ssid, bss)) {
-               struct wpabuf *hs20;
-               hs20 = wpabuf_alloc(20);
-               if (hs20) {
-                       int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
-                       size_t len;
-
-                       wpas_hs20_add_indication(hs20, pps_mo_id);
-                       len = sizeof(wpa_s->sme.assoc_req_ie) -
-                               wpa_s->sme.assoc_req_ie_len;
-                       if (wpabuf_len(hs20) <= len) {
-                               os_memcpy(wpa_s->sme.assoc_req_ie +
-                                         wpa_s->sme.assoc_req_ie_len,
-                                         wpabuf_head(hs20), wpabuf_len(hs20));
-                               wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20);
-                       }
-                       wpabuf_free(hs20);
-               }
-       }
-#endif /* CONFIG_HS20 */
-
 #ifdef CONFIG_FST
        if (wpa_s->fst_ies) {
                int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
@@ -456,20 +566,17 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        }
 #endif /* CONFIG_FST */
 
-#ifdef CONFIG_MBO
-       mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
-       if (mbo) {
-               int len;
+       sme_auth_handle_rrm(wpa_s, bss);
 
-               len = wpas_mbo_supp_op_class_ie(
-                       wpa_s, bss->freq,
-                       wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
-                       sizeof(wpa_s->sme.assoc_req_ie) -
-                       wpa_s->sme.assoc_req_ie_len);
-               if (len > 0)
-                       wpa_s->sme.assoc_req_ie_len += len;
-       }
-#endif /* CONFIG_MBO */
+       wpa_s->sme.assoc_req_ie_len += wpas_supp_op_class_ie(
+               wpa_s, ssid, bss->freq,
+               wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+               sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len);
+
+       if (params.p2p)
+               wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
+       else
+               wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
 
        ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
                                             sizeof(ext_capab));
@@ -484,6 +591,71 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                os_memcpy(pos, ext_capab, ext_capab_len);
        }
 
+#ifdef CONFIG_TESTING_OPTIONS
+       if (wpa_s->rsnxe_override_assoc &&
+           wpabuf_len(wpa_s->rsnxe_override_assoc) <=
+           sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len) {
+               wpa_printf(MSG_DEBUG, "TESTING: RSNXE AssocReq override");
+               os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+                         wpabuf_head(wpa_s->rsnxe_override_assoc),
+                         wpabuf_len(wpa_s->rsnxe_override_assoc));
+               wpa_s->sme.assoc_req_ie_len +=
+                       wpabuf_len(wpa_s->rsnxe_override_assoc);
+       } else
+#endif /* CONFIG_TESTING_OPTIONS */
+       if (wpa_s->rsnxe_len > 0 &&
+           wpa_s->rsnxe_len <=
+           sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len) {
+               os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+                         wpa_s->rsnxe, wpa_s->rsnxe_len);
+               wpa_s->sme.assoc_req_ie_len += wpa_s->rsnxe_len;
+       }
+
+#ifdef CONFIG_HS20
+       if (is_hs20_network(wpa_s, ssid, bss)) {
+               struct wpabuf *hs20;
+
+               hs20 = wpabuf_alloc(20 + MAX_ROAMING_CONS_OI_LEN);
+               if (hs20) {
+                       int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+                       size_t len;
+
+                       wpas_hs20_add_indication(hs20, pps_mo_id,
+                                                get_hs20_version(bss));
+                       wpas_hs20_add_roam_cons_sel(hs20, ssid);
+                       len = sizeof(wpa_s->sme.assoc_req_ie) -
+                               wpa_s->sme.assoc_req_ie_len;
+                       if (wpabuf_len(hs20) <= len) {
+                               os_memcpy(wpa_s->sme.assoc_req_ie +
+                                         wpa_s->sme.assoc_req_ie_len,
+                                         wpabuf_head(hs20), wpabuf_len(hs20));
+                               wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20);
+                       }
+                       wpabuf_free(hs20);
+               }
+       }
+#endif /* CONFIG_HS20 */
+
+       if (wpa_ie) {
+               size_t len;
+
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Reinsert WPA IE");
+
+               len = sizeof(wpa_s->sme.assoc_req_ie) -
+                       wpa_s->sme.assoc_req_ie_len;
+
+               if (len > wpa_ie_len) {
+                       os_memcpy(wpa_s->sme.assoc_req_ie +
+                                 wpa_s->sme.assoc_req_ie_len,
+                                 wpa_ie, wpa_ie_len);
+                       wpa_s->sme.assoc_req_ie_len += wpa_ie_len;
+               } else {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Failed to add WPA IE");
+               }
+
+               os_free(wpa_ie);
+       }
+
        if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
                struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
                size_t len;
@@ -498,16 +670,17 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                }
        }
 
-       sme_auth_handle_rrm(wpa_s, bss);
-
 #ifdef CONFIG_MBO
-       if (mbo) {
+       mbo_ie = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
+       if (!wpa_s->disable_mbo_oce && mbo_ie) {
                int len;
 
                len = wpas_mbo_ie(wpa_s, wpa_s->sme.assoc_req_ie +
                                  wpa_s->sme.assoc_req_ie_len,
                                  sizeof(wpa_s->sme.assoc_req_ie) -
-                                 wpa_s->sme.assoc_req_ie_len);
+                                 wpa_s->sme.assoc_req_ie_len,
+                                 !!mbo_attr_from_mbo_ie(mbo_ie,
+                                                        OCE_ATTR_ID_CAPA_IND));
                if (len >= 0)
                        wpa_s->sme.assoc_req_ie_len += len;
        }
@@ -515,10 +688,14 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_SAE
        if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE &&
-           pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0) == 0)
-       {
+           pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0,
+                                   NULL,
+                                   wpa_s->key_mgmt == WPA_KEY_MGMT_FT_SAE ?
+                                   WPA_KEY_MGMT_FT_SAE :
+                                   WPA_KEY_MGMT_SAE) == 0) {
                wpa_dbg(wpa_s, MSG_DEBUG,
                        "PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication");
+               wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
                params.auth_alg = WPA_AUTH_ALG_OPEN;
                wpa_s->sme.sae_pmksa_caching = 1;
        }
@@ -526,19 +703,112 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) {
                if (start)
                        resp = sme_auth_build_sae_commit(wpa_s, ssid,
-                                                        bss->bssid);
+                                                        bss->bssid, 0,
+                                                        start == 2, NULL);
                else
-                       resp = sme_auth_build_sae_confirm(wpa_s);
+                       resp = sme_auth_build_sae_confirm(wpa_s, 0);
                if (resp == NULL) {
                        wpas_connection_failed(wpa_s, bss->bssid);
                        return;
                }
-               params.sae_data = wpabuf_head(resp);
-               params.sae_data_len = wpabuf_len(resp);
+               params.auth_data = wpabuf_head(resp);
+               params.auth_data_len = wpabuf_len(resp);
                wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED;
        }
 #endif /* CONFIG_SAE */
 
+       bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
+       os_memset(wpa_s->bssid, 0, ETH_ALEN);
+       os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
+       if (bssid_changed)
+               wpas_notify_bssid_changed(wpa_s);
+
+       old_ssid = wpa_s->current_ssid;
+       wpa_s->current_ssid = ssid;
+       wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+       wpa_supplicant_initiate_eapol(wpa_s);
+
+#ifdef CONFIG_FILS
+       /* TODO: FILS operations can in some cases be done between different
+        * network_ctx (i.e., same credentials can be used with multiple
+        * networks). */
+       if (params.auth_alg == WPA_AUTH_ALG_OPEN &&
+           wpa_key_mgmt_fils(ssid->key_mgmt)) {
+               const u8 *indic;
+               u16 fils_info;
+               const u8 *realm, *username, *rrk;
+               size_t realm_len, username_len, rrk_len;
+               u16 next_seq_num;
+
+               /*
+                * Check FILS Indication element (FILS Information field) bits
+                * indicating supported authentication algorithms against local
+                * configuration (ssid->fils_dh_group). Try to use FILS
+                * authentication only if the AP supports the combination in the
+                * network profile. */
+               indic = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
+               if (!indic || indic[1] < 2) {
+                       wpa_printf(MSG_DEBUG, "SME: " MACSTR
+                                  " does not include FILS Indication element - cannot use FILS authentication with it",
+                                  MAC2STR(bss->bssid));
+                       goto no_fils;
+               }
+
+               fils_info = WPA_GET_LE16(indic + 2);
+               if (ssid->fils_dh_group == 0 && !(fils_info & BIT(9))) {
+                       wpa_printf(MSG_DEBUG, "SME: " MACSTR
+                                  " does not support FILS SK without PFS - cannot use FILS authentication with it",
+                                  MAC2STR(bss->bssid));
+                       goto no_fils;
+               }
+               if (ssid->fils_dh_group != 0 && !(fils_info & BIT(10))) {
+                       wpa_printf(MSG_DEBUG, "SME: " MACSTR
+                                  " does not support FILS SK with PFS - cannot use FILS authentication with it",
+                                  MAC2STR(bss->bssid));
+                       goto no_fils;
+               }
+
+               if (wpa_s->last_con_fail_realm &&
+                   eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap,
+                                         &username, &username_len,
+                                         &realm, &realm_len, &next_seq_num,
+                                         &rrk, &rrk_len) == 0 &&
+                   realm && realm_len == wpa_s->last_con_fail_realm_len &&
+                   os_memcmp(realm, wpa_s->last_con_fail_realm,
+                             realm_len) == 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "SME: FILS authentication for this realm failed last time - try to regenerate ERP key hierarchy");
+                       goto no_fils;
+               }
+
+               if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+                                           ssid, 0,
+                                           wpa_bss_get_fils_cache_id(bss),
+                                           0) == 0)
+                       wpa_printf(MSG_DEBUG,
+                                  "SME: Try to use FILS with PMKSA caching");
+               resp = fils_build_auth(wpa_s->wpa, ssid->fils_dh_group, md);
+               if (resp) {
+                       int auth_alg;
+
+                       if (ssid->fils_dh_group)
+                               wpa_printf(MSG_DEBUG,
+                                          "SME: Try to use FILS SK authentication with PFS (DH Group %u)",
+                                          ssid->fils_dh_group);
+                       else
+                               wpa_printf(MSG_DEBUG,
+                                          "SME: Try to use FILS SK authentication without PFS");
+                       auth_alg = ssid->fils_dh_group ?
+                               WPA_AUTH_ALG_FILS_SK_PFS : WPA_AUTH_ALG_FILS;
+                       params.auth_alg = auth_alg;
+                       params.auth_data = wpabuf_head(resp);
+                       params.auth_data_len = wpabuf_len(resp);
+                       wpa_s->sme.auth_alg = auth_alg;
+               }
+       }
+no_fils:
+#endif /* CONFIG_FILS */
+
        wpa_supplicant_cancel_sched_scan(wpa_s);
        wpa_supplicant_cancel_scan(wpa_s);
 
@@ -546,15 +816,16 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
                wpa_ssid_txt(params.ssid, params.ssid_len), params.freq);
 
+       eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
        wpa_clear_keys(wpa_s, bss->bssid);
        wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
-       old_ssid = wpa_s->current_ssid;
-       wpa_s->current_ssid = ssid;
-       wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
-       wpa_supplicant_initiate_eapol(wpa_s);
        if (old_ssid != wpa_s->current_ssid)
                wpas_notify_network_changed(wpa_s);
 
+#ifdef CONFIG_HS20
+       hs20_configure_frame_filters(wpa_s);
+#endif /* CONFIG_HS20 */
+
 #ifdef CONFIG_P2P
        /*
         * If multi-channel concurrency is not supported, check for any
@@ -636,6 +907,12 @@ static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit)
                return;
        }
 
+       /* Starting new connection, so clear the possibly used WPA IE from the
+        * previous association. */
+       wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+       wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
+       wpa_s->rsnxe_len = 0;
+
        sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
 }
 
@@ -686,8 +963,213 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_SAE
 
+static int sme_external_auth_build_buf(struct wpabuf *buf,
+                                      struct wpabuf *params,
+                                      const u8 *sa, const u8 *da,
+                                      u16 auth_transaction, u16 seq_num,
+                                      u16 status_code)
+{
+       struct ieee80211_mgmt *resp;
+
+       resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
+                                       u.auth.variable));
+
+       resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+                                          (WLAN_FC_STYPE_AUTH << 4));
+       os_memcpy(resp->da, da, ETH_ALEN);
+       os_memcpy(resp->sa, sa, ETH_ALEN);
+       os_memcpy(resp->bssid, da, ETH_ALEN);
+       resp->u.auth.auth_alg = host_to_le16(WLAN_AUTH_SAE);
+       resp->seq_ctrl = host_to_le16(seq_num << 4);
+       resp->u.auth.auth_transaction = host_to_le16(auth_transaction);
+       resp->u.auth.status_code = host_to_le16(status_code);
+       if (params)
+               wpabuf_put_buf(buf, params);
+
+       return 0;
+}
+
+
+static int sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
+                                            const u8 *bssid,
+                                            struct wpa_ssid *ssid)
+{
+       struct wpabuf *resp, *buf;
+       int use_pt;
+
+       resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0, &use_pt);
+       if (!resp) {
+               wpa_printf(MSG_DEBUG, "SAE: Failed to build SAE commit");
+               return -1;
+       }
+
+       wpa_s->sme.sae.state = SAE_COMMITTED;
+       buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + wpabuf_len(resp));
+       if (!buf) {
+               wpabuf_free(resp);
+               return -1;
+       }
+
+       wpa_s->sme.seq_num++;
+       sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
+                                   bssid, 1, wpa_s->sme.seq_num,
+                                   use_pt ? WLAN_STATUS_SAE_HASH_TO_ELEMENT :
+                                   WLAN_STATUS_SUCCESS);
+       wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0);
+       wpabuf_free(resp);
+       wpabuf_free(buf);
+
+       return 0;
+}
+
+
+static void sme_send_external_auth_status(struct wpa_supplicant *wpa_s,
+                                         u16 status)
+{
+       struct external_auth params;
+
+       os_memset(&params, 0, sizeof(params));
+       params.status = status;
+       params.ssid = wpa_s->sme.ext_auth_ssid;
+       params.ssid_len = wpa_s->sme.ext_auth_ssid_len;
+       params.bssid = wpa_s->sme.ext_auth_bssid;
+       if (wpa_s->conf->sae_pmkid_in_assoc && status == WLAN_STATUS_SUCCESS)
+               params.pmkid = wpa_s->sme.sae.pmkid;
+       wpa_drv_send_external_auth_status(wpa_s, &params);
+}
+
+
+static int sme_handle_external_auth_start(struct wpa_supplicant *wpa_s,
+                                         union wpa_event_data *data)
+{
+       struct wpa_ssid *ssid;
+       size_t ssid_str_len = data->external_auth.ssid_len;
+       const u8 *ssid_str = data->external_auth.ssid;
+
+       /* Get the SSID conf from the ssid string obtained */
+       for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+               if (!wpas_network_disabled(wpa_s, ssid) &&
+                   ssid_str_len == ssid->ssid_len &&
+                   os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0 &&
+                   (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)))
+                       break;
+       }
+       if (!ssid ||
+           sme_external_auth_send_sae_commit(wpa_s, data->external_auth.bssid,
+                                             ssid) < 0)
+               return -1;
+
+       return 0;
+}
+
+
+static void sme_external_auth_send_sae_confirm(struct wpa_supplicant *wpa_s,
+                                              const u8 *da)
+{
+       struct wpabuf *resp, *buf;
+
+       resp = sme_auth_build_sae_confirm(wpa_s, 1);
+       if (!resp) {
+               wpa_printf(MSG_DEBUG, "SAE: Confirm message buf alloc failure");
+               return;
+       }
+
+       wpa_s->sme.sae.state = SAE_CONFIRMED;
+       buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN + wpabuf_len(resp));
+       if (!buf) {
+               wpa_printf(MSG_DEBUG, "SAE: Auth Confirm buf alloc failure");
+               wpabuf_free(resp);
+               return;
+       }
+       wpa_s->sme.seq_num++;
+       sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
+                                   da, 2, wpa_s->sme.seq_num,
+                                   WLAN_STATUS_SUCCESS);
+       wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0);
+       wpabuf_free(resp);
+       wpabuf_free(buf);
+}
+
+
+void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
+                              union wpa_event_data *data)
+{
+       if (RSN_SELECTOR_GET(&data->external_auth.key_mgmt_suite) !=
+           RSN_AUTH_KEY_MGMT_SAE)
+               return;
+
+       if (data->external_auth.action == EXT_AUTH_START) {
+               if (!data->external_auth.bssid || !data->external_auth.ssid)
+                       return;
+               os_memcpy(wpa_s->sme.ext_auth_bssid, data->external_auth.bssid,
+                         ETH_ALEN);
+               os_memcpy(wpa_s->sme.ext_auth_ssid, data->external_auth.ssid,
+                         data->external_auth.ssid_len);
+               wpa_s->sme.ext_auth_ssid_len = data->external_auth.ssid_len;
+               wpa_s->sme.seq_num = 0;
+               wpa_s->sme.sae.state = SAE_NOTHING;
+               wpa_s->sme.sae.send_confirm = 0;
+               wpa_s->sme.sae_group_index = 0;
+               if (sme_handle_external_auth_start(wpa_s, data) < 0)
+                       sme_send_external_auth_status(wpa_s,
+                                             WLAN_STATUS_UNSPECIFIED_FAILURE);
+       } else if (data->external_auth.action == EXT_AUTH_ABORT) {
+               /* Report failure to driver for the wrong trigger */
+               sme_send_external_auth_status(wpa_s,
+                                             WLAN_STATUS_UNSPECIFIED_FAILURE);
+       }
+}
+
+
+static int sme_sae_is_group_enabled(struct wpa_supplicant *wpa_s, int group)
+{
+       int *groups = wpa_s->conf->sae_groups;
+       int default_groups[] = { 19, 20, 21, 0 };
+       int i;
+
+       if (!groups)
+               groups = default_groups;
+
+       for (i = 0; groups[i] > 0; i++) {
+               if (groups[i] == group)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+static int sme_check_sae_rejected_groups(struct wpa_supplicant *wpa_s,
+                                        const struct wpabuf *groups)
+{
+       size_t i, count;
+       const u8 *pos;
+
+       if (!groups)
+               return 0;
+
+       pos = wpabuf_head(groups);
+       count = wpabuf_len(groups) / 2;
+       for (i = 0; i < count; i++) {
+               int enabled;
+               u16 group;
+
+               group = WPA_GET_LE16(pos);
+               pos += 2;
+               enabled = sme_sae_is_group_enabled(wpa_s, group);
+               wpa_printf(MSG_DEBUG, "SAE: Rejected group %u is %s",
+                          group, enabled ? "enabled" : "disabled");
+               if (enabled)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
 static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
-                       u16 status_code, const u8 *data, size_t len)
+                       u16 status_code, const u8 *data, size_t len,
+                       int external, const u8 *sa)
 {
        int *groups;
 
@@ -697,14 +1179,19 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
        if (auth_transaction == 1 &&
            status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
            wpa_s->sme.sae.state == SAE_COMMITTED &&
-           wpa_s->current_bss && wpa_s->current_ssid) {
-               int default_groups[] = { 19, 20, 21, 25, 26, 0 };
+           (external || wpa_s->current_bss) && wpa_s->current_ssid) {
+               int default_groups[] = { 19, 20, 21, 0 };
                u16 group;
+               const u8 *token_pos;
+               size_t token_len;
+               int h2e = 0;
 
                groups = wpa_s->conf->sae_groups;
                if (!groups || groups[0] <= 0)
                        groups = default_groups;
 
+               wpa_hexdump(MSG_DEBUG, "SME: SAE anti-clogging token request",
+                           data, len);
                if (len < sizeof(le16)) {
                        wpa_dbg(wpa_s, MSG_DEBUG,
                                "SME: Too short SAE anti-clogging token request");
@@ -722,28 +1209,73 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                        return -1;
                }
                wpabuf_free(wpa_s->sme.sae_token);
-               wpa_s->sme.sae_token = wpabuf_alloc_copy(data + sizeof(le16),
-                                                        len - sizeof(le16));
-               sme_send_authentication(wpa_s, wpa_s->current_bss,
-                                       wpa_s->current_ssid, 1);
+               token_pos = data + sizeof(le16);
+               token_len = len - sizeof(le16);
+               if (wpa_s->sme.sae.tmp)
+                       h2e = wpa_s->sme.sae.tmp->h2e;
+               if (h2e) {
+                       if (token_len < 3) {
+                               wpa_dbg(wpa_s, MSG_DEBUG,
+                                       "SME: Too short SAE anti-clogging token container");
+                               return -1;
+                       }
+                       if (token_pos[0] != WLAN_EID_EXTENSION ||
+                           token_pos[1] == 0 ||
+                           token_pos[1] > token_len - 2 ||
+                           token_pos[2] != WLAN_EID_EXT_ANTI_CLOGGING_TOKEN) {
+                               wpa_dbg(wpa_s, MSG_DEBUG,
+                                       "SME: Invalid SAE anti-clogging token container header");
+                               return -1;
+                       }
+                       token_len = token_pos[1] - 1;
+                       token_pos += 3;
+               }
+               wpa_s->sme.sae_token = wpabuf_alloc_copy(token_pos, token_len);
+               wpa_hexdump_buf(MSG_DEBUG, "SME: Requested anti-clogging token",
+                               wpa_s->sme.sae_token);
+               if (!external)
+                       sme_send_authentication(wpa_s, wpa_s->current_bss,
+                                               wpa_s->current_ssid, 2);
+               else
+                       sme_external_auth_send_sae_commit(
+                               wpa_s, wpa_s->sme.ext_auth_bssid,
+                               wpa_s->current_ssid);
                return 0;
        }
 
        if (auth_transaction == 1 &&
            status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
            wpa_s->sme.sae.state == SAE_COMMITTED &&
-           wpa_s->current_bss && wpa_s->current_ssid) {
+           (external || wpa_s->current_bss) && wpa_s->current_ssid) {
                wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported");
+               int_array_add_unique(&wpa_s->sme.sae_rejected_groups,
+                                    wpa_s->sme.sae.group);
                wpa_s->sme.sae_group_index++;
                if (sme_set_sae_group(wpa_s) < 0)
                        return -1; /* no other groups enabled */
                wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group");
-               sme_send_authentication(wpa_s, wpa_s->current_bss,
-                                       wpa_s->current_ssid, 1);
+               if (!external)
+                       sme_send_authentication(wpa_s, wpa_s->current_bss,
+                                               wpa_s->current_ssid, 1);
+               else
+                       sme_external_auth_send_sae_commit(
+                               wpa_s, wpa_s->sme.ext_auth_bssid,
+                               wpa_s->current_ssid);
                return 0;
        }
 
-       if (status_code != WLAN_STATUS_SUCCESS)
+       if (auth_transaction == 1 &&
+           status_code == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
+               const u8 *bssid = sa ? sa : wpa_s->pending_bssid;
+
+               wpa_msg(wpa_s, MSG_INFO,
+                       WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER MACSTR,
+                       MAC2STR(bssid));
+               return -1;
+       }
+
+       if (status_code != WLAN_STATUS_SUCCESS &&
+           status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT)
                return -1;
 
        if (auth_transaction == 1) {
@@ -752,15 +1284,32 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                groups = wpa_s->conf->sae_groups;
 
                wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit");
-               if (wpa_s->current_bss == NULL ||
+               if ((!external && wpa_s->current_bss == NULL) ||
                    wpa_s->current_ssid == NULL)
                        return -1;
-               if (wpa_s->sme.sae.state != SAE_COMMITTED)
+               if (wpa_s->sme.sae.state != SAE_COMMITTED) {
+                       wpa_printf(MSG_DEBUG,
+                                  "SAE: Ignore commit message while waiting for confirm");
+                       return 0;
+               }
+               if (wpa_s->sme.sae.tmp && wpa_s->sme.sae.tmp->h2e &&
+                   status_code == WLAN_STATUS_SUCCESS) {
+                       wpa_printf(MSG_DEBUG,
+                                  "SAE: Unexpected use of status code 0 in SAE commit when H2E was expected");
                        return -1;
+               }
+               if (wpa_s->sme.sae.tmp && !wpa_s->sme.sae.tmp->h2e &&
+                   status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
+                       wpa_printf(MSG_DEBUG,
+                                  "SAE: Unexpected use of status code for H2E in SAE commit when H2E was not expected");
+                       return -1;
+               }
+
                if (groups && groups[0] <= 0)
                        groups = NULL;
                res = sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
-                                      groups);
+                                      groups, status_code ==
+                                      WLAN_STATUS_SAE_HASH_TO_ELEMENT);
                if (res == SAE_SILENTLY_DISCARD) {
                        wpa_printf(MSG_DEBUG,
                                   "SAE: Drop commit message due to reflection attack");
@@ -769,6 +1318,12 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                if (res != WLAN_STATUS_SUCCESS)
                        return -1;
 
+               if (wpa_s->sme.sae.tmp &&
+                   sme_check_sae_rejected_groups(
+                           wpa_s,
+                           wpa_s->sme.sae.tmp->peer_rejected_groups))
+                       return -1;
+
                if (sae_process_commit(&wpa_s->sme.sae) < 0) {
                        wpa_printf(MSG_DEBUG, "SAE: Failed to process peer "
                                   "commit");
@@ -777,10 +1332,15 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
 
                wpabuf_free(wpa_s->sme.sae_token);
                wpa_s->sme.sae_token = NULL;
-               sme_send_authentication(wpa_s, wpa_s->current_bss,
-                                       wpa_s->current_ssid, 0);
+               if (!external)
+                       sme_send_authentication(wpa_s, wpa_s->current_bss,
+                                               wpa_s->current_ssid, 0);
+               else
+                       sme_external_auth_send_sae_confirm(wpa_s, sa);
                return 0;
        } else if (auth_transaction == 2) {
+               if (status_code != WLAN_STATUS_SUCCESS)
+                       return -1;
                wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
                if (wpa_s->sme.sae.state != SAE_CONFIRMED)
                        return -1;
@@ -788,11 +1348,89 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                        return -1;
                wpa_s->sme.sae.state = SAE_ACCEPTED;
                sae_clear_temp_data(&wpa_s->sme.sae);
+
+               if (external) {
+                       /* Report success to driver */
+                       sme_send_external_auth_status(wpa_s,
+                                                     WLAN_STATUS_SUCCESS);
+               }
+
                return 1;
        }
 
        return -1;
 }
+
+
+static int sme_sae_set_pmk(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+       wpa_printf(MSG_DEBUG,
+                  "SME: SAE completed - setting PMK for 4-way handshake");
+       wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
+                      wpa_s->sme.sae.pmkid, bssid);
+       if (wpa_s->conf->sae_pmkid_in_assoc) {
+               /* Update the own RSNE contents now that we have set the PMK
+                * and added a PMKSA cache entry based on the successfully
+                * completed SAE exchange. In practice, this will add the PMKID
+                * into RSNE. */
+               if (wpa_s->sme.assoc_req_ie_len + 2 + PMKID_LEN >
+                   sizeof(wpa_s->sme.assoc_req_ie)) {
+                       wpa_msg(wpa_s, MSG_WARNING,
+                               "RSN: Not enough room for inserting own PMKID into RSNE");
+                       return -1;
+               }
+               if (wpa_insert_pmkid(wpa_s->sme.assoc_req_ie,
+                                    &wpa_s->sme.assoc_req_ie_len,
+                                    wpa_s->sme.sae.pmkid) < 0)
+                       return -1;
+               wpa_hexdump(MSG_DEBUG,
+                           "SME: Updated Association Request IEs",
+                           wpa_s->sme.assoc_req_ie,
+                           wpa_s->sme.assoc_req_ie_len);
+       }
+
+       return 0;
+}
+
+
+void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
+                              const u8 *auth_frame, size_t len)
+{
+       const struct ieee80211_mgmt *header;
+       size_t auth_length;
+
+       header = (const struct ieee80211_mgmt *) auth_frame;
+       auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth);
+
+       if (len < auth_length) {
+               /* Notify failure to the driver */
+               sme_send_external_auth_status(wpa_s,
+                                             WLAN_STATUS_UNSPECIFIED_FAILURE);
+               return;
+       }
+
+       if (le_to_host16(header->u.auth.auth_alg) == WLAN_AUTH_SAE) {
+               int res;
+
+               res = sme_sae_auth(
+                       wpa_s, le_to_host16(header->u.auth.auth_transaction),
+                       le_to_host16(header->u.auth.status_code),
+                       header->u.auth.variable,
+                       len - auth_length, 1, header->sa);
+               if (res < 0) {
+                       /* Notify failure to the driver */
+                       sme_send_external_auth_status(
+                               wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE);
+                       return;
+               }
+               if (res != 1)
+                       return;
+
+               if (sme_sae_set_pmk(wpa_s, wpa_s->sme.ext_auth_bssid) < 0)
+                       return;
+       }
+}
+
 #endif /* CONFIG_SAE */
 
 
@@ -833,7 +1471,7 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
                int res;
                res = sme_sae_auth(wpa_s, data->auth.auth_transaction,
                                   data->auth.status_code, data->auth.ies,
-                                  data->auth.ies_len);
+                                  data->auth.ies_len, 0, NULL);
                if (res < 0) {
                        wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
                        wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
@@ -842,10 +1480,8 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
                if (res != 1)
                        return;
 
-               wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for "
-                          "4-way handshake");
-               wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
-                              wpa_s->sme.sae.pmkid, wpa_s->pending_bssid);
+               if (sme_sae_set_pmk(wpa_s, wpa_s->pending_bssid) < 0)
+                       return;
        }
 #endif /* CONFIG_SAE */
 
@@ -861,12 +1497,19 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
                        }
                }
                wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AUTH_REJECT MACSTR
-                       " auth_type=%u auth_transaction=%u status_code=%u ie=%s",
+                       " auth_type=%u auth_transaction=%u status_code=%u%s%s",
                        MAC2STR(data->auth.peer), data->auth.auth_type,
                        data->auth.auth_transaction, data->auth.status_code,
-                       ie_txt);
+                       ie_txt ? " ie=" : "",
+                       ie_txt ? ie_txt : "");
                os_free(ie_txt);
 
+#ifdef CONFIG_FILS
+               if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS ||
+                   wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS_SK_PFS)
+                       fils_connection_failure(wpa_s);
+#endif /* CONFIG_FILS */
+
                if (data->auth.status_code !=
                    WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG ||
                    wpa_s->sme.auth_alg == data->auth.auth_type ||
@@ -902,9 +1545,17 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
 
 #ifdef CONFIG_IEEE80211R
        if (data->auth.auth_type == WLAN_AUTH_FT) {
+               const u8 *ric_ies = NULL;
+               size_t ric_ies_len = 0;
+
+               if (wpa_s->ric_ies) {
+                       ric_ies = wpabuf_head(wpa_s->ric_ies);
+                       ric_ies_len = wpabuf_len(wpa_s->ric_ies);
+               }
                if (wpa_ft_process_response(wpa_s->wpa, data->auth.ies,
                                            data->auth.ies_len, 0,
-                                           data->auth.peer, NULL, 0) < 0) {
+                                           data->auth.peer,
+                                           ric_ies, ric_ies_len) < 0) {
                        wpa_dbg(wpa_s, MSG_DEBUG,
                                "SME: FT Authentication response processing failed");
                        wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
@@ -919,16 +1570,73 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
        }
 #endif /* CONFIG_IEEE80211R */
 
+#ifdef CONFIG_FILS
+       if (data->auth.auth_type == WLAN_AUTH_FILS_SK ||
+           data->auth.auth_type == WLAN_AUTH_FILS_SK_PFS) {
+               u16 expect_auth_type;
+
+               expect_auth_type = wpa_s->sme.auth_alg ==
+                       WPA_AUTH_ALG_FILS_SK_PFS ? WLAN_AUTH_FILS_SK_PFS :
+                       WLAN_AUTH_FILS_SK;
+               if (data->auth.auth_type != expect_auth_type) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "SME: FILS Authentication response used different auth alg (%u; expected %u)",
+                               data->auth.auth_type, expect_auth_type);
+                       wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
+                               MACSTR
+                               " reason=%d locally_generated=1",
+                               MAC2STR(wpa_s->pending_bssid),
+                               WLAN_REASON_DEAUTH_LEAVING);
+                       wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+                       wpa_supplicant_mark_disassoc(wpa_s);
+                       return;
+               }
+
+               if (fils_process_auth(wpa_s->wpa, wpa_s->pending_bssid,
+                                     data->auth.ies, data->auth.ies_len) < 0) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "SME: FILS Authentication response processing failed");
+                       wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
+                               MACSTR
+                               " reason=%d locally_generated=1",
+                               MAC2STR(wpa_s->pending_bssid),
+                               WLAN_REASON_DEAUTH_LEAVING);
+                       wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+                       wpa_supplicant_mark_disassoc(wpa_s);
+                       return;
+               }
+       }
+#endif /* CONFIG_FILS */
+
        sme_associate(wpa_s, ssid->mode, data->auth.peer,
                      data->auth.auth_type);
 }
 
 
+#ifdef CONFIG_IEEE80211R
+static void remove_ie(u8 *buf, size_t *len, u8 eid)
+{
+       u8 *pos, *next, *end;
+
+       pos = (u8 *) get_ie(buf, *len, eid);
+       if (pos) {
+               next = pos + 2 + pos[1];
+               end = buf + *len;
+               *len -= 2 + pos[1];
+               os_memmove(pos, next, end - next);
+       }
+}
+#endif /* CONFIG_IEEE80211R */
+
+
 void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
                   const u8 *bssid, u16 auth_type)
 {
        struct wpa_driver_associate_params params;
        struct ieee802_11_elems elems;
+#ifdef CONFIG_FILS
+       u8 nonces[2 * FILS_NONCE_LEN];
+#endif /* CONFIG_FILS */
 #ifdef CONFIG_HT_OVERRIDES
        struct ieee80211_ht_capabilities htcaps;
        struct ieee80211_ht_capabilities htcaps_mask;
@@ -939,6 +1647,184 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
 #endif /* CONFIG_VHT_OVERRIDES */
 
        os_memset(&params, 0, sizeof(params));
+
+#ifdef CONFIG_FILS
+       if (auth_type == WLAN_AUTH_FILS_SK ||
+           auth_type == WLAN_AUTH_FILS_SK_PFS) {
+               struct wpabuf *buf;
+               const u8 *snonce, *anonce;
+               const unsigned int max_hlp = 20;
+               struct wpabuf *hlp[max_hlp];
+               unsigned int i, num_hlp = 0;
+               struct fils_hlp_req *req;
+
+               dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req,
+                                list) {
+                       hlp[num_hlp] = wpabuf_alloc(2 * ETH_ALEN + 6 +
+                                             wpabuf_len(req->pkt));
+                       if (!hlp[num_hlp])
+                               break;
+                       wpabuf_put_data(hlp[num_hlp], req->dst, ETH_ALEN);
+                       wpabuf_put_data(hlp[num_hlp], wpa_s->own_addr,
+                                       ETH_ALEN);
+                       wpabuf_put_data(hlp[num_hlp],
+                                       "\xaa\xaa\x03\x00\x00\x00", 6);
+                       wpabuf_put_buf(hlp[num_hlp], req->pkt);
+                       num_hlp++;
+                       if (num_hlp >= max_hlp)
+                               break;
+               }
+
+               buf = fils_build_assoc_req(wpa_s->wpa, &params.fils_kek,
+                                          &params.fils_kek_len, &snonce,
+                                          &anonce,
+                                          (const struct wpabuf **) hlp,
+                                          num_hlp);
+               for (i = 0; i < num_hlp; i++)
+                       wpabuf_free(hlp[i]);
+               if (!buf)
+                       return;
+               wpa_hexdump(MSG_DEBUG, "FILS: assoc_req before FILS elements",
+                           wpa_s->sme.assoc_req_ie,
+                           wpa_s->sme.assoc_req_ie_len);
+#ifdef CONFIG_IEEE80211R
+               if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) {
+                       /* Remove RSNE and MDE to allow them to be overridden
+                        * with FILS+FT specific values from
+                        * fils_build_assoc_req(). */
+                       remove_ie(wpa_s->sme.assoc_req_ie,
+                                 &wpa_s->sme.assoc_req_ie_len,
+                                 WLAN_EID_RSN);
+                       wpa_hexdump(MSG_DEBUG,
+                                   "FILS: assoc_req after RSNE removal",
+                                   wpa_s->sme.assoc_req_ie,
+                                   wpa_s->sme.assoc_req_ie_len);
+                       remove_ie(wpa_s->sme.assoc_req_ie,
+                                 &wpa_s->sme.assoc_req_ie_len,
+                                 WLAN_EID_MOBILITY_DOMAIN);
+                       wpa_hexdump(MSG_DEBUG,
+                                   "FILS: assoc_req after MDE removal",
+                                   wpa_s->sme.assoc_req_ie,
+                                   wpa_s->sme.assoc_req_ie_len);
+               }
+#endif /* CONFIG_IEEE80211R */
+               /* TODO: Make wpa_s->sme.assoc_req_ie use dynamic allocation */
+               if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(buf) >
+                   sizeof(wpa_s->sme.assoc_req_ie)) {
+                       wpa_printf(MSG_ERROR,
+                                  "FILS: Not enough buffer room for own AssocReq elements");
+                       wpabuf_free(buf);
+                       return;
+               }
+               os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+                         wpabuf_head(buf), wpabuf_len(buf));
+               wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf);
+               wpabuf_free(buf);
+               wpa_hexdump(MSG_DEBUG, "FILS: assoc_req after FILS elements",
+                           wpa_s->sme.assoc_req_ie,
+                           wpa_s->sme.assoc_req_ie_len);
+
+               os_memcpy(nonces, snonce, FILS_NONCE_LEN);
+               os_memcpy(nonces + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN);
+               params.fils_nonces = nonces;
+               params.fils_nonces_len = sizeof(nonces);
+       }
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+#ifdef CONFIG_TESTING_OPTIONS
+       if (get_ie_ext(wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len,
+                      WLAN_EID_EXT_OWE_DH_PARAM)) {
+               wpa_printf(MSG_INFO, "TESTING: Override OWE DH element");
+       } else
+#endif /* CONFIG_TESTING_OPTIONS */
+       if (auth_type == WLAN_AUTH_OPEN &&
+           wpa_s->key_mgmt == WPA_KEY_MGMT_OWE) {
+               struct wpabuf *owe_ie;
+               u16 group;
+
+               if (wpa_s->current_ssid && wpa_s->current_ssid->owe_group) {
+                       group = wpa_s->current_ssid->owe_group;
+               } else if (wpa_s->assoc_status_code ==
+                          WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) {
+                       if (wpa_s->last_owe_group == 19)
+                               group = 20;
+                       else if (wpa_s->last_owe_group == 20)
+                               group = 21;
+                       else
+                               group = OWE_DH_GROUP;
+               } else {
+                       group = OWE_DH_GROUP;
+               }
+
+               wpa_s->last_owe_group = group;
+               wpa_printf(MSG_DEBUG, "OWE: Try to use group %u", group);
+               owe_ie = owe_build_assoc_req(wpa_s->wpa, group);
+               if (!owe_ie) {
+                       wpa_printf(MSG_ERROR,
+                                  "OWE: Failed to build IE for Association Request frame");
+                       return;
+               }
+               if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(owe_ie) >
+                   sizeof(wpa_s->sme.assoc_req_ie)) {
+                       wpa_printf(MSG_ERROR,
+                                  "OWE: Not enough buffer room for own Association Request frame elements");
+                       wpabuf_free(owe_ie);
+                       return;
+               }
+               os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+                         wpabuf_head(owe_ie), wpabuf_len(owe_ie));
+               wpa_s->sme.assoc_req_ie_len += wpabuf_len(owe_ie);
+               wpabuf_free(owe_ie);
+       }
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP2
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP && wpa_s->current_ssid &&
+           wpa_s->current_ssid->dpp_netaccesskey) {
+               struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+               dpp_pfs_free(wpa_s->dpp_pfs);
+               wpa_s->dpp_pfs = dpp_pfs_init(ssid->dpp_netaccesskey,
+                                             ssid->dpp_netaccesskey_len);
+               if (!wpa_s->dpp_pfs) {
+                       wpa_printf(MSG_DEBUG, "DPP: Could not initialize PFS");
+                       /* Try to continue without PFS */
+                       goto pfs_fail;
+               }
+               if (wpa_s->sme.assoc_req_ie_len +
+                   wpabuf_len(wpa_s->dpp_pfs->ie) >
+                   sizeof(wpa_s->sme.assoc_req_ie)) {
+                       wpa_printf(MSG_ERROR,
+                                  "DPP: Not enough buffer room for own Association Request frame elements");
+                       dpp_pfs_free(wpa_s->dpp_pfs);
+                       wpa_s->dpp_pfs = NULL;
+                       goto pfs_fail;
+               }
+               os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+                         wpabuf_head(wpa_s->dpp_pfs->ie),
+                         wpabuf_len(wpa_s->dpp_pfs->ie));
+               wpa_s->sme.assoc_req_ie_len += wpabuf_len(wpa_s->dpp_pfs->ie);
+       }
+pfs_fail:
+#endif /* CONFIG_DPP2 */
+
+       if (wpa_s->current_ssid && wpa_s->current_ssid->multi_ap_backhaul_sta) {
+               size_t multi_ap_ie_len;
+
+               multi_ap_ie_len = add_multi_ap_ie(
+                       wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+                       sizeof(wpa_s->sme.assoc_req_ie) -
+                       wpa_s->sme.assoc_req_ie_len,
+                       MULTI_AP_BACKHAUL_STA);
+               if (multi_ap_ie_len == 0) {
+                       wpa_printf(MSG_ERROR,
+                                  "Multi-AP: Failed to build Multi-AP IE");
+                       return;
+               }
+               wpa_s->sme.assoc_req_ie_len += multi_ap_ie_len;
+       }
+
        params.bssid = bssid;
        params.ssid = wpa_s->sme.ssid;
        params.ssid_len = wpa_s->sme.ssid_len;
@@ -948,8 +1834,11 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
        params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
                wpa_s->sme.assoc_req_ie : NULL;
        params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
+       wpa_hexdump(MSG_DEBUG, "SME: Association Request IEs",
+                   params.wpa_ie, params.wpa_ie_len);
        params.pairwise_suite = wpa_s->pairwise_cipher;
        params.group_suite = wpa_s->group_cipher;
+       params.mgmt_group_suite = wpa_s->mgmt_group_cipher;
        params.key_mgmt_suite = wpa_s->key_mgmt;
        params.wpa_proto = wpa_s->wpa_proto;
 #ifdef CONFIG_HT_OVERRIDES
@@ -967,9 +1856,85 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
        wpa_supplicant_apply_vht_overrides(wpa_s, wpa_s->current_ssid, &params);
 #endif /* CONFIG_VHT_OVERRIDES */
 #ifdef CONFIG_IEEE80211R
-       if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
+       if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies &&
+           get_ie(wpa_s->sme.ft_ies, wpa_s->sme.ft_ies_len,
+                  WLAN_EID_RIC_DATA)) {
+               /* There seems to be a pretty inconvenient bug in the Linux
+                * kernel IE splitting functionality when RIC is used. For now,
+                * skip correct behavior in IE construction here (i.e., drop the
+                * additional non-FT-specific IEs) to avoid kernel issues. This
+                * is fine since RIC is used only for testing purposes in the
+                * current implementation. */
+               wpa_printf(MSG_INFO,
+                          "SME: Linux kernel workaround - do not try to include additional IEs with RIC");
                params.wpa_ie = wpa_s->sme.ft_ies;
                params.wpa_ie_len = wpa_s->sme.ft_ies_len;
+       } else if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
+               const u8 *rm_en, *pos, *end;
+               size_t rm_en_len = 0;
+               u8 *rm_en_dup = NULL, *wpos;
+
+               /* Remove RSNE, MDE, FTE to allow them to be overridden with
+                * FT specific values */
+               remove_ie(wpa_s->sme.assoc_req_ie,
+                         &wpa_s->sme.assoc_req_ie_len,
+                         WLAN_EID_RSN);
+               remove_ie(wpa_s->sme.assoc_req_ie,
+                         &wpa_s->sme.assoc_req_ie_len,
+                         WLAN_EID_MOBILITY_DOMAIN);
+               remove_ie(wpa_s->sme.assoc_req_ie,
+                         &wpa_s->sme.assoc_req_ie_len,
+                         WLAN_EID_FAST_BSS_TRANSITION);
+               rm_en = get_ie(wpa_s->sme.assoc_req_ie,
+                              wpa_s->sme.assoc_req_ie_len,
+                              WLAN_EID_RRM_ENABLED_CAPABILITIES);
+               if (rm_en) {
+                       /* Need to remove RM Enabled Capabilities element as
+                        * well temporarily, so that it can be placed between
+                        * RSNE and MDE. */
+                       rm_en_len = 2 + rm_en[1];
+                       rm_en_dup = os_memdup(rm_en, rm_en_len);
+                       remove_ie(wpa_s->sme.assoc_req_ie,
+                                 &wpa_s->sme.assoc_req_ie_len,
+                                 WLAN_EID_RRM_ENABLED_CAPABILITIES);
+               }
+               wpa_hexdump(MSG_DEBUG,
+                           "SME: Association Request IEs after FT IE removal",
+                           wpa_s->sme.assoc_req_ie,
+                           wpa_s->sme.assoc_req_ie_len);
+               if (wpa_s->sme.assoc_req_ie_len + wpa_s->sme.ft_ies_len +
+                   rm_en_len > sizeof(wpa_s->sme.assoc_req_ie)) {
+                       wpa_printf(MSG_ERROR,
+                                  "SME: Not enough buffer room for FT IEs in Association Request frame");
+                       os_free(rm_en_dup);
+                       return;
+               }
+
+               os_memmove(wpa_s->sme.assoc_req_ie + wpa_s->sme.ft_ies_len +
+                          rm_en_len,
+                          wpa_s->sme.assoc_req_ie,
+                          wpa_s->sme.assoc_req_ie_len);
+               pos = wpa_s->sme.ft_ies;
+               end = pos + wpa_s->sme.ft_ies_len;
+               wpos = wpa_s->sme.assoc_req_ie;
+               if (*pos == WLAN_EID_RSN) {
+                       os_memcpy(wpos, pos, 2 + pos[1]);
+                       wpos += 2 + pos[1];
+                       pos += 2 + pos[1];
+               }
+               if (rm_en_dup) {
+                       os_memcpy(wpos, rm_en_dup, rm_en_len);
+                       wpos += rm_en_len;
+                       os_free(rm_en_dup);
+               }
+               os_memcpy(wpos, pos, end - pos);
+               wpa_s->sme.assoc_req_ie_len += wpa_s->sme.ft_ies_len +
+                       rm_en_len;
+               params.wpa_ie = wpa_s->sme.assoc_req_ie;
+               params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
+               wpa_hexdump(MSG_DEBUG,
+                           "SME: Association Request IEs after FT override",
+                           params.wpa_ie, params.wpa_ie_len);
        }
 #endif /* CONFIG_IEEE80211R */
        params.mode = mode;
@@ -1005,11 +1970,16 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
                                        elems.osen_len + 2);
        } else
                wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+       if (elems.rsnxe)
+               wpa_sm_set_assoc_rsnxe(wpa_s->wpa, elems.rsnxe - 2,
+                                      elems.rsnxe_len + 2);
+       else
+               wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
        if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group)
                params.p2p = 1;
 
-       if (wpa_s->parent->set_sta_uapsd)
-               params.uapsd = wpa_s->parent->sta_uapsd;
+       if (wpa_s->p2pdev->set_sta_uapsd)
+               params.uapsd = wpa_s->p2pdev->sta_uapsd;
        else
                params.uapsd = -1;
 
@@ -1024,6 +1994,14 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
 
        eloop_register_timeout(SME_ASSOC_TIMEOUT, 0, sme_assoc_timer, wpa_s,
                               NULL);
+
+#ifdef CONFIG_TESTING_OPTIONS
+       wpabuf_free(wpa_s->last_assoc_req_wpa_ie);
+       wpa_s->last_assoc_req_wpa_ie = NULL;
+       if (params.wpa_ie)
+               wpa_s->last_assoc_req_wpa_ie =
+                       wpabuf_alloc_copy(params.wpa_ie, params.wpa_ie_len);
+#endif /* CONFIG_TESTING_OPTIONS */
 }
 
 
@@ -1042,10 +2020,9 @@ int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
        os_memcpy(wpa_s->sme.mobility_domain, md, MOBILITY_DOMAIN_ID_LEN);
        wpa_hexdump(MSG_DEBUG, "SME: FT IEs", ies, ies_len);
        os_free(wpa_s->sme.ft_ies);
-       wpa_s->sme.ft_ies = os_malloc(ies_len);
+       wpa_s->sme.ft_ies = os_memdup(ies, ies_len);
        if (wpa_s->sme.ft_ies == NULL)
                return -1;
-       os_memcpy(wpa_s->sme.ft_ies, ies, ies_len);
        wpa_s->sme.ft_ies_len = ies_len;
        return 0;
 }
@@ -1212,21 +2189,20 @@ void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s)
        sae_clear_data(&wpa_s->sme.sae);
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_IEEE80211R
-       if (wpa_s->sme.ft_ies)
+       if (wpa_s->sme.ft_ies || wpa_s->sme.ft_used)
                sme_update_ft_ies(wpa_s, NULL, NULL, 0);
 #endif /* CONFIG_IEEE80211R */
+       sme_stop_sa_query(wpa_s);
 }
 
 
 void sme_deinit(struct wpa_supplicant *wpa_s)
 {
-       os_free(wpa_s->sme.ft_ies);
-       wpa_s->sme.ft_ies = NULL;
-       wpa_s->sme.ft_ies_len = 0;
-#ifdef CONFIG_IEEE80211W
-       sme_stop_sa_query(wpa_s);
-#endif /* CONFIG_IEEE80211W */
        sme_clear_on_disassoc(wpa_s);
+#ifdef CONFIG_SAE
+       os_free(wpa_s->sme.sae_rejected_groups);
+       wpa_s->sme.sae_rejected_groups = NULL;
+#endif /* CONFIG_SAE */
 
        eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
        eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
@@ -1284,13 +2260,14 @@ static void sme_send_2040_bss_coex(struct wpa_supplicant *wpa_s,
 }
 
 
-int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
+int sme_proc_obss_scan(struct wpa_supplicant *wpa_s,
+                      struct wpa_scan_results *scan_res)
 {
-       struct wpa_bss *bss;
        const u8 *ie;
-       u16 ht_cap;
        u8 chan_list[P2P_MAX_CHANNELS], channel;
        u8 num_channels = 0, num_intol = 0, i;
+       size_t j;
+       int pri_freq, sec_freq;
 
        if (!wpa_s->sme.sched_obss_scan)
                return 0;
@@ -1318,22 +2295,36 @@ int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
 
        os_memset(chan_list, 0, sizeof(chan_list));
 
-       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
-               /* Skip other band bss */
+       pri_freq = wpa_s->assoc_freq;
+
+       switch (wpa_s->sme.ht_sec_chan) {
+       case HT_SEC_CHAN_ABOVE:
+               sec_freq = pri_freq + 20;
+               break;
+       case HT_SEC_CHAN_BELOW:
+               sec_freq = pri_freq - 20;
+               break;
+       case HT_SEC_CHAN_UNKNOWN:
+       default:
+               wpa_msg(wpa_s, MSG_WARNING,
+                       "Undefined secondary channel: drop OBSS scan results");
+               return 1;
+       }
+
+       for (j = 0; j < scan_res->num; j++) {
+               struct wpa_scan_res *bss = scan_res->res[j];
                enum hostapd_hw_mode mode;
+               int res;
+
+               /* Skip other band bss */
                mode = ieee80211_freq_to_chan(bss->freq, &channel);
                if (mode != HOSTAPD_MODE_IEEE80211G &&
                    mode != HOSTAPD_MODE_IEEE80211B)
                        continue;
 
-               ie = wpa_bss_get_ie(bss, WLAN_EID_HT_CAP);
-               ht_cap = (ie && (ie[1] == 26)) ? WPA_GET_LE16(ie + 2) : 0;
-               wpa_printf(MSG_DEBUG, "SME OBSS scan BSS " MACSTR
-                          " freq=%u chan=%u ht_cap=0x%x",
-                          MAC2STR(bss->bssid), bss->freq, channel, ht_cap);
-
-               if (!ht_cap || (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)) {
-                       if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)
+               res = check_bss_coex_40mhz(bss, pri_freq, sec_freq);
+               if (res) {
+                       if (res == 2)
                                num_intol++;
 
                        /* Check whether the channel is already considered */
@@ -1362,7 +2353,7 @@ static void wpa_obss_scan_freqs_list(struct wpa_supplicant *wpa_s,
        int start, end;
 
        mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
-                       HOSTAPD_MODE_IEEE80211G);
+                       HOSTAPD_MODE_IEEE80211G, 0);
        if (mode == NULL) {
                /* No channels supported in this band - use empty list */
                params->freqs = os_zalloc(sizeof(int));
@@ -1469,7 +2460,7 @@ void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable)
         */
        if (!((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) ||
              (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OBSS_SCAN)) ||
-           ssid == NULL || ssid->mode != IEEE80211_MODE_INFRA)
+           ssid == NULL || ssid->mode != WPAS_MODE_INFRA)
                return;
 
        if (!wpa_s->hw.modes)
@@ -1515,10 +2506,9 @@ void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable)
 }
 
 
-#ifdef CONFIG_IEEE80211W
-
 static const unsigned int sa_query_max_timeout = 1000;
 static const unsigned int sa_query_retry_timeout = 201;
+static const unsigned int sa_query_ch_switch_max_delay = 5000; /* in usec */
 
 static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s)
 {
@@ -1542,7 +2532,9 @@ static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s)
 static void sme_send_sa_query_req(struct wpa_supplicant *wpa_s,
                                  const u8 *trans_id)
 {
-       u8 req[2 + WLAN_SA_QUERY_TR_ID_LEN];
+       u8 req[2 + WLAN_SA_QUERY_TR_ID_LEN + OCV_OCI_EXTENDED_LEN];
+       u8 req_len = 2 + WLAN_SA_QUERY_TR_ID_LEN;
+
        wpa_dbg(wpa_s, MSG_DEBUG, "SME: Sending SA Query Request to "
                MACSTR, MAC2STR(wpa_s->bssid));
        wpa_hexdump(MSG_DEBUG, "SME: SA Query Transaction ID",
@@ -1550,9 +2542,27 @@ static void sme_send_sa_query_req(struct wpa_supplicant *wpa_s,
        req[0] = WLAN_ACTION_SA_QUERY;
        req[1] = WLAN_SA_QUERY_REQUEST;
        os_memcpy(req + 2, trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+#ifdef CONFIG_OCV
+       if (wpa_sm_ocv_enabled(wpa_s->wpa)) {
+               struct wpa_channel_info ci;
+
+               if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
+                       wpa_printf(MSG_WARNING,
+                                  "Failed to get channel info for OCI element in SA Query Request frame");
+                       return;
+               }
+
+               if (ocv_insert_extended_oci(&ci, req + req_len) < 0)
+                       return;
+
+               req_len += OCV_OCI_EXTENDED_LEN;
+       }
+#endif /* CONFIG_OCV */
+
        if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
                                wpa_s->own_addr, wpa_s->bssid,
-                               req, sizeof(req), 0) < 0)
+                               req, req_len, 0) < 0)
                wpa_msg(wpa_s, MSG_INFO, "SME: Failed to send SA Query "
                        "Request");
 }
@@ -1571,8 +2581,10 @@ static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
        nbuf = os_realloc_array(wpa_s->sme.sa_query_trans_id,
                                wpa_s->sme.sa_query_count + 1,
                                WLAN_SA_QUERY_TR_ID_LEN);
-       if (nbuf == NULL)
+       if (nbuf == NULL) {
+               sme_stop_sa_query(wpa_s);
                return;
+       }
        if (wpa_s->sme.sa_query_count == 0) {
                /* Starting a new SA Query procedure */
                os_get_reltime(&wpa_s->sme.sa_query_start);
@@ -1583,6 +2595,7 @@ static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
 
        if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) {
                wpa_printf(MSG_DEBUG, "Could not generate SA Query ID");
+               sme_stop_sa_query(wpa_s);
                return;
        }
 
@@ -1606,6 +2619,8 @@ static void sme_start_sa_query(struct wpa_supplicant *wpa_s)
 
 static void sme_stop_sa_query(struct wpa_supplicant *wpa_s)
 {
+       if (wpa_s->sme.sa_query_trans_id)
+               wpa_dbg(wpa_s, MSG_DEBUG, "SME: Stop SA Query");
        eloop_cancel_timeout(sme_sa_query_timer, wpa_s, NULL);
        os_free(wpa_s->sme.sa_query_trans_id);
        wpa_s->sme.sa_query_trans_id = NULL;
@@ -1644,15 +2659,74 @@ void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
 }
 
 
-void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
-                    const u8 *data, size_t len)
+void sme_event_ch_switch(struct wpa_supplicant *wpa_s)
+{
+       unsigned int usec;
+       u32 _rand;
+
+       if (wpa_s->wpa_state != WPA_COMPLETED ||
+           !wpa_sm_ocv_enabled(wpa_s->wpa))
+               return;
+
+       wpa_dbg(wpa_s, MSG_DEBUG,
+               "SME: Channel switch completed - trigger new SA Query to verify new operating channel");
+       sme_stop_sa_query(wpa_s);
+
+       if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
+               _rand = os_random();
+       usec = _rand % (sa_query_ch_switch_max_delay + 1);
+       eloop_register_timeout(0, usec, sme_sa_query_timer, wpa_s, NULL);
+}
+
+
+static void sme_process_sa_query_request(struct wpa_supplicant *wpa_s,
+                                        const u8 *sa, const u8 *data,
+                                        size_t len)
+{
+       u8 resp[2 + WLAN_SA_QUERY_TR_ID_LEN + OCV_OCI_EXTENDED_LEN];
+       u8 resp_len = 2 + WLAN_SA_QUERY_TR_ID_LEN;
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "SME: Sending SA Query Response to "
+               MACSTR, MAC2STR(wpa_s->bssid));
+
+       resp[0] = WLAN_ACTION_SA_QUERY;
+       resp[1] = WLAN_SA_QUERY_RESPONSE;
+       os_memcpy(resp + 2, data + 1, WLAN_SA_QUERY_TR_ID_LEN);
+
+#ifdef CONFIG_OCV
+       if (wpa_sm_ocv_enabled(wpa_s->wpa)) {
+               struct wpa_channel_info ci;
+
+               if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
+                       wpa_printf(MSG_WARNING,
+                                  "Failed to get channel info for OCI element in SA Query Response frame");
+                       return;
+               }
+
+               if (ocv_insert_extended_oci(&ci, resp + resp_len) < 0)
+                       return;
+
+               resp_len += OCV_OCI_EXTENDED_LEN;
+       }
+#endif /* CONFIG_OCV */
+
+       if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+                               wpa_s->own_addr, wpa_s->bssid,
+                               resp, resp_len, 0) < 0)
+               wpa_msg(wpa_s, MSG_INFO,
+                       "SME: Failed to send SA Query Response");
+}
+
+
+static void sme_process_sa_query_response(struct wpa_supplicant *wpa_s,
+                                         const u8 *sa, const u8 *data,
+                                         size_t len)
 {
        int i;
 
-       if (wpa_s->sme.sa_query_trans_id == NULL ||
-           len < 1 + WLAN_SA_QUERY_TR_ID_LEN ||
-           data[0] != WLAN_SA_QUERY_RESPONSE)
+       if (!wpa_s->sme.sa_query_trans_id)
                return;
+
        wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query response from "
                MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]);
 
@@ -1677,4 +2751,46 @@ void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
        sme_stop_sa_query(wpa_s);
 }
 
-#endif /* CONFIG_IEEE80211W */
+
+void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
+                    const u8 *data, size_t len)
+{
+       if (len < 1 + WLAN_SA_QUERY_TR_ID_LEN)
+               return;
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query frame from "
+               MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]);
+
+#ifdef CONFIG_OCV
+       if (wpa_sm_ocv_enabled(wpa_s->wpa)) {
+               struct ieee802_11_elems elems;
+               struct wpa_channel_info ci;
+
+               if (ieee802_11_parse_elems(data + 1 + WLAN_SA_QUERY_TR_ID_LEN,
+                                          len - 1 - WLAN_SA_QUERY_TR_ID_LEN,
+                                          &elems, 1) == ParseFailed) {
+                       wpa_printf(MSG_DEBUG,
+                                  "SA Query: Failed to parse elements");
+                       return;
+               }
+
+               if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
+                       wpa_printf(MSG_WARNING,
+                                  "Failed to get channel info to validate received OCI in SA Query Action frame");
+                       return;
+               }
+
+               if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
+                                        channel_width_to_int(ci.chanwidth),
+                                        ci.seg1_idx) != 0) {
+                       wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
+                       return;
+               }
+       }
+#endif /* CONFIG_OCV */
+
+       if (data[0] == WLAN_SA_QUERY_REQUEST)
+               sme_process_sa_query_request(wpa_s, sa, data, len);
+       else if (data[0] == WLAN_SA_QUERY_RESPONSE)
+               sme_process_sa_query_response(wpa_s, sa, data, len);
+}