]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
FT: Do not try to use FT protocol between mobility domains
authorJouni Malinen <quic_jouni@quicinc.com>
Tue, 8 Nov 2022 21:43:43 +0000 (23:43 +0200)
committerJouni Malinen <j@w1.fi>
Tue, 8 Nov 2022 22:54:41 +0000 (00:54 +0200)
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>
src/rsn_supp/wpa.c
src/rsn_supp/wpa.h
src/rsn_supp/wpa_ft.c
src/rsn_supp/wpa_i.h
tests/hwsim/test_ap_ft.py
tests/hwsim/wpasupplicant.py
wpa_supplicant/ctrl_iface.c
wpa_supplicant/sme.c
wpa_supplicant/wpa_supplicant.c

index cd6e7575b8ba519f385be39c5d360b4ecabf62e5..74ea660883f8af2e4891af51beb3336a3b971cbe 100644 (file)
@@ -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);
index 0ee847b8ba88f82f7d54ab80775c5fd93eb27fef..f8854078a05c0f9b261d1da40fe5a21dedd1eb1f 100644 (file)
@@ -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
 
index 70fd348686761fc50180bf7ad14746c7adb22164..fdf921d5d1188b8a4ed8ab138de5a1d332dec790 100644 (file)
@@ -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));
 
index 5912a3b8f28d5086b225f804e675552792dc49de..3c933e9e368307485603ed5bdb3bbdbdb56e2c7d 100644 (file)
@@ -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];
index 12afe5b8dcaef766d177b67853750f86ccc5b0ba..d5d5e154e2cbef0e12955b06dc3505fb36fe510d 100644 (file)
@@ -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"""
index fe189278597258f221a4d17fed19eac70c12dee9..1b2f1786168aa9d24dd55aa2e43eb8044eba4f40 100644 (file)
@@ -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)
index 6dbab7a2853311de6708f669b93a7760717a85d3..05c9299f22204dea3595c669517f099fe044e0d2 100644 (file)
@@ -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 */
 
index 98a975c093104160806175bc5411f88d8e3311e6..c5e473a0b2b2f6396f5292e5caa4d7afff470a4b 100644 (file)
@@ -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;
index b55612aec1a1a3a74f263fcde2d76b6922a606ee..f9409f56a829a8168f65d5ce3e123071f4079338 100644 (file)
@@ -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;