]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
RSNE/RSNXE overriding for AP
authorJouni Malinen <quic_jouni@quicinc.com>
Wed, 11 Oct 2023 09:48:05 +0000 (12:48 +0300)
committerJouni Malinen <j@w1.fi>
Sat, 20 Jul 2024 18:28:28 +0000 (21:28 +0300)
Allow hostapd to be configured to advertised two separate sets of
RSNE/RSNXE parameters so that RSNE/RSNXE can use a reduced set of
capabilities (e.g., WPA2-Personal only) for supporting deployed STAs
that have issues with transition modes while the new override elements
can use a newer security option (e.g., WPA3-Personal only) for STAs that
support the new mechanism.

Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
12 files changed:
hostapd/config_file.c
hostapd/hostapd.conf
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/beacon.c
src/ap/drv_callbacks.c
src/ap/ieee802_11.c
src/ap/wpa_auth.c
src/ap/wpa_auth.h
src/ap/wpa_auth_glue.c
src/ap/wpa_auth_i.h
src/ap/wpa_auth_ie.c

index 3fb059770d49fd3d3867cd53014aa9258479e1b7..84cb04af47a9a0eb9015068ad6ca56e3f30bb82d 100644 (file)
@@ -3156,6 +3156,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                bss->wpa_key_mgmt = hostapd_config_parse_key_mgmt(line, pos);
                if (bss->wpa_key_mgmt == -1)
                        return 1;
+       } else if (os_strcmp(buf, "rsn_override_key_mgmt") == 0) {
+               bss->rsn_override_key_mgmt =
+                       hostapd_config_parse_key_mgmt(line, pos);
+               if (bss->rsn_override_key_mgmt == -1)
+                       return 1;
+       } else if (os_strcmp(buf, "rsn_override_key_mgmt_2") == 0) {
+               bss->rsn_override_key_mgmt_2 =
+                       hostapd_config_parse_key_mgmt(line, pos);
+               if (bss->rsn_override_key_mgmt_2 == -1)
+                       return 1;
        } else if (os_strcmp(buf, "wpa_psk_radius") == 0) {
                bss->wpa_psk_radius = atoi(pos);
                if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
@@ -3187,6 +3197,32 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                                   line, pos);
                        return 1;
                }
