FT: Allow PMKIDs from AssocReq to be in EAPOL-Key msg 2/4

The standard is somewhat unclear on whether the PMKIDs used in
(Re)Association Request frame (i.e., potential PMKIDs that could be used
for PMKSA caching during the initial mobility domain association) are to
be retained or removed when generating EAPOL-Key msg 2/4.

hostapd used to require that only the PMKR1Name is included in the PMKID
List of RSNE in EAPOL-Key msg 2/4. Extend this to allow the PMKIDs that
were included in the (Re)Association Request frame to be present as long
as the correct PMKR1Name is also present. This would allow PMKSA caching
to be used in initial mobility domain association with supplicant
implementations that insert the PMKR1Name without removing the PMKIDs
used in the (Re)Association Request frame. wpa_supplicant did not use to
that, but other implementations might.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2024-02-03 20:39:56 +02:00
parent 5603899976
commit 9929426b92
2 changed files with 56 additions and 28 deletions

View file

@ -921,19 +921,70 @@ static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, struct wpa_state_machine *sm,
struct wpa_eapol_ie_parse *kde) struct wpa_eapol_ie_parse *kde)
{ {
struct wpa_ie_data ie; struct wpa_ie_data ie, assoc_ie;
struct rsn_mdie *mdie; struct rsn_mdie *mdie;
unsigned int i, j;
bool found = false;
/* Verify that PMKR1Name from EAPOL-Key message 2/4 matches the value
* we derived. */
if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 || if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 ||
ie.num_pmkid != 1 || !ie.pmkid) { ie.num_pmkid < 1 || !ie.pmkid) {
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
"FT: No PMKR1Name in FT 4-way handshake message 2/4"); "FT: No PMKR1Name in FT 4-way handshake message 2/4");
return -1; return -1;
} }
os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN); if (wpa_parse_wpa_ie_rsn(sm->wpa_ie, sm->wpa_ie_len, &assoc_ie) < 0) {
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant", wpa_printf(MSG_DEBUG,
sm->sup_pmk_r1_name, PMKID_LEN); "FT: Could not parse (Re)Association Request frame RSNE");
os_memset(&assoc_ie, 0, sizeof(assoc_ie));
/* Continue to allow PMKR1Name matching to be done to cover the
* case where it is the only listed PMKID. */
}
for (i = 0; i < ie.num_pmkid; i++) {
const u8 *pmkid = ie.pmkid + i * PMKID_LEN;
if (os_memcmp_const(pmkid, sm->pmk_r1_name,
WPA_PMK_NAME_LEN) == 0) {
wpa_printf(MSG_DEBUG,
"FT: RSNE[PMKID[%u]] from supplicant matches PMKR1Name",
i);
found = true;
} else {
for (j = 0; j < assoc_ie.num_pmkid; j++) {
if (os_memcmp(pmkid,
assoc_ie.pmkid + j * PMKID_LEN,
PMKID_LEN) == 0)
break;
}
if (j == assoc_ie.num_pmkid) {
wpa_printf(MSG_DEBUG,
"FT: RSNE[PMKID[%u]] from supplicant is neither PMKR1Name nor included in AssocReq",
i);
found = false;
break;
}
wpa_printf(MSG_DEBUG,
"FT: RSNE[PMKID[%u]] from supplicant is not PMKR1Name, but matches a PMKID in AssocReq",
i);
}
}
if (!found) {
wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
LOGGER_DEBUG,
"PMKR1Name mismatch in FT 4-way handshake");
wpa_hexdump(MSG_DEBUG,
"FT: PMKIDs/PMKR1Name from Supplicant",
ie.pmkid, ie.num_pmkid * PMKID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
sm->pmk_r1_name, WPA_PMK_NAME_LEN);
return -1;
}
if (!kde->mdie || !kde->ftie) { if (!kde->mdie || !kde->ftie) {
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
@ -3644,27 +3695,6 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
return; return;
} }
#ifdef CONFIG_IEEE80211R_AP
if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
/*
* Verify that PMKR1Name from EAPOL-Key message 2/4 matches
* with the value we derived.
*/
if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name,
WPA_PMK_NAME_LEN) != 0) {
wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
LOGGER_DEBUG,
"PMKR1Name mismatch in FT 4-way handshake");
wpa_hexdump(MSG_DEBUG,
"FT: PMKR1Name from Supplicant",
sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
sm->pmk_r1_name, WPA_PMK_NAME_LEN);
goto out;
}
}
#endif /* CONFIG_IEEE80211R_AP */
if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) { wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) {
wpa_sta_disconnect(wpa_auth, sm->addr, wpa_sta_disconnect(wpa_auth, sm->addr,

View file

@ -134,8 +134,6 @@ struct wpa_state_machine {
* Request */ * Request */
u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */ u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */
size_t r0kh_id_len; size_t r0kh_id_len;
u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
* message 2/4 */
u8 *assoc_resp_ftie; u8 *assoc_resp_ftie;
void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid, void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid,