RSNO: Use the RSN Selection element to indicate which variant was used

This replaces the use of the RSNE Override and RSNE Override 2 elements
with empty payload to indicate which RSNE variant was used.

In addition, this adds stricter validation of the RSNE in
(Re)Association Request frame to allow only the pairwise cipher suites
and AKMs listed in the indicated RSNE variant to be used.

Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
This commit is contained in:
Jouni Malinen 2024-07-29 15:41:59 +03:00 committed by Jouni Malinen
parent 524c452153
commit 62ca121f96
17 changed files with 223 additions and 55 deletions

View file

@ -515,10 +515,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
"Failed to initialize WPA state machine");
return -1;
}
wpa_auth_set_rsn_override(sta->wpa_sm,
elems.rsne_override != NULL);
wpa_auth_set_rsn_override_2(sta->wpa_sm,
elems.rsne_override_2 != NULL);
wpa_auth_set_rsn_selection(sta->wpa_sm, elems.rsn_selection,
elems.rsn_selection_len);
#ifdef CONFIG_IEEE80211BE
if (ap_sta_is_mld(hapd, sta)) {
wpa_printf(MSG_DEBUG,

View file

@ -1935,6 +1935,8 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
goto fail;
}
wpa_auth_set_rsn_selection(sta->wpa_sm, elems.rsn_selection,
elems.rsn_selection_len);
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
hapd->iface->freq,
elems.rsn_ie - 2, elems.rsn_ie_len + 2,
@ -1945,9 +1947,6 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
if (resp != WLAN_STATUS_SUCCESS)
goto fail;
wpa_auth_set_rsn_override(sta->wpa_sm, elems.rsne_override != NULL);
wpa_auth_set_rsn_override_2(sta->wpa_sm, elems.rsne_override_2 != NULL);
if (!elems.fils_nonce) {
wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
@ -4134,10 +4133,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
#endif /* CONFIG_IEEE80211BE */
wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
wpa_auth_set_rsn_override(sta->wpa_sm,
elems->rsne_override != NULL);
wpa_auth_set_rsn_override_2(sta->wpa_sm,
elems->rsne_override_2 != NULL);
wpa_auth_set_rsn_selection(sta->wpa_sm, elems->rsn_selection,
elems->rsn_selection_len);
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
hapd->iface->freq,
wpa_ie, wpa_ie_len,

View file

@ -1046,6 +1046,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
os_free(sm->last_rx_eapol_key);
os_free(sm->wpa_ie);
os_free(sm->rsnxe);
os_free(sm->rsn_selection);
#ifdef CONFIG_IEEE80211BE
for_each_sm_auth(sm, link_id) {
wpa_group_put(sm->mld_links[link_id].wpa_auth,
@ -3919,6 +3920,27 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
goto out;
}
#endif /* CONFIG_IEEE80211R_AP */
/* Verify RSN Selection element for RSN overriding */
if ((sm->rsn_selection && !kde.rsn_selection) ||
(!sm->rsn_selection && kde.rsn_selection) ||
(sm->rsn_selection && kde.rsn_selection &&
(sm->rsn_selection_len != kde.rsn_selection_len ||
os_memcmp(sm->rsn_selection, kde.rsn_selection,
sm->rsn_selection_len) != 0))) {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
"RSN Selection element from (Re)AssocReq did not match the one in EAPOL-Key msg 2/4");
wpa_hexdump(MSG_DEBUG, "RSN Selection in AssocReq",
sm->rsn_selection, sm->rsn_selection_len);
wpa_hexdump(MSG_DEBUG, "RSN Selection in EAPOL-Key msg 2/4",
kde.rsn_selection, kde.rsn_selection_len);
/* MLME-DEAUTHENTICATE.request */
wpa_sta_disconnect(wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
goto out;
}
#ifdef CONFIG_P2P
if (kde.ip_addr_req && kde.ip_addr_req[0] &&
wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
@ -6965,17 +6987,27 @@ void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg)
}
void wpa_auth_set_rsn_override(struct wpa_state_machine *sm, bool val)
void wpa_auth_set_rsn_selection(struct wpa_state_machine *sm, const u8 *ie,
size_t len)
{
if (sm)
sm->rsn_override = val;
}
void wpa_auth_set_rsn_override_2(struct wpa_state_machine *sm, bool val)
{
if (sm)
sm->rsn_override_2 = val;
if (!sm)
return;
os_free(sm->rsn_selection);
sm->rsn_selection = NULL;
sm->rsn_selection_len = 0;
sm->rsn_override = false;
sm->rsn_override_2 = false;
if (ie) {
if (len >= 1) {
if (ie[0] == RSN_SELECTION_RSNE_OVERRIDE)
sm->rsn_override = true;
else if (ie[0] == RSN_SELECTION_RSNE_OVERRIDE_2)
sm->rsn_override_2 = true;
}
sm->rsn_selection = os_memdup(ie, len);
if (sm->rsn_selection)
sm->rsn_selection_len = len;
}
}

View file

@ -629,8 +629,8 @@ u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm,
bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth,
u8 *fd_rsn_info);
void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg);
void wpa_auth_set_rsn_override(struct wpa_state_machine *sm, bool val);
void wpa_auth_set_rsn_override_2(struct wpa_state_machine *sm, bool val);
void wpa_auth_set_rsn_selection(struct wpa_state_machine *sm, const u8 *ie,
size_t len);
void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z);
void wpa_auth_set_ssid_protection(struct wpa_state_machine *sm, bool val);
void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,

View file

@ -111,6 +111,8 @@ struct wpa_state_machine {
size_t wpa_ie_len;
u8 *rsnxe;
size_t rsnxe_len;
u8 *rsn_selection;
size_t rsn_selection_len;
enum {
WPA_VERSION_NO_WPA = 0 /* WPA not used */,

View file

@ -1014,9 +1014,13 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
return WPA_INVALID_GROUP;
}
key_mgmt = data.key_mgmt & (wpa_auth->conf.wpa_key_mgmt |
wpa_auth->conf.rsn_override_key_mgmt |
wpa_auth->conf.rsn_override_key_mgmt_2);
if (sm->rsn_override_2)
key_mgmt = data.key_mgmt &
wpa_auth->conf.rsn_override_key_mgmt_2;
else if (sm->rsn_override)
key_mgmt = data.key_mgmt & wpa_auth->conf.rsn_override_key_mgmt;
else
key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt;
if (!key_mgmt) {
wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from "
MACSTR, data.key_mgmt, MAC2STR(sm->addr));
@ -1085,11 +1089,14 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
else
sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
if (version == WPA_PROTO_RSN)
if (version == WPA_PROTO_RSN && sm->rsn_override_2)
ciphers = data.pairwise_cipher &
(wpa_auth->conf.rsn_pairwise |
wpa_auth->conf.rsn_override_pairwise |
wpa_auth->conf.rsn_override_pairwise_2);
wpa_auth->conf.rsn_override_pairwise_2;
else if (version == WPA_PROTO_RSN && sm->rsn_override)
ciphers = data.pairwise_cipher &
wpa_auth->conf.rsn_override_pairwise;
else if (version == WPA_PROTO_RSN)
ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise;
else
ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise;
if (!ciphers) {

View file

@ -148,6 +148,15 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
elems->rsne_override_2 = pos;
elems->rsne_override_2_len = elen;
break;
case WFA_RSN_SELECTION_OUI_TYPE:
if (elen < 4 + 1) {
wpa_printf(MSG_DEBUG,
"Too short RSN Selection element ignored");
return -1;
}
elems->rsn_selection = pos + 4;
elems->rsn_selection_len = elen - 4;
break;
default:
wpa_printf(MSG_MSGDUMP, "Unknown WFA "
"information element ignored "

View file

@ -118,6 +118,7 @@ struct ieee802_11_elems {
const u8 *mbssid;
const u8 *rsne_override;
const u8 *rsne_override_2;
const u8 *rsn_selection;
u8 ssid_len;
u8 supp_rates_len;
@ -183,6 +184,7 @@ struct ieee802_11_elems {
u8 mbssid_len;
size_t rsne_override_len;
size_t rsne_override_2_len;
size_t rsn_selection_len;
struct mb_ies_info mb_ies;

View file

@ -1449,9 +1449,11 @@ struct ieee80211_ampe_ie {
#define WFA_RSNE_OVERRIDE_OUI_TYPE 0x29
#define WFA_RSNE_OVERRIDE_2_OUI_TYPE 0x2a
#define WFA_RSNXE_OVERRIDE_OUI_TYPE 0x2b
#define WFA_RSN_SELECTION_OUI_TYPE 0x2c
#define RSNE_OVERRIDE_IE_VENDOR_TYPE 0x506f9a29
#define RSNE_OVERRIDE_2_IE_VENDOR_TYPE 0x506f9a2a
#define RSNXE_OVERRIDE_IE_VENDOR_TYPE 0x506f9a2b
#define RSN_SELECTION_IE_VENDOR_TYPE 0x506f9a2c
#define MULTI_AP_SUB_ELEM_TYPE 0x06
#define MULTI_AP_PROFILE_SUB_ELEM_TYPE 0x07

View file

@ -3629,6 +3629,12 @@ static int wpa_parse_generic(const u8 *pos, struct wpa_eapol_ie_parse *ie)
return 0;
}
if (selector == RSN_SELECTION_IE_VENDOR_TYPE) {
ie->rsn_selection = p;
ie->rsn_selection_len = left;
return 0;
}
return 2;
}

View file

@ -643,6 +643,14 @@ struct wpa_pasn_params_data {
#define WPA_PASN_PUBKEY_COMPRESSED_1 0x03
#define WPA_PASN_PUBKEY_UNCOMPRESSED 0x04
/* WPA3 specification - RSN Selection element */
enum rsn_selection_variant {
RSN_SELECTION_RSNE = 0,
RSN_SELECTION_RSNE_OVERRIDE = 1,
RSN_SELECTION_RSNE_OVERRIDE_2 = 2,
};
int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
int key_mgmt, bool reassoc_resp);
void wpa_ft_parse_ies_free(struct wpa_ft_ies *parse);
@ -704,6 +712,8 @@ struct wpa_eapol_ie_parse {
u16 aid;
const u8 *wmm;
size_t wmm_len;
const u8 *rsn_selection;
size_t rsn_selection_len;
u16 valid_mlo_gtks; /* bitmap of valid link GTK KDEs */
const u8 *mlo_gtk[MAX_NUM_MLD_LINKS];
size_t mlo_gtk_len[MAX_NUM_MLD_LINKS];

View file

@ -531,7 +531,7 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
size_t mic_len, hdrlen, rlen, extra_len = 0;
struct wpa_eapol_key *reply;
u8 *rbuf, *key_mic;
u8 *rsn_ie_buf = NULL;
u8 *rsn_ie_buf = NULL, *buf2 = NULL;
u16 key_info;
#ifdef CONFIG_TESTING_OPTIONS
size_t pad_len = 0;
@ -581,6 +581,37 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
}
#endif /* CONFIG_IEEE80211R */
if (sm->rsn_override != RSN_OVERRIDE_NOT_USED) {
u8 *pos;
buf2 = os_malloc(wpa_ie_len + 2 + 4 + 1);
if (!buf2) {
os_free(rsn_ie_buf);
return -1;
}
os_memcpy(buf2, wpa_ie, wpa_ie_len);
pos = buf2 + wpa_ie_len;
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = 4 + 1;
WPA_PUT_BE32(pos, RSN_SELECTION_IE_VENDOR_TYPE);
pos += 4;
if (sm->rsn_override == RSN_OVERRIDE_RSNE) {
*pos++ = RSN_SELECTION_RSNE;
} else if (sm->rsn_override == RSN_OVERRIDE_RSNE_OVERRIDE) {
*pos++ = RSN_SELECTION_RSNE_OVERRIDE;
} else if (sm->rsn_override == RSN_OVERRIDE_RSNE_OVERRIDE_2) {
*pos++ = RSN_SELECTION_RSNE_OVERRIDE_2;
} else {
os_free(rsn_ie_buf);
os_free(buf2);
return -1;
}
wpa_ie = buf2;
wpa_ie_len += 2 + 4 + 1;
}
wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
#ifdef CONFIG_TESTING_OPTIONS
@ -601,6 +632,7 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
&rlen, (void *) &reply);
if (rbuf == NULL) {
os_free(rsn_ie_buf);
os_free(buf2);
return -1;
}
@ -633,6 +665,7 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
WPA_PUT_BE16(key_mic + mic_len, wpa_ie_len + extra_len);
os_memcpy(key_mic + mic_len + 2, wpa_ie, wpa_ie_len); /* Key Data */
os_free(rsn_ie_buf);
os_free(buf2);
#ifdef CONFIG_TESTING_OPTIONS
if (sm->test_eapol_m2_elems) {
os_memcpy(key_mic + mic_len + 2 + wpa_ie_len,
@ -4767,6 +4800,9 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
case WPA_PARAM_SSID_PROTECTION:
sm->ssid_protection = value;
break;
case WPA_PARAM_RSN_OVERRIDE:
sm->rsn_override = value;
break;
default:
break;
}

View file

@ -137,6 +137,14 @@ enum wpa_sm_conf_params {
WPA_PARAM_ENCRYPT_EAPOL_M4,
WPA_PARAM_FT_PREPEND_PMKID,
WPA_PARAM_SSID_PROTECTION,
WPA_PARAM_RSN_OVERRIDE,
};
enum wpa_rsn_override {
RSN_OVERRIDE_NOT_USED,
RSN_OVERRIDE_RSNE,
RSN_OVERRIDE_RSNE_OVERRIDE,
RSN_OVERRIDE_RSNE_OVERRIDE_2,
};
struct rsn_supp_config {

View file

@ -229,6 +229,8 @@ struct wpa_sm {
bool wmm_enabled;
bool driver_bss_selection;
bool ft_prepend_pmkid;
enum wpa_rsn_override rsn_override;
};

View file

@ -3371,9 +3371,10 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
int l, len, found = 0, found_x = 0, wpa_found, rsn_found;
const u8 *p;
const u8 *p, *ie;
u8 bssid[ETH_ALEN];
bool bssid_known;
enum wpa_rsn_override rsn_override;
wpa_dbg(wpa_s, MSG_DEBUG, "Association info event");
wpa_s->ssid_verified = false;
@ -3485,6 +3486,25 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
if (!found_x && data->assoc_info.req_ies)
wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
rsn_override = RSN_OVERRIDE_NOT_USED;
ie = get_vendor_ie(data->assoc_info.req_ies,
data->assoc_info.req_ies_len,
RSN_SELECTION_IE_VENDOR_TYPE);
if (ie && ie[1] >= 4 + 1) {
switch (ie[2 + 4]) {
case RSN_SELECTION_RSNE:
rsn_override = RSN_OVERRIDE_RSNE;
break;
case RSN_SELECTION_RSNE_OVERRIDE:
rsn_override = RSN_OVERRIDE_RSNE_OVERRIDE;
break;
case RSN_SELECTION_RSNE_OVERRIDE_2:
rsn_override = RSN_OVERRIDE_RSNE_OVERRIDE_2;
break;
}
}
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE, rsn_override);
#ifdef CONFIG_FILS
#ifdef CONFIG_SME
if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS ||

View file

@ -2467,28 +2467,46 @@ mscs_fail:
wpa_s->sme.assoc_req_ie_len += multi_ap_ie_len;
}
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE,
RSN_OVERRIDE_NOT_USED);
if (wpas_rsn_overriding(wpa_s) &&
wpas_ap_supports_rsn_overriding(wpa_s, wpa_s->current_bss) &&
wpa_s->sme.assoc_req_ie_len + 2 + 4 <=
sizeof(wpa_s->sme.assoc_req_ie)) {
u8 *pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len;
u32 type = 0;
const u8 *ie;
enum rsn_selection_variant variant = RSN_SELECTION_RSNE;
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE,
RSN_OVERRIDE_RSNE);
ie = wpa_bss_get_rsne(wpa_s, wpa_s->current_bss, ssid,
wpa_s->valid_links);
if (ie && ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie[1] >= 4)
type = WPA_GET_BE32(&ie[2]);
if (ie && ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie[1] >= 4) {
u32 type;
if (type) {
/* Indicate support for RSN overriding */
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = 4;
WPA_PUT_BE32(pos, type);
wpa_s->sme.assoc_req_ie_len += 2 + 4;
type = WPA_GET_BE32(&ie[2]);
if (type == RSNE_OVERRIDE_IE_VENDOR_TYPE) {
variant = RSN_SELECTION_RSNE_OVERRIDE;
wpa_sm_set_param(wpa_s->wpa,
WPA_PARAM_RSN_OVERRIDE,
RSN_OVERRIDE_RSNE_OVERRIDE);
} else if (type == RSNE_OVERRIDE_2_IE_VENDOR_TYPE) {
variant = RSN_SELECTION_RSNE_OVERRIDE_2;
wpa_sm_set_param(wpa_s->wpa,
WPA_PARAM_RSN_OVERRIDE,
RSN_OVERRIDE_RSNE_OVERRIDE_2);
}
}
/* Indicate which RSNE variant was used */
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = 4 + 1;
WPA_PUT_BE32(pos, RSN_SELECTION_IE_VENDOR_TYPE);
pos += 4;
*pos = variant;
wpa_s->sme.assoc_req_ie_len += 2 + 4 + 1;
}
params.bssid = bssid;
params.ssid = wpa_s->sme.ssid;
params.ssid_len = wpa_s->sme.ssid_len;

View file

@ -3943,32 +3943,51 @@ mscs_end:
wpa_ie_len += multi_ap_ie_len;
}
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE,
RSN_OVERRIDE_NOT_USED);
if (!wpas_driver_bss_selection(wpa_s) &&
wpas_rsn_overriding(wpa_s) &&
wpas_ap_supports_rsn_overriding(wpa_s, bss) &&
wpa_ie_len + 2 + 4 <= max_wpa_ie_len) {
u8 *pos = wpa_ie + wpa_ie_len;
u32 type = 0;
wpa_ie_len + 2 + 4 + 1 <= max_wpa_ie_len) {
u8 *pos = wpa_ie + wpa_ie_len, *start = pos;
const u8 *ie;
enum rsn_selection_variant variant = RSN_SELECTION_RSNE;
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE,
RSN_OVERRIDE_RSNE);
ie = wpa_bss_get_rsne(wpa_s, bss, ssid, wpa_s->valid_links);
if (ie && ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie[1] >= 4)
type = WPA_GET_BE32(&ie[2]);
if (ie && ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie[1] >= 4) {
u32 type;
if (type) {
/* Indicate support for RSN overriding */
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = 4;
WPA_PUT_BE32(pos, type);
pos += 4;
wpa_hexdump(MSG_MSGDUMP, "RSNE Override", wpa_ie,
pos - wpa_ie);
wpa_ie_len += 2 + 4;
type = WPA_GET_BE32(&ie[2]);
if (type == RSNE_OVERRIDE_IE_VENDOR_TYPE) {
variant = RSN_SELECTION_RSNE_OVERRIDE;
wpa_sm_set_param(wpa_s->wpa,
WPA_PARAM_RSN_OVERRIDE,
RSN_OVERRIDE_RSNE_OVERRIDE);
} else if (type == RSNE_OVERRIDE_2_IE_VENDOR_TYPE) {
variant = RSN_SELECTION_RSNE_OVERRIDE_2;
wpa_sm_set_param(wpa_s->wpa,
WPA_PARAM_RSN_OVERRIDE,
RSN_OVERRIDE_RSNE_OVERRIDE_2);
}
}
/* Indicate which RSNE variant was used */
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = 4 + 1;
WPA_PUT_BE32(pos, RSN_SELECTION_IE_VENDOR_TYPE);
pos += 4;
*pos++ = variant;
wpa_hexdump(MSG_MSGDUMP, "RSN Selection", start, pos - start);
wpa_ie_len += pos - start;
}
if (wpas_driver_bss_selection(wpa_s) &&
wpas_rsn_overriding(wpa_s)) {
/* TODO: Replace this indication of support for RSN overriding
* to the driver in driver-based BSS selection cases with
* something cleaner. */
if (wpa_ie_len + 2 + 4 <= max_wpa_ie_len) {
u8 *pos = wpa_ie + wpa_ie_len;