wlantest: Validate FT elements in Reassociation Request frame

Verify that RSNE, MDE, and FTE have valid information in FT
Reassociation Request frames.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2020-05-23 21:44:16 +03:00
parent 59d9994ac7
commit e10144c910
2 changed files with 180 additions and 4 deletions

View file

@ -879,7 +879,179 @@ static void rx_mgmt_reassoc_req(struct wlantest *wt, const u8 *data,
sta->assocreq_seen = 1; sta->assocreq_seen = 1;
sta_update_assoc(sta, &elems); sta_update_assoc(sta, &elems);
/* TODO: FT protocol: verify FTE MIC and update GTK/IGTK for the BSS */ if (elems.ftie) {
struct wpa_ft_ies parse;
int use_sha384;
struct rsn_mdie *mde;
const u8 *anonce, *snonce, *fte_mic;
u8 fte_elem_count;
unsigned int count;
u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
size_t mic_len = 16;
const u8 *kck;
size_t kck_len;
use_sha384 = wpa_key_mgmt_sha384(sta->key_mgmt);
if (wpa_ft_parse_ies(ie, ie_len, &parse, use_sha384) < 0) {
add_note(wt, MSG_INFO, "FT: Failed to parse FT IEs");
return;
}
if (!parse.rsn) {
add_note(wt, MSG_INFO, "FT: No RSNE in Reassoc Req");
return;
}
if (!parse.rsn_pmkid) {
add_note(wt, MSG_INFO, "FT: No PMKID in RSNE");
return;
}
if (os_memcmp_const(parse.rsn_pmkid, sta->pmk_r1_name,
WPA_PMK_NAME_LEN) != 0) {
add_note(wt, MSG_INFO,
"FT: PMKID in Reassoc Req did not match PMKR1Name");
wpa_hexdump(MSG_DEBUG,
"FT: Received RSNE[PMKR1Name]",
parse.rsn_pmkid, WPA_PMK_NAME_LEN);
wpa_hexdump(MSG_DEBUG,
"FT: Previously derived PMKR1Name",
sta->pmk_r1_name, WPA_PMK_NAME_LEN);
return;
}
mde = (struct rsn_mdie *) parse.mdie;
if (!mde || parse.mdie_len < sizeof(*mde) ||
os_memcmp(mde->mobility_domain, bss->mdid,
MOBILITY_DOMAIN_ID_LEN) != 0) {
add_note(wt, MSG_INFO, "FT: Invalid MDE");
}
if (use_sha384) {
struct rsn_ftie_sha384 *fte;
fte = (struct rsn_ftie_sha384 *) parse.ftie;
if (!fte || parse.ftie_len < sizeof(*fte)) {
add_note(wt, MSG_INFO, "FT: Invalid FTE");
return;
}
anonce = fte->anonce;
snonce = fte->snonce;
fte_elem_count = fte->mic_control[1];
fte_mic = fte->mic;
} else {
struct rsn_ftie *fte;
fte = (struct rsn_ftie *) parse.ftie;
if (!fte || parse.ftie_len < sizeof(*fte)) {
add_note(wt, MSG_INFO, "FT: Invalid FTIE");
return;
}
anonce = fte->anonce;
snonce = fte->snonce;
fte_elem_count = fte->mic_control[1];
fte_mic = fte->mic;
}
if (os_memcmp(snonce, sta->snonce, WPA_NONCE_LEN) != 0) {
add_note(wt, MSG_INFO, "FT: SNonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sta->snonce, WPA_NONCE_LEN);
return;
}
if (os_memcmp(anonce, sta->anonce, WPA_NONCE_LEN) != 0) {
add_note(wt, MSG_INFO, "FT: ANonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
anonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
sta->anonce, WPA_NONCE_LEN);
return;
}
if (!parse.r0kh_id) {
add_note(wt, MSG_INFO, "FT: No R0KH-ID subelem in FTE");
return;
}
os_memcpy(bss->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
bss->r0kh_id_len = parse.r0kh_id_len;
if (!parse.r1kh_id) {
add_note(wt, MSG_INFO, "FT: No R1KH-ID subelem in FTE");
return;
}
os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
if (!parse.rsn_pmkid ||
os_memcmp_const(parse.rsn_pmkid, sta->pmk_r1_name,
WPA_PMK_NAME_LEN)) {
add_note(wt, MSG_INFO,
"FT: No matching PMKR1Name (PMKID) in RSNE (pmkid=%d)",
!!parse.rsn_pmkid);
return;
}
count = 3;
if (parse.ric)
count += ieee802_11_ie_count(parse.ric, parse.ric_len);
if (parse.rsnxe)
count++;
if (fte_elem_count != count) {
add_note(wt, MSG_INFO,
"FT: Unexpected IE count in MIC Control: received %u expected %u",
fte_elem_count, count);
return;
}
if (wpa_key_mgmt_fils(sta->key_mgmt)) {
kck = sta->ptk.kck2;
kck_len = sta->ptk.kck2_len;
} else {
kck = sta->ptk.kck;
kck_len = sta->ptk.kck_len;
}
if (wpa_ft_mic(kck, kck_len, sta->addr, bss->bssid, 5,
parse.mdie - 2, parse.mdie_len + 2,
parse.ftie - 2, parse.ftie_len + 2,
parse.rsn - 2, parse.rsn_len + 2,
parse.ric, parse.ric_len,
parse.rsnxe ? parse.rsnxe - 2 : NULL,
parse.rsnxe ? parse.rsnxe_len + 2 : 0,
mic) < 0) {
add_note(wt, MSG_INFO, "FT: Failed to calculate MIC");
return;
}
if (os_memcmp_const(mic, fte_mic, mic_len) != 0) {
add_note(wt, MSG_INFO, "FT: Invalid MIC in FTE");
wpa_printf(MSG_DEBUG,
"FT: addr=" MACSTR " auth_addr=" MACSTR,
MAC2STR(sta->addr),
MAC2STR(bss->bssid));
wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
fte_mic, mic_len);
wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC",
mic, mic_len);
wpa_hexdump(MSG_MSGDUMP, "FT: MDE",
parse.mdie - 2, parse.mdie_len + 2);
wpa_hexdump(MSG_MSGDUMP, "FT: FTE",
parse.ftie - 2, parse.ftie_len + 2);
wpa_hexdump(MSG_MSGDUMP, "FT: RSN",
parse.rsn - 2, parse.rsn_len + 2);
wpa_hexdump(MSG_MSGDUMP, "FT: RSNXE",
parse.rsnxe ? parse.rsnxe - 2 : NULL,
parse.rsnxe ? parse.rsnxe_len + 2 : 0);
return;
}
add_note(wt, MSG_INFO, "FT: Valid FTE MIC");
}
} }
@ -1188,6 +1360,8 @@ static void rx_mgmt_action_ft_response(struct wlantest *wt,
new_sta->ptk_set = 1; new_sta->ptk_set = 1;
os_memset(new_sta->rsc_tods, 0, sizeof(new_sta->rsc_tods)); os_memset(new_sta->rsc_tods, 0, sizeof(new_sta->rsc_tods));
os_memset(new_sta->rsc_fromds, 0, sizeof(new_sta->rsc_fromds)); os_memset(new_sta->rsc_fromds, 0, sizeof(new_sta->rsc_fromds));
os_memcpy(new_sta->snonce, parse.fte_snonce, WPA_NONCE_LEN);
os_memcpy(new_sta->anonce, parse.fte_anonce, WPA_NONCE_LEN);
} }

