]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
SSID protection in 4-way handshake on STA
authorJouni Malinen <quic_jouni@quicinc.com>
Tue, 18 Jun 2024 22:07:36 +0000 (01:07 +0300)
committerJouni Malinen <j@w1.fi>
Wed, 19 Jun 2024 09:31:04 +0000 (12:31 +0300)
Add support for SSID protection in 4-way handshake based on the
mechanism added in IEEE 802.11REVme/D6.0. This is a mitigation against
CVE-2023-52424 (a.k.a. the SSID Confusion Attack).

This functionality is disabled by default and can be enabled with
ssid_protection=1 in the network profile. Once there has been more
testing of this to confirm there is no significant interoperability
issues, the goal is to be able to change this to be enabled by default.

Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
12 files changed:
src/common/ieee802_11_defs.h
src/common/wpa_common.c
src/common/wpa_common.h
src/rsn_supp/wpa.c
src/rsn_supp/wpa.h
src/rsn_supp/wpa_i.h
src/rsn_supp/wpa_ie.c
wpa_supplicant/config.c
wpa_supplicant/config_file.c
wpa_supplicant/config_ssid.h
wpa_supplicant/sme.c
wpa_supplicant/wpa_supplicant.c

index 5b39a61e10672eb4afc4e7d221b05c012744de17..0fe63102603fd114931490a11485bc84a22cc581 100644 (file)
 #define WLAN_RSNX_CAPAB_SECURE_RTT 9
 #define WLAN_RSNX_CAPAB_URNM_MFPR_X20 10
 #define WLAN_RSNX_CAPAB_URNM_MFPR 15
+#define WLAN_RSNX_CAPAB_SSID_PROTECTION 21
 
 /* Multiple BSSID element subelements */
 #define WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE 0
index 6ea3311ce1277330cb0e53106e88ef1ea1ce8b5f..94ce43ddeae0b850213267d731e818f247225acd 100644 (file)
@@ -3743,6 +3743,11 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie)
                                ie->supp_oper_classes = pos + 2;
                                ie->supp_oper_classes_len = pos[1];
                        }
+               } else if (*pos == WLAN_EID_SSID) {
+                       ie->ssid = pos + 2;
+                       ie->ssid_len = pos[1];
+                       wpa_hexdump_ascii(MSG_DEBUG, "RSN: SSID in EAPOL-Key",
+                                         ie->ssid, ie->ssid_len);
                } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
                        ret = wpa_parse_generic(pos, ie);
                        if (ret == 1) {
index 8f52984a4c06a4f065ada5e031fa8358c93357ce..1e3136843437250cf3ba94345cf34dc8d8a8a35a 100644 (file)
@@ -698,6 +698,8 @@ struct wpa_eapol_ie_parse {
        size_t supp_channels_len;
        const u8 *supp_oper_classes;
        size_t supp_oper_classes_len;
+       const u8 *ssid;
+       size_t ssid_len;
        u8 qosinfo;
        u16 aid;
        const u8 *wmm;
index 8956c40722f8eb169b1676c0654ece07fa4d6ec7..21dfeb5fb61382f3b9540bdb5d399ce3800686db 100644 (file)
@@ -2542,6 +2542,28 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
        if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
                goto failed;
 
+       if (sm->ssid_protection) {
+               if (!ie.ssid) {
+                       wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                               "RSN: No SSID included in EAPOL-Key msg 3/4");
+                       goto failed;
+               }
+
+               if (ie.ssid_len != sm->ssid_len ||
+                   os_memcmp(ie.ssid, sm->ssid, sm->ssid_len) != 0) {
+                       wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                               "RSN: SSID mismatch in EAPOL-Key msg 3/4");
+                       wpa_hexdump_ascii(MSG_DEBUG, "RSN: Received SSID",
+                                         ie.ssid, ie.ssid_len);
+                       wpa_hexdump_ascii(MSG_DEBUG, "RSN: Expected SSID",
+                                         sm->ssid, sm->ssid_len);
+                       goto failed;
+               }
+
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                       "RSN: SSID matched expected value");
+       }
+
        if (mlo && !ie.valid_mlo_gtks) {
                wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                        "MLO RSN: No GTK KDE included in EAPOL-Key msg 3/4");
@@ -4460,6 +4482,20 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)
 }
 
 
+void wpa_sm_set_ssid(struct wpa_sm *sm, const u8 *ssid, size_t ssid_len)
+{
+       if (!sm)
+               return;
+
+       if (ssid) {
+               os_memcpy(sm->ssid, ssid, ssid_len);
+               sm->ssid_len = ssid_len;
+       } else {
+               sm->ssid_len = 0;
+       }
+}
+
+
 int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo)
 {
        int i;
@@ -4689,6 +4725,9 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
        case WPA_PARAM_FT_PREPEND_PMKID:
                sm->ft_prepend_pmkid = value;
                break;
+       case WPA_PARAM_SSID_PROTECTION:
+               sm->ssid_protection = value;
+               break;
        default:
                break;
        }
@@ -4960,6 +4999,14 @@ int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len)
                sm->assoc_rsnxe_len = len;
        }
 
+       if (sm->ssid_protection &&
+           !ieee802_11_rsnx_capab(sm->assoc_rsnxe,
+                                  WLAN_RSNX_CAPAB_SSID_PROTECTION)) {
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                       "RSN: Disabling SSID protection based on own RSNXE update");
+               sm->ssid_protection = 0;
+       }
+
        return 0;
 }
 
index ba5c3e1b4390ec3cc19ed5cd6d97040124c06229..c3ea689068fe4a6cc82a17c8259fafd7b0696a13 100644 (file)
@@ -135,6 +135,7 @@ enum wpa_sm_conf_params {
        WPA_PARAM_ENCRYPT_EAPOL_M2,
        WPA_PARAM_ENCRYPT_EAPOL_M4,
        WPA_PARAM_FT_PREPEND_PMKID,
+       WPA_PARAM_SSID_PROTECTION,
 };
 
 struct rsn_supp_config {
@@ -188,6 +189,7 @@ void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm);
 void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth);
 void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx);
 void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config);
+void wpa_sm_set_ssid(struct wpa_sm *sm, const u8 *ssid, size_t ssid_len);
 void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr);
 void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
                       const char *bridge_ifname);
index ca97c12937cf62d414b7817ba2aea3680cb67bfe..dc429b8b261e7c3213c7bbaa7b280a7d90337fb1 100644 (file)
@@ -112,6 +112,7 @@ struct wpa_sm {
        unsigned int secure_ltf:1;
        unsigned int secure_rtt:1;
        unsigned int prot_range_neg:1;
+       unsigned int ssid_protection:1;
 
        u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
        size_t assoc_wpa_ie_len;
index d1510aad788c3fde320a69424dd70e19a0755956..eeedf8ba54a8b39ab1a107920c07d2587672c041 100644 (file)
@@ -366,7 +366,7 @@ int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len)
 int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len)
 {
        u8 *pos = rsnxe;
-       u16 capab = 0;
+       u32 capab = 0, tmp;
        size_t flen;
 
        if (wpa_key_mgmt_sae(sm->key_mgmt) &&
@@ -385,20 +385,27 @@ int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len)
                capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
        if (sm->prot_range_neg)
                capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+       if (sm->ssid_protection)
+               capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
 
-       flen = (capab & 0xff00) ? 2 : 1;
        if (!capab)
                return 0; /* no supported extended RSN capabilities */
+       tmp = capab;
+       flen = 0;
+       while (tmp) {
+               flen++;
+               tmp >>= 8;
+       }
        if (rsnxe_len < 2 + flen)
                return -1;
        capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
 
        *pos++ = WLAN_EID_RSNX;
        *pos++ = flen;
-       *pos++ = capab & 0x00ff;
-       capab >>= 8;
-       if (capab)
-               *pos++ = capab;
+       while (capab) {
+               *pos++ = capab & 0xff;
+               capab >>= 8;
+       }
 
        return pos - rsnxe;
 }
