]> 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 925cfa0550ed99e4815dd5c41852bc83613946e4..81151a7fb710a0b0a63f281ed954d58e3bbf4377 100644 (file)
 #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"
@@ -36,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
@@ -85,11 +85,16 @@ 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, int external,
-                                                int reuse)
+                                                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) {
@@ -97,8 +102,10 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
                buf = wpabuf_alloc(4 + wpabuf_len(wpa_s->sae_commit_override));
                if (!buf)
                        return NULL;
-               wpabuf_put_le16(buf, 1); /* Transaction seq# */
-               wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+               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;
        }
@@ -116,6 +123,7 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
            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) {
@@ -123,7 +131,36 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
                return NULL;
        }
 
-       if (sae_prepare_commit(wpa_s->own_addr, bssid,
+       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) {
@@ -134,7 +171,7 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
                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;
+       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);
@@ -142,10 +179,13 @@ reuse_data:
                return NULL;
        if (!external) {
                wpabuf_put_le16(buf, 1); /* Transaction seq# */
-               wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+               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;
 }
@@ -313,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");
@@ -331,12 +377,6 @@ 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)) {
@@ -445,13 +485,14 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        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))
-               md = NULL;
        if (md) {
                wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT mobility domain %02x%02x",
                        md[0], md[1]);
@@ -482,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);
@@ -495,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) {
@@ -552,6 +591,26 @@ 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;
@@ -613,7 +672,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_MBO
        mbo_ie = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
-       if (mbo_ie) {
+       if (!wpa_s->disable_mbo_oce && mbo_ie) {
                int len;
 
                len = wpas_mbo_ie(wpa_s, wpa_s->sme.assoc_req_ie +
@@ -630,7 +689,10 @@ 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, WPA_KEY_MGMT_SAE) == 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);
@@ -642,7 +704,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                if (start)
                        resp = sme_auth_build_sae_commit(wpa_s, ssid,
                                                         bss->bssid, 0,
-                                                        start == 2);
+                                                        start == 2, NULL);
                else
                        resp = sme_auth_build_sae_confirm(wpa_s, 0);
                if (resp == NULL) {
@@ -655,6 +717,12 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        }
 #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);
@@ -842,6 +910,8 @@ static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit)
        /* 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);
 }
@@ -896,7 +966,8 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
 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 auth_transaction, u16 seq_num,
+                                      u16 status_code)
 {
        struct ieee80211_mgmt *resp;
 
@@ -911,7 +982,7 @@ static int sme_external_auth_build_buf(struct wpabuf *buf,
        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);
+       resp->u.auth.status_code = host_to_le16(status_code);
        if (params)
                wpabuf_put_buf(buf, params);
 
@@ -919,29 +990,36 @@ static int sme_external_auth_build_buf(struct wpabuf *buf,
 }
 
 
-static void sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
-                                             const u8 *bssid,
-                                             struct wpa_ssid *ssid)
+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);
-       if (!resp)
-               return;
+       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;
+               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);
+                                   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;
 }
 
 
@@ -952,36 +1030,36 @@ static void sme_send_external_auth_status(struct wpa_supplicant *wpa_s,
 
        os_memset(&params, 0, sizeof(params));
        params.status = status;
-       os_memcpy(params.ssid, wpa_s->sme.ext_auth.ssid,
-                 wpa_s->sme.ext_auth.ssid_len);
-       params.ssid_len = wpa_s->sme.ext_auth.ssid_len;
-       os_memcpy(params.bssid, wpa_s->sme.ext_auth.bssid, ETH_ALEN);
+       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 void sme_handle_external_auth_start(struct wpa_supplicant *wpa_s,
-                                          union wpa_event_data *data)
+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;
-       u8 *ssid_str = data->external_auth.ssid;
+       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))
+                   (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);
-       else
-               sme_send_external_auth_status(wpa_s,
-                                             WLAN_STATUS_UNSPECIFIED_FAILURE);
+       if (!ssid ||
+           sme_external_auth_send_sae_commit(wpa_s, data->external_auth.bssid,
+                                             ssid) < 0)
+               return -1;
+
+       return 0;
 }
 
 
@@ -1005,7 +1083,8 @@ static void sme_external_auth_send_sae_confirm(struct wpa_supplicant *wpa_s,
        }
        wpa_s->sme.seq_num++;
        sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
-                                   da, 2, wpa_s->sme.seq_num);
+                                   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);
@@ -1020,13 +1099,20 @@ void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
                return;
 
        if (data->external_auth.action == EXT_AUTH_START) {
-               os_memcpy(&wpa_s->sme.ext_auth, data,
-                         sizeof(struct external_auth));
+               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;
-               sme_handle_external_auth_start(wpa_s, data);
+               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,
@@ -1035,6 +1121,52 @@ void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
 }
 
 
+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,
                        int external, const u8 *sa)
@@ -1050,11 +1182,16 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
            (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");
@@ -1072,14 +1209,36 @@ 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));
+               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, wpa_s->sme.ext_auth_bssid,
                                wpa_s->current_ssid);
                return 0;
        }
@@ -1089,6 +1248,8 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
            wpa_s->sme.sae.state == SAE_COMMITTED &&
            (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 */
@@ -1098,7 +1259,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                                                wpa_s->current_ssid, 1);
                else
                        sme_external_auth_send_sae_commit(
-                               wpa_s, wpa_s->sme.ext_auth.bssid,
+                               wpa_s, wpa_s->sme.ext_auth_bssid,
                                wpa_s->current_ssid);
                return 0;
        }
@@ -1113,7 +1274,8 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                return -1;
        }
 
-       if (status_code != WLAN_STATUS_SUCCESS)
+       if (status_code != WLAN_STATUS_SUCCESS &&
+           status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT)
                return -1;
 
        if (auth_transaction == 1) {
@@ -1125,12 +1287,29 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                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");
@@ -1139,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");
@@ -1154,6 +1339,8 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                        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;
@@ -1175,6 +1362,37 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
 }
 
 
+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)
 {
@@ -1208,10 +1426,8 @@ void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
                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->sme.ext_auth_bssid) < 0)
+                       return;
        }
 }
 
@@ -1264,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 */
 
@@ -1565,6 +1779,36 @@ 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;
 
@@ -1726,6 +1970,11 @@ 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;
 
@@ -1943,18 +2192,17 @@ void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s)
        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);
@@ -2012,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;
@@ -2046,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 */
@@ -2090,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));
@@ -2197,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)
@@ -2243,8 +2506,6 @@ 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 */
@@ -2358,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;
@@ -2531,5 +2794,3 @@ void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
        else if (data[0] == WLAN_SA_QUERY_RESPONSE)
                sme_process_sa_query_response(wpa_s, sa, data, len);
 }
-
-#endif /* CONFIG_IEEE80211W */