From faf6894f35f6f2592ff05324db9e6e6184ebc041 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 21 Feb 2020 19:40:32 +0200 Subject: [PATCH] wlantest: BIGTK fetching and Beacon protection validation Fetch the BIGTK from EAPOL-Key msg 3/4 and use it to validate MME in Beacon frames when the AP uses Beacon protection. Signed-off-by: Jouni Malinen --- wlantest/rx_eapol.c | 59 ++++++++++++++++++++++++++++++++++++++ wlantest/rx_mgmt.c | 69 +++++++++++++++++++++++++++++++++++++++++++++ wlantest/wlantest.h | 7 +++-- 3 files changed, 132 insertions(+), 3 deletions(-) diff --git a/wlantest/rx_eapol.c b/wlantest/rx_eapol.c index e18449542..e4fe7e23e 100644 --- a/wlantest/rx_eapol.c +++ b/wlantest/rx_eapol.c @@ -548,6 +548,65 @@ static void learn_kde_keys(struct wlantest *wt, struct wlantest_bss *bss, (unsigned) ie.igtk_len); } } + + if (ie.bigtk) { + wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - BIGTK KDE", + ie.bigtk, ie.bigtk_len); + if (ie.bigtk_len == 24) { + u16 id; + + id = WPA_GET_LE16(ie.bigtk); + if (id < 6 || id > 7) { + add_note(wt, MSG_INFO, + "Unexpected BIGTK KeyID %u", id); + } else { + const u8 *ipn; + + add_note(wt, MSG_DEBUG, "BIGTK KeyID %u", id); + wpa_hexdump(MSG_DEBUG, "BIPN", ie.bigtk + 2, 6); + wpa_hexdump(MSG_DEBUG, "BIGTK", ie.bigtk + 8, + 16); + os_memcpy(bss->igtk[id], ie.bigtk + 8, 16); + bss->igtk_len[id] = 16; + ipn = ie.bigtk + 2; + bss->ipn[id][0] = ipn[5]; + bss->ipn[id][1] = ipn[4]; + bss->ipn[id][2] = ipn[3]; + bss->ipn[id][3] = ipn[2]; + bss->ipn[id][4] = ipn[1]; + bss->ipn[id][5] = ipn[0]; + bss->bigtk_idx = id; + } + } else if (ie.bigtk_len == 40) { + u16 id; + + id = WPA_GET_LE16(ie.bigtk); + if (id < 6 || id > 7) { + add_note(wt, MSG_INFO, + "Unexpected BIGTK KeyID %u", id); + } else { + const u8 *ipn; + + add_note(wt, MSG_DEBUG, "BIGTK KeyID %u", id); + wpa_hexdump(MSG_DEBUG, "BIPN", ie.bigtk + 2, 6); + wpa_hexdump(MSG_DEBUG, "BIGTK", ie.bigtk + 8, + 32); + os_memcpy(bss->igtk[id], ie.bigtk + 8, 32); + bss->igtk_len[id] = 32; + ipn = ie.bigtk + 2; + bss->ipn[id][0] = ipn[5]; + bss->ipn[id][1] = ipn[4]; + bss->ipn[id][2] = ipn[3]; + bss->ipn[id][3] = ipn[2]; + bss->ipn[id][4] = ipn[1]; + bss->ipn[id][5] = ipn[0]; + bss->bigtk_idx = id; + } + } else { + add_note(wt, MSG_INFO, "Invalid BIGTK KDE length %u", + (unsigned) ie.bigtk_len); + } + } } diff --git a/wlantest/rx_mgmt.c b/wlantest/rx_mgmt.c index 39b23b27c..92762b8f0 100644 --- a/wlantest/rx_mgmt.c +++ b/wlantest/rx_mgmt.c @@ -19,6 +19,11 @@ #include "wlantest.h" +static int check_mmie_mic(unsigned int mgmt_group_cipher, + const u8 *igtk, size_t igtk_len, + const u8 *data, size_t len); + + static const char * mgmt_stype(u16 stype) { switch (stype) { @@ -57,6 +62,9 @@ static void rx_mgmt_beacon(struct wlantest *wt, const u8 *data, size_t len) struct wlantest_bss *bss; struct ieee802_11_elems elems; size_t offset; + const u8 *mme; + size_t mic_len; + u16 keyid; mgmt = (const struct ieee80211_mgmt *) data; offset = mgmt->u.beacon.variable - data; @@ -79,6 +87,62 @@ static void rx_mgmt_beacon(struct wlantest *wt, const u8 *data, size_t len) } bss_update(wt, bss, &elems); + + mme = get_ie(mgmt->u.beacon.variable, len - offset, WLAN_EID_MMIE); + if (!mme) { + if (bss->bigtk_idx) { + add_note(wt, MSG_INFO, + "Unexpected unprotected Beacon frame from " + MACSTR, MAC2STR(mgmt->sa)); + bss->counters[WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE]++; + } + return; + } + + mic_len = bss->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC ? 8 : 16; + if (len < 24 + 10 + mic_len || + data[len - (10 + mic_len)] != WLAN_EID_MMIE || + data[len - (10 + mic_len - 1)] != 8 + mic_len) { + add_note(wt, MSG_INFO, "Invalid MME in a Beacon frame from " + MACSTR, MAC2STR(mgmt->sa)); + return; + } + + mme += 2; + keyid = WPA_GET_LE16(mme); + if (keyid < 6 || keyid > 7) { + add_note(wt, MSG_INFO, "Unexpected MME KeyID %u from " MACSTR, + keyid, MAC2STR(mgmt->sa)); + bss->counters[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE]++; + return; + } + + wpa_printf(MSG_DEBUG, "Beacon frame MME KeyID %u", keyid); + wpa_hexdump(MSG_MSGDUMP, "MME IPN", mme + 2, 6); + wpa_hexdump(MSG_MSGDUMP, "MME MIC", mme + 8, mic_len); + + if (!bss->igtk_len[keyid]) { + add_note(wt, MSG_DEBUG, "No BIGTK known to validate BIP frame"); + return; + } + + if (os_memcmp(mme + 2, bss->ipn[keyid], 6) <= 0) { + add_note(wt, MSG_INFO, "BIP replay detected: SA=" MACSTR, + MAC2STR(mgmt->sa)); + wpa_hexdump(MSG_INFO, "RX IPN", mme + 2, 6); + wpa_hexdump(MSG_INFO, "Last RX IPN", bss->ipn[keyid], 6); + } + + if (check_mmie_mic(bss->mgmt_group_cipher, bss->igtk[keyid], + bss->igtk_len[keyid], data, len) < 0) { + add_note(wt, MSG_INFO, "Invalid MME MIC in a Beacon frame from " + MACSTR, MAC2STR(mgmt->sa)); + bss->counters[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE]++; + return; + } + + add_note(wt, MSG_DEBUG, "Valid MME MIC in Beacon frame"); + os_memcpy(bss->ipn[keyid], mme + 2, 6); } @@ -1329,6 +1393,11 @@ static int check_mmie_mic(unsigned int mgmt_group_cipher, os_memcpy(buf + 20, data + 24, len - 24 - mic_len); os_memset(buf + 20 + len - 24 - mic_len, 0, mic_len); + if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) { + /* Timestamp field masked to zero */ + os_memset(buf + 20, 0, 8); + } + wpa_hexdump(MSG_MSGDUMP, "BIP: AAD|Body(masked)", buf, len + 20 - 24); /* MIC = L(AES-128-CMAC(AAD || Frame Body(masked)), 0, 64) */ if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { diff --git a/wlantest/wlantest.h b/wlantest/wlantest.h index 4f90b20e2..82eddc11c 100644 --- a/wlantest/wlantest.h +++ b/wlantest/wlantest.h @@ -150,10 +150,11 @@ struct wlantest_bss { size_t gtk_len[4]; int gtk_idx; u8 rsc[4][6]; - u8 igtk[6][32]; - size_t igtk_len[6]; + u8 igtk[8][32]; + size_t igtk_len[8]; int igtk_idx; - u8 ipn[6][6]; + u8 ipn[8][6]; + int bigtk_idx; u32 counters[NUM_WLANTEST_BSS_COUNTER]; struct dl_list tdls; /* struct wlantest_tdls */ u8 mdid[2];