blob: 523e0a32219ee3522a2e289c552a90713232b2d0 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * hostapd / IEEE 802.11 Management
Dmitry Shmidt29333592017-01-09 12:27:11 -08003 * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
8
9#include "utils/includes.h"
10
11#ifndef CONFIG_NATIVE_WINDOWS
12
13#include "utils/common.h"
14#include "utils/eloop.h"
15#include "crypto/crypto.h"
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080016#include "crypto/sha256.h"
Dmitry Shmidtd2986c22017-10-23 14:22:09 -070017#include "crypto/sha384.h"
18#include "crypto/sha512.h"
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080019#include "crypto/random.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070020#include "common/ieee802_11_defs.h"
21#include "common/ieee802_11_common.h"
22#include "common/wpa_ctrl.h"
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080023#include "common/sae.h"
Hai Shalom021b0b52019-04-10 11:17:58 -070024#include "common/dpp.h"
Hai Shalom74f70d42019-02-11 14:42:39 -080025#include "common/ocv.h"
Hai Shalom81f62d82019-07-22 12:10:00 -070026#include "common/wpa_common.h"
Hai Shalom899fcc72020-10-19 14:38:18 -070027#include "common/wpa_ctrl.h"
Hai Shalom60840252021-02-19 19:02:11 -080028#include "common/ptksa_cache.h"
Sunil Ravi79e6c4f2025-01-04 00:47:06 +000029#include "common/nan_de.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070030#include "radius/radius.h"
31#include "radius/radius_client.h"
32#include "p2p/p2p.h"
33#include "wps/wps.h"
Dmitry Shmidtd80a4012015-11-05 16:35:40 -080034#include "fst/fst.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070035#include "hostapd.h"
36#include "beacon.h"
37#include "ieee802_11_auth.h"
38#include "sta_info.h"
39#include "ieee802_1x.h"
40#include "wpa_auth.h"
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -080041#include "pmksa_cache_auth.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070042#include "wmm.h"
43#include "ap_list.h"
44#include "accounting.h"
45#include "ap_config.h"
46#include "ap_mlme.h"
47#include "p2p_hostapd.h"
48#include "ap_drv_ops.h"
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080049#include "wnm_ap.h"
Dmitry Shmidtd80a4012015-11-05 16:35:40 -080050#include "hw_features.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070051#include "ieee802_11.h"
Dmitry Shmidtf21452a2014-02-26 10:55:25 -080052#include "dfs.h"
Dmitry Shmidt57c2d392016-02-23 13:40:19 -080053#include "mbo_ap.h"
Dmitry Shmidt849734c2016-05-27 09:59:01 -070054#include "rrm.h"
Dmitry Shmidtaca489e2016-09-28 15:44:14 -070055#include "taxonomy.h"
Dmitry Shmidtebd93af2017-02-21 13:40:44 -080056#include "fils_hlp.h"
Dmitry Shmidtd2986c22017-10-23 14:22:09 -070057#include "dpp_hostapd.h"
58#include "gas_query_ap.h"
Sunil Ravi77d572f2023-01-17 23:58:31 +000059#include "comeback_token.h"
Sunil Ravib0ac25f2024-07-12 01:42:03 +000060#include "nan_usd_ap.h"
Sunil Ravi77d572f2023-01-17 23:58:31 +000061#include "pasn/pasn_common.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070062
63
Dmitry Shmidtd2986c22017-10-23 14:22:09 -070064#ifdef CONFIG_FILS
65static struct wpabuf *
66prepare_auth_resp_fils(struct hostapd_data *hapd,
67 struct sta_info *sta, u16 *resp,
68 struct rsn_pmksa_cache_entry *pmksa,
69 struct wpabuf *erp_resp,
70 const u8 *msk, size_t msk_len,
71 int *is_pub);
72#endif /* CONFIG_FILS */
Hai Shalom60840252021-02-19 19:02:11 -080073
74#ifdef CONFIG_PASN
Hai Shalom60840252021-02-19 19:02:11 -080075#ifdef CONFIG_FILS
76
77static void pasn_fils_auth_resp(struct hostapd_data *hapd,
78 struct sta_info *sta, u16 status,
79 struct wpabuf *erp_resp,
80 const u8 *msk, size_t msk_len);
81
82#endif /* CONFIG_FILS */
83#endif /* CONFIG_PASN */
84
Hai Shalom021b0b52019-04-10 11:17:58 -070085static void handle_auth(struct hostapd_data *hapd,
86 const struct ieee80211_mgmt *mgmt, size_t len,
87 int rssi, int from_queue);
Sunil Ravi2a14cf12023-11-21 00:54:38 +000088static int add_associated_sta(struct hostapd_data *hapd,
89 struct sta_info *sta, int reassoc);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -070090
Hai Shalom74f70d42019-02-11 14:42:39 -080091
Sunil Ravi99c035e2024-07-12 01:42:03 +000092static u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid, size_t len)
Hai Shalom74f70d42019-02-11 14:42:39 -080093{
Sunil Ravi99c035e2024-07-12 01:42:03 +000094 struct multi_ap_params multi_ap = { 0 };
Hai Shalom74f70d42019-02-11 14:42:39 -080095
96 if (!hapd->conf->multi_ap)
97 return eid;
Sunil Raviaf399a82024-05-05 20:56:55 +000098
Sunil Ravi99c035e2024-07-12 01:42:03 +000099 if (hapd->conf->multi_ap & BACKHAUL_BSS)
100 multi_ap.capability |= MULTI_AP_BACKHAUL_BSS;
101 if (hapd->conf->multi_ap & FRONTHAUL_BSS)
102 multi_ap.capability |= MULTI_AP_FRONTHAUL_BSS;
103
104 if (hapd->conf->multi_ap_client_disallow &
105 PROFILE1_CLIENT_ASSOC_DISALLOW)
106 multi_ap.capability |=
107 MULTI_AP_PROFILE1_BACKHAUL_STA_DISALLOWED;
108 if (hapd->conf->multi_ap_client_disallow &
109 PROFILE2_CLIENT_ASSOC_DISALLOW)
110 multi_ap.capability |=
111 MULTI_AP_PROFILE2_BACKHAUL_STA_DISALLOWED;
112
113 multi_ap.profile = hapd->conf->multi_ap_profile;
114 multi_ap.vlanid = hapd->conf->multi_ap_vlanid;
115
116 return eid + add_multi_ap_ie(eid, len, &multi_ap);
Hai Shalom74f70d42019-02-11 14:42:39 -0800117}
118
119
Sunil Ravi876a49b2025-02-03 19:18:32 +0000120static size_t hostapd_supp_rates(struct hostapd_data *hapd, u8 *buf)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700121{
Sunil Ravi876a49b2025-02-03 19:18:32 +0000122 u8 *pos = buf;
123 int i;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700124
Sunil Ravi876a49b2025-02-03 19:18:32 +0000125 if (!hapd->iface->current_rates)
126 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700127
Sunil Ravi876a49b2025-02-03 19:18:32 +0000128 for (i = 0; i < hapd->iface->num_rates; i++) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700129 *pos = hapd->iface->current_rates[i].rate / 5;
130 if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
131 *pos |= 0x80;
132 pos++;
133 }
134
Sunil Ravi876a49b2025-02-03 19:18:32 +0000135 if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700136 *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800137
Sunil Ravi876a49b2025-02-03 19:18:32 +0000138 if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800139 *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700140
Sunil Ravi77d572f2023-01-17 23:58:31 +0000141#ifdef CONFIG_IEEE80211AX
Sunil Ravi876a49b2025-02-03 19:18:32 +0000142 if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
Sunil Ravi77d572f2023-01-17 23:58:31 +0000143 *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
Sunil Ravi77d572f2023-01-17 23:58:31 +0000144#endif /* CONFIG_IEEE80211AX */
145
Sunil Ravi876a49b2025-02-03 19:18:32 +0000146#ifdef CONFIG_SAE
147 if ((hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
148 hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
149 hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
150 wpa_key_mgmt_only_sae(hapd->conf->wpa_key_mgmt))
Hai Shalomc3565922019-10-28 11:58:20 -0700151 *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
Sunil Ravi876a49b2025-02-03 19:18:32 +0000152#endif /* CONFIG_SAE */
153
154 return pos - buf;
155}
156
157
158u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
159{
160 u8 *pos = eid;
161 u8 buf[100];
162 size_t len;
163
164 len = hostapd_supp_rates(hapd, buf);
165 if (len == 0)
166 return eid;
167 /* Only up to first eight values in this element */
168 if (len > 8)
169 len = 8;
170
171 *pos++ = WLAN_EID_SUPP_RATES;
172 *pos++ = len;
173 os_memcpy(pos, buf, len);
174 pos += len;
Hai Shalomc3565922019-10-28 11:58:20 -0700175
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700176 return pos;
177}
178
179
180u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
181{
182 u8 *pos = eid;
Sunil Ravi876a49b2025-02-03 19:18:32 +0000183 u8 buf[100];
184 size_t len;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700185
Sunil Ravi876a49b2025-02-03 19:18:32 +0000186 len = hostapd_supp_rates(hapd, buf);
187 /* Starting from the 9th value for this element */
188 if (len <= 8)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700189 return eid;
190
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700191 *pos++ = WLAN_EID_EXT_SUPP_RATES;
Sunil Ravi876a49b2025-02-03 19:18:32 +0000192 *pos++ = len - 8;
193 os_memcpy(pos, &buf[8], len - 8);
194 pos += len - 8;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700195
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700196 return pos;
197}
198
199
Hai Shalomfdcde762020-04-02 11:19:20 -0700200u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
201 size_t len)
202{
203 size_t i;
204
205 for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
206 if (hapd->conf->radio_measurements[i])
207 break;
208 }
209
210 if (i == RRM_CAPABILITIES_IE_LEN || len < 2 + RRM_CAPABILITIES_IE_LEN)
211 return eid;
212
213 *eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
214 *eid++ = RRM_CAPABILITIES_IE_LEN;
215 os_memcpy(eid, hapd->conf->radio_measurements, RRM_CAPABILITIES_IE_LEN);
216
217 return eid + RRM_CAPABILITIES_IE_LEN;
218}
219
220
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -0700221u16 hostapd_own_capab_info(struct hostapd_data *hapd)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700222{
223 int capab = WLAN_CAPABILITY_ESS;
Hai Shalomfdcde762020-04-02 11:19:20 -0700224 int privacy = 0;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800225 int dfs;
Dmitry Shmidt849734c2016-05-27 09:59:01 -0700226 int i;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800227
228 /* Check if any of configured channels require DFS */
229 dfs = hostapd_is_dfs_required(hapd->iface);
230 if (dfs < 0) {
231 wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
232 dfs);
233 dfs = 0;
234 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700235
236 if (hapd->iface->num_sta_no_short_preamble == 0 &&
237 hapd->iconf->preamble == SHORT_PREAMBLE)
238 capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
239
Hai Shalomfdcde762020-04-02 11:19:20 -0700240#ifdef CONFIG_WEP
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700241 privacy = hapd->conf->ssid.wep.keys_set;
242
243 if (hapd->conf->ieee802_1x &&
244 (hapd->conf->default_wep_key_len ||
245 hapd->conf->individual_wep_key_len))
246 privacy = 1;
Hai Shalomfdcde762020-04-02 11:19:20 -0700247#endif /* CONFIG_WEP */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700248
249 if (hapd->conf->wpa)
250 privacy = 1;
251
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700252 if (privacy)
253 capab |= WLAN_CAPABILITY_PRIVACY;
254
255 if (hapd->iface->current_mode &&
256 hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
257 hapd->iface->num_sta_no_short_slot_time == 0)
258 capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
259
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800260 /*
261 * Currently, Spectrum Management capability bit is set when directly
262 * requested in configuration by spectrum_mgmt_required or when AP is
263 * running on DFS channel.
264 * TODO: Also consider driver support for TPC to set Spectrum Mgmt bit
265 */
266 if (hapd->iface->current_mode &&
267 hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
268 (hapd->iconf->spectrum_mgmt_required || dfs))
269 capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
270
Dmitry Shmidt849734c2016-05-27 09:59:01 -0700271 for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
272 if (hapd->conf->radio_measurements[i]) {
273 capab |= IEEE80211_CAP_RRM;
274 break;
275 }
276 }
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800277
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700278 return capab;
279}
280
281
Hai Shalomfdcde762020-04-02 11:19:20 -0700282#ifdef CONFIG_WEP
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800283#ifndef CONFIG_NO_RC4
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700284static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
285 u16 auth_transaction, const u8 *challenge,
286 int iswep)
287{
288 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
289 HOSTAPD_LEVEL_DEBUG,
290 "authentication (shared key, transaction %d)",
291 auth_transaction);
292
293 if (auth_transaction == 1) {
294 if (!sta->challenge) {
295 /* Generate a pseudo-random challenge */
296 u8 key[8];
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800297
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700298 sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN);
299 if (sta->challenge == NULL)
300 return WLAN_STATUS_UNSPECIFIED_FAILURE;
301
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800302 if (os_get_random(key, sizeof(key)) < 0) {
303 os_free(sta->challenge);
304 sta->challenge = NULL;
305 return WLAN_STATUS_UNSPECIFIED_FAILURE;
306 }
307
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700308 rc4_skip(key, sizeof(key), 0,
309 sta->challenge, WLAN_AUTH_CHALLENGE_LEN);
310 }
311 return 0;
312 }
313
314 if (auth_transaction != 3)
315 return WLAN_STATUS_UNSPECIFIED_FAILURE;
316
317 /* Transaction 3 */
318 if (!iswep || !sta->challenge || !challenge ||
Dmitry Shmidtc2817022014-07-02 10:32:10 -0700319 os_memcmp_const(sta->challenge, challenge,
320 WLAN_AUTH_CHALLENGE_LEN)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700321 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
322 HOSTAPD_LEVEL_INFO,
323 "shared key authentication - invalid "
324 "challenge-response");
325 return WLAN_STATUS_CHALLENGE_FAIL;
326 }
327
328 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
329 HOSTAPD_LEVEL_DEBUG,
330 "authentication OK (shared key)");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700331 sta->flags |= WLAN_STA_AUTH;
332 wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700333 os_free(sta->challenge);
334 sta->challenge = NULL;
335
336 return 0;
337}
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800338#endif /* CONFIG_NO_RC4 */
Hai Shalomfdcde762020-04-02 11:19:20 -0700339#endif /* CONFIG_WEP */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700340
341
Hai Shalomfdcde762020-04-02 11:19:20 -0700342static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
Sunil Ravi7f769292024-07-23 22:21:32 +0000343 const u8 *dst,
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800344 u16 auth_alg, u16 auth_transaction, u16 resp,
Roshan Pius3a1667e2018-07-03 15:17:14 -0700345 const u8 *ies, size_t ies_len, const char *dbg)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700346{
347 struct ieee80211_mgmt *reply;
348 u8 *buf;
349 size_t rlen;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800350 int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000351 const u8 *sa = hapd->own_addr;
352 struct wpabuf *ml_resp = NULL;
353
354#ifdef CONFIG_IEEE80211BE
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000355 if (ap_sta_is_mld(hapd, sta)) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000356 ml_resp = hostapd_ml_auth_resp(hapd);
357 if (!ml_resp)
358 return -1;
359 }
360#endif /* CONFIG_IEEE80211BE */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700361
362 rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000363 if (ml_resp)
364 rlen += wpabuf_len(ml_resp);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700365 buf = os_zalloc(rlen);
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000366 if (!buf) {
367 wpabuf_free(ml_resp);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800368 return -1;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000369 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700370
371 reply = (struct ieee80211_mgmt *) buf;
372 reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
373 WLAN_FC_STYPE_AUTH);
374 os_memcpy(reply->da, dst, ETH_ALEN);
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000375 os_memcpy(reply->sa, sa, ETH_ALEN);
Sunil Ravi7f769292024-07-23 22:21:32 +0000376 os_memcpy(reply->bssid, sa, ETH_ALEN);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700377
378 reply->u.auth.auth_alg = host_to_le16(auth_alg);
379 reply->u.auth.auth_transaction = host_to_le16(auth_transaction);
380 reply->u.auth.status_code = host_to_le16(resp);
381
382 if (ies && ies_len)
383 os_memcpy(reply->u.auth.variable, ies, ies_len);
384
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000385#ifdef CONFIG_IEEE80211BE
386 if (ml_resp)
387 os_memcpy(reply->u.auth.variable + ies_len,
388 wpabuf_head(ml_resp), wpabuf_len(ml_resp));
389
390 wpabuf_free(ml_resp);
391#endif /* CONFIG_IEEE80211BE */
392
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700393 wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR
Roshan Pius3a1667e2018-07-03 15:17:14 -0700394 " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)",
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700395 MAC2STR(dst), auth_alg, auth_transaction,
Roshan Pius3a1667e2018-07-03 15:17:14 -0700396 resp, (unsigned long) ies_len, dbg);
Hai Shalomfdcde762020-04-02 11:19:20 -0700397#ifdef CONFIG_TESTING_OPTIONS
398#ifdef CONFIG_SAE
399 if (hapd->conf->sae_confirm_immediate == 2 &&
400 auth_alg == WLAN_AUTH_SAE) {
401 if (auth_transaction == 1 && sta &&
402 (resp == WLAN_STATUS_SUCCESS ||
Hai Shalom899fcc72020-10-19 14:38:18 -0700403 resp == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
404 resp == WLAN_STATUS_SAE_PK)) {
Hai Shalomfdcde762020-04-02 11:19:20 -0700405 wpa_printf(MSG_DEBUG,
406 "TESTING: Postpone SAE Commit transmission until Confirm is ready");
407 os_free(sta->sae_postponed_commit);
408 sta->sae_postponed_commit = buf;
409 sta->sae_postponed_commit_len = rlen;
410 return WLAN_STATUS_SUCCESS;
411 }
412
413 if (auth_transaction == 2 && sta && sta->sae_postponed_commit) {
414 wpa_printf(MSG_DEBUG,
415 "TESTING: Send postponed SAE Commit first, immediately followed by SAE Confirm");
416 if (hostapd_drv_send_mlme(hapd,
417 sta->sae_postponed_commit,
418 sta->sae_postponed_commit_len,
419 0, NULL, 0, 0) < 0)
420 wpa_printf(MSG_INFO, "send_auth_reply: send failed");
421 os_free(sta->sae_postponed_commit);
422 sta->sae_postponed_commit = NULL;
423 sta->sae_postponed_commit_len = 0;
424 }
425 }
426#endif /* CONFIG_SAE */
427#endif /* CONFIG_TESTING_OPTIONS */
428 if (hostapd_drv_send_mlme(hapd, reply, rlen, 0, NULL, 0, 0) < 0)
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800429 wpa_printf(MSG_INFO, "send_auth_reply: send failed");
430 else
431 reply_res = WLAN_STATUS_SUCCESS;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700432
433 os_free(buf);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800434
435 return reply_res;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700436}
437
438
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -0800439#ifdef CONFIG_IEEE80211R_AP
Sunil Ravi7f769292024-07-23 22:21:32 +0000440static void handle_auth_ft_finish(void *ctx, const u8 *dst,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700441 u16 auth_transaction, u16 status,
442 const u8 *ies, size_t ies_len)
443{
444 struct hostapd_data *hapd = ctx;
445 struct sta_info *sta;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800446 int reply_res;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700447
Sunil Ravi7f769292024-07-23 22:21:32 +0000448 reply_res = send_auth_reply(hapd, NULL, dst, WLAN_AUTH_FT,
Roshan Pius3a1667e2018-07-03 15:17:14 -0700449 auth_transaction, status, ies, ies_len,
450 "auth-ft-finish");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700451
452 sta = ap_get_sta(hapd, dst);
453 if (sta == NULL)
454 return;
455
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800456 if (sta->added_unassoc && (reply_res != WLAN_STATUS_SUCCESS ||
457 status != WLAN_STATUS_SUCCESS)) {
458 hostapd_drv_sta_remove(hapd, sta->addr);
459 sta->added_unassoc = 0;
460 return;
461 }
462
463 if (status != WLAN_STATUS_SUCCESS)
464 return;
465
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700466 hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
467 HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
468 sta->flags |= WLAN_STA_AUTH;
469 mlme_authenticate_indication(hapd, sta);
470}
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -0800471#endif /* CONFIG_IEEE80211R_AP */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700472
473
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800474#ifdef CONFIG_SAE
475
Roshan Pius3a1667e2018-07-03 15:17:14 -0700476static void sae_set_state(struct sta_info *sta, enum sae_state state,
477 const char *reason)
478{
479 wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)",
480 sae_state_txt(sta->sae->state), sae_state_txt(state),
481 MAC2STR(sta->addr), reason);
482 sta->sae->state = state;
483}
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800484
485
Sunil Ravi876a49b2025-02-03 19:18:32 +0000486static bool in_mac_addr_list(const u8 *list, unsigned int num, const u8 *addr)
487{
488 unsigned int i;
489
490 for (i = 0; list && i < num; i++) {
491 if (ether_addr_equal(&list[i * ETH_ALEN], addr))
492 return true;
493 }
494
495 return false;
496}
497
498
499static struct sae_password_entry *
500sae_password_find_pw(struct hostapd_data *hapd, struct sta_info *sta)
501{
502 struct sae_password_entry *pw = NULL;
503
504 if (!sta->sae || !sta->sae->tmp || !sta->sae->tmp->used_pw)
505 return NULL;
506
507
508 for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
509 if (pw == sta->sae->tmp->used_pw)
510 return pw;
511 }
512
513 return NULL;
514}
515
516
517static bool is_other_sae_password(struct hostapd_data *hapd,
518 struct sta_info *sta,
519 struct sae_password_entry *used_pw)
520{
521 struct sae_password_entry *pw;
522
523 for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
524 if (pw == used_pw ||
525 pw->identifier ||
526 !is_broadcast_ether_addr(pw->peer_addr))
527 continue;
528
529 if (in_mac_addr_list(pw->success_mac,
530 pw->num_success_mac,
531 sta->addr))
532 return true;
533
534 if (!in_mac_addr_list(pw->fail_mac, pw->num_fail_mac,
535 sta->addr))
536 return true;
537 }
538
539 return false;
540}
541
542
543static bool has_sae_success_seen(struct hostapd_data *hapd,
544 struct sta_info *sta)
545{
546 struct sae_password_entry *pw;
547
548 for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
549 if (pw->identifier ||
550 !is_broadcast_ether_addr(pw->peer_addr))
551 continue;
552
553 if (in_mac_addr_list(pw->success_mac,
554 pw->num_success_mac,
555 sta->addr))
556 return true;
557 }
558
559 return false;
560}
561
562
563static void sae_password_track_success(struct hostapd_data *hapd,
564 struct sta_info *sta)
565{
566 struct sae_password_entry *pw;
567
568 if (!hapd->conf->sae_track_password)
569 return;
570
571 pw = sae_password_find_pw(hapd, sta);
572 if (!pw)
573 return;
574
575 if (in_mac_addr_list(pw->success_mac,
576 pw->num_success_mac,
577 sta->addr))
578 return;
579
580 if (!pw->success_mac) {
581 pw->success_mac = os_zalloc(hapd->conf->sae_track_password *
582 ETH_ALEN);
583 if (!pw->success_mac)
584 return;
585 pw->num_success_mac = hapd->conf->sae_track_password;
586 }
587
588 os_memcpy(&pw->success_mac[pw->next_success_mac * ETH_ALEN], sta->addr,
589 ETH_ALEN);
590 pw->next_success_mac = (pw->next_success_mac + 1) % pw->num_success_mac;
591}
592
593
594static bool sae_password_track_fail(struct hostapd_data *hapd,
595 struct sta_info *sta)
596{
597 struct sae_password_entry *pw;
598
599 if (!hapd->conf->sae_track_password)
600 return false;
601
602 pw = sae_password_find_pw(hapd, sta);
603 if (!pw)
604 return false;
605
606 if (in_mac_addr_list(pw->fail_mac,
607 pw->num_fail_mac,
608 sta->addr))
609 return is_other_sae_password(hapd, sta, pw);
610
611 if (!pw->fail_mac) {
612 pw->fail_mac = os_zalloc(hapd->conf->sae_track_password *
613 ETH_ALEN);
614 if (!pw->fail_mac)
615 return false;
616 pw->num_fail_mac = hapd->conf->sae_track_password;
617 }
618
619 os_memcpy(&pw->fail_mac[pw->next_fail_mac * ETH_ALEN], sta->addr,
620 ETH_ALEN);
621 pw->next_fail_mac = (pw->next_fail_mac + 1) % pw->num_fail_mac;
622
623 return is_other_sae_password(hapd, sta, pw);
624}
625
626
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000627const char * sae_get_password(struct hostapd_data *hapd,
628 struct sta_info *sta,
629 const char *rx_id,
630 struct sae_password_entry **pw_entry,
631 struct sae_pt **s_pt,
632 const struct sae_pk **s_pk)
Hai Shalom60840252021-02-19 19:02:11 -0800633{
634 const char *password = NULL;
635 struct sae_password_entry *pw;
636 struct sae_pt *pt = NULL;
637 const struct sae_pk *pk = NULL;
Sunil Ravia04bd252022-05-02 22:54:18 -0700638 struct hostapd_sta_wpa_psk_short *psk = NULL;
Hai Shalom60840252021-02-19 19:02:11 -0800639
Sunil Ravi876a49b2025-02-03 19:18:32 +0000640 /* With sae_track_password functionality enabled, try to first find the
641 * next viable wildcard-address password if a password identifier was
642 * not used. Select an wildcard-addr entry if the STA is known to have
643 * used it successfully before. If no such entry exists, pick a
644 * wildcard-addr entry that does not have a failed entry tracked for the
645 * STA. */
646 if (!rx_id && sta && hapd->conf->sae_track_password) {
647 struct sae_password_entry *success = NULL, *no_fail = NULL;
648
649 for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
650 if (pw->identifier ||
651 !is_broadcast_ether_addr(pw->peer_addr))
652 continue;
653 if (in_mac_addr_list(pw->success_mac,
654 pw->num_success_mac,
655 sta->addr)) {
656 success = pw;
657 break;
658 }
659
660 if (!no_fail &&
661 !in_mac_addr_list(pw->fail_mac, pw->num_fail_mac,
662 sta->addr))
663 no_fail = pw;
664 }
665
666 pw = success ? success : no_fail;
667 if (pw) {
668 password = pw->password;
669 pt = pw->pt;
670 if (!(hapd->conf->mesh & MESH_ENABLED))
671 pk = pw->pk;
672 goto found;
673 }
674 }
675
676 /* If sae_track_password functionality is not enabled or no suitable
677 * password entry was found with it, pick the first entry that matches
678 * the STA MAC address and password identifier (if used). */
Hai Shalom60840252021-02-19 19:02:11 -0800679 for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
680 if (!is_broadcast_ether_addr(pw->peer_addr) &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000681 (!sta ||
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000682 !ether_addr_equal(pw->peer_addr, sta->addr)))
Hai Shalom60840252021-02-19 19:02:11 -0800683 continue;
684 if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
685 continue;
686 if (rx_id && pw->identifier &&
687 os_strcmp(rx_id, pw->identifier) != 0)
688 continue;
689 password = pw->password;
690 pt = pw->pt;
691 if (!(hapd->conf->mesh & MESH_ENABLED))
692 pk = pw->pk;
693 break;
694 }
Sunil Ravi79e6c4f2025-01-04 00:47:06 +0000695 if (!password && !rx_id) {
Hai Shalom60840252021-02-19 19:02:11 -0800696 password = hapd->conf->ssid.wpa_passphrase;
697 pt = hapd->conf->ssid.pt;
698 }
699
Sunil Ravi79e6c4f2025-01-04 00:47:06 +0000700 if (!password && sta && !rx_id) {
Sunil Ravia04bd252022-05-02 22:54:18 -0700701 for (psk = sta->psk; psk; psk = psk->next) {
702 if (psk->is_passphrase) {
703 password = psk->passphrase;
704 break;
705 }
706 }
707 }
708
Sunil Ravi876a49b2025-02-03 19:18:32 +0000709found:
Hai Shalom60840252021-02-19 19:02:11 -0800710 if (pw_entry)
711 *pw_entry = pw;
712 if (s_pt)
713 *s_pt = pt;
714 if (s_pk)
715 *s_pk = pk;
716
717 return password;
718}
719
720
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800721static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
Hai Shalomc3565922019-10-28 11:58:20 -0700722 struct sta_info *sta, int update,
723 int status_code)
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800724{
725 struct wpabuf *buf;
Roshan Pius3a1667e2018-07-03 15:17:14 -0700726 const char *password = NULL;
727 struct sae_password_entry *pw;
728 const char *rx_id = NULL;
Hai Shalomc3565922019-10-28 11:58:20 -0700729 int use_pt = 0;
730 struct sae_pt *pt = NULL;
Hai Shalom899fcc72020-10-19 14:38:18 -0700731 const struct sae_pk *pk = NULL;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000732 const u8 *own_addr = hapd->own_addr;
733
734#ifdef CONFIG_IEEE80211BE
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000735 if (ap_sta_is_mld(hapd, sta))
Sunil Ravi99c035e2024-07-12 01:42:03 +0000736 own_addr = hapd->mld->mld_addr;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000737#endif /* CONFIG_IEEE80211BE */
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800738
Hai Shalomc3565922019-10-28 11:58:20 -0700739 if (sta->sae->tmp) {
Sunil Ravi79e6c4f2025-01-04 00:47:06 +0000740 rx_id = sta->sae->tmp->parsed_pw_id ?
741 sta->sae->tmp->parsed_pw_id : sta->sae->tmp->pw_id;
Hai Shalom899fcc72020-10-19 14:38:18 -0700742 use_pt = sta->sae->h2e;
743#ifdef CONFIG_SAE_PK
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000744 os_memcpy(sta->sae->tmp->own_addr, own_addr, ETH_ALEN);
Hai Shalom899fcc72020-10-19 14:38:18 -0700745 os_memcpy(sta->sae->tmp->peer_addr, sta->addr, ETH_ALEN);
746#endif /* CONFIG_SAE_PK */
Hai Shalomc3565922019-10-28 11:58:20 -0700747 }
748
Sunil Ravi77d572f2023-01-17 23:58:31 +0000749 if (rx_id && hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
Hai Shalomfdcde762020-04-02 11:19:20 -0700750 use_pt = 1;
751 else if (status_code == WLAN_STATUS_SUCCESS)
Hai Shalomc3565922019-10-28 11:58:20 -0700752 use_pt = 0;
Hai Shalom899fcc72020-10-19 14:38:18 -0700753 else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
754 status_code == WLAN_STATUS_SAE_PK)
Hai Shalomc3565922019-10-28 11:58:20 -0700755 use_pt = 1;
Roshan Pius3a1667e2018-07-03 15:17:14 -0700756
Hai Shalom60840252021-02-19 19:02:11 -0800757 password = sae_get_password(hapd, sta, rx_id, &pw, &pt, &pk);
Hai Shalomc3565922019-10-28 11:58:20 -0700758 if (!password || (use_pt && !pt)) {
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800759 wpa_printf(MSG_DEBUG, "SAE: No password available");
760 return NULL;
761 }
762
Hai Shalomc3565922019-10-28 11:58:20 -0700763 if (update && use_pt &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000764 sae_prepare_commit_pt(sta->sae, pt, own_addr, sta->addr,
Hai Shalom899fcc72020-10-19 14:38:18 -0700765 NULL, pk) < 0)
Hai Shalomc3565922019-10-28 11:58:20 -0700766 return NULL;
767
768 if (update && !use_pt &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000769 sae_prepare_commit(own_addr, sta->addr,
Hai Shaloma20dcd72022-02-04 13:43:00 -0800770 (u8 *) password, os_strlen(password),
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800771 sta->sae) < 0) {
772 wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
773 return NULL;
774 }
775
Sunil Ravi876a49b2025-02-03 19:18:32 +0000776 if (pw && sta->sae->tmp)
777 sta->sae->tmp->used_pw = pw;
778
Hai Shalom021b0b52019-04-10 11:17:58 -0700779 if (pw && pw->vlan_id) {
780 if (!sta->sae->tmp) {
781 wpa_printf(MSG_INFO,
782 "SAE: No temporary data allocated - cannot store VLAN ID");
783 return NULL;
784 }
785 sta->sae->tmp->vlan_id = pw->vlan_id;
786 }
787
Roshan Pius3a1667e2018-07-03 15:17:14 -0700788 buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
789 (rx_id ? 3 + os_strlen(rx_id) : 0));
Hai Shalomfdcde762020-04-02 11:19:20 -0700790 if (buf &&
791 sae_write_commit(sta->sae, buf, sta->sae->tmp ?
792 sta->sae->tmp->anti_clogging_token : NULL,
793 rx_id) < 0) {
794 wpabuf_free(buf);
795 buf = NULL;
796 }
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800797
798 return buf;
799}
800
801
802static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd,
803 struct sta_info *sta)
804{
805 struct wpabuf *buf;
806
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800807 buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800808 if (buf == NULL)
809 return NULL;
810
Hai Shalom899fcc72020-10-19 14:38:18 -0700811#ifdef CONFIG_SAE_PK
812#ifdef CONFIG_TESTING_OPTIONS
813 if (sta->sae->tmp)
814 sta->sae->tmp->omit_pk_elem = hapd->conf->sae_pk_omit;
815#endif /* CONFIG_TESTING_OPTIONS */
816#endif /* CONFIG_SAE_PK */
817
818 if (sae_write_confirm(sta->sae, buf) < 0) {
819 wpabuf_free(buf);
820 return NULL;
821 }
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800822
823 return buf;
824}
825
826
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800827static int auth_sae_send_commit(struct hostapd_data *hapd,
828 struct sta_info *sta,
Sunil Ravi7f769292024-07-23 22:21:32 +0000829 int update, int status_code)
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800830{
831 struct wpabuf *data;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800832 int reply_res;
Hai Shalomc3565922019-10-28 11:58:20 -0700833 u16 status;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800834
Hai Shalomc3565922019-10-28 11:58:20 -0700835 data = auth_build_sae_commit(hapd, sta, update, status_code);
Sunil Ravi79e6c4f2025-01-04 00:47:06 +0000836 if (!data && sta->sae->tmp &&
837 (sta->sae->tmp->pw_id || sta->sae->tmp->parsed_pw_id))
Roshan Pius3a1667e2018-07-03 15:17:14 -0700838 return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800839 if (data == NULL)
840 return WLAN_STATUS_UNSPECIFIED_FAILURE;
841
Hai Shalom899fcc72020-10-19 14:38:18 -0700842 if (sta->sae->tmp && sta->sae->pk)
843 status = WLAN_STATUS_SAE_PK;
844 else if (sta->sae->tmp && sta->sae->h2e)
845 status = WLAN_STATUS_SAE_HASH_TO_ELEMENT;
846 else
847 status = WLAN_STATUS_SUCCESS;
848#ifdef CONFIG_TESTING_OPTIONS
849 if (hapd->conf->sae_commit_status >= 0 &&
850 hapd->conf->sae_commit_status != status) {
851 wpa_printf(MSG_INFO,
852 "TESTING: Override SAE commit status code %u --> %d",
853 status, hapd->conf->sae_commit_status);
854 status = hapd->conf->sae_commit_status;
855 }
856#endif /* CONFIG_TESTING_OPTIONS */
Sunil Ravi7f769292024-07-23 22:21:32 +0000857 reply_res = send_auth_reply(hapd, sta, sta->addr,
Hai Shalomfdcde762020-04-02 11:19:20 -0700858 WLAN_AUTH_SAE, 1,
Hai Shalomc3565922019-10-28 11:58:20 -0700859 status, wpabuf_head(data),
Roshan Pius3a1667e2018-07-03 15:17:14 -0700860 wpabuf_len(data), "sae-send-commit");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800861
862 wpabuf_free(data);
863
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800864 return reply_res;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800865}
866
867
868static int auth_sae_send_confirm(struct hostapd_data *hapd,
Sunil Ravi7f769292024-07-23 22:21:32 +0000869 struct sta_info *sta)
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800870{
871 struct wpabuf *data;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800872 int reply_res;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800873
874 data = auth_build_sae_confirm(hapd, sta);
875 if (data == NULL)
876 return WLAN_STATUS_UNSPECIFIED_FAILURE;
877
Sunil Ravi7f769292024-07-23 22:21:32 +0000878 reply_res = send_auth_reply(hapd, sta, sta->addr,
Hai Shalomfdcde762020-04-02 11:19:20 -0700879 WLAN_AUTH_SAE, 2,
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800880 WLAN_STATUS_SUCCESS, wpabuf_head(data),
Roshan Pius3a1667e2018-07-03 15:17:14 -0700881 wpabuf_len(data), "sae-send-confirm");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800882
883 wpabuf_free(data);
884
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800885 return reply_res;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800886}
887
Hai Shaloma20dcd72022-02-04 13:43:00 -0800888#endif /* CONFIG_SAE */
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800889
Hai Shaloma20dcd72022-02-04 13:43:00 -0800890
891#if defined(CONFIG_SAE) || defined(CONFIG_PASN)
892
893static int use_anti_clogging(struct hostapd_data *hapd)
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800894{
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800895 struct sta_info *sta;
896 unsigned int open = 0;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800897
Hai Shaloma20dcd72022-02-04 13:43:00 -0800898 if (hapd->conf->anti_clogging_threshold == 0)
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800899 return 1;
900
901 for (sta = hapd->sta_list; sta; sta = sta->next) {
Hai Shaloma20dcd72022-02-04 13:43:00 -0800902#ifdef CONFIG_SAE
903 if (sta->sae &&
904 (sta->sae->state == SAE_COMMITTED ||
905 sta->sae->state == SAE_CONFIRMED))
906 open++;
907#endif /* CONFIG_SAE */
908#ifdef CONFIG_PASN
909 if (sta->pasn && sta->pasn->ecdh)
910 open++;
911#endif /* CONFIG_PASN */
912 if (open >= hapd->conf->anti_clogging_threshold)
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800913 return 1;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800914 }
915
Hai Shaloma20dcd72022-02-04 13:43:00 -0800916#ifdef CONFIG_SAE
Hai Shalom021b0b52019-04-10 11:17:58 -0700917 /* In addition to already existing open SAE sessions, check whether
918 * there are enough pending commit messages in the processing queue to
919 * potentially result in too many open sessions. */
920 if (open + dl_list_len(&hapd->sae_commit_queue) >=
Hai Shaloma20dcd72022-02-04 13:43:00 -0800921 hapd->conf->anti_clogging_threshold)
Hai Shalom021b0b52019-04-10 11:17:58 -0700922 return 1;
Hai Shaloma20dcd72022-02-04 13:43:00 -0800923#endif /* CONFIG_SAE */
Hai Shalom021b0b52019-04-10 11:17:58 -0700924
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800925 return 0;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800926}
927
Hai Shaloma20dcd72022-02-04 13:43:00 -0800928#endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
929
930
931#ifdef CONFIG_SAE
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800932
Roshan Pius3a1667e2018-07-03 15:17:14 -0700933static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta)
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800934{
Roshan Pius3a1667e2018-07-03 15:17:14 -0700935 if (sta->sae->sync > hapd->conf->sae_sync) {
936 sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync");
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800937 sta->sae->sync = 0;
Sunil Ravi7f769292024-07-23 22:21:32 +0000938 if (sta->sae->tmp) {
939 /* Disable this SAE instance for 10 seconds to avoid
940 * unnecessary flood of multiple SAE commits in
941 * unexpected mesh cases. */
942 if (os_get_reltime(&sta->sae->tmp->disabled_until) == 0)
943 sta->sae->tmp->disabled_until.sec += 10;
944 }
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800945 return -1;
946 }
947 return 0;
948}
949
950
Sunil Ravi7f769292024-07-23 22:21:32 +0000951static bool sae_proto_instance_disabled(struct sta_info *sta)
952{
953 struct sae_temporary_data *tmp;
954
955 if (!sta->sae)
956 return false;
957 tmp = sta->sae->tmp;
958 if (!tmp)
959 return false;
960
961 if (os_reltime_initialized(&tmp->disabled_until)) {
962 struct os_reltime now;
963
964 os_get_reltime(&now);
965 if (os_reltime_before(&now, &tmp->disabled_until))
966 return true;
967 }
968
969 return false;
970}
971
972
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800973static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data)
974{
975 struct hostapd_data *hapd = eloop_ctx;
976 struct sta_info *sta = eloop_data;
977 int ret;
978
Roshan Pius3a1667e2018-07-03 15:17:14 -0700979 if (sae_check_big_sync(hapd, sta))
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800980 return;
981 sta->sae->sync++;
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -0700982 wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR
Roshan Pius3a1667e2018-07-03 15:17:14 -0700983 " (sync=%d state=%s)",
984 MAC2STR(sta->addr), sta->sae->sync,
985 sae_state_txt(sta->sae->state));
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800986
987 switch (sta->sae->state) {
988 case SAE_COMMITTED:
Sunil Ravi7f769292024-07-23 22:21:32 +0000989 ret = auth_sae_send_commit(hapd, sta, 0, -1);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800990 eloop_register_timeout(0,
991 hapd->dot11RSNASAERetransPeriod * 1000,
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800992 auth_sae_retransmit_timer, hapd, sta);
993 break;
994 case SAE_CONFIRMED:
Sunil Ravi7f769292024-07-23 22:21:32 +0000995 ret = auth_sae_send_confirm(hapd, sta);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800996 eloop_register_timeout(0,
997 hapd->dot11RSNASAERetransPeriod * 1000,
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800998 auth_sae_retransmit_timer, hapd, sta);
999 break;
1000 default:
1001 ret = -1;
1002 break;
1003 }
1004
1005 if (ret != WLAN_STATUS_SUCCESS)
1006 wpa_printf(MSG_INFO, "SAE: Failed to retransmit: ret=%d", ret);
1007}
1008
1009
1010void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta)
1011{
1012 eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
1013}
1014
1015
1016static void sae_set_retransmit_timer(struct hostapd_data *hapd,
1017 struct sta_info *sta)
1018{
1019 if (!(hapd->conf->mesh & MESH_ENABLED))
1020 return;
1021
1022 eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001023 eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000,
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001024 auth_sae_retransmit_timer, hapd, sta);
1025}
1026
1027
Hai Shalom5f92bc92019-04-18 11:54:11 -07001028static void sae_sme_send_external_auth_status(struct hostapd_data *hapd,
1029 struct sta_info *sta, u16 status)
1030{
1031 struct external_auth params;
1032
1033 os_memset(&params, 0, sizeof(params));
1034 params.status = status;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001035
1036#ifdef CONFIG_IEEE80211BE
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001037 if (ap_sta_is_mld(hapd, sta))
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001038 params.bssid =
1039 sta->mld_info.links[sta->mld_assoc_link_id].peer_addr;
1040#endif /* CONFIG_IEEE80211BE */
1041 if (!params.bssid)
1042 params.bssid = sta->addr;
1043
Hai Shalom81f62d82019-07-22 12:10:00 -07001044 if (status == WLAN_STATUS_SUCCESS && sta->sae &&
1045 !hapd->conf->disable_pmksa_caching)
Hai Shalom5f92bc92019-04-18 11:54:11 -07001046 params.pmkid = sta->sae->pmkid;
1047
1048 hostapd_drv_send_external_auth_status(hapd, &params);
1049}
1050
1051
Dmitry Shmidte4663042016-04-04 10:07:49 -07001052void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
1053{
Hai Shalom021b0b52019-04-10 11:17:58 -07001054#ifndef CONFIG_NO_VLAN
1055 struct vlan_description vlan_desc;
1056
1057 if (sta->sae->tmp && sta->sae->tmp->vlan_id > 0) {
1058 wpa_printf(MSG_DEBUG, "SAE: Assign STA " MACSTR
1059 " to VLAN ID %d",
1060 MAC2STR(sta->addr), sta->sae->tmp->vlan_id);
1061
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001062 if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
1063 os_memset(&vlan_desc, 0, sizeof(vlan_desc));
1064 vlan_desc.notempty = 1;
1065 vlan_desc.untagged = sta->sae->tmp->vlan_id;
1066 if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
1067 wpa_printf(MSG_INFO,
1068 "Invalid VLAN ID %d in sae_password",
1069 sta->sae->tmp->vlan_id);
1070 return;
1071 }
Hai Shalom021b0b52019-04-10 11:17:58 -07001072
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001073 if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 ||
1074 ap_sta_bind_vlan(hapd, sta) < 0) {
1075 wpa_printf(MSG_INFO,
1076 "Failed to assign VLAN ID %d from sae_password to "
1077 MACSTR, sta->sae->tmp->vlan_id,
1078 MAC2STR(sta->addr));
1079 return;
1080 }
1081 } else {
1082 sta->vlan_id = sta->sae->tmp->vlan_id;
Hai Shalom021b0b52019-04-10 11:17:58 -07001083 }
1084 }
1085#endif /* CONFIG_NO_VLAN */
1086
Dmitry Shmidte4663042016-04-04 10:07:49 -07001087 sta->flags |= WLAN_STA_AUTH;
1088 sta->auth_alg = WLAN_AUTH_SAE;
1089 mlme_authenticate_indication(hapd, sta);
1090 wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
Roshan Pius3a1667e2018-07-03 15:17:14 -07001091 sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm");
Hai Shalomfdcde762020-04-02 11:19:20 -07001092 crypto_bignum_deinit(sta->sae->peer_commit_scalar_accepted, 0);
1093 sta->sae->peer_commit_scalar_accepted = sta->sae->peer_commit_scalar;
1094 sta->sae->peer_commit_scalar = NULL;
Dmitry Shmidte4663042016-04-04 10:07:49 -07001095 wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
Sunil Ravi89eba102022-09-13 21:04:37 -07001096 sta->sae->pmk, sta->sae->pmk_len,
Sunil Ravi876a49b2025-02-03 19:18:32 +00001097 sta->sae->pmkid, sta->sae->akmp,
1098 ap_sta_is_mld(hapd, sta));
Hai Shalom5f92bc92019-04-18 11:54:11 -07001099 sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
Dmitry Shmidte4663042016-04-04 10:07:49 -07001100}
1101
1102
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001103static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
Sunil Ravi7f769292024-07-23 22:21:32 +00001104 u16 auth_transaction, u16 status_code,
Hai Shalomc3565922019-10-28 11:58:20 -07001105 int allow_reuse, int *sta_removed)
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001106{
1107 int ret;
1108
Hai Shalom5f92bc92019-04-18 11:54:11 -07001109 *sta_removed = 0;
1110
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001111 if (auth_transaction != 1 && auth_transaction != 2)
1112 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1113
Roshan Pius3a1667e2018-07-03 15:17:14 -07001114 wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u",
1115 MAC2STR(sta->addr), sae_state_txt(sta->sae->state),
1116 auth_transaction);
Sunil Ravi7f769292024-07-23 22:21:32 +00001117
1118 if (auth_transaction == 1 && sae_proto_instance_disabled(sta)) {
1119 wpa_printf(MSG_DEBUG,
1120 "SAE: Protocol instance temporarily disabled - discard received SAE commit");
1121 return WLAN_STATUS_SUCCESS;
1122 }
1123
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001124 switch (sta->sae->state) {
1125 case SAE_NOTHING:
1126 if (auth_transaction == 1) {
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001127 struct sae_temporary_data *tmp = sta->sae->tmp;
Sunil Ravi876a49b2025-02-03 19:18:32 +00001128 bool immediate_confirm;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001129
1130 if (tmp) {
Hai Shalom899fcc72020-10-19 14:38:18 -07001131 sta->sae->h2e =
1132 (status_code ==
1133 WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
1134 status_code == WLAN_STATUS_SAE_PK);
1135 sta->sae->pk =
1136 status_code == WLAN_STATUS_SAE_PK;
1137 }
Sunil Ravi7f769292024-07-23 22:21:32 +00001138 ret = auth_sae_send_commit(hapd, sta,
Hai Shalomc3565922019-10-28 11:58:20 -07001139 !allow_reuse, status_code);
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001140 if (ret == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER)
1141 wpa_msg(hapd->msg_ctx, MSG_INFO,
1142 WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER
1143 MACSTR, MAC2STR(sta->addr));
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001144 if (ret)
1145 return ret;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001146
1147 if (tmp && tmp->parsed_pw_id && !tmp->pw_id) {
1148 tmp->pw_id = tmp->parsed_pw_id;
1149 tmp->parsed_pw_id = NULL;
1150 wpa_printf(MSG_DEBUG,
1151 "SAE: Known Password Identifier bound to this STA: '%s'",
1152 tmp->pw_id);
1153 }
1154
Roshan Pius3a1667e2018-07-03 15:17:14 -07001155 sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001156
1157 if (sae_process_commit(sta->sae) < 0)
1158 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1159
1160 /*
Hai Shalomc3565922019-10-28 11:58:20 -07001161 * In mesh case, both Commit and Confirm are sent
1162 * immediately. In infrastructure BSS, by default, only
1163 * a single Authentication frame (Commit) is expected
1164 * from the AP here and the second one (Confirm) will
1165 * be sent once the STA has sent its second
1166 * Authentication frame (Confirm). This behavior can be
1167 * overridden with explicit configuration so that the
1168 * infrastructure BSS case sends both frames together.
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001169 */
Sunil Ravi876a49b2025-02-03 19:18:32 +00001170 immediate_confirm = (hapd->conf->mesh & MESH_ENABLED) ||
1171 hapd->conf->sae_confirm_immediate;
1172
1173 /* If sae_track_password is enabled and the STA has not
1174 * yet been tracked to having successfully completed
1175 * SAE authentication with the password that the AP
1176 * tries to use, do not send Confirm immediately to
1177 * avoid an explicit indication on the STA side on
1178 * password mismatch. */
1179 if (immediate_confirm &&
1180 hapd->conf->sae_track_password &&
1181 (!sta->sae->tmp || !sta->sae->tmp->parsed_pw_id) &&
1182 !has_sae_success_seen(hapd, sta))
1183 immediate_confirm = false;
1184
1185 if (immediate_confirm) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001186 /*
1187 * Send both Commit and Confirm immediately
1188 * based on SAE finite state machine
1189 * Nothing -> Confirm transition.
1190 */
Sunil Ravi7f769292024-07-23 22:21:32 +00001191 ret = auth_sae_send_confirm(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001192 if (ret)
1193 return ret;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001194 sae_set_state(sta, SAE_CONFIRMED,
1195 "Sent Confirm (mesh)");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001196 } else {
1197 /*
1198 * For infrastructure BSS, send only the Commit
1199 * message now to get alternating sequence of
1200 * Authentication frames between the AP and STA.
1201 * Confirm will be sent in
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001202 * Committed -> Confirmed/Accepted transition
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001203 * when receiving Confirm from STA.
1204 */
1205 }
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001206 sta->sae->sync = 0;
1207 sae_set_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001208 } else {
1209 hostapd_logger(hapd, sta->addr,
1210 HOSTAPD_MODULE_IEEE80211,
1211 HOSTAPD_LEVEL_DEBUG,
1212 "SAE confirm before commit");
1213 }
1214 break;
1215 case SAE_COMMITTED:
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001216 sae_clear_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001217 if (auth_transaction == 1) {
1218 if (sae_process_commit(sta->sae) < 0)
1219 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1220
Sunil Ravi7f769292024-07-23 22:21:32 +00001221 ret = auth_sae_send_confirm(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001222 if (ret)
1223 return ret;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001224 sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001225 sta->sae->sync = 0;
1226 sae_set_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001227 } else if (hapd->conf->mesh & MESH_ENABLED) {
1228 /*
1229 * In mesh case, follow SAE finite state machine and
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001230 * send Commit now, if sync count allows.
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001231 */
Roshan Pius3a1667e2018-07-03 15:17:14 -07001232 if (sae_check_big_sync(hapd, sta))
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001233 return WLAN_STATUS_SUCCESS;
1234 sta->sae->sync++;
1235
Sunil Ravi7f769292024-07-23 22:21:32 +00001236 ret = auth_sae_send_commit(hapd, sta, 0, status_code);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001237 if (ret)
1238 return ret;
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001239
1240 sae_set_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001241 } else {
1242 /*
1243 * For instructure BSS, send the postponed Confirm from
1244 * Nothing -> Confirmed transition that was reduced to
1245 * Nothing -> Committed above.
1246 */
Sunil Ravi7f769292024-07-23 22:21:32 +00001247 ret = auth_sae_send_confirm(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001248 if (ret)
1249 return ret;
1250
Roshan Pius3a1667e2018-07-03 15:17:14 -07001251 sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001252
1253 /*
1254 * Since this was triggered on Confirm RX, run another
1255 * step to get to Accepted without waiting for
1256 * additional events.
1257 */
Sunil Ravi7f769292024-07-23 22:21:32 +00001258 return sae_sm_step(hapd, sta, auth_transaction,
Hai Shalomc3565922019-10-28 11:58:20 -07001259 WLAN_STATUS_SUCCESS, 0, sta_removed);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001260 }
1261 break;
1262 case SAE_CONFIRMED:
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001263 sae_clear_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001264 if (auth_transaction == 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07001265 if (sae_check_big_sync(hapd, sta))
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001266 return WLAN_STATUS_SUCCESS;
1267 sta->sae->sync++;
1268
Sunil Ravi7f769292024-07-23 22:21:32 +00001269 ret = auth_sae_send_commit(hapd, sta, 1, status_code);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001270 if (ret)
1271 return ret;
1272
1273 if (sae_process_commit(sta->sae) < 0)
1274 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1275
Sunil Ravi7f769292024-07-23 22:21:32 +00001276 ret = auth_sae_send_confirm(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001277 if (ret)
1278 return ret;
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001279
1280 sae_set_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001281 } else {
Roshan Pius3a1667e2018-07-03 15:17:14 -07001282 sta->sae->send_confirm = 0xffff;
Dmitry Shmidte4663042016-04-04 10:07:49 -07001283 sae_accept_sta(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001284 }
1285 break;
1286 case SAE_ACCEPTED:
Roshan Pius3a1667e2018-07-03 15:17:14 -07001287 if (auth_transaction == 1 &&
1288 (hapd->conf->mesh & MESH_ENABLED)) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001289 wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR
1290 ") doing reauthentication",
1291 MAC2STR(sta->addr));
Dmitry Shmidte4663042016-04-04 10:07:49 -07001292 wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
Hai Shalom5f92bc92019-04-18 11:54:11 -07001293 ap_free_sta(hapd, sta);
1294 *sta_removed = 1;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001295 } else if (auth_transaction == 1) {
1296 wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
Sunil Ravi7f769292024-07-23 22:21:32 +00001297 ret = auth_sae_send_commit(hapd, sta, 1, status_code);
Roshan Pius3a1667e2018-07-03 15:17:14 -07001298 if (ret)
1299 return ret;
1300 sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
1301
1302 if (sae_process_commit(sta->sae) < 0)
1303 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1304 sta->sae->sync = 0;
1305 sae_set_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001306 } else {
Roshan Pius3a1667e2018-07-03 15:17:14 -07001307 if (sae_check_big_sync(hapd, sta))
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001308 return WLAN_STATUS_SUCCESS;
1309 sta->sae->sync++;
1310
Sunil Ravi7f769292024-07-23 22:21:32 +00001311 ret = auth_sae_send_confirm(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001312 sae_clear_temp_data(sta->sae);
1313 if (ret)
1314 return ret;
1315 }
1316 break;
1317 default:
1318 wpa_printf(MSG_ERROR, "SAE: invalid state %d",
1319 sta->sae->state);
1320 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1321 }
1322 return WLAN_STATUS_SUCCESS;
1323}
1324
1325
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001326static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta)
1327{
1328 struct sae_data *sae = sta->sae;
Sunil Ravic0f5d412024-09-11 22:12:49 +00001329 struct hostapd_bss_config *conf = hapd->conf;
1330 int i, *groups = conf->sae_groups;
1331 int default_groups[] = { 19, 0, 0 };
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001332
1333 if (sae->state != SAE_COMMITTED)
1334 return;
1335
1336 wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group);
1337
Sunil Ravic0f5d412024-09-11 22:12:49 +00001338 if (!groups) {
Hai Shalom021b0b52019-04-10 11:17:58 -07001339 groups = default_groups;
Sunil Ravic0f5d412024-09-11 22:12:49 +00001340 if (wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt |
1341 conf->rsn_override_key_mgmt |
1342 conf->rsn_override_key_mgmt_2))
1343 default_groups[1] = 20;
1344 }
1345
Hai Shalom021b0b52019-04-10 11:17:58 -07001346 for (i = 0; groups[i] > 0; i++) {
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001347 if (sae->group == groups[i])
1348 break;
1349 }
1350
Hai Shalom021b0b52019-04-10 11:17:58 -07001351 if (groups[i] <= 0) {
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001352 wpa_printf(MSG_DEBUG,
1353 "SAE: Previously selected group not found from the current configuration");
1354 return;
1355 }
1356
1357 for (;;) {
1358 i++;
1359 if (groups[i] <= 0) {
1360 wpa_printf(MSG_DEBUG,
1361 "SAE: No alternative group enabled");
1362 return;
1363 }
1364
1365 if (sae_set_group(sae, groups[i]) < 0)
1366 continue;
1367
1368 break;
1369 }
1370 wpa_printf(MSG_DEBUG, "SAE: Selected new group: %d", groups[i]);
1371}
1372
1373
Hai Shalomc3565922019-10-28 11:58:20 -07001374static int sae_status_success(struct hostapd_data *hapd, u16 status_code)
1375{
Sunil Ravi77d572f2023-01-17 23:58:31 +00001376 enum sae_pwe sae_pwe = hapd->conf->sae_pwe;
Hai Shalomfdcde762020-04-02 11:19:20 -07001377 int id_in_use;
Hai Shalom60840252021-02-19 19:02:11 -08001378 bool sae_pk = false;
Hai Shalomfdcde762020-04-02 11:19:20 -07001379
1380 id_in_use = hostapd_sae_pw_id_in_use(hapd->conf);
Sunil Ravi77d572f2023-01-17 23:58:31 +00001381 if (id_in_use == 2 && sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
1382 sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
1383 else if (id_in_use == 1 && sae_pwe == SAE_PWE_HUNT_AND_PECK)
1384 sae_pwe = SAE_PWE_BOTH;
Hai Shalom899fcc72020-10-19 14:38:18 -07001385#ifdef CONFIG_SAE_PK
Hai Shalom60840252021-02-19 19:02:11 -08001386 sae_pk = hostapd_sae_pk_in_use(hapd->conf);
Sunil Ravi77d572f2023-01-17 23:58:31 +00001387 if (sae_pwe == SAE_PWE_HUNT_AND_PECK && sae_pk)
1388 sae_pwe = SAE_PWE_BOTH;
Hai Shalom899fcc72020-10-19 14:38:18 -07001389#endif /* CONFIG_SAE_PK */
Sunil Ravi77d572f2023-01-17 23:58:31 +00001390 if (sae_pwe == SAE_PWE_HUNT_AND_PECK &&
Sunil Ravi89eba102022-09-13 21:04:37 -07001391 (hapd->conf->wpa_key_mgmt &
1392 (WPA_KEY_MGMT_SAE_EXT_KEY | WPA_KEY_MGMT_FT_SAE_EXT_KEY)))
Sunil Ravi77d572f2023-01-17 23:58:31 +00001393 sae_pwe = SAE_PWE_BOTH;
Hai Shalomfdcde762020-04-02 11:19:20 -07001394
Sunil Ravi77d572f2023-01-17 23:58:31 +00001395 return ((sae_pwe == SAE_PWE_HUNT_AND_PECK ||
1396 sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) &&
Hai Shalomc3565922019-10-28 11:58:20 -07001397 status_code == WLAN_STATUS_SUCCESS) ||
Sunil Ravi77d572f2023-01-17 23:58:31 +00001398 (sae_pwe == SAE_PWE_HASH_TO_ELEMENT &&
Hai Shalom899fcc72020-10-19 14:38:18 -07001399 (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
Hai Shalom60840252021-02-19 19:02:11 -08001400 (sae_pk && status_code == WLAN_STATUS_SAE_PK))) ||
Sunil Ravi77d572f2023-01-17 23:58:31 +00001401 (sae_pwe == SAE_PWE_BOTH &&
Hai Shalomc3565922019-10-28 11:58:20 -07001402 (status_code == WLAN_STATUS_SUCCESS ||
Hai Shalom899fcc72020-10-19 14:38:18 -07001403 status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
Hai Shalom60840252021-02-19 19:02:11 -08001404 (sae_pk && status_code == WLAN_STATUS_SAE_PK)));
Hai Shalomc3565922019-10-28 11:58:20 -07001405}
1406
1407
1408static int sae_is_group_enabled(struct hostapd_data *hapd, int group)
1409{
Sunil Ravic0f5d412024-09-11 22:12:49 +00001410 struct hostapd_bss_config *conf = hapd->conf;
1411 int *groups = conf->sae_groups;
1412 int default_groups[] = { 19, 0, 0 };
Hai Shalomc3565922019-10-28 11:58:20 -07001413 int i;
1414
Sunil Ravic0f5d412024-09-11 22:12:49 +00001415 if (!groups) {
Hai Shalomc3565922019-10-28 11:58:20 -07001416 groups = default_groups;
Sunil Ravic0f5d412024-09-11 22:12:49 +00001417 if (wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt |
1418 conf->rsn_override_key_mgmt |
1419 conf->rsn_override_key_mgmt_2))
1420 default_groups[1] = 20;
1421 }
Hai Shalomc3565922019-10-28 11:58:20 -07001422
1423 for (i = 0; groups[i] > 0; i++) {
1424 if (groups[i] == group)
1425 return 1;
1426 }
1427
1428 return 0;
1429}
1430
1431
1432static int check_sae_rejected_groups(struct hostapd_data *hapd,
Hai Shalom899fcc72020-10-19 14:38:18 -07001433 struct sae_data *sae)
Hai Shalomc3565922019-10-28 11:58:20 -07001434{
Hai Shalom899fcc72020-10-19 14:38:18 -07001435 const struct wpabuf *groups;
Sunil Ravi7f769292024-07-23 22:21:32 +00001436 size_t i, count, len;
Hai Shalomc3565922019-10-28 11:58:20 -07001437 const u8 *pos;
1438
Hai Shalom899fcc72020-10-19 14:38:18 -07001439 if (!sae->tmp)
1440 return 0;
1441 groups = sae->tmp->peer_rejected_groups;
Hai Shalomc3565922019-10-28 11:58:20 -07001442 if (!groups)
1443 return 0;
1444
1445 pos = wpabuf_head(groups);
Sunil Ravi7f769292024-07-23 22:21:32 +00001446 len = wpabuf_len(groups);
1447 if (len & 1) {
1448 wpa_printf(MSG_DEBUG,
1449 "SAE: Invalid length of the Rejected Groups element payload: %zu",
1450 len);
1451 return 1;
1452 }
1453
1454 count = len / 2;
Hai Shalomc3565922019-10-28 11:58:20 -07001455 for (i = 0; i < count; i++) {
1456 int enabled;
1457 u16 group;
1458
1459 group = WPA_GET_LE16(pos);
1460 pos += 2;
1461 enabled = sae_is_group_enabled(hapd, group);
1462 wpa_printf(MSG_DEBUG, "SAE: Rejected group %u is %s",
1463 group, enabled ? "enabled" : "disabled");
1464 if (enabled)
1465 return 1;
1466 }
1467
1468 return 0;
1469}
1470
1471
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001472static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
1473 const struct ieee80211_mgmt *mgmt, size_t len,
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001474 u16 auth_transaction, u16 status_code)
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001475{
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001476 int resp = WLAN_STATUS_SUCCESS;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001477 struct wpabuf *data = NULL;
Sunil Ravic0f5d412024-09-11 22:12:49 +00001478 struct hostapd_bss_config *conf = hapd->conf;
1479 int *groups = conf->sae_groups;
1480 int default_groups[] = { 19, 0, 0 };
Hai Shalom021b0b52019-04-10 11:17:58 -07001481 const u8 *pos, *end;
Hai Shalom5f92bc92019-04-18 11:54:11 -07001482 int sta_removed = 0;
Hai Shalom60840252021-02-19 19:02:11 -08001483 bool success_status;
Hai Shalom021b0b52019-04-10 11:17:58 -07001484
Sunil Ravic0f5d412024-09-11 22:12:49 +00001485 if (!groups) {
Hai Shalom021b0b52019-04-10 11:17:58 -07001486 groups = default_groups;
Sunil Ravic0f5d412024-09-11 22:12:49 +00001487 if (wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt |
1488 conf->rsn_override_key_mgmt |
1489 conf->rsn_override_key_mgmt_2))
1490 default_groups[1] = 20;
1491 }
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001492
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001493#ifdef CONFIG_TESTING_OPTIONS
1494 if (hapd->conf->sae_reflection_attack && auth_transaction == 1) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001495 wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack");
1496 pos = mgmt->u.auth.variable;
1497 end = ((const u8 *) mgmt) + len;
Hai Shalom899fcc72020-10-19 14:38:18 -07001498 resp = status_code;
Sunil Ravi7f769292024-07-23 22:21:32 +00001499 send_auth_reply(hapd, sta, sta->addr,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001500 WLAN_AUTH_SAE,
Roshan Pius3a1667e2018-07-03 15:17:14 -07001501 auth_transaction, resp, pos, end - pos,
1502 "auth-sae-reflection-attack");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001503 goto remove_sta;
1504 }
1505
1506 if (hapd->conf->sae_commit_override && auth_transaction == 1) {
1507 wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
Sunil Ravi7f769292024-07-23 22:21:32 +00001508 send_auth_reply(hapd, sta, sta->addr,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001509 WLAN_AUTH_SAE,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001510 auth_transaction, resp,
1511 wpabuf_head(hapd->conf->sae_commit_override),
Roshan Pius3a1667e2018-07-03 15:17:14 -07001512 wpabuf_len(hapd->conf->sae_commit_override),
1513 "sae-commit-override");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001514 goto remove_sta;
1515 }
1516#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001517 if (!sta->sae) {
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001518 if (auth_transaction != 1 ||
Hai Shalomc3565922019-10-28 11:58:20 -07001519 !sae_status_success(hapd, status_code)) {
Ahmed ElArabawy0ff61c52019-12-26 12:38:39 -08001520 wpa_printf(MSG_DEBUG, "SAE: Unexpected Status Code %u",
1521 status_code);
1522 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1523 goto reply;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001524 }
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001525 sta->sae = os_zalloc(sizeof(*sta->sae));
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001526 if (!sta->sae) {
1527 resp = -1;
1528 goto remove_sta;
1529 }
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001530 if (!hostapd_sae_pw_id_in_use(hapd->conf))
1531 sta->sae->no_pw_id = 1;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001532 sae_set_state(sta, SAE_NOTHING, "Init");
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001533 sta->sae->sync = 0;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001534 }
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001535
Dmitry Shmidte4663042016-04-04 10:07:49 -07001536 if (sta->mesh_sae_pmksa_caching) {
1537 wpa_printf(MSG_DEBUG,
1538 "SAE: Cancel use of mesh PMKSA caching because peer starts SAE authentication");
1539 wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
1540 sta->mesh_sae_pmksa_caching = 0;
1541 }
1542
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001543 if (auth_transaction == 1) {
Hai Shalom021b0b52019-04-10 11:17:58 -07001544 const u8 *token = NULL;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001545 size_t token_len = 0;
Hai Shalom021b0b52019-04-10 11:17:58 -07001546 int allow_reuse = 0;
1547
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001548 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
1549 HOSTAPD_LEVEL_DEBUG,
Hai Shalom81f62d82019-07-22 12:10:00 -07001550 "start SAE authentication (RX commit, status=%u (%s))",
1551 status_code, status2str(status_code));
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001552
1553 if ((hapd->conf->mesh & MESH_ENABLED) &&
1554 status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
1555 sta->sae->tmp) {
1556 pos = mgmt->u.auth.variable;
1557 end = ((const u8 *) mgmt) + len;
1558 if (pos + sizeof(le16) > end) {
1559 wpa_printf(MSG_ERROR,
1560 "SAE: Too short anti-clogging token request");
1561 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1562 goto reply;
1563 }
Hai Shalom021b0b52019-04-10 11:17:58 -07001564 resp = sae_group_allowed(sta->sae, groups,
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001565 WPA_GET_LE16(pos));
1566 if (resp != WLAN_STATUS_SUCCESS) {
1567 wpa_printf(MSG_ERROR,
1568 "SAE: Invalid group in anti-clogging token request");
1569 goto reply;
1570 }
1571 pos += sizeof(le16);
1572
1573 wpabuf_free(sta->sae->tmp->anti_clogging_token);
1574 sta->sae->tmp->anti_clogging_token =
1575 wpabuf_alloc_copy(pos, end - pos);
1576 if (sta->sae->tmp->anti_clogging_token == NULL) {
1577 wpa_printf(MSG_ERROR,
1578 "SAE: Failed to alloc for anti-clogging token");
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001579 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1580 goto remove_sta;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001581 }
1582
1583 /*
1584 * IEEE Std 802.11-2012, 11.3.8.6.4: If the Status code
1585 * is 76, a new Commit Message shall be constructed
1586 * with the Anti-Clogging Token from the received
1587 * Authentication frame, and the commit-scalar and
1588 * COMMIT-ELEMENT previously sent.
1589 */
Sunil Ravi7f769292024-07-23 22:21:32 +00001590 resp = auth_sae_send_commit(hapd, sta, 0, status_code);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001591 if (resp != WLAN_STATUS_SUCCESS) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001592 wpa_printf(MSG_ERROR,
1593 "SAE: Failed to send commit message");
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001594 goto remove_sta;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001595 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07001596 sae_set_state(sta, SAE_COMMITTED,
1597 "Sent Commit (anti-clogging token case in mesh)");
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001598 sta->sae->sync = 0;
1599 sae_set_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001600 return;
1601 }
1602
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001603 if ((hapd->conf->mesh & MESH_ENABLED) &&
1604 status_code ==
1605 WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
1606 sta->sae->tmp) {
1607 wpa_printf(MSG_DEBUG,
1608 "SAE: Peer did not accept our SAE group");
1609 sae_pick_next_group(hapd, sta);
1610 goto remove_sta;
1611 }
1612
Hai Shalomc3565922019-10-28 11:58:20 -07001613 if (!sae_status_success(hapd, status_code))
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001614 goto remove_sta;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001615
Sunil Ravi7f769292024-07-23 22:21:32 +00001616 if (sae_proto_instance_disabled(sta)) {
1617 wpa_printf(MSG_DEBUG,
1618 "SAE: Protocol instance temporarily disabled - discard received SAE commit");
1619 return;
1620 }
1621
Roshan Pius3a1667e2018-07-03 15:17:14 -07001622 if (!(hapd->conf->mesh & MESH_ENABLED) &&
1623 sta->sae->state == SAE_COMMITTED) {
1624 /* This is needed in the infrastructure BSS case to
1625 * address a sequence where a STA entry may remain in
1626 * hostapd across two attempts to do SAE authentication
1627 * by the same STA. The second attempt may end up trying
1628 * to use a different group and that would not be
1629 * allowed if we remain in Committed state with the
1630 * previously set parameters. */
Hai Shalom021b0b52019-04-10 11:17:58 -07001631 pos = mgmt->u.auth.variable;
1632 end = ((const u8 *) mgmt) + len;
Sunil Ravi876a49b2025-02-03 19:18:32 +00001633 if ((!sta->sae->tmp ||
1634 !sta->sae->tmp->try_other_password) &&
1635 end - pos >= (int) sizeof(le16) &&
Hai Shalom021b0b52019-04-10 11:17:58 -07001636 sae_group_allowed(sta->sae, groups,
1637 WPA_GET_LE16(pos)) ==
1638 WLAN_STATUS_SUCCESS) {
1639 /* Do not waste resources deriving the same PWE
1640 * again since the same group is reused. */
1641 sae_set_state(sta, SAE_NOTHING,
1642 "Allow previous PWE to be reused");
1643 allow_reuse = 1;
1644 } else {
1645 sae_set_state(sta, SAE_NOTHING,
1646 "Clear existing state to allow restart");
1647 sae_clear_data(sta->sae);
1648 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07001649 }
1650
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001651 resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
1652 ((const u8 *) mgmt) + len -
1653 mgmt->u.auth.variable, &token,
Hai Shalomc3565922019-10-28 11:58:20 -07001654 &token_len, groups, status_code ==
Hai Shalom899fcc72020-10-19 14:38:18 -07001655 WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
Sunil Ravi77d572f2023-01-17 23:58:31 +00001656 status_code == WLAN_STATUS_SAE_PK,
1657 NULL);
Dmitry Shmidt41712582015-06-29 11:02:15 -07001658 if (resp == SAE_SILENTLY_DISCARD) {
1659 wpa_printf(MSG_DEBUG,
1660 "SAE: Drop commit message from " MACSTR " due to reflection attack",
1661 MAC2STR(sta->addr));
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001662 goto remove_sta;
Dmitry Shmidt41712582015-06-29 11:02:15 -07001663 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07001664
1665 if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
1666 wpa_msg(hapd->msg_ctx, MSG_INFO,
1667 WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER
1668 MACSTR, MAC2STR(sta->addr));
1669 sae_clear_retransmit_timer(hapd, sta);
1670 sae_set_state(sta, SAE_NOTHING,
1671 "Unknown Password Identifier");
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001672 if (sta->sae->state == SAE_NOTHING)
1673 goto reply;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001674 goto remove_sta;
1675 }
1676
Hai Shaloma20dcd72022-02-04 13:43:00 -08001677 if (token &&
Sunil Ravi77d572f2023-01-17 23:58:31 +00001678 check_comeback_token(hapd->comeback_key,
1679 hapd->comeback_pending_idx, sta->addr,
1680 token, token_len)
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001681 < 0) {
1682 wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
1683 "incorrect token from " MACSTR,
1684 MAC2STR(sta->addr));
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001685 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1686 goto remove_sta;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001687 }
1688
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001689 if (resp != WLAN_STATUS_SUCCESS)
1690 goto reply;
1691
Hai Shalom899fcc72020-10-19 14:38:18 -07001692 if (check_sae_rejected_groups(hapd, sta->sae)) {
Hai Shalomc3565922019-10-28 11:58:20 -07001693 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Ahmed ElArabawy0ff61c52019-12-26 12:38:39 -08001694 goto reply;
Hai Shalomc3565922019-10-28 11:58:20 -07001695 }
1696
Hai Shaloma20dcd72022-02-04 13:43:00 -08001697 if (!token && use_anti_clogging(hapd) && !allow_reuse) {
Hai Shalomfdcde762020-04-02 11:19:20 -07001698 int h2e = 0;
1699
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001700 wpa_printf(MSG_DEBUG,
1701 "SAE: Request anti-clogging token from "
1702 MACSTR, MAC2STR(sta->addr));
Hai Shalomfdcde762020-04-02 11:19:20 -07001703 if (sta->sae->tmp)
Hai Shalom899fcc72020-10-19 14:38:18 -07001704 h2e = sta->sae->h2e;
1705 if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
1706 status_code == WLAN_STATUS_SAE_PK)
Hai Shalomfdcde762020-04-02 11:19:20 -07001707 h2e = 1;
Sunil Ravi77d572f2023-01-17 23:58:31 +00001708 data = auth_build_token_req(
1709 &hapd->last_comeback_key_update,
1710 hapd->comeback_key,
1711 hapd->comeback_idx,
1712 hapd->comeback_pending_idx,
1713 sizeof(hapd->comeback_pending_idx),
1714 sta->sae->group,
1715 sta->addr, h2e);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001716 resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
1717 if (hapd->conf->mesh & MESH_ENABLED)
Roshan Pius3a1667e2018-07-03 15:17:14 -07001718 sae_set_state(sta, SAE_NOTHING,
1719 "Request anti-clogging token case in mesh");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001720 goto reply;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001721 }
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001722
Sunil Ravi7f769292024-07-23 22:21:32 +00001723 resp = sae_sm_step(hapd, sta, auth_transaction,
Hai Shalomc3565922019-10-28 11:58:20 -07001724 status_code, allow_reuse, &sta_removed);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001725 } else if (auth_transaction == 2) {
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001726 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
1727 HOSTAPD_LEVEL_DEBUG,
Hai Shalom81f62d82019-07-22 12:10:00 -07001728 "SAE authentication (RX confirm, status=%u (%s))",
1729 status_code, status2str(status_code));
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001730 if (status_code != WLAN_STATUS_SUCCESS)
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001731 goto remove_sta;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001732 if (sta->sae->state >= SAE_CONFIRMED ||
1733 !(hapd->conf->mesh & MESH_ENABLED)) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07001734 const u8 *var;
1735 size_t var_len;
1736 u16 peer_send_confirm;
1737
1738 var = mgmt->u.auth.variable;
1739 var_len = ((u8 *) mgmt) + len - mgmt->u.auth.variable;
1740 if (var_len < 2) {
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001741 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001742 goto reply;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001743 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07001744
1745 peer_send_confirm = WPA_GET_LE16(var);
1746
1747 if (sta->sae->state == SAE_ACCEPTED &&
1748 (peer_send_confirm <= sta->sae->rc ||
1749 peer_send_confirm == 0xffff)) {
1750 wpa_printf(MSG_DEBUG,
1751 "SAE: Silently ignore unexpected Confirm from peer "
1752 MACSTR
1753 " (peer-send-confirm=%u Rc=%u)",
1754 MAC2STR(sta->addr),
1755 peer_send_confirm, sta->sae->rc);
1756 return;
1757 }
1758
Sunil Ravi77d572f2023-01-17 23:58:31 +00001759 if (sae_check_confirm(sta->sae, var, var_len,
1760 NULL) < 0) {
Sunil Ravi876a49b2025-02-03 19:18:32 +00001761 if (sae_password_track_fail(hapd, sta)) {
1762 wpa_printf(MSG_DEBUG,
1763 "SAE: Reject mismatching Confirm so that another password can be attempted by "
1764 MACSTR,
1765 MAC2STR(sta->addr));
1766 if (sta->sae->tmp)
1767 sta->sae->tmp->
1768 try_other_password = 1;
1769 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1770 goto reply;
1771 }
Sunil Ravi77d572f2023-01-17 23:58:31 +00001772 resp = WLAN_STATUS_CHALLENGE_FAIL;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001773 goto reply;
1774 }
Sunil Ravi876a49b2025-02-03 19:18:32 +00001775 sae_password_track_success(hapd, sta);
Roshan Pius3a1667e2018-07-03 15:17:14 -07001776 sta->sae->rc = peer_send_confirm;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001777 }
Sunil Ravi7f769292024-07-23 22:21:32 +00001778 resp = sae_sm_step(hapd, sta, auth_transaction,
Hai Shalomc3565922019-10-28 11:58:20 -07001779 status_code, 0, &sta_removed);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001780 } else {
1781 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
1782 HOSTAPD_LEVEL_DEBUG,
Hai Shalom81f62d82019-07-22 12:10:00 -07001783 "unexpected SAE authentication transaction %u (status=%u (%s))",
1784 auth_transaction, status_code,
1785 status2str(status_code));
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001786 if (status_code != WLAN_STATUS_SUCCESS)
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001787 goto remove_sta;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001788 resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
1789 }
1790
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001791reply:
Hai Shalom5f92bc92019-04-18 11:54:11 -07001792 if (!sta_removed && resp != WLAN_STATUS_SUCCESS) {
Hai Shalom021b0b52019-04-10 11:17:58 -07001793 pos = mgmt->u.auth.variable;
1794 end = ((const u8 *) mgmt) + len;
1795
1796 /* Copy the Finite Cyclic Group field from the request if we
1797 * rejected it as unsupported group. */
1798 if (resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
1799 !data && end - pos >= 2)
1800 data = wpabuf_alloc_copy(pos, 2);
1801
Sunil Ravi7f769292024-07-23 22:21:32 +00001802 send_auth_reply(hapd, sta, sta->addr,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001803 WLAN_AUTH_SAE,
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001804 auth_transaction, resp,
1805 data ? wpabuf_head(data) : (u8 *) "",
Roshan Pius3a1667e2018-07-03 15:17:14 -07001806 data ? wpabuf_len(data) : 0, "auth-sae");
Sunil Ravic0f5d412024-09-11 22:12:49 +00001807 sae_sme_send_external_auth_status(hapd, sta, resp);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001808 }
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001809
1810remove_sta:
Hai Shalom60840252021-02-19 19:02:11 -08001811 if (auth_transaction == 1)
1812 success_status = sae_status_success(hapd, status_code);
1813 else
1814 success_status = status_code == WLAN_STATUS_SUCCESS;
Hai Shalom5f92bc92019-04-18 11:54:11 -07001815 if (!sta_removed && sta->added_unassoc &&
Hai Shalom60840252021-02-19 19:02:11 -08001816 (resp != WLAN_STATUS_SUCCESS || !success_status)) {
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001817 hostapd_drv_sta_remove(hapd, sta->addr);
1818 sta->added_unassoc = 0;
1819 }
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001820 wpabuf_free(data);
1821}
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001822
1823
1824/**
1825 * auth_sae_init_committed - Send COMMIT and start SAE in committed state
1826 * @hapd: BSS data for the device initiating the authentication
1827 * @sta: the peer to which commit authentication frame is sent
1828 *
1829 * This function implements Init event handling (IEEE Std 802.11-2012,
1830 * 11.3.8.6.3) in which initial COMMIT message is sent. Prior to calling, the
1831 * sta->sae structure should be initialized appropriately via a call to
1832 * sae_prepare_commit().
1833 */
1834int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta)
1835{
1836 int ret;
1837
1838 if (!sta->sae || !sta->sae->tmp)
1839 return -1;
1840
1841 if (sta->sae->state != SAE_NOTHING)
1842 return -1;
1843
Sunil Ravi7f769292024-07-23 22:21:32 +00001844 ret = auth_sae_send_commit(hapd, sta, 0, -1);
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001845 if (ret)
1846 return -1;
1847
Roshan Pius3a1667e2018-07-03 15:17:14 -07001848 sae_set_state(sta, SAE_COMMITTED, "Init and sent commit");
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001849 sta->sae->sync = 0;
1850 sae_set_retransmit_timer(hapd, sta);
1851
1852 return 0;
1853}
1854
Hai Shalom021b0b52019-04-10 11:17:58 -07001855
1856void auth_sae_process_commit(void *eloop_ctx, void *user_ctx)
1857{
1858 struct hostapd_data *hapd = eloop_ctx;
1859 struct hostapd_sae_commit_queue *q;
1860 unsigned int queue_len;
1861
1862 q = dl_list_first(&hapd->sae_commit_queue,
1863 struct hostapd_sae_commit_queue, list);
1864 if (!q)
1865 return;
1866 wpa_printf(MSG_DEBUG,
1867 "SAE: Process next available message from queue");
1868 dl_list_del(&q->list);
1869 handle_auth(hapd, (const struct ieee80211_mgmt *) q->msg, q->len,
1870 q->rssi, 1);
1871 os_free(q);
1872
1873 if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
1874 return;
1875 queue_len = dl_list_len(&hapd->sae_commit_queue);
1876 eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
1877 hapd, NULL);
1878}
1879
1880
1881static void auth_sae_queue(struct hostapd_data *hapd,
1882 const struct ieee80211_mgmt *mgmt, size_t len,
1883 int rssi)
1884{
1885 struct hostapd_sae_commit_queue *q, *q2;
1886 unsigned int queue_len;
1887 const struct ieee80211_mgmt *mgmt2;
1888
1889 queue_len = dl_list_len(&hapd->sae_commit_queue);
1890 if (queue_len >= 15) {
1891 wpa_printf(MSG_DEBUG,
1892 "SAE: No more room in message queue - drop the new frame from "
1893 MACSTR, MAC2STR(mgmt->sa));
1894 return;
1895 }
1896
1897 wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from "
1898 MACSTR " for processing (queue_len %u)", MAC2STR(mgmt->sa),
1899 queue_len);
1900 q = os_zalloc(sizeof(*q) + len);
1901 if (!q)
1902 return;
1903 q->rssi = rssi;
1904 q->len = len;
1905 os_memcpy(q->msg, mgmt, len);
1906
1907 /* Check whether there is already a queued Authentication frame from the
1908 * same station with the same transaction number and if so, replace that
1909 * queue entry with the new one. This avoids issues with a peer that
1910 * sends multiple times (e.g., due to frequent SAE retries). There is no
1911 * point in us trying to process the old attempts after a new one has
1912 * obsoleted them. */
1913 dl_list_for_each(q2, &hapd->sae_commit_queue,
1914 struct hostapd_sae_commit_queue, list) {
1915 mgmt2 = (const struct ieee80211_mgmt *) q2->msg;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001916 if (ether_addr_equal(mgmt->sa, mgmt2->sa) &&
Hai Shalom021b0b52019-04-10 11:17:58 -07001917 mgmt->u.auth.auth_transaction ==
1918 mgmt2->u.auth.auth_transaction) {
1919 wpa_printf(MSG_DEBUG,
1920 "SAE: Replace queued message from same STA with same transaction number");
1921 dl_list_add(&q2->list, &q->list);
1922 dl_list_del(&q2->list);
1923 os_free(q2);
1924 goto queued;
1925 }
1926 }
1927
1928 /* No pending identical entry, so add to the end of the queue */
1929 dl_list_add_tail(&hapd->sae_commit_queue, &q->list);
1930
1931queued:
1932 if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
1933 return;
1934 eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
1935 hapd, NULL);
1936}
1937
1938
1939static int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr)
1940{
1941 struct hostapd_sae_commit_queue *q;
1942 const struct ieee80211_mgmt *mgmt;
1943
1944 dl_list_for_each(q, &hapd->sae_commit_queue,
1945 struct hostapd_sae_commit_queue, list) {
1946 mgmt = (const struct ieee80211_mgmt *) q->msg;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001947 if (ether_addr_equal(addr, mgmt->sa))
Hai Shalom021b0b52019-04-10 11:17:58 -07001948 return 1;
1949 }
1950
1951 return 0;
1952}
1953
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001954#endif /* CONFIG_SAE */
1955
1956
Hai Shalomfdcde762020-04-02 11:19:20 -07001957static u16 wpa_res_to_status_code(enum wpa_validate_result res)
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001958{
Hai Shalomfdcde762020-04-02 11:19:20 -07001959 switch (res) {
1960 case WPA_IE_OK:
1961 return WLAN_STATUS_SUCCESS;
1962 case WPA_INVALID_IE:
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001963 return WLAN_STATUS_INVALID_IE;
Hai Shalomfdcde762020-04-02 11:19:20 -07001964 case WPA_INVALID_GROUP:
1965 return WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
1966 case WPA_INVALID_PAIRWISE:
1967 return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
1968 case WPA_INVALID_AKMP:
1969 return WLAN_STATUS_AKMP_NOT_VALID;
1970 case WPA_NOT_ENABLED:
1971 return WLAN_STATUS_INVALID_IE;
1972 case WPA_ALLOC_FAIL:
1973 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1974 case WPA_MGMT_FRAME_PROTECTION_VIOLATION:
1975 return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
1976 case WPA_INVALID_MGMT_GROUP_CIPHER:
1977 return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
1978 case WPA_INVALID_MDIE:
1979 return WLAN_STATUS_INVALID_MDIE;
1980 case WPA_INVALID_PROTO:
1981 return WLAN_STATUS_INVALID_IE;
1982 case WPA_INVALID_PMKID:
1983 return WLAN_STATUS_INVALID_PMKID;
1984 case WPA_DENIED_OTHER_REASON:
1985 return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
1986 }
1987 return WLAN_STATUS_INVALID_IE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001988}
1989
1990
1991#ifdef CONFIG_FILS
1992
1993static void handle_auth_fils_finish(struct hostapd_data *hapd,
1994 struct sta_info *sta, u16 resp,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001995 struct wpabuf *data, int pub);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001996
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001997void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
1998 const u8 *pos, size_t len, u16 auth_alg,
1999 u16 auth_transaction, u16 status_code,
2000 void (*cb)(struct hostapd_data *hapd,
2001 struct sta_info *sta, u16 resp,
2002 struct wpabuf *data, int pub))
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002003{
2004 u16 resp = WLAN_STATUS_SUCCESS;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002005 const u8 *end;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002006 struct ieee802_11_elems elems;
Hai Shalomfdcde762020-04-02 11:19:20 -07002007 enum wpa_validate_result res;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002008 struct wpa_ie_data rsn;
2009 struct rsn_pmksa_cache_entry *pmksa = NULL;
2010
2011 if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
2012 return;
2013
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002014 end = pos + len;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002015
2016 wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
2017 pos, end - pos);
2018
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002019 /* TODO: FILS PK */
2020#ifdef CONFIG_FILS_SK_PFS
2021 if (auth_alg == WLAN_AUTH_FILS_SK_PFS) {
2022 u16 group;
2023 struct wpabuf *pub;
2024 size_t elem_len;
2025
2026 /* Using FILS PFS */
2027
2028 /* Finite Cyclic Group */
2029 if (end - pos < 2) {
2030 wpa_printf(MSG_DEBUG,
2031 "FILS: No room for Finite Cyclic Group");
2032 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2033 goto fail;
2034 }
2035 group = WPA_GET_LE16(pos);
2036 pos += 2;
2037 if (group != hapd->conf->fils_dh_group) {
2038 wpa_printf(MSG_DEBUG,
2039 "FILS: Unsupported Finite Cyclic Group: %u (expected %u)",
2040 group, hapd->conf->fils_dh_group);
2041 resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
2042 goto fail;
2043 }
2044
2045 crypto_ecdh_deinit(sta->fils_ecdh);
2046 sta->fils_ecdh = crypto_ecdh_init(group);
2047 if (!sta->fils_ecdh) {
2048 wpa_printf(MSG_INFO,
2049 "FILS: Could not initialize ECDH with group %d",
2050 group);
2051 resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
2052 goto fail;
2053 }
2054
2055 pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
2056 if (!pub) {
2057 wpa_printf(MSG_DEBUG,
2058 "FILS: Failed to derive ECDH public key");
2059 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2060 goto fail;
2061 }
2062 elem_len = wpabuf_len(pub);
2063 wpabuf_free(pub);
2064
2065 /* Element */
2066 if ((size_t) (end - pos) < elem_len) {
2067 wpa_printf(MSG_DEBUG, "FILS: No room for Element");
2068 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2069 goto fail;
2070 }
2071
2072 wpabuf_free(sta->fils_g_sta);
2073 sta->fils_g_sta = wpabuf_alloc_copy(pos, elem_len);
2074 wpabuf_clear_free(sta->fils_dh_ss);
2075 sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1,
2076 pos, elem_len);
2077 if (!sta->fils_dh_ss) {
2078 wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
2079 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2080 goto fail;
2081 }
2082 wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss);
2083 pos += elem_len;
2084 } else {
2085 crypto_ecdh_deinit(sta->fils_ecdh);
2086 sta->fils_ecdh = NULL;
2087 wpabuf_clear_free(sta->fils_dh_ss);
2088 sta->fils_dh_ss = NULL;
2089 }
2090#endif /* CONFIG_FILS_SK_PFS */
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002091
2092 wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
2093 if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
2094 wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
2095 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2096 goto fail;
2097 }
2098
2099 /* RSNE */
2100 wpa_hexdump(MSG_DEBUG, "FILS: RSN element",
2101 elems.rsn_ie, elems.rsn_ie_len);
2102 if (!elems.rsn_ie ||
2103 wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
2104 &rsn) < 0) {
2105 wpa_printf(MSG_DEBUG, "FILS: No valid RSN element");
2106 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2107 goto fail;
2108 }
2109
2110 if (!sta->wpa_sm)
2111 sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr,
2112 NULL);
2113 if (!sta->wpa_sm) {
2114 wpa_printf(MSG_DEBUG,
2115 "FILS: Failed to initialize RSN state machine");
2116 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2117 goto fail;
2118 }
2119
Sunil Ravic0f5d412024-09-11 22:12:49 +00002120 wpa_auth_set_rsn_selection(sta->wpa_sm, elems.rsn_selection,
2121 elems.rsn_selection_len);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002122 res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
Hai Shalom021b0b52019-04-10 11:17:58 -07002123 hapd->iface->freq,
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002124 elems.rsn_ie - 2, elems.rsn_ie_len + 2,
Hai Shalomc3565922019-10-28 11:58:20 -07002125 elems.rsnxe ? elems.rsnxe - 2 : NULL,
2126 elems.rsnxe ? elems.rsnxe_len + 2 : 0,
Sunil Ravi876a49b2025-02-03 19:18:32 +00002127 elems.mdie, elems.mdie_len, NULL, 0, NULL,
2128 ap_sta_is_mld(hapd, sta));
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002129 resp = wpa_res_to_status_code(res);
2130 if (resp != WLAN_STATUS_SUCCESS)
2131 goto fail;
2132
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002133 if (!elems.fils_nonce) {
2134 wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
2135 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2136 goto fail;
2137 }
2138 wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce,
2139 FILS_NONCE_LEN);
2140 os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN);
2141
2142 /* PMKID List */
2143 if (rsn.pmkid && rsn.num_pmkid > 0) {
2144 u8 num;
2145 const u8 *pmkid;
2146
2147 wpa_hexdump(MSG_DEBUG, "FILS: PMKID List",
2148 rsn.pmkid, rsn.num_pmkid * PMKID_LEN);
2149
2150 pmkid = rsn.pmkid;
2151 num = rsn.num_pmkid;
2152 while (num) {
2153 wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN);
2154 pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr,
2155 pmkid);
2156 if (pmksa)
2157 break;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002158 pmksa = wpa_auth_pmksa_get_fils_cache_id(hapd->wpa_auth,
2159 sta->addr,
2160 pmkid);
2161 if (pmksa)
2162 break;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002163 pmkid += PMKID_LEN;
2164 num--;
2165 }
2166 }
2167 if (pmksa && wpa_auth_sta_key_mgmt(sta->wpa_sm) != pmksa->akmp) {
2168 wpa_printf(MSG_DEBUG,
2169 "FILS: Matching PMKSA cache entry has different AKMP (0x%x != 0x%x) - ignore",
2170 wpa_auth_sta_key_mgmt(sta->wpa_sm), pmksa->akmp);
2171 pmksa = NULL;
2172 }
2173 if (pmksa)
2174 wpa_printf(MSG_DEBUG, "FILS: Found matching PMKSA cache entry");
2175
2176 /* FILS Session */
2177 if (!elems.fils_session) {
2178 wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
2179 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2180 goto fail;
2181 }
2182 wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
2183 FILS_SESSION_LEN);
2184 os_memcpy(sta->fils_session, elems.fils_session, FILS_SESSION_LEN);
2185
Hai Shalomfdcde762020-04-02 11:19:20 -07002186 /* Wrapped Data */
2187 if (elems.wrapped_data) {
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002188 wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data",
Hai Shalomfdcde762020-04-02 11:19:20 -07002189 elems.wrapped_data,
2190 elems.wrapped_data_len);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002191 if (!pmksa) {
2192#ifndef CONFIG_NO_RADIUS
2193 if (!sta->eapol_sm) {
2194 sta->eapol_sm =
2195 ieee802_1x_alloc_eapol_sm(hapd, sta);
2196 }
2197 wpa_printf(MSG_DEBUG,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002198 "FILS: Forward EAP-Initiate/Re-auth to authentication server");
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002199 ieee802_1x_encapsulate_radius(
Hai Shalomfdcde762020-04-02 11:19:20 -07002200 hapd, sta, elems.wrapped_data,
2201 elems.wrapped_data_len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002202 sta->fils_pending_cb = cb;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002203 wpa_printf(MSG_DEBUG,
2204 "FILS: Will send Authentication frame once the response from authentication server is available");
2205 sta->flags |= WLAN_STA_PENDING_FILS_ERP;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002206 /* Calculate pending PMKID here so that we do not need
2207 * to maintain a copy of the EAP-Initiate/Reauth
2208 * message. */
2209 if (fils_pmkid_erp(wpa_auth_sta_key_mgmt(sta->wpa_sm),
Hai Shalomfdcde762020-04-02 11:19:20 -07002210 elems.wrapped_data,
2211 elems.wrapped_data_len,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002212 sta->fils_erp_pmkid) == 0)
2213 sta->fils_erp_pmkid_set = 1;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002214 return;
2215#else /* CONFIG_NO_RADIUS */
2216 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2217 goto fail;
2218#endif /* CONFIG_NO_RADIUS */
2219 }
2220 }
2221
2222fail:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002223 if (cb) {
2224 struct wpabuf *data;
2225 int pub = 0;
2226
2227 data = prepare_auth_resp_fils(hapd, sta, &resp, pmksa, NULL,
2228 NULL, 0, &pub);
2229 if (!data) {
2230 wpa_printf(MSG_DEBUG,
2231 "%s: prepare_auth_resp_fils() returned failure",
2232 __func__);
2233 }
2234
2235 cb(hapd, sta, resp, data, pub);
2236 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002237}
2238
2239
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002240static struct wpabuf *
2241prepare_auth_resp_fils(struct hostapd_data *hapd,
2242 struct sta_info *sta, u16 *resp,
2243 struct rsn_pmksa_cache_entry *pmksa,
2244 struct wpabuf *erp_resp,
2245 const u8 *msk, size_t msk_len,
2246 int *is_pub)
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002247{
2248 u8 fils_nonce[FILS_NONCE_LEN];
2249 size_t ielen;
2250 struct wpabuf *data = NULL;
2251 const u8 *ie;
2252 u8 *ie_buf = NULL;
2253 const u8 *pmk = NULL;
2254 size_t pmk_len = 0;
Paul Stewart092955c2017-02-06 09:13:09 -08002255 u8 pmk_buf[PMK_LEN_MAX];
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002256 struct wpabuf *pub = NULL;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002257
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002258 if (*resp != WLAN_STATUS_SUCCESS)
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002259 goto fail;
2260
2261 ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
2262 if (!ie) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002263 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002264 goto fail;
2265 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002266
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002267 if (pmksa) {
2268 /* Add PMKID of the selected PMKSA into RSNE */
2269 ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN);
2270 if (!ie_buf) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002271 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002272 goto fail;
2273 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002274
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002275 os_memcpy(ie_buf, ie, ielen);
Sunil Ravib0ac25f2024-07-12 01:42:03 +00002276 if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid, true) < 0) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002277 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002278 goto fail;
2279 }
2280 ie = ie_buf;
2281 }
2282
2283 if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002284 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002285 goto fail;
2286 }
2287 wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
2288 fils_nonce, FILS_NONCE_LEN);
2289
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002290#ifdef CONFIG_FILS_SK_PFS
2291 if (sta->fils_dh_ss && sta->fils_ecdh) {
2292 pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
2293 if (!pub) {
2294 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2295 goto fail;
2296 }
2297 }
2298#endif /* CONFIG_FILS_SK_PFS */
2299
2300 data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0));
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002301 if (!data) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002302 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002303 goto fail;
2304 }
2305
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002306 /* TODO: FILS PK */
2307#ifdef CONFIG_FILS_SK_PFS
2308 if (pub) {
2309 /* Finite Cyclic Group */
2310 wpabuf_put_le16(data, hapd->conf->fils_dh_group);
2311
2312 /* Element */
2313 wpabuf_put_buf(data, pub);
2314 }
2315#endif /* CONFIG_FILS_SK_PFS */
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002316
2317 /* RSNE */
2318 wpabuf_put_data(data, ie, ielen);
2319
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002320 /* MDE when using FILS+FT (already included in ie,ielen with RSNE) */
2321
2322#ifdef CONFIG_IEEE80211R_AP
2323 if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) {
2324 /* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */
2325 int res;
2326
Sunil Ravi77d572f2023-01-17 23:58:31 +00002327 res = wpa_auth_write_fte(hapd->wpa_auth, sta->wpa_sm,
Roshan Pius3a1667e2018-07-03 15:17:14 -07002328 wpabuf_put(data, 0),
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002329 wpabuf_tailroom(data));
2330 if (res < 0) {
2331 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2332 goto fail;
2333 }
2334 wpabuf_put(data, res);
2335 }
2336#endif /* CONFIG_IEEE80211R_AP */
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002337
2338 /* FILS Nonce */
2339 wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
2340 wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */
2341 /* Element ID Extension */
2342 wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE);
2343 wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN);
2344
2345 /* FILS Session */
2346 wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
2347 wpabuf_put_u8(data, 1 + FILS_SESSION_LEN); /* Length */
2348 /* Element ID Extension */
2349 wpabuf_put_u8(data, WLAN_EID_EXT_FILS_SESSION);
2350 wpabuf_put_data(data, sta->fils_session, FILS_SESSION_LEN);
2351
Hai Shalomfdcde762020-04-02 11:19:20 -07002352 /* Wrapped Data */
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002353 if (!pmksa && erp_resp) {
2354 wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
2355 wpabuf_put_u8(data, 1 + wpabuf_len(erp_resp)); /* Length */
2356 /* Element ID Extension */
Hai Shalomfdcde762020-04-02 11:19:20 -07002357 wpabuf_put_u8(data, WLAN_EID_EXT_WRAPPED_DATA);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002358 wpabuf_put_buf(data, erp_resp);
2359
Paul Stewart092955c2017-02-06 09:13:09 -08002360 if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm),
2361 msk, msk_len, sta->fils_snonce, fils_nonce,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002362 sta->fils_dh_ss ?
2363 wpabuf_head(sta->fils_dh_ss) : NULL,
2364 sta->fils_dh_ss ?
2365 wpabuf_len(sta->fils_dh_ss) : 0,
2366 pmk_buf, &pmk_len)) {
Paul Stewart092955c2017-02-06 09:13:09 -08002367 wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002368 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Paul Stewart092955c2017-02-06 09:13:09 -08002369 wpabuf_free(data);
2370 data = NULL;
2371 goto fail;
2372 }
2373 pmk = pmk_buf;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002374
2375 /* Don't use DHss in PTK derivation if PMKSA caching is not
2376 * used. */
2377 wpabuf_clear_free(sta->fils_dh_ss);
2378 sta->fils_dh_ss = NULL;
2379
2380 if (sta->fils_erp_pmkid_set) {
2381 /* TODO: get PMKLifetime from WPA parameters */
2382 unsigned int dot11RSNAConfigPMKLifetime = 43200;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002383 int session_timeout;
2384
2385 session_timeout = dot11RSNAConfigPMKLifetime;
2386 if (sta->session_timeout_set) {
2387 struct os_reltime now, diff;
2388
2389 os_get_reltime(&now);
2390 os_reltime_sub(&sta->session_timeout, &now,
2391 &diff);
2392 session_timeout = diff.sec;
2393 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002394
2395 sta->fils_erp_pmkid_set = 0;
Hai Shalom81f62d82019-07-22 12:10:00 -07002396 wpa_auth_add_fils_pmk_pmkid(sta->wpa_sm, pmk, pmk_len,
2397 sta->fils_erp_pmkid);
Hai Shalom021b0b52019-04-10 11:17:58 -07002398 if (!hapd->conf->disable_pmksa_caching &&
2399 wpa_auth_pmksa_add2(
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002400 hapd->wpa_auth, sta->addr,
2401 pmk, pmk_len,
2402 sta->fils_erp_pmkid,
Roshan Pius3a1667e2018-07-03 15:17:14 -07002403 session_timeout,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00002404 wpa_auth_sta_key_mgmt(sta->wpa_sm),
Sunil Ravi876a49b2025-02-03 19:18:32 +00002405 NULL, ap_sta_is_mld(hapd, sta)) < 0) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002406 wpa_printf(MSG_ERROR,
2407 "FILS: Failed to add PMKSA cache entry based on ERP");
2408 }
2409 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002410 } else if (pmksa) {
2411 pmk = pmksa->pmk;
2412 pmk_len = pmksa->pmk_len;
2413 }
2414
2415 if (!pmk) {
2416 wpa_printf(MSG_DEBUG, "FILS: No PMK available");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002417 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002418 wpabuf_free(data);
2419 data = NULL;
2420 goto fail;
2421 }
2422
2423 if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002424 sta->fils_snonce, fils_nonce,
2425 sta->fils_dh_ss ?
2426 wpabuf_head(sta->fils_dh_ss) : NULL,
2427 sta->fils_dh_ss ?
2428 wpabuf_len(sta->fils_dh_ss) : 0,
2429 sta->fils_g_sta, pub) < 0) {
2430 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002431 wpabuf_free(data);
2432 data = NULL;
2433 goto fail;
2434 }
2435
2436fail:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002437 if (is_pub)
2438 *is_pub = pub != NULL;
2439 os_free(ie_buf);
2440 wpabuf_free(pub);
2441 wpabuf_clear_free(sta->fils_dh_ss);
2442 sta->fils_dh_ss = NULL;
2443#ifdef CONFIG_FILS_SK_PFS
2444 crypto_ecdh_deinit(sta->fils_ecdh);
2445 sta->fils_ecdh = NULL;
2446#endif /* CONFIG_FILS_SK_PFS */
2447 return data;
2448}
2449
2450
2451static void handle_auth_fils_finish(struct hostapd_data *hapd,
2452 struct sta_info *sta, u16 resp,
2453 struct wpabuf *data, int pub)
2454{
2455 u16 auth_alg;
2456
2457 auth_alg = (pub ||
2458 resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ?
2459 WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
Sunil Ravi7f769292024-07-23 22:21:32 +00002460 send_auth_reply(hapd, sta, sta->addr, auth_alg, 2, resp,
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002461 data ? wpabuf_head(data) : (u8 *) "",
Roshan Pius3a1667e2018-07-03 15:17:14 -07002462 data ? wpabuf_len(data) : 0, "auth-fils-finish");
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002463 wpabuf_free(data);
2464
2465 if (resp == WLAN_STATUS_SUCCESS) {
2466 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
2467 HOSTAPD_LEVEL_DEBUG,
2468 "authentication OK (FILS)");
2469 sta->flags |= WLAN_STA_AUTH;
2470 wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002471 sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002472 mlme_authenticate_indication(hapd, sta);
2473 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002474}
2475
2476
2477void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
2478 struct sta_info *sta, int success,
2479 struct wpabuf *erp_resp,
2480 const u8 *msk, size_t msk_len)
2481{
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002482 u16 resp;
Hai Shalom60840252021-02-19 19:02:11 -08002483 u32 flags = sta->flags;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002484
Hai Shalom60840252021-02-19 19:02:11 -08002485 sta->flags &= ~(WLAN_STA_PENDING_FILS_ERP |
2486 WLAN_STA_PENDING_PASN_FILS_ERP);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002487
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002488 resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE;
Hai Shalom60840252021-02-19 19:02:11 -08002489
2490 if (flags & WLAN_STA_PENDING_FILS_ERP) {
2491 struct wpabuf *data;
2492 int pub = 0;
2493
2494 if (!sta->fils_pending_cb)
2495 return;
2496
2497 data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp,
2498 msk, msk_len, &pub);
2499 if (!data) {
2500 wpa_printf(MSG_DEBUG,
2501 "%s: prepare_auth_resp_fils() failure",
2502 __func__);
2503 }
2504 sta->fils_pending_cb(hapd, sta, resp, data, pub);
2505#ifdef CONFIG_PASN
2506 } else if (flags & WLAN_STA_PENDING_PASN_FILS_ERP) {
2507 pasn_fils_auth_resp(hapd, sta, resp, erp_resp,
2508 msk, msk_len);
2509#endif /* CONFIG_PASN */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002510 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002511}
2512
2513#endif /* CONFIG_FILS */
2514
2515
Hai Shalomfdcde762020-04-02 11:19:20 -07002516static int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
2517 const u8 *msg, size_t len,
2518 struct radius_sta *info)
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002519{
2520 int res;
2521
Hai Shalomfdcde762020-04-02 11:19:20 -07002522 res = hostapd_allowed_address(hapd, addr, msg, len, info, 0);
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002523
2524 if (res == HOSTAPD_ACL_REJECT) {
Hai Shalomfdcde762020-04-02 11:19:20 -07002525 wpa_printf(MSG_DEBUG, "Station " MACSTR
2526 " not allowed to authenticate",
2527 MAC2STR(addr));
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002528 return HOSTAPD_ACL_REJECT;
2529 }
2530
2531 if (res == HOSTAPD_ACL_PENDING) {
2532 wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR
2533 " waiting for an external authentication",
2534 MAC2STR(addr));
2535 /* Authentication code will re-send the authentication frame
2536 * after it has received (and cached) information from the
2537 * external source. */
2538 return HOSTAPD_ACL_PENDING;
2539 }
2540
2541 return res;
2542}
2543
2544
Sunil Ravia04bd252022-05-02 22:54:18 -07002545int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
2546 int res, struct radius_sta *info)
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002547{
Hai Shalomfdcde762020-04-02 11:19:20 -07002548 u32 session_timeout = info->session_timeout;
2549 u32 acct_interim_interval = info->acct_interim_interval;
2550 struct vlan_description *vlan_id = &info->vlan_id;
2551 struct hostapd_sta_wpa_psk_short *psk = info->psk;
2552 char *identity = info->identity;
2553 char *radius_cui = info->radius_cui;
2554
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002555 if (vlan_id->notempty &&
2556 !hostapd_vlan_valid(hapd->conf->vlan, vlan_id)) {
2557 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
2558 HOSTAPD_LEVEL_INFO,
2559 "Invalid VLAN %d%s received from RADIUS server",
2560 vlan_id->untagged,
2561 vlan_id->tagged[0] ? "+" : "");
2562 return -1;
2563 }
2564 if (ap_sta_set_vlan(hapd, sta, vlan_id) < 0)
2565 return -1;
2566 if (sta->vlan_id)
2567 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
2568 HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
2569
2570 hostapd_free_psk_list(sta->psk);
Hai Shalomfdcde762020-04-02 11:19:20 -07002571 if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED)
2572 hostapd_copy_psk_list(&sta->psk, psk);
2573 else
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002574 sta->psk = NULL;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002575
Roshan Pius3a1667e2018-07-03 15:17:14 -07002576 os_free(sta->identity);
Hai Shalomfdcde762020-04-02 11:19:20 -07002577 if (identity)
2578 sta->identity = os_strdup(identity);
2579 else
2580 sta->identity = NULL;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002581
2582 os_free(sta->radius_cui);
Hai Shalomfdcde762020-04-02 11:19:20 -07002583 if (radius_cui)
2584 sta->radius_cui = os_strdup(radius_cui);
2585 else
2586 sta->radius_cui = NULL;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002587
2588 if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
2589 sta->acct_interim_interval = acct_interim_interval;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002590 if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) {
2591 sta->session_timeout_set = 1;
2592 os_get_reltime(&sta->session_timeout);
2593 sta->session_timeout.sec += session_timeout;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002594 ap_sta_session_timeout(hapd, sta, session_timeout);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002595 } else {
2596 sta->session_timeout_set = 0;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002597 ap_sta_no_session_timeout(hapd, sta);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002598 }
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002599
2600 return 0;
2601}
2602
2603
Hai Shalom60840252021-02-19 19:02:11 -08002604#ifdef CONFIG_PASN
Hai Shalom60840252021-02-19 19:02:11 -08002605#ifdef CONFIG_FILS
2606
Hai Shalom60840252021-02-19 19:02:11 -08002607static void pasn_fils_auth_resp(struct hostapd_data *hapd,
2608 struct sta_info *sta, u16 status,
2609 struct wpabuf *erp_resp,
2610 const u8 *msk, size_t msk_len)
2611{
2612 struct pasn_data *pasn = sta->pasn;
Sunil Ravi77d572f2023-01-17 23:58:31 +00002613 struct pasn_fils *fils = &pasn->fils;
Hai Shalom60840252021-02-19 19:02:11 -08002614 u8 pmk[PMK_LEN_MAX];
2615 size_t pmk_len;
2616 int ret;
2617
2618 wpa_printf(MSG_DEBUG, "PASN: FILS: Handle AS response - status=%u",
2619 status);
2620
2621 if (status != WLAN_STATUS_SUCCESS)
2622 goto fail;
2623
2624 if (!pasn->secret) {
2625 wpa_printf(MSG_DEBUG, "PASN: FILS: Missing secret");
2626 goto fail;
2627 }
2628
2629 if (random_get_bytes(fils->anonce, FILS_NONCE_LEN) < 0) {
2630 wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ANonce");
2631 goto fail;
2632 }
2633
2634 wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS ANonce",
2635 fils->anonce, FILS_NONCE_LEN);
2636
Sunil Ravi99c035e2024-07-12 01:42:03 +00002637 ret = fils_rmsk_to_pmk(pasn_get_akmp(pasn), msk, msk_len, fils->nonce,
Hai Shalom60840252021-02-19 19:02:11 -08002638 fils->anonce, NULL, 0, pmk, &pmk_len);
2639 if (ret) {
2640 wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
2641 goto fail;
2642 }
2643
2644 ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr,
2645 wpabuf_head(pasn->secret),
2646 wpabuf_len(pasn->secret),
Sunil Ravi99c035e2024-07-12 01:42:03 +00002647 pasn_get_ptk(sta->pasn), pasn_get_akmp(sta->pasn),
Sunil Ravic0f5d412024-09-11 22:12:49 +00002648 pasn_get_cipher(sta->pasn), sta->pasn->kdk_len,
2649 sta->pasn->kek_len);
Hai Shalom60840252021-02-19 19:02:11 -08002650 if (ret) {
2651 wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PTK");
2652 goto fail;
2653 }
2654
Sunil Ravi89eba102022-09-13 21:04:37 -07002655 if (pasn->secure_ltf) {
Sunil Ravi99c035e2024-07-12 01:42:03 +00002656 ret = wpa_ltf_keyseed(pasn_get_ptk(pasn), pasn_get_akmp(pasn),
2657 pasn_get_cipher(pasn));
Sunil Ravi89eba102022-09-13 21:04:37 -07002658 if (ret) {
2659 wpa_printf(MSG_DEBUG,
2660 "PASN: FILS: Failed to derive LTF keyseed");
2661 goto fail;
2662 }
2663 }
2664
Hai Shalom60840252021-02-19 19:02:11 -08002665 wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
2666
2667 wpabuf_free(pasn->secret);
2668 pasn->secret = NULL;
2669
2670 fils->erp_resp = erp_resp;
Sunil Ravi77d572f2023-01-17 23:58:31 +00002671 ret = handle_auth_pasn_resp(sta->pasn, hapd->own_addr, sta->addr, NULL,
2672 WLAN_STATUS_SUCCESS);
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00002673 wpabuf_free(pasn->frame);
2674 pasn->frame = NULL;
Hai Shalom60840252021-02-19 19:02:11 -08002675 fils->erp_resp = NULL;
2676
2677 if (ret) {
2678 wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to send response");
2679 goto fail;
2680 }
2681
2682 fils->state = PASN_FILS_STATE_COMPLETE;
2683 return;
2684fail:
2685 ap_free_sta(hapd, sta);
2686}
2687
2688
2689static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta,
2690 struct wpabuf *wd)
2691{
Hai Shaloma20dcd72022-02-04 13:43:00 -08002692#ifdef CONFIG_NO_RADIUS
2693 wpa_printf(MSG_DEBUG, "PASN: FILS: RADIUS is not configured. Fail");
2694 return -1;
2695#else /* CONFIG_NO_RADIUS */
Hai Shalom60840252021-02-19 19:02:11 -08002696 struct pasn_data *pasn = sta->pasn;
Sunil Ravi77d572f2023-01-17 23:58:31 +00002697 struct pasn_fils *fils = &pasn->fils;
Hai Shalom60840252021-02-19 19:02:11 -08002698 struct ieee802_11_elems elems;
2699 struct wpa_ie_data rsne_data;
2700 struct wpabuf *fils_wd;
2701 const u8 *data;
2702 size_t buf_len;
2703 u16 alg, seq, status;
2704 int ret;
2705
2706 if (fils->state != PASN_FILS_STATE_NONE) {
2707 wpa_printf(MSG_DEBUG, "PASN: FILS: Not expecting wrapped data");
2708 return -1;
2709 }
2710
2711 if (!wd) {
2712 wpa_printf(MSG_DEBUG, "PASN: FILS: No wrapped data");
2713 return -1;
2714 }
2715
2716 data = wpabuf_head_u8(wd);
2717 buf_len = wpabuf_len(wd);
2718
2719 if (buf_len < 6) {
Hai Shaloma20dcd72022-02-04 13:43:00 -08002720 wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short. len=%zu",
Hai Shalom60840252021-02-19 19:02:11 -08002721 buf_len);
2722 return -1;
2723 }
2724
2725 alg = WPA_GET_LE16(data);
2726 seq = WPA_GET_LE16(data + 2);
2727 status = WPA_GET_LE16(data + 4);
2728
2729 wpa_printf(MSG_DEBUG, "PASN: FILS: alg=%u, seq=%u, status=%u",
2730 alg, seq, status);
2731
2732 if (alg != WLAN_AUTH_FILS_SK || seq != 1 ||
2733 status != WLAN_STATUS_SUCCESS) {
2734 wpa_printf(MSG_DEBUG,
2735 "PASN: FILS: Dropping peer authentication");
2736 return -1;
2737 }
2738
2739 data += 6;
2740 buf_len -= 6;
2741
2742 if (ieee802_11_parse_elems(data, buf_len, &elems, 1) == ParseFailed) {
2743 wpa_printf(MSG_DEBUG, "PASN: FILS: Could not parse elements");
2744 return -1;
2745 }
2746
2747 if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce ||
Sunil Ravi77d572f2023-01-17 23:58:31 +00002748 !elems.wrapped_data || !elems.fils_session) {
Hai Shalom60840252021-02-19 19:02:11 -08002749 wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs");
2750 return -1;
2751 }
2752
2753 ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
2754 &rsne_data);
2755 if (ret) {
Sunil Ravi77d572f2023-01-17 23:58:31 +00002756 wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RSNE");
Hai Shalom60840252021-02-19 19:02:11 -08002757 return -1;
2758 }
2759
2760 ret = wpa_pasn_validate_rsne(&rsne_data);
2761 if (ret) {
2762 wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE");
2763 return -1;
2764 }
2765
2766 if (rsne_data.num_pmkid) {
2767 wpa_printf(MSG_DEBUG,
2768 "PASN: FILS: Not expecting PMKID in RSNE");
2769 return -1;
2770 }
2771
2772 wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", elems.fils_nonce,
2773 FILS_NONCE_LEN);
2774 os_memcpy(fils->nonce, elems.fils_nonce, FILS_NONCE_LEN);
2775
2776 wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", elems.fils_session,
2777 FILS_SESSION_LEN);
2778 os_memcpy(fils->session, elems.fils_session, FILS_SESSION_LEN);
2779
Sunil Ravib0ac25f2024-07-12 01:42:03 +00002780 fils_wd = ieee802_11_defrag(elems.wrapped_data, elems.wrapped_data_len,
2781 true);
Hai Shalom60840252021-02-19 19:02:11 -08002782
2783 if (!fils_wd) {
2784 wpa_printf(MSG_DEBUG, "PASN: FILS: Missing wrapped data");
2785 return -1;
2786 }
2787
2788 if (!sta->eapol_sm)
2789 sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
2790
2791 wpa_printf(MSG_DEBUG,
2792 "PASN: FILS: Forward EAP-Initiate/Re-auth to AS");
2793
2794 ieee802_1x_encapsulate_radius(hapd, sta, wpabuf_head(fils_wd),
2795 wpabuf_len(fils_wd));
2796
2797 sta->flags |= WLAN_STA_PENDING_PASN_FILS_ERP;
2798
2799 fils->state = PASN_FILS_STATE_PENDING_AS;
2800
2801 /*
2802 * Calculate pending PMKID here so that we do not need to maintain a
2803 * copy of the EAP-Initiate/Reautt message.
2804 */
Sunil Ravi99c035e2024-07-12 01:42:03 +00002805 fils_pmkid_erp(pasn_get_akmp(pasn),
2806 wpabuf_head(fils_wd), wpabuf_len(fils_wd),
Hai Shalom60840252021-02-19 19:02:11 -08002807 fils->erp_pmkid);
2808
2809 wpabuf_free(fils_wd);
2810 return 0;
Hai Shaloma20dcd72022-02-04 13:43:00 -08002811#endif /* CONFIG_NO_RADIUS */
Hai Shalom60840252021-02-19 19:02:11 -08002812}
2813
2814#endif /* CONFIG_FILS */
2815
2816
Sunil Ravi77d572f2023-01-17 23:58:31 +00002817static int hapd_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len,
2818 int noack, unsigned int freq, unsigned int wait)
Hai Shalom60840252021-02-19 19:02:11 -08002819{
Sunil Ravi77d572f2023-01-17 23:58:31 +00002820 struct hostapd_data *hapd = ctx;
2821
2822 return hostapd_drv_send_mlme(hapd, data, data_len, 0, NULL, 0, 0);
2823}
2824
2825
2826static void hapd_initialize_pasn(struct hostapd_data *hapd,
2827 struct sta_info *sta)
2828{
2829 struct pasn_data *pasn = sta->pasn;
2830
Sunil Ravi99c035e2024-07-12 01:42:03 +00002831 pasn_register_callbacks(pasn, hapd, hapd_pasn_send_mlme, NULL);
2832 pasn_set_bssid(pasn, hapd->own_addr);
2833 pasn_set_own_addr(pasn, hapd->own_addr);
2834 pasn_set_peer_addr(pasn, sta->addr);
2835 pasn_set_wpa_key_mgmt(pasn, hapd->conf->wpa_key_mgmt);
2836 pasn_set_rsn_pairwise(pasn, hapd->conf->rsn_pairwise);
Sunil Ravi77d572f2023-01-17 23:58:31 +00002837 pasn->pasn_groups = hapd->conf->pasn_groups;
Sunil Ravi640215c2023-06-28 23:08:09 +00002838 pasn->noauth = hapd->conf->pasn_noauth;
Sunil Ravi99c035e2024-07-12 01:42:03 +00002839 if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP)
2840 pasn_enable_kdk_derivation(pasn);
2841
Sunil Ravi77d572f2023-01-17 23:58:31 +00002842#ifdef CONFIG_TESTING_OPTIONS
2843 pasn->corrupt_mic = hapd->conf->pasn_corrupt_mic;
2844 if (hapd->conf->force_kdk_derivation)
Sunil Ravi99c035e2024-07-12 01:42:03 +00002845 pasn_enable_kdk_derivation(pasn);
Sunil Ravi77d572f2023-01-17 23:58:31 +00002846#endif /* CONFIG_TESTING_OPTIONS */
2847 pasn->use_anti_clogging = use_anti_clogging(hapd);
Sunil Ravi99c035e2024-07-12 01:42:03 +00002848 pasn_set_password(pasn, sae_get_password(hapd, sta, NULL, NULL,
2849 &pasn->pt, NULL));
Sunil Ravi77d572f2023-01-17 23:58:31 +00002850 pasn->rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &pasn->rsn_ie_len);
Sunil Ravi99c035e2024-07-12 01:42:03 +00002851 pasn_set_rsnxe_ie(pasn, hostapd_wpa_ie(hapd, WLAN_EID_RSNX));
Sunil Ravi77d572f2023-01-17 23:58:31 +00002852 pasn->disable_pmksa_caching = hapd->conf->disable_pmksa_caching;
Sunil Ravi99c035e2024-07-12 01:42:03 +00002853 pasn_set_responder_pmksa(pasn,
2854 wpa_auth_get_pmksa_cache(hapd->wpa_auth));
Sunil Ravi77d572f2023-01-17 23:58:31 +00002855
2856 pasn->comeback_after = hapd->conf->pasn_comeback_after;
2857 pasn->comeback_idx = hapd->comeback_idx;
2858 pasn->comeback_key = hapd->comeback_key;
2859 pasn->comeback_pending_idx = hapd->comeback_pending_idx;
Hai Shalom60840252021-02-19 19:02:11 -08002860}
2861
2862
Sunil Ravi89eba102022-09-13 21:04:37 -07002863static int pasn_set_keys_from_cache(struct hostapd_data *hapd,
2864 const u8 *own_addr, const u8 *sta_addr,
2865 int cipher, int akmp)
2866{
2867 struct ptksa_cache_entry *entry;
2868
2869 entry = ptksa_cache_get(hapd->ptksa, sta_addr, cipher);
2870 if (!entry) {
2871 wpa_printf(MSG_DEBUG, "PASN: peer " MACSTR
2872 " not present in PTKSA cache", MAC2STR(sta_addr));
2873 return -1;
2874 }
2875
Sunil Ravib0ac25f2024-07-12 01:42:03 +00002876 if (!ether_addr_equal(entry->own_addr, own_addr)) {
Sunil Ravi89eba102022-09-13 21:04:37 -07002877 wpa_printf(MSG_DEBUG,
2878 "PASN: own addr " MACSTR " and PTKSA entry own addr "
2879 MACSTR " differ",
2880 MAC2STR(own_addr), MAC2STR(entry->own_addr));
2881 return -1;
2882 }
2883
2884 wpa_printf(MSG_DEBUG, "PASN: " MACSTR " present in PTKSA cache",
2885 MAC2STR(sta_addr));
2886 hostapd_drv_set_secure_ranging_ctx(hapd, own_addr, sta_addr, cipher,
2887 entry->ptk.tk_len, entry->ptk.tk,
2888 entry->ptk.ltf_keyseed_len,
2889 entry->ptk.ltf_keyseed, 0);
2890
2891 return 0;
2892}
2893
2894
Sunil Ravi77d572f2023-01-17 23:58:31 +00002895static void hapd_pasn_update_params(struct hostapd_data *hapd,
2896 struct sta_info *sta,
2897 const struct ieee80211_mgmt *mgmt,
2898 size_t len)
Hai Shalom60840252021-02-19 19:02:11 -08002899{
Sunil Ravi77d572f2023-01-17 23:58:31 +00002900 struct pasn_data *pasn = sta->pasn;
Hai Shalom60840252021-02-19 19:02:11 -08002901 struct ieee802_11_elems elems;
2902 struct wpa_ie_data rsn_data;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00002903#ifdef CONFIG_FILS
Hai Shalom60840252021-02-19 19:02:11 -08002904 struct wpa_pasn_params_data pasn_params;
Hai Shalom60840252021-02-19 19:02:11 -08002905 struct wpabuf *wrapped_data = NULL;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00002906#endif /* CONFIG_FILS */
Sunil Ravi99c035e2024-07-12 01:42:03 +00002907 int akmp;
Hai Shalom60840252021-02-19 19:02:11 -08002908
2909 if (ieee802_11_parse_elems(mgmt->u.auth.variable,
2910 len - offsetof(struct ieee80211_mgmt,
2911 u.auth.variable),
2912 &elems, 0) == ParseFailed) {
2913 wpa_printf(MSG_DEBUG,
2914 "PASN: Failed parsing Authentication frame");
Sunil Ravi77d572f2023-01-17 23:58:31 +00002915 return;
Hai Shalom60840252021-02-19 19:02:11 -08002916 }
2917
Sunil Ravi77d572f2023-01-17 23:58:31 +00002918 if (!elems.rsn_ie ||
2919 wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
2920 &rsn_data)) {
2921 wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE");
2922 return;
2923 }
2924
2925 if (!(rsn_data.key_mgmt & pasn->wpa_key_mgmt) ||
2926 !(rsn_data.pairwise_cipher & pasn->rsn_pairwise)) {
2927 wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
2928 return;
2929 }
2930
Sunil Ravi99c035e2024-07-12 01:42:03 +00002931 pasn_set_akmp(pasn, rsn_data.key_mgmt);
2932 pasn_set_cipher(pasn, rsn_data.pairwise_cipher);
Sunil Ravi77d572f2023-01-17 23:58:31 +00002933
Sunil Ravi7f769292024-07-23 22:21:32 +00002934 if (pasn->derive_kdk &&
2935 !ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
2936 WLAN_RSNX_CAPAB_SECURE_LTF))
2937 pasn_disable_kdk_derivation(pasn);
2938#ifdef CONFIG_TESTING_OPTIONS
2939 if (hapd->conf->force_kdk_derivation)
2940 pasn_enable_kdk_derivation(pasn);
2941#endif /* CONFIG_TESTING_OPTIONS */
Sunil Ravi99c035e2024-07-12 01:42:03 +00002942 akmp = pasn_get_akmp(pasn);
2943
2944 if (wpa_key_mgmt_ft(akmp) && rsn_data.num_pmkid) {
Sunil Ravi77d572f2023-01-17 23:58:31 +00002945#ifdef CONFIG_IEEE80211R_AP
2946 pasn->pmk_r1_len = 0;
2947 wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr,
2948 rsn_data.pmkid,
2949 pasn->pmk_r1, &pasn->pmk_r1_len, NULL,
2950 NULL, NULL, NULL,
2951 NULL, NULL, NULL);
2952#endif /* CONFIG_IEEE80211R_AP */
2953 }
2954#ifdef CONFIG_FILS
Sunil Ravi99c035e2024-07-12 01:42:03 +00002955 if (akmp != WPA_KEY_MGMT_FILS_SHA256 &&
2956 akmp != WPA_KEY_MGMT_FILS_SHA384)
Sunil Ravi77d572f2023-01-17 23:58:31 +00002957 return;
2958 if (!elems.pasn_params ||
2959 wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
2960 elems.pasn_params_len + 3,
2961 false, &pasn_params)) {
Hai Shalom60840252021-02-19 19:02:11 -08002962 wpa_printf(MSG_DEBUG,
Sunil Ravi77d572f2023-01-17 23:58:31 +00002963 "PASN: Failed validation of PASN Parameters element");
2964 return;
Hai Shalom60840252021-02-19 19:02:11 -08002965 }
Hai Shalom60840252021-02-19 19:02:11 -08002966 if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +00002967 wrapped_data = ieee802_11_defrag(elems.wrapped_data,
2968 elems.wrapped_data_len, true);
Hai Shalom60840252021-02-19 19:02:11 -08002969 if (!wrapped_data) {
2970 wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
Sunil Ravi77d572f2023-01-17 23:58:31 +00002971 return;
Hai Shalom60840252021-02-19 19:02:11 -08002972 }
Sunil Ravi77d572f2023-01-17 23:58:31 +00002973 if (pasn_wd_handle_fils(hapd, sta, wrapped_data))
2974 wpa_printf(MSG_DEBUG,
2975 "PASN: Failed processing FILS wrapped data");
2976 else
2977 pasn->fils_wd_valid = true;
Hai Shalom60840252021-02-19 19:02:11 -08002978 }
Sunil Ravi77d572f2023-01-17 23:58:31 +00002979 wpabuf_free(wrapped_data);
2980#endif /* CONFIG_FILS */
Hai Shalom60840252021-02-19 19:02:11 -08002981}
2982
2983
2984static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta,
2985 const struct ieee80211_mgmt *mgmt, size_t len,
2986 u16 trans_seq, u16 status)
2987{
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00002988 int ret;
2989#ifdef CONFIG_P2P
2990 struct ieee802_11_elems elems;
2991
2992 if (len < 24) {
2993 wpa_printf(MSG_DEBUG, "PASN: Too short Management frame");
2994 return;
2995 }
2996
2997 if (ieee802_11_parse_elems(mgmt->u.auth.variable,
2998 len - offsetof(struct ieee80211_mgmt,
2999 u.auth.variable),
3000 &elems, 1) == ParseFailed) {
3001 wpa_printf(MSG_DEBUG,
3002 "PASN: Failed parsing Authentication frame");
3003 return;
3004 }
3005
3006 if ((hapd->conf->p2p & (P2P_ENABLED | P2P_GROUP_OWNER)) ==
3007 (P2P_ENABLED | P2P_GROUP_OWNER) &&
3008 hapd->p2p && elems.p2p2_ie && elems.p2p2_ie_len) {
3009 p2p_pasn_auth_rx(hapd->p2p, mgmt, len, hapd->iface->freq);
3010 return;
3011 }
3012#endif /* CONFIG_P2P */
3013
Hai Shalom60840252021-02-19 19:02:11 -08003014 if (hapd->conf->wpa != WPA_PROTO_RSN) {
3015 wpa_printf(MSG_INFO, "PASN: RSN is not configured");
3016 return;
3017 }
3018
3019 wpa_printf(MSG_INFO, "PASN authentication: sta=" MACSTR,
3020 MAC2STR(sta->addr));
3021
3022 if (trans_seq == 1) {
3023 if (sta->pasn) {
3024 wpa_printf(MSG_DEBUG,
3025 "PASN: Not expecting transaction == 1");
3026 return;
3027 }
3028
3029 if (status != WLAN_STATUS_SUCCESS) {
3030 wpa_printf(MSG_DEBUG,
3031 "PASN: Failure status in transaction == 1");
3032 return;
3033 }
3034
Sunil Ravi99c035e2024-07-12 01:42:03 +00003035 sta->pasn = pasn_data_init();
Hai Shalom60840252021-02-19 19:02:11 -08003036 if (!sta->pasn) {
3037 wpa_printf(MSG_DEBUG,
3038 "PASN: Failed to allocate PASN context");
3039 return;
3040 }
3041
Sunil Ravi77d572f2023-01-17 23:58:31 +00003042 hapd_initialize_pasn(hapd, sta);
3043
3044 hapd_pasn_update_params(hapd, sta, mgmt, len);
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00003045 ret = handle_auth_pasn_1(sta->pasn, hapd->own_addr, sta->addr,
3046 mgmt, len, false);
3047 wpabuf_free(sta->pasn->frame);
3048 sta->pasn->frame = NULL;
3049 if (ret < 0)
Sunil Ravi77d572f2023-01-17 23:58:31 +00003050 ap_free_sta(hapd, sta);
Hai Shalom60840252021-02-19 19:02:11 -08003051 } else if (trans_seq == 3) {
3052 if (!sta->pasn) {
3053 wpa_printf(MSG_DEBUG,
3054 "PASN: Not expecting transaction == 3");
3055 return;
3056 }
3057
3058 if (status != WLAN_STATUS_SUCCESS) {
3059 wpa_printf(MSG_DEBUG,
3060 "PASN: Failure status in transaction == 3");
3061 ap_free_sta_pasn(hapd, sta);
3062 return;
3063 }
3064
Sunil Ravi77d572f2023-01-17 23:58:31 +00003065 if (handle_auth_pasn_3(sta->pasn, hapd->own_addr,
3066 sta->addr, mgmt, len) == 0) {
3067 ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr,
Sunil Ravi99c035e2024-07-12 01:42:03 +00003068 pasn_get_cipher(sta->pasn), 43200,
3069 pasn_get_ptk(sta->pasn), NULL, NULL,
3070 pasn_get_akmp(sta->pasn));
Sunil Ravi77d572f2023-01-17 23:58:31 +00003071
3072 pasn_set_keys_from_cache(hapd, hapd->own_addr,
Sunil Ravi99c035e2024-07-12 01:42:03 +00003073 sta->addr,
3074 pasn_get_cipher(sta->pasn),
3075 pasn_get_akmp(sta->pasn));
Sunil Ravi77d572f2023-01-17 23:58:31 +00003076 }
3077 ap_free_sta(hapd, sta);
Hai Shalom60840252021-02-19 19:02:11 -08003078 } else {
3079 wpa_printf(MSG_DEBUG,
3080 "PASN: Invalid transaction %u - ignore", trans_seq);
3081 }
3082}
3083
3084#endif /* CONFIG_PASN */
3085
3086
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003087static void handle_auth(struct hostapd_data *hapd,
Hai Shalom74f70d42019-02-11 14:42:39 -08003088 const struct ieee80211_mgmt *mgmt, size_t len,
Hai Shalom021b0b52019-04-10 11:17:58 -07003089 int rssi, int from_queue)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003090{
3091 u16 auth_alg, auth_transaction, status_code;
3092 u16 resp = WLAN_STATUS_SUCCESS;
3093 struct sta_info *sta = NULL;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003094 int res, reply_res;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003095 u16 fc;
3096 const u8 *challenge = NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003097 u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
3098 size_t resp_ies_len = 0;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003099 u16 seq_ctrl;
Hai Shalomfdcde762020-04-02 11:19:20 -07003100 struct radius_sta rad_info;
Sunil Ravi7f769292024-07-23 22:21:32 +00003101 const u8 *dst, *sa;
Sunil Ravi99c035e2024-07-12 01:42:03 +00003102#ifdef CONFIG_IEEE80211BE
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003103 bool mld_sta = false;
Sunil Ravi99c035e2024-07-12 01:42:03 +00003104#endif /* CONFIG_IEEE80211BE */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003105
3106 if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08003107 wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
3108 (unsigned long) len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003109 return;
3110 }
3111
Dmitry Shmidt8da800a2013-04-24 12:57:01 -07003112#ifdef CONFIG_TESTING_OPTIONS
Dmitry Shmidt7832adb2014-04-29 10:53:02 -07003113 if (hapd->iconf->ignore_auth_probability > 0.0 &&
Dmitry Shmidt8da800a2013-04-24 12:57:01 -07003114 drand48() < hapd->iconf->ignore_auth_probability) {
3115 wpa_printf(MSG_INFO,
3116 "TESTING: ignoring auth frame from " MACSTR,
3117 MAC2STR(mgmt->sa));
3118 return;
3119 }
3120#endif /* CONFIG_TESTING_OPTIONS */
3121
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003122 sa = mgmt->sa;
3123#ifdef CONFIG_IEEE80211BE
3124 /*
3125 * Handle MLO authentication before the station is added to hostapd and
3126 * the driver so that the station MLD MAC address would be used in both
3127 * hostapd and the driver.
3128 */
3129 sa = hostapd_process_ml_auth(hapd, mgmt, len);
3130 if (sa)
3131 mld_sta = true;
3132 else
3133 sa = mgmt->sa;
3134#endif /* CONFIG_IEEE80211BE */
3135
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003136 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
3137 auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
3138 status_code = le_to_host16(mgmt->u.auth.status_code);
3139 fc = le_to_host16(mgmt->frame_control);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003140 seq_ctrl = le_to_host16(mgmt->seq_ctrl);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003141
3142 if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) +
3143 2 + WLAN_AUTH_CHALLENGE_LEN &&
3144 mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE &&
3145 mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN)
3146 challenge = &mgmt->u.auth.variable[2];
3147
3148 wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003149 "auth_transaction=%d status_code=%d wep=%d%s "
Hai Shalom021b0b52019-04-10 11:17:58 -07003150 "seq_ctrl=0x%x%s%s",
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003151 MAC2STR(sa), auth_alg, auth_transaction,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003152 status_code, !!(fc & WLAN_FC_ISWEP),
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003153 challenge ? " challenge" : "",
Hai Shalom021b0b52019-04-10 11:17:58 -07003154 seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "",
3155 from_queue ? " (from queue)" : "");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003156
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08003157#ifdef CONFIG_NO_RC4
3158 if (auth_alg == WLAN_AUTH_SHARED_KEY) {
3159 wpa_printf(MSG_INFO,
3160 "Unsupported authentication algorithm (%d)",
3161 auth_alg);
3162 resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
3163 goto fail;
3164 }
3165#endif /* CONFIG_NO_RC4 */
3166
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003167 if (hapd->tkip_countermeasures) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003168 wpa_printf(MSG_DEBUG,
3169 "Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication");
3170 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003171 goto fail;
3172 }
3173
3174 if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) &&
3175 auth_alg == WLAN_AUTH_OPEN) ||
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003176#ifdef CONFIG_IEEE80211R_AP
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08003177 (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003178 auth_alg == WLAN_AUTH_FT) ||
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003179#endif /* CONFIG_IEEE80211R_AP */
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08003180#ifdef CONFIG_SAE
Sunil Ravi7f769292024-07-23 22:21:32 +00003181 (hapd->conf->wpa &&
3182 wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt |
3183 hapd->conf->rsn_override_key_mgmt |
3184 hapd->conf->rsn_override_key_mgmt_2) &&
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08003185 auth_alg == WLAN_AUTH_SAE) ||
3186#endif /* CONFIG_SAE */
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003187#ifdef CONFIG_FILS
3188 (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
3189 auth_alg == WLAN_AUTH_FILS_SK) ||
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003190 (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
3191 hapd->conf->fils_dh_group &&
3192 auth_alg == WLAN_AUTH_FILS_SK_PFS) ||
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003193#endif /* CONFIG_FILS */
Hai Shalom60840252021-02-19 19:02:11 -08003194#ifdef CONFIG_PASN
3195 (hapd->conf->wpa &&
3196 (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) &&
3197 auth_alg == WLAN_AUTH_PASN) ||
3198#endif /* CONFIG_PASN */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003199 ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
3200 auth_alg == WLAN_AUTH_SHARED_KEY))) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08003201 wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)",
3202 auth_alg);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003203 resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
3204 goto fail;
3205 }
3206
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08003207 if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE ||
Hai Shalom60840252021-02-19 19:02:11 -08003208#ifdef CONFIG_PASN
3209 (auth_alg == WLAN_AUTH_PASN && auth_transaction == 3) ||
3210#endif /* CONFIG_PASN */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003211 (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08003212 wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)",
3213 auth_transaction);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003214 resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
3215 goto fail;
3216 }
3217
Sunil Ravib0ac25f2024-07-12 01:42:03 +00003218 if (ether_addr_equal(mgmt->sa, hapd->own_addr)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08003219 wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003220 MAC2STR(sa));
3221 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
3222 goto fail;
3223 }
3224
Sunil Ravi99c035e2024-07-12 01:42:03 +00003225#ifdef CONFIG_IEEE80211BE
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003226 if (mld_sta &&
Sunil Ravib0ac25f2024-07-12 01:42:03 +00003227 (ether_addr_equal(sa, hapd->own_addr) ||
Sunil Ravi99c035e2024-07-12 01:42:03 +00003228 ether_addr_equal(sa, hapd->mld->mld_addr))) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003229 wpa_printf(MSG_INFO,
3230 "Station " MACSTR " not allowed to authenticate",
3231 MAC2STR(sa));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003232 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
3233 goto fail;
3234 }
Sunil Ravi99c035e2024-07-12 01:42:03 +00003235#endif /* CONFIG_IEEE80211BE */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003236
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08003237 if (hapd->conf->no_auth_if_seen_on) {
3238 struct hostapd_data *other;
3239
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003240 other = sta_track_seen_on(hapd->iface, sa,
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08003241 hapd->conf->no_auth_if_seen_on);
3242 if (other) {
3243 u8 *pos;
3244 u32 info;
3245 u8 op_class, channel, phytype;
3246
3247 wpa_printf(MSG_DEBUG, "%s: Reject authentication from "
3248 MACSTR " since STA has been seen on %s",
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003249 hapd->conf->iface, MAC2STR(sa),
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08003250 hapd->conf->no_auth_if_seen_on);
3251
3252 resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION;
3253 pos = &resp_ies[0];
3254 *pos++ = WLAN_EID_NEIGHBOR_REPORT;
3255 *pos++ = 13;
3256 os_memcpy(pos, other->own_addr, ETH_ALEN);
3257 pos += ETH_ALEN;
3258 info = 0; /* TODO: BSSID Information */
3259 WPA_PUT_LE32(pos, info);
3260 pos += 4;
3261 if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
3262 phytype = 8; /* dmg */
3263 else if (other->iconf->ieee80211ac)
3264 phytype = 9; /* vht */
3265 else if (other->iconf->ieee80211n)
3266 phytype = 7; /* ht */
3267 else if (other->iconf->hw_mode ==
3268 HOSTAPD_MODE_IEEE80211A)
3269 phytype = 4; /* ofdm */
3270 else if (other->iconf->hw_mode ==
3271 HOSTAPD_MODE_IEEE80211G)
3272 phytype = 6; /* erp */
3273 else
3274 phytype = 5; /* hrdsss */
3275 if (ieee80211_freq_to_channel_ext(
3276 hostapd_hw_get_freq(other,
3277 other->iconf->channel),
3278 other->iconf->secondary_channel,
3279 other->iconf->ieee80211ac,
3280 &op_class, &channel) == NUM_HOSTAPD_MODES) {
3281 op_class = 0;
3282 channel = other->iconf->channel;
3283 }
3284 *pos++ = op_class;
3285 *pos++ = channel;
3286 *pos++ = phytype;
3287 resp_ies_len = pos - &resp_ies[0];
3288 goto fail;
3289 }
3290 }
3291
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003292 res = ieee802_11_allowed_address(hapd, sa, (const u8 *) mgmt, len,
Hai Shalomfdcde762020-04-02 11:19:20 -07003293 &rad_info);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003294 if (res == HOSTAPD_ACL_REJECT) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003295 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
3296 "Ignore Authentication frame from " MACSTR
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003297 " due to ACL reject", MAC2STR(sa));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003298 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
3299 goto fail;
3300 }
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08003301 if (res == HOSTAPD_ACL_PENDING)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003302 return;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003303
Hai Shalom021b0b52019-04-10 11:17:58 -07003304#ifdef CONFIG_SAE
3305 if (auth_alg == WLAN_AUTH_SAE && !from_queue &&
3306 (auth_transaction == 1 ||
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003307 (auth_transaction == 2 && auth_sae_queued_addr(hapd, sa)))) {
Hai Shalom021b0b52019-04-10 11:17:58 -07003308 /* Handle SAE Authentication commit message through a queue to
3309 * provide more control for postponing the needed heavy
3310 * processing under a possible DoS attack scenario. In addition,
3311 * queue SAE Authentication confirm message if there happens to
3312 * be a queued commit message from the same peer. This is needed
3313 * to avoid reordering Authentication frames within the same
3314 * SAE exchange. */
3315 auth_sae_queue(hapd, mgmt, len, rssi);
3316 return;
3317 }
3318#endif /* CONFIG_SAE */
3319
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003320 sta = ap_get_sta(hapd, sa);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003321 if (sta) {
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003322 sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
Hai Shalom74f70d42019-02-11 14:42:39 -08003323 sta->ft_over_ds = 0;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003324 if ((fc & WLAN_FC_RETRY) &&
3325 sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
3326 sta->last_seq_ctrl == seq_ctrl &&
3327 sta->last_subtype == WLAN_FC_STYPE_AUTH) {
3328 hostapd_logger(hapd, sta->addr,
3329 HOSTAPD_MODULE_IEEE80211,
3330 HOSTAPD_LEVEL_DEBUG,
3331 "Drop repeated authentication frame seq_ctrl=0x%x",
3332 seq_ctrl);
3333 return;
3334 }
Hai Shalom60840252021-02-19 19:02:11 -08003335#ifdef CONFIG_PASN
3336 if (auth_alg == WLAN_AUTH_PASN &&
3337 (sta->flags & WLAN_STA_ASSOC)) {
3338 wpa_printf(MSG_DEBUG,
3339 "PASN: auth: Existing station: " MACSTR,
3340 MAC2STR(sta->addr));
3341 return;
3342 }
3343#endif /* CONFIG_PASN */
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003344 } else {
3345#ifdef CONFIG_MESH
3346 if (hapd->conf->mesh & MESH_ENABLED) {
3347 /* if the mesh peer is not available, we don't do auth.
3348 */
3349 wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003350 " not yet known - drop Authentication frame",
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003351 MAC2STR(sa));
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003352 /*
3353 * Save a copy of the frame so that it can be processed
3354 * if a new peer entry is added shortly after this.
3355 */
3356 wpabuf_free(hapd->mesh_pending_auth);
3357 hapd->mesh_pending_auth = wpabuf_alloc_copy(mgmt, len);
3358 os_get_reltime(&hapd->mesh_pending_auth_time);
3359 return;
3360 }
3361#endif /* CONFIG_MESH */
3362
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003363 sta = ap_sta_add(hapd, sa);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003364 if (!sta) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003365 wpa_printf(MSG_DEBUG, "ap_sta_add() failed");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003366 resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
3367 goto fail;
3368 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003369 }
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003370
3371#ifdef CONFIG_IEEE80211BE
Sunil Ravi99c035e2024-07-12 01:42:03 +00003372 /* Set the non-AP MLD information based on the initial Authentication
3373 * frame. Once the STA entry has been added to the driver, the driver
3374 * will translate addresses in the frame and we need to avoid overriding
3375 * peer_addr based on mgmt->sa which would have been translated to the
3376 * MLD MAC address. */
3377 if (!sta->added_unassoc && auth_transaction == 1) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +00003378 ap_sta_free_sta_profile(&sta->mld_info);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003379 os_memset(&sta->mld_info, 0, sizeof(sta->mld_info));
3380
3381 if (mld_sta) {
3382 u8 link_id = hapd->mld_link_id;
3383
Sunil Ravib0ac25f2024-07-12 01:42:03 +00003384 ap_sta_set_mld(sta, true);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003385 sta->mld_assoc_link_id = link_id;
3386
3387 /*
3388 * Set the MLD address as the station address and the
3389 * station addresses.
3390 */
3391 os_memcpy(sta->mld_info.common_info.mld_addr, sa,
3392 ETH_ALEN);
3393 os_memcpy(sta->mld_info.links[link_id].peer_addr,
3394 mgmt->sa, ETH_ALEN);
3395 os_memcpy(sta->mld_info.links[link_id].local_addr,
3396 hapd->own_addr, ETH_ALEN);
3397 }
3398 }
3399#endif /* CONFIG_IEEE80211BE */
3400
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003401 sta->last_seq_ctrl = seq_ctrl;
3402 sta->last_subtype = WLAN_FC_STYPE_AUTH;
Hai Shalom74f70d42019-02-11 14:42:39 -08003403#ifdef CONFIG_MBO
3404 sta->auth_rssi = rssi;
3405#endif /* CONFIG_MBO */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003406
Hai Shalomfdcde762020-04-02 11:19:20 -07003407 res = ieee802_11_set_radius_info(hapd, sta, res, &rad_info);
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08003408 if (res) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003409 wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed");
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003410 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
3411 goto fail;
3412 }
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07003413
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003414 sta->flags &= ~WLAN_STA_PREAUTH;
3415 ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
3416
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003417 /*
3418 * If the driver supports full AP client state, add a station to the
3419 * driver before sending authentication reply to make sure the driver
3420 * has resources, and not to go through the entire authentication and
3421 * association handshake, and fail it at the end.
3422 *
3423 * If this is not the first transaction, in a multi-step authentication
3424 * algorithm, the station already exists in the driver
3425 * (sta->added_unassoc = 1) so skip it.
3426 *
3427 * In mesh mode, the station was already added to the driver when the
3428 * NEW_PEER_CANDIDATE event is received.
Dmitry Shmidtabb90a32016-12-05 15:34:39 -08003429 *
3430 * If PMF was negotiated for the existing association, skip this to
3431 * avoid dropping the STA entry and the associated keys. This is needed
3432 * to allow the original connection work until the attempt can complete
3433 * (re)association, so that unprotected Authentication frame cannot be
3434 * used to bypass PMF protection.
Hai Shalom60840252021-02-19 19:02:11 -08003435 *
3436 * PASN authentication does not require adding/removing station to the
3437 * driver so skip this flow in case of PASN authentication.
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003438 */
3439 if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) &&
Dmitry Shmidtabb90a32016-12-05 15:34:39 -08003440 (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) &&
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003441 !(hapd->conf->mesh & MESH_ENABLED) &&
Hai Shalom60840252021-02-19 19:02:11 -08003442 !(sta->added_unassoc) && auth_alg != WLAN_AUTH_PASN) {
Hai Shalomb755a2a2020-04-23 21:49:02 -07003443 if (ap_sta_re_add(hapd, sta) < 0) {
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003444 resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
3445 goto fail;
3446 }
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003447 }
3448
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003449 switch (auth_alg) {
3450 case WLAN_AUTH_OPEN:
3451 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
3452 HOSTAPD_LEVEL_DEBUG,
3453 "authentication OK (open system)");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003454 sta->flags |= WLAN_STA_AUTH;
3455 wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
3456 sta->auth_alg = WLAN_AUTH_OPEN;
3457 mlme_authenticate_indication(hapd, sta);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003458 break;
Hai Shalomfdcde762020-04-02 11:19:20 -07003459#ifdef CONFIG_WEP
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08003460#ifndef CONFIG_NO_RC4
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003461 case WLAN_AUTH_SHARED_KEY:
3462 resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
3463 fc & WLAN_FC_ISWEP);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003464 if (resp != 0)
3465 wpa_printf(MSG_DEBUG,
3466 "auth_shared_key() failed: status=%d", resp);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003467 sta->auth_alg = WLAN_AUTH_SHARED_KEY;
3468 mlme_authenticate_indication(hapd, sta);
3469 if (sta->challenge && auth_transaction == 1) {
3470 resp_ies[0] = WLAN_EID_CHALLENGE;
3471 resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN;
3472 os_memcpy(resp_ies + 2, sta->challenge,
3473 WLAN_AUTH_CHALLENGE_LEN);
3474 resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN;
3475 }
3476 break;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08003477#endif /* CONFIG_NO_RC4 */
Hai Shalomfdcde762020-04-02 11:19:20 -07003478#endif /* CONFIG_WEP */
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003479#ifdef CONFIG_IEEE80211R_AP
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003480 case WLAN_AUTH_FT:
3481 sta->auth_alg = WLAN_AUTH_FT;
3482 if (sta->wpa_sm == NULL)
3483 sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07003484 sta->addr, NULL);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003485 if (sta->wpa_sm == NULL) {
3486 wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
3487 "state machine");
3488 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
3489 goto fail;
3490 }
Sunil Ravi7f769292024-07-23 22:21:32 +00003491 wpa_ft_process_auth(sta->wpa_sm,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003492 auth_transaction, mgmt->u.auth.variable,
3493 len - IEEE80211_HDRLEN -
3494 sizeof(mgmt->u.auth),
3495 handle_auth_ft_finish, hapd);
3496 /* handle_auth_ft_finish() callback will complete auth. */
3497 return;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003498#endif /* CONFIG_IEEE80211R_AP */
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08003499#ifdef CONFIG_SAE
3500 case WLAN_AUTH_SAE:
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003501#ifdef CONFIG_MESH
3502 if (status_code == WLAN_STATUS_SUCCESS &&
3503 hapd->conf->mesh & MESH_ENABLED) {
3504 if (sta->wpa_sm == NULL)
3505 sta->wpa_sm =
3506 wpa_auth_sta_init(hapd->wpa_auth,
3507 sta->addr, NULL);
3508 if (sta->wpa_sm == NULL) {
3509 wpa_printf(MSG_DEBUG,
3510 "SAE: Failed to initialize WPA state machine");
3511 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
3512 goto fail;
3513 }
3514 }
3515#endif /* CONFIG_MESH */
3516 handle_auth_sae(hapd, sta, mgmt, len, auth_transaction,
3517 status_code);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08003518 return;
3519#endif /* CONFIG_SAE */
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003520#ifdef CONFIG_FILS
3521 case WLAN_AUTH_FILS_SK:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003522 case WLAN_AUTH_FILS_SK_PFS:
3523 handle_auth_fils(hapd, sta, mgmt->u.auth.variable,
3524 len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth),
3525 auth_alg, auth_transaction, status_code,
3526 handle_auth_fils_finish);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003527 return;
3528#endif /* CONFIG_FILS */
Hai Shalom60840252021-02-19 19:02:11 -08003529#ifdef CONFIG_PASN
3530 case WLAN_AUTH_PASN:
3531 handle_auth_pasn(hapd, sta, mgmt, len, auth_transaction,
3532 status_code);
3533 return;
3534#endif /* CONFIG_PASN */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003535 }
3536
3537 fail:
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003538 dst = mgmt->sa;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003539
3540#ifdef CONFIG_IEEE80211BE
Sunil Ravi7f769292024-07-23 22:21:32 +00003541 if (ap_sta_is_mld(hapd, sta))
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003542 dst = sta->addr;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003543#endif /* CONFIG_IEEE80211BE */
3544
Sunil Ravi7f769292024-07-23 22:21:32 +00003545 reply_res = send_auth_reply(hapd, sta, dst, auth_alg,
Hai Shaloma20dcd72022-02-04 13:43:00 -08003546 auth_alg == WLAN_AUTH_SAE ?
3547 auth_transaction : auth_transaction + 1,
3548 resp, resp_ies, resp_ies_len,
3549 "handle-auth");
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003550
3551 if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
3552 reply_res != WLAN_STATUS_SUCCESS)) {
3553 hostapd_drv_sta_remove(hapd, sta->addr);
3554 sta->added_unassoc = 0;
3555 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003556}
3557
3558
Sunil Ravi77d572f2023-01-17 23:58:31 +00003559static u8 hostapd_max_bssid_indicator(struct hostapd_data *hapd)
3560{
3561 size_t num_bss_nontx;
3562 u8 max_bssid_ind = 0;
3563
3564 if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1)
3565 return 0;
3566
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00003567 if (hapd->iface->conf->mbssid_max > 0)
3568 num_bss_nontx = hapd->iface->conf->mbssid_max - 1;
3569 else
3570 num_bss_nontx = hapd->iface->conf->num_bss - 1;
Sunil Ravi77d572f2023-01-17 23:58:31 +00003571 while (num_bss_nontx > 0) {
3572 max_bssid_ind++;
3573 num_bss_nontx >>= 1;
3574 }
3575 return max_bssid_ind;
3576}
3577
3578
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003579static u32 hostapd_get_aid_word(struct hostapd_data *hapd,
3580 struct sta_info *sta, int i)
3581{
3582#ifdef CONFIG_IEEE80211BE
3583 u32 aid_word = 0;
3584
3585 /* Do not assign an AID that is in use on any of the affiliated links
3586 * when finding an AID for a non-AP MLD. */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00003587 if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003588 int j;
3589
3590 for (j = 0; j < MAX_NUM_MLD_LINKS; j++) {
3591 struct hostapd_data *link_bss;
3592
3593 if (!sta->mld_info.links[j].valid)
3594 continue;
3595
3596 link_bss = hostapd_mld_get_link_bss(hapd, j);
3597 if (!link_bss) {
3598 /* This shouldn't happen, just skip */
3599 wpa_printf(MSG_ERROR,
3600 "MLD: Failed to get link BSS for AID");
3601 continue;
3602 }
3603
3604 aid_word |= link_bss->sta_aid[i];
3605 }
3606
3607 return aid_word;
3608 }
3609#endif /* CONFIG_IEEE80211BE */
3610
3611 return hapd->sta_aid[i];
3612}
3613
3614
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08003615int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003616{
3617 int i, j = 32, aid;
3618
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003619 /* Transmitted and non-transmitted BSSIDs share the same AID pool, so
3620 * use the shared storage in the transmitted BSS to find the next
3621 * available value. */
3622 hapd = hostapd_mbssid_get_tx_bss(hapd);
3623
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003624 /* get a unique AID */
3625 if (sta->aid > 0) {
3626 wpa_printf(MSG_DEBUG, " old AID %d", sta->aid);
3627 return 0;
3628 }
3629
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -07003630 if (TEST_FAIL())
3631 return -1;
3632
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003633 for (i = 0; i < AID_WORDS; i++) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003634 u32 aid_word = hostapd_get_aid_word(hapd, sta, i);
3635
3636 if (aid_word == (u32) -1)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003637 continue;
3638 for (j = 0; j < 32; j++) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003639 if (!(aid_word & BIT(j)))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003640 break;
3641 }
3642 if (j < 32)
3643 break;
3644 }
3645 if (j == 32)
3646 return -1;
Sunil Ravi77d572f2023-01-17 23:58:31 +00003647 aid = i * 32 + j + (1 << hostapd_max_bssid_indicator(hapd));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003648 if (aid > 2007)
3649 return -1;
3650
3651 sta->aid = aid;
3652 hapd->sta_aid[i] |= BIT(j);
3653 wpa_printf(MSG_DEBUG, " new AID %d", sta->aid);
3654 return 0;
3655}
3656
3657
3658static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta,
3659 const u8 *ssid_ie, size_t ssid_ie_len)
3660{
3661 if (ssid_ie == NULL)
3662 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3663
3664 if (ssid_ie_len != hapd->conf->ssid.ssid_len ||
3665 os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003666 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
3667 HOSTAPD_LEVEL_INFO,
3668 "Station tried to associate with unknown SSID "
Dmitry Shmidt3c479372014-02-04 10:50:36 -08003669 "'%s'", wpa_ssid_txt(ssid_ie, ssid_ie_len));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003670 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3671 }
3672
3673 return WLAN_STATUS_SUCCESS;
3674}
3675
3676
3677static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta,
3678 const u8 *wmm_ie, size_t wmm_ie_len)
3679{
3680 sta->flags &= ~WLAN_STA_WMM;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08003681 sta->qosinfo = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003682 if (wmm_ie && hapd->conf->wmm_enabled) {
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08003683 struct wmm_information_element *wmm;
3684
3685 if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003686 hostapd_logger(hapd, sta->addr,
3687 HOSTAPD_MODULE_WPA,
3688 HOSTAPD_LEVEL_DEBUG,
3689 "invalid WMM element in association "
3690 "request");
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08003691 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3692 }
3693
3694 sta->flags |= WLAN_STA_WMM;
3695 wmm = (struct wmm_information_element *) wmm_ie;
3696 sta->qosinfo = wmm->qos_info;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003697 }
3698 return WLAN_STATUS_SUCCESS;
3699}
3700
Hai Shalom74f70d42019-02-11 14:42:39 -08003701static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta,
3702 const u8 *multi_ap_ie, size_t multi_ap_len)
3703{
Sunil Ravi99c035e2024-07-12 01:42:03 +00003704 struct multi_ap_params multi_ap;
3705 u16 status;
Hai Shalom74f70d42019-02-11 14:42:39 -08003706
3707 sta->flags &= ~WLAN_STA_MULTI_AP;
3708
3709 if (!hapd->conf->multi_ap)
3710 return WLAN_STATUS_SUCCESS;
3711
Sunil Ravi99c035e2024-07-12 01:42:03 +00003712 if (!multi_ap_ie) {
3713 if (!(hapd->conf->multi_ap & FRONTHAUL_BSS)) {
Hai Shalom74f70d42019-02-11 14:42:39 -08003714 hostapd_logger(hapd, sta->addr,
3715 HOSTAPD_MODULE_IEEE80211,
3716 HOSTAPD_LEVEL_INFO,
Sunil Ravi99c035e2024-07-12 01:42:03 +00003717 "Non-Multi-AP STA tries to associate with backhaul-only BSS");
3718 return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
Hai Shalom74f70d42019-02-11 14:42:39 -08003719 }
Sunil Ravi99c035e2024-07-12 01:42:03 +00003720
3721 return WLAN_STATUS_SUCCESS;
Hai Shalom74f70d42019-02-11 14:42:39 -08003722 }
3723
Sunil Ravi99c035e2024-07-12 01:42:03 +00003724 status = check_multi_ap_ie(multi_ap_ie + 4, multi_ap_len - 4,
3725 &multi_ap);
3726 if (status != WLAN_STATUS_SUCCESS)
3727 return status;
3728
3729 if (multi_ap.capability && multi_ap.capability != MULTI_AP_BACKHAUL_STA)
Hai Shalom021b0b52019-04-10 11:17:58 -07003730 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
3731 HOSTAPD_LEVEL_INFO,
3732 "Multi-AP IE with unexpected value 0x%02x",
Sunil Ravi99c035e2024-07-12 01:42:03 +00003733 multi_ap.capability);
Hai Shalom74f70d42019-02-11 14:42:39 -08003734
Sunil Ravi99c035e2024-07-12 01:42:03 +00003735 if (multi_ap.profile == MULTI_AP_PROFILE_1 &&
3736 (hapd->conf->multi_ap_client_disallow &
3737 PROFILE1_CLIENT_ASSOC_DISALLOW)) {
3738 hostapd_logger(hapd, sta->addr,
3739 HOSTAPD_MODULE_IEEE80211,
3740 HOSTAPD_LEVEL_INFO,
3741 "Multi-AP Profile-1 clients not allowed");
3742 return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
3743 }
3744
3745 if (multi_ap.profile >= MULTI_AP_PROFILE_2 &&
3746 (hapd->conf->multi_ap_client_disallow &
3747 PROFILE2_CLIENT_ASSOC_DISALLOW)) {
3748 hostapd_logger(hapd, sta->addr,
3749 HOSTAPD_MODULE_IEEE80211,
3750 HOSTAPD_LEVEL_INFO,
3751 "Multi-AP Profile-2 clients not allowed");
3752 return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
3753 }
3754
3755 if (!(multi_ap.capability & MULTI_AP_BACKHAUL_STA)) {
Hai Shalom021b0b52019-04-10 11:17:58 -07003756 if (hapd->conf->multi_ap & FRONTHAUL_BSS)
3757 return WLAN_STATUS_SUCCESS;
Hai Shalom74f70d42019-02-11 14:42:39 -08003758
Hai Shalom021b0b52019-04-10 11:17:58 -07003759 hostapd_logger(hapd, sta->addr,
3760 HOSTAPD_MODULE_IEEE80211,
3761 HOSTAPD_LEVEL_INFO,
3762 "Non-Multi-AP STA tries to associate with backhaul-only BSS");
3763 return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
Hai Shalom74f70d42019-02-11 14:42:39 -08003764 }
3765
Hai Shalom021b0b52019-04-10 11:17:58 -07003766 if (!(hapd->conf->multi_ap & BACKHAUL_BSS))
3767 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
3768 HOSTAPD_LEVEL_DEBUG,
3769 "Backhaul STA tries to associate with fronthaul-only BSS");
3770
3771 sta->flags |= WLAN_STA_MULTI_AP;
3772 return WLAN_STATUS_SUCCESS;
Hai Shalom74f70d42019-02-11 14:42:39 -08003773}
3774
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003775
3776static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
3777 struct ieee802_11_elems *elems)
3778{
Dmitry Shmidt29333592017-01-09 12:27:11 -08003779 /* Supported rates not used in IEEE 802.11ad/DMG */
3780 if (hapd->iface->current_mode &&
3781 hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD)
3782 return WLAN_STATUS_SUCCESS;
3783
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003784 if (!elems->supp_rates) {
3785 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
3786 HOSTAPD_LEVEL_DEBUG,
3787 "No supported rates element in AssocReq");
3788 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3789 }
3790
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07003791 if (elems->supp_rates_len + elems->ext_supp_rates_len >
3792 sizeof(sta->supported_rates)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003793 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
3794 HOSTAPD_LEVEL_DEBUG,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07003795 "Invalid supported rates element length %d+%d",
3796 elems->supp_rates_len,
3797 elems->ext_supp_rates_len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003798 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3799 }
3800
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07003801 sta->supported_rates_len = merge_byte_arrays(
3802 sta->supported_rates, sizeof(sta->supported_rates),
3803 elems->supp_rates, elems->supp_rates_len,
3804 elems->ext_supp_rates, elems->ext_supp_rates_len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003805
3806 return WLAN_STATUS_SUCCESS;
3807}
3808
3809
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003810#ifdef CONFIG_OWE
3811
3812static int owe_group_supported(struct hostapd_data *hapd, u16 group)
3813{
3814 int i;
3815 int *groups = hapd->conf->owe_groups;
3816
3817 if (group != 19 && group != 20 && group != 21)
3818 return 0;
3819
3820 if (!groups)
3821 return 1;
3822
3823 for (i = 0; groups[i] > 0; i++) {
3824 if (groups[i] == group)
3825 return 1;
3826 }
3827
3828 return 0;
3829}
3830
3831
3832static u16 owe_process_assoc_req(struct hostapd_data *hapd,
3833 struct sta_info *sta, const u8 *owe_dh,
3834 u8 owe_dh_len)
3835{
3836 struct wpabuf *secret, *pub, *hkey;
3837 int res;
3838 u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN];
3839 const char *info = "OWE Key Generation";
3840 const u8 *addr[2];
3841 size_t len[2];
3842 u16 group;
3843 size_t hash_len, prime_len;
3844
3845 if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
3846 wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
3847 return WLAN_STATUS_SUCCESS;
3848 }
3849
3850 group = WPA_GET_LE16(owe_dh);
3851 if (!owe_group_supported(hapd, group)) {
3852 wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group);
3853 return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
3854 }
3855 if (group == 19)
3856 prime_len = 32;
3857 else if (group == 20)
3858 prime_len = 48;
3859 else if (group == 21)
3860 prime_len = 66;
3861 else
3862 return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
3863
Sunil Ravia04bd252022-05-02 22:54:18 -07003864 if (sta->owe_group == group && sta->owe_ecdh) {
3865 /* This is a workaround for mac80211 behavior of retransmitting
3866 * the Association Request frames multiple times if the link
3867 * layer retries (i.e., seq# remains same) fail. The mac80211
3868 * initiated retransmission will use a different seq# and as
3869 * such, will go through duplicate detection. If we were to
3870 * change our DH key for that attempt, there would be two
3871 * different DH shared secrets and the STA would likely select
3872 * the wrong one. */
3873 wpa_printf(MSG_DEBUG,
3874 "OWE: Try to reuse own previous DH key since the STA tried to go through OWE association again");
3875 } else {
3876 crypto_ecdh_deinit(sta->owe_ecdh);
3877 sta->owe_ecdh = crypto_ecdh_init(group);
3878 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003879 if (!sta->owe_ecdh)
3880 return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
3881 sta->owe_group = group;
3882
3883 secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2,
3884 owe_dh_len - 2);
3885 secret = wpabuf_zeropad(secret, prime_len);
3886 if (!secret) {
3887 wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
3888 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3889 }
3890 wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret);
3891
3892 /* prk = HKDF-extract(C | A | group, z) */
3893
3894 pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
3895 if (!pub) {
3896 wpabuf_clear_free(secret);
3897 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3898 }
3899
3900 /* PMKID = Truncate-128(Hash(C | A)) */
3901 addr[0] = owe_dh + 2;
3902 len[0] = owe_dh_len - 2;
3903 addr[1] = wpabuf_head(pub);
3904 len[1] = wpabuf_len(pub);
3905 if (group == 19) {
3906 res = sha256_vector(2, addr, len, pmkid);
3907 hash_len = SHA256_MAC_LEN;
3908 } else if (group == 20) {
3909 res = sha384_vector(2, addr, len, pmkid);
3910 hash_len = SHA384_MAC_LEN;
3911 } else if (group == 21) {
3912 res = sha512_vector(2, addr, len, pmkid);
3913 hash_len = SHA512_MAC_LEN;
3914 } else {
3915 wpabuf_free(pub);
3916 wpabuf_clear_free(secret);
3917 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3918 }
3919 pub = wpabuf_zeropad(pub, prime_len);
3920 if (res < 0 || !pub) {
3921 wpabuf_free(pub);
3922 wpabuf_clear_free(secret);
3923 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3924 }
3925
3926 hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2);
3927 if (!hkey) {
3928 wpabuf_free(pub);
3929 wpabuf_clear_free(secret);
3930 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3931 }
3932
3933 wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */
3934 wpabuf_put_buf(hkey, pub); /* A */
3935 wpabuf_free(pub);
3936 wpabuf_put_le16(hkey, group); /* group */
3937 if (group == 19)
3938 res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
3939 wpabuf_head(secret), wpabuf_len(secret), prk);
3940 else if (group == 20)
3941 res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey),
3942 wpabuf_head(secret), wpabuf_len(secret), prk);
3943 else if (group == 21)
3944 res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey),
3945 wpabuf_head(secret), wpabuf_len(secret), prk);
3946 wpabuf_clear_free(hkey);
3947 wpabuf_clear_free(secret);
3948 if (res < 0)
3949 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3950
3951 wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len);
3952
3953 /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
3954
3955 os_free(sta->owe_pmk);
3956 sta->owe_pmk = os_malloc(hash_len);
3957 if (!sta->owe_pmk) {
3958 os_memset(prk, 0, SHA512_MAC_LEN);
3959 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3960 }
3961
3962 if (group == 19)
3963 res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info,
3964 os_strlen(info), sta->owe_pmk, hash_len);
3965 else if (group == 20)
3966 res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info,
3967 os_strlen(info), sta->owe_pmk, hash_len);
3968 else if (group == 21)
3969 res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
3970 os_strlen(info), sta->owe_pmk, hash_len);
3971 os_memset(prk, 0, SHA512_MAC_LEN);
3972 if (res < 0) {
3973 os_free(sta->owe_pmk);
3974 sta->owe_pmk = NULL;
3975 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3976 }
3977 sta->owe_pmk_len = hash_len;
3978
3979 wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len);
3980 wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
3981 wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk,
Sunil Ravi876a49b2025-02-03 19:18:32 +00003982 sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE,
3983 NULL, ap_sta_is_mld(hapd, sta));
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003984
3985 return WLAN_STATUS_SUCCESS;
3986}
3987
Hai Shalom81f62d82019-07-22 12:10:00 -07003988
3989u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
3990 const u8 *rsn_ie, size_t rsn_ie_len,
3991 const u8 *owe_dh, size_t owe_dh_len)
3992{
3993 struct wpa_ie_data data;
3994 int res;
3995
3996 if (!rsn_ie || rsn_ie_len < 2) {
3997 wpa_printf(MSG_DEBUG, "OWE: Invalid RSNE from " MACSTR,
3998 MAC2STR(peer));
3999 return WLAN_STATUS_INVALID_IE;
4000 }
4001 rsn_ie -= 2;
4002 rsn_ie_len += 2;
4003
4004 res = wpa_parse_wpa_ie_rsn(rsn_ie, rsn_ie_len, &data);
4005 if (res) {
4006 wpa_printf(MSG_DEBUG, "Failed to parse RSNE from " MACSTR
4007 " (res=%d)", MAC2STR(peer), res);
4008 wpa_hexdump(MSG_DEBUG, "RSNE", rsn_ie, rsn_ie_len);
4009 return wpa_res_to_status_code(res);
4010 }
4011 if (!(data.key_mgmt & WPA_KEY_MGMT_OWE)) {
4012 wpa_printf(MSG_DEBUG,
4013 "OWE: Unexpected key mgmt 0x%x from " MACSTR,
4014 (unsigned int) data.key_mgmt, MAC2STR(peer));
4015 return WLAN_STATUS_AKMP_NOT_VALID;
4016 }
4017 if (!owe_dh) {
4018 wpa_printf(MSG_DEBUG,
4019 "OWE: No Diffie-Hellman Parameter element from "
4020 MACSTR, MAC2STR(peer));
4021 return WLAN_STATUS_AKMP_NOT_VALID;
4022 }
4023
4024 return WLAN_STATUS_SUCCESS;
4025}
4026
4027
4028u16 owe_process_rsn_ie(struct hostapd_data *hapd,
4029 struct sta_info *sta,
4030 const u8 *rsn_ie, size_t rsn_ie_len,
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004031 const u8 *owe_dh, size_t owe_dh_len,
4032 const u8 *link_addr)
Hai Shalom81f62d82019-07-22 12:10:00 -07004033{
4034 u16 status;
4035 u8 *owe_buf, ie[256 * 2];
4036 size_t ie_len = 0;
Hai Shalomfdcde762020-04-02 11:19:20 -07004037 enum wpa_validate_result res;
Hai Shalom81f62d82019-07-22 12:10:00 -07004038
4039 if (!rsn_ie || rsn_ie_len < 2) {
4040 wpa_printf(MSG_DEBUG, "OWE: No RSNE in (Re)AssocReq");
4041 status = WLAN_STATUS_INVALID_IE;
4042 goto end;
4043 }
4044
4045 if (!sta->wpa_sm)
4046 sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr,
4047 NULL);
4048 if (!sta->wpa_sm) {
4049 wpa_printf(MSG_WARNING,
4050 "OWE: Failed to initialize WPA state machine");
4051 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
4052 goto end;
4053 }
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004054#ifdef CONFIG_IEEE80211BE
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004055 if (ap_sta_is_mld(hapd, sta))
Sunil Ravi7f769292024-07-23 22:21:32 +00004056 wpa_auth_set_ml_info(sta->wpa_sm,
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004057 sta->mld_assoc_link_id, &sta->mld_info);
4058#endif /* CONFIG_IEEE80211BE */
Hai Shalom81f62d82019-07-22 12:10:00 -07004059 rsn_ie -= 2;
4060 rsn_ie_len += 2;
4061 res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
4062 hapd->iface->freq, rsn_ie, rsn_ie_len,
Sunil Ravi876a49b2025-02-03 19:18:32 +00004063 NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL,
4064 ap_sta_is_mld(hapd, sta));
Hai Shalom81f62d82019-07-22 12:10:00 -07004065 status = wpa_res_to_status_code(res);
4066 if (status != WLAN_STATUS_SUCCESS)
4067 goto end;
4068 status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
4069 if (status != WLAN_STATUS_SUCCESS)
4070 goto end;
4071 owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, ie, sizeof(ie),
4072 NULL, 0);
4073 if (!owe_buf) {
4074 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
4075 goto end;
4076 }
4077
4078 if (sta->owe_ecdh) {
4079 struct wpabuf *pub;
4080
4081 pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
4082 if (!pub) {
4083 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
4084 goto end;
4085 }
4086
4087 /* OWE Diffie-Hellman Parameter element */
4088 *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
4089 *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
4090 *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
4091 */
4092 WPA_PUT_LE16(owe_buf, sta->owe_group);
4093 owe_buf += 2;
4094 os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
4095 owe_buf += wpabuf_len(pub);
4096 wpabuf_free(pub);
4097 sta->external_dh_updated = 1;
4098 }
4099 ie_len = owe_buf - ie;
4100
4101end:
4102 wpa_printf(MSG_DEBUG, "OWE: Update status %d, ie len %d for peer "
4103 MACSTR, status, (unsigned int) ie_len,
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004104 MAC2STR(link_addr ? link_addr : sta->addr));
4105 hostapd_drv_update_dh_ie(hapd, link_addr ? link_addr : sta->addr,
4106 status,
Hai Shalom81f62d82019-07-22 12:10:00 -07004107 status == WLAN_STATUS_SUCCESS ? ie : NULL,
4108 ie_len);
4109
4110 return status;
4111}
4112
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004113#endif /* CONFIG_OWE */
4114
4115
Sunil Ravi876a49b2025-02-03 19:18:32 +00004116static bool hapd_is_known_sta(struct hostapd_data *hapd, struct sta_info *sta,
4117 const u8 *ies, size_t ies_len)
4118{
4119 const u8 *ie, *pos, *end, *timestamp_pos, *mic;
4120 u64 timestamp;
4121 u8 mic_len;
4122
4123 if (!hapd->conf->known_sta_identification)
4124 return false;
4125
4126 ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_KNOWN_STA_IDENTIFICATION);
4127 if (!ie)
4128 return false;
4129
4130 pos = ie + 3;
4131 end = &ie[2 + ie[1]];
4132 if (end - pos < 8 + 1)
4133 return false; /* truncated element */
4134 timestamp_pos = pos;
4135 timestamp = WPA_GET_LE64(pos);
4136 pos += 8;
4137 mic_len = *pos++;
4138 if (mic_len > end - pos)
4139 return false; /* truncated element */
4140 mic = pos;
4141
4142 wpa_printf(MSG_DEBUG, "RSN: STA " MACSTR
4143 " included Known STA Identification element: Timestamp=0x%llx mic_len=%u",
4144 MAC2STR(sta->addr), (unsigned long long) timestamp, mic_len);
4145
4146 if (timestamp <= sta->last_known_sta_id_timestamp) {
4147 wpa_printf(MSG_DEBUG,
4148 "RSN: Ignore reused or old Known STA Identification");
4149 return false;
4150 }
4151
4152 if (!wpa_auth_sm_known_sta_identification(sta->wpa_sm, timestamp_pos,
4153 mic, mic_len)) {
4154 wpa_printf(MSG_DEBUG,
4155 "RSN: Ignore Known STA Identification with invalid MIC or due to KCK not available");
4156 return false;
4157 }
4158
4159 wpa_printf(MSG_DEBUG, "RSN: Valid Known STA Identification");
4160 sta->last_known_sta_id_timestamp = timestamp;
4161
4162 return true;
4163}
4164
4165
Hai Shalom899fcc72020-10-19 14:38:18 -07004166static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta,
Sunil Ravi876a49b2025-02-03 19:18:32 +00004167 int reassoc, const u8 *ies, size_t ies_len)
Hai Shalom899fcc72020-10-19 14:38:18 -07004168{
4169 if ((sta->flags &
4170 (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) !=
4171 (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED))
4172 return false;
4173
4174 if (!sta->sa_query_timed_out && sta->sa_query_count > 0)
4175 ap_check_sa_query_timeout(hapd, sta);
4176
4177 if (!sta->sa_query_timed_out &&
4178 (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
Sunil Ravi876a49b2025-02-03 19:18:32 +00004179 if (hapd_is_known_sta(hapd, sta, ies, ies_len))
4180 return false;
4181
Hai Shalom899fcc72020-10-19 14:38:18 -07004182 /*
4183 * STA has already been associated with MFP and SA Query timeout
4184 * has not been reached. Reject the association attempt
4185 * temporarily and start SA Query, if one is not pending.
4186 */
4187 if (sta->sa_query_count == 0)
4188 ap_sta_start_sa_query(hapd, sta);
4189
4190 return true;
4191 }
4192
4193 return false;
4194}
4195
4196
Sunil Ravi036cec52023-03-29 11:35:17 -07004197static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
4198 const u8 *ies, size_t ies_len,
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004199 struct ieee802_11_elems *elems, int reassoc,
4200 bool link)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004201{
Hai Shalomb755a2a2020-04-23 21:49:02 -07004202 int resp;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004203 const u8 *wpa_ie;
4204 size_t wpa_ie_len;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07004205 const u8 *p2p_dev_addr = NULL;
Sunil Ravi7f769292024-07-23 22:21:32 +00004206 struct hostapd_data *assoc_hapd;
4207 struct sta_info *assoc_sta = NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004208
Sunil Ravi036cec52023-03-29 11:35:17 -07004209 resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004210 if (resp != WLAN_STATUS_SUCCESS)
4211 return resp;
Sunil Ravi036cec52023-03-29 11:35:17 -07004212 resp = check_wmm(hapd, sta, elems->wmm, elems->wmm_len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004213 if (resp != WLAN_STATUS_SUCCESS)
4214 return resp;
Sunil Ravi036cec52023-03-29 11:35:17 -07004215 resp = check_ext_capab(hapd, sta, elems->ext_capab,
4216 elems->ext_capab_len);
Dmitry Shmidt051af732013-10-22 13:52:46 -07004217 if (resp != WLAN_STATUS_SUCCESS)
4218 return resp;
Sunil Ravi036cec52023-03-29 11:35:17 -07004219 resp = copy_supp_rates(hapd, sta, elems);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004220 if (resp != WLAN_STATUS_SUCCESS)
4221 return resp;
Hai Shalom74f70d42019-02-11 14:42:39 -08004222
Sunil Ravi036cec52023-03-29 11:35:17 -07004223 resp = check_multi_ap(hapd, sta, elems->multi_ap, elems->multi_ap_len);
Hai Shalom74f70d42019-02-11 14:42:39 -08004224 if (resp != WLAN_STATUS_SUCCESS)
4225 return resp;
4226
Sunil Ravi036cec52023-03-29 11:35:17 -07004227 resp = copy_sta_ht_capab(hapd, sta, elems->ht_capabilities);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004228 if (resp != WLAN_STATUS_SUCCESS)
4229 return resp;
4230 if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
4231 !(sta->flags & WLAN_STA_HT)) {
4232 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
4233 HOSTAPD_LEVEL_INFO, "Station does not support "
4234 "mandatory HT PHY - reject association");
4235 return WLAN_STATUS_ASSOC_DENIED_NO_HT;
4236 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004237
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07004238#ifdef CONFIG_IEEE80211AC
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08004239 if (hapd->iconf->ieee80211ac) {
Sunil Ravi036cec52023-03-29 11:35:17 -07004240 resp = copy_sta_vht_capab(hapd, sta, elems->vht_capabilities);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08004241 if (resp != WLAN_STATUS_SUCCESS)
4242 return resp;
Dmitry Shmidtbd14a572014-02-18 10:33:49 -08004243
Sunil Ravi640215c2023-06-28 23:08:09 +00004244 resp = set_sta_vht_opmode(hapd, sta, elems->opmode_notif);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08004245 if (resp != WLAN_STATUS_SUCCESS)
4246 return resp;
4247 }
Dmitry Shmidtbd14a572014-02-18 10:33:49 -08004248
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07004249 if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht &&
4250 !(sta->flags & WLAN_STA_VHT)) {
4251 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
4252 HOSTAPD_LEVEL_INFO, "Station does not support "
4253 "mandatory VHT PHY - reject association");
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08004254 return WLAN_STATUS_ASSOC_DENIED_NO_VHT;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07004255 }
Dmitry Shmidt2f74e362015-01-21 13:19:05 -08004256
Sunil Ravi036cec52023-03-29 11:35:17 -07004257 if (hapd->conf->vendor_vht && !elems->vht_capabilities) {
4258 resp = copy_sta_vendor_vht(hapd, sta, elems->vendor_vht,
4259 elems->vendor_vht_len);
Dmitry Shmidt2f74e362015-01-21 13:19:05 -08004260 if (resp != WLAN_STATUS_SUCCESS)
4261 return resp;
4262 }
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07004263#endif /* CONFIG_IEEE80211AC */
Hai Shalom81f62d82019-07-22 12:10:00 -07004264#ifdef CONFIG_IEEE80211AX
Hai Shalom60840252021-02-19 19:02:11 -08004265 if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
Hai Shalom81f62d82019-07-22 12:10:00 -07004266 resp = copy_sta_he_capab(hapd, sta, IEEE80211_MODE_AP,
Sunil Ravi036cec52023-03-29 11:35:17 -07004267 elems->he_capabilities,
4268 elems->he_capabilities_len);
Hai Shalom81f62d82019-07-22 12:10:00 -07004269 if (resp != WLAN_STATUS_SUCCESS)
4270 return resp;
Sunil Ravi77d572f2023-01-17 23:58:31 +00004271
4272 if (hapd->iconf->require_he && !(sta->flags & WLAN_STA_HE)) {
4273 hostapd_logger(hapd, sta->addr,
4274 HOSTAPD_MODULE_IEEE80211,
4275 HOSTAPD_LEVEL_INFO,
4276 "Station does not support mandatory HE PHY - reject association");
4277 return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
4278 }
4279
Hai Shalom4fbc08f2020-05-18 12:37:00 -07004280 if (is_6ghz_op_class(hapd->iconf->op_class)) {
Hai Shalom899fcc72020-10-19 14:38:18 -07004281 if (!(sta->flags & WLAN_STA_HE)) {
4282 hostapd_logger(hapd, sta->addr,
4283 HOSTAPD_MODULE_IEEE80211,
4284 HOSTAPD_LEVEL_INFO,
4285 "Station does not support mandatory HE PHY - reject association");
4286 return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
4287 }
Hai Shalom4fbc08f2020-05-18 12:37:00 -07004288 resp = copy_sta_he_6ghz_capab(hapd, sta,
Sunil Ravi036cec52023-03-29 11:35:17 -07004289 elems->he_6ghz_band_cap);
Hai Shalom4fbc08f2020-05-18 12:37:00 -07004290 if (resp != WLAN_STATUS_SUCCESS)
4291 return resp;
4292 }
Hai Shalom81f62d82019-07-22 12:10:00 -07004293 }
4294#endif /* CONFIG_IEEE80211AX */
Sunil Ravia04bd252022-05-02 22:54:18 -07004295#ifdef CONFIG_IEEE80211BE
4296 if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
4297 resp = copy_sta_eht_capab(hapd, sta, IEEE80211_MODE_AP,
Sunil Ravi036cec52023-03-29 11:35:17 -07004298 elems->he_capabilities,
4299 elems->he_capabilities_len,
4300 elems->eht_capabilities,
4301 elems->eht_capabilities_len);
Sunil Ravia04bd252022-05-02 22:54:18 -07004302 if (resp != WLAN_STATUS_SUCCESS)
4303 return resp;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004304
4305 if (!link) {
4306 resp = hostapd_process_ml_assoc_req(hapd, elems, sta);
4307 if (resp != WLAN_STATUS_SUCCESS)
4308 return resp;
4309 }
Sunil Ravia04bd252022-05-02 22:54:18 -07004310 }
4311#endif /* CONFIG_IEEE80211BE */
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07004312
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07004313#ifdef CONFIG_P2P
Sunil Ravi036cec52023-03-29 11:35:17 -07004314 if (elems->p2p && ies && ies_len) {
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07004315 wpabuf_free(sta->p2p_ie);
4316 sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
4317 P2P_IE_VENDOR_TYPE);
4318 if (sta->p2p_ie)
4319 p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
4320 } else {
4321 wpabuf_free(sta->p2p_ie);
4322 sta->p2p_ie = NULL;
4323 }
4324#endif /* CONFIG_P2P */
4325
Sunil Ravi036cec52023-03-29 11:35:17 -07004326 if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems->rsn_ie) {
4327 wpa_ie = elems->rsn_ie;
4328 wpa_ie_len = elems->rsn_ie_len;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004329 } else if ((hapd->conf->wpa & WPA_PROTO_WPA) &&
Sunil Ravi036cec52023-03-29 11:35:17 -07004330 elems->wpa_ie) {
4331 wpa_ie = elems->wpa_ie;
4332 wpa_ie_len = elems->wpa_ie_len;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004333 } else {
4334 wpa_ie = NULL;
4335 wpa_ie_len = 0;
4336 }
4337
4338#ifdef CONFIG_WPS
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08004339 sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
Sunil Ravi036cec52023-03-29 11:35:17 -07004340 if (hapd->conf->wps_state && elems->wps_ie && ies && ies_len) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004341 wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association "
4342 "Request - assume WPS is used");
4343 sta->flags |= WLAN_STA_WPS;
4344 wpabuf_free(sta->wps_ie);
4345 sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
4346 WPS_IE_VENDOR_TYPE);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08004347 if (sta->wps_ie && wps_is_20(sta->wps_ie)) {
4348 wpa_printf(MSG_DEBUG, "WPS: STA supports WPS 2.0");
4349 sta->flags |= WLAN_STA_WPS2;
4350 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004351 wpa_ie = NULL;
4352 wpa_ie_len = 0;
4353 if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) {
4354 wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in "
4355 "(Re)Association Request - reject");
4356 return WLAN_STATUS_INVALID_IE;
4357 }
4358 } else if (hapd->conf->wps_state && wpa_ie == NULL) {
4359 wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in "
4360 "(Re)Association Request - possible WPS use");
4361 sta->flags |= WLAN_STA_MAYBE_WPS;
4362 } else
4363#endif /* CONFIG_WPS */
4364 if (hapd->conf->wpa && wpa_ie == NULL) {
4365 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
4366 HOSTAPD_LEVEL_INFO,
4367 "No WPA/RSN IE in association request");
4368 return WLAN_STATUS_INVALID_IE;
4369 }
4370
4371 if (hapd->conf->wpa && wpa_ie) {
Hai Shalomfdcde762020-04-02 11:19:20 -07004372 enum wpa_validate_result res;
Sunil Ravi7f769292024-07-23 22:21:32 +00004373#ifdef CONFIG_IEEE80211BE
4374 struct mld_info *info = &sta->mld_info;
4375 bool init = !sta->wpa_sm;
4376#endif /* CONFIG_IEEE80211BE */
Hai Shalomfdcde762020-04-02 11:19:20 -07004377
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004378 wpa_ie -= 2;
4379 wpa_ie_len += 2;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004380
4381 if (!sta->wpa_sm) {
Sunil Ravi7f769292024-07-23 22:21:32 +00004382 if (!link)
4383 assoc_sta = hostapd_ml_get_assoc_sta(
4384 hapd, sta, &assoc_hapd);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004385
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004386 sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07004387 sta->addr,
4388 p2p_dev_addr);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004389
4390 if (!sta->wpa_sm) {
4391 wpa_printf(MSG_WARNING,
4392 "Failed to initialize RSN state machine");
4393 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4394 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004395 }
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004396
Sunil Ravi7f769292024-07-23 22:21:32 +00004397#ifdef CONFIG_IEEE80211BE
4398 if (ap_sta_is_mld(hapd, sta)) {
4399 wpa_printf(MSG_DEBUG,
4400 "MLD: %s ML info in RSN Authenticator",
4401 init ? "Set" : "Reset");
4402 wpa_auth_set_ml_info(sta->wpa_sm,
4403 sta->mld_assoc_link_id,
4404 info);
4405 }
4406#endif /* CONFIG_IEEE80211BE */
4407
Hai Shalom021b0b52019-04-10 11:17:58 -07004408 wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
Sunil Ravic0f5d412024-09-11 22:12:49 +00004409 wpa_auth_set_rsn_selection(sta->wpa_sm, elems->rsn_selection,
4410 elems->rsn_selection_len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004411 res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
Hai Shalom021b0b52019-04-10 11:17:58 -07004412 hapd->iface->freq,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004413 wpa_ie, wpa_ie_len,
Sunil Ravi036cec52023-03-29 11:35:17 -07004414 elems->rsnxe ? elems->rsnxe - 2 :
4415 NULL,
4416 elems->rsnxe ? elems->rsnxe_len + 2 :
4417 0,
4418 elems->mdie, elems->mdie_len,
Sunil Ravi7f769292024-07-23 22:21:32 +00004419 elems->owe_dh, elems->owe_dh_len,
Sunil Ravi876a49b2025-02-03 19:18:32 +00004420 assoc_sta ? assoc_sta->wpa_sm : NULL,
4421 ap_sta_is_mld(hapd, sta));
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08004422 resp = wpa_res_to_status_code(res);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004423 if (resp != WLAN_STATUS_SUCCESS)
4424 return resp;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004425
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004426 if (wpa_auth_uses_mfp(sta->wpa_sm))
4427 sta->flags |= WLAN_STA_MFP;
4428 else
4429 sta->flags &= ~WLAN_STA_MFP;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004430
Sunil Ravi876a49b2025-02-03 19:18:32 +00004431 if (wpa_auth_uses_spp_amsdu(sta->wpa_sm))
4432 sta->flags |= WLAN_STA_SPP_AMSDU;
4433 else
4434 sta->flags &= ~WLAN_STA_SPP_AMSDU;
4435
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08004436#ifdef CONFIG_IEEE80211R_AP
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004437 if (sta->auth_alg == WLAN_AUTH_FT) {
4438 if (!reassoc) {
4439 wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried "
4440 "to use association (not "
4441 "re-association) with FT auth_alg",
4442 MAC2STR(sta->addr));
4443 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4444 }
4445
4446 resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies,
4447 ies_len);
4448 if (resp != WLAN_STATUS_SUCCESS)
4449 return resp;
4450 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08004451#endif /* CONFIG_IEEE80211R_AP */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004452
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004453 if (link)
4454 goto skip_sae_owe;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08004455#ifdef CONFIG_SAE
Roshan Pius3a1667e2018-07-03 15:17:14 -07004456 if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae &&
4457 sta->sae->state == SAE_ACCEPTED)
4458 wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid);
4459
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08004460 if (wpa_auth_uses_sae(sta->wpa_sm) &&
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08004461 sta->auth_alg == WLAN_AUTH_OPEN) {
4462 struct rsn_pmksa_cache_entry *sa;
4463 sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
Sunil Ravi89eba102022-09-13 21:04:37 -07004464 if (!sa || !wpa_key_mgmt_sae(sa->akmp)) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08004465 wpa_printf(MSG_DEBUG,
4466 "SAE: No PMKSA cache entry found for "
4467 MACSTR, MAC2STR(sta->addr));
4468 return WLAN_STATUS_INVALID_PMKID;
4469 }
4470 wpa_printf(MSG_DEBUG, "SAE: " MACSTR
4471 " using PMKSA caching", MAC2STR(sta->addr));
4472 } else if (wpa_auth_uses_sae(sta->wpa_sm) &&
4473 sta->auth_alg != WLAN_AUTH_SAE &&
4474 !(sta->auth_alg == WLAN_AUTH_FT &&
4475 wpa_auth_uses_ft_sae(sta->wpa_sm))) {
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08004476 wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use "
4477 "SAE AKM after non-SAE auth_alg %u",
4478 MAC2STR(sta->addr), sta->auth_alg);
4479 return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
4480 }
Hai Shalomc3565922019-10-28 11:58:20 -07004481
Sunil Ravi77d572f2023-01-17 23:58:31 +00004482 if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
Hai Shalomc3565922019-10-28 11:58:20 -07004483 sta->auth_alg == WLAN_AUTH_SAE &&
Hai Shalom899fcc72020-10-19 14:38:18 -07004484 sta->sae && !sta->sae->h2e &&
Sunil Ravi036cec52023-03-29 11:35:17 -07004485 ieee802_11_rsnx_capab_len(elems->rsnxe, elems->rsnxe_len,
Hai Shaloma20dcd72022-02-04 13:43:00 -08004486 WLAN_RSNX_CAPAB_SAE_H2E)) {
Hai Shalomc3565922019-10-28 11:58:20 -07004487 wpa_printf(MSG_INFO, "SAE: " MACSTR
4488 " indicates support for SAE H2E, but did not use it",
4489 MAC2STR(sta->addr));
4490 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4491 }
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08004492#endif /* CONFIG_SAE */
4493
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004494#ifdef CONFIG_OWE
4495 if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
4496 wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
Sunil Ravi036cec52023-03-29 11:35:17 -07004497 elems->owe_dh) {
4498 resp = owe_process_assoc_req(hapd, sta, elems->owe_dh,
4499 elems->owe_dh_len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004500 if (resp != WLAN_STATUS_SUCCESS)
4501 return resp;
4502 }
4503#endif /* CONFIG_OWE */
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004504 skip_sae_owe:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004505
Hai Shalom021b0b52019-04-10 11:17:58 -07004506#ifdef CONFIG_DPP2
4507 dpp_pfs_free(sta->dpp_pfs);
4508 sta->dpp_pfs = NULL;
4509
Hai Shalom4fbc08f2020-05-18 12:37:00 -07004510 if (DPP_VERSION > 1 &&
4511 (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
Hai Shalom021b0b52019-04-10 11:17:58 -07004512 hapd->conf->dpp_netaccesskey && sta->wpa_sm &&
4513 wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP &&
Sunil Ravi036cec52023-03-29 11:35:17 -07004514 elems->owe_dh) {
Hai Shalom021b0b52019-04-10 11:17:58 -07004515 sta->dpp_pfs = dpp_pfs_init(
4516 wpabuf_head(hapd->conf->dpp_netaccesskey),
4517 wpabuf_len(hapd->conf->dpp_netaccesskey));
4518 if (!sta->dpp_pfs) {
4519 wpa_printf(MSG_DEBUG,
4520 "DPP: Could not initialize PFS");
4521 /* Try to continue without PFS */
4522 goto pfs_fail;
4523 }
4524
Sunil Ravi036cec52023-03-29 11:35:17 -07004525 if (dpp_pfs_process(sta->dpp_pfs, elems->owe_dh,
4526 elems->owe_dh_len) < 0) {
Hai Shalom021b0b52019-04-10 11:17:58 -07004527 dpp_pfs_free(sta->dpp_pfs);
4528 sta->dpp_pfs = NULL;
4529 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4530 }
4531 }
4532
4533 wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
4534 sta->dpp_pfs->secret : NULL);
4535 pfs_fail:
4536#endif /* CONFIG_DPP2 */
4537
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07004538 if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004539 wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
4540 hostapd_logger(hapd, sta->addr,
4541 HOSTAPD_MODULE_IEEE80211,
4542 HOSTAPD_LEVEL_INFO,
4543 "Station tried to use TKIP with HT "
4544 "association");
4545 return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
4546 }
Sunil Ravi7f769292024-07-23 22:21:32 +00004547
4548 wpa_auth_set_ssid_protection(
4549 sta->wpa_sm,
4550 hapd->conf->ssid_protection &&
4551 ieee802_11_rsnx_capab_len(
4552 elems->rsnxe, elems->rsnxe_len,
4553 WLAN_RSNX_CAPAB_SSID_PROTECTION));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004554 } else
4555 wpa_auth_sta_no_wpa(sta->wpa_sm);
4556
4557#ifdef CONFIG_P2P
Sunil Ravi876a49b2025-02-03 19:18:32 +00004558 if (ies && ies_len)
4559 p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004560#endif /* CONFIG_P2P */
4561
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08004562#ifdef CONFIG_HS20
4563 wpabuf_free(sta->hs20_ie);
Sunil Ravi036cec52023-03-29 11:35:17 -07004564 if (elems->hs20 && elems->hs20_len > 4) {
Hai Shalom74f70d42019-02-11 14:42:39 -08004565 int release;
4566
Sunil Ravi036cec52023-03-29 11:35:17 -07004567 sta->hs20_ie = wpabuf_alloc_copy(elems->hs20 + 4,
4568 elems->hs20_len - 4);
4569 release = ((elems->hs20[4] >> 4) & 0x0f) + 1;
Hai Shalomc3565922019-10-28 11:58:20 -07004570 if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm) &&
4571 hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
Hai Shalom74f70d42019-02-11 14:42:39 -08004572 wpa_printf(MSG_DEBUG,
4573 "HS 2.0: PMF not negotiated by release %d station "
4574 MACSTR, release, MAC2STR(sta->addr));
4575 return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
4576 }
4577 } else {
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08004578 sta->hs20_ie = NULL;
Hai Shalom74f70d42019-02-11 14:42:39 -08004579 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07004580
4581 wpabuf_free(sta->roaming_consortium);
Sunil Ravi036cec52023-03-29 11:35:17 -07004582 if (elems->roaming_cons_sel)
Roshan Pius3a1667e2018-07-03 15:17:14 -07004583 sta->roaming_consortium = wpabuf_alloc_copy(
Sunil Ravi036cec52023-03-29 11:35:17 -07004584 elems->roaming_cons_sel + 4,
4585 elems->roaming_cons_sel_len - 4);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004586 else
4587 sta->roaming_consortium = NULL;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08004588#endif /* CONFIG_HS20 */
4589
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08004590#ifdef CONFIG_FST
4591 wpabuf_free(sta->mb_ies);
4592 if (hapd->iface->fst)
Sunil Ravi036cec52023-03-29 11:35:17 -07004593 sta->mb_ies = mb_ies_by_info(&elems->mb_ies);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08004594 else
4595 sta->mb_ies = NULL;
4596#endif /* CONFIG_FST */
4597
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004598#ifdef CONFIG_MBO
Sunil Ravi036cec52023-03-29 11:35:17 -07004599 mbo_ap_check_sta_assoc(hapd, sta, elems);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004600
4601 if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) &&
Sunil Ravi036cec52023-03-29 11:35:17 -07004602 elems->mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) &&
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004603 hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
4604 wpa_printf(MSG_INFO,
4605 "MBO: Reject WPA2 association without PMF");
4606 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4607 }
4608#endif /* CONFIG_MBO */
4609
Hai Shalom74f70d42019-02-11 14:42:39 -08004610#if defined(CONFIG_FILS) && defined(CONFIG_OCV)
4611 if (wpa_auth_uses_ocv(sta->wpa_sm) &&
4612 (sta->auth_alg == WLAN_AUTH_FILS_SK ||
4613 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
4614 sta->auth_alg == WLAN_AUTH_FILS_PK)) {
4615 struct wpa_channel_info ci;
4616 int tx_chanwidth;
4617 int tx_seg1_idx;
Hai Shalom899fcc72020-10-19 14:38:18 -07004618 enum oci_verify_result res;
Hai Shalom74f70d42019-02-11 14:42:39 -08004619
4620 if (hostapd_drv_channel_info(hapd, &ci) != 0) {
4621 wpa_printf(MSG_WARNING,
4622 "Failed to get channel info to validate received OCI in FILS (Re)Association Request frame");
4623 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4624 }
4625
4626 if (get_sta_tx_parameters(sta->wpa_sm,
4627 channel_width_to_int(ci.chanwidth),
4628 ci.seg1_idx, &tx_chanwidth,
4629 &tx_seg1_idx) < 0)
4630 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4631
Sunil Ravi036cec52023-03-29 11:35:17 -07004632 res = ocv_verify_tx_params(elems->oci, elems->oci_len, &ci,
Hai Shalom899fcc72020-10-19 14:38:18 -07004633 tx_chanwidth, tx_seg1_idx);
4634 if (wpa_auth_uses_ocv(sta->wpa_sm) == 2 &&
4635 res == OCI_NOT_FOUND) {
4636 /* Work around misbehaving STAs */
4637 wpa_printf(MSG_INFO,
4638 "FILS: Disable OCV with a STA that does not send OCI");
4639 wpa_auth_set_ocv(sta->wpa_sm, 0);
4640 } else if (res != OCI_SUCCESS) {
4641 wpa_printf(MSG_WARNING, "FILS: OCV failed: %s",
4642 ocv_errorstr);
4643 wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="
4644 MACSTR " frame=fils-reassoc-req error=%s",
4645 MAC2STR(sta->addr), ocv_errorstr);
Hai Shalom74f70d42019-02-11 14:42:39 -08004646 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4647 }
4648 }
4649#endif /* CONFIG_FILS && CONFIG_OCV */
4650
Sunil Ravi036cec52023-03-29 11:35:17 -07004651 ap_copy_sta_supp_op_classes(sta, elems->supp_op_classes,
4652 elems->supp_op_classes_len);
Dmitry Shmidt9c175262016-03-03 10:20:07 -08004653
Dmitry Shmidt849734c2016-05-27 09:59:01 -07004654 if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) &&
Sunil Ravi036cec52023-03-29 11:35:17 -07004655 elems->rrm_enabled &&
4656 elems->rrm_enabled_len >= sizeof(sta->rrm_enabled_capa))
4657 os_memcpy(sta->rrm_enabled_capa, elems->rrm_enabled,
Dmitry Shmidt849734c2016-05-27 09:59:01 -07004658 sizeof(sta->rrm_enabled_capa));
4659
Sunil Ravi036cec52023-03-29 11:35:17 -07004660 if (elems->power_capab) {
4661 sta->min_tx_power = elems->power_capab[0];
4662 sta->max_tx_power = elems->power_capab[1];
Roshan Pius3a1667e2018-07-03 15:17:14 -07004663 sta->power_capab = 1;
4664 } else {
4665 sta->power_capab = 0;
4666 }
4667
Sunil Ravi7f769292024-07-23 22:21:32 +00004668 if (elems->bss_max_idle_period &&
4669 hapd->conf->max_acceptable_idle_period) {
4670 u16 req;
4671
4672 req = WPA_GET_LE16(elems->bss_max_idle_period);
4673 if (req <= hapd->conf->max_acceptable_idle_period)
4674 sta->max_idle_period = req;
4675 else if (hapd->conf->max_acceptable_idle_period >
4676 hapd->conf->ap_max_inactivity)
4677 sta->max_idle_period =
4678 hapd->conf->max_acceptable_idle_period;
4679 }
4680
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00004681 if (elems->wfa_capab)
4682 hostapd_wfa_capab(hapd, sta, elems->wfa_capab,
4683 elems->wfa_capab + elems->wfa_capab_len);
4684
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004685 return WLAN_STATUS_SUCCESS;
4686}
4687
4688
Sunil Ravi036cec52023-03-29 11:35:17 -07004689static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
4690 const u8 *ies, size_t ies_len, int reassoc)
4691{
4692 struct ieee802_11_elems elems;
4693
4694 if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
4695 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
4696 HOSTAPD_LEVEL_INFO,
4697 "Station sent an invalid association request");
4698 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4699 }
4700
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004701 return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, reassoc,
4702 false);
4703}
4704
4705
4706#ifdef CONFIG_IEEE80211BE
4707
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004708static void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
4709 struct mld_link_info *link)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004710{
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004711 u8 buf[EHT_ML_MAX_STA_PROF_LEN];
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004712 u8 *p = buf;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004713 size_t buflen = sizeof(buf);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004714
4715 /* Capability Info */
4716 WPA_PUT_LE16(p, hostapd_own_capab_info(hapd));
4717 p += 2;
4718
4719 /* Status Code */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004720 WPA_PUT_LE16(p, link->status);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004721 p += 2;
4722
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004723 if (link->status != WLAN_STATUS_SUCCESS)
4724 goto out;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004725
4726 /* AID is not included */
4727 p = hostapd_eid_supp_rates(hapd, p);
4728 p = hostapd_eid_ext_supp_rates(hapd, p);
4729 p = hostapd_eid_rm_enabled_capab(hapd, p, buf + buflen - p);
4730 p = hostapd_eid_ht_capabilities(hapd, p);
4731 p = hostapd_eid_ht_operation(hapd, p);
4732
4733 if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
4734 p = hostapd_eid_vht_capabilities(hapd, p, 0);
4735 p = hostapd_eid_vht_operation(hapd, p);
4736 }
4737
4738 if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
4739 p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
4740 p = hostapd_eid_he_operation(hapd, p);
4741 p = hostapd_eid_spatial_reuse(hapd, p);
4742 p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
4743 p = hostapd_eid_he_6ghz_band_cap(hapd, p);
4744 if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
4745 p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
4746 p = hostapd_eid_eht_operation(hapd, p);
4747 }
4748 }
4749
4750 p = hostapd_eid_ext_capab(hapd, p, false);
4751 p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
4752 p = hostapd_eid_wmm(hapd, p);
4753
4754 if (hapd->conf->assocresp_elements &&
4755 (size_t) (buf + buflen - p) >=
4756 wpabuf_len(hapd->conf->assocresp_elements)) {
4757 os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
4758 wpabuf_len(hapd->conf->assocresp_elements));
4759 p += wpabuf_len(hapd->conf->assocresp_elements);
4760 }
4761
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004762out:
4763 os_free(link->resp_sta_profile);
4764 link->resp_sta_profile = os_memdup(buf, p - buf);
4765 link->resp_sta_profile_len = link->resp_sta_profile ? p - buf : 0;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004766}
4767
4768
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004769static int ieee80211_ml_process_link(struct hostapd_data *hapd,
4770 struct sta_info *origin_sta,
4771 struct mld_link_info *link,
4772 const u8 *ies, size_t ies_len,
4773 bool reassoc, bool offload)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004774{
4775 struct ieee802_11_elems elems;
4776 struct wpabuf *mlbuf = NULL;
4777 struct sta_info *sta = NULL;
4778 u16 status = WLAN_STATUS_SUCCESS;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004779 int i;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004780
4781 wpa_printf(MSG_DEBUG, "MLD: link: link_id=%u, peer=" MACSTR,
4782 hapd->mld_link_id, MAC2STR(link->peer_addr));
4783
4784 if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
4785 wpa_printf(MSG_DEBUG, "MLD: link: Element parsing failed");
4786 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
4787 goto out;
4788 }
4789
4790 sta = ap_get_sta(hapd, origin_sta->addr);
4791 if (sta) {
4792 wpa_printf(MSG_INFO, "MLD: link: Station already exists");
4793 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
4794 sta = NULL;
4795 goto out;
4796 }
4797
4798 sta = ap_sta_add(hapd, origin_sta->addr);
4799 if (!sta) {
4800 wpa_printf(MSG_DEBUG, "MLD: link: ap_sta_add() failed");
4801 status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
4802 goto out;
4803 }
4804
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004805 mlbuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len, true);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004806 if (!mlbuf)
4807 goto out;
4808
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00004809 if (ieee802_11_parse_link_assoc_req(&elems, mlbuf, hapd->mld_link_id,
4810 true) == ParseFailed) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004811 wpa_printf(MSG_DEBUG,
4812 "MLD: link: Failed to parse association request Multi-Link element");
4813 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
4814 goto out;
4815 }
4816
4817 sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
Sunil Ravi7f769292024-07-23 22:21:32 +00004818 sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
Sunil Ravi876a49b2025-02-03 19:18:32 +00004819 ap_sta_set_mld(sta, true);
Sunil Ravi7f769292024-07-23 22:21:32 +00004820
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004821 status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
4822 if (status != WLAN_STATUS_SUCCESS) {
4823 wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
4824 goto out;
4825 }
4826
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004827 os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004828 for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
4829 struct mld_link_info *li = &sta->mld_info.links[i];
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004830
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004831 li->resp_sta_profile = NULL;
4832 li->resp_sta_profile_len = 0;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004833 }
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004834
4835 if (!offload) {
4836 /*
4837 * Get the AID from the station on which the association was
4838 * performed, and mark it as used.
4839 */
4840 sta->aid = origin_sta->aid;
4841 if (sta->aid == 0) {
4842 wpa_printf(MSG_DEBUG, "MLD: link: No AID assigned");
4843 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
4844 goto out;
4845 }
4846 hapd->sta_aid[(sta->aid - 1) / 32] |= BIT((sta->aid - 1) % 32);
4847 sta->listen_interval = origin_sta->listen_interval;
4848 if (update_ht_state(hapd, sta) > 0)
4849 ieee802_11_update_beacons(hapd->iface);
4850 }
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004851
Sunil Ravi7f769292024-07-23 22:21:32 +00004852 /* Maintain state machine reference on all link STAs, this is needed
4853 * during group rekey handling.
4854 */
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004855 wpa_auth_sta_deinit(sta->wpa_sm);
Sunil Ravi7f769292024-07-23 22:21:32 +00004856 sta->wpa_sm = origin_sta->wpa_sm;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004857
4858 /*
4859 * Do not initialize the EAPOL state machine.
4860 * TODO: Maybe it is needed?
4861 */
4862 sta->eapol_sm = NULL;
4863
4864 wpa_printf(MSG_DEBUG, "MLD: link=%u, association OK (aid=%u)",
4865 hapd->mld_link_id, sta->aid);
4866
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004867 sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC_REQ_OK;
4868
4869 /* TODO: What other processing is required? */
4870
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004871 if (!offload && add_associated_sta(hapd, sta, reassoc))
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004872 status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
4873out:
4874 wpabuf_free(mlbuf);
4875 link->status = status;
4876
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004877 if (!offload)
4878 ieee80211_ml_build_assoc_resp(hapd, link);
Sunil Ravi72e01222024-03-09 01:25:43 +00004879
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004880 wpa_printf(MSG_DEBUG, "MLD: link: status=%u", status);
4881 if (status != WLAN_STATUS_SUCCESS) {
4882 if (sta)
4883 ap_free_sta(hapd, sta);
4884 return -1;
4885 }
4886
4887 return 0;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004888}
4889
4890
4891bool hostapd_is_mld_ap(struct hostapd_data *hapd)
4892{
4893 if (!hapd->conf->mld_ap)
4894 return false;
4895
4896 if (!hapd->iface || !hapd->iface->interfaces ||
4897 hapd->iface->interfaces->count <= 1)
4898 return false;
4899
4900 return true;
4901}
4902
4903#endif /* CONFIG_IEEE80211BE */
4904
4905
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004906int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
4907 struct sta_info *sta,
4908 const u8 *ies, size_t ies_len,
4909 bool reassoc, int tx_link_status,
4910 bool offload)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004911{
4912#ifdef CONFIG_IEEE80211BE
Sunil Ravi7f769292024-07-23 22:21:32 +00004913 unsigned int i;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004914
4915 if (!hostapd_is_mld_ap(hapd))
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004916 return 0;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004917
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004918 for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
Sunil Ravi7f769292024-07-23 22:21:32 +00004919 struct hostapd_data *bss = NULL;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004920 struct mld_link_info *link = &sta->mld_info.links[i];
Sunil Ravi7f769292024-07-23 22:21:32 +00004921 bool link_bss_found = false;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004922
Sunil Ravi7f769292024-07-23 22:21:32 +00004923 if (!link->valid || i == sta->mld_assoc_link_id)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004924 continue;
4925
Sunil Ravi7f769292024-07-23 22:21:32 +00004926 for_each_mld_link(bss, hapd) {
4927 if (bss == hapd)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004928 continue;
4929
Sunil Ravi7f769292024-07-23 22:21:32 +00004930 if (bss->mld_link_id != i)
4931 continue;
4932
4933 link_bss_found = true;
4934 break;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004935 }
4936
Sunil Ravi7f769292024-07-23 22:21:32 +00004937 if (!link_bss_found || TEST_FAIL()) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004938 wpa_printf(MSG_DEBUG,
4939 "MLD: No link match for link_id=%u", i);
4940
4941 link->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004942 if (!offload)
4943 ieee80211_ml_build_assoc_resp(hapd, link);
4944 } else if (tx_link_status != WLAN_STATUS_SUCCESS) {
4945 /* TX link rejected the connection */
4946 link->status = WLAN_STATUS_DENIED_TX_LINK_NOT_ACCEPTED;
4947 if (!offload)
4948 ieee80211_ml_build_assoc_resp(hapd, link);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004949 } else {
Sunil Ravi7f769292024-07-23 22:21:32 +00004950 if (ieee80211_ml_process_link(bss, sta, link,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004951 ies, ies_len, reassoc,
4952 offload))
4953 return -1;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004954 }
4955 }
4956#endif /* CONFIG_IEEE80211BE */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004957
4958 return 0;
Sunil Ravi036cec52023-03-29 11:35:17 -07004959}
4960
4961
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004962static void send_deauth(struct hostapd_data *hapd, const u8 *addr,
4963 u16 reason_code)
4964{
4965 int send_len;
4966 struct ieee80211_mgmt reply;
4967
4968 os_memset(&reply, 0, sizeof(reply));
4969 reply.frame_control =
4970 IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH);
4971 os_memcpy(reply.da, addr, ETH_ALEN);
4972 os_memcpy(reply.sa, hapd->own_addr, ETH_ALEN);
4973 os_memcpy(reply.bssid, hapd->own_addr, ETH_ALEN);
4974
4975 send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth);
4976 reply.u.deauth.reason_code = host_to_le16(reason_code);
4977
Hai Shalomfdcde762020-04-02 11:19:20 -07004978 if (hostapd_drv_send_mlme(hapd, &reply, send_len, 0, NULL, 0, 0) < 0)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004979 wpa_printf(MSG_INFO, "Failed to send deauth: %s",
4980 strerror(errno));
4981}
4982
4983
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004984static int add_associated_sta(struct hostapd_data *hapd,
Hai Shalom74f70d42019-02-11 14:42:39 -08004985 struct sta_info *sta, int reassoc)
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004986{
4987 struct ieee80211_ht_capabilities ht_cap;
4988 struct ieee80211_vht_capabilities vht_cap;
Hai Shalom81f62d82019-07-22 12:10:00 -07004989 struct ieee80211_he_capabilities he_cap;
Sunil Ravia04bd252022-05-02 22:54:18 -07004990 struct ieee80211_eht_capabilities eht_cap;
Mathy Vanhoeff6e1f662017-07-14 15:15:35 +02004991 int set = 1;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004992 const u8 *mld_link_addr = NULL;
4993 bool mld_link_sta = false;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00004994 u16 eml_cap = 0;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004995
4996#ifdef CONFIG_IEEE80211BE
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004997 if (ap_sta_is_mld(hapd, sta)) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004998 u8 mld_link_id = hapd->mld_link_id;
4999
5000 mld_link_sta = sta->mld_assoc_link_id != mld_link_id;
5001 mld_link_addr = sta->mld_info.links[mld_link_id].peer_addr;
5002
5003 if (hapd->mld_link_id != sta->mld_assoc_link_id)
5004 set = 0;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00005005 eml_cap = sta->mld_info.common_info.eml_capa;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005006 }
5007#endif /* CONFIG_IEEE80211BE */
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005008
5009 /*
5010 * Remove the STA entry to ensure the STA PS state gets cleared and
5011 * configuration gets updated. This is relevant for cases, such as
5012 * FT-over-the-DS, where a station re-associates back to the same AP but
5013 * skips the authentication flow, or if working with a driver that
5014 * does not support full AP client state.
Mathy Vanhoeff6e1f662017-07-14 15:15:35 +02005015 *
5016 * Skip this if the STA has already completed FT reassociation and the
5017 * TK has been configured since the TX/RX PN must not be reset to 0 for
5018 * the same key.
Hai Shalom74f70d42019-02-11 14:42:39 -08005019 *
5020 * FT-over-the-DS has a special case where the STA entry (and as such,
5021 * the TK) has not yet been configured to the driver depending on which
5022 * driver interface is used. For that case, allow add-STA operation to
5023 * be used (instead of set-STA). This is needed to allow mac80211-based
5024 * drivers to accept the STA parameter configuration. Since this is
5025 * after a new FT-over-DS exchange, a new TK has been derived, so key
5026 * reinstallation is not a concern for this case.
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005027 */
Hai Shalom74f70d42019-02-11 14:42:39 -08005028 wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
5029 " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
5030 MAC2STR(sta->addr), sta->added_unassoc, sta->auth_alg,
5031 sta->ft_over_ds, reassoc,
5032 !!(sta->flags & WLAN_STA_AUTHORIZED),
5033 wpa_auth_sta_ft_tk_already_set(sta->wpa_sm),
5034 wpa_auth_sta_fils_tk_already_set(sta->wpa_sm));
5035
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005036 if (!mld_link_sta && !sta->added_unassoc &&
Mathy Vanhoeff6e1f662017-07-14 15:15:35 +02005037 (!(sta->flags & WLAN_STA_AUTHORIZED) ||
Hai Shalom74f70d42019-02-11 14:42:39 -08005038 (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005039 (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
5040 !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005041 hostapd_drv_sta_remove(hapd, sta->addr);
Mathy Vanhoeff6e1f662017-07-14 15:15:35 +02005042 wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
5043 set = 0;
Hai Shalom74f70d42019-02-11 14:42:39 -08005044
5045 /* Do not allow the FT-over-DS exception to be used more than
5046 * once per authentication exchange to guarantee a new TK is
5047 * used here */
5048 sta->ft_over_ds = 0;
Mathy Vanhoeff6e1f662017-07-14 15:15:35 +02005049 }
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005050
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005051 if (sta->flags & WLAN_STA_HT)
5052 hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005053#ifdef CONFIG_IEEE80211AC
5054 if (sta->flags & WLAN_STA_VHT)
5055 hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
5056#endif /* CONFIG_IEEE80211AC */
Hai Shalom81f62d82019-07-22 12:10:00 -07005057#ifdef CONFIG_IEEE80211AX
5058 if (sta->flags & WLAN_STA_HE) {
5059 hostapd_get_he_capab(hapd, sta->he_capab, &he_cap,
5060 sta->he_capab_len);
5061 }
5062#endif /* CONFIG_IEEE80211AX */
Sunil Ravia04bd252022-05-02 22:54:18 -07005063#ifdef CONFIG_IEEE80211BE
5064 if (sta->flags & WLAN_STA_EHT)
5065 hostapd_get_eht_capab(hapd, sta->eht_capab, &eht_cap,
5066 sta->eht_capab_len);
5067#endif /* CONFIG_IEEE80211BE */
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005068
5069 /*
5070 * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
5071 * will be set when the ACK frame for the (Re)Association Response frame
5072 * is processed (TX status driver event).
5073 */
5074 if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
5075 sta->supported_rates, sta->supported_rates_len,
5076 sta->listen_interval,
5077 sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
5078 sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
Hai Shalom81f62d82019-07-22 12:10:00 -07005079 sta->flags & WLAN_STA_HE ? &he_cap : NULL,
5080 sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0,
Sunil Ravia04bd252022-05-02 22:54:18 -07005081 sta->flags & WLAN_STA_EHT ? &eht_cap : NULL,
5082 sta->flags & WLAN_STA_EHT ? sta->eht_capab_len : 0,
Hai Shalom4fbc08f2020-05-18 12:37:00 -07005083 sta->he_6ghz_capab,
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005084 sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
Dmitry Shmidt849734c2016-05-27 09:59:01 -07005085 sta->vht_opmode, sta->p2p_ie ? 1 : 0,
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00005086 set, mld_link_addr, mld_link_sta, eml_cap)) {
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005087 hostapd_logger(hapd, sta->addr,
5088 HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
5089 "Could not %s STA to kernel driver",
Mathy Vanhoeff6e1f662017-07-14 15:15:35 +02005090 set ? "set" : "add");
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005091
5092 if (sta->added_unassoc) {
5093 hostapd_drv_sta_remove(hapd, sta->addr);
5094 sta->added_unassoc = 0;
5095 }
5096
5097 return -1;
5098 }
5099
5100 sta->added_unassoc = 0;
5101
5102 return 0;
5103}
5104
5105
5106static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
Dmitry Shmidt29333592017-01-09 12:27:11 -08005107 const u8 *addr, u16 status_code, int reassoc,
Hai Shalomfdcde762020-04-02 11:19:20 -07005108 const u8 *ies, size_t ies_len, int rssi,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00005109 int omit_rsnxe, bool allow_mld_addr_trans)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005110{
5111 int send_len;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005112 u8 *buf;
5113 size_t buflen;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005114 struct ieee80211_mgmt *reply;
5115 u8 *p;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005116 u16 res = WLAN_STATUS_SUCCESS;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005117
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005118 buflen = sizeof(struct ieee80211_mgmt) + 1024;
5119#ifdef CONFIG_FILS
5120 if (sta && sta->fils_hlp_resp)
5121 buflen += wpabuf_len(sta->fils_hlp_resp);
Hai Shalom81f62d82019-07-22 12:10:00 -07005122 if (sta)
5123 buflen += 150;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005124#endif /* CONFIG_FILS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005125#ifdef CONFIG_OWE
5126 if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
5127 buflen += 150;
5128#endif /* CONFIG_OWE */
Hai Shalom021b0b52019-04-10 11:17:58 -07005129#ifdef CONFIG_DPP2
5130 if (sta && sta->dpp_pfs)
5131 buflen += 5 + sta->dpp_pfs->curve->prime_len;
5132#endif /* CONFIG_DPP2 */
Sunil Ravia04bd252022-05-02 22:54:18 -07005133#ifdef CONFIG_IEEE80211BE
5134 if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
5135 buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
5136 buflen += 3 + sizeof(struct ieee80211_eht_operation);
Sunil Ravi036cec52023-03-29 11:35:17 -07005137 if (hapd->iconf->punct_bitmap)
5138 buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
Sunil Ravia04bd252022-05-02 22:54:18 -07005139 }
5140#endif /* CONFIG_IEEE80211BE */
5141
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005142 buf = os_zalloc(buflen);
5143 if (!buf) {
5144 res = WLAN_STATUS_UNSPECIFIED_FAILURE;
5145 goto done;
5146 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005147 reply = (struct ieee80211_mgmt *) buf;
5148 reply->frame_control =
5149 IEEE80211_FC(WLAN_FC_TYPE_MGMT,
5150 (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
5151 WLAN_FC_STYPE_ASSOC_RESP));
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005152
Dmitry Shmidt29333592017-01-09 12:27:11 -08005153 os_memcpy(reply->da, addr, ETH_ALEN);
Sunil Ravi7f769292024-07-23 22:21:32 +00005154 os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
5155 os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005156
5157 send_len = IEEE80211_HDRLEN;
5158 send_len += sizeof(reply->u.assoc_resp);
5159 reply->u.assoc_resp.capab_info =
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -07005160 host_to_le16(hostapd_own_capab_info(hapd));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005161 reply->u.assoc_resp.status_code = host_to_le16(status_code);
Dmitry Shmidt29333592017-01-09 12:27:11 -08005162
5163 reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) |
5164 BIT(14) | BIT(15));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005165 /* Supported rates */
5166 p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
5167 /* Extended supported rates */
5168 p = hostapd_eid_ext_supp_rates(hapd, p);
5169
Hai Shalomfdcde762020-04-02 11:19:20 -07005170 /* Radio measurement capabilities */
5171 p = hostapd_eid_rm_enabled_capab(hapd, p, buf + buflen - p);
5172
Hai Shalom74f70d42019-02-11 14:42:39 -08005173#ifdef CONFIG_MBO
5174 if (status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS &&
5175 rssi != 0) {
5176 int delta = hapd->iconf->rssi_reject_assoc_rssi - rssi;
5177
5178 p = hostapd_eid_mbo_rssi_assoc_rej(hapd, p, buf + buflen - p,
5179 delta);
5180 }
5181#endif /* CONFIG_MBO */
5182
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005183#ifdef CONFIG_IEEE80211R_AP
Dmitry Shmidt29333592017-01-09 12:27:11 -08005184 if (sta && status_code == WLAN_STATUS_SUCCESS) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005185 /* IEEE 802.11r: Mobility Domain Information, Fast BSS
5186 * Transition Information, RSN, [RIC Response] */
5187 p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005188 buf + buflen - p,
Hai Shalomfdcde762020-04-02 11:19:20 -07005189 sta->auth_alg, ies, ies_len,
5190 omit_rsnxe);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005191 if (!p) {
5192 wpa_printf(MSG_DEBUG,
5193 "FT: Failed to write AssocResp IEs");
5194 res = WLAN_STATUS_UNSPECIFIED_FAILURE;
5195 goto done;
5196 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005197 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005198#endif /* CONFIG_IEEE80211R_AP */
Hai Shalom81f62d82019-07-22 12:10:00 -07005199#ifdef CONFIG_FILS
5200 if (sta && status_code == WLAN_STATUS_SUCCESS &&
5201 (sta->auth_alg == WLAN_AUTH_FILS_SK ||
5202 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
5203 sta->auth_alg == WLAN_AUTH_FILS_PK))
5204 p = wpa_auth_write_assoc_resp_fils(sta->wpa_sm, p,
5205 buf + buflen - p,
5206 ies, ies_len);
5207#endif /* CONFIG_FILS */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005208
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005209#ifdef CONFIG_OWE
Hai Shalom74f70d42019-02-11 14:42:39 -08005210 if (sta && status_code == WLAN_STATUS_SUCCESS &&
5211 (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005212 p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p,
5213 buf + buflen - p,
5214 ies, ies_len);
5215#endif /* CONFIG_OWE */
5216
Dmitry Shmidt29333592017-01-09 12:27:11 -08005217 if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005218 p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005219
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005220 p = hostapd_eid_ht_capabilities(hapd, p);
5221 p = hostapd_eid_ht_operation(hapd, p);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005222
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07005223#ifdef CONFIG_IEEE80211AC
Hai Shalomc3565922019-10-28 11:58:20 -07005224 if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
5225 !is_6ghz_op_class(hapd->iconf->op_class)) {
Dmitry Shmidt7d175302016-09-06 13:11:34 -07005226 u32 nsts = 0, sta_nsts;
5227
Dmitry Shmidt29333592017-01-09 12:27:11 -08005228 if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) {
Dmitry Shmidt7d175302016-09-06 13:11:34 -07005229 struct ieee80211_vht_capabilities *capa;
5230
5231 nsts = (hapd->iface->conf->vht_capab >>
5232 VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
5233 capa = sta->vht_capabilities;
5234 sta_nsts = (le_to_host32(capa->vht_capabilities_info) >>
5235 VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
5236
5237 if (nsts < sta_nsts)
5238 nsts = 0;
5239 else
5240 nsts = sta_nsts;
5241 }
5242 p = hostapd_eid_vht_capabilities(hapd, p, nsts);
Dmitry Shmidt2f74e362015-01-21 13:19:05 -08005243 p = hostapd_eid_vht_operation(hapd, p);
5244 }
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07005245#endif /* CONFIG_IEEE80211AC */
5246
Hai Shalom81f62d82019-07-22 12:10:00 -07005247#ifdef CONFIG_IEEE80211AX
Hai Shalom60840252021-02-19 19:02:11 -08005248 if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
Hai Shalom81f62d82019-07-22 12:10:00 -07005249 p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
5250 p = hostapd_eid_he_operation(hapd, p);
Sunil Ravia04bd252022-05-02 22:54:18 -07005251 p = hostapd_eid_cca(hapd, p);
Hai Shalom81f62d82019-07-22 12:10:00 -07005252 p = hostapd_eid_spatial_reuse(hapd, p);
5253 p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
Hai Shalom4fbc08f2020-05-18 12:37:00 -07005254 p = hostapd_eid_he_6ghz_band_cap(hapd, p);
Hai Shalom81f62d82019-07-22 12:10:00 -07005255 }
5256#endif /* CONFIG_IEEE80211AX */
5257
Sunil Ravi77d572f2023-01-17 23:58:31 +00005258 p = hostapd_eid_ext_capab(hapd, p, false);
Sunil Ravic0f5d412024-09-11 22:12:49 +00005259 p = hostapd_eid_bss_max_idle_period(hapd, p,
5260 sta ? sta->max_idle_period : 0);
Dmitry Shmidt29333592017-01-09 12:27:11 -08005261 if (sta && sta->qos_map_enabled)
Dmitry Shmidt051af732013-10-22 13:52:46 -07005262 p = hostapd_eid_qos_map_set(hapd, p);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005263
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08005264#ifdef CONFIG_FST
5265 if (hapd->iface->fst_ies) {
5266 os_memcpy(p, wpabuf_head(hapd->iface->fst_ies),
5267 wpabuf_len(hapd->iface->fst_ies));
5268 p += wpabuf_len(hapd->iface->fst_ies);
5269 }
5270#endif /* CONFIG_FST */
5271
Hai Shalomfdcde762020-04-02 11:19:20 -07005272#ifdef CONFIG_TESTING_OPTIONS
5273 if (hapd->conf->rsnxe_override_ft &&
5274 buf + buflen - p >=
5275 (long int) wpabuf_len(hapd->conf->rsnxe_override_ft) &&
5276 sta && sta->auth_alg == WLAN_AUTH_FT) {
5277 wpa_printf(MSG_DEBUG, "TESTING: RSNXE FT override");
5278 os_memcpy(p, wpabuf_head(hapd->conf->rsnxe_override_ft),
5279 wpabuf_len(hapd->conf->rsnxe_override_ft));
5280 p += wpabuf_len(hapd->conf->rsnxe_override_ft);
5281 goto rsnxe_done;
5282 }
5283#endif /* CONFIG_TESTING_OPTIONS */
5284 if (!omit_rsnxe)
5285 p = hostapd_eid_rsnxe(hapd, p, buf + buflen - p);
5286#ifdef CONFIG_TESTING_OPTIONS
5287rsnxe_done:
5288#endif /* CONFIG_TESTING_OPTIONS */
Hai Shalomc3565922019-10-28 11:58:20 -07005289
Sunil Ravia04bd252022-05-02 22:54:18 -07005290#ifdef CONFIG_IEEE80211BE
5291 if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005292 if (hapd->conf->mld_ap)
Sunil Ravib0ac25f2024-07-12 01:42:03 +00005293 p = hostapd_eid_eht_ml_assoc(hapd, sta, p);
Sunil Ravia04bd252022-05-02 22:54:18 -07005294 p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
5295 p = hostapd_eid_eht_operation(hapd, p);
5296 }
5297#endif /* CONFIG_IEEE80211BE */
5298
Hai Shalom021b0b52019-04-10 11:17:58 -07005299#ifdef CONFIG_OWE
5300 if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
5301 sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
Hai Shalom899fcc72020-10-19 14:38:18 -07005302 wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
5303 !wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
Hai Shalom021b0b52019-04-10 11:17:58 -07005304 struct wpabuf *pub;
5305
5306 pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
5307 if (!pub) {
5308 res = WLAN_STATUS_UNSPECIFIED_FAILURE;
5309 goto done;
5310 }
5311 /* OWE Diffie-Hellman Parameter element */
5312 *p++ = WLAN_EID_EXTENSION; /* Element ID */
5313 *p++ = 1 + 2 + wpabuf_len(pub); /* Length */
5314 *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */
5315 WPA_PUT_LE16(p, sta->owe_group);
5316 p += 2;
5317 os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub));
5318 p += wpabuf_len(pub);
5319 wpabuf_free(pub);
5320 }
5321#endif /* CONFIG_OWE */
5322
5323#ifdef CONFIG_DPP2
Hai Shalom4fbc08f2020-05-18 12:37:00 -07005324 if (DPP_VERSION > 1 && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
Hai Shalom021b0b52019-04-10 11:17:58 -07005325 sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS &&
5326 wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) {
5327 os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie),
5328 wpabuf_len(sta->dpp_pfs->ie));
5329 p += wpabuf_len(sta->dpp_pfs->ie);
5330 }
5331#endif /* CONFIG_DPP2 */
5332
Dmitry Shmidt2f74e362015-01-21 13:19:05 -08005333#ifdef CONFIG_IEEE80211AC
Dmitry Shmidt29333592017-01-09 12:27:11 -08005334 if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
Dmitry Shmidt2f74e362015-01-21 13:19:05 -08005335 p = hostapd_eid_vendor_vht(hapd, p);
5336#endif /* CONFIG_IEEE80211AC */
5337
Dmitry Shmidt29333592017-01-09 12:27:11 -08005338 if (sta && (sta->flags & WLAN_STA_WMM))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005339 p = hostapd_eid_wmm(hapd, p);
5340
5341#ifdef CONFIG_WPS
Dmitry Shmidt29333592017-01-09 12:27:11 -08005342 if (sta &&
5343 ((sta->flags & WLAN_STA_WPS) ||
5344 ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa))) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005345 struct wpabuf *wps = wps_build_assoc_resp_ie();
5346 if (wps) {
5347 os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
5348 p += wpabuf_len(wps);
5349 wpabuf_free(wps);
5350 }
5351 }
5352#endif /* CONFIG_WPS */
5353
Hai Shalom74f70d42019-02-11 14:42:39 -08005354 if (sta && (sta->flags & WLAN_STA_MULTI_AP))
Sunil Ravi99c035e2024-07-12 01:42:03 +00005355 p = hostapd_eid_multi_ap(hapd, p, buf + buflen - p);
Hai Shalom74f70d42019-02-11 14:42:39 -08005356
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005357#ifdef CONFIG_P2P
Dmitry Shmidt29333592017-01-09 12:27:11 -08005358 if (sta && sta->p2p_ie && hapd->p2p_group) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005359 struct wpabuf *p2p_resp_ie;
5360 enum p2p_status_code status;
5361 switch (status_code) {
5362 case WLAN_STATUS_SUCCESS:
5363 status = P2P_SC_SUCCESS;
5364 break;
5365 case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
5366 status = P2P_SC_FAIL_LIMIT_REACHED;
5367 break;
5368 default:
5369 status = P2P_SC_FAIL_INVALID_PARAMS;
5370 break;
5371 }
5372 p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status);
5373 if (p2p_resp_ie) {
5374 os_memcpy(p, wpabuf_head(p2p_resp_ie),
5375 wpabuf_len(p2p_resp_ie));
5376 p += wpabuf_len(p2p_resp_ie);
5377 wpabuf_free(p2p_resp_ie);
5378 }
5379 }
5380#endif /* CONFIG_P2P */
5381
5382#ifdef CONFIG_P2P_MANAGER
5383 if (hapd->conf->p2p & P2P_MANAGE)
5384 p = hostapd_eid_p2p_manage(hapd, p);
5385#endif /* CONFIG_P2P_MANAGER */
5386
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005387 p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005388
Dmitry Shmidt849734c2016-05-27 09:59:01 -07005389 if (hapd->conf->assocresp_elements &&
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005390 (size_t) (buf + buflen - p) >=
Dmitry Shmidt849734c2016-05-27 09:59:01 -07005391 wpabuf_len(hapd->conf->assocresp_elements)) {
5392 os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
5393 wpabuf_len(hapd->conf->assocresp_elements));
5394 p += wpabuf_len(hapd->conf->assocresp_elements);
5395 }
5396
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005397 send_len += p - reply->u.assoc_resp.variable;
5398
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005399#ifdef CONFIG_FILS
Dmitry Shmidt29333592017-01-09 12:27:11 -08005400 if (sta &&
5401 (sta->auth_alg == WLAN_AUTH_FILS_SK ||
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005402 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
5403 sta->auth_alg == WLAN_AUTH_FILS_PK) &&
5404 status_code == WLAN_STATUS_SUCCESS) {
5405 struct ieee802_11_elems elems;
5406
5407 if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005408 ParseFailed || !elems.fils_session) {
5409 res = WLAN_STATUS_UNSPECIFIED_FAILURE;
5410 goto done;
5411 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005412
5413 /* FILS Session */
5414 *p++ = WLAN_EID_EXTENSION; /* Element ID */
5415 *p++ = 1 + FILS_SESSION_LEN; /* Length */
5416 *p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
5417 os_memcpy(p, elems.fils_session, FILS_SESSION_LEN);
5418 send_len += 2 + 1 + FILS_SESSION_LEN;
5419
5420 send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len,
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005421 buflen, sta->fils_hlp_resp);
5422 if (send_len < 0) {
5423 res = WLAN_STATUS_UNSPECIFIED_FAILURE;
5424 goto done;
5425 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005426 }
5427#endif /* CONFIG_FILS */
5428
Hai Shalomfdcde762020-04-02 11:19:20 -07005429 if (hostapd_drv_send_mlme(hapd, reply, send_len, 0, NULL, 0, 0) < 0) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005430 wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
5431 strerror(errno));
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005432 res = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005433 }
5434
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005435done:
5436 os_free(buf);
5437 return res;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005438}
5439
5440
Roshan Pius3a1667e2018-07-03 15:17:14 -07005441#ifdef CONFIG_OWE
5442u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
5443 const u8 *owe_dh, u8 owe_dh_len,
Hai Shalomfdcde762020-04-02 11:19:20 -07005444 u8 *owe_buf, size_t owe_buf_len, u16 *status)
Roshan Pius3a1667e2018-07-03 15:17:14 -07005445{
5446#ifdef CONFIG_TESTING_OPTIONS
5447 if (hapd->conf->own_ie_override) {
5448 wpa_printf(MSG_DEBUG, "OWE: Using IE override");
Hai Shalomfdcde762020-04-02 11:19:20 -07005449 *status = WLAN_STATUS_SUCCESS;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005450 return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
5451 owe_buf_len, NULL, 0);
5452 }
5453#endif /* CONFIG_TESTING_OPTIONS */
5454
5455 if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
5456 wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
5457 owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
5458 owe_buf_len, NULL, 0);
Hai Shalomfdcde762020-04-02 11:19:20 -07005459 *status = WLAN_STATUS_SUCCESS;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005460 return owe_buf;
5461 }
5462
Hai Shalom81f62d82019-07-22 12:10:00 -07005463 if (sta->owe_pmk && sta->external_dh_updated) {
5464 wpa_printf(MSG_DEBUG, "OWE: Using previously derived PMK");
Hai Shalomfdcde762020-04-02 11:19:20 -07005465 *status = WLAN_STATUS_SUCCESS;
Hai Shalom81f62d82019-07-22 12:10:00 -07005466 return owe_buf;
5467 }
5468
Hai Shalomfdcde762020-04-02 11:19:20 -07005469 *status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
5470 if (*status != WLAN_STATUS_SUCCESS)
Roshan Pius3a1667e2018-07-03 15:17:14 -07005471 return NULL;
5472
5473 owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
5474 owe_buf_len, NULL, 0);
5475
5476 if (sta->owe_ecdh && owe_buf) {
5477 struct wpabuf *pub;
5478
5479 pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
5480 if (!pub) {
Hai Shalomfdcde762020-04-02 11:19:20 -07005481 *status = WLAN_STATUS_UNSPECIFIED_FAILURE;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005482 return owe_buf;
5483 }
5484
5485 /* OWE Diffie-Hellman Parameter element */
5486 *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
5487 *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
5488 *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
5489 */
5490 WPA_PUT_LE16(owe_buf, sta->owe_group);
5491 owe_buf += 2;
5492 os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
5493 owe_buf += wpabuf_len(pub);
5494 wpabuf_free(pub);
5495 }
5496
5497 return owe_buf;
5498}
5499#endif /* CONFIG_OWE */
5500
5501
Paul Stewart092955c2017-02-06 09:13:09 -08005502#ifdef CONFIG_FILS
5503
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005504void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta)
Paul Stewart092955c2017-02-06 09:13:09 -08005505{
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005506 u16 reply_res;
Paul Stewart092955c2017-02-06 09:13:09 -08005507
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005508 wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR,
5509 MAC2STR(sta->addr));
5510 eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
5511 if (!sta->fils_pending_assoc_req)
Paul Stewart092955c2017-02-06 09:13:09 -08005512 return;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005513 reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS,
5514 sta->fils_pending_assoc_is_reassoc,
5515 sta->fils_pending_assoc_req,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00005516 sta->fils_pending_assoc_req_len, 0, 0,
5517 true);
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005518 os_free(sta->fils_pending_assoc_req);
5519 sta->fils_pending_assoc_req = NULL;
5520 sta->fils_pending_assoc_req_len = 0;
5521 wpabuf_free(sta->fils_hlp_resp);
5522 sta->fils_hlp_resp = NULL;
5523 wpabuf_free(sta->hlp_dhcp_discover);
5524 sta->hlp_dhcp_discover = NULL;
Paul Stewart092955c2017-02-06 09:13:09 -08005525
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005526 /*
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005527 * Remove the station in case transmission of a success response fails.
5528 * At this point the station was already added associated to the driver.
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005529 */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005530 if (reply_res != WLAN_STATUS_SUCCESS)
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005531 hostapd_drv_sta_remove(hapd, sta->addr);
Paul Stewart092955c2017-02-06 09:13:09 -08005532}
5533
5534
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005535void fils_hlp_timeout(void *eloop_ctx, void *eloop_data)
Paul Stewart092955c2017-02-06 09:13:09 -08005536{
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005537 struct hostapd_data *hapd = eloop_ctx;
5538 struct sta_info *sta = eloop_data;
Paul Stewart092955c2017-02-06 09:13:09 -08005539
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005540 wpa_printf(MSG_DEBUG,
5541 "FILS: HLP response timeout - continue with association response for "
5542 MACSTR, MAC2STR(sta->addr));
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005543 if (sta->fils_drv_assoc_finish)
5544 hostapd_notify_assoc_fils_finish(hapd, sta);
5545 else
5546 fils_hlp_finish_assoc(hapd, sta);
Paul Stewart092955c2017-02-06 09:13:09 -08005547}
5548
5549#endif /* CONFIG_FILS */
5550
5551
Sunil Ravib0ac25f2024-07-12 01:42:03 +00005552#ifdef CONFIG_IEEE80211BE
5553static struct sta_info * handle_mlo_translate(struct hostapd_data *hapd,
5554 const struct ieee80211_mgmt *mgmt,
5555 size_t len, bool reassoc,
5556 struct hostapd_data **assoc_hapd)
5557{
5558 struct sta_info *sta;
5559 struct ieee802_11_elems elems;
5560 u8 mld_addr[ETH_ALEN];
5561 const u8 *pos;
5562
5563 if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be)
5564 return NULL;
5565
5566 if (reassoc) {
5567 len -= IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req);
5568 pos = mgmt->u.reassoc_req.variable;
5569 } else {
5570 len -= IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req);
5571 pos = mgmt->u.assoc_req.variable;
5572 }
5573
5574 if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed)
5575 return NULL;
5576
5577 if (hostapd_process_ml_assoc_req_addr(hapd, elems.basic_mle,
5578 elems.basic_mle_len,
5579 mld_addr))
5580 return NULL;
5581
5582 sta = ap_get_sta(hapd, mld_addr);
5583 if (!sta)
5584 return NULL;
5585
5586 wpa_printf(MSG_DEBUG, "MLD: assoc: mld=" MACSTR ", link=" MACSTR,
5587 MAC2STR(mld_addr), MAC2STR(mgmt->sa));
5588
5589 return hostapd_ml_get_assoc_sta(hapd, sta, assoc_hapd);
5590}
5591#endif /* CONFIG_IEEE80211BE */
5592
5593
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005594static void handle_assoc(struct hostapd_data *hapd,
5595 const struct ieee80211_mgmt *mgmt, size_t len,
Hai Shalom74f70d42019-02-11 14:42:39 -08005596 int reassoc, int rssi)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005597{
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005598 u16 capab_info, listen_interval, seq_ctrl, fc;
Hai Shalomb755a2a2020-04-23 21:49:02 -07005599 int resp = WLAN_STATUS_SUCCESS;
Hai Shalom899fcc72020-10-19 14:38:18 -07005600 u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005601 const u8 *pos;
5602 int left, i;
5603 struct sta_info *sta;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005604 u8 *tmp = NULL;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005605#ifdef CONFIG_FILS
5606 int delay_assoc = 0;
5607#endif /* CONFIG_FILS */
Hai Shalomfdcde762020-04-02 11:19:20 -07005608 int omit_rsnxe = 0;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005609 bool set_beacon = false;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00005610 bool mld_addrs_not_translated = false;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005611
5612 if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
5613 sizeof(mgmt->u.assoc_req))) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08005614 wpa_printf(MSG_INFO, "handle_assoc(reassoc=%d) - too short payload (len=%lu)",
5615 reassoc, (unsigned long) len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005616 return;
5617 }
5618
Dmitry Shmidt8da800a2013-04-24 12:57:01 -07005619#ifdef CONFIG_TESTING_OPTIONS
5620 if (reassoc) {
Dmitry Shmidt7832adb2014-04-29 10:53:02 -07005621 if (hapd->iconf->ignore_reassoc_probability > 0.0 &&
Dmitry Shmidt8da800a2013-04-24 12:57:01 -07005622 drand48() < hapd->iconf->ignore_reassoc_probability) {
5623 wpa_printf(MSG_INFO,
5624 "TESTING: ignoring reassoc request from "
5625 MACSTR, MAC2STR(mgmt->sa));
5626 return;
5627 }
5628 } else {
Dmitry Shmidt7832adb2014-04-29 10:53:02 -07005629 if (hapd->iconf->ignore_assoc_probability > 0.0 &&
Dmitry Shmidt8da800a2013-04-24 12:57:01 -07005630 drand48() < hapd->iconf->ignore_assoc_probability) {
5631 wpa_printf(MSG_INFO,
5632 "TESTING: ignoring assoc request from "
5633 MACSTR, MAC2STR(mgmt->sa));
5634 return;
5635 }
5636 }
5637#endif /* CONFIG_TESTING_OPTIONS */
5638
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005639 fc = le_to_host16(mgmt->frame_control);
5640 seq_ctrl = le_to_host16(mgmt->seq_ctrl);
5641
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005642 if (reassoc) {
5643 capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info);
5644 listen_interval = le_to_host16(
5645 mgmt->u.reassoc_req.listen_interval);
5646 wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR
5647 " capab_info=0x%02x listen_interval=%d current_ap="
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005648 MACSTR " seq_ctrl=0x%x%s",
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005649 MAC2STR(mgmt->sa), capab_info, listen_interval,
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005650 MAC2STR(mgmt->u.reassoc_req.current_ap),
5651 seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005652 left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
5653 pos = mgmt->u.reassoc_req.variable;
5654 } else {
5655 capab_info = le_to_host16(mgmt->u.assoc_req.capab_info);
5656 listen_interval = le_to_host16(
5657 mgmt->u.assoc_req.listen_interval);
5658 wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005659 " capab_info=0x%02x listen_interval=%d "
5660 "seq_ctrl=0x%x%s",
5661 MAC2STR(mgmt->sa), capab_info, listen_interval,
5662 seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005663 left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
5664 pos = mgmt->u.assoc_req.variable;
5665 }
5666
5667 sta = ap_get_sta(hapd, mgmt->sa);
Sunil Ravib0ac25f2024-07-12 01:42:03 +00005668
5669#ifdef CONFIG_IEEE80211BE
5670 /*
5671 * It is possible that the association frame is from an associated
5672 * non-AP MLD station, that tries to re-associate using different link
5673 * addresses. In such a case, try to find the station based on the AP
5674 * MLD MAC address.
5675 */
5676 if (!sta) {
5677 struct hostapd_data *assoc_hapd;
5678
5679 sta = handle_mlo_translate(hapd, mgmt, len, reassoc,
5680 &assoc_hapd);
5681 if (sta) {
5682 wpa_printf(MSG_DEBUG,
5683 "MLD: Switching to assoc hapd/station");
5684 hapd = assoc_hapd;
5685 mld_addrs_not_translated = true;
5686 }
5687 }
5688#endif /* CONFIG_IEEE80211BE */
5689
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005690#ifdef CONFIG_IEEE80211R_AP
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005691 if (sta && sta->auth_alg == WLAN_AUTH_FT &&
5692 (sta->flags & WLAN_STA_AUTH) == 0) {
5693 wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
5694 "prior to authentication since it is using "
5695 "over-the-DS FT", MAC2STR(mgmt->sa));
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005696
5697 /*
5698 * Mark station as authenticated, to avoid adding station
5699 * entry in the driver as associated and not authenticated
5700 */
5701 sta->flags |= WLAN_STA_AUTH;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005702 } else
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005703#endif /* CONFIG_IEEE80211R_AP */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005704 if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
Dmitry Shmidt29333592017-01-09 12:27:11 -08005705 if (hapd->iface->current_mode &&
5706 hapd->iface->current_mode->mode ==
5707 HOSTAPD_MODE_IEEE80211AD) {
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005708 int acl_res;
Hai Shalomfdcde762020-04-02 11:19:20 -07005709 struct radius_sta info;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005710
Hai Shalomfdcde762020-04-02 11:19:20 -07005711 acl_res = ieee802_11_allowed_address(hapd, mgmt->sa,
5712 (const u8 *) mgmt,
5713 len, &info);
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005714 if (acl_res == HOSTAPD_ACL_REJECT) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005715 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
5716 "Ignore Association Request frame from "
5717 MACSTR " due to ACL reject",
5718 MAC2STR(mgmt->sa));
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005719 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
5720 goto fail;
5721 }
5722 if (acl_res == HOSTAPD_ACL_PENDING)
5723 return;
5724
Dmitry Shmidt29333592017-01-09 12:27:11 -08005725 /* DMG/IEEE 802.11ad does not use authentication.
5726 * Allocate sta entry upon association. */
5727 sta = ap_sta_add(hapd, mgmt->sa);
5728 if (!sta) {
5729 hostapd_logger(hapd, mgmt->sa,
5730 HOSTAPD_MODULE_IEEE80211,
5731 HOSTAPD_LEVEL_INFO,
5732 "Failed to add STA");
5733 resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
5734 goto fail;
5735 }
5736
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005737 acl_res = ieee802_11_set_radius_info(
Hai Shalomfdcde762020-04-02 11:19:20 -07005738 hapd, sta, acl_res, &info);
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005739 if (acl_res) {
5740 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
5741 goto fail;
5742 }
5743
Dmitry Shmidt29333592017-01-09 12:27:11 -08005744 hostapd_logger(hapd, sta->addr,
5745 HOSTAPD_MODULE_IEEE80211,
5746 HOSTAPD_LEVEL_DEBUG,
5747 "Skip authentication for DMG/IEEE 802.11ad");
5748 sta->flags |= WLAN_STA_AUTH;
5749 wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
5750 sta->auth_alg = WLAN_AUTH_OPEN;
5751 } else {
5752 hostapd_logger(hapd, mgmt->sa,
5753 HOSTAPD_MODULE_IEEE80211,
5754 HOSTAPD_LEVEL_INFO,
5755 "Station tried to associate before authentication (aid=%d flags=0x%x)",
5756 sta ? sta->aid : -1,
5757 sta ? sta->flags : 0);
5758 send_deauth(hapd, mgmt->sa,
5759 WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
5760 return;
5761 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005762 }
5763
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005764 if ((fc & WLAN_FC_RETRY) &&
5765 sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
5766 sta->last_seq_ctrl == seq_ctrl &&
Paul Stewart092955c2017-02-06 09:13:09 -08005767 sta->last_subtype == (reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
5768 WLAN_FC_STYPE_ASSOC_REQ)) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005769 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
5770 HOSTAPD_LEVEL_DEBUG,
5771 "Drop repeated association frame seq_ctrl=0x%x",
5772 seq_ctrl);
5773 return;
5774 }
5775 sta->last_seq_ctrl = seq_ctrl;
5776 sta->last_subtype = reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
5777 WLAN_FC_STYPE_ASSOC_REQ;
5778
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005779 if (hapd->tkip_countermeasures) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005780 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005781 goto fail;
5782 }
5783
5784 if (listen_interval > hapd->conf->max_listen_interval) {
5785 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
5786 HOSTAPD_LEVEL_DEBUG,
5787 "Too large Listen Interval (%d)",
5788 listen_interval);
5789 resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE;
5790 goto fail;
5791 }
5792
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005793#ifdef CONFIG_MBO
5794 if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
5795 resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
5796 goto fail;
5797 }
Hai Shalom74f70d42019-02-11 14:42:39 -08005798
5799 if (hapd->iconf->rssi_reject_assoc_rssi && rssi &&
5800 rssi < hapd->iconf->rssi_reject_assoc_rssi &&
5801 (sta->auth_rssi == 0 ||
5802 sta->auth_rssi < hapd->iconf->rssi_reject_assoc_rssi)) {
5803 resp = WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS;
5804 goto fail;
5805 }
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005806#endif /* CONFIG_MBO */
5807
Sunil Ravi876a49b2025-02-03 19:18:32 +00005808 if (hapd->conf->wpa && check_sa_query(hapd, sta, reassoc, pos, left)) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005809 resp = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
5810 goto fail;
5811 }
5812
Dmitry Shmidt849734c2016-05-27 09:59:01 -07005813 /*
5814 * sta->capability is used in check_assoc_ies() for RRM enabled
5815 * capability element.
5816 */
5817 sta->capability = capab_info;
5818
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005819#ifdef CONFIG_FILS
5820 if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
5821 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
5822 sta->auth_alg == WLAN_AUTH_FILS_PK) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005823 int res;
5824
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005825 /* The end of the payload is encrypted. Need to decrypt it
5826 * before parsing. */
5827
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005828 tmp = os_memdup(pos, left);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005829 if (!tmp) {
5830 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
5831 goto fail;
5832 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005833
Roshan Pius3a1667e2018-07-03 15:17:14 -07005834 res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
5835 len, tmp, left);
5836 if (res < 0) {
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005837 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
5838 goto fail;
5839 }
5840 pos = tmp;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005841 left = res;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005842 }
5843#endif /* CONFIG_FILS */
5844
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005845 /* followed by SSID and Supported rates; and HT capabilities if 802.11n
5846 * is used */
5847 resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
5848 if (resp != WLAN_STATUS_SUCCESS)
5849 goto fail;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00005850#ifdef CONFIG_IEEE80211R_AP
5851 if (reassoc && sta->auth_alg == WLAN_AUTH_FT)
5852 omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
5853#endif /* CONFIG_IEEE80211R_AP */
5854 if (hapd->conf->rsn_override_omit_rsnxe)
5855 omit_rsnxe = 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005856
5857 if (hostapd_get_aid(hapd, sta) < 0) {
5858 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
5859 HOSTAPD_LEVEL_INFO, "No room for more AIDs");
5860 resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
5861 goto fail;
5862 }
5863
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005864 sta->listen_interval = listen_interval;
5865
Roshan Pius3a1667e2018-07-03 15:17:14 -07005866 if (hapd->iface->current_mode &&
5867 hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005868 sta->flags |= WLAN_STA_NONERP;
5869 for (i = 0; i < sta->supported_rates_len; i++) {
5870 if ((sta->supported_rates[i] & 0x7f) > 22) {
5871 sta->flags &= ~WLAN_STA_NONERP;
5872 break;
5873 }
5874 }
5875 if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) {
5876 sta->nonerp_set = 1;
5877 hapd->iface->num_sta_non_erp++;
5878 if (hapd->iface->num_sta_non_erp == 1)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005879 set_beacon = true;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005880 }
5881
5882 if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) &&
5883 !sta->no_short_slot_time_set) {
5884 sta->no_short_slot_time_set = 1;
5885 hapd->iface->num_sta_no_short_slot_time++;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005886 if (hapd->iface->current_mode &&
5887 hapd->iface->current_mode->mode ==
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005888 HOSTAPD_MODE_IEEE80211G &&
5889 hapd->iface->num_sta_no_short_slot_time == 1)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005890 set_beacon = true;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005891 }
5892
5893 if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
5894 sta->flags |= WLAN_STA_SHORT_PREAMBLE;
5895 else
5896 sta->flags &= ~WLAN_STA_SHORT_PREAMBLE;
5897
5898 if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
5899 !sta->no_short_preamble_set) {
5900 sta->no_short_preamble_set = 1;
5901 hapd->iface->num_sta_no_short_preamble++;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005902 if (hapd->iface->current_mode &&
5903 hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005904 && hapd->iface->num_sta_no_short_preamble == 1)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005905 set_beacon = true;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005906 }
5907
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005908 if (update_ht_state(hapd, sta) > 0)
5909 set_beacon = true;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005910
5911 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
5912 HOSTAPD_LEVEL_DEBUG,
5913 "association OK (aid %d)", sta->aid);
5914 /* Station will be marked associated, after it acknowledges AssocResp
5915 */
5916 sta->flags |= WLAN_STA_ASSOC_REQ_OK;
5917
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005918 if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) {
5919 wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out "
5920 "SA Query procedure", reassoc ? "re" : "");
5921 /* TODO: Send a protected Disassociate frame to the STA using
5922 * the old key and Reason Code "Previous Authentication no
5923 * longer valid". Make sure this is only sent protected since
5924 * unprotected frame would be received by the STA that is now
5925 * trying to associate.
5926 */
5927 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005928
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005929 /* Make sure that the previously registered inactivity timer will not
5930 * remove the STA immediately. */
5931 sta->timeout_next = STA_NULLFUNC;
5932
Dmitry Shmidtaca489e2016-09-28 15:44:14 -07005933#ifdef CONFIG_TAXONOMY
5934 taxonomy_sta_info_assoc_req(hapd, sta, pos, left);
5935#endif /* CONFIG_TAXONOMY */
5936
Dmitry Shmidt29333592017-01-09 12:27:11 -08005937 sta->pending_wds_enable = 0;
5938
Paul Stewart092955c2017-02-06 09:13:09 -08005939#ifdef CONFIG_FILS
5940 if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
5941 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005942 sta->auth_alg == WLAN_AUTH_FILS_PK) {
5943 if (fils_process_hlp(hapd, sta, pos, left) > 0)
5944 delay_assoc = 1;
5945 }
Paul Stewart092955c2017-02-06 09:13:09 -08005946#endif /* CONFIG_FILS */
5947
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005948 if (set_beacon)
Sunil Ravi7f769292024-07-23 22:21:32 +00005949 ieee802_11_update_beacons(hapd->iface);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005950
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005951 fail:
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005952
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005953 /*
5954 * In case of a successful response, add the station to the driver.
5955 * Otherwise, the kernel may ignore Data frames before we process the
5956 * ACK frame (TX status). In case of a failure, this station will be
5957 * removed.
5958 *
5959 * Note that this is not compliant with the IEEE 802.11 standard that
5960 * states that a non-AP station should transition into the
5961 * authenticated/associated state only after the station acknowledges
5962 * the (Re)Association Response frame. However, still do this as:
5963 *
5964 * 1. In case the station does not acknowledge the (Re)Association
5965 * Response frame, it will be removed.
5966 * 2. Data frames will be dropped in the kernel until the station is
5967 * set into authorized state, and there are no significant known
5968 * issues with processing other non-Data Class 3 frames during this
5969 * window.
5970 */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00005971 if (sta)
5972 hostapd_process_assoc_ml_info(hapd, sta, pos, left, reassoc,
5973 resp, false);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005974
Hai Shalom74f70d42019-02-11 14:42:39 -08005975 if (resp == WLAN_STATUS_SUCCESS && sta &&
5976 add_associated_sta(hapd, sta, reassoc))
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005977 resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
5978
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005979#ifdef CONFIG_FILS
Hai Shalom74f70d42019-02-11 14:42:39 -08005980 if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS &&
5981 eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta) &&
5982 sta->fils_pending_assoc_req) {
5983 /* Do not reschedule fils_hlp_timeout in case the station
5984 * retransmits (Re)Association Request frame while waiting for
5985 * the previously started FILS HLP wait, so that the timeout can
5986 * be determined from the first pending attempt. */
5987 wpa_printf(MSG_DEBUG,
5988 "FILS: Continue waiting for HLP processing before sending (Re)Association Response frame to "
5989 MACSTR, MAC2STR(sta->addr));
5990 os_free(tmp);
5991 return;
5992 }
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005993 if (sta) {
5994 eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
5995 os_free(sta->fils_pending_assoc_req);
5996 sta->fils_pending_assoc_req = NULL;
5997 sta->fils_pending_assoc_req_len = 0;
5998 wpabuf_free(sta->fils_hlp_resp);
5999 sta->fils_hlp_resp = NULL;
6000 }
6001 if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) {
6002 sta->fils_pending_assoc_req = tmp;
6003 sta->fils_pending_assoc_req_len = left;
6004 sta->fils_pending_assoc_is_reassoc = reassoc;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006005 sta->fils_drv_assoc_finish = 0;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08006006 wpa_printf(MSG_DEBUG,
6007 "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
6008 MACSTR, MAC2STR(sta->addr));
6009 eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
6010 eloop_register_timeout(0, hapd->conf->fils_hlp_wait_time * 1024,
6011 fils_hlp_timeout, hapd, sta);
6012 return;
6013 }
6014#endif /* CONFIG_FILS */
6015
Hai Shalomb755a2a2020-04-23 21:49:02 -07006016 if (resp >= 0)
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006017 reply_res = send_assoc_resp(hapd,
6018 mld_addrs_not_translated ?
6019 NULL : sta,
6020 mgmt->sa, resp, reassoc,
6021 pos, left, rssi, omit_rsnxe,
6022 !mld_addrs_not_translated);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08006023 os_free(tmp);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006024
6025 /*
Hai Shalom899fcc72020-10-19 14:38:18 -07006026 * Remove the station in case transmission of a success response fails
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006027 * (the STA was added associated to the driver) or if the station was
6028 * previously added unassociated.
6029 */
Dmitry Shmidt29333592017-01-09 12:27:11 -08006030 if (sta && ((reply_res != WLAN_STATUS_SUCCESS &&
6031 resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc)) {
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006032 hostapd_drv_sta_remove(hapd, sta->addr);
6033 sta->added_unassoc = 0;
6034 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006035}
6036
6037
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006038static void hostapd_deauth_sta(struct hostapd_data *hapd,
6039 struct sta_info *sta,
6040 const struct ieee80211_mgmt *mgmt)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006041{
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006042 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
6043 "deauthentication: STA=" MACSTR " reason_code=%d",
6044 MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006045
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006046 ap_sta_set_authorized(hapd, sta, 0);
6047 sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
6048 sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
6049 WLAN_STA_ASSOC_REQ_OK);
6050 hostapd_set_sta_flags(hapd, sta);
6051 wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
6052 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
6053 HOSTAPD_LEVEL_DEBUG, "deauthenticated");
6054 mlme_deauthenticate_indication(
6055 hapd, sta, le_to_host16(mgmt->u.deauth.reason_code));
6056 sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
6057 ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
6058 ap_free_sta(hapd, sta);
6059}
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006060
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006061
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006062static void hostapd_disassoc_sta(struct hostapd_data *hapd,
6063 struct sta_info *sta,
6064 const struct ieee80211_mgmt *mgmt)
6065{
6066 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
6067 "disassocation: STA=" MACSTR " reason_code=%d",
6068 MAC2STR(mgmt->sa), le_to_host16(mgmt->u.disassoc.reason_code));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006069
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08006070 ap_sta_set_authorized(hapd, sta, 0);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08006071 sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006072 sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
Hai Shalomfdcde762020-04-02 11:19:20 -07006073 hostapd_set_sta_flags(hapd, sta);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006074 wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
6075 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
6076 HOSTAPD_LEVEL_INFO, "disassociated");
6077 sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
6078 ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
6079 /* Stop Accounting and IEEE 802.1X sessions, but leave the STA
6080 * authenticated. */
6081 accounting_sta_stop(hapd, sta);
Dmitry Shmidtde47be72016-01-07 12:52:55 -08006082 ieee802_1x_free_station(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08006083 if (sta->ipaddr)
6084 hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
6085 ap_sta_ip6addr_del(hapd, sta);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006086 hostapd_drv_sta_remove(hapd, sta->addr);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006087 sta->added_unassoc = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006088
6089 if (sta->timeout_next == STA_NULLFUNC ||
6090 sta->timeout_next == STA_DISASSOC) {
6091 sta->timeout_next = STA_DEAUTH;
6092 eloop_cancel_timeout(ap_handle_timer, hapd, sta);
6093 eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
6094 hapd, sta);
6095 }
6096
6097 mlme_disassociate_indication(
6098 hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code));
Dmitry Shmidt29333592017-01-09 12:27:11 -08006099
6100 /* DMG/IEEE 802.11ad does not use deauthication. Deallocate sta upon
6101 * disassociation. */
6102 if (hapd->iface->current_mode &&
6103 hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
6104 sta->flags &= ~WLAN_STA_AUTH;
6105 wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
6106 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
6107 HOSTAPD_LEVEL_DEBUG, "deauthenticated");
6108 ap_free_sta(hapd, sta);
6109 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006110}
6111
6112
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006113static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
6114 struct sta_info *sta,
6115 const struct ieee80211_mgmt *mgmt,
6116 bool disassoc)
6117{
6118#ifdef CONFIG_IEEE80211BE
6119 struct hostapd_data *assoc_hapd, *tmp_hapd;
6120 struct sta_info *assoc_sta;
Sunil Ravi7f769292024-07-23 22:21:32 +00006121 struct sta_info *tmp_sta;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006122
6123 if (!hostapd_is_mld_ap(hapd))
6124 return false;
6125
6126 /*
6127 * Get the station on which the association was performed, as it holds
6128 * the information about all the other links.
6129 */
6130 assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006131 if (!assoc_sta)
6132 return false;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006133
Sunil Ravi7f769292024-07-23 22:21:32 +00006134 for_each_mld_link(tmp_hapd, assoc_hapd) {
6135 if (tmp_hapd == assoc_hapd)
6136 continue;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006137
Sunil Ravi7f769292024-07-23 22:21:32 +00006138 if (!assoc_sta->mld_info.links[tmp_hapd->mld_link_id].valid)
6139 continue;
6140
6141 for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
6142 tmp_sta = tmp_sta->next) {
6143 if (tmp_sta->mld_assoc_link_id !=
6144 assoc_sta->mld_assoc_link_id ||
6145 tmp_sta->aid != assoc_sta->aid)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006146 continue;
6147
Sunil Ravi7f769292024-07-23 22:21:32 +00006148 if (!disassoc)
6149 hostapd_deauth_sta(tmp_hapd, tmp_sta, mgmt);
6150 else
6151 hostapd_disassoc_sta(tmp_hapd, tmp_sta, mgmt);
6152 break;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006153 }
6154 }
6155
6156 /* Remove the station on which the association was performed. */
6157 if (!disassoc)
6158 hostapd_deauth_sta(assoc_hapd, assoc_sta, mgmt);
6159 else
6160 hostapd_disassoc_sta(assoc_hapd, assoc_sta, mgmt);
6161
6162 return true;
6163#else /* CONFIG_IEEE80211BE */
6164 return false;
6165#endif /* CONFIG_IEEE80211BE */
6166}
6167
6168
6169static void handle_disassoc(struct hostapd_data *hapd,
6170 const struct ieee80211_mgmt *mgmt, size_t len)
6171{
6172 struct sta_info *sta;
6173
6174 if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
6175 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
6176 "handle_disassoc - too short payload (len=%lu)",
6177 (unsigned long) len);
6178 return;
6179 }
6180
6181 sta = ap_get_sta(hapd, mgmt->sa);
6182 if (!sta) {
6183 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
6184 " trying to disassociate, but it is not associated",
6185 MAC2STR(mgmt->sa));
6186 return;
6187 }
6188
6189 if (hostapd_ml_handle_disconnect(hapd, sta, mgmt, true))
6190 return;
6191
6192 hostapd_disassoc_sta(hapd, sta, mgmt);
6193}
6194
6195
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006196static void handle_deauth(struct hostapd_data *hapd,
6197 const struct ieee80211_mgmt *mgmt, size_t len)
6198{
6199 struct sta_info *sta;
6200
6201 if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006202 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
6203 "handle_deauth - too short payload (len=%lu)",
6204 (unsigned long) len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006205 return;
6206 }
6207
Hai Shaloma20dcd72022-02-04 13:43:00 -08006208 /* Clear the PTKSA cache entries for PASN */
6209 ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE);
6210
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006211 sta = ap_get_sta(hapd, mgmt->sa);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006212 if (!sta) {
6213 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
6214 " trying to deauthenticate, but it is not authenticated",
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006215 MAC2STR(mgmt->sa));
6216 return;
6217 }
6218
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006219 if (hostapd_ml_handle_disconnect(hapd, sta, mgmt, false))
6220 return;
6221
6222 hostapd_deauth_sta(hapd, sta, mgmt);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006223}
6224
6225
6226static void handle_beacon(struct hostapd_data *hapd,
6227 const struct ieee80211_mgmt *mgmt, size_t len,
6228 struct hostapd_frame_info *fi)
6229{
6230 struct ieee802_11_elems elems;
6231
6232 if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08006233 wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)",
6234 (unsigned long) len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006235 return;
6236 }
6237
6238 (void) ieee802_11_parse_elems(mgmt->u.beacon.variable,
6239 len - (IEEE80211_HDRLEN +
6240 sizeof(mgmt->u.beacon)), &elems,
6241 0);
6242
6243 ap_list_process_beacon(hapd->iface, mgmt, &elems, fi);
6244}
6245
6246
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00006247static int hostapd_action_vs(struct hostapd_data *hapd,
6248 struct sta_info *sta,
6249 const struct ieee80211_mgmt *mgmt, size_t len,
6250 unsigned int freq, bool protected)
6251{
6252 const u8 *pos, *end;
6253 u32 oui_type;
6254
Sunil Ravi876a49b2025-02-03 19:18:32 +00006255 pos = (const u8 *) &mgmt->u.action;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00006256 end = ((const u8 *) mgmt) + len;
6257
6258 if (end - pos < 1 + 4)
6259 return -1;
6260 pos++;
6261
6262 oui_type = WPA_GET_BE32(pos);
6263 pos += 4;
6264
6265 switch (oui_type) {
6266 case WFA_CAPAB_VENDOR_TYPE:
6267 hostapd_wfa_capab(hapd, sta, pos, end);
6268 return 0;
6269 default:
6270 wpa_printf(MSG_DEBUG,
6271 "Ignore unknown Vendor Specific Action frame OUI/type %08x%s",
6272 oui_type, protected ? " (protected)" : "");
6273 break;
6274 }
6275
6276 return -1;
6277}
6278
6279
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006280static int robust_action_frame(u8 category)
6281{
6282 return category != WLAN_ACTION_PUBLIC &&
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00006283 category != WLAN_ACTION_HT &&
6284 category != WLAN_ACTION_UNPROTECTED_WNM &&
6285 category != WLAN_ACTION_SELF_PROTECTED &&
6286 category != WLAN_ACTION_UNPROTECTED_DMG &&
6287 category != WLAN_ACTION_VHT &&
6288 category != WLAN_ACTION_UNPROTECTED_S1G &&
6289 category != WLAN_ACTION_HE &&
6290 category != WLAN_ACTION_EHT &&
6291 category != WLAN_ACTION_VENDOR_SPECIFIC;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006292}
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006293
6294
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006295static int handle_action(struct hostapd_data *hapd,
Roshan Pius3a1667e2018-07-03 15:17:14 -07006296 const struct ieee80211_mgmt *mgmt, size_t len,
6297 unsigned int freq)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006298{
6299 struct sta_info *sta;
Hai Shalom74f70d42019-02-11 14:42:39 -08006300 u8 *action __maybe_unused;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006301
Hai Shalom74f70d42019-02-11 14:42:39 -08006302 if (len < IEEE80211_HDRLEN + 2 + 1) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006303 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
6304 HOSTAPD_LEVEL_DEBUG,
6305 "handle_action - too short payload (len=%lu)",
6306 (unsigned long) len);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006307 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006308 }
6309
Hai Shalom74f70d42019-02-11 14:42:39 -08006310 action = (u8 *) &mgmt->u.action.u;
6311 wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR
6312 " da " MACSTR " len %d freq %u",
6313 mgmt->u.action.category, *action,
6314 MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) len, freq);
6315
6316 sta = ap_get_sta(hapd, mgmt->sa);
6317
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08006318 if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
6319 (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
6320 wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
6321 "frame (category=%u) from unassociated STA " MACSTR,
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08006322 mgmt->u.action.category, MAC2STR(mgmt->sa));
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006323 return 0;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08006324 }
6325
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006326 if (sta && (sta->flags & WLAN_STA_MFP) &&
Dmitry Shmidt18463232014-01-24 12:29:41 -08006327 !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) &&
6328 robust_action_frame(mgmt->u.action.category)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006329 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
6330 HOSTAPD_LEVEL_DEBUG,
6331 "Dropped unprotected Robust Action frame from "
6332 "an MFP STA");
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006333 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006334 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006335
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08006336 if (sta) {
6337 u16 fc = le_to_host16(mgmt->frame_control);
6338 u16 seq_ctrl = le_to_host16(mgmt->seq_ctrl);
6339
6340 if ((fc & WLAN_FC_RETRY) &&
6341 sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
6342 sta->last_seq_ctrl == seq_ctrl &&
6343 sta->last_subtype == WLAN_FC_STYPE_ACTION) {
6344 hostapd_logger(hapd, sta->addr,
6345 HOSTAPD_MODULE_IEEE80211,
6346 HOSTAPD_LEVEL_DEBUG,
6347 "Drop repeated action frame seq_ctrl=0x%x",
6348 seq_ctrl);
6349 return 1;
6350 }
6351
6352 sta->last_seq_ctrl = seq_ctrl;
6353 sta->last_subtype = WLAN_FC_STYPE_ACTION;
6354 }
6355
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006356 switch (mgmt->u.action.category) {
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08006357#ifdef CONFIG_IEEE80211R_AP
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006358 case WLAN_ACTION_FT:
Dmitry Shmidt7832adb2014-04-29 10:53:02 -07006359 if (!sta ||
6360 wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006361 len - IEEE80211_HDRLEN))
6362 break;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006363 return 1;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08006364#endif /* CONFIG_IEEE80211R_AP */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006365 case WLAN_ACTION_WMM:
6366 hostapd_wmm_action(hapd, mgmt, len);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006367 return 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006368 case WLAN_ACTION_SA_QUERY:
Hai Shalom021b0b52019-04-10 11:17:58 -07006369 ieee802_11_sa_query_action(hapd, mgmt, len);
6370 return 1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006371#ifdef CONFIG_WNM_AP
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08006372 case WLAN_ACTION_WNM:
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006373 ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
6374 return 1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006375#endif /* CONFIG_WNM_AP */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08006376#ifdef CONFIG_FST
6377 case WLAN_ACTION_FST:
6378 if (hapd->iface->fst)
6379 fst_rx_action(hapd->iface->fst, mgmt, len);
6380 else
6381 wpa_printf(MSG_DEBUG,
6382 "FST: Ignore FST Action frame - no FST attached");
6383 return 1;
6384#endif /* CONFIG_FST */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006385 case WLAN_ACTION_PUBLIC:
Dmitry Shmidt18463232014-01-24 12:29:41 -08006386 case WLAN_ACTION_PROTECTED_DUAL:
Dmitry Shmidtcc00d5d2015-05-04 10:34:12 -07006387 if (len >= IEEE80211_HDRLEN + 2 &&
6388 mgmt->u.action.u.public_action.action ==
Dmitry Shmidt7832adb2014-04-29 10:53:02 -07006389 WLAN_PA_20_40_BSS_COEX) {
Dmitry Shmidt7832adb2014-04-29 10:53:02 -07006390 hostapd_2040_coex_action(hapd, mgmt, len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006391 return 1;
Dmitry Shmidt7832adb2014-04-29 10:53:02 -07006392 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006393#ifdef CONFIG_DPP
6394 if (len >= IEEE80211_HDRLEN + 6 &&
6395 mgmt->u.action.u.vs_public_action.action ==
6396 WLAN_PA_VENDOR_SPECIFIC &&
6397 WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
6398 OUI_WFA &&
6399 mgmt->u.action.u.vs_public_action.variable[0] ==
6400 DPP_OUI_TYPE) {
6401 const u8 *pos, *end;
6402
6403 pos = mgmt->u.action.u.vs_public_action.oui;
6404 end = ((const u8 *) mgmt) + len;
6405 hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
Roshan Pius3a1667e2018-07-03 15:17:14 -07006406 freq);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006407 return 1;
6408 }
6409 if (len >= IEEE80211_HDRLEN + 2 &&
6410 (mgmt->u.action.u.public_action.action ==
6411 WLAN_PA_GAS_INITIAL_RESP ||
6412 mgmt->u.action.u.public_action.action ==
6413 WLAN_PA_GAS_COMEBACK_RESP)) {
6414 const u8 *pos, *end;
6415
6416 pos = &mgmt->u.action.u.public_action.action;
6417 end = ((const u8 *) mgmt) + len;
Sunil Ravi036cec52023-03-29 11:35:17 -07006418 if (gas_query_ap_rx(hapd->gas, mgmt->sa,
6419 mgmt->u.action.category,
6420 pos, end - pos, freq) == 0)
6421 return 1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006422 }
6423#endif /* CONFIG_DPP */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006424#ifdef CONFIG_NAN_USD
6425 if (mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
6426 len >= IEEE80211_HDRLEN + 5 &&
6427 mgmt->u.action.u.vs_public_action.action ==
6428 WLAN_PA_VENDOR_SPECIFIC &&
6429 WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
6430 OUI_WFA &&
6431 mgmt->u.action.u.vs_public_action.variable[0] ==
6432 NAN_OUI_TYPE) {
6433 const u8 *pos, *end;
6434
6435 pos = mgmt->u.action.u.vs_public_action.variable;
6436 end = ((const u8 *) mgmt) + len;
6437 pos++;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00006438 hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, mgmt->bssid,
6439 freq, pos, end - pos);
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006440 return 1;
6441 }
6442#endif /* CONFIG_NAN_USD */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006443 if (hapd->public_action_cb) {
6444 hapd->public_action_cb(hapd->public_action_cb_ctx,
Hai Shaloma20dcd72022-02-04 13:43:00 -08006445 (u8 *) mgmt, len, freq);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006446 }
Dmitry Shmidt4b9d52f2013-02-05 17:44:43 -08006447 if (hapd->public_action_cb2) {
Dmitry Shmidtf8623282013-02-20 14:34:59 -08006448 hapd->public_action_cb2(hapd->public_action_cb2_ctx,
Hai Shaloma20dcd72022-02-04 13:43:00 -08006449 (u8 *) mgmt, len, freq);
Dmitry Shmidt4b9d52f2013-02-05 17:44:43 -08006450 }
6451 if (hapd->public_action_cb || hapd->public_action_cb2)
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006452 return 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006453 break;
6454 case WLAN_ACTION_VENDOR_SPECIFIC:
6455 if (hapd->vendor_action_cb) {
6456 if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx,
Hai Shaloma20dcd72022-02-04 13:43:00 -08006457 (u8 *) mgmt, len, freq) == 0)
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006458 return 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006459 }
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00006460 if (sta &&
6461 hostapd_action_vs(hapd, sta, mgmt, len, freq, false) == 0)
6462 return 1;
6463 break;
6464 case WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED:
6465 if (sta &&
6466 hostapd_action_vs(hapd, sta, mgmt, len, freq, true) == 0)
6467 return 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006468 break;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006469#ifndef CONFIG_NO_RRM
Dmitry Shmidt849734c2016-05-27 09:59:01 -07006470 case WLAN_ACTION_RADIO_MEASUREMENT:
6471 hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
6472 return 1;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006473#endif /* CONFIG_NO_RRM */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006474 }
6475
6476 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
6477 HOSTAPD_LEVEL_DEBUG,
6478 "handle_action - unknown action category %d or invalid "
6479 "frame",
6480 mgmt->u.action.category);
Dmitry Shmidtd13095b2016-08-22 14:02:19 -07006481 if (!is_multicast_ether_addr(mgmt->da) &&
6482 !(mgmt->u.action.category & 0x80) &&
6483 !is_multicast_ether_addr(mgmt->sa)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006484 struct ieee80211_mgmt *resp;
6485
6486 /*
6487 * IEEE 802.11-REVma/D9.0 - 7.3.1.11
6488 * Return the Action frame to the source without change
6489 * except that MSB of the Category set to 1.
6490 */
6491 wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action "
6492 "frame back to sender");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006493 resp = os_memdup(mgmt, len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006494 if (resp == NULL)
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006495 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006496 os_memcpy(resp->da, resp->sa, ETH_ALEN);
6497 os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
6498 os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
6499 resp->u.action.category |= 0x80;
6500
Hai Shalomfdcde762020-04-02 11:19:20 -07006501 if (hostapd_drv_send_mlme(hapd, resp, len, 0, NULL, 0, 0) < 0) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07006502 wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send "
6503 "Action frame");
6504 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006505 os_free(resp);
6506 }
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006507
6508 return 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006509}
6510
6511
6512/**
Hai Shalom60840252021-02-19 19:02:11 -08006513 * notify_mgmt_frame - Notify of Management frames on the control interface
6514 * @hapd: hostapd BSS data structure (the BSS to which the Management frame was
6515 * sent to)
6516 * @buf: Management frame data (starting from the IEEE 802.11 header)
6517 * @len: Length of frame data in octets
6518 *
6519 * Notify the control interface of any received Management frame.
6520 */
6521static void notify_mgmt_frame(struct hostapd_data *hapd, const u8 *buf,
6522 size_t len)
6523{
6524
6525 int hex_len = len * 2 + 1;
6526 char *hex = os_malloc(hex_len);
6527
6528 if (hex) {
6529 wpa_snprintf_hex(hex, hex_len, buf, len);
6530 wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO,
6531 AP_MGMT_FRAME_RECEIVED "buf=%s", hex);
6532 os_free(hex);
6533 }
6534}
6535
6536
6537/**
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006538 * ieee802_11_mgmt - process incoming IEEE 802.11 management frames
6539 * @hapd: hostapd BSS data structure (the BSS to which the management frame was
6540 * sent to)
6541 * @buf: management frame data (starting from IEEE 802.11 header)
6542 * @len: length of frame data in octets
6543 * @fi: meta data about received frame (signal level, etc.)
6544 *
6545 * Process all incoming IEEE 802.11 management frames. This will be called for
6546 * each frame received from the kernel driver through wlan#ap interface. In
6547 * addition, it can be called to re-inserted pending frames (e.g., when using
6548 * external RADIUS server as an MAC ACL).
6549 */
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006550int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
6551 struct hostapd_frame_info *fi)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006552{
6553 struct ieee80211_mgmt *mgmt;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006554 u16 fc, stype;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006555 int ret = 0;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006556 unsigned int freq;
6557 int ssi_signal = fi ? fi->ssi_signal : 0;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006558#ifdef CONFIG_NAN_USD
6559 static const u8 nan_network_id[ETH_ALEN] =
6560 { 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 };
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00006561 static const u8 p2p_network_id[ETH_ALEN] =
6562 { 0x51, 0x6f, 0x9a, 0x02, 0x00, 0x00 };
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006563#endif /* CONFIG_NAN_USD */
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006564
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006565 if (len < 24)
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006566 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006567
Roshan Pius3a1667e2018-07-03 15:17:14 -07006568 if (fi && fi->freq)
6569 freq = fi->freq;
6570 else
6571 freq = hapd->iface->freq;
6572
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006573 mgmt = (struct ieee80211_mgmt *) buf;
6574 fc = le_to_host16(mgmt->frame_control);
6575 stype = WLAN_FC_GET_STYPE(fc);
6576
Hai Shalomc3565922019-10-28 11:58:20 -07006577 if (is_multicast_ether_addr(mgmt->sa) ||
6578 is_zero_ether_addr(mgmt->sa) ||
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006579 ether_addr_equal(mgmt->sa, hapd->own_addr)) {
Hai Shalomc3565922019-10-28 11:58:20 -07006580 /* Do not process any frames with unexpected/invalid SA so that
6581 * we do not add any state for unexpected STA addresses or end
6582 * up sending out frames to unexpected destination. */
6583 wpa_printf(MSG_DEBUG, "MGMT: Invalid SA=" MACSTR
6584 " in received frame - ignore this frame silently",
6585 MAC2STR(mgmt->sa));
6586 return 0;
6587 }
6588
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006589 if (stype == WLAN_FC_STYPE_BEACON) {
6590 handle_beacon(hapd, mgmt, len, fi);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006591 return 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006592 }
6593
Dmitry Shmidt7f2c7532016-08-15 09:48:12 -07006594 if (!is_broadcast_ether_addr(mgmt->bssid) &&
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00006595#ifdef CONFIG_NAN_USD
6596 !nan_de_is_nan_network_id(mgmt->bssid) &&
6597 !nan_de_is_p2p_network_id(mgmt->bssid) &&
6598#endif /* CONFIG_NAN_USD */
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08006599#ifdef CONFIG_P2P
6600 /* Invitation responses can be sent with the peer MAC as BSSID */
6601 !((hapd->conf->p2p & P2P_GROUP_OWNER) &&
6602 stype == WLAN_FC_STYPE_ACTION) &&
6603#endif /* CONFIG_P2P */
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08006604#ifdef CONFIG_MESH
6605 !(hapd->conf->mesh & MESH_ENABLED) &&
6606#endif /* CONFIG_MESH */
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006607#ifdef CONFIG_IEEE80211BE
6608 !(hapd->conf->mld_ap &&
Sunil Ravi99c035e2024-07-12 01:42:03 +00006609 ether_addr_equal(hapd->mld->mld_addr, mgmt->bssid)) &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006610#endif /* CONFIG_IEEE80211BE */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006611 !ether_addr_equal(mgmt->bssid, hapd->own_addr)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08006612 wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address",
6613 MAC2STR(mgmt->bssid));
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006614 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006615 }
6616
Hai Shalom4fbc08f2020-05-18 12:37:00 -07006617 if (hapd->iface->state != HAPD_IFACE_ENABLED) {
6618 wpa_printf(MSG_DEBUG, "MGMT: Ignore management frame while interface is not enabled (SA=" MACSTR " DA=" MACSTR " subtype=%u)",
6619 MAC2STR(mgmt->sa), MAC2STR(mgmt->da), stype);
6620 return 1;
6621 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006622
6623 if (stype == WLAN_FC_STYPE_PROBE_REQ) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07006624 handle_probe_req(hapd, mgmt, len, ssi_signal);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006625 return 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006626 }
6627
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006628 if ((!is_broadcast_ether_addr(mgmt->da) ||
6629 stype != WLAN_FC_STYPE_ACTION) &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006630#ifdef CONFIG_IEEE80211BE
6631 !(hapd->conf->mld_ap &&
Sunil Ravi99c035e2024-07-12 01:42:03 +00006632 ether_addr_equal(hapd->mld->mld_addr, mgmt->bssid)) &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006633#endif /* CONFIG_IEEE80211BE */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006634#ifdef CONFIG_NAN_USD
6635 !ether_addr_equal(mgmt->da, nan_network_id) &&
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00006636 !ether_addr_equal(mgmt->da, p2p_network_id) &&
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006637#endif /* CONFIG_NAN_USD */
6638 !ether_addr_equal(mgmt->da, hapd->own_addr)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006639 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
6640 HOSTAPD_LEVEL_DEBUG,
6641 "MGMT: DA=" MACSTR " not our address",
6642 MAC2STR(mgmt->da));
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006643 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006644 }
6645
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08006646 if (hapd->iconf->track_sta_max_num)
Roshan Pius3a1667e2018-07-03 15:17:14 -07006647 sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08006648
Hai Shalom60840252021-02-19 19:02:11 -08006649 if (hapd->conf->notify_mgmt_frames)
6650 notify_mgmt_frame(hapd, buf, len);
6651
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006652 switch (stype) {
6653 case WLAN_FC_STYPE_AUTH:
6654 wpa_printf(MSG_DEBUG, "mgmt::auth");
Hai Shalom021b0b52019-04-10 11:17:58 -07006655 handle_auth(hapd, mgmt, len, ssi_signal, 0);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006656 ret = 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006657 break;
6658 case WLAN_FC_STYPE_ASSOC_REQ:
6659 wpa_printf(MSG_DEBUG, "mgmt::assoc_req");
Hai Shalom74f70d42019-02-11 14:42:39 -08006660 handle_assoc(hapd, mgmt, len, 0, ssi_signal);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006661 ret = 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006662 break;
6663 case WLAN_FC_STYPE_REASSOC_REQ:
6664 wpa_printf(MSG_DEBUG, "mgmt::reassoc_req");
Hai Shalom74f70d42019-02-11 14:42:39 -08006665 handle_assoc(hapd, mgmt, len, 1, ssi_signal);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006666 ret = 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006667 break;
6668 case WLAN_FC_STYPE_DISASSOC:
6669 wpa_printf(MSG_DEBUG, "mgmt::disassoc");
6670 handle_disassoc(hapd, mgmt, len);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006671 ret = 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006672 break;
6673 case WLAN_FC_STYPE_DEAUTH:
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08006674 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006675 handle_deauth(hapd, mgmt, len);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006676 ret = 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006677 break;
6678 case WLAN_FC_STYPE_ACTION:
6679 wpa_printf(MSG_DEBUG, "mgmt::action");
Roshan Pius3a1667e2018-07-03 15:17:14 -07006680 ret = handle_action(hapd, mgmt, len, freq);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006681 break;
6682 default:
6683 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
6684 HOSTAPD_LEVEL_DEBUG,
6685 "unknown mgmt frame subtype %d", stype);
6686 break;
6687 }
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006688
6689 return ret;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006690}
6691
6692
6693static void handle_auth_cb(struct hostapd_data *hapd,
6694 const struct ieee80211_mgmt *mgmt,
6695 size_t len, int ok)
6696{
6697 u16 auth_alg, auth_transaction, status_code;
6698 struct sta_info *sta;
Hai Shalom60840252021-02-19 19:02:11 -08006699 bool success_status;
Hai Shalome5e28bb2019-01-28 14:51:04 -08006700
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006701 sta = ap_get_sta(hapd, mgmt->da);
6702 if (!sta) {
Hai Shalom39ba6fc2019-01-22 12:40:38 -08006703 wpa_printf(MSG_DEBUG, "handle_auth_cb: STA " MACSTR
6704 " not found",
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006705 MAC2STR(mgmt->da));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006706 return;
6707 }
6708
Hai Shalom60840252021-02-19 19:02:11 -08006709 if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
6710 wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)",
6711 (unsigned long) len);
6712 auth_alg = 0;
6713 auth_transaction = 0;
6714 status_code = WLAN_STATUS_UNSPECIFIED_FAILURE;
6715 goto fail;
6716 }
6717
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006718 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
6719 auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
6720 status_code = le_to_host16(mgmt->u.auth.status_code);
6721
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006722 if (!ok) {
6723 hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
6724 HOSTAPD_LEVEL_NOTICE,
6725 "did not acknowledge authentication response");
6726 goto fail;
6727 }
6728
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006729 if (status_code == WLAN_STATUS_SUCCESS &&
6730 ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
6731 (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
6732 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
6733 HOSTAPD_LEVEL_INFO, "authenticated");
6734 sta->flags |= WLAN_STA_AUTH;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006735 if (sta->added_unassoc)
6736 hostapd_set_sta_flags(hapd, sta);
6737 return;
6738 }
6739
6740fail:
Hai Shalom60840252021-02-19 19:02:11 -08006741 success_status = status_code == WLAN_STATUS_SUCCESS;
6742#ifdef CONFIG_SAE
6743 if (auth_alg == WLAN_AUTH_SAE && auth_transaction == 1)
6744 success_status = sae_status_success(hapd, status_code);
6745#endif /* CONFIG_SAE */
6746 if (!success_status && sta->added_unassoc) {
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006747 hostapd_drv_sta_remove(hapd, sta->addr);
6748 sta->added_unassoc = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006749 }
6750}
6751
6752
Dmitry Shmidtc2ebb4b2013-07-24 12:57:51 -07006753static void hostapd_set_wds_encryption(struct hostapd_data *hapd,
6754 struct sta_info *sta,
6755 char *ifname_wds)
6756{
Hai Shalomfdcde762020-04-02 11:19:20 -07006757#ifdef CONFIG_WEP
Dmitry Shmidtc2ebb4b2013-07-24 12:57:51 -07006758 int i;
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -07006759 struct hostapd_ssid *ssid = &hapd->conf->ssid;
Dmitry Shmidtc2ebb4b2013-07-24 12:57:51 -07006760
6761 if (hapd->conf->ieee802_1x || hapd->conf->wpa)
6762 return;
6763
6764 for (i = 0; i < 4; i++) {
6765 if (ssid->wep.key[i] &&
6766 hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i,
Hai Shalomfdcde762020-04-02 11:19:20 -07006767 0, i == ssid->wep.idx, NULL, 0,
6768 ssid->wep.key[i], ssid->wep.len[i],
6769 i == ssid->wep.idx ?
6770 KEY_FLAG_GROUP_RX_TX_DEFAULT :
6771 KEY_FLAG_GROUP_RX_TX)) {
Dmitry Shmidtc2ebb4b2013-07-24 12:57:51 -07006772 wpa_printf(MSG_WARNING,
6773 "Could not set WEP keys for WDS interface; %s",
6774 ifname_wds);
6775 break;
6776 }
6777 }
Hai Shalomfdcde762020-04-02 11:19:20 -07006778#endif /* CONFIG_WEP */
Dmitry Shmidtc2ebb4b2013-07-24 12:57:51 -07006779}
6780
6781
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006782#ifdef CONFIG_IEEE80211BE
6783static void ieee80211_ml_link_sta_assoc_cb(struct hostapd_data *hapd,
6784 struct sta_info *sta,
6785 struct mld_link_info *link,
6786 bool ok)
6787{
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006788 bool updated = false;
6789
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006790 if (!ok) {
6791 hostapd_logger(hapd, link->peer_addr, HOSTAPD_MODULE_IEEE80211,
6792 HOSTAPD_LEVEL_DEBUG,
6793 "did not acknowledge association response");
6794 sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
6795
6796 /* The STA is added only in case of SUCCESS */
6797 if (link->status == WLAN_STATUS_SUCCESS)
6798 hostapd_drv_sta_remove(hapd, sta->addr);
6799
6800 return;
6801 }
6802
6803 if (link->status != WLAN_STATUS_SUCCESS)
6804 return;
6805
6806 sta->flags |= WLAN_STA_ASSOC;
6807 sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
6808
6809 if (!hapd->conf->ieee802_1x && !hapd->conf->wpa)
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006810 updated = ap_sta_set_authorized_flag(hapd, sta, 1);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006811
6812 hostapd_set_sta_flags(hapd, sta);
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006813 if (updated)
6814 ap_sta_set_authorized_event(hapd, sta, 1);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006815
6816 /*
6817 * TODOs:
6818 * - IEEE 802.1X port enablement is not needed as done on the station
6819 * doing the connection.
6820 * - Not handling accounting
6821 * - Need to handle VLAN configuration
6822 */
6823}
6824#endif /* CONFIG_IEEE80211BE */
6825
6826
6827static void hostapd_ml_handle_assoc_cb(struct hostapd_data *hapd,
6828 struct sta_info *sta, bool ok)
6829{
6830#ifdef CONFIG_IEEE80211BE
Sunil Ravi7f769292024-07-23 22:21:32 +00006831 struct hostapd_data *tmp_hapd;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006832
6833 if (!hostapd_is_mld_ap(hapd))
6834 return;
6835
Sunil Ravi7f769292024-07-23 22:21:32 +00006836 for_each_mld_link(tmp_hapd, hapd) {
6837 struct mld_link_info *link;
6838 struct sta_info *tmp_sta;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006839
Sunil Ravi7f769292024-07-23 22:21:32 +00006840 if (tmp_hapd == hapd)
6841 continue;
6842
6843 link = &sta->mld_info.links[tmp_hapd->mld_link_id];
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006844 if (!link->valid)
6845 continue;
6846
Sunil Ravi7f769292024-07-23 22:21:32 +00006847 for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
6848 tmp_sta = tmp_sta->next) {
6849 if (tmp_sta == sta ||
6850 tmp_sta->mld_assoc_link_id !=
6851 sta->mld_assoc_link_id ||
6852 tmp_sta->aid != sta->aid)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006853 continue;
6854
Sunil Ravi7f769292024-07-23 22:21:32 +00006855 ieee80211_ml_link_sta_assoc_cb(tmp_hapd, tmp_sta, link,
6856 ok);
6857 break;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006858 }
6859 }
6860#endif /* CONFIG_IEEE80211BE */
6861}
6862
6863
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006864static void handle_assoc_cb(struct hostapd_data *hapd,
6865 const struct ieee80211_mgmt *mgmt,
6866 size_t len, int reassoc, int ok)
6867{
6868 u16 status;
6869 struct sta_info *sta;
6870 int new_assoc = 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006871
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006872 sta = ap_get_sta(hapd, mgmt->da);
6873 if (!sta) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08006874 wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found",
6875 MAC2STR(mgmt->da));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006876 return;
6877 }
6878
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006879#ifdef CONFIG_IEEE80211BE
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006880 if (ap_sta_is_mld(hapd, sta) &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006881 hapd->mld_link_id != sta->mld_assoc_link_id) {
6882 /* See ieee80211_ml_link_sta_assoc_cb() for the MLD case */
6883 wpa_printf(MSG_DEBUG,
6884 "%s: MLD: ignore on link station (%d != %d)",
6885 __func__, hapd->mld_link_id, sta->mld_assoc_link_id);
6886 return;
6887 }
6888#endif /* CONFIG_IEEE80211BE */
6889
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006890 if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
6891 sizeof(mgmt->u.assoc_resp))) {
6892 wpa_printf(MSG_INFO,
6893 "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)",
6894 reassoc, (unsigned long) len);
6895 hostapd_drv_sta_remove(hapd, sta->addr);
Dmitry Shmidtaa532512012-09-24 10:35:31 -07006896 return;
6897 }
6898
6899 if (reassoc)
6900 status = le_to_host16(mgmt->u.reassoc_resp.status_code);
6901 else
6902 status = le_to_host16(mgmt->u.assoc_resp.status_code);
6903
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006904 if (!ok) {
6905 hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
6906 HOSTAPD_LEVEL_DEBUG,
6907 "did not acknowledge association response");
6908 sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
6909 /* The STA is added only in case of SUCCESS */
6910 if (status == WLAN_STATUS_SUCCESS)
6911 hostapd_drv_sta_remove(hapd, sta->addr);
6912
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006913 goto handle_ml;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006914 }
6915
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006916 if (status != WLAN_STATUS_SUCCESS)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006917 goto handle_ml;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006918
6919 /* Stop previous accounting session, if one is started, and allocate
6920 * new session id for the new session. */
6921 accounting_sta_stop(hapd, sta);
6922
6923 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
6924 HOSTAPD_LEVEL_INFO,
6925 "associated (aid %d)",
6926 sta->aid);
6927
6928 if (sta->flags & WLAN_STA_ASSOC)
6929 new_assoc = 0;
6930 sta->flags |= WLAN_STA_ASSOC;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006931 sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
Sunil Ravi876a49b2025-02-03 19:18:32 +00006932 if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08006933 sta->auth_alg == WLAN_AUTH_FILS_SK ||
6934 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
6935 sta->auth_alg == WLAN_AUTH_FILS_PK ||
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006936 sta->auth_alg == WLAN_AUTH_FT) {
6937 /*
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08006938 * Open, static WEP, FT protocol, or FILS; no separate
6939 * authorization step.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006940 */
6941 ap_sta_set_authorized(hapd, sta, 1);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006942 }
6943
6944 if (reassoc)
6945 mlme_reassociate_indication(hapd, sta);
6946 else
6947 mlme_associate_indication(hapd, sta);
6948
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006949 sta->sa_query_timed_out = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006950
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006951 if (sta->eapol_sm == NULL) {
6952 /*
6953 * This STA does not use RADIUS server for EAP authentication,
6954 * so bind it to the selected VLAN interface now, since the
6955 * interface selection is not going to change anymore.
6956 */
Dmitry Shmidt83474442015-04-15 13:47:09 -07006957 if (ap_sta_bind_vlan(hapd, sta) < 0)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006958 goto handle_ml;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006959 } else if (sta->vlan_id) {
6960 /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */
Dmitry Shmidt83474442015-04-15 13:47:09 -07006961 if (ap_sta_bind_vlan(hapd, sta) < 0)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006962 goto handle_ml;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006963 }
6964
6965 hostapd_set_sta_flags(hapd, sta);
6966
Dmitry Shmidt29333592017-01-09 12:27:11 -08006967 if (!(sta->flags & WLAN_STA_WDS) && sta->pending_wds_enable) {
6968 wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for STA "
6969 MACSTR " based on pending request",
6970 MAC2STR(sta->addr));
6971 sta->pending_wds_enable = 0;
6972 sta->flags |= WLAN_STA_WDS;
6973 }
6974
Sunil Ravi640215c2023-06-28 23:08:09 +00006975 /* WPS not supported on backhaul BSS. Disable 4addr mode on fronthaul */
6976 if ((sta->flags & WLAN_STA_WDS) ||
6977 (sta->flags & WLAN_STA_MULTI_AP &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006978 (hapd->conf->multi_ap & BACKHAUL_BSS) &&
Sunil Ravi7f769292024-07-23 22:21:32 +00006979 hapd->conf->wds_sta &&
Sunil Ravi640215c2023-06-28 23:08:09 +00006980 !(sta->flags & WLAN_STA_WPS))) {
Dmitry Shmidtabb90a32016-12-05 15:34:39 -08006981 int ret;
6982 char ifname_wds[IFNAMSIZ + 1];
6983
6984 wpa_printf(MSG_DEBUG, "Reenable 4-address WDS mode for STA "
6985 MACSTR " (aid %u)",
6986 MAC2STR(sta->addr), sta->aid);
6987 ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
6988 sta->aid, 1);
6989 if (!ret)
6990 hostapd_set_wds_encryption(hapd, sta, ifname_wds);
6991 }
6992
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006993 if (sta->auth_alg == WLAN_AUTH_FT)
6994 wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
6995 else
6996 wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
6997 hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006998 ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
Dmitry Shmidt31a29cc2016-03-09 15:58:17 -08006999
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08007000#ifdef CONFIG_FILS
7001 if ((sta->auth_alg == WLAN_AUTH_FILS_SK ||
7002 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
7003 sta->auth_alg == WLAN_AUTH_FILS_PK) &&
7004 fils_set_tk(sta->wpa_sm) < 0) {
7005 wpa_printf(MSG_DEBUG, "FILS: TK configuration failed");
7006 ap_sta_disconnect(hapd, sta, sta->addr,
7007 WLAN_REASON_UNSPECIFIED);
7008 return;
7009 }
7010#endif /* CONFIG_FILS */
7011
Dmitry Shmidt31a29cc2016-03-09 15:58:17 -08007012 if (sta->pending_eapol_rx) {
7013 struct os_reltime now, age;
7014
7015 os_get_reltime(&now);
7016 os_reltime_sub(&now, &sta->pending_eapol_rx->rx_time, &age);
7017 if (age.sec == 0 && age.usec < 200000) {
7018 wpa_printf(MSG_DEBUG,
7019 "Process pending EAPOL frame that was received from " MACSTR " just before association notification",
7020 MAC2STR(sta->addr));
7021 ieee802_1x_receive(
7022 hapd, mgmt->da,
7023 wpabuf_head(sta->pending_eapol_rx->buf),
Sunil8cd6f4d2022-06-28 18:40:46 +00007024 wpabuf_len(sta->pending_eapol_rx->buf),
7025 sta->pending_eapol_rx->encrypted);
Dmitry Shmidt31a29cc2016-03-09 15:58:17 -08007026 }
7027 wpabuf_free(sta->pending_eapol_rx->buf);
7028 os_free(sta->pending_eapol_rx);
7029 sta->pending_eapol_rx = NULL;
7030 }
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007031
7032handle_ml:
7033 hostapd_ml_handle_assoc_cb(hapd, sta, ok);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007034}
7035
7036
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08007037static void handle_deauth_cb(struct hostapd_data *hapd,
7038 const struct ieee80211_mgmt *mgmt,
7039 size_t len, int ok)
7040{
7041 struct sta_info *sta;
Dmitry Shmidtd13095b2016-08-22 14:02:19 -07007042 if (is_multicast_ether_addr(mgmt->da))
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08007043 return;
7044 sta = ap_get_sta(hapd, mgmt->da);
7045 if (!sta) {
7046 wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR
7047 " not found", MAC2STR(mgmt->da));
7048 return;
7049 }
7050 if (ok)
7051 wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth",
7052 MAC2STR(sta->addr));
7053 else
7054 wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
7055 "deauth", MAC2STR(sta->addr));
7056
7057 ap_sta_deauth_cb(hapd, sta);
7058}
7059
7060
7061static void handle_disassoc_cb(struct hostapd_data *hapd,
7062 const struct ieee80211_mgmt *mgmt,
7063 size_t len, int ok)
7064{
7065 struct sta_info *sta;
Dmitry Shmidtd13095b2016-08-22 14:02:19 -07007066 if (is_multicast_ether_addr(mgmt->da))
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08007067 return;
7068 sta = ap_get_sta(hapd, mgmt->da);
7069 if (!sta) {
7070 wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR
7071 " not found", MAC2STR(mgmt->da));
7072 return;
7073 }
7074 if (ok)
7075 wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc",
7076 MAC2STR(sta->addr));
7077 else
7078 wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
7079 "disassoc", MAC2STR(sta->addr));
7080
7081 ap_sta_disassoc_cb(hapd, sta);
7082}
7083
7084
Dmitry Shmidt29333592017-01-09 12:27:11 -08007085static void handle_action_cb(struct hostapd_data *hapd,
7086 const struct ieee80211_mgmt *mgmt,
7087 size_t len, int ok)
7088{
7089 struct sta_info *sta;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007090#ifndef CONFIG_NO_RRM
Paul Stewart092955c2017-02-06 09:13:09 -08007091 const struct rrm_measurement_report_element *report;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007092#endif /* CONFIG_NO_RRM */
Dmitry Shmidt29333592017-01-09 12:27:11 -08007093
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007094#ifdef CONFIG_DPP
7095 if (len >= IEEE80211_HDRLEN + 6 &&
7096 mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
7097 mgmt->u.action.u.vs_public_action.action ==
7098 WLAN_PA_VENDOR_SPECIFIC &&
7099 WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
7100 OUI_WFA &&
7101 mgmt->u.action.u.vs_public_action.variable[0] ==
7102 DPP_OUI_TYPE) {
7103 const u8 *pos, *end;
7104
7105 pos = &mgmt->u.action.u.vs_public_action.variable[1];
7106 end = ((const u8 *) mgmt) + len;
7107 hostapd_dpp_tx_status(hapd, mgmt->da, pos, end - pos, ok);
7108 return;
7109 }
7110 if (len >= IEEE80211_HDRLEN + 2 &&
7111 mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
7112 (mgmt->u.action.u.public_action.action ==
7113 WLAN_PA_GAS_INITIAL_REQ ||
7114 mgmt->u.action.u.public_action.action ==
7115 WLAN_PA_GAS_COMEBACK_REQ)) {
7116 const u8 *pos, *end;
7117
7118 pos = mgmt->u.action.u.public_action.variable;
7119 end = ((const u8 *) mgmt) + len;
7120 gas_query_ap_tx_status(hapd->gas, mgmt->da, pos, end - pos, ok);
7121 return;
7122 }
7123#endif /* CONFIG_DPP */
Hai Shaloma20dcd72022-02-04 13:43:00 -08007124 if (is_multicast_ether_addr(mgmt->da))
7125 return;
Dmitry Shmidt29333592017-01-09 12:27:11 -08007126 sta = ap_get_sta(hapd, mgmt->da);
7127 if (!sta) {
7128 wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR
7129 " not found", MAC2STR(mgmt->da));
7130 return;
7131 }
7132
Sunil Ravi77d572f2023-01-17 23:58:31 +00007133#ifdef CONFIG_HS20
7134 if (ok && len >= IEEE80211_HDRLEN + 2 &&
7135 mgmt->u.action.category == WLAN_ACTION_WNM &&
7136 mgmt->u.action.u.vs_public_action.action == WNM_NOTIFICATION_REQ &&
7137 sta->hs20_deauth_on_ack) {
7138 wpa_printf(MSG_DEBUG, "HS 2.0: Deauthenticate STA " MACSTR
7139 " on acknowledging the WNM-Notification",
7140 MAC2STR(sta->addr));
7141 ap_sta_session_timeout(hapd, sta, 0);
7142 return;
7143 }
7144#endif /* CONFIG_HS20 */
7145
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007146#ifndef CONFIG_NO_RRM
Paul Stewart092955c2017-02-06 09:13:09 -08007147 if (len < 24 + 5 + sizeof(*report))
Dmitry Shmidt29333592017-01-09 12:27:11 -08007148 return;
Paul Stewart092955c2017-02-06 09:13:09 -08007149 report = (const struct rrm_measurement_report_element *)
7150 &mgmt->u.action.u.rrm.variable[2];
Dmitry Shmidt29333592017-01-09 12:27:11 -08007151 if (mgmt->u.action.category == WLAN_ACTION_RADIO_MEASUREMENT &&
Paul Stewart092955c2017-02-06 09:13:09 -08007152 mgmt->u.action.u.rrm.action == WLAN_RRM_RADIO_MEASUREMENT_REQUEST &&
7153 report->eid == WLAN_EID_MEASURE_REQUEST &&
7154 report->len >= 3 &&
7155 report->type == MEASURE_TYPE_BEACON)
Dmitry Shmidt29333592017-01-09 12:27:11 -08007156 hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok);
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007157#endif /* CONFIG_NO_RRM */
Dmitry Shmidt29333592017-01-09 12:27:11 -08007158}
7159
7160
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007161/**
7162 * ieee802_11_mgmt_cb - Process management frame TX status callback
7163 * @hapd: hostapd BSS data structure (the BSS from which the management frame
7164 * was sent from)
7165 * @buf: management frame data (starting from IEEE 802.11 header)
7166 * @len: length of frame data in octets
7167 * @stype: management frame subtype from frame control field
7168 * @ok: Whether the frame was ACK'ed
7169 */
7170void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
7171 u16 stype, int ok)
7172{
7173 const struct ieee80211_mgmt *mgmt;
7174 mgmt = (const struct ieee80211_mgmt *) buf;
7175
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08007176#ifdef CONFIG_TESTING_OPTIONS
7177 if (hapd->ext_mgmt_frame_handling) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007178 size_t hex_len = 2 * len + 1;
7179 char *hex = os_malloc(hex_len);
7180
7181 if (hex) {
7182 wpa_snprintf_hex(hex, hex_len, buf, len);
7183 wpa_msg(hapd->msg_ctx, MSG_INFO,
7184 "MGMT-TX-STATUS stype=%u ok=%d buf=%s",
7185 stype, ok, hex);
7186 os_free(hex);
7187 }
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08007188 return;
7189 }
7190#endif /* CONFIG_TESTING_OPTIONS */
7191
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007192 switch (stype) {
7193 case WLAN_FC_STYPE_AUTH:
7194 wpa_printf(MSG_DEBUG, "mgmt::auth cb");
7195 handle_auth_cb(hapd, mgmt, len, ok);
7196 break;
7197 case WLAN_FC_STYPE_ASSOC_RESP:
7198 wpa_printf(MSG_DEBUG, "mgmt::assoc_resp cb");
7199 handle_assoc_cb(hapd, mgmt, len, 0, ok);
7200 break;
7201 case WLAN_FC_STYPE_REASSOC_RESP:
7202 wpa_printf(MSG_DEBUG, "mgmt::reassoc_resp cb");
7203 handle_assoc_cb(hapd, mgmt, len, 1, ok);
7204 break;
7205 case WLAN_FC_STYPE_PROBE_RESP:
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08007206 wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb ok=%d", ok);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007207 break;
7208 case WLAN_FC_STYPE_DEAUTH:
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08007209 wpa_printf(MSG_DEBUG, "mgmt::deauth cb");
7210 handle_deauth_cb(hapd, mgmt, len, ok);
7211 break;
7212 case WLAN_FC_STYPE_DISASSOC:
7213 wpa_printf(MSG_DEBUG, "mgmt::disassoc cb");
7214 handle_disassoc_cb(hapd, mgmt, len, ok);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007215 break;
7216 case WLAN_FC_STYPE_ACTION:
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08007217 wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok);
Dmitry Shmidt29333592017-01-09 12:27:11 -08007218 handle_action_cb(hapd, mgmt, len, ok);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007219 break;
7220 default:
Dmitry Shmidtcce06662013-11-04 18:44:24 -08007221 wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007222 break;
7223 }
7224}
7225
7226
7227int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
7228{
7229 /* TODO */
7230 return 0;
7231}
7232
7233
7234int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
7235 char *buf, size_t buflen)
7236{
7237 /* TODO */
7238 return 0;
7239}
7240
7241
7242void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
7243 const u8 *buf, size_t len, int ack)
7244{
7245 struct sta_info *sta;
7246 struct hostapd_iface *iface = hapd->iface;
7247
7248 sta = ap_get_sta(hapd, addr);
7249 if (sta == NULL && iface->num_bss > 1) {
7250 size_t j;
7251 for (j = 0; j < iface->num_bss; j++) {
7252 hapd = iface->bss[j];
7253 sta = ap_get_sta(hapd, addr);
7254 if (sta)
7255 break;
7256 }
7257 }
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08007258 if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007259 return;
7260 if (sta->flags & WLAN_STA_PENDING_POLL) {
7261 wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending "
7262 "activity poll", MAC2STR(sta->addr),
7263 ack ? "ACKed" : "did not ACK");
7264 if (ack)
7265 sta->flags &= ~WLAN_STA_PENDING_POLL;
7266 }
7267
7268 ieee802_1x_tx_status(hapd, sta, buf, len, ack);
7269}
7270
7271
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08007272void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
7273{
7274 struct sta_info *sta;
7275 struct hostapd_iface *iface = hapd->iface;
7276
7277 sta = ap_get_sta(hapd, addr);
7278 if (sta == NULL && iface->num_bss > 1) {
7279 size_t j;
7280 for (j = 0; j < iface->num_bss; j++) {
7281 hapd = iface->bss[j];
7282 sta = ap_get_sta(hapd, addr);
7283 if (sta)
7284 break;
7285 }
7286 }
7287 if (sta == NULL)
7288 return;
Dmitry Shmidt849734c2016-05-27 09:59:01 -07007289 wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POLL_OK MACSTR,
7290 MAC2STR(sta->addr));
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08007291 if (!(sta->flags & WLAN_STA_PENDING_POLL))
7292 return;
7293
7294 wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending "
7295 "activity poll", MAC2STR(sta->addr));
7296 sta->flags &= ~WLAN_STA_PENDING_POLL;
7297}
7298
7299
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007300void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
7301 int wds)
7302{
7303 struct sta_info *sta;
7304
7305 sta = ap_get_sta(hapd, src);
Dmitry Shmidt29333592017-01-09 12:27:11 -08007306 if (sta &&
7307 ((sta->flags & WLAN_STA_ASSOC) ||
7308 ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) {
Dmitry Shmidtaa532512012-09-24 10:35:31 -07007309 if (!hapd->conf->wds_sta)
7310 return;
7311
Dmitry Shmidt29333592017-01-09 12:27:11 -08007312 if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK)) ==
7313 WLAN_STA_ASSOC_REQ_OK) {
7314 wpa_printf(MSG_DEBUG,
7315 "Postpone 4-address WDS mode enabling for STA "
7316 MACSTR " since TX status for AssocResp is not yet known",
7317 MAC2STR(sta->addr));
7318 sta->pending_wds_enable = 1;
7319 return;
7320 }
7321
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007322 if (wds && !(sta->flags & WLAN_STA_WDS)) {
Dmitry Shmidtc2ebb4b2013-07-24 12:57:51 -07007323 int ret;
7324 char ifname_wds[IFNAMSIZ + 1];
7325
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007326 wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for "
7327 "STA " MACSTR " (aid %u)",
7328 MAC2STR(sta->addr), sta->aid);
7329 sta->flags |= WLAN_STA_WDS;
Dmitry Shmidtc2ebb4b2013-07-24 12:57:51 -07007330 ret = hostapd_set_wds_sta(hapd, ifname_wds,
7331 sta->addr, sta->aid, 1);
7332 if (!ret)
7333 hostapd_set_wds_encryption(hapd, sta,
7334 ifname_wds);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007335 }
7336 return;
7337 }
7338
7339 wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
7340 MACSTR, MAC2STR(src));
Hai Shalomc3565922019-10-28 11:58:20 -07007341 if (is_multicast_ether_addr(src) || is_zero_ether_addr(src) ||
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007342 ether_addr_equal(src, hapd->own_addr)) {
Hai Shalomc3565922019-10-28 11:58:20 -07007343 /* Broadcast bit set in SA or unexpected SA?! Ignore the frame
7344 * silently. */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007345 return;
7346 }
7347
7348 if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) {
7349 wpa_printf(MSG_DEBUG, "Association Response to the STA has "
7350 "already been sent, but no TX status yet known - "
7351 "ignore Class 3 frame issue with " MACSTR,
7352 MAC2STR(src));
7353 return;
7354 }
7355
7356 if (sta && (sta->flags & WLAN_STA_AUTH))
7357 hostapd_drv_sta_disassoc(
7358 hapd, src,
7359 WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
7360 else
7361 hostapd_drv_sta_deauth(
7362 hapd, src,
7363 WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
7364}
7365
7366
Sunil Ravia04bd252022-05-02 22:54:18 -07007367static u8 * hostapd_add_tpe_info(u8 *eid, u8 tx_pwr_count,
7368 enum max_tx_pwr_interpretation tx_pwr_intrpn,
7369 u8 tx_pwr_cat, u8 tx_pwr)
7370{
7371 int i;
7372
7373 *eid++ = WLAN_EID_TRANSMIT_POWER_ENVELOPE; /* Element ID */
7374 *eid++ = 2 + tx_pwr_count; /* Length */
7375
7376 /*
7377 * Transmit Power Information field
7378 * bits 0-2 : Maximum Transmit Power Count
7379 * bits 3-5 : Maximum Transmit Power Interpretation
7380 * bits 6-7 : Maximum Transmit Power Category
7381 */
7382 *eid++ = tx_pwr_count | (tx_pwr_intrpn << 3) | (tx_pwr_cat << 6);
7383
7384 /* Maximum Transmit Power field */
7385 for (i = 0; i <= tx_pwr_count; i++)
7386 *eid++ = tx_pwr;
7387
7388 return eid;
7389}
7390
7391
7392/*
7393 * TODO: Extract power limits from channel data after 6G regulatory
7394 * support.
7395 */
7396#define REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT (-1) /* dBm/MHz */
7397#define REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT 5 /* dBm/MHz */
7398
Hai Shalom60840252021-02-19 19:02:11 -08007399u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
7400{
7401 struct hostapd_iface *iface = hapd->iface;
7402 struct hostapd_config *iconf = iface->conf;
7403 struct hostapd_hw_modes *mode = iface->current_mode;
7404 struct hostapd_channel_data *chan;
7405 int dfs, i;
7406 u8 channel, tx_pwr_count, local_pwr_constraint;
7407 int max_tx_power;
7408 u8 tx_pwr;
7409
7410 if (!mode)
7411 return eid;
7412
7413 if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES)
7414 return eid;
7415
7416 for (i = 0; i < mode->num_channels; i++) {
7417 if (mode->channels[i].freq == iface->freq)
7418 break;
7419 }
7420 if (i == mode->num_channels)
7421 return eid;
7422
Sunil Ravia04bd252022-05-02 22:54:18 -07007423#ifdef CONFIG_IEEE80211AX
7424 /* IEEE Std 802.11ax-2021, Annex E.2.7 (6 GHz band in the United
7425 * States): An AP that is an Indoor Access Point per regulatory rules
7426 * shall send at least two Transmit Power Envelope elements in Beacon
7427 * and Probe Response frames as follows:
7428 * - Maximum Transmit Power Category subfield = Default;
7429 * Unit interpretation = Regulatory client EIRP PSD
7430 * - Maximum Transmit Power Category subfield = Subordinate Device;
7431 * Unit interpretation = Regulatory client EIRP PSD
7432 */
7433 if (is_6ghz_op_class(iconf->op_class)) {
7434 enum max_tx_pwr_interpretation tx_pwr_intrpn;
7435
7436 /* Same Maximum Transmit Power for all 20 MHz bands */
7437 tx_pwr_count = 0;
7438 tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD;
7439
7440 /* Default Transmit Power Envelope for Global Operating Class */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007441 if (hapd->iconf->reg_def_cli_eirp_psd != -1)
7442 tx_pwr = hapd->iconf->reg_def_cli_eirp_psd;
7443 else
7444 tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
7445
Sunil Ravia04bd252022-05-02 22:54:18 -07007446 eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn,
7447 REG_DEFAULT_CLIENT, tx_pwr);
7448
7449 /* Indoor Access Point must include an additional TPE for
7450 * subordinate devices */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007451 if (he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type)) {
Sunil Ravia04bd252022-05-02 22:54:18 -07007452 /* TODO: Extract PSD limits from channel data */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007453 if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
7454 tx_pwr = hapd->iconf->reg_sub_cli_eirp_psd;
7455 else
7456 tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
Sunil Ravia04bd252022-05-02 22:54:18 -07007457 eid = hostapd_add_tpe_info(eid, tx_pwr_count,
7458 tx_pwr_intrpn,
7459 REG_SUBORDINATE_CLIENT,
7460 tx_pwr);
7461 }
7462
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007463 if (iconf->reg_def_cli_eirp != -1 &&
7464 he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
7465 eid = hostapd_add_tpe_info(
7466 eid, tx_pwr_count, REGULATORY_CLIENT_EIRP,
7467 REG_DEFAULT_CLIENT,
7468 hapd->iconf->reg_def_cli_eirp);
7469
Sunil Ravia04bd252022-05-02 22:54:18 -07007470 return eid;
7471 }
7472#endif /* CONFIG_IEEE80211AX */
7473
Hai Shalom60840252021-02-19 19:02:11 -08007474 switch (hostapd_get_oper_chwidth(iconf)) {
Sunil8cd6f4d2022-06-28 18:40:46 +00007475 case CONF_OPER_CHWIDTH_USE_HT:
Hai Shalom60840252021-02-19 19:02:11 -08007476 if (iconf->secondary_channel == 0) {
7477 /* Max Transmit Power count = 0 (20 MHz) */
7478 tx_pwr_count = 0;
7479 } else {
7480 /* Max Transmit Power count = 1 (20, 40 MHz) */
7481 tx_pwr_count = 1;
7482 }
7483 break;
Sunil8cd6f4d2022-06-28 18:40:46 +00007484 case CONF_OPER_CHWIDTH_80MHZ:
Hai Shalom60840252021-02-19 19:02:11 -08007485 /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */
7486 tx_pwr_count = 2;
7487 break;
Sunil8cd6f4d2022-06-28 18:40:46 +00007488 case CONF_OPER_CHWIDTH_80P80MHZ:
7489 case CONF_OPER_CHWIDTH_160MHZ:
Hai Shalom60840252021-02-19 19:02:11 -08007490 /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */
7491 tx_pwr_count = 3;
7492 break;
7493 default:
7494 return eid;
7495 }
7496
7497 /*
7498 * Below local_pwr_constraint logic is referred from
7499 * hostapd_eid_pwr_constraint.
7500 *
7501 * Check if DFS is required by regulatory.
7502 */
7503 dfs = hostapd_is_dfs_required(hapd->iface);
7504 if (dfs < 0)
7505 dfs = 0;
7506
7507 /*
7508 * In order to meet regulations when TPC is not implemented using
7509 * a transmit power that is below the legal maximum (including any
7510 * mitigation factor) should help. In this case, indicate 3 dB below
7511 * maximum allowed transmit power.
7512 */
7513 if (hapd->iconf->local_pwr_constraint == -1)
7514 local_pwr_constraint = (dfs == 0) ? 0 : 3;
7515 else
7516 local_pwr_constraint = hapd->iconf->local_pwr_constraint;
7517
7518 /*
7519 * A STA that is not an AP shall use a transmit power less than or
7520 * equal to the local maximum transmit power level for the channel.
7521 * The local maximum transmit power can be calculated from the formula:
7522 * local max TX pwr = max TX pwr - local pwr constraint
7523 * Where max TX pwr is maximum transmit power level specified for
7524 * channel in Country element and local pwr constraint is specified
7525 * for channel in this Power Constraint element.
7526 */
7527 chan = &mode->channels[i];
7528 max_tx_power = chan->max_tx_power - local_pwr_constraint;
7529
7530 /*
7531 * Local Maximum Transmit power is encoded as two's complement
7532 * with a 0.5 dB step.
7533 */
7534 max_tx_power *= 2; /* in 0.5 dB steps */
7535 if (max_tx_power > 127) {
7536 /* 63.5 has special meaning of 63.5 dBm or higher */
7537 max_tx_power = 127;
7538 }
7539 if (max_tx_power < -128)
7540 max_tx_power = -128;
7541 if (max_tx_power < 0)
7542 tx_pwr = 0x80 + max_tx_power + 128;
7543 else
7544 tx_pwr = max_tx_power;
7545
Sunil Ravia04bd252022-05-02 22:54:18 -07007546 return hostapd_add_tpe_info(eid, tx_pwr_count, LOCAL_EIRP,
7547 0 /* Reserved for bands other than 6 GHz */,
7548 tx_pwr);
Hai Shalom60840252021-02-19 19:02:11 -08007549}
7550
7551
Sunil Ravic0f5d412024-09-11 22:12:49 +00007552/* Wide Bandwidth Channel Switch subelement */
7553static u8 * hostapd_eid_wb_channel_switch(struct hostapd_data *hapd, u8 *eid,
7554 u8 chan1, u8 chan2)
Hai Shalom899fcc72020-10-19 14:38:18 -07007555{
Sunil Ravic0f5d412024-09-11 22:12:49 +00007556 u8 bw;
Hai Shalom899fcc72020-10-19 14:38:18 -07007557
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007558 /* bandwidth: 0: 40, 1: 80, 160, 80+80, 4 to 255 reserved as per
7559 * IEEE P802.11-REVme/D7.0, 9.4.2.159 and Table 9-316.
7560 */
Hai Shalom899fcc72020-10-19 14:38:18 -07007561 switch (hapd->cs_freq_params.bandwidth) {
Sunil Ravi640215c2023-06-28 23:08:09 +00007562 case 320:
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007563 /* As per IEEE P802.11be/D7.0, 35.15.3,
7564 * For EHT BSS operating channel width wider than 160 MHz,
7565 * the announced BSS bandwidth in the Wide Bandwidth
7566 * Channel Switch element is less than the BSS bandwidth
7567 * in the Bandwidth Indication element
7568 */
Hai Shalom899fcc72020-10-19 14:38:18 -07007569
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007570 /* Modifying the center frequency to 160 MHz */
7571 if (hapd->cs_freq_params.channel < chan1)
7572 chan1 -= 16;
7573 else
7574 chan1 += 16;
7575
7576 /* fallthrough */
7577 case 160:
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007578 /* Update the CCFS0 and CCFS1 values in the element based on
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007579 * IEEE P802.11-REVme/D7.0, Table 9-316
7580 */
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007581
7582 /* CCFS1 - The channel center frequency index of the 160 MHz
7583 * channel. */
7584 chan2 = chan1;
7585
7586 /* CCFS0 - The channel center frequency index of the 80 MHz
7587 * channel segment that contains the primary channel. */
7588 if (hapd->cs_freq_params.channel < chan1)
7589 chan1 -= 8;
7590 else
7591 chan1 += 8;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007592
7593 bw = 1;
7594 break;
7595 case 80:
7596 bw = 1;
7597 break;
7598 case 40:
7599 bw = 0;
7600 break;
7601 default:
7602 /* not valid VHT bandwidth or not in CSA */
7603 return eid;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007604 }
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007605
7606 *eid++ = WLAN_EID_WIDE_BW_CHSWITCH;
7607 *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
7608 *eid++ = bw; /* New Channel Width */
Hai Shalom899fcc72020-10-19 14:38:18 -07007609 *eid++ = chan1; /* New Channel Center Frequency Segment 0 */
7610 *eid++ = chan2; /* New Channel Center Frequency Segment 1 */
7611
7612 return eid;
7613}
7614
Hai Shaloma20dcd72022-02-04 13:43:00 -08007615
Sunil Ravic0f5d412024-09-11 22:12:49 +00007616#ifdef CONFIG_IEEE80211BE
7617/* Bandwidth Indication element that is also used as the Bandwidth Indication
7618 * For Channel Switch subelement within a Channel Switch Wrapper element. */
7619static u8 * hostapd_eid_bw_indication(struct hostapd_data *hapd, u8 *eid,
7620 u8 chan1, u8 chan2)
7621{
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007622 u16 punct_bitmap = hapd->cs_freq_params.punct_bitmap;
Sunil Ravic0f5d412024-09-11 22:12:49 +00007623 struct ieee80211_bw_ind_element *bw_ind_elem;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007624 size_t elen = 4;
Sunil Ravic0f5d412024-09-11 22:12:49 +00007625
7626 if (hapd->cs_freq_params.bandwidth <= 160 && !punct_bitmap)
7627 return eid;
7628
7629 if (punct_bitmap)
7630 elen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
7631
7632 *eid++ = WLAN_EID_EXTENSION;
7633 *eid++ = 1 + elen;
7634 *eid++ = WLAN_EID_EXT_BANDWIDTH_INDICATION;
7635
7636 bw_ind_elem = (struct ieee80211_bw_ind_element *) eid;
7637 os_memset(bw_ind_elem, 0, sizeof(struct ieee80211_bw_ind_element));
7638
7639 switch (hapd->cs_freq_params.bandwidth) {
7640 case 320:
7641 bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_320MHZ;
7642 chan2 = chan1;
7643 if (hapd->cs_freq_params.channel < chan1)
7644 chan1 -= 16;
7645 else
7646 chan1 += 16;
7647 break;
7648 case 160:
7649 bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_160MHZ;
7650 chan2 = chan1;
7651 if (hapd->cs_freq_params.channel < chan1)
7652 chan1 -= 8;
7653 else
7654 chan1 += 8;
7655 break;
7656 case 80:
7657 bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_80MHZ;
7658 break;
7659 case 40:
7660 if (hapd->cs_freq_params.sec_channel_offset == 1)
7661 bw_ind_elem->bw_ind_info.control |=
7662 BW_IND_CHANNEL_WIDTH_40MHZ;
7663 else
7664 bw_ind_elem->bw_ind_info.control |=
7665 BW_IND_CHANNEL_WIDTH_20MHZ;
7666 break;
7667 default:
7668 bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_20MHZ;
7669 break;
7670 }
7671
7672 bw_ind_elem->bw_ind_info.ccfs0 = chan1;
7673 bw_ind_elem->bw_ind_info.ccfs1 = chan2;
7674
7675 if (punct_bitmap) {
7676 bw_ind_elem->bw_ind_params |=
7677 BW_IND_PARAMETER_DISABLED_SUBCHAN_BITMAP_PRESENT;
7678 bw_ind_elem->bw_ind_info.disabled_chan_bitmap =
7679 host_to_le16(punct_bitmap);
7680 }
7681
7682 return eid + elen;
7683}
7684#endif /* CONFIG_IEEE80211BE */
7685
7686
7687u8 * hostapd_eid_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
7688{
7689 u8 chan1 = 0, chan2 = 0;
7690 u8 *eid_len_offset;
7691 int freq1;
7692
7693 if (!hapd->cs_freq_params.channel ||
7694 (!hapd->cs_freq_params.vht_enabled &&
7695 !hapd->cs_freq_params.he_enabled &&
7696 !hapd->cs_freq_params.eht_enabled))
7697 return eid;
7698
7699 freq1 = hapd->cs_freq_params.center_freq1 ?
7700 hapd->cs_freq_params.center_freq1 :
7701 hapd->cs_freq_params.freq;
7702 if (ieee80211_freq_to_chan(freq1, &chan1) !=
7703 HOSTAPD_MODE_IEEE80211A)
7704 return eid;
7705
7706 if (hapd->cs_freq_params.center_freq2 &&
7707 ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
7708 &chan2) != HOSTAPD_MODE_IEEE80211A)
7709 return eid;
7710
7711 *eid++ = WLAN_EID_CHANNEL_SWITCH_WRAPPER;
7712 eid_len_offset = eid++; /* Length of Channel Switch Wrapper element */
7713
7714 eid = hostapd_eid_wb_channel_switch(hapd, eid, chan1, chan2);
7715
7716#ifdef CONFIG_IEEE80211BE
7717 if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
7718 /* Bandwidth Indication For Channel Switch subelement */
7719 eid = hostapd_eid_bw_indication(hapd, eid, chan1, chan2);
7720 }
7721#endif /* CONFIG_IEEE80211BE */
7722
7723 *eid_len_offset = (eid - eid_len_offset) - 1;
7724 return eid;
7725}
7726
7727
Hai Shaloma20dcd72022-02-04 13:43:00 -08007728static size_t hostapd_eid_nr_db_len(struct hostapd_data *hapd,
7729 size_t *current_len)
7730{
7731 struct hostapd_neighbor_entry *nr;
7732 size_t total_len = 0, len = *current_len;
7733
7734 dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
7735 list) {
7736 if (!nr->nr || wpabuf_len(nr->nr) < 12)
7737 continue;
7738
7739 if (nr->short_ssid == hapd->conf->ssid.short_ssid)
7740 continue;
7741
7742 /* Start a new element */
7743 if (!len ||
7744 len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
7745 len = RNR_HEADER_LEN;
7746 total_len += RNR_HEADER_LEN;
7747 }
7748
7749 len += RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN;
7750 total_len += RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN;
7751 }
7752
7753 *current_len = len;
7754 return total_len;
7755}
7756
7757
Sunil Ravi640215c2023-06-28 23:08:09 +00007758struct mbssid_ie_profiles {
7759 u8 start;
7760 u8 end;
7761};
7762
Sunil Ravi7f769292024-07-23 22:21:32 +00007763static bool hostapd_skip_rnr(size_t i, struct mbssid_ie_profiles *skip_profiles,
7764 bool ap_mld, u8 tbtt_info_len, bool mld_update,
7765 struct hostapd_data *reporting_hapd,
7766 struct hostapd_data *bss)
7767{
7768 if (skip_profiles &&
7769 i >= skip_profiles->start && i < skip_profiles->end)
7770 return true;
7771
7772 /* No need to report if length is for normal TBTT and the BSS is
7773 * affiliated with an AP MLD. MLD TBTT will include this. */
7774 if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld)
7775 return true;
7776
7777 /* No need to report if length is for MLD TBTT and the BSS is not
7778 * affiliated with an aP MLD. Normal TBTT will include this. */
7779 if (tbtt_info_len == RNR_TBTT_INFO_MLD_LEN && !ap_mld)
7780 return true;
7781
7782#ifdef CONFIG_IEEE80211BE
7783 /* If building for co-location and they are ML partners, no need to
7784 * include since the ML RNR will carry this. */
7785 if (!mld_update && hostapd_is_ml_partner(reporting_hapd, bss))
7786 return true;
7787
7788 /* If building for ML RNR and they are not ML partners, don't include.
7789 */
7790 if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss))
7791 return true;
7792#endif /* CONFIG_IEEE80211BE */
7793
7794 return false;
7795}
7796
7797
Sunil Ravi640215c2023-06-28 23:08:09 +00007798static size_t
7799hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
7800 struct hostapd_data *reporting_hapd,
7801 size_t *current_len,
Sunil Ravi7f769292024-07-23 22:21:32 +00007802 struct mbssid_ie_profiles *skip_profiles,
7803 bool mld_update)
Hai Shaloma20dcd72022-02-04 13:43:00 -08007804{
7805 size_t total_len = 0, len = *current_len;
Sunil Ravi7f769292024-07-23 22:21:32 +00007806 int tbtt_count, total_tbtt_count = 0;
7807 size_t i, start;
7808 u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN :
7809 RNR_TBTT_INFO_LEN;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007810
Sunil Ravi7f769292024-07-23 22:21:32 +00007811repeat_rnr_len:
7812 start = 0;
7813 tbtt_count = 0;
Hai Shaloma20dcd72022-02-04 13:43:00 -08007814
7815 while (start < hapd->iface->num_bss) {
7816 if (!len ||
Sunil Ravi7f769292024-07-23 22:21:32 +00007817 len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007818 tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
Hai Shaloma20dcd72022-02-04 13:43:00 -08007819 len = RNR_HEADER_LEN;
7820 total_len += RNR_HEADER_LEN;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007821 tbtt_count = 0;
Hai Shaloma20dcd72022-02-04 13:43:00 -08007822 }
7823
7824 len += RNR_TBTT_HEADER_LEN;
7825 total_len += RNR_TBTT_HEADER_LEN;
7826
7827 for (i = start; i < hapd->iface->num_bss; i++) {
7828 struct hostapd_data *bss = hapd->iface->bss[i];
Sunil Ravi7f769292024-07-23 22:21:32 +00007829 bool ap_mld = false;
Hai Shaloma20dcd72022-02-04 13:43:00 -08007830
7831 if (!bss || !bss->conf || !bss->started)
7832 continue;
7833
Sunil Ravi7f769292024-07-23 22:21:32 +00007834#ifdef CONFIG_IEEE80211BE
7835 ap_mld = bss->conf->mld_ap;
7836#endif /* CONFIG_IEEE80211BE */
7837
Hai Shaloma20dcd72022-02-04 13:43:00 -08007838 if (bss == reporting_hapd ||
7839 bss->conf->ignore_broadcast_ssid)
7840 continue;
7841
Sunil Ravi7f769292024-07-23 22:21:32 +00007842 if (hostapd_skip_rnr(i, skip_profiles, ap_mld,
7843 tbtt_info_len, mld_update,
7844 reporting_hapd, bss))
Sunil Ravi640215c2023-06-28 23:08:09 +00007845 continue;
7846
Sunil Ravi7f769292024-07-23 22:21:32 +00007847 if (len + tbtt_info_len > 255 ||
Hai Shaloma20dcd72022-02-04 13:43:00 -08007848 tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
7849 break;
7850
Sunil Ravi7f769292024-07-23 22:21:32 +00007851 len += tbtt_info_len;
7852 total_len += tbtt_info_len;
Hai Shaloma20dcd72022-02-04 13:43:00 -08007853 tbtt_count++;
7854 }
7855 start = i;
7856 }
7857
Sunil Ravi7f769292024-07-23 22:21:32 +00007858 total_tbtt_count += tbtt_count;
7859
7860 /* If building for co-location, re-build again but this time include
7861 * ML TBTTs.
7862 */
7863 if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
7864 tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
7865
7866 /* If no TBTT was found, adjust the len and total_len since it
7867 * would have incremented before we checked all BSSs. */
Sunil Ravi876a49b2025-02-03 19:18:32 +00007868 if (!tbtt_count && len >= RNR_TBTT_HEADER_LEN &&
7869 total_len >= RNR_TBTT_HEADER_LEN) {
Sunil Ravi7f769292024-07-23 22:21:32 +00007870 len -= RNR_TBTT_HEADER_LEN;
7871 total_len -= RNR_TBTT_HEADER_LEN;
7872 }
7873
7874 goto repeat_rnr_len;
7875 }
7876
7877 /* This is possible when in the re-built case and no suitable TBTT was
7878 * found. Adjust the length accordingly. */
Sunil Ravi876a49b2025-02-03 19:18:32 +00007879 if (!tbtt_count && total_tbtt_count && len >= RNR_TBTT_HEADER_LEN &&
7880 total_len >= RNR_TBTT_HEADER_LEN) {
Sunil Ravi7f769292024-07-23 22:21:32 +00007881 len -= RNR_TBTT_HEADER_LEN;
7882 total_len -= RNR_TBTT_HEADER_LEN;
7883 }
7884
7885 if (!total_tbtt_count)
Hai Shaloma20dcd72022-02-04 13:43:00 -08007886 total_len = 0;
7887 else
7888 *current_len = len;
7889
7890 return total_len;
7891}
7892
7893
7894enum colocation_mode {
7895 NO_COLOCATED_6GHZ,
7896 STANDALONE_6GHZ,
7897 COLOCATED_6GHZ,
7898 COLOCATED_LOWER_BAND,
7899};
7900
7901static enum colocation_mode get_colocation_mode(struct hostapd_data *hapd)
7902{
7903 u8 i;
7904 bool is_6ghz = is_6ghz_op_class(hapd->iconf->op_class);
7905
7906 if (!hapd->iface || !hapd->iface->interfaces)
7907 return NO_COLOCATED_6GHZ;
7908
7909 if (is_6ghz && hapd->iface->interfaces->count == 1)
7910 return STANDALONE_6GHZ;
7911
7912 for (i = 0; i < hapd->iface->interfaces->count; i++) {
7913 struct hostapd_iface *iface;
7914 bool is_colocated_6ghz;
7915
7916 iface = hapd->iface->interfaces->iface[i];
7917 if (iface == hapd->iface || !iface || !iface->conf)
7918 continue;
7919
7920 is_colocated_6ghz = is_6ghz_op_class(iface->conf->op_class);
7921 if (!is_6ghz && is_colocated_6ghz)
7922 return COLOCATED_LOWER_BAND;
7923 if (is_6ghz && !is_colocated_6ghz)
7924 return COLOCATED_6GHZ;
7925 }
7926
7927 if (is_6ghz)
7928 return STANDALONE_6GHZ;
7929
7930 return NO_COLOCATED_6GHZ;
7931}
7932
7933
Sunil Ravi7f769292024-07-23 22:21:32 +00007934static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd,
7935 size_t *current_len)
Hai Shaloma20dcd72022-02-04 13:43:00 -08007936{
7937 struct hostapd_iface *iface;
7938 size_t len = 0;
7939 size_t i;
7940
7941 if (!hapd->iface || !hapd->iface->interfaces)
7942 return 0;
7943
7944 for (i = 0; i < hapd->iface->interfaces->count; i++) {
7945 iface = hapd->iface->interfaces->iface[i];
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007946
Sunil Ravi7f769292024-07-23 22:21:32 +00007947 if (!iface || iface == hapd->iface ||
Sunil Ravi99c035e2024-07-12 01:42:03 +00007948 iface->state != HAPD_IFACE_ENABLED ||
Sunil Ravi7f769292024-07-23 22:21:32 +00007949 !is_6ghz_op_class(iface->conf->op_class))
Hai Shaloma20dcd72022-02-04 13:43:00 -08007950 continue;
7951
7952 len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
Sunil Ravi7f769292024-07-23 22:21:32 +00007953 current_len, NULL, false);
Hai Shaloma20dcd72022-02-04 13:43:00 -08007954 }
7955
7956 return len;
7957}
7958
7959
Sunil Ravi7f769292024-07-23 22:21:32 +00007960static size_t hostapd_eid_rnr_mlo_len(struct hostapd_data *hapd, u32 type,
7961 size_t *current_len)
7962{
7963 size_t len = 0;
7964#ifdef CONFIG_IEEE80211BE
7965 struct hostapd_iface *iface;
7966 size_t i;
7967
7968 if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap)
7969 return 0;
7970
7971 /* TODO: Allow for FILS/Action as well */
7972 if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
7973 return 0;
7974
7975 for (i = 0; i < hapd->iface->interfaces->count; i++) {
7976 iface = hapd->iface->interfaces->iface[i];
7977
7978 if (!iface || iface == hapd->iface ||
7979 hapd->iface->freq == iface->freq)
7980 continue;
7981
7982 len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
7983 current_len, NULL, true);
7984 }
7985#endif /* CONFIG_IEEE80211BE */
7986
7987 return len;
7988}
7989
7990
7991size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type,
7992 bool include_mld_params)
Hai Shaloma20dcd72022-02-04 13:43:00 -08007993{
7994 size_t total_len = 0, current_len = 0;
7995 enum colocation_mode mode = get_colocation_mode(hapd);
7996
7997 switch (type) {
7998 case WLAN_FC_STYPE_BEACON:
7999 if (hapd->conf->rnr)
8000 total_len += hostapd_eid_nr_db_len(hapd, &current_len);
8001 /* fallthrough */
Hai Shaloma20dcd72022-02-04 13:43:00 -08008002 case WLAN_FC_STYPE_PROBE_RESP:
Sunil Ravi7f769292024-07-23 22:21:32 +00008003 if (mode == COLOCATED_LOWER_BAND)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00008004 total_len +=
Sunil Ravi7f769292024-07-23 22:21:32 +00008005 hostapd_eid_rnr_colocation_len(hapd,
8006 &current_len);
Hai Shaloma20dcd72022-02-04 13:43:00 -08008007
Sunil Ravi640215c2023-06-28 23:08:09 +00008008 if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
8009 !hapd->iconf->mbssid)
Hai Shaloma20dcd72022-02-04 13:43:00 -08008010 total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
Sunil Ravi640215c2023-06-28 23:08:09 +00008011 &current_len,
Sunil Ravi7f769292024-07-23 22:21:32 +00008012 NULL, false);
Hai Shaloma20dcd72022-02-04 13:43:00 -08008013 break;
Hai Shaloma20dcd72022-02-04 13:43:00 -08008014 case WLAN_FC_STYPE_ACTION:
8015 if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
8016 total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
Sunil Ravi640215c2023-06-28 23:08:09 +00008017 &current_len,
Sunil Ravi7f769292024-07-23 22:21:32 +00008018 NULL, false);
Hai Shaloma20dcd72022-02-04 13:43:00 -08008019 break;
8020 }
8021
Sunil Ravi7f769292024-07-23 22:21:32 +00008022 /* For EMA Beacons, MLD neighbor repoting is added as part of
8023 * MBSSID RNR. */
8024 if (include_mld_params &&
8025 (type != WLAN_FC_STYPE_BEACON ||
8026 hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
8027 total_len += hostapd_eid_rnr_mlo_len(hapd, type, &current_len);
8028
Hai Shaloma20dcd72022-02-04 13:43:00 -08008029 return total_len;
8030}
8031
8032
8033static u8 * hostapd_eid_nr_db(struct hostapd_data *hapd, u8 *eid,
8034 size_t *current_len)
8035{
8036 struct hostapd_neighbor_entry *nr;
8037 size_t len = *current_len;
8038 u8 *size_offset = (eid - len) + 1;
8039
8040 dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
8041 list) {
8042 if (!nr->nr || wpabuf_len(nr->nr) < 12)
8043 continue;
8044
8045 if (nr->short_ssid == hapd->conf->ssid.short_ssid)
8046 continue;
8047
8048 /* Start a new element */
8049 if (!len ||
8050 len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
8051 *eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
8052 size_offset = eid++;
8053 len = RNR_HEADER_LEN;
8054 }
8055
8056 /* TBTT Information Header subfield (2 octets) */
8057 *eid++ = 0;
8058 /* TBTT Information Length */
8059 *eid++ = RNR_TBTT_INFO_LEN;
8060 /* Operating Class */
8061 *eid++ = wpabuf_head_u8(nr->nr)[10];
8062 /* Channel Number */
8063 *eid++ = wpabuf_head_u8(nr->nr)[11];
8064 len += RNR_TBTT_HEADER_LEN;
8065 /* TBTT Information Set */
8066 /* TBTT Information field */
8067 /* Neighbor AP TBTT Offset */
8068 *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
8069 /* BSSID */
8070 os_memcpy(eid, nr->bssid, ETH_ALEN);
8071 eid += ETH_ALEN;
8072 /* Short SSID */
8073 os_memcpy(eid, &nr->short_ssid, 4);
8074 eid += 4;
8075 /* BSS parameters */
8076 *eid++ = nr->bss_parameters;
8077 /* 20 MHz PSD */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008078 *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
Hai Shaloma20dcd72022-02-04 13:43:00 -08008079 len += RNR_TBTT_INFO_LEN;
8080 *size_offset = (eid - size_offset) - 1;
8081 }
8082
8083 *current_len = len;
8084 return eid;
8085}
8086
8087
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008088static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
8089 struct hostapd_data *reporting_hapd,
8090 struct mbssid_ie_profiles *skip_profiles,
8091 size_t i, u8 *tbtt_count, size_t *len,
Sunil Ravi7f769292024-07-23 22:21:32 +00008092 u8 **pos, u8 **tbtt_count_pos, u8 tbtt_info_len,
8093 u8 op_class, bool mld_update)
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008094{
8095 struct hostapd_iface *iface = hapd->iface;
8096 struct hostapd_data *bss = iface->bss[i];
8097 u8 bss_param = 0;
8098 bool ap_mld = false;
8099 u8 *eid = *pos;
8100
8101#ifdef CONFIG_IEEE80211BE
8102 ap_mld = !!hapd->conf->mld_ap;
8103#endif /* CONFIG_IEEE80211BE */
8104
8105 if (!bss || !bss->conf || !bss->started ||
8106 bss == reporting_hapd || bss->conf->ignore_broadcast_ssid)
8107 return false;
8108
Sunil Ravi7f769292024-07-23 22:21:32 +00008109 if (hostapd_skip_rnr(i, skip_profiles, ap_mld, tbtt_info_len,
8110 mld_update, reporting_hapd, bss))
8111 return false;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008112
8113 if (*len + RNR_TBTT_INFO_LEN > 255 ||
8114 *tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
8115 return true;
8116
Sunil Ravi7f769292024-07-23 22:21:32 +00008117 if (!(*tbtt_count)) {
8118 /* Add neighbor report header info only if there is at least
8119 * one TBTT info available. */
8120 *tbtt_count_pos = eid++;
8121 *eid++ = tbtt_info_len;
8122 *eid++ = op_class;
8123 *eid++ = bss->iconf->channel;
8124 *len += RNR_TBTT_HEADER_LEN;
8125 }
8126
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008127 *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
8128 os_memcpy(eid, bss->own_addr, ETH_ALEN);
8129 eid += ETH_ALEN;
8130 os_memcpy(eid, &bss->conf->ssid.short_ssid, 4);
8131 eid += 4;
8132 if (bss->conf->ssid.short_ssid == reporting_hapd->conf->ssid.short_ssid)
8133 bss_param |= RNR_BSS_PARAM_SAME_SSID;
8134
8135 if (iface->conf->mbssid != MBSSID_DISABLED && iface->num_bss > 1) {
8136 bss_param |= RNR_BSS_PARAM_MULTIPLE_BSSID;
8137 if (bss == hostapd_mbssid_get_tx_bss(hapd))
8138 bss_param |= RNR_BSS_PARAM_TRANSMITTED_BSSID;
8139 }
8140
8141 if (is_6ghz_op_class(hapd->iconf->op_class) &&
8142 bss->conf->unsol_bcast_probe_resp_interval)
8143 bss_param |= RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE;
8144
8145 bss_param |= RNR_BSS_PARAM_CO_LOCATED;
8146
8147 *eid++ = bss_param;
8148 *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
8149
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008150#ifdef CONFIG_IEEE80211BE
Sunil Ravi7f769292024-07-23 22:21:32 +00008151 if (ap_mld) {
8152 u8 param_ch = bss->eht_mld_bss_param_change;
8153 bool is_partner;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008154
Sunil Ravi7f769292024-07-23 22:21:32 +00008155 /* If BSS is not a partner of the reporting_hapd
8156 * a) MLD ID advertised shall be 255.
8157 * b) Link ID advertised shall be 15.
8158 * c) BPCC advertised shall be 255 */
8159 is_partner = hostapd_is_ml_partner(bss, reporting_hapd);
8160 /* MLD ID */
8161 *eid++ = is_partner ? hostapd_get_mld_id(bss) : 0xFF;
8162 /* Link ID (Bit 3 to Bit 0)
8163 * BPCC (Bit 4 to Bit 7) */
8164 *eid++ = is_partner ?
8165 bss->mld_link_id | ((param_ch & 0xF) << 4) :
8166 (MAX_NUM_MLD_LINKS | 0xF0);
8167 /* BPCC (Bit 3 to Bit 0) */
8168 *eid = is_partner ? ((param_ch & 0xF0) >> 4) : 0x0F;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008169#ifdef CONFIG_TESTING_OPTIONS
Sunil Ravi7f769292024-07-23 22:21:32 +00008170 if (bss->conf->mld_indicate_disabled)
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008171 *eid |= RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
8172#endif /* CONFIG_TESTING_OPTIONS */
8173 eid++;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008174 }
Sunil Ravi7f769292024-07-23 22:21:32 +00008175#endif /* CONFIG_IEEE80211BE */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008176
Sunil Ravi7f769292024-07-23 22:21:32 +00008177 *len += tbtt_info_len;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008178 (*tbtt_count)++;
8179 *pos = eid;
8180
8181 return false;
8182}
8183
8184
Hai Shaloma20dcd72022-02-04 13:43:00 -08008185static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
8186 struct hostapd_data *reporting_hapd,
Sunil Ravi640215c2023-06-28 23:08:09 +00008187 u8 *eid, size_t *current_len,
Sunil Ravi7f769292024-07-23 22:21:32 +00008188 struct mbssid_ie_profiles *skip_profiles,
8189 bool mld_update)
Hai Shaloma20dcd72022-02-04 13:43:00 -08008190{
Hai Shaloma20dcd72022-02-04 13:43:00 -08008191 struct hostapd_iface *iface = hapd->iface;
Sunil Ravi7f769292024-07-23 22:21:32 +00008192 size_t i, start;
Hai Shaloma20dcd72022-02-04 13:43:00 -08008193 size_t len = *current_len;
Sunil Ravi7f769292024-07-23 22:21:32 +00008194 u8 *eid_start = eid, *size_offset = (eid - len) + 1;
8195 u8 *tbtt_count_pos = size_offset + 1;
8196 u8 tbtt_count, total_tbtt_count = 0, op_class, channel;
8197 u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN :
8198 RNR_TBTT_INFO_LEN;
Hai Shaloma20dcd72022-02-04 13:43:00 -08008199
8200 if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq)
8201 return eid;
8202
8203 if (ieee80211_freq_to_channel_ext(iface->freq,
8204 hapd->iconf->secondary_channel,
8205 hostapd_get_oper_chwidth(hapd->iconf),
8206 &op_class, &channel) ==
8207 NUM_HOSTAPD_MODES)
8208 return eid;
8209
Sunil Ravi7f769292024-07-23 22:21:32 +00008210repeat_rnr:
8211 start = 0;
8212 tbtt_count = 0;
Hai Shaloma20dcd72022-02-04 13:43:00 -08008213 while (start < iface->num_bss) {
8214 if (!len ||
Sunil Ravi7f769292024-07-23 22:21:32 +00008215 len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
Sunil Ravi2a14cf12023-11-21 00:54:38 +00008216 tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
Hai Shaloma20dcd72022-02-04 13:43:00 -08008217 eid_start = eid;
8218 *eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
8219 size_offset = eid++;
8220 len = RNR_HEADER_LEN;
8221 tbtt_count = 0;
8222 }
8223
Hai Shaloma20dcd72022-02-04 13:43:00 -08008224 for (i = start; i < iface->num_bss; i++) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008225 if (hostapd_eid_rnr_bss(hapd, reporting_hapd,
8226 skip_profiles, i,
Sunil Ravi7f769292024-07-23 22:21:32 +00008227 &tbtt_count, &len, &eid,
8228 &tbtt_count_pos, tbtt_info_len,
8229 op_class, mld_update))
Hai Shaloma20dcd72022-02-04 13:43:00 -08008230 break;
Hai Shaloma20dcd72022-02-04 13:43:00 -08008231 }
8232
8233 start = i;
Sunil Ravi7f769292024-07-23 22:21:32 +00008234
8235 if (tbtt_count) {
8236 *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
8237 *size_offset = (eid - size_offset) - 1;
8238 }
Hai Shaloma20dcd72022-02-04 13:43:00 -08008239 }
8240
Sunil Ravi7f769292024-07-23 22:21:32 +00008241 total_tbtt_count += tbtt_count;
8242
8243 /* If building for co-location, re-build again but this time include
8244 * ML TBTTs.
8245 */
8246 if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
8247 tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
8248 goto repeat_rnr;
8249 }
8250
8251 if (!total_tbtt_count)
Hai Shaloma20dcd72022-02-04 13:43:00 -08008252 return eid_start;
8253
8254 *current_len = len;
8255 return eid;
8256}
8257
8258
Sunil Ravi876a49b2025-02-03 19:18:32 +00008259static u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
8260 size_t *current_len)
Hai Shaloma20dcd72022-02-04 13:43:00 -08008261{
8262 struct hostapd_iface *iface;
8263 size_t i;
8264
8265 if (!hapd->iface || !hapd->iface->interfaces)
8266 return eid;
8267
8268 for (i = 0; i < hapd->iface->interfaces->count; i++) {
8269 iface = hapd->iface->interfaces->iface[i];
Sunil Ravi2a14cf12023-11-21 00:54:38 +00008270
Sunil Ravi7f769292024-07-23 22:21:32 +00008271 if (!iface || iface == hapd->iface ||
Sunil Ravi99c035e2024-07-12 01:42:03 +00008272 iface->state != HAPD_IFACE_ENABLED ||
Sunil Ravi7f769292024-07-23 22:21:32 +00008273 !is_6ghz_op_class(iface->conf->op_class))
Hai Shaloma20dcd72022-02-04 13:43:00 -08008274 continue;
8275
8276 eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
Sunil Ravi7f769292024-07-23 22:21:32 +00008277 current_len, NULL, false);
Hai Shaloma20dcd72022-02-04 13:43:00 -08008278 }
8279
8280 return eid;
8281}
8282
8283
Sunil Ravi876a49b2025-02-03 19:18:32 +00008284static u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
8285 u8 *eid, size_t *current_len)
Sunil Ravi7f769292024-07-23 22:21:32 +00008286{
8287#ifdef CONFIG_IEEE80211BE
8288 struct hostapd_iface *iface;
8289 size_t i;
8290
8291 if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap)
8292 return eid;
8293
8294 /* TODO: Allow for FILS/Action as well */
8295 if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
8296 return eid;
8297
8298 for (i = 0; i < hapd->iface->interfaces->count; i++) {
8299 iface = hapd->iface->interfaces->iface[i];
8300
8301 if (!iface || iface == hapd->iface ||
8302 hapd->iface->freq == iface->freq)
8303 continue;
8304
8305 eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
8306 current_len, NULL, true);
8307 }
8308#endif /* CONFIG_IEEE80211BE */
8309
8310 return eid;
8311}
8312
8313
8314u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type,
8315 bool include_mld_params)
Hai Shaloma20dcd72022-02-04 13:43:00 -08008316{
8317 u8 *eid_start = eid;
8318 size_t current_len = 0;
8319 enum colocation_mode mode = get_colocation_mode(hapd);
8320
8321 switch (type) {
8322 case WLAN_FC_STYPE_BEACON:
8323 if (hapd->conf->rnr)
8324 eid = hostapd_eid_nr_db(hapd, eid, &current_len);
8325 /* fallthrough */
Hai Shaloma20dcd72022-02-04 13:43:00 -08008326 case WLAN_FC_STYPE_PROBE_RESP:
Sunil Ravi7f769292024-07-23 22:21:32 +00008327 if (mode == COLOCATED_LOWER_BAND)
8328 eid = hostapd_eid_rnr_colocation(hapd, eid,
8329 &current_len);
Hai Shaloma20dcd72022-02-04 13:43:00 -08008330
Sunil Ravi640215c2023-06-28 23:08:09 +00008331 if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
8332 !hapd->iconf->mbssid)
Hai Shaloma20dcd72022-02-04 13:43:00 -08008333 eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
Sunil Ravi7f769292024-07-23 22:21:32 +00008334 &current_len, NULL, false);
Hai Shaloma20dcd72022-02-04 13:43:00 -08008335 break;
Hai Shaloma20dcd72022-02-04 13:43:00 -08008336 case WLAN_FC_STYPE_ACTION:
8337 if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008338 eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
Sunil Ravi7f769292024-07-23 22:21:32 +00008339 &current_len, NULL, false);
Hai Shaloma20dcd72022-02-04 13:43:00 -08008340 break;
Hai Shaloma20dcd72022-02-04 13:43:00 -08008341 default:
8342 return eid_start;
8343 }
8344
Sunil Ravi7f769292024-07-23 22:21:32 +00008345 /* For EMA Beacons, MLD neighbor repoting is added as part of
8346 * MBSSID RNR. */
8347 if (include_mld_params &&
8348 (type != WLAN_FC_STYPE_BEACON ||
8349 hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
8350 eid = hostapd_eid_rnr_mlo(hapd, type, eid, &current_len);
8351
Hai Shaloma20dcd72022-02-04 13:43:00 -08008352 if (eid == eid_start + 2)
8353 return eid_start;
8354
8355 return eid;
8356}
8357
Sunil Ravi77d572f2023-01-17 23:58:31 +00008358
8359static bool mbssid_known_bss(unsigned int i, const u8 *known_bss,
8360 size_t known_bss_len)
8361{
8362 if (!known_bss || known_bss_len <= i / 8)
8363 return false;
8364 known_bss = &known_bss[i / 8];
8365 return *known_bss & (u8) (BIT(i % 8));
8366}
8367
8368
Sunil Ravi99c035e2024-07-12 01:42:03 +00008369static size_t hostapd_mbssid_ext_capa(struct hostapd_data *bss,
8370 struct hostapd_data *tx_bss, u8 *buf)
8371{
8372 u8 ext_capa_tx[20], *ext_capa_tx_end, ext_capa[20], *ext_capa_end;
8373 size_t ext_capa_len, ext_capa_tx_len;
8374
8375 ext_capa_tx_end = hostapd_eid_ext_capab(tx_bss, ext_capa_tx,
8376 true);
8377 ext_capa_tx_len = ext_capa_tx_end - ext_capa_tx;
8378 ext_capa_end = hostapd_eid_ext_capab(bss, ext_capa, true);
8379 ext_capa_len = ext_capa_end - ext_capa;
8380 if (ext_capa_tx_len != ext_capa_len ||
8381 os_memcmp(ext_capa_tx, ext_capa, ext_capa_len) != 0) {
8382 os_memcpy(buf, ext_capa, ext_capa_len);
8383 return ext_capa_len;
8384 }
8385
8386 return 0;
8387}
8388
8389
Sunil Ravi77d572f2023-01-17 23:58:31 +00008390static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
8391 u32 frame_type, size_t *bss_index,
8392 const u8 *known_bss,
8393 size_t known_bss_len)
8394{
8395 struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
Sunil Ravi876a49b2025-02-03 19:18:32 +00008396 size_t len, i, tx_xrate_len;
8397 u8 ext_capa[20], buf[100];
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008398
8399 /* Element ID: 1 octet
8400 * Length: 1 octet
8401 * MaxBSSID Indicator: 1 octet
8402 * Optional Subelements: vatiable
8403 *
8404 * Total fixed length: 3 octets
8405 *
8406 * 1 octet in len for the MaxBSSID Indicator field.
8407 */
8408 len = 1;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008409
Sunil Ravi876a49b2025-02-03 19:18:32 +00008410 tx_xrate_len = hostapd_eid_ext_supp_rates(tx_bss, buf) - buf;
8411
Sunil Ravi77d572f2023-01-17 23:58:31 +00008412 for (i = *bss_index; i < hapd->iface->num_bss; i++) {
8413 struct hostapd_data *bss = hapd->iface->bss[i];
8414 const u8 *auth, *rsn = NULL, *rsnx = NULL;
Sunil Ravi876a49b2025-02-03 19:18:32 +00008415 size_t nontx_profile_len, auth_len, xrate_len;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008416 u8 ie_count = 0;
8417
8418 if (!bss || !bss->conf || !bss->started ||
8419 mbssid_known_bss(i, known_bss, known_bss_len))
8420 continue;
8421
8422 /*
8423 * Sublement ID: 1 octet
8424 * Length: 1 octet
8425 * Nontransmitted capabilities: 4 octets
8426 * SSID element: 2 + variable
8427 * Multiple BSSID Index Element: 3 octets (+2 octets in beacons)
8428 * Fixed length = 1 + 1 + 4 + 2 + 3 = 11
8429 */
8430 nontx_profile_len = 11 + bss->conf->ssid.ssid_len;
8431
8432 if (frame_type == WLAN_FC_STYPE_BEACON)
8433 nontx_profile_len += 2;
8434
8435 auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
8436 if (auth) {
8437 rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
8438 if (rsn)
8439 nontx_profile_len += 2 + rsn[1];
8440
8441 rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
8442 if (rsnx)
8443 nontx_profile_len += 2 + rsnx[1];
8444 }
Sunil Ravi99c035e2024-07-12 01:42:03 +00008445
8446 nontx_profile_len += hostapd_mbssid_ext_capa(bss, tx_bss,
8447 ext_capa);
8448
Sunil Ravi77d572f2023-01-17 23:58:31 +00008449 if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
8450 ie_count++;
8451 if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
8452 ie_count++;
Sunil Ravi876a49b2025-02-03 19:18:32 +00008453
8454 xrate_len = hostapd_eid_ext_supp_rates(bss, buf) - buf;
8455
8456 if (xrate_len)
8457 nontx_profile_len += xrate_len;
8458 else if (tx_xrate_len)
Sunil Ravi77d572f2023-01-17 23:58:31 +00008459 ie_count++;
8460 if (ie_count)
Sunil Ravi876a49b2025-02-03 19:18:32 +00008461 nontx_profile_len += 4 + ie_count + 1;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008462
8463 if (len + nontx_profile_len > 255)
8464 break;
8465
8466 len += nontx_profile_len;
8467 }
8468
8469 *bss_index = i;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008470
8471 /* Add 2 octets to get the full size of the element */
8472 return len + 2;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008473}
8474
8475
8476size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
8477 u8 *elem_count, const u8 *known_bss,
Sunil Ravi640215c2023-06-28 23:08:09 +00008478 size_t known_bss_len, size_t *rnr_len)
Sunil Ravi77d572f2023-01-17 23:58:31 +00008479{
8480 size_t len = 0, bss_index = 1;
Sunil Ravi7f769292024-07-23 22:21:32 +00008481 bool ap_mld = false;
8482
8483#ifdef CONFIG_IEEE80211BE
8484 ap_mld = hapd->conf->mld_ap;
8485#endif /* CONFIG_IEEE80211BE */
Sunil Ravi77d572f2023-01-17 23:58:31 +00008486
8487 if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
8488 (frame_type != WLAN_FC_STYPE_BEACON &&
8489 frame_type != WLAN_FC_STYPE_PROBE_RESP))
8490 return 0;
8491
8492 if (frame_type == WLAN_FC_STYPE_BEACON) {
8493 if (!elem_count) {
8494 wpa_printf(MSG_INFO,
8495 "MBSSID: Insufficient data for Beacon frames");
8496 return 0;
8497 }
8498 *elem_count = 0;
8499 }
8500
8501 while (bss_index < hapd->iface->num_bss) {
Sunil Ravi640215c2023-06-28 23:08:09 +00008502 size_t rnr_count = bss_index;
8503
Sunil Ravi77d572f2023-01-17 23:58:31 +00008504 len += hostapd_eid_mbssid_elem_len(hapd, frame_type,
8505 &bss_index, known_bss,
8506 known_bss_len);
8507
8508 if (frame_type == WLAN_FC_STYPE_BEACON)
8509 *elem_count += 1;
Sunil Ravi640215c2023-06-28 23:08:09 +00008510 if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && rnr_len) {
8511 size_t rnr_cur_len = 0;
8512 struct mbssid_ie_profiles skip_profiles = {
8513 rnr_count, bss_index
8514 };
8515
8516 *rnr_len += hostapd_eid_rnr_iface_len(
8517 hapd, hostapd_mbssid_get_tx_bss(hapd),
Sunil Ravi7f769292024-07-23 22:21:32 +00008518 &rnr_cur_len, &skip_profiles, ap_mld);
Sunil Ravi640215c2023-06-28 23:08:09 +00008519 }
Sunil Ravi77d572f2023-01-17 23:58:31 +00008520 }
Sunil Ravi640215c2023-06-28 23:08:09 +00008521
8522 if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && rnr_len)
Sunil Ravi7f769292024-07-23 22:21:32 +00008523 *rnr_len += hostapd_eid_rnr_len(hapd, frame_type, false);
Sunil Ravi640215c2023-06-28 23:08:09 +00008524
Sunil Ravi77d572f2023-01-17 23:58:31 +00008525 return len;
8526}
8527
8528
8529static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
8530 u32 frame_type, u8 max_bssid_indicator,
8531 size_t *bss_index, u8 elem_count,
8532 const u8 *known_bss, size_t known_bss_len)
8533{
8534 struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
Sunil Ravi876a49b2025-02-03 19:18:32 +00008535 size_t i, tx_xrate_len;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008536 u8 *eid_len_offset, *max_bssid_indicator_offset;
Sunil Ravi876a49b2025-02-03 19:18:32 +00008537 u8 buf[100];
Sunil Ravi77d572f2023-01-17 23:58:31 +00008538
8539 *eid++ = WLAN_EID_MULTIPLE_BSSID;
8540 eid_len_offset = eid++;
8541 max_bssid_indicator_offset = eid++;
8542
Sunil Ravi876a49b2025-02-03 19:18:32 +00008543 tx_xrate_len = hostapd_eid_ext_supp_rates(tx_bss, buf) - buf;
8544
Sunil Ravi77d572f2023-01-17 23:58:31 +00008545 for (i = *bss_index; i < hapd->iface->num_bss; i++) {
8546 struct hostapd_data *bss = hapd->iface->bss[i];
8547 struct hostapd_bss_config *conf;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00008548 struct hostapd_bss_config *tx_conf = tx_bss->conf;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008549 u8 *eid_len_pos, *nontx_bss_start = eid;
8550 const u8 *auth, *rsn = NULL, *rsnx = NULL;
8551 u8 ie_count = 0, non_inherit_ie[3];
Sunil Ravi876a49b2025-02-03 19:18:32 +00008552 size_t auth_len = 0, xrate_len;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008553 u16 capab_info;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00008554 u8 mbssindex = i;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008555
8556 if (!bss || !bss->conf || !bss->started ||
8557 mbssid_known_bss(i, known_bss, known_bss_len))
8558 continue;
8559 conf = bss->conf;
8560
8561 *eid++ = WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE;
8562 eid_len_pos = eid++;
8563
8564 capab_info = hostapd_own_capab_info(bss);
8565 *eid++ = WLAN_EID_NONTRANSMITTED_BSSID_CAPA;
8566 *eid++ = sizeof(capab_info);
8567 WPA_PUT_LE16(eid, capab_info);
8568 eid += sizeof(capab_info);
8569
8570 *eid++ = WLAN_EID_SSID;
8571 *eid++ = conf->ssid.ssid_len;
8572 os_memcpy(eid, conf->ssid.ssid, conf->ssid.ssid_len);
8573 eid += conf->ssid.ssid_len;
8574
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00008575 if (conf->mbssid_index &&
8576 conf->mbssid_index > tx_conf->mbssid_index)
8577 mbssindex = conf->mbssid_index - tx_conf->mbssid_index;
8578
Sunil Ravi77d572f2023-01-17 23:58:31 +00008579 *eid++ = WLAN_EID_MULTIPLE_BSSID_INDEX;
8580 if (frame_type == WLAN_FC_STYPE_BEACON) {
8581 *eid++ = 3;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00008582 *eid++ = mbssindex; /* BSSID Index */
Sunil Ravi77d572f2023-01-17 23:58:31 +00008583 if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
8584 (conf->dtim_period % elem_count))
8585 conf->dtim_period = elem_count;
8586 *eid++ = conf->dtim_period;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00008587 /* The driver is expected to update the DTIM Count
8588 * field for each BSS that corresponds to a
8589 * nontransmitted BSSID. The value is initialized to
8590 * 0 here so that the DTIM count would be somewhat
8591 * functional even if the driver were not to update
8592 * this. */
8593 *eid++ = 0; /* DTIM Count */
Sunil Ravi77d572f2023-01-17 23:58:31 +00008594 } else {
8595 /* Probe Request frame does not include DTIM Period and
8596 * DTIM Count fields. */
8597 *eid++ = 1;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00008598 *eid++ = mbssindex; /* BSSID Index */
Sunil Ravi77d572f2023-01-17 23:58:31 +00008599 }
8600
8601 auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
8602 if (auth) {
8603 rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
8604 if (rsn) {
8605 os_memcpy(eid, rsn, 2 + rsn[1]);
8606 eid += 2 + rsn[1];
8607 }
8608
8609 rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
8610 if (rsnx) {
8611 os_memcpy(eid, rsnx, 2 + rsnx[1]);
8612 eid += 2 + rsnx[1];
8613 }
8614 }
Sunil Ravi99c035e2024-07-12 01:42:03 +00008615
8616 eid += hostapd_mbssid_ext_capa(bss, tx_bss, eid);
Sunil Ravi876a49b2025-02-03 19:18:32 +00008617 xrate_len = hostapd_eid_ext_supp_rates(bss, eid) - eid;
8618 eid += xrate_len;
Sunil Ravi99c035e2024-07-12 01:42:03 +00008619
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008620 /* List of Element ID values in increasing order */
Sunil Ravi77d572f2023-01-17 23:58:31 +00008621 if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
8622 non_inherit_ie[ie_count++] = WLAN_EID_RSN;
Sunil Ravi876a49b2025-02-03 19:18:32 +00008623 if (tx_xrate_len && !xrate_len)
Sunil Ravi77d572f2023-01-17 23:58:31 +00008624 non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008625 if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
8626 non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008627 if (ie_count) {
8628 *eid++ = WLAN_EID_EXTENSION;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00008629 *eid++ = 2 + ie_count + 1;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008630 *eid++ = WLAN_EID_EXT_NON_INHERITANCE;
8631 *eid++ = ie_count;
8632 os_memcpy(eid, non_inherit_ie, ie_count);
8633 eid += ie_count;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00008634 *eid++ = 0; /* No Element ID Extension List */
Sunil Ravi77d572f2023-01-17 23:58:31 +00008635 }
8636
8637 *eid_len_pos = (eid - eid_len_pos) - 1;
8638
8639 if (((eid - eid_len_offset) - 1) > 255) {
8640 eid = nontx_bss_start;
8641 break;
8642 }
8643 }
8644
8645 *bss_index = i;
8646 *max_bssid_indicator_offset = max_bssid_indicator;
8647 if (*max_bssid_indicator_offset < 1)
8648 *max_bssid_indicator_offset = 1;
8649 *eid_len_offset = (eid - eid_len_offset) - 1;
8650 return eid;
8651}
8652
8653
8654u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
8655 unsigned int frame_stype, u8 elem_count,
8656 u8 **elem_offset,
Sunil Ravi640215c2023-06-28 23:08:09 +00008657 const u8 *known_bss, size_t known_bss_len, u8 *rnr_eid,
8658 u8 *rnr_count, u8 **rnr_offset, size_t rnr_len)
Sunil Ravi77d572f2023-01-17 23:58:31 +00008659{
Sunil Ravi640215c2023-06-28 23:08:09 +00008660 size_t bss_index = 1, cur_len = 0;
8661 u8 elem_index = 0, *rnr_start_eid = rnr_eid;
Sunil Ravi7f769292024-07-23 22:21:32 +00008662 bool add_rnr, ap_mld = false;
8663
8664#ifdef CONFIG_IEEE80211BE
8665 ap_mld = hapd->conf->mld_ap;
8666#endif /* CONFIG_IEEE80211BE */
Sunil Ravi77d572f2023-01-17 23:58:31 +00008667
8668 if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
8669 (frame_stype != WLAN_FC_STYPE_BEACON &&
8670 frame_stype != WLAN_FC_STYPE_PROBE_RESP))
8671 return eid;
8672
8673 if (frame_stype == WLAN_FC_STYPE_BEACON && !elem_offset) {
8674 wpa_printf(MSG_INFO,
8675 "MBSSID: Insufficient data for Beacon frames");
8676 return eid;
8677 }
8678
Sunil Ravi640215c2023-06-28 23:08:09 +00008679 add_rnr = hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
8680 frame_stype == WLAN_FC_STYPE_BEACON &&
8681 rnr_eid && rnr_count && rnr_offset && rnr_len;
8682
Sunil Ravi77d572f2023-01-17 23:58:31 +00008683 while (bss_index < hapd->iface->num_bss) {
Sunil Ravi640215c2023-06-28 23:08:09 +00008684 unsigned int rnr_start_count = bss_index;
8685
Sunil Ravi77d572f2023-01-17 23:58:31 +00008686 if (frame_stype == WLAN_FC_STYPE_BEACON) {
8687 if (elem_index == elem_count) {
8688 wpa_printf(MSG_WARNING,
8689 "MBSSID: Larger number of elements than there is room in the provided array");
8690 break;
8691 }
8692
8693 elem_offset[elem_index] = eid;
8694 elem_index = elem_index + 1;
8695 }
8696 eid = hostapd_eid_mbssid_elem(hapd, eid, end, frame_stype,
8697 hostapd_max_bssid_indicator(hapd),
8698 &bss_index, elem_count,
8699 known_bss, known_bss_len);
Sunil Ravi640215c2023-06-28 23:08:09 +00008700
8701 if (add_rnr) {
8702 struct mbssid_ie_profiles skip_profiles = {
8703 rnr_start_count, bss_index
8704 };
8705
8706 rnr_offset[*rnr_count] = rnr_eid;
8707 *rnr_count = *rnr_count + 1;
8708 cur_len = 0;
8709 rnr_eid = hostapd_eid_rnr_iface(
8710 hapd, hostapd_mbssid_get_tx_bss(hapd),
Sunil Ravi7f769292024-07-23 22:21:32 +00008711 rnr_eid, &cur_len, &skip_profiles, ap_mld);
Sunil Ravi640215c2023-06-28 23:08:09 +00008712 }
8713 }
8714
8715 if (add_rnr && (size_t) (rnr_eid - rnr_start_eid) < rnr_len) {
8716 rnr_offset[*rnr_count] = rnr_eid;
8717 *rnr_count = *rnr_count + 1;
8718 cur_len = 0;
8719
8720 if (hapd->conf->rnr)
8721 rnr_eid = hostapd_eid_nr_db(hapd, rnr_eid, &cur_len);
8722 if (get_colocation_mode(hapd) == COLOCATED_LOWER_BAND)
Sunil Ravi7f769292024-07-23 22:21:32 +00008723 rnr_eid = hostapd_eid_rnr_colocation(hapd, rnr_eid,
8724 &cur_len);
Sunil Ravi77d572f2023-01-17 23:58:31 +00008725 }
8726
8727 return eid;
8728}
8729
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07008730#endif /* CONFIG_NATIVE_WINDOWS */