diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index cd6e7575b..74ea66088 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -4770,12 +4770,21 @@ void wpa_sm_drop_sa(struct wpa_sm *sm) } -int wpa_sm_has_ptk(struct wpa_sm *sm) +#ifdef CONFIG_IEEE80211R +bool wpa_sm_has_ft_keys(struct wpa_sm *sm, const u8 *md) { - if (sm == NULL) - return 0; + if (!sm) + return false; + if (!wpa_key_mgmt_ft(sm->key_mgmt) || + os_memcmp(md, sm->key_mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + /* Do not allow FT protocol to be used even if we were to have + * an PTK since the mobility domain has changed. */ + return false; + } return sm->ptk_set; } +#endif /* CONFIG_IEEE80211R */ int wpa_sm_has_ptk_installed(struct wpa_sm *sm) @@ -5485,6 +5494,9 @@ static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf) } os_memcpy(pos, sm->pmk_r1_name, WPA_PMK_NAME_LEN); + os_memcpy(sm->key_mobility_domain, sm->mobility_domain, + MOBILITY_DOMAIN_ID_LEN); + if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { /* Management Group Cipher Suite */ pos = wpabuf_put(buf, RSN_SELECTOR_LEN); diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 0ee847b8b..f8854078a 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -229,7 +229,7 @@ struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_get(struct wpa_sm *sm, const u8 *pmkid, const void *network_ctx, int akmp); -int wpa_sm_has_ptk(struct wpa_sm *sm); +bool wpa_sm_has_ft_keys(struct wpa_sm *sm, const u8 *md); int wpa_sm_has_ptk_installed(struct wpa_sm *sm); void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr); @@ -483,7 +483,7 @@ void wpa_reset_ft_completed(struct wpa_sm *sm); int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *src_addr); int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, - const u8 *mdie); + const u8 *mdie, bool force); #ifdef CONFIG_PASN diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index 70fd34868..fdf921d5d 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -89,6 +89,9 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, return ret; } + os_memcpy(sm->key_mobility_domain, sm->mobility_domain, + MOBILITY_DOMAIN_ID_LEN); + #ifdef CONFIG_PASN if (sm->secure_ltf && ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) @@ -704,6 +707,9 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, kdk_len) < 0) return -1; + os_memcpy(sm->key_mobility_domain, sm->mobility_domain, + MOBILITY_DOMAIN_ID_LEN); + #ifdef CONFIG_PASN if (sm->secure_ltf && ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) && @@ -1258,14 +1264,24 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, * @sm: Pointer to WPA state machine data from wpa_sm_init() * @target_ap: Target AP Address * @mdie: Mobility Domain IE from the target AP + * @force: Whether to force the request and ignore mobility domain differences + * (only for testing purposes) * Returns: 0 on success, -1 on failure */ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, - const u8 *mdie) + const u8 *mdie, bool force) { u8 *ft_ies; size_t ft_ies_len; + if (!force && + (!mdie || mdie[1] < 3 || !wpa_sm_has_ft_keys(sm, mdie + 2))) { + wpa_printf(MSG_DEBUG, "FT: Cannot use over-the DS with " MACSTR + " - no keys matching the mobility domain", + MAC2STR(target_ap)); + return -1; + } + wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR, MAC2STR(target_ap)); diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 5912a3b8f..3c933e9e3 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -150,6 +150,7 @@ struct wpa_sm { size_t pmk_r1_len; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 key_mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; size_t r0kh_id_len; u8 r1kh_id[FT_R1KH_ID_LEN]; diff --git a/tests/hwsim/test_ap_ft.py b/tests/hwsim/test_ap_ft.py index 12afe5b8d..d5d5e154e 100644 --- a/tests/hwsim/test_ap_ft.py +++ b/tests/hwsim/test_ap_ft.py @@ -849,7 +849,7 @@ def test_ap_ft_over_ds_unknown_target(dev, apdev): dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2", scan_freq="2412") - dev[0].roam_over_ds("02:11:22:33:44:55", fail_test=True) + dev[0].roam_over_ds("02:11:22:33:44:55", fail_test=True, force=True) @remote_compatible def test_ap_ft_over_ds_unexpected(dev, apdev): @@ -3002,7 +3002,7 @@ def test_ap_ft_internal_rrb_check(dev, apdev): # Try over_ds roaming to non-WPA-enabled AP. # If hostapd does not check hapd->wpa_auth internally, it will crash now. - dev[0].roam_over_ds(apdev[1]['bssid'], fail_test=True) + dev[0].roam_over_ds(apdev[1]['bssid'], fail_test=True, force=True) def test_ap_ft_extra_ie(dev, apdev): """WPA2-PSK-FT AP with WPA2-PSK enabled and unexpected MDE""" diff --git a/tests/hwsim/wpasupplicant.py b/tests/hwsim/wpasupplicant.py index fe1892785..1b2f17861 100644 --- a/tests/hwsim/wpasupplicant.py +++ b/tests/hwsim/wpasupplicant.py @@ -1244,9 +1244,12 @@ class WpaSupplicant: if check_bssid and self.get_status_field('bssid') != bssid: raise Exception("Did not roam to correct BSSID") - def roam_over_ds(self, bssid, fail_test=False): + def roam_over_ds(self, bssid, fail_test=False, force=False): self.dump_monitor() - if "OK" not in self.request("FT_DS " + bssid): + cmd = "FT_DS " + bssid + if force: + cmd += " force" + if "OK" not in self.request(cmd): raise Exception("FT_DS failed") if fail_test: ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=1) diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 6dbab7a28..05c9299f2 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -1317,6 +1317,7 @@ static int wpa_supplicant_ctrl_iface_ft_ds( u8 target_ap[ETH_ALEN]; struct wpa_bss *bss; const u8 *mdie; + bool force = os_strstr(addr, " force") != NULL; if (hwaddr_aton(addr, target_ap)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid " @@ -1332,7 +1333,7 @@ static int wpa_supplicant_ctrl_iface_ft_ds( else mdie = NULL; - return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie); + return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie, force); } #endif /* CONFIG_IEEE80211R */ diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 98a975c09..c5e473a0b 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -619,7 +619,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (wpa_s->sme.prev_bssid_set && wpa_s->sme.ft_used && os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 && - wpa_sm_has_ptk(wpa_s->wpa)) { + wpa_sm_has_ft_keys(wpa_s->wpa, md)) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT " "over-the-air"); params.auth_alg = WPA_AUTH_ALG_FT; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index b55612aec..f9409f56a 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -3490,7 +3490,7 @@ pfs_fail: } #ifdef CONFIG_SME if (len > 0 && wpa_s->sme.ft_used && - wpa_sm_has_ptk(wpa_s->wpa)) { + wpa_sm_has_ft_keys(wpa_s->wpa, md)) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT over-the-air"); algs |= WPA_AUTH_ALG_FT;