blob: a9ed6eb0fabce7a3c56a233cde79b381d823f591 [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
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700120u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
121{
122 u8 *pos = eid;
123 int i, num, count;
Hai Shalomfdcde762020-04-02 11:19:20 -0700124 int h2e_required;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700125
126 if (hapd->iface->current_rates == NULL)
127 return eid;
128
129 *pos++ = WLAN_EID_SUPP_RATES;
130 num = hapd->iface->num_rates;
131 if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
132 num++;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800133 if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
134 num++;
Sunil Ravi77d572f2023-01-17 23:58:31 +0000135#ifdef CONFIG_IEEE80211AX
136 if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
137 num++;
138#endif /* CONFIG_IEEE80211AX */
139 h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
Hai Shalomfdcde762020-04-02 11:19:20 -0700140 hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
Sunil Ravi77d572f2023-01-17 23:58:31 +0000141 hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
Hai Shalomfdcde762020-04-02 11:19:20 -0700142 wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
143 if (h2e_required)
Hai Shalomc3565922019-10-28 11:58:20 -0700144 num++;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700145 if (num > 8) {
146 /* rest of the rates are encoded in Extended supported
147 * rates element */
148 num = 8;
149 }
150
151 *pos++ = num;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700152 for (i = 0, count = 0; i < hapd->iface->num_rates && count < num;
153 i++) {
154 count++;
155 *pos = hapd->iface->current_rates[i].rate / 5;
156 if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
157 *pos |= 0x80;
158 pos++;
159 }
160
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800161 if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) {
162 count++;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700163 *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800164 }
165
166 if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) {
167 count++;
168 *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
169 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700170
Sunil Ravi77d572f2023-01-17 23:58:31 +0000171#ifdef CONFIG_IEEE80211AX
172 if (hapd->iconf->ieee80211ax && hapd->iconf->require_he && count < 8) {
173 count++;
174 *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
175 }
176#endif /* CONFIG_IEEE80211AX */
177
Hai Shalomfdcde762020-04-02 11:19:20 -0700178 if (h2e_required && count < 8) {
Hai Shalomc3565922019-10-28 11:58:20 -0700179 count++;
180 *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
181 }
182
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700183 return pos;
184}
185
186
187u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
188{
189 u8 *pos = eid;
190 int i, num, count;
Hai Shalomfdcde762020-04-02 11:19:20 -0700191 int h2e_required;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700192
Sunil Ravi77d572f2023-01-17 23:58:31 +0000193 hapd->conf->xrates_supported = false;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700194 if (hapd->iface->current_rates == NULL)
195 return eid;
196
197 num = hapd->iface->num_rates;
198 if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
199 num++;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800200 if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
201 num++;
Sunil Ravi77d572f2023-01-17 23:58:31 +0000202#ifdef CONFIG_IEEE80211AX
203 if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
204 num++;
205#endif /* CONFIG_IEEE80211AX */
206 h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
Hai Shalomfdcde762020-04-02 11:19:20 -0700207 hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
Sunil Ravi77d572f2023-01-17 23:58:31 +0000208 hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
Hai Shalomfdcde762020-04-02 11:19:20 -0700209 wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
210 if (h2e_required)
Hai Shalomc3565922019-10-28 11:58:20 -0700211 num++;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700212 if (num <= 8)
213 return eid;
214 num -= 8;
215
216 *pos++ = WLAN_EID_EXT_SUPP_RATES;
217 *pos++ = num;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700218 for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8;
219 i++) {
220 count++;
221 if (count <= 8)
222 continue; /* already in SuppRates IE */
223 *pos = hapd->iface->current_rates[i].rate / 5;
224 if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
225 *pos |= 0x80;
226 pos++;
227 }
228
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800229 if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) {
230 count++;
231 if (count > 8)
232 *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
233 }
234
235 if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) {
236 count++;
237 if (count > 8)
238 *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
239 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700240
Sunil Ravi77d572f2023-01-17 23:58:31 +0000241#ifdef CONFIG_IEEE80211AX
242 if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) {
243 count++;
244 if (count > 8)
245 *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
246 }
247#endif /* CONFIG_IEEE80211AX */
248
Hai Shalomfdcde762020-04-02 11:19:20 -0700249 if (h2e_required) {
Hai Shalomc3565922019-10-28 11:58:20 -0700250 count++;
251 if (count > 8)
252 *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
253 }
254
Sunil Ravi77d572f2023-01-17 23:58:31 +0000255 hapd->conf->xrates_supported = true;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700256 return pos;
257}
258
259
Hai Shalomfdcde762020-04-02 11:19:20 -0700260u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
261 size_t len)
262{
263 size_t i;
264
265 for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
266 if (hapd->conf->radio_measurements[i])
267 break;
268 }
269
270 if (i == RRM_CAPABILITIES_IE_LEN || len < 2 + RRM_CAPABILITIES_IE_LEN)
271 return eid;
272
273 *eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
274 *eid++ = RRM_CAPABILITIES_IE_LEN;
275 os_memcpy(eid, hapd->conf->radio_measurements, RRM_CAPABILITIES_IE_LEN);
276
277 return eid + RRM_CAPABILITIES_IE_LEN;
278}
279
280
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -0700281u16 hostapd_own_capab_info(struct hostapd_data *hapd)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700282{
283 int capab = WLAN_CAPABILITY_ESS;
Hai Shalomfdcde762020-04-02 11:19:20 -0700284 int privacy = 0;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800285 int dfs;
Dmitry Shmidt849734c2016-05-27 09:59:01 -0700286 int i;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800287
288 /* Check if any of configured channels require DFS */
289 dfs = hostapd_is_dfs_required(hapd->iface);
290 if (dfs < 0) {
291 wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
292 dfs);
293 dfs = 0;
294 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700295
296 if (hapd->iface->num_sta_no_short_preamble == 0 &&
297 hapd->iconf->preamble == SHORT_PREAMBLE)
298 capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
299
Hai Shalomfdcde762020-04-02 11:19:20 -0700300#ifdef CONFIG_WEP
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700301 privacy = hapd->conf->ssid.wep.keys_set;
302
303 if (hapd->conf->ieee802_1x &&
304 (hapd->conf->default_wep_key_len ||
305 hapd->conf->individual_wep_key_len))
306 privacy = 1;
Hai Shalomfdcde762020-04-02 11:19:20 -0700307#endif /* CONFIG_WEP */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700308
309 if (hapd->conf->wpa)
310 privacy = 1;
311
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800312#ifdef CONFIG_HS20
313 if (hapd->conf->osen)
314 privacy = 1;
315#endif /* CONFIG_HS20 */
316
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700317 if (privacy)
318 capab |= WLAN_CAPABILITY_PRIVACY;
319
320 if (hapd->iface->current_mode &&
321 hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
322 hapd->iface->num_sta_no_short_slot_time == 0)
323 capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
324
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800325 /*
326 * Currently, Spectrum Management capability bit is set when directly
327 * requested in configuration by spectrum_mgmt_required or when AP is
328 * running on DFS channel.
329 * TODO: Also consider driver support for TPC to set Spectrum Mgmt bit
330 */
331 if (hapd->iface->current_mode &&
332 hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
333 (hapd->iconf->spectrum_mgmt_required || dfs))
334 capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
335
Dmitry Shmidt849734c2016-05-27 09:59:01 -0700336 for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
337 if (hapd->conf->radio_measurements[i]) {
338 capab |= IEEE80211_CAP_RRM;
339 break;
340 }
341 }
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800342
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700343 return capab;
344}
345
346
Hai Shalomfdcde762020-04-02 11:19:20 -0700347#ifdef CONFIG_WEP
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800348#ifndef CONFIG_NO_RC4
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700349static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
350 u16 auth_transaction, const u8 *challenge,
351 int iswep)
352{
353 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
354 HOSTAPD_LEVEL_DEBUG,
355 "authentication (shared key, transaction %d)",
356 auth_transaction);
357
358 if (auth_transaction == 1) {
359 if (!sta->challenge) {
360 /* Generate a pseudo-random challenge */
361 u8 key[8];
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800362
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700363 sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN);
364 if (sta->challenge == NULL)
365 return WLAN_STATUS_UNSPECIFIED_FAILURE;
366
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800367 if (os_get_random(key, sizeof(key)) < 0) {
368 os_free(sta->challenge);
369 sta->challenge = NULL;
370 return WLAN_STATUS_UNSPECIFIED_FAILURE;
371 }
372
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700373 rc4_skip(key, sizeof(key), 0,
374 sta->challenge, WLAN_AUTH_CHALLENGE_LEN);
375 }
376 return 0;
377 }
378
379 if (auth_transaction != 3)
380 return WLAN_STATUS_UNSPECIFIED_FAILURE;
381
382 /* Transaction 3 */
383 if (!iswep || !sta->challenge || !challenge ||
Dmitry Shmidtc2817022014-07-02 10:32:10 -0700384 os_memcmp_const(sta->challenge, challenge,
385 WLAN_AUTH_CHALLENGE_LEN)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700386 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
387 HOSTAPD_LEVEL_INFO,
388 "shared key authentication - invalid "
389 "challenge-response");
390 return WLAN_STATUS_CHALLENGE_FAIL;
391 }
392
393 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
394 HOSTAPD_LEVEL_DEBUG,
395 "authentication OK (shared key)");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700396 sta->flags |= WLAN_STA_AUTH;
397 wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700398 os_free(sta->challenge);
399 sta->challenge = NULL;
400
401 return 0;
402}
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800403#endif /* CONFIG_NO_RC4 */
Hai Shalomfdcde762020-04-02 11:19:20 -0700404#endif /* CONFIG_WEP */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700405
406
Hai Shalomfdcde762020-04-02 11:19:20 -0700407static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
Sunil Ravi7f769292024-07-23 22:21:32 +0000408 const u8 *dst,
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800409 u16 auth_alg, u16 auth_transaction, u16 resp,
Roshan Pius3a1667e2018-07-03 15:17:14 -0700410 const u8 *ies, size_t ies_len, const char *dbg)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700411{
412 struct ieee80211_mgmt *reply;
413 u8 *buf;
414 size_t rlen;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800415 int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000416 const u8 *sa = hapd->own_addr;
417 struct wpabuf *ml_resp = NULL;
418
419#ifdef CONFIG_IEEE80211BE
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000420 if (ap_sta_is_mld(hapd, sta)) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000421 ml_resp = hostapd_ml_auth_resp(hapd);
422 if (!ml_resp)
423 return -1;
424 }
425#endif /* CONFIG_IEEE80211BE */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700426
427 rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000428 if (ml_resp)
429 rlen += wpabuf_len(ml_resp);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700430 buf = os_zalloc(rlen);
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000431 if (!buf) {
432 wpabuf_free(ml_resp);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800433 return -1;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000434 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700435
436 reply = (struct ieee80211_mgmt *) buf;
437 reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
438 WLAN_FC_STYPE_AUTH);
439 os_memcpy(reply->da, dst, ETH_ALEN);
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000440 os_memcpy(reply->sa, sa, ETH_ALEN);
Sunil Ravi7f769292024-07-23 22:21:32 +0000441 os_memcpy(reply->bssid, sa, ETH_ALEN);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700442
443 reply->u.auth.auth_alg = host_to_le16(auth_alg);
444 reply->u.auth.auth_transaction = host_to_le16(auth_transaction);
445 reply->u.auth.status_code = host_to_le16(resp);
446
447 if (ies && ies_len)
448 os_memcpy(reply->u.auth.variable, ies, ies_len);
449
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000450#ifdef CONFIG_IEEE80211BE
451 if (ml_resp)
452 os_memcpy(reply->u.auth.variable + ies_len,
453 wpabuf_head(ml_resp), wpabuf_len(ml_resp));
454
455 wpabuf_free(ml_resp);
456#endif /* CONFIG_IEEE80211BE */
457
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700458 wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR
Roshan Pius3a1667e2018-07-03 15:17:14 -0700459 " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)",
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700460 MAC2STR(dst), auth_alg, auth_transaction,
Roshan Pius3a1667e2018-07-03 15:17:14 -0700461 resp, (unsigned long) ies_len, dbg);
Hai Shalomfdcde762020-04-02 11:19:20 -0700462#ifdef CONFIG_TESTING_OPTIONS
463#ifdef CONFIG_SAE
464 if (hapd->conf->sae_confirm_immediate == 2 &&
465 auth_alg == WLAN_AUTH_SAE) {
466 if (auth_transaction == 1 && sta &&
467 (resp == WLAN_STATUS_SUCCESS ||
Hai Shalom899fcc72020-10-19 14:38:18 -0700468 resp == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
469 resp == WLAN_STATUS_SAE_PK)) {
Hai Shalomfdcde762020-04-02 11:19:20 -0700470 wpa_printf(MSG_DEBUG,
471 "TESTING: Postpone SAE Commit transmission until Confirm is ready");
472 os_free(sta->sae_postponed_commit);
473 sta->sae_postponed_commit = buf;
474 sta->sae_postponed_commit_len = rlen;
475 return WLAN_STATUS_SUCCESS;
476 }
477
478 if (auth_transaction == 2 && sta && sta->sae_postponed_commit) {
479 wpa_printf(MSG_DEBUG,
480 "TESTING: Send postponed SAE Commit first, immediately followed by SAE Confirm");
481 if (hostapd_drv_send_mlme(hapd,
482 sta->sae_postponed_commit,
483 sta->sae_postponed_commit_len,
484 0, NULL, 0, 0) < 0)
485 wpa_printf(MSG_INFO, "send_auth_reply: send failed");
486 os_free(sta->sae_postponed_commit);
487 sta->sae_postponed_commit = NULL;
488 sta->sae_postponed_commit_len = 0;
489 }
490 }
491#endif /* CONFIG_SAE */
492#endif /* CONFIG_TESTING_OPTIONS */
493 if (hostapd_drv_send_mlme(hapd, reply, rlen, 0, NULL, 0, 0) < 0)
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800494 wpa_printf(MSG_INFO, "send_auth_reply: send failed");
495 else
496 reply_res = WLAN_STATUS_SUCCESS;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700497
498 os_free(buf);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800499
500 return reply_res;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700501}
502
503
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -0800504#ifdef CONFIG_IEEE80211R_AP
Sunil Ravi7f769292024-07-23 22:21:32 +0000505static void handle_auth_ft_finish(void *ctx, const u8 *dst,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700506 u16 auth_transaction, u16 status,
507 const u8 *ies, size_t ies_len)
508{
509 struct hostapd_data *hapd = ctx;
510 struct sta_info *sta;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800511 int reply_res;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700512
Sunil Ravi7f769292024-07-23 22:21:32 +0000513 reply_res = send_auth_reply(hapd, NULL, dst, WLAN_AUTH_FT,
Roshan Pius3a1667e2018-07-03 15:17:14 -0700514 auth_transaction, status, ies, ies_len,
515 "auth-ft-finish");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700516
517 sta = ap_get_sta(hapd, dst);
518 if (sta == NULL)
519 return;
520
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800521 if (sta->added_unassoc && (reply_res != WLAN_STATUS_SUCCESS ||
522 status != WLAN_STATUS_SUCCESS)) {
523 hostapd_drv_sta_remove(hapd, sta->addr);
524 sta->added_unassoc = 0;
525 return;
526 }
527
528 if (status != WLAN_STATUS_SUCCESS)
529 return;
530
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700531 hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
532 HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
533 sta->flags |= WLAN_STA_AUTH;
534 mlme_authenticate_indication(hapd, sta);
535}
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -0800536#endif /* CONFIG_IEEE80211R_AP */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700537
538
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800539#ifdef CONFIG_SAE
540
Roshan Pius3a1667e2018-07-03 15:17:14 -0700541static void sae_set_state(struct sta_info *sta, enum sae_state state,
542 const char *reason)
543{
544 wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)",
545 sae_state_txt(sta->sae->state), sae_state_txt(state),
546 MAC2STR(sta->addr), reason);
547 sta->sae->state = state;
548}
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800549
550
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000551const char * sae_get_password(struct hostapd_data *hapd,
552 struct sta_info *sta,
553 const char *rx_id,
554 struct sae_password_entry **pw_entry,
555 struct sae_pt **s_pt,
556 const struct sae_pk **s_pk)
Hai Shalom60840252021-02-19 19:02:11 -0800557{
558 const char *password = NULL;
559 struct sae_password_entry *pw;
560 struct sae_pt *pt = NULL;
561 const struct sae_pk *pk = NULL;
Sunil Ravia04bd252022-05-02 22:54:18 -0700562 struct hostapd_sta_wpa_psk_short *psk = NULL;
Hai Shalom60840252021-02-19 19:02:11 -0800563
564 for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
565 if (!is_broadcast_ether_addr(pw->peer_addr) &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000566 (!sta ||
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000567 !ether_addr_equal(pw->peer_addr, sta->addr)))
Hai Shalom60840252021-02-19 19:02:11 -0800568 continue;
569 if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
570 continue;
571 if (rx_id && pw->identifier &&
572 os_strcmp(rx_id, pw->identifier) != 0)
573 continue;
574 password = pw->password;
575 pt = pw->pt;
576 if (!(hapd->conf->mesh & MESH_ENABLED))
577 pk = pw->pk;
578 break;
579 }
Sunil Ravi79e6c4f2025-01-04 00:47:06 +0000580 if (!password && !rx_id) {
Hai Shalom60840252021-02-19 19:02:11 -0800581 password = hapd->conf->ssid.wpa_passphrase;
582 pt = hapd->conf->ssid.pt;
583 }
584
Sunil Ravi79e6c4f2025-01-04 00:47:06 +0000585 if (!password && sta && !rx_id) {
Sunil Ravia04bd252022-05-02 22:54:18 -0700586 for (psk = sta->psk; psk; psk = psk->next) {
587 if (psk->is_passphrase) {
588 password = psk->passphrase;
589 break;
590 }
591 }
592 }
593
Hai Shalom60840252021-02-19 19:02:11 -0800594 if (pw_entry)
595 *pw_entry = pw;
596 if (s_pt)
597 *s_pt = pt;
598 if (s_pk)
599 *s_pk = pk;
600
601 return password;
602}
603
604
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800605static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
Hai Shalomc3565922019-10-28 11:58:20 -0700606 struct sta_info *sta, int update,
607 int status_code)
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800608{
609 struct wpabuf *buf;
Roshan Pius3a1667e2018-07-03 15:17:14 -0700610 const char *password = NULL;
611 struct sae_password_entry *pw;
612 const char *rx_id = NULL;
Hai Shalomc3565922019-10-28 11:58:20 -0700613 int use_pt = 0;
614 struct sae_pt *pt = NULL;
Hai Shalom899fcc72020-10-19 14:38:18 -0700615 const struct sae_pk *pk = NULL;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000616 const u8 *own_addr = hapd->own_addr;
617
618#ifdef CONFIG_IEEE80211BE
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000619 if (ap_sta_is_mld(hapd, sta))
Sunil Ravi99c035e2024-07-12 01:42:03 +0000620 own_addr = hapd->mld->mld_addr;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000621#endif /* CONFIG_IEEE80211BE */
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800622
Hai Shalomc3565922019-10-28 11:58:20 -0700623 if (sta->sae->tmp) {
Sunil Ravi79e6c4f2025-01-04 00:47:06 +0000624 rx_id = sta->sae->tmp->parsed_pw_id ?
625 sta->sae->tmp->parsed_pw_id : sta->sae->tmp->pw_id;
Hai Shalom899fcc72020-10-19 14:38:18 -0700626 use_pt = sta->sae->h2e;
627#ifdef CONFIG_SAE_PK
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000628 os_memcpy(sta->sae->tmp->own_addr, own_addr, ETH_ALEN);
Hai Shalom899fcc72020-10-19 14:38:18 -0700629 os_memcpy(sta->sae->tmp->peer_addr, sta->addr, ETH_ALEN);
630#endif /* CONFIG_SAE_PK */
Hai Shalomc3565922019-10-28 11:58:20 -0700631 }
632
Sunil Ravi77d572f2023-01-17 23:58:31 +0000633 if (rx_id && hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
Hai Shalomfdcde762020-04-02 11:19:20 -0700634 use_pt = 1;
635 else if (status_code == WLAN_STATUS_SUCCESS)
Hai Shalomc3565922019-10-28 11:58:20 -0700636 use_pt = 0;
Hai Shalom899fcc72020-10-19 14:38:18 -0700637 else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
638 status_code == WLAN_STATUS_SAE_PK)
Hai Shalomc3565922019-10-28 11:58:20 -0700639 use_pt = 1;
Roshan Pius3a1667e2018-07-03 15:17:14 -0700640
Hai Shalom60840252021-02-19 19:02:11 -0800641 password = sae_get_password(hapd, sta, rx_id, &pw, &pt, &pk);
Hai Shalomc3565922019-10-28 11:58:20 -0700642 if (!password || (use_pt && !pt)) {
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800643 wpa_printf(MSG_DEBUG, "SAE: No password available");
644 return NULL;
645 }
646
Hai Shalomc3565922019-10-28 11:58:20 -0700647 if (update && use_pt &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000648 sae_prepare_commit_pt(sta->sae, pt, own_addr, sta->addr,
Hai Shalom899fcc72020-10-19 14:38:18 -0700649 NULL, pk) < 0)
Hai Shalomc3565922019-10-28 11:58:20 -0700650 return NULL;
651
652 if (update && !use_pt &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000653 sae_prepare_commit(own_addr, sta->addr,
Hai Shaloma20dcd72022-02-04 13:43:00 -0800654 (u8 *) password, os_strlen(password),
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800655 sta->sae) < 0) {
656 wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
657 return NULL;
658 }
659
Hai Shalom021b0b52019-04-10 11:17:58 -0700660 if (pw && pw->vlan_id) {
661 if (!sta->sae->tmp) {
662 wpa_printf(MSG_INFO,
663 "SAE: No temporary data allocated - cannot store VLAN ID");
664 return NULL;
665 }
666 sta->sae->tmp->vlan_id = pw->vlan_id;
667 }
668
Roshan Pius3a1667e2018-07-03 15:17:14 -0700669 buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
670 (rx_id ? 3 + os_strlen(rx_id) : 0));
Hai Shalomfdcde762020-04-02 11:19:20 -0700671 if (buf &&
672 sae_write_commit(sta->sae, buf, sta->sae->tmp ?
673 sta->sae->tmp->anti_clogging_token : NULL,
674 rx_id) < 0) {
675 wpabuf_free(buf);
676 buf = NULL;
677 }
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800678
679 return buf;
680}
681
682
683static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd,
684 struct sta_info *sta)
685{
686 struct wpabuf *buf;
687
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800688 buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800689 if (buf == NULL)
690 return NULL;
691
Hai Shalom899fcc72020-10-19 14:38:18 -0700692#ifdef CONFIG_SAE_PK
693#ifdef CONFIG_TESTING_OPTIONS
694 if (sta->sae->tmp)
695 sta->sae->tmp->omit_pk_elem = hapd->conf->sae_pk_omit;
696#endif /* CONFIG_TESTING_OPTIONS */
697#endif /* CONFIG_SAE_PK */
698
699 if (sae_write_confirm(sta->sae, buf) < 0) {
700 wpabuf_free(buf);
701 return NULL;
702 }
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800703
704 return buf;
705}
706
707
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800708static int auth_sae_send_commit(struct hostapd_data *hapd,
709 struct sta_info *sta,
Sunil Ravi7f769292024-07-23 22:21:32 +0000710 int update, int status_code)
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800711{
712 struct wpabuf *data;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800713 int reply_res;
Hai Shalomc3565922019-10-28 11:58:20 -0700714 u16 status;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800715
Hai Shalomc3565922019-10-28 11:58:20 -0700716 data = auth_build_sae_commit(hapd, sta, update, status_code);
Sunil Ravi79e6c4f2025-01-04 00:47:06 +0000717 if (!data && sta->sae->tmp &&
718 (sta->sae->tmp->pw_id || sta->sae->tmp->parsed_pw_id))
Roshan Pius3a1667e2018-07-03 15:17:14 -0700719 return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800720 if (data == NULL)
721 return WLAN_STATUS_UNSPECIFIED_FAILURE;
722
Hai Shalom899fcc72020-10-19 14:38:18 -0700723 if (sta->sae->tmp && sta->sae->pk)
724 status = WLAN_STATUS_SAE_PK;
725 else if (sta->sae->tmp && sta->sae->h2e)
726 status = WLAN_STATUS_SAE_HASH_TO_ELEMENT;
727 else
728 status = WLAN_STATUS_SUCCESS;
729#ifdef CONFIG_TESTING_OPTIONS
730 if (hapd->conf->sae_commit_status >= 0 &&
731 hapd->conf->sae_commit_status != status) {
732 wpa_printf(MSG_INFO,
733 "TESTING: Override SAE commit status code %u --> %d",
734 status, hapd->conf->sae_commit_status);
735 status = hapd->conf->sae_commit_status;
736 }
737#endif /* CONFIG_TESTING_OPTIONS */
Sunil Ravi7f769292024-07-23 22:21:32 +0000738 reply_res = send_auth_reply(hapd, sta, sta->addr,
Hai Shalomfdcde762020-04-02 11:19:20 -0700739 WLAN_AUTH_SAE, 1,
Hai Shalomc3565922019-10-28 11:58:20 -0700740 status, wpabuf_head(data),
Roshan Pius3a1667e2018-07-03 15:17:14 -0700741 wpabuf_len(data), "sae-send-commit");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800742
743 wpabuf_free(data);
744
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800745 return reply_res;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800746}
747
748
749static int auth_sae_send_confirm(struct hostapd_data *hapd,
Sunil Ravi7f769292024-07-23 22:21:32 +0000750 struct sta_info *sta)
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800751{
752 struct wpabuf *data;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800753 int reply_res;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800754
755 data = auth_build_sae_confirm(hapd, sta);
756 if (data == NULL)
757 return WLAN_STATUS_UNSPECIFIED_FAILURE;
758
Sunil Ravi7f769292024-07-23 22:21:32 +0000759 reply_res = send_auth_reply(hapd, sta, sta->addr,
Hai Shalomfdcde762020-04-02 11:19:20 -0700760 WLAN_AUTH_SAE, 2,
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800761 WLAN_STATUS_SUCCESS, wpabuf_head(data),
Roshan Pius3a1667e2018-07-03 15:17:14 -0700762 wpabuf_len(data), "sae-send-confirm");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800763
764 wpabuf_free(data);
765
Dmitry Shmidt57c2d392016-02-23 13:40:19 -0800766 return reply_res;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800767}
768
Hai Shaloma20dcd72022-02-04 13:43:00 -0800769#endif /* CONFIG_SAE */
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800770
Hai Shaloma20dcd72022-02-04 13:43:00 -0800771
772#if defined(CONFIG_SAE) || defined(CONFIG_PASN)
773
774static int use_anti_clogging(struct hostapd_data *hapd)
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800775{
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800776 struct sta_info *sta;
777 unsigned int open = 0;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800778
Hai Shaloma20dcd72022-02-04 13:43:00 -0800779 if (hapd->conf->anti_clogging_threshold == 0)
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800780 return 1;
781
782 for (sta = hapd->sta_list; sta; sta = sta->next) {
Hai Shaloma20dcd72022-02-04 13:43:00 -0800783#ifdef CONFIG_SAE
784 if (sta->sae &&
785 (sta->sae->state == SAE_COMMITTED ||
786 sta->sae->state == SAE_CONFIRMED))
787 open++;
788#endif /* CONFIG_SAE */
789#ifdef CONFIG_PASN
790 if (sta->pasn && sta->pasn->ecdh)
791 open++;
792#endif /* CONFIG_PASN */
793 if (open >= hapd->conf->anti_clogging_threshold)
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800794 return 1;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800795 }
796
Hai Shaloma20dcd72022-02-04 13:43:00 -0800797#ifdef CONFIG_SAE
Hai Shalom021b0b52019-04-10 11:17:58 -0700798 /* In addition to already existing open SAE sessions, check whether
799 * there are enough pending commit messages in the processing queue to
800 * potentially result in too many open sessions. */
801 if (open + dl_list_len(&hapd->sae_commit_queue) >=
Hai Shaloma20dcd72022-02-04 13:43:00 -0800802 hapd->conf->anti_clogging_threshold)
Hai Shalom021b0b52019-04-10 11:17:58 -0700803 return 1;
Hai Shaloma20dcd72022-02-04 13:43:00 -0800804#endif /* CONFIG_SAE */
Hai Shalom021b0b52019-04-10 11:17:58 -0700805
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800806 return 0;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800807}
808
Hai Shaloma20dcd72022-02-04 13:43:00 -0800809#endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
810
811
812#ifdef CONFIG_SAE
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800813
Roshan Pius3a1667e2018-07-03 15:17:14 -0700814static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta)
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800815{
Roshan Pius3a1667e2018-07-03 15:17:14 -0700816 if (sta->sae->sync > hapd->conf->sae_sync) {
817 sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync");
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800818 sta->sae->sync = 0;
Sunil Ravi7f769292024-07-23 22:21:32 +0000819 if (sta->sae->tmp) {
820 /* Disable this SAE instance for 10 seconds to avoid
821 * unnecessary flood of multiple SAE commits in
822 * unexpected mesh cases. */
823 if (os_get_reltime(&sta->sae->tmp->disabled_until) == 0)
824 sta->sae->tmp->disabled_until.sec += 10;
825 }
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800826 return -1;
827 }
828 return 0;
829}
830
831
Sunil Ravi7f769292024-07-23 22:21:32 +0000832static bool sae_proto_instance_disabled(struct sta_info *sta)
833{
834 struct sae_temporary_data *tmp;
835
836 if (!sta->sae)
837 return false;
838 tmp = sta->sae->tmp;
839 if (!tmp)
840 return false;
841
842 if (os_reltime_initialized(&tmp->disabled_until)) {
843 struct os_reltime now;
844
845 os_get_reltime(&now);
846 if (os_reltime_before(&now, &tmp->disabled_until))
847 return true;
848 }
849
850 return false;
851}
852
853
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800854static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data)
855{
856 struct hostapd_data *hapd = eloop_ctx;
857 struct sta_info *sta = eloop_data;
858 int ret;
859
Roshan Pius3a1667e2018-07-03 15:17:14 -0700860 if (sae_check_big_sync(hapd, sta))
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800861 return;
862 sta->sae->sync++;
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -0700863 wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR
Roshan Pius3a1667e2018-07-03 15:17:14 -0700864 " (sync=%d state=%s)",
865 MAC2STR(sta->addr), sta->sae->sync,
866 sae_state_txt(sta->sae->state));
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800867
868 switch (sta->sae->state) {
869 case SAE_COMMITTED:
Sunil Ravi7f769292024-07-23 22:21:32 +0000870 ret = auth_sae_send_commit(hapd, sta, 0, -1);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800871 eloop_register_timeout(0,
872 hapd->dot11RSNASAERetransPeriod * 1000,
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800873 auth_sae_retransmit_timer, hapd, sta);
874 break;
875 case SAE_CONFIRMED:
Sunil Ravi7f769292024-07-23 22:21:32 +0000876 ret = auth_sae_send_confirm(hapd, sta);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800877 eloop_register_timeout(0,
878 hapd->dot11RSNASAERetransPeriod * 1000,
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800879 auth_sae_retransmit_timer, hapd, sta);
880 break;
881 default:
882 ret = -1;
883 break;
884 }
885
886 if (ret != WLAN_STATUS_SUCCESS)
887 wpa_printf(MSG_INFO, "SAE: Failed to retransmit: ret=%d", ret);
888}
889
890
891void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta)
892{
893 eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
894}
895
896
897static void sae_set_retransmit_timer(struct hostapd_data *hapd,
898 struct sta_info *sta)
899{
900 if (!(hapd->conf->mesh & MESH_ENABLED))
901 return;
902
903 eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800904 eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000,
Dmitry Shmidtff787d52015-01-12 13:01:47 -0800905 auth_sae_retransmit_timer, hapd, sta);
906}
907
908
Hai Shalom5f92bc92019-04-18 11:54:11 -0700909static void sae_sme_send_external_auth_status(struct hostapd_data *hapd,
910 struct sta_info *sta, u16 status)
911{
912 struct external_auth params;
913
914 os_memset(&params, 0, sizeof(params));
915 params.status = status;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000916
917#ifdef CONFIG_IEEE80211BE
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000918 if (ap_sta_is_mld(hapd, sta))
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000919 params.bssid =
920 sta->mld_info.links[sta->mld_assoc_link_id].peer_addr;
921#endif /* CONFIG_IEEE80211BE */
922 if (!params.bssid)
923 params.bssid = sta->addr;
924
Hai Shalom81f62d82019-07-22 12:10:00 -0700925 if (status == WLAN_STATUS_SUCCESS && sta->sae &&
926 !hapd->conf->disable_pmksa_caching)
Hai Shalom5f92bc92019-04-18 11:54:11 -0700927 params.pmkid = sta->sae->pmkid;
928
929 hostapd_drv_send_external_auth_status(hapd, &params);
930}
931
932
Dmitry Shmidte4663042016-04-04 10:07:49 -0700933void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
934{
Hai Shalom021b0b52019-04-10 11:17:58 -0700935#ifndef CONFIG_NO_VLAN
936 struct vlan_description vlan_desc;
937
938 if (sta->sae->tmp && sta->sae->tmp->vlan_id > 0) {
939 wpa_printf(MSG_DEBUG, "SAE: Assign STA " MACSTR
940 " to VLAN ID %d",
941 MAC2STR(sta->addr), sta->sae->tmp->vlan_id);
942
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000943 if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
944 os_memset(&vlan_desc, 0, sizeof(vlan_desc));
945 vlan_desc.notempty = 1;
946 vlan_desc.untagged = sta->sae->tmp->vlan_id;
947 if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
948 wpa_printf(MSG_INFO,
949 "Invalid VLAN ID %d in sae_password",
950 sta->sae->tmp->vlan_id);
951 return;
952 }
Hai Shalom021b0b52019-04-10 11:17:58 -0700953
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000954 if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 ||
955 ap_sta_bind_vlan(hapd, sta) < 0) {
956 wpa_printf(MSG_INFO,
957 "Failed to assign VLAN ID %d from sae_password to "
958 MACSTR, sta->sae->tmp->vlan_id,
959 MAC2STR(sta->addr));
960 return;
961 }
962 } else {
963 sta->vlan_id = sta->sae->tmp->vlan_id;
Hai Shalom021b0b52019-04-10 11:17:58 -0700964 }
965 }
966#endif /* CONFIG_NO_VLAN */
967
Dmitry Shmidte4663042016-04-04 10:07:49 -0700968 sta->flags |= WLAN_STA_AUTH;
969 sta->auth_alg = WLAN_AUTH_SAE;
970 mlme_authenticate_indication(hapd, sta);
971 wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
Roshan Pius3a1667e2018-07-03 15:17:14 -0700972 sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm");
Hai Shalomfdcde762020-04-02 11:19:20 -0700973 crypto_bignum_deinit(sta->sae->peer_commit_scalar_accepted, 0);
974 sta->sae->peer_commit_scalar_accepted = sta->sae->peer_commit_scalar;
975 sta->sae->peer_commit_scalar = NULL;
Dmitry Shmidte4663042016-04-04 10:07:49 -0700976 wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
Sunil Ravi89eba102022-09-13 21:04:37 -0700977 sta->sae->pmk, sta->sae->pmk_len,
978 sta->sae->pmkid, sta->sae->akmp);
Hai Shalom5f92bc92019-04-18 11:54:11 -0700979 sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
Dmitry Shmidte4663042016-04-04 10:07:49 -0700980}
981
982
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800983static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
Sunil Ravi7f769292024-07-23 22:21:32 +0000984 u16 auth_transaction, u16 status_code,
Hai Shalomc3565922019-10-28 11:58:20 -0700985 int allow_reuse, int *sta_removed)
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800986{
987 int ret;
988
Hai Shalom5f92bc92019-04-18 11:54:11 -0700989 *sta_removed = 0;
990
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800991 if (auth_transaction != 1 && auth_transaction != 2)
992 return WLAN_STATUS_UNSPECIFIED_FAILURE;
993
Roshan Pius3a1667e2018-07-03 15:17:14 -0700994 wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u",
995 MAC2STR(sta->addr), sae_state_txt(sta->sae->state),
996 auth_transaction);
Sunil Ravi7f769292024-07-23 22:21:32 +0000997
998 if (auth_transaction == 1 && sae_proto_instance_disabled(sta)) {
999 wpa_printf(MSG_DEBUG,
1000 "SAE: Protocol instance temporarily disabled - discard received SAE commit");
1001 return WLAN_STATUS_SUCCESS;
1002 }
1003
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001004 switch (sta->sae->state) {
1005 case SAE_NOTHING:
1006 if (auth_transaction == 1) {
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001007 struct sae_temporary_data *tmp = sta->sae->tmp;
1008
1009 if (tmp) {
Hai Shalom899fcc72020-10-19 14:38:18 -07001010 sta->sae->h2e =
1011 (status_code ==
1012 WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
1013 status_code == WLAN_STATUS_SAE_PK);
1014 sta->sae->pk =
1015 status_code == WLAN_STATUS_SAE_PK;
1016 }
Sunil Ravi7f769292024-07-23 22:21:32 +00001017 ret = auth_sae_send_commit(hapd, sta,
Hai Shalomc3565922019-10-28 11:58:20 -07001018 !allow_reuse, status_code);
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001019 if (ret == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER)
1020 wpa_msg(hapd->msg_ctx, MSG_INFO,
1021 WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER
1022 MACSTR, MAC2STR(sta->addr));
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001023 if (ret)
1024 return ret;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001025
1026 if (tmp && tmp->parsed_pw_id && !tmp->pw_id) {
1027 tmp->pw_id = tmp->parsed_pw_id;
1028 tmp->parsed_pw_id = NULL;
1029 wpa_printf(MSG_DEBUG,
1030 "SAE: Known Password Identifier bound to this STA: '%s'",
1031 tmp->pw_id);
1032 }
1033
Roshan Pius3a1667e2018-07-03 15:17:14 -07001034 sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001035
1036 if (sae_process_commit(sta->sae) < 0)
1037 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1038
1039 /*
Hai Shalomc3565922019-10-28 11:58:20 -07001040 * In mesh case, both Commit and Confirm are sent
1041 * immediately. In infrastructure BSS, by default, only
1042 * a single Authentication frame (Commit) is expected
1043 * from the AP here and the second one (Confirm) will
1044 * be sent once the STA has sent its second
1045 * Authentication frame (Confirm). This behavior can be
1046 * overridden with explicit configuration so that the
1047 * infrastructure BSS case sends both frames together.
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001048 */
Hai Shalomc3565922019-10-28 11:58:20 -07001049 if ((hapd->conf->mesh & MESH_ENABLED) ||
1050 hapd->conf->sae_confirm_immediate) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001051 /*
1052 * Send both Commit and Confirm immediately
1053 * based on SAE finite state machine
1054 * Nothing -> Confirm transition.
1055 */
Sunil Ravi7f769292024-07-23 22:21:32 +00001056 ret = auth_sae_send_confirm(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001057 if (ret)
1058 return ret;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001059 sae_set_state(sta, SAE_CONFIRMED,
1060 "Sent Confirm (mesh)");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001061 } else {
1062 /*
1063 * For infrastructure BSS, send only the Commit
1064 * message now to get alternating sequence of
1065 * Authentication frames between the AP and STA.
1066 * Confirm will be sent in
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001067 * Committed -> Confirmed/Accepted transition
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001068 * when receiving Confirm from STA.
1069 */
1070 }
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001071 sta->sae->sync = 0;
1072 sae_set_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001073 } else {
1074 hostapd_logger(hapd, sta->addr,
1075 HOSTAPD_MODULE_IEEE80211,
1076 HOSTAPD_LEVEL_DEBUG,
1077 "SAE confirm before commit");
1078 }
1079 break;
1080 case SAE_COMMITTED:
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001081 sae_clear_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001082 if (auth_transaction == 1) {
1083 if (sae_process_commit(sta->sae) < 0)
1084 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1085
Sunil Ravi7f769292024-07-23 22:21:32 +00001086 ret = auth_sae_send_confirm(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001087 if (ret)
1088 return ret;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001089 sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001090 sta->sae->sync = 0;
1091 sae_set_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001092 } else if (hapd->conf->mesh & MESH_ENABLED) {
1093 /*
1094 * In mesh case, follow SAE finite state machine and
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001095 * send Commit now, if sync count allows.
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001096 */
Roshan Pius3a1667e2018-07-03 15:17:14 -07001097 if (sae_check_big_sync(hapd, sta))
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001098 return WLAN_STATUS_SUCCESS;
1099 sta->sae->sync++;
1100
Sunil Ravi7f769292024-07-23 22:21:32 +00001101 ret = auth_sae_send_commit(hapd, sta, 0, status_code);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001102 if (ret)
1103 return ret;
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001104
1105 sae_set_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001106 } else {
1107 /*
1108 * For instructure BSS, send the postponed Confirm from
1109 * Nothing -> Confirmed transition that was reduced to
1110 * Nothing -> Committed above.
1111 */
Sunil Ravi7f769292024-07-23 22:21:32 +00001112 ret = auth_sae_send_confirm(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001113 if (ret)
1114 return ret;
1115
Roshan Pius3a1667e2018-07-03 15:17:14 -07001116 sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001117
1118 /*
1119 * Since this was triggered on Confirm RX, run another
1120 * step to get to Accepted without waiting for
1121 * additional events.
1122 */
Sunil Ravi7f769292024-07-23 22:21:32 +00001123 return sae_sm_step(hapd, sta, auth_transaction,
Hai Shalomc3565922019-10-28 11:58:20 -07001124 WLAN_STATUS_SUCCESS, 0, sta_removed);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001125 }
1126 break;
1127 case SAE_CONFIRMED:
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001128 sae_clear_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001129 if (auth_transaction == 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07001130 if (sae_check_big_sync(hapd, sta))
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001131 return WLAN_STATUS_SUCCESS;
1132 sta->sae->sync++;
1133
Sunil Ravi7f769292024-07-23 22:21:32 +00001134 ret = auth_sae_send_commit(hapd, sta, 1, status_code);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001135 if (ret)
1136 return ret;
1137
1138 if (sae_process_commit(sta->sae) < 0)
1139 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1140
Sunil Ravi7f769292024-07-23 22:21:32 +00001141 ret = auth_sae_send_confirm(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001142 if (ret)
1143 return ret;
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001144
1145 sae_set_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001146 } else {
Roshan Pius3a1667e2018-07-03 15:17:14 -07001147 sta->sae->send_confirm = 0xffff;
Dmitry Shmidte4663042016-04-04 10:07:49 -07001148 sae_accept_sta(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001149 }
1150 break;
1151 case SAE_ACCEPTED:
Roshan Pius3a1667e2018-07-03 15:17:14 -07001152 if (auth_transaction == 1 &&
1153 (hapd->conf->mesh & MESH_ENABLED)) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001154 wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR
1155 ") doing reauthentication",
1156 MAC2STR(sta->addr));
Dmitry Shmidte4663042016-04-04 10:07:49 -07001157 wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
Hai Shalom5f92bc92019-04-18 11:54:11 -07001158 ap_free_sta(hapd, sta);
1159 *sta_removed = 1;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001160 } else if (auth_transaction == 1) {
1161 wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
Sunil Ravi7f769292024-07-23 22:21:32 +00001162 ret = auth_sae_send_commit(hapd, sta, 1, status_code);
Roshan Pius3a1667e2018-07-03 15:17:14 -07001163 if (ret)
1164 return ret;
1165 sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
1166
1167 if (sae_process_commit(sta->sae) < 0)
1168 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1169 sta->sae->sync = 0;
1170 sae_set_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001171 } else {
Roshan Pius3a1667e2018-07-03 15:17:14 -07001172 if (sae_check_big_sync(hapd, sta))
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001173 return WLAN_STATUS_SUCCESS;
1174 sta->sae->sync++;
1175
Sunil Ravi7f769292024-07-23 22:21:32 +00001176 ret = auth_sae_send_confirm(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001177 sae_clear_temp_data(sta->sae);
1178 if (ret)
1179 return ret;
1180 }
1181 break;
1182 default:
1183 wpa_printf(MSG_ERROR, "SAE: invalid state %d",
1184 sta->sae->state);
1185 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1186 }
1187 return WLAN_STATUS_SUCCESS;
1188}
1189
1190
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001191static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta)
1192{
1193 struct sae_data *sae = sta->sae;
Sunil Ravic0f5d412024-09-11 22:12:49 +00001194 struct hostapd_bss_config *conf = hapd->conf;
1195 int i, *groups = conf->sae_groups;
1196 int default_groups[] = { 19, 0, 0 };
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001197
1198 if (sae->state != SAE_COMMITTED)
1199 return;
1200
1201 wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group);
1202
Sunil Ravic0f5d412024-09-11 22:12:49 +00001203 if (!groups) {
Hai Shalom021b0b52019-04-10 11:17:58 -07001204 groups = default_groups;
Sunil Ravic0f5d412024-09-11 22:12:49 +00001205 if (wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt |
1206 conf->rsn_override_key_mgmt |
1207 conf->rsn_override_key_mgmt_2))
1208 default_groups[1] = 20;
1209 }
1210
Hai Shalom021b0b52019-04-10 11:17:58 -07001211 for (i = 0; groups[i] > 0; i++) {
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001212 if (sae->group == groups[i])
1213 break;
1214 }
1215
Hai Shalom021b0b52019-04-10 11:17:58 -07001216 if (groups[i] <= 0) {
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001217 wpa_printf(MSG_DEBUG,
1218 "SAE: Previously selected group not found from the current configuration");
1219 return;
1220 }
1221
1222 for (;;) {
1223 i++;
1224 if (groups[i] <= 0) {
1225 wpa_printf(MSG_DEBUG,
1226 "SAE: No alternative group enabled");
1227 return;
1228 }
1229
1230 if (sae_set_group(sae, groups[i]) < 0)
1231 continue;
1232
1233 break;
1234 }
1235 wpa_printf(MSG_DEBUG, "SAE: Selected new group: %d", groups[i]);
1236}
1237
1238
Hai Shalomc3565922019-10-28 11:58:20 -07001239static int sae_status_success(struct hostapd_data *hapd, u16 status_code)
1240{
Sunil Ravi77d572f2023-01-17 23:58:31 +00001241 enum sae_pwe sae_pwe = hapd->conf->sae_pwe;
Hai Shalomfdcde762020-04-02 11:19:20 -07001242 int id_in_use;
Hai Shalom60840252021-02-19 19:02:11 -08001243 bool sae_pk = false;
Hai Shalomfdcde762020-04-02 11:19:20 -07001244
1245 id_in_use = hostapd_sae_pw_id_in_use(hapd->conf);
Sunil Ravi77d572f2023-01-17 23:58:31 +00001246 if (id_in_use == 2 && sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
1247 sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
1248 else if (id_in_use == 1 && sae_pwe == SAE_PWE_HUNT_AND_PECK)
1249 sae_pwe = SAE_PWE_BOTH;
Hai Shalom899fcc72020-10-19 14:38:18 -07001250#ifdef CONFIG_SAE_PK
Hai Shalom60840252021-02-19 19:02:11 -08001251 sae_pk = hostapd_sae_pk_in_use(hapd->conf);
Sunil Ravi77d572f2023-01-17 23:58:31 +00001252 if (sae_pwe == SAE_PWE_HUNT_AND_PECK && sae_pk)
1253 sae_pwe = SAE_PWE_BOTH;
Hai Shalom899fcc72020-10-19 14:38:18 -07001254#endif /* CONFIG_SAE_PK */
Sunil Ravi77d572f2023-01-17 23:58:31 +00001255 if (sae_pwe == SAE_PWE_HUNT_AND_PECK &&
Sunil Ravi89eba102022-09-13 21:04:37 -07001256 (hapd->conf->wpa_key_mgmt &
1257 (WPA_KEY_MGMT_SAE_EXT_KEY | WPA_KEY_MGMT_FT_SAE_EXT_KEY)))
Sunil Ravi77d572f2023-01-17 23:58:31 +00001258 sae_pwe = SAE_PWE_BOTH;
Hai Shalomfdcde762020-04-02 11:19:20 -07001259
Sunil Ravi77d572f2023-01-17 23:58:31 +00001260 return ((sae_pwe == SAE_PWE_HUNT_AND_PECK ||
1261 sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) &&
Hai Shalomc3565922019-10-28 11:58:20 -07001262 status_code == WLAN_STATUS_SUCCESS) ||
Sunil Ravi77d572f2023-01-17 23:58:31 +00001263 (sae_pwe == SAE_PWE_HASH_TO_ELEMENT &&
Hai Shalom899fcc72020-10-19 14:38:18 -07001264 (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
Hai Shalom60840252021-02-19 19:02:11 -08001265 (sae_pk && status_code == WLAN_STATUS_SAE_PK))) ||
Sunil Ravi77d572f2023-01-17 23:58:31 +00001266 (sae_pwe == SAE_PWE_BOTH &&
Hai Shalomc3565922019-10-28 11:58:20 -07001267 (status_code == WLAN_STATUS_SUCCESS ||
Hai Shalom899fcc72020-10-19 14:38:18 -07001268 status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
Hai Shalom60840252021-02-19 19:02:11 -08001269 (sae_pk && status_code == WLAN_STATUS_SAE_PK)));
Hai Shalomc3565922019-10-28 11:58:20 -07001270}
1271
1272
1273static int sae_is_group_enabled(struct hostapd_data *hapd, int group)
1274{
Sunil Ravic0f5d412024-09-11 22:12:49 +00001275 struct hostapd_bss_config *conf = hapd->conf;
1276 int *groups = conf->sae_groups;
1277 int default_groups[] = { 19, 0, 0 };
Hai Shalomc3565922019-10-28 11:58:20 -07001278 int i;
1279
Sunil Ravic0f5d412024-09-11 22:12:49 +00001280 if (!groups) {
Hai Shalomc3565922019-10-28 11:58:20 -07001281 groups = default_groups;
Sunil Ravic0f5d412024-09-11 22:12:49 +00001282 if (wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt |
1283 conf->rsn_override_key_mgmt |
1284 conf->rsn_override_key_mgmt_2))
1285 default_groups[1] = 20;
1286 }
Hai Shalomc3565922019-10-28 11:58:20 -07001287
1288 for (i = 0; groups[i] > 0; i++) {
1289 if (groups[i] == group)
1290 return 1;
1291 }
1292
1293 return 0;
1294}
1295
1296
1297static int check_sae_rejected_groups(struct hostapd_data *hapd,
Hai Shalom899fcc72020-10-19 14:38:18 -07001298 struct sae_data *sae)
Hai Shalomc3565922019-10-28 11:58:20 -07001299{
Hai Shalom899fcc72020-10-19 14:38:18 -07001300 const struct wpabuf *groups;
Sunil Ravi7f769292024-07-23 22:21:32 +00001301 size_t i, count, len;
Hai Shalomc3565922019-10-28 11:58:20 -07001302 const u8 *pos;
1303
Hai Shalom899fcc72020-10-19 14:38:18 -07001304 if (!sae->tmp)
1305 return 0;
1306 groups = sae->tmp->peer_rejected_groups;
Hai Shalomc3565922019-10-28 11:58:20 -07001307 if (!groups)
1308 return 0;
1309
1310 pos = wpabuf_head(groups);
Sunil Ravi7f769292024-07-23 22:21:32 +00001311 len = wpabuf_len(groups);
1312 if (len & 1) {
1313 wpa_printf(MSG_DEBUG,
1314 "SAE: Invalid length of the Rejected Groups element payload: %zu",
1315 len);
1316 return 1;
1317 }
1318
1319 count = len / 2;
Hai Shalomc3565922019-10-28 11:58:20 -07001320 for (i = 0; i < count; i++) {
1321 int enabled;
1322 u16 group;
1323
1324 group = WPA_GET_LE16(pos);
1325 pos += 2;
1326 enabled = sae_is_group_enabled(hapd, group);
1327 wpa_printf(MSG_DEBUG, "SAE: Rejected group %u is %s",
1328 group, enabled ? "enabled" : "disabled");
1329 if (enabled)
1330 return 1;
1331 }
1332
1333 return 0;
1334}
1335
1336
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001337static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
1338 const struct ieee80211_mgmt *mgmt, size_t len,
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001339 u16 auth_transaction, u16 status_code)
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001340{
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001341 int resp = WLAN_STATUS_SUCCESS;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001342 struct wpabuf *data = NULL;
Sunil Ravic0f5d412024-09-11 22:12:49 +00001343 struct hostapd_bss_config *conf = hapd->conf;
1344 int *groups = conf->sae_groups;
1345 int default_groups[] = { 19, 0, 0 };
Hai Shalom021b0b52019-04-10 11:17:58 -07001346 const u8 *pos, *end;
Hai Shalom5f92bc92019-04-18 11:54:11 -07001347 int sta_removed = 0;
Hai Shalom60840252021-02-19 19:02:11 -08001348 bool success_status;
Hai Shalom021b0b52019-04-10 11:17:58 -07001349
Sunil Ravic0f5d412024-09-11 22:12:49 +00001350 if (!groups) {
Hai Shalom021b0b52019-04-10 11:17:58 -07001351 groups = default_groups;
Sunil Ravic0f5d412024-09-11 22:12:49 +00001352 if (wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt |
1353 conf->rsn_override_key_mgmt |
1354 conf->rsn_override_key_mgmt_2))
1355 default_groups[1] = 20;
1356 }
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001357
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001358#ifdef CONFIG_TESTING_OPTIONS
1359 if (hapd->conf->sae_reflection_attack && auth_transaction == 1) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001360 wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack");
1361 pos = mgmt->u.auth.variable;
1362 end = ((const u8 *) mgmt) + len;
Hai Shalom899fcc72020-10-19 14:38:18 -07001363 resp = status_code;
Sunil Ravi7f769292024-07-23 22:21:32 +00001364 send_auth_reply(hapd, sta, sta->addr,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001365 WLAN_AUTH_SAE,
Roshan Pius3a1667e2018-07-03 15:17:14 -07001366 auth_transaction, resp, pos, end - pos,
1367 "auth-sae-reflection-attack");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001368 goto remove_sta;
1369 }
1370
1371 if (hapd->conf->sae_commit_override && auth_transaction == 1) {
1372 wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
Sunil Ravi7f769292024-07-23 22:21:32 +00001373 send_auth_reply(hapd, sta, sta->addr,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001374 WLAN_AUTH_SAE,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001375 auth_transaction, resp,
1376 wpabuf_head(hapd->conf->sae_commit_override),
Roshan Pius3a1667e2018-07-03 15:17:14 -07001377 wpabuf_len(hapd->conf->sae_commit_override),
1378 "sae-commit-override");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001379 goto remove_sta;
1380 }
1381#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001382 if (!sta->sae) {
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001383 if (auth_transaction != 1 ||
Hai Shalomc3565922019-10-28 11:58:20 -07001384 !sae_status_success(hapd, status_code)) {
Ahmed ElArabawy0ff61c52019-12-26 12:38:39 -08001385 wpa_printf(MSG_DEBUG, "SAE: Unexpected Status Code %u",
1386 status_code);
1387 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1388 goto reply;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001389 }
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001390 sta->sae = os_zalloc(sizeof(*sta->sae));
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001391 if (!sta->sae) {
1392 resp = -1;
1393 goto remove_sta;
1394 }
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001395 if (!hostapd_sae_pw_id_in_use(hapd->conf))
1396 sta->sae->no_pw_id = 1;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001397 sae_set_state(sta, SAE_NOTHING, "Init");
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001398 sta->sae->sync = 0;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001399 }
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001400
Dmitry Shmidte4663042016-04-04 10:07:49 -07001401 if (sta->mesh_sae_pmksa_caching) {
1402 wpa_printf(MSG_DEBUG,
1403 "SAE: Cancel use of mesh PMKSA caching because peer starts SAE authentication");
1404 wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
1405 sta->mesh_sae_pmksa_caching = 0;
1406 }
1407
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001408 if (auth_transaction == 1) {
Hai Shalom021b0b52019-04-10 11:17:58 -07001409 const u8 *token = NULL;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001410 size_t token_len = 0;
Hai Shalom021b0b52019-04-10 11:17:58 -07001411 int allow_reuse = 0;
1412
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001413 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
1414 HOSTAPD_LEVEL_DEBUG,
Hai Shalom81f62d82019-07-22 12:10:00 -07001415 "start SAE authentication (RX commit, status=%u (%s))",
1416 status_code, status2str(status_code));
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001417
1418 if ((hapd->conf->mesh & MESH_ENABLED) &&
1419 status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
1420 sta->sae->tmp) {
1421 pos = mgmt->u.auth.variable;
1422 end = ((const u8 *) mgmt) + len;
1423 if (pos + sizeof(le16) > end) {
1424 wpa_printf(MSG_ERROR,
1425 "SAE: Too short anti-clogging token request");
1426 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1427 goto reply;
1428 }
Hai Shalom021b0b52019-04-10 11:17:58 -07001429 resp = sae_group_allowed(sta->sae, groups,
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001430 WPA_GET_LE16(pos));
1431 if (resp != WLAN_STATUS_SUCCESS) {
1432 wpa_printf(MSG_ERROR,
1433 "SAE: Invalid group in anti-clogging token request");
1434 goto reply;
1435 }
1436 pos += sizeof(le16);
1437
1438 wpabuf_free(sta->sae->tmp->anti_clogging_token);
1439 sta->sae->tmp->anti_clogging_token =
1440 wpabuf_alloc_copy(pos, end - pos);
1441 if (sta->sae->tmp->anti_clogging_token == NULL) {
1442 wpa_printf(MSG_ERROR,
1443 "SAE: Failed to alloc for anti-clogging token");
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001444 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1445 goto remove_sta;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001446 }
1447
1448 /*
1449 * IEEE Std 802.11-2012, 11.3.8.6.4: If the Status code
1450 * is 76, a new Commit Message shall be constructed
1451 * with the Anti-Clogging Token from the received
1452 * Authentication frame, and the commit-scalar and
1453 * COMMIT-ELEMENT previously sent.
1454 */
Sunil Ravi7f769292024-07-23 22:21:32 +00001455 resp = auth_sae_send_commit(hapd, sta, 0, status_code);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001456 if (resp != WLAN_STATUS_SUCCESS) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001457 wpa_printf(MSG_ERROR,
1458 "SAE: Failed to send commit message");
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001459 goto remove_sta;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001460 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07001461 sae_set_state(sta, SAE_COMMITTED,
1462 "Sent Commit (anti-clogging token case in mesh)");
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001463 sta->sae->sync = 0;
1464 sae_set_retransmit_timer(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001465 return;
1466 }
1467
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -07001468 if ((hapd->conf->mesh & MESH_ENABLED) &&
1469 status_code ==
1470 WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
1471 sta->sae->tmp) {
1472 wpa_printf(MSG_DEBUG,
1473 "SAE: Peer did not accept our SAE group");
1474 sae_pick_next_group(hapd, sta);
1475 goto remove_sta;
1476 }
1477
Hai Shalomc3565922019-10-28 11:58:20 -07001478 if (!sae_status_success(hapd, status_code))
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001479 goto remove_sta;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001480
Sunil Ravi7f769292024-07-23 22:21:32 +00001481 if (sae_proto_instance_disabled(sta)) {
1482 wpa_printf(MSG_DEBUG,
1483 "SAE: Protocol instance temporarily disabled - discard received SAE commit");
1484 return;
1485 }
1486
Roshan Pius3a1667e2018-07-03 15:17:14 -07001487 if (!(hapd->conf->mesh & MESH_ENABLED) &&
1488 sta->sae->state == SAE_COMMITTED) {
1489 /* This is needed in the infrastructure BSS case to
1490 * address a sequence where a STA entry may remain in
1491 * hostapd across two attempts to do SAE authentication
1492 * by the same STA. The second attempt may end up trying
1493 * to use a different group and that would not be
1494 * allowed if we remain in Committed state with the
1495 * previously set parameters. */
Hai Shalom021b0b52019-04-10 11:17:58 -07001496 pos = mgmt->u.auth.variable;
1497 end = ((const u8 *) mgmt) + len;
1498 if (end - pos >= (int) sizeof(le16) &&
1499 sae_group_allowed(sta->sae, groups,
1500 WPA_GET_LE16(pos)) ==
1501 WLAN_STATUS_SUCCESS) {
1502 /* Do not waste resources deriving the same PWE
1503 * again since the same group is reused. */
1504 sae_set_state(sta, SAE_NOTHING,
1505 "Allow previous PWE to be reused");
1506 allow_reuse = 1;
1507 } else {
1508 sae_set_state(sta, SAE_NOTHING,
1509 "Clear existing state to allow restart");
1510 sae_clear_data(sta->sae);
1511 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07001512 }
1513
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001514 resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
1515 ((const u8 *) mgmt) + len -
1516 mgmt->u.auth.variable, &token,
Hai Shalomc3565922019-10-28 11:58:20 -07001517 &token_len, groups, status_code ==
Hai Shalom899fcc72020-10-19 14:38:18 -07001518 WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
Sunil Ravi77d572f2023-01-17 23:58:31 +00001519 status_code == WLAN_STATUS_SAE_PK,
1520 NULL);
Dmitry Shmidt41712582015-06-29 11:02:15 -07001521 if (resp == SAE_SILENTLY_DISCARD) {
1522 wpa_printf(MSG_DEBUG,
1523 "SAE: Drop commit message from " MACSTR " due to reflection attack",
1524 MAC2STR(sta->addr));
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001525 goto remove_sta;
Dmitry Shmidt41712582015-06-29 11:02:15 -07001526 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07001527
1528 if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
1529 wpa_msg(hapd->msg_ctx, MSG_INFO,
1530 WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER
1531 MACSTR, MAC2STR(sta->addr));
1532 sae_clear_retransmit_timer(hapd, sta);
1533 sae_set_state(sta, SAE_NOTHING,
1534 "Unknown Password Identifier");
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001535 if (sta->sae->state == SAE_NOTHING)
1536 goto reply;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001537 goto remove_sta;
1538 }
1539
Hai Shaloma20dcd72022-02-04 13:43:00 -08001540 if (token &&
Sunil Ravi77d572f2023-01-17 23:58:31 +00001541 check_comeback_token(hapd->comeback_key,
1542 hapd->comeback_pending_idx, sta->addr,
1543 token, token_len)
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001544 < 0) {
1545 wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
1546 "incorrect token from " MACSTR,
1547 MAC2STR(sta->addr));
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001548 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1549 goto remove_sta;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001550 }
1551
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001552 if (resp != WLAN_STATUS_SUCCESS)
1553 goto reply;
1554
Hai Shalom899fcc72020-10-19 14:38:18 -07001555 if (check_sae_rejected_groups(hapd, sta->sae)) {
Hai Shalomc3565922019-10-28 11:58:20 -07001556 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Ahmed ElArabawy0ff61c52019-12-26 12:38:39 -08001557 goto reply;
Hai Shalomc3565922019-10-28 11:58:20 -07001558 }
1559
Hai Shaloma20dcd72022-02-04 13:43:00 -08001560 if (!token && use_anti_clogging(hapd) && !allow_reuse) {
Hai Shalomfdcde762020-04-02 11:19:20 -07001561 int h2e = 0;
1562
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001563 wpa_printf(MSG_DEBUG,
1564 "SAE: Request anti-clogging token from "
1565 MACSTR, MAC2STR(sta->addr));
Hai Shalomfdcde762020-04-02 11:19:20 -07001566 if (sta->sae->tmp)
Hai Shalom899fcc72020-10-19 14:38:18 -07001567 h2e = sta->sae->h2e;
1568 if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
1569 status_code == WLAN_STATUS_SAE_PK)
Hai Shalomfdcde762020-04-02 11:19:20 -07001570 h2e = 1;
Sunil Ravi77d572f2023-01-17 23:58:31 +00001571 data = auth_build_token_req(
1572 &hapd->last_comeback_key_update,
1573 hapd->comeback_key,
1574 hapd->comeback_idx,
1575 hapd->comeback_pending_idx,
1576 sizeof(hapd->comeback_pending_idx),
1577 sta->sae->group,
1578 sta->addr, h2e);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001579 resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
1580 if (hapd->conf->mesh & MESH_ENABLED)
Roshan Pius3a1667e2018-07-03 15:17:14 -07001581 sae_set_state(sta, SAE_NOTHING,
1582 "Request anti-clogging token case in mesh");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001583 goto reply;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001584 }
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001585
Sunil Ravi7f769292024-07-23 22:21:32 +00001586 resp = sae_sm_step(hapd, sta, auth_transaction,
Hai Shalomc3565922019-10-28 11:58:20 -07001587 status_code, allow_reuse, &sta_removed);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001588 } else if (auth_transaction == 2) {
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001589 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
1590 HOSTAPD_LEVEL_DEBUG,
Hai Shalom81f62d82019-07-22 12:10:00 -07001591 "SAE authentication (RX confirm, status=%u (%s))",
1592 status_code, status2str(status_code));
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001593 if (status_code != WLAN_STATUS_SUCCESS)
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001594 goto remove_sta;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001595 if (sta->sae->state >= SAE_CONFIRMED ||
1596 !(hapd->conf->mesh & MESH_ENABLED)) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07001597 const u8 *var;
1598 size_t var_len;
1599 u16 peer_send_confirm;
1600
1601 var = mgmt->u.auth.variable;
1602 var_len = ((u8 *) mgmt) + len - mgmt->u.auth.variable;
1603 if (var_len < 2) {
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001604 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001605 goto reply;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001606 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07001607
1608 peer_send_confirm = WPA_GET_LE16(var);
1609
1610 if (sta->sae->state == SAE_ACCEPTED &&
1611 (peer_send_confirm <= sta->sae->rc ||
1612 peer_send_confirm == 0xffff)) {
1613 wpa_printf(MSG_DEBUG,
1614 "SAE: Silently ignore unexpected Confirm from peer "
1615 MACSTR
1616 " (peer-send-confirm=%u Rc=%u)",
1617 MAC2STR(sta->addr),
1618 peer_send_confirm, sta->sae->rc);
1619 return;
1620 }
1621
Sunil Ravi77d572f2023-01-17 23:58:31 +00001622 if (sae_check_confirm(sta->sae, var, var_len,
1623 NULL) < 0) {
1624 resp = WLAN_STATUS_CHALLENGE_FAIL;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001625 goto reply;
1626 }
1627 sta->sae->rc = peer_send_confirm;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001628 }
Sunil Ravi7f769292024-07-23 22:21:32 +00001629 resp = sae_sm_step(hapd, sta, auth_transaction,
Hai Shalomc3565922019-10-28 11:58:20 -07001630 status_code, 0, &sta_removed);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001631 } else {
1632 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
1633 HOSTAPD_LEVEL_DEBUG,
Hai Shalom81f62d82019-07-22 12:10:00 -07001634 "unexpected SAE authentication transaction %u (status=%u (%s))",
1635 auth_transaction, status_code,
1636 status2str(status_code));
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001637 if (status_code != WLAN_STATUS_SUCCESS)
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001638 goto remove_sta;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001639 resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
1640 }
1641
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001642reply:
Hai Shalom5f92bc92019-04-18 11:54:11 -07001643 if (!sta_removed && resp != WLAN_STATUS_SUCCESS) {
Hai Shalom021b0b52019-04-10 11:17:58 -07001644 pos = mgmt->u.auth.variable;
1645 end = ((const u8 *) mgmt) + len;
1646
1647 /* Copy the Finite Cyclic Group field from the request if we
1648 * rejected it as unsupported group. */
1649 if (resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
1650 !data && end - pos >= 2)
1651 data = wpabuf_alloc_copy(pos, 2);
1652
Sunil Ravi7f769292024-07-23 22:21:32 +00001653 send_auth_reply(hapd, sta, sta->addr,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001654 WLAN_AUTH_SAE,
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001655 auth_transaction, resp,
1656 data ? wpabuf_head(data) : (u8 *) "",
Roshan Pius3a1667e2018-07-03 15:17:14 -07001657 data ? wpabuf_len(data) : 0, "auth-sae");
Sunil Ravic0f5d412024-09-11 22:12:49 +00001658 sae_sme_send_external_auth_status(hapd, sta, resp);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001659 }
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001660
1661remove_sta:
Hai Shalom60840252021-02-19 19:02:11 -08001662 if (auth_transaction == 1)
1663 success_status = sae_status_success(hapd, status_code);
1664 else
1665 success_status = status_code == WLAN_STATUS_SUCCESS;
Hai Shalom5f92bc92019-04-18 11:54:11 -07001666 if (!sta_removed && sta->added_unassoc &&
Hai Shalom60840252021-02-19 19:02:11 -08001667 (resp != WLAN_STATUS_SUCCESS || !success_status)) {
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08001668 hostapd_drv_sta_remove(hapd, sta->addr);
1669 sta->added_unassoc = 0;
1670 }
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001671 wpabuf_free(data);
1672}
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001673
1674
1675/**
1676 * auth_sae_init_committed - Send COMMIT and start SAE in committed state
1677 * @hapd: BSS data for the device initiating the authentication
1678 * @sta: the peer to which commit authentication frame is sent
1679 *
1680 * This function implements Init event handling (IEEE Std 802.11-2012,
1681 * 11.3.8.6.3) in which initial COMMIT message is sent. Prior to calling, the
1682 * sta->sae structure should be initialized appropriately via a call to
1683 * sae_prepare_commit().
1684 */
1685int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta)
1686{
1687 int ret;
1688
1689 if (!sta->sae || !sta->sae->tmp)
1690 return -1;
1691
1692 if (sta->sae->state != SAE_NOTHING)
1693 return -1;
1694
Sunil Ravi7f769292024-07-23 22:21:32 +00001695 ret = auth_sae_send_commit(hapd, sta, 0, -1);
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001696 if (ret)
1697 return -1;
1698
Roshan Pius3a1667e2018-07-03 15:17:14 -07001699 sae_set_state(sta, SAE_COMMITTED, "Init and sent commit");
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001700 sta->sae->sync = 0;
1701 sae_set_retransmit_timer(hapd, sta);
1702
1703 return 0;
1704}
1705
Hai Shalom021b0b52019-04-10 11:17:58 -07001706
1707void auth_sae_process_commit(void *eloop_ctx, void *user_ctx)
1708{
1709 struct hostapd_data *hapd = eloop_ctx;
1710 struct hostapd_sae_commit_queue *q;
1711 unsigned int queue_len;
1712
1713 q = dl_list_first(&hapd->sae_commit_queue,
1714 struct hostapd_sae_commit_queue, list);
1715 if (!q)
1716 return;
1717 wpa_printf(MSG_DEBUG,
1718 "SAE: Process next available message from queue");
1719 dl_list_del(&q->list);
1720 handle_auth(hapd, (const struct ieee80211_mgmt *) q->msg, q->len,
1721 q->rssi, 1);
1722 os_free(q);
1723
1724 if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
1725 return;
1726 queue_len = dl_list_len(&hapd->sae_commit_queue);
1727 eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
1728 hapd, NULL);
1729}
1730
1731
1732static void auth_sae_queue(struct hostapd_data *hapd,
1733 const struct ieee80211_mgmt *mgmt, size_t len,
1734 int rssi)
1735{
1736 struct hostapd_sae_commit_queue *q, *q2;
1737 unsigned int queue_len;
1738 const struct ieee80211_mgmt *mgmt2;
1739
1740 queue_len = dl_list_len(&hapd->sae_commit_queue);
1741 if (queue_len >= 15) {
1742 wpa_printf(MSG_DEBUG,
1743 "SAE: No more room in message queue - drop the new frame from "
1744 MACSTR, MAC2STR(mgmt->sa));
1745 return;
1746 }
1747
1748 wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from "
1749 MACSTR " for processing (queue_len %u)", MAC2STR(mgmt->sa),
1750 queue_len);
1751 q = os_zalloc(sizeof(*q) + len);
1752 if (!q)
1753 return;
1754 q->rssi = rssi;
1755 q->len = len;
1756 os_memcpy(q->msg, mgmt, len);
1757
1758 /* Check whether there is already a queued Authentication frame from the
1759 * same station with the same transaction number and if so, replace that
1760 * queue entry with the new one. This avoids issues with a peer that
1761 * sends multiple times (e.g., due to frequent SAE retries). There is no
1762 * point in us trying to process the old attempts after a new one has
1763 * obsoleted them. */
1764 dl_list_for_each(q2, &hapd->sae_commit_queue,
1765 struct hostapd_sae_commit_queue, list) {
1766 mgmt2 = (const struct ieee80211_mgmt *) q2->msg;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001767 if (ether_addr_equal(mgmt->sa, mgmt2->sa) &&
Hai Shalom021b0b52019-04-10 11:17:58 -07001768 mgmt->u.auth.auth_transaction ==
1769 mgmt2->u.auth.auth_transaction) {
1770 wpa_printf(MSG_DEBUG,
1771 "SAE: Replace queued message from same STA with same transaction number");
1772 dl_list_add(&q2->list, &q->list);
1773 dl_list_del(&q2->list);
1774 os_free(q2);
1775 goto queued;
1776 }
1777 }
1778
1779 /* No pending identical entry, so add to the end of the queue */
1780 dl_list_add_tail(&hapd->sae_commit_queue, &q->list);
1781
1782queued:
1783 if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
1784 return;
1785 eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
1786 hapd, NULL);
1787}
1788
1789
1790static int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr)
1791{
1792 struct hostapd_sae_commit_queue *q;
1793 const struct ieee80211_mgmt *mgmt;
1794
1795 dl_list_for_each(q, &hapd->sae_commit_queue,
1796 struct hostapd_sae_commit_queue, list) {
1797 mgmt = (const struct ieee80211_mgmt *) q->msg;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001798 if (ether_addr_equal(addr, mgmt->sa))
Hai Shalom021b0b52019-04-10 11:17:58 -07001799 return 1;
1800 }
1801
1802 return 0;
1803}
1804
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08001805#endif /* CONFIG_SAE */
1806
1807
Hai Shalomfdcde762020-04-02 11:19:20 -07001808static u16 wpa_res_to_status_code(enum wpa_validate_result res)
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001809{
Hai Shalomfdcde762020-04-02 11:19:20 -07001810 switch (res) {
1811 case WPA_IE_OK:
1812 return WLAN_STATUS_SUCCESS;
1813 case WPA_INVALID_IE:
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001814 return WLAN_STATUS_INVALID_IE;
Hai Shalomfdcde762020-04-02 11:19:20 -07001815 case WPA_INVALID_GROUP:
1816 return WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
1817 case WPA_INVALID_PAIRWISE:
1818 return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
1819 case WPA_INVALID_AKMP:
1820 return WLAN_STATUS_AKMP_NOT_VALID;
1821 case WPA_NOT_ENABLED:
1822 return WLAN_STATUS_INVALID_IE;
1823 case WPA_ALLOC_FAIL:
1824 return WLAN_STATUS_UNSPECIFIED_FAILURE;
1825 case WPA_MGMT_FRAME_PROTECTION_VIOLATION:
1826 return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
1827 case WPA_INVALID_MGMT_GROUP_CIPHER:
1828 return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
1829 case WPA_INVALID_MDIE:
1830 return WLAN_STATUS_INVALID_MDIE;
1831 case WPA_INVALID_PROTO:
1832 return WLAN_STATUS_INVALID_IE;
1833 case WPA_INVALID_PMKID:
1834 return WLAN_STATUS_INVALID_PMKID;
1835 case WPA_DENIED_OTHER_REASON:
1836 return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
1837 }
1838 return WLAN_STATUS_INVALID_IE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001839}
1840
1841
1842#ifdef CONFIG_FILS
1843
1844static void handle_auth_fils_finish(struct hostapd_data *hapd,
1845 struct sta_info *sta, u16 resp,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001846 struct wpabuf *data, int pub);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001847
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001848void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
1849 const u8 *pos, size_t len, u16 auth_alg,
1850 u16 auth_transaction, u16 status_code,
1851 void (*cb)(struct hostapd_data *hapd,
1852 struct sta_info *sta, u16 resp,
1853 struct wpabuf *data, int pub))
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001854{
1855 u16 resp = WLAN_STATUS_SUCCESS;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001856 const u8 *end;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001857 struct ieee802_11_elems elems;
Hai Shalomfdcde762020-04-02 11:19:20 -07001858 enum wpa_validate_result res;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001859 struct wpa_ie_data rsn;
1860 struct rsn_pmksa_cache_entry *pmksa = NULL;
1861
1862 if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
1863 return;
1864
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001865 end = pos + len;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001866
1867 wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
1868 pos, end - pos);
1869
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001870 /* TODO: FILS PK */
1871#ifdef CONFIG_FILS_SK_PFS
1872 if (auth_alg == WLAN_AUTH_FILS_SK_PFS) {
1873 u16 group;
1874 struct wpabuf *pub;
1875 size_t elem_len;
1876
1877 /* Using FILS PFS */
1878
1879 /* Finite Cyclic Group */
1880 if (end - pos < 2) {
1881 wpa_printf(MSG_DEBUG,
1882 "FILS: No room for Finite Cyclic Group");
1883 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1884 goto fail;
1885 }
1886 group = WPA_GET_LE16(pos);
1887 pos += 2;
1888 if (group != hapd->conf->fils_dh_group) {
1889 wpa_printf(MSG_DEBUG,
1890 "FILS: Unsupported Finite Cyclic Group: %u (expected %u)",
1891 group, hapd->conf->fils_dh_group);
1892 resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
1893 goto fail;
1894 }
1895
1896 crypto_ecdh_deinit(sta->fils_ecdh);
1897 sta->fils_ecdh = crypto_ecdh_init(group);
1898 if (!sta->fils_ecdh) {
1899 wpa_printf(MSG_INFO,
1900 "FILS: Could not initialize ECDH with group %d",
1901 group);
1902 resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
1903 goto fail;
1904 }
1905
1906 pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
1907 if (!pub) {
1908 wpa_printf(MSG_DEBUG,
1909 "FILS: Failed to derive ECDH public key");
1910 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1911 goto fail;
1912 }
1913 elem_len = wpabuf_len(pub);
1914 wpabuf_free(pub);
1915
1916 /* Element */
1917 if ((size_t) (end - pos) < elem_len) {
1918 wpa_printf(MSG_DEBUG, "FILS: No room for Element");
1919 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1920 goto fail;
1921 }
1922
1923 wpabuf_free(sta->fils_g_sta);
1924 sta->fils_g_sta = wpabuf_alloc_copy(pos, elem_len);
1925 wpabuf_clear_free(sta->fils_dh_ss);
1926 sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1,
1927 pos, elem_len);
1928 if (!sta->fils_dh_ss) {
1929 wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
1930 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1931 goto fail;
1932 }
1933 wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss);
1934 pos += elem_len;
1935 } else {
1936 crypto_ecdh_deinit(sta->fils_ecdh);
1937 sta->fils_ecdh = NULL;
1938 wpabuf_clear_free(sta->fils_dh_ss);
1939 sta->fils_dh_ss = NULL;
1940 }
1941#endif /* CONFIG_FILS_SK_PFS */
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001942
1943 wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
1944 if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
1945 wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
1946 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1947 goto fail;
1948 }
1949
1950 /* RSNE */
1951 wpa_hexdump(MSG_DEBUG, "FILS: RSN element",
1952 elems.rsn_ie, elems.rsn_ie_len);
1953 if (!elems.rsn_ie ||
1954 wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
1955 &rsn) < 0) {
1956 wpa_printf(MSG_DEBUG, "FILS: No valid RSN element");
1957 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1958 goto fail;
1959 }
1960
1961 if (!sta->wpa_sm)
1962 sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr,
1963 NULL);
1964 if (!sta->wpa_sm) {
1965 wpa_printf(MSG_DEBUG,
1966 "FILS: Failed to initialize RSN state machine");
1967 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1968 goto fail;
1969 }
1970
Sunil Ravic0f5d412024-09-11 22:12:49 +00001971 wpa_auth_set_rsn_selection(sta->wpa_sm, elems.rsn_selection,
1972 elems.rsn_selection_len);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001973 res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
Hai Shalom021b0b52019-04-10 11:17:58 -07001974 hapd->iface->freq,
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001975 elems.rsn_ie - 2, elems.rsn_ie_len + 2,
Hai Shalomc3565922019-10-28 11:58:20 -07001976 elems.rsnxe ? elems.rsnxe - 2 : NULL,
1977 elems.rsnxe ? elems.rsnxe_len + 2 : 0,
Sunil Ravi7f769292024-07-23 22:21:32 +00001978 elems.mdie, elems.mdie_len, NULL, 0, NULL);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001979 resp = wpa_res_to_status_code(res);
1980 if (resp != WLAN_STATUS_SUCCESS)
1981 goto fail;
1982
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08001983 if (!elems.fils_nonce) {
1984 wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
1985 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
1986 goto fail;
1987 }
1988 wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce,
1989 FILS_NONCE_LEN);
1990 os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN);
1991
1992 /* PMKID List */
1993 if (rsn.pmkid && rsn.num_pmkid > 0) {
1994 u8 num;
1995 const u8 *pmkid;
1996
1997 wpa_hexdump(MSG_DEBUG, "FILS: PMKID List",
1998 rsn.pmkid, rsn.num_pmkid * PMKID_LEN);
1999
2000 pmkid = rsn.pmkid;
2001 num = rsn.num_pmkid;
2002 while (num) {
2003 wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN);
2004 pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr,
2005 pmkid);
2006 if (pmksa)
2007 break;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002008 pmksa = wpa_auth_pmksa_get_fils_cache_id(hapd->wpa_auth,
2009 sta->addr,
2010 pmkid);
2011 if (pmksa)
2012 break;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002013 pmkid += PMKID_LEN;
2014 num--;
2015 }
2016 }
2017 if (pmksa && wpa_auth_sta_key_mgmt(sta->wpa_sm) != pmksa->akmp) {
2018 wpa_printf(MSG_DEBUG,
2019 "FILS: Matching PMKSA cache entry has different AKMP (0x%x != 0x%x) - ignore",
2020 wpa_auth_sta_key_mgmt(sta->wpa_sm), pmksa->akmp);
2021 pmksa = NULL;
2022 }
2023 if (pmksa)
2024 wpa_printf(MSG_DEBUG, "FILS: Found matching PMKSA cache entry");
2025
2026 /* FILS Session */
2027 if (!elems.fils_session) {
2028 wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
2029 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2030 goto fail;
2031 }
2032 wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
2033 FILS_SESSION_LEN);
2034 os_memcpy(sta->fils_session, elems.fils_session, FILS_SESSION_LEN);
2035
Hai Shalomfdcde762020-04-02 11:19:20 -07002036 /* Wrapped Data */
2037 if (elems.wrapped_data) {
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002038 wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data",
Hai Shalomfdcde762020-04-02 11:19:20 -07002039 elems.wrapped_data,
2040 elems.wrapped_data_len);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002041 if (!pmksa) {
2042#ifndef CONFIG_NO_RADIUS
2043 if (!sta->eapol_sm) {
2044 sta->eapol_sm =
2045 ieee802_1x_alloc_eapol_sm(hapd, sta);
2046 }
2047 wpa_printf(MSG_DEBUG,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002048 "FILS: Forward EAP-Initiate/Re-auth to authentication server");
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002049 ieee802_1x_encapsulate_radius(
Hai Shalomfdcde762020-04-02 11:19:20 -07002050 hapd, sta, elems.wrapped_data,
2051 elems.wrapped_data_len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002052 sta->fils_pending_cb = cb;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002053 wpa_printf(MSG_DEBUG,
2054 "FILS: Will send Authentication frame once the response from authentication server is available");
2055 sta->flags |= WLAN_STA_PENDING_FILS_ERP;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002056 /* Calculate pending PMKID here so that we do not need
2057 * to maintain a copy of the EAP-Initiate/Reauth
2058 * message. */
2059 if (fils_pmkid_erp(wpa_auth_sta_key_mgmt(sta->wpa_sm),
Hai Shalomfdcde762020-04-02 11:19:20 -07002060 elems.wrapped_data,
2061 elems.wrapped_data_len,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002062 sta->fils_erp_pmkid) == 0)
2063 sta->fils_erp_pmkid_set = 1;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002064 return;
2065#else /* CONFIG_NO_RADIUS */
2066 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2067 goto fail;
2068#endif /* CONFIG_NO_RADIUS */
2069 }
2070 }
2071
2072fail:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002073 if (cb) {
2074 struct wpabuf *data;
2075 int pub = 0;
2076
2077 data = prepare_auth_resp_fils(hapd, sta, &resp, pmksa, NULL,
2078 NULL, 0, &pub);
2079 if (!data) {
2080 wpa_printf(MSG_DEBUG,
2081 "%s: prepare_auth_resp_fils() returned failure",
2082 __func__);
2083 }
2084
2085 cb(hapd, sta, resp, data, pub);
2086 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002087}
2088
2089
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002090static struct wpabuf *
2091prepare_auth_resp_fils(struct hostapd_data *hapd,
2092 struct sta_info *sta, u16 *resp,
2093 struct rsn_pmksa_cache_entry *pmksa,
2094 struct wpabuf *erp_resp,
2095 const u8 *msk, size_t msk_len,
2096 int *is_pub)
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002097{
2098 u8 fils_nonce[FILS_NONCE_LEN];
2099 size_t ielen;
2100 struct wpabuf *data = NULL;
2101 const u8 *ie;
2102 u8 *ie_buf = NULL;
2103 const u8 *pmk = NULL;
2104 size_t pmk_len = 0;
Paul Stewart092955c2017-02-06 09:13:09 -08002105 u8 pmk_buf[PMK_LEN_MAX];
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002106 struct wpabuf *pub = NULL;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002107
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002108 if (*resp != WLAN_STATUS_SUCCESS)
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002109 goto fail;
2110
2111 ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
2112 if (!ie) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002113 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002114 goto fail;
2115 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002116
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002117 if (pmksa) {
2118 /* Add PMKID of the selected PMKSA into RSNE */
2119 ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN);
2120 if (!ie_buf) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002121 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002122 goto fail;
2123 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002124
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002125 os_memcpy(ie_buf, ie, ielen);
Sunil Ravib0ac25f2024-07-12 01:42:03 +00002126 if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid, true) < 0) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002127 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002128 goto fail;
2129 }
2130 ie = ie_buf;
2131 }
2132
2133 if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002134 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002135 goto fail;
2136 }
2137 wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
2138 fils_nonce, FILS_NONCE_LEN);
2139
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002140#ifdef CONFIG_FILS_SK_PFS
2141 if (sta->fils_dh_ss && sta->fils_ecdh) {
2142 pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
2143 if (!pub) {
2144 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2145 goto fail;
2146 }
2147 }
2148#endif /* CONFIG_FILS_SK_PFS */
2149
2150 data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0));
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002151 if (!data) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002152 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002153 goto fail;
2154 }
2155
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002156 /* TODO: FILS PK */
2157#ifdef CONFIG_FILS_SK_PFS
2158 if (pub) {
2159 /* Finite Cyclic Group */
2160 wpabuf_put_le16(data, hapd->conf->fils_dh_group);
2161
2162 /* Element */
2163 wpabuf_put_buf(data, pub);
2164 }
2165#endif /* CONFIG_FILS_SK_PFS */
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002166
2167 /* RSNE */
2168 wpabuf_put_data(data, ie, ielen);
2169
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002170 /* MDE when using FILS+FT (already included in ie,ielen with RSNE) */
2171
2172#ifdef CONFIG_IEEE80211R_AP
2173 if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) {
2174 /* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */
2175 int res;
2176
Sunil Ravi77d572f2023-01-17 23:58:31 +00002177 res = wpa_auth_write_fte(hapd->wpa_auth, sta->wpa_sm,
Roshan Pius3a1667e2018-07-03 15:17:14 -07002178 wpabuf_put(data, 0),
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002179 wpabuf_tailroom(data));
2180 if (res < 0) {
2181 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
2182 goto fail;
2183 }
2184 wpabuf_put(data, res);
2185 }
2186#endif /* CONFIG_IEEE80211R_AP */
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002187
2188 /* FILS Nonce */
2189 wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
2190 wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */
2191 /* Element ID Extension */
2192 wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE);
2193 wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN);
2194
2195 /* FILS Session */
2196 wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
2197 wpabuf_put_u8(data, 1 + FILS_SESSION_LEN); /* Length */
2198 /* Element ID Extension */
2199 wpabuf_put_u8(data, WLAN_EID_EXT_FILS_SESSION);
2200 wpabuf_put_data(data, sta->fils_session, FILS_SESSION_LEN);
2201
Hai Shalomfdcde762020-04-02 11:19:20 -07002202 /* Wrapped Data */
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002203 if (!pmksa && erp_resp) {
2204 wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
2205 wpabuf_put_u8(data, 1 + wpabuf_len(erp_resp)); /* Length */
2206 /* Element ID Extension */
Hai Shalomfdcde762020-04-02 11:19:20 -07002207 wpabuf_put_u8(data, WLAN_EID_EXT_WRAPPED_DATA);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002208 wpabuf_put_buf(data, erp_resp);
2209
Paul Stewart092955c2017-02-06 09:13:09 -08002210 if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm),
2211 msk, msk_len, sta->fils_snonce, fils_nonce,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002212 sta->fils_dh_ss ?
2213 wpabuf_head(sta->fils_dh_ss) : NULL,
2214 sta->fils_dh_ss ?
2215 wpabuf_len(sta->fils_dh_ss) : 0,
2216 pmk_buf, &pmk_len)) {
Paul Stewart092955c2017-02-06 09:13:09 -08002217 wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002218 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Paul Stewart092955c2017-02-06 09:13:09 -08002219 wpabuf_free(data);
2220 data = NULL;
2221 goto fail;
2222 }
2223 pmk = pmk_buf;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002224
2225 /* Don't use DHss in PTK derivation if PMKSA caching is not
2226 * used. */
2227 wpabuf_clear_free(sta->fils_dh_ss);
2228 sta->fils_dh_ss = NULL;
2229
2230 if (sta->fils_erp_pmkid_set) {
2231 /* TODO: get PMKLifetime from WPA parameters */
2232 unsigned int dot11RSNAConfigPMKLifetime = 43200;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002233 int session_timeout;
2234
2235 session_timeout = dot11RSNAConfigPMKLifetime;
2236 if (sta->session_timeout_set) {
2237 struct os_reltime now, diff;
2238
2239 os_get_reltime(&now);
2240 os_reltime_sub(&sta->session_timeout, &now,
2241 &diff);
2242 session_timeout = diff.sec;
2243 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002244
2245 sta->fils_erp_pmkid_set = 0;
Hai Shalom81f62d82019-07-22 12:10:00 -07002246 wpa_auth_add_fils_pmk_pmkid(sta->wpa_sm, pmk, pmk_len,
2247 sta->fils_erp_pmkid);
Hai Shalom021b0b52019-04-10 11:17:58 -07002248 if (!hapd->conf->disable_pmksa_caching &&
2249 wpa_auth_pmksa_add2(
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002250 hapd->wpa_auth, sta->addr,
2251 pmk, pmk_len,
2252 sta->fils_erp_pmkid,
Roshan Pius3a1667e2018-07-03 15:17:14 -07002253 session_timeout,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00002254 wpa_auth_sta_key_mgmt(sta->wpa_sm),
2255 NULL) < 0) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002256 wpa_printf(MSG_ERROR,
2257 "FILS: Failed to add PMKSA cache entry based on ERP");
2258 }
2259 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002260 } else if (pmksa) {
2261 pmk = pmksa->pmk;
2262 pmk_len = pmksa->pmk_len;
2263 }
2264
2265 if (!pmk) {
2266 wpa_printf(MSG_DEBUG, "FILS: No PMK available");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002267 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002268 wpabuf_free(data);
2269 data = NULL;
2270 goto fail;
2271 }
2272
2273 if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002274 sta->fils_snonce, fils_nonce,
2275 sta->fils_dh_ss ?
2276 wpabuf_head(sta->fils_dh_ss) : NULL,
2277 sta->fils_dh_ss ?
2278 wpabuf_len(sta->fils_dh_ss) : 0,
2279 sta->fils_g_sta, pub) < 0) {
2280 *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002281 wpabuf_free(data);
2282 data = NULL;
2283 goto fail;
2284 }
2285
2286fail:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002287 if (is_pub)
2288 *is_pub = pub != NULL;
2289 os_free(ie_buf);
2290 wpabuf_free(pub);
2291 wpabuf_clear_free(sta->fils_dh_ss);
2292 sta->fils_dh_ss = NULL;
2293#ifdef CONFIG_FILS_SK_PFS
2294 crypto_ecdh_deinit(sta->fils_ecdh);
2295 sta->fils_ecdh = NULL;
2296#endif /* CONFIG_FILS_SK_PFS */
2297 return data;
2298}
2299
2300
2301static void handle_auth_fils_finish(struct hostapd_data *hapd,
2302 struct sta_info *sta, u16 resp,
2303 struct wpabuf *data, int pub)
2304{
2305 u16 auth_alg;
2306
2307 auth_alg = (pub ||
2308 resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ?
2309 WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
Sunil Ravi7f769292024-07-23 22:21:32 +00002310 send_auth_reply(hapd, sta, sta->addr, auth_alg, 2, resp,
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002311 data ? wpabuf_head(data) : (u8 *) "",
Roshan Pius3a1667e2018-07-03 15:17:14 -07002312 data ? wpabuf_len(data) : 0, "auth-fils-finish");
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002313 wpabuf_free(data);
2314
2315 if (resp == WLAN_STATUS_SUCCESS) {
2316 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
2317 HOSTAPD_LEVEL_DEBUG,
2318 "authentication OK (FILS)");
2319 sta->flags |= WLAN_STA_AUTH;
2320 wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002321 sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002322 mlme_authenticate_indication(hapd, sta);
2323 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002324}
2325
2326
2327void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
2328 struct sta_info *sta, int success,
2329 struct wpabuf *erp_resp,
2330 const u8 *msk, size_t msk_len)
2331{
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002332 u16 resp;
Hai Shalom60840252021-02-19 19:02:11 -08002333 u32 flags = sta->flags;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002334
Hai Shalom60840252021-02-19 19:02:11 -08002335 sta->flags &= ~(WLAN_STA_PENDING_FILS_ERP |
2336 WLAN_STA_PENDING_PASN_FILS_ERP);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002337
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002338 resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE;
Hai Shalom60840252021-02-19 19:02:11 -08002339
2340 if (flags & WLAN_STA_PENDING_FILS_ERP) {
2341 struct wpabuf *data;
2342 int pub = 0;
2343
2344 if (!sta->fils_pending_cb)
2345 return;
2346
2347 data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp,
2348 msk, msk_len, &pub);
2349 if (!data) {
2350 wpa_printf(MSG_DEBUG,
2351 "%s: prepare_auth_resp_fils() failure",
2352 __func__);
2353 }
2354 sta->fils_pending_cb(hapd, sta, resp, data, pub);
2355#ifdef CONFIG_PASN
2356 } else if (flags & WLAN_STA_PENDING_PASN_FILS_ERP) {
2357 pasn_fils_auth_resp(hapd, sta, resp, erp_resp,
2358 msk, msk_len);
2359#endif /* CONFIG_PASN */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002360 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08002361}
2362
2363#endif /* CONFIG_FILS */
2364
2365
Hai Shalomfdcde762020-04-02 11:19:20 -07002366static int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
2367 const u8 *msg, size_t len,
2368 struct radius_sta *info)
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002369{
2370 int res;
2371
Hai Shalomfdcde762020-04-02 11:19:20 -07002372 res = hostapd_allowed_address(hapd, addr, msg, len, info, 0);
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002373
2374 if (res == HOSTAPD_ACL_REJECT) {
Hai Shalomfdcde762020-04-02 11:19:20 -07002375 wpa_printf(MSG_DEBUG, "Station " MACSTR
2376 " not allowed to authenticate",
2377 MAC2STR(addr));
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002378 return HOSTAPD_ACL_REJECT;
2379 }
2380
2381 if (res == HOSTAPD_ACL_PENDING) {
2382 wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR
2383 " waiting for an external authentication",
2384 MAC2STR(addr));
2385 /* Authentication code will re-send the authentication frame
2386 * after it has received (and cached) information from the
2387 * external source. */
2388 return HOSTAPD_ACL_PENDING;
2389 }
2390
2391 return res;
2392}
2393
2394
Sunil Ravia04bd252022-05-02 22:54:18 -07002395int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
2396 int res, struct radius_sta *info)
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002397{
Hai Shalomfdcde762020-04-02 11:19:20 -07002398 u32 session_timeout = info->session_timeout;
2399 u32 acct_interim_interval = info->acct_interim_interval;
2400 struct vlan_description *vlan_id = &info->vlan_id;
2401 struct hostapd_sta_wpa_psk_short *psk = info->psk;
2402 char *identity = info->identity;
2403 char *radius_cui = info->radius_cui;
2404
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002405 if (vlan_id->notempty &&
2406 !hostapd_vlan_valid(hapd->conf->vlan, vlan_id)) {
2407 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
2408 HOSTAPD_LEVEL_INFO,
2409 "Invalid VLAN %d%s received from RADIUS server",
2410 vlan_id->untagged,
2411 vlan_id->tagged[0] ? "+" : "");
2412 return -1;
2413 }
2414 if (ap_sta_set_vlan(hapd, sta, vlan_id) < 0)
2415 return -1;
2416 if (sta->vlan_id)
2417 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
2418 HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
2419
2420 hostapd_free_psk_list(sta->psk);
Hai Shalomfdcde762020-04-02 11:19:20 -07002421 if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED)
2422 hostapd_copy_psk_list(&sta->psk, psk);
2423 else
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002424 sta->psk = NULL;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002425
Roshan Pius3a1667e2018-07-03 15:17:14 -07002426 os_free(sta->identity);
Hai Shalomfdcde762020-04-02 11:19:20 -07002427 if (identity)
2428 sta->identity = os_strdup(identity);
2429 else
2430 sta->identity = NULL;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002431
2432 os_free(sta->radius_cui);
Hai Shalomfdcde762020-04-02 11:19:20 -07002433 if (radius_cui)
2434 sta->radius_cui = os_strdup(radius_cui);
2435 else
2436 sta->radius_cui = NULL;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002437
2438 if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
2439 sta->acct_interim_interval = acct_interim_interval;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002440 if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) {
2441 sta->session_timeout_set = 1;
2442 os_get_reltime(&sta->session_timeout);
2443 sta->session_timeout.sec += session_timeout;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002444 ap_sta_session_timeout(hapd, sta, session_timeout);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002445 } else {
2446 sta->session_timeout_set = 0;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002447 ap_sta_no_session_timeout(hapd, sta);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002448 }
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08002449
2450 return 0;
2451}
2452
2453
Hai Shalom60840252021-02-19 19:02:11 -08002454#ifdef CONFIG_PASN
Hai Shalom60840252021-02-19 19:02:11 -08002455#ifdef CONFIG_FILS
2456
Hai Shalom60840252021-02-19 19:02:11 -08002457static void pasn_fils_auth_resp(struct hostapd_data *hapd,
2458 struct sta_info *sta, u16 status,
2459 struct wpabuf *erp_resp,
2460 const u8 *msk, size_t msk_len)
2461{
2462 struct pasn_data *pasn = sta->pasn;
Sunil Ravi77d572f2023-01-17 23:58:31 +00002463 struct pasn_fils *fils = &pasn->fils;
Hai Shalom60840252021-02-19 19:02:11 -08002464 u8 pmk[PMK_LEN_MAX];
2465 size_t pmk_len;
2466 int ret;
2467
2468 wpa_printf(MSG_DEBUG, "PASN: FILS: Handle AS response - status=%u",
2469 status);
2470
2471 if (status != WLAN_STATUS_SUCCESS)
2472 goto fail;
2473
2474 if (!pasn->secret) {
2475 wpa_printf(MSG_DEBUG, "PASN: FILS: Missing secret");
2476 goto fail;
2477 }
2478
2479 if (random_get_bytes(fils->anonce, FILS_NONCE_LEN) < 0) {
2480 wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ANonce");
2481 goto fail;
2482 }
2483
2484 wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS ANonce",
2485 fils->anonce, FILS_NONCE_LEN);
2486
Sunil Ravi99c035e2024-07-12 01:42:03 +00002487 ret = fils_rmsk_to_pmk(pasn_get_akmp(pasn), msk, msk_len, fils->nonce,
Hai Shalom60840252021-02-19 19:02:11 -08002488 fils->anonce, NULL, 0, pmk, &pmk_len);
2489 if (ret) {
2490 wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
2491 goto fail;
2492 }
2493
2494 ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr,
2495 wpabuf_head(pasn->secret),
2496 wpabuf_len(pasn->secret),
Sunil Ravi99c035e2024-07-12 01:42:03 +00002497 pasn_get_ptk(sta->pasn), pasn_get_akmp(sta->pasn),
Sunil Ravic0f5d412024-09-11 22:12:49 +00002498 pasn_get_cipher(sta->pasn), sta->pasn->kdk_len,
2499 sta->pasn->kek_len);
Hai Shalom60840252021-02-19 19:02:11 -08002500 if (ret) {
2501 wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PTK");
2502 goto fail;
2503 }
2504
Sunil Ravi89eba102022-09-13 21:04:37 -07002505 if (pasn->secure_ltf) {
Sunil Ravi99c035e2024-07-12 01:42:03 +00002506 ret = wpa_ltf_keyseed(pasn_get_ptk(pasn), pasn_get_akmp(pasn),
2507 pasn_get_cipher(pasn));
Sunil Ravi89eba102022-09-13 21:04:37 -07002508 if (ret) {
2509 wpa_printf(MSG_DEBUG,
2510 "PASN: FILS: Failed to derive LTF keyseed");
2511 goto fail;
2512 }
2513 }
2514
Hai Shalom60840252021-02-19 19:02:11 -08002515 wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
2516
2517 wpabuf_free(pasn->secret);
2518 pasn->secret = NULL;
2519
2520 fils->erp_resp = erp_resp;
Sunil Ravi77d572f2023-01-17 23:58:31 +00002521 ret = handle_auth_pasn_resp(sta->pasn, hapd->own_addr, sta->addr, NULL,
2522 WLAN_STATUS_SUCCESS);
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00002523 wpabuf_free(pasn->frame);
2524 pasn->frame = NULL;
Hai Shalom60840252021-02-19 19:02:11 -08002525 fils->erp_resp = NULL;
2526
2527 if (ret) {
2528 wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to send response");
2529 goto fail;
2530 }
2531
2532 fils->state = PASN_FILS_STATE_COMPLETE;
2533 return;
2534fail:
2535 ap_free_sta(hapd, sta);
2536}
2537
2538
2539static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta,
2540 struct wpabuf *wd)
2541{
Hai Shaloma20dcd72022-02-04 13:43:00 -08002542#ifdef CONFIG_NO_RADIUS
2543 wpa_printf(MSG_DEBUG, "PASN: FILS: RADIUS is not configured. Fail");
2544 return -1;
2545#else /* CONFIG_NO_RADIUS */
Hai Shalom60840252021-02-19 19:02:11 -08002546 struct pasn_data *pasn = sta->pasn;
Sunil Ravi77d572f2023-01-17 23:58:31 +00002547 struct pasn_fils *fils = &pasn->fils;
Hai Shalom60840252021-02-19 19:02:11 -08002548 struct ieee802_11_elems elems;
2549 struct wpa_ie_data rsne_data;
2550 struct wpabuf *fils_wd;
2551 const u8 *data;
2552 size_t buf_len;
2553 u16 alg, seq, status;
2554 int ret;
2555
2556 if (fils->state != PASN_FILS_STATE_NONE) {
2557 wpa_printf(MSG_DEBUG, "PASN: FILS: Not expecting wrapped data");
2558 return -1;
2559 }
2560
2561 if (!wd) {
2562 wpa_printf(MSG_DEBUG, "PASN: FILS: No wrapped data");
2563 return -1;
2564 }
2565
2566 data = wpabuf_head_u8(wd);
2567 buf_len = wpabuf_len(wd);
2568
2569 if (buf_len < 6) {
Hai Shaloma20dcd72022-02-04 13:43:00 -08002570 wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short. len=%zu",
Hai Shalom60840252021-02-19 19:02:11 -08002571 buf_len);
2572 return -1;
2573 }
2574
2575 alg = WPA_GET_LE16(data);
2576 seq = WPA_GET_LE16(data + 2);
2577 status = WPA_GET_LE16(data + 4);
2578
2579 wpa_printf(MSG_DEBUG, "PASN: FILS: alg=%u, seq=%u, status=%u",
2580 alg, seq, status);
2581
2582 if (alg != WLAN_AUTH_FILS_SK || seq != 1 ||
2583 status != WLAN_STATUS_SUCCESS) {
2584 wpa_printf(MSG_DEBUG,
2585 "PASN: FILS: Dropping peer authentication");
2586 return -1;
2587 }
2588
2589 data += 6;
2590 buf_len -= 6;
2591
2592 if (ieee802_11_parse_elems(data, buf_len, &elems, 1) == ParseFailed) {
2593 wpa_printf(MSG_DEBUG, "PASN: FILS: Could not parse elements");
2594 return -1;
2595 }
2596
2597 if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce ||
Sunil Ravi77d572f2023-01-17 23:58:31 +00002598 !elems.wrapped_data || !elems.fils_session) {
Hai Shalom60840252021-02-19 19:02:11 -08002599 wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs");
2600 return -1;
2601 }
2602
2603 ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
2604 &rsne_data);
2605 if (ret) {
Sunil Ravi77d572f2023-01-17 23:58:31 +00002606 wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RSNE");
Hai Shalom60840252021-02-19 19:02:11 -08002607 return -1;
2608 }
2609
2610 ret = wpa_pasn_validate_rsne(&rsne_data);
2611 if (ret) {
2612 wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE");
2613 return -1;
2614 }
2615
2616 if (rsne_data.num_pmkid) {
2617 wpa_printf(MSG_DEBUG,
2618 "PASN: FILS: Not expecting PMKID in RSNE");
2619 return -1;
2620 }
2621
2622 wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", elems.fils_nonce,
2623 FILS_NONCE_LEN);
2624 os_memcpy(fils->nonce, elems.fils_nonce, FILS_NONCE_LEN);
2625
2626 wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", elems.fils_session,
2627 FILS_SESSION_LEN);
2628 os_memcpy(fils->session, elems.fils_session, FILS_SESSION_LEN);
2629
Sunil Ravib0ac25f2024-07-12 01:42:03 +00002630 fils_wd = ieee802_11_defrag(elems.wrapped_data, elems.wrapped_data_len,
2631 true);
Hai Shalom60840252021-02-19 19:02:11 -08002632
2633 if (!fils_wd) {
2634 wpa_printf(MSG_DEBUG, "PASN: FILS: Missing wrapped data");
2635 return -1;
2636 }
2637
2638 if (!sta->eapol_sm)
2639 sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
2640
2641 wpa_printf(MSG_DEBUG,
2642 "PASN: FILS: Forward EAP-Initiate/Re-auth to AS");
2643
2644 ieee802_1x_encapsulate_radius(hapd, sta, wpabuf_head(fils_wd),
2645 wpabuf_len(fils_wd));
2646
2647 sta->flags |= WLAN_STA_PENDING_PASN_FILS_ERP;
2648
2649 fils->state = PASN_FILS_STATE_PENDING_AS;
2650
2651 /*
2652 * Calculate pending PMKID here so that we do not need to maintain a
2653 * copy of the EAP-Initiate/Reautt message.
2654 */
Sunil Ravi99c035e2024-07-12 01:42:03 +00002655 fils_pmkid_erp(pasn_get_akmp(pasn),
2656 wpabuf_head(fils_wd), wpabuf_len(fils_wd),
Hai Shalom60840252021-02-19 19:02:11 -08002657 fils->erp_pmkid);
2658
2659 wpabuf_free(fils_wd);
2660 return 0;
Hai Shaloma20dcd72022-02-04 13:43:00 -08002661#endif /* CONFIG_NO_RADIUS */
Hai Shalom60840252021-02-19 19:02:11 -08002662}
2663
2664#endif /* CONFIG_FILS */
2665
2666
Sunil Ravi77d572f2023-01-17 23:58:31 +00002667static int hapd_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len,
2668 int noack, unsigned int freq, unsigned int wait)
Hai Shalom60840252021-02-19 19:02:11 -08002669{
Sunil Ravi77d572f2023-01-17 23:58:31 +00002670 struct hostapd_data *hapd = ctx;
2671
2672 return hostapd_drv_send_mlme(hapd, data, data_len, 0, NULL, 0, 0);
2673}
2674
2675
2676static void hapd_initialize_pasn(struct hostapd_data *hapd,
2677 struct sta_info *sta)
2678{
2679 struct pasn_data *pasn = sta->pasn;
2680
Sunil Ravi99c035e2024-07-12 01:42:03 +00002681 pasn_register_callbacks(pasn, hapd, hapd_pasn_send_mlme, NULL);
2682 pasn_set_bssid(pasn, hapd->own_addr);
2683 pasn_set_own_addr(pasn, hapd->own_addr);
2684 pasn_set_peer_addr(pasn, sta->addr);
2685 pasn_set_wpa_key_mgmt(pasn, hapd->conf->wpa_key_mgmt);
2686 pasn_set_rsn_pairwise(pasn, hapd->conf->rsn_pairwise);
Sunil Ravi77d572f2023-01-17 23:58:31 +00002687 pasn->pasn_groups = hapd->conf->pasn_groups;
Sunil Ravi640215c2023-06-28 23:08:09 +00002688 pasn->noauth = hapd->conf->pasn_noauth;
Sunil Ravi99c035e2024-07-12 01:42:03 +00002689 if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP)
2690 pasn_enable_kdk_derivation(pasn);
2691
Sunil Ravi77d572f2023-01-17 23:58:31 +00002692#ifdef CONFIG_TESTING_OPTIONS
2693 pasn->corrupt_mic = hapd->conf->pasn_corrupt_mic;
2694 if (hapd->conf->force_kdk_derivation)
Sunil Ravi99c035e2024-07-12 01:42:03 +00002695 pasn_enable_kdk_derivation(pasn);
Sunil Ravi77d572f2023-01-17 23:58:31 +00002696#endif /* CONFIG_TESTING_OPTIONS */
2697 pasn->use_anti_clogging = use_anti_clogging(hapd);
Sunil Ravi99c035e2024-07-12 01:42:03 +00002698 pasn_set_password(pasn, sae_get_password(hapd, sta, NULL, NULL,
2699 &pasn->pt, NULL));
Sunil Ravi77d572f2023-01-17 23:58:31 +00002700 pasn->rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &pasn->rsn_ie_len);
Sunil Ravi99c035e2024-07-12 01:42:03 +00002701 pasn_set_rsnxe_ie(pasn, hostapd_wpa_ie(hapd, WLAN_EID_RSNX));
Sunil Ravi77d572f2023-01-17 23:58:31 +00002702 pasn->disable_pmksa_caching = hapd->conf->disable_pmksa_caching;
Sunil Ravi99c035e2024-07-12 01:42:03 +00002703 pasn_set_responder_pmksa(pasn,
2704 wpa_auth_get_pmksa_cache(hapd->wpa_auth));
Sunil Ravi77d572f2023-01-17 23:58:31 +00002705
2706 pasn->comeback_after = hapd->conf->pasn_comeback_after;
2707 pasn->comeback_idx = hapd->comeback_idx;
2708 pasn->comeback_key = hapd->comeback_key;
2709 pasn->comeback_pending_idx = hapd->comeback_pending_idx;
Hai Shalom60840252021-02-19 19:02:11 -08002710}
2711
2712
Sunil Ravi89eba102022-09-13 21:04:37 -07002713static int pasn_set_keys_from_cache(struct hostapd_data *hapd,
2714 const u8 *own_addr, const u8 *sta_addr,
2715 int cipher, int akmp)
2716{
2717 struct ptksa_cache_entry *entry;
2718
2719 entry = ptksa_cache_get(hapd->ptksa, sta_addr, cipher);
2720 if (!entry) {
2721 wpa_printf(MSG_DEBUG, "PASN: peer " MACSTR
2722 " not present in PTKSA cache", MAC2STR(sta_addr));
2723 return -1;
2724 }
2725
Sunil Ravib0ac25f2024-07-12 01:42:03 +00002726 if (!ether_addr_equal(entry->own_addr, own_addr)) {
Sunil Ravi89eba102022-09-13 21:04:37 -07002727 wpa_printf(MSG_DEBUG,
2728 "PASN: own addr " MACSTR " and PTKSA entry own addr "
2729 MACSTR " differ",
2730 MAC2STR(own_addr), MAC2STR(entry->own_addr));
2731 return -1;
2732 }
2733
2734 wpa_printf(MSG_DEBUG, "PASN: " MACSTR " present in PTKSA cache",
2735 MAC2STR(sta_addr));
2736 hostapd_drv_set_secure_ranging_ctx(hapd, own_addr, sta_addr, cipher,
2737 entry->ptk.tk_len, entry->ptk.tk,
2738 entry->ptk.ltf_keyseed_len,
2739 entry->ptk.ltf_keyseed, 0);
2740
2741 return 0;
2742}
2743
2744
Sunil Ravi77d572f2023-01-17 23:58:31 +00002745static void hapd_pasn_update_params(struct hostapd_data *hapd,
2746 struct sta_info *sta,
2747 const struct ieee80211_mgmt *mgmt,
2748 size_t len)
Hai Shalom60840252021-02-19 19:02:11 -08002749{
Sunil Ravi77d572f2023-01-17 23:58:31 +00002750 struct pasn_data *pasn = sta->pasn;
Hai Shalom60840252021-02-19 19:02:11 -08002751 struct ieee802_11_elems elems;
2752 struct wpa_ie_data rsn_data;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00002753#ifdef CONFIG_FILS
Hai Shalom60840252021-02-19 19:02:11 -08002754 struct wpa_pasn_params_data pasn_params;
Hai Shalom60840252021-02-19 19:02:11 -08002755 struct wpabuf *wrapped_data = NULL;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00002756#endif /* CONFIG_FILS */
Sunil Ravi99c035e2024-07-12 01:42:03 +00002757 int akmp;
Hai Shalom60840252021-02-19 19:02:11 -08002758
2759 if (ieee802_11_parse_elems(mgmt->u.auth.variable,
2760 len - offsetof(struct ieee80211_mgmt,
2761 u.auth.variable),
2762 &elems, 0) == ParseFailed) {
2763 wpa_printf(MSG_DEBUG,
2764 "PASN: Failed parsing Authentication frame");
Sunil Ravi77d572f2023-01-17 23:58:31 +00002765 return;
Hai Shalom60840252021-02-19 19:02:11 -08002766 }
2767
Sunil Ravi77d572f2023-01-17 23:58:31 +00002768 if (!elems.rsn_ie ||
2769 wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
2770 &rsn_data)) {
2771 wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE");
2772 return;
2773 }
2774
2775 if (!(rsn_data.key_mgmt & pasn->wpa_key_mgmt) ||
2776 !(rsn_data.pairwise_cipher & pasn->rsn_pairwise)) {
2777 wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
2778 return;
2779 }
2780
Sunil Ravi99c035e2024-07-12 01:42:03 +00002781 pasn_set_akmp(pasn, rsn_data.key_mgmt);
2782 pasn_set_cipher(pasn, rsn_data.pairwise_cipher);
Sunil Ravi77d572f2023-01-17 23:58:31 +00002783
Sunil Ravi7f769292024-07-23 22:21:32 +00002784 if (pasn->derive_kdk &&
2785 !ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
2786 WLAN_RSNX_CAPAB_SECURE_LTF))
2787 pasn_disable_kdk_derivation(pasn);
2788#ifdef CONFIG_TESTING_OPTIONS
2789 if (hapd->conf->force_kdk_derivation)
2790 pasn_enable_kdk_derivation(pasn);
2791#endif /* CONFIG_TESTING_OPTIONS */
Sunil Ravi99c035e2024-07-12 01:42:03 +00002792 akmp = pasn_get_akmp(pasn);
2793
2794 if (wpa_key_mgmt_ft(akmp) && rsn_data.num_pmkid) {
Sunil Ravi77d572f2023-01-17 23:58:31 +00002795#ifdef CONFIG_IEEE80211R_AP
2796 pasn->pmk_r1_len = 0;
2797 wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr,
2798 rsn_data.pmkid,
2799 pasn->pmk_r1, &pasn->pmk_r1_len, NULL,
2800 NULL, NULL, NULL,
2801 NULL, NULL, NULL);
2802#endif /* CONFIG_IEEE80211R_AP */
2803 }
2804#ifdef CONFIG_FILS
Sunil Ravi99c035e2024-07-12 01:42:03 +00002805 if (akmp != WPA_KEY_MGMT_FILS_SHA256 &&
2806 akmp != WPA_KEY_MGMT_FILS_SHA384)
Sunil Ravi77d572f2023-01-17 23:58:31 +00002807 return;
2808 if (!elems.pasn_params ||
2809 wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
2810 elems.pasn_params_len + 3,
2811 false, &pasn_params)) {
Hai Shalom60840252021-02-19 19:02:11 -08002812 wpa_printf(MSG_DEBUG,
Sunil Ravi77d572f2023-01-17 23:58:31 +00002813 "PASN: Failed validation of PASN Parameters element");
2814 return;
Hai Shalom60840252021-02-19 19:02:11 -08002815 }
Hai Shalom60840252021-02-19 19:02:11 -08002816 if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +00002817 wrapped_data = ieee802_11_defrag(elems.wrapped_data,
2818 elems.wrapped_data_len, true);
Hai Shalom60840252021-02-19 19:02:11 -08002819 if (!wrapped_data) {
2820 wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
Sunil Ravi77d572f2023-01-17 23:58:31 +00002821 return;
Hai Shalom60840252021-02-19 19:02:11 -08002822 }
Sunil Ravi77d572f2023-01-17 23:58:31 +00002823 if (pasn_wd_handle_fils(hapd, sta, wrapped_data))
2824 wpa_printf(MSG_DEBUG,
2825 "PASN: Failed processing FILS wrapped data");
2826 else
2827 pasn->fils_wd_valid = true;
Hai Shalom60840252021-02-19 19:02:11 -08002828 }
Sunil Ravi77d572f2023-01-17 23:58:31 +00002829 wpabuf_free(wrapped_data);
2830#endif /* CONFIG_FILS */
Hai Shalom60840252021-02-19 19:02:11 -08002831}
2832
2833
2834static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta,
2835 const struct ieee80211_mgmt *mgmt, size_t len,
2836 u16 trans_seq, u16 status)
2837{
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00002838 int ret;
2839#ifdef CONFIG_P2P
2840 struct ieee802_11_elems elems;
2841
2842 if (len < 24) {
2843 wpa_printf(MSG_DEBUG, "PASN: Too short Management frame");
2844 return;
2845 }
2846
2847 if (ieee802_11_parse_elems(mgmt->u.auth.variable,
2848 len - offsetof(struct ieee80211_mgmt,
2849 u.auth.variable),
2850 &elems, 1) == ParseFailed) {
2851 wpa_printf(MSG_DEBUG,
2852 "PASN: Failed parsing Authentication frame");
2853 return;
2854 }
2855
2856 if ((hapd->conf->p2p & (P2P_ENABLED | P2P_GROUP_OWNER)) ==
2857 (P2P_ENABLED | P2P_GROUP_OWNER) &&
2858 hapd->p2p && elems.p2p2_ie && elems.p2p2_ie_len) {
2859 p2p_pasn_auth_rx(hapd->p2p, mgmt, len, hapd->iface->freq);
2860 return;
2861 }
2862#endif /* CONFIG_P2P */
2863
Hai Shalom60840252021-02-19 19:02:11 -08002864 if (hapd->conf->wpa != WPA_PROTO_RSN) {
2865 wpa_printf(MSG_INFO, "PASN: RSN is not configured");
2866 return;
2867 }
2868
2869 wpa_printf(MSG_INFO, "PASN authentication: sta=" MACSTR,
2870 MAC2STR(sta->addr));
2871
2872 if (trans_seq == 1) {
2873 if (sta->pasn) {
2874 wpa_printf(MSG_DEBUG,
2875 "PASN: Not expecting transaction == 1");
2876 return;
2877 }
2878
2879 if (status != WLAN_STATUS_SUCCESS) {
2880 wpa_printf(MSG_DEBUG,
2881 "PASN: Failure status in transaction == 1");
2882 return;
2883 }
2884
Sunil Ravi99c035e2024-07-12 01:42:03 +00002885 sta->pasn = pasn_data_init();
Hai Shalom60840252021-02-19 19:02:11 -08002886 if (!sta->pasn) {
2887 wpa_printf(MSG_DEBUG,
2888 "PASN: Failed to allocate PASN context");
2889 return;
2890 }
2891
Sunil Ravi77d572f2023-01-17 23:58:31 +00002892 hapd_initialize_pasn(hapd, sta);
2893
2894 hapd_pasn_update_params(hapd, sta, mgmt, len);
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00002895 ret = handle_auth_pasn_1(sta->pasn, hapd->own_addr, sta->addr,
2896 mgmt, len, false);
2897 wpabuf_free(sta->pasn->frame);
2898 sta->pasn->frame = NULL;
2899 if (ret < 0)
Sunil Ravi77d572f2023-01-17 23:58:31 +00002900 ap_free_sta(hapd, sta);
Hai Shalom60840252021-02-19 19:02:11 -08002901 } else if (trans_seq == 3) {
2902 if (!sta->pasn) {
2903 wpa_printf(MSG_DEBUG,
2904 "PASN: Not expecting transaction == 3");
2905 return;
2906 }
2907
2908 if (status != WLAN_STATUS_SUCCESS) {
2909 wpa_printf(MSG_DEBUG,
2910 "PASN: Failure status in transaction == 3");
2911 ap_free_sta_pasn(hapd, sta);
2912 return;
2913 }
2914
Sunil Ravi77d572f2023-01-17 23:58:31 +00002915 if (handle_auth_pasn_3(sta->pasn, hapd->own_addr,
2916 sta->addr, mgmt, len) == 0) {
2917 ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr,
Sunil Ravi99c035e2024-07-12 01:42:03 +00002918 pasn_get_cipher(sta->pasn), 43200,
2919 pasn_get_ptk(sta->pasn), NULL, NULL,
2920 pasn_get_akmp(sta->pasn));
Sunil Ravi77d572f2023-01-17 23:58:31 +00002921
2922 pasn_set_keys_from_cache(hapd, hapd->own_addr,
Sunil Ravi99c035e2024-07-12 01:42:03 +00002923 sta->addr,
2924 pasn_get_cipher(sta->pasn),
2925 pasn_get_akmp(sta->pasn));
Sunil Ravi77d572f2023-01-17 23:58:31 +00002926 }
2927 ap_free_sta(hapd, sta);
Hai Shalom60840252021-02-19 19:02:11 -08002928 } else {
2929 wpa_printf(MSG_DEBUG,
2930 "PASN: Invalid transaction %u - ignore", trans_seq);
2931 }
2932}
2933
2934#endif /* CONFIG_PASN */
2935
2936
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002937static void handle_auth(struct hostapd_data *hapd,
Hai Shalom74f70d42019-02-11 14:42:39 -08002938 const struct ieee80211_mgmt *mgmt, size_t len,
Hai Shalom021b0b52019-04-10 11:17:58 -07002939 int rssi, int from_queue)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002940{
2941 u16 auth_alg, auth_transaction, status_code;
2942 u16 resp = WLAN_STATUS_SUCCESS;
2943 struct sta_info *sta = NULL;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08002944 int res, reply_res;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002945 u16 fc;
2946 const u8 *challenge = NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002947 u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
2948 size_t resp_ies_len = 0;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002949 u16 seq_ctrl;
Hai Shalomfdcde762020-04-02 11:19:20 -07002950 struct radius_sta rad_info;
Sunil Ravi7f769292024-07-23 22:21:32 +00002951 const u8 *dst, *sa;
Sunil Ravi99c035e2024-07-12 01:42:03 +00002952#ifdef CONFIG_IEEE80211BE
Sunil Ravi2a14cf12023-11-21 00:54:38 +00002953 bool mld_sta = false;
Sunil Ravi99c035e2024-07-12 01:42:03 +00002954#endif /* CONFIG_IEEE80211BE */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002955
2956 if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08002957 wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
2958 (unsigned long) len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002959 return;
2960 }
2961
Dmitry Shmidt8da800a2013-04-24 12:57:01 -07002962#ifdef CONFIG_TESTING_OPTIONS
Dmitry Shmidt7832adb2014-04-29 10:53:02 -07002963 if (hapd->iconf->ignore_auth_probability > 0.0 &&
Dmitry Shmidt8da800a2013-04-24 12:57:01 -07002964 drand48() < hapd->iconf->ignore_auth_probability) {
2965 wpa_printf(MSG_INFO,
2966 "TESTING: ignoring auth frame from " MACSTR,
2967 MAC2STR(mgmt->sa));
2968 return;
2969 }
2970#endif /* CONFIG_TESTING_OPTIONS */
2971
Sunil Ravi2a14cf12023-11-21 00:54:38 +00002972 sa = mgmt->sa;
2973#ifdef CONFIG_IEEE80211BE
2974 /*
2975 * Handle MLO authentication before the station is added to hostapd and
2976 * the driver so that the station MLD MAC address would be used in both
2977 * hostapd and the driver.
2978 */
2979 sa = hostapd_process_ml_auth(hapd, mgmt, len);
2980 if (sa)
2981 mld_sta = true;
2982 else
2983 sa = mgmt->sa;
2984#endif /* CONFIG_IEEE80211BE */
2985
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002986 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
2987 auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
2988 status_code = le_to_host16(mgmt->u.auth.status_code);
2989 fc = le_to_host16(mgmt->frame_control);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002990 seq_ctrl = le_to_host16(mgmt->seq_ctrl);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002991
2992 if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) +
2993 2 + WLAN_AUTH_CHALLENGE_LEN &&
2994 mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE &&
2995 mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN)
2996 challenge = &mgmt->u.auth.variable[2];
2997
2998 wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002999 "auth_transaction=%d status_code=%d wep=%d%s "
Hai Shalom021b0b52019-04-10 11:17:58 -07003000 "seq_ctrl=0x%x%s%s",
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003001 MAC2STR(sa), auth_alg, auth_transaction,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003002 status_code, !!(fc & WLAN_FC_ISWEP),
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003003 challenge ? " challenge" : "",
Hai Shalom021b0b52019-04-10 11:17:58 -07003004 seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "",
3005 from_queue ? " (from queue)" : "");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003006
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08003007#ifdef CONFIG_NO_RC4
3008 if (auth_alg == WLAN_AUTH_SHARED_KEY) {
3009 wpa_printf(MSG_INFO,
3010 "Unsupported authentication algorithm (%d)",
3011 auth_alg);
3012 resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
3013 goto fail;
3014 }
3015#endif /* CONFIG_NO_RC4 */
3016
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003017 if (hapd->tkip_countermeasures) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003018 wpa_printf(MSG_DEBUG,
3019 "Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication");
3020 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003021 goto fail;
3022 }
3023
3024 if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) &&
3025 auth_alg == WLAN_AUTH_OPEN) ||
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003026#ifdef CONFIG_IEEE80211R_AP
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08003027 (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003028 auth_alg == WLAN_AUTH_FT) ||
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003029#endif /* CONFIG_IEEE80211R_AP */
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08003030#ifdef CONFIG_SAE
Sunil Ravi7f769292024-07-23 22:21:32 +00003031 (hapd->conf->wpa &&
3032 wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt |
3033 hapd->conf->rsn_override_key_mgmt |
3034 hapd->conf->rsn_override_key_mgmt_2) &&
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08003035 auth_alg == WLAN_AUTH_SAE) ||
3036#endif /* CONFIG_SAE */
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003037#ifdef CONFIG_FILS
3038 (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
3039 auth_alg == WLAN_AUTH_FILS_SK) ||
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003040 (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
3041 hapd->conf->fils_dh_group &&
3042 auth_alg == WLAN_AUTH_FILS_SK_PFS) ||
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003043#endif /* CONFIG_FILS */
Hai Shalom60840252021-02-19 19:02:11 -08003044#ifdef CONFIG_PASN
3045 (hapd->conf->wpa &&
3046 (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) &&
3047 auth_alg == WLAN_AUTH_PASN) ||
3048#endif /* CONFIG_PASN */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003049 ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
3050 auth_alg == WLAN_AUTH_SHARED_KEY))) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08003051 wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)",
3052 auth_alg);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003053 resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
3054 goto fail;
3055 }
3056
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08003057 if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE ||
Hai Shalom60840252021-02-19 19:02:11 -08003058#ifdef CONFIG_PASN
3059 (auth_alg == WLAN_AUTH_PASN && auth_transaction == 3) ||
3060#endif /* CONFIG_PASN */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003061 (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08003062 wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)",
3063 auth_transaction);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003064 resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
3065 goto fail;
3066 }
3067
Sunil Ravib0ac25f2024-07-12 01:42:03 +00003068 if (ether_addr_equal(mgmt->sa, hapd->own_addr)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08003069 wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003070 MAC2STR(sa));
3071 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
3072 goto fail;
3073 }
3074
Sunil Ravi99c035e2024-07-12 01:42:03 +00003075#ifdef CONFIG_IEEE80211BE
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003076 if (mld_sta &&
Sunil Ravib0ac25f2024-07-12 01:42:03 +00003077 (ether_addr_equal(sa, hapd->own_addr) ||
Sunil Ravi99c035e2024-07-12 01:42:03 +00003078 ether_addr_equal(sa, hapd->mld->mld_addr))) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003079 wpa_printf(MSG_INFO,
3080 "Station " MACSTR " not allowed to authenticate",
3081 MAC2STR(sa));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003082 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
3083 goto fail;
3084 }
Sunil Ravi99c035e2024-07-12 01:42:03 +00003085#endif /* CONFIG_IEEE80211BE */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003086
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08003087 if (hapd->conf->no_auth_if_seen_on) {
3088 struct hostapd_data *other;
3089
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003090 other = sta_track_seen_on(hapd->iface, sa,
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08003091 hapd->conf->no_auth_if_seen_on);
3092 if (other) {
3093 u8 *pos;
3094 u32 info;
3095 u8 op_class, channel, phytype;
3096
3097 wpa_printf(MSG_DEBUG, "%s: Reject authentication from "
3098 MACSTR " since STA has been seen on %s",
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003099 hapd->conf->iface, MAC2STR(sa),
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08003100 hapd->conf->no_auth_if_seen_on);
3101
3102 resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION;
3103 pos = &resp_ies[0];
3104 *pos++ = WLAN_EID_NEIGHBOR_REPORT;
3105 *pos++ = 13;
3106 os_memcpy(pos, other->own_addr, ETH_ALEN);
3107 pos += ETH_ALEN;
3108 info = 0; /* TODO: BSSID Information */
3109 WPA_PUT_LE32(pos, info);
3110 pos += 4;
3111 if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
3112 phytype = 8; /* dmg */
3113 else if (other->iconf->ieee80211ac)
3114 phytype = 9; /* vht */
3115 else if (other->iconf->ieee80211n)
3116 phytype = 7; /* ht */
3117 else if (other->iconf->hw_mode ==
3118 HOSTAPD_MODE_IEEE80211A)
3119 phytype = 4; /* ofdm */
3120 else if (other->iconf->hw_mode ==
3121 HOSTAPD_MODE_IEEE80211G)
3122 phytype = 6; /* erp */
3123 else
3124 phytype = 5; /* hrdsss */
3125 if (ieee80211_freq_to_channel_ext(
3126 hostapd_hw_get_freq(other,
3127 other->iconf->channel),
3128 other->iconf->secondary_channel,
3129 other->iconf->ieee80211ac,
3130 &op_class, &channel) == NUM_HOSTAPD_MODES) {
3131 op_class = 0;
3132 channel = other->iconf->channel;
3133 }
3134 *pos++ = op_class;
3135 *pos++ = channel;
3136 *pos++ = phytype;
3137 resp_ies_len = pos - &resp_ies[0];
3138 goto fail;
3139 }
3140 }
3141
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003142 res = ieee802_11_allowed_address(hapd, sa, (const u8 *) mgmt, len,
Hai Shalomfdcde762020-04-02 11:19:20 -07003143 &rad_info);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003144 if (res == HOSTAPD_ACL_REJECT) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003145 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
3146 "Ignore Authentication frame from " MACSTR
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003147 " due to ACL reject", MAC2STR(sa));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003148 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
3149 goto fail;
3150 }
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08003151 if (res == HOSTAPD_ACL_PENDING)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003152 return;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003153
Hai Shalom021b0b52019-04-10 11:17:58 -07003154#ifdef CONFIG_SAE
3155 if (auth_alg == WLAN_AUTH_SAE && !from_queue &&
3156 (auth_transaction == 1 ||
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003157 (auth_transaction == 2 && auth_sae_queued_addr(hapd, sa)))) {
Hai Shalom021b0b52019-04-10 11:17:58 -07003158 /* Handle SAE Authentication commit message through a queue to
3159 * provide more control for postponing the needed heavy
3160 * processing under a possible DoS attack scenario. In addition,
3161 * queue SAE Authentication confirm message if there happens to
3162 * be a queued commit message from the same peer. This is needed
3163 * to avoid reordering Authentication frames within the same
3164 * SAE exchange. */
3165 auth_sae_queue(hapd, mgmt, len, rssi);
3166 return;
3167 }
3168#endif /* CONFIG_SAE */
3169
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003170 sta = ap_get_sta(hapd, sa);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003171 if (sta) {
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003172 sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
Hai Shalom74f70d42019-02-11 14:42:39 -08003173 sta->ft_over_ds = 0;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003174 if ((fc & WLAN_FC_RETRY) &&
3175 sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
3176 sta->last_seq_ctrl == seq_ctrl &&
3177 sta->last_subtype == WLAN_FC_STYPE_AUTH) {
3178 hostapd_logger(hapd, sta->addr,
3179 HOSTAPD_MODULE_IEEE80211,
3180 HOSTAPD_LEVEL_DEBUG,
3181 "Drop repeated authentication frame seq_ctrl=0x%x",
3182 seq_ctrl);
3183 return;
3184 }
Hai Shalom60840252021-02-19 19:02:11 -08003185#ifdef CONFIG_PASN
3186 if (auth_alg == WLAN_AUTH_PASN &&
3187 (sta->flags & WLAN_STA_ASSOC)) {
3188 wpa_printf(MSG_DEBUG,
3189 "PASN: auth: Existing station: " MACSTR,
3190 MAC2STR(sta->addr));
3191 return;
3192 }
3193#endif /* CONFIG_PASN */
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003194 } else {
3195#ifdef CONFIG_MESH
3196 if (hapd->conf->mesh & MESH_ENABLED) {
3197 /* if the mesh peer is not available, we don't do auth.
3198 */
3199 wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003200 " not yet known - drop Authentication frame",
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003201 MAC2STR(sa));
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003202 /*
3203 * Save a copy of the frame so that it can be processed
3204 * if a new peer entry is added shortly after this.
3205 */
3206 wpabuf_free(hapd->mesh_pending_auth);
3207 hapd->mesh_pending_auth = wpabuf_alloc_copy(mgmt, len);
3208 os_get_reltime(&hapd->mesh_pending_auth_time);
3209 return;
3210 }
3211#endif /* CONFIG_MESH */
3212
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003213 sta = ap_sta_add(hapd, sa);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003214 if (!sta) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003215 wpa_printf(MSG_DEBUG, "ap_sta_add() failed");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003216 resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
3217 goto fail;
3218 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003219 }
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003220
3221#ifdef CONFIG_IEEE80211BE
Sunil Ravi99c035e2024-07-12 01:42:03 +00003222 /* Set the non-AP MLD information based on the initial Authentication
3223 * frame. Once the STA entry has been added to the driver, the driver
3224 * will translate addresses in the frame and we need to avoid overriding
3225 * peer_addr based on mgmt->sa which would have been translated to the
3226 * MLD MAC address. */
3227 if (!sta->added_unassoc && auth_transaction == 1) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +00003228 ap_sta_free_sta_profile(&sta->mld_info);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003229 os_memset(&sta->mld_info, 0, sizeof(sta->mld_info));
3230
3231 if (mld_sta) {
3232 u8 link_id = hapd->mld_link_id;
3233
Sunil Ravib0ac25f2024-07-12 01:42:03 +00003234 ap_sta_set_mld(sta, true);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003235 sta->mld_assoc_link_id = link_id;
3236
3237 /*
3238 * Set the MLD address as the station address and the
3239 * station addresses.
3240 */
3241 os_memcpy(sta->mld_info.common_info.mld_addr, sa,
3242 ETH_ALEN);
3243 os_memcpy(sta->mld_info.links[link_id].peer_addr,
3244 mgmt->sa, ETH_ALEN);
3245 os_memcpy(sta->mld_info.links[link_id].local_addr,
3246 hapd->own_addr, ETH_ALEN);
3247 }
3248 }
3249#endif /* CONFIG_IEEE80211BE */
3250
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003251 sta->last_seq_ctrl = seq_ctrl;
3252 sta->last_subtype = WLAN_FC_STYPE_AUTH;
Hai Shalom74f70d42019-02-11 14:42:39 -08003253#ifdef CONFIG_MBO
3254 sta->auth_rssi = rssi;
3255#endif /* CONFIG_MBO */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003256
Hai Shalomfdcde762020-04-02 11:19:20 -07003257 res = ieee802_11_set_radius_info(hapd, sta, res, &rad_info);
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08003258 if (res) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003259 wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed");
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003260 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
3261 goto fail;
3262 }
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07003263
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003264 sta->flags &= ~WLAN_STA_PREAUTH;
3265 ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
3266
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003267 /*
3268 * If the driver supports full AP client state, add a station to the
3269 * driver before sending authentication reply to make sure the driver
3270 * has resources, and not to go through the entire authentication and
3271 * association handshake, and fail it at the end.
3272 *
3273 * If this is not the first transaction, in a multi-step authentication
3274 * algorithm, the station already exists in the driver
3275 * (sta->added_unassoc = 1) so skip it.
3276 *
3277 * In mesh mode, the station was already added to the driver when the
3278 * NEW_PEER_CANDIDATE event is received.
Dmitry Shmidtabb90a32016-12-05 15:34:39 -08003279 *
3280 * If PMF was negotiated for the existing association, skip this to
3281 * avoid dropping the STA entry and the associated keys. This is needed
3282 * to allow the original connection work until the attempt can complete
3283 * (re)association, so that unprotected Authentication frame cannot be
3284 * used to bypass PMF protection.
Hai Shalom60840252021-02-19 19:02:11 -08003285 *
3286 * PASN authentication does not require adding/removing station to the
3287 * driver so skip this flow in case of PASN authentication.
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003288 */
3289 if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) &&
Dmitry Shmidtabb90a32016-12-05 15:34:39 -08003290 (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) &&
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003291 !(hapd->conf->mesh & MESH_ENABLED) &&
Hai Shalom60840252021-02-19 19:02:11 -08003292 !(sta->added_unassoc) && auth_alg != WLAN_AUTH_PASN) {
Hai Shalomb755a2a2020-04-23 21:49:02 -07003293 if (ap_sta_re_add(hapd, sta) < 0) {
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003294 resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
3295 goto fail;
3296 }
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003297 }
3298
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003299 switch (auth_alg) {
3300 case WLAN_AUTH_OPEN:
3301 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
3302 HOSTAPD_LEVEL_DEBUG,
3303 "authentication OK (open system)");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003304 sta->flags |= WLAN_STA_AUTH;
3305 wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
3306 sta->auth_alg = WLAN_AUTH_OPEN;
3307 mlme_authenticate_indication(hapd, sta);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003308 break;
Hai Shalomfdcde762020-04-02 11:19:20 -07003309#ifdef CONFIG_WEP
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08003310#ifndef CONFIG_NO_RC4
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003311 case WLAN_AUTH_SHARED_KEY:
3312 resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
3313 fc & WLAN_FC_ISWEP);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003314 if (resp != 0)
3315 wpa_printf(MSG_DEBUG,
3316 "auth_shared_key() failed: status=%d", resp);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003317 sta->auth_alg = WLAN_AUTH_SHARED_KEY;
3318 mlme_authenticate_indication(hapd, sta);
3319 if (sta->challenge && auth_transaction == 1) {
3320 resp_ies[0] = WLAN_EID_CHALLENGE;
3321 resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN;
3322 os_memcpy(resp_ies + 2, sta->challenge,
3323 WLAN_AUTH_CHALLENGE_LEN);
3324 resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN;
3325 }
3326 break;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08003327#endif /* CONFIG_NO_RC4 */
Hai Shalomfdcde762020-04-02 11:19:20 -07003328#endif /* CONFIG_WEP */
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003329#ifdef CONFIG_IEEE80211R_AP
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003330 case WLAN_AUTH_FT:
3331 sta->auth_alg = WLAN_AUTH_FT;
3332 if (sta->wpa_sm == NULL)
3333 sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07003334 sta->addr, NULL);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003335 if (sta->wpa_sm == NULL) {
3336 wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
3337 "state machine");
3338 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
3339 goto fail;
3340 }
Sunil Ravi7f769292024-07-23 22:21:32 +00003341 wpa_ft_process_auth(sta->wpa_sm,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003342 auth_transaction, mgmt->u.auth.variable,
3343 len - IEEE80211_HDRLEN -
3344 sizeof(mgmt->u.auth),
3345 handle_auth_ft_finish, hapd);
3346 /* handle_auth_ft_finish() callback will complete auth. */
3347 return;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003348#endif /* CONFIG_IEEE80211R_AP */
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08003349#ifdef CONFIG_SAE
3350 case WLAN_AUTH_SAE:
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08003351#ifdef CONFIG_MESH
3352 if (status_code == WLAN_STATUS_SUCCESS &&
3353 hapd->conf->mesh & MESH_ENABLED) {
3354 if (sta->wpa_sm == NULL)
3355 sta->wpa_sm =
3356 wpa_auth_sta_init(hapd->wpa_auth,
3357 sta->addr, NULL);
3358 if (sta->wpa_sm == NULL) {
3359 wpa_printf(MSG_DEBUG,
3360 "SAE: Failed to initialize WPA state machine");
3361 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
3362 goto fail;
3363 }
3364 }
3365#endif /* CONFIG_MESH */
3366 handle_auth_sae(hapd, sta, mgmt, len, auth_transaction,
3367 status_code);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08003368 return;
3369#endif /* CONFIG_SAE */
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003370#ifdef CONFIG_FILS
3371 case WLAN_AUTH_FILS_SK:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003372 case WLAN_AUTH_FILS_SK_PFS:
3373 handle_auth_fils(hapd, sta, mgmt->u.auth.variable,
3374 len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth),
3375 auth_alg, auth_transaction, status_code,
3376 handle_auth_fils_finish);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08003377 return;
3378#endif /* CONFIG_FILS */
Hai Shalom60840252021-02-19 19:02:11 -08003379#ifdef CONFIG_PASN
3380 case WLAN_AUTH_PASN:
3381 handle_auth_pasn(hapd, sta, mgmt, len, auth_transaction,
3382 status_code);
3383 return;
3384#endif /* CONFIG_PASN */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003385 }
3386
3387 fail:
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003388 dst = mgmt->sa;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003389
3390#ifdef CONFIG_IEEE80211BE
Sunil Ravi7f769292024-07-23 22:21:32 +00003391 if (ap_sta_is_mld(hapd, sta))
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003392 dst = sta->addr;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003393#endif /* CONFIG_IEEE80211BE */
3394
Sunil Ravi7f769292024-07-23 22:21:32 +00003395 reply_res = send_auth_reply(hapd, sta, dst, auth_alg,
Hai Shaloma20dcd72022-02-04 13:43:00 -08003396 auth_alg == WLAN_AUTH_SAE ?
3397 auth_transaction : auth_transaction + 1,
3398 resp, resp_ies, resp_ies_len,
3399 "handle-auth");
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08003400
3401 if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
3402 reply_res != WLAN_STATUS_SUCCESS)) {
3403 hostapd_drv_sta_remove(hapd, sta->addr);
3404 sta->added_unassoc = 0;
3405 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003406}
3407
3408
Sunil Ravi77d572f2023-01-17 23:58:31 +00003409static u8 hostapd_max_bssid_indicator(struct hostapd_data *hapd)
3410{
3411 size_t num_bss_nontx;
3412 u8 max_bssid_ind = 0;
3413
3414 if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1)
3415 return 0;
3416
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00003417 if (hapd->iface->conf->mbssid_max > 0)
3418 num_bss_nontx = hapd->iface->conf->mbssid_max - 1;
3419 else
3420 num_bss_nontx = hapd->iface->conf->num_bss - 1;
Sunil Ravi77d572f2023-01-17 23:58:31 +00003421 while (num_bss_nontx > 0) {
3422 max_bssid_ind++;
3423 num_bss_nontx >>= 1;
3424 }
3425 return max_bssid_ind;
3426}
3427
3428
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003429static u32 hostapd_get_aid_word(struct hostapd_data *hapd,
3430 struct sta_info *sta, int i)
3431{
3432#ifdef CONFIG_IEEE80211BE
3433 u32 aid_word = 0;
3434
3435 /* Do not assign an AID that is in use on any of the affiliated links
3436 * when finding an AID for a non-AP MLD. */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00003437 if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003438 int j;
3439
3440 for (j = 0; j < MAX_NUM_MLD_LINKS; j++) {
3441 struct hostapd_data *link_bss;
3442
3443 if (!sta->mld_info.links[j].valid)
3444 continue;
3445
3446 link_bss = hostapd_mld_get_link_bss(hapd, j);
3447 if (!link_bss) {
3448 /* This shouldn't happen, just skip */
3449 wpa_printf(MSG_ERROR,
3450 "MLD: Failed to get link BSS for AID");
3451 continue;
3452 }
3453
3454 aid_word |= link_bss->sta_aid[i];
3455 }
3456
3457 return aid_word;
3458 }
3459#endif /* CONFIG_IEEE80211BE */
3460
3461 return hapd->sta_aid[i];
3462}
3463
3464
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08003465int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003466{
3467 int i, j = 32, aid;
3468
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003469 /* Transmitted and non-transmitted BSSIDs share the same AID pool, so
3470 * use the shared storage in the transmitted BSS to find the next
3471 * available value. */
3472 hapd = hostapd_mbssid_get_tx_bss(hapd);
3473
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003474 /* get a unique AID */
3475 if (sta->aid > 0) {
3476 wpa_printf(MSG_DEBUG, " old AID %d", sta->aid);
3477 return 0;
3478 }
3479
Dmitry Shmidt58d12ad2016-07-28 10:07:03 -07003480 if (TEST_FAIL())
3481 return -1;
3482
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003483 for (i = 0; i < AID_WORDS; i++) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003484 u32 aid_word = hostapd_get_aid_word(hapd, sta, i);
3485
3486 if (aid_word == (u32) -1)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003487 continue;
3488 for (j = 0; j < 32; j++) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003489 if (!(aid_word & BIT(j)))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003490 break;
3491 }
3492 if (j < 32)
3493 break;
3494 }
3495 if (j == 32)
3496 return -1;
Sunil Ravi77d572f2023-01-17 23:58:31 +00003497 aid = i * 32 + j + (1 << hostapd_max_bssid_indicator(hapd));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003498 if (aid > 2007)
3499 return -1;
3500
3501 sta->aid = aid;
3502 hapd->sta_aid[i] |= BIT(j);
3503 wpa_printf(MSG_DEBUG, " new AID %d", sta->aid);
3504 return 0;
3505}
3506
3507
3508static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta,
3509 const u8 *ssid_ie, size_t ssid_ie_len)
3510{
3511 if (ssid_ie == NULL)
3512 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3513
3514 if (ssid_ie_len != hapd->conf->ssid.ssid_len ||
3515 os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003516 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
3517 HOSTAPD_LEVEL_INFO,
3518 "Station tried to associate with unknown SSID "
Dmitry Shmidt3c479372014-02-04 10:50:36 -08003519 "'%s'", wpa_ssid_txt(ssid_ie, ssid_ie_len));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003520 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3521 }
3522
3523 return WLAN_STATUS_SUCCESS;
3524}
3525
3526
3527static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta,
3528 const u8 *wmm_ie, size_t wmm_ie_len)
3529{
3530 sta->flags &= ~WLAN_STA_WMM;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08003531 sta->qosinfo = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003532 if (wmm_ie && hapd->conf->wmm_enabled) {
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08003533 struct wmm_information_element *wmm;
3534
3535 if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003536 hostapd_logger(hapd, sta->addr,
3537 HOSTAPD_MODULE_WPA,
3538 HOSTAPD_LEVEL_DEBUG,
3539 "invalid WMM element in association "
3540 "request");
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08003541 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3542 }
3543
3544 sta->flags |= WLAN_STA_WMM;
3545 wmm = (struct wmm_information_element *) wmm_ie;
3546 sta->qosinfo = wmm->qos_info;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003547 }
3548 return WLAN_STATUS_SUCCESS;
3549}
3550
Hai Shalom74f70d42019-02-11 14:42:39 -08003551static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta,
3552 const u8 *multi_ap_ie, size_t multi_ap_len)
3553{
Sunil Ravi99c035e2024-07-12 01:42:03 +00003554 struct multi_ap_params multi_ap;
3555 u16 status;
Hai Shalom74f70d42019-02-11 14:42:39 -08003556
3557 sta->flags &= ~WLAN_STA_MULTI_AP;
3558
3559 if (!hapd->conf->multi_ap)
3560 return WLAN_STATUS_SUCCESS;
3561
Sunil Ravi99c035e2024-07-12 01:42:03 +00003562 if (!multi_ap_ie) {
3563 if (!(hapd->conf->multi_ap & FRONTHAUL_BSS)) {
Hai Shalom74f70d42019-02-11 14:42:39 -08003564 hostapd_logger(hapd, sta->addr,
3565 HOSTAPD_MODULE_IEEE80211,
3566 HOSTAPD_LEVEL_INFO,
Sunil Ravi99c035e2024-07-12 01:42:03 +00003567 "Non-Multi-AP STA tries to associate with backhaul-only BSS");
3568 return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
Hai Shalom74f70d42019-02-11 14:42:39 -08003569 }
Sunil Ravi99c035e2024-07-12 01:42:03 +00003570
3571 return WLAN_STATUS_SUCCESS;
Hai Shalom74f70d42019-02-11 14:42:39 -08003572 }
3573
Sunil Ravi99c035e2024-07-12 01:42:03 +00003574 status = check_multi_ap_ie(multi_ap_ie + 4, multi_ap_len - 4,
3575 &multi_ap);
3576 if (status != WLAN_STATUS_SUCCESS)
3577 return status;
3578
3579 if (multi_ap.capability && multi_ap.capability != MULTI_AP_BACKHAUL_STA)
Hai Shalom021b0b52019-04-10 11:17:58 -07003580 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
3581 HOSTAPD_LEVEL_INFO,
3582 "Multi-AP IE with unexpected value 0x%02x",
Sunil Ravi99c035e2024-07-12 01:42:03 +00003583 multi_ap.capability);
Hai Shalom74f70d42019-02-11 14:42:39 -08003584
Sunil Ravi99c035e2024-07-12 01:42:03 +00003585 if (multi_ap.profile == MULTI_AP_PROFILE_1 &&
3586 (hapd->conf->multi_ap_client_disallow &
3587 PROFILE1_CLIENT_ASSOC_DISALLOW)) {
3588 hostapd_logger(hapd, sta->addr,
3589 HOSTAPD_MODULE_IEEE80211,
3590 HOSTAPD_LEVEL_INFO,
3591 "Multi-AP Profile-1 clients not allowed");
3592 return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
3593 }
3594
3595 if (multi_ap.profile >= MULTI_AP_PROFILE_2 &&
3596 (hapd->conf->multi_ap_client_disallow &
3597 PROFILE2_CLIENT_ASSOC_DISALLOW)) {
3598 hostapd_logger(hapd, sta->addr,
3599 HOSTAPD_MODULE_IEEE80211,
3600 HOSTAPD_LEVEL_INFO,
3601 "Multi-AP Profile-2 clients not allowed");
3602 return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
3603 }
3604
3605 if (!(multi_ap.capability & MULTI_AP_BACKHAUL_STA)) {
Hai Shalom021b0b52019-04-10 11:17:58 -07003606 if (hapd->conf->multi_ap & FRONTHAUL_BSS)
3607 return WLAN_STATUS_SUCCESS;
Hai Shalom74f70d42019-02-11 14:42:39 -08003608
Hai Shalom021b0b52019-04-10 11:17:58 -07003609 hostapd_logger(hapd, sta->addr,
3610 HOSTAPD_MODULE_IEEE80211,
3611 HOSTAPD_LEVEL_INFO,
3612 "Non-Multi-AP STA tries to associate with backhaul-only BSS");
3613 return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
Hai Shalom74f70d42019-02-11 14:42:39 -08003614 }
3615
Hai Shalom021b0b52019-04-10 11:17:58 -07003616 if (!(hapd->conf->multi_ap & BACKHAUL_BSS))
3617 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
3618 HOSTAPD_LEVEL_DEBUG,
3619 "Backhaul STA tries to associate with fronthaul-only BSS");
3620
3621 sta->flags |= WLAN_STA_MULTI_AP;
3622 return WLAN_STATUS_SUCCESS;
Hai Shalom74f70d42019-02-11 14:42:39 -08003623}
3624
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003625
3626static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
3627 struct ieee802_11_elems *elems)
3628{
Dmitry Shmidt29333592017-01-09 12:27:11 -08003629 /* Supported rates not used in IEEE 802.11ad/DMG */
3630 if (hapd->iface->current_mode &&
3631 hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD)
3632 return WLAN_STATUS_SUCCESS;
3633
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003634 if (!elems->supp_rates) {
3635 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
3636 HOSTAPD_LEVEL_DEBUG,
3637 "No supported rates element in AssocReq");
3638 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3639 }
3640
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07003641 if (elems->supp_rates_len + elems->ext_supp_rates_len >
3642 sizeof(sta->supported_rates)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003643 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
3644 HOSTAPD_LEVEL_DEBUG,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07003645 "Invalid supported rates element length %d+%d",
3646 elems->supp_rates_len,
3647 elems->ext_supp_rates_len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003648 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3649 }
3650
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07003651 sta->supported_rates_len = merge_byte_arrays(
3652 sta->supported_rates, sizeof(sta->supported_rates),
3653 elems->supp_rates, elems->supp_rates_len,
3654 elems->ext_supp_rates, elems->ext_supp_rates_len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003655
3656 return WLAN_STATUS_SUCCESS;
3657}
3658
3659
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003660#ifdef CONFIG_OWE
3661
3662static int owe_group_supported(struct hostapd_data *hapd, u16 group)
3663{
3664 int i;
3665 int *groups = hapd->conf->owe_groups;
3666
3667 if (group != 19 && group != 20 && group != 21)
3668 return 0;
3669
3670 if (!groups)
3671 return 1;
3672
3673 for (i = 0; groups[i] > 0; i++) {
3674 if (groups[i] == group)
3675 return 1;
3676 }
3677
3678 return 0;
3679}
3680
3681
3682static u16 owe_process_assoc_req(struct hostapd_data *hapd,
3683 struct sta_info *sta, const u8 *owe_dh,
3684 u8 owe_dh_len)
3685{
3686 struct wpabuf *secret, *pub, *hkey;
3687 int res;
3688 u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN];
3689 const char *info = "OWE Key Generation";
3690 const u8 *addr[2];
3691 size_t len[2];
3692 u16 group;
3693 size_t hash_len, prime_len;
3694
3695 if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
3696 wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
3697 return WLAN_STATUS_SUCCESS;
3698 }
3699
3700 group = WPA_GET_LE16(owe_dh);
3701 if (!owe_group_supported(hapd, group)) {
3702 wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group);
3703 return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
3704 }
3705 if (group == 19)
3706 prime_len = 32;
3707 else if (group == 20)
3708 prime_len = 48;
3709 else if (group == 21)
3710 prime_len = 66;
3711 else
3712 return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
3713
Sunil Ravia04bd252022-05-02 22:54:18 -07003714 if (sta->owe_group == group && sta->owe_ecdh) {
3715 /* This is a workaround for mac80211 behavior of retransmitting
3716 * the Association Request frames multiple times if the link
3717 * layer retries (i.e., seq# remains same) fail. The mac80211
3718 * initiated retransmission will use a different seq# and as
3719 * such, will go through duplicate detection. If we were to
3720 * change our DH key for that attempt, there would be two
3721 * different DH shared secrets and the STA would likely select
3722 * the wrong one. */
3723 wpa_printf(MSG_DEBUG,
3724 "OWE: Try to reuse own previous DH key since the STA tried to go through OWE association again");
3725 } else {
3726 crypto_ecdh_deinit(sta->owe_ecdh);
3727 sta->owe_ecdh = crypto_ecdh_init(group);
3728 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003729 if (!sta->owe_ecdh)
3730 return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
3731 sta->owe_group = group;
3732
3733 secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2,
3734 owe_dh_len - 2);
3735 secret = wpabuf_zeropad(secret, prime_len);
3736 if (!secret) {
3737 wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
3738 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3739 }
3740 wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret);
3741
3742 /* prk = HKDF-extract(C | A | group, z) */
3743
3744 pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
3745 if (!pub) {
3746 wpabuf_clear_free(secret);
3747 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3748 }
3749
3750 /* PMKID = Truncate-128(Hash(C | A)) */
3751 addr[0] = owe_dh + 2;
3752 len[0] = owe_dh_len - 2;
3753 addr[1] = wpabuf_head(pub);
3754 len[1] = wpabuf_len(pub);
3755 if (group == 19) {
3756 res = sha256_vector(2, addr, len, pmkid);
3757 hash_len = SHA256_MAC_LEN;
3758 } else if (group == 20) {
3759 res = sha384_vector(2, addr, len, pmkid);
3760 hash_len = SHA384_MAC_LEN;
3761 } else if (group == 21) {
3762 res = sha512_vector(2, addr, len, pmkid);
3763 hash_len = SHA512_MAC_LEN;
3764 } else {
3765 wpabuf_free(pub);
3766 wpabuf_clear_free(secret);
3767 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3768 }
3769 pub = wpabuf_zeropad(pub, prime_len);
3770 if (res < 0 || !pub) {
3771 wpabuf_free(pub);
3772 wpabuf_clear_free(secret);
3773 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3774 }
3775
3776 hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2);
3777 if (!hkey) {
3778 wpabuf_free(pub);
3779 wpabuf_clear_free(secret);
3780 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3781 }
3782
3783 wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */
3784 wpabuf_put_buf(hkey, pub); /* A */
3785 wpabuf_free(pub);
3786 wpabuf_put_le16(hkey, group); /* group */
3787 if (group == 19)
3788 res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
3789 wpabuf_head(secret), wpabuf_len(secret), prk);
3790 else if (group == 20)
3791 res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey),
3792 wpabuf_head(secret), wpabuf_len(secret), prk);
3793 else if (group == 21)
3794 res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey),
3795 wpabuf_head(secret), wpabuf_len(secret), prk);
3796 wpabuf_clear_free(hkey);
3797 wpabuf_clear_free(secret);
3798 if (res < 0)
3799 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3800
3801 wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len);
3802
3803 /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
3804
3805 os_free(sta->owe_pmk);
3806 sta->owe_pmk = os_malloc(hash_len);
3807 if (!sta->owe_pmk) {
3808 os_memset(prk, 0, SHA512_MAC_LEN);
3809 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3810 }
3811
3812 if (group == 19)
3813 res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info,
3814 os_strlen(info), sta->owe_pmk, hash_len);
3815 else if (group == 20)
3816 res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info,
3817 os_strlen(info), sta->owe_pmk, hash_len);
3818 else if (group == 21)
3819 res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
3820 os_strlen(info), sta->owe_pmk, hash_len);
3821 os_memset(prk, 0, SHA512_MAC_LEN);
3822 if (res < 0) {
3823 os_free(sta->owe_pmk);
3824 sta->owe_pmk = NULL;
3825 return WLAN_STATUS_UNSPECIFIED_FAILURE;
3826 }
3827 sta->owe_pmk_len = hash_len;
3828
3829 wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len);
3830 wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
3831 wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00003832 sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE, NULL);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003833
3834 return WLAN_STATUS_SUCCESS;
3835}
3836
Hai Shalom81f62d82019-07-22 12:10:00 -07003837
3838u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
3839 const u8 *rsn_ie, size_t rsn_ie_len,
3840 const u8 *owe_dh, size_t owe_dh_len)
3841{
3842 struct wpa_ie_data data;
3843 int res;
3844
3845 if (!rsn_ie || rsn_ie_len < 2) {
3846 wpa_printf(MSG_DEBUG, "OWE: Invalid RSNE from " MACSTR,
3847 MAC2STR(peer));
3848 return WLAN_STATUS_INVALID_IE;
3849 }
3850 rsn_ie -= 2;
3851 rsn_ie_len += 2;
3852
3853 res = wpa_parse_wpa_ie_rsn(rsn_ie, rsn_ie_len, &data);
3854 if (res) {
3855 wpa_printf(MSG_DEBUG, "Failed to parse RSNE from " MACSTR
3856 " (res=%d)", MAC2STR(peer), res);
3857 wpa_hexdump(MSG_DEBUG, "RSNE", rsn_ie, rsn_ie_len);
3858 return wpa_res_to_status_code(res);
3859 }
3860 if (!(data.key_mgmt & WPA_KEY_MGMT_OWE)) {
3861 wpa_printf(MSG_DEBUG,
3862 "OWE: Unexpected key mgmt 0x%x from " MACSTR,
3863 (unsigned int) data.key_mgmt, MAC2STR(peer));
3864 return WLAN_STATUS_AKMP_NOT_VALID;
3865 }
3866 if (!owe_dh) {
3867 wpa_printf(MSG_DEBUG,
3868 "OWE: No Diffie-Hellman Parameter element from "
3869 MACSTR, MAC2STR(peer));
3870 return WLAN_STATUS_AKMP_NOT_VALID;
3871 }
3872
3873 return WLAN_STATUS_SUCCESS;
3874}
3875
3876
3877u16 owe_process_rsn_ie(struct hostapd_data *hapd,
3878 struct sta_info *sta,
3879 const u8 *rsn_ie, size_t rsn_ie_len,
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003880 const u8 *owe_dh, size_t owe_dh_len,
3881 const u8 *link_addr)
Hai Shalom81f62d82019-07-22 12:10:00 -07003882{
3883 u16 status;
3884 u8 *owe_buf, ie[256 * 2];
3885 size_t ie_len = 0;
Hai Shalomfdcde762020-04-02 11:19:20 -07003886 enum wpa_validate_result res;
Hai Shalom81f62d82019-07-22 12:10:00 -07003887
3888 if (!rsn_ie || rsn_ie_len < 2) {
3889 wpa_printf(MSG_DEBUG, "OWE: No RSNE in (Re)AssocReq");
3890 status = WLAN_STATUS_INVALID_IE;
3891 goto end;
3892 }
3893
3894 if (!sta->wpa_sm)
3895 sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr,
3896 NULL);
3897 if (!sta->wpa_sm) {
3898 wpa_printf(MSG_WARNING,
3899 "OWE: Failed to initialize WPA state machine");
3900 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
3901 goto end;
3902 }
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003903#ifdef CONFIG_IEEE80211BE
Sunil Ravib0ac25f2024-07-12 01:42:03 +00003904 if (ap_sta_is_mld(hapd, sta))
Sunil Ravi7f769292024-07-23 22:21:32 +00003905 wpa_auth_set_ml_info(sta->wpa_sm,
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003906 sta->mld_assoc_link_id, &sta->mld_info);
3907#endif /* CONFIG_IEEE80211BE */
Hai Shalom81f62d82019-07-22 12:10:00 -07003908 rsn_ie -= 2;
3909 rsn_ie_len += 2;
3910 res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
3911 hapd->iface->freq, rsn_ie, rsn_ie_len,
Sunil Ravi7f769292024-07-23 22:21:32 +00003912 NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL);
Hai Shalom81f62d82019-07-22 12:10:00 -07003913 status = wpa_res_to_status_code(res);
3914 if (status != WLAN_STATUS_SUCCESS)
3915 goto end;
3916 status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
3917 if (status != WLAN_STATUS_SUCCESS)
3918 goto end;
3919 owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, ie, sizeof(ie),
3920 NULL, 0);
3921 if (!owe_buf) {
3922 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
3923 goto end;
3924 }
3925
3926 if (sta->owe_ecdh) {
3927 struct wpabuf *pub;
3928
3929 pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
3930 if (!pub) {
3931 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
3932 goto end;
3933 }
3934
3935 /* OWE Diffie-Hellman Parameter element */
3936 *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
3937 *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
3938 *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
3939 */
3940 WPA_PUT_LE16(owe_buf, sta->owe_group);
3941 owe_buf += 2;
3942 os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
3943 owe_buf += wpabuf_len(pub);
3944 wpabuf_free(pub);
3945 sta->external_dh_updated = 1;
3946 }
3947 ie_len = owe_buf - ie;
3948
3949end:
3950 wpa_printf(MSG_DEBUG, "OWE: Update status %d, ie len %d for peer "
3951 MACSTR, status, (unsigned int) ie_len,
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003952 MAC2STR(link_addr ? link_addr : sta->addr));
3953 hostapd_drv_update_dh_ie(hapd, link_addr ? link_addr : sta->addr,
3954 status,
Hai Shalom81f62d82019-07-22 12:10:00 -07003955 status == WLAN_STATUS_SUCCESS ? ie : NULL,
3956 ie_len);
3957
3958 return status;
3959}
3960
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003961#endif /* CONFIG_OWE */
3962
3963
Hai Shalom899fcc72020-10-19 14:38:18 -07003964static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta,
3965 int reassoc)
3966{
3967 if ((sta->flags &
3968 (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) !=
3969 (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED))
3970 return false;
3971
3972 if (!sta->sa_query_timed_out && sta->sa_query_count > 0)
3973 ap_check_sa_query_timeout(hapd, sta);
3974
3975 if (!sta->sa_query_timed_out &&
3976 (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
3977 /*
3978 * STA has already been associated with MFP and SA Query timeout
3979 * has not been reached. Reject the association attempt
3980 * temporarily and start SA Query, if one is not pending.
3981 */
3982 if (sta->sa_query_count == 0)
3983 ap_sta_start_sa_query(hapd, sta);
3984
3985 return true;
3986 }
3987
3988 return false;
3989}
3990
3991
Sunil Ravi036cec52023-03-29 11:35:17 -07003992static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
3993 const u8 *ies, size_t ies_len,
Sunil Ravi2a14cf12023-11-21 00:54:38 +00003994 struct ieee802_11_elems *elems, int reassoc,
3995 bool link)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003996{
Hai Shalomb755a2a2020-04-23 21:49:02 -07003997 int resp;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07003998 const u8 *wpa_ie;
3999 size_t wpa_ie_len;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07004000 const u8 *p2p_dev_addr = NULL;
Sunil Ravi7f769292024-07-23 22:21:32 +00004001 struct hostapd_data *assoc_hapd;
4002 struct sta_info *assoc_sta = NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004003
Sunil Ravi036cec52023-03-29 11:35:17 -07004004 resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004005 if (resp != WLAN_STATUS_SUCCESS)
4006 return resp;
Sunil Ravi036cec52023-03-29 11:35:17 -07004007 resp = check_wmm(hapd, sta, elems->wmm, elems->wmm_len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004008 if (resp != WLAN_STATUS_SUCCESS)
4009 return resp;
Sunil Ravi036cec52023-03-29 11:35:17 -07004010 resp = check_ext_capab(hapd, sta, elems->ext_capab,
4011 elems->ext_capab_len);
Dmitry Shmidt051af732013-10-22 13:52:46 -07004012 if (resp != WLAN_STATUS_SUCCESS)
4013 return resp;
Sunil Ravi036cec52023-03-29 11:35:17 -07004014 resp = copy_supp_rates(hapd, sta, elems);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004015 if (resp != WLAN_STATUS_SUCCESS)
4016 return resp;
Hai Shalom74f70d42019-02-11 14:42:39 -08004017
Sunil Ravi036cec52023-03-29 11:35:17 -07004018 resp = check_multi_ap(hapd, sta, elems->multi_ap, elems->multi_ap_len);
Hai Shalom74f70d42019-02-11 14:42:39 -08004019 if (resp != WLAN_STATUS_SUCCESS)
4020 return resp;
4021
Sunil Ravi036cec52023-03-29 11:35:17 -07004022 resp = copy_sta_ht_capab(hapd, sta, elems->ht_capabilities);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004023 if (resp != WLAN_STATUS_SUCCESS)
4024 return resp;
4025 if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
4026 !(sta->flags & WLAN_STA_HT)) {
4027 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
4028 HOSTAPD_LEVEL_INFO, "Station does not support "
4029 "mandatory HT PHY - reject association");
4030 return WLAN_STATUS_ASSOC_DENIED_NO_HT;
4031 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004032
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07004033#ifdef CONFIG_IEEE80211AC
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08004034 if (hapd->iconf->ieee80211ac) {
Sunil Ravi036cec52023-03-29 11:35:17 -07004035 resp = copy_sta_vht_capab(hapd, sta, elems->vht_capabilities);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08004036 if (resp != WLAN_STATUS_SUCCESS)
4037 return resp;
Dmitry Shmidtbd14a572014-02-18 10:33:49 -08004038
Sunil Ravi640215c2023-06-28 23:08:09 +00004039 resp = set_sta_vht_opmode(hapd, sta, elems->opmode_notif);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08004040 if (resp != WLAN_STATUS_SUCCESS)
4041 return resp;
4042 }
Dmitry Shmidtbd14a572014-02-18 10:33:49 -08004043
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07004044 if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht &&
4045 !(sta->flags & WLAN_STA_VHT)) {
4046 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
4047 HOSTAPD_LEVEL_INFO, "Station does not support "
4048 "mandatory VHT PHY - reject association");
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08004049 return WLAN_STATUS_ASSOC_DENIED_NO_VHT;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07004050 }
Dmitry Shmidt2f74e362015-01-21 13:19:05 -08004051
Sunil Ravi036cec52023-03-29 11:35:17 -07004052 if (hapd->conf->vendor_vht && !elems->vht_capabilities) {
4053 resp = copy_sta_vendor_vht(hapd, sta, elems->vendor_vht,
4054 elems->vendor_vht_len);
Dmitry Shmidt2f74e362015-01-21 13:19:05 -08004055 if (resp != WLAN_STATUS_SUCCESS)
4056 return resp;
4057 }
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07004058#endif /* CONFIG_IEEE80211AC */
Hai Shalom81f62d82019-07-22 12:10:00 -07004059#ifdef CONFIG_IEEE80211AX
Hai Shalom60840252021-02-19 19:02:11 -08004060 if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
Hai Shalom81f62d82019-07-22 12:10:00 -07004061 resp = copy_sta_he_capab(hapd, sta, IEEE80211_MODE_AP,
Sunil Ravi036cec52023-03-29 11:35:17 -07004062 elems->he_capabilities,
4063 elems->he_capabilities_len);
Hai Shalom81f62d82019-07-22 12:10:00 -07004064 if (resp != WLAN_STATUS_SUCCESS)
4065 return resp;
Sunil Ravi77d572f2023-01-17 23:58:31 +00004066
4067 if (hapd->iconf->require_he && !(sta->flags & WLAN_STA_HE)) {
4068 hostapd_logger(hapd, sta->addr,
4069 HOSTAPD_MODULE_IEEE80211,
4070 HOSTAPD_LEVEL_INFO,
4071 "Station does not support mandatory HE PHY - reject association");
4072 return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
4073 }
4074
Hai Shalom4fbc08f2020-05-18 12:37:00 -07004075 if (is_6ghz_op_class(hapd->iconf->op_class)) {
Hai Shalom899fcc72020-10-19 14:38:18 -07004076 if (!(sta->flags & WLAN_STA_HE)) {
4077 hostapd_logger(hapd, sta->addr,
4078 HOSTAPD_MODULE_IEEE80211,
4079 HOSTAPD_LEVEL_INFO,
4080 "Station does not support mandatory HE PHY - reject association");
4081 return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
4082 }
Hai Shalom4fbc08f2020-05-18 12:37:00 -07004083 resp = copy_sta_he_6ghz_capab(hapd, sta,
Sunil Ravi036cec52023-03-29 11:35:17 -07004084 elems->he_6ghz_band_cap);
Hai Shalom4fbc08f2020-05-18 12:37:00 -07004085 if (resp != WLAN_STATUS_SUCCESS)
4086 return resp;
4087 }
Hai Shalom81f62d82019-07-22 12:10:00 -07004088 }
4089#endif /* CONFIG_IEEE80211AX */
Sunil Ravia04bd252022-05-02 22:54:18 -07004090#ifdef CONFIG_IEEE80211BE
4091 if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
4092 resp = copy_sta_eht_capab(hapd, sta, IEEE80211_MODE_AP,
Sunil Ravi036cec52023-03-29 11:35:17 -07004093 elems->he_capabilities,
4094 elems->he_capabilities_len,
4095 elems->eht_capabilities,
4096 elems->eht_capabilities_len);
Sunil Ravia04bd252022-05-02 22:54:18 -07004097 if (resp != WLAN_STATUS_SUCCESS)
4098 return resp;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004099
4100 if (!link) {
4101 resp = hostapd_process_ml_assoc_req(hapd, elems, sta);
4102 if (resp != WLAN_STATUS_SUCCESS)
4103 return resp;
4104 }
Sunil Ravia04bd252022-05-02 22:54:18 -07004105 }
4106#endif /* CONFIG_IEEE80211BE */
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07004107
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07004108#ifdef CONFIG_P2P
Sunil Ravi036cec52023-03-29 11:35:17 -07004109 if (elems->p2p && ies && ies_len) {
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07004110 wpabuf_free(sta->p2p_ie);
4111 sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
4112 P2P_IE_VENDOR_TYPE);
4113 if (sta->p2p_ie)
4114 p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
4115 } else {
4116 wpabuf_free(sta->p2p_ie);
4117 sta->p2p_ie = NULL;
4118 }
4119#endif /* CONFIG_P2P */
4120
Sunil Ravi036cec52023-03-29 11:35:17 -07004121 if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems->rsn_ie) {
4122 wpa_ie = elems->rsn_ie;
4123 wpa_ie_len = elems->rsn_ie_len;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004124 } else if ((hapd->conf->wpa & WPA_PROTO_WPA) &&
Sunil Ravi036cec52023-03-29 11:35:17 -07004125 elems->wpa_ie) {
4126 wpa_ie = elems->wpa_ie;
4127 wpa_ie_len = elems->wpa_ie_len;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004128 } else {
4129 wpa_ie = NULL;
4130 wpa_ie_len = 0;
4131 }
4132
4133#ifdef CONFIG_WPS
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08004134 sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
Sunil Ravi036cec52023-03-29 11:35:17 -07004135 if (hapd->conf->wps_state && elems->wps_ie && ies && ies_len) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004136 wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association "
4137 "Request - assume WPS is used");
4138 sta->flags |= WLAN_STA_WPS;
4139 wpabuf_free(sta->wps_ie);
4140 sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
4141 WPS_IE_VENDOR_TYPE);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08004142 if (sta->wps_ie && wps_is_20(sta->wps_ie)) {
4143 wpa_printf(MSG_DEBUG, "WPS: STA supports WPS 2.0");
4144 sta->flags |= WLAN_STA_WPS2;
4145 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004146 wpa_ie = NULL;
4147 wpa_ie_len = 0;
4148 if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) {
4149 wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in "
4150 "(Re)Association Request - reject");
4151 return WLAN_STATUS_INVALID_IE;
4152 }
4153 } else if (hapd->conf->wps_state && wpa_ie == NULL) {
4154 wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in "
4155 "(Re)Association Request - possible WPS use");
4156 sta->flags |= WLAN_STA_MAYBE_WPS;
4157 } else
4158#endif /* CONFIG_WPS */
4159 if (hapd->conf->wpa && wpa_ie == NULL) {
4160 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
4161 HOSTAPD_LEVEL_INFO,
4162 "No WPA/RSN IE in association request");
4163 return WLAN_STATUS_INVALID_IE;
4164 }
4165
4166 if (hapd->conf->wpa && wpa_ie) {
Hai Shalomfdcde762020-04-02 11:19:20 -07004167 enum wpa_validate_result res;
Sunil Ravi7f769292024-07-23 22:21:32 +00004168#ifdef CONFIG_IEEE80211BE
4169 struct mld_info *info = &sta->mld_info;
4170 bool init = !sta->wpa_sm;
4171#endif /* CONFIG_IEEE80211BE */
Hai Shalomfdcde762020-04-02 11:19:20 -07004172
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004173 wpa_ie -= 2;
4174 wpa_ie_len += 2;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004175
4176 if (!sta->wpa_sm) {
Sunil Ravi7f769292024-07-23 22:21:32 +00004177 if (!link)
4178 assoc_sta = hostapd_ml_get_assoc_sta(
4179 hapd, sta, &assoc_hapd);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004180
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004181 sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07004182 sta->addr,
4183 p2p_dev_addr);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004184
4185 if (!sta->wpa_sm) {
4186 wpa_printf(MSG_WARNING,
4187 "Failed to initialize RSN state machine");
4188 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4189 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004190 }
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004191
Sunil Ravi7f769292024-07-23 22:21:32 +00004192#ifdef CONFIG_IEEE80211BE
4193 if (ap_sta_is_mld(hapd, sta)) {
4194 wpa_printf(MSG_DEBUG,
4195 "MLD: %s ML info in RSN Authenticator",
4196 init ? "Set" : "Reset");
4197 wpa_auth_set_ml_info(sta->wpa_sm,
4198 sta->mld_assoc_link_id,
4199 info);
4200 }
4201#endif /* CONFIG_IEEE80211BE */
4202
Hai Shalom021b0b52019-04-10 11:17:58 -07004203 wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
Sunil Ravic0f5d412024-09-11 22:12:49 +00004204 wpa_auth_set_rsn_selection(sta->wpa_sm, elems->rsn_selection,
4205 elems->rsn_selection_len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004206 res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
Hai Shalom021b0b52019-04-10 11:17:58 -07004207 hapd->iface->freq,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004208 wpa_ie, wpa_ie_len,
Sunil Ravi036cec52023-03-29 11:35:17 -07004209 elems->rsnxe ? elems->rsnxe - 2 :
4210 NULL,
4211 elems->rsnxe ? elems->rsnxe_len + 2 :
4212 0,
4213 elems->mdie, elems->mdie_len,
Sunil Ravi7f769292024-07-23 22:21:32 +00004214 elems->owe_dh, elems->owe_dh_len,
4215 assoc_sta ? assoc_sta->wpa_sm : NULL);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08004216 resp = wpa_res_to_status_code(res);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004217 if (resp != WLAN_STATUS_SUCCESS)
4218 return resp;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004219
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004220 if (wpa_auth_uses_mfp(sta->wpa_sm))
4221 sta->flags |= WLAN_STA_MFP;
4222 else
4223 sta->flags &= ~WLAN_STA_MFP;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004224
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08004225#ifdef CONFIG_IEEE80211R_AP
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004226 if (sta->auth_alg == WLAN_AUTH_FT) {
4227 if (!reassoc) {
4228 wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried "
4229 "to use association (not "
4230 "re-association) with FT auth_alg",
4231 MAC2STR(sta->addr));
4232 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4233 }
4234
4235 resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies,
4236 ies_len);
4237 if (resp != WLAN_STATUS_SUCCESS)
4238 return resp;
4239 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08004240#endif /* CONFIG_IEEE80211R_AP */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004241
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004242 if (link)
4243 goto skip_sae_owe;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08004244#ifdef CONFIG_SAE
Roshan Pius3a1667e2018-07-03 15:17:14 -07004245 if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae &&
4246 sta->sae->state == SAE_ACCEPTED)
4247 wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid);
4248
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08004249 if (wpa_auth_uses_sae(sta->wpa_sm) &&
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08004250 sta->auth_alg == WLAN_AUTH_OPEN) {
4251 struct rsn_pmksa_cache_entry *sa;
4252 sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
Sunil Ravi89eba102022-09-13 21:04:37 -07004253 if (!sa || !wpa_key_mgmt_sae(sa->akmp)) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08004254 wpa_printf(MSG_DEBUG,
4255 "SAE: No PMKSA cache entry found for "
4256 MACSTR, MAC2STR(sta->addr));
4257 return WLAN_STATUS_INVALID_PMKID;
4258 }
4259 wpa_printf(MSG_DEBUG, "SAE: " MACSTR
4260 " using PMKSA caching", MAC2STR(sta->addr));
4261 } else if (wpa_auth_uses_sae(sta->wpa_sm) &&
4262 sta->auth_alg != WLAN_AUTH_SAE &&
4263 !(sta->auth_alg == WLAN_AUTH_FT &&
4264 wpa_auth_uses_ft_sae(sta->wpa_sm))) {
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08004265 wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use "
4266 "SAE AKM after non-SAE auth_alg %u",
4267 MAC2STR(sta->addr), sta->auth_alg);
4268 return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
4269 }
Hai Shalomc3565922019-10-28 11:58:20 -07004270
Sunil Ravi77d572f2023-01-17 23:58:31 +00004271 if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
Hai Shalomc3565922019-10-28 11:58:20 -07004272 sta->auth_alg == WLAN_AUTH_SAE &&
Hai Shalom899fcc72020-10-19 14:38:18 -07004273 sta->sae && !sta->sae->h2e &&
Sunil Ravi036cec52023-03-29 11:35:17 -07004274 ieee802_11_rsnx_capab_len(elems->rsnxe, elems->rsnxe_len,
Hai Shaloma20dcd72022-02-04 13:43:00 -08004275 WLAN_RSNX_CAPAB_SAE_H2E)) {
Hai Shalomc3565922019-10-28 11:58:20 -07004276 wpa_printf(MSG_INFO, "SAE: " MACSTR
4277 " indicates support for SAE H2E, but did not use it",
4278 MAC2STR(sta->addr));
4279 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4280 }
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08004281#endif /* CONFIG_SAE */
4282
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004283#ifdef CONFIG_OWE
4284 if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
4285 wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
Sunil Ravi036cec52023-03-29 11:35:17 -07004286 elems->owe_dh) {
4287 resp = owe_process_assoc_req(hapd, sta, elems->owe_dh,
4288 elems->owe_dh_len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004289 if (resp != WLAN_STATUS_SUCCESS)
4290 return resp;
4291 }
4292#endif /* CONFIG_OWE */
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004293 skip_sae_owe:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004294
Hai Shalom021b0b52019-04-10 11:17:58 -07004295#ifdef CONFIG_DPP2
4296 dpp_pfs_free(sta->dpp_pfs);
4297 sta->dpp_pfs = NULL;
4298
Hai Shalom4fbc08f2020-05-18 12:37:00 -07004299 if (DPP_VERSION > 1 &&
4300 (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
Hai Shalom021b0b52019-04-10 11:17:58 -07004301 hapd->conf->dpp_netaccesskey && sta->wpa_sm &&
4302 wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP &&
Sunil Ravi036cec52023-03-29 11:35:17 -07004303 elems->owe_dh) {
Hai Shalom021b0b52019-04-10 11:17:58 -07004304 sta->dpp_pfs = dpp_pfs_init(
4305 wpabuf_head(hapd->conf->dpp_netaccesskey),
4306 wpabuf_len(hapd->conf->dpp_netaccesskey));
4307 if (!sta->dpp_pfs) {
4308 wpa_printf(MSG_DEBUG,
4309 "DPP: Could not initialize PFS");
4310 /* Try to continue without PFS */
4311 goto pfs_fail;
4312 }
4313
Sunil Ravi036cec52023-03-29 11:35:17 -07004314 if (dpp_pfs_process(sta->dpp_pfs, elems->owe_dh,
4315 elems->owe_dh_len) < 0) {
Hai Shalom021b0b52019-04-10 11:17:58 -07004316 dpp_pfs_free(sta->dpp_pfs);
4317 sta->dpp_pfs = NULL;
4318 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4319 }
4320 }
4321
4322 wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
4323 sta->dpp_pfs->secret : NULL);
4324 pfs_fail:
4325#endif /* CONFIG_DPP2 */
4326
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07004327 if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004328 wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
4329 hostapd_logger(hapd, sta->addr,
4330 HOSTAPD_MODULE_IEEE80211,
4331 HOSTAPD_LEVEL_INFO,
4332 "Station tried to use TKIP with HT "
4333 "association");
4334 return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
4335 }
Sunil Ravi7f769292024-07-23 22:21:32 +00004336
4337 wpa_auth_set_ssid_protection(
4338 sta->wpa_sm,
4339 hapd->conf->ssid_protection &&
4340 ieee802_11_rsnx_capab_len(
4341 elems->rsnxe, elems->rsnxe_len,
4342 WLAN_RSNX_CAPAB_SSID_PROTECTION));
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08004343#ifdef CONFIG_HS20
4344 } else if (hapd->conf->osen) {
Sunil Ravi036cec52023-03-29 11:35:17 -07004345 if (!elems->osen) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08004346 hostapd_logger(
4347 hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
4348 HOSTAPD_LEVEL_INFO,
4349 "No HS 2.0 OSEN element in association request");
4350 return WLAN_STATUS_INVALID_IE;
4351 }
4352
4353 wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
4354 if (sta->wpa_sm == NULL)
4355 sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
4356 sta->addr, NULL);
4357 if (sta->wpa_sm == NULL) {
4358 wpa_printf(MSG_WARNING, "Failed to initialize WPA "
4359 "state machine");
4360 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4361 }
4362 if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
Sunil Ravi036cec52023-03-29 11:35:17 -07004363 elems->osen - 2, elems->osen_len + 2) < 0)
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08004364 return WLAN_STATUS_INVALID_IE;
4365#endif /* CONFIG_HS20 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004366 } else
4367 wpa_auth_sta_no_wpa(sta->wpa_sm);
4368
4369#ifdef CONFIG_P2P
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004370 p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
4371#endif /* CONFIG_P2P */
4372
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08004373#ifdef CONFIG_HS20
4374 wpabuf_free(sta->hs20_ie);
Sunil Ravi036cec52023-03-29 11:35:17 -07004375 if (elems->hs20 && elems->hs20_len > 4) {
Hai Shalom74f70d42019-02-11 14:42:39 -08004376 int release;
4377
Sunil Ravi036cec52023-03-29 11:35:17 -07004378 sta->hs20_ie = wpabuf_alloc_copy(elems->hs20 + 4,
4379 elems->hs20_len - 4);
4380 release = ((elems->hs20[4] >> 4) & 0x0f) + 1;
Hai Shalomc3565922019-10-28 11:58:20 -07004381 if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm) &&
4382 hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
Hai Shalom74f70d42019-02-11 14:42:39 -08004383 wpa_printf(MSG_DEBUG,
4384 "HS 2.0: PMF not negotiated by release %d station "
4385 MACSTR, release, MAC2STR(sta->addr));
4386 return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
4387 }
4388 } else {
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08004389 sta->hs20_ie = NULL;
Hai Shalom74f70d42019-02-11 14:42:39 -08004390 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07004391
4392 wpabuf_free(sta->roaming_consortium);
Sunil Ravi036cec52023-03-29 11:35:17 -07004393 if (elems->roaming_cons_sel)
Roshan Pius3a1667e2018-07-03 15:17:14 -07004394 sta->roaming_consortium = wpabuf_alloc_copy(
Sunil Ravi036cec52023-03-29 11:35:17 -07004395 elems->roaming_cons_sel + 4,
4396 elems->roaming_cons_sel_len - 4);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004397 else
4398 sta->roaming_consortium = NULL;
Dmitry Shmidtd5e49232012-12-03 15:08:10 -08004399#endif /* CONFIG_HS20 */
4400
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08004401#ifdef CONFIG_FST
4402 wpabuf_free(sta->mb_ies);
4403 if (hapd->iface->fst)
Sunil Ravi036cec52023-03-29 11:35:17 -07004404 sta->mb_ies = mb_ies_by_info(&elems->mb_ies);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08004405 else
4406 sta->mb_ies = NULL;
4407#endif /* CONFIG_FST */
4408
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004409#ifdef CONFIG_MBO
Sunil Ravi036cec52023-03-29 11:35:17 -07004410 mbo_ap_check_sta_assoc(hapd, sta, elems);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004411
4412 if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) &&
Sunil Ravi036cec52023-03-29 11:35:17 -07004413 elems->mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) &&
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004414 hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
4415 wpa_printf(MSG_INFO,
4416 "MBO: Reject WPA2 association without PMF");
4417 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4418 }
4419#endif /* CONFIG_MBO */
4420
Hai Shalom74f70d42019-02-11 14:42:39 -08004421#if defined(CONFIG_FILS) && defined(CONFIG_OCV)
4422 if (wpa_auth_uses_ocv(sta->wpa_sm) &&
4423 (sta->auth_alg == WLAN_AUTH_FILS_SK ||
4424 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
4425 sta->auth_alg == WLAN_AUTH_FILS_PK)) {
4426 struct wpa_channel_info ci;
4427 int tx_chanwidth;
4428 int tx_seg1_idx;
Hai Shalom899fcc72020-10-19 14:38:18 -07004429 enum oci_verify_result res;
Hai Shalom74f70d42019-02-11 14:42:39 -08004430
4431 if (hostapd_drv_channel_info(hapd, &ci) != 0) {
4432 wpa_printf(MSG_WARNING,
4433 "Failed to get channel info to validate received OCI in FILS (Re)Association Request frame");
4434 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4435 }
4436
4437 if (get_sta_tx_parameters(sta->wpa_sm,
4438 channel_width_to_int(ci.chanwidth),
4439 ci.seg1_idx, &tx_chanwidth,
4440 &tx_seg1_idx) < 0)
4441 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4442
Sunil Ravi036cec52023-03-29 11:35:17 -07004443 res = ocv_verify_tx_params(elems->oci, elems->oci_len, &ci,
Hai Shalom899fcc72020-10-19 14:38:18 -07004444 tx_chanwidth, tx_seg1_idx);
4445 if (wpa_auth_uses_ocv(sta->wpa_sm) == 2 &&
4446 res == OCI_NOT_FOUND) {
4447 /* Work around misbehaving STAs */
4448 wpa_printf(MSG_INFO,
4449 "FILS: Disable OCV with a STA that does not send OCI");
4450 wpa_auth_set_ocv(sta->wpa_sm, 0);
4451 } else if (res != OCI_SUCCESS) {
4452 wpa_printf(MSG_WARNING, "FILS: OCV failed: %s",
4453 ocv_errorstr);
4454 wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="
4455 MACSTR " frame=fils-reassoc-req error=%s",
4456 MAC2STR(sta->addr), ocv_errorstr);
Hai Shalom74f70d42019-02-11 14:42:39 -08004457 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4458 }
4459 }
4460#endif /* CONFIG_FILS && CONFIG_OCV */
4461
Sunil Ravi036cec52023-03-29 11:35:17 -07004462 ap_copy_sta_supp_op_classes(sta, elems->supp_op_classes,
4463 elems->supp_op_classes_len);
Dmitry Shmidt9c175262016-03-03 10:20:07 -08004464
Dmitry Shmidt849734c2016-05-27 09:59:01 -07004465 if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) &&
Sunil Ravi036cec52023-03-29 11:35:17 -07004466 elems->rrm_enabled &&
4467 elems->rrm_enabled_len >= sizeof(sta->rrm_enabled_capa))
4468 os_memcpy(sta->rrm_enabled_capa, elems->rrm_enabled,
Dmitry Shmidt849734c2016-05-27 09:59:01 -07004469 sizeof(sta->rrm_enabled_capa));
4470
Sunil Ravi036cec52023-03-29 11:35:17 -07004471 if (elems->power_capab) {
4472 sta->min_tx_power = elems->power_capab[0];
4473 sta->max_tx_power = elems->power_capab[1];
Roshan Pius3a1667e2018-07-03 15:17:14 -07004474 sta->power_capab = 1;
4475 } else {
4476 sta->power_capab = 0;
4477 }
4478
Sunil Ravi7f769292024-07-23 22:21:32 +00004479 if (elems->bss_max_idle_period &&
4480 hapd->conf->max_acceptable_idle_period) {
4481 u16 req;
4482
4483 req = WPA_GET_LE16(elems->bss_max_idle_period);
4484 if (req <= hapd->conf->max_acceptable_idle_period)
4485 sta->max_idle_period = req;
4486 else if (hapd->conf->max_acceptable_idle_period >
4487 hapd->conf->ap_max_inactivity)
4488 sta->max_idle_period =
4489 hapd->conf->max_acceptable_idle_period;
4490 }
4491
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00004492 if (elems->wfa_capab)
4493 hostapd_wfa_capab(hapd, sta, elems->wfa_capab,
4494 elems->wfa_capab + elems->wfa_capab_len);
4495
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004496 return WLAN_STATUS_SUCCESS;
4497}
4498
4499
Sunil Ravi036cec52023-03-29 11:35:17 -07004500static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
4501 const u8 *ies, size_t ies_len, int reassoc)
4502{
4503 struct ieee802_11_elems elems;
4504
4505 if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
4506 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
4507 HOSTAPD_LEVEL_INFO,
4508 "Station sent an invalid association request");
4509 return WLAN_STATUS_UNSPECIFIED_FAILURE;
4510 }
4511
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004512 return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, reassoc,
4513 false);
4514}
4515
4516
4517#ifdef CONFIG_IEEE80211BE
4518
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004519static void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
4520 struct mld_link_info *link)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004521{
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004522 u8 buf[EHT_ML_MAX_STA_PROF_LEN];
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004523 u8 *p = buf;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004524 size_t buflen = sizeof(buf);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004525
4526 /* Capability Info */
4527 WPA_PUT_LE16(p, hostapd_own_capab_info(hapd));
4528 p += 2;
4529
4530 /* Status Code */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004531 WPA_PUT_LE16(p, link->status);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004532 p += 2;
4533
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004534 if (link->status != WLAN_STATUS_SUCCESS)
4535 goto out;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004536
4537 /* AID is not included */
4538 p = hostapd_eid_supp_rates(hapd, p);
4539 p = hostapd_eid_ext_supp_rates(hapd, p);
4540 p = hostapd_eid_rm_enabled_capab(hapd, p, buf + buflen - p);
4541 p = hostapd_eid_ht_capabilities(hapd, p);
4542 p = hostapd_eid_ht_operation(hapd, p);
4543
4544 if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
4545 p = hostapd_eid_vht_capabilities(hapd, p, 0);
4546 p = hostapd_eid_vht_operation(hapd, p);
4547 }
4548
4549 if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
4550 p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
4551 p = hostapd_eid_he_operation(hapd, p);
4552 p = hostapd_eid_spatial_reuse(hapd, p);
4553 p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
4554 p = hostapd_eid_he_6ghz_band_cap(hapd, p);
4555 if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
4556 p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
4557 p = hostapd_eid_eht_operation(hapd, p);
4558 }
4559 }
4560
4561 p = hostapd_eid_ext_capab(hapd, p, false);
4562 p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
4563 p = hostapd_eid_wmm(hapd, p);
4564
4565 if (hapd->conf->assocresp_elements &&
4566 (size_t) (buf + buflen - p) >=
4567 wpabuf_len(hapd->conf->assocresp_elements)) {
4568 os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
4569 wpabuf_len(hapd->conf->assocresp_elements));
4570 p += wpabuf_len(hapd->conf->assocresp_elements);
4571 }
4572
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004573out:
4574 os_free(link->resp_sta_profile);
4575 link->resp_sta_profile = os_memdup(buf, p - buf);
4576 link->resp_sta_profile_len = link->resp_sta_profile ? p - buf : 0;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004577}
4578
4579
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004580static int ieee80211_ml_process_link(struct hostapd_data *hapd,
4581 struct sta_info *origin_sta,
4582 struct mld_link_info *link,
4583 const u8 *ies, size_t ies_len,
4584 bool reassoc, bool offload)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004585{
4586 struct ieee802_11_elems elems;
4587 struct wpabuf *mlbuf = NULL;
4588 struct sta_info *sta = NULL;
4589 u16 status = WLAN_STATUS_SUCCESS;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004590 int i;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004591
4592 wpa_printf(MSG_DEBUG, "MLD: link: link_id=%u, peer=" MACSTR,
4593 hapd->mld_link_id, MAC2STR(link->peer_addr));
4594
4595 if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
4596 wpa_printf(MSG_DEBUG, "MLD: link: Element parsing failed");
4597 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
4598 goto out;
4599 }
4600
4601 sta = ap_get_sta(hapd, origin_sta->addr);
4602 if (sta) {
4603 wpa_printf(MSG_INFO, "MLD: link: Station already exists");
4604 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
4605 sta = NULL;
4606 goto out;
4607 }
4608
4609 sta = ap_sta_add(hapd, origin_sta->addr);
4610 if (!sta) {
4611 wpa_printf(MSG_DEBUG, "MLD: link: ap_sta_add() failed");
4612 status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
4613 goto out;
4614 }
4615
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004616 mlbuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len, true);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004617 if (!mlbuf)
4618 goto out;
4619
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00004620 if (ieee802_11_parse_link_assoc_req(&elems, mlbuf, hapd->mld_link_id,
4621 true) == ParseFailed) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004622 wpa_printf(MSG_DEBUG,
4623 "MLD: link: Failed to parse association request Multi-Link element");
4624 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
4625 goto out;
4626 }
4627
4628 sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
Sunil Ravi7f769292024-07-23 22:21:32 +00004629 sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
4630
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004631 status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
4632 if (status != WLAN_STATUS_SUCCESS) {
4633 wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
4634 goto out;
4635 }
4636
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004637 ap_sta_set_mld(sta, true);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004638
4639 os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004640 for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
4641 struct mld_link_info *li = &sta->mld_info.links[i];
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004642
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004643 li->resp_sta_profile = NULL;
4644 li->resp_sta_profile_len = 0;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004645 }
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004646
4647 if (!offload) {
4648 /*
4649 * Get the AID from the station on which the association was
4650 * performed, and mark it as used.
4651 */
4652 sta->aid = origin_sta->aid;
4653 if (sta->aid == 0) {
4654 wpa_printf(MSG_DEBUG, "MLD: link: No AID assigned");
4655 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
4656 goto out;
4657 }
4658 hapd->sta_aid[(sta->aid - 1) / 32] |= BIT((sta->aid - 1) % 32);
4659 sta->listen_interval = origin_sta->listen_interval;
4660 if (update_ht_state(hapd, sta) > 0)
4661 ieee802_11_update_beacons(hapd->iface);
4662 }
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004663
Sunil Ravi7f769292024-07-23 22:21:32 +00004664 /* Maintain state machine reference on all link STAs, this is needed
4665 * during group rekey handling.
4666 */
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004667 wpa_auth_sta_deinit(sta->wpa_sm);
Sunil Ravi7f769292024-07-23 22:21:32 +00004668 sta->wpa_sm = origin_sta->wpa_sm;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004669
4670 /*
4671 * Do not initialize the EAPOL state machine.
4672 * TODO: Maybe it is needed?
4673 */
4674 sta->eapol_sm = NULL;
4675
4676 wpa_printf(MSG_DEBUG, "MLD: link=%u, association OK (aid=%u)",
4677 hapd->mld_link_id, sta->aid);
4678
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004679 sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC_REQ_OK;
4680
4681 /* TODO: What other processing is required? */
4682
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004683 if (!offload && add_associated_sta(hapd, sta, reassoc))
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004684 status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
4685out:
4686 wpabuf_free(mlbuf);
4687 link->status = status;
4688
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004689 if (!offload)
4690 ieee80211_ml_build_assoc_resp(hapd, link);
Sunil Ravi72e01222024-03-09 01:25:43 +00004691
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004692 wpa_printf(MSG_DEBUG, "MLD: link: status=%u", status);
4693 if (status != WLAN_STATUS_SUCCESS) {
4694 if (sta)
4695 ap_free_sta(hapd, sta);
4696 return -1;
4697 }
4698
4699 return 0;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004700}
4701
4702
4703bool hostapd_is_mld_ap(struct hostapd_data *hapd)
4704{
4705 if (!hapd->conf->mld_ap)
4706 return false;
4707
4708 if (!hapd->iface || !hapd->iface->interfaces ||
4709 hapd->iface->interfaces->count <= 1)
4710 return false;
4711
4712 return true;
4713}
4714
4715#endif /* CONFIG_IEEE80211BE */
4716
4717
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004718int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
4719 struct sta_info *sta,
4720 const u8 *ies, size_t ies_len,
4721 bool reassoc, int tx_link_status,
4722 bool offload)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004723{
4724#ifdef CONFIG_IEEE80211BE
Sunil Ravi7f769292024-07-23 22:21:32 +00004725 unsigned int i;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004726
4727 if (!hostapd_is_mld_ap(hapd))
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004728 return 0;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004729
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004730 for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
Sunil Ravi7f769292024-07-23 22:21:32 +00004731 struct hostapd_data *bss = NULL;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004732 struct mld_link_info *link = &sta->mld_info.links[i];
Sunil Ravi7f769292024-07-23 22:21:32 +00004733 bool link_bss_found = false;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004734
Sunil Ravi7f769292024-07-23 22:21:32 +00004735 if (!link->valid || i == sta->mld_assoc_link_id)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004736 continue;
4737
Sunil Ravi7f769292024-07-23 22:21:32 +00004738 for_each_mld_link(bss, hapd) {
4739 if (bss == hapd)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004740 continue;
4741
Sunil Ravi7f769292024-07-23 22:21:32 +00004742 if (bss->mld_link_id != i)
4743 continue;
4744
4745 link_bss_found = true;
4746 break;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004747 }
4748
Sunil Ravi7f769292024-07-23 22:21:32 +00004749 if (!link_bss_found || TEST_FAIL()) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004750 wpa_printf(MSG_DEBUG,
4751 "MLD: No link match for link_id=%u", i);
4752
4753 link->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004754 if (!offload)
4755 ieee80211_ml_build_assoc_resp(hapd, link);
4756 } else if (tx_link_status != WLAN_STATUS_SUCCESS) {
4757 /* TX link rejected the connection */
4758 link->status = WLAN_STATUS_DENIED_TX_LINK_NOT_ACCEPTED;
4759 if (!offload)
4760 ieee80211_ml_build_assoc_resp(hapd, link);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004761 } else {
Sunil Ravi7f769292024-07-23 22:21:32 +00004762 if (ieee80211_ml_process_link(bss, sta, link,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004763 ies, ies_len, reassoc,
4764 offload))
4765 return -1;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004766 }
4767 }
4768#endif /* CONFIG_IEEE80211BE */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004769
4770 return 0;
Sunil Ravi036cec52023-03-29 11:35:17 -07004771}
4772
4773
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004774static void send_deauth(struct hostapd_data *hapd, const u8 *addr,
4775 u16 reason_code)
4776{
4777 int send_len;
4778 struct ieee80211_mgmt reply;
4779
4780 os_memset(&reply, 0, sizeof(reply));
4781 reply.frame_control =
4782 IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH);
4783 os_memcpy(reply.da, addr, ETH_ALEN);
4784 os_memcpy(reply.sa, hapd->own_addr, ETH_ALEN);
4785 os_memcpy(reply.bssid, hapd->own_addr, ETH_ALEN);
4786
4787 send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth);
4788 reply.u.deauth.reason_code = host_to_le16(reason_code);
4789
Hai Shalomfdcde762020-04-02 11:19:20 -07004790 if (hostapd_drv_send_mlme(hapd, &reply, send_len, 0, NULL, 0, 0) < 0)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004791 wpa_printf(MSG_INFO, "Failed to send deauth: %s",
4792 strerror(errno));
4793}
4794
4795
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004796static int add_associated_sta(struct hostapd_data *hapd,
Hai Shalom74f70d42019-02-11 14:42:39 -08004797 struct sta_info *sta, int reassoc)
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004798{
4799 struct ieee80211_ht_capabilities ht_cap;
4800 struct ieee80211_vht_capabilities vht_cap;
Hai Shalom81f62d82019-07-22 12:10:00 -07004801 struct ieee80211_he_capabilities he_cap;
Sunil Ravia04bd252022-05-02 22:54:18 -07004802 struct ieee80211_eht_capabilities eht_cap;
Mathy Vanhoeff6e1f662017-07-14 15:15:35 +02004803 int set = 1;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004804 const u8 *mld_link_addr = NULL;
4805 bool mld_link_sta = false;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00004806 u16 eml_cap = 0;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004807
4808#ifdef CONFIG_IEEE80211BE
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004809 if (ap_sta_is_mld(hapd, sta)) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004810 u8 mld_link_id = hapd->mld_link_id;
4811
4812 mld_link_sta = sta->mld_assoc_link_id != mld_link_id;
4813 mld_link_addr = sta->mld_info.links[mld_link_id].peer_addr;
4814
4815 if (hapd->mld_link_id != sta->mld_assoc_link_id)
4816 set = 0;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00004817 eml_cap = sta->mld_info.common_info.eml_capa;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004818 }
4819#endif /* CONFIG_IEEE80211BE */
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004820
4821 /*
4822 * Remove the STA entry to ensure the STA PS state gets cleared and
4823 * configuration gets updated. This is relevant for cases, such as
4824 * FT-over-the-DS, where a station re-associates back to the same AP but
4825 * skips the authentication flow, or if working with a driver that
4826 * does not support full AP client state.
Mathy Vanhoeff6e1f662017-07-14 15:15:35 +02004827 *
4828 * Skip this if the STA has already completed FT reassociation and the
4829 * TK has been configured since the TX/RX PN must not be reset to 0 for
4830 * the same key.
Hai Shalom74f70d42019-02-11 14:42:39 -08004831 *
4832 * FT-over-the-DS has a special case where the STA entry (and as such,
4833 * the TK) has not yet been configured to the driver depending on which
4834 * driver interface is used. For that case, allow add-STA operation to
4835 * be used (instead of set-STA). This is needed to allow mac80211-based
4836 * drivers to accept the STA parameter configuration. Since this is
4837 * after a new FT-over-DS exchange, a new TK has been derived, so key
4838 * reinstallation is not a concern for this case.
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004839 */
Hai Shalom74f70d42019-02-11 14:42:39 -08004840 wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
4841 " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
4842 MAC2STR(sta->addr), sta->added_unassoc, sta->auth_alg,
4843 sta->ft_over_ds, reassoc,
4844 !!(sta->flags & WLAN_STA_AUTHORIZED),
4845 wpa_auth_sta_ft_tk_already_set(sta->wpa_sm),
4846 wpa_auth_sta_fils_tk_already_set(sta->wpa_sm));
4847
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004848 if (!mld_link_sta && !sta->added_unassoc &&
Mathy Vanhoeff6e1f662017-07-14 15:15:35 +02004849 (!(sta->flags & WLAN_STA_AUTHORIZED) ||
Hai Shalom74f70d42019-02-11 14:42:39 -08004850 (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004851 (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
4852 !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004853 hostapd_drv_sta_remove(hapd, sta->addr);
Mathy Vanhoeff6e1f662017-07-14 15:15:35 +02004854 wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
4855 set = 0;
Hai Shalom74f70d42019-02-11 14:42:39 -08004856
4857 /* Do not allow the FT-over-DS exception to be used more than
4858 * once per authentication exchange to guarantee a new TK is
4859 * used here */
4860 sta->ft_over_ds = 0;
Mathy Vanhoeff6e1f662017-07-14 15:15:35 +02004861 }
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004862
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004863 if (sta->flags & WLAN_STA_HT)
4864 hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004865#ifdef CONFIG_IEEE80211AC
4866 if (sta->flags & WLAN_STA_VHT)
4867 hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
4868#endif /* CONFIG_IEEE80211AC */
Hai Shalom81f62d82019-07-22 12:10:00 -07004869#ifdef CONFIG_IEEE80211AX
4870 if (sta->flags & WLAN_STA_HE) {
4871 hostapd_get_he_capab(hapd, sta->he_capab, &he_cap,
4872 sta->he_capab_len);
4873 }
4874#endif /* CONFIG_IEEE80211AX */
Sunil Ravia04bd252022-05-02 22:54:18 -07004875#ifdef CONFIG_IEEE80211BE
4876 if (sta->flags & WLAN_STA_EHT)
4877 hostapd_get_eht_capab(hapd, sta->eht_capab, &eht_cap,
4878 sta->eht_capab_len);
4879#endif /* CONFIG_IEEE80211BE */
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004880
4881 /*
4882 * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
4883 * will be set when the ACK frame for the (Re)Association Response frame
4884 * is processed (TX status driver event).
4885 */
4886 if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
4887 sta->supported_rates, sta->supported_rates_len,
4888 sta->listen_interval,
4889 sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
4890 sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
Hai Shalom81f62d82019-07-22 12:10:00 -07004891 sta->flags & WLAN_STA_HE ? &he_cap : NULL,
4892 sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0,
Sunil Ravia04bd252022-05-02 22:54:18 -07004893 sta->flags & WLAN_STA_EHT ? &eht_cap : NULL,
4894 sta->flags & WLAN_STA_EHT ? sta->eht_capab_len : 0,
Hai Shalom4fbc08f2020-05-18 12:37:00 -07004895 sta->he_6ghz_capab,
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004896 sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
Dmitry Shmidt849734c2016-05-27 09:59:01 -07004897 sta->vht_opmode, sta->p2p_ie ? 1 : 0,
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00004898 set, mld_link_addr, mld_link_sta, eml_cap)) {
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004899 hostapd_logger(hapd, sta->addr,
4900 HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
4901 "Could not %s STA to kernel driver",
Mathy Vanhoeff6e1f662017-07-14 15:15:35 +02004902 set ? "set" : "add");
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08004903
4904 if (sta->added_unassoc) {
4905 hostapd_drv_sta_remove(hapd, sta->addr);
4906 sta->added_unassoc = 0;
4907 }
4908
4909 return -1;
4910 }
4911
4912 sta->added_unassoc = 0;
4913
4914 return 0;
4915}
4916
4917
4918static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
Dmitry Shmidt29333592017-01-09 12:27:11 -08004919 const u8 *addr, u16 status_code, int reassoc,
Hai Shalomfdcde762020-04-02 11:19:20 -07004920 const u8 *ies, size_t ies_len, int rssi,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00004921 int omit_rsnxe, bool allow_mld_addr_trans)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004922{
4923 int send_len;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08004924 u8 *buf;
4925 size_t buflen;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004926 struct ieee80211_mgmt *reply;
4927 u8 *p;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08004928 u16 res = WLAN_STATUS_SUCCESS;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004929
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08004930 buflen = sizeof(struct ieee80211_mgmt) + 1024;
4931#ifdef CONFIG_FILS
4932 if (sta && sta->fils_hlp_resp)
4933 buflen += wpabuf_len(sta->fils_hlp_resp);
Hai Shalom81f62d82019-07-22 12:10:00 -07004934 if (sta)
4935 buflen += 150;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08004936#endif /* CONFIG_FILS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004937#ifdef CONFIG_OWE
4938 if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
4939 buflen += 150;
4940#endif /* CONFIG_OWE */
Hai Shalom021b0b52019-04-10 11:17:58 -07004941#ifdef CONFIG_DPP2
4942 if (sta && sta->dpp_pfs)
4943 buflen += 5 + sta->dpp_pfs->curve->prime_len;
4944#endif /* CONFIG_DPP2 */
Sunil Ravia04bd252022-05-02 22:54:18 -07004945#ifdef CONFIG_IEEE80211BE
4946 if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
4947 buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
4948 buflen += 3 + sizeof(struct ieee80211_eht_operation);
Sunil Ravi036cec52023-03-29 11:35:17 -07004949 if (hapd->iconf->punct_bitmap)
4950 buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
Sunil Ravia04bd252022-05-02 22:54:18 -07004951 }
4952#endif /* CONFIG_IEEE80211BE */
4953
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08004954 buf = os_zalloc(buflen);
4955 if (!buf) {
4956 res = WLAN_STATUS_UNSPECIFIED_FAILURE;
4957 goto done;
4958 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004959 reply = (struct ieee80211_mgmt *) buf;
4960 reply->frame_control =
4961 IEEE80211_FC(WLAN_FC_TYPE_MGMT,
4962 (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
4963 WLAN_FC_STYPE_ASSOC_RESP));
Sunil Ravi2a14cf12023-11-21 00:54:38 +00004964
Dmitry Shmidt29333592017-01-09 12:27:11 -08004965 os_memcpy(reply->da, addr, ETH_ALEN);
Sunil Ravi7f769292024-07-23 22:21:32 +00004966 os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
4967 os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004968
4969 send_len = IEEE80211_HDRLEN;
4970 send_len += sizeof(reply->u.assoc_resp);
4971 reply->u.assoc_resp.capab_info =
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -07004972 host_to_le16(hostapd_own_capab_info(hapd));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004973 reply->u.assoc_resp.status_code = host_to_le16(status_code);
Dmitry Shmidt29333592017-01-09 12:27:11 -08004974
4975 reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) |
4976 BIT(14) | BIT(15));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004977 /* Supported rates */
4978 p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
4979 /* Extended supported rates */
4980 p = hostapd_eid_ext_supp_rates(hapd, p);
4981
Hai Shalomfdcde762020-04-02 11:19:20 -07004982 /* Radio measurement capabilities */
4983 p = hostapd_eid_rm_enabled_capab(hapd, p, buf + buflen - p);
4984
Hai Shalom74f70d42019-02-11 14:42:39 -08004985#ifdef CONFIG_MBO
4986 if (status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS &&
4987 rssi != 0) {
4988 int delta = hapd->iconf->rssi_reject_assoc_rssi - rssi;
4989
4990 p = hostapd_eid_mbo_rssi_assoc_rej(hapd, p, buf + buflen - p,
4991 delta);
4992 }
4993#endif /* CONFIG_MBO */
4994
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08004995#ifdef CONFIG_IEEE80211R_AP
Dmitry Shmidt29333592017-01-09 12:27:11 -08004996 if (sta && status_code == WLAN_STATUS_SUCCESS) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004997 /* IEEE 802.11r: Mobility Domain Information, Fast BSS
4998 * Transition Information, RSN, [RIC Response] */
4999 p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005000 buf + buflen - p,
Hai Shalomfdcde762020-04-02 11:19:20 -07005001 sta->auth_alg, ies, ies_len,
5002 omit_rsnxe);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005003 if (!p) {
5004 wpa_printf(MSG_DEBUG,
5005 "FT: Failed to write AssocResp IEs");
5006 res = WLAN_STATUS_UNSPECIFIED_FAILURE;
5007 goto done;
5008 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005009 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005010#endif /* CONFIG_IEEE80211R_AP */
Hai Shalom81f62d82019-07-22 12:10:00 -07005011#ifdef CONFIG_FILS
5012 if (sta && status_code == WLAN_STATUS_SUCCESS &&
5013 (sta->auth_alg == WLAN_AUTH_FILS_SK ||
5014 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
5015 sta->auth_alg == WLAN_AUTH_FILS_PK))
5016 p = wpa_auth_write_assoc_resp_fils(sta->wpa_sm, p,
5017 buf + buflen - p,
5018 ies, ies_len);
5019#endif /* CONFIG_FILS */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005020
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005021#ifdef CONFIG_OWE
Hai Shalom74f70d42019-02-11 14:42:39 -08005022 if (sta && status_code == WLAN_STATUS_SUCCESS &&
5023 (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005024 p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p,
5025 buf + buflen - p,
5026 ies, ies_len);
5027#endif /* CONFIG_OWE */
5028
Dmitry Shmidt29333592017-01-09 12:27:11 -08005029 if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005030 p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005031
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005032 p = hostapd_eid_ht_capabilities(hapd, p);
5033 p = hostapd_eid_ht_operation(hapd, p);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005034
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07005035#ifdef CONFIG_IEEE80211AC
Hai Shalomc3565922019-10-28 11:58:20 -07005036 if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
5037 !is_6ghz_op_class(hapd->iconf->op_class)) {
Dmitry Shmidt7d175302016-09-06 13:11:34 -07005038 u32 nsts = 0, sta_nsts;
5039
Dmitry Shmidt29333592017-01-09 12:27:11 -08005040 if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) {
Dmitry Shmidt7d175302016-09-06 13:11:34 -07005041 struct ieee80211_vht_capabilities *capa;
5042
5043 nsts = (hapd->iface->conf->vht_capab >>
5044 VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
5045 capa = sta->vht_capabilities;
5046 sta_nsts = (le_to_host32(capa->vht_capabilities_info) >>
5047 VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
5048
5049 if (nsts < sta_nsts)
5050 nsts = 0;
5051 else
5052 nsts = sta_nsts;
5053 }
5054 p = hostapd_eid_vht_capabilities(hapd, p, nsts);
Dmitry Shmidt2f74e362015-01-21 13:19:05 -08005055 p = hostapd_eid_vht_operation(hapd, p);
5056 }
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07005057#endif /* CONFIG_IEEE80211AC */
5058
Hai Shalom81f62d82019-07-22 12:10:00 -07005059#ifdef CONFIG_IEEE80211AX
Hai Shalom60840252021-02-19 19:02:11 -08005060 if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
Hai Shalom81f62d82019-07-22 12:10:00 -07005061 p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
5062 p = hostapd_eid_he_operation(hapd, p);
Sunil Ravia04bd252022-05-02 22:54:18 -07005063 p = hostapd_eid_cca(hapd, p);
Hai Shalom81f62d82019-07-22 12:10:00 -07005064 p = hostapd_eid_spatial_reuse(hapd, p);
5065 p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
Hai Shalom4fbc08f2020-05-18 12:37:00 -07005066 p = hostapd_eid_he_6ghz_band_cap(hapd, p);
Hai Shalom81f62d82019-07-22 12:10:00 -07005067 }
5068#endif /* CONFIG_IEEE80211AX */
5069
Sunil Ravi77d572f2023-01-17 23:58:31 +00005070 p = hostapd_eid_ext_capab(hapd, p, false);
Sunil Ravic0f5d412024-09-11 22:12:49 +00005071 p = hostapd_eid_bss_max_idle_period(hapd, p,
5072 sta ? sta->max_idle_period : 0);
Dmitry Shmidt29333592017-01-09 12:27:11 -08005073 if (sta && sta->qos_map_enabled)
Dmitry Shmidt051af732013-10-22 13:52:46 -07005074 p = hostapd_eid_qos_map_set(hapd, p);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005075
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08005076#ifdef CONFIG_FST
5077 if (hapd->iface->fst_ies) {
5078 os_memcpy(p, wpabuf_head(hapd->iface->fst_ies),
5079 wpabuf_len(hapd->iface->fst_ies));
5080 p += wpabuf_len(hapd->iface->fst_ies);
5081 }
5082#endif /* CONFIG_FST */
5083
Hai Shalomfdcde762020-04-02 11:19:20 -07005084#ifdef CONFIG_TESTING_OPTIONS
5085 if (hapd->conf->rsnxe_override_ft &&
5086 buf + buflen - p >=
5087 (long int) wpabuf_len(hapd->conf->rsnxe_override_ft) &&
5088 sta && sta->auth_alg == WLAN_AUTH_FT) {
5089 wpa_printf(MSG_DEBUG, "TESTING: RSNXE FT override");
5090 os_memcpy(p, wpabuf_head(hapd->conf->rsnxe_override_ft),
5091 wpabuf_len(hapd->conf->rsnxe_override_ft));
5092 p += wpabuf_len(hapd->conf->rsnxe_override_ft);
5093 goto rsnxe_done;
5094 }
5095#endif /* CONFIG_TESTING_OPTIONS */
5096 if (!omit_rsnxe)
5097 p = hostapd_eid_rsnxe(hapd, p, buf + buflen - p);
5098#ifdef CONFIG_TESTING_OPTIONS
5099rsnxe_done:
5100#endif /* CONFIG_TESTING_OPTIONS */
Hai Shalomc3565922019-10-28 11:58:20 -07005101
Sunil Ravia04bd252022-05-02 22:54:18 -07005102#ifdef CONFIG_IEEE80211BE
5103 if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005104 if (hapd->conf->mld_ap)
Sunil Ravib0ac25f2024-07-12 01:42:03 +00005105 p = hostapd_eid_eht_ml_assoc(hapd, sta, p);
Sunil Ravia04bd252022-05-02 22:54:18 -07005106 p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
5107 p = hostapd_eid_eht_operation(hapd, p);
5108 }
5109#endif /* CONFIG_IEEE80211BE */
5110
Hai Shalom021b0b52019-04-10 11:17:58 -07005111#ifdef CONFIG_OWE
5112 if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
5113 sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
Hai Shalom899fcc72020-10-19 14:38:18 -07005114 wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
5115 !wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
Hai Shalom021b0b52019-04-10 11:17:58 -07005116 struct wpabuf *pub;
5117
5118 pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
5119 if (!pub) {
5120 res = WLAN_STATUS_UNSPECIFIED_FAILURE;
5121 goto done;
5122 }
5123 /* OWE Diffie-Hellman Parameter element */
5124 *p++ = WLAN_EID_EXTENSION; /* Element ID */
5125 *p++ = 1 + 2 + wpabuf_len(pub); /* Length */
5126 *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */
5127 WPA_PUT_LE16(p, sta->owe_group);
5128 p += 2;
5129 os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub));
5130 p += wpabuf_len(pub);
5131 wpabuf_free(pub);
5132 }
5133#endif /* CONFIG_OWE */
5134
5135#ifdef CONFIG_DPP2
Hai Shalom4fbc08f2020-05-18 12:37:00 -07005136 if (DPP_VERSION > 1 && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
Hai Shalom021b0b52019-04-10 11:17:58 -07005137 sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS &&
5138 wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) {
5139 os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie),
5140 wpabuf_len(sta->dpp_pfs->ie));
5141 p += wpabuf_len(sta->dpp_pfs->ie);
5142 }
5143#endif /* CONFIG_DPP2 */
5144
Dmitry Shmidt2f74e362015-01-21 13:19:05 -08005145#ifdef CONFIG_IEEE80211AC
Dmitry Shmidt29333592017-01-09 12:27:11 -08005146 if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
Dmitry Shmidt2f74e362015-01-21 13:19:05 -08005147 p = hostapd_eid_vendor_vht(hapd, p);
5148#endif /* CONFIG_IEEE80211AC */
5149
Dmitry Shmidt29333592017-01-09 12:27:11 -08005150 if (sta && (sta->flags & WLAN_STA_WMM))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005151 p = hostapd_eid_wmm(hapd, p);
5152
5153#ifdef CONFIG_WPS
Dmitry Shmidt29333592017-01-09 12:27:11 -08005154 if (sta &&
5155 ((sta->flags & WLAN_STA_WPS) ||
5156 ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa))) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005157 struct wpabuf *wps = wps_build_assoc_resp_ie();
5158 if (wps) {
5159 os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
5160 p += wpabuf_len(wps);
5161 wpabuf_free(wps);
5162 }
5163 }
5164#endif /* CONFIG_WPS */
5165
Hai Shalom74f70d42019-02-11 14:42:39 -08005166 if (sta && (sta->flags & WLAN_STA_MULTI_AP))
Sunil Ravi99c035e2024-07-12 01:42:03 +00005167 p = hostapd_eid_multi_ap(hapd, p, buf + buflen - p);
Hai Shalom74f70d42019-02-11 14:42:39 -08005168
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005169#ifdef CONFIG_P2P
Dmitry Shmidt29333592017-01-09 12:27:11 -08005170 if (sta && sta->p2p_ie && hapd->p2p_group) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005171 struct wpabuf *p2p_resp_ie;
5172 enum p2p_status_code status;
5173 switch (status_code) {
5174 case WLAN_STATUS_SUCCESS:
5175 status = P2P_SC_SUCCESS;
5176 break;
5177 case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
5178 status = P2P_SC_FAIL_LIMIT_REACHED;
5179 break;
5180 default:
5181 status = P2P_SC_FAIL_INVALID_PARAMS;
5182 break;
5183 }
5184 p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status);
5185 if (p2p_resp_ie) {
5186 os_memcpy(p, wpabuf_head(p2p_resp_ie),
5187 wpabuf_len(p2p_resp_ie));
5188 p += wpabuf_len(p2p_resp_ie);
5189 wpabuf_free(p2p_resp_ie);
5190 }
5191 }
5192#endif /* CONFIG_P2P */
5193
5194#ifdef CONFIG_P2P_MANAGER
5195 if (hapd->conf->p2p & P2P_MANAGE)
5196 p = hostapd_eid_p2p_manage(hapd, p);
5197#endif /* CONFIG_P2P_MANAGER */
5198
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005199 p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005200
Dmitry Shmidt849734c2016-05-27 09:59:01 -07005201 if (hapd->conf->assocresp_elements &&
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005202 (size_t) (buf + buflen - p) >=
Dmitry Shmidt849734c2016-05-27 09:59:01 -07005203 wpabuf_len(hapd->conf->assocresp_elements)) {
5204 os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
5205 wpabuf_len(hapd->conf->assocresp_elements));
5206 p += wpabuf_len(hapd->conf->assocresp_elements);
5207 }
5208
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005209 send_len += p - reply->u.assoc_resp.variable;
5210
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005211#ifdef CONFIG_FILS
Dmitry Shmidt29333592017-01-09 12:27:11 -08005212 if (sta &&
5213 (sta->auth_alg == WLAN_AUTH_FILS_SK ||
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005214 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
5215 sta->auth_alg == WLAN_AUTH_FILS_PK) &&
5216 status_code == WLAN_STATUS_SUCCESS) {
5217 struct ieee802_11_elems elems;
5218
5219 if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005220 ParseFailed || !elems.fils_session) {
5221 res = WLAN_STATUS_UNSPECIFIED_FAILURE;
5222 goto done;
5223 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005224
5225 /* FILS Session */
5226 *p++ = WLAN_EID_EXTENSION; /* Element ID */
5227 *p++ = 1 + FILS_SESSION_LEN; /* Length */
5228 *p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
5229 os_memcpy(p, elems.fils_session, FILS_SESSION_LEN);
5230 send_len += 2 + 1 + FILS_SESSION_LEN;
5231
5232 send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len,
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005233 buflen, sta->fils_hlp_resp);
5234 if (send_len < 0) {
5235 res = WLAN_STATUS_UNSPECIFIED_FAILURE;
5236 goto done;
5237 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005238 }
5239#endif /* CONFIG_FILS */
5240
Hai Shalomfdcde762020-04-02 11:19:20 -07005241 if (hostapd_drv_send_mlme(hapd, reply, send_len, 0, NULL, 0, 0) < 0) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005242 wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
5243 strerror(errno));
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005244 res = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005245 }
5246
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005247done:
5248 os_free(buf);
5249 return res;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005250}
5251
5252
Roshan Pius3a1667e2018-07-03 15:17:14 -07005253#ifdef CONFIG_OWE
5254u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
5255 const u8 *owe_dh, u8 owe_dh_len,
Hai Shalomfdcde762020-04-02 11:19:20 -07005256 u8 *owe_buf, size_t owe_buf_len, u16 *status)
Roshan Pius3a1667e2018-07-03 15:17:14 -07005257{
5258#ifdef CONFIG_TESTING_OPTIONS
5259 if (hapd->conf->own_ie_override) {
5260 wpa_printf(MSG_DEBUG, "OWE: Using IE override");
Hai Shalomfdcde762020-04-02 11:19:20 -07005261 *status = WLAN_STATUS_SUCCESS;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005262 return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
5263 owe_buf_len, NULL, 0);
5264 }
5265#endif /* CONFIG_TESTING_OPTIONS */
5266
5267 if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
5268 wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
5269 owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
5270 owe_buf_len, NULL, 0);
Hai Shalomfdcde762020-04-02 11:19:20 -07005271 *status = WLAN_STATUS_SUCCESS;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005272 return owe_buf;
5273 }
5274
Hai Shalom81f62d82019-07-22 12:10:00 -07005275 if (sta->owe_pmk && sta->external_dh_updated) {
5276 wpa_printf(MSG_DEBUG, "OWE: Using previously derived PMK");
Hai Shalomfdcde762020-04-02 11:19:20 -07005277 *status = WLAN_STATUS_SUCCESS;
Hai Shalom81f62d82019-07-22 12:10:00 -07005278 return owe_buf;
5279 }
5280
Hai Shalomfdcde762020-04-02 11:19:20 -07005281 *status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
5282 if (*status != WLAN_STATUS_SUCCESS)
Roshan Pius3a1667e2018-07-03 15:17:14 -07005283 return NULL;
5284
5285 owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
5286 owe_buf_len, NULL, 0);
5287
5288 if (sta->owe_ecdh && owe_buf) {
5289 struct wpabuf *pub;
5290
5291 pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
5292 if (!pub) {
Hai Shalomfdcde762020-04-02 11:19:20 -07005293 *status = WLAN_STATUS_UNSPECIFIED_FAILURE;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005294 return owe_buf;
5295 }
5296
5297 /* OWE Diffie-Hellman Parameter element */
5298 *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
5299 *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
5300 *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
5301 */
5302 WPA_PUT_LE16(owe_buf, sta->owe_group);
5303 owe_buf += 2;
5304 os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
5305 owe_buf += wpabuf_len(pub);
5306 wpabuf_free(pub);
5307 }
5308
5309 return owe_buf;
5310}
5311#endif /* CONFIG_OWE */
5312
5313
Paul Stewart092955c2017-02-06 09:13:09 -08005314#ifdef CONFIG_FILS
5315
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005316void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta)
Paul Stewart092955c2017-02-06 09:13:09 -08005317{
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005318 u16 reply_res;
Paul Stewart092955c2017-02-06 09:13:09 -08005319
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005320 wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR,
5321 MAC2STR(sta->addr));
5322 eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
5323 if (!sta->fils_pending_assoc_req)
Paul Stewart092955c2017-02-06 09:13:09 -08005324 return;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005325 reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS,
5326 sta->fils_pending_assoc_is_reassoc,
5327 sta->fils_pending_assoc_req,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00005328 sta->fils_pending_assoc_req_len, 0, 0,
5329 true);
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005330 os_free(sta->fils_pending_assoc_req);
5331 sta->fils_pending_assoc_req = NULL;
5332 sta->fils_pending_assoc_req_len = 0;
5333 wpabuf_free(sta->fils_hlp_resp);
5334 sta->fils_hlp_resp = NULL;
5335 wpabuf_free(sta->hlp_dhcp_discover);
5336 sta->hlp_dhcp_discover = NULL;
Paul Stewart092955c2017-02-06 09:13:09 -08005337
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005338 /*
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005339 * Remove the station in case transmission of a success response fails.
5340 * At this point the station was already added associated to the driver.
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005341 */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005342 if (reply_res != WLAN_STATUS_SUCCESS)
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005343 hostapd_drv_sta_remove(hapd, sta->addr);
Paul Stewart092955c2017-02-06 09:13:09 -08005344}
5345
5346
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005347void fils_hlp_timeout(void *eloop_ctx, void *eloop_data)
Paul Stewart092955c2017-02-06 09:13:09 -08005348{
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005349 struct hostapd_data *hapd = eloop_ctx;
5350 struct sta_info *sta = eloop_data;
Paul Stewart092955c2017-02-06 09:13:09 -08005351
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005352 wpa_printf(MSG_DEBUG,
5353 "FILS: HLP response timeout - continue with association response for "
5354 MACSTR, MAC2STR(sta->addr));
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005355 if (sta->fils_drv_assoc_finish)
5356 hostapd_notify_assoc_fils_finish(hapd, sta);
5357 else
5358 fils_hlp_finish_assoc(hapd, sta);
Paul Stewart092955c2017-02-06 09:13:09 -08005359}
5360
5361#endif /* CONFIG_FILS */
5362
5363
Sunil Ravib0ac25f2024-07-12 01:42:03 +00005364#ifdef CONFIG_IEEE80211BE
5365static struct sta_info * handle_mlo_translate(struct hostapd_data *hapd,
5366 const struct ieee80211_mgmt *mgmt,
5367 size_t len, bool reassoc,
5368 struct hostapd_data **assoc_hapd)
5369{
5370 struct sta_info *sta;
5371 struct ieee802_11_elems elems;
5372 u8 mld_addr[ETH_ALEN];
5373 const u8 *pos;
5374
5375 if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be)
5376 return NULL;
5377
5378 if (reassoc) {
5379 len -= IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req);
5380 pos = mgmt->u.reassoc_req.variable;
5381 } else {
5382 len -= IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req);
5383 pos = mgmt->u.assoc_req.variable;
5384 }
5385
5386 if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed)
5387 return NULL;
5388
5389 if (hostapd_process_ml_assoc_req_addr(hapd, elems.basic_mle,
5390 elems.basic_mle_len,
5391 mld_addr))
5392 return NULL;
5393
5394 sta = ap_get_sta(hapd, mld_addr);
5395 if (!sta)
5396 return NULL;
5397
5398 wpa_printf(MSG_DEBUG, "MLD: assoc: mld=" MACSTR ", link=" MACSTR,
5399 MAC2STR(mld_addr), MAC2STR(mgmt->sa));
5400
5401 return hostapd_ml_get_assoc_sta(hapd, sta, assoc_hapd);
5402}
5403#endif /* CONFIG_IEEE80211BE */
5404
5405
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005406static void handle_assoc(struct hostapd_data *hapd,
5407 const struct ieee80211_mgmt *mgmt, size_t len,
Hai Shalom74f70d42019-02-11 14:42:39 -08005408 int reassoc, int rssi)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005409{
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005410 u16 capab_info, listen_interval, seq_ctrl, fc;
Hai Shalomb755a2a2020-04-23 21:49:02 -07005411 int resp = WLAN_STATUS_SUCCESS;
Hai Shalom899fcc72020-10-19 14:38:18 -07005412 u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005413 const u8 *pos;
5414 int left, i;
5415 struct sta_info *sta;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005416 u8 *tmp = NULL;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005417#ifdef CONFIG_FILS
5418 int delay_assoc = 0;
5419#endif /* CONFIG_FILS */
Hai Shalomfdcde762020-04-02 11:19:20 -07005420 int omit_rsnxe = 0;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005421 bool set_beacon = false;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00005422 bool mld_addrs_not_translated = false;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005423
5424 if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
5425 sizeof(mgmt->u.assoc_req))) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08005426 wpa_printf(MSG_INFO, "handle_assoc(reassoc=%d) - too short payload (len=%lu)",
5427 reassoc, (unsigned long) len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005428 return;
5429 }
5430
Dmitry Shmidt8da800a2013-04-24 12:57:01 -07005431#ifdef CONFIG_TESTING_OPTIONS
5432 if (reassoc) {
Dmitry Shmidt7832adb2014-04-29 10:53:02 -07005433 if (hapd->iconf->ignore_reassoc_probability > 0.0 &&
Dmitry Shmidt8da800a2013-04-24 12:57:01 -07005434 drand48() < hapd->iconf->ignore_reassoc_probability) {
5435 wpa_printf(MSG_INFO,
5436 "TESTING: ignoring reassoc request from "
5437 MACSTR, MAC2STR(mgmt->sa));
5438 return;
5439 }
5440 } else {
Dmitry Shmidt7832adb2014-04-29 10:53:02 -07005441 if (hapd->iconf->ignore_assoc_probability > 0.0 &&
Dmitry Shmidt8da800a2013-04-24 12:57:01 -07005442 drand48() < hapd->iconf->ignore_assoc_probability) {
5443 wpa_printf(MSG_INFO,
5444 "TESTING: ignoring assoc request from "
5445 MACSTR, MAC2STR(mgmt->sa));
5446 return;
5447 }
5448 }
5449#endif /* CONFIG_TESTING_OPTIONS */
5450
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005451 fc = le_to_host16(mgmt->frame_control);
5452 seq_ctrl = le_to_host16(mgmt->seq_ctrl);
5453
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005454 if (reassoc) {
5455 capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info);
5456 listen_interval = le_to_host16(
5457 mgmt->u.reassoc_req.listen_interval);
5458 wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR
5459 " capab_info=0x%02x listen_interval=%d current_ap="
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005460 MACSTR " seq_ctrl=0x%x%s",
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005461 MAC2STR(mgmt->sa), capab_info, listen_interval,
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005462 MAC2STR(mgmt->u.reassoc_req.current_ap),
5463 seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005464 left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
5465 pos = mgmt->u.reassoc_req.variable;
5466 } else {
5467 capab_info = le_to_host16(mgmt->u.assoc_req.capab_info);
5468 listen_interval = le_to_host16(
5469 mgmt->u.assoc_req.listen_interval);
5470 wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005471 " capab_info=0x%02x listen_interval=%d "
5472 "seq_ctrl=0x%x%s",
5473 MAC2STR(mgmt->sa), capab_info, listen_interval,
5474 seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005475 left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
5476 pos = mgmt->u.assoc_req.variable;
5477 }
5478
5479 sta = ap_get_sta(hapd, mgmt->sa);
Sunil Ravib0ac25f2024-07-12 01:42:03 +00005480
5481#ifdef CONFIG_IEEE80211BE
5482 /*
5483 * It is possible that the association frame is from an associated
5484 * non-AP MLD station, that tries to re-associate using different link
5485 * addresses. In such a case, try to find the station based on the AP
5486 * MLD MAC address.
5487 */
5488 if (!sta) {
5489 struct hostapd_data *assoc_hapd;
5490
5491 sta = handle_mlo_translate(hapd, mgmt, len, reassoc,
5492 &assoc_hapd);
5493 if (sta) {
5494 wpa_printf(MSG_DEBUG,
5495 "MLD: Switching to assoc hapd/station");
5496 hapd = assoc_hapd;
5497 mld_addrs_not_translated = true;
5498 }
5499 }
5500#endif /* CONFIG_IEEE80211BE */
5501
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005502#ifdef CONFIG_IEEE80211R_AP
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005503 if (sta && sta->auth_alg == WLAN_AUTH_FT &&
5504 (sta->flags & WLAN_STA_AUTH) == 0) {
5505 wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
5506 "prior to authentication since it is using "
5507 "over-the-DS FT", MAC2STR(mgmt->sa));
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005508
5509 /*
5510 * Mark station as authenticated, to avoid adding station
5511 * entry in the driver as associated and not authenticated
5512 */
5513 sta->flags |= WLAN_STA_AUTH;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005514 } else
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005515#endif /* CONFIG_IEEE80211R_AP */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005516 if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
Dmitry Shmidt29333592017-01-09 12:27:11 -08005517 if (hapd->iface->current_mode &&
5518 hapd->iface->current_mode->mode ==
5519 HOSTAPD_MODE_IEEE80211AD) {
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005520 int acl_res;
Hai Shalomfdcde762020-04-02 11:19:20 -07005521 struct radius_sta info;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005522
Hai Shalomfdcde762020-04-02 11:19:20 -07005523 acl_res = ieee802_11_allowed_address(hapd, mgmt->sa,
5524 (const u8 *) mgmt,
5525 len, &info);
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005526 if (acl_res == HOSTAPD_ACL_REJECT) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005527 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
5528 "Ignore Association Request frame from "
5529 MACSTR " due to ACL reject",
5530 MAC2STR(mgmt->sa));
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005531 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
5532 goto fail;
5533 }
5534 if (acl_res == HOSTAPD_ACL_PENDING)
5535 return;
5536
Dmitry Shmidt29333592017-01-09 12:27:11 -08005537 /* DMG/IEEE 802.11ad does not use authentication.
5538 * Allocate sta entry upon association. */
5539 sta = ap_sta_add(hapd, mgmt->sa);
5540 if (!sta) {
5541 hostapd_logger(hapd, mgmt->sa,
5542 HOSTAPD_MODULE_IEEE80211,
5543 HOSTAPD_LEVEL_INFO,
5544 "Failed to add STA");
5545 resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
5546 goto fail;
5547 }
5548
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005549 acl_res = ieee802_11_set_radius_info(
Hai Shalomfdcde762020-04-02 11:19:20 -07005550 hapd, sta, acl_res, &info);
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005551 if (acl_res) {
5552 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
5553 goto fail;
5554 }
5555
Dmitry Shmidt29333592017-01-09 12:27:11 -08005556 hostapd_logger(hapd, sta->addr,
5557 HOSTAPD_MODULE_IEEE80211,
5558 HOSTAPD_LEVEL_DEBUG,
5559 "Skip authentication for DMG/IEEE 802.11ad");
5560 sta->flags |= WLAN_STA_AUTH;
5561 wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
5562 sta->auth_alg = WLAN_AUTH_OPEN;
5563 } else {
5564 hostapd_logger(hapd, mgmt->sa,
5565 HOSTAPD_MODULE_IEEE80211,
5566 HOSTAPD_LEVEL_INFO,
5567 "Station tried to associate before authentication (aid=%d flags=0x%x)",
5568 sta ? sta->aid : -1,
5569 sta ? sta->flags : 0);
5570 send_deauth(hapd, mgmt->sa,
5571 WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
5572 return;
5573 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005574 }
5575
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005576 if ((fc & WLAN_FC_RETRY) &&
5577 sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
5578 sta->last_seq_ctrl == seq_ctrl &&
Paul Stewart092955c2017-02-06 09:13:09 -08005579 sta->last_subtype == (reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
5580 WLAN_FC_STYPE_ASSOC_REQ)) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005581 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
5582 HOSTAPD_LEVEL_DEBUG,
5583 "Drop repeated association frame seq_ctrl=0x%x",
5584 seq_ctrl);
5585 return;
5586 }
5587 sta->last_seq_ctrl = seq_ctrl;
5588 sta->last_subtype = reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
5589 WLAN_FC_STYPE_ASSOC_REQ;
5590
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005591 if (hapd->tkip_countermeasures) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005592 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005593 goto fail;
5594 }
5595
5596 if (listen_interval > hapd->conf->max_listen_interval) {
5597 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
5598 HOSTAPD_LEVEL_DEBUG,
5599 "Too large Listen Interval (%d)",
5600 listen_interval);
5601 resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE;
5602 goto fail;
5603 }
5604
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005605#ifdef CONFIG_MBO
5606 if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
5607 resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
5608 goto fail;
5609 }
Hai Shalom74f70d42019-02-11 14:42:39 -08005610
5611 if (hapd->iconf->rssi_reject_assoc_rssi && rssi &&
5612 rssi < hapd->iconf->rssi_reject_assoc_rssi &&
5613 (sta->auth_rssi == 0 ||
5614 sta->auth_rssi < hapd->iconf->rssi_reject_assoc_rssi)) {
5615 resp = WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS;
5616 goto fail;
5617 }
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005618#endif /* CONFIG_MBO */
5619
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005620 if (hapd->conf->wpa && check_sa_query(hapd, sta, reassoc)) {
5621 resp = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
5622 goto fail;
5623 }
5624
Dmitry Shmidt849734c2016-05-27 09:59:01 -07005625 /*
5626 * sta->capability is used in check_assoc_ies() for RRM enabled
5627 * capability element.
5628 */
5629 sta->capability = capab_info;
5630
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005631#ifdef CONFIG_FILS
5632 if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
5633 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
5634 sta->auth_alg == WLAN_AUTH_FILS_PK) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005635 int res;
5636
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005637 /* The end of the payload is encrypted. Need to decrypt it
5638 * before parsing. */
5639
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005640 tmp = os_memdup(pos, left);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005641 if (!tmp) {
5642 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
5643 goto fail;
5644 }
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005645
Roshan Pius3a1667e2018-07-03 15:17:14 -07005646 res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
5647 len, tmp, left);
5648 if (res < 0) {
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005649 resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
5650 goto fail;
5651 }
5652 pos = tmp;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005653 left = res;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005654 }
5655#endif /* CONFIG_FILS */
5656
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005657 /* followed by SSID and Supported rates; and HT capabilities if 802.11n
5658 * is used */
5659 resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
5660 if (resp != WLAN_STATUS_SUCCESS)
5661 goto fail;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00005662#ifdef CONFIG_IEEE80211R_AP
5663 if (reassoc && sta->auth_alg == WLAN_AUTH_FT)
5664 omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
5665#endif /* CONFIG_IEEE80211R_AP */
5666 if (hapd->conf->rsn_override_omit_rsnxe)
5667 omit_rsnxe = 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005668
5669 if (hostapd_get_aid(hapd, sta) < 0) {
5670 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
5671 HOSTAPD_LEVEL_INFO, "No room for more AIDs");
5672 resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
5673 goto fail;
5674 }
5675
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005676 sta->listen_interval = listen_interval;
5677
Roshan Pius3a1667e2018-07-03 15:17:14 -07005678 if (hapd->iface->current_mode &&
5679 hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005680 sta->flags |= WLAN_STA_NONERP;
5681 for (i = 0; i < sta->supported_rates_len; i++) {
5682 if ((sta->supported_rates[i] & 0x7f) > 22) {
5683 sta->flags &= ~WLAN_STA_NONERP;
5684 break;
5685 }
5686 }
5687 if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) {
5688 sta->nonerp_set = 1;
5689 hapd->iface->num_sta_non_erp++;
5690 if (hapd->iface->num_sta_non_erp == 1)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005691 set_beacon = true;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005692 }
5693
5694 if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) &&
5695 !sta->no_short_slot_time_set) {
5696 sta->no_short_slot_time_set = 1;
5697 hapd->iface->num_sta_no_short_slot_time++;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005698 if (hapd->iface->current_mode &&
5699 hapd->iface->current_mode->mode ==
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005700 HOSTAPD_MODE_IEEE80211G &&
5701 hapd->iface->num_sta_no_short_slot_time == 1)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005702 set_beacon = true;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005703 }
5704
5705 if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
5706 sta->flags |= WLAN_STA_SHORT_PREAMBLE;
5707 else
5708 sta->flags &= ~WLAN_STA_SHORT_PREAMBLE;
5709
5710 if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
5711 !sta->no_short_preamble_set) {
5712 sta->no_short_preamble_set = 1;
5713 hapd->iface->num_sta_no_short_preamble++;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005714 if (hapd->iface->current_mode &&
5715 hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005716 && hapd->iface->num_sta_no_short_preamble == 1)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005717 set_beacon = true;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005718 }
5719
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005720 if (update_ht_state(hapd, sta) > 0)
5721 set_beacon = true;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005722
5723 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
5724 HOSTAPD_LEVEL_DEBUG,
5725 "association OK (aid %d)", sta->aid);
5726 /* Station will be marked associated, after it acknowledges AssocResp
5727 */
5728 sta->flags |= WLAN_STA_ASSOC_REQ_OK;
5729
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005730 if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) {
5731 wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out "
5732 "SA Query procedure", reassoc ? "re" : "");
5733 /* TODO: Send a protected Disassociate frame to the STA using
5734 * the old key and Reason Code "Previous Authentication no
5735 * longer valid". Make sure this is only sent protected since
5736 * unprotected frame would be received by the STA that is now
5737 * trying to associate.
5738 */
5739 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005740
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005741 /* Make sure that the previously registered inactivity timer will not
5742 * remove the STA immediately. */
5743 sta->timeout_next = STA_NULLFUNC;
5744
Dmitry Shmidtaca489e2016-09-28 15:44:14 -07005745#ifdef CONFIG_TAXONOMY
5746 taxonomy_sta_info_assoc_req(hapd, sta, pos, left);
5747#endif /* CONFIG_TAXONOMY */
5748
Dmitry Shmidt29333592017-01-09 12:27:11 -08005749 sta->pending_wds_enable = 0;
5750
Paul Stewart092955c2017-02-06 09:13:09 -08005751#ifdef CONFIG_FILS
5752 if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
5753 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005754 sta->auth_alg == WLAN_AUTH_FILS_PK) {
5755 if (fils_process_hlp(hapd, sta, pos, left) > 0)
5756 delay_assoc = 1;
5757 }
Paul Stewart092955c2017-02-06 09:13:09 -08005758#endif /* CONFIG_FILS */
5759
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005760 if (set_beacon)
Sunil Ravi7f769292024-07-23 22:21:32 +00005761 ieee802_11_update_beacons(hapd->iface);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005762
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005763 fail:
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005764
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005765 /*
5766 * In case of a successful response, add the station to the driver.
5767 * Otherwise, the kernel may ignore Data frames before we process the
5768 * ACK frame (TX status). In case of a failure, this station will be
5769 * removed.
5770 *
5771 * Note that this is not compliant with the IEEE 802.11 standard that
5772 * states that a non-AP station should transition into the
5773 * authenticated/associated state only after the station acknowledges
5774 * the (Re)Association Response frame. However, still do this as:
5775 *
5776 * 1. In case the station does not acknowledge the (Re)Association
5777 * Response frame, it will be removed.
5778 * 2. Data frames will be dropped in the kernel until the station is
5779 * set into authorized state, and there are no significant known
5780 * issues with processing other non-Data Class 3 frames during this
5781 * window.
5782 */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00005783 if (sta)
5784 hostapd_process_assoc_ml_info(hapd, sta, pos, left, reassoc,
5785 resp, false);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005786
Hai Shalom74f70d42019-02-11 14:42:39 -08005787 if (resp == WLAN_STATUS_SUCCESS && sta &&
5788 add_associated_sta(hapd, sta, reassoc))
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005789 resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
5790
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005791#ifdef CONFIG_FILS
Hai Shalom74f70d42019-02-11 14:42:39 -08005792 if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS &&
5793 eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta) &&
5794 sta->fils_pending_assoc_req) {
5795 /* Do not reschedule fils_hlp_timeout in case the station
5796 * retransmits (Re)Association Request frame while waiting for
5797 * the previously started FILS HLP wait, so that the timeout can
5798 * be determined from the first pending attempt. */
5799 wpa_printf(MSG_DEBUG,
5800 "FILS: Continue waiting for HLP processing before sending (Re)Association Response frame to "
5801 MACSTR, MAC2STR(sta->addr));
5802 os_free(tmp);
5803 return;
5804 }
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005805 if (sta) {
5806 eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
5807 os_free(sta->fils_pending_assoc_req);
5808 sta->fils_pending_assoc_req = NULL;
5809 sta->fils_pending_assoc_req_len = 0;
5810 wpabuf_free(sta->fils_hlp_resp);
5811 sta->fils_hlp_resp = NULL;
5812 }
5813 if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) {
5814 sta->fils_pending_assoc_req = tmp;
5815 sta->fils_pending_assoc_req_len = left;
5816 sta->fils_pending_assoc_is_reassoc = reassoc;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005817 sta->fils_drv_assoc_finish = 0;
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08005818 wpa_printf(MSG_DEBUG,
5819 "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
5820 MACSTR, MAC2STR(sta->addr));
5821 eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
5822 eloop_register_timeout(0, hapd->conf->fils_hlp_wait_time * 1024,
5823 fils_hlp_timeout, hapd, sta);
5824 return;
5825 }
5826#endif /* CONFIG_FILS */
5827
Hai Shalomb755a2a2020-04-23 21:49:02 -07005828 if (resp >= 0)
Sunil Ravib0ac25f2024-07-12 01:42:03 +00005829 reply_res = send_assoc_resp(hapd,
5830 mld_addrs_not_translated ?
5831 NULL : sta,
5832 mgmt->sa, resp, reassoc,
5833 pos, left, rssi, omit_rsnxe,
5834 !mld_addrs_not_translated);
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08005835 os_free(tmp);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005836
5837 /*
Hai Shalom899fcc72020-10-19 14:38:18 -07005838 * Remove the station in case transmission of a success response fails
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005839 * (the STA was added associated to the driver) or if the station was
5840 * previously added unassociated.
5841 */
Dmitry Shmidt29333592017-01-09 12:27:11 -08005842 if (sta && ((reply_res != WLAN_STATUS_SUCCESS &&
5843 resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc)) {
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005844 hostapd_drv_sta_remove(hapd, sta->addr);
5845 sta->added_unassoc = 0;
5846 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005847}
5848
5849
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005850static void hostapd_deauth_sta(struct hostapd_data *hapd,
5851 struct sta_info *sta,
5852 const struct ieee80211_mgmt *mgmt)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005853{
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005854 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
5855 "deauthentication: STA=" MACSTR " reason_code=%d",
5856 MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005857
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005858 ap_sta_set_authorized(hapd, sta, 0);
5859 sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
5860 sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
5861 WLAN_STA_ASSOC_REQ_OK);
5862 hostapd_set_sta_flags(hapd, sta);
5863 wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
5864 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
5865 HOSTAPD_LEVEL_DEBUG, "deauthenticated");
5866 mlme_deauthenticate_indication(
5867 hapd, sta, le_to_host16(mgmt->u.deauth.reason_code));
5868 sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
5869 ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
5870 ap_free_sta(hapd, sta);
5871}
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005872
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005873
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005874static void hostapd_disassoc_sta(struct hostapd_data *hapd,
5875 struct sta_info *sta,
5876 const struct ieee80211_mgmt *mgmt)
5877{
5878 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
5879 "disassocation: STA=" MACSTR " reason_code=%d",
5880 MAC2STR(mgmt->sa), le_to_host16(mgmt->u.disassoc.reason_code));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005881
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08005882 ap_sta_set_authorized(hapd, sta, 0);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005883 sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005884 sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
Hai Shalomfdcde762020-04-02 11:19:20 -07005885 hostapd_set_sta_flags(hapd, sta);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005886 wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
5887 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
5888 HOSTAPD_LEVEL_INFO, "disassociated");
5889 sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
5890 ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
5891 /* Stop Accounting and IEEE 802.1X sessions, but leave the STA
5892 * authenticated. */
5893 accounting_sta_stop(hapd, sta);
Dmitry Shmidtde47be72016-01-07 12:52:55 -08005894 ieee802_1x_free_station(hapd, sta);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08005895 if (sta->ipaddr)
5896 hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
5897 ap_sta_ip6addr_del(hapd, sta);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005898 hostapd_drv_sta_remove(hapd, sta->addr);
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08005899 sta->added_unassoc = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005900
5901 if (sta->timeout_next == STA_NULLFUNC ||
5902 sta->timeout_next == STA_DISASSOC) {
5903 sta->timeout_next = STA_DEAUTH;
5904 eloop_cancel_timeout(ap_handle_timer, hapd, sta);
5905 eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
5906 hapd, sta);
5907 }
5908
5909 mlme_disassociate_indication(
5910 hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code));
Dmitry Shmidt29333592017-01-09 12:27:11 -08005911
5912 /* DMG/IEEE 802.11ad does not use deauthication. Deallocate sta upon
5913 * disassociation. */
5914 if (hapd->iface->current_mode &&
5915 hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
5916 sta->flags &= ~WLAN_STA_AUTH;
5917 wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
5918 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
5919 HOSTAPD_LEVEL_DEBUG, "deauthenticated");
5920 ap_free_sta(hapd, sta);
5921 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07005922}
5923
5924
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005925static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
5926 struct sta_info *sta,
5927 const struct ieee80211_mgmt *mgmt,
5928 bool disassoc)
5929{
5930#ifdef CONFIG_IEEE80211BE
5931 struct hostapd_data *assoc_hapd, *tmp_hapd;
5932 struct sta_info *assoc_sta;
Sunil Ravi7f769292024-07-23 22:21:32 +00005933 struct sta_info *tmp_sta;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005934
5935 if (!hostapd_is_mld_ap(hapd))
5936 return false;
5937
5938 /*
5939 * Get the station on which the association was performed, as it holds
5940 * the information about all the other links.
5941 */
5942 assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
Sunil Ravib0ac25f2024-07-12 01:42:03 +00005943 if (!assoc_sta)
5944 return false;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005945
Sunil Ravi7f769292024-07-23 22:21:32 +00005946 for_each_mld_link(tmp_hapd, assoc_hapd) {
5947 if (tmp_hapd == assoc_hapd)
5948 continue;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005949
Sunil Ravi7f769292024-07-23 22:21:32 +00005950 if (!assoc_sta->mld_info.links[tmp_hapd->mld_link_id].valid)
5951 continue;
5952
5953 for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
5954 tmp_sta = tmp_sta->next) {
5955 if (tmp_sta->mld_assoc_link_id !=
5956 assoc_sta->mld_assoc_link_id ||
5957 tmp_sta->aid != assoc_sta->aid)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005958 continue;
5959
Sunil Ravi7f769292024-07-23 22:21:32 +00005960 if (!disassoc)
5961 hostapd_deauth_sta(tmp_hapd, tmp_sta, mgmt);
5962 else
5963 hostapd_disassoc_sta(tmp_hapd, tmp_sta, mgmt);
5964 break;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00005965 }
5966 }
5967
5968 /* Remove the station on which the association was performed. */
5969 if (!disassoc)
5970 hostapd_deauth_sta(assoc_hapd, assoc_sta, mgmt);
5971 else
5972 hostapd_disassoc_sta(assoc_hapd, assoc_sta, mgmt);
5973
5974 return true;
5975#else /* CONFIG_IEEE80211BE */
5976 return false;
5977#endif /* CONFIG_IEEE80211BE */
5978}
5979
5980
5981static void handle_disassoc(struct hostapd_data *hapd,
5982 const struct ieee80211_mgmt *mgmt, size_t len)
5983{
5984 struct sta_info *sta;
5985
5986 if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
5987 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
5988 "handle_disassoc - too short payload (len=%lu)",
5989 (unsigned long) len);
5990 return;
5991 }
5992
5993 sta = ap_get_sta(hapd, mgmt->sa);
5994 if (!sta) {
5995 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
5996 " trying to disassociate, but it is not associated",
5997 MAC2STR(mgmt->sa));
5998 return;
5999 }
6000
6001 if (hostapd_ml_handle_disconnect(hapd, sta, mgmt, true))
6002 return;
6003
6004 hostapd_disassoc_sta(hapd, sta, mgmt);
6005}
6006
6007
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006008static void handle_deauth(struct hostapd_data *hapd,
6009 const struct ieee80211_mgmt *mgmt, size_t len)
6010{
6011 struct sta_info *sta;
6012
6013 if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006014 wpa_msg(hapd->msg_ctx, MSG_DEBUG,
6015 "handle_deauth - too short payload (len=%lu)",
6016 (unsigned long) len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006017 return;
6018 }
6019
Hai Shaloma20dcd72022-02-04 13:43:00 -08006020 /* Clear the PTKSA cache entries for PASN */
6021 ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE);
6022
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006023 sta = ap_get_sta(hapd, mgmt->sa);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006024 if (!sta) {
6025 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
6026 " trying to deauthenticate, but it is not authenticated",
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006027 MAC2STR(mgmt->sa));
6028 return;
6029 }
6030
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006031 if (hostapd_ml_handle_disconnect(hapd, sta, mgmt, false))
6032 return;
6033
6034 hostapd_deauth_sta(hapd, sta, mgmt);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006035}
6036
6037
6038static void handle_beacon(struct hostapd_data *hapd,
6039 const struct ieee80211_mgmt *mgmt, size_t len,
6040 struct hostapd_frame_info *fi)
6041{
6042 struct ieee802_11_elems elems;
6043
6044 if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08006045 wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)",
6046 (unsigned long) len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006047 return;
6048 }
6049
6050 (void) ieee802_11_parse_elems(mgmt->u.beacon.variable,
6051 len - (IEEE80211_HDRLEN +
6052 sizeof(mgmt->u.beacon)), &elems,
6053 0);
6054
6055 ap_list_process_beacon(hapd->iface, mgmt, &elems, fi);
6056}
6057
6058
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00006059static int hostapd_action_vs(struct hostapd_data *hapd,
6060 struct sta_info *sta,
6061 const struct ieee80211_mgmt *mgmt, size_t len,
6062 unsigned int freq, bool protected)
6063{
6064 const u8 *pos, *end;
6065 u32 oui_type;
6066
6067 pos = &mgmt->u.action.category;
6068 end = ((const u8 *) mgmt) + len;
6069
6070 if (end - pos < 1 + 4)
6071 return -1;
6072 pos++;
6073
6074 oui_type = WPA_GET_BE32(pos);
6075 pos += 4;
6076
6077 switch (oui_type) {
6078 case WFA_CAPAB_VENDOR_TYPE:
6079 hostapd_wfa_capab(hapd, sta, pos, end);
6080 return 0;
6081 default:
6082 wpa_printf(MSG_DEBUG,
6083 "Ignore unknown Vendor Specific Action frame OUI/type %08x%s",
6084 oui_type, protected ? " (protected)" : "");
6085 break;
6086 }
6087
6088 return -1;
6089}
6090
6091
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006092static int robust_action_frame(u8 category)
6093{
6094 return category != WLAN_ACTION_PUBLIC &&
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00006095 category != WLAN_ACTION_HT &&
6096 category != WLAN_ACTION_UNPROTECTED_WNM &&
6097 category != WLAN_ACTION_SELF_PROTECTED &&
6098 category != WLAN_ACTION_UNPROTECTED_DMG &&
6099 category != WLAN_ACTION_VHT &&
6100 category != WLAN_ACTION_UNPROTECTED_S1G &&
6101 category != WLAN_ACTION_HE &&
6102 category != WLAN_ACTION_EHT &&
6103 category != WLAN_ACTION_VENDOR_SPECIFIC;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006104}
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006105
6106
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006107static int handle_action(struct hostapd_data *hapd,
Roshan Pius3a1667e2018-07-03 15:17:14 -07006108 const struct ieee80211_mgmt *mgmt, size_t len,
6109 unsigned int freq)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006110{
6111 struct sta_info *sta;
Hai Shalom74f70d42019-02-11 14:42:39 -08006112 u8 *action __maybe_unused;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006113
Hai Shalom74f70d42019-02-11 14:42:39 -08006114 if (len < IEEE80211_HDRLEN + 2 + 1) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006115 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
6116 HOSTAPD_LEVEL_DEBUG,
6117 "handle_action - too short payload (len=%lu)",
6118 (unsigned long) len);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006119 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006120 }
6121
Hai Shalom74f70d42019-02-11 14:42:39 -08006122 action = (u8 *) &mgmt->u.action.u;
6123 wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR
6124 " da " MACSTR " len %d freq %u",
6125 mgmt->u.action.category, *action,
6126 MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) len, freq);
6127
6128 sta = ap_get_sta(hapd, mgmt->sa);
6129
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08006130 if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
6131 (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
6132 wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
6133 "frame (category=%u) from unassociated STA " MACSTR,
Dmitry Shmidtebd93af2017-02-21 13:40:44 -08006134 mgmt->u.action.category, MAC2STR(mgmt->sa));
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006135 return 0;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08006136 }
6137
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006138 if (sta && (sta->flags & WLAN_STA_MFP) &&
Dmitry Shmidt18463232014-01-24 12:29:41 -08006139 !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) &&
6140 robust_action_frame(mgmt->u.action.category)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006141 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
6142 HOSTAPD_LEVEL_DEBUG,
6143 "Dropped unprotected Robust Action frame from "
6144 "an MFP STA");
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006145 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006146 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006147
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08006148 if (sta) {
6149 u16 fc = le_to_host16(mgmt->frame_control);
6150 u16 seq_ctrl = le_to_host16(mgmt->seq_ctrl);
6151
6152 if ((fc & WLAN_FC_RETRY) &&
6153 sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
6154 sta->last_seq_ctrl == seq_ctrl &&
6155 sta->last_subtype == WLAN_FC_STYPE_ACTION) {
6156 hostapd_logger(hapd, sta->addr,
6157 HOSTAPD_MODULE_IEEE80211,
6158 HOSTAPD_LEVEL_DEBUG,
6159 "Drop repeated action frame seq_ctrl=0x%x",
6160 seq_ctrl);
6161 return 1;
6162 }
6163
6164 sta->last_seq_ctrl = seq_ctrl;
6165 sta->last_subtype = WLAN_FC_STYPE_ACTION;
6166 }
6167
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006168 switch (mgmt->u.action.category) {
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08006169#ifdef CONFIG_IEEE80211R_AP
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006170 case WLAN_ACTION_FT:
Dmitry Shmidt7832adb2014-04-29 10:53:02 -07006171 if (!sta ||
6172 wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006173 len - IEEE80211_HDRLEN))
6174 break;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006175 return 1;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08006176#endif /* CONFIG_IEEE80211R_AP */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006177 case WLAN_ACTION_WMM:
6178 hostapd_wmm_action(hapd, mgmt, len);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006179 return 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006180 case WLAN_ACTION_SA_QUERY:
Hai Shalom021b0b52019-04-10 11:17:58 -07006181 ieee802_11_sa_query_action(hapd, mgmt, len);
6182 return 1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006183#ifdef CONFIG_WNM_AP
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08006184 case WLAN_ACTION_WNM:
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006185 ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
6186 return 1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006187#endif /* CONFIG_WNM_AP */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08006188#ifdef CONFIG_FST
6189 case WLAN_ACTION_FST:
6190 if (hapd->iface->fst)
6191 fst_rx_action(hapd->iface->fst, mgmt, len);
6192 else
6193 wpa_printf(MSG_DEBUG,
6194 "FST: Ignore FST Action frame - no FST attached");
6195 return 1;
6196#endif /* CONFIG_FST */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006197 case WLAN_ACTION_PUBLIC:
Dmitry Shmidt18463232014-01-24 12:29:41 -08006198 case WLAN_ACTION_PROTECTED_DUAL:
Dmitry Shmidtcc00d5d2015-05-04 10:34:12 -07006199 if (len >= IEEE80211_HDRLEN + 2 &&
6200 mgmt->u.action.u.public_action.action ==
Dmitry Shmidt7832adb2014-04-29 10:53:02 -07006201 WLAN_PA_20_40_BSS_COEX) {
Dmitry Shmidt7832adb2014-04-29 10:53:02 -07006202 hostapd_2040_coex_action(hapd, mgmt, len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006203 return 1;
Dmitry Shmidt7832adb2014-04-29 10:53:02 -07006204 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006205#ifdef CONFIG_DPP
6206 if (len >= IEEE80211_HDRLEN + 6 &&
6207 mgmt->u.action.u.vs_public_action.action ==
6208 WLAN_PA_VENDOR_SPECIFIC &&
6209 WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
6210 OUI_WFA &&
6211 mgmt->u.action.u.vs_public_action.variable[0] ==
6212 DPP_OUI_TYPE) {
6213 const u8 *pos, *end;
6214
6215 pos = mgmt->u.action.u.vs_public_action.oui;
6216 end = ((const u8 *) mgmt) + len;
6217 hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
Roshan Pius3a1667e2018-07-03 15:17:14 -07006218 freq);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006219 return 1;
6220 }
6221 if (len >= IEEE80211_HDRLEN + 2 &&
6222 (mgmt->u.action.u.public_action.action ==
6223 WLAN_PA_GAS_INITIAL_RESP ||
6224 mgmt->u.action.u.public_action.action ==
6225 WLAN_PA_GAS_COMEBACK_RESP)) {
6226 const u8 *pos, *end;
6227
6228 pos = &mgmt->u.action.u.public_action.action;
6229 end = ((const u8 *) mgmt) + len;
Sunil Ravi036cec52023-03-29 11:35:17 -07006230 if (gas_query_ap_rx(hapd->gas, mgmt->sa,
6231 mgmt->u.action.category,
6232 pos, end - pos, freq) == 0)
6233 return 1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006234 }
6235#endif /* CONFIG_DPP */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006236#ifdef CONFIG_NAN_USD
6237 if (mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
6238 len >= IEEE80211_HDRLEN + 5 &&
6239 mgmt->u.action.u.vs_public_action.action ==
6240 WLAN_PA_VENDOR_SPECIFIC &&
6241 WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
6242 OUI_WFA &&
6243 mgmt->u.action.u.vs_public_action.variable[0] ==
6244 NAN_OUI_TYPE) {
6245 const u8 *pos, *end;
6246
6247 pos = mgmt->u.action.u.vs_public_action.variable;
6248 end = ((const u8 *) mgmt) + len;
6249 pos++;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00006250 hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, mgmt->bssid,
6251 freq, pos, end - pos);
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006252 return 1;
6253 }
6254#endif /* CONFIG_NAN_USD */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006255 if (hapd->public_action_cb) {
6256 hapd->public_action_cb(hapd->public_action_cb_ctx,
Hai Shaloma20dcd72022-02-04 13:43:00 -08006257 (u8 *) mgmt, len, freq);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006258 }
Dmitry Shmidt4b9d52f2013-02-05 17:44:43 -08006259 if (hapd->public_action_cb2) {
Dmitry Shmidtf8623282013-02-20 14:34:59 -08006260 hapd->public_action_cb2(hapd->public_action_cb2_ctx,
Hai Shaloma20dcd72022-02-04 13:43:00 -08006261 (u8 *) mgmt, len, freq);
Dmitry Shmidt4b9d52f2013-02-05 17:44:43 -08006262 }
6263 if (hapd->public_action_cb || hapd->public_action_cb2)
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006264 return 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006265 break;
6266 case WLAN_ACTION_VENDOR_SPECIFIC:
6267 if (hapd->vendor_action_cb) {
6268 if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx,
Hai Shaloma20dcd72022-02-04 13:43:00 -08006269 (u8 *) mgmt, len, freq) == 0)
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006270 return 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006271 }
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00006272 if (sta &&
6273 hostapd_action_vs(hapd, sta, mgmt, len, freq, false) == 0)
6274 return 1;
6275 break;
6276 case WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED:
6277 if (sta &&
6278 hostapd_action_vs(hapd, sta, mgmt, len, freq, true) == 0)
6279 return 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006280 break;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006281#ifndef CONFIG_NO_RRM
Dmitry Shmidt849734c2016-05-27 09:59:01 -07006282 case WLAN_ACTION_RADIO_MEASUREMENT:
6283 hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
6284 return 1;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006285#endif /* CONFIG_NO_RRM */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006286 }
6287
6288 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
6289 HOSTAPD_LEVEL_DEBUG,
6290 "handle_action - unknown action category %d or invalid "
6291 "frame",
6292 mgmt->u.action.category);
Dmitry Shmidtd13095b2016-08-22 14:02:19 -07006293 if (!is_multicast_ether_addr(mgmt->da) &&
6294 !(mgmt->u.action.category & 0x80) &&
6295 !is_multicast_ether_addr(mgmt->sa)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006296 struct ieee80211_mgmt *resp;
6297
6298 /*
6299 * IEEE 802.11-REVma/D9.0 - 7.3.1.11
6300 * Return the Action frame to the source without change
6301 * except that MSB of the Category set to 1.
6302 */
6303 wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action "
6304 "frame back to sender");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006305 resp = os_memdup(mgmt, len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006306 if (resp == NULL)
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006307 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006308 os_memcpy(resp->da, resp->sa, ETH_ALEN);
6309 os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
6310 os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
6311 resp->u.action.category |= 0x80;
6312
Hai Shalomfdcde762020-04-02 11:19:20 -07006313 if (hostapd_drv_send_mlme(hapd, resp, len, 0, NULL, 0, 0) < 0) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07006314 wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send "
6315 "Action frame");
6316 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006317 os_free(resp);
6318 }
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006319
6320 return 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006321}
6322
6323
6324/**
Hai Shalom60840252021-02-19 19:02:11 -08006325 * notify_mgmt_frame - Notify of Management frames on the control interface
6326 * @hapd: hostapd BSS data structure (the BSS to which the Management frame was
6327 * sent to)
6328 * @buf: Management frame data (starting from the IEEE 802.11 header)
6329 * @len: Length of frame data in octets
6330 *
6331 * Notify the control interface of any received Management frame.
6332 */
6333static void notify_mgmt_frame(struct hostapd_data *hapd, const u8 *buf,
6334 size_t len)
6335{
6336
6337 int hex_len = len * 2 + 1;
6338 char *hex = os_malloc(hex_len);
6339
6340 if (hex) {
6341 wpa_snprintf_hex(hex, hex_len, buf, len);
6342 wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO,
6343 AP_MGMT_FRAME_RECEIVED "buf=%s", hex);
6344 os_free(hex);
6345 }
6346}
6347
6348
6349/**
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006350 * ieee802_11_mgmt - process incoming IEEE 802.11 management frames
6351 * @hapd: hostapd BSS data structure (the BSS to which the management frame was
6352 * sent to)
6353 * @buf: management frame data (starting from IEEE 802.11 header)
6354 * @len: length of frame data in octets
6355 * @fi: meta data about received frame (signal level, etc.)
6356 *
6357 * Process all incoming IEEE 802.11 management frames. This will be called for
6358 * each frame received from the kernel driver through wlan#ap interface. In
6359 * addition, it can be called to re-inserted pending frames (e.g., when using
6360 * external RADIUS server as an MAC ACL).
6361 */
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006362int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
6363 struct hostapd_frame_info *fi)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006364{
6365 struct ieee80211_mgmt *mgmt;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006366 u16 fc, stype;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006367 int ret = 0;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006368 unsigned int freq;
6369 int ssi_signal = fi ? fi->ssi_signal : 0;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006370#ifdef CONFIG_NAN_USD
6371 static const u8 nan_network_id[ETH_ALEN] =
6372 { 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 };
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00006373 static const u8 p2p_network_id[ETH_ALEN] =
6374 { 0x51, 0x6f, 0x9a, 0x02, 0x00, 0x00 };
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006375#endif /* CONFIG_NAN_USD */
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006376
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006377 if (len < 24)
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006378 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006379
Roshan Pius3a1667e2018-07-03 15:17:14 -07006380 if (fi && fi->freq)
6381 freq = fi->freq;
6382 else
6383 freq = hapd->iface->freq;
6384
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006385 mgmt = (struct ieee80211_mgmt *) buf;
6386 fc = le_to_host16(mgmt->frame_control);
6387 stype = WLAN_FC_GET_STYPE(fc);
6388
Hai Shalomc3565922019-10-28 11:58:20 -07006389 if (is_multicast_ether_addr(mgmt->sa) ||
6390 is_zero_ether_addr(mgmt->sa) ||
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006391 ether_addr_equal(mgmt->sa, hapd->own_addr)) {
Hai Shalomc3565922019-10-28 11:58:20 -07006392 /* Do not process any frames with unexpected/invalid SA so that
6393 * we do not add any state for unexpected STA addresses or end
6394 * up sending out frames to unexpected destination. */
6395 wpa_printf(MSG_DEBUG, "MGMT: Invalid SA=" MACSTR
6396 " in received frame - ignore this frame silently",
6397 MAC2STR(mgmt->sa));
6398 return 0;
6399 }
6400
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006401 if (stype == WLAN_FC_STYPE_BEACON) {
6402 handle_beacon(hapd, mgmt, len, fi);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006403 return 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006404 }
6405
Dmitry Shmidt7f2c7532016-08-15 09:48:12 -07006406 if (!is_broadcast_ether_addr(mgmt->bssid) &&
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00006407#ifdef CONFIG_NAN_USD
6408 !nan_de_is_nan_network_id(mgmt->bssid) &&
6409 !nan_de_is_p2p_network_id(mgmt->bssid) &&
6410#endif /* CONFIG_NAN_USD */
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08006411#ifdef CONFIG_P2P
6412 /* Invitation responses can be sent with the peer MAC as BSSID */
6413 !((hapd->conf->p2p & P2P_GROUP_OWNER) &&
6414 stype == WLAN_FC_STYPE_ACTION) &&
6415#endif /* CONFIG_P2P */
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08006416#ifdef CONFIG_MESH
6417 !(hapd->conf->mesh & MESH_ENABLED) &&
6418#endif /* CONFIG_MESH */
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006419#ifdef CONFIG_IEEE80211BE
6420 !(hapd->conf->mld_ap &&
Sunil Ravi99c035e2024-07-12 01:42:03 +00006421 ether_addr_equal(hapd->mld->mld_addr, mgmt->bssid)) &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006422#endif /* CONFIG_IEEE80211BE */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006423 !ether_addr_equal(mgmt->bssid, hapd->own_addr)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08006424 wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address",
6425 MAC2STR(mgmt->bssid));
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006426 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006427 }
6428
Hai Shalom4fbc08f2020-05-18 12:37:00 -07006429 if (hapd->iface->state != HAPD_IFACE_ENABLED) {
6430 wpa_printf(MSG_DEBUG, "MGMT: Ignore management frame while interface is not enabled (SA=" MACSTR " DA=" MACSTR " subtype=%u)",
6431 MAC2STR(mgmt->sa), MAC2STR(mgmt->da), stype);
6432 return 1;
6433 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006434
6435 if (stype == WLAN_FC_STYPE_PROBE_REQ) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07006436 handle_probe_req(hapd, mgmt, len, ssi_signal);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006437 return 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006438 }
6439
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006440 if ((!is_broadcast_ether_addr(mgmt->da) ||
6441 stype != WLAN_FC_STYPE_ACTION) &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006442#ifdef CONFIG_IEEE80211BE
6443 !(hapd->conf->mld_ap &&
Sunil Ravi99c035e2024-07-12 01:42:03 +00006444 ether_addr_equal(hapd->mld->mld_addr, mgmt->bssid)) &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006445#endif /* CONFIG_IEEE80211BE */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006446#ifdef CONFIG_NAN_USD
6447 !ether_addr_equal(mgmt->da, nan_network_id) &&
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00006448 !ether_addr_equal(mgmt->da, p2p_network_id) &&
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006449#endif /* CONFIG_NAN_USD */
6450 !ether_addr_equal(mgmt->da, hapd->own_addr)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006451 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
6452 HOSTAPD_LEVEL_DEBUG,
6453 "MGMT: DA=" MACSTR " not our address",
6454 MAC2STR(mgmt->da));
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006455 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006456 }
6457
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08006458 if (hapd->iconf->track_sta_max_num)
Roshan Pius3a1667e2018-07-03 15:17:14 -07006459 sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08006460
Hai Shalom60840252021-02-19 19:02:11 -08006461 if (hapd->conf->notify_mgmt_frames)
6462 notify_mgmt_frame(hapd, buf, len);
6463
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006464 switch (stype) {
6465 case WLAN_FC_STYPE_AUTH:
6466 wpa_printf(MSG_DEBUG, "mgmt::auth");
Hai Shalom021b0b52019-04-10 11:17:58 -07006467 handle_auth(hapd, mgmt, len, ssi_signal, 0);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006468 ret = 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006469 break;
6470 case WLAN_FC_STYPE_ASSOC_REQ:
6471 wpa_printf(MSG_DEBUG, "mgmt::assoc_req");
Hai Shalom74f70d42019-02-11 14:42:39 -08006472 handle_assoc(hapd, mgmt, len, 0, ssi_signal);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006473 ret = 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006474 break;
6475 case WLAN_FC_STYPE_REASSOC_REQ:
6476 wpa_printf(MSG_DEBUG, "mgmt::reassoc_req");
Hai Shalom74f70d42019-02-11 14:42:39 -08006477 handle_assoc(hapd, mgmt, len, 1, ssi_signal);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006478 ret = 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006479 break;
6480 case WLAN_FC_STYPE_DISASSOC:
6481 wpa_printf(MSG_DEBUG, "mgmt::disassoc");
6482 handle_disassoc(hapd, mgmt, len);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006483 ret = 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006484 break;
6485 case WLAN_FC_STYPE_DEAUTH:
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08006486 wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006487 handle_deauth(hapd, mgmt, len);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006488 ret = 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006489 break;
6490 case WLAN_FC_STYPE_ACTION:
6491 wpa_printf(MSG_DEBUG, "mgmt::action");
Roshan Pius3a1667e2018-07-03 15:17:14 -07006492 ret = handle_action(hapd, mgmt, len, freq);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006493 break;
6494 default:
6495 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
6496 HOSTAPD_LEVEL_DEBUG,
6497 "unknown mgmt frame subtype %d", stype);
6498 break;
6499 }
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006500
6501 return ret;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006502}
6503
6504
6505static void handle_auth_cb(struct hostapd_data *hapd,
6506 const struct ieee80211_mgmt *mgmt,
6507 size_t len, int ok)
6508{
6509 u16 auth_alg, auth_transaction, status_code;
6510 struct sta_info *sta;
Hai Shalom60840252021-02-19 19:02:11 -08006511 bool success_status;
Hai Shalome5e28bb2019-01-28 14:51:04 -08006512
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006513 sta = ap_get_sta(hapd, mgmt->da);
6514 if (!sta) {
Hai Shalom39ba6fc2019-01-22 12:40:38 -08006515 wpa_printf(MSG_DEBUG, "handle_auth_cb: STA " MACSTR
6516 " not found",
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006517 MAC2STR(mgmt->da));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006518 return;
6519 }
6520
Hai Shalom60840252021-02-19 19:02:11 -08006521 if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
6522 wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)",
6523 (unsigned long) len);
6524 auth_alg = 0;
6525 auth_transaction = 0;
6526 status_code = WLAN_STATUS_UNSPECIFIED_FAILURE;
6527 goto fail;
6528 }
6529
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006530 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
6531 auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
6532 status_code = le_to_host16(mgmt->u.auth.status_code);
6533
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006534 if (!ok) {
6535 hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
6536 HOSTAPD_LEVEL_NOTICE,
6537 "did not acknowledge authentication response");
6538 goto fail;
6539 }
6540
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006541 if (status_code == WLAN_STATUS_SUCCESS &&
6542 ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
6543 (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
6544 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
6545 HOSTAPD_LEVEL_INFO, "authenticated");
6546 sta->flags |= WLAN_STA_AUTH;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006547 if (sta->added_unassoc)
6548 hostapd_set_sta_flags(hapd, sta);
6549 return;
6550 }
6551
6552fail:
Hai Shalom60840252021-02-19 19:02:11 -08006553 success_status = status_code == WLAN_STATUS_SUCCESS;
6554#ifdef CONFIG_SAE
6555 if (auth_alg == WLAN_AUTH_SAE && auth_transaction == 1)
6556 success_status = sae_status_success(hapd, status_code);
6557#endif /* CONFIG_SAE */
6558 if (!success_status && sta->added_unassoc) {
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006559 hostapd_drv_sta_remove(hapd, sta->addr);
6560 sta->added_unassoc = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006561 }
6562}
6563
6564
Dmitry Shmidtc2ebb4b2013-07-24 12:57:51 -07006565static void hostapd_set_wds_encryption(struct hostapd_data *hapd,
6566 struct sta_info *sta,
6567 char *ifname_wds)
6568{
Hai Shalomfdcde762020-04-02 11:19:20 -07006569#ifdef CONFIG_WEP
Dmitry Shmidtc2ebb4b2013-07-24 12:57:51 -07006570 int i;
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -07006571 struct hostapd_ssid *ssid = &hapd->conf->ssid;
Dmitry Shmidtc2ebb4b2013-07-24 12:57:51 -07006572
6573 if (hapd->conf->ieee802_1x || hapd->conf->wpa)
6574 return;
6575
6576 for (i = 0; i < 4; i++) {
6577 if (ssid->wep.key[i] &&
6578 hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i,
Hai Shalomfdcde762020-04-02 11:19:20 -07006579 0, i == ssid->wep.idx, NULL, 0,
6580 ssid->wep.key[i], ssid->wep.len[i],
6581 i == ssid->wep.idx ?
6582 KEY_FLAG_GROUP_RX_TX_DEFAULT :
6583 KEY_FLAG_GROUP_RX_TX)) {
Dmitry Shmidtc2ebb4b2013-07-24 12:57:51 -07006584 wpa_printf(MSG_WARNING,
6585 "Could not set WEP keys for WDS interface; %s",
6586 ifname_wds);
6587 break;
6588 }
6589 }
Hai Shalomfdcde762020-04-02 11:19:20 -07006590#endif /* CONFIG_WEP */
Dmitry Shmidtc2ebb4b2013-07-24 12:57:51 -07006591}
6592
6593
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006594#ifdef CONFIG_IEEE80211BE
6595static void ieee80211_ml_link_sta_assoc_cb(struct hostapd_data *hapd,
6596 struct sta_info *sta,
6597 struct mld_link_info *link,
6598 bool ok)
6599{
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006600 bool updated = false;
6601
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006602 if (!ok) {
6603 hostapd_logger(hapd, link->peer_addr, HOSTAPD_MODULE_IEEE80211,
6604 HOSTAPD_LEVEL_DEBUG,
6605 "did not acknowledge association response");
6606 sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
6607
6608 /* The STA is added only in case of SUCCESS */
6609 if (link->status == WLAN_STATUS_SUCCESS)
6610 hostapd_drv_sta_remove(hapd, sta->addr);
6611
6612 return;
6613 }
6614
6615 if (link->status != WLAN_STATUS_SUCCESS)
6616 return;
6617
6618 sta->flags |= WLAN_STA_ASSOC;
6619 sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
6620
6621 if (!hapd->conf->ieee802_1x && !hapd->conf->wpa)
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006622 updated = ap_sta_set_authorized_flag(hapd, sta, 1);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006623
6624 hostapd_set_sta_flags(hapd, sta);
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006625 if (updated)
6626 ap_sta_set_authorized_event(hapd, sta, 1);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006627
6628 /*
6629 * TODOs:
6630 * - IEEE 802.1X port enablement is not needed as done on the station
6631 * doing the connection.
6632 * - Not handling accounting
6633 * - Need to handle VLAN configuration
6634 */
6635}
6636#endif /* CONFIG_IEEE80211BE */
6637
6638
6639static void hostapd_ml_handle_assoc_cb(struct hostapd_data *hapd,
6640 struct sta_info *sta, bool ok)
6641{
6642#ifdef CONFIG_IEEE80211BE
Sunil Ravi7f769292024-07-23 22:21:32 +00006643 struct hostapd_data *tmp_hapd;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006644
6645 if (!hostapd_is_mld_ap(hapd))
6646 return;
6647
Sunil Ravi7f769292024-07-23 22:21:32 +00006648 for_each_mld_link(tmp_hapd, hapd) {
6649 struct mld_link_info *link;
6650 struct sta_info *tmp_sta;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006651
Sunil Ravi7f769292024-07-23 22:21:32 +00006652 if (tmp_hapd == hapd)
6653 continue;
6654
6655 link = &sta->mld_info.links[tmp_hapd->mld_link_id];
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006656 if (!link->valid)
6657 continue;
6658
Sunil Ravi7f769292024-07-23 22:21:32 +00006659 for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
6660 tmp_sta = tmp_sta->next) {
6661 if (tmp_sta == sta ||
6662 tmp_sta->mld_assoc_link_id !=
6663 sta->mld_assoc_link_id ||
6664 tmp_sta->aid != sta->aid)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006665 continue;
6666
Sunil Ravi7f769292024-07-23 22:21:32 +00006667 ieee80211_ml_link_sta_assoc_cb(tmp_hapd, tmp_sta, link,
6668 ok);
6669 break;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006670 }
6671 }
6672#endif /* CONFIG_IEEE80211BE */
6673}
6674
6675
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006676static void handle_assoc_cb(struct hostapd_data *hapd,
6677 const struct ieee80211_mgmt *mgmt,
6678 size_t len, int reassoc, int ok)
6679{
6680 u16 status;
6681 struct sta_info *sta;
6682 int new_assoc = 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006683
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006684 sta = ap_get_sta(hapd, mgmt->da);
6685 if (!sta) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08006686 wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found",
6687 MAC2STR(mgmt->da));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006688 return;
6689 }
6690
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006691#ifdef CONFIG_IEEE80211BE
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006692 if (ap_sta_is_mld(hapd, sta) &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006693 hapd->mld_link_id != sta->mld_assoc_link_id) {
6694 /* See ieee80211_ml_link_sta_assoc_cb() for the MLD case */
6695 wpa_printf(MSG_DEBUG,
6696 "%s: MLD: ignore on link station (%d != %d)",
6697 __func__, hapd->mld_link_id, sta->mld_assoc_link_id);
6698 return;
6699 }
6700#endif /* CONFIG_IEEE80211BE */
6701
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006702 if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
6703 sizeof(mgmt->u.assoc_resp))) {
6704 wpa_printf(MSG_INFO,
6705 "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)",
6706 reassoc, (unsigned long) len);
6707 hostapd_drv_sta_remove(hapd, sta->addr);
Dmitry Shmidtaa532512012-09-24 10:35:31 -07006708 return;
6709 }
6710
6711 if (reassoc)
6712 status = le_to_host16(mgmt->u.reassoc_resp.status_code);
6713 else
6714 status = le_to_host16(mgmt->u.assoc_resp.status_code);
6715
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006716 if (!ok) {
6717 hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
6718 HOSTAPD_LEVEL_DEBUG,
6719 "did not acknowledge association response");
6720 sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
6721 /* The STA is added only in case of SUCCESS */
6722 if (status == WLAN_STATUS_SUCCESS)
6723 hostapd_drv_sta_remove(hapd, sta->addr);
6724
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006725 goto handle_ml;
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08006726 }
6727
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006728 if (status != WLAN_STATUS_SUCCESS)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006729 goto handle_ml;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006730
6731 /* Stop previous accounting session, if one is started, and allocate
6732 * new session id for the new session. */
6733 accounting_sta_stop(hapd, sta);
6734
6735 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
6736 HOSTAPD_LEVEL_INFO,
6737 "associated (aid %d)",
6738 sta->aid);
6739
6740 if (sta->flags & WLAN_STA_ASSOC)
6741 new_assoc = 0;
6742 sta->flags |= WLAN_STA_ASSOC;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006743 sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08006744 if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
6745 !hapd->conf->osen) ||
6746 sta->auth_alg == WLAN_AUTH_FILS_SK ||
6747 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
6748 sta->auth_alg == WLAN_AUTH_FILS_PK ||
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006749 sta->auth_alg == WLAN_AUTH_FT) {
6750 /*
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08006751 * Open, static WEP, FT protocol, or FILS; no separate
6752 * authorization step.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006753 */
6754 ap_sta_set_authorized(hapd, sta, 1);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006755 }
6756
6757 if (reassoc)
6758 mlme_reassociate_indication(hapd, sta);
6759 else
6760 mlme_associate_indication(hapd, sta);
6761
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006762 sta->sa_query_timed_out = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006763
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006764 if (sta->eapol_sm == NULL) {
6765 /*
6766 * This STA does not use RADIUS server for EAP authentication,
6767 * so bind it to the selected VLAN interface now, since the
6768 * interface selection is not going to change anymore.
6769 */
Dmitry Shmidt83474442015-04-15 13:47:09 -07006770 if (ap_sta_bind_vlan(hapd, sta) < 0)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006771 goto handle_ml;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006772 } else if (sta->vlan_id) {
6773 /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */
Dmitry Shmidt83474442015-04-15 13:47:09 -07006774 if (ap_sta_bind_vlan(hapd, sta) < 0)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006775 goto handle_ml;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006776 }
6777
6778 hostapd_set_sta_flags(hapd, sta);
6779
Dmitry Shmidt29333592017-01-09 12:27:11 -08006780 if (!(sta->flags & WLAN_STA_WDS) && sta->pending_wds_enable) {
6781 wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for STA "
6782 MACSTR " based on pending request",
6783 MAC2STR(sta->addr));
6784 sta->pending_wds_enable = 0;
6785 sta->flags |= WLAN_STA_WDS;
6786 }
6787
Sunil Ravi640215c2023-06-28 23:08:09 +00006788 /* WPS not supported on backhaul BSS. Disable 4addr mode on fronthaul */
6789 if ((sta->flags & WLAN_STA_WDS) ||
6790 (sta->flags & WLAN_STA_MULTI_AP &&
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006791 (hapd->conf->multi_ap & BACKHAUL_BSS) &&
Sunil Ravi7f769292024-07-23 22:21:32 +00006792 hapd->conf->wds_sta &&
Sunil Ravi640215c2023-06-28 23:08:09 +00006793 !(sta->flags & WLAN_STA_WPS))) {
Dmitry Shmidtabb90a32016-12-05 15:34:39 -08006794 int ret;
6795 char ifname_wds[IFNAMSIZ + 1];
6796
6797 wpa_printf(MSG_DEBUG, "Reenable 4-address WDS mode for STA "
6798 MACSTR " (aid %u)",
6799 MAC2STR(sta->addr), sta->aid);
6800 ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
6801 sta->aid, 1);
6802 if (!ret)
6803 hostapd_set_wds_encryption(hapd, sta, ifname_wds);
6804 }
6805
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006806 if (sta->auth_alg == WLAN_AUTH_FT)
6807 wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
6808 else
6809 wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
6810 hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006811 ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
Dmitry Shmidt31a29cc2016-03-09 15:58:17 -08006812
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -08006813#ifdef CONFIG_FILS
6814 if ((sta->auth_alg == WLAN_AUTH_FILS_SK ||
6815 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
6816 sta->auth_alg == WLAN_AUTH_FILS_PK) &&
6817 fils_set_tk(sta->wpa_sm) < 0) {
6818 wpa_printf(MSG_DEBUG, "FILS: TK configuration failed");
6819 ap_sta_disconnect(hapd, sta, sta->addr,
6820 WLAN_REASON_UNSPECIFIED);
6821 return;
6822 }
6823#endif /* CONFIG_FILS */
6824
Dmitry Shmidt31a29cc2016-03-09 15:58:17 -08006825 if (sta->pending_eapol_rx) {
6826 struct os_reltime now, age;
6827
6828 os_get_reltime(&now);
6829 os_reltime_sub(&now, &sta->pending_eapol_rx->rx_time, &age);
6830 if (age.sec == 0 && age.usec < 200000) {
6831 wpa_printf(MSG_DEBUG,
6832 "Process pending EAPOL frame that was received from " MACSTR " just before association notification",
6833 MAC2STR(sta->addr));
6834 ieee802_1x_receive(
6835 hapd, mgmt->da,
6836 wpabuf_head(sta->pending_eapol_rx->buf),
Sunil8cd6f4d2022-06-28 18:40:46 +00006837 wpabuf_len(sta->pending_eapol_rx->buf),
6838 sta->pending_eapol_rx->encrypted);
Dmitry Shmidt31a29cc2016-03-09 15:58:17 -08006839 }
6840 wpabuf_free(sta->pending_eapol_rx->buf);
6841 os_free(sta->pending_eapol_rx);
6842 sta->pending_eapol_rx = NULL;
6843 }
Sunil Ravi2a14cf12023-11-21 00:54:38 +00006844
6845handle_ml:
6846 hostapd_ml_handle_assoc_cb(hapd, sta, ok);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006847}
6848
6849
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08006850static void handle_deauth_cb(struct hostapd_data *hapd,
6851 const struct ieee80211_mgmt *mgmt,
6852 size_t len, int ok)
6853{
6854 struct sta_info *sta;
Dmitry Shmidtd13095b2016-08-22 14:02:19 -07006855 if (is_multicast_ether_addr(mgmt->da))
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08006856 return;
6857 sta = ap_get_sta(hapd, mgmt->da);
6858 if (!sta) {
6859 wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR
6860 " not found", MAC2STR(mgmt->da));
6861 return;
6862 }
6863 if (ok)
6864 wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth",
6865 MAC2STR(sta->addr));
6866 else
6867 wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
6868 "deauth", MAC2STR(sta->addr));
6869
6870 ap_sta_deauth_cb(hapd, sta);
6871}
6872
6873
6874static void handle_disassoc_cb(struct hostapd_data *hapd,
6875 const struct ieee80211_mgmt *mgmt,
6876 size_t len, int ok)
6877{
6878 struct sta_info *sta;
Dmitry Shmidtd13095b2016-08-22 14:02:19 -07006879 if (is_multicast_ether_addr(mgmt->da))
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08006880 return;
6881 sta = ap_get_sta(hapd, mgmt->da);
6882 if (!sta) {
6883 wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR
6884 " not found", MAC2STR(mgmt->da));
6885 return;
6886 }
6887 if (ok)
6888 wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc",
6889 MAC2STR(sta->addr));
6890 else
6891 wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
6892 "disassoc", MAC2STR(sta->addr));
6893
6894 ap_sta_disassoc_cb(hapd, sta);
6895}
6896
6897
Dmitry Shmidt29333592017-01-09 12:27:11 -08006898static void handle_action_cb(struct hostapd_data *hapd,
6899 const struct ieee80211_mgmt *mgmt,
6900 size_t len, int ok)
6901{
6902 struct sta_info *sta;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006903#ifndef CONFIG_NO_RRM
Paul Stewart092955c2017-02-06 09:13:09 -08006904 const struct rrm_measurement_report_element *report;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006905#endif /* CONFIG_NO_RRM */
Dmitry Shmidt29333592017-01-09 12:27:11 -08006906
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006907#ifdef CONFIG_DPP
6908 if (len >= IEEE80211_HDRLEN + 6 &&
6909 mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
6910 mgmt->u.action.u.vs_public_action.action ==
6911 WLAN_PA_VENDOR_SPECIFIC &&
6912 WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
6913 OUI_WFA &&
6914 mgmt->u.action.u.vs_public_action.variable[0] ==
6915 DPP_OUI_TYPE) {
6916 const u8 *pos, *end;
6917
6918 pos = &mgmt->u.action.u.vs_public_action.variable[1];
6919 end = ((const u8 *) mgmt) + len;
6920 hostapd_dpp_tx_status(hapd, mgmt->da, pos, end - pos, ok);
6921 return;
6922 }
6923 if (len >= IEEE80211_HDRLEN + 2 &&
6924 mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
6925 (mgmt->u.action.u.public_action.action ==
6926 WLAN_PA_GAS_INITIAL_REQ ||
6927 mgmt->u.action.u.public_action.action ==
6928 WLAN_PA_GAS_COMEBACK_REQ)) {
6929 const u8 *pos, *end;
6930
6931 pos = mgmt->u.action.u.public_action.variable;
6932 end = ((const u8 *) mgmt) + len;
6933 gas_query_ap_tx_status(hapd->gas, mgmt->da, pos, end - pos, ok);
6934 return;
6935 }
6936#endif /* CONFIG_DPP */
Hai Shaloma20dcd72022-02-04 13:43:00 -08006937 if (is_multicast_ether_addr(mgmt->da))
6938 return;
Dmitry Shmidt29333592017-01-09 12:27:11 -08006939 sta = ap_get_sta(hapd, mgmt->da);
6940 if (!sta) {
6941 wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR
6942 " not found", MAC2STR(mgmt->da));
6943 return;
6944 }
6945
Sunil Ravi77d572f2023-01-17 23:58:31 +00006946#ifdef CONFIG_HS20
6947 if (ok && len >= IEEE80211_HDRLEN + 2 &&
6948 mgmt->u.action.category == WLAN_ACTION_WNM &&
6949 mgmt->u.action.u.vs_public_action.action == WNM_NOTIFICATION_REQ &&
6950 sta->hs20_deauth_on_ack) {
6951 wpa_printf(MSG_DEBUG, "HS 2.0: Deauthenticate STA " MACSTR
6952 " on acknowledging the WNM-Notification",
6953 MAC2STR(sta->addr));
6954 ap_sta_session_timeout(hapd, sta, 0);
6955 return;
6956 }
6957#endif /* CONFIG_HS20 */
6958
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006959#ifndef CONFIG_NO_RRM
Paul Stewart092955c2017-02-06 09:13:09 -08006960 if (len < 24 + 5 + sizeof(*report))
Dmitry Shmidt29333592017-01-09 12:27:11 -08006961 return;
Paul Stewart092955c2017-02-06 09:13:09 -08006962 report = (const struct rrm_measurement_report_element *)
6963 &mgmt->u.action.u.rrm.variable[2];
Dmitry Shmidt29333592017-01-09 12:27:11 -08006964 if (mgmt->u.action.category == WLAN_ACTION_RADIO_MEASUREMENT &&
Paul Stewart092955c2017-02-06 09:13:09 -08006965 mgmt->u.action.u.rrm.action == WLAN_RRM_RADIO_MEASUREMENT_REQUEST &&
6966 report->eid == WLAN_EID_MEASURE_REQUEST &&
6967 report->len >= 3 &&
6968 report->type == MEASURE_TYPE_BEACON)
Dmitry Shmidt29333592017-01-09 12:27:11 -08006969 hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok);
Sunil Ravib0ac25f2024-07-12 01:42:03 +00006970#endif /* CONFIG_NO_RRM */
Dmitry Shmidt29333592017-01-09 12:27:11 -08006971}
6972
6973
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07006974/**
6975 * ieee802_11_mgmt_cb - Process management frame TX status callback
6976 * @hapd: hostapd BSS data structure (the BSS from which the management frame
6977 * was sent from)
6978 * @buf: management frame data (starting from IEEE 802.11 header)
6979 * @len: length of frame data in octets
6980 * @stype: management frame subtype from frame control field
6981 * @ok: Whether the frame was ACK'ed
6982 */
6983void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
6984 u16 stype, int ok)
6985{
6986 const struct ieee80211_mgmt *mgmt;
6987 mgmt = (const struct ieee80211_mgmt *) buf;
6988
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08006989#ifdef CONFIG_TESTING_OPTIONS
6990 if (hapd->ext_mgmt_frame_handling) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006991 size_t hex_len = 2 * len + 1;
6992 char *hex = os_malloc(hex_len);
6993
6994 if (hex) {
6995 wpa_snprintf_hex(hex, hex_len, buf, len);
6996 wpa_msg(hapd->msg_ctx, MSG_INFO,
6997 "MGMT-TX-STATUS stype=%u ok=%d buf=%s",
6998 stype, ok, hex);
6999 os_free(hex);
7000 }
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08007001 return;
7002 }
7003#endif /* CONFIG_TESTING_OPTIONS */
7004
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007005 switch (stype) {
7006 case WLAN_FC_STYPE_AUTH:
7007 wpa_printf(MSG_DEBUG, "mgmt::auth cb");
7008 handle_auth_cb(hapd, mgmt, len, ok);
7009 break;
7010 case WLAN_FC_STYPE_ASSOC_RESP:
7011 wpa_printf(MSG_DEBUG, "mgmt::assoc_resp cb");
7012 handle_assoc_cb(hapd, mgmt, len, 0, ok);
7013 break;
7014 case WLAN_FC_STYPE_REASSOC_RESP:
7015 wpa_printf(MSG_DEBUG, "mgmt::reassoc_resp cb");
7016 handle_assoc_cb(hapd, mgmt, len, 1, ok);
7017 break;
7018 case WLAN_FC_STYPE_PROBE_RESP:
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08007019 wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb ok=%d", ok);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007020 break;
7021 case WLAN_FC_STYPE_DEAUTH:
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08007022 wpa_printf(MSG_DEBUG, "mgmt::deauth cb");
7023 handle_deauth_cb(hapd, mgmt, len, ok);
7024 break;
7025 case WLAN_FC_STYPE_DISASSOC:
7026 wpa_printf(MSG_DEBUG, "mgmt::disassoc cb");
7027 handle_disassoc_cb(hapd, mgmt, len, ok);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007028 break;
7029 case WLAN_FC_STYPE_ACTION:
Dmitry Shmidt57c2d392016-02-23 13:40:19 -08007030 wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok);
Dmitry Shmidt29333592017-01-09 12:27:11 -08007031 handle_action_cb(hapd, mgmt, len, ok);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007032 break;
7033 default:
Dmitry Shmidtcce06662013-11-04 18:44:24 -08007034 wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007035 break;
7036 }
7037}
7038
7039
7040int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
7041{
7042 /* TODO */
7043 return 0;
7044}
7045
7046
7047int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
7048 char *buf, size_t buflen)
7049{
7050 /* TODO */
7051 return 0;
7052}
7053
7054
7055void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
7056 const u8 *buf, size_t len, int ack)
7057{
7058 struct sta_info *sta;
7059 struct hostapd_iface *iface = hapd->iface;
7060
7061 sta = ap_get_sta(hapd, addr);
7062 if (sta == NULL && iface->num_bss > 1) {
7063 size_t j;
7064 for (j = 0; j < iface->num_bss; j++) {
7065 hapd = iface->bss[j];
7066 sta = ap_get_sta(hapd, addr);
7067 if (sta)
7068 break;
7069 }
7070 }
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08007071 if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007072 return;
7073 if (sta->flags & WLAN_STA_PENDING_POLL) {
7074 wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending "
7075 "activity poll", MAC2STR(sta->addr),
7076 ack ? "ACKed" : "did not ACK");
7077 if (ack)
7078 sta->flags &= ~WLAN_STA_PENDING_POLL;
7079 }
7080
7081 ieee802_1x_tx_status(hapd, sta, buf, len, ack);
7082}
7083
7084
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08007085void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
7086{
7087 struct sta_info *sta;
7088 struct hostapd_iface *iface = hapd->iface;
7089
7090 sta = ap_get_sta(hapd, addr);
7091 if (sta == NULL && iface->num_bss > 1) {
7092 size_t j;
7093 for (j = 0; j < iface->num_bss; j++) {
7094 hapd = iface->bss[j];
7095 sta = ap_get_sta(hapd, addr);
7096 if (sta)
7097 break;
7098 }
7099 }
7100 if (sta == NULL)
7101 return;
Dmitry Shmidt849734c2016-05-27 09:59:01 -07007102 wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POLL_OK MACSTR,
7103 MAC2STR(sta->addr));
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08007104 if (!(sta->flags & WLAN_STA_PENDING_POLL))
7105 return;
7106
7107 wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending "
7108 "activity poll", MAC2STR(sta->addr));
7109 sta->flags &= ~WLAN_STA_PENDING_POLL;
7110}
7111
7112
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007113void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
7114 int wds)
7115{
7116 struct sta_info *sta;
7117
7118 sta = ap_get_sta(hapd, src);
Dmitry Shmidt29333592017-01-09 12:27:11 -08007119 if (sta &&
7120 ((sta->flags & WLAN_STA_ASSOC) ||
7121 ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) {
Dmitry Shmidtaa532512012-09-24 10:35:31 -07007122 if (!hapd->conf->wds_sta)
7123 return;
7124
Dmitry Shmidt29333592017-01-09 12:27:11 -08007125 if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK)) ==
7126 WLAN_STA_ASSOC_REQ_OK) {
7127 wpa_printf(MSG_DEBUG,
7128 "Postpone 4-address WDS mode enabling for STA "
7129 MACSTR " since TX status for AssocResp is not yet known",
7130 MAC2STR(sta->addr));
7131 sta->pending_wds_enable = 1;
7132 return;
7133 }
7134
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007135 if (wds && !(sta->flags & WLAN_STA_WDS)) {
Dmitry Shmidtc2ebb4b2013-07-24 12:57:51 -07007136 int ret;
7137 char ifname_wds[IFNAMSIZ + 1];
7138
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007139 wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for "
7140 "STA " MACSTR " (aid %u)",
7141 MAC2STR(sta->addr), sta->aid);
7142 sta->flags |= WLAN_STA_WDS;
Dmitry Shmidtc2ebb4b2013-07-24 12:57:51 -07007143 ret = hostapd_set_wds_sta(hapd, ifname_wds,
7144 sta->addr, sta->aid, 1);
7145 if (!ret)
7146 hostapd_set_wds_encryption(hapd, sta,
7147 ifname_wds);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007148 }
7149 return;
7150 }
7151
7152 wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
7153 MACSTR, MAC2STR(src));
Hai Shalomc3565922019-10-28 11:58:20 -07007154 if (is_multicast_ether_addr(src) || is_zero_ether_addr(src) ||
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007155 ether_addr_equal(src, hapd->own_addr)) {
Hai Shalomc3565922019-10-28 11:58:20 -07007156 /* Broadcast bit set in SA or unexpected SA?! Ignore the frame
7157 * silently. */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007158 return;
7159 }
7160
7161 if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) {
7162 wpa_printf(MSG_DEBUG, "Association Response to the STA has "
7163 "already been sent, but no TX status yet known - "
7164 "ignore Class 3 frame issue with " MACSTR,
7165 MAC2STR(src));
7166 return;
7167 }
7168
7169 if (sta && (sta->flags & WLAN_STA_AUTH))
7170 hostapd_drv_sta_disassoc(
7171 hapd, src,
7172 WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
7173 else
7174 hostapd_drv_sta_deauth(
7175 hapd, src,
7176 WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
7177}
7178
7179
Sunil Ravia04bd252022-05-02 22:54:18 -07007180static u8 * hostapd_add_tpe_info(u8 *eid, u8 tx_pwr_count,
7181 enum max_tx_pwr_interpretation tx_pwr_intrpn,
7182 u8 tx_pwr_cat, u8 tx_pwr)
7183{
7184 int i;
7185
7186 *eid++ = WLAN_EID_TRANSMIT_POWER_ENVELOPE; /* Element ID */
7187 *eid++ = 2 + tx_pwr_count; /* Length */
7188
7189 /*
7190 * Transmit Power Information field
7191 * bits 0-2 : Maximum Transmit Power Count
7192 * bits 3-5 : Maximum Transmit Power Interpretation
7193 * bits 6-7 : Maximum Transmit Power Category
7194 */
7195 *eid++ = tx_pwr_count | (tx_pwr_intrpn << 3) | (tx_pwr_cat << 6);
7196
7197 /* Maximum Transmit Power field */
7198 for (i = 0; i <= tx_pwr_count; i++)
7199 *eid++ = tx_pwr;
7200
7201 return eid;
7202}
7203
7204
7205/*
7206 * TODO: Extract power limits from channel data after 6G regulatory
7207 * support.
7208 */
7209#define REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT (-1) /* dBm/MHz */
7210#define REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT 5 /* dBm/MHz */
7211
Hai Shalom60840252021-02-19 19:02:11 -08007212u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
7213{
7214 struct hostapd_iface *iface = hapd->iface;
7215 struct hostapd_config *iconf = iface->conf;
7216 struct hostapd_hw_modes *mode = iface->current_mode;
7217 struct hostapd_channel_data *chan;
7218 int dfs, i;
7219 u8 channel, tx_pwr_count, local_pwr_constraint;
7220 int max_tx_power;
7221 u8 tx_pwr;
7222
7223 if (!mode)
7224 return eid;
7225
7226 if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES)
7227 return eid;
7228
7229 for (i = 0; i < mode->num_channels; i++) {
7230 if (mode->channels[i].freq == iface->freq)
7231 break;
7232 }
7233 if (i == mode->num_channels)
7234 return eid;
7235
Sunil Ravia04bd252022-05-02 22:54:18 -07007236#ifdef CONFIG_IEEE80211AX
7237 /* IEEE Std 802.11ax-2021, Annex E.2.7 (6 GHz band in the United
7238 * States): An AP that is an Indoor Access Point per regulatory rules
7239 * shall send at least two Transmit Power Envelope elements in Beacon
7240 * and Probe Response frames as follows:
7241 * - Maximum Transmit Power Category subfield = Default;
7242 * Unit interpretation = Regulatory client EIRP PSD
7243 * - Maximum Transmit Power Category subfield = Subordinate Device;
7244 * Unit interpretation = Regulatory client EIRP PSD
7245 */
7246 if (is_6ghz_op_class(iconf->op_class)) {
7247 enum max_tx_pwr_interpretation tx_pwr_intrpn;
7248
7249 /* Same Maximum Transmit Power for all 20 MHz bands */
7250 tx_pwr_count = 0;
7251 tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD;
7252
7253 /* Default Transmit Power Envelope for Global Operating Class */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007254 if (hapd->iconf->reg_def_cli_eirp_psd != -1)
7255 tx_pwr = hapd->iconf->reg_def_cli_eirp_psd;
7256 else
7257 tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
7258
Sunil Ravia04bd252022-05-02 22:54:18 -07007259 eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn,
7260 REG_DEFAULT_CLIENT, tx_pwr);
7261
7262 /* Indoor Access Point must include an additional TPE for
7263 * subordinate devices */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007264 if (he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type)) {
Sunil Ravia04bd252022-05-02 22:54:18 -07007265 /* TODO: Extract PSD limits from channel data */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007266 if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
7267 tx_pwr = hapd->iconf->reg_sub_cli_eirp_psd;
7268 else
7269 tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
Sunil Ravia04bd252022-05-02 22:54:18 -07007270 eid = hostapd_add_tpe_info(eid, tx_pwr_count,
7271 tx_pwr_intrpn,
7272 REG_SUBORDINATE_CLIENT,
7273 tx_pwr);
7274 }
7275
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007276 if (iconf->reg_def_cli_eirp != -1 &&
7277 he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
7278 eid = hostapd_add_tpe_info(
7279 eid, tx_pwr_count, REGULATORY_CLIENT_EIRP,
7280 REG_DEFAULT_CLIENT,
7281 hapd->iconf->reg_def_cli_eirp);
7282
Sunil Ravia04bd252022-05-02 22:54:18 -07007283 return eid;
7284 }
7285#endif /* CONFIG_IEEE80211AX */
7286
Hai Shalom60840252021-02-19 19:02:11 -08007287 switch (hostapd_get_oper_chwidth(iconf)) {
Sunil8cd6f4d2022-06-28 18:40:46 +00007288 case CONF_OPER_CHWIDTH_USE_HT:
Hai Shalom60840252021-02-19 19:02:11 -08007289 if (iconf->secondary_channel == 0) {
7290 /* Max Transmit Power count = 0 (20 MHz) */
7291 tx_pwr_count = 0;
7292 } else {
7293 /* Max Transmit Power count = 1 (20, 40 MHz) */
7294 tx_pwr_count = 1;
7295 }
7296 break;
Sunil8cd6f4d2022-06-28 18:40:46 +00007297 case CONF_OPER_CHWIDTH_80MHZ:
Hai Shalom60840252021-02-19 19:02:11 -08007298 /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */
7299 tx_pwr_count = 2;
7300 break;
Sunil8cd6f4d2022-06-28 18:40:46 +00007301 case CONF_OPER_CHWIDTH_80P80MHZ:
7302 case CONF_OPER_CHWIDTH_160MHZ:
Hai Shalom60840252021-02-19 19:02:11 -08007303 /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */
7304 tx_pwr_count = 3;
7305 break;
7306 default:
7307 return eid;
7308 }
7309
7310 /*
7311 * Below local_pwr_constraint logic is referred from
7312 * hostapd_eid_pwr_constraint.
7313 *
7314 * Check if DFS is required by regulatory.
7315 */
7316 dfs = hostapd_is_dfs_required(hapd->iface);
7317 if (dfs < 0)
7318 dfs = 0;
7319
7320 /*
7321 * In order to meet regulations when TPC is not implemented using
7322 * a transmit power that is below the legal maximum (including any
7323 * mitigation factor) should help. In this case, indicate 3 dB below
7324 * maximum allowed transmit power.
7325 */
7326 if (hapd->iconf->local_pwr_constraint == -1)
7327 local_pwr_constraint = (dfs == 0) ? 0 : 3;
7328 else
7329 local_pwr_constraint = hapd->iconf->local_pwr_constraint;
7330
7331 /*
7332 * A STA that is not an AP shall use a transmit power less than or
7333 * equal to the local maximum transmit power level for the channel.
7334 * The local maximum transmit power can be calculated from the formula:
7335 * local max TX pwr = max TX pwr - local pwr constraint
7336 * Where max TX pwr is maximum transmit power level specified for
7337 * channel in Country element and local pwr constraint is specified
7338 * for channel in this Power Constraint element.
7339 */
7340 chan = &mode->channels[i];
7341 max_tx_power = chan->max_tx_power - local_pwr_constraint;
7342
7343 /*
7344 * Local Maximum Transmit power is encoded as two's complement
7345 * with a 0.5 dB step.
7346 */
7347 max_tx_power *= 2; /* in 0.5 dB steps */
7348 if (max_tx_power > 127) {
7349 /* 63.5 has special meaning of 63.5 dBm or higher */
7350 max_tx_power = 127;
7351 }
7352 if (max_tx_power < -128)
7353 max_tx_power = -128;
7354 if (max_tx_power < 0)
7355 tx_pwr = 0x80 + max_tx_power + 128;
7356 else
7357 tx_pwr = max_tx_power;
7358
Sunil Ravia04bd252022-05-02 22:54:18 -07007359 return hostapd_add_tpe_info(eid, tx_pwr_count, LOCAL_EIRP,
7360 0 /* Reserved for bands other than 6 GHz */,
7361 tx_pwr);
Hai Shalom60840252021-02-19 19:02:11 -08007362}
7363
7364
Sunil Ravic0f5d412024-09-11 22:12:49 +00007365/* Wide Bandwidth Channel Switch subelement */
7366static u8 * hostapd_eid_wb_channel_switch(struct hostapd_data *hapd, u8 *eid,
7367 u8 chan1, u8 chan2)
Hai Shalom899fcc72020-10-19 14:38:18 -07007368{
Sunil Ravic0f5d412024-09-11 22:12:49 +00007369 u8 bw;
Hai Shalom899fcc72020-10-19 14:38:18 -07007370
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007371 /* bandwidth: 0: 40, 1: 80, 160, 80+80, 4 to 255 reserved as per
7372 * IEEE P802.11-REVme/D7.0, 9.4.2.159 and Table 9-316.
7373 */
Hai Shalom899fcc72020-10-19 14:38:18 -07007374 switch (hapd->cs_freq_params.bandwidth) {
Sunil Ravi640215c2023-06-28 23:08:09 +00007375 case 320:
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007376 /* As per IEEE P802.11be/D7.0, 35.15.3,
7377 * For EHT BSS operating channel width wider than 160 MHz,
7378 * the announced BSS bandwidth in the Wide Bandwidth
7379 * Channel Switch element is less than the BSS bandwidth
7380 * in the Bandwidth Indication element
7381 */
Hai Shalom899fcc72020-10-19 14:38:18 -07007382
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007383 /* Modifying the center frequency to 160 MHz */
7384 if (hapd->cs_freq_params.channel < chan1)
7385 chan1 -= 16;
7386 else
7387 chan1 += 16;
7388
7389 /* fallthrough */
7390 case 160:
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007391 /* Update the CCFS0 and CCFS1 values in the element based on
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007392 * IEEE P802.11-REVme/D7.0, Table 9-316
7393 */
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007394
7395 /* CCFS1 - The channel center frequency index of the 160 MHz
7396 * channel. */
7397 chan2 = chan1;
7398
7399 /* CCFS0 - The channel center frequency index of the 80 MHz
7400 * channel segment that contains the primary channel. */
7401 if (hapd->cs_freq_params.channel < chan1)
7402 chan1 -= 8;
7403 else
7404 chan1 += 8;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007405
7406 bw = 1;
7407 break;
7408 case 80:
7409 bw = 1;
7410 break;
7411 case 40:
7412 bw = 0;
7413 break;
7414 default:
7415 /* not valid VHT bandwidth or not in CSA */
7416 return eid;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007417 }
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007418
7419 *eid++ = WLAN_EID_WIDE_BW_CHSWITCH;
7420 *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
7421 *eid++ = bw; /* New Channel Width */
Hai Shalom899fcc72020-10-19 14:38:18 -07007422 *eid++ = chan1; /* New Channel Center Frequency Segment 0 */
7423 *eid++ = chan2; /* New Channel Center Frequency Segment 1 */
7424
7425 return eid;
7426}
7427
Hai Shaloma20dcd72022-02-04 13:43:00 -08007428
Sunil Ravic0f5d412024-09-11 22:12:49 +00007429#ifdef CONFIG_IEEE80211BE
7430/* Bandwidth Indication element that is also used as the Bandwidth Indication
7431 * For Channel Switch subelement within a Channel Switch Wrapper element. */
7432static u8 * hostapd_eid_bw_indication(struct hostapd_data *hapd, u8 *eid,
7433 u8 chan1, u8 chan2)
7434{
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007435 u16 punct_bitmap = hapd->cs_freq_params.punct_bitmap;
Sunil Ravic0f5d412024-09-11 22:12:49 +00007436 struct ieee80211_bw_ind_element *bw_ind_elem;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00007437 size_t elen = 4;
Sunil Ravic0f5d412024-09-11 22:12:49 +00007438
7439 if (hapd->cs_freq_params.bandwidth <= 160 && !punct_bitmap)
7440 return eid;
7441
7442 if (punct_bitmap)
7443 elen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
7444
7445 *eid++ = WLAN_EID_EXTENSION;
7446 *eid++ = 1 + elen;
7447 *eid++ = WLAN_EID_EXT_BANDWIDTH_INDICATION;
7448
7449 bw_ind_elem = (struct ieee80211_bw_ind_element *) eid;
7450 os_memset(bw_ind_elem, 0, sizeof(struct ieee80211_bw_ind_element));
7451
7452 switch (hapd->cs_freq_params.bandwidth) {
7453 case 320:
7454 bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_320MHZ;
7455 chan2 = chan1;
7456 if (hapd->cs_freq_params.channel < chan1)
7457 chan1 -= 16;
7458 else
7459 chan1 += 16;
7460 break;
7461 case 160:
7462 bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_160MHZ;
7463 chan2 = chan1;
7464 if (hapd->cs_freq_params.channel < chan1)
7465 chan1 -= 8;
7466 else
7467 chan1 += 8;
7468 break;
7469 case 80:
7470 bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_80MHZ;
7471 break;
7472 case 40:
7473 if (hapd->cs_freq_params.sec_channel_offset == 1)
7474 bw_ind_elem->bw_ind_info.control |=
7475 BW_IND_CHANNEL_WIDTH_40MHZ;
7476 else
7477 bw_ind_elem->bw_ind_info.control |=
7478 BW_IND_CHANNEL_WIDTH_20MHZ;
7479 break;
7480 default:
7481 bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_20MHZ;
7482 break;
7483 }
7484
7485 bw_ind_elem->bw_ind_info.ccfs0 = chan1;
7486 bw_ind_elem->bw_ind_info.ccfs1 = chan2;
7487
7488 if (punct_bitmap) {
7489 bw_ind_elem->bw_ind_params |=
7490 BW_IND_PARAMETER_DISABLED_SUBCHAN_BITMAP_PRESENT;
7491 bw_ind_elem->bw_ind_info.disabled_chan_bitmap =
7492 host_to_le16(punct_bitmap);
7493 }
7494
7495 return eid + elen;
7496}
7497#endif /* CONFIG_IEEE80211BE */
7498
7499
7500u8 * hostapd_eid_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
7501{
7502 u8 chan1 = 0, chan2 = 0;
7503 u8 *eid_len_offset;
7504 int freq1;
7505
7506 if (!hapd->cs_freq_params.channel ||
7507 (!hapd->cs_freq_params.vht_enabled &&
7508 !hapd->cs_freq_params.he_enabled &&
7509 !hapd->cs_freq_params.eht_enabled))
7510 return eid;
7511
7512 freq1 = hapd->cs_freq_params.center_freq1 ?
7513 hapd->cs_freq_params.center_freq1 :
7514 hapd->cs_freq_params.freq;
7515 if (ieee80211_freq_to_chan(freq1, &chan1) !=
7516 HOSTAPD_MODE_IEEE80211A)
7517 return eid;
7518
7519 if (hapd->cs_freq_params.center_freq2 &&
7520 ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
7521 &chan2) != HOSTAPD_MODE_IEEE80211A)
7522 return eid;
7523
7524 *eid++ = WLAN_EID_CHANNEL_SWITCH_WRAPPER;
7525 eid_len_offset = eid++; /* Length of Channel Switch Wrapper element */
7526
7527 eid = hostapd_eid_wb_channel_switch(hapd, eid, chan1, chan2);
7528
7529#ifdef CONFIG_IEEE80211BE
7530 if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
7531 /* Bandwidth Indication For Channel Switch subelement */
7532 eid = hostapd_eid_bw_indication(hapd, eid, chan1, chan2);
7533 }
7534#endif /* CONFIG_IEEE80211BE */
7535
7536 *eid_len_offset = (eid - eid_len_offset) - 1;
7537 return eid;
7538}
7539
7540
Hai Shaloma20dcd72022-02-04 13:43:00 -08007541static size_t hostapd_eid_nr_db_len(struct hostapd_data *hapd,
7542 size_t *current_len)
7543{
7544 struct hostapd_neighbor_entry *nr;
7545 size_t total_len = 0, len = *current_len;
7546
7547 dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
7548 list) {
7549 if (!nr->nr || wpabuf_len(nr->nr) < 12)
7550 continue;
7551
7552 if (nr->short_ssid == hapd->conf->ssid.short_ssid)
7553 continue;
7554
7555 /* Start a new element */
7556 if (!len ||
7557 len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
7558 len = RNR_HEADER_LEN;
7559 total_len += RNR_HEADER_LEN;
7560 }
7561
7562 len += RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN;
7563 total_len += RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN;
7564 }
7565
7566 *current_len = len;
7567 return total_len;
7568}
7569
7570
Sunil Ravi640215c2023-06-28 23:08:09 +00007571struct mbssid_ie_profiles {
7572 u8 start;
7573 u8 end;
7574};
7575
Sunil Ravi7f769292024-07-23 22:21:32 +00007576static bool hostapd_skip_rnr(size_t i, struct mbssid_ie_profiles *skip_profiles,
7577 bool ap_mld, u8 tbtt_info_len, bool mld_update,
7578 struct hostapd_data *reporting_hapd,
7579 struct hostapd_data *bss)
7580{
7581 if (skip_profiles &&
7582 i >= skip_profiles->start && i < skip_profiles->end)
7583 return true;
7584
7585 /* No need to report if length is for normal TBTT and the BSS is
7586 * affiliated with an AP MLD. MLD TBTT will include this. */
7587 if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld)
7588 return true;
7589
7590 /* No need to report if length is for MLD TBTT and the BSS is not
7591 * affiliated with an aP MLD. Normal TBTT will include this. */
7592 if (tbtt_info_len == RNR_TBTT_INFO_MLD_LEN && !ap_mld)
7593 return true;
7594
7595#ifdef CONFIG_IEEE80211BE
7596 /* If building for co-location and they are ML partners, no need to
7597 * include since the ML RNR will carry this. */
7598 if (!mld_update && hostapd_is_ml_partner(reporting_hapd, bss))
7599 return true;
7600
7601 /* If building for ML RNR and they are not ML partners, don't include.
7602 */
7603 if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss))
7604 return true;
7605#endif /* CONFIG_IEEE80211BE */
7606
7607 return false;
7608}
7609
7610
Sunil Ravi640215c2023-06-28 23:08:09 +00007611static size_t
7612hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
7613 struct hostapd_data *reporting_hapd,
7614 size_t *current_len,
Sunil Ravi7f769292024-07-23 22:21:32 +00007615 struct mbssid_ie_profiles *skip_profiles,
7616 bool mld_update)
Hai Shaloma20dcd72022-02-04 13:43:00 -08007617{
7618 size_t total_len = 0, len = *current_len;
Sunil Ravi7f769292024-07-23 22:21:32 +00007619 int tbtt_count, total_tbtt_count = 0;
7620 size_t i, start;
7621 u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN :
7622 RNR_TBTT_INFO_LEN;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007623
Sunil Ravi7f769292024-07-23 22:21:32 +00007624repeat_rnr_len:
7625 start = 0;
7626 tbtt_count = 0;
Hai Shaloma20dcd72022-02-04 13:43:00 -08007627
7628 while (start < hapd->iface->num_bss) {
7629 if (!len ||
Sunil Ravi7f769292024-07-23 22:21:32 +00007630 len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007631 tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
Hai Shaloma20dcd72022-02-04 13:43:00 -08007632 len = RNR_HEADER_LEN;
7633 total_len += RNR_HEADER_LEN;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007634 tbtt_count = 0;
Hai Shaloma20dcd72022-02-04 13:43:00 -08007635 }
7636
7637 len += RNR_TBTT_HEADER_LEN;
7638 total_len += RNR_TBTT_HEADER_LEN;
7639
7640 for (i = start; i < hapd->iface->num_bss; i++) {
7641 struct hostapd_data *bss = hapd->iface->bss[i];
Sunil Ravi7f769292024-07-23 22:21:32 +00007642 bool ap_mld = false;
Hai Shaloma20dcd72022-02-04 13:43:00 -08007643
7644 if (!bss || !bss->conf || !bss->started)
7645 continue;
7646
Sunil Ravi7f769292024-07-23 22:21:32 +00007647#ifdef CONFIG_IEEE80211BE
7648 ap_mld = bss->conf->mld_ap;
7649#endif /* CONFIG_IEEE80211BE */
7650
Hai Shaloma20dcd72022-02-04 13:43:00 -08007651 if (bss == reporting_hapd ||
7652 bss->conf->ignore_broadcast_ssid)
7653 continue;
7654
Sunil Ravi7f769292024-07-23 22:21:32 +00007655 if (hostapd_skip_rnr(i, skip_profiles, ap_mld,
7656 tbtt_info_len, mld_update,
7657 reporting_hapd, bss))
Sunil Ravi640215c2023-06-28 23:08:09 +00007658 continue;
7659
Sunil Ravi7f769292024-07-23 22:21:32 +00007660 if (len + tbtt_info_len > 255 ||
Hai Shaloma20dcd72022-02-04 13:43:00 -08007661 tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
7662 break;
7663
Sunil Ravi7f769292024-07-23 22:21:32 +00007664 len += tbtt_info_len;
7665 total_len += tbtt_info_len;
Hai Shaloma20dcd72022-02-04 13:43:00 -08007666 tbtt_count++;
7667 }
7668 start = i;
7669 }
7670
Sunil Ravi7f769292024-07-23 22:21:32 +00007671 total_tbtt_count += tbtt_count;
7672
7673 /* If building for co-location, re-build again but this time include
7674 * ML TBTTs.
7675 */
7676 if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
7677 tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
7678
7679 /* If no TBTT was found, adjust the len and total_len since it
7680 * would have incremented before we checked all BSSs. */
7681 if (!tbtt_count) {
7682 len -= RNR_TBTT_HEADER_LEN;
7683 total_len -= RNR_TBTT_HEADER_LEN;
7684 }
7685
7686 goto repeat_rnr_len;
7687 }
7688
7689 /* This is possible when in the re-built case and no suitable TBTT was
7690 * found. Adjust the length accordingly. */
7691 if (!tbtt_count && total_tbtt_count) {
7692 len -= RNR_TBTT_HEADER_LEN;
7693 total_len -= RNR_TBTT_HEADER_LEN;
7694 }
7695
7696 if (!total_tbtt_count)
Hai Shaloma20dcd72022-02-04 13:43:00 -08007697 total_len = 0;
7698 else
7699 *current_len = len;
7700
7701 return total_len;
7702}
7703
7704
7705enum colocation_mode {
7706 NO_COLOCATED_6GHZ,
7707 STANDALONE_6GHZ,
7708 COLOCATED_6GHZ,
7709 COLOCATED_LOWER_BAND,
7710};
7711
7712static enum colocation_mode get_colocation_mode(struct hostapd_data *hapd)
7713{
7714 u8 i;
7715 bool is_6ghz = is_6ghz_op_class(hapd->iconf->op_class);
7716
7717 if (!hapd->iface || !hapd->iface->interfaces)
7718 return NO_COLOCATED_6GHZ;
7719
7720 if (is_6ghz && hapd->iface->interfaces->count == 1)
7721 return STANDALONE_6GHZ;
7722
7723 for (i = 0; i < hapd->iface->interfaces->count; i++) {
7724 struct hostapd_iface *iface;
7725 bool is_colocated_6ghz;
7726
7727 iface = hapd->iface->interfaces->iface[i];
7728 if (iface == hapd->iface || !iface || !iface->conf)
7729 continue;
7730
7731 is_colocated_6ghz = is_6ghz_op_class(iface->conf->op_class);
7732 if (!is_6ghz && is_colocated_6ghz)
7733 return COLOCATED_LOWER_BAND;
7734 if (is_6ghz && !is_colocated_6ghz)
7735 return COLOCATED_6GHZ;
7736 }
7737
7738 if (is_6ghz)
7739 return STANDALONE_6GHZ;
7740
7741 return NO_COLOCATED_6GHZ;
7742}
7743
7744
Sunil Ravi7f769292024-07-23 22:21:32 +00007745static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd,
7746 size_t *current_len)
Hai Shaloma20dcd72022-02-04 13:43:00 -08007747{
7748 struct hostapd_iface *iface;
7749 size_t len = 0;
7750 size_t i;
7751
7752 if (!hapd->iface || !hapd->iface->interfaces)
7753 return 0;
7754
7755 for (i = 0; i < hapd->iface->interfaces->count; i++) {
7756 iface = hapd->iface->interfaces->iface[i];
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007757
Sunil Ravi7f769292024-07-23 22:21:32 +00007758 if (!iface || iface == hapd->iface ||
Sunil Ravi99c035e2024-07-12 01:42:03 +00007759 iface->state != HAPD_IFACE_ENABLED ||
Sunil Ravi7f769292024-07-23 22:21:32 +00007760 !is_6ghz_op_class(iface->conf->op_class))
Hai Shaloma20dcd72022-02-04 13:43:00 -08007761 continue;
7762
7763 len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
Sunil Ravi7f769292024-07-23 22:21:32 +00007764 current_len, NULL, false);
Hai Shaloma20dcd72022-02-04 13:43:00 -08007765 }
7766
7767 return len;
7768}
7769
7770
Sunil Ravi7f769292024-07-23 22:21:32 +00007771static size_t hostapd_eid_rnr_mlo_len(struct hostapd_data *hapd, u32 type,
7772 size_t *current_len)
7773{
7774 size_t len = 0;
7775#ifdef CONFIG_IEEE80211BE
7776 struct hostapd_iface *iface;
7777 size_t i;
7778
7779 if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap)
7780 return 0;
7781
7782 /* TODO: Allow for FILS/Action as well */
7783 if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
7784 return 0;
7785
7786 for (i = 0; i < hapd->iface->interfaces->count; i++) {
7787 iface = hapd->iface->interfaces->iface[i];
7788
7789 if (!iface || iface == hapd->iface ||
7790 hapd->iface->freq == iface->freq)
7791 continue;
7792
7793 len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
7794 current_len, NULL, true);
7795 }
7796#endif /* CONFIG_IEEE80211BE */
7797
7798 return len;
7799}
7800
7801
7802size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type,
7803 bool include_mld_params)
Hai Shaloma20dcd72022-02-04 13:43:00 -08007804{
7805 size_t total_len = 0, current_len = 0;
7806 enum colocation_mode mode = get_colocation_mode(hapd);
7807
7808 switch (type) {
7809 case WLAN_FC_STYPE_BEACON:
7810 if (hapd->conf->rnr)
7811 total_len += hostapd_eid_nr_db_len(hapd, &current_len);
7812 /* fallthrough */
Hai Shaloma20dcd72022-02-04 13:43:00 -08007813 case WLAN_FC_STYPE_PROBE_RESP:
Sunil Ravi7f769292024-07-23 22:21:32 +00007814 if (mode == COLOCATED_LOWER_BAND)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00007815 total_len +=
Sunil Ravi7f769292024-07-23 22:21:32 +00007816 hostapd_eid_rnr_colocation_len(hapd,
7817 &current_len);
Hai Shaloma20dcd72022-02-04 13:43:00 -08007818
Sunil Ravi640215c2023-06-28 23:08:09 +00007819 if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
7820 !hapd->iconf->mbssid)
Hai Shaloma20dcd72022-02-04 13:43:00 -08007821 total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
Sunil Ravi640215c2023-06-28 23:08:09 +00007822 &current_len,
Sunil Ravi7f769292024-07-23 22:21:32 +00007823 NULL, false);
Hai Shaloma20dcd72022-02-04 13:43:00 -08007824 break;
Hai Shaloma20dcd72022-02-04 13:43:00 -08007825 case WLAN_FC_STYPE_ACTION:
7826 if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
7827 total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
Sunil Ravi640215c2023-06-28 23:08:09 +00007828 &current_len,
Sunil Ravi7f769292024-07-23 22:21:32 +00007829 NULL, false);
Hai Shaloma20dcd72022-02-04 13:43:00 -08007830 break;
7831 }
7832
Sunil Ravi7f769292024-07-23 22:21:32 +00007833 /* For EMA Beacons, MLD neighbor repoting is added as part of
7834 * MBSSID RNR. */
7835 if (include_mld_params &&
7836 (type != WLAN_FC_STYPE_BEACON ||
7837 hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
7838 total_len += hostapd_eid_rnr_mlo_len(hapd, type, &current_len);
7839
Hai Shaloma20dcd72022-02-04 13:43:00 -08007840 return total_len;
7841}
7842
7843
7844static u8 * hostapd_eid_nr_db(struct hostapd_data *hapd, u8 *eid,
7845 size_t *current_len)
7846{
7847 struct hostapd_neighbor_entry *nr;
7848 size_t len = *current_len;
7849 u8 *size_offset = (eid - len) + 1;
7850
7851 dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
7852 list) {
7853 if (!nr->nr || wpabuf_len(nr->nr) < 12)
7854 continue;
7855
7856 if (nr->short_ssid == hapd->conf->ssid.short_ssid)
7857 continue;
7858
7859 /* Start a new element */
7860 if (!len ||
7861 len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
7862 *eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
7863 size_offset = eid++;
7864 len = RNR_HEADER_LEN;
7865 }
7866
7867 /* TBTT Information Header subfield (2 octets) */
7868 *eid++ = 0;
7869 /* TBTT Information Length */
7870 *eid++ = RNR_TBTT_INFO_LEN;
7871 /* Operating Class */
7872 *eid++ = wpabuf_head_u8(nr->nr)[10];
7873 /* Channel Number */
7874 *eid++ = wpabuf_head_u8(nr->nr)[11];
7875 len += RNR_TBTT_HEADER_LEN;
7876 /* TBTT Information Set */
7877 /* TBTT Information field */
7878 /* Neighbor AP TBTT Offset */
7879 *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
7880 /* BSSID */
7881 os_memcpy(eid, nr->bssid, ETH_ALEN);
7882 eid += ETH_ALEN;
7883 /* Short SSID */
7884 os_memcpy(eid, &nr->short_ssid, 4);
7885 eid += 4;
7886 /* BSS parameters */
7887 *eid++ = nr->bss_parameters;
7888 /* 20 MHz PSD */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007889 *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
Hai Shaloma20dcd72022-02-04 13:43:00 -08007890 len += RNR_TBTT_INFO_LEN;
7891 *size_offset = (eid - size_offset) - 1;
7892 }
7893
7894 *current_len = len;
7895 return eid;
7896}
7897
7898
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007899static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
7900 struct hostapd_data *reporting_hapd,
7901 struct mbssid_ie_profiles *skip_profiles,
7902 size_t i, u8 *tbtt_count, size_t *len,
Sunil Ravi7f769292024-07-23 22:21:32 +00007903 u8 **pos, u8 **tbtt_count_pos, u8 tbtt_info_len,
7904 u8 op_class, bool mld_update)
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007905{
7906 struct hostapd_iface *iface = hapd->iface;
7907 struct hostapd_data *bss = iface->bss[i];
7908 u8 bss_param = 0;
7909 bool ap_mld = false;
7910 u8 *eid = *pos;
7911
7912#ifdef CONFIG_IEEE80211BE
7913 ap_mld = !!hapd->conf->mld_ap;
7914#endif /* CONFIG_IEEE80211BE */
7915
7916 if (!bss || !bss->conf || !bss->started ||
7917 bss == reporting_hapd || bss->conf->ignore_broadcast_ssid)
7918 return false;
7919
Sunil Ravi7f769292024-07-23 22:21:32 +00007920 if (hostapd_skip_rnr(i, skip_profiles, ap_mld, tbtt_info_len,
7921 mld_update, reporting_hapd, bss))
7922 return false;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007923
7924 if (*len + RNR_TBTT_INFO_LEN > 255 ||
7925 *tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
7926 return true;
7927
Sunil Ravi7f769292024-07-23 22:21:32 +00007928 if (!(*tbtt_count)) {
7929 /* Add neighbor report header info only if there is at least
7930 * one TBTT info available. */
7931 *tbtt_count_pos = eid++;
7932 *eid++ = tbtt_info_len;
7933 *eid++ = op_class;
7934 *eid++ = bss->iconf->channel;
7935 *len += RNR_TBTT_HEADER_LEN;
7936 }
7937
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007938 *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
7939 os_memcpy(eid, bss->own_addr, ETH_ALEN);
7940 eid += ETH_ALEN;
7941 os_memcpy(eid, &bss->conf->ssid.short_ssid, 4);
7942 eid += 4;
7943 if (bss->conf->ssid.short_ssid == reporting_hapd->conf->ssid.short_ssid)
7944 bss_param |= RNR_BSS_PARAM_SAME_SSID;
7945
7946 if (iface->conf->mbssid != MBSSID_DISABLED && iface->num_bss > 1) {
7947 bss_param |= RNR_BSS_PARAM_MULTIPLE_BSSID;
7948 if (bss == hostapd_mbssid_get_tx_bss(hapd))
7949 bss_param |= RNR_BSS_PARAM_TRANSMITTED_BSSID;
7950 }
7951
7952 if (is_6ghz_op_class(hapd->iconf->op_class) &&
7953 bss->conf->unsol_bcast_probe_resp_interval)
7954 bss_param |= RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE;
7955
7956 bss_param |= RNR_BSS_PARAM_CO_LOCATED;
7957
7958 *eid++ = bss_param;
7959 *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
7960
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007961#ifdef CONFIG_IEEE80211BE
Sunil Ravi7f769292024-07-23 22:21:32 +00007962 if (ap_mld) {
7963 u8 param_ch = bss->eht_mld_bss_param_change;
7964 bool is_partner;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007965
Sunil Ravi7f769292024-07-23 22:21:32 +00007966 /* If BSS is not a partner of the reporting_hapd
7967 * a) MLD ID advertised shall be 255.
7968 * b) Link ID advertised shall be 15.
7969 * c) BPCC advertised shall be 255 */
7970 is_partner = hostapd_is_ml_partner(bss, reporting_hapd);
7971 /* MLD ID */
7972 *eid++ = is_partner ? hostapd_get_mld_id(bss) : 0xFF;
7973 /* Link ID (Bit 3 to Bit 0)
7974 * BPCC (Bit 4 to Bit 7) */
7975 *eid++ = is_partner ?
7976 bss->mld_link_id | ((param_ch & 0xF) << 4) :
7977 (MAX_NUM_MLD_LINKS | 0xF0);
7978 /* BPCC (Bit 3 to Bit 0) */
7979 *eid = is_partner ? ((param_ch & 0xF0) >> 4) : 0x0F;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007980#ifdef CONFIG_TESTING_OPTIONS
Sunil Ravi7f769292024-07-23 22:21:32 +00007981 if (bss->conf->mld_indicate_disabled)
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007982 *eid |= RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
7983#endif /* CONFIG_TESTING_OPTIONS */
7984 eid++;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007985 }
Sunil Ravi7f769292024-07-23 22:21:32 +00007986#endif /* CONFIG_IEEE80211BE */
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007987
Sunil Ravi7f769292024-07-23 22:21:32 +00007988 *len += tbtt_info_len;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00007989 (*tbtt_count)++;
7990 *pos = eid;
7991
7992 return false;
7993}
7994
7995
Hai Shaloma20dcd72022-02-04 13:43:00 -08007996static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
7997 struct hostapd_data *reporting_hapd,
Sunil Ravi640215c2023-06-28 23:08:09 +00007998 u8 *eid, size_t *current_len,
Sunil Ravi7f769292024-07-23 22:21:32 +00007999 struct mbssid_ie_profiles *skip_profiles,
8000 bool mld_update)
Hai Shaloma20dcd72022-02-04 13:43:00 -08008001{
Hai Shaloma20dcd72022-02-04 13:43:00 -08008002 struct hostapd_iface *iface = hapd->iface;
Sunil Ravi7f769292024-07-23 22:21:32 +00008003 size_t i, start;
Hai Shaloma20dcd72022-02-04 13:43:00 -08008004 size_t len = *current_len;
Sunil Ravi7f769292024-07-23 22:21:32 +00008005 u8 *eid_start = eid, *size_offset = (eid - len) + 1;
8006 u8 *tbtt_count_pos = size_offset + 1;
8007 u8 tbtt_count, total_tbtt_count = 0, op_class, channel;
8008 u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN :
8009 RNR_TBTT_INFO_LEN;
Hai Shaloma20dcd72022-02-04 13:43:00 -08008010
8011 if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq)
8012 return eid;
8013
8014 if (ieee80211_freq_to_channel_ext(iface->freq,
8015 hapd->iconf->secondary_channel,
8016 hostapd_get_oper_chwidth(hapd->iconf),
8017 &op_class, &channel) ==
8018 NUM_HOSTAPD_MODES)
8019 return eid;
8020
Sunil Ravi7f769292024-07-23 22:21:32 +00008021repeat_rnr:
8022 start = 0;
8023 tbtt_count = 0;
Hai Shaloma20dcd72022-02-04 13:43:00 -08008024 while (start < iface->num_bss) {
8025 if (!len ||
Sunil Ravi7f769292024-07-23 22:21:32 +00008026 len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
Sunil Ravi2a14cf12023-11-21 00:54:38 +00008027 tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
Hai Shaloma20dcd72022-02-04 13:43:00 -08008028 eid_start = eid;
8029 *eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
8030 size_offset = eid++;
8031 len = RNR_HEADER_LEN;
8032 tbtt_count = 0;
8033 }
8034
Hai Shaloma20dcd72022-02-04 13:43:00 -08008035 for (i = start; i < iface->num_bss; i++) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008036 if (hostapd_eid_rnr_bss(hapd, reporting_hapd,
8037 skip_profiles, i,
Sunil Ravi7f769292024-07-23 22:21:32 +00008038 &tbtt_count, &len, &eid,
8039 &tbtt_count_pos, tbtt_info_len,
8040 op_class, mld_update))
Hai Shaloma20dcd72022-02-04 13:43:00 -08008041 break;
Hai Shaloma20dcd72022-02-04 13:43:00 -08008042 }
8043
8044 start = i;
Sunil Ravi7f769292024-07-23 22:21:32 +00008045
8046 if (tbtt_count) {
8047 *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
8048 *size_offset = (eid - size_offset) - 1;
8049 }
Hai Shaloma20dcd72022-02-04 13:43:00 -08008050 }
8051
Sunil Ravi7f769292024-07-23 22:21:32 +00008052 total_tbtt_count += tbtt_count;
8053
8054 /* If building for co-location, re-build again but this time include
8055 * ML TBTTs.
8056 */
8057 if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
8058 tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
8059 goto repeat_rnr;
8060 }
8061
8062 if (!total_tbtt_count)
Hai Shaloma20dcd72022-02-04 13:43:00 -08008063 return eid_start;
8064
8065 *current_len = len;
8066 return eid;
8067}
8068
8069
Sunil Ravi7f769292024-07-23 22:21:32 +00008070u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
8071 size_t *current_len)
Hai Shaloma20dcd72022-02-04 13:43:00 -08008072{
8073 struct hostapd_iface *iface;
8074 size_t i;
8075
8076 if (!hapd->iface || !hapd->iface->interfaces)
8077 return eid;
8078
8079 for (i = 0; i < hapd->iface->interfaces->count; i++) {
8080 iface = hapd->iface->interfaces->iface[i];
Sunil Ravi2a14cf12023-11-21 00:54:38 +00008081
Sunil Ravi7f769292024-07-23 22:21:32 +00008082 if (!iface || iface == hapd->iface ||
Sunil Ravi99c035e2024-07-12 01:42:03 +00008083 iface->state != HAPD_IFACE_ENABLED ||
Sunil Ravi7f769292024-07-23 22:21:32 +00008084 !is_6ghz_op_class(iface->conf->op_class))
Hai Shaloma20dcd72022-02-04 13:43:00 -08008085 continue;
8086
8087 eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
Sunil Ravi7f769292024-07-23 22:21:32 +00008088 current_len, NULL, false);
Hai Shaloma20dcd72022-02-04 13:43:00 -08008089 }
8090
8091 return eid;
8092}
8093
8094
Sunil Ravi7f769292024-07-23 22:21:32 +00008095u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
8096 u8 *eid, size_t *current_len)
8097{
8098#ifdef CONFIG_IEEE80211BE
8099 struct hostapd_iface *iface;
8100 size_t i;
8101
8102 if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap)
8103 return eid;
8104
8105 /* TODO: Allow for FILS/Action as well */
8106 if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
8107 return eid;
8108
8109 for (i = 0; i < hapd->iface->interfaces->count; i++) {
8110 iface = hapd->iface->interfaces->iface[i];
8111
8112 if (!iface || iface == hapd->iface ||
8113 hapd->iface->freq == iface->freq)
8114 continue;
8115
8116 eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
8117 current_len, NULL, true);
8118 }
8119#endif /* CONFIG_IEEE80211BE */
8120
8121 return eid;
8122}
8123
8124
8125u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type,
8126 bool include_mld_params)
Hai Shaloma20dcd72022-02-04 13:43:00 -08008127{
8128 u8 *eid_start = eid;
8129 size_t current_len = 0;
8130 enum colocation_mode mode = get_colocation_mode(hapd);
8131
8132 switch (type) {
8133 case WLAN_FC_STYPE_BEACON:
8134 if (hapd->conf->rnr)
8135 eid = hostapd_eid_nr_db(hapd, eid, &current_len);
8136 /* fallthrough */
Hai Shaloma20dcd72022-02-04 13:43:00 -08008137 case WLAN_FC_STYPE_PROBE_RESP:
Sunil Ravi7f769292024-07-23 22:21:32 +00008138 if (mode == COLOCATED_LOWER_BAND)
8139 eid = hostapd_eid_rnr_colocation(hapd, eid,
8140 &current_len);
Hai Shaloma20dcd72022-02-04 13:43:00 -08008141
Sunil Ravi640215c2023-06-28 23:08:09 +00008142 if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
8143 !hapd->iconf->mbssid)
Hai Shaloma20dcd72022-02-04 13:43:00 -08008144 eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
Sunil Ravi7f769292024-07-23 22:21:32 +00008145 &current_len, NULL, false);
Hai Shaloma20dcd72022-02-04 13:43:00 -08008146 break;
Hai Shaloma20dcd72022-02-04 13:43:00 -08008147 case WLAN_FC_STYPE_ACTION:
8148 if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008149 eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
Sunil Ravi7f769292024-07-23 22:21:32 +00008150 &current_len, NULL, false);
Hai Shaloma20dcd72022-02-04 13:43:00 -08008151 break;
Hai Shaloma20dcd72022-02-04 13:43:00 -08008152 default:
8153 return eid_start;
8154 }
8155
Sunil Ravi7f769292024-07-23 22:21:32 +00008156 /* For EMA Beacons, MLD neighbor repoting is added as part of
8157 * MBSSID RNR. */
8158 if (include_mld_params &&
8159 (type != WLAN_FC_STYPE_BEACON ||
8160 hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
8161 eid = hostapd_eid_rnr_mlo(hapd, type, eid, &current_len);
8162
Hai Shaloma20dcd72022-02-04 13:43:00 -08008163 if (eid == eid_start + 2)
8164 return eid_start;
8165
8166 return eid;
8167}
8168
Sunil Ravi77d572f2023-01-17 23:58:31 +00008169
8170static bool mbssid_known_bss(unsigned int i, const u8 *known_bss,
8171 size_t known_bss_len)
8172{
8173 if (!known_bss || known_bss_len <= i / 8)
8174 return false;
8175 known_bss = &known_bss[i / 8];
8176 return *known_bss & (u8) (BIT(i % 8));
8177}
8178
8179
Sunil Ravi99c035e2024-07-12 01:42:03 +00008180static size_t hostapd_mbssid_ext_capa(struct hostapd_data *bss,
8181 struct hostapd_data *tx_bss, u8 *buf)
8182{
8183 u8 ext_capa_tx[20], *ext_capa_tx_end, ext_capa[20], *ext_capa_end;
8184 size_t ext_capa_len, ext_capa_tx_len;
8185
8186 ext_capa_tx_end = hostapd_eid_ext_capab(tx_bss, ext_capa_tx,
8187 true);
8188 ext_capa_tx_len = ext_capa_tx_end - ext_capa_tx;
8189 ext_capa_end = hostapd_eid_ext_capab(bss, ext_capa, true);
8190 ext_capa_len = ext_capa_end - ext_capa;
8191 if (ext_capa_tx_len != ext_capa_len ||
8192 os_memcmp(ext_capa_tx, ext_capa, ext_capa_len) != 0) {
8193 os_memcpy(buf, ext_capa, ext_capa_len);
8194 return ext_capa_len;
8195 }
8196
8197 return 0;
8198}
8199
8200
Sunil Ravi77d572f2023-01-17 23:58:31 +00008201static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
8202 u32 frame_type, size_t *bss_index,
8203 const u8 *known_bss,
8204 size_t known_bss_len)
8205{
8206 struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008207 size_t len, i;
Sunil Ravi99c035e2024-07-12 01:42:03 +00008208 u8 ext_capa[20];
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008209
8210 /* Element ID: 1 octet
8211 * Length: 1 octet
8212 * MaxBSSID Indicator: 1 octet
8213 * Optional Subelements: vatiable
8214 *
8215 * Total fixed length: 3 octets
8216 *
8217 * 1 octet in len for the MaxBSSID Indicator field.
8218 */
8219 len = 1;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008220
8221 for (i = *bss_index; i < hapd->iface->num_bss; i++) {
8222 struct hostapd_data *bss = hapd->iface->bss[i];
8223 const u8 *auth, *rsn = NULL, *rsnx = NULL;
8224 size_t nontx_profile_len, auth_len;
8225 u8 ie_count = 0;
8226
8227 if (!bss || !bss->conf || !bss->started ||
8228 mbssid_known_bss(i, known_bss, known_bss_len))
8229 continue;
8230
8231 /*
8232 * Sublement ID: 1 octet
8233 * Length: 1 octet
8234 * Nontransmitted capabilities: 4 octets
8235 * SSID element: 2 + variable
8236 * Multiple BSSID Index Element: 3 octets (+2 octets in beacons)
8237 * Fixed length = 1 + 1 + 4 + 2 + 3 = 11
8238 */
8239 nontx_profile_len = 11 + bss->conf->ssid.ssid_len;
8240
8241 if (frame_type == WLAN_FC_STYPE_BEACON)
8242 nontx_profile_len += 2;
8243
8244 auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
8245 if (auth) {
8246 rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
8247 if (rsn)
8248 nontx_profile_len += 2 + rsn[1];
8249
8250 rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
8251 if (rsnx)
8252 nontx_profile_len += 2 + rsnx[1];
8253 }
Sunil Ravi99c035e2024-07-12 01:42:03 +00008254
8255 nontx_profile_len += hostapd_mbssid_ext_capa(bss, tx_bss,
8256 ext_capa);
8257
Sunil Ravi77d572f2023-01-17 23:58:31 +00008258 if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
8259 ie_count++;
8260 if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
8261 ie_count++;
8262 if (bss->conf->xrates_supported)
8263 nontx_profile_len += 8;
8264 else if (hapd->conf->xrates_supported)
8265 ie_count++;
8266 if (ie_count)
8267 nontx_profile_len += 4 + ie_count;
8268
8269 if (len + nontx_profile_len > 255)
8270 break;
8271
8272 len += nontx_profile_len;
8273 }
8274
8275 *bss_index = i;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008276
8277 /* Add 2 octets to get the full size of the element */
8278 return len + 2;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008279}
8280
8281
8282size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
8283 u8 *elem_count, const u8 *known_bss,
Sunil Ravi640215c2023-06-28 23:08:09 +00008284 size_t known_bss_len, size_t *rnr_len)
Sunil Ravi77d572f2023-01-17 23:58:31 +00008285{
8286 size_t len = 0, bss_index = 1;
Sunil Ravi7f769292024-07-23 22:21:32 +00008287 bool ap_mld = false;
8288
8289#ifdef CONFIG_IEEE80211BE
8290 ap_mld = hapd->conf->mld_ap;
8291#endif /* CONFIG_IEEE80211BE */
Sunil Ravi77d572f2023-01-17 23:58:31 +00008292
8293 if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
8294 (frame_type != WLAN_FC_STYPE_BEACON &&
8295 frame_type != WLAN_FC_STYPE_PROBE_RESP))
8296 return 0;
8297
8298 if (frame_type == WLAN_FC_STYPE_BEACON) {
8299 if (!elem_count) {
8300 wpa_printf(MSG_INFO,
8301 "MBSSID: Insufficient data for Beacon frames");
8302 return 0;
8303 }
8304 *elem_count = 0;
8305 }
8306
8307 while (bss_index < hapd->iface->num_bss) {
Sunil Ravi640215c2023-06-28 23:08:09 +00008308 size_t rnr_count = bss_index;
8309
Sunil Ravi77d572f2023-01-17 23:58:31 +00008310 len += hostapd_eid_mbssid_elem_len(hapd, frame_type,
8311 &bss_index, known_bss,
8312 known_bss_len);
8313
8314 if (frame_type == WLAN_FC_STYPE_BEACON)
8315 *elem_count += 1;
Sunil Ravi640215c2023-06-28 23:08:09 +00008316 if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && rnr_len) {
8317 size_t rnr_cur_len = 0;
8318 struct mbssid_ie_profiles skip_profiles = {
8319 rnr_count, bss_index
8320 };
8321
8322 *rnr_len += hostapd_eid_rnr_iface_len(
8323 hapd, hostapd_mbssid_get_tx_bss(hapd),
Sunil Ravi7f769292024-07-23 22:21:32 +00008324 &rnr_cur_len, &skip_profiles, ap_mld);
Sunil Ravi640215c2023-06-28 23:08:09 +00008325 }
Sunil Ravi77d572f2023-01-17 23:58:31 +00008326 }
Sunil Ravi640215c2023-06-28 23:08:09 +00008327
8328 if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && rnr_len)
Sunil Ravi7f769292024-07-23 22:21:32 +00008329 *rnr_len += hostapd_eid_rnr_len(hapd, frame_type, false);
Sunil Ravi640215c2023-06-28 23:08:09 +00008330
Sunil Ravi77d572f2023-01-17 23:58:31 +00008331 return len;
8332}
8333
8334
8335static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
8336 u32 frame_type, u8 max_bssid_indicator,
8337 size_t *bss_index, u8 elem_count,
8338 const u8 *known_bss, size_t known_bss_len)
8339{
8340 struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
8341 size_t i;
8342 u8 *eid_len_offset, *max_bssid_indicator_offset;
8343
8344 *eid++ = WLAN_EID_MULTIPLE_BSSID;
8345 eid_len_offset = eid++;
8346 max_bssid_indicator_offset = eid++;
8347
8348 for (i = *bss_index; i < hapd->iface->num_bss; i++) {
8349 struct hostapd_data *bss = hapd->iface->bss[i];
8350 struct hostapd_bss_config *conf;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00008351 struct hostapd_bss_config *tx_conf = tx_bss->conf;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008352 u8 *eid_len_pos, *nontx_bss_start = eid;
8353 const u8 *auth, *rsn = NULL, *rsnx = NULL;
8354 u8 ie_count = 0, non_inherit_ie[3];
8355 size_t auth_len = 0;
8356 u16 capab_info;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00008357 u8 mbssindex = i;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008358
8359 if (!bss || !bss->conf || !bss->started ||
8360 mbssid_known_bss(i, known_bss, known_bss_len))
8361 continue;
8362 conf = bss->conf;
8363
8364 *eid++ = WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE;
8365 eid_len_pos = eid++;
8366
8367 capab_info = hostapd_own_capab_info(bss);
8368 *eid++ = WLAN_EID_NONTRANSMITTED_BSSID_CAPA;
8369 *eid++ = sizeof(capab_info);
8370 WPA_PUT_LE16(eid, capab_info);
8371 eid += sizeof(capab_info);
8372
8373 *eid++ = WLAN_EID_SSID;
8374 *eid++ = conf->ssid.ssid_len;
8375 os_memcpy(eid, conf->ssid.ssid, conf->ssid.ssid_len);
8376 eid += conf->ssid.ssid_len;
8377
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00008378 if (conf->mbssid_index &&
8379 conf->mbssid_index > tx_conf->mbssid_index)
8380 mbssindex = conf->mbssid_index - tx_conf->mbssid_index;
8381
Sunil Ravi77d572f2023-01-17 23:58:31 +00008382 *eid++ = WLAN_EID_MULTIPLE_BSSID_INDEX;
8383 if (frame_type == WLAN_FC_STYPE_BEACON) {
8384 *eid++ = 3;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00008385 *eid++ = mbssindex; /* BSSID Index */
Sunil Ravi77d572f2023-01-17 23:58:31 +00008386 if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
8387 (conf->dtim_period % elem_count))
8388 conf->dtim_period = elem_count;
8389 *eid++ = conf->dtim_period;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00008390 /* The driver is expected to update the DTIM Count
8391 * field for each BSS that corresponds to a
8392 * nontransmitted BSSID. The value is initialized to
8393 * 0 here so that the DTIM count would be somewhat
8394 * functional even if the driver were not to update
8395 * this. */
8396 *eid++ = 0; /* DTIM Count */
Sunil Ravi77d572f2023-01-17 23:58:31 +00008397 } else {
8398 /* Probe Request frame does not include DTIM Period and
8399 * DTIM Count fields. */
8400 *eid++ = 1;
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00008401 *eid++ = mbssindex; /* BSSID Index */
Sunil Ravi77d572f2023-01-17 23:58:31 +00008402 }
8403
8404 auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
8405 if (auth) {
8406 rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
8407 if (rsn) {
8408 os_memcpy(eid, rsn, 2 + rsn[1]);
8409 eid += 2 + rsn[1];
8410 }
8411
8412 rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
8413 if (rsnx) {
8414 os_memcpy(eid, rsnx, 2 + rsnx[1]);
8415 eid += 2 + rsnx[1];
8416 }
8417 }
Sunil Ravi99c035e2024-07-12 01:42:03 +00008418
8419 eid += hostapd_mbssid_ext_capa(bss, tx_bss, eid);
8420
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008421 /* List of Element ID values in increasing order */
Sunil Ravi77d572f2023-01-17 23:58:31 +00008422 if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
8423 non_inherit_ie[ie_count++] = WLAN_EID_RSN;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008424 if (hapd->conf->xrates_supported &&
8425 !bss->conf->xrates_supported)
8426 non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00008427 if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
8428 non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008429 if (ie_count) {
8430 *eid++ = WLAN_EID_EXTENSION;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00008431 *eid++ = 2 + ie_count + 1;
Sunil Ravi77d572f2023-01-17 23:58:31 +00008432 *eid++ = WLAN_EID_EXT_NON_INHERITANCE;
8433 *eid++ = ie_count;
8434 os_memcpy(eid, non_inherit_ie, ie_count);
8435 eid += ie_count;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00008436 *eid++ = 0; /* No Element ID Extension List */
Sunil Ravi77d572f2023-01-17 23:58:31 +00008437 }
8438
8439 *eid_len_pos = (eid - eid_len_pos) - 1;
8440
8441 if (((eid - eid_len_offset) - 1) > 255) {
8442 eid = nontx_bss_start;
8443 break;
8444 }
8445 }
8446
8447 *bss_index = i;
8448 *max_bssid_indicator_offset = max_bssid_indicator;
8449 if (*max_bssid_indicator_offset < 1)
8450 *max_bssid_indicator_offset = 1;
8451 *eid_len_offset = (eid - eid_len_offset) - 1;
8452 return eid;
8453}
8454
8455
8456u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
8457 unsigned int frame_stype, u8 elem_count,
8458 u8 **elem_offset,
Sunil Ravi640215c2023-06-28 23:08:09 +00008459 const u8 *known_bss, size_t known_bss_len, u8 *rnr_eid,
8460 u8 *rnr_count, u8 **rnr_offset, size_t rnr_len)
Sunil Ravi77d572f2023-01-17 23:58:31 +00008461{
Sunil Ravi640215c2023-06-28 23:08:09 +00008462 size_t bss_index = 1, cur_len = 0;
8463 u8 elem_index = 0, *rnr_start_eid = rnr_eid;
Sunil Ravi7f769292024-07-23 22:21:32 +00008464 bool add_rnr, ap_mld = false;
8465
8466#ifdef CONFIG_IEEE80211BE
8467 ap_mld = hapd->conf->mld_ap;
8468#endif /* CONFIG_IEEE80211BE */
Sunil Ravi77d572f2023-01-17 23:58:31 +00008469
8470 if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
8471 (frame_stype != WLAN_FC_STYPE_BEACON &&
8472 frame_stype != WLAN_FC_STYPE_PROBE_RESP))
8473 return eid;
8474
8475 if (frame_stype == WLAN_FC_STYPE_BEACON && !elem_offset) {
8476 wpa_printf(MSG_INFO,
8477 "MBSSID: Insufficient data for Beacon frames");
8478 return eid;
8479 }
8480
Sunil Ravi640215c2023-06-28 23:08:09 +00008481 add_rnr = hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
8482 frame_stype == WLAN_FC_STYPE_BEACON &&
8483 rnr_eid && rnr_count && rnr_offset && rnr_len;
8484
Sunil Ravi77d572f2023-01-17 23:58:31 +00008485 while (bss_index < hapd->iface->num_bss) {
Sunil Ravi640215c2023-06-28 23:08:09 +00008486 unsigned int rnr_start_count = bss_index;
8487
Sunil Ravi77d572f2023-01-17 23:58:31 +00008488 if (frame_stype == WLAN_FC_STYPE_BEACON) {
8489 if (elem_index == elem_count) {
8490 wpa_printf(MSG_WARNING,
8491 "MBSSID: Larger number of elements than there is room in the provided array");
8492 break;
8493 }
8494
8495 elem_offset[elem_index] = eid;
8496 elem_index = elem_index + 1;
8497 }
8498 eid = hostapd_eid_mbssid_elem(hapd, eid, end, frame_stype,
8499 hostapd_max_bssid_indicator(hapd),
8500 &bss_index, elem_count,
8501 known_bss, known_bss_len);
Sunil Ravi640215c2023-06-28 23:08:09 +00008502
8503 if (add_rnr) {
8504 struct mbssid_ie_profiles skip_profiles = {
8505 rnr_start_count, bss_index
8506 };
8507
8508 rnr_offset[*rnr_count] = rnr_eid;
8509 *rnr_count = *rnr_count + 1;
8510 cur_len = 0;
8511 rnr_eid = hostapd_eid_rnr_iface(
8512 hapd, hostapd_mbssid_get_tx_bss(hapd),
Sunil Ravi7f769292024-07-23 22:21:32 +00008513 rnr_eid, &cur_len, &skip_profiles, ap_mld);
Sunil Ravi640215c2023-06-28 23:08:09 +00008514 }
8515 }
8516
8517 if (add_rnr && (size_t) (rnr_eid - rnr_start_eid) < rnr_len) {
8518 rnr_offset[*rnr_count] = rnr_eid;
8519 *rnr_count = *rnr_count + 1;
8520 cur_len = 0;
8521
8522 if (hapd->conf->rnr)
8523 rnr_eid = hostapd_eid_nr_db(hapd, rnr_eid, &cur_len);
8524 if (get_colocation_mode(hapd) == COLOCATED_LOWER_BAND)
Sunil Ravi7f769292024-07-23 22:21:32 +00008525 rnr_eid = hostapd_eid_rnr_colocation(hapd, rnr_eid,
8526 &cur_len);
Sunil Ravi77d572f2023-01-17 23:58:31 +00008527 }
8528
8529 return eid;
8530}
8531
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07008532#endif /* CONFIG_NATIVE_WINDOWS */