From ba150059d1ec964add8f29eb2c92dd6dfde97308 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 14 Feb 2023 11:29:30 +0200 Subject: [PATCH] FT: Store PMK-R0/PMK-R1 after EAPOL-Key msg 2/4 MIC validation hostapd was previously storing the derived PMK-R0 and PMK-R1 as soon as these keys were derived. While that is fine for most purposes, it is unnecessary to do that so quickly and if anything were to fail before the supplicant is able to return a valid EAPOL-Key msg 2/4, there would not really be any real use for the derived keys. For the special case of FT-PSK and VLAN determination based on the wpa_psk file, the VLAN information is set in the per-STA data structures only after the EAPOL-Key msg 2/4 MIC has been verified. This ended up storing the PMK-R0/PMK-R1 entries without correct VLAN assignment and as such, any use of the FT protocol would not be able to transfer the VLAN information through RRB. Split local storing of the FT key hierarchy for the cases using the FT 4-way handshake so that PMK-R0 and PMK-R1 are first derived and then stored as a separate step after having verified the MIC in the EAPOL-Key msg 2/4 (i.e., after having confirmed the per-STA passphrase/PSK was selected) and VLAN update. This fixes VLAN information for the wpa_psk_file cases with FT-PSK. Signed-off-by: Jouni Malinen --- src/ap/wpa_auth.c | 80 ++++++++++++++++++++++++++++++++------------ src/ap/wpa_auth_ft.c | 78 +++++++++++++++++++++++------------------- src/ap/wpa_auth_i.h | 7 +++- 3 files changed, 107 insertions(+), 58 deletions(-) diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 5fde990ea..c49cdaa67 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -59,7 +59,9 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, const u8 *pmk, unsigned int pmk_len, - struct wpa_ptk *ptk, int force_sha256); + struct wpa_ptk *ptk, int force_sha256, + u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name, + size_t *key_len); static void wpa_group_free(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_group_get(struct wpa_authenticator *wpa_auth, @@ -950,6 +952,10 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, const u8 *pmk = NULL; size_t pmk_len; int vlan_id = 0; + u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN_MAX]; + size_t key_len; + int ret = -1; os_memset(&PTK, 0, sizeof(PTK)); for (;;) { @@ -971,8 +977,8 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, pmk_len = sm->pmk_len; } - if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK, 0) < - 0) + if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK, 0, + pmk_r0, pmk_r1, pmk_r0_name, &key_len) < 0) break; if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, @@ -993,7 +999,7 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, if (!ok) { wpa_printf(MSG_DEBUG, "WPA: Earlier SNonce did not result in matching MIC"); - return -1; + goto fail; } wpa_printf(MSG_DEBUG, @@ -1002,14 +1008,26 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && wpa_auth_update_vlan(sm->wpa_auth, sm->addr, vlan_id) < 0) - return -1; + goto fail; + +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) && !sm->ft_completed) { + wpa_printf(MSG_DEBUG, "FT: Store PMK-R0/PMK-R1"); + wpa_auth_ft_store_keys(sm, pmk_r0, pmk_r1, pmk_r0_name, + key_len); + } +#endif /* CONFIG_IEEE80211R_AP */ os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN); os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); forced_memzero(&PTK, sizeof(PTK)); sm->PTK_valid = true; - return 0; + ret = 0; +fail: + forced_memzero(pmk_r0, sizeof(pmk_r0)); + forced_memzero(pmk_r1, sizeof(pmk_r1)); + return ret; } @@ -2347,7 +2365,9 @@ SM_STATE(WPA_PTK, PTKSTART) static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, const u8 *pmk, unsigned int pmk_len, - struct wpa_ptk *ptk, int force_sha256) + struct wpa_ptk *ptk, int force_sha256, + u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name, + size_t *key_len) { const u8 *z = NULL; size_t z_len = 0, kdk_len; @@ -2373,7 +2393,8 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, ptk_name, sm->wpa_key_mgmt, sm->pairwise, kdk_len); } else { - ret = wpa_auth_derive_ptk_ft(sm, ptk); + ret = wpa_auth_derive_ptk_ft(sm, ptk, pmk_r0, pmk_r1, + pmk_r0_name, key_len); } if (ret) { wpa_printf(MSG_ERROR, "FT: PTK derivation failed"); @@ -3058,6 +3079,9 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) struct wpa_eapol_ie_parse kde; int vlan_id = 0; int owe_ptk_workaround = !!wpa_auth->conf.owe_ptk_workaround; + u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN_MAX]; + size_t key_len; SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = false; @@ -3096,7 +3120,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) } if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK, - owe_ptk_workaround == 2) < 0) + owe_ptk_workaround == 2, pmk_r0, pmk_r1, + pmk_r0_name, &key_len) < 0) break; if (mic_len && @@ -3145,7 +3170,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) sm->last_rx_eapol_key, sm->last_rx_eapol_key_len); sm->waiting_radius_psk = 1; - return; + goto out; } if (!ok) { @@ -3153,7 +3178,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) "invalid MIC in msg 2/4 of 4-Way Handshake"); if (psk_found) wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr); - return; + goto out; } /* @@ -3167,12 +3192,12 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) key_data_length = WPA_GET_BE16(mic + mic_len); if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) - sizeof(*key) - mic_len - 2) - return; + goto out; if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 2/4 with invalid Key Data contents"); - return; + goto out; } if (kde.rsn_ie) { eapol_key_ie = kde.rsn_ie; @@ -3199,7 +3224,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) /* MLME-DEAUTHENTICATE.request */ wpa_sta_disconnect(wpa_auth, sm->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); - return; + goto out; } if ((!sm->rsnxe && kde.rsnxe) || (sm->rsnxe && !kde.rsnxe) || @@ -3215,7 +3240,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) /* MLME-DEAUTHENTICATE.request */ wpa_sta_disconnect(wpa_auth, sm->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); - return; + goto out; } #ifdef CONFIG_OCV if (wpa_auth_uses_ocv(sm)) { @@ -3227,14 +3252,14 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) if (wpa_channel_info(wpa_auth, &ci) != 0) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "Failed to get channel info to validate received OCI in EAPOL-Key 2/4"); - return; + goto out; } if (get_sta_tx_parameters(sm, channel_width_to_int(ci.chanwidth), ci.seg1_idx, &tx_chanwidth, &tx_seg1_idx) < 0) - return; + goto out; res = ocv_verify_tx_params(kde.oci, kde.oci_len, &ci, tx_chanwidth, tx_seg1_idx); @@ -3251,7 +3276,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) OCV_FAILURE "addr=" MACSTR " frame=eapol-key-m2 error=%s", MAC2STR(sm->addr), ocv_errorstr); - return; + goto out; } } #endif /* CONFIG_OCV */ @@ -3259,7 +3284,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { wpa_sta_disconnect(wpa_auth, sm->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); - return; + goto out; } #endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_P2P @@ -3297,7 +3322,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) "DPP: Peer indicated it supports PFS and local configuration allows this, but PFS was not negotiated for the association"); wpa_sta_disconnect(wpa_auth, sm->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); - return; + goto out; } } #endif /* CONFIG_DPP2 */ @@ -3317,7 +3342,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN); wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); - return; + goto out; } } #endif /* CONFIG_IEEE80211R_AP */ @@ -3326,7 +3351,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) { wpa_sta_disconnect(wpa_auth, sm->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); - return; + goto out; } sm->pending_1_of_4_timeout = 0; @@ -3342,9 +3367,20 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) sm->MICVerified = true; +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) && !sm->ft_completed) { + wpa_printf(MSG_DEBUG, "FT: Store PMK-R0/PMK-R1"); + wpa_auth_ft_store_keys(sm, pmk_r0, pmk_r1, pmk_r0_name, + key_len); + } +#endif /* CONFIG_IEEE80211R_AP */ + os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); forced_memzero(&PTK, sizeof(PTK)); sm->PTK_valid = true; +out: + forced_memzero(pmk_r0, sizeof(pmk_r0)); + forced_memzero(pmk_r1, sizeof(pmk_r1)); } diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index 88d63bb77..7a5e58401 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -2109,11 +2109,11 @@ int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, } -int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk) +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk, + u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name, + size_t *key_len) { - u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN]; size_t pmk_r0_len, pmk_r1_len; - u8 pmk_r1[PMK_LEN_MAX]; u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *mdid = sm->wpa_auth->conf.mobility_domain; const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder; @@ -2121,12 +2121,6 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk) const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder; const u8 *ssid = sm->wpa_auth->conf.ssid; size_t ssid_len = sm->wpa_auth->conf.ssid_len; - int psk_local = sm->wpa_auth->conf.ft_psk_generate_local; - int expires_in = sm->wpa_auth->conf.r0_key_lifetime; - struct vlan_description vlan; - const u8 *identity, *radius_cui; - size_t identity_len, radius_cui_len; - int session_timeout; const u8 *mpmk; size_t mpmk_len; @@ -2139,7 +2133,7 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk) pmk_r0_len = SHA384_MAC_LEN; else pmk_r0_len = PMK_LEN; - pmk_r1_len = pmk_r0_len; + *key_len = pmk_r1_len = pmk_r0_len; if (sm->xxkey_len > 0) { mpmk = sm->xxkey; @@ -2153,10 +2147,39 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk) return -1; } + if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid, + r0kh, r0kh_len, sm->addr, + pmk_r0, pmk_r0_name, + sm->wpa_key_mgmt) < 0 || + wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr, + pmk_r1, sm->pmk_r1_name) < 0) + return -1; + + return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce, + sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name, + ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise, + 0); +} + + +void wpa_auth_ft_store_keys(struct wpa_state_machine *sm, const u8 *pmk_r0, + const u8 *pmk_r1, const u8 *pmk_r0_name, + size_t key_len) +{ + int psk_local = sm->wpa_auth->conf.ft_psk_generate_local; + int expires_in = sm->wpa_auth->conf.r0_key_lifetime; + struct vlan_description vlan; + const u8 *identity, *radius_cui; + size_t identity_len, radius_cui_len; + int session_timeout; + + if (psk_local && wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) + return; + if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) { wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR, MAC2STR(sm->addr)); - return -1; + return; } identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity); @@ -2164,31 +2187,16 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk) &radius_cui); session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr); - if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid, - r0kh, r0kh_len, sm->addr, - pmk_r0, pmk_r0_name, - sm->wpa_key_mgmt) < 0) - return -1; - if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) - wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len, - pmk_r0_name, - sm->pairwise, &vlan, expires_in, - session_timeout, identity, identity_len, - radius_cui, radius_cui_len); - if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr, - pmk_r1, sm->pmk_r1_name) < 0) - return -1; - if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) - wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_len, - sm->pmk_r1_name, sm->pairwise, &vlan, - expires_in, session_timeout, identity, - identity_len, radius_cui, radius_cui_len); - - return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce, - sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name, - ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise, - 0); + wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, key_len, + pmk_r0_name, + sm->pairwise, &vlan, expires_in, + session_timeout, identity, identity_len, + radius_cui, radius_cui_len); + wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, key_len, + sm->pmk_r1_name, sm->pairwise, &vlan, + expires_in, session_timeout, identity, + identity_len, radius_cui, radius_cui_len); } diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index 7ed3f2b6b..f3cb9be31 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -304,7 +304,12 @@ int wpa_write_ftie(struct wpa_auth_config *conf, int key_mgmt, size_t key_len, const u8 *anonce, const u8 *snonce, u8 *buf, size_t len, const u8 *subelem, size_t subelem_len, int rsnxe_used); -int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk); +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk, + u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name, + size_t *key_len); +void wpa_auth_ft_store_keys(struct wpa_state_machine *sm, const u8 *pmk_r0, + const u8 *pmk_r1, const u8 *pmk_r0_name, + size_t key_len); struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); void wpa_ft_install_ptk(struct wpa_state_machine *sm, int retry);