Add support for PASN authentication with FT key derivation: - As Draft 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 SM 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@xxxxxxxxx> --- src/rsn_supp/wpa.c | 5 ++ src/rsn_supp/wpa.h | 17 ++++++ src/rsn_supp/wpa_ft.c | 97 +++++++++++++++++++++++++++++++ src/rsn_supp/wpa_i.h | 16 +++++ wpa_supplicant/pasn_supplicant.c | 66 +++++++++++++++++---- wpa_supplicant/wpa_supplicant_i.h | 6 ++ 6 files changed, 197 insertions(+), 10 deletions(-) diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index a18a8fa7f9..ed9aaae1a3 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -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 */ } diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index cb268648a7..2b181653eb 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -432,6 +432,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 @@ -475,6 +482,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 */ diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index 7fa47a1e5a..cbf3caeb01 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -24,6 +24,10 @@ #ifdef CONFIG_IEEE80211R +#ifdef 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 +60,11 @@ 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; + +#ifdef CONFIG_PASN + wpa_ft_pasn_store_r1kh(sm, src_addr); +#endif /* CONFIG_PASN*/ + 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 +658,11 @@ 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; + +#ifdef CONFIG_PASN + wpa_ft_pasn_store_r1kh(sm, bssid); +#endif /* CONFIG_PASN*/ + 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 +1256,87 @@ 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 SM, + * 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 */ diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index d188a33824..72a6023dd3 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -14,6 +14,11 @@ 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 SM stores the PMK-R1, PMK-R1-Name and R1KH-ID only + * for the current association. As PMK-R1 is required in order 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; + size_t n_pasn_r1kh; +#endif /* CONFIG_PASN*/ #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c index 9ab02192b2..bdf72d51c6 100644 --- a/wpa_supplicant/pasn_supplicant.c +++ b/wpa_supplicant/pasn_supplicant.c @@ -592,6 +592,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", @@ -616,7 +622,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_NO_WRAPPED_DATA; case WPA_KEY_MGMT_PASN: default: return WPA_PASN_NO_WRAPPED_DATA; @@ -629,7 +640,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; @@ -657,22 +668,37 @@ 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_NO_WRAPPED_DATA) { - pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid, + 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_NO_WRAPPED_DATA) { + struct rsn_pmksa_cache_entry *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. + * 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; } - wpa_pasn_add_rsne(buf, pmksa ? pmksa->pmkid : NULL, - pasn->akmp, pasn->cipher); + wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher); if (!wrapped_data_buf) wrapped_data = WPA_PASN_NO_WRAPPED_DATA; @@ -818,6 +844,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 + os_memset(pasn->pmk_r1, 0, sizeof(pasn->pmk_r1)); + os_memset(pasn->pmk_r1_name, 0, sizeof(pasn->pmk_r1_name)); + pasn->pmk_r1_len = 0; +#endif /* CONFIG_IEEE80211R */ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; } @@ -843,6 +874,21 @@ 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 = 1; + return 0; + +#endif /* CONFIG_IEEE80211R */ + + wpa_printf(MSG_DEBUG, "PASN: FT: not supported"); + return -1; + } + if (rsn_data->num_pmkid) { struct rsn_pmksa_cache_entry *pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid, diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 244c43f2ed..3114b60dd2 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -565,6 +565,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 */ -- 2.17.1 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap