diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 79504de7a..4e5b0a4f7 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -2466,6 +2466,9 @@ void wpa_sm_deinit(struct wpa_sm *sm) #ifdef CONFIG_FILS_SK_PFS crypto_ecdh_deinit(sm->fils_ecdh); #endif /* CONFIG_FILS_SK_PFS */ +#ifdef CONFIG_FILS + wpabuf_free(sm->fils_ft_ies); +#endif /* CONFIG_FILS */ #ifdef CONFIG_OWE crypto_ecdh_deinit(sm->owe_ecdh); #endif /* CONFIG_OWE */ @@ -3503,8 +3506,53 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data, os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN); - /* TODO: MDE when using FILS+FT */ - /* TODO: FTE when using FILS+FT */ + if (wpa_key_mgmt_ft(sm->key_mgmt)) { + struct wpa_ft_ies parse; + + if (!elems.mdie || !elems.ftie) { + wpa_printf(MSG_DEBUG, "FILS+FT: No MDE or FTE"); + goto fail; + } + + if (wpa_ft_parse_ies(pos, end - pos, &parse) < 0) { + wpa_printf(MSG_DEBUG, "FILS+FT: Failed to parse IEs"); + goto fail; + } + + if (!parse.r0kh_id) { + wpa_printf(MSG_DEBUG, + "FILS+FT: No R0KH-ID subelem in FTE"); + goto fail; + } + os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len); + sm->r0kh_id_len = parse.r0kh_id_len; + wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID", + sm->r0kh_id, sm->r0kh_id_len); + + if (!parse.r1kh_id) { + wpa_printf(MSG_DEBUG, + "FILS+FT: No R1KH-ID subelem in FTE"); + goto fail; + } + os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); + wpa_hexdump(MSG_DEBUG, "FILS+FT: R1KH-ID", + sm->r1kh_id, FT_R1KH_ID_LEN); + + /* TODO: Check MDE and FTE payload */ + + wpabuf_free(sm->fils_ft_ies); + sm->fils_ft_ies = wpabuf_alloc(2 + elems.mdie_len + + 2 + elems.ftie_len); + if (!sm->fils_ft_ies) + goto fail; + wpabuf_put_data(sm->fils_ft_ies, elems.mdie - 2, + 2 + elems.mdie_len); + wpabuf_put_data(sm->fils_ft_ies, elems.ftie - 2, + 2 + elems.ftie_len); + } else { + wpabuf_free(sm->fils_ft_ies); + sm->fils_ft_ies = NULL; + } /* PMKID List */ if (rsn.pmkid && rsn.num_pmkid > 0) { @@ -3604,7 +3652,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data, if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid, sm->fils_nonce, sm->fils_anonce, &sm->ptk, ick, &ick_len, sm->key_mgmt, sm->pairwise_cipher, - NULL, NULL) < 0) { + sm->fils_ft, &sm->fils_ft_len) < 0) { wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK"); goto fail; } @@ -3648,6 +3696,110 @@ fail: } +#ifdef CONFIG_IEEE80211R +static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf) +{ + struct rsn_ie_hdr *rsnie; + u16 capab; + u8 *pos; + + /* RSNIE[PMKR0Name/PMKR1Name] */ + rsnie = wpabuf_put(buf, sizeof(*rsnie)); + rsnie->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(rsnie->version, RSN_VERSION); + + /* Group Suite Selector */ + if (!wpa_cipher_valid_group(sm->group_cipher)) { + wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", + sm->group_cipher); + return -1; + } + pos = wpabuf_put(buf, RSN_SELECTOR_LEN); + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->group_cipher)); + + /* Pairwise Suite Count */ + wpabuf_put_le16(buf, 1); + + /* Pairwise Suite List */ + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { + wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)", + sm->pairwise_cipher); + return -1; + } + pos = wpabuf_put(buf, RSN_SELECTOR_LEN); + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->pairwise_cipher)); + + /* Authenticated Key Management Suite Count */ + wpabuf_put_le16(buf, 1); + + /* Authenticated Key Management Suite List */ + pos = wpabuf_put(buf, RSN_SELECTOR_LEN); + if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256); + else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384); + else { + wpa_printf(MSG_WARNING, + "FILS+FT: Invalid key management type (%d)", + sm->key_mgmt); + return -1; + } + + /* RSN Capabilities */ + capab = 0; +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) + capab |= WPA_CAPABILITY_MFPC; +#endif /* CONFIG_IEEE80211W */ + wpabuf_put_le16(buf, capab); + + /* PMKID Count */ + wpabuf_put_le16(buf, 1); + + /* PMKID List [PMKR1Name] */ + wpa_hexdump_key(MSG_DEBUG, "FILS+FT: XXKey (FILS-FT)", + sm->fils_ft, sm->fils_ft_len); + wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: SSID", sm->ssid, sm->ssid_len); + wpa_hexdump(MSG_DEBUG, "FILS+FT: MDID", + sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID", + sm->r0kh_id, sm->r0kh_id_len); + if (wpa_derive_pmk_r0(sm->fils_ft, sm->fils_ft_len, sm->ssid, + sm->ssid_len, sm->mobility_domain, + sm->r0kh_id, sm->r0kh_id_len, sm->own_addr, + sm->pmk_r0, sm->pmk_r0_name) < 0) { + wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMK-R0"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", sm->pmk_r0, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name", + sm->pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_printf(MSG_DEBUG, "FILS+FT: R1KH-ID: " MACSTR, + MAC2STR(sm->r1kh_id)); + pos = wpabuf_put(buf, WPA_PMK_NAME_LEN); + if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr, + pos) < 0) { + wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", pos, WPA_PMK_NAME_LEN); + +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { + /* Management Group Cipher Suite */ + pos = wpabuf_put(buf, RSN_SELECTOR_LEN); + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + } +#endif /* CONFIG_IEEE80211W */ + + rsnie->len = ((u8 *) wpabuf_put(buf, 0) - (u8 *) rsnie) - 2; + return 0; +} +#endif /* CONFIG_IEEE80211R */ + + struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, size_t *kek_len, const u8 **snonce, const u8 **anonce, @@ -3659,12 +3811,30 @@ struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, unsigned int i; len = 1000; +#ifdef CONFIG_IEEE80211R + if (sm->fils_ft_ies) + len += wpabuf_len(sm->fils_ft_ies); + if (wpa_key_mgmt_ft(sm->key_mgmt)) + len += 256; +#endif /* CONFIG_IEEE80211R */ for (i = 0; hlp && i < num_hlp; i++) len += 10 + wpabuf_len(hlp[i]); buf = wpabuf_alloc(len); if (!buf) return NULL; +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) { + /* MDE and FTE when using FILS+FT */ + wpabuf_put_buf(buf, sm->fils_ft_ies); + /* RSNE with PMKR1Name in PMKID field */ + if (fils_ft_build_assoc_req_rsne(sm, buf) < 0) { + wpabuf_free(buf); + return NULL; + } + } +#endif /* CONFIG_IEEE80211R */ + /* FILS Session */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */ diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 62cd3c114..82e194188 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -154,6 +154,9 @@ struct wpa_sm { struct crypto_ecdh *fils_ecdh; int fils_dh_group; size_t fils_dh_elem_len; + struct wpabuf *fils_ft_ies; + u8 fils_ft[FILS_FT_MAX_LEN]; + size_t fils_ft_len; #endif /* CONFIG_FILS */ #ifdef CONFIG_OWE diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 7c097e4e7..08315a5cf 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -1058,6 +1058,22 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) } +#ifdef CONFIG_IEEE80211R +static void remove_ie(u8 *buf, size_t *len, u8 eid) +{ + u8 *pos, *next, *end; + + pos = (u8 *) get_ie(buf, *len, eid); + if (pos) { + next = pos + 2 + pos[1]; + end = buf + *len; + *len -= 2 + pos[1]; + os_memmove(pos, next, end - next); + } +} +#endif /* CONFIG_IEEE80211R */ + + void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, const u8 *bssid, u16 auth_type) { @@ -1113,6 +1129,30 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, wpabuf_free(hlp[i]); if (!buf) return; + wpa_hexdump(MSG_DEBUG, "FILS: assoc_req before FILS elements", + wpa_s->sme.assoc_req_ie, + wpa_s->sme.assoc_req_ie_len); +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) { + /* Remove RSNE and MDE to allow them to be overridden + * with FILS+FT specific values from + * fils_build_assoc_req(). */ + remove_ie(wpa_s->sme.assoc_req_ie, + &wpa_s->sme.assoc_req_ie_len, + WLAN_EID_RSN); + wpa_hexdump(MSG_DEBUG, + "FILS: assoc_req after RSNE removal", + wpa_s->sme.assoc_req_ie, + wpa_s->sme.assoc_req_ie_len); + remove_ie(wpa_s->sme.assoc_req_ie, + &wpa_s->sme.assoc_req_ie_len, + WLAN_EID_MOBILITY_DOMAIN); + wpa_hexdump(MSG_DEBUG, + "FILS: assoc_req after MDE removal", + wpa_s->sme.assoc_req_ie, + wpa_s->sme.assoc_req_ie_len); + } +#endif /* CONFIG_IEEE80211R */ /* TODO: Make wpa_s->sme.assoc_req_ie use dynamic allocation */ if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(buf) > sizeof(wpa_s->sme.assoc_req_ie)) { @@ -1125,6 +1165,9 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, wpabuf_head(buf), wpabuf_len(buf)); wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf); wpabuf_free(buf); + wpa_hexdump(MSG_DEBUG, "FILS: assoc_req after FILS elements", + wpa_s->sme.assoc_req_ie, + wpa_s->sme.assoc_req_ie_len); os_memcpy(nonces, snonce, FILS_NONCE_LEN); os_memcpy(nonces + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN);