From 16889aff408ec585af64d10fc6adc3a7588a2585 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 17 Feb 2020 23:08:05 +0200 Subject: [PATCH] Add BIGTK KDE and subelement similarly to IGTK This provides the BIGTK updates to associated stations similarly to IGTK. Signed-off-by: Jouni Malinen --- src/ap/wnm_ap.c | 24 +++++++++--- src/ap/wpa_auth.c | 68 ++++++++++++++++++++++++++++++---- src/ap/wpa_auth.h | 1 + src/ap/wpa_auth_ft.c | 71 ++++++++++++++++++++++++++++++++++++ src/common/ieee802_11_defs.h | 3 +- 5 files changed, 153 insertions(+), 14 deletions(-) diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c index 29ca95092..891f90f29 100644 --- a/src/ap/wnm_ap.c +++ b/src/ap/wnm_ap.c @@ -54,6 +54,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, size_t len; size_t gtk_elem_len = 0; size_t igtk_elem_len = 0; + size_t bigtk_elem_len = 0; struct wnm_sleep_element wnmsleep_ie; u8 *wnmtfs_ie, *oci_ie; u8 wnmsleep_ie_len, oci_ie_len; @@ -122,8 +123,10 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, #define MAX_GTK_SUBELEM_LEN 45 #define MAX_IGTK_SUBELEM_LEN 26 +#define MAX_BIGTK_SUBELEM_LEN 26 mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN + + MAX_BIGTK_SUBELEM_LEN + oci_ie_len); if (mgmt == NULL) { wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " @@ -157,10 +160,19 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, pos += igtk_elem_len; wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d", (int) igtk_elem_len); + if (hapd->conf->beacon_prot) { + res = wpa_wnmsleep_bigtk_subelem(sta->wpa_sm, pos); + if (res < 0) + goto fail; + bigtk_elem_len = res; + pos += bigtk_elem_len; + wpa_printf(MSG_DEBUG, "Pass 4 bigtk_len = %d", + (int) bigtk_elem_len); + } WPA_PUT_LE16((u8 *) &mgmt->u.action.u.wnm_sleep_resp.keydata_len, - gtk_elem_len + igtk_elem_len); + gtk_elem_len + igtk_elem_len + bigtk_elem_len); } os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len); /* copy TFS IE here */ @@ -176,7 +188,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, #endif /* CONFIG_OCV */ len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len + - igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len; + igtk_elem_len + bigtk_elem_len + + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len; /* In driver, response frame should be forced to sent when STA is in * PS mode */ @@ -189,8 +202,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, /* when entering wnmsleep * 1. pause the node in driver - * 2. mark the node so that AP won't update GTK/IGTK during - * WNM Sleep + * 2. mark the node so that AP won't update GTK/IGTK/BIGTK + * during WNM Sleep */ if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT && wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) { @@ -201,7 +214,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, } /* when exiting wnmsleep * 1. unmark the node - * 2. start GTK/IGTK update if MFP is not used + * 2. start GTK/IGTK/BIGTK update if MFP is not used * 3. unpause the node in driver */ if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT || @@ -221,6 +234,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, #undef MAX_GTK_SUBELEM_LEN #undef MAX_IGTK_SUBELEM_LEN +#undef MAX_BIGTK_SUBELEM_LEN fail: os_free(wnmtfs_ie); os_free(oci_ie); diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index ee75837db..1f835d80a 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -63,6 +63,7 @@ static void wpa_group_get(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_group_put(struct wpa_authenticator *wpa_auth, struct wpa_group *group); +static int ieee80211w_kde_len(struct wpa_state_machine *sm); static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos); static const u32 eapol_key_timeout_first = 100; /* ms */ @@ -2679,7 +2680,7 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm, size_t gtk_len; struct wpa_group *gsm; - plain = wpabuf_alloc(1000); + plain = wpabuf_alloc(1000 + ieee80211w_kde_len(sm)); if (!plain) return NULL; @@ -2727,7 +2728,7 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm, gtk, gtk_len); wpabuf_put(plain, tmp2 - tmp); - /* IGTK KDE */ + /* IGTK KDE and BIGTK KDE */ tmp = wpabuf_put(plain, 0); tmp2 = ieee80211w_kde_add(sm, tmp); wpabuf_put(plain, tmp2 - tmp); @@ -3105,19 +3106,25 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) static int ieee80211w_kde_len(struct wpa_state_machine *sm) { + size_t len = 0; + if (sm->mgmt_frame_prot) { - size_t len; - len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); - return 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN + len; + len += 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN; + len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); + } + if (sm->mgmt_frame_prot && sm->wpa_auth->conf.beacon_prot) { + len += 2 + RSN_SELECTOR_LEN + WPA_BIGTK_KDE_PREFIX_LEN; + len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); } - return 0; + return len; } static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) { struct wpa_igtk_kde igtk; + struct wpa_bigtk_kde bigtk; struct wpa_group *gsm = sm->group; u8 rsc[WPA_KEY_RSC_LEN]; size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); @@ -3146,6 +3153,21 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len, NULL, 0); + if (!sm->wpa_auth->conf.beacon_prot) + return pos; + + bigtk.keyid[0] = gsm->GN_bigtk; + bigtk.keyid[1] = 0; + if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE || + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, rsc) < 0) + os_memset(bigtk.pn, 0, sizeof(bigtk.pn)); + else + os_memcpy(bigtk.pn, rsc, sizeof(bigtk.pn)); + os_memcpy(bigtk.bigtk, gsm->BIGTK[gsm->GN_bigtk - 6], len); + pos = wpa_add_kde(pos, RSN_KEY_DATA_BIGTK, + (const u8 *) &bigtk, WPA_BIGTK_KDE_PREFIX_LEN + len, + NULL, 0); + return pos; } @@ -3205,7 +3227,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) } /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE], - GTK[GN], IGTK, [FTIE], [TIE * 2]) + GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2]) */ os_memset(rsc, 0, WPA_KEY_RSC_LEN); wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); @@ -3978,6 +4000,36 @@ int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos) return pos - start; } + +int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos) +{ + struct wpa_group *gsm = sm->group; + u8 *start = pos; + size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); + + /* + * BIGTK subelement: + * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16] + */ + *pos++ = WNM_SLEEP_SUBELEM_BIGTK; + *pos++ = 2 + 6 + len; + WPA_PUT_LE16(pos, gsm->GN_bigtk); + pos += 2; + if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos) != 0) + return 0; + pos += 6; + + os_memcpy(pos, gsm->BIGTK[gsm->GN_bigtk - 6], len); + pos += len; + + wpa_printf(MSG_DEBUG, "WNM: BIGTK Key ID %u in WNM-Sleep Mode exit", + gsm->GN_bigtk); + wpa_hexdump_key(MSG_DEBUG, "WNM: BIGTK in WNM-Sleep Mode exit", + gsm->IGTK[gsm->GN_bigtk - 6], len); + + return pos - start; +} + #endif /* CONFIG_WNM_AP */ @@ -5074,7 +5126,7 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm, int wpa_ie_len, secure, gtkidx, encr = 0; /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE], - GTK[GN], IGTK, [FTIE], [TIE * 2]) + GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2]) */ /* Use 0 RSC */ diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index c066d9979..93e0c745d 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -438,6 +438,7 @@ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag); int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos); int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos); +int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos); int wpa_auth_uses_sae(struct wpa_state_machine *sm); int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm); diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index 462876195..c0b462558 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -2282,6 +2282,54 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) } +static u8 * wpa_ft_bigtk_subelem(struct wpa_state_machine *sm, size_t *len) +{ + u8 *subelem, *pos; + struct wpa_group *gsm = sm->group; + size_t subelem_len; + const u8 *kek; + size_t kek_len; + size_t bigtk_len; + + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + kek = sm->PTK.kek2; + kek_len = sm->PTK.kek2_len; + } else { + kek = sm->PTK.kek; + kek_len = sm->PTK.kek_len; + } + + bigtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); + + /* Sub-elem ID[1] | Length[1] | KeyID[2] | BIPN[6] | Key Length[1] | + * Key[16+8] */ + subelem_len = 1 + 1 + 2 + 6 + 1 + bigtk_len + 8; + subelem = os_zalloc(subelem_len); + if (subelem == NULL) + return NULL; + + pos = subelem; + *pos++ = FTIE_SUBELEM_BIGTK; + *pos++ = subelem_len - 2; + WPA_PUT_LE16(pos, gsm->GN_bigtk); + pos += 2; + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos); + pos += 6; + *pos++ = bigtk_len; + if (aes_wrap(kek, kek_len, bigtk_len / 8, + gsm->IGTK[gsm->GN_bigtk - 6], pos)) { + wpa_printf(MSG_DEBUG, + "FT: BIGTK subelem encryption failed: kek_len=%d", + (int) kek_len); + os_free(subelem); + return NULL; + } + + *len = subelem_len; + return subelem; +} + + static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, u8 *pos, u8 *end, u8 id, u8 descr_count, const u8 *ies, size_t ies_len) @@ -2511,6 +2559,29 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, subelem_len += igtk_len; os_free(igtk); } + if (sm->mgmt_frame_prot && conf->beacon_prot) { + u8 *bigtk; + size_t bigtk_len; + u8 *nbuf; + + bigtk = wpa_ft_bigtk_subelem(sm, &bigtk_len); + if (!bigtk) { + wpa_printf(MSG_DEBUG, + "FT: Failed to add BIGTK subelement"); + os_free(subelem); + return NULL; + } + nbuf = os_realloc(subelem, subelem_len + bigtk_len); + if (!nbuf) { + os_free(subelem); + os_free(bigtk); + return NULL; + } + subelem = nbuf; + os_memcpy(subelem + subelem_len, bigtk, bigtk_len); + subelem_len += bigtk_len; + os_free(bigtk); + } #ifdef CONFIG_OCV if (wpa_auth_uses_ocv(sm)) { struct wpa_channel_info ci; diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 23e739d0e..a78ed0a12 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -1875,7 +1875,8 @@ enum wnm_sleep_mode_response_status { /* WNM-Sleep Mode subelement IDs */ enum wnm_sleep_mode_subelement_id { WNM_SLEEP_SUBELEM_GTK = 0, - WNM_SLEEP_SUBELEM_IGTK = 1 + WNM_SLEEP_SUBELEM_IGTK = 1, + WNM_SLEEP_SUBELEM_BIGTK = 2, }; /* Channel Switch modes (802.11h) */