]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
PASN: Support PASN with FT key derivation
authorIlan Peer <ilan.peer@intel.com>
Wed, 16 Dec 2020 11:01:03 +0000 (13:01 +0200)
committerJouni Malinen <j@w1.fi>
Tue, 26 Jan 2021 15:38:30 +0000 (17:38 +0200)
Add support for PASN authentication with FT key derivation:

- As IEEE P802.11az/D2.6 states that wrapped data is optional and
  is only needed for further validation of the FT security parameters,
  do not include them in the first PASN frame.

- PASN with FT key derivation requires knowledge of the PMK-R1 and
  PMK-R1-Name for the target AP. As the WPA state machine stores PMK-R1,
  etc. only for the currently associated AP, store the mapping of
  BSSID to R1KH-ID for each previous association, so the R1KH-ID
  could be used to derive PMK-R1 and PMK-R1-Name. Do so instead
  of storing the PMK-R1 to avoid maintaining keys that might not
  be used.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
src/rsn_supp/wpa.c
src/rsn_supp/wpa.h
src/rsn_supp/wpa_ft.c
src/rsn_supp/wpa_i.h
wpa_supplicant/pasn_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index d8beecc239b813a5dca25c90e9e87a4ade473f30..9ef07311403e03efcb7441ee047f0f73b4c32d36 100644 (file)
@@ -3819,6 +3819,11 @@ void wpa_sm_drop_sa(struct wpa_sm *sm)
        sm->pmk_r0_len = 0;
        os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
        sm->pmk_r1_len = 0;
+#ifdef CONFIG_PASN
+       os_free(sm->pasn_r1kh);
+       sm->pasn_r1kh = NULL;
+       sm->n_pasn_r1kh = 0;
+#endif /* CONFIG_PASN */
 #endif /* CONFIG_IEEE80211R */
 }
 
index af4a6f90d24859dccae4f2f8373caccf2ab0a599..9f0861cd2d89105b37f97b1a2a074bcfd9b7c3e6 100644 (file)
@@ -430,6 +430,13 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
 int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
                         const u8 *mdie);
 
+#ifdef CONFIG_PASN
+
+int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id,
+                             u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name);
+
+#endif /* CONFIG_PASN */
+
 #else /* CONFIG_IEEE80211R */
 
 static inline int
@@ -473,6 +480,16 @@ wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
        return -1;
 }
 
+#ifdef CONFIG_PASN
+
+int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id,
+                             u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name)
+{
+       return -1;
+}
+
+#endif /* CONFIG_PASN */
+
 #endif /* CONFIG_IEEE80211R */
 
 
index 7fa47a1e5a04be3584ee438a3a12cfa1d81b1f0f..28985f23884cdca522da593546c6cd2c30e6e20e 100644 (file)
 
 #ifdef CONFIG_IEEE80211R
 
+#ifdef CONFIG_PASN
+static void wpa_ft_pasn_store_r1kh(struct wpa_sm *sm, const u8 *bssid);
+#else /* CONFIG_PASN */
+static void wpa_ft_pasn_store_r1kh(struct wpa_sm *sm, const u8 *bssid)
+{
+}
+#endif /* CONFIG_PASN */
+
+
 int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
                      const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
 {
@@ -56,6 +65,9 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
                              sm->r1kh_id, sm->own_addr, sm->pmk_r1,
                              sm->pmk_r1_name) < 0)
                return -1;
+
+       wpa_ft_pasn_store_r1kh(sm, src_addr);
+
        return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, anonce,
                                 sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk,
                                 ptk_name, sm->key_mgmt, sm->pairwise_cipher,
@@ -649,6 +661,9 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
        sm->pmk_r1_len = sm->pmk_r0_len;
 
        bssid = target_ap;
+
+       wpa_ft_pasn_store_r1kh(sm, bssid);
+
        if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
                              anonce, sm->own_addr, bssid,
                              sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt,
@@ -1242,4 +1257,88 @@ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
        return 0;
 }
 
+
+#ifdef CONFIG_PASN
+
+static struct pasn_ft_r1kh * wpa_ft_pasn_get_r1kh(struct wpa_sm *sm,
+                                                 const u8 *bssid)
+{
+       size_t i;
+
+       for (i = 0; i < sm->n_pasn_r1kh; i++)
+               if (os_memcmp(sm->pasn_r1kh[i].bssid, bssid, ETH_ALEN) == 0)
+                       return &sm->pasn_r1kh[i];
+
+       return NULL;
+}
+
+
+static void wpa_ft_pasn_store_r1kh(struct wpa_sm *sm, const u8 *bssid)
+{
+       struct pasn_ft_r1kh *tmp = wpa_ft_pasn_get_r1kh(sm, bssid);
+
+       if (tmp)
+               return;
+
+       tmp = os_realloc_array(sm->pasn_r1kh, sm->n_pasn_r1kh + 1,
+                              sizeof(*tmp));
+       if (!tmp) {
+               wpa_printf(MSG_DEBUG, "PASN: FT: Failed to store R1KH");
+               return;
+       }
+
+       sm->pasn_r1kh = tmp;
+       tmp = &sm->pasn_r1kh[sm->n_pasn_r1kh];
+
+       wpa_printf(MSG_DEBUG, "PASN: FT: Store R1KH for " MACSTR,
+                  MAC2STR(bssid));
+
+       os_memcpy(tmp->bssid, bssid, ETH_ALEN);
+       os_memcpy(tmp->r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN);
+
+       sm->n_pasn_r1kh++;
+}
+
+
+int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *bssid,
+                             u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name)
+{
+       struct pasn_ft_r1kh *r1kh_entry;
+
+       if (sm->key_mgmt != (unsigned int) akmp) {
+               wpa_printf(MSG_DEBUG,
+                          "PASN: FT: Key management mismatch: %u != %u",
+                          sm->key_mgmt, akmp);
+               return -1;
+       }
+
+       r1kh_entry = wpa_ft_pasn_get_r1kh(sm, bssid);
+       if (!r1kh_entry) {
+               wpa_printf(MSG_DEBUG,
+                          "PASN: FT: Cannot find R1KH-ID for " MACSTR,
+                          MAC2STR(bssid));
+               return -1;
+       }
+
+       /*
+        * Note: PMK R0 etc. were already derived and are maintained by the
+        * state machine, and as the same key hierarchy is used, there is no
+        * need to derive them again, so only derive PMK R1 etc.
+        */
+       if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
+                             r1kh_entry->r1kh_id, sm->own_addr, pmk_r1,
+                             pmk_r1_name) < 0)
+               return -1;
+
+       *pmk_r1_len = sm->pmk_r0_len;
+
+       wpa_hexdump_key(MSG_DEBUG, "PASN: FT: PMK-R1", pmk_r1, sm->pmk_r0_len);
+       wpa_hexdump(MSG_DEBUG, "PASN: FT: PMKR1Name", pmk_r1_name,
+                   WPA_PMK_NAME_LEN);
+
+       return 0;
+}
+
+#endif /* CONFIG_PASN */
+
 #endif /* CONFIG_IEEE80211R */
index 728280e3b01dbef2840324e05ba3b0026b6ba3ca..b593f71af4e21f9f72fdb590abb7b82cbd42c671 100644 (file)
 struct wpa_tdls_peer;
 struct wpa_eapol_key;
 
+struct pasn_ft_r1kh {
+       u8 bssid[ETH_ALEN];
+       u8 r1kh_id[FT_R1KH_ID_LEN];
+};
+
 /**
  * struct wpa_sm - Internal WPA state machine data
  */
