From 2d4c78aef718b9a3e5947c4fad4bbd634b6eab9d Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 18 Feb 2020 00:06:26 +0200 Subject: [PATCH] Configure received BIGTK on station/supplicant side Signed-off-by: Jouni Malinen --- src/rsn_supp/wpa.c | 73 +++++++++++++++++++++++++++++++++++++- src/rsn_supp/wpa_ft.c | 75 +++++++++++++++++++++++++++++++++++++--- src/rsn_supp/wpa_i.h | 2 ++ wpa_supplicant/wnm_sta.c | 9 +++++ 4 files changed, 154 insertions(+), 5 deletions(-) diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 981602c5d..1e209ada5 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -1140,14 +1140,66 @@ static int wpa_supplicant_install_igtk(struct wpa_sm *sm, } +static int wpa_supplicant_install_bigtk(struct wpa_sm *sm, + const struct wpa_bigtk_kde *bigtk, + int wnm_sleep) +{ + size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher); + u16 keyidx = WPA_GET_LE16(bigtk->keyid); + + /* Detect possible key reinstallation */ + if ((sm->bigtk.bigtk_len == len && + os_memcmp(sm->bigtk.bigtk, bigtk->bigtk, + sm->bigtk.bigtk_len) == 0) || + (sm->bigtk_wnm_sleep.bigtk_len == len && + os_memcmp(sm->bigtk_wnm_sleep.bigtk, bigtk->bigtk, + sm->bigtk_wnm_sleep.bigtk_len) == 0)) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Not reinstalling already in-use BIGTK to the driver (keyidx=%d)", + keyidx); + return 0; + } + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: BIGTK keyid %d pn " COMPACT_MACSTR, + keyidx, MAC2STR(bigtk->pn)); + wpa_hexdump_key(MSG_DEBUG, "WPA: BIGTK", bigtk->bigtk, len); + if (keyidx < 6 || keyidx > 7) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid BIGTK KeyID %d", keyidx); + return -1; + } + if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), + broadcast_ether_addr, + keyidx, 0, bigtk->pn, sizeof(bigtk->pn), + bigtk->bigtk, len, KEY_FLAG_GROUP_RX) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to configure BIGTK to the driver"); + return -1; + } + + if (wnm_sleep) { + sm->bigtk_wnm_sleep.bigtk_len = len; + os_memcpy(sm->bigtk_wnm_sleep.bigtk, bigtk->bigtk, + sm->bigtk_wnm_sleep.bigtk_len); + } else { + sm->bigtk.bigtk_len = len; + os_memcpy(sm->bigtk.bigtk, bigtk->bigtk, sm->bigtk.bigtk_len); + } + + return 0; +} + + static int ieee80211w_set_keys(struct wpa_sm *sm, struct wpa_eapol_ie_parse *ie) { + size_t len; + if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher)) return 0; if (ie->igtk) { - size_t len; const struct wpa_igtk_kde *igtk; len = wpa_cipher_key_len(sm->mgmt_group_cipher); @@ -1159,6 +1211,18 @@ static int ieee80211w_set_keys(struct wpa_sm *sm, return -1; } + if (ie->bigtk && sm->beacon_prot) { + const struct wpa_bigtk_kde *bigtk; + + len = wpa_cipher_key_len(sm->mgmt_group_cipher); + if (ie->bigtk_len != WPA_BIGTK_KDE_PREFIX_LEN + len) + return -1; + + bigtk = (const struct wpa_bigtk_kde *) ie->bigtk; + if (wpa_supplicant_install_bigtk(sm, bigtk, 0) < 0) + return -1; + } + return 0; } @@ -3595,6 +3659,13 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) igtk = (const struct wpa_igtk_kde *) (buf + 2); if (wpa_supplicant_install_igtk(sm, igtk, 1) < 0) return -1; + } else if (subelem_id == WNM_SLEEP_SUBELEM_BIGTK) { + const struct wpa_bigtk_kde *bigtk; + + bigtk = (const struct wpa_bigtk_kde *) (buf + 2); + if (sm->beacon_prot && + wpa_supplicant_install_bigtk(sm, bigtk, 1) < 0) + return -1; } else { wpa_printf(MSG_DEBUG, "Unknown element id"); return -1; diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index 8a8c545d3..046bdfd16 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -855,6 +855,74 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, } +static int wpa_ft_process_bigtk_subelem(struct wpa_sm *sm, const u8 *bigtk_elem, + size_t bigtk_elem_len) +{ + u8 bigtk[WPA_BIGTK_MAX_LEN]; + size_t bigtk_len; + u16 keyidx; + const u8 *kek; + size_t kek_len; + + if (!sm->beacon_prot || !bigtk_elem || + (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC && + sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_128 && + sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_256 && + sm->mgmt_group_cipher != WPA_CIPHER_BIP_CMAC_256)) + return 0; + + if (wpa_key_mgmt_fils(sm->key_mgmt)) { + kek = sm->ptk.kek2; + kek_len = sm->ptk.kek2_len; + } else { + kek = sm->ptk.kek; + kek_len = sm->ptk.kek_len; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: Received BIGTK in Reassoc Resp", + bigtk_elem, bigtk_elem_len); + + bigtk_len = wpa_cipher_key_len(sm->mgmt_group_cipher); + if (bigtk_elem_len != 2 + 6 + 1 + bigtk_len + 8) { + wpa_printf(MSG_DEBUG, + "FT: Invalid BIGTK sub-elem length %lu", + (unsigned long) bigtk_elem_len); + return -1; + } + if (bigtk_elem[8] != bigtk_len) { + wpa_printf(MSG_DEBUG, + "FT: Invalid BIGTK sub-elem Key Length %d", + bigtk_elem[8]); + return -1; + } + + if (aes_unwrap(kek, kek_len, bigtk_len / 8, bigtk_elem + 9, bigtk)) { + wpa_printf(MSG_WARNING, + "FT: AES unwrap failed - could not decrypt BIGTK"); + return -1; + } + + /* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */ + + keyidx = WPA_GET_LE16(bigtk_elem); + + wpa_hexdump_key(MSG_DEBUG, "FT: BIGTK from Reassoc Resp", bigtk, + bigtk_len); + if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), + broadcast_ether_addr, keyidx, 0, + bigtk_elem + 2, 6, bigtk, bigtk_len, + KEY_FLAG_GROUP_RX) < 0) { + wpa_printf(MSG_WARNING, + "WPA: Failed to set BIGTK to the driver"); + forced_memzero(bigtk, sizeof(bigtk)); + return -1; + } + forced_memzero(bigtk, sizeof(bigtk)); + + return 0; +} + + int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *src_addr) { @@ -1036,10 +1104,9 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, sm->ft_reassoc_completed = 1; - if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0) - return -1; - - if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0) + if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0 || + wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0 || + wpa_ft_process_bigtk_subelem(sm, parse.bigtk, parse.bigtk_len) < 0) return -1; if (sm->set_ptk_after_assoc) { diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 164adfbc0..bd4446402 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -33,6 +33,8 @@ struct wpa_sm { struct wpa_gtk gtk_wnm_sleep; struct wpa_igtk igtk; struct wpa_igtk igtk_wnm_sleep; + struct wpa_bigtk bigtk; + struct wpa_bigtk bigtk_wnm_sleep; struct eapol_sm *eapol; /* EAPOL state machine from upper level code */ diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 270be9e2e..06cb70fe5 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -280,6 +280,15 @@ static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s, wpa_wnmsleep_install_key(wpa_s->wpa, WNM_SLEEP_SUBELEM_IGTK, ptr); ptr += 10 + WPA_IGTK_LEN; + } else if (*ptr == WNM_SLEEP_SUBELEM_BIGTK) { + if (ptr[1] < 2 + 6 + WPA_BIGTK_LEN) { + wpa_printf(MSG_DEBUG, + "WNM: Too short BIGTK subelem"); + break; + } + wpa_wnmsleep_install_key(wpa_s->wpa, + WNM_SLEEP_SUBELEM_BIGTK, ptr); + ptr += 10 + WPA_BIGTK_LEN; } else break; /* skip the loop */ }