diff --git a/hostapd/config_file.c b/hostapd/config_file.c index c96aa37c2..8933908b2 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -1,6 +1,6 @@ /* * hostapd / Configuration file parser - * Copyright (c) 2003-2018, Jouni Malinen + * Copyright (c) 2003-2024, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -4450,6 +4450,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } else if (os_strcmp(buf, "eapol_m3_no_encrypt") == 0) { bss->eapol_m3_no_encrypt = atoi(pos); + } else if (os_strcmp(buf, "test_assoc_comeback_type") == 0) { + bss->test_assoc_comeback_type = atoi(pos); #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_SAE } else if (os_strcmp(buf, "sae_password") == 0) { diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 1ff677371..ebf6828d8 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -1,6 +1,6 @@ /* * hostapd / Configuration helper functions - * Copyright (c) 2003-2022, Jouni Malinen + * Copyright (c) 2003-2024, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -165,6 +165,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) #ifdef CONFIG_TESTING_OPTIONS bss->sae_commit_status = -1; + bss->test_assoc_comeback_type = -1; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_PASN diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 46a88394a..8c459bd59 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -1,6 +1,6 @@ /* * hostapd / Configuration definitions and helpers functions - * Copyright (c) 2003-2022, Jouni Malinen + * Copyright (c) 2003-2024, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -708,6 +708,7 @@ struct hostapd_bss_config { struct wpabuf *eapol_m1_elements; struct wpabuf *eapol_m3_elements; bool eapol_m3_no_encrypt; + int test_assoc_comeback_type; #ifdef CONFIG_IEEE80211BE u16 eht_oper_puncturing_override; diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index c2d38e715..0cd6f9503 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.11 Management - * Copyright (c) 2002-2012, Jouni Malinen + * Copyright (c) 2002-2024, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -21,16 +21,25 @@ #include "ieee802_11.h" +static u8 * hostapd_eid_timeout_interval(u8 *pos, u8 type, u32 value) +{ + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = type; + WPA_PUT_LE32(pos, value); + pos += 4; + + return pos; +} + + u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, struct sta_info *sta, u8 *eid) { - u8 *pos = eid; u32 timeout, tu; struct os_reltime now, passed; + u8 type = WLAN_TIMEOUT_ASSOC_COMEBACK; - *pos++ = WLAN_EID_TIMEOUT_INTERVAL; - *pos++ = 5; - *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; os_get_reltime(&now); os_reltime_sub(&now, &sta->sa_query_start, &passed); tu = (passed.sec * 1000000 + passed.usec) / 1024; @@ -40,10 +49,12 @@ u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, timeout = 0; if (timeout < hapd->conf->assoc_sa_query_max_timeout) timeout++; /* add some extra time for local timers */ - WPA_PUT_LE32(pos, timeout); - pos += 4; - return pos; +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->test_assoc_comeback_type != -1) + type = hapd->conf->test_assoc_comeback_type; +#endif /* CONFIG_TESTING_OPTIONS */ + return hostapd_eid_timeout_interval(eid, type, timeout); } diff --git a/tests/hwsim/test_ap_pmf.py b/tests/hwsim/test_ap_pmf.py index 20ad4ce06..194cb7e28 100644 --- a/tests/hwsim/test_ap_pmf.py +++ b/tests/hwsim/test_ap_pmf.py @@ -1,5 +1,5 @@ # Protected management frames tests -# Copyright (c) 2013, Jouni Malinen +# Copyright (c) 2013-2024, Jouni Malinen # # This software may be distributed under the terms of the BSD license. # See README for more details. @@ -272,6 +272,61 @@ def run_ap_pmf_assoc_comeback(dev, apdev, comeback=None): dev[0].p2p_interface_addr()) < 1: raise Exception("AP did not use association comeback request") +def test_ap_pmf_assoc_comeback_in_wpas(dev, apdev): + """WPA2-PSK AP with PMF association comeback in wpa_supplicant""" + ssid = "assoc-comeback" + params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678") + params["wpa_key_mgmt"] = "WPA-PSK-SHA256" + params["ieee80211w"] = "2" + params["test_assoc_comeback_type"] = "255" + hapd = hostapd.add_ap(apdev[0], params) + + dev[0].set("test_assoc_comeback_type", "255") + dev[0].connect(ssid, psk="12345678", ieee80211w="1", + key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2", + scan_freq="2412") + hapd.wait_sta(wait_4way_hs=True) + hapd.set("ext_mgmt_frame_handling", "1") + dev[0].request("DISCONNECT") + dev[0].wait_disconnected(timeout=10) + ev = hapd.wait_event(["MGMT-RX"], timeout=1) + if ev is None: + raise Exception("Deauthentication frame RX not reported") + hapd.set("ext_mgmt_frame_handling", "0") + dev[0].request("REASSOCIATE") + ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10) + if ev is None or "status_code=30" not in ev: + raise Exception("Association comeback not requested") + ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", + "CTRL-EVENT-ASSOC-REJECT"], timeout=10) + if ev is None: + raise Exception("Association not reported") + if "CTRL-EVENT-ASSOC-REJECT" in ev: + raise Exception("Unexpected association rejection: " + ev) + hapd.wait_4way_hs() + + hapd.set("ext_mgmt_frame_handling", "1") + dev[0].request("DISCONNECT") + dev[0].wait_disconnected(timeout=10) + ev = hapd.wait_event(["MGMT-RX"], timeout=1) + if ev is None: + raise Exception("Deauthentication frame RX not reported") + hapd.set("ext_mgmt_frame_handling", "0") + dev[0].set("test_assoc_comeback_type", "254") + dev[0].request("REASSOCIATE") + ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10) + if ev is None or "status_code=30" not in ev: + raise Exception("Association comeback not requested") + ev = dev[0].wait_event(["SME: Temporary assoc reject: missing association comeback time", + "CTRL-EVENT-CONNECTED", + "CTRL-EVENT-ASSOC-REJECT"], timeout=10) + if ev is None: + raise Exception("Association not reported") + if "SME: Temporary assoc reject: missing association comeback time" not in ev: + raise Exception("Unexpected result: " + ev) + dev[0].wait_connected(timeout=20, error="Timeout on re-connection with misbehaving AP") + hapd.wait_4way_hs() + @remote_compatible def test_ap_pmf_assoc_comeback2(dev, apdev): """WPA2-PSK AP with PMF association comeback (using DROP_SA)""" diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 99a8475f0..92b46a897 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / Control interface (shared code for all backends) - * Copyright (c) 2004-2020, Jouni Malinen + * Copyright (c) 2004-2024, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -850,6 +850,8 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, /* Populate value to wpa_sm if already associated. */ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DISABLE_EAPOL_G2_TX, wpa_s->disable_eapol_g2_tx); + } else if (os_strcasecmp(cmd, "test_assoc_comeback_type") == 0) { + wpa_s->test_assoc_comeback_type = atoi(value); #ifdef CONFIG_DPP } else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) { os_free(wpa_s->dpp_config_obj_override); @@ -8888,6 +8890,7 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpa_s->oci_freq_override_fils_assoc = 0; wpa_s->oci_freq_override_wnm_sleep = 0; wpa_s->disable_eapol_g2_tx = 0; + wpa_s->test_assoc_comeback_type = -1; #ifdef CONFIG_DPP os_free(wpa_s->dpp_config_obj_override); wpa_s->dpp_config_obj_override = NULL; diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index eff7430ef..706310b61 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -1,6 +1,6 @@ /* * wpa_supplicant - SME - * Copyright (c) 2009-2014, Jouni Malinen + * Copyright (c) 2009-2024, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -2745,6 +2745,12 @@ static bool sme_try_assoc_comeback(struct wpa_supplicant *wpa_s, struct ieee802_11_elems elems; u32 timeout_interval; unsigned long comeback_usec; + u8 type = WLAN_TIMEOUT_ASSOC_COMEBACK; + +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->test_assoc_comeback_type != -1) + type = wpa_s->test_assoc_comeback_type; +#endif /* CONFIG_TESTING_OPTIONS */ if (ieee802_11_parse_elems(data->assoc_reject.resp_ies, data->assoc_reject.resp_ies_len, @@ -2760,7 +2766,7 @@ static bool sme_try_assoc_comeback(struct wpa_supplicant *wpa_s, return false; } - if (elems.timeout_int[0] != WLAN_TIMEOUT_ASSOC_COMEBACK) { + if (elems.timeout_int[0] != type) { wpa_msg(wpa_s, MSG_INFO, "SME: Temporary assoc reject: missing association comeback time"); return false; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index b8d15e409..ecfc3b3ef 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - * Copyright (c) 2003-2022, Jouni Malinen + * Copyright (c) 2003-2024, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -5857,6 +5857,7 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent) dl_list_init(&wpa_s->fils_hlp_req); #ifdef CONFIG_TESTING_OPTIONS dl_list_init(&wpa_s->drv_signal_override); + wpa_s->test_assoc_comeback_type = -1; #endif /* CONFIG_TESTING_OPTIONS */ #ifndef CONFIG_NO_ROBUST_AV dl_list_init(&wpa_s->active_scs_ids); diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 301be2051..933fc3626 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1,6 +1,6 @@ /* * wpa_supplicant - Internal definitions - * Copyright (c) 2003-2014, Jouni Malinen + * Copyright (c) 2003-2024, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -1366,6 +1366,7 @@ struct wpa_supplicant { unsigned int oci_freq_override_fils_assoc; unsigned int oci_freq_override_wnm_sleep; unsigned int disable_eapol_g2_tx; + int test_assoc_comeback_type; #endif /* CONFIG_TESTING_OPTIONS */ struct wmm_ac_assoc_data *wmm_ac_assoc_info;