diff options
Diffstat (limited to 'src/ap/ieee802_11.c')
-rw-r--r-- | src/ap/ieee802_11.c | 1932 |
1 files changed, 790 insertions, 1142 deletions
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 6140a492..0142ee44 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -55,6 +55,8 @@ #include "fils_hlp.h" #include "dpp_hostapd.h" #include "gas_query_ap.h" +#include "comeback_token.h" +#include "pasn/pasn_common.h" #ifdef CONFIG_FILS @@ -68,11 +70,6 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, #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, @@ -118,9 +115,13 @@ 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 || +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) + num++; +#endif /* CONFIG_IEEE80211AX */ + h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT || hostapd_sae_pw_id_in_use(hapd->conf) == 2) && - hapd->conf->sae_pwe != 3 && + hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt); if (h2e_required) num++; @@ -150,6 +151,13 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; } +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax && hapd->iconf->require_he && count < 8) { + count++; + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY; + } +#endif /* CONFIG_IEEE80211AX */ + if (h2e_required && count < 8) { count++; *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY; @@ -165,6 +173,7 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) int i, num, count; int h2e_required; + hapd->conf->xrates_supported = false; if (hapd->iface->current_rates == NULL) return eid; @@ -173,9 +182,13 @@ 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 || +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) + num++; +#endif /* CONFIG_IEEE80211AX */ + h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT || hostapd_sae_pw_id_in_use(hapd->conf) == 2) && - hapd->conf->sae_pwe != 3 && + hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt); if (h2e_required) num++; @@ -208,12 +221,21 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; } +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) { + count++; + if (count > 8) + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY; + } +#endif /* CONFIG_IEEE80211AX */ + if (h2e_required) { count++; if (count > 8) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY; } + hapd->conf->xrates_supported = true; return pos; } @@ -498,6 +520,7 @@ static const char * sae_get_password(struct hostapd_data *hapd, struct sae_password_entry *pw; struct sae_pt *pt = NULL; const struct sae_pk *pk = NULL; + struct hostapd_sta_wpa_psk_short *psk = NULL; for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { if (!is_broadcast_ether_addr(pw->peer_addr) && @@ -519,6 +542,15 @@ static const char * sae_get_password(struct hostapd_data *hapd, pt = hapd->conf->ssid.pt; } + if (!password) { + for (psk = sta->psk; psk; psk = psk->next) { + if (psk->is_passphrase) { + password = psk->passphrase; + break; + } + } + } + if (pw_entry) *pw_entry = pw; if (s_pt) @@ -551,7 +583,7 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, #endif /* CONFIG_SAE_PK */ } - if (rx_id && hapd->conf->sae_pwe != 3) + if (rx_id && hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) use_pt = 1; else if (status_code == WLAN_STATUS_SUCCESS) use_pt = 0; @@ -727,124 +759,6 @@ static int use_anti_clogging(struct hostapd_data *hapd) return 0; } - -static int comeback_token_hash(struct hostapd_data *hapd, const u8 *addr, - u8 *idx) -{ - u8 hash[SHA256_MAC_LEN]; - - 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_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]; - size_t len[2]; - u16 token_idx; - u8 idx; - - if (token_len != SHA256_MAC_LEN || - comeback_token_hash(hapd, addr, &idx) < 0) - return -1; - token_idx = hapd->comeback_pending_idx[idx]; - if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) { - 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; - } - - addrs[0] = addr; - len[0] = ETH_ALEN; - addrs[1] = token; - len[1] = 2; - 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->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 h2e) -{ - struct wpabuf *buf; - u8 *token; - struct os_reltime now; - u8 idx[2]; - const u8 *addrs[2]; - size_t len[2]; - u8 p_idx; - u16 token_idx; - - os_get_reltime(&now); - 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, "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) + 3 + SHA256_MAC_LEN); - if (buf == NULL) - return NULL; - - 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; - } - - token_idx = hapd->comeback_pending_idx[p_idx]; - if (!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); - addrs[0] = addr; - len[0] = ETH_ALEN; - addrs[1] = idx; - len[1] = sizeof(idx); - if (hmac_sha256_vector(hapd->comeback_key, sizeof(hapd->comeback_key), - 2, addrs, len, token) < 0) { - wpabuf_free(buf); - return NULL; - } - WPA_PUT_BE16(token, token_idx); - - return buf; -} - #endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */ @@ -972,7 +886,8 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) 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); + sta->sae->pmk, sta->sae->pmk_len, + sta->sae->pmkid, sta->sae->akmp); sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS); } @@ -1209,27 +1124,32 @@ 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; + enum sae_pwe 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; + if (id_in_use == 2 && sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) + sae_pwe = SAE_PWE_HASH_TO_ELEMENT; + else if (id_in_use == 1 && sae_pwe == SAE_PWE_HUNT_AND_PECK) + sae_pwe = SAE_PWE_BOTH; #ifdef CONFIG_SAE_PK sae_pk = hostapd_sae_pk_in_use(hapd->conf); - if (sae_pwe == 0 && sae_pk) - sae_pwe = 2; + if (sae_pwe == SAE_PWE_HUNT_AND_PECK && sae_pk) + sae_pwe = SAE_PWE_BOTH; #endif /* CONFIG_SAE_PK */ + if (sae_pwe == SAE_PWE_HUNT_AND_PECK && + (hapd->conf->wpa_key_mgmt & + (WPA_KEY_MGMT_SAE_EXT_KEY | WPA_KEY_MGMT_FT_SAE_EXT_KEY))) + sae_pwe = SAE_PWE_BOTH; - return ((sae_pwe == 0 || sae_pwe == 3) && + return ((sae_pwe == SAE_PWE_HUNT_AND_PECK || + sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) && status_code == WLAN_STATUS_SUCCESS) || - (sae_pwe == 1 && + (sae_pwe == SAE_PWE_HASH_TO_ELEMENT && (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || (sae_pk && status_code == WLAN_STATUS_SAE_PK))) || - (sae_pwe == 2 && + (sae_pwe == SAE_PWE_BOTH && (status_code == WLAN_STATUS_SUCCESS || status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || (sae_pk && status_code == WLAN_STATUS_SAE_PK))); @@ -1453,7 +1373,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, mgmt->u.auth.variable, &token, &token_len, groups, status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || - status_code == WLAN_STATUS_SAE_PK); + status_code == WLAN_STATUS_SAE_PK, + NULL); if (resp == SAE_SILENTLY_DISCARD) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message from " MACSTR " due to reflection attack", @@ -1472,7 +1393,9 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } if (token && - check_comeback_token(hapd, sta->addr, token, token_len) + check_comeback_token(hapd->comeback_key, + hapd->comeback_pending_idx, sta->addr, + token, token_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " "incorrect token from " MACSTR, @@ -1500,8 +1423,14 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, 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, h2e); + data = auth_build_token_req( + &hapd->last_comeback_key_update, + hapd->comeback_key, + hapd->comeback_idx, + hapd->comeback_pending_idx, + sizeof(hapd->comeback_pending_idx), + sta->sae->group, + sta->addr, h2e); resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ; if (hapd->conf->mesh & MESH_ENABLED) sae_set_state(sta, SAE_NOTHING, @@ -1545,8 +1474,9 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, return; } - if (sae_check_confirm(sta->sae, var, var_len) < 0) { - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + if (sae_check_confirm(sta->sae, var, var_len, + NULL) < 0) { + resp = WLAN_STATUS_CHALLENGE_FAIL; goto reply; } sta->sae->rc = peer_send_confirm; @@ -2095,10 +2025,8 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) { /* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */ int res; - int use_sha384 = wpa_key_mgmt_sha384( - wpa_auth_sta_key_mgmt(sta->wpa_sm)); - res = wpa_auth_write_fte(hapd->wpa_auth, use_sha384, + res = wpa_auth_write_fte(hapd->wpa_auth, sta->wpa_sm, wpabuf_put(data, 0), wpabuf_tailroom(data)); if (res < 0) { @@ -2315,9 +2243,8 @@ static int 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, struct radius_sta *info) +int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, + int res, struct radius_sta *info) { u32 session_timeout = info->session_timeout; u32 acct_interim_interval = info->acct_interim_interval; @@ -2376,242 +2303,15 @@ 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; + struct pasn_fils *fils = &pasn->fils; u8 pmk[PMK_LEN_MAX]; size_t pmk_len; int ret; @@ -2652,13 +2352,23 @@ static void pasn_fils_auth_resp(struct hostapd_data *hapd, goto fail; } + if (pasn->secure_ltf) { + ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, pasn->cipher); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: FILS: Failed to derive LTF keyseed"); + 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); + ret = handle_auth_pasn_resp(sta->pasn, hapd->own_addr, sta->addr, NULL, + WLAN_STATUS_SUCCESS); fils->erp_resp = NULL; if (ret) { @@ -2681,7 +2391,7 @@ static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta, return -1; #else /* CONFIG_NO_RADIUS */ struct pasn_data *pasn = sta->pasn; - struct pasn_fils_data *fils = &pasn->fils; + struct pasn_fils *fils = &pasn->fils; struct ieee802_11_elems elems; struct wpa_ie_data rsne_data; struct wpabuf *fils_wd; @@ -2732,7 +2442,7 @@ static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta, } if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce || - !elems.wrapped_data) { + !elems.wrapped_data || !elems.fils_session) { wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs"); return -1; } @@ -2740,7 +2450,7 @@ static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta, 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"); + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RSNE"); return -1; } @@ -2800,321 +2510,90 @@ static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta, #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 int hapd_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len, + int noack, unsigned int freq, unsigned int wait) { - 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; - } + struct hostapd_data *hapd = ctx; - wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived"); - return 0; + return hostapd_drv_send_mlme(hapd, data, data_len, 0, NULL, 0, 0); } -static void handle_auth_pasn_comeback(struct hostapd_data *hapd, - struct sta_info *sta, u16 group) +static void hapd_initialize_pasn(struct hostapd_data *hapd, + struct sta_info *sta) { - 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"); + struct pasn_data *pasn = sta->pasn; - wpabuf_free(buf); + pasn->cb_ctx = hapd; + pasn->send_mgmt = hapd_pasn_send_mlme; + pasn->pasn_groups = hapd->conf->pasn_groups; + pasn->wpa_key_mgmt = hapd->conf->wpa_key_mgmt; + pasn->rsn_pairwise = hapd->conf->rsn_pairwise; + pasn->derive_kdk = hapd->iface->drv_flags2 & + WPA_DRIVER_FLAGS2_SEC_LTF_AP; +#ifdef CONFIG_TESTING_OPTIONS + pasn->corrupt_mic = hapd->conf->pasn_corrupt_mic; + if (hapd->conf->force_kdk_derivation) + pasn->derive_kdk = true; +#endif /* CONFIG_TESTING_OPTIONS */ + pasn->use_anti_clogging = use_anti_clogging(hapd); + pasn->password = sae_get_password(hapd, sta, NULL, NULL, &pasn->pt, + NULL); + pasn->rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &pasn->rsn_ie_len); + pasn->rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX); + pasn->disable_pmksa_caching = hapd->conf->disable_pmksa_caching; + pasn->pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth); + + pasn->comeback_after = hapd->conf->pasn_comeback_after; + pasn->comeback_idx = hapd->comeback_idx; + pasn->comeback_key = hapd->comeback_key; + pasn->comeback_pending_idx = hapd->comeback_pending_idx; + os_memcpy(pasn->bssid, hapd->own_addr, ETH_ALEN); } -static int handle_auth_pasn_resp(struct hostapd_data *hapd, - struct sta_info *sta, - struct rsn_pmksa_cache_entry *pmksa, - u16 status) +static int pasn_set_keys_from_cache(struct hostapd_data *hapd, + const u8 *own_addr, const u8 *sta_addr, + int cipher, int akmp) { - 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; + struct ptksa_cache_entry *entry; - 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; + entry = ptksa_cache_get(hapd->ptksa, sta_addr, cipher); + if (!entry) { + wpa_printf(MSG_DEBUG, "PASN: peer " MACSTR + " not present in PTKSA cache", MAC2STR(sta_addr)); + return -1; } -#ifdef CONFIG_TESTING_OPTIONS - if (hapd->conf->pasn_corrupt_mic) { - wpa_printf(MSG_DEBUG, "PASN: frame 2: Corrupt MIC"); - mic[0] = ~mic[0]; + if (os_memcmp(entry->own_addr, own_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "PASN: own addr " MACSTR " and PTKSA entry own addr " + MACSTR " differ", + MAC2STR(own_addr), MAC2STR(entry->own_addr)); + return -1; } -#endif /* CONFIG_TESTING_OPTIONS */ - os_memcpy(ptr, mic, mic_len); + wpa_printf(MSG_DEBUG, "PASN: " MACSTR " present in PTKSA cache", + MAC2STR(sta_addr)); + hostapd_drv_set_secure_ranging_ctx(hapd, own_addr, sta_addr, cipher, + entry->ptk.tk_len, entry->ptk.tk, + entry->ptk.ltf_keyseed_len, + entry->ptk.ltf_keyseed, 0); -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; + return 0; } -static void handle_auth_pasn_1(struct hostapd_data *hapd, struct sta_info *sta, - const struct ieee80211_mgmt *mgmt, size_t len) +static void hapd_pasn_update_params(struct hostapd_data *hapd, + struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, + size_t len) { + struct pasn_data *pasn = sta->pasn; 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; + struct wpabuf *wrapped_data = NULL; if (ieee802_11_parse_elems(mgmt->u.auth.variable, len - offsetof(struct ieee80211_mgmt, @@ -3122,370 +2601,62 @@ static void handle_auth_pasn_1(struct hostapd_data *hapd, struct sta_info *sta, &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; + return; } - 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 (!elems.rsn_ie || + wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn_data)) { + wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE"); + return; } - if (!(rsn_data.key_mgmt & hapd->conf->wpa_key_mgmt) || - !(rsn_data.pairwise_cipher & hapd->conf->rsn_pairwise)) { + if (!(rsn_data.key_mgmt & pasn->wpa_key_mgmt) || + !(rsn_data.pairwise_cipher & pasn->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; + return; } - for (i = 0; groups[i] > 0 && groups[i] != pasn_params.group; i++) - ; + pasn->akmp = rsn_data.key_mgmt; + pasn->cipher = rsn_data.pairwise_cipher; - 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 (wpa_key_mgmt_ft(pasn->akmp) && rsn_data.num_pmkid) { +#ifdef CONFIG_IEEE80211R_AP + pasn->pmk_r1_len = 0; + wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr, + rsn_data.pmkid, + pasn->pmk_r1, &pasn->pmk_r1_len, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); +#endif /* CONFIG_IEEE80211R_AP */ } - - 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); +#ifdef CONFIG_FILS + if (pasn->akmp != WPA_KEY_MGMT_FILS_SHA256 && + pasn->akmp != WPA_KEY_MGMT_FILS_SHA384) 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 { + if (!elems.pasn_params || + wpa_pasn_parse_parameter_ie(elems.pasn_params - 3, + elems.pasn_params_len + 3, + false, &pasn_params)) { 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; + "PASN: Failed validation of PASN Parameters element"); + return; } - - derive_keys = true; if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) { - wrapped_data = ieee802_11_defrag(&elems, - WLAN_EID_EXTENSION, + 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; - } + return; } -#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; - } - + if (pasn_wd_handle_fils(hapd, sta, wrapped_data)) 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"); + "PASN: Failed processing FILS wrapped data"); + else + pasn->fils_wd_valid = true; } - - 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); } @@ -3521,7 +2692,12 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta, return; } - handle_auth_pasn_1(hapd, sta, mgmt, len); + hapd_initialize_pasn(hapd, sta); + + hapd_pasn_update_params(hapd, sta, mgmt, len); + if (handle_auth_pasn_1(sta->pasn, hapd->own_addr, + sta->addr, mgmt, len) < 0) + ap_free_sta(hapd, sta); } else if (trans_seq == 3) { if (!sta->pasn) { wpa_printf(MSG_DEBUG, @@ -3536,7 +2712,18 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta, return; } - handle_auth_pasn_3(hapd, sta, mgmt, len); + if (handle_auth_pasn_3(sta->pasn, hapd->own_addr, + sta->addr, mgmt, len) == 0) { + ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr, + sta->pasn->cipher, 43200, + &sta->pasn->ptk, NULL, NULL, + sta->pasn->akmp); + + pasn_set_keys_from_cache(hapd, hapd->own_addr, + sta->addr, sta->pasn->cipher, + sta->pasn->akmp); + } + ap_free_sta(hapd, sta); } else { wpa_printf(MSG_DEBUG, "PASN: Invalid transaction %u - ignore", trans_seq); @@ -3956,6 +3143,23 @@ static void handle_auth(struct hostapd_data *hapd, } +static u8 hostapd_max_bssid_indicator(struct hostapd_data *hapd) +{ + size_t num_bss_nontx; + u8 max_bssid_ind = 0; + + if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1) + return 0; + + num_bss_nontx = hapd->iface->num_bss - 1; + while (num_bss_nontx > 0) { + max_bssid_ind++; + num_bss_nontx >>= 1; + } + return max_bssid_ind; +} + + int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) { int i, j = 32, aid; @@ -3981,7 +3185,7 @@ int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) } if (j == 32) return -1; - aid = i * 32 + j + 1; + aid = i * 32 + j + (1 << hostapd_max_bssid_indicator(hapd)); if (aid > 2007) return -1; @@ -4123,32 +3327,6 @@ static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, } -static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *ext_capab_ie, size_t ext_capab_ie_len) -{ -#ifdef CONFIG_INTERWORKING - /* check for QoS Map support */ - if (ext_capab_ie_len >= 5) { - if (ext_capab_ie[4] & 0x01) - sta->qos_map_enabled = 1; - } -#endif /* CONFIG_INTERWORKING */ - - if (ext_capab_ie_len > 0) { - sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2)); - os_free(sta->ext_capability); - sta->ext_capability = os_malloc(1 + ext_capab_ie_len); - if (sta->ext_capability) { - sta->ext_capability[0] = ext_capab_ie_len; - os_memcpy(sta->ext_capability + 1, ext_capab_ie, - ext_capab_ie_len); - } - } - - return WLAN_STATUS_SUCCESS; -} - - #ifdef CONFIG_OWE static int owe_group_supported(struct hostapd_data *hapd, u16 group) @@ -4203,8 +3381,21 @@ static u16 owe_process_assoc_req(struct hostapd_data *hapd, else return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; - crypto_ecdh_deinit(sta->owe_ecdh); - sta->owe_ecdh = crypto_ecdh_init(group); + if (sta->owe_group == group && sta->owe_ecdh) { + /* This is a workaround for mac80211 behavior of retransmitting + * the Association Request frames multiple times if the link + * layer retries (i.e., seq# remains same) fail. The mac80211 + * initiated retransmission will use a different seq# and as + * such, will go through duplicate detection. If we were to + * change our DH key for that attempt, there would be two + * different DH shared secrets and the STA would likely select + * the wrong one. */ + wpa_printf(MSG_DEBUG, + "OWE: Try to reuse own previous DH key since the STA tried to go through OWE association again"); + } else { + crypto_ecdh_deinit(sta->owe_ecdh); + sta->owe_ecdh = crypto_ecdh_init(group); + } if (!sta->owe_ecdh) return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; sta->owe_group = group; @@ -4461,40 +3652,34 @@ static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta, } -static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *ies, size_t ies_len, int reassoc) +static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ies, size_t ies_len, + struct ieee802_11_elems *elems, int reassoc) { - struct ieee802_11_elems elems; int resp; const u8 *wpa_ie; size_t wpa_ie_len; const u8 *p2p_dev_addr = NULL; - if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "Station sent an invalid " - "association request"); - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } - - resp = check_ssid(hapd, sta, elems.ssid, elems.ssid_len); + resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len); if (resp != WLAN_STATUS_SUCCESS) return resp; - resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len); + resp = check_wmm(hapd, sta, elems->wmm, elems->wmm_len); if (resp != WLAN_STATUS_SUCCESS) return resp; - resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len); + resp = check_ext_capab(hapd, sta, elems->ext_capab, + elems->ext_capab_len); if (resp != WLAN_STATUS_SUCCESS) return resp; - resp = copy_supp_rates(hapd, sta, &elems); + resp = copy_supp_rates(hapd, sta, elems); if (resp != WLAN_STATUS_SUCCESS) return resp; - resp = check_multi_ap(hapd, sta, elems.multi_ap, elems.multi_ap_len); + resp = check_multi_ap(hapd, sta, elems->multi_ap, elems->multi_ap_len); if (resp != WLAN_STATUS_SUCCESS) return resp; - resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities); + resp = copy_sta_ht_capab(hapd, sta, elems->ht_capabilities); if (resp != WLAN_STATUS_SUCCESS) return resp; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && @@ -4507,11 +3692,11 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac) { - resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities); + resp = copy_sta_vht_capab(hapd, sta, elems->vht_capabilities); if (resp != WLAN_STATUS_SUCCESS) return resp; - resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif); + resp = set_sta_vht_opmode(hapd, sta, elems->vht_opmode_notif); if (resp != WLAN_STATUS_SUCCESS) return resp; } @@ -4524,9 +3709,9 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_ASSOC_DENIED_NO_VHT; } - if (hapd->conf->vendor_vht && !elems.vht_capabilities) { - resp = copy_sta_vendor_vht(hapd, sta, elems.vendor_vht, - elems.vendor_vht_len); + if (hapd->conf->vendor_vht && !elems->vht_capabilities) { + resp = copy_sta_vendor_vht(hapd, sta, elems->vendor_vht, + elems->vendor_vht_len); if (resp != WLAN_STATUS_SUCCESS) return resp; } @@ -4534,10 +3719,19 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_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); + elems->he_capabilities, + elems->he_capabilities_len); if (resp != WLAN_STATUS_SUCCESS) return resp; + + if (hapd->iconf->require_he && !(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; + } + if (is_6ghz_op_class(hapd->iconf->op_class)) { if (!(sta->flags & WLAN_STA_HE)) { hostapd_logger(hapd, sta->addr, @@ -4547,15 +3741,26 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED; } resp = copy_sta_he_6ghz_capab(hapd, sta, - elems.he_6ghz_band_cap); + elems->he_6ghz_band_cap); if (resp != WLAN_STATUS_SUCCESS) return resp; } } #endif /* CONFIG_IEEE80211AX */ +#ifdef CONFIG_IEEE80211BE + if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { + resp = copy_sta_eht_capab(hapd, sta, IEEE80211_MODE_AP, + elems->he_capabilities, + elems->he_capabilities_len, + elems->eht_capabilities, + elems->eht_capabilities_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + } +#endif /* CONFIG_IEEE80211BE */ #ifdef CONFIG_P2P - if (elems.p2p) { + if (elems->p2p && ies && ies_len) { wpabuf_free(sta->p2p_ie); sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE); @@ -4567,13 +3772,13 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_P2P */ - if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) { - wpa_ie = elems.rsn_ie; - wpa_ie_len = elems.rsn_ie_len; + if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems->rsn_ie) { + wpa_ie = elems->rsn_ie; + wpa_ie_len = elems->rsn_ie_len; } else if ((hapd->conf->wpa & WPA_PROTO_WPA) && - elems.wpa_ie) { - wpa_ie = elems.wpa_ie; - wpa_ie_len = elems.wpa_ie_len; + elems->wpa_ie) { + wpa_ie = elems->wpa_ie; + wpa_ie_len = elems->wpa_ie_len; } else { wpa_ie = NULL; wpa_ie_len = 0; @@ -4581,7 +3786,7 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_WPS sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); - if (hapd->conf->wps_state && elems.wps_ie) { + if (hapd->conf->wps_state && elems->wps_ie && ies && ies_len) { wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association " "Request - assume WPS is used"); if (check_sa_query(hapd, sta, reassoc)) @@ -4617,6 +3822,9 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (hapd->conf->wpa && wpa_ie) { enum wpa_validate_result res; + if (check_sa_query(hapd, sta, reassoc)) + return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; + wpa_ie -= 2; wpa_ie_len += 2; if (sta->wpa_sm == NULL) @@ -4632,17 +3840,16 @@ static int 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); + 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; - 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 @@ -4674,7 +3881,7 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, sta->auth_alg == WLAN_AUTH_OPEN) { struct rsn_pmksa_cache_entry *sa; sa = wpa_auth_sta_get_pmksa(sta->wpa_sm); - if (!sa || sa->akmp != WPA_KEY_MGMT_SAE) { + if (!sa || !wpa_key_mgmt_sae(sa->akmp)) { wpa_printf(MSG_DEBUG, "SAE: No PMKSA cache entry found for " MACSTR, MAC2STR(sta->addr)); @@ -4692,10 +3899,10 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; } - if (hapd->conf->sae_pwe == 2 && + if (hapd->conf->sae_pwe == SAE_PWE_BOTH && sta->auth_alg == WLAN_AUTH_SAE && sta->sae && !sta->sae->h2e && - ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len, + 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", @@ -4707,9 +3914,9 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_OWE if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE && - elems.owe_dh) { - resp = owe_process_assoc_req(hapd, sta, elems.owe_dh, - elems.owe_dh_len); + elems->owe_dh) { + resp = owe_process_assoc_req(hapd, sta, elems->owe_dh, + elems->owe_dh_len); if (resp != WLAN_STATUS_SUCCESS) return resp; } @@ -4723,7 +3930,7 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, (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) { + elems->owe_dh) { sta->dpp_pfs = dpp_pfs_init( wpabuf_head(hapd->conf->dpp_netaccesskey), wpabuf_len(hapd->conf->dpp_netaccesskey)); @@ -4734,8 +3941,8 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, goto pfs_fail; } - if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh, - elems.owe_dh_len) < 0) { + if (dpp_pfs_process(sta->dpp_pfs, elems->owe_dh, + elems->owe_dh_len) < 0) { dpp_pfs_free(sta->dpp_pfs); sta->dpp_pfs = NULL; return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -4758,7 +3965,7 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #ifdef CONFIG_HS20 } else if (hapd->conf->osen) { - if (elems.osen == NULL) { + if (!elems->osen) { hostapd_logger( hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, @@ -4776,7 +3983,7 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm, - elems.osen - 2, elems.osen_len + 2) < 0) + elems->osen - 2, elems->osen_len + 2) < 0) return WLAN_STATUS_INVALID_IE; #endif /* CONFIG_HS20 */ } else @@ -4788,12 +3995,12 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_HS20 wpabuf_free(sta->hs20_ie); - if (elems.hs20 && elems.hs20_len > 4) { + if (elems->hs20 && elems->hs20_len > 4) { int release; - sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, - elems.hs20_len - 4); - release = ((elems.hs20[4] >> 4) & 0x0f) + 1; + 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) && hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { wpa_printf(MSG_DEBUG, @@ -4806,10 +4013,10 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } wpabuf_free(sta->roaming_consortium); - if (elems.roaming_cons_sel) + if (elems->roaming_cons_sel) sta->roaming_consortium = wpabuf_alloc_copy( - elems.roaming_cons_sel + 4, - elems.roaming_cons_sel_len - 4); + elems->roaming_cons_sel + 4, + elems->roaming_cons_sel_len - 4); else sta->roaming_consortium = NULL; #endif /* CONFIG_HS20 */ @@ -4817,16 +4024,16 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_FST wpabuf_free(sta->mb_ies); if (hapd->iface->fst) - sta->mb_ies = mb_ies_by_info(&elems.mb_ies); + sta->mb_ies = mb_ies_by_info(&elems->mb_ies); else sta->mb_ies = NULL; #endif /* CONFIG_FST */ #ifdef CONFIG_MBO - mbo_ap_check_sta_assoc(hapd, sta, &elems); + mbo_ap_check_sta_assoc(hapd, sta, elems); if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) && - elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) && + elems->mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) && hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { wpa_printf(MSG_INFO, "MBO: Reject WPA2 association without PMF"); @@ -4856,7 +4063,7 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, &tx_seg1_idx) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; - res = ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, + 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) { @@ -4875,18 +4082,18 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_FILS && CONFIG_OCV */ - ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes, - elems.supp_op_classes_len); + ap_copy_sta_supp_op_classes(sta, elems->supp_op_classes, + elems->supp_op_classes_len); if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) && - elems.rrm_enabled && - elems.rrm_enabled_len >= sizeof(sta->rrm_enabled_capa)) - os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled, + elems->rrm_enabled && + elems->rrm_enabled_len >= sizeof(sta->rrm_enabled_capa)) + os_memcpy(sta->rrm_enabled_capa, elems->rrm_enabled, sizeof(sta->rrm_enabled_capa)); - if (elems.power_capab) { - sta->min_tx_power = elems.power_capab[0]; - sta->max_tx_power = elems.power_capab[1]; + if (elems->power_capab) { + sta->min_tx_power = elems->power_capab[0]; + sta->max_tx_power = elems->power_capab[1]; sta->power_capab = 1; } else { sta->power_capab = 0; @@ -4896,6 +4103,22 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } +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; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Station sent an invalid association request"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, reassoc); +} + + static void send_deauth(struct hostapd_data *hapd, const u8 *addr, u16 reason_code) { @@ -4924,6 +4147,7 @@ static int add_associated_sta(struct hostapd_data *hapd, struct ieee80211_ht_capabilities ht_cap; struct ieee80211_vht_capabilities vht_cap; struct ieee80211_he_capabilities he_cap; + struct ieee80211_eht_capabilities eht_cap; int set = 1; /* @@ -4980,6 +4204,11 @@ static int add_associated_sta(struct hostapd_data *hapd, sta->he_capab_len); } #endif /* CONFIG_IEEE80211AX */ +#ifdef CONFIG_IEEE80211BE + if (sta->flags & WLAN_STA_EHT) + hostapd_get_eht_capab(hapd, sta->eht_capab, &eht_cap, + sta->eht_capab_len); +#endif /* CONFIG_IEEE80211BE */ /* * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags @@ -4993,6 +4222,8 @@ 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->flags & WLAN_STA_EHT ? &eht_cap : NULL, + sta->flags & WLAN_STA_EHT ? sta->eht_capab_len : 0, sta->he_6ghz_capab, sta->flags | WLAN_STA_ASSOC, sta->qosinfo, sta->vht_opmode, sta->p2p_ie ? 1 : 0, @@ -5043,6 +4274,15 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, if (sta && sta->dpp_pfs) buflen += 5 + sta->dpp_pfs->curve->prime_len; #endif /* CONFIG_DPP2 */ +#ifdef CONFIG_IEEE80211BE + if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { + buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP); + buflen += 3 + sizeof(struct ieee80211_eht_operation); + if (hapd->iconf->punct_bitmap) + buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE; + } +#endif /* CONFIG_IEEE80211BE */ + buf = os_zalloc(buflen); if (!buf) { res = WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -5151,13 +4391,14 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, 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_cca(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 */ - p = hostapd_eid_ext_capab(hapd, p); + p = hostapd_eid_ext_capab(hapd, p, false); p = hostapd_eid_bss_max_idle_period(hapd, p); if (sta && sta->qos_map_enabled) p = hostapd_eid_qos_map_set(hapd, p); @@ -5188,6 +4429,13 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, rsnxe_done: #endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_IEEE80211BE + if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { + p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP); + p = hostapd_eid_eht_operation(hapd, p); + } +#endif /* CONFIG_IEEE80211BE */ + #ifdef CONFIG_OWE if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS && @@ -6096,10 +5344,10 @@ static int handle_action(struct hostapd_data *hapd, pos = &mgmt->u.action.u.public_action.action; end = ((const u8 *) mgmt) + len; - gas_query_ap_rx(hapd->gas, mgmt->sa, - mgmt->u.action.category, - pos, end - pos, freq); - return 1; + if (gas_query_ap_rx(hapd->gas, mgmt->sa, + mgmt->u.action.category, + pos, end - pos, freq) == 0) + return 1; } #endif /* CONFIG_DPP */ if (hapd->public_action_cb) { @@ -6554,7 +5802,8 @@ static void handle_assoc_cb(struct hostapd_data *hapd, ieee802_1x_receive( hapd, mgmt->da, wpabuf_head(sta->pending_eapol_rx->buf), - wpabuf_len(sta->pending_eapol_rx->buf)); + wpabuf_len(sta->pending_eapol_rx->buf), + sta->pending_eapol_rx->encrypted); } wpabuf_free(sta->pending_eapol_rx->buf); os_free(sta->pending_eapol_rx); @@ -6657,6 +5906,19 @@ static void handle_action_cb(struct hostapd_data *hapd, return; } +#ifdef CONFIG_HS20 + if (ok && len >= IEEE80211_HDRLEN + 2 && + mgmt->u.action.category == WLAN_ACTION_WNM && + mgmt->u.action.u.vs_public_action.action == WNM_NOTIFICATION_REQ && + sta->hs20_deauth_on_ack) { + wpa_printf(MSG_DEBUG, "HS 2.0: Deauthenticate STA " MACSTR + " on acknowledging the WNM-Notification", + MAC2STR(sta->addr)); + ap_sta_session_timeout(hapd, sta, 0); + return; + } +#endif /* CONFIG_HS20 */ + if (len < 24 + 5 + sizeof(*report)) return; report = (const struct rrm_measurement_report_element *) @@ -6903,6 +6165,38 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, } +static u8 * hostapd_add_tpe_info(u8 *eid, u8 tx_pwr_count, + enum max_tx_pwr_interpretation tx_pwr_intrpn, + u8 tx_pwr_cat, u8 tx_pwr) +{ + int i; + + *eid++ = WLAN_EID_TRANSMIT_POWER_ENVELOPE; /* Element ID */ + *eid++ = 2 + tx_pwr_count; /* Length */ + + /* + * Transmit Power Information field + * bits 0-2 : Maximum Transmit Power Count + * bits 3-5 : Maximum Transmit Power Interpretation + * bits 6-7 : Maximum Transmit Power Category + */ + *eid++ = tx_pwr_count | (tx_pwr_intrpn << 3) | (tx_pwr_cat << 6); + + /* Maximum Transmit Power field */ + for (i = 0; i <= tx_pwr_count; i++) + *eid++ = tx_pwr; + + return eid; +} + + +/* + * TODO: Extract power limits from channel data after 6G regulatory + * support. + */ +#define REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT (-1) /* dBm/MHz */ +#define REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT 5 /* dBm/MHz */ + u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) { struct hostapd_iface *iface = hapd->iface; @@ -6927,8 +6221,45 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) if (i == mode->num_channels) return eid; +#ifdef CONFIG_IEEE80211AX + /* IEEE Std 802.11ax-2021, Annex E.2.7 (6 GHz band in the United + * States): An AP that is an Indoor Access Point per regulatory rules + * shall send at least two Transmit Power Envelope elements in Beacon + * and Probe Response frames as follows: + * - Maximum Transmit Power Category subfield = Default; + * Unit interpretation = Regulatory client EIRP PSD + * - Maximum Transmit Power Category subfield = Subordinate Device; + * Unit interpretation = Regulatory client EIRP PSD + */ + if (is_6ghz_op_class(iconf->op_class)) { + enum max_tx_pwr_interpretation tx_pwr_intrpn; + + /* Same Maximum Transmit Power for all 20 MHz bands */ + tx_pwr_count = 0; + tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD; + + /* Default Transmit Power Envelope for Global Operating Class */ + tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2; + eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn, + REG_DEFAULT_CLIENT, tx_pwr); + + /* Indoor Access Point must include an additional TPE for + * subordinate devices */ + if (iconf->he_6ghz_reg_pwr_type == HE_6GHZ_INDOOR_AP) { + /* TODO: Extract PSD limits from channel data */ + tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2; + eid = hostapd_add_tpe_info(eid, tx_pwr_count, + tx_pwr_intrpn, + REG_SUBORDINATE_CLIENT, + tx_pwr); + } + + return eid; + } +#endif /* CONFIG_IEEE80211AX */ + switch (hostapd_get_oper_chwidth(iconf)) { - case CHANWIDTH_USE_HT: + case CONF_OPER_CHWIDTH_USE_HT: if (iconf->secondary_channel == 0) { /* Max Transmit Power count = 0 (20 MHz) */ tx_pwr_count = 0; @@ -6937,12 +6268,12 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) tx_pwr_count = 1; } break; - case CHANWIDTH_80MHZ: + case CONF_OPER_CHWIDTH_80MHZ: /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */ tx_pwr_count = 2; break; - case CHANWIDTH_80P80MHZ: - case CHANWIDTH_160MHZ: + case CONF_OPER_CHWIDTH_80P80MHZ: + case CONF_OPER_CHWIDTH_160MHZ: /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */ tx_pwr_count = 3; break; @@ -6999,19 +6330,9 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) 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; + return hostapd_add_tpe_info(eid, tx_pwr_count, LOCAL_EIRP, + 0 /* Reserved for bands other than 6 GHz */, + tx_pwr); } @@ -7022,7 +6343,8 @@ u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid) if (!hapd->cs_freq_params.channel || (!hapd->cs_freq_params.vht_enabled && - !hapd->cs_freq_params.he_enabled)) + !hapd->cs_freq_params.he_enabled && + !hapd->cs_freq_params.eht_enabled)) return eid; /* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */ @@ -7354,7 +6676,7 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd, break; *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN; - os_memcpy(eid, bss->conf->bssid, ETH_ALEN); + os_memcpy(eid, bss->own_addr, ETH_ALEN); eid += ETH_ALEN; os_memcpy(eid, &bss->conf->ssid.short_ssid, 4); eid += 4; @@ -7362,6 +6684,14 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd, reporting_hapd->conf->ssid.short_ssid) bss_param |= RNR_BSS_PARAM_SAME_SSID; + if (iface->conf->mbssid != MBSSID_DISABLED && + iface->num_bss > 1) { + bss_param |= RNR_BSS_PARAM_MULTIPLE_BSSID; + if (i == 0) + bss_param |= + RNR_BSS_PARAM_TRANSMITTED_BSSID; + } + if (is_6ghz_op_class(hapd->iconf->op_class) && bss->conf->unsol_bcast_probe_resp_interval) bss_param |= @@ -7450,4 +6780,322 @@ u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type) return eid; } + +static bool mbssid_known_bss(unsigned int i, const u8 *known_bss, + size_t known_bss_len) +{ + if (!known_bss || known_bss_len <= i / 8) + return false; + known_bss = &known_bss[i / 8]; + return *known_bss & (u8) (BIT(i % 8)); +} + + +static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd, + u32 frame_type, size_t *bss_index, + const u8 *known_bss, + size_t known_bss_len) +{ + struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd); + size_t len = 3, i; + + for (i = *bss_index; i < hapd->iface->num_bss; i++) { + struct hostapd_data *bss = hapd->iface->bss[i]; + const u8 *auth, *rsn = NULL, *rsnx = NULL; + size_t nontx_profile_len, auth_len; + u8 ie_count = 0; + + if (!bss || !bss->conf || !bss->started || + mbssid_known_bss(i, known_bss, known_bss_len)) + continue; + + /* + * Sublement ID: 1 octet + * Length: 1 octet + * Nontransmitted capabilities: 4 octets + * SSID element: 2 + variable + * Multiple BSSID Index Element: 3 octets (+2 octets in beacons) + * Fixed length = 1 + 1 + 4 + 2 + 3 = 11 + */ + nontx_profile_len = 11 + bss->conf->ssid.ssid_len; + + if (frame_type == WLAN_FC_STYPE_BEACON) + nontx_profile_len += 2; + + auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len); + if (auth) { + rsn = get_ie(auth, auth_len, WLAN_EID_RSN); + if (rsn) + nontx_profile_len += 2 + rsn[1]; + + rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX); + if (rsnx) + nontx_profile_len += 2 + rsnx[1]; + } + if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN)) + ie_count++; + if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX)) + ie_count++; + if (bss->conf->xrates_supported) + nontx_profile_len += 8; + else if (hapd->conf->xrates_supported) + ie_count++; + if (ie_count) + nontx_profile_len += 4 + ie_count; + + if (len + nontx_profile_len > 255) + break; + + len += nontx_profile_len; + } + + *bss_index = i; + return len; +} + + +size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type, + u8 *elem_count, const u8 *known_bss, + size_t known_bss_len) +{ + size_t len = 0, bss_index = 1; + + if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 || + (frame_type != WLAN_FC_STYPE_BEACON && + frame_type != WLAN_FC_STYPE_PROBE_RESP)) + return 0; + + if (frame_type == WLAN_FC_STYPE_BEACON) { + if (!elem_count) { + wpa_printf(MSG_INFO, + "MBSSID: Insufficient data for Beacon frames"); + return 0; + } + *elem_count = 0; + } + + while (bss_index < hapd->iface->num_bss) { + len += hostapd_eid_mbssid_elem_len(hapd, frame_type, + &bss_index, known_bss, + known_bss_len); + + if (frame_type == WLAN_FC_STYPE_BEACON) + *elem_count += 1; + } + return len; +} + + +static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end, + u32 frame_type, u8 max_bssid_indicator, + size_t *bss_index, u8 elem_count, + const u8 *known_bss, size_t known_bss_len) +{ + struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd); + size_t i; + u8 *eid_len_offset, *max_bssid_indicator_offset; + + *eid++ = WLAN_EID_MULTIPLE_BSSID; + eid_len_offset = eid++; + max_bssid_indicator_offset = eid++; + + for (i = *bss_index; i < hapd->iface->num_bss; i++) { + struct hostapd_data *bss = hapd->iface->bss[i]; + struct hostapd_bss_config *conf; + u8 *eid_len_pos, *nontx_bss_start = eid; + const u8 *auth, *rsn = NULL, *rsnx = NULL; + u8 ie_count = 0, non_inherit_ie[3]; + size_t auth_len = 0; + u16 capab_info; + + if (!bss || !bss->conf || !bss->started || + mbssid_known_bss(i, known_bss, known_bss_len)) + continue; + conf = bss->conf; + + *eid++ = WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE; + eid_len_pos = eid++; + + capab_info = hostapd_own_capab_info(bss); + *eid++ = WLAN_EID_NONTRANSMITTED_BSSID_CAPA; + *eid++ = sizeof(capab_info); + WPA_PUT_LE16(eid, capab_info); + eid += sizeof(capab_info); + + *eid++ = WLAN_EID_SSID; + *eid++ = conf->ssid.ssid_len; + os_memcpy(eid, conf->ssid.ssid, conf->ssid.ssid_len); + eid += conf->ssid.ssid_len; + + *eid++ = WLAN_EID_MULTIPLE_BSSID_INDEX; + if (frame_type == WLAN_FC_STYPE_BEACON) { + *eid++ = 3; + *eid++ = i; /* BSSID Index */ + if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && + (conf->dtim_period % elem_count)) + conf->dtim_period = elem_count; + *eid++ = conf->dtim_period; + *eid++ = 0xFF; /* DTIM Count */ + } else { + /* Probe Request frame does not include DTIM Period and + * DTIM Count fields. */ + *eid++ = 1; + *eid++ = i; /* BSSID Index */ + } + + auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len); + if (auth) { + rsn = get_ie(auth, auth_len, WLAN_EID_RSN); + if (rsn) { + os_memcpy(eid, rsn, 2 + rsn[1]); + eid += 2 + rsn[1]; + } + + rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX); + if (rsnx) { + os_memcpy(eid, rsnx, 2 + rsnx[1]); + eid += 2 + rsnx[1]; + } + } + if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN)) + non_inherit_ie[ie_count++] = WLAN_EID_RSN; + if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX)) + non_inherit_ie[ie_count++] = WLAN_EID_RSNX; + if (hapd->conf->xrates_supported && + !bss->conf->xrates_supported) + non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES; + if (ie_count) { + *eid++ = WLAN_EID_EXTENSION; + *eid++ = 2 + ie_count; + *eid++ = WLAN_EID_EXT_NON_INHERITANCE; + *eid++ = ie_count; + os_memcpy(eid, non_inherit_ie, ie_count); + eid += ie_count; + } + + *eid_len_pos = (eid - eid_len_pos) - 1; + + if (((eid - eid_len_offset) - 1) > 255) { + eid = nontx_bss_start; + break; + } + } + + *bss_index = i; + *max_bssid_indicator_offset = max_bssid_indicator; + if (*max_bssid_indicator_offset < 1) + *max_bssid_indicator_offset = 1; + *eid_len_offset = (eid - eid_len_offset) - 1; + return eid; +} + + +u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end, + unsigned int frame_stype, u8 elem_count, + u8 **elem_offset, + const u8 *known_bss, size_t known_bss_len) +{ + size_t bss_index = 1; + u8 elem_index = 0; + + if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 || + (frame_stype != WLAN_FC_STYPE_BEACON && + frame_stype != WLAN_FC_STYPE_PROBE_RESP)) + return eid; + + if (frame_stype == WLAN_FC_STYPE_BEACON && !elem_offset) { + wpa_printf(MSG_INFO, + "MBSSID: Insufficient data for Beacon frames"); + return eid; + } + + while (bss_index < hapd->iface->num_bss) { + if (frame_stype == WLAN_FC_STYPE_BEACON) { + if (elem_index == elem_count) { + wpa_printf(MSG_WARNING, + "MBSSID: Larger number of elements than there is room in the provided array"); + break; + } + + elem_offset[elem_index] = eid; + elem_index = elem_index + 1; + } + eid = hostapd_eid_mbssid_elem(hapd, eid, end, frame_stype, + hostapd_max_bssid_indicator(hapd), + &bss_index, elem_count, + known_bss, known_bss_len); + } + + return eid; +} + + +static void punct_update_legacy_bw_80(u8 bitmap, u8 pri_chan, u8 *seg0) +{ + u8 first_chan = *seg0 - 6, sec_chan; + + switch (bitmap) { + case 0x6: + *seg0 = 0; + return; + case 0x8: + case 0x4: + case 0x2: + case 0x1: + case 0xC: + case 0x3: + if (pri_chan < *seg0) + *seg0 -= 4; + else + *seg0 += 4; + break; + } + + if (pri_chan < *seg0) + sec_chan = pri_chan + 4; + else + sec_chan = pri_chan - 4; + + if (bitmap & BIT((sec_chan - first_chan) / 4)) + *seg0 = 0; +} + + +static void punct_update_legacy_bw_160(u8 bitmap, u8 pri, + enum oper_chan_width *width, u8 *seg0) +{ + if (pri < *seg0) { + *seg0 -= 8; + if (bitmap & 0x0F) { + *width = 0; + punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0); + } + } else { + *seg0 += 8; + if (bitmap & 0xF0) { + *width = 0; + punct_update_legacy_bw_80((bitmap & 0xF0) >> 4, pri, + seg0); + } + } +} + + +void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width, + u8 *seg0, u8 *seg1) +{ + if (*width == CONF_OPER_CHWIDTH_80MHZ && (bitmap & 0xF)) { + *width = CONF_OPER_CHWIDTH_USE_HT; + punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0); + } + + if (*width == CONF_OPER_CHWIDTH_160MHZ && (bitmap & 0xFF)) { + *width = CONF_OPER_CHWIDTH_80MHZ; + *seg1 = 0; + punct_update_legacy_bw_160(bitmap & 0xFF, pri, width, seg0); + } + + /* TODO: 320 MHz */ +} + #endif /* CONFIG_NATIVE_WINDOWS */ |