+       } else if (os_strcmp(buf, "rsn_override_pairwise") == 0) {
+               bss->rsn_override_pairwise =
+                       hostapd_config_parse_cipher(line, pos);
+               if (bss->rsn_override_pairwise == -1 ||
+                   bss->rsn_override_pairwise == 0)
+                       return 1;
+               if (bss->rsn_override_pairwise &
+                   (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: unsupported pairwise cipher suite '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "rsn_override_pairwise_2") == 0) {
+               bss->rsn_override_pairwise_2 =
+                       hostapd_config_parse_cipher(line, pos);
+               if (bss->rsn_override_pairwise_2 == -1 ||
+                   bss->rsn_override_pairwise_2 == 0)
+                       return 1;
+               if (bss->rsn_override_pairwise_2 &
+                   (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: unsupported pairwise cipher suite '%s'",
+                                  line, pos);
+                       return 1;
+               }
        } else if (os_strcmp(buf, "group_cipher") == 0) {
                bss->group_cipher = hostapd_config_parse_cipher(line, pos);
                if (bss->group_cipher == -1 || bss->group_cipher == 0)
@@ -3642,6 +3678,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                conf->use_driver_iface_addr = atoi(pos);
        } else if (os_strcmp(buf, "ieee80211w") == 0) {
                bss->ieee80211w = atoi(pos);
+       } else if (os_strcmp(buf, "rsn_override_mfp") == 0) {
+               bss->rsn_override_mfp = atoi(pos);
+       } else if (os_strcmp(buf, "rsn_override_mfp_2") == 0) {
+               bss->rsn_override_mfp_2 = atoi(pos);
        } else if (os_strcmp(buf, "group_mgmt_cipher") == 0) {
                if (os_strcmp(pos, "AES-128-CMAC") == 0) {
                        bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
index d875d5fc63b05af8994db2c692c64d849dac4045..24f398655f473aac7861f3c417206c5e9cf66752 100644 (file)
@@ -2298,6 +2298,58 @@ own_ip_addr=127.0.0.1
 #
 #ssid_protection=0
 
+# RSNE/RSNXE override
+#
+# These parameters can be used to configure RSN parameters for STAs that support
+# the override elements. The RSN parameters for STAs that do not support these
+# mechanisms are configured in the referenced configuration parameters. The AP
+# allows STAs to use either of the configured sets for negotiating RSN
+# parameters.
+#
+# The main purpose of this mechanism is to make the AP look like it is using an
+# older security mechanism (e.g., WPA2-Personal) to older STAs while allowing
+# new stations use newer security mechanisms (e.g., WPA3-Personal) based on the
+# override values. This might be needed to work around issues with deployed
+# STAs that do not implement RSNE extensibility correctly and may fail to
+# connect when the AP is using a transition mode like WPA3-Personal transition
+# mode.
+#
+# Key management; see wpa_key_mgmt for RSNE configuration
+#rsn_override_key_mgmt=<accepted key management algorithms>
+#
+# Pairwise cipher suites; see rsn_pairwise for RSNE configuration
+#rsn_override_pairwise=<accepted cipher suites)
+#
+# Management frame protection (MFP/PMF); see ieee80211w for RSNE configuration
+# 0 = disabled
+# 1 = optional
+# 2 = required
+#rsn_override_mfp=<0/1/2>
+#
+# Second set of similar parameters. These are required to be used for
+# Wi-Fi 7 (EHT/MLO) associations with RSN overriding and can optionally be used
+# in cases that do not use Wi-Fi 7.
+#rsn_override_key_mgmt_2
+#rsn_override_pairwise_2
+#rsn_override_mfp_2
+#
+# Example configuration for WPA2-Personal/PMF-optional in RSNE and
+# WPA3-Personal/PMF-required/MLO in override elements
+#wpa_key_mgmt=WPA-PSK
+#rsn_pairwise=CCMP
+#ieee80211w=1
+#rsn_override_key_mgmt=SAE
+#rsn_override_pairwise=GCMP-256
+#rsn_override_mfp=2
+#rsn_override_key_mgmt_2=SAE-EXT-KEY
+#rsn_override_pairwise_2=GCMP-256
+#rsn_override_mfp_2=2
+#beacon_prot=1
+#sae_groups=19 20
+#sae_require_mfp=1
+#sae_pwe=2
+
+
 ##### IEEE 802.11r configuration ##############################################
 
 # Mobility Domain identifier (dot11FTMobilityDomainID, MDID)
index 565b587166daf7ec9b3103f19f984a386f181835..f7117b599e64a9cbdd0ddd6ce057a3b5bad9051b 100644 (file)
@@ -494,10 +494,14 @@ int hostapd_setup_sae_pt(struct hostapd_bss_config *conf)
 
        if ((conf->sae_pwe == SAE_PWE_HUNT_AND_PECK &&
             !hostapd_sae_pw_id_in_use(conf) &&
-            !wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt) &&
+            !wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt |
+                                      conf->rsn_override_key_mgmt |
+                                      conf->rsn_override_key_mgmt_2) &&
             !hostapd_sae_pk_in_use(conf)) ||
            conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK ||
-           !wpa_key_mgmt_sae(conf->wpa_key_mgmt))
+           !wpa_key_mgmt_sae(conf->wpa_key_mgmt |
+                             conf->rsn_override_key_mgmt |
+                             conf->rsn_override_key_mgmt_2))
                return 0; /* PT not needed */
 
        sae_deinit_pt(ssid->pt);
index ced2181ab8aebb846a79bab352a636157cdf7426..8f1b98622c75d34e02a92300df98cf7f1398c551 100644 (file)
@@ -358,7 +358,11 @@ struct hostapd_bss_config {
        int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
        int extended_key_id;
        int wpa_key_mgmt;
+       int rsn_override_key_mgmt;
+       int rsn_override_key_mgmt_2;
        enum mfp_options ieee80211w;
+       enum mfp_options rsn_override_mfp;
+       enum mfp_options rsn_override_mfp_2;
        int group_mgmt_cipher;
        int beacon_prot;
        /* dot11AssociationSAQueryMaximumTimeout (in TUs) */
@@ -387,6 +391,8 @@ struct hostapd_bss_config {
        u32 wpa_pairwise_update_count;
        int wpa_disable_eapol_key_retries;
        int rsn_pairwise;
+       int rsn_override_pairwise;
+       int rsn_override_pairwise_2;
        int rsn_preauth;
        char *rsn_preauth_interfaces;
 
index cec0c9829fd954c6f655cb3fda5815ff6a861991..f8ce8103f0ad29166425c5727a246097f1da89c7 100644 (file)
@@ -403,6 +403,81 @@ static u8 * hostapd_get_osen_ie(struct hostapd_data *hapd, u8 *pos, size_t len)
 }
 
 
+static u8 * hostapd_get_rsne_override(struct hostapd_data *hapd, u8 *pos,
+                                     size_t len)
+{
+       const u8 *ie;
+
+       ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_IE_VENDOR_TYPE);
+       if (!ie || 2U + ie[1] > len)
+               return pos;
+
+       os_memcpy(pos, ie, 2 + ie[1]);
+       return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_get_rsne_override_2(struct hostapd_data *hapd, u8 *pos,
+                                       size_t len)
+{
+       const u8 *ie;
+
+       ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_2_IE_VENDOR_TYPE);
+       if (!ie || 2U + ie[1] > len)
+               return pos;
+
+       os_memcpy(pos, ie, 2 + ie[1]);
+       return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_get_rsnxe_override(struct hostapd_data *hapd, u8 *pos,
+                                      size_t len)
+{
+       const u8 *ie;
+
+       ie = hostapd_vendor_wpa_ie(hapd, RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+       if (!ie || 2U + ie[1] > len)
+               return pos;
+
+       os_memcpy(pos, ie, 2 + ie[1]);
+       return pos + 2 + ie[1];
+}
+
+
+static size_t hostapd_get_rsne_override_len(struct hostapd_data *hapd)
+{
+       const u8 *ie;
+
+       ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_IE_VENDOR_TYPE);
+       if (!ie)
+               return 0;
+       return 2 + ie[1];
+}
+
+
+static size_t hostapd_get_rsne_override_2_len(struct hostapd_data *hapd)
+{
+       const u8 *ie;
+
+       ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_2_IE_VENDOR_TYPE);
+       if (!ie)
+               return 0;
+       return 2 + ie[1];
+}
+
+
+static size_t hostapd_get_rsnxe_override_len(struct hostapd_data *hapd)
+{
+       const u8 *ie;
+
+       ie = hostapd_vendor_wpa_ie(hapd, RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+       if (!ie)
+               return 0;
+       return 2 + ie[1];
+}
+
+
 static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
 {
 #ifdef CONFIG_TESTING_OPTIONS
@@ -686,6 +761,9 @@ static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
        buflen += hostapd_mbo_ie_len(hapd);
        buflen += hostapd_eid_owe_trans_len(hapd);
        buflen += hostapd_eid_dpp_cc_len(hapd);
+       buflen += hostapd_get_rsne_override_len(hapd);
+       buflen += hostapd_get_rsne_override_2_len(hapd);
+       buflen += hostapd_get_rsnxe_override_len(hapd);
 
        return buflen;
 }
@@ -885,6 +963,10 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
        pos = hostapd_eid_owe_trans(hapd, pos, epos - pos);
        pos = hostapd_eid_dpp_cc(hapd, pos, epos - pos);
 
+       pos = hostapd_get_rsne_override(hapd, pos, epos - pos);
+       pos = hostapd_get_rsne_override_2(hapd, pos, epos - pos);
+       pos = hostapd_get_rsnxe_override(hapd, pos, epos - pos);
+
        if (hapd->conf->vendor_elements) {
                os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
                          wpabuf_len(hapd->conf->vendor_elements));
@@ -2157,6 +2239,9 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
        tail_len += hostapd_mbo_ie_len(hapd);
        tail_len += hostapd_eid_owe_trans_len(hapd);
        tail_len += hostapd_eid_dpp_cc_len(hapd);
+       tail_len += hostapd_get_rsne_override_len(hapd);
+       tail_len += hostapd_get_rsne_override_2_len(hapd);
+       tail_len += hostapd_get_rsnxe_override_len(hapd);
 
        tailpos = tail = os_malloc(tail_len);
        if (head == NULL || tail == NULL) {
@@ -2368,6 +2453,13 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
                                        tail + tail_len - tailpos);
        tailpos = hostapd_eid_dpp_cc(hapd, tailpos, tail + tail_len - tailpos);
 
+       tailpos = hostapd_get_rsne_override(hapd, tailpos,
+                                           tail + tail_len - tailpos);
+       tailpos = hostapd_get_rsne_override_2(hapd, tailpos,
+                                             tail + tail_len - tailpos);
+       tailpos = hostapd_get_rsnxe_override(hapd, tailpos,
+                                            tail + tail_len - tailpos);
+
        if (hapd->conf->vendor_elements) {
                os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
                          wpabuf_len(hapd->conf->vendor_elements));
@@ -2400,7 +2492,9 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
        /* If SAE offload is enabled, provide password to lower layer for
         * SAE authentication and PMK generation.
         */
-       if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+       if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt |
+                            hapd->conf->rsn_override_key_mgmt |
+                            hapd->conf->rsn_override_key_mgmt_2) &&
            (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP)) {
                if (hostapd_sae_pk_in_use(hapd->conf)) {
                        wpa_printf(MSG_ERROR,
@@ -2445,7 +2539,9 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
        else if (hapd->conf->wpa & WPA_PROTO_WPA)
                params->pairwise_ciphers = hapd->conf->wpa_pairwise;
        params->group_cipher = hapd->conf->wpa_group;
-       params->key_mgmt_suites = hapd->conf->wpa_key_mgmt;
+       params->key_mgmt_suites = hapd->conf->wpa_key_mgmt |
+               hapd->conf->rsn_override_key_mgmt |
+               hapd->conf->rsn_override_key_mgmt_2;
        params->auth_algs = hapd->conf->auth_algs;
        params->wpa_version = hapd->conf->wpa;
        params->privacy = hapd->conf->wpa;
index 8b49b531e915ffd6644fc4b652e4bfc6c12c6520..13d5d8f712a216474337e73d3ff09b7601c4cd08 100644 (file)
@@ -513,6 +513,10 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
                                   "Failed to initialize WPA state machine");
                        return -1;
                }
+               wpa_auth_set_rsn_override(sta->wpa_sm,
+                                         elems.rsne_override != NULL);
+               wpa_auth_set_rsn_override_2(sta->wpa_sm,
+                                           elems.rsne_override_2 != NULL);
 #ifdef CONFIG_IEEE80211BE
                if (ap_sta_is_mld(hapd, sta)) {
                        wpa_printf(MSG_DEBUG,
index 6c516bc8a93d2232d3ca610d652829a2456f8c5d..1cd76ca785831df853a5ad7787a0e0e6cf01ab84 100644 (file)
@@ -1945,6 +1945,9 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
        if (resp != WLAN_STATUS_SUCCESS)
                goto fail;
 
+       wpa_auth_set_rsn_override(sta->wpa_sm, elems.rsne_override != NULL);
+       wpa_auth_set_rsn_override_2(sta->wpa_sm, elems.rsne_override_2 != NULL);
+
        if (!elems.fils_nonce) {
                wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
                resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -2961,7 +2964,10 @@ static void handle_auth(struct hostapd_data *hapd,
               auth_alg == WLAN_AUTH_FT) ||
 #endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_SAE
-             (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+             (hapd->conf->wpa &&
+              wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt |
+                               hapd->conf->rsn_override_key_mgmt |
+                               hapd->conf->rsn_override_key_mgmt_2) &&
               auth_alg == WLAN_AUTH_SAE) ||
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_FILS
@@ -4128,6 +4134,10 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 #endif /* CONFIG_IEEE80211BE */
 
                wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
+               wpa_auth_set_rsn_override(sta->wpa_sm,
+                                         elems->rsne_override != NULL);
+               wpa_auth_set_rsn_override_2(sta->wpa_sm,
+                                           elems->rsne_override_2 != NULL);
                res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
                                          hapd->iface->freq,
                                          wpa_ie, wpa_ie_len,
index 504ecf13428fde976b02e033629b607fa881b1bb..9494411049b55b141ee70f6599e1f2caef770bfb 100644 (file)
@@ -885,6 +885,9 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth)
 
 
        os_free(wpa_auth->wpa_ie);
+       os_free(wpa_auth->rsne_override);
+       os_free(wpa_auth->rsne_override_2);
+       os_free(wpa_auth->rsnxe_override);
 
        group = wpa_auth->group;
        while (group) {
@@ -4449,7 +4452,7 @@ static size_t wpa_auth_ml_kdes_len(struct wpa_state_machine *sm)
        /* MLO Link KDE for each link */
        for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
                struct wpa_authenticator *wpa_auth;
-               const u8 *ie;
+               const u8 *ie, *ieo;
 
                wpa_auth = wpa_get_link_auth(sm->wpa_auth, link_id);
                if (!wpa_auth)
@@ -4458,11 +4461,22 @@ static size_t wpa_auth_ml_kdes_len(struct wpa_state_machine *sm)
                kde_len += 2 + RSN_SELECTOR_LEN + 1 + ETH_ALEN;
                ie = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
                            WLAN_EID_RSN);
-               if (ie)
+               ieo = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+                                   sm->rsn_override_2 ?
+                                   RSNE_OVERRIDE_2_IE_VENDOR_TYPE :
+                                   RSNE_OVERRIDE_IE_VENDOR_TYPE);
+               if ((sm->rsn_override || sm->rsn_override_2) && ieo)
+                       kde_len += 2 + ieo[1 - 4];
+               else
                        kde_len += 2 + ie[1];
+
                ie = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
                            WLAN_EID_RSNX);
-               if (ie)
+               ieo = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+                                   RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+               if ((sm->rsn_override || sm->rsn_override_2) && ieo)
+                       kde_len += 2 + ieo[1] - 4;
+               else if (ie)
                        kde_len += 2 + ie[1];
        }
 
@@ -4488,7 +4502,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
 
        for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
                struct wpa_authenticator *wpa_auth;
-               const u8 *rsne, *rsnxe;
+               const u8 *rsne, *rsnxe, *rsneo, *rsnxeo;
                size_t rsne_len, rsnxe_len;
 
                wpa_auth = wpa_get_link_auth(sm->wpa_auth, link_id);
@@ -4498,10 +4512,24 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
                rsne = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
                             WLAN_EID_RSN);
                rsne_len = rsne ? 2 + rsne[1] : 0;
+               rsneo = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+                                     sm->rsn_override_2 ?
+                                     RSNE_OVERRIDE_2_IE_VENDOR_TYPE :
+                                     RSNE_OVERRIDE_IE_VENDOR_TYPE);
+               if ((sm->rsn_override || sm->rsn_override_2) && rsneo)
+                       rsne_len = 2 + rsneo[1] - 4;
+               else
+                       rsneo = NULL;
 
                rsnxe = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
                               WLAN_EID_RSNX);
                rsnxe_len = rsnxe ? 2 + rsnxe[1] : 0;
+               rsnxeo = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+                                      RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+               if ((sm->rsn_override || sm->rsn_override_2) && rsnxeo)
+                       rsnxe_len = 2 + rsnxeo[1] - 4;
+               else
+                       rsnxeo = NULL;
 
                wpa_printf(MSG_DEBUG,
                           "RSN: MLO Link: link=%u, len=%zu", link_id,
@@ -4527,13 +4555,27 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
                pos += ETH_ALEN;
 
                if (rsne_len) {
-                       os_memcpy(pos, rsne, rsne_len);
-                       pos += rsne_len;
+                       if (rsneo) {
+                               *pos++ = WLAN_EID_RSN;
+                               *pos++ = rsneo[1] - 4;
+                               os_memcpy(pos, &rsneo[2 + 4], rsneo[1] - 4);
+                               pos += rsneo[1] - 4;
+                       } else {
+                               os_memcpy(pos, rsne, rsne_len);
+                               pos += rsne_len;
+                       }
                }
 
                if (rsnxe_len) {
-                       os_memcpy(pos, rsnxe, rsnxe_len);
-                       pos += rsnxe_len;
+                       if (rsnxeo) {
+                               *pos++ = WLAN_EID_RSNX;
+                               *pos++ = rsnxeo[1] - 4;
+                               os_memcpy(pos, &rsnxeo[2 + 4], rsnxeo[1] - 4);
+                               pos += rsnxeo[1] - 4;
+                       } else {
+                               os_memcpy(pos, rsnxe, rsnxe_len);
+                               pos += rsnxe_len;
+                       }
                }
        }
 
@@ -4552,7 +4594,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
        struct wpa_group *gsm = sm->group;
        u8 *wpa_ie;
        int secure, gtkidx, encr = 0;
-       u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL;
+       u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL, *wpa_ie_buf3 = NULL;
        u8 hdr[2];
        struct wpa_auth_config *conf = &sm->wpa_auth->conf;
 #ifdef CONFIG_IEEE80211BE
@@ -4593,6 +4635,80 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
                        wpa_ie = wpa_ie + wpa_ie[1] + 2;
                wpa_ie_len = wpa_ie[1] + 2;
        }
+       if ((sm->rsn_override &&
+            get_vendor_ie(wpa_ie, wpa_ie_len, RSNE_OVERRIDE_IE_VENDOR_TYPE)) ||
+           (sm->rsn_override_2 &&
+            get_vendor_ie(wpa_ie, wpa_ie_len,
+                          RSNE_OVERRIDE_2_IE_VENDOR_TYPE))) {
+               const u8 *mde, *fte, *tie, *tie2 = NULL;
+               const u8 *override_rsne = NULL, *override_rsnxe = NULL;
+               const struct element *elem;
+
+               wpa_printf(MSG_DEBUG,
+                          "RSN: Use RSNE/RSNXE override element contents");
+               mde = get_ie(wpa_ie, wpa_ie_len, WLAN_EID_MOBILITY_DOMAIN);
+               fte = get_ie(wpa_ie, wpa_ie_len, WLAN_EID_FAST_BSS_TRANSITION);
+               tie = get_ie(wpa_ie, wpa_ie_len, WLAN_EID_TIMEOUT_INTERVAL);
+               if (tie) {
+                       const u8 *next = tie + 2 + tie[1];
+
+                       tie2 = get_ie(next, wpa_ie + wpa_ie_len - next,
+                                     WLAN_EID_TIMEOUT_INTERVAL);
+               }
+               for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC,
+                                   wpa_ie, wpa_ie_len) {
+                       if (elem->datalen >= 4) {
+                               if (WPA_GET_BE32(elem->data) ==
+                                   (sm->rsn_override_2 ?
+                                    RSNE_OVERRIDE_2_IE_VENDOR_TYPE :
+                                    RSNE_OVERRIDE_IE_VENDOR_TYPE))
+                                       override_rsne = &elem->id;
+                               if (WPA_GET_BE32(elem->data) ==
+                                   RSNXE_OVERRIDE_IE_VENDOR_TYPE)
+                                       override_rsnxe = &elem->id;
+                       }
+               }
+               wpa_hexdump(MSG_DEBUG, "EAPOL-Key msg 3/4 IEs before edits",
+                           wpa_ie, wpa_ie_len);
+               wpa_ie_buf3 = os_malloc(wpa_ie_len);
+               if (!wpa_ie_buf3)
+                       goto done;
+               pos = wpa_ie_buf3;
+               if (override_rsne) {
+                       *pos++ = WLAN_EID_RSN;
+                       *pos++ = override_rsne[1] - 4;
+                       os_memcpy(pos, &override_rsne[2 + 4],
+                                 override_rsne[1] - 4);
+                       pos += override_rsne[1] - 4;
+               }
+               if (mde) {
+                       os_memcpy(pos, mde, 2 + mde[1]);
+                       pos += 2 + mde[1];
+               }
+               if (fte) {
+                       os_memcpy(pos, fte, 2 + fte[1]);
+                       pos += 2 + fte[1];
+               }
+               if (tie) {
+                       os_memcpy(pos, tie, 2 + tie[1]);
+                       pos += 2 + tie[1];
+               }
+               if (tie2) {
+                       os_memcpy(pos, tie2, 2 + tie2[1]);
+                       pos += 2 + tie2[1];
+               }
+               if (override_rsnxe) {
+                       *pos++ = WLAN_EID_RSNX;
+                       *pos++ = override_rsnxe[1] - 4;
+                       os_memcpy(pos, &override_rsnxe[2 + 4],
+                                 override_rsnxe[1] - 4);
+                       pos += override_rsnxe[1] - 4;
+               }
+               wpa_ie = wpa_ie_buf3;
+               wpa_ie_len = pos - wpa_ie_buf3;
+               wpa_hexdump(MSG_DEBUG, "EAPOL-Key msg 3/4 IEs after edits",
+                           wpa_ie, wpa_ie_len);
+       }
 #ifdef CONFIG_TESTING_OPTIONS
        if (conf->rsne_override_eapol_set) {
                wpa_ie_buf2 = replace_ie(
@@ -4862,6 +4978,7 @@ done:
        bin_clear_free(kde, kde_len);
        os_free(wpa_ie_buf);
        os_free(wpa_ie_buf2);
+       os_free(wpa_ie_buf3);
 }
 
 
@@ -6813,6 +6930,20 @@ void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg)
 }
 
 
+void wpa_auth_set_rsn_override(struct wpa_state_machine *sm, bool val)
+{
+       if (sm)
+               sm->rsn_override = val;
+}
+
+
+void wpa_auth_set_rsn_override_2(struct wpa_state_machine *sm, bool val)
+{
+       if (sm)
+               sm->rsn_override_2 = val;
+}
+
+
 #ifdef CONFIG_DPP2
 void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z)
 {
index b588f9fdcd78e4031b305682ad6a37184e4d2eb5..ea9a60f986b7db66d211a69c4448f9a47d745613 100644 (file)
@@ -173,6 +173,8 @@ struct wpa_auth_config {
        int wpa;
        int extended_key_id;
        int wpa_key_mgmt;
+       int rsn_override_key_mgmt;
+       int rsn_override_key_mgmt_2;
        int wpa_pairwise;
        int wpa_group;
        int wpa_group_rekey;
@@ -184,6 +186,8 @@ struct wpa_auth_config {
        u32 wpa_pairwise_update_count;
        int wpa_disable_eapol_key_retries;
        int rsn_pairwise;
+       int rsn_override_pairwise;
+       int rsn_override_pairwise_2;
        int rsn_preauth;
        int eapol_version;
        int wmm_enabled;
@@ -192,6 +196,8 @@ struct wpa_auth_config {
        int okc;
        int tx_status;
        enum mfp_options ieee80211w;
+       enum mfp_options rsn_override_mfp;
+       enum mfp_options rsn_override_mfp_2;
        int beacon_prot;
        int group_mgmt_cipher;
        int sae_require_mfp;
@@ -605,6 +611,8 @@ u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm,
 bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth,
                                u8 *fd_rsn_info);
 void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg);
+void wpa_auth_set_rsn_override(struct wpa_state_machine *sm, bool val);
+void wpa_auth_set_rsn_override_2(struct wpa_state_machine *sm, bool val);
 void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z);
 void wpa_auth_set_ssid_protection(struct wpa_state_machine *sm, bool val);
 void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,
index e725615d932cb8e315c51357dbbfaff7514a19bf..60996bf54734dad3a8ac94ea858c2938e06ef371 100644 (file)
@@ -45,6 +45,8 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
        wconf->wpa = conf->wpa;
        wconf->extended_key_id = conf->extended_key_id;
        wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
+       wconf->rsn_override_key_mgmt = conf->rsn_override_key_mgmt;
+       wconf->rsn_override_key_mgmt_2 = conf->rsn_override_key_mgmt_2;
        wconf->wpa_pairwise = conf->wpa_pairwise;
        wconf->wpa_group = conf->wpa_group;
        wconf->wpa_group_rekey = conf->wpa_group_rekey;
@@ -56,6 +58,8 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
                conf->wpa_disable_eapol_key_retries;
        wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count;
        wconf->rsn_pairwise = conf->rsn_pairwise;
+       wconf->rsn_override_pairwise = conf->rsn_override_pairwise;
+       wconf->rsn_override_pairwise_2 = conf->rsn_override_pairwise_2;
        wconf->rsn_preauth = conf->rsn_preauth;
        wconf->eapol_version = conf->eapol_version;
 #ifdef CONFIG_MACSEC
@@ -70,6 +74,8 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
 #endif /* CONFIG_OCV */
        wconf->okc = conf->okc;
        wconf->ieee80211w = conf->ieee80211w;
+       wconf->rsn_override_mfp = conf->rsn_override_mfp;
+       wconf->rsn_override_mfp_2 = conf->rsn_override_mfp_2;
        wconf->beacon_prot = conf->beacon_prot;
        wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
        wconf->sae_require_mfp = conf->sae_require_mfp;
index 4e5ba3e2ed6f2d9223c81b384800aee04c24c20f..29988c27e6dbff1a58b36e9a233e178e306a290f 100644 (file)
@@ -124,6 +124,9 @@ struct wpa_state_machine {
        u32 dot11RSNAStatsTKIPLocalMICFailures;
        u32 dot11RSNAStatsTKIPRemoteMICFailures;
 
+       bool rsn_override;
+       bool rsn_override_2;
+
 #ifdef CONFIG_IEEE80211R_AP
        u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
                                * first 384 bits of MSK */
@@ -248,6 +251,9 @@ struct wpa_authenticator {
 
        u8 *wpa_ie;
        size_t wpa_ie_len;
+       u8 *rsne_override; /* RSNE with overridden payload */
+       u8 *rsne_override_2; /* RSNE with overridden (2) payload */
+       u8 *rsnxe_override; /* RSNXE with overridden payload */
 
        u8 addr[ETH_ALEN];
 
index 2efadf896186c07cc8d9bc580fe544bcc05e559e..f4f9cc8a46867e373ff86fd1ba4d3e9ca64e2e4c 100644 (file)
@@ -89,7 +89,8 @@ static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len)
 }
 
 
-static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf)
+static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf,
+                            enum mfp_options mfp)
 {
        u16 capab = 0;
 
@@ -99,9 +100,9 @@ static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf)
                /* 4 PTKSA replay counters when using WMM */
                capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
        }