index 858c9b312e7715d3f2d9ce648a4342274bafb8fb..24861c68f0bd8957b4916f7dbfc390027e3c1477 100644 (file)
@@ -2755,6 +2755,7 @@ static const struct parse_data ssid_fields[] = {
        { INT_RANGE(disable_eht, 0, 1)},
        { INT_RANGE(enable_4addr_mode, 0, 1)},
        { INT_RANGE(max_idle, 0, 65535)},
+       { INT_RANGE(ssid_protection, 0, 1)},
 };
 
 #undef OFFSET
index 6dffcaf77112817c62e9996a144589265d81aa7b..ad37bcf22f33bcfec587431a7e5fbd153ccebbf2 100644 (file)
@@ -894,6 +894,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
        INT(disable_eht);
        INT(enable_4addr_mode);
        INT(max_idle);
+       INT(ssid_protection);
 
 #undef STR
 #undef INT
index 74f7fed8946644196e185771c67ef171fd6e5c91..d64c305082c1e4f4da3fa254a2d60f29a6ee3446 100644 (file)
@@ -1278,6 +1278,11 @@ struct wpa_ssid {
         * as the maximum idle period for the STA during association.
         */
        int max_idle;
+
+       /**
+        * ssid_protection - Whether to use SSID protection in 4-way handshake
+        */
+       bool ssid_protection;
 };
 
 #endif /* CONFIG_SSID_H */
index a6f51a5f0220378192dc15eac1b3e3a727fa42a1..906fa1036a55fcd3b309b62a65ee630932a37c3d 100644 (file)
@@ -1048,6 +1048,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        old_ssid = wpa_s->current_ssid;
        wpa_s->current_ssid = ssid;
        wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+       wpa_sm_set_ssid(wpa_s->wpa, bss->ssid, bss->ssid_len);
        wpa_supplicant_initiate_eapol(wpa_s);
 
 #ifdef CONFIG_FILS
index 5bca64e541748dea9bb85c7163f014a6a5126b07..1b5a26090bfff6f83911a77f3272d9262bae21b3 100644 (file)
@@ -443,6 +443,7 @@ void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
        wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
        wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
                         wpa_s->mgmt_group_cipher);
+       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SSID_PROTECTION, 0);
 
        pmksa_cache_clear_current(wpa_s->wpa);
        os_memset(&mlo, 0, sizeof(mlo));
@@ -2024,6 +2025,22 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                wmm = !!wpa_bss_get_vendor_ie(bss, WMM_IE_VENDOR_TYPE);
        wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_WMM_ENABLED, wmm);
 
+       if (ssid->ssid_protection && proto == WPA_PROTO_RSN) {
+               bool ssid_prot;
+
+               /* Enable SSID protection based on the AP advertising support
+                * for it to avoid potential interoperability issues with
+                * incorrect AP behavior if we were to send an "unexpected"
+                * RSNXE with multiple octets of payload. */
+               ssid_prot = ieee802_11_rsnx_capab(
+                       bss_rsnx, WLAN_RSNX_CAPAB_SSID_PROTECTION);
+               if (!skip_default_rsne)
+                       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SSID_PROTECTION,
+                                        proto == WPA_PROTO_RSN && ssid_prot);
+       } else {
+               wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SSID_PROTECTION, false);
+       }
+
        if (!skip_default_rsne) {
                if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie,
                                                    wpa_ie_len)) {
@@ -4550,6 +4567,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
        }
 
        wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+       if (bss)
+               wpa_sm_set_ssid(wpa_s->wpa, bss->ssid, bss->ssid_len);
        wpa_supplicant_initiate_eapol(wpa_s);
        if (old_ssid != wpa_s->current_ssid)
                wpas_notify_network_changed(wpa_s);