FT: Do not try to use FT protocol between mobility domains
wpa_supplicant has support for only a single FT key hierarchy and as such, cannot use more than a single mobility domain at a time. Do not allow FT protocol to be started if there is a request to reassociate to a different BSS within the same ESS if that BSS is in a different mobility domain. This results in the initial mobility domain association being used whenever moving to another mobility domain. While it would be possible to add support for multiple FT key hierachies and multiple mobility domains in theory, there does not yet seem to be sufficient justification to add the complexity needed for that due to limited, if any, deployment of such networks. As such, it is simplest to just prevent these attempts for now and start with a clean initial mobility domain association. Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
This commit is contained in:
parent
b92f61885c
commit
bbe5f0c1eb
9 changed files with 46 additions and 13 deletions
|
@ -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)
|
if (!sm)
|
||||||
return 0;
|
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;
|
return sm->ptk_set;
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_IEEE80211R */
|
||||||
|
|
||||||
|
|
||||||
int wpa_sm_has_ptk_installed(struct wpa_sm *sm)
|
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(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) {
|
if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
|
||||||
/* Management Group Cipher Suite */
|
/* Management Group Cipher Suite */
|
||||||
pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
|
pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
|
||||||
|
|
|
@ -229,7 +229,7 @@ struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_get(struct wpa_sm *sm,
|
||||||
const u8 *pmkid,
|
const u8 *pmkid,
|
||||||
const void *network_ctx,
|
const void *network_ctx,
|
||||||
int akmp);
|
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);
|
int wpa_sm_has_ptk_installed(struct wpa_sm *sm);
|
||||||
|
|
||||||
void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr);
|
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,
|
int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
|
||||||
size_t ies_len, const u8 *src_addr);
|
size_t ies_len, const u8 *src_addr);
|
||||||
int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
|
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
|
#ifdef CONFIG_PASN
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,9 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
os_memcpy(sm->key_mobility_domain, sm->mobility_domain,
|
||||||
|
MOBILITY_DOMAIN_ID_LEN);
|
||||||
|
|
||||||
#ifdef CONFIG_PASN
|
#ifdef CONFIG_PASN
|
||||||
if (sm->secure_ltf &&
|
if (sm->secure_ltf &&
|
||||||
ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_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)
|
kdk_len) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
os_memcpy(sm->key_mobility_domain, sm->mobility_domain,
|
||||||
|
MOBILITY_DOMAIN_ID_LEN);
|
||||||
|
|
||||||
#ifdef CONFIG_PASN
|
#ifdef CONFIG_PASN
|
||||||
if (sm->secure_ltf &&
|
if (sm->secure_ltf &&
|
||||||
ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_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()
|
* @sm: Pointer to WPA state machine data from wpa_sm_init()
|
||||||
* @target_ap: Target AP Address
|
* @target_ap: Target AP Address
|
||||||
* @mdie: Mobility Domain IE from the target AP
|
* @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
|
* Returns: 0 on success, -1 on failure
|
||||||
*/
|
*/
|
||||||
int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
|
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;
|
u8 *ft_ies;
|
||||||
size_t ft_ies_len;
|
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,
|
wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR,
|
||||||
MAC2STR(target_ap));
|
MAC2STR(target_ap));
|
||||||
|
|
||||||
|
|
|
@ -150,6 +150,7 @@ struct wpa_sm {
|
||||||
size_t pmk_r1_len;
|
size_t pmk_r1_len;
|
||||||
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
|
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
|
||||||
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
|
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
|
||||||
|
u8 key_mobility_domain[MOBILITY_DOMAIN_ID_LEN];
|
||||||
u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
|
u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
|
||||||
size_t r0kh_id_len;
|
size_t r0kh_id_len;
|
||||||
u8 r1kh_id[FT_R1KH_ID_LEN];
|
u8 r1kh_id[FT_R1KH_ID_LEN];
|
||||||
|
|
|
@ -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",
|
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
|
||||||
scan_freq="2412")
|
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
|
@remote_compatible
|
||||||
def test_ap_ft_over_ds_unexpected(dev, apdev):
|
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.
|
# Try over_ds roaming to non-WPA-enabled AP.
|
||||||
# If hostapd does not check hapd->wpa_auth internally, it will crash now.
|
# 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):
|
def test_ap_ft_extra_ie(dev, apdev):
|
||||||
"""WPA2-PSK-FT AP with WPA2-PSK enabled and unexpected MDE"""
|
"""WPA2-PSK-FT AP with WPA2-PSK enabled and unexpected MDE"""
|
||||||
|
|
|
@ -1244,9 +1244,12 @@ class WpaSupplicant:
|
||||||
if check_bssid and self.get_status_field('bssid') != bssid:
|
if check_bssid and self.get_status_field('bssid') != bssid:
|
||||||
raise Exception("Did not roam to correct 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()
|
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")
|
raise Exception("FT_DS failed")
|
||||||
if fail_test:
|
if fail_test:
|
||||||
ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
|
ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
|
||||||
|
|
|
@ -1317,6 +1317,7 @@ static int wpa_supplicant_ctrl_iface_ft_ds(
|
||||||
u8 target_ap[ETH_ALEN];
|
u8 target_ap[ETH_ALEN];
|
||||||
struct wpa_bss *bss;
|
struct wpa_bss *bss;
|
||||||
const u8 *mdie;
|
const u8 *mdie;
|
||||||
|
bool force = os_strstr(addr, " force") != NULL;
|
||||||
|
|
||||||
if (hwaddr_aton(addr, target_ap)) {
|
if (hwaddr_aton(addr, target_ap)) {
|
||||||
wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid "
|
wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid "
|
||||||
|
@ -1332,7 +1333,7 @@ static int wpa_supplicant_ctrl_iface_ft_ds(
|
||||||
else
|
else
|
||||||
mdie = NULL;
|
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 */
|
#endif /* CONFIG_IEEE80211R */
|
||||||
|
|
||||||
|
|
|
@ -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 &&
|
if (wpa_s->sme.prev_bssid_set && wpa_s->sme.ft_used &&
|
||||||
os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 &&
|
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 "
|
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT "
|
||||||
"over-the-air");
|
"over-the-air");
|
||||||
params.auth_alg = WPA_AUTH_ALG_FT;
|
params.auth_alg = WPA_AUTH_ALG_FT;
|
||||||
|
|
|
@ -3490,7 +3490,7 @@ pfs_fail:
|
||||||
}
|
}
|
||||||
#ifdef CONFIG_SME
|
#ifdef CONFIG_SME
|
||||||
if (len > 0 && wpa_s->sme.ft_used &&
|
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,
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
||||||
"SME: Trying to use FT over-the-air");
|
"SME: Trying to use FT over-the-air");
|
||||||
algs |= WPA_AUTH_ALG_FT;
|
algs |= WPA_AUTH_ALG_FT;
|
||||||
|
|
Loading…
Add table
Reference in a new issue