-       if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+       if (mfp != NO_MGMT_FRAME_PROTECTION) {
                capab |= WPA_CAPABILITY_MFPC;
-               if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+               if (mfp == MGMT_FRAME_PROTECTION_REQUIRED)
                        capab |= WPA_CAPABILITY_MFPR;
        }
 #ifdef CONFIG_OCV
@@ -119,24 +120,19 @@ static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf)
 }
 
 
-int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
-                    const u8 *pmkid)
+static u8 * rsne_write_data(u8 *buf, size_t len, u8 *pos, int group,
+                           int pairwise, int key_mgmt, u16 rsn_capab,
+                           const u8 *pmkid, enum mfp_options mfp,
+                           int group_mgmt_cipher)
 {
-       struct rsn_ie_hdr *hdr;
        int num_suites, res;
-       u8 *pos, *count;
+       u8 *count;
        u32 suite;
 
-       hdr = (struct rsn_ie_hdr *) buf;
-       hdr->elem_id = WLAN_EID_RSN;
-       WPA_PUT_LE16(hdr->version, RSN_VERSION);
-       pos = (u8 *) (hdr + 1);
-
-       suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group);
+       suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group);
        if (suite == 0) {
-               wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
-                          conf->wpa_group);
-               return -1;
+               wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", group);
+               return NULL;
        }
        RSN_SELECTOR_PUT(pos, suite);
        pos += RSN_SELECTOR_LEN;
@@ -153,7 +149,7 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
        }
 #endif /* CONFIG_RSN_TESTING */
 
-       res = rsn_cipher_put_suites(pos, conf->rsn_pairwise);
+       res = rsn_cipher_put_suites(pos, pairwise);
        num_suites += res;
        pos += res * RSN_SELECTOR_LEN;
 
@@ -167,8 +163,8 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
 
        if (num_suites == 0) {
                wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
-                          conf->rsn_pairwise);
-               return -1;
+                          pairwise);
+               return NULL;
        }
        WPA_PUT_LE16(count, num_suites);
 
@@ -184,102 +180,102 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
        }
 #endif /* CONFIG_RSN_TESTING */
 
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+       if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+       if (key_mgmt & WPA_KEY_MGMT_PSK) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
 #ifdef CONFIG_IEEE80211R_AP
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+       if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
 #ifdef CONFIG_SHA384
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+       if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
 #endif /* CONFIG_SHA384 */
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+       if (key_mgmt & WPA_KEY_MGMT_FT_PSK) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
 #endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_SHA384
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) {
+       if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA384);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
 #endif /* CONFIG_SHA384 */
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+       if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+       if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
 #ifdef CONFIG_SAE
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
+       if (key_mgmt & WPA_KEY_MGMT_SAE) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+       if (key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+       if (key_mgmt & WPA_KEY_MGMT_FT_SAE) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+       if (key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
 #endif /* CONFIG_SAE */
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+       if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+       if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
 #ifdef CONFIG_FILS
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+       if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+       if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
 #ifdef CONFIG_IEEE80211R_AP
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+       if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+       if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
@@ -287,28 +283,28 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
 #endif /* CONFIG_IEEE80211R_AP */
 #endif /* CONFIG_FILS */
 #ifdef CONFIG_OWE
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
+       if (key_mgmt & WPA_KEY_MGMT_OWE) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
 #endif /* CONFIG_OWE */
 #ifdef CONFIG_DPP
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+       if (key_mgmt & WPA_KEY_MGMT_DPP) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
 #endif /* CONFIG_DPP */
 #ifdef CONFIG_HS20
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OSEN) {
+       if (key_mgmt & WPA_KEY_MGMT_OSEN) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
        }
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_PASN
-       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) {
+       if (key_mgmt & WPA_KEY_MGMT_PASN) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN);
                pos += RSN_SELECTOR_LEN;
                num_suites++;
@@ -325,18 +321,18 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
 
        if (num_suites == 0) {
                wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
-                          conf->wpa_key_mgmt);
-               return -1;
+                          key_mgmt);
+               return NULL;
        }
        WPA_PUT_LE16(count, num_suites);
 
        /* RSN Capabilities */
