diff options
Diffstat (limited to 'contrib/wpa/src/ap/ieee802_11.c')
-rw-r--r-- | contrib/wpa/src/ap/ieee802_11.c | 2808 |
1 files changed, 2493 insertions, 315 deletions
diff --git a/contrib/wpa/src/ap/ieee802_11.c b/contrib/wpa/src/ap/ieee802_11.c index c85a28db44b7..db41049287fc 100644 --- a/contrib/wpa/src/ap/ieee802_11.c +++ b/contrib/wpa/src/ap/ieee802_11.c @@ -24,6 +24,8 @@ #include "common/dpp.h" #include "common/ocv.h" #include "common/wpa_common.h" +#include "common/wpa_ctrl.h" +#include "common/ptksa_cache.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" @@ -64,6 +66,23 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, const u8 *msk, size_t msk_len, int *is_pub); #endif /* CONFIG_FILS */ + +#ifdef CONFIG_PASN + +static int handle_auth_pasn_resp(struct hostapd_data *hapd, + struct sta_info *sta, + struct rsn_pmksa_cache_entry *pmksa, + u16 status); +#ifdef CONFIG_FILS + +static void pasn_fils_auth_resp(struct hostapd_data *hapd, + struct sta_info *sta, u16 status, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len); + +#endif /* CONFIG_FILS */ +#endif /* CONFIG_PASN */ + static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int rssi, int from_queue); @@ -88,6 +107,7 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; int i, num, count; + int h2e_required; if (hapd->iface->current_rates == NULL) return eid; @@ -98,6 +118,12 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) num++; if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) num++; + h2e_required = (hapd->conf->sae_pwe == 1 || + hostapd_sae_pw_id_in_use(hapd->conf) == 2) && + hapd->conf->sae_pwe != 3 && + wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt); + if (h2e_required) + num++; if (num > 8) { /* rest of the rates are encoded in Extended supported * rates element */ @@ -124,6 +150,11 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; } + if (h2e_required && count < 8) { + count++; + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY; + } + return pos; } @@ -132,6 +163,7 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; int i, num, count; + int h2e_required; if (hapd->iface->current_rates == NULL) return eid; @@ -141,6 +173,12 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) num++; if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) num++; + h2e_required = (hapd->conf->sae_pwe == 1 || + hostapd_sae_pw_id_in_use(hapd->conf) == 2) && + hapd->conf->sae_pwe != 3 && + wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt); + if (h2e_required) + num++; if (num <= 8) return eid; num -= 8; @@ -170,14 +208,41 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; } + if (h2e_required) { + count++; + if (count > 8) + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY; + } + return pos; } +u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid, + size_t len) +{ + size_t i; + + for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) { + if (hapd->conf->radio_measurements[i]) + break; + } + + if (i == RRM_CAPABILITIES_IE_LEN || len < 2 + RRM_CAPABILITIES_IE_LEN) + return eid; + + *eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES; + *eid++ = RRM_CAPABILITIES_IE_LEN; + os_memcpy(eid, hapd->conf->radio_measurements, RRM_CAPABILITIES_IE_LEN); + + return eid + RRM_CAPABILITIES_IE_LEN; +} + + u16 hostapd_own_capab_info(struct hostapd_data *hapd) { int capab = WLAN_CAPABILITY_ESS; - int privacy; + int privacy = 0; int dfs; int i; @@ -193,12 +258,14 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd) hapd->iconf->preamble == SHORT_PREAMBLE) capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; +#ifdef CONFIG_WEP privacy = hapd->conf->ssid.wep.keys_set; if (hapd->conf->ieee802_1x && (hapd->conf->default_wep_key_len || hapd->conf->individual_wep_key_len)) privacy = 1; +#endif /* CONFIG_WEP */ if (hapd->conf->wpa) privacy = 1; @@ -238,6 +305,7 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd) } +#ifdef CONFIG_WEP #ifndef CONFIG_NO_RC4 static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, u16 auth_transaction, const u8 *challenge, @@ -294,9 +362,10 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, return 0; } #endif /* CONFIG_NO_RC4 */ +#endif /* CONFIG_WEP */ -static int send_auth_reply(struct hostapd_data *hapd, +static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta, const u8 *dst, const u8 *bssid, u16 auth_alg, u16 auth_transaction, u16 resp, const u8 *ies, size_t ies_len, const char *dbg) @@ -329,7 +398,38 @@ static int send_auth_reply(struct hostapd_data *hapd, " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)", MAC2STR(dst), auth_alg, auth_transaction, resp, (unsigned long) ies_len, dbg); - if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0) +#ifdef CONFIG_TESTING_OPTIONS +#ifdef CONFIG_SAE + if (hapd->conf->sae_confirm_immediate == 2 && + auth_alg == WLAN_AUTH_SAE) { + if (auth_transaction == 1 && sta && + (resp == WLAN_STATUS_SUCCESS || + resp == WLAN_STATUS_SAE_HASH_TO_ELEMENT || + resp == WLAN_STATUS_SAE_PK)) { + wpa_printf(MSG_DEBUG, + "TESTING: Postpone SAE Commit transmission until Confirm is ready"); + os_free(sta->sae_postponed_commit); + sta->sae_postponed_commit = buf; + sta->sae_postponed_commit_len = rlen; + return WLAN_STATUS_SUCCESS; + } + + if (auth_transaction == 2 && sta && sta->sae_postponed_commit) { + wpa_printf(MSG_DEBUG, + "TESTING: Send postponed SAE Commit first, immediately followed by SAE Confirm"); + if (hostapd_drv_send_mlme(hapd, + sta->sae_postponed_commit, + sta->sae_postponed_commit_len, + 0, NULL, 0, 0) < 0) + wpa_printf(MSG_INFO, "send_auth_reply: send failed"); + os_free(sta->sae_postponed_commit); + sta->sae_postponed_commit = NULL; + sta->sae_postponed_commit_len = 0; + } + } +#endif /* CONFIG_SAE */ +#endif /* CONFIG_TESTING_OPTIONS */ + if (hostapd_drv_send_mlme(hapd, reply, rlen, 0, NULL, 0, 0) < 0) wpa_printf(MSG_INFO, "send_auth_reply: send failed"); else reply_res = WLAN_STATUS_SUCCESS; @@ -349,7 +449,7 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, struct sta_info *sta; int reply_res; - reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, + reply_res = send_auth_reply(hapd, NULL, dst, bssid, WLAN_AUTH_FT, auth_transaction, status, ies, ies_len, "auth-ft-finish"); @@ -387,16 +487,17 @@ static void sae_set_state(struct sta_info *sta, enum sae_state state, } -static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, - struct sta_info *sta, int update) +static const char * sae_get_password(struct hostapd_data *hapd, + struct sta_info *sta, + const char *rx_id, + struct sae_password_entry **pw_entry, + struct sae_pt **s_pt, + const struct sae_pk **s_pk) { - struct wpabuf *buf; const char *password = NULL; struct sae_password_entry *pw; - const char *rx_id = NULL; - - if (sta->sae->tmp) - rx_id = sta->sae->tmp->pw_id; + struct sae_pt *pt = NULL; + const struct sae_pk *pk = NULL; for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { if (!is_broadcast_ether_addr(pw->peer_addr) && @@ -408,18 +509,70 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, os_strcmp(rx_id, pw->identifier) != 0) continue; password = pw->password; + pt = pw->pt; + if (!(hapd->conf->mesh & MESH_ENABLED)) + pk = pw->pk; break; } - if (!password) - password = hapd->conf->ssid.wpa_passphrase; if (!password) { + password = hapd->conf->ssid.wpa_passphrase; + pt = hapd->conf->ssid.pt; + } + + if (pw_entry) + *pw_entry = pw; + if (s_pt) + *s_pt = pt; + if (s_pk) + *s_pk = pk; + + return password; +} + + +static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, + struct sta_info *sta, int update, + int status_code) +{ + struct wpabuf *buf; + const char *password = NULL; + struct sae_password_entry *pw; + const char *rx_id = NULL; + int use_pt = 0; + struct sae_pt *pt = NULL; + const struct sae_pk *pk = NULL; + + if (sta->sae->tmp) { + rx_id = sta->sae->tmp->pw_id; + use_pt = sta->sae->h2e; +#ifdef CONFIG_SAE_PK + os_memcpy(sta->sae->tmp->own_addr, hapd->own_addr, ETH_ALEN); + os_memcpy(sta->sae->tmp->peer_addr, sta->addr, ETH_ALEN); +#endif /* CONFIG_SAE_PK */ + } + + if (rx_id && hapd->conf->sae_pwe != 3) + use_pt = 1; + else if (status_code == WLAN_STATUS_SUCCESS) + use_pt = 0; + else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || + status_code == WLAN_STATUS_SAE_PK) + use_pt = 1; + + password = sae_get_password(hapd, sta, rx_id, &pw, &pt, &pk); + if (!password || (use_pt && !pt)) { wpa_printf(MSG_DEBUG, "SAE: No password available"); return NULL; } - if (update && + if (update && use_pt && + sae_prepare_commit_pt(sta->sae, pt, hapd->own_addr, sta->addr, + NULL, pk) < 0) + return NULL; + + if (update && !use_pt && sae_prepare_commit(hapd->own_addr, sta->addr, - (u8 *) password, os_strlen(password), rx_id, + (u8 *) password, os_strlen(password), sta->sae) < 0) { wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); return NULL; @@ -436,10 +589,13 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN + (rx_id ? 3 + os_strlen(rx_id) : 0)); - if (buf == NULL) - return NULL; - sae_write_commit(sta->sae, buf, sta->sae->tmp ? - sta->sae->tmp->anti_clogging_token : NULL, rx_id); + if (buf && + sae_write_commit(sta->sae, buf, sta->sae->tmp ? + sta->sae->tmp->anti_clogging_token : NULL, + rx_id) < 0) { + wpabuf_free(buf); + buf = NULL; + } return buf; } @@ -454,7 +610,17 @@ static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd, if (buf == NULL) return NULL; - sae_write_confirm(sta->sae, buf); +#ifdef CONFIG_SAE_PK +#ifdef CONFIG_TESTING_OPTIONS + if (sta->sae->tmp) + sta->sae->tmp->omit_pk_elem = hapd->conf->sae_pk_omit; +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_SAE_PK */ + + if (sae_write_confirm(sta->sae, buf) < 0) { + wpabuf_free(buf); + return NULL; + } return buf; } @@ -462,19 +628,36 @@ static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd, static int auth_sae_send_commit(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *bssid, int update) + const u8 *bssid, int update, int status_code) { struct wpabuf *data; int reply_res; + u16 status; - data = auth_build_sae_commit(hapd, sta, update); + data = auth_build_sae_commit(hapd, sta, update, status_code); if (!data && sta->sae->tmp && sta->sae->tmp->pw_id) return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; if (data == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1, - WLAN_STATUS_SUCCESS, wpabuf_head(data), + if (sta->sae->tmp && sta->sae->pk) + status = WLAN_STATUS_SAE_PK; + else if (sta->sae->tmp && sta->sae->h2e) + status = WLAN_STATUS_SAE_HASH_TO_ELEMENT; + else + status = WLAN_STATUS_SUCCESS; +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->sae_commit_status >= 0 && + hapd->conf->sae_commit_status != status) { + wpa_printf(MSG_INFO, + "TESTING: Override SAE commit status code %u --> %d", + status, hapd->conf->sae_commit_status); + status = hapd->conf->sae_commit_status; + } +#endif /* CONFIG_TESTING_OPTIONS */ + reply_res = send_auth_reply(hapd, sta, sta->addr, bssid, + WLAN_AUTH_SAE, 1, + status, wpabuf_head(data), wpabuf_len(data), "sae-send-commit"); wpabuf_free(data); @@ -494,7 +677,8 @@ static int auth_sae_send_confirm(struct hostapd_data *hapd, if (data == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2, + reply_res = send_auth_reply(hapd, sta, sta->addr, bssid, + WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS, wpabuf_head(data), wpabuf_len(data), "sae-send-confirm"); @@ -503,49 +687,62 @@ static int auth_sae_send_confirm(struct hostapd_data *hapd, return reply_res; } +#endif /* CONFIG_SAE */ + + +#if defined(CONFIG_SAE) || defined(CONFIG_PASN) -static int use_sae_anti_clogging(struct hostapd_data *hapd) +static int use_anti_clogging(struct hostapd_data *hapd) { struct sta_info *sta; unsigned int open = 0; - if (hapd->conf->sae_anti_clogging_threshold == 0) + if (hapd->conf->anti_clogging_threshold == 0) return 1; for (sta = hapd->sta_list; sta; sta = sta->next) { - if (!sta->sae) - continue; - if (sta->sae->state != SAE_COMMITTED && - sta->sae->state != SAE_CONFIRMED) - continue; - open++; - if (open >= hapd->conf->sae_anti_clogging_threshold) +#ifdef CONFIG_SAE + if (sta->sae && + (sta->sae->state == SAE_COMMITTED || + sta->sae->state == SAE_CONFIRMED)) + open++; +#endif /* CONFIG_SAE */ +#ifdef CONFIG_PASN + if (sta->pasn && sta->pasn->ecdh) + open++; +#endif /* CONFIG_PASN */ + if (open >= hapd->conf->anti_clogging_threshold) return 1; } +#ifdef CONFIG_SAE /* In addition to already existing open SAE sessions, check whether * there are enough pending commit messages in the processing queue to * potentially result in too many open sessions. */ if (open + dl_list_len(&hapd->sae_commit_queue) >= - hapd->conf->sae_anti_clogging_threshold) + hapd->conf->anti_clogging_threshold) return 1; +#endif /* CONFIG_SAE */ return 0; } -static u8 sae_token_hash(struct hostapd_data *hapd, const u8 *addr) +static int comeback_token_hash(struct hostapd_data *hapd, const u8 *addr, + u8 *idx) { u8 hash[SHA256_MAC_LEN]; - hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), - addr, ETH_ALEN, hash); - return hash[0]; + if (hmac_sha256(hapd->comeback_key, sizeof(hapd->comeback_key), + addr, ETH_ALEN, hash) < 0) + return -1; + *idx = hash[0]; + return 0; } -static int check_sae_token(struct hostapd_data *hapd, const u8 *addr, - const u8 *token, size_t token_len) +static int check_comeback_token(struct hostapd_data *hapd, const u8 *addr, + const u8 *token, size_t token_len) { u8 mac[SHA256_MAC_LEN]; const u8 *addrs[2]; @@ -553,12 +750,13 @@ static int check_sae_token(struct hostapd_data *hapd, const u8 *addr, u16 token_idx; u8 idx; - if (token_len != SHA256_MAC_LEN) + if (token_len != SHA256_MAC_LEN || + comeback_token_hash(hapd, addr, &idx) < 0) return -1; - idx = sae_token_hash(hapd, addr); - token_idx = hapd->sae_pending_token_idx[idx]; + token_idx = hapd->comeback_pending_idx[idx]; if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) { - wpa_printf(MSG_DEBUG, "SAE: Invalid anti-clogging token from " + wpa_printf(MSG_DEBUG, + "Comeback: Invalid anti-clogging token from " MACSTR " - token_idx 0x%04x, expected 0x%04x", MAC2STR(addr), WPA_GET_BE16(token), token_idx); return -1; @@ -568,19 +766,19 @@ static int check_sae_token(struct hostapd_data *hapd, const u8 *addr, len[0] = ETH_ALEN; addrs[1] = token; len[1] = 2; - if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key), + if (hmac_sha256_vector(hapd->comeback_key, sizeof(hapd->comeback_key), 2, addrs, len, mac) < 0 || os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0) return -1; - hapd->sae_pending_token_idx[idx] = 0; /* invalidate used token */ + hapd->comeback_pending_idx[idx] = 0; /* invalidate used token */ return 0; } static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, - int group, const u8 *addr) + int group, const u8 *addr, int h2e) { struct wpabuf *buf; u8 *token; @@ -592,32 +790,44 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, u16 token_idx; os_get_reltime(&now); - if (!os_reltime_initialized(&hapd->last_sae_token_key_update) || - os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60) || - hapd->sae_token_idx == 0xffff) { - if (random_get_bytes(hapd->sae_token_key, - sizeof(hapd->sae_token_key)) < 0) + if (!os_reltime_initialized(&hapd->last_comeback_key_update) || + os_reltime_expired(&now, &hapd->last_comeback_key_update, 60) || + hapd->comeback_idx == 0xffff) { + if (random_get_bytes(hapd->comeback_key, + sizeof(hapd->comeback_key)) < 0) return NULL; - wpa_hexdump(MSG_DEBUG, "SAE: Updated token key", - hapd->sae_token_key, sizeof(hapd->sae_token_key)); - hapd->last_sae_token_key_update = now; - hapd->sae_token_idx = 0; - os_memset(hapd->sae_pending_token_idx, 0, - sizeof(hapd->sae_pending_token_idx)); + wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key", + hapd->comeback_key, sizeof(hapd->comeback_key)); + hapd->last_comeback_key_update = now; + hapd->comeback_idx = 0; + os_memset(hapd->comeback_pending_idx, 0, + sizeof(hapd->comeback_pending_idx)); } - buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN); + buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN); if (buf == NULL) return NULL; - wpabuf_put_le16(buf, group); /* Finite Cyclic Group */ + if (group) + wpabuf_put_le16(buf, group); /* Finite Cyclic Group */ + + if (h2e) { + /* Encapsulate Anti-clogging Token field in a container IE */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN); + } + + if (comeback_token_hash(hapd, addr, &p_idx) < 0) { + wpabuf_free(buf); + return NULL; + } - p_idx = sae_token_hash(hapd, addr); - token_idx = hapd->sae_pending_token_idx[p_idx]; + token_idx = hapd->comeback_pending_idx[p_idx]; if (!token_idx) { - hapd->sae_token_idx++; - token_idx = hapd->sae_token_idx; - hapd->sae_pending_token_idx[p_idx] = token_idx; + hapd->comeback_idx++; + token_idx = hapd->comeback_idx; + hapd->comeback_pending_idx[p_idx] = token_idx; } WPA_PUT_BE16(idx, token_idx); token = wpabuf_put(buf, SHA256_MAC_LEN); @@ -625,7 +835,7 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, len[0] = ETH_ALEN; addrs[1] = idx; len[1] = sizeof(idx); - if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key), + if (hmac_sha256_vector(hapd->comeback_key, sizeof(hapd->comeback_key), 2, addrs, len, token) < 0) { wpabuf_free(buf); return NULL; @@ -635,6 +845,10 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, return buf; } +#endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */ + + +#ifdef CONFIG_SAE static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta) { @@ -663,7 +877,7 @@ static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data) switch (sta->sae->state) { case SAE_COMMITTED: - ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0); + ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1); eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); @@ -754,6 +968,9 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) mlme_authenticate_indication(hapd, sta); wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm"); + crypto_bignum_deinit(sta->sae->peer_commit_scalar_accepted, 0); + sta->sae->peer_commit_scalar_accepted = sta->sae->peer_commit_scalar; + sta->sae->peer_commit_scalar = NULL; wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, sta->sae->pmk, sta->sae->pmkid); sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS); @@ -761,8 +978,8 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *bssid, u8 auth_transaction, int allow_reuse, - int *sta_removed) + const u8 *bssid, u16 auth_transaction, u16 status_code, + int allow_reuse, int *sta_removed) { int ret; @@ -777,8 +994,16 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, switch (sta->sae->state) { case SAE_NOTHING: if (auth_transaction == 1) { + if (sta->sae->tmp) { + sta->sae->h2e = + (status_code == + WLAN_STATUS_SAE_HASH_TO_ELEMENT || + status_code == WLAN_STATUS_SAE_PK); + sta->sae->pk = + status_code == WLAN_STATUS_SAE_PK; + } ret = auth_sae_send_commit(hapd, sta, bssid, - !allow_reuse); + !allow_reuse, status_code); if (ret) return ret; sae_set_state(sta, SAE_COMMITTED, "Sent Commit"); @@ -787,14 +1012,17 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_UNSPECIFIED_FAILURE; /* - * In mesh case, both Commit and Confirm can be sent - * immediately. In infrastructure BSS, only a single - * Authentication frame (Commit) is expected from the AP - * here and the second one (Confirm) will be sent once - * the STA has sent its second Authentication frame - * (Confirm). + * In mesh case, both Commit and Confirm are sent + * immediately. In infrastructure BSS, by default, only + * a single Authentication frame (Commit) is expected + * from the AP here and the second one (Confirm) will + * be sent once the STA has sent its second + * Authentication frame (Confirm). This behavior can be + * overridden with explicit configuration so that the + * infrastructure BSS case sends both frames together. */ - if (hapd->conf->mesh & MESH_ENABLED) { + if ((hapd->conf->mesh & MESH_ENABLED) || + hapd->conf->sae_confirm_immediate) { /* * Send both Commit and Confirm immediately * based on SAE finite state machine @@ -845,7 +1073,8 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_SUCCESS; sta->sae->sync++; - ret = auth_sae_send_commit(hapd, sta, bssid, 0); + ret = auth_sae_send_commit(hapd, sta, bssid, 0, + status_code); if (ret) return ret; @@ -868,7 +1097,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, * additional events. */ return sae_sm_step(hapd, sta, bssid, auth_transaction, - 0, sta_removed); + WLAN_STATUS_SUCCESS, 0, sta_removed); } break; case SAE_CONFIRMED: @@ -878,7 +1107,8 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_SUCCESS; sta->sae->sync++; - ret = auth_sae_send_commit(hapd, sta, bssid, 1); + ret = auth_sae_send_commit(hapd, sta, bssid, 1, + status_code); if (ret) return ret; @@ -906,7 +1136,8 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, *sta_removed = 1; } else if (auth_transaction == 1) { wpa_printf(MSG_DEBUG, "SAE: Start reauthentication"); - ret = auth_sae_send_commit(hapd, sta, bssid, 1); + ret = auth_sae_send_commit(hapd, sta, bssid, 1, + status_code); if (ret) return ret; sae_set_state(sta, SAE_COMMITTED, "Sent Commit"); @@ -976,6 +1207,85 @@ static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta) } +static int sae_status_success(struct hostapd_data *hapd, u16 status_code) +{ + int sae_pwe = hapd->conf->sae_pwe; + int id_in_use; + bool sae_pk = false; + + id_in_use = hostapd_sae_pw_id_in_use(hapd->conf); + if (id_in_use == 2 && sae_pwe != 3) + sae_pwe = 1; + else if (id_in_use == 1 && sae_pwe == 0) + sae_pwe = 2; +#ifdef CONFIG_SAE_PK + sae_pk = hostapd_sae_pk_in_use(hapd->conf); + if (sae_pwe == 0 && sae_pk) + sae_pwe = 2; +#endif /* CONFIG_SAE_PK */ + + return ((sae_pwe == 0 || sae_pwe == 3) && + status_code == WLAN_STATUS_SUCCESS) || + (sae_pwe == 1 && + (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || + (sae_pk && status_code == WLAN_STATUS_SAE_PK))) || + (sae_pwe == 2 && + (status_code == WLAN_STATUS_SUCCESS || + status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || + (sae_pk && status_code == WLAN_STATUS_SAE_PK))); +} + + +static int sae_is_group_enabled(struct hostapd_data *hapd, int group) +{ + int *groups = hapd->conf->sae_groups; + int default_groups[] = { 19, 0 }; + int i; + + if (!groups) + groups = default_groups; + + for (i = 0; groups[i] > 0; i++) { + if (groups[i] == group) + return 1; + } + + return 0; +} + + +static int check_sae_rejected_groups(struct hostapd_data *hapd, + struct sae_data *sae) +{ + const struct wpabuf *groups; + size_t i, count; + const u8 *pos; + + if (!sae->tmp) + return 0; + groups = sae->tmp->peer_rejected_groups; + if (!groups) + return 0; + + pos = wpabuf_head(groups); + count = wpabuf_len(groups) / 2; + for (i = 0; i < count; i++) { + int enabled; + u16 group; + + group = WPA_GET_LE16(pos); + pos += 2; + enabled = sae_is_group_enabled(hapd, group); + wpa_printf(MSG_DEBUG, "SAE: Rejected group %u is %s", + group, enabled ? "enabled" : "disabled"); + if (enabled) + return 1; + } + + return 0; +} + + static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *mgmt, size_t len, u16 auth_transaction, u16 status_code) @@ -986,6 +1296,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, int default_groups[] = { 19, 0 }; const u8 *pos, *end; int sta_removed = 0; + bool success_status; if (!groups) groups = default_groups; @@ -995,7 +1306,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack"); pos = mgmt->u.auth.variable; end = ((const u8 *) mgmt) + len; - send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, + resp = status_code; + send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, auth_transaction, resp, pos, end - pos, "auth-sae-reflection-attack"); goto remove_sta; @@ -1003,7 +1315,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, if (hapd->conf->sae_commit_override && auth_transaction == 1) { wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override"); - send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, + send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, auth_transaction, resp, wpabuf_head(hapd->conf->sae_commit_override), wpabuf_len(hapd->conf->sae_commit_override), @@ -1013,9 +1325,11 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_TESTING_OPTIONS */ if (!sta->sae) { if (auth_transaction != 1 || - status_code != WLAN_STATUS_SUCCESS) { - resp = -1; - goto remove_sta; + !sae_status_success(hapd, status_code)) { + wpa_printf(MSG_DEBUG, "SAE: Unexpected Status Code %u", + status_code); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; } sta->sae = os_zalloc(sizeof(*sta->sae)); if (!sta->sae) { @@ -1080,7 +1394,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, * Authentication frame, and the commit-scalar and * COMMIT-ELEMENT previously sent. */ - resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0); + resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0, + status_code); if (resp != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_ERROR, "SAE: Failed to send commit message"); @@ -1103,7 +1418,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, goto remove_sta; } - if (status_code != WLAN_STATUS_SUCCESS) + if (!sae_status_success(hapd, status_code)) goto remove_sta; if (!(hapd->conf->mesh & MESH_ENABLED) && @@ -1136,7 +1451,9 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, ((const u8 *) mgmt) + len - mgmt->u.auth.variable, &token, - &token_len, groups); + &token_len, groups, status_code == + WLAN_STATUS_SAE_HASH_TO_ELEMENT || + status_code == WLAN_STATUS_SAE_PK); if (resp == SAE_SILENTLY_DISCARD) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message from " MACSTR " due to reflection attack", @@ -1154,7 +1471,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, goto remove_sta; } - if (token && check_sae_token(hapd, sta->addr, token, token_len) + if (token && + check_comeback_token(hapd, sta->addr, token, token_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " "incorrect token from " MACSTR, @@ -1166,12 +1484,24 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) goto reply; - if (!token && use_sae_anti_clogging(hapd) && !allow_reuse) { + if (check_sae_rejected_groups(hapd, sta->sae)) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } + + if (!token && use_anti_clogging(hapd) && !allow_reuse) { + int h2e = 0; + wpa_printf(MSG_DEBUG, "SAE: Request anti-clogging token from " MACSTR, MAC2STR(sta->addr)); + if (sta->sae->tmp) + h2e = sta->sae->h2e; + if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || + status_code == WLAN_STATUS_SAE_PK) + h2e = 1; data = auth_build_token_req(hapd, sta->sae->group, - sta->addr); + sta->addr, h2e); resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ; if (hapd->conf->mesh & MESH_ENABLED) sae_set_state(sta, SAE_NOTHING, @@ -1180,7 +1510,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, - allow_reuse, &sta_removed); + status_code, allow_reuse, &sta_removed); } else if (auth_transaction == 2) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -1221,8 +1551,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } sta->sae->rc = peer_send_confirm; } - resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, 0, - &sta_removed); + resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, + status_code, 0, &sta_removed); } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -1246,16 +1576,19 @@ reply: data = wpabuf_alloc_copy(pos, 2); sae_sme_send_external_auth_status(hapd, sta, resp); - send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, + send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, auth_transaction, resp, data ? wpabuf_head(data) : (u8 *) "", data ? wpabuf_len(data) : 0, "auth-sae"); } remove_sta: + if (auth_transaction == 1) + success_status = sae_status_success(hapd, status_code); + else + success_status = status_code == WLAN_STATUS_SUCCESS; if (!sta_removed && sta->added_unassoc && - (resp != WLAN_STATUS_SUCCESS || - status_code != WLAN_STATUS_SUCCESS)) { + (resp != WLAN_STATUS_SUCCESS || !success_status)) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } @@ -1283,7 +1616,7 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta) if (sta->sae->state != SAE_NOTHING) return -1; - ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0); + ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1); if (ret) return -1; @@ -1396,29 +1729,37 @@ static int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr) #endif /* CONFIG_SAE */ -static u16 wpa_res_to_status_code(int res) +static u16 wpa_res_to_status_code(enum wpa_validate_result res) { - if (res == WPA_INVALID_GROUP) + switch (res) { + case WPA_IE_OK: + return WLAN_STATUS_SUCCESS; + case WPA_INVALID_IE: + return WLAN_STATUS_INVALID_IE; + case WPA_INVALID_GROUP: return WLAN_STATUS_GROUP_CIPHER_NOT_VALID; - if (res == WPA_INVALID_PAIRWISE) + case WPA_INVALID_PAIRWISE: return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; - if (res == WPA_INVALID_AKMP) + case WPA_INVALID_AKMP: return WLAN_STATUS_AKMP_NOT_VALID; - if (res == WPA_ALLOC_FAIL) + case WPA_NOT_ENABLED: + return WLAN_STATUS_INVALID_IE; + case WPA_ALLOC_FAIL: return WLAN_STATUS_UNSPECIFIED_FAILURE; -#ifdef CONFIG_IEEE80211W - if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) + case WPA_MGMT_FRAME_PROTECTION_VIOLATION: return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; - if (res == WPA_INVALID_MGMT_GROUP_CIPHER) + case WPA_INVALID_MGMT_GROUP_CIPHER: return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; -#endif /* CONFIG_IEEE80211W */ - if (res == WPA_INVALID_MDIE) + case WPA_INVALID_MDIE: return WLAN_STATUS_INVALID_MDIE; - if (res == WPA_INVALID_PMKID) - return WLAN_STATUS_INVALID_PMKID; - if (res != WPA_IE_OK) + case WPA_INVALID_PROTO: return WLAN_STATUS_INVALID_IE; - return WLAN_STATUS_SUCCESS; + case WPA_INVALID_PMKID: + return WLAN_STATUS_INVALID_PMKID; + case WPA_DENIED_OTHER_REASON: + return WLAN_STATUS_ASSOC_DENIED_UNSPEC; + } + return WLAN_STATUS_INVALID_IE; } @@ -1438,7 +1779,7 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, u16 resp = WLAN_STATUS_SUCCESS; const u8 *end; struct ieee802_11_elems elems; - int res; + enum wpa_validate_result res; struct wpa_ie_data rsn; struct rsn_pmksa_cache_entry *pmksa = NULL; @@ -1554,6 +1895,8 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, hapd->iface->freq, elems.rsn_ie - 2, elems.rsn_ie_len + 2, + elems.rsnxe ? elems.rsnxe - 2 : NULL, + elems.rsnxe ? elems.rsnxe_len + 2 : 0, elems.mdie, elems.mdie_len, NULL, 0); resp = wpa_res_to_status_code(res); if (resp != WLAN_STATUS_SUCCESS) @@ -1612,11 +1955,11 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, FILS_SESSION_LEN); os_memcpy(sta->fils_session, elems.fils_session, FILS_SESSION_LEN); - /* FILS Wrapped Data */ - if (elems.fils_wrapped_data) { + /* Wrapped Data */ + if (elems.wrapped_data) { wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data", - elems.fils_wrapped_data, - elems.fils_wrapped_data_len); + elems.wrapped_data, + elems.wrapped_data_len); if (!pmksa) { #ifndef CONFIG_NO_RADIUS if (!sta->eapol_sm) { @@ -1626,8 +1969,8 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "FILS: Forward EAP-Initiate/Re-auth to authentication server"); ieee802_1x_encapsulate_radius( - hapd, sta, elems.fils_wrapped_data, - elems.fils_wrapped_data_len); + hapd, sta, elems.wrapped_data, + elems.wrapped_data_len); sta->fils_pending_cb = cb; wpa_printf(MSG_DEBUG, "FILS: Will send Authentication frame once the response from authentication server is available"); @@ -1636,8 +1979,8 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, * to maintain a copy of the EAP-Initiate/Reauth * message. */ if (fils_pmkid_erp(wpa_auth_sta_key_mgmt(sta->wpa_sm), - elems.fils_wrapped_data, - elems.fils_wrapped_data_len, + elems.wrapped_data, + elems.wrapped_data_len, sta->fils_erp_pmkid) == 0) sta->fils_erp_pmkid_set = 1; return; @@ -1780,12 +2123,12 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, wpabuf_put_u8(data, WLAN_EID_EXT_FILS_SESSION); wpabuf_put_data(data, sta->fils_session, FILS_SESSION_LEN); - /* FILS Wrapped Data */ + /* Wrapped Data */ if (!pmksa && erp_resp) { wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ wpabuf_put_u8(data, 1 + wpabuf_len(erp_resp)); /* Length */ /* Element ID Extension */ - wpabuf_put_u8(data, WLAN_EID_EXT_FILS_WRAPPED_DATA); + wpabuf_put_u8(data, WLAN_EID_EXT_WRAPPED_DATA); wpabuf_put_buf(data, erp_resp); if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm), @@ -1887,7 +2230,7 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd, auth_alg = (pub || resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK; - send_auth_reply(hapd, sta->addr, hapd->own_addr, auth_alg, 2, resp, + send_auth_reply(hapd, sta, sta->addr, hapd->own_addr, auth_alg, 2, resp, data ? wpabuf_head(data) : (u8 *) "", data ? wpabuf_len(data) : 0, "auth-fils-finish"); wpabuf_free(data); @@ -1909,50 +2252,52 @@ void ieee802_11_finish_fils_auth(struct hostapd_data *hapd, struct wpabuf *erp_resp, const u8 *msk, size_t msk_len) { - struct wpabuf *data; - int pub = 0; u16 resp; + u32 flags = sta->flags; - sta->flags &= ~WLAN_STA_PENDING_FILS_ERP; + sta->flags &= ~(WLAN_STA_PENDING_FILS_ERP | + WLAN_STA_PENDING_PASN_FILS_ERP); - if (!sta->fils_pending_cb) - return; resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE; - data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp, - msk, msk_len, &pub); - if (!data) { - wpa_printf(MSG_DEBUG, - "%s: prepare_auth_resp_fils() returned failure", - __func__); + + if (flags & WLAN_STA_PENDING_FILS_ERP) { + struct wpabuf *data; + int pub = 0; + + if (!sta->fils_pending_cb) + return; + + data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp, + msk, msk_len, &pub); + if (!data) { + wpa_printf(MSG_DEBUG, + "%s: prepare_auth_resp_fils() failure", + __func__); + } + sta->fils_pending_cb(hapd, sta, resp, data, pub); +#ifdef CONFIG_PASN + } else if (flags & WLAN_STA_PENDING_PASN_FILS_ERP) { + pasn_fils_auth_resp(hapd, sta, resp, erp_resp, + msk, msk_len); +#endif /* CONFIG_PASN */ } - sta->fils_pending_cb(hapd, sta, resp, data, pub); } #endif /* CONFIG_FILS */ -int -ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, - const u8 *msg, size_t len, u32 *session_timeout, - u32 *acct_interim_interval, - struct vlan_description *vlan_id, - struct hostapd_sta_wpa_psk_short **psk, - char **identity, char **radius_cui, int is_probe_req) +static int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, + const u8 *msg, size_t len, + struct radius_sta *info) { int res; - os_memset(vlan_id, 0, sizeof(*vlan_id)); - res = hostapd_allowed_address(hapd, addr, msg, len, - session_timeout, acct_interim_interval, - vlan_id, psk, identity, radius_cui, - is_probe_req); + res = hostapd_allowed_address(hapd, addr, msg, len, info, 0); if (res == HOSTAPD_ACL_REJECT) { - if (!is_probe_req) - wpa_printf(MSG_DEBUG, - "Station " MACSTR - " not allowed to authenticate", - MAC2STR(addr)); + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not allowed to authenticate", + MAC2STR(addr)); return HOSTAPD_ACL_REJECT; } @@ -1972,12 +2317,15 @@ ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, static int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, - int res, u32 session_timeout, - u32 acct_interim_interval, - struct vlan_description *vlan_id, - struct hostapd_sta_wpa_psk_short **psk, - char **identity, char **radius_cui) + int res, struct radius_sta *info) { + u32 session_timeout = info->session_timeout; + u32 acct_interim_interval = info->acct_interim_interval; + struct vlan_description *vlan_id = &info->vlan_id; + struct hostapd_sta_wpa_psk_short *psk = info->psk; + char *identity = info->identity; + char *radius_cui = info->radius_cui; + if (vlan_id->notempty && !hostapd_vlan_valid(hapd->conf->vlan, vlan_id)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, @@ -1994,20 +2342,22 @@ ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); hostapd_free_psk_list(sta->psk); - if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { - sta->psk = *psk; - *psk = NULL; - } else { + if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) + hostapd_copy_psk_list(&sta->psk, psk); + else sta->psk = NULL; - } os_free(sta->identity); - sta->identity = *identity; - *identity = NULL; + if (identity) + sta->identity = os_strdup(identity); + else + sta->identity = NULL; os_free(sta->radius_cui); - sta->radius_cui = *radius_cui; - *radius_cui = NULL; + if (radius_cui) + sta->radius_cui = os_strdup(radius_cui); + else + sta->radius_cui = NULL; if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval) sta->acct_interim_interval = acct_interim_interval; @@ -2025,6 +2375,1177 @@ ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, } +#ifdef CONFIG_PASN +#ifdef CONFIG_SAE + +static int pasn_wd_handle_sae_commit(struct hostapd_data *hapd, + struct sta_info *sta, + struct wpabuf *wd) +{ + struct pasn_data *pasn = sta->pasn; + const char *password; + const u8 *data; + size_t buf_len; + u16 res, alg, seq, status; + int groups[] = { pasn->group, 0 }; + struct sae_pt *pt = NULL; + int ret; + + if (!wd) + return -1; + + data = wpabuf_head_u8(wd); + buf_len = wpabuf_len(wd); + + if (buf_len < 6) { + wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu", + buf_len); + return -1; + } + + alg = WPA_GET_LE16(data); + seq = WPA_GET_LE16(data + 2); + status = WPA_GET_LE16(data + 4); + + wpa_printf(MSG_DEBUG, "PASN: SAE commit: alg=%u, seq=%u, status=%u", + alg, seq, status); + + if (alg != WLAN_AUTH_SAE || seq != 1 || + status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) { + wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE commit"); + return -1; + } + + sae_clear_data(&pasn->sae); + pasn->sae.state = SAE_NOTHING; + + ret = sae_set_group(&pasn->sae, pasn->group); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group"); + return -1; + } + + password = sae_get_password(hapd, sta, NULL, NULL, &pt, NULL); + if (!password || !pt) { + wpa_printf(MSG_DEBUG, "PASN: No SAE PT found"); + return -1; + } + + ret = sae_prepare_commit_pt(&pasn->sae, pt, hapd->own_addr, sta->addr, + NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit"); + return -1; + } + + res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, 0, + groups, 0); + if (res != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: Failed parsing SAE commit"); + return -1; + } + + /* Process the commit message and derive the PMK */ + ret = sae_process_commit(&pasn->sae); + if (ret) { + wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit"); + return -1; + } + + pasn->sae.state = SAE_COMMITTED; + + return 0; +} + + +static int pasn_wd_handle_sae_confirm(struct hostapd_data *hapd, + struct sta_info *sta, + struct wpabuf *wd) +{ + struct pasn_data *pasn = sta->pasn; + const u8 *data; + size_t buf_len; + u16 res, alg, seq, status; + + if (!wd) + return -1; + + data = wpabuf_head_u8(wd); + buf_len = wpabuf_len(wd); + + if (buf_len < 6) { + wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu", + buf_len); + return -1; + } + + alg = WPA_GET_LE16(data); + seq = WPA_GET_LE16(data + 2); + status = WPA_GET_LE16(data + 4); + + wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u", + alg, seq, status); + + if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm"); + return -1; + } + + res = sae_check_confirm(&pasn->sae, data + 6, buf_len - 6); + if (res != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm"); + return -1; + } + + pasn->sae.state = SAE_ACCEPTED; + + /* + * TODO: Based on on IEEE P802.11az/D2.6, the PMKSA derived with + * PASN/SAE should only be allowed with future PASN only. For now do not + * restrict this only for PASN. + */ + wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, + pasn->sae.pmk, pasn->sae.pmkid); + return 0; +} + + +static struct wpabuf * pasn_get_sae_wd(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct pasn_data *pasn = sta->pasn; + struct wpabuf *buf = NULL; + u8 *len_ptr; + size_t len; + + /* Need to add the entire Authentication frame body */ + buf = wpabuf_alloc(8 + SAE_COMMIT_MAX_LEN + 8 + SAE_CONFIRM_MAX_LEN); + if (!buf) { + wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer"); + return NULL; + } + + /* Need to add the entire authentication frame body for the commit */ + len_ptr = wpabuf_put(buf, 2); + wpabuf_put_le16(buf, WLAN_AUTH_SAE); + wpabuf_put_le16(buf, 1); + wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT); + + /* Write the actual commit and update the length accordingly */ + sae_write_commit(&pasn->sae, buf, NULL, 0); + len = wpabuf_len(buf); + WPA_PUT_LE16(len_ptr, len - 2); + + /* Need to add the entire Authentication frame body for the confirm */ + len_ptr = wpabuf_put(buf, 2); + wpabuf_put_le16(buf, WLAN_AUTH_SAE); + wpabuf_put_le16(buf, 2); + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + + sae_write_confirm(&pasn->sae, buf); + WPA_PUT_LE16(len_ptr, wpabuf_len(buf) - len - 2); + + pasn->sae.state = SAE_CONFIRMED; + + return buf; +} + +#endif /* CONFIG_SAE */ + + +#ifdef CONFIG_FILS + +static struct wpabuf * pasn_get_fils_wd(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct pasn_data *pasn = sta->pasn; + struct pasn_fils_data *fils = &pasn->fils; + struct wpabuf *buf = NULL; + + if (!fils->erp_resp) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Missing erp_resp"); + return NULL; + } + + buf = wpabuf_alloc(1500); + if (!buf) + return NULL; + + /* Add the authentication algorithm */ + wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK); + + /* Authentication Transaction seq# */ + wpabuf_put_le16(buf, 2); + + /* Status Code */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + + /* Own RSNE */ + wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher); + + /* FILS Nonce */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE); + wpabuf_put_data(buf, fils->anonce, FILS_NONCE_LEN); + + /* FILS Session */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION); + wpabuf_put_data(buf, fils->session, FILS_SESSION_LEN); + + /* Wrapped Data */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + wpabuf_len(fils->erp_resp)); + wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA); + wpabuf_put_buf(buf, fils->erp_resp); + + return buf; +} + + +static void pasn_fils_auth_resp(struct hostapd_data *hapd, + struct sta_info *sta, u16 status, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len) +{ + struct pasn_data *pasn = sta->pasn; + struct pasn_fils_data *fils = &pasn->fils; + u8 pmk[PMK_LEN_MAX]; + size_t pmk_len; + int ret; + + wpa_printf(MSG_DEBUG, "PASN: FILS: Handle AS response - status=%u", + status); + + if (status != WLAN_STATUS_SUCCESS) + goto fail; + + if (!pasn->secret) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Missing secret"); + goto fail; + } + + if (random_get_bytes(fils->anonce, FILS_NONCE_LEN) < 0) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ANonce"); + goto fail; + } + + wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS ANonce", + fils->anonce, FILS_NONCE_LEN); + + ret = fils_rmsk_to_pmk(pasn->akmp, msk, msk_len, fils->nonce, + fils->anonce, NULL, 0, pmk, &pmk_len); + if (ret) { + wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK"); + goto fail; + } + + ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr, + wpabuf_head(pasn->secret), + wpabuf_len(pasn->secret), + &sta->pasn->ptk, sta->pasn->akmp, + sta->pasn->cipher, sta->pasn->kdk_len); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PTK"); + goto fail; + } + + wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived"); + + wpabuf_free(pasn->secret); + pasn->secret = NULL; + + fils->erp_resp = erp_resp; + ret = handle_auth_pasn_resp(hapd, sta, NULL, WLAN_STATUS_SUCCESS); + fils->erp_resp = NULL; + + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to send response"); + goto fail; + } + + fils->state = PASN_FILS_STATE_COMPLETE; + return; +fail: + ap_free_sta(hapd, sta); +} + + +static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta, + struct wpabuf *wd) +{ +#ifdef CONFIG_NO_RADIUS + wpa_printf(MSG_DEBUG, "PASN: FILS: RADIUS is not configured. Fail"); + return -1; +#else /* CONFIG_NO_RADIUS */ + struct pasn_data *pasn = sta->pasn; + struct pasn_fils_data *fils = &pasn->fils; + struct ieee802_11_elems elems; + struct wpa_ie_data rsne_data; + struct wpabuf *fils_wd; + const u8 *data; + size_t buf_len; + u16 alg, seq, status; + int ret; + + if (fils->state != PASN_FILS_STATE_NONE) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Not expecting wrapped data"); + return -1; + } + + if (!wd) { + wpa_printf(MSG_DEBUG, "PASN: FILS: No wrapped data"); + return -1; + } + + data = wpabuf_head_u8(wd); + buf_len = wpabuf_len(wd); + + if (buf_len < 6) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short. len=%zu", + buf_len); + return -1; + } + + alg = WPA_GET_LE16(data); + seq = WPA_GET_LE16(data + 2); + status = WPA_GET_LE16(data + 4); + + wpa_printf(MSG_DEBUG, "PASN: FILS: alg=%u, seq=%u, status=%u", + alg, seq, status); + + if (alg != WLAN_AUTH_FILS_SK || seq != 1 || + status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, + "PASN: FILS: Dropping peer authentication"); + return -1; + } + + data += 6; + buf_len -= 6; + + if (ieee802_11_parse_elems(data, buf_len, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Could not parse elements"); + return -1; + } + + if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce || + !elems.wrapped_data) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs"); + return -1; + } + + ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsne_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RNSE"); + return -1; + } + + ret = wpa_pasn_validate_rsne(&rsne_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE"); + return -1; + } + + if (rsne_data.num_pmkid) { + wpa_printf(MSG_DEBUG, + "PASN: FILS: Not expecting PMKID in RSNE"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", elems.fils_nonce, + FILS_NONCE_LEN); + os_memcpy(fils->nonce, elems.fils_nonce, FILS_NONCE_LEN); + + wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", elems.fils_session, + FILS_SESSION_LEN); + os_memcpy(fils->session, elems.fils_session, FILS_SESSION_LEN); + + fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION, + WLAN_EID_EXT_WRAPPED_DATA); + + if (!fils_wd) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Missing wrapped data"); + return -1; + } + + if (!sta->eapol_sm) + sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta); + + wpa_printf(MSG_DEBUG, + "PASN: FILS: Forward EAP-Initiate/Re-auth to AS"); + + ieee802_1x_encapsulate_radius(hapd, sta, wpabuf_head(fils_wd), + wpabuf_len(fils_wd)); + + sta->flags |= WLAN_STA_PENDING_PASN_FILS_ERP; + + fils->state = PASN_FILS_STATE_PENDING_AS; + + /* + * Calculate pending PMKID here so that we do not need to maintain a + * copy of the EAP-Initiate/Reautt message. + */ + fils_pmkid_erp(pasn->akmp, wpabuf_head(fils_wd), wpabuf_len(fils_wd), + fils->erp_pmkid); + + wpabuf_free(fils_wd); + return 0; +#endif /* CONFIG_NO_RADIUS */ +} + +#endif /* CONFIG_FILS */ + + +static struct wpabuf * pasn_get_wrapped_data(struct hostapd_data *hapd, + struct sta_info *sta) +{ + switch (sta->pasn->akmp) { + case WPA_KEY_MGMT_PASN: + /* no wrapped data */ + return NULL; + case WPA_KEY_MGMT_SAE: +#ifdef CONFIG_SAE + return pasn_get_sae_wd(hapd, sta); +#else /* CONFIG_SAE */ + wpa_printf(MSG_ERROR, + "PASN: SAE: Cannot derive wrapped data"); + return NULL; +#endif /* CONFIG_SAE */ + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FILS_SHA384: +#ifdef CONFIG_FILS + return pasn_get_fils_wd(hapd, sta); +#endif /* CONFIG_FILS */ + /* fall through */ + case WPA_KEY_MGMT_FT_PSK: + case WPA_KEY_MGMT_FT_IEEE8021X: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + default: + wpa_printf(MSG_ERROR, + "PASN: TODO: Wrapped data for akmp=0x%x", + sta->pasn->akmp); + return NULL; + } +} + + +static int +pasn_derive_keys(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *cached_pmk, size_t cached_pmk_len, + struct wpa_pasn_params_data *pasn_data, + struct wpabuf *wrapped_data, + struct wpabuf *secret) +{ + static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'}; + u8 pmk[PMK_LEN_MAX]; + u8 pmk_len; + int ret; + + os_memset(pmk, 0, sizeof(pmk)); + pmk_len = 0; + + if (!cached_pmk || !cached_pmk_len) + wpa_printf(MSG_DEBUG, "PASN: No valid PMKSA entry"); + + if (sta->pasn->akmp == WPA_KEY_MGMT_PASN) { + wpa_printf(MSG_DEBUG, "PASN: Using default PMK"); + + pmk_len = WPA_PASN_PMK_LEN; + os_memcpy(pmk, pasn_default_pmk, sizeof(pasn_default_pmk)); + } else if (cached_pmk && cached_pmk_len) { + wpa_printf(MSG_DEBUG, "PASN: Using PMKSA entry"); + + pmk_len = cached_pmk_len; + os_memcpy(pmk, cached_pmk, cached_pmk_len); + } else { + switch (sta->pasn->akmp) { +#ifdef CONFIG_SAE + case WPA_KEY_MGMT_SAE: + if (sta->pasn->sae.state == SAE_COMMITTED) { + pmk_len = PMK_LEN; + os_memcpy(pmk, sta->pasn->sae.pmk, PMK_LEN); + break; + } +#endif /* CONFIG_SAE */ + /* fall through */ + default: + /* TODO: Derive PMK based on wrapped data */ + wpa_printf(MSG_DEBUG, + "PASN: Missing PMK derivation"); + return -1; + } + } + + ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr, + wpabuf_head(secret), wpabuf_len(secret), + &sta->pasn->ptk, sta->pasn->akmp, + sta->pasn->cipher, sta->pasn->kdk_len); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK"); + return -1; + } + + wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived"); + return 0; +} + + +static void handle_auth_pasn_comeback(struct hostapd_data *hapd, + struct sta_info *sta, u16 group) +{ + struct wpabuf *buf, *comeback; + int ret; + + wpa_printf(MSG_DEBUG, + "PASN: Building comeback frame 2. Comeback after=%u", + hapd->conf->pasn_comeback_after); + + buf = wpabuf_alloc(1500); + if (!buf) + return; + + wpa_pasn_build_auth_header(buf, hapd->own_addr, hapd->own_addr, + sta->addr, 2, + WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY); + + /* + * Do not include the group as a part of the token since it is not going + * to be used. + */ + comeback = auth_build_token_req(hapd, 0, sta->addr, 0); + if (!comeback) { + wpa_printf(MSG_DEBUG, + "PASN: Failed sending auth with comeback"); + wpabuf_free(buf); + return; + } + + wpa_pasn_add_parameter_ie(buf, group, + WPA_PASN_WRAPPED_DATA_NO, + NULL, 0, comeback, + hapd->conf->pasn_comeback_after); + wpabuf_free(comeback); + + wpa_printf(MSG_DEBUG, + "PASN: comeback: STA=" MACSTR, MAC2STR(sta->addr)); + + ret = hostapd_drv_send_mlme(hapd, wpabuf_head(buf), wpabuf_len(buf), 0, + NULL, 0, 0); + if (ret) + wpa_printf(MSG_INFO, "PASN: Failed to send comeback frame 2"); + + wpabuf_free(buf); +} + + +static int handle_auth_pasn_resp(struct hostapd_data *hapd, + struct sta_info *sta, + struct rsn_pmksa_cache_entry *pmksa, + u16 status) +{ + struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL; + u8 mic[WPA_PASN_MAX_MIC_LEN]; + u8 mic_len; + u8 *ptr; + const u8 *frame, *data, *rsn_ie, *rsnxe_ie; + u8 *data_buf = NULL; + size_t rsn_ie_len, frame_len, data_len; + int ret; + const u8 *pmkid = NULL; + + wpa_printf(MSG_DEBUG, "PASN: Building frame 2: status=%u", status); + + buf = wpabuf_alloc(1500); + if (!buf) + goto fail; + + wpa_pasn_build_auth_header(buf, hapd->own_addr, hapd->own_addr, + sta->addr, 2, status); + + if (status != WLAN_STATUS_SUCCESS) + goto done; + + if (pmksa) { + pmkid = pmksa->pmkid; +#ifdef CONFIG_SAE + } else if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) { + wpa_printf(MSG_DEBUG, "PASN: Use SAE PMKID"); + pmkid = sta->pasn->sae.pmkid; +#endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + } else if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 || + sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) { + wpa_printf(MSG_DEBUG, "PASN: Use FILS ERP PMKID"); + pmkid = sta->pasn->fils.erp_pmkid; +#endif /* CONFIG_FILS */ + } + + if (wpa_pasn_add_rsne(buf, pmkid, + sta->pasn->akmp, sta->pasn->cipher) < 0) + goto fail; + + /* No need to derive PMK if PMKSA is given */ + if (!pmksa) + wrapped_data_buf = pasn_get_wrapped_data(hapd, sta); + else + sta->pasn->wrapped_data_format = WPA_PASN_WRAPPED_DATA_NO; + + /* Get public key */ + pubkey = crypto_ecdh_get_pubkey(sta->pasn->ecdh, 0); + pubkey = wpabuf_zeropad(pubkey, + crypto_ecdh_prime_len(sta->pasn->ecdh)); + if (!pubkey) { + wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey"); + goto fail; + } + + wpa_pasn_add_parameter_ie(buf, sta->pasn->group, + sta->pasn->wrapped_data_format, + pubkey, true, NULL, 0); + + if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0) + goto fail; + + wpabuf_free(wrapped_data_buf); + wrapped_data_buf = NULL; + wpabuf_free(pubkey); + pubkey = NULL; + + /* Add RSNXE if needed */ + rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX); + if (rsnxe_ie) + wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]); + + /* Add the mic */ + mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher); + wpabuf_put_u8(buf, WLAN_EID_MIC); + wpabuf_put_u8(buf, mic_len); + ptr = wpabuf_put(buf, mic_len); + + os_memset(ptr, 0, mic_len); + + frame = wpabuf_head_u8(buf) + IEEE80211_HDRLEN; + frame_len = wpabuf_len(buf) - IEEE80211_HDRLEN; + + rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &rsn_ie_len); + if (!rsn_ie || !rsn_ie_len) + goto fail; + + /* + * Note: wpa_auth_get_wpa_ie() might return not only the RSNE but also + * MDE, etc. Thus, do not use the returned length but instead use the + * length specified in the IE header. + */ + data_len = rsn_ie[1] + 2; + if (rsnxe_ie) { + data_buf = os_zalloc(rsn_ie[1] + 2 + rsnxe_ie[1] + 2); + if (!data_buf) + goto fail; + + os_memcpy(data_buf, rsn_ie, rsn_ie[1] + 2); + os_memcpy(data_buf + rsn_ie[1] + 2, rsnxe_ie, rsnxe_ie[1] + 2); + data_len += rsnxe_ie[1] + 2; + data = data_buf; + } else { + data = rsn_ie; + } + + ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher, + hapd->own_addr, sta->addr, data, data_len, + frame, frame_len, mic); + os_free(data_buf); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Frame 3: Failed MIC calculation"); + goto fail; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->pasn_corrupt_mic) { + wpa_printf(MSG_DEBUG, "PASN: frame 2: Corrupt MIC"); + mic[0] = ~mic[0]; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + os_memcpy(ptr, mic, mic_len); + +done: + wpa_printf(MSG_DEBUG, + "PASN: Building frame 2: success; resp STA=" MACSTR, + MAC2STR(sta->addr)); + + ret = hostapd_drv_send_mlme(hapd, wpabuf_head(buf), wpabuf_len(buf), 0, + NULL, 0, 0); + if (ret) + wpa_printf(MSG_INFO, "send_auth_reply: Send failed"); + + wpabuf_free(buf); + return ret; +fail: + wpabuf_free(wrapped_data_buf); + wpabuf_free(pubkey); + wpabuf_free(buf); + return -1; +} + + +static void handle_auth_pasn_1(struct hostapd_data *hapd, struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + struct ieee802_11_elems elems; + struct wpa_ie_data rsn_data; + struct wpa_pasn_params_data pasn_params; + struct rsn_pmksa_cache_entry *pmksa = NULL; + const u8 *cached_pmk = NULL; + size_t cached_pmk_len = 0; +#ifdef CONFIG_IEEE80211R_AP + u8 pmk_r1[PMK_LEN_MAX]; + size_t pmk_r1_len; +#endif /* CONFIG_IEEE80211R_AP */ + struct wpabuf *wrapped_data = NULL, *secret = NULL; + const int *groups = hapd->conf->pasn_groups; + static const int default_groups[] = { 19, 0 }; + u16 status = WLAN_STATUS_SUCCESS; + int ret, inc_y; + bool derive_keys; + u32 i; + + if (!groups) + groups = default_groups; + + if (ieee802_11_parse_elems(mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable), + &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "PASN: Failed parsing Authentication frame"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed parsing RNSE"); + status = WLAN_STATUS_INVALID_RSNIE; + goto send_resp; + } + + ret = wpa_pasn_validate_rsne(&rsn_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE"); + status = WLAN_STATUS_INVALID_RSNIE; + goto send_resp; + } + + if (!(rsn_data.key_mgmt & hapd->conf->wpa_key_mgmt) || + !(rsn_data.pairwise_cipher & hapd->conf->rsn_pairwise)) { + wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher"); + status = WLAN_STATUS_INVALID_RSNIE; + goto send_resp; + } + + sta->pasn->akmp = rsn_data.key_mgmt; + sta->pasn->cipher = rsn_data.pairwise_cipher; + + if (hapd->conf->force_kdk_derivation || + ((hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF) && + ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len, + WLAN_RSNX_CAPAB_SECURE_LTF))) + sta->pasn->kdk_len = WPA_KDK_MAX_LEN; + else + sta->pasn->kdk_len = 0; + wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", sta->pasn->kdk_len); + + if (!elems.pasn_params || !elems.pasn_params_len) { + wpa_printf(MSG_DEBUG, + "PASN: No PASN Parameters element found"); + status = WLAN_STATUS_INVALID_PARAMETERS; + goto send_resp; + } + + ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3, + elems.pasn_params_len + 3, + false, &pasn_params); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed validation of PASN Parameters IE"); + status = WLAN_STATUS_INVALID_PARAMETERS; + goto send_resp; + } + + for (i = 0; groups[i] > 0 && groups[i] != pasn_params.group; i++) + ; + + if (!pasn_params.group || groups[i] != pasn_params.group) { + wpa_printf(MSG_DEBUG, "PASN: Requested group=%hu not allowed", + pasn_params.group); + status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + goto send_resp; + } + + if (!pasn_params.pubkey || !pasn_params.pubkey_len) { + wpa_printf(MSG_DEBUG, "PASN: Invalid public key"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + if (pasn_params.comeback) { + wpa_printf(MSG_DEBUG, "PASN: Checking peer comeback token"); + + ret = check_comeback_token(hapd, sta->addr, + pasn_params.comeback, + pasn_params.comeback_len); + + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Invalid comeback token"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + } else if (use_anti_clogging(hapd)) { + wpa_printf(MSG_DEBUG, "PASN: Respond with comeback"); + handle_auth_pasn_comeback(hapd, sta, pasn_params.group); + ap_free_sta(hapd, sta); + return; + } + + sta->pasn->ecdh = crypto_ecdh_init(pasn_params.group); + if (!sta->pasn->ecdh) { + wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + sta->pasn->group = pasn_params.group; + + if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) { + inc_y = 1; + } else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 || + pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) { + inc_y = 0; + } else { + wpa_printf(MSG_DEBUG, + "PASN: Invalid first octet in pubkey=0x%x", + pasn_params.pubkey[0]); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + secret = crypto_ecdh_set_peerkey(sta->pasn->ecdh, inc_y, + pasn_params.pubkey + 1, + pasn_params.pubkey_len - 1); + if (!secret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + derive_keys = true; + if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) { + wrapped_data = ieee802_11_defrag(&elems, + WLAN_EID_EXTENSION, + WLAN_EID_EXT_WRAPPED_DATA); + if (!wrapped_data) { + wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + +#ifdef CONFIG_SAE + if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) { + ret = pasn_wd_handle_sae_commit(hapd, sta, + wrapped_data); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed processing SAE commit"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + } +#endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 || + sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) { + ret = pasn_wd_handle_fils(hapd, sta, wrapped_data); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed processing FILS wrapped data"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + wpa_printf(MSG_DEBUG, + "PASN: FILS: Pending AS response"); + + /* + * With PASN/FILS, keys can be derived only after a + * response from the AS is processed. + */ + derive_keys = false; + } +#endif /* CONFIG_FILS */ + } + + sta->pasn->wrapped_data_format = pasn_params.wrapped_data_format; + + ret = pasn_auth_frame_hash(sta->pasn->akmp, sta->pasn->cipher, + ((const u8 *) mgmt) + IEEE80211_HDRLEN, + len - IEEE80211_HDRLEN, sta->pasn->hash); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + if (!derive_keys) { + wpa_printf(MSG_DEBUG, "PASN: Storing secret"); + sta->pasn->secret = secret; + wpabuf_free(wrapped_data); + return; + } + + if (rsn_data.num_pmkid) { + if (wpa_key_mgmt_ft(sta->pasn->akmp)) { +#ifdef CONFIG_IEEE80211R_AP + wpa_printf(MSG_DEBUG, "PASN: FT: Fetch PMK-R1"); + + ret = wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr, + rsn_data.pmkid, + pmk_r1, &pmk_r1_len, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: FT: Failed getting PMK-R1"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + cached_pmk = pmk_r1; + cached_pmk_len = pmk_r1_len; +#else /* CONFIG_IEEE80211R_AP */ + wpa_printf(MSG_DEBUG, "PASN: FT: Not supported"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; +#endif /* CONFIG_IEEE80211R_AP */ + } else { + wpa_printf(MSG_DEBUG, "PASN: Try to find PMKSA entry"); + + pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, + rsn_data.pmkid); + if (pmksa) { + cached_pmk = pmksa->pmk; + cached_pmk_len = pmksa->pmk_len; + } + } + } else { + wpa_printf(MSG_DEBUG, "PASN: No PMKID specified"); + } + + ret = pasn_derive_keys(hapd, sta, cached_pmk, cached_pmk_len, + &pasn_params, wrapped_data, secret); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to derive keys"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + ret = pasn_auth_frame_hash(sta->pasn->akmp, sta->pasn->cipher, + ((const u8 *) mgmt) + IEEE80211_HDRLEN, + len - IEEE80211_HDRLEN, sta->pasn->hash); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + } + +send_resp: + ret = handle_auth_pasn_resp(hapd, sta, pmksa, status); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to send response"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + } else { + wpa_printf(MSG_DEBUG, + "PASN: Success handling transaction == 1"); + } + + wpabuf_free(secret); + wpabuf_free(wrapped_data); + + if (status != WLAN_STATUS_SUCCESS) + ap_free_sta(hapd, sta); +} + + +static void handle_auth_pasn_3(struct hostapd_data *hapd, struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + struct ieee802_11_elems elems; + struct wpa_pasn_params_data pasn_params; + struct wpabuf *wrapped_data = NULL; + u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN]; + u8 mic_len; + int ret; + + if (ieee802_11_parse_elems(mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable), + &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "PASN: Failed parsing Authentication frame"); + goto fail; + } + + /* Check that the MIC IE exists. Save it and zero out the memory. */ + mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher); + if (!elems.mic || elems.mic_len != mic_len) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid MIC. Expecting len=%u", mic_len); + goto fail; + } else { + os_memcpy(mic, elems.mic, mic_len); + /* TODO: Clean this up.. Should not modify received frame + * buffer. */ + os_memset((u8 *) elems.mic, 0, mic_len); + } + + if (!elems.pasn_params || !elems.pasn_params_len) { + wpa_printf(MSG_DEBUG, + "PASN: No PASN Parameters element found"); + goto fail; + } + + ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3, + elems.pasn_params_len + 3, + false, &pasn_params); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed validation of PASN Parameters IE"); + goto fail; + } + + if (pasn_params.pubkey || pasn_params.pubkey_len) { + wpa_printf(MSG_DEBUG, + "PASN: Public key should not be included"); + goto fail; + } + + /* Verify the MIC */ + ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher, + sta->addr, hapd->own_addr, + sta->pasn->hash, mic_len * 2, + (u8 *) &mgmt->u.auth, + len - offsetof(struct ieee80211_mgmt, u.auth), + out_mic); + + wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len); + if (ret || os_memcmp(mic, out_mic, mic_len) != 0) { + wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification"); + goto fail; + } + + if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) { + wrapped_data = ieee802_11_defrag(&elems, + WLAN_EID_EXTENSION, + WLAN_EID_EXT_WRAPPED_DATA); + + if (!wrapped_data) { + wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data"); + goto fail; + } + +#ifdef CONFIG_SAE + if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) { + ret = pasn_wd_handle_sae_confirm(hapd, sta, + wrapped_data); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed processing SAE confirm"); + wpabuf_free(wrapped_data); + goto fail; + } + } +#endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 || + sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) { + if (wrapped_data) { + wpa_printf(MSG_DEBUG, + "PASN: FILS: Ignore wrapped data"); + } + } +#endif /* CONFIG_FILS */ + wpabuf_free(wrapped_data); + } + + wpa_printf(MSG_INFO, + "PASN: Success handling transaction == 3. Store PTK"); + + ptksa_cache_add(hapd->ptksa, sta->addr, sta->pasn->cipher, 43200, + &sta->pasn->ptk); +fail: + ap_free_sta(hapd, sta); +} + + +static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, size_t len, + u16 trans_seq, u16 status) +{ + if (hapd->conf->wpa != WPA_PROTO_RSN) { + wpa_printf(MSG_INFO, "PASN: RSN is not configured"); + return; + } + + wpa_printf(MSG_INFO, "PASN authentication: sta=" MACSTR, + MAC2STR(sta->addr)); + + if (trans_seq == 1) { + if (sta->pasn) { + wpa_printf(MSG_DEBUG, + "PASN: Not expecting transaction == 1"); + return; + } + + if (status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, + "PASN: Failure status in transaction == 1"); + return; + } + + sta->pasn = os_zalloc(sizeof(*sta->pasn)); + if (!sta->pasn) { + wpa_printf(MSG_DEBUG, + "PASN: Failed to allocate PASN context"); + return; + } + + handle_auth_pasn_1(hapd, sta, mgmt, len); + } else if (trans_seq == 3) { + if (!sta->pasn) { + wpa_printf(MSG_DEBUG, + "PASN: Not expecting transaction == 3"); + return; + } + + if (status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, + "PASN: Failure status in transaction == 3"); + ap_free_sta_pasn(hapd, sta); + return; + } + + handle_auth_pasn_3(hapd, sta, mgmt, len); + } else { + wpa_printf(MSG_DEBUG, + "PASN: Invalid transaction %u - ignore", trans_seq); + } +} + +#endif /* CONFIG_PASN */ + + static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int rssi, int from_queue) @@ -2035,14 +3556,10 @@ static void handle_auth(struct hostapd_data *hapd, int res, reply_res; u16 fc; const u8 *challenge = NULL; - u32 session_timeout, acct_interim_interval; - struct vlan_description vlan_id; - struct hostapd_sta_wpa_psk_short *psk = NULL; u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; size_t resp_ies_len = 0; - char *identity = NULL; - char *radius_cui = NULL; u16 seq_ctrl; + struct radius_sta rad_info; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)", @@ -2115,6 +3632,11 @@ static void handle_auth(struct hostapd_data *hapd, hapd->conf->fils_dh_group && auth_alg == WLAN_AUTH_FILS_SK_PFS) || #endif /* CONFIG_FILS */ +#ifdef CONFIG_PASN + (hapd->conf->wpa && + (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) && + auth_alg == WLAN_AUTH_PASN) || +#endif /* CONFIG_PASN */ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && auth_alg == WLAN_AUTH_SHARED_KEY))) { wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", @@ -2124,6 +3646,9 @@ static void handle_auth(struct hostapd_data *hapd, } if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE || +#ifdef CONFIG_PASN + (auth_alg == WLAN_AUTH_PASN && auth_transaction == 3) || +#endif /* CONFIG_PASN */ (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) { wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)", auth_transaction); @@ -2193,10 +3718,8 @@ static void handle_auth(struct hostapd_data *hapd, } } - res = ieee802_11_allowed_address( - hapd, mgmt->sa, (const u8 *) mgmt, len, &session_timeout, - &acct_interim_interval, &vlan_id, &psk, &identity, &radius_cui, - 0); + res = ieee802_11_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len, + &rad_info); if (res == HOSTAPD_ACL_REJECT) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Ignore Authentication frame from " MACSTR @@ -2247,6 +3770,15 @@ static void handle_auth(struct hostapd_data *hapd, return; } #endif /* CONFIG_MESH */ +#ifdef CONFIG_PASN + if (auth_alg == WLAN_AUTH_PASN && + (sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, + "PASN: auth: Existing station: " MACSTR, + MAC2STR(sta->addr)); + return; + } +#endif /* CONFIG_PASN */ } else { #ifdef CONFIG_MESH if (hapd->conf->mesh & MESH_ENABLED) { @@ -2279,9 +3811,7 @@ static void handle_auth(struct hostapd_data *hapd, sta->auth_rssi = rssi; #endif /* CONFIG_MBO */ - res = ieee802_11_set_radius_info( - hapd, sta, res, session_timeout, acct_interim_interval, - &vlan_id, &psk, &identity, &radius_cui); + res = ieee802_11_set_radius_info(hapd, sta, res, &rad_info); if (res) { wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -2309,37 +3839,18 @@ static void handle_auth(struct hostapd_data *hapd, * to allow the original connection work until the attempt can complete * (re)association, so that unprotected Authentication frame cannot be * used to bypass PMF protection. + * + * PASN authentication does not require adding/removing station to the + * driver so skip this flow in case of PASN authentication. */ if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) && !(hapd->conf->mesh & MESH_ENABLED) && - !(sta->added_unassoc)) { - /* - * If a station that is already associated to the AP, is trying - * to authenticate again, remove the STA entry, in order to make - * sure the STA PS state gets cleared and configuration gets - * updated. To handle this, station's added_unassoc flag is - * cleared once the station has completed association. - */ - ap_sta_set_authorized(hapd, sta, 0); - hostapd_drv_sta_remove(hapd, sta->addr); - sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH | - WLAN_STA_AUTHORIZED); - - if (hostapd_sta_add(hapd, sta->addr, 0, 0, - sta->supported_rates, - sta->supported_rates_len, - 0, NULL, NULL, NULL, 0, - sta->flags, 0, 0, 0, 0)) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_NOTICE, - "Could not add STA to kernel driver"); + !(sta->added_unassoc) && auth_alg != WLAN_AUTH_PASN) { + if (ap_sta_re_add(hapd, sta) < 0) { resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } - - sta->added_unassoc = 1; } switch (auth_alg) { @@ -2352,6 +3863,7 @@ static void handle_auth(struct hostapd_data *hapd, sta->auth_alg = WLAN_AUTH_OPEN; mlme_authenticate_indication(hapd, sta); break; +#ifdef CONFIG_WEP #ifndef CONFIG_NO_RC4 case WLAN_AUTH_SHARED_KEY: resp = auth_shared_key(hapd, sta, auth_transaction, challenge, @@ -2370,6 +3882,7 @@ static void handle_auth(struct hostapd_data *hapd, } break; #endif /* CONFIG_NO_RC4 */ +#endif /* CONFIG_WEP */ #ifdef CONFIG_IEEE80211R_AP case WLAN_AUTH_FT: sta->auth_alg = WLAN_AUTH_FT; @@ -2420,16 +3933,20 @@ static void handle_auth(struct hostapd_data *hapd, handle_auth_fils_finish); return; #endif /* CONFIG_FILS */ +#ifdef CONFIG_PASN + case WLAN_AUTH_PASN: + handle_auth_pasn(hapd, sta, mgmt, len, auth_transaction, + status_code); + return; +#endif /* CONFIG_PASN */ } fail: - os_free(identity); - os_free(radius_cui); - hostapd_free_psk_list(psk); - - reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, - auth_transaction + 1, resp, resp_ies, - resp_ies_len, "handle-auth"); + reply_res = send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, auth_alg, + auth_alg == WLAN_AUTH_SAE ? + auth_transaction : auth_transaction + 1, + resp, resp_ies, resp_ies_len, + "handle-auth"); if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || reply_res != WLAN_STATUS_SUCCESS)) { @@ -2844,7 +4361,7 @@ u16 owe_process_rsn_ie(struct hostapd_data *hapd, u16 status; u8 *owe_buf, ie[256 * 2]; size_t ie_len = 0; - int res; + enum wpa_validate_result res; if (!rsn_ie || rsn_ie_len < 2) { wpa_printf(MSG_DEBUG, "OWE: No RSNE in (Re)AssocReq"); @@ -2865,7 +4382,7 @@ u16 owe_process_rsn_ie(struct hostapd_data *hapd, rsn_ie_len += 2; res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, hapd->iface->freq, rsn_ie, rsn_ie_len, - NULL, 0, owe_dh, owe_dh_len); + NULL, 0, NULL, 0, owe_dh, owe_dh_len); status = wpa_res_to_status_code(res); if (status != WLAN_STATUS_SUCCESS) goto end; @@ -2916,11 +4433,39 @@ end: #endif /* CONFIG_OWE */ -static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, +static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta, + int reassoc) +{ + if ((sta->flags & + (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) != + (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) + return false; + + if (!sta->sa_query_timed_out && sta->sa_query_count > 0) + ap_check_sa_query_timeout(hapd, sta); + + if (!sta->sa_query_timed_out && + (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) { + /* + * STA has already been associated with MFP and SA Query timeout + * has not been reached. Reject the association attempt + * temporarily and start SA Query, if one is not pending. + */ + if (sta->sa_query_count == 0) + ap_sta_start_sa_query(hapd, sta); + + return true; + } + + return false; +} + + +static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ies, size_t ies_len, int reassoc) { struct ieee802_11_elems elems; - u16 resp; + int resp; const u8 *wpa_ie; size_t wpa_ie_len; const u8 *p2p_dev_addr = NULL; @@ -2949,7 +4494,6 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) return resp; -#ifdef CONFIG_IEEE80211N resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities); if (resp != WLAN_STATUS_SUCCESS) return resp; @@ -2960,7 +4504,6 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, "mandatory HT PHY - reject association"); return WLAN_STATUS_ASSOC_DENIED_NO_HT; } -#endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac) { @@ -2989,12 +4532,25 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax) { + if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { resp = copy_sta_he_capab(hapd, sta, IEEE80211_MODE_AP, elems.he_capabilities, elems.he_capabilities_len); if (resp != WLAN_STATUS_SUCCESS) return resp; + if (is_6ghz_op_class(hapd->iconf->op_class)) { + if (!(sta->flags & WLAN_STA_HE)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Station does not support mandatory HE PHY - reject association"); + return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED; + } + resp = copy_sta_he_6ghz_capab(hapd, sta, + elems.he_6ghz_band_cap); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + } } #endif /* CONFIG_IEEE80211AX */ @@ -3028,6 +4584,8 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (hapd->conf->wps_state && elems.wps_ie) { wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association " "Request - assume WPS is used"); + if (check_sa_query(hapd, sta, reassoc)) + return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; sta->flags |= WLAN_STA_WPS; wpabuf_free(sta->wps_ie); sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, @@ -3057,7 +4615,8 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } if (hapd->conf->wpa && wpa_ie) { - int res; + enum wpa_validate_result res; + wpa_ie -= 2; wpa_ie_len += 2; if (sta->wpa_sm == NULL) @@ -3073,39 +4632,21 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, hapd->iface->freq, wpa_ie, wpa_ie_len, + elems.rsnxe ? elems.rsnxe - 2 : NULL, + elems.rsnxe ? elems.rsnxe_len + 2 : 0, elems.mdie, elems.mdie_len, elems.owe_dh, elems.owe_dh_len); resp = wpa_res_to_status_code(res); if (resp != WLAN_STATUS_SUCCESS) return resp; -#ifdef CONFIG_IEEE80211W - if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == - (WLAN_STA_ASSOC | WLAN_STA_MFP) && - !sta->sa_query_timed_out && - sta->sa_query_count > 0) - ap_check_sa_query_timeout(hapd, sta); - if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == - (WLAN_STA_ASSOC | WLAN_STA_MFP) && - !sta->sa_query_timed_out && - (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) { - /* - * STA has already been associated with MFP and SA - * Query timeout has not been reached. Reject the - * association attempt temporarily and start SA Query, - * if one is not pending. - */ - - if (sta->sa_query_count == 0) - ap_sta_start_sa_query(hapd, sta); + if (check_sa_query(hapd, sta, reassoc)) return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; - } if (wpa_auth_uses_mfp(sta->wpa_sm)) sta->flags |= WLAN_STA_MFP; else sta->flags &= ~WLAN_STA_MFP; -#endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R_AP if (sta->auth_alg == WLAN_AUTH_FT) { @@ -3150,6 +4691,17 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, MAC2STR(sta->addr), sta->auth_alg); return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; } + + if (hapd->conf->sae_pwe == 2 && + sta->auth_alg == WLAN_AUTH_SAE && + sta->sae && !sta->sae->h2e && + ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len, + WLAN_RSNX_CAPAB_SAE_H2E)) { + wpa_printf(MSG_INFO, "SAE: " MACSTR + " indicates support for SAE H2E, but did not use it", + MAC2STR(sta->addr)); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } #endif /* CONFIG_SAE */ #ifdef CONFIG_OWE @@ -3167,7 +4719,8 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, dpp_pfs_free(sta->dpp_pfs); sta->dpp_pfs = NULL; - if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && + if (DPP_VERSION > 1 && + (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && hapd->conf->dpp_netaccesskey && sta->wpa_sm && wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP && elems.owe_dh) { @@ -3194,7 +4747,6 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, pfs_fail: #endif /* CONFIG_DPP2 */ -#ifdef CONFIG_IEEE80211N if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) && wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { hostapd_logger(hapd, sta->addr, @@ -3204,7 +4756,6 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, "association"); return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; } -#endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_HS20 } else if (hapd->conf->osen) { if (elems.osen == NULL) { @@ -3243,7 +4794,8 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, elems.hs20_len - 4); release = ((elems.hs20[4] >> 4) & 0x0f) + 1; - if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm)) { + if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm) && + hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { wpa_printf(MSG_DEBUG, "HS 2.0: PMF not negotiated by release %d station " MACSTR, release, MAC2STR(sta->addr)); @@ -3290,6 +4842,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, struct wpa_channel_info ci; int tx_chanwidth; int tx_seg1_idx; + enum oci_verify_result res; if (hostapd_drv_channel_info(hapd, &ci) != 0) { wpa_printf(MSG_WARNING, @@ -3303,9 +4856,20 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, &tx_seg1_idx) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; - if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, - tx_chanwidth, tx_seg1_idx) != 0) { - wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr); + res = ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, + tx_chanwidth, tx_seg1_idx); + if (wpa_auth_uses_ocv(sta->wpa_sm) == 2 && + res == OCI_NOT_FOUND) { + /* Work around misbehaving STAs */ + wpa_printf(MSG_INFO, + "FILS: Disable OCV with a STA that does not send OCI"); + wpa_auth_set_ocv(sta->wpa_sm, 0); + } else if (res != OCI_SUCCESS) { + wpa_printf(MSG_WARNING, "FILS: OCV failed: %s", + ocv_errorstr); + wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr=" + MACSTR " frame=fils-reassoc-req error=%s", + MAC2STR(sta->addr), ocv_errorstr); return WLAN_STATUS_UNSPECIFIED_FAILURE; } } @@ -3348,7 +4912,7 @@ static void send_deauth(struct hostapd_data *hapd, const u8 *addr, send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth); reply.u.deauth.reason_code = host_to_le16(reason_code); - if (hostapd_drv_send_mlme(hapd, &reply, send_len, 0) < 0) + if (hostapd_drv_send_mlme(hapd, &reply, send_len, 0, NULL, 0, 0) < 0) wpa_printf(MSG_INFO, "Failed to send deauth: %s", strerror(errno)); } @@ -3404,10 +4968,8 @@ static int add_associated_sta(struct hostapd_data *hapd, sta->ft_over_ds = 0; } -#ifdef CONFIG_IEEE80211N if (sta->flags & WLAN_STA_HT) hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); -#endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC if (sta->flags & WLAN_STA_VHT) hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); @@ -3431,6 +4993,7 @@ static int add_associated_sta(struct hostapd_data *hapd, sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, sta->flags & WLAN_STA_HE ? &he_cap : NULL, sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0, + sta->he_6ghz_capab, sta->flags | WLAN_STA_ASSOC, sta->qosinfo, sta->vht_opmode, sta->p2p_ie ? 1 : 0, set)) { @@ -3455,7 +5018,8 @@ static int add_associated_sta(struct hostapd_data *hapd, static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 status_code, int reassoc, - const u8 *ies, size_t ies_len, int rssi) + const u8 *ies, size_t ies_len, int rssi, + int omit_rsnxe) { int send_len; u8 *buf; @@ -3506,6 +5070,9 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, /* Extended supported rates */ p = hostapd_eid_ext_supp_rates(hapd, p); + /* Radio measurement capabilities */ + p = hostapd_eid_rm_enabled_capab(hapd, p, buf + buflen - p); + #ifdef CONFIG_MBO if (status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS && rssi != 0) { @@ -3522,7 +5089,8 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, * Transition Information, RSN, [RIC Response] */ p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p, buf + buflen - p, - sta->auth_alg, ies, ies_len); + sta->auth_alg, ies, ies_len, + omit_rsnxe); if (!p) { wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs"); @@ -3549,18 +5117,15 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, ies, ies_len); #endif /* CONFIG_OWE */ -#ifdef CONFIG_IEEE80211W if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) p = hostapd_eid_assoc_comeback_time(hapd, sta, p); -#endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211N p = hostapd_eid_ht_capabilities(hapd, p); p = hostapd_eid_ht_operation(hapd, p); -#endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC - if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac && + !is_6ghz_op_class(hapd->iconf->op_class)) { u32 nsts = 0, sta_nsts; if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) { @@ -3583,11 +5148,12 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax) { + if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP); p = hostapd_eid_he_operation(hapd, p); p = hostapd_eid_spatial_reuse(hapd, p); p = hostapd_eid_he_mu_edca_parameter_set(hapd, p); + p = hostapd_eid_he_6ghz_band_cap(hapd, p); } #endif /* CONFIG_IEEE80211AX */ @@ -3604,10 +5170,29 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_FST */ +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->rsnxe_override_ft && + buf + buflen - p >= + (long int) wpabuf_len(hapd->conf->rsnxe_override_ft) && + sta && sta->auth_alg == WLAN_AUTH_FT) { + wpa_printf(MSG_DEBUG, "TESTING: RSNXE FT override"); + os_memcpy(p, wpabuf_head(hapd->conf->rsnxe_override_ft), + wpabuf_len(hapd->conf->rsnxe_override_ft)); + p += wpabuf_len(hapd->conf->rsnxe_override_ft); + goto rsnxe_done; + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (!omit_rsnxe) + p = hostapd_eid_rsnxe(hapd, p, buf + buflen - p); +#ifdef CONFIG_TESTING_OPTIONS +rsnxe_done: +#endif /* CONFIG_TESTING_OPTIONS */ + #ifdef CONFIG_OWE if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS && - wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) { + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE && + !wpa_auth_sta_get_pmksa(sta->wpa_sm)) { struct wpabuf *pub; pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); @@ -3628,7 +5213,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_OWE */ #ifdef CONFIG_DPP2 - if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && + if (DPP_VERSION > 1 && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS && wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) { os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie), @@ -3733,7 +5318,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_FILS */ - if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) { + if (hostapd_drv_send_mlme(hapd, reply, send_len, 0, NULL, 0, 0) < 0) { wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); res = WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -3748,12 +5333,12 @@ done: #ifdef CONFIG_OWE u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, const u8 *owe_dh, u8 owe_dh_len, - u8 *owe_buf, size_t owe_buf_len, u16 *reason) + u8 *owe_buf, size_t owe_buf_len, u16 *status) { #ifdef CONFIG_TESTING_OPTIONS if (hapd->conf->own_ie_override) { wpa_printf(MSG_DEBUG, "OWE: Using IE override"); - *reason = WLAN_STATUS_SUCCESS; + *status = WLAN_STATUS_SUCCESS; return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, owe_buf_len, NULL, 0); } @@ -3763,18 +5348,18 @@ u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching"); owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, owe_buf_len, NULL, 0); - *reason = WLAN_STATUS_SUCCESS; + *status = WLAN_STATUS_SUCCESS; return owe_buf; } if (sta->owe_pmk && sta->external_dh_updated) { wpa_printf(MSG_DEBUG, "OWE: Using previously derived PMK"); - *reason = WLAN_STATUS_SUCCESS; + *status = WLAN_STATUS_SUCCESS; return owe_buf; } - *reason = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len); - if (*reason != WLAN_STATUS_SUCCESS) + *status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len); + if (*status != WLAN_STATUS_SUCCESS) return NULL; owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, @@ -3785,7 +5370,7 @@ u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); if (!pub) { - *reason = WLAN_STATUS_UNSPECIFIED_FAILURE; + *status = WLAN_STATUS_UNSPECIFIED_FAILURE; return owe_buf; } @@ -3820,7 +5405,7 @@ void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta) reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS, sta->fils_pending_assoc_is_reassoc, sta->fils_pending_assoc_req, - sta->fils_pending_assoc_req_len, 0); + sta->fils_pending_assoc_req_len, 0, 0); os_free(sta->fils_pending_assoc_req); sta->fils_pending_assoc_req = NULL; sta->fils_pending_assoc_req_len = 0; @@ -3860,17 +5445,16 @@ static void handle_assoc(struct hostapd_data *hapd, int reassoc, int rssi) { u16 capab_info, listen_interval, seq_ctrl, fc; - u16 resp = WLAN_STATUS_SUCCESS, reply_res; + int resp = WLAN_STATUS_SUCCESS; + u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE; const u8 *pos; int left, i; struct sta_info *sta; u8 *tmp = NULL; - struct hostapd_sta_wpa_psk_short *psk = NULL; - char *identity = NULL; - char *radius_cui = NULL; #ifdef CONFIG_FILS int delay_assoc = 0; #endif /* CONFIG_FILS */ + int omit_rsnxe = 0; if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : sizeof(mgmt->u.assoc_req))) { @@ -3947,13 +5531,11 @@ static void handle_assoc(struct hostapd_data *hapd, hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { int acl_res; - u32 session_timeout, acct_interim_interval; - struct vlan_description vlan_id; + struct radius_sta info; - acl_res = ieee802_11_allowed_address( - hapd, mgmt->sa, (const u8 *) mgmt, len, - &session_timeout, &acct_interim_interval, - &vlan_id, &psk, &identity, &radius_cui, 0); + acl_res = ieee802_11_allowed_address(hapd, mgmt->sa, + (const u8 *) mgmt, + len, &info); if (acl_res == HOSTAPD_ACL_REJECT) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Ignore Association Request frame from " @@ -3978,9 +5560,7 @@ static void handle_assoc(struct hostapd_data *hapd, } acl_res = ieee802_11_set_radius_info( - hapd, sta, acl_res, session_timeout, - acct_interim_interval, &vlan_id, &psk, - &identity, &radius_cui); + hapd, sta, acl_res, &info); if (acl_res) { resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; @@ -4087,6 +5667,7 @@ static void handle_assoc(struct hostapd_data *hapd, resp = check_assoc_ies(hapd, sta, pos, left, reassoc); if (resp != WLAN_STATUS_SUCCESS) goto fail; + omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX); if (hostapd_get_aid(hapd, sta) < 0) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -4139,9 +5720,7 @@ static void handle_assoc(struct hostapd_data *hapd, ieee802_11_set_beacons(hapd->iface); } -#ifdef CONFIG_IEEE80211N update_ht_state(hapd, sta); -#endif /* CONFIG_IEEE80211N */ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -4150,7 +5729,6 @@ static void handle_assoc(struct hostapd_data *hapd, */ sta->flags |= WLAN_STA_ASSOC_REQ_OK; -#ifdef CONFIG_IEEE80211W if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) { wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out " "SA Query procedure", reassoc ? "re" : ""); @@ -4161,7 +5739,6 @@ static void handle_assoc(struct hostapd_data *hapd, * trying to associate. */ } -#endif /* CONFIG_IEEE80211W */ /* Make sure that the previously registered inactivity timer will not * remove the STA immediately. */ @@ -4183,9 +5760,6 @@ static void handle_assoc(struct hostapd_data *hapd, #endif /* CONFIG_FILS */ fail: - os_free(identity); - os_free(radius_cui); - hostapd_free_psk_list(psk); /* * In case of a successful response, add the station to the driver. @@ -4246,12 +5820,13 @@ static void handle_assoc(struct hostapd_data *hapd, } #endif /* CONFIG_FILS */ - reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, pos, - left, rssi); + if (resp >= 0) + reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, + pos, left, rssi, omit_rsnxe); os_free(tmp); /* - * Remove the station in case tranmission of a success response fails + * Remove the station in case transmission of a success response fails * (the STA was added associated to the driver) or if the station was * previously added unassociated. */ @@ -4288,6 +5863,7 @@ static void handle_disassoc(struct hostapd_data *hapd, ap_sta_set_authorized(hapd, sta, 0); sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); + hostapd_set_sta_flags(hapd, sta); wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated"); @@ -4342,6 +5918,9 @@ static void handle_deauth(struct hostapd_data *hapd, " reason_code=%d", MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code)); + /* Clear the PTKSA cache entries for PASN */ + ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE); + sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying " @@ -4354,6 +5933,7 @@ static void handle_deauth(struct hostapd_data *hapd, sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); + hostapd_set_sta_flags(hapd, sta); wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "deauthenticated"); @@ -4386,13 +5966,11 @@ static void handle_beacon(struct hostapd_data *hapd, } -#ifdef CONFIG_IEEE80211W static int robust_action_frame(u8 category) { return category != WLAN_ACTION_PUBLIC && category != WLAN_ACTION_HT; } -#endif /* CONFIG_IEEE80211W */ static int handle_action(struct hostapd_data *hapd, @@ -4426,7 +6004,6 @@ static int handle_action(struct hostapd_data *hapd, return 0; } -#ifdef CONFIG_IEEE80211W if (sta && (sta->flags & WLAN_STA_MFP) && !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) && robust_action_frame(mgmt->u.action.category)) { @@ -4436,7 +6013,6 @@ static int handle_action(struct hostapd_data *hapd, "an MFP STA"); return 0; } -#endif /* CONFIG_IEEE80211W */ if (sta) { u16 fc = le_to_host16(mgmt->frame_control); @@ -4470,11 +6046,9 @@ static int handle_action(struct hostapd_data *hapd, case WLAN_ACTION_WMM: hostapd_wmm_action(hapd, mgmt, len); return 1; -#ifdef CONFIG_IEEE80211W case WLAN_ACTION_SA_QUERY: ieee802_11_sa_query_action(hapd, mgmt, len); return 1; -#endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WNM_AP case WLAN_ACTION_WNM: ieee802_11_rx_wnm_action_ap(hapd, mgmt, len); @@ -4491,14 +6065,12 @@ static int handle_action(struct hostapd_data *hapd, #endif /* CONFIG_FST */ case WLAN_ACTION_PUBLIC: case WLAN_ACTION_PROTECTED_DUAL: -#ifdef CONFIG_IEEE80211N if (len >= IEEE80211_HDRLEN + 2 && mgmt->u.action.u.public_action.action == WLAN_PA_20_40_BSS_COEX) { hostapd_2040_coex_action(hapd, mgmt, len); return 1; } -#endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_DPP if (len >= IEEE80211_HDRLEN + 6 && mgmt->u.action.u.vs_public_action.action == @@ -4581,7 +6153,7 @@ static int handle_action(struct hostapd_data *hapd, os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); resp->u.action.category |= 0x80; - if (hostapd_drv_send_mlme(hapd, resp, len, 0) < 0) { + if (hostapd_drv_send_mlme(hapd, resp, len, 0, NULL, 0, 0) < 0) { wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send " "Action frame"); } @@ -4593,6 +6165,31 @@ static int handle_action(struct hostapd_data *hapd, /** + * notify_mgmt_frame - Notify of Management frames on the control interface + * @hapd: hostapd BSS data structure (the BSS to which the Management frame was + * sent to) + * @buf: Management frame data (starting from the IEEE 802.11 header) + * @len: Length of frame data in octets + * + * Notify the control interface of any received Management frame. + */ +static void notify_mgmt_frame(struct hostapd_data *hapd, const u8 *buf, + size_t len) +{ + + int hex_len = len * 2 + 1; + char *hex = os_malloc(hex_len); + + if (hex) { + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, + AP_MGMT_FRAME_RECEIVED "buf=%s", hex); + os_free(hex); + } +} + + +/** * ieee802_11_mgmt - process incoming IEEE 802.11 management frames * @hapd: hostapd BSS data structure (the BSS to which the management frame was * sent to) @@ -4626,6 +6223,18 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); + if (is_multicast_ether_addr(mgmt->sa) || + is_zero_ether_addr(mgmt->sa) || + os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) { + /* Do not process any frames with unexpected/invalid SA so that + * we do not add any state for unexpected STA addresses or end + * up sending out frames to unexpected destination. */ + wpa_printf(MSG_DEBUG, "MGMT: Invalid SA=" MACSTR + " in received frame - ignore this frame silently", + MAC2STR(mgmt->sa)); + return 0; + } + if (stype == WLAN_FC_STYPE_BEACON) { handle_beacon(hapd, mgmt, len, fi); return 1; @@ -4646,6 +6255,11 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, return 0; } + if (hapd->iface->state != HAPD_IFACE_ENABLED) { + wpa_printf(MSG_DEBUG, "MGMT: Ignore management frame while interface is not enabled (SA=" MACSTR " DA=" MACSTR " subtype=%u)", + MAC2STR(mgmt->sa), MAC2STR(mgmt->da), stype); + return 1; + } if (stype == WLAN_FC_STYPE_PROBE_REQ) { handle_probe_req(hapd, mgmt, len, ssi_signal); @@ -4665,6 +6279,9 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, if (hapd->iconf->track_sta_max_num) sta_track_add(hapd->iface, mgmt->sa, ssi_signal); + if (hapd->conf->notify_mgmt_frames) + notify_mgmt_frame(hapd, buf, len); + switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth"); @@ -4712,6 +6329,7 @@ static void handle_auth_cb(struct hostapd_data *hapd, { u16 auth_alg, auth_transaction, status_code; struct sta_info *sta; + bool success_status; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { @@ -4721,6 +6339,15 @@ static void handle_auth_cb(struct hostapd_data *hapd, return; } + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { + wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", + (unsigned long) len); + auth_alg = 0; + auth_transaction = 0; + status_code = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + auth_alg = le_to_host16(mgmt->u.auth.auth_alg); auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); status_code = le_to_host16(mgmt->u.auth.status_code); @@ -4732,12 +6359,6 @@ static void handle_auth_cb(struct hostapd_data *hapd, goto fail; } - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { - wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", - (unsigned long) len); - goto fail; - } - if (status_code == WLAN_STATUS_SUCCESS && ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { @@ -4750,7 +6371,12 @@ static void handle_auth_cb(struct hostapd_data *hapd, } fail: - if (status_code != WLAN_STATUS_SUCCESS && sta->added_unassoc) { + success_status = status_code == WLAN_STATUS_SUCCESS; +#ifdef CONFIG_SAE + if (auth_alg == WLAN_AUTH_SAE && auth_transaction == 1) + success_status = sae_status_success(hapd, status_code); +#endif /* CONFIG_SAE */ + if (!success_status && sta->added_unassoc) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } @@ -4761,6 +6387,7 @@ static void hostapd_set_wds_encryption(struct hostapd_data *hapd, struct sta_info *sta, char *ifname_wds) { +#ifdef CONFIG_WEP int i; struct hostapd_ssid *ssid = &hapd->conf->ssid; @@ -4770,14 +6397,18 @@ static void hostapd_set_wds_encryption(struct hostapd_data *hapd, for (i = 0; i < 4; i++) { if (ssid->wep.key[i] && hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i, - i == ssid->wep.idx, NULL, 0, - ssid->wep.key[i], ssid->wep.len[i])) { + 0, i == ssid->wep.idx, NULL, 0, + ssid->wep.key[i], ssid->wep.len[i], + i == ssid->wep.idx ? + KEY_FLAG_GROUP_RX_TX_DEFAULT : + KEY_FLAG_GROUP_RX_TX)) { wpa_printf(MSG_WARNING, "Could not set WEP keys for WDS interface; %s", ifname_wds); break; } } +#endif /* CONFIG_WEP */ } @@ -4856,9 +6487,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd, else mlme_associate_indication(hapd, sta); -#ifdef CONFIG_IEEE80211W sta->sa_query_timed_out = 0; -#endif /* CONFIG_IEEE80211W */ if (sta->eapol_sm == NULL) { /* @@ -5251,8 +6880,10 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA " MACSTR, MAC2STR(src)); - if (is_multicast_ether_addr(src)) { - /* Broadcast bit set in SA?! Ignore the frame silently. */ + if (is_multicast_ether_addr(src) || is_zero_ether_addr(src) || + os_memcmp(src, hapd->own_addr, ETH_ALEN) == 0) { + /* Broadcast bit set in SA or unexpected SA?! Ignore the frame + * silently. */ return; } @@ -5275,4 +6906,551 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, } +u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) +{ + struct hostapd_iface *iface = hapd->iface; + struct hostapd_config *iconf = iface->conf; + struct hostapd_hw_modes *mode = iface->current_mode; + struct hostapd_channel_data *chan; + int dfs, i; + u8 channel, tx_pwr_count, local_pwr_constraint; + int max_tx_power; + u8 tx_pwr; + + if (!mode) + return eid; + + if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES) + return eid; + + for (i = 0; i < mode->num_channels; i++) { + if (mode->channels[i].freq == iface->freq) + break; + } + if (i == mode->num_channels) + return eid; + + switch (hostapd_get_oper_chwidth(iconf)) { + case CHANWIDTH_USE_HT: + if (iconf->secondary_channel == 0) { + /* Max Transmit Power count = 0 (20 MHz) */ + tx_pwr_count = 0; + } else { + /* Max Transmit Power count = 1 (20, 40 MHz) */ + tx_pwr_count = 1; + } + break; + case CHANWIDTH_80MHZ: + /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */ + tx_pwr_count = 2; + break; + case CHANWIDTH_80P80MHZ: + case CHANWIDTH_160MHZ: + /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */ + tx_pwr_count = 3; + break; + default: + return eid; + } + + /* + * Below local_pwr_constraint logic is referred from + * hostapd_eid_pwr_constraint. + * + * Check if DFS is required by regulatory. + */ + dfs = hostapd_is_dfs_required(hapd->iface); + if (dfs < 0) + dfs = 0; + + /* + * In order to meet regulations when TPC is not implemented using + * a transmit power that is below the legal maximum (including any + * mitigation factor) should help. In this case, indicate 3 dB below + * maximum allowed transmit power. + */ + if (hapd->iconf->local_pwr_constraint == -1) + local_pwr_constraint = (dfs == 0) ? 0 : 3; + else + local_pwr_constraint = hapd->iconf->local_pwr_constraint; + + /* + * A STA that is not an AP shall use a transmit power less than or + * equal to the local maximum transmit power level for the channel. + * The local maximum transmit power can be calculated from the formula: + * local max TX pwr = max TX pwr - local pwr constraint + * Where max TX pwr is maximum transmit power level specified for + * channel in Country element and local pwr constraint is specified + * for channel in this Power Constraint element. + */ + chan = &mode->channels[i]; + max_tx_power = chan->max_tx_power - local_pwr_constraint; + + /* + * Local Maximum Transmit power is encoded as two's complement + * with a 0.5 dB step. + */ + max_tx_power *= 2; /* in 0.5 dB steps */ + if (max_tx_power > 127) { + /* 63.5 has special meaning of 63.5 dBm or higher */ + max_tx_power = 127; + } + if (max_tx_power < -128) + max_tx_power = -128; + if (max_tx_power < 0) + tx_pwr = 0x80 + max_tx_power + 128; + else + tx_pwr = max_tx_power; + + *eid++ = WLAN_EID_TRANSMIT_POWER_ENVELOPE; + *eid++ = 2 + tx_pwr_count; + + /* + * Max Transmit Power count and + * Max Transmit Power units = 0 (EIRP) + */ + *eid++ = tx_pwr_count; + + for (i = 0; i <= tx_pwr_count; i++) + *eid++ = tx_pwr; + + return eid; +} + + +u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid) +{ + u8 bw, chan1, chan2 = 0; + int freq1; + + if (!hapd->cs_freq_params.channel || + (!hapd->cs_freq_params.vht_enabled && + !hapd->cs_freq_params.he_enabled)) + return eid; + + /* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */ + switch (hapd->cs_freq_params.bandwidth) { + case 40: + bw = 0; + break; + case 80: + /* check if it's 80+80 */ + if (!hapd->cs_freq_params.center_freq2) + bw = 1; + else + bw = 3; + break; + case 160: + bw = 2; + break; + default: + /* not valid VHT bandwidth or not in CSA */ + return eid; + } + + freq1 = hapd->cs_freq_params.center_freq1 ? + hapd->cs_freq_params.center_freq1 : + hapd->cs_freq_params.freq; + if (ieee80211_freq_to_chan(freq1, &chan1) != + HOSTAPD_MODE_IEEE80211A) + return eid; + + if (hapd->cs_freq_params.center_freq2 && + ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2, + &chan2) != HOSTAPD_MODE_IEEE80211A) + return eid; + + *eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER; + *eid++ = 5; /* Length of Channel Switch Wrapper */ + *eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH; + *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */ + *eid++ = bw; /* New Channel Width */ + *eid++ = chan1; /* New Channel Center Frequency Segment 0 */ + *eid++ = chan2; /* New Channel Center Frequency Segment 1 */ + + return eid; +} + + +static size_t hostapd_eid_nr_db_len(struct hostapd_data *hapd, + size_t *current_len) +{ + struct hostapd_neighbor_entry *nr; + size_t total_len = 0, len = *current_len; + + dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, + list) { + if (!nr->nr || wpabuf_len(nr->nr) < 12) + continue; + + if (nr->short_ssid == hapd->conf->ssid.short_ssid) + continue; + + /* Start a new element */ + if (!len || + len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) { + len = RNR_HEADER_LEN; + total_len += RNR_HEADER_LEN; + } + + len += RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN; + total_len += RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN; + } + + *current_len = len; + return total_len; +} + + +static size_t hostapd_eid_rnr_iface_len(struct hostapd_data *hapd, + struct hostapd_data *reporting_hapd, + size_t *current_len) +{ + size_t total_len = 0, len = *current_len; + int tbtt_count = 0; + size_t i, start = 0; + + while (start < hapd->iface->num_bss) { + if (!len || + len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) { + len = RNR_HEADER_LEN; + total_len += RNR_HEADER_LEN; + } + + len += RNR_TBTT_HEADER_LEN; + total_len += RNR_TBTT_HEADER_LEN; + + for (i = start; i < hapd->iface->num_bss; i++) { + struct hostapd_data *bss = hapd->iface->bss[i]; + + if (!bss || !bss->conf || !bss->started) + continue; + + if (bss == reporting_hapd || + bss->conf->ignore_broadcast_ssid) + continue; + + if (len + RNR_TBTT_INFO_LEN > 255 || + tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) + break; + + len += RNR_TBTT_INFO_LEN; + total_len += RNR_TBTT_INFO_LEN; + tbtt_count++; + } + start = i; + } + + if (!tbtt_count) + total_len = 0; + else + *current_len = len; + + return total_len; +} + + +enum colocation_mode { + NO_COLOCATED_6GHZ, + STANDALONE_6GHZ, + COLOCATED_6GHZ, + COLOCATED_LOWER_BAND, +}; + +static enum colocation_mode get_colocation_mode(struct hostapd_data *hapd) +{ + u8 i; + bool is_6ghz = is_6ghz_op_class(hapd->iconf->op_class); + + if (!hapd->iface || !hapd->iface->interfaces) + return NO_COLOCATED_6GHZ; + + if (is_6ghz && hapd->iface->interfaces->count == 1) + return STANDALONE_6GHZ; + + for (i = 0; i < hapd->iface->interfaces->count; i++) { + struct hostapd_iface *iface; + bool is_colocated_6ghz; + + iface = hapd->iface->interfaces->iface[i]; + if (iface == hapd->iface || !iface || !iface->conf) + continue; + + is_colocated_6ghz = is_6ghz_op_class(iface->conf->op_class); + if (!is_6ghz && is_colocated_6ghz) + return COLOCATED_LOWER_BAND; + if (is_6ghz && !is_colocated_6ghz) + return COLOCATED_6GHZ; + } + + if (is_6ghz) + return STANDALONE_6GHZ; + + return NO_COLOCATED_6GHZ; +} + + +static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd, + size_t *current_len) +{ + struct hostapd_iface *iface; + size_t len = 0; + size_t i; + + if (!hapd->iface || !hapd->iface->interfaces) + return 0; + + for (i = 0; i < hapd->iface->interfaces->count; i++) { + iface = hapd->iface->interfaces->iface[i]; + + if (iface == hapd->iface || + !is_6ghz_op_class(iface->conf->op_class)) + continue; + + len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd, + current_len); + } + + return len; +} + + +size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type) +{ + size_t total_len = 0, current_len = 0; + enum colocation_mode mode = get_colocation_mode(hapd); + + switch (type) { + case WLAN_FC_STYPE_BEACON: + if (hapd->conf->rnr) + total_len += hostapd_eid_nr_db_len(hapd, ¤t_len); + /* fallthrough */ + + case WLAN_FC_STYPE_PROBE_RESP: + if (mode == COLOCATED_LOWER_BAND) + total_len += hostapd_eid_rnr_colocation_len( + hapd, ¤t_len); + + if (hapd->conf->rnr && hapd->iface->num_bss > 1) + total_len += hostapd_eid_rnr_iface_len(hapd, hapd, + ¤t_len); + break; + + case WLAN_FC_STYPE_ACTION: + if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ) + total_len += hostapd_eid_rnr_iface_len(hapd, hapd, + ¤t_len); + break; + + default: + break; + } + + return total_len; +} + + +static u8 * hostapd_eid_nr_db(struct hostapd_data *hapd, u8 *eid, + size_t *current_len) +{ + struct hostapd_neighbor_entry *nr; + size_t len = *current_len; + u8 *size_offset = (eid - len) + 1; + + dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, + list) { + if (!nr->nr || wpabuf_len(nr->nr) < 12) + continue; + + if (nr->short_ssid == hapd->conf->ssid.short_ssid) + continue; + + /* Start a new element */ + if (!len || + len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) { + *eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT; + size_offset = eid++; + len = RNR_HEADER_LEN; + } + + /* TBTT Information Header subfield (2 octets) */ + *eid++ = 0; + /* TBTT Information Length */ + *eid++ = RNR_TBTT_INFO_LEN; + /* Operating Class */ + *eid++ = wpabuf_head_u8(nr->nr)[10]; + /* Channel Number */ + *eid++ = wpabuf_head_u8(nr->nr)[11]; + len += RNR_TBTT_HEADER_LEN; + /* TBTT Information Set */ + /* TBTT Information field */ + /* Neighbor AP TBTT Offset */ + *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN; + /* BSSID */ + os_memcpy(eid, nr->bssid, ETH_ALEN); + eid += ETH_ALEN; + /* Short SSID */ + os_memcpy(eid, &nr->short_ssid, 4); + eid += 4; + /* BSS parameters */ + *eid++ = nr->bss_parameters; + /* 20 MHz PSD */ + *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1; + len += RNR_TBTT_INFO_LEN; + *size_offset = (eid - size_offset) - 1; + } + + *current_len = len; + return eid; +} + + +static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd, + struct hostapd_data *reporting_hapd, + u8 *eid, size_t *current_len) +{ + struct hostapd_data *bss; + struct hostapd_iface *iface = hapd->iface; + size_t i, start = 0; + size_t len = *current_len; + u8 *tbtt_count_pos, *eid_start = eid, *size_offset = (eid - len) + 1; + u8 tbtt_count = 0, op_class, channel, bss_param; + + if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq) + return eid; + + if (ieee80211_freq_to_channel_ext(iface->freq, + hapd->iconf->secondary_channel, + hostapd_get_oper_chwidth(hapd->iconf), + &op_class, &channel) == + NUM_HOSTAPD_MODES) + return eid; + + while (start < iface->num_bss) { + if (!len || + len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) { + eid_start = eid; + *eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT; + size_offset = eid++; + len = RNR_HEADER_LEN; + tbtt_count = 0; + } + + tbtt_count_pos = eid++; + *eid++ = RNR_TBTT_INFO_LEN; + *eid++ = op_class; + *eid++ = hapd->iconf->channel; + len += RNR_TBTT_HEADER_LEN; + + for (i = start; i < iface->num_bss; i++) { + bss_param = 0; + bss = iface->bss[i]; + if (!bss || !bss->conf || !bss->started) + continue; + + if (bss == reporting_hapd || + bss->conf->ignore_broadcast_ssid) + continue; + + if (len + RNR_TBTT_INFO_LEN > 255 || + tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) + break; + + *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN; + os_memcpy(eid, bss->conf->bssid, ETH_ALEN); + eid += ETH_ALEN; + os_memcpy(eid, &bss->conf->ssid.short_ssid, 4); + eid += 4; + if (bss->conf->ssid.short_ssid == + reporting_hapd->conf->ssid.short_ssid) + bss_param |= RNR_BSS_PARAM_SAME_SSID; + + if (is_6ghz_op_class(hapd->iconf->op_class) && + bss->conf->unsol_bcast_probe_resp_interval) + bss_param |= + RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE; + + bss_param |= RNR_BSS_PARAM_CO_LOCATED; + + *eid++ = bss_param; + *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1; + len += RNR_TBTT_INFO_LEN; + tbtt_count += 1; + } + + start = i; + *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1); + *size_offset = (eid - size_offset) - 1; + } + + if (tbtt_count == 0) + return eid_start; + + *current_len = len; + return eid; +} + + +static u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid, + size_t *current_len) +{ + struct hostapd_iface *iface; + size_t i; + + if (!hapd->iface || !hapd->iface->interfaces) + return eid; + + for (i = 0; i < hapd->iface->interfaces->count; i++) { + iface = hapd->iface->interfaces->iface[i]; + + if (iface == hapd->iface || + !is_6ghz_op_class(iface->conf->op_class)) + continue; + + eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid, + current_len); + } + + return eid; +} + + +u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type) +{ + u8 *eid_start = eid; + size_t current_len = 0; + enum colocation_mode mode = get_colocation_mode(hapd); + + switch (type) { + case WLAN_FC_STYPE_BEACON: + if (hapd->conf->rnr) + eid = hostapd_eid_nr_db(hapd, eid, ¤t_len); + /* fallthrough */ + + case WLAN_FC_STYPE_PROBE_RESP: + if (mode == COLOCATED_LOWER_BAND) + eid = hostapd_eid_rnr_colocation(hapd, eid, + ¤t_len); + + if (hapd->conf->rnr && hapd->iface->num_bss > 1) + eid = hostapd_eid_rnr_iface(hapd, hapd, eid, + ¤t_len); + break; + + case WLAN_FC_STYPE_ACTION: + if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ) + eid = hostapd_eid_rnr_iface(hapd, hapd, eid, + ¤t_len); + break; + + default: + return eid_start; + } + + if (eid == eid_start + 2) + return eid_start; + + return eid; +} + #endif /* CONFIG_NATIVE_WINDOWS */ |