diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index d8beecc23..9ef073114 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 af4a6f90d..9f0861cd2 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -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 */ diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index 7fa47a1e5..28985f238 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -24,6 +24,15 @@ #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 */ diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 728280e3b..b593f71af 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 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 diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c index dd4bd1996..1f3dd08ba 100644 --- a/wpa_supplicant/pasn_supplicant.c +++ b/wpa_supplicant/pasn_supplicant.c @@ -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; diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index de22a495a..2e8584565 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -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 */