diff --git a/feeds/ipq807x_v5.4/hostapd/patches/zzz-FT-Store-PMK-R0-PMK-R1-after-EAPOL-Key-msg-2-4-MIC-v.patch b/feeds/ipq807x_v5.4/hostapd/patches/zzz-FT-Store-PMK-R0-PMK-R1-after-EAPOL-Key-msg-2-4-MIC-v.patch new file mode 100644 index 000000000..14f959cf5 --- /dev/null +++ b/feeds/ipq807x_v5.4/hostapd/patches/zzz-FT-Store-PMK-R0-PMK-R1-after-EAPOL-Key-msg-2-4-MIC-v.patch @@ -0,0 +1,411 @@ +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 +--- + +--- a/src/ap/wpa_auth.c ++++ b/src/ap/wpa_auth.c +@@ -58,7 +58,9 @@ static int wpa_group_config_group_keys(s + 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, +@@ -940,6 +942,10 @@ static int wpa_try_alt_snonce(struct wpa + 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 (;;) { +@@ -961,8 +967,8 @@ static int wpa_try_alt_snonce(struct wpa + 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, +@@ -983,7 +989,7 @@ static int wpa_try_alt_snonce(struct wpa + if (!ok) { + wpa_printf(MSG_DEBUG, + "WPA: Earlier SNonce did not result in matching MIC"); +- return -1; ++ goto fail; + } + + wpa_printf(MSG_DEBUG, +@@ -992,14 +998,26 @@ static int wpa_try_alt_snonce(struct wpa + + 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; + } + + +@@ -2283,7 +2301,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; +@@ -2311,7 +2331,8 @@ static int wpa_derive_ptk(struct wpa_sta + sm->pairwise, + kdk_len); + } +- return wpa_auth_derive_ptk_ft(sm, ptk); ++ return wpa_auth_derive_ptk_ft(sm, ptk, pmk_r0, pmk_r1, ++ pmk_r0_name, key_len); + } + #endif /* CONFIG_IEEE80211R_AP */ + +@@ -2934,6 +2955,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; +@@ -2972,7 +2996,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 && +@@ -3021,7 +3046,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) { +@@ -3029,7 +3054,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; + } + + /* +@@ -3043,12 +3068,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; +@@ -3075,7 +3100,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) || +@@ -3091,7 +3116,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)) { +@@ -3103,14 +3128,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); +@@ -3127,7 +3152,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 */ +@@ -3135,7 +3160,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 +@@ -3171,7 +3196,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 */ +@@ -3191,7 +3216,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 */ +@@ -3200,7 +3225,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; +@@ -3216,9 +3241,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)); + } + + +--- a/src/ap/wpa_auth_ft.c ++++ b/src/ap/wpa_auth_ft.c +@@ -2175,13 +2175,13 @@ int wpa_ft_store_pmk_fils(struct wpa_sta + } + + +-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 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ? + SHA384_MAC_LEN : PMK_LEN; + size_t pmk_r1_len = pmk_r0_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; +@@ -2189,13 +2189,6 @@ int wpa_auth_derive_ptk_ft(struct wpa_st + 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; +- struct rate_description rate; +- const u8 *identity, *radius_cui; +- size_t identity_len, radius_cui_len; +- int session_timeout; + const u8 *mpmk; + size_t mpmk_len; + +@@ -2211,10 +2204,41 @@ int wpa_auth_derive_ptk_ft(struct wpa_st + return -1; + } + ++ *key_len = pmk_r0_len; ++ if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid, ++ r0kh, r0kh_len, sm->addr, ++ pmk_r0, pmk_r0_name, ++ pmk_r0_len == SHA384_MAC_LEN) < 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; ++ struct rate_description rate; ++ 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; + } + + wpa_ft_get_rate_limit(sm->wpa_auth, sm->addr, &rate); +@@ -2224,32 +2248,16 @@ int wpa_auth_derive_ptk_ft(struct wpa_st + &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, +- wpa_key_mgmt_sha384(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, &rate); +- +- 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, +- &rate); +- +- 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, &rate); ++ ++ 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, &rate); + } + + +--- a/src/ap/wpa_auth_i.h ++++ b/src/ap/wpa_auth_i.h +@@ -302,7 +302,12 @@ int wpa_write_ftie(struct wpa_auth_confi + 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);