Configure received BIGTK on station/supplicant side

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
Jouni Malinen 2020-02-18 00:06:26 +02:00 committed by Jouni Malinen
parent ecbf59e693
commit 2d4c78aef7
4 changed files with 154 additions and 5 deletions

View file

@ -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, static int ieee80211w_set_keys(struct wpa_sm *sm,
struct wpa_eapol_ie_parse *ie) struct wpa_eapol_ie_parse *ie)
{ {
size_t len;
if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher)) if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher))
return 0; return 0;
if (ie->igtk) { if (ie->igtk) {
size_t len;
const struct wpa_igtk_kde *igtk; const struct wpa_igtk_kde *igtk;
len = wpa_cipher_key_len(sm->mgmt_group_cipher); len = wpa_cipher_key_len(sm->mgmt_group_cipher);
@ -1159,6 +1211,18 @@ static int ieee80211w_set_keys(struct wpa_sm *sm,
return -1; 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; 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); igtk = (const struct wpa_igtk_kde *) (buf + 2);
if (wpa_supplicant_install_igtk(sm, igtk, 1) < 0) if (wpa_supplicant_install_igtk(sm, igtk, 1) < 0)
return -1; 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 { } else {
wpa_printf(MSG_DEBUG, "Unknown element id"); wpa_printf(MSG_DEBUG, "Unknown element id");
return -1; return -1;

View file

@ -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, int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
size_t ies_len, const u8 *src_addr) 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; sm->ft_reassoc_completed = 1;
if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0) if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0 ||
return -1; wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0 ||
wpa_ft_process_bigtk_subelem(sm, parse.bigtk, parse.bigtk_len) < 0)
if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0)
return -1; return -1;
if (sm->set_ptk_after_assoc) { if (sm->set_ptk_after_assoc) {

View file

@ -33,6 +33,8 @@ struct wpa_sm {
struct wpa_gtk gtk_wnm_sleep; struct wpa_gtk gtk_wnm_sleep;
struct wpa_igtk igtk; struct wpa_igtk igtk;
struct wpa_igtk igtk_wnm_sleep; 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 */ struct eapol_sm *eapol; /* EAPOL state machine from upper level code */

View file

@ -280,6 +280,15 @@ static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
wpa_wnmsleep_install_key(wpa_s->wpa, wpa_wnmsleep_install_key(wpa_s->wpa,
WNM_SLEEP_SUBELEM_IGTK, ptr); WNM_SLEEP_SUBELEM_IGTK, ptr);
ptr += 10 + WPA_IGTK_LEN; 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 } else
break; /* skip the loop */ break; /* skip the loop */
} }