]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - wpa_supplicant/sme.c
EAP: Increase the maximum number of message exchanges
[thirdparty/hostap.git] / wpa_supplicant / sme.c
index d57195f1531739a29f839b3abf8fcf498646bae0..c60ec4711ef3549671b257f864edba9438d9d741 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"
@@ -56,7 +58,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 +74,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,7 +85,8 @@ 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)
+                                                const u8 *bssid, int external,
+                                                int reuse)
 {
        struct wpabuf *buf;
        size_t len;
@@ -95,8 +98,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;
        }
@@ -110,6 +115,12 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
                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;
@@ -122,7 +133,10 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
                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);
@@ -240,6 +254,11 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        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 "
@@ -297,6 +316,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");
@@ -349,6 +374,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)) {
                /*
@@ -388,18 +427,41 @@ 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))
-               md = NULL;
        if (md) {
                wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT mobility domain %02x%02x",
                        md[0], md[1]);
@@ -418,7 +480,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 "
@@ -478,7 +540,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);
 
@@ -509,7 +571,8 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                        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;
@@ -524,6 +587,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;
@@ -539,13 +622,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 (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;
        }
@@ -554,7 +640,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);
@@ -565,7 +654,8 @@ 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, 0);
+                                                        bss->bssid, 0,
+                                                        start == 2);
                else
                        resp = sme_auth_build_sae_confirm(wpa_s, 0);
                if (resp == NULL) {
@@ -591,6 +681,9 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
            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
@@ -620,6 +713,19 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                        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),
@@ -815,10 +921,10 @@ static int sme_external_auth_build_buf(struct wpabuf *buf,
        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 = WLAN_AUTH_SAE;
-       resp->seq_ctrl = seq_num << 4;
-       resp->u.auth.auth_transaction = auth_transaction;
-       resp->u.auth.status_code = WLAN_STATUS_SUCCESS;
+       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);
 
@@ -826,21 +932,23 @@ 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;
 
-       resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1);
-       if (!resp)
-               return;
+       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;
+               return -1;
        }
 
        wpa_s->sme.seq_num++;
@@ -849,6 +957,8 @@ static void sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
        wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0);
        wpabuf_free(resp);
        wpabuf_free(buf);
+
+       return 0;
 }
 
 
@@ -859,35 +969,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)
+                   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);
-       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;
 }
 
 
@@ -926,13 +1037,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,
@@ -954,7 +1072,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
            status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
            wpa_s->sme.sae.state == SAE_COMMITTED &&
            (external || wpa_s->current_bss) && wpa_s->current_ssid) {
-               int default_groups[] = { 19, 20, 21, 25, 26, 0 };
+               int default_groups[] = { 19, 20, 21, 0 };
                u16 group;
 
                groups = wpa_s->conf->sae_groups;
@@ -982,10 +1100,10 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                                                         len - sizeof(le16));
                if (!external)
                        sme_send_authentication(wpa_s, wpa_s->current_bss,
-                                               wpa_s->current_ssid, 1);
+                                               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;
        }
@@ -1004,7 +1122,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;
        }
@@ -1081,6 +1199,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)
 {
@@ -1097,13 +1246,14 @@ void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
                return;
        }
 
-       if (header->u.auth.auth_alg == WLAN_AUTH_SAE) {
+       if (le_to_host16(header->u.auth.auth_alg) == WLAN_AUTH_SAE) {
                int res;
 
-               res = sme_sae_auth(wpa_s, header->u.auth.auth_transaction,
-                                  header->u.auth.status_code,
-                                  header->u.auth.variable,
-                                  len - auth_length, 1, header->sa);
+               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(
@@ -1113,10 +1263,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;
        }
 }
 
@@ -1169,10 +1317,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 */
 
@@ -1195,6 +1341,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 ||
@@ -1298,7 +1450,6 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
 }
 
 
-#ifdef CONFIG_FILS
 #ifdef CONFIG_IEEE80211R
 static void remove_ie(u8 *buf, size_t *len, u8 eid)
 {
@@ -1313,7 +1464,6 @@ static void remove_ie(u8 *buf, size_t *len, u8 eid)
        }
 }
 #endif /* CONFIG_IEEE80211R */
-#endif /* CONFIG_FILS */
 
 
 void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
@@ -1432,14 +1582,18 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
 
                if (wpa_s->current_ssid && wpa_s->current_ssid->owe_group) {
                        group = wpa_s->current_ssid->owe_group;
-               } else {
+               } else if (wpa_s->assoc_status_code ==
+                          WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) {
                        if (wpa_s->last_owe_group == 19)
                                group = 20;
                        else if (wpa_s->last_owe_group == 20)
                                group = 21;
                        else
                                group = OWE_DH_GROUP;
+               } else {
+                       group = OWE_DH_GROUP;
                }
+
                wpa_s->last_owe_group = group;
                wpa_printf(MSG_DEBUG, "OWE: Try to use group %u", group);
                owe_ie = owe_build_assoc_req(wpa_s->wpa, group);
@@ -1462,6 +1616,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;
@@ -1471,6 +1671,8 @@ 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;
@@ -1491,9 +1693,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;
@@ -1746,17 +2024,14 @@ 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 */
+#ifdef CONFIG_IEEE80211W
+       sme_stop_sa_query(wpa_s);
+#endif /* CONFIG_IEEE80211W */
 }
 
 
 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);
 
        eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
@@ -2000,7 +2275,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)
@@ -2050,6 +2325,7 @@ void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable)
 
 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)
 {
@@ -2073,7 +2349,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",
@@ -2081,9 +2359,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");
 }
@@ -2140,6 +2436,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;
@@ -2178,15 +2476,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]);
 
@@ -2211,4 +2568,48 @@ void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
        sme_stop_sa_query(wpa_s);
 }
 
+
+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);
+}
+
 #endif /* CONFIG_IEEE80211W */