From 37a289f8bc764529b3af5e971df7f3b06a55caea Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 19 Jun 2024 01:09:52 +0300 Subject: [PATCH] SSID protection in 4-way handshake on AP 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. 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 --- hostapd/config_file.c | 6 ++++++ hostapd/hostapd.conf | 19 +++++++++++++++++++ src/ap/ap_config.h | 2 ++ src/ap/ieee802_11.c | 7 +++++++ src/ap/ieee802_11_shared.c | 24 +++++++++++++++++------- src/ap/wpa_auth.c | 17 +++++++++++++++++ src/ap/wpa_auth.h | 5 ++++- src/ap/wpa_auth_glue.c | 3 ++- src/ap/wpa_auth_i.h | 2 ++ src/ap/wpa_auth_ie.c | 19 +++++++++++++------ 10 files changed, 89 insertions(+), 15 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index d728be888..96c28aea2 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -5044,6 +5044,12 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } else if (os_strcmp(buf, "rnr") == 0) { bss->rnr = atoi(pos); + } else if (os_strcmp(buf, "ssid_protection") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 1) + return 1; + bss->ssid_protection = val; #ifdef CONFIG_IEEE80211BE } else if (os_strcmp(buf, "ieee80211be") == 0) { conf->ieee80211be = atoi(pos); diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index bc9099bc8..e3367b708 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -2268,6 +2268,25 @@ own_ip_addr=127.0.0.1 # (default: 1 = activated) #pasn_noauth=1 +# SSID protection in 4-way handshake +# The IEEE 802.11i-2004 RSN design did not provide means for protecting the +# SSID in the general case. IEEE P802.11REVme/D6.0 added support for this in +# 4-way handshake. This capability allows a STA to confirm that the AP has the +# same understanding on which SSID is being used for an association in a +# protected manner in cases where both the AP and the STA has this capability. +# This can be used to mitigate CVE-2023-52424 (a.k.a. the SSID Confusion +# Attack). +# +# Ideally, this capability would be enabled by default on the AP, but since this +# is new functionality with limited testing, the default is to disable this for +# now and require explicitly configuration to enable. The default behavior is +# like to change once this capability has received more testing. +# +# 0 = SSID protection in 4-way handshake disabled (default) +# 1 = SSID protection in 4-way handshake enabled +# +#ssid_protection=0 + ##### IEEE 802.11r configuration ############################################## # Mobility Domain identifier (dot11FTMobilityDomainID, MDID) diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 000858322..fda937ecf 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -960,6 +960,8 @@ struct hostapd_bss_config { char *config_id; bool xrates_supported; + bool ssid_protection; + #ifdef CONFIG_IEEE80211BE /* The AP is part of an AP MLD */ u8 mld_ap; diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 80e54b267..6a5137d87 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -4192,6 +4192,13 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, "association"); return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; } + + wpa_auth_set_ssid_protection( + sta->wpa_sm, + hapd->conf->ssid_protection && + ieee802_11_rsnx_capab_len( + elems->rsnxe, elems->rsnxe_len, + WLAN_RSNX_CAPAB_SSID_PROTECTION)); #ifdef CONFIG_HS20 } else if (hapd->conf->osen) { if (!elems->osen) { diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index fd8ec6004..3dd3a6a77 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -1097,7 +1097,7 @@ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len) { u8 *pos = eid; bool sae_pk = false; - u16 capab = 0; + u32 capab = 0, tmp; size_t flen; if (!(hapd->conf->wpa & WPA_PROTO_RSN)) @@ -1126,18 +1126,28 @@ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len) capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT); if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP) capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR); + if (hapd->conf->ssid_protection) + capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION); - flen = (capab & 0xff00) ? 2 : 1; - if (len < 2 + flen || !capab) + if (!capab) + return eid; /* no supported extended RSN capabilities */ + tmp = capab; + flen = 0; + while (tmp) { + flen++; + tmp >>= 8; + } + + if (len < 2 + flen) return eid; /* no supported extended RSN capabilities */ 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; } diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 08260c4b3..7302e6247 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -4684,6 +4684,9 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) kde_len += wpa_auth_ml_kdes_len(sm); + if (sm->ssid_protection) + kde_len += 2 + conf->ssid_len; + #ifdef CONFIG_TESTING_OPTIONS if (conf->eapol_m3_elements) kde_len += wpabuf_len(conf->eapol_m3_elements); @@ -4803,6 +4806,13 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) pos = wpa_auth_ml_kdes(sm, pos); + if (sm->ssid_protection) { + *pos++ = WLAN_EID_SSID; + *pos++ = conf->ssid_len; + os_memcpy(pos, conf->ssid, conf->ssid_len); + pos += conf->ssid_len; + } + #ifdef CONFIG_TESTING_OPTIONS if (conf->eapol_m3_elements) { os_memcpy(pos, wpabuf_head(conf->eapol_m3_elements), @@ -6787,6 +6797,13 @@ void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z) #endif /* CONFIG_DPP2 */ +void wpa_auth_set_ssid_protection(struct wpa_state_machine *sm, bool val) +{ + if (sm) + sm->ssid_protection = val; +} + + void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth, u8 val) { diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 89bc5e7f2..b588f9fdc 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -198,9 +198,9 @@ struct wpa_auth_config { #ifdef CONFIG_OCV int ocv; /* Operating Channel Validation */ #endif /* CONFIG_OCV */ -#ifdef CONFIG_IEEE80211R_AP u8 ssid[SSID_MAX_LEN]; size_t ssid_len; +#ifdef CONFIG_IEEE80211R_AP u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r0_key_holder[FT_R0KH_ID_MAX_LEN]; size_t r0_key_holder_len; @@ -293,6 +293,8 @@ struct wpa_auth_config { int link_id; struct wpa_authenticator *first_link_auth; #endif /* CONFIG_IEEE80211BE */ + + bool ssid_protection; }; typedef enum { @@ -604,6 +606,7 @@ 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_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, u8 val); diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 9f8451004..e725615d9 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -73,11 +73,12 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->beacon_prot = conf->beacon_prot; wconf->group_mgmt_cipher = conf->group_mgmt_cipher; wconf->sae_require_mfp = conf->sae_require_mfp; -#ifdef CONFIG_IEEE80211R_AP + wconf->ssid_protection = conf->ssid_protection; wconf->ssid_len = conf->ssid.ssid_len; if (wconf->ssid_len > SSID_MAX_LEN) wconf->ssid_len = SSID_MAX_LEN; os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len); +#ifdef CONFIG_IEEE80211R_AP os_memcpy(wconf->mobility_domain, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); if (conf->nas_identifier && diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index 97cb0501e..4e5ba3e2e 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -183,6 +183,8 @@ struct wpa_state_machine { struct wpa_authenticator *wpa_auth; } mld_links[MAX_NUM_MLD_LINKS]; #endif /* CONFIG_IEEE80211BE */ + + bool ssid_protection; }; diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index 5a17f7c34..2efadf896 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -409,7 +409,7 @@ 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) { u8 *pos = buf; - u16 capab = 0; + u32 capab = 0, tmp; size_t flen; if (wpa_key_mgmt_sae(conf->wpa_key_mgmt) && @@ -429,20 +429,27 @@ int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len) capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT); if (conf->prot_range_neg) capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR); + if (conf->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 (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 - buf; }