View file

@ -70,8 +70,10 @@ struct wlantest_sta {
int group_cipher; int group_cipher;
int key_mgmt; int key_mgmt;
int rsn_capab; int rsn_capab;
u8 anonce[32]; /* ANonce from the previous EAPOL-Key msg 1/4 or 3/4 */ /* ANonce from the previous EAPOL-Key msg 1/4 or 3/4 */
u8 snonce[32]; /* SNonce from the previous EAPOL-Key msg 2/4 */ u8 anonce[WPA_NONCE_LEN];
/* SNonce from the previous EAPOL-Key msg 2/4 */
u8 snonce[WPA_NONCE_LEN];
u8 pmk_r0[PMK_LEN_MAX]; u8 pmk_r0[PMK_LEN_MAX];
size_t pmk_r0_len; size_t pmk_r0_len;
u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 pmk_r0_name[WPA_PMK_NAME_LEN];
@ -164,7 +166,7 @@ struct wlantest_bss {
int bigtk_idx; int bigtk_idx;
u32 counters[NUM_WLANTEST_BSS_COUNTER]; u32 counters[NUM_WLANTEST_BSS_COUNTER];
struct dl_list tdls; /* struct wlantest_tdls */ struct dl_list tdls; /* struct wlantest_tdls */
u8 mdid[2]; u8 mdid[MOBILITY_DOMAIN_ID_LEN];
u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
size_t r0kh_id_len; size_t r0kh_id_len;
u8 r1kh_id[FT_R1KH_ID_LEN]; u8 r1kh_id[FT_R1KH_ID_LEN];