-       WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf));
+       WPA_PUT_LE16(pos, rsn_capab);
        pos += 2;
 
        if (pmkid) {
                if (2 + PMKID_LEN > buf + len - pos)
-                       return -1;
+                       return NULL;
                /* PMKID Count */
                WPA_PUT_LE16(pos, 1);
                pos += 2;
@@ -344,18 +340,19 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
                pos += PMKID_LEN;
        }
 
-       if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
-           conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) {
+
+       if (mfp != NO_MGMT_FRAME_PROTECTION &&
+           group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) {
                if (2 + 4 > buf + len - pos)
-                       return -1;
-               if (pmkid == NULL) {
+                       return NULL;
+               if (!pmkid) {
                        /* PMKID Count */
                        WPA_PUT_LE16(pos, 0);
                        pos += 2;
                }
 
                /* Management Group Cipher Suite */
-               switch (conf->group_mgmt_cipher) {
+               switch (group_mgmt_cipher) {
                case WPA_CIPHER_AES_128_CMAC:
                        RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
                        break;
@@ -371,8 +368,8 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
                default:
                        wpa_printf(MSG_DEBUG,
                                   "Invalid group management cipher (0x%x)",
-                                  conf->group_mgmt_cipher);
-                       return -1;
+                                  group_mgmt_cipher);
+                       return NULL;
                }
                pos += RSN_SELECTOR_LEN;
        }
@@ -384,12 +381,12 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
                 * the element.
                 */
                int pmkid_count_set = pmkid != NULL;
-               if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION)
+               if (mfp != NO_MGMT_FRAME_PROTECTION)
                        pmkid_count_set = 1;
                /* PMKID Count */
                WPA_PUT_LE16(pos, 0);
                pos += 2;
-               if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+               if (mfp == NO_MGMT_FRAME_PROTECTION) {
                        /* Management Group Cipher Suite */
                        RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
                        pos += RSN_SELECTOR_LEN;
@@ -399,6 +396,27 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
                pos += 17;
        }
 #endif /* CONFIG_RSN_TESTING */
+       return pos;
+}
+
+
+int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
+                    const u8 *pmkid)
+{
+       struct rsn_ie_hdr *hdr;
+       u8 *pos;
+
+       hdr = (struct rsn_ie_hdr *) buf;
+       hdr->elem_id = WLAN_EID_RSN;
+       WPA_PUT_LE16(hdr->version, RSN_VERSION);
+       pos = (u8 *) (hdr + 1);
+
+       pos = rsne_write_data(buf, len, pos, conf->wpa_group,
+                             conf->rsn_pairwise, conf->wpa_key_mgmt,
+                             wpa_own_rsn_capab(conf, conf->ieee80211w), pmkid,
+                             conf->ieee80211w, conf->group_mgmt_cipher);
+       if (!pos)
+               return -1;
 
        hdr->len = (pos - buf) - 2;
 
@@ -406,16 +424,74 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
 }
 
 
-int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
+static int wpa_write_rsne_override(struct wpa_auth_config *conf, u8 *buf,
+                                  size_t len)
 {
-       u8 *pos = buf;
-       u32 capab = 0, tmp;
-       size_t flen;
+       u8 *pos, *len_pos;
+
+       pos = buf;
+       *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+       len_pos = pos++;
+
+       WPA_PUT_BE32(pos, RSNE_OVERRIDE_IE_VENDOR_TYPE);
+       pos += 4;
+
+       WPA_PUT_LE16(pos, RSN_VERSION);
+       pos += 2;
+
+       pos = rsne_write_data(buf, len, pos, conf->wpa_group,
+                             conf->rsn_override_pairwise,
+                             conf->rsn_override_key_mgmt,
+                             wpa_own_rsn_capab(conf, conf->rsn_override_mfp),
+                             NULL, conf->rsn_override_mfp,
+                             conf->group_mgmt_cipher);
+       if (!pos)
+               return -1;
+
+       *len_pos = (pos - buf) - 2;
+
+       return pos - buf;
+}
+
+
+static int wpa_write_rsne_override_2(struct wpa_auth_config *conf, u8 *buf,
+                                    size_t len)
+{
+       u8 *pos, *len_pos;
+
+       pos = buf;
+       *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+       len_pos = pos++;
+
+       WPA_PUT_BE32(pos, RSNE_OVERRIDE_2_IE_VENDOR_TYPE);
+       pos += 4;
+
+       WPA_PUT_LE16(pos, RSN_VERSION);
+       pos += 2;
 
-       if (wpa_key_mgmt_sae(conf->wpa_key_mgmt) &&
+       pos = rsne_write_data(buf, len, pos, conf->wpa_group,
+                             conf->rsn_override_pairwise_2,
+                             conf->rsn_override_key_mgmt_2,
+                             wpa_own_rsn_capab(conf, conf->rsn_override_mfp_2),
+                             NULL, conf->rsn_override_mfp_2,
+                             conf->group_mgmt_cipher);
+       if (!pos)
+               return -1;
+
+       *len_pos = (pos - buf) - 2;
+
+       return pos - buf;
+}
+
+
+static u32 rsnxe_capab(struct wpa_auth_config *conf, int key_mgmt)
+{
+       u32 capab = 0;
+
+       if (wpa_key_mgmt_sae(key_mgmt) &&
            (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
             conf->sae_pwe == SAE_PWE_BOTH || conf->sae_pk ||
-            wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt))) {
+            wpa_key_mgmt_sae_ext_key(key_mgmt))) {
                capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
 #ifdef CONFIG_SAE_PK
                if (conf->sae_pk)
@@ -432,6 +508,18 @@ int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
        if (conf->ssid_protection)
                capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
 
+       return capab;
+}
+
+
+int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+       u8 *pos = buf;
+       u32 capab = 0, tmp;
+       size_t flen;
+
+       capab = rsnxe_capab(conf, conf->wpa_key_mgmt);
+
        if (!capab)
                return 0; /* no supported extended RSN capabilities */
        tmp = capab;
@@ -455,6 +543,37 @@ int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
 }
 
 
+static int wpa_write_rsnxe_override(struct wpa_auth_config *conf, u8 *buf,
+                                   size_t len)
+{
+       u8 *pos = buf;
+       u16 capab;
+       size_t flen;
+
+       capab = rsnxe_capab(conf, conf->rsn_override_key_mgmt |
+                           conf->rsn_override_key_mgmt_2);
+
+       flen = (capab & 0xff00) ? 2 : 1;
+       if (!capab)
+               return 0; /* no supported extended RSN capabilities */
+       if (len < 2 + flen)
+               return -1;
+       capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
+
+       *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+       *pos++ = 4 + flen;
+       WPA_PUT_BE32(pos, RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+       pos += 4;
+
+       *pos++ = capab & 0x00ff;
+       capab >>= 8;
+       if (capab)
+               *pos++ = capab;
+
+       return pos - buf;
+}
+
+
 static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid)
 {
        u8 *len;
@@ -508,7 +627,7 @@ static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid)
 
 int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
 {
-       u8 *pos, buf[128];
+       u8 *pos, buf[256];
        int res;
 
 #ifdef CONFIG_TESTING_OPTIONS
@@ -561,6 +680,31 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
                        return res;
                pos += res;
        }
+       if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+           wpa_auth->conf.rsn_override_key_mgmt) {
+               res = wpa_write_rsne_override(&wpa_auth->conf,
+                                             pos, buf + sizeof(buf) - pos);
+               if (res < 0)
+                       return res;
+               pos += res;
+       }
+       if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+           wpa_auth->conf.rsn_override_key_mgmt_2) {
+               res = wpa_write_rsne_override_2(&wpa_auth->conf, pos,
+                                               buf + sizeof(buf) - pos);
+               if (res < 0)
+                       return res;
+               pos += res;
+       }
+       if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+           (wpa_auth->conf.rsn_override_key_mgmt ||
+            wpa_auth->conf.rsn_override_key_mgmt_2)) {
+               res = wpa_write_rsnxe_override(&wpa_auth->conf, pos,
+                                              buf + sizeof(buf) - pos);
+               if (res < 0)
+                       return res;
+               pos += res;
+       }
 
        os_free(wpa_auth->wpa_ie);
        wpa_auth->wpa_ie = os_malloc(pos - buf);
@@ -569,6 +713,59 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
        os_memcpy(wpa_auth->wpa_ie, buf, pos - buf);
        wpa_auth->wpa_ie_len = pos - buf;
 
+       if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+           wpa_auth->conf.rsn_override_key_mgmt) {
+               res = wpa_write_rsne_override(&wpa_auth->conf, buf,
+                                             sizeof(buf));
+               if (res < 0)
+                       return res;
+               os_free(wpa_auth->rsne_override);
+               wpa_auth->rsne_override = os_malloc(res - 4);
+               if (!wpa_auth->rsne_override)
+                       return -1;
+               pos = wpa_auth->rsne_override;
+               *pos++ = WLAN_EID_RSN;
+               *pos++ = res - 2 - 4;
+               os_memcpy(pos, &buf[2 + 4], res - 2 - 4);
+       }
+
+       if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+           wpa_auth->conf.rsn_override_key_mgmt_2) {
+               res = wpa_write_rsne_override_2(&wpa_auth->conf, buf,
+                                               sizeof(buf));
+               if (res < 0)
+                       return res;
+               os_free(wpa_auth->rsne_override_2);
+               wpa_auth->rsne_override_2 = os_malloc(res - 4);
+               if (!wpa_auth->rsne_override_2)
+                       return -1;
+               pos = wpa_auth->rsne_override_2;
+               *pos++ = WLAN_EID_RSN;
+               *pos++ = res - 2 - 4;
+               os_memcpy(pos, &buf[2 + 4], res - 2 - 4);
+       }
+
+       if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+           (wpa_auth->conf.rsn_override_key_mgmt ||
+            wpa_auth->conf.rsn_override_key_mgmt_2)) {
+               res = wpa_write_rsnxe_override(&wpa_auth->conf, buf,
+                                              sizeof(buf));
+               if (res < 0)
+                       return res;
+               os_free(wpa_auth->rsnxe_override);
+               if (res == 0) {
+                       wpa_auth->rsnxe_override = NULL;
+                       return 0;
+               }
+               wpa_auth->rsnxe_override = os_malloc(res - 4);
+               if (!wpa_auth->rsnxe_override)
+                       return -1;
+               pos = wpa_auth->rsnxe_override;
+               *pos++ = WLAN_EID_RSNX;
+               *pos++ = res - 2 - 4;
+               os_memcpy(pos, &buf[2 + 4], res - 2 - 4);
+       }
+
        return 0;
 }
 
@@ -773,7 +970,9 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
                return WPA_INVALID_GROUP;
        }
 
-       key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt;
+       key_mgmt = data.key_mgmt & (wpa_auth->conf.wpa_key_mgmt |
+                                   wpa_auth->conf.rsn_override_key_mgmt |
+                                   wpa_auth->conf.rsn_override_key_mgmt_2);
        if (!key_mgmt) {
                wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from "
                           MACSTR, data.key_mgmt, MAC2STR(sm->addr));
@@ -843,7 +1042,10 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
                sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
 
        if (version == WPA_PROTO_RSN)
-               ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise;
+               ciphers = data.pairwise_cipher &
+                       (wpa_auth->conf.rsn_pairwise |
+                        wpa_auth->conf.rsn_override_pairwise |
+                        wpa_auth->conf.rsn_override_pairwise_2);
        else
                ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise;
        if (!ciphers) {
@@ -1229,7 +1431,7 @@ bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth,
                return false;
 
        /* RSN Capability (B0..B15) */
-       WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf));
+       WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf, conf->ieee80211w));
        pos += 2;
 
        /* Group Data Cipher Suite Selector (B16..B21) */