From dab7549d68754bd08ca430a78dd85021c44dc3f5 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 19 Jun 2024 01:07:36 +0300 Subject: [PATCH] SSID protection in 4-way handshake on STA 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 --- src/common/ieee802_11_defs.h | 1 + src/common/wpa_common.c | 5 ++++ src/common/wpa_common.h | 2 ++ src/rsn_supp/wpa.c | 47 +++++++++++++++++++++++++++++++++ src/rsn_supp/wpa.h | 2 ++ src/rsn_supp/wpa_i.h | 1 + src/rsn_supp/wpa_ie.c | 19 ++++++++----- wpa_supplicant/config.c | 1 + wpa_supplicant/config_file.c | 1 + wpa_supplicant/config_ssid.h | 5 ++++ wpa_supplicant/sme.c | 1 + wpa_supplicant/wpa_supplicant.c | 19 +++++++++++++ 12 files changed, 98 insertions(+), 6 deletions(-) diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 5b39a61e1..0fe631026 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -616,6 +616,7 @@ #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 diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 6ea3311ce..94ce43dde 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -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) { diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 8f52984a4..1e3136843 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -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; diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 8956c4072..21dfeb5fb 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -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; } diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index ba5c3e1b4..c3ea68906 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -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); diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index ca97c1293..dc429b8b2 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -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; diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index d1510aad7..eeedf8ba5 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -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; } diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 858c9b312..24861c68f 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -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 diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 6dffcaf77..ad37bcf22 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -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 diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index 74f7fed89..d64c30508 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -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 */ diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index a6f51a5f0..906fa1036 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -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 diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 5bca64e54..1b5a26090 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -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);