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; }