@@ -152,6 +157,17 @@ struct wpa_sm {
        u8 mdie_ft_capab; /* FT Capability and Policy from target AP MDIE */
        u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */
        size_t assoc_resp_ies_len;
+#ifdef CONFIG_PASN
+       /*
+        * Currently, the WPA state machine stores the PMK-R1, PMK-R1-Name and
+        * R1KH-ID only for the current association. As PMK-R1 is required to
+        * perform PASN authentication with FT, store the R1KH-ID for previous
+        * associations, which would later be used to derive the PMK-R1 as part
+        * of the PASN authentication flow.
+        */
+       struct pasn_ft_r1kh *pasn_r1kh;
+       unsigned int n_pasn_r1kh;
+#endif /* CONFIG_PASN */
 #endif /* CONFIG_IEEE80211R */
 
 #ifdef CONFIG_P2P
index dd4bd1996a165b3bf92b59dfcae2e82bde668c69..1f3dd08ba4c2e42a38bb393d867e5ddd198d77e9 100644 (file)
@@ -569,6 +569,12 @@ static struct wpabuf * wpas_pasn_get_wrapped_data(struct wpa_supplicant *wpa_s)
        case WPA_KEY_MGMT_FT_PSK:
        case WPA_KEY_MGMT_FT_IEEE8021X:
        case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+               /*
+                * Wrapped data with these AKMs is optional and is only needed
+                * for further validation of FT security parameters. For now do
+                * not use them.
+                */
+               return NULL;
        default:
                wpa_printf(MSG_ERROR,
                           "PASN: TODO: Wrapped data for akmp=0x%x",
@@ -593,7 +599,12 @@ static u8 wpas_pasn_get_wrapped_data_format(struct wpas_pasn *pasn)
        case WPA_KEY_MGMT_FT_PSK:
        case WPA_KEY_MGMT_FT_IEEE8021X:
        case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
-               return WPA_PASN_WRAPPED_DATA_FT;
+               /*
+                * Wrapped data with these AKMs is optional and is only needed
+                * for further validation of FT security parameters. For now do
+                * not use them.
+                */
+               return WPA_PASN_WRAPPED_DATA_NO;
        case WPA_KEY_MGMT_PASN:
        default:
                return WPA_PASN_WRAPPED_DATA_NO;
@@ -605,7 +616,7 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s)
 {
        struct wpas_pasn *pasn = &wpa_s->pasn;
        struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
-       struct rsn_pmksa_cache_entry *pmksa;
+       const u8 *pmkid;
        u8 wrapped_data;
        int ret;
 
@@ -632,21 +643,36 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s)
                                   wpa_s->own_addr, pasn->bssid,
                                   pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
 
-       if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
+       pmkid = NULL;
+       if (wpa_key_mgmt_ft(pasn->akmp)) {
+               ret = wpa_pasn_ft_derive_pmk_r1(wpa_s->wpa, pasn->akmp,
+                                               pasn->bssid,
+                                               pasn->pmk_r1,
+                                               &pasn->pmk_r1_len,
+                                               pasn->pmk_r1_name);
+               if (ret) {
+                       wpa_printf(MSG_DEBUG,
+                                  "PASN: FT: Failed to derive keys");
+                       goto fail;
+               }
+
+               pmkid = pasn->pmk_r1_name;
+       } else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
+               struct rsn_pmksa_cache_entry *pmksa;
+
                pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
                                               NULL, NULL, pasn->akmp);
+               if (pmksa)
+                       pmkid = pmksa->pmkid;
 
                /*
                 * Note: Even when PMKSA is available, also add wrapped data as
                 * it is possible that the PMKID is no longer valid at the AP.
                 */
                wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
-       } else {
-               pmksa = NULL;
        }
 
-       if (wpa_pasn_add_rsne(buf, pmksa ? pmksa->pmkid : NULL,
-                             pasn->akmp, pasn->cipher) < 0)
+       if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0)
                goto fail;
 
        if (!wrapped_data_buf)
@@ -791,6 +817,11 @@ static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
        os_memset(&pasn->fils, 0, sizeof(pasn->fils));
 #endif /* CONFIG_FILS*/
 
+#ifdef CONFIG_IEEE80211R
+       forced_memzero(pasn->pmk_r1, sizeof(pasn->pmk_r1));
+       pasn->pmk_r1_len = 0;
+       os_memset(pasn->pmk_r1_name, 0, sizeof(pasn->pmk_r1_name));
+#endif /* CONFIG_IEEE80211R */
        pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
 }
 
@@ -815,6 +846,19 @@ static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s,
                return 0;
        }
 
+       if (wpa_key_mgmt_ft(pasn->akmp)) {
+#ifdef CONFIG_IEEE80211R
+               wpa_printf(MSG_DEBUG, "PASN: FT: Using PMK-R1");
+               pasn->pmk_len = pasn->pmk_r1_len;
+               os_memcpy(pasn->pmk, pasn->pmk_r1, pasn->pmk_r1_len);
+               pasn->using_pmksa = true;
+               return 0;
+#else /* CONFIG_IEEE80211R */
+               wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
+               return -1;
+#endif /* CONFIG_IEEE80211R */
+       }
+
        if (rsn_data->num_pmkid) {
                struct rsn_pmksa_cache_entry *pmksa;
 
index de22a495a20cde102482da7bf40d88115166bae8..2e8584565d2cf8bf39bf3b991877b654744a01f2 100644 (file)
@@ -555,6 +555,12 @@ struct wpas_pasn {
 #ifdef CONFIG_FILS
        struct pasn_fils fils;
 #endif /* CONFIG_FILS */
+
+#ifdef CONFIG_IEEE80211R
+       u8 pmk_r1[PMK_LEN_MAX];
+       size_t pmk_r1_len;
+       u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+#endif /* CONFIG_IEEE80211R */
 };
 #endif /* CONFIG_PASN */