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 <quic_jouni@quicinc.com>
This commit is contained in:
parent
e4d1000cac
commit
ba150059d1
3 changed files with 107 additions and 58 deletions
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue