]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - wpa_supplicant/sme.c
SAE: Collect list of rejected groups for H2E in STA
[thirdparty/hostap.git] / wpa_supplicant / sme.c
index 76d1acd00b964536d64dc7269cdbf91c2b0f8b5f..51d7fed24ed44bba7061e1d6a1dec43759efcd55 100644 (file)
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/ocv.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 +37,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 +56,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 +72,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 +83,77 @@ 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)
 {
        struct wpabuf *buf;
        size_t len;
+       const char *password;
+
+#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");
+               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),
+                              (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);
 
+reuse_data:
        len = wpa_s->sme.sae_token ? 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, WLAN_STATUS_SUCCESS);
+       }
+       sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token,
+                        ssid->sae_password_id);
 
        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 +161,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;
@@ -208,14 +244,19 @@ 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_ie;
+#endif /* CONFIG_MBO */
 
        if (bss == NULL) {
                wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for "
@@ -273,6 +314,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");
@@ -313,7 +360,8 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_FILS */
                if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
                                            wpa_s->current_ssid,
-                                           try_opportunistic, cache_id) == 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,
@@ -324,6 +372,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)) {
                /*
@@ -363,17 +425,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;
@@ -388,7 +478,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 "
@@ -400,7 +490,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);
@@ -413,7 +502,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) {
@@ -448,7 +536,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        sme_auth_handle_rrm(wpa_s, bss);
 
        wpa_s->sme.assoc_req_ie_len += wpas_supp_op_class_ie(
-               wpa_s, bss->freq,
+               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);
 
@@ -474,12 +562,14 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        if (is_hs20_network(wpa_s, ssid, bss)) {
                struct wpabuf *hs20;
 
-               hs20 = wpabuf_alloc(20);
+               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);
+                       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) {
@@ -493,6 +583,26 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        }
 #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;
@@ -508,13 +618,16 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        }
 
 #ifdef CONFIG_MBO
-       if (wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
+       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;
        }
@@ -523,9 +636,13 @@ 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,
-                                   NULL) == 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;
        }
@@ -533,9 +650,10 @@ 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);
                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;
@@ -557,20 +675,79 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
         * 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)
+                                           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);
+               resp = fils_build_auth(wpa_s->wpa, ssid->fils_dh_group, md);
                if (resp) {
-                       params.auth_alg = WPA_AUTH_ALG_FILS;
+                       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 = WPA_AUTH_ALG_FILS;
+                       wpa_s->sme.auth_alg = auth_alg;
                }
        }
+no_fils:
 #endif /* CONFIG_FILS */
 
        wpa_supplicant_cancel_sched_scan(wpa_s);
@@ -725,8 +902,162 @@ 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)
+{
+       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(WLAN_STATUS_SUCCESS);
+       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;
+
+       resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0);
+       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);
+       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);
+       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_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;
 
@@ -736,8 +1067,8 @@ 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;
 
                groups = wpa_s->conf->sae_groups;
@@ -763,25 +1094,47 @@ 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 = wpabuf_alloc_copy(data + sizeof(le16),
                                                         len - sizeof(le16));
-               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, 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 (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)
                return -1;
 
@@ -791,7 +1144,7 @@ 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)
@@ -799,7 +1152,8 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                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");
@@ -816,8 +1170,11 @@ 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) {
                wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
@@ -827,11 +1184,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 */
 
 
@@ -872,7 +1307,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);
@@ -881,10 +1316,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 */
 
@@ -907,6 +1340,12 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
                        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 ||
@@ -968,7 +1407,27 @@ 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) {
+       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,
@@ -990,6 +1449,22 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
 }
 
 
+#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)
 {
@@ -1010,7 +1485,8 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
        os_memset(&params, 0, sizeof(params));
 
 #ifdef CONFIG_FILS
-       if (auth_type == WLAN_AUTH_FILS_SK) {
+       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;
@@ -1044,6 +1520,30 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
                        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)) {
@@ -1056,6 +1556,9 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
                          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);
@@ -1065,11 +1568,34 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
 #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;
+               }
 
-               owe_ie = owe_build_assoc_req(wpa_s->wpa);
+               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");
@@ -1089,6 +1615,52 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
        }
 #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;
@@ -1098,8 +1670,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
@@ -1117,9 +1692,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;
@@ -1174,6 +1825,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 */
 }
 
 
@@ -1361,21 +2020,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);
@@ -1618,7 +2276,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)
@@ -1664,10 +2322,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)
 {
@@ -1691,7 +2348,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",
@@ -1699,9 +2358,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");
 }
@@ -1758,6 +2435,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;
@@ -1796,15 +2475,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]);
 
@@ -1829,4 +2567,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);
+}