diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index f5231c138..504feaf2a 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -655,51 +655,51 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, kde = sm->assoc_wpa_ie; kde_len = sm->assoc_wpa_ie_len; + kde_buf = os_malloc(kde_len + + 2 + RSN_SELECTOR_LEN + 3 + + sm->assoc_rsnxe_len + + 2 + RSN_SELECTOR_LEN + 1); + if (!kde_buf) + goto failed; + os_memcpy(kde_buf, kde, kde_len); + kde = kde_buf; #ifdef CONFIG_OCV if (wpa_sm_ocv_enabled(sm)) { struct wpa_channel_info ci; u8 *pos; + pos = kde + kde_len; if (wpa_sm_channel_info(sm, &ci) != 0) { wpa_printf(MSG_WARNING, "Failed to get channel info for OCI element in EAPOL-Key 2/4"); goto failed; } - kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 3); - if (!kde_buf) { - wpa_printf(MSG_WARNING, - "Failed to allocate memory for KDE with OCI in EAPOL-Key 2/4"); - goto failed; - } - - os_memcpy(kde_buf, kde, kde_len); - kde = kde_buf; - pos = kde + kde_len; if (ocv_insert_oci_kde(&ci, &pos) < 0) goto failed; kde_len = pos - kde; } #endif /* CONFIG_OCV */ + if (sm->assoc_rsnxe && sm->assoc_rsnxe_len) { + os_memcpy(kde + kde_len, sm->assoc_rsnxe, sm->assoc_rsnxe_len); + kde_len += sm->assoc_rsnxe_len; + } + #ifdef CONFIG_P2P if (sm->p2p) { - kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1); - if (kde_buf) { - u8 *pos; - wpa_printf(MSG_DEBUG, "P2P: Add IP Address Request KDE " - "into EAPOL-Key 2/4"); - os_memcpy(kde_buf, kde, kde_len); - kde = kde_buf; - pos = kde + kde_len; - *pos++ = WLAN_EID_VENDOR_SPECIFIC; - *pos++ = RSN_SELECTOR_LEN + 1; - RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ); - pos += RSN_SELECTOR_LEN; - *pos++ = 0x01; - kde_len = pos - kde; - } + u8 *pos; + + wpa_printf(MSG_DEBUG, + "P2P: Add IP Address Request KDE into EAPOL-Key 2/4"); + pos = kde + kde_len; + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = RSN_SELECTOR_LEN + 1; + RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ); + pos += RSN_SELECTOR_LEN; + *pos++ = 0x01; + kde_len = pos - kde; } #endif /* CONFIG_P2P */ @@ -2672,6 +2672,7 @@ void wpa_sm_deinit(struct wpa_sm *sm) eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL); eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); os_free(sm->assoc_wpa_ie); + os_free(sm->assoc_rsnxe); os_free(sm->ap_wpa_ie); os_free(sm->ap_rsn_ie); os_free(sm->ap_rsnxe); @@ -3049,6 +3050,9 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, case WPA_PARAM_OCV: sm->ocv = value; break; + case WPA_PARAM_SAE_PWE: + sm->sae_pwe = value; + break; default: break; } @@ -3226,6 +3230,83 @@ int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) } +/** + * wpa_sm_set_assoc_rsnxe_default - Generate own RSNXE from configuration + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @rsnxe: Pointer to buffer for RSNXE + * @rsnxe_len: Pointer to the length of the rsne buffer + * Returns: 0 on success, -1 on failure + */ +int wpa_sm_set_assoc_rsnxe_default(struct wpa_sm *sm, u8 *rsnxe, + size_t *rsnxe_len) +{ + int res; + + if (!sm) + return -1; + + res = wpa_gen_rsnxe(sm, rsnxe, *rsnxe_len); + if (res < 0) + return -1; + *rsnxe_len = res; + + wpa_hexdump(MSG_DEBUG, "RSN: Set own RSNXE default", rsnxe, *rsnxe_len); + + if (sm->assoc_rsnxe) { + wpa_hexdump(MSG_DEBUG, + "RSN: Leave previously set RSNXE default", + sm->assoc_rsnxe, sm->assoc_rsnxe_len); + } else if (*rsnxe_len > 0) { + /* + * Make a copy of the RSNXE so that 4-Way Handshake gets the + * correct version of the IE even if it gets changed. + */ + sm->assoc_rsnxe = os_memdup(rsnxe, *rsnxe_len); + if (!sm->assoc_rsnxe) + return -1; + + sm->assoc_rsnxe_len = *rsnxe_len; + } + + return 0; +} + + +/** + * wpa_sm_set_assoc_rsnxe - Set own RSNXE from (Re)AssocReq + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ie: Pointer to IE data (starting from id) + * @len: IE length + * Returns: 0 on success, -1 on failure + * + * Inform WPA state machine about the RSNXE used in (Re)Association Request + * frame. The IE will be used to override the default value generated + * with wpa_sm_set_assoc_rsnxe_default(). + */ +int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len) +{ + if (!sm) + return -1; + + os_free(sm->assoc_rsnxe); + if (!ie || len == 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: clearing own RSNXE"); + sm->assoc_rsnxe = NULL; + sm->assoc_rsnxe_len = 0; + } else { + wpa_hexdump(MSG_DEBUG, "RSN: set own RSNXE", ie, len); + sm->assoc_rsnxe = os_memdup(ie, len); + if (!sm->assoc_rsnxe) + return -1; + + sm->assoc_rsnxe_len = len; + } + + return 0; +} + + /** * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp * @sm: Pointer to WPA state machine data from wpa_sm_init() diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 125d359ef..f1fbb1bb5 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -98,7 +98,8 @@ enum wpa_sm_conf_params { WPA_PARAM_MGMT_GROUP, WPA_PARAM_RSN_ENABLED, WPA_PARAM_MFP, - WPA_PARAM_OCV + WPA_PARAM_OCV, + WPA_PARAM_SAE_PWE, }; struct rsn_supp_config { @@ -134,6 +135,9 @@ void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol); int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, size_t *wpa_ie_len); +int wpa_sm_set_assoc_rsnxe_default(struct wpa_sm *sm, u8 *rsnxe, + size_t *rsnxe_len); +int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len); diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 16224c9ef..2a433425c 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -85,9 +85,12 @@ struct wpa_sm { int rsn_enabled; /* Whether RSN is enabled in configuration */ int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */ int ocv; /* Operating Channel Validation */ + int sae_pwe; /* SAE PWE generation options */ u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */ size_t assoc_wpa_ie_len; + u8 *assoc_rsnxe; /* Own RSNXE from (Re)AssocReq */ + size_t assoc_rsnxe_len; u8 *ap_wpa_ie, *ap_rsn_ie, *ap_rsnxe; size_t ap_wpa_ie_len, ap_rsn_ie_len, ap_rsnxe_len; diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index e6af6c10e..f4ac25c05 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -342,6 +342,28 @@ int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) } +int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len) +{ + u8 *pos = rsnxe; + + if (!wpa_key_mgmt_sae(sm->key_mgmt)) + return 0; /* SAE not in use */ + if (sm->sae_pwe != 1 && sm->sae_pwe != 2) + return 0; /* no supported extended RSN capabilities */ + + if (rsnxe_len < 3) + return -1; + + *pos++ = WLAN_EID_RSNX; + *pos++ = 1; + /* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is + * used for now */ + *pos++ = BIT(WLAN_RSNX_CAPAB_SAE_H2E); + + return pos - rsnxe; +} + + /** * wpa_parse_vendor_specific - Parse Vendor Specific IEs * @pos: Pointer to the IE header diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h index 6f18bbe2a..fa7d0a317 100644 --- a/src/rsn_supp/wpa_ie.h +++ b/src/rsn_supp/wpa_ie.h @@ -62,5 +62,6 @@ struct wpa_eapol_ie_parse { int wpa_supplicant_parse_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie); int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len); +int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len); #endif /* WPA_IE_H */ diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 9ab5ebc92..ac4274cfb 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -2411,7 +2411,7 @@ static int wpas_fst_update_mbie(struct wpa_supplicant *wpa_s, static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { - int l, len, found = 0, wpa_found, rsn_found; + int l, len, found = 0, found_x = 0, wpa_found, rsn_found; const u8 *p; #if defined(CONFIG_IEEE80211R) || defined(CONFIG_OWE) u8 bssid[ETH_ALEN]; @@ -2483,22 +2483,29 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, p, l); break; } - if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && - (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) || - (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 4 && - (os_memcmp(&p[2], "\x50\x6F\x9A\x12", 4) == 0)) || - (p[0] == WLAN_EID_RSN && p[1] >= 2)) { + if (!found && + ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && + (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) || + (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 4 && + (os_memcmp(&p[2], "\x50\x6F\x9A\x12", 4) == 0)) || + (p[0] == WLAN_EID_RSN && p[1] >= 2))) { if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len)) break; found = 1; wpa_find_assoc_pmkid(wpa_s); - break; + } + if (!found_x && p[0] == WLAN_EID_RSNX) { + if (wpa_sm_set_assoc_rsnxe(wpa_s->wpa, p, len)) + break; + found_x = 1; } l -= len; p += len; } if (!found && data->assoc_info.req_ies) wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + if (!found_x && data->assoc_info.req_ies) + wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0); #ifdef CONFIG_FILS #ifdef CONFIG_SME diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index fbec8967f..c8a5d4b85 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -591,6 +591,14 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, os_memcpy(pos, ext_capab, ext_capab_len); } + if (wpa_s->rsnxe_len > 0 && + wpa_s->rsnxe_len <= + sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len) { + os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, + wpa_s->rsnxe, wpa_s->rsnxe_len); + wpa_s->sme.assoc_req_ie_len += wpa_s->rsnxe_len; + } + #ifdef CONFIG_HS20 if (is_hs20_network(wpa_s, ssid, bss)) { struct wpabuf *hs20; @@ -884,6 +892,8 @@ static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit) /* Starting new connection, so clear the possibly used WPA IE from the * previous association. */ wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0); + wpa_s->rsnxe_len = 0; sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1); } @@ -1899,6 +1909,11 @@ pfs_fail: elems.osen_len + 2); } else wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + if (elems.rsnxe) + wpa_sm_set_assoc_rsnxe(wpa_s->wpa, elems.rsnxe - 2, + elems.rsnxe_len + 2); + else + wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0); if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group) params.p2p = 1; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 29f482038..6688d71af 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -404,6 +404,8 @@ void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0); wpa_sm_set_ap_rsnxe(wpa_s->wpa, NULL, 0); wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0); + wpa_s->rsnxe_len = 0; wpa_s->pairwise_cipher = WPA_CIPHER_NONE; wpa_s->group_cipher = WPA_CIPHER_NONE; wpa_s->mgmt_group_cipher = 0; @@ -1578,12 +1580,20 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, #ifdef CONFIG_OCV wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV, ssid->ocv); #endif /* CONFIG_OCV */ + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PWE, wpa_s->conf->sae_pwe); if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE"); return -1; } + wpa_s->rsnxe_len = sizeof(wpa_s->rsnxe); + if (wpa_sm_set_assoc_rsnxe_default(wpa_s->wpa, wpa_s->rsnxe, + &wpa_s->rsnxe_len)) { + wpa_msg(wpa_s, MSG_WARNING, "RSN: Failed to generate RSNXE"); + return -1; + } + if (0) { #ifdef CONFIG_DPP } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) { @@ -2995,6 +3005,12 @@ pfs_fail: } #endif /* CONFIG_IEEE80211R */ + if (wpa_s->rsnxe_len > 0 && + wpa_s->rsnxe_len <= max_wpa_ie_len - wpa_ie_len) { + os_memcpy(wpa_ie + wpa_ie_len, wpa_s->rsnxe, wpa_s->rsnxe_len); + wpa_ie_len += wpa_s->rsnxe_len; + } + if (ssid->multi_ap_backhaul_sta) { size_t multi_ap_ie_len; @@ -3305,6 +3321,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) /* Starting new association, so clear the possibly used WPA IE from the * previous association. */ wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0); + wpa_s->rsnxe_len = 0; wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, ¶ms, NULL); if (!wpa_ie) { diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 1b29617ca..8c5defc3b 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -614,6 +614,9 @@ struct wpa_supplicant { int eapol_received; /* number of EAPOL packets received after the * previous association event */ + u8 rsnxe[20]; + size_t rsnxe_len; + struct scard_data *scard; char imsi[20]; int mnc_len;