xref: /freebsd/contrib/wpa/src/ap/ieee802_1x.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
1e28a4053SRui Paulo /*
2e28a4053SRui Paulo  * hostapd / IEEE 802.1X-2004 Authenticator
34bc52338SCy Schubert  * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
4e28a4053SRui Paulo  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
7e28a4053SRui Paulo  */
8e28a4053SRui Paulo 
9e28a4053SRui Paulo #include "utils/includes.h"
10206b73d0SCy Schubert #ifdef CONFIG_SQLITE
11206b73d0SCy Schubert #include <sqlite3.h>
12206b73d0SCy Schubert #endif /* CONFIG_SQLITE */
13e28a4053SRui Paulo 
14e28a4053SRui Paulo #include "utils/common.h"
15e28a4053SRui Paulo #include "utils/eloop.h"
16e28a4053SRui Paulo #include "crypto/md5.h"
17e28a4053SRui Paulo #include "crypto/crypto.h"
18f05cddf9SRui Paulo #include "crypto/random.h"
19e28a4053SRui Paulo #include "common/ieee802_11_defs.h"
20e28a4053SRui Paulo #include "radius/radius.h"
21e28a4053SRui Paulo #include "radius/radius_client.h"
22e28a4053SRui Paulo #include "eap_server/eap.h"
23e28a4053SRui Paulo #include "eap_common/eap_wsc_common.h"
24e28a4053SRui Paulo #include "eapol_auth/eapol_auth_sm.h"
25e28a4053SRui Paulo #include "eapol_auth/eapol_auth_sm_i.h"
26f05cddf9SRui Paulo #include "p2p/p2p.h"
27e28a4053SRui Paulo #include "hostapd.h"
28e28a4053SRui Paulo #include "accounting.h"
29e28a4053SRui Paulo #include "sta_info.h"
30e28a4053SRui Paulo #include "wpa_auth.h"
31e28a4053SRui Paulo #include "preauth_auth.h"
32e28a4053SRui Paulo #include "pmksa_cache_auth.h"
33e28a4053SRui Paulo #include "ap_config.h"
34f05cddf9SRui Paulo #include "ap_drv_ops.h"
355b9c547cSRui Paulo #include "wps_hostapd.h"
365b9c547cSRui Paulo #include "hs20.h"
3785732ac8SCy Schubert /* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */
3885732ac8SCy Schubert #include "ieee802_11.h"
39e28a4053SRui Paulo #include "ieee802_1x.h"
40206b73d0SCy Schubert #include "wpa_auth_kay.h"
41e28a4053SRui Paulo 
42e28a4053SRui Paulo 
43780fb4a2SCy Schubert #ifdef CONFIG_HS20
44780fb4a2SCy Schubert static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx);
45780fb4a2SCy Schubert #endif /* CONFIG_HS20 */
46*a90b9d01SCy Schubert static bool ieee802_1x_finished(struct hostapd_data *hapd,
475b9c547cSRui Paulo 				struct sta_info *sta, int success,
48*a90b9d01SCy Schubert 				int remediation, bool logoff);
49e28a4053SRui Paulo 
50e28a4053SRui Paulo 
ieee802_1x_send(struct hostapd_data * hapd,struct sta_info * sta,u8 type,const u8 * data,size_t datalen)51e28a4053SRui Paulo static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
52e28a4053SRui Paulo 			    u8 type, const u8 *data, size_t datalen)
53e28a4053SRui Paulo {
54e28a4053SRui Paulo 	u8 *buf;
55e28a4053SRui Paulo 	struct ieee802_1x_hdr *xhdr;
56e28a4053SRui Paulo 	size_t len;
57e28a4053SRui Paulo 	int encrypt = 0;
58e28a4053SRui Paulo 
59e28a4053SRui Paulo 	len = sizeof(*xhdr) + datalen;
60e28a4053SRui Paulo 	buf = os_zalloc(len);
61c1d255d3SCy Schubert 	if (!buf) {
62c1d255d3SCy Schubert 		wpa_printf(MSG_ERROR, "malloc() failed for %s(len=%lu)",
63c1d255d3SCy Schubert 			   __func__, (unsigned long) len);
64e28a4053SRui Paulo 		return;
65e28a4053SRui Paulo 	}
66e28a4053SRui Paulo 
67e28a4053SRui Paulo 	xhdr = (struct ieee802_1x_hdr *) buf;
68e28a4053SRui Paulo 	xhdr->version = hapd->conf->eapol_version;
69206b73d0SCy Schubert #ifdef CONFIG_MACSEC
70206b73d0SCy Schubert 	if (xhdr->version > 2 && hapd->conf->macsec_policy == 0)
71206b73d0SCy Schubert 		xhdr->version = 2;
72206b73d0SCy Schubert #endif /* CONFIG_MACSEC */
73e28a4053SRui Paulo 	xhdr->type = type;
74e28a4053SRui Paulo 	xhdr->length = host_to_be16(datalen);
75e28a4053SRui Paulo 
76e28a4053SRui Paulo 	if (datalen > 0 && data != NULL)
77e28a4053SRui Paulo 		os_memcpy(xhdr + 1, data, datalen);
78e28a4053SRui Paulo 
79e28a4053SRui Paulo 	if (wpa_auth_pairwise_set(sta->wpa_sm))
80e28a4053SRui Paulo 		encrypt = 1;
815b9c547cSRui Paulo #ifdef CONFIG_TESTING_OPTIONS
825b9c547cSRui Paulo 	if (hapd->ext_eapol_frame_io) {
835b9c547cSRui Paulo 		size_t hex_len = 2 * len + 1;
845b9c547cSRui Paulo 		char *hex = os_malloc(hex_len);
855b9c547cSRui Paulo 
865b9c547cSRui Paulo 		if (hex) {
875b9c547cSRui Paulo 			wpa_snprintf_hex(hex, hex_len, buf, len);
885b9c547cSRui Paulo 			wpa_msg(hapd->msg_ctx, MSG_INFO,
895b9c547cSRui Paulo 				"EAPOL-TX " MACSTR " %s",
905b9c547cSRui Paulo 				MAC2STR(sta->addr), hex);
915b9c547cSRui Paulo 			os_free(hex);
925b9c547cSRui Paulo 		}
935b9c547cSRui Paulo 	} else
945b9c547cSRui Paulo #endif /* CONFIG_TESTING_OPTIONS */
95e28a4053SRui Paulo 	if (sta->flags & WLAN_STA_PREAUTH) {
96e28a4053SRui Paulo 		rsn_preauth_send(hapd, sta, buf, len);
97e28a4053SRui Paulo 	} else {
98*a90b9d01SCy Schubert 		int link_id = -1;
99*a90b9d01SCy Schubert 
100*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
101*a90b9d01SCy Schubert 		link_id = hapd->conf->mld_ap ? hapd->mld_link_id : -1;
102*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
103f05cddf9SRui Paulo 		hostapd_drv_hapd_send_eapol(
104f05cddf9SRui Paulo 			hapd, sta->addr, buf, len,
105*a90b9d01SCy Schubert 			encrypt, hostapd_sta_flags_to_drv(sta->flags), link_id);
106e28a4053SRui Paulo 	}
107e28a4053SRui Paulo 
108e28a4053SRui Paulo 	os_free(buf);
109e28a4053SRui Paulo }
110e28a4053SRui Paulo 
111e28a4053SRui Paulo 
ieee802_1x_set_authorized(struct hostapd_data * hapd,struct sta_info * sta,bool authorized,bool mld)112*a90b9d01SCy Schubert static void ieee802_1x_set_authorized(struct hostapd_data *hapd,
113*a90b9d01SCy Schubert 				      struct sta_info *sta,
114*a90b9d01SCy Schubert 				      bool authorized, bool mld)
115e28a4053SRui Paulo {
116e28a4053SRui Paulo 	int res;
117*a90b9d01SCy Schubert 	bool update;
118e28a4053SRui Paulo 
119e28a4053SRui Paulo 	if (sta->flags & WLAN_STA_PREAUTH)
120e28a4053SRui Paulo 		return;
121e28a4053SRui Paulo 
122*a90b9d01SCy Schubert 	update = ap_sta_set_authorized_flag(hapd, sta, authorized);
123*a90b9d01SCy Schubert 	res = hostapd_set_authorized(hapd, sta, authorized);
124*a90b9d01SCy Schubert 	if (update)
125*a90b9d01SCy Schubert 		ap_sta_set_authorized_event(hapd, sta, authorized);
126e28a4053SRui Paulo 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
127*a90b9d01SCy Schubert 		       HOSTAPD_LEVEL_DEBUG, "%sauthorizing port",
128*a90b9d01SCy Schubert 		       authorized ? "" : "un");
129e28a4053SRui Paulo 
130*a90b9d01SCy Schubert 	if (!mld && res && errno != ENOENT) {
1315b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "Could not set station " MACSTR
1325b9c547cSRui Paulo 			   " flags for kernel driver (errno=%d).",
1335b9c547cSRui Paulo 			   MAC2STR(sta->addr), errno);
134*a90b9d01SCy Schubert 	} else if (mld && res) {
135*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
136*a90b9d01SCy Schubert 			   "MLD: Could not set station " MACSTR " flags",
137*a90b9d01SCy Schubert 			   MAC2STR(sta->addr));
138e28a4053SRui Paulo 	}
139e28a4053SRui Paulo 
140f05cddf9SRui Paulo 	if (authorized) {
1415b9c547cSRui Paulo 		os_get_reltime(&sta->connected_time);
142e28a4053SRui Paulo 		accounting_sta_start(hapd, sta);
143e28a4053SRui Paulo 	}
144f05cddf9SRui Paulo }
145e28a4053SRui Paulo 
146e28a4053SRui Paulo 
ieee802_1x_ml_set_sta_authorized(struct hostapd_data * hapd,struct sta_info * sta,bool authorized)147*a90b9d01SCy Schubert static void ieee802_1x_ml_set_sta_authorized(struct hostapd_data *hapd,
148*a90b9d01SCy Schubert 					     struct sta_info *sta,
149*a90b9d01SCy Schubert 					     bool authorized)
150*a90b9d01SCy Schubert {
151*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
152*a90b9d01SCy Schubert 	unsigned int i, link_id;
153*a90b9d01SCy Schubert 
154*a90b9d01SCy Schubert 	if (!hostapd_is_mld_ap(hapd))
155*a90b9d01SCy Schubert 		return;
156*a90b9d01SCy Schubert 
157*a90b9d01SCy Schubert 	/*
158*a90b9d01SCy Schubert 	 * Authorizing the station should be done only in the station
159*a90b9d01SCy Schubert 	 * performing the association
160*a90b9d01SCy Schubert 	 */
161*a90b9d01SCy Schubert 	if (authorized && hapd->mld_link_id != sta->mld_assoc_link_id)
162*a90b9d01SCy Schubert 		return;
163*a90b9d01SCy Schubert 
164*a90b9d01SCy Schubert 	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
165*a90b9d01SCy Schubert 		struct mld_link_info *link = &sta->mld_info.links[link_id];
166*a90b9d01SCy Schubert 
167*a90b9d01SCy Schubert 		if (!link->valid)
168*a90b9d01SCy Schubert 			continue;
169*a90b9d01SCy Schubert 
170*a90b9d01SCy Schubert 		for (i = 0; i < hapd->iface->interfaces->count; i++) {
171*a90b9d01SCy Schubert 			struct sta_info *tmp_sta;
172*a90b9d01SCy Schubert 			struct hostapd_data *tmp_hapd =
173*a90b9d01SCy Schubert 				hapd->iface->interfaces->iface[i]->bss[0];
174*a90b9d01SCy Schubert 
175*a90b9d01SCy Schubert 			if (!hostapd_is_ml_partner(hapd, tmp_hapd))
176*a90b9d01SCy Schubert 				continue;
177*a90b9d01SCy Schubert 
178*a90b9d01SCy Schubert 			for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
179*a90b9d01SCy Schubert 			     tmp_sta = tmp_sta->next) {
180*a90b9d01SCy Schubert 				if (tmp_sta == sta ||
181*a90b9d01SCy Schubert 				    tmp_sta->mld_assoc_link_id !=
182*a90b9d01SCy Schubert 				    sta->mld_assoc_link_id ||
183*a90b9d01SCy Schubert 				    tmp_sta->aid != sta->aid)
184*a90b9d01SCy Schubert 					continue;
185*a90b9d01SCy Schubert 
186*a90b9d01SCy Schubert 				ieee802_1x_set_authorized(tmp_hapd, tmp_sta,
187*a90b9d01SCy Schubert 							  authorized, true);
188*a90b9d01SCy Schubert 				break;
189*a90b9d01SCy Schubert 			}
190*a90b9d01SCy Schubert 		}
191*a90b9d01SCy Schubert 	}
192*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
193*a90b9d01SCy Schubert }
194*a90b9d01SCy Schubert 
195*a90b9d01SCy Schubert 
196*a90b9d01SCy Schubert 
ieee802_1x_set_sta_authorized(struct hostapd_data * hapd,struct sta_info * sta,int authorized)197*a90b9d01SCy Schubert void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
198*a90b9d01SCy Schubert 				   struct sta_info *sta, int authorized)
199*a90b9d01SCy Schubert {
200*a90b9d01SCy Schubert 	ieee802_1x_set_authorized(hapd, sta, authorized, false);
201*a90b9d01SCy Schubert 	ieee802_1x_ml_set_sta_authorized(hapd, sta, !!authorized);
202*a90b9d01SCy Schubert }
203*a90b9d01SCy Schubert 
204*a90b9d01SCy Schubert 
205c1d255d3SCy Schubert #ifdef CONFIG_WEP
206325151a3SRui Paulo #ifndef CONFIG_FIPS
207325151a3SRui Paulo #ifndef CONFIG_NO_RC4
208325151a3SRui Paulo 
ieee802_1x_tx_key_one(struct hostapd_data * hapd,struct sta_info * sta,int idx,int broadcast,u8 * key_data,size_t key_len)209e28a4053SRui Paulo static void ieee802_1x_tx_key_one(struct hostapd_data *hapd,
210e28a4053SRui Paulo 				  struct sta_info *sta,
211e28a4053SRui Paulo 				  int idx, int broadcast,
212e28a4053SRui Paulo 				  u8 *key_data, size_t key_len)
213e28a4053SRui Paulo {
214e28a4053SRui Paulo 	u8 *buf, *ekey;
215e28a4053SRui Paulo 	struct ieee802_1x_hdr *hdr;
216e28a4053SRui Paulo 	struct ieee802_1x_eapol_key *key;
217e28a4053SRui Paulo 	size_t len, ekey_len;
218e28a4053SRui Paulo 	struct eapol_state_machine *sm = sta->eapol_sm;
219e28a4053SRui Paulo 
220c1d255d3SCy Schubert 	if (!sm)
221e28a4053SRui Paulo 		return;
222e28a4053SRui Paulo 
223e28a4053SRui Paulo 	len = sizeof(*key) + key_len;
224e28a4053SRui Paulo 	buf = os_zalloc(sizeof(*hdr) + len);
225c1d255d3SCy Schubert 	if (!buf)
226e28a4053SRui Paulo 		return;
227e28a4053SRui Paulo 
228e28a4053SRui Paulo 	hdr = (struct ieee802_1x_hdr *) buf;
229e28a4053SRui Paulo 	key = (struct ieee802_1x_eapol_key *) (hdr + 1);
230e28a4053SRui Paulo 	key->type = EAPOL_KEY_TYPE_RC4;
231f05cddf9SRui Paulo 	WPA_PUT_BE16(key->key_length, key_len);
232e28a4053SRui Paulo 	wpa_get_ntp_timestamp(key->replay_counter);
233206b73d0SCy Schubert 	if (os_memcmp(key->replay_counter,
234206b73d0SCy Schubert 		      hapd->last_1x_eapol_key_replay_counter,
235206b73d0SCy Schubert 		      IEEE8021X_REPLAY_COUNTER_LEN) <= 0) {
236206b73d0SCy Schubert 		/* NTP timestamp did not increment from last EAPOL-Key frame;
237206b73d0SCy Schubert 		 * use previously used value + 1 instead. */
238206b73d0SCy Schubert 		inc_byte_array(hapd->last_1x_eapol_key_replay_counter,
239206b73d0SCy Schubert 			       IEEE8021X_REPLAY_COUNTER_LEN);
240206b73d0SCy Schubert 		os_memcpy(key->replay_counter,
241206b73d0SCy Schubert 			  hapd->last_1x_eapol_key_replay_counter,
242206b73d0SCy Schubert 			  IEEE8021X_REPLAY_COUNTER_LEN);
243206b73d0SCy Schubert 	} else {
244206b73d0SCy Schubert 		os_memcpy(hapd->last_1x_eapol_key_replay_counter,
245206b73d0SCy Schubert 			  key->replay_counter,
246206b73d0SCy Schubert 			  IEEE8021X_REPLAY_COUNTER_LEN);
247206b73d0SCy Schubert 	}
248e28a4053SRui Paulo 
249f05cddf9SRui Paulo 	if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) {
250e28a4053SRui Paulo 		wpa_printf(MSG_ERROR, "Could not get random numbers");
251e28a4053SRui Paulo 		os_free(buf);
252e28a4053SRui Paulo 		return;
253e28a4053SRui Paulo 	}
254e28a4053SRui Paulo 
255e28a4053SRui Paulo 	key->key_index = idx | (broadcast ? 0 : BIT(7));
256e28a4053SRui Paulo 	if (hapd->conf->eapol_key_index_workaround) {
257e28a4053SRui Paulo 		/* According to some information, WinXP Supplicant seems to
258e28a4053SRui Paulo 		 * interpret bit7 as an indication whether the key is to be
259e28a4053SRui Paulo 		 * activated, so make it possible to enable workaround that
260e28a4053SRui Paulo 		 * sets this bit for all keys. */
261e28a4053SRui Paulo 		key->key_index |= BIT(7);
262e28a4053SRui Paulo 	}
263e28a4053SRui Paulo 
264e28a4053SRui Paulo 	/* Key is encrypted using "Key-IV + MSK[0..31]" as the RC4-key and
265e28a4053SRui Paulo 	 * MSK[32..63] is used to sign the message. */
266c1d255d3SCy Schubert 	if (!sm->eap_if->eapKeyData || sm->eap_if->eapKeyDataLen < 64) {
267c1d255d3SCy Schubert 		wpa_printf(MSG_ERROR,
268c1d255d3SCy Schubert 			   "No eapKeyData available for encrypting and signing EAPOL-Key");
269e28a4053SRui Paulo 		os_free(buf);
270e28a4053SRui Paulo 		return;
271e28a4053SRui Paulo 	}
272e28a4053SRui Paulo 	os_memcpy((u8 *) (key + 1), key_data, key_len);
273e28a4053SRui Paulo 	ekey_len = sizeof(key->key_iv) + 32;
274e28a4053SRui Paulo 	ekey = os_malloc(ekey_len);
275c1d255d3SCy Schubert 	if (!ekey) {
276e28a4053SRui Paulo 		wpa_printf(MSG_ERROR, "Could not encrypt key");
277e28a4053SRui Paulo 		os_free(buf);
278e28a4053SRui Paulo 		return;
279e28a4053SRui Paulo 	}
280e28a4053SRui Paulo 	os_memcpy(ekey, key->key_iv, sizeof(key->key_iv));
281e28a4053SRui Paulo 	os_memcpy(ekey + sizeof(key->key_iv), sm->eap_if->eapKeyData, 32);
282e28a4053SRui Paulo 	rc4_skip(ekey, ekey_len, 0, (u8 *) (key + 1), key_len);
283e28a4053SRui Paulo 	os_free(ekey);
284e28a4053SRui Paulo 
285e28a4053SRui Paulo 	/* This header is needed here for HMAC-MD5, but it will be regenerated
286e28a4053SRui Paulo 	 * in ieee802_1x_send() */
287e28a4053SRui Paulo 	hdr->version = hapd->conf->eapol_version;
288206b73d0SCy Schubert #ifdef CONFIG_MACSEC
289206b73d0SCy Schubert 	if (hdr->version > 2)
290206b73d0SCy Schubert 		hdr->version = 2;
291206b73d0SCy Schubert #endif /* CONFIG_MACSEC */
292e28a4053SRui Paulo 	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
293e28a4053SRui Paulo 	hdr->length = host_to_be16(len);
294e28a4053SRui Paulo 	hmac_md5(sm->eap_if->eapKeyData + 32, 32, buf, sizeof(*hdr) + len,
295e28a4053SRui Paulo 		 key->key_signature);
296e28a4053SRui Paulo 
297e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key to " MACSTR
298e28a4053SRui Paulo 		   " (%s index=%d)", MAC2STR(sm->addr),
299e28a4053SRui Paulo 		   broadcast ? "broadcast" : "unicast", idx);
300e28a4053SRui Paulo 	ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAPOL_KEY, (u8 *) key, len);
301e28a4053SRui Paulo 	if (sta->eapol_sm)
302e28a4053SRui Paulo 		sta->eapol_sm->dot1xAuthEapolFramesTx++;
303e28a4053SRui Paulo 	os_free(buf);
304e28a4053SRui Paulo }
305e28a4053SRui Paulo 
306e28a4053SRui Paulo 
ieee802_1x_tx_key(struct hostapd_data * hapd,struct sta_info * sta)307325151a3SRui Paulo static void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
308e28a4053SRui Paulo {
309e28a4053SRui Paulo 	struct eapol_authenticator *eapol = hapd->eapol_auth;
310e28a4053SRui Paulo 	struct eapol_state_machine *sm = sta->eapol_sm;
311e28a4053SRui Paulo 
312c1d255d3SCy Schubert 	if (!sm || !sm->eap_if->eapKeyData)
313e28a4053SRui Paulo 		return;
314e28a4053SRui Paulo 
315e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR,
316e28a4053SRui Paulo 		   MAC2STR(sta->addr));
317e28a4053SRui Paulo 
318e28a4053SRui Paulo #ifndef CONFIG_NO_VLAN
319780fb4a2SCy Schubert 	if (sta->vlan_id > 0) {
3205b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported.");
3215b9c547cSRui Paulo 		return;
3225b9c547cSRui Paulo 	}
323e28a4053SRui Paulo #endif /* CONFIG_NO_VLAN */
3245b9c547cSRui Paulo 
325e28a4053SRui Paulo 	if (eapol->default_wep_key) {
326e28a4053SRui Paulo 		ieee802_1x_tx_key_one(hapd, sta, eapol->default_wep_key_idx, 1,
327e28a4053SRui Paulo 				      eapol->default_wep_key,
328e28a4053SRui Paulo 				      hapd->conf->default_wep_key_len);
329e28a4053SRui Paulo 	}
330e28a4053SRui Paulo 
331e28a4053SRui Paulo 	if (hapd->conf->individual_wep_key_len > 0) {
332e28a4053SRui Paulo 		u8 *ikey;
333c1d255d3SCy Schubert 
334e28a4053SRui Paulo 		ikey = os_malloc(hapd->conf->individual_wep_key_len);
335c1d255d3SCy Schubert 		if (!ikey ||
336f05cddf9SRui Paulo 		    random_get_bytes(ikey, hapd->conf->individual_wep_key_len))
337f05cddf9SRui Paulo 		{
338c1d255d3SCy Schubert 			wpa_printf(MSG_ERROR,
339c1d255d3SCy Schubert 				   "Could not generate random individual WEP key");
340e28a4053SRui Paulo 			os_free(ikey);
341e28a4053SRui Paulo 			return;
342e28a4053SRui Paulo 		}
343e28a4053SRui Paulo 
344e28a4053SRui Paulo 		wpa_hexdump_key(MSG_DEBUG, "Individual WEP key",
345e28a4053SRui Paulo 				ikey, hapd->conf->individual_wep_key_len);
346e28a4053SRui Paulo 
347e28a4053SRui Paulo 		ieee802_1x_tx_key_one(hapd, sta, 0, 0, ikey,
348e28a4053SRui Paulo 				      hapd->conf->individual_wep_key_len);
349e28a4053SRui Paulo 
350e28a4053SRui Paulo 		/* TODO: set encryption in TX callback, i.e., only after STA
351e28a4053SRui Paulo 		 * has ACKed EAPOL-Key frame */
352f05cddf9SRui Paulo 		if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
353c1d255d3SCy Schubert 					sta->addr, 0, 0, 1, NULL, 0, ikey,
354c1d255d3SCy Schubert 					hapd->conf->individual_wep_key_len,
355c1d255d3SCy Schubert 					KEY_FLAG_PAIRWISE_RX_TX)) {
356c1d255d3SCy Schubert 			wpa_printf(MSG_ERROR,
357c1d255d3SCy Schubert 				   "Could not set individual WEP encryption");
358e28a4053SRui Paulo 		}
359e28a4053SRui Paulo 
360e28a4053SRui Paulo 		os_free(ikey);
361e28a4053SRui Paulo 	}
362e28a4053SRui Paulo }
363e28a4053SRui Paulo 
364325151a3SRui Paulo #endif /* CONFIG_NO_RC4 */
365325151a3SRui Paulo #endif /* CONFIG_FIPS */
366c1d255d3SCy Schubert #endif /* CONFIG_WEP */
367325151a3SRui Paulo 
368e28a4053SRui Paulo 
radius_mode_txt(struct hostapd_data * hapd)369e28a4053SRui Paulo const char *radius_mode_txt(struct hostapd_data *hapd)
370e28a4053SRui Paulo {
371e28a4053SRui Paulo 	switch (hapd->iface->conf->hw_mode) {
372f05cddf9SRui Paulo 	case HOSTAPD_MODE_IEEE80211AD:
373f05cddf9SRui Paulo 		return "802.11ad";
374e28a4053SRui Paulo 	case HOSTAPD_MODE_IEEE80211A:
375e28a4053SRui Paulo 		return "802.11a";
376e28a4053SRui Paulo 	case HOSTAPD_MODE_IEEE80211G:
377e28a4053SRui Paulo 		return "802.11g";
378e28a4053SRui Paulo 	case HOSTAPD_MODE_IEEE80211B:
379e28a4053SRui Paulo 	default:
380e28a4053SRui Paulo 		return "802.11b";
381e28a4053SRui Paulo 	}
382e28a4053SRui Paulo }
383e28a4053SRui Paulo 
384e28a4053SRui Paulo 
radius_sta_rate(struct hostapd_data * hapd,struct sta_info * sta)385e28a4053SRui Paulo int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta)
386e28a4053SRui Paulo {
387e28a4053SRui Paulo 	int i;
388e28a4053SRui Paulo 	u8 rate = 0;
389e28a4053SRui Paulo 
390e28a4053SRui Paulo 	for (i = 0; i < sta->supported_rates_len; i++)
391e28a4053SRui Paulo 		if ((sta->supported_rates[i] & 0x7f) > rate)
392e28a4053SRui Paulo 			rate = sta->supported_rates[i] & 0x7f;
393e28a4053SRui Paulo 
394e28a4053SRui Paulo 	return rate;
395e28a4053SRui Paulo }
396e28a4053SRui Paulo 
397e28a4053SRui Paulo 
398e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
ieee802_1x_learn_identity(struct hostapd_data * hapd,struct eapol_state_machine * sm,const u8 * eap,size_t len)399e28a4053SRui Paulo static void ieee802_1x_learn_identity(struct hostapd_data *hapd,
400e28a4053SRui Paulo 				      struct eapol_state_machine *sm,
401e28a4053SRui Paulo 				      const u8 *eap, size_t len)
402e28a4053SRui Paulo {
403e28a4053SRui Paulo 	const u8 *identity;
404e28a4053SRui Paulo 	size_t identity_len;
4055b9c547cSRui Paulo 	const struct eap_hdr *hdr = (const struct eap_hdr *) eap;
406e28a4053SRui Paulo 
407e28a4053SRui Paulo 	if (len <= sizeof(struct eap_hdr) ||
4085b9c547cSRui Paulo 	    (hdr->code == EAP_CODE_RESPONSE &&
4095b9c547cSRui Paulo 	     eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) ||
4105b9c547cSRui Paulo 	    (hdr->code == EAP_CODE_INITIATE &&
4115b9c547cSRui Paulo 	     eap[sizeof(struct eap_hdr)] != EAP_ERP_TYPE_REAUTH) ||
4125b9c547cSRui Paulo 	    (hdr->code != EAP_CODE_RESPONSE &&
4135b9c547cSRui Paulo 	     hdr->code != EAP_CODE_INITIATE))
414e28a4053SRui Paulo 		return;
415e28a4053SRui Paulo 
41685732ac8SCy Schubert 	eap_erp_update_identity(sm->eap, eap, len);
417e28a4053SRui Paulo 	identity = eap_get_identity(sm->eap, &identity_len);
418c1d255d3SCy Schubert 	if (!identity)
419e28a4053SRui Paulo 		return;
420e28a4053SRui Paulo 
421e28a4053SRui Paulo 	/* Save station identity for future RADIUS packets */
422e28a4053SRui Paulo 	os_free(sm->identity);
4235b9c547cSRui Paulo 	sm->identity = (u8 *) dup_binstr(identity, identity_len);
424c1d255d3SCy Schubert 	if (!sm->identity) {
425e28a4053SRui Paulo 		sm->identity_len = 0;
426e28a4053SRui Paulo 		return;
427e28a4053SRui Paulo 	}
428e28a4053SRui Paulo 
429e28a4053SRui Paulo 	sm->identity_len = identity_len;
430e28a4053SRui Paulo 	hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
431e28a4053SRui Paulo 		       HOSTAPD_LEVEL_DEBUG, "STA identity '%s'", sm->identity);
432e28a4053SRui Paulo 	sm->dot1xAuthEapolRespIdFramesRx++;
433e28a4053SRui Paulo }
434e28a4053SRui Paulo 
435e28a4053SRui Paulo 
add_common_radius_sta_attr_rsn(struct hostapd_data * hapd,struct hostapd_radius_attr * req_attr,struct sta_info * sta,struct radius_msg * msg)4365b9c547cSRui Paulo static int add_common_radius_sta_attr_rsn(struct hostapd_data *hapd,
4375b9c547cSRui Paulo 					  struct hostapd_radius_attr *req_attr,
4385b9c547cSRui Paulo 					  struct sta_info *sta,
4395b9c547cSRui Paulo 					  struct radius_msg *msg)
4405b9c547cSRui Paulo {
4415b9c547cSRui Paulo 	u32 suite;
4425b9c547cSRui Paulo 	int ver, val;
4435b9c547cSRui Paulo 
4445b9c547cSRui Paulo 	ver = wpa_auth_sta_wpa_version(sta->wpa_sm);
4455b9c547cSRui Paulo 	val = wpa_auth_get_pairwise(sta->wpa_sm);
4465b9c547cSRui Paulo 	suite = wpa_cipher_to_suite(ver, val);
4475b9c547cSRui Paulo 	if (val != -1 &&
4485b9c547cSRui Paulo 	    !hostapd_config_get_radius_attr(req_attr,
4495b9c547cSRui Paulo 					    RADIUS_ATTR_WLAN_PAIRWISE_CIPHER) &&
4505b9c547cSRui Paulo 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_PAIRWISE_CIPHER,
4515b9c547cSRui Paulo 				       suite)) {
4525b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "Could not add WLAN-Pairwise-Cipher");
4535b9c547cSRui Paulo 		return -1;
4545b9c547cSRui Paulo 	}
4555b9c547cSRui Paulo 
456325151a3SRui Paulo 	suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2) ||
457325151a3SRui Paulo 				     hapd->conf->osen) ?
4585b9c547cSRui Paulo 				    WPA_PROTO_RSN : WPA_PROTO_WPA,
4595b9c547cSRui Paulo 				    hapd->conf->wpa_group);
4605b9c547cSRui Paulo 	if (!hostapd_config_get_radius_attr(req_attr,
4615b9c547cSRui Paulo 					    RADIUS_ATTR_WLAN_GROUP_CIPHER) &&
4625b9c547cSRui Paulo 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_GROUP_CIPHER,
4635b9c547cSRui Paulo 				       suite)) {
4645b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "Could not add WLAN-Group-Cipher");
4655b9c547cSRui Paulo 		return -1;
4665b9c547cSRui Paulo 	}
4675b9c547cSRui Paulo 
4685b9c547cSRui Paulo 	val = wpa_auth_sta_key_mgmt(sta->wpa_sm);
4695b9c547cSRui Paulo 	suite = wpa_akm_to_suite(val);
4705b9c547cSRui Paulo 	if (val != -1 &&
4715b9c547cSRui Paulo 	    !hostapd_config_get_radius_attr(req_attr,
4725b9c547cSRui Paulo 					    RADIUS_ATTR_WLAN_AKM_SUITE) &&
4735b9c547cSRui Paulo 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE,
4745b9c547cSRui Paulo 				       suite)) {
4755b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "Could not add WLAN-AKM-Suite");
4765b9c547cSRui Paulo 		return -1;
4775b9c547cSRui Paulo 	}
4785b9c547cSRui Paulo 
4795b9c547cSRui Paulo 	if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
4805b9c547cSRui Paulo 		suite = wpa_cipher_to_suite(WPA_PROTO_RSN,
4815b9c547cSRui Paulo 					    hapd->conf->group_mgmt_cipher);
4825b9c547cSRui Paulo 		if (!hostapd_config_get_radius_attr(
4835b9c547cSRui Paulo 			    req_attr, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER) &&
4845b9c547cSRui Paulo 		    !radius_msg_add_attr_int32(
4855b9c547cSRui Paulo 			    msg, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, suite)) {
4865b9c547cSRui Paulo 			wpa_printf(MSG_ERROR,
4875b9c547cSRui Paulo 				   "Could not add WLAN-Group-Mgmt-Cipher");
4885b9c547cSRui Paulo 			return -1;
4895b9c547cSRui Paulo 		}
4905b9c547cSRui Paulo 	}
4915b9c547cSRui Paulo 
4925b9c547cSRui Paulo 	return 0;
4935b9c547cSRui Paulo }
4945b9c547cSRui Paulo 
4955b9c547cSRui Paulo 
add_common_radius_sta_attr(struct hostapd_data * hapd,struct hostapd_radius_attr * req_attr,struct sta_info * sta,struct radius_msg * msg)496f05cddf9SRui Paulo static int add_common_radius_sta_attr(struct hostapd_data *hapd,
497f05cddf9SRui Paulo 				      struct hostapd_radius_attr *req_attr,
498f05cddf9SRui Paulo 				      struct sta_info *sta,
499f05cddf9SRui Paulo 				      struct radius_msg *msg)
500f05cddf9SRui Paulo {
501f05cddf9SRui Paulo 	char buf[128];
502f05cddf9SRui Paulo 
503f05cddf9SRui Paulo 	if (!hostapd_config_get_radius_attr(req_attr,
504780fb4a2SCy Schubert 					    RADIUS_ATTR_SERVICE_TYPE) &&
505780fb4a2SCy Schubert 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE,
506780fb4a2SCy Schubert 				       RADIUS_SERVICE_TYPE_FRAMED)) {
507780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "Could not add Service-Type");
508780fb4a2SCy Schubert 		return -1;
509780fb4a2SCy Schubert 	}
510780fb4a2SCy Schubert 
511780fb4a2SCy Schubert 	if (!hostapd_config_get_radius_attr(req_attr,
512f05cddf9SRui Paulo 					    RADIUS_ATTR_NAS_PORT) &&
513780fb4a2SCy Schubert 	    sta->aid > 0 &&
514f05cddf9SRui Paulo 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
515f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Could not add NAS-Port");
516f05cddf9SRui Paulo 		return -1;
517f05cddf9SRui Paulo 	}
518f05cddf9SRui Paulo 
519f05cddf9SRui Paulo 	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
520f05cddf9SRui Paulo 		    MAC2STR(sta->addr));
521f05cddf9SRui Paulo 	buf[sizeof(buf) - 1] = '\0';
522f05cddf9SRui Paulo 	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
523f05cddf9SRui Paulo 				 (u8 *) buf, os_strlen(buf))) {
524f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Could not add Calling-Station-Id");
525f05cddf9SRui Paulo 		return -1;
526f05cddf9SRui Paulo 	}
527f05cddf9SRui Paulo 
528f05cddf9SRui Paulo 	if (sta->flags & WLAN_STA_PREAUTH) {
529f05cddf9SRui Paulo 		os_strlcpy(buf, "IEEE 802.11i Pre-Authentication",
530f05cddf9SRui Paulo 			   sizeof(buf));
531f05cddf9SRui Paulo 	} else {
532f05cddf9SRui Paulo 		os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
533f05cddf9SRui Paulo 			    radius_sta_rate(hapd, sta) / 2,
534f05cddf9SRui Paulo 			    (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
535f05cddf9SRui Paulo 			    radius_mode_txt(hapd));
536f05cddf9SRui Paulo 		buf[sizeof(buf) - 1] = '\0';
537f05cddf9SRui Paulo 	}
538f05cddf9SRui Paulo 	if (!hostapd_config_get_radius_attr(req_attr,
539f05cddf9SRui Paulo 					    RADIUS_ATTR_CONNECT_INFO) &&
540f05cddf9SRui Paulo 	    !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
541f05cddf9SRui Paulo 				 (u8 *) buf, os_strlen(buf))) {
542f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Could not add Connect-Info");
543f05cddf9SRui Paulo 		return -1;
544f05cddf9SRui Paulo 	}
545f05cddf9SRui Paulo 
546780fb4a2SCy Schubert 	if (sta->acct_session_id) {
547780fb4a2SCy Schubert 		os_snprintf(buf, sizeof(buf), "%016llX",
548780fb4a2SCy Schubert 			    (unsigned long long) sta->acct_session_id);
549f05cddf9SRui Paulo 		if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
550f05cddf9SRui Paulo 					 (u8 *) buf, os_strlen(buf))) {
551f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
552f05cddf9SRui Paulo 			return -1;
553f05cddf9SRui Paulo 		}
554f05cddf9SRui Paulo 	}
555f05cddf9SRui Paulo 
556780fb4a2SCy Schubert 	if ((hapd->conf->wpa & 2) &&
557780fb4a2SCy Schubert 	    !hapd->conf->disable_pmksa_caching &&
558780fb4a2SCy Schubert 	    sta->eapol_sm && sta->eapol_sm->acct_multi_session_id) {
559780fb4a2SCy Schubert 		os_snprintf(buf, sizeof(buf), "%016llX",
560780fb4a2SCy Schubert 			    (unsigned long long)
561780fb4a2SCy Schubert 			    sta->eapol_sm->acct_multi_session_id);
562780fb4a2SCy Schubert 		if (!radius_msg_add_attr(
563780fb4a2SCy Schubert 			    msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
564780fb4a2SCy Schubert 			    (u8 *) buf, os_strlen(buf))) {
565780fb4a2SCy Schubert 			wpa_printf(MSG_INFO,
566780fb4a2SCy Schubert 				   "Could not add Acct-Multi-Session-Id");
567780fb4a2SCy Schubert 			return -1;
568780fb4a2SCy Schubert 		}
569780fb4a2SCy Schubert 	}
570780fb4a2SCy Schubert 
57185732ac8SCy Schubert #ifdef CONFIG_IEEE80211R_AP
5725b9c547cSRui Paulo 	if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
5735b9c547cSRui Paulo 	    sta->wpa_sm &&
5745b9c547cSRui Paulo 	    (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) ||
5755b9c547cSRui Paulo 	     sta->auth_alg == WLAN_AUTH_FT) &&
5765b9c547cSRui Paulo 	    !hostapd_config_get_radius_attr(req_attr,
5775b9c547cSRui Paulo 					    RADIUS_ATTR_MOBILITY_DOMAIN_ID) &&
5785b9c547cSRui Paulo 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_MOBILITY_DOMAIN_ID,
5795b9c547cSRui Paulo 				       WPA_GET_BE16(
5805b9c547cSRui Paulo 					       hapd->conf->mobility_domain))) {
5815b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id");
5825b9c547cSRui Paulo 		return -1;
5835b9c547cSRui Paulo 	}
58485732ac8SCy Schubert #endif /* CONFIG_IEEE80211R_AP */
5855b9c547cSRui Paulo 
586325151a3SRui Paulo 	if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm &&
5875b9c547cSRui Paulo 	    add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
5885b9c547cSRui Paulo 		return -1;
5895b9c547cSRui Paulo 
590f05cddf9SRui Paulo 	return 0;
591f05cddf9SRui Paulo }
592f05cddf9SRui Paulo 
593f05cddf9SRui Paulo 
add_common_radius_attr(struct hostapd_data * hapd,struct hostapd_radius_attr * req_attr,struct sta_info * sta,struct radius_msg * msg)594f05cddf9SRui Paulo int add_common_radius_attr(struct hostapd_data *hapd,
595f05cddf9SRui Paulo 			   struct hostapd_radius_attr *req_attr,
596f05cddf9SRui Paulo 			   struct sta_info *sta,
597f05cddf9SRui Paulo 			   struct radius_msg *msg)
598f05cddf9SRui Paulo {
599f05cddf9SRui Paulo 	char buf[128];
600f05cddf9SRui Paulo 	struct hostapd_radius_attr *attr;
601780fb4a2SCy Schubert 	int len;
602f05cddf9SRui Paulo 
603f05cddf9SRui Paulo 	if (!hostapd_config_get_radius_attr(req_attr,
604f05cddf9SRui Paulo 					    RADIUS_ATTR_NAS_IP_ADDRESS) &&
605f05cddf9SRui Paulo 	    hapd->conf->own_ip_addr.af == AF_INET &&
606f05cddf9SRui Paulo 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
607f05cddf9SRui Paulo 				 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
608f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Could not add NAS-IP-Address");
609f05cddf9SRui Paulo 		return -1;
610f05cddf9SRui Paulo 	}
611f05cddf9SRui Paulo 
612f05cddf9SRui Paulo #ifdef CONFIG_IPV6
613f05cddf9SRui Paulo 	if (!hostapd_config_get_radius_attr(req_attr,
614f05cddf9SRui Paulo 					    RADIUS_ATTR_NAS_IPV6_ADDRESS) &&
615f05cddf9SRui Paulo 	    hapd->conf->own_ip_addr.af == AF_INET6 &&
616f05cddf9SRui Paulo 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
617f05cddf9SRui Paulo 				 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
618f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Could not add NAS-IPv6-Address");
619f05cddf9SRui Paulo 		return -1;
620f05cddf9SRui Paulo 	}
621f05cddf9SRui Paulo #endif /* CONFIG_IPV6 */
622f05cddf9SRui Paulo 
623f05cddf9SRui Paulo 	if (!hostapd_config_get_radius_attr(req_attr,
624f05cddf9SRui Paulo 					    RADIUS_ATTR_NAS_IDENTIFIER) &&
625f05cddf9SRui Paulo 	    hapd->conf->nas_identifier &&
626f05cddf9SRui Paulo 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
627f05cddf9SRui Paulo 				 (u8 *) hapd->conf->nas_identifier,
628f05cddf9SRui Paulo 				 os_strlen(hapd->conf->nas_identifier))) {
629f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Could not add NAS-Identifier");
630f05cddf9SRui Paulo 		return -1;
631f05cddf9SRui Paulo 	}
632f05cddf9SRui Paulo 
633780fb4a2SCy Schubert 	len = os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":",
634780fb4a2SCy Schubert 			  MAC2STR(hapd->own_addr));
635780fb4a2SCy Schubert 	os_memcpy(&buf[len], hapd->conf->ssid.ssid,
636780fb4a2SCy Schubert 		  hapd->conf->ssid.ssid_len);
637780fb4a2SCy Schubert 	len += hapd->conf->ssid.ssid_len;
638f05cddf9SRui Paulo 	if (!hostapd_config_get_radius_attr(req_attr,
639f05cddf9SRui Paulo 					    RADIUS_ATTR_CALLED_STATION_ID) &&
640f05cddf9SRui Paulo 	    !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
641780fb4a2SCy Schubert 				 (u8 *) buf, len)) {
642f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Could not add Called-Station-Id");
643f05cddf9SRui Paulo 		return -1;
644f05cddf9SRui Paulo 	}
645f05cddf9SRui Paulo 
646f05cddf9SRui Paulo 	if (!hostapd_config_get_radius_attr(req_attr,
647f05cddf9SRui Paulo 					    RADIUS_ATTR_NAS_PORT_TYPE) &&
648f05cddf9SRui Paulo 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
649f05cddf9SRui Paulo 				       RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
650f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Could not add NAS-Port-Type");
651f05cddf9SRui Paulo 		return -1;
652f05cddf9SRui Paulo 	}
653f05cddf9SRui Paulo 
6545b9c547cSRui Paulo #ifdef CONFIG_INTERWORKING
6555b9c547cSRui Paulo 	if (hapd->conf->interworking &&
6565b9c547cSRui Paulo 	    !is_zero_ether_addr(hapd->conf->hessid)) {
6575b9c547cSRui Paulo 		os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
6585b9c547cSRui Paulo 			    MAC2STR(hapd->conf->hessid));
6595b9c547cSRui Paulo 		buf[sizeof(buf) - 1] = '\0';
6605b9c547cSRui Paulo 		if (!hostapd_config_get_radius_attr(req_attr,
6615b9c547cSRui Paulo 						    RADIUS_ATTR_WLAN_HESSID) &&
6625b9c547cSRui Paulo 		    !radius_msg_add_attr(msg, RADIUS_ATTR_WLAN_HESSID,
6635b9c547cSRui Paulo 					 (u8 *) buf, os_strlen(buf))) {
6645b9c547cSRui Paulo 			wpa_printf(MSG_ERROR, "Could not add WLAN-HESSID");
6655b9c547cSRui Paulo 			return -1;
6665b9c547cSRui Paulo 		}
6675b9c547cSRui Paulo 	}
6685b9c547cSRui Paulo #endif /* CONFIG_INTERWORKING */
6695b9c547cSRui Paulo 
670f05cddf9SRui Paulo 	if (sta && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0)
671f05cddf9SRui Paulo 		return -1;
672f05cddf9SRui Paulo 
673f05cddf9SRui Paulo 	for (attr = req_attr; attr; attr = attr->next) {
674f05cddf9SRui Paulo 		if (!radius_msg_add_attr(msg, attr->type,
675f05cddf9SRui Paulo 					 wpabuf_head(attr->val),
676f05cddf9SRui Paulo 					 wpabuf_len(attr->val))) {
677c1d255d3SCy Schubert 			wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
678f05cddf9SRui Paulo 			return -1;
679f05cddf9SRui Paulo 		}
680f05cddf9SRui Paulo 	}
681f05cddf9SRui Paulo 
682f05cddf9SRui Paulo 	return 0;
683f05cddf9SRui Paulo }
684f05cddf9SRui Paulo 
685f05cddf9SRui Paulo 
add_sqlite_radius_attr(struct hostapd_data * hapd,struct sta_info * sta,struct radius_msg * msg,int acct)686206b73d0SCy Schubert int add_sqlite_radius_attr(struct hostapd_data *hapd, struct sta_info *sta,
687206b73d0SCy Schubert 			   struct radius_msg *msg, int acct)
688206b73d0SCy Schubert {
689206b73d0SCy Schubert #ifdef CONFIG_SQLITE
690206b73d0SCy Schubert 	const char *attrtxt;
691206b73d0SCy Schubert 	char addrtxt[3 * ETH_ALEN];
692206b73d0SCy Schubert 	char *sql;
693206b73d0SCy Schubert 	sqlite3_stmt *stmt = NULL;
694206b73d0SCy Schubert 
695206b73d0SCy Schubert 	if (!hapd->rad_attr_db)
696206b73d0SCy Schubert 		return 0;
697206b73d0SCy Schubert 
698206b73d0SCy Schubert 	os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, MAC2STR(sta->addr));
699206b73d0SCy Schubert 
700206b73d0SCy Schubert 	sql = "SELECT attr FROM radius_attributes WHERE sta=? AND (reqtype=? OR reqtype IS NULL);";
701206b73d0SCy Schubert 	if (sqlite3_prepare_v2(hapd->rad_attr_db, sql, os_strlen(sql), &stmt,
702206b73d0SCy Schubert 			       NULL) != SQLITE_OK) {
703206b73d0SCy Schubert 		wpa_printf(MSG_ERROR, "DB: Failed to prepare SQL statement: %s",
704206b73d0SCy Schubert 			   sqlite3_errmsg(hapd->rad_attr_db));
705206b73d0SCy Schubert 		return -1;
706206b73d0SCy Schubert 	}
707206b73d0SCy Schubert 	sqlite3_bind_text(stmt, 1, addrtxt, os_strlen(addrtxt), SQLITE_STATIC);
708206b73d0SCy Schubert 	sqlite3_bind_text(stmt, 2, acct ? "acct" : "auth", 4, SQLITE_STATIC);
709206b73d0SCy Schubert 	while (sqlite3_step(stmt) == SQLITE_ROW) {
710206b73d0SCy Schubert 		struct hostapd_radius_attr *attr;
711206b73d0SCy Schubert 		struct radius_attr_hdr *hdr;
712206b73d0SCy Schubert 
713206b73d0SCy Schubert 		attrtxt = (const char *) sqlite3_column_text(stmt, 0);
714206b73d0SCy Schubert 		attr = hostapd_parse_radius_attr(attrtxt);
715206b73d0SCy Schubert 		if (!attr) {
716206b73d0SCy Schubert 			wpa_printf(MSG_ERROR,
717206b73d0SCy Schubert 				   "Skipping invalid attribute from SQL: %s",
718206b73d0SCy Schubert 				   attrtxt);
719206b73d0SCy Schubert 			continue;
720206b73d0SCy Schubert 		}
721206b73d0SCy Schubert 		wpa_printf(MSG_DEBUG, "Adding RADIUS attribute from SQL: %s",
722206b73d0SCy Schubert 			   attrtxt);
723206b73d0SCy Schubert 		hdr = radius_msg_add_attr(msg, attr->type,
724206b73d0SCy Schubert 					  wpabuf_head(attr->val),
725206b73d0SCy Schubert 					  wpabuf_len(attr->val));
726206b73d0SCy Schubert 		hostapd_config_free_radius_attr(attr);
727206b73d0SCy Schubert 		if (!hdr) {
728206b73d0SCy Schubert 			wpa_printf(MSG_ERROR,
729206b73d0SCy Schubert 				   "Could not add RADIUS attribute from SQL");
730206b73d0SCy Schubert 			continue;
731206b73d0SCy Schubert 		}
732206b73d0SCy Schubert 	}
733206b73d0SCy Schubert 
734206b73d0SCy Schubert 	sqlite3_reset(stmt);
735206b73d0SCy Schubert 	sqlite3_clear_bindings(stmt);
736206b73d0SCy Schubert 	sqlite3_finalize(stmt);
737206b73d0SCy Schubert #endif /* CONFIG_SQLITE */
738206b73d0SCy Schubert 
739206b73d0SCy Schubert 	return 0;
740206b73d0SCy Schubert }
741206b73d0SCy Schubert 
742206b73d0SCy Schubert 
ieee802_1x_encapsulate_radius(struct hostapd_data * hapd,struct sta_info * sta,const u8 * eap,size_t len)74385732ac8SCy Schubert void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
744e28a4053SRui Paulo 				   struct sta_info *sta,
745e28a4053SRui Paulo 				   const u8 *eap, size_t len)
746e28a4053SRui Paulo {
747e28a4053SRui Paulo 	struct radius_msg *msg;
748e28a4053SRui Paulo 	struct eapol_state_machine *sm = sta->eapol_sm;
749e28a4053SRui Paulo 
750c1d255d3SCy Schubert 	if (!sm)
751e28a4053SRui Paulo 		return;
752e28a4053SRui Paulo 
753e28a4053SRui Paulo 	ieee802_1x_learn_identity(hapd, sm, eap, len);
754e28a4053SRui Paulo 
755c1d255d3SCy Schubert 	wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS packet");
756e28a4053SRui Paulo 
757e28a4053SRui Paulo 	sm->radius_identifier = radius_client_get_id(hapd->radius);
758e28a4053SRui Paulo 	msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
759e28a4053SRui Paulo 			     sm->radius_identifier);
760c1d255d3SCy Schubert 	if (!msg) {
7615b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
762e28a4053SRui Paulo 		return;
763e28a4053SRui Paulo 	}
764e28a4053SRui Paulo 
765780fb4a2SCy Schubert 	if (radius_msg_make_authenticator(msg) < 0) {
766780fb4a2SCy Schubert 		wpa_printf(MSG_INFO, "Could not make Request Authenticator");
767780fb4a2SCy Schubert 		goto fail;
768780fb4a2SCy Schubert 	}
769e28a4053SRui Paulo 
770*a90b9d01SCy Schubert 	if (!radius_msg_add_msg_auth(msg))
771*a90b9d01SCy Schubert 		goto fail;
772*a90b9d01SCy Schubert 
773e28a4053SRui Paulo 	if (sm->identity &&
774e28a4053SRui Paulo 	    !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
775e28a4053SRui Paulo 				 sm->identity, sm->identity_len)) {
7765b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "Could not add User-Name");
777e28a4053SRui Paulo 		goto fail;
778e28a4053SRui Paulo 	}
779e28a4053SRui Paulo 
780f05cddf9SRui Paulo 	if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, sta,
781f05cddf9SRui Paulo 				   msg) < 0)
782e28a4053SRui Paulo 		goto fail;
783e28a4053SRui Paulo 
784206b73d0SCy Schubert 	if (sta && add_sqlite_radius_attr(hapd, sta, msg, 0) < 0)
785206b73d0SCy Schubert 		goto fail;
786206b73d0SCy Schubert 
787e28a4053SRui Paulo 	/* TODO: should probably check MTU from driver config; 2304 is max for
788e28a4053SRui Paulo 	 * IEEE 802.11, but use 1400 to avoid problems with too large packets
789e28a4053SRui Paulo 	 */
790f05cddf9SRui Paulo 	if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
791f05cddf9SRui Paulo 					    RADIUS_ATTR_FRAMED_MTU) &&
792f05cddf9SRui Paulo 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
7935b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "Could not add Framed-MTU");
794e28a4053SRui Paulo 		goto fail;
795e28a4053SRui Paulo 	}
796e28a4053SRui Paulo 
797325151a3SRui Paulo 	if (!radius_msg_add_eap(msg, eap, len)) {
7985b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "Could not add EAP-Message");
799e28a4053SRui Paulo 		goto fail;
800e28a4053SRui Paulo 	}
801e28a4053SRui Paulo 
802e28a4053SRui Paulo 	/* State attribute must be copied if and only if this packet is
803e28a4053SRui Paulo 	 * Access-Request reply to the previous Access-Challenge */
804e28a4053SRui Paulo 	if (sm->last_recv_radius &&
805e28a4053SRui Paulo 	    radius_msg_get_hdr(sm->last_recv_radius)->code ==
806e28a4053SRui Paulo 	    RADIUS_CODE_ACCESS_CHALLENGE) {
807e28a4053SRui Paulo 		int res = radius_msg_copy_attr(msg, sm->last_recv_radius,
808e28a4053SRui Paulo 					       RADIUS_ATTR_STATE);
809e28a4053SRui Paulo 		if (res < 0) {
810c1d255d3SCy Schubert 			wpa_printf(MSG_INFO,
811c1d255d3SCy Schubert 				   "Could not copy State attribute from previous Access-Challenge");
812e28a4053SRui Paulo 			goto fail;
813e28a4053SRui Paulo 		}
814c1d255d3SCy Schubert 		if (res > 0)
815e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "Copied RADIUS State Attribute");
816e28a4053SRui Paulo 	}
817e28a4053SRui Paulo 
818f05cddf9SRui Paulo 	if (hapd->conf->radius_request_cui) {
819f05cddf9SRui Paulo 		const u8 *cui;
820f05cddf9SRui Paulo 		size_t cui_len;
821f05cddf9SRui Paulo 		/* Add previously learned CUI or nul CUI to request CUI */
822f05cddf9SRui Paulo 		if (sm->radius_cui) {
823f05cddf9SRui Paulo 			cui = wpabuf_head(sm->radius_cui);
824f05cddf9SRui Paulo 			cui_len = wpabuf_len(sm->radius_cui);
825f05cddf9SRui Paulo 		} else {
826f05cddf9SRui Paulo 			cui = (const u8 *) "\0";
827f05cddf9SRui Paulo 			cui_len = 1;
828f05cddf9SRui Paulo 		}
829f05cddf9SRui Paulo 		if (!radius_msg_add_attr(msg,
830f05cddf9SRui Paulo 					 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
831f05cddf9SRui Paulo 					 cui, cui_len)) {
832f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "Could not add CUI");
833f05cddf9SRui Paulo 			goto fail;
834f05cddf9SRui Paulo 		}
835f05cddf9SRui Paulo 	}
836f05cddf9SRui Paulo 
8375b9c547cSRui Paulo #ifdef CONFIG_HS20
8385b9c547cSRui Paulo 	if (hapd->conf->hs20) {
8394bc52338SCy Schubert 		u8 ver = hapd->conf->hs20_release - 1;
8404bc52338SCy Schubert 
8415b9c547cSRui Paulo 		if (!radius_msg_add_wfa(
8425b9c547cSRui Paulo 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION,
8435b9c547cSRui Paulo 			    &ver, 1)) {
844c1d255d3SCy Schubert 			wpa_printf(MSG_ERROR,
845c1d255d3SCy Schubert 				   "Could not add HS 2.0 AP version");
8465b9c547cSRui Paulo 			goto fail;
8475b9c547cSRui Paulo 		}
8485b9c547cSRui Paulo 
8495b9c547cSRui Paulo 		if (sta->hs20_ie && wpabuf_len(sta->hs20_ie) > 0) {
8505b9c547cSRui Paulo 			const u8 *pos;
8515b9c547cSRui Paulo 			u8 buf[3];
8525b9c547cSRui Paulo 			u16 id;
853c1d255d3SCy Schubert 
8545b9c547cSRui Paulo 			pos = wpabuf_head_u8(sta->hs20_ie);
8555b9c547cSRui Paulo 			buf[0] = (*pos) >> 4;
8565b9c547cSRui Paulo 			if (((*pos) & HS20_PPS_MO_ID_PRESENT) &&
8575b9c547cSRui Paulo 			    wpabuf_len(sta->hs20_ie) >= 3)
8585b9c547cSRui Paulo 				id = WPA_GET_LE16(pos + 1);
8595b9c547cSRui Paulo 			else
8605b9c547cSRui Paulo 				id = 0;
8615b9c547cSRui Paulo 			WPA_PUT_BE16(buf + 1, id);
8625b9c547cSRui Paulo 			if (!radius_msg_add_wfa(
8635b9c547cSRui Paulo 				    msg,
8645b9c547cSRui Paulo 				    RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION,
8655b9c547cSRui Paulo 				    buf, sizeof(buf))) {
866c1d255d3SCy Schubert 				wpa_printf(MSG_ERROR,
867c1d255d3SCy Schubert 					   "Could not add HS 2.0 STA version");
8685b9c547cSRui Paulo 				goto fail;
8695b9c547cSRui Paulo 			}
8705b9c547cSRui Paulo 		}
87185732ac8SCy Schubert 
87285732ac8SCy Schubert 		if (sta->roaming_consortium &&
87385732ac8SCy Schubert 		    !radius_msg_add_wfa(
87485732ac8SCy Schubert 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM,
87585732ac8SCy Schubert 			    wpabuf_head(sta->roaming_consortium),
87685732ac8SCy Schubert 			    wpabuf_len(sta->roaming_consortium))) {
87785732ac8SCy Schubert 			wpa_printf(MSG_ERROR,
87885732ac8SCy Schubert 				   "Could not add HS 2.0 Roaming Consortium");
87985732ac8SCy Schubert 			goto fail;
88085732ac8SCy Schubert 		}
88185732ac8SCy Schubert 
88285732ac8SCy Schubert 		if (hapd->conf->t_c_filename) {
88385732ac8SCy Schubert 			be32 timestamp;
88485732ac8SCy Schubert 
88585732ac8SCy Schubert 			if (!radius_msg_add_wfa(
88685732ac8SCy Schubert 				    msg,
88785732ac8SCy Schubert 				    RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME,
88885732ac8SCy Schubert 				    (const u8 *) hapd->conf->t_c_filename,
88985732ac8SCy Schubert 				    os_strlen(hapd->conf->t_c_filename))) {
89085732ac8SCy Schubert 				wpa_printf(MSG_ERROR,
89185732ac8SCy Schubert 					   "Could not add HS 2.0 T&C Filename");
89285732ac8SCy Schubert 				goto fail;
89385732ac8SCy Schubert 			}
89485732ac8SCy Schubert 
89585732ac8SCy Schubert 			timestamp = host_to_be32(hapd->conf->t_c_timestamp);
89685732ac8SCy Schubert 			if (!radius_msg_add_wfa(
89785732ac8SCy Schubert 				    msg,
89885732ac8SCy Schubert 				    RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP,
89985732ac8SCy Schubert 				    (const u8 *) &timestamp,
90085732ac8SCy Schubert 				    sizeof(timestamp))) {
90185732ac8SCy Schubert 				wpa_printf(MSG_ERROR,
90285732ac8SCy Schubert 					   "Could not add HS 2.0 Timestamp");
90385732ac8SCy Schubert 				goto fail;
90485732ac8SCy Schubert 			}
90585732ac8SCy Schubert 		}
9065b9c547cSRui Paulo 	}
9075b9c547cSRui Paulo #endif /* CONFIG_HS20 */
9085b9c547cSRui Paulo 
909f05cddf9SRui Paulo 	if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0)
910f05cddf9SRui Paulo 		goto fail;
911f05cddf9SRui Paulo 
912e28a4053SRui Paulo 	return;
913e28a4053SRui Paulo 
914e28a4053SRui Paulo  fail:
915e28a4053SRui Paulo 	radius_msg_free(msg);
916e28a4053SRui Paulo }
917e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
918e28a4053SRui Paulo 
919e28a4053SRui Paulo 
handle_eap_response(struct hostapd_data * hapd,struct sta_info * sta,struct eap_hdr * eap,size_t len)920e28a4053SRui Paulo static void handle_eap_response(struct hostapd_data *hapd,
921e28a4053SRui Paulo 				struct sta_info *sta, struct eap_hdr *eap,
922e28a4053SRui Paulo 				size_t len)
923e28a4053SRui Paulo {
924e28a4053SRui Paulo 	u8 type, *data;
925e28a4053SRui Paulo 	struct eapol_state_machine *sm = sta->eapol_sm;
926c1d255d3SCy Schubert 
927c1d255d3SCy Schubert 	if (!sm)
928e28a4053SRui Paulo 		return;
929e28a4053SRui Paulo 
930e28a4053SRui Paulo 	data = (u8 *) (eap + 1);
931e28a4053SRui Paulo 
932e28a4053SRui Paulo 	if (len < sizeof(*eap) + 1) {
933c1d255d3SCy Schubert 		wpa_printf(MSG_INFO, "%s: too short response data", __func__);
934e28a4053SRui Paulo 		return;
935e28a4053SRui Paulo 	}
936e28a4053SRui Paulo 
937e28a4053SRui Paulo 	sm->eap_type_supp = type = data[0];
938e28a4053SRui Paulo 
939e28a4053SRui Paulo 	hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
940e28a4053SRui Paulo 		       HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d "
941e28a4053SRui Paulo 		       "id=%d len=%d) from STA: EAP Response-%s (%d)",
942e28a4053SRui Paulo 		       eap->code, eap->identifier, be_to_host16(eap->length),
943e28a4053SRui Paulo 		       eap_server_get_name(0, type), type);
944e28a4053SRui Paulo 
945e28a4053SRui Paulo 	sm->dot1xAuthEapolRespFramesRx++;
946e28a4053SRui Paulo 
947e28a4053SRui Paulo 	wpabuf_free(sm->eap_if->eapRespData);
948e28a4053SRui Paulo 	sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
949c1d255d3SCy Schubert 	sm->eapolEap = true;
950e28a4053SRui Paulo }
951e28a4053SRui Paulo 
952e28a4053SRui Paulo 
handle_eap_initiate(struct hostapd_data * hapd,struct sta_info * sta,struct eap_hdr * eap,size_t len)9535b9c547cSRui Paulo static void handle_eap_initiate(struct hostapd_data *hapd,
9545b9c547cSRui Paulo 				struct sta_info *sta, struct eap_hdr *eap,
9555b9c547cSRui Paulo 				size_t len)
9565b9c547cSRui Paulo {
9575b9c547cSRui Paulo #ifdef CONFIG_ERP
9585b9c547cSRui Paulo 	u8 type, *data;
9595b9c547cSRui Paulo 	struct eapol_state_machine *sm = sta->eapol_sm;
9605b9c547cSRui Paulo 
961c1d255d3SCy Schubert 	if (!sm)
9625b9c547cSRui Paulo 		return;
9635b9c547cSRui Paulo 
9645b9c547cSRui Paulo 	if (len < sizeof(*eap) + 1) {
965c1d255d3SCy Schubert 		wpa_printf(MSG_INFO, "%s: too short response data", __func__);
9665b9c547cSRui Paulo 		return;
9675b9c547cSRui Paulo 	}
9685b9c547cSRui Paulo 
9695b9c547cSRui Paulo 	data = (u8 *) (eap + 1);
9705b9c547cSRui Paulo 	type = data[0];
9715b9c547cSRui Paulo 
9725b9c547cSRui Paulo 	hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
973c1d255d3SCy Schubert 		       HOSTAPD_LEVEL_DEBUG,
974c1d255d3SCy Schubert 		       "received EAP packet (code=%d id=%d len=%d) from STA: EAP Initiate type %u",
9755b9c547cSRui Paulo 		       eap->code, eap->identifier, be_to_host16(eap->length),
9765b9c547cSRui Paulo 		       type);
9775b9c547cSRui Paulo 
9785b9c547cSRui Paulo 	wpabuf_free(sm->eap_if->eapRespData);
9795b9c547cSRui Paulo 	sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
980c1d255d3SCy Schubert 	sm->eapolEap = true;
9815b9c547cSRui Paulo #endif /* CONFIG_ERP */
9825b9c547cSRui Paulo }
9835b9c547cSRui Paulo 
9845b9c547cSRui Paulo 
985c1d255d3SCy Schubert #ifndef CONFIG_NO_STDOUT_DEBUG
eap_code_str(u8 code)986c1d255d3SCy Schubert static const char * eap_code_str(u8 code)
987c1d255d3SCy Schubert {
988c1d255d3SCy Schubert 	switch (code) {
989c1d255d3SCy Schubert 	case EAP_CODE_REQUEST:
990c1d255d3SCy Schubert 		return "request";
991c1d255d3SCy Schubert 	case EAP_CODE_RESPONSE:
992c1d255d3SCy Schubert 		return "response";
993c1d255d3SCy Schubert 	case EAP_CODE_SUCCESS:
994c1d255d3SCy Schubert 		return "success";
995c1d255d3SCy Schubert 	case EAP_CODE_FAILURE:
996c1d255d3SCy Schubert 		return "failure";
997c1d255d3SCy Schubert 	case EAP_CODE_INITIATE:
998c1d255d3SCy Schubert 		return "initiate";
999c1d255d3SCy Schubert 	case EAP_CODE_FINISH:
1000c1d255d3SCy Schubert 		return "finish";
1001c1d255d3SCy Schubert 	default:
1002c1d255d3SCy Schubert 		return "unknown";
1003c1d255d3SCy Schubert 	}
1004c1d255d3SCy Schubert }
1005c1d255d3SCy Schubert #endif /* CONFIG_NO_STDOUT_DEBUG */
1006c1d255d3SCy Schubert 
1007c1d255d3SCy Schubert 
1008e28a4053SRui Paulo /* Process incoming EAP packet from Supplicant */
handle_eap(struct hostapd_data * hapd,struct sta_info * sta,u8 * buf,size_t len)1009e28a4053SRui Paulo static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
1010e28a4053SRui Paulo 		       u8 *buf, size_t len)
1011e28a4053SRui Paulo {
1012e28a4053SRui Paulo 	struct eap_hdr *eap;
1013e28a4053SRui Paulo 	u16 eap_len;
1014e28a4053SRui Paulo 
1015e28a4053SRui Paulo 	if (len < sizeof(*eap)) {
10165b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "   too short EAP packet");
1017e28a4053SRui Paulo 		return;
1018e28a4053SRui Paulo 	}
1019e28a4053SRui Paulo 
1020e28a4053SRui Paulo 	eap = (struct eap_hdr *) buf;
1021e28a4053SRui Paulo 
1022e28a4053SRui Paulo 	eap_len = be_to_host16(eap->length);
1023c1d255d3SCy Schubert 	wpa_printf(MSG_DEBUG, "EAP: code=%d (%s) identifier=%d length=%d",
1024c1d255d3SCy Schubert 		   eap->code, eap_code_str(eap->code), eap->identifier,
1025c1d255d3SCy Schubert 		   eap_len);
1026e28a4053SRui Paulo 	if (eap_len < sizeof(*eap)) {
1027e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "   Invalid EAP length");
1028e28a4053SRui Paulo 		return;
1029e28a4053SRui Paulo 	} else if (eap_len > len) {
1030c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG,
1031c1d255d3SCy Schubert 			   "   Too short frame to contain this EAP packet");
1032e28a4053SRui Paulo 		return;
1033e28a4053SRui Paulo 	} else if (eap_len < len) {
1034c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG,
1035c1d255d3SCy Schubert 			   "   Ignoring %lu extra bytes after EAP packet",
1036c1d255d3SCy Schubert 			   (unsigned long) len - eap_len);
1037e28a4053SRui Paulo 	}
1038e28a4053SRui Paulo 
1039e28a4053SRui Paulo 	switch (eap->code) {
1040e28a4053SRui Paulo 	case EAP_CODE_RESPONSE:
1041e28a4053SRui Paulo 		handle_eap_response(hapd, sta, eap, eap_len);
1042e28a4053SRui Paulo 		break;
10435b9c547cSRui Paulo 	case EAP_CODE_INITIATE:
10445b9c547cSRui Paulo 		handle_eap_initiate(hapd, sta, eap, eap_len);
10455b9c547cSRui Paulo 		break;
1046e28a4053SRui Paulo 	}
1047e28a4053SRui Paulo }
1048e28a4053SRui Paulo 
1049e28a4053SRui Paulo 
105085732ac8SCy Schubert struct eapol_state_machine *
ieee802_1x_alloc_eapol_sm(struct hostapd_data * hapd,struct sta_info * sta)1051e28a4053SRui Paulo ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
1052e28a4053SRui Paulo {
1053e28a4053SRui Paulo 	int flags = 0;
1054c1d255d3SCy Schubert 
1055e28a4053SRui Paulo 	if (sta->flags & WLAN_STA_PREAUTH)
1056e28a4053SRui Paulo 		flags |= EAPOL_SM_PREAUTH;
1057e28a4053SRui Paulo 	if (sta->wpa_sm) {
1058e28a4053SRui Paulo 		flags |= EAPOL_SM_USES_WPA;
1059e28a4053SRui Paulo 		if (wpa_auth_sta_get_pmksa(sta->wpa_sm))
1060e28a4053SRui Paulo 			flags |= EAPOL_SM_FROM_PMKSA_CACHE;
1061e28a4053SRui Paulo 	}
1062e28a4053SRui Paulo 	return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags,
1063f05cddf9SRui Paulo 				sta->wps_ie, sta->p2p_ie, sta,
1064f05cddf9SRui Paulo 				sta->identity, sta->radius_cui);
1065e28a4053SRui Paulo }
1066e28a4053SRui Paulo 
1067e28a4053SRui Paulo 
ieee802_1x_save_eapol(struct sta_info * sta,const u8 * buf,size_t len,enum frame_encryption encrypted)1068780fb4a2SCy Schubert static void ieee802_1x_save_eapol(struct sta_info *sta, const u8 *buf,
1069*a90b9d01SCy Schubert 				  size_t len, enum frame_encryption encrypted)
1070780fb4a2SCy Schubert {
1071780fb4a2SCy Schubert 	if (sta->pending_eapol_rx) {
1072780fb4a2SCy Schubert 		wpabuf_free(sta->pending_eapol_rx->buf);
1073780fb4a2SCy Schubert 	} else {
1074780fb4a2SCy Schubert 		sta->pending_eapol_rx =
1075780fb4a2SCy Schubert 			os_malloc(sizeof(*sta->pending_eapol_rx));
1076780fb4a2SCy Schubert 		if (!sta->pending_eapol_rx)
1077780fb4a2SCy Schubert 			return;
1078780fb4a2SCy Schubert 	}
1079780fb4a2SCy Schubert 
1080780fb4a2SCy Schubert 	sta->pending_eapol_rx->buf = wpabuf_alloc_copy(buf, len);
1081780fb4a2SCy Schubert 	if (!sta->pending_eapol_rx->buf) {
1082780fb4a2SCy Schubert 		os_free(sta->pending_eapol_rx);
1083780fb4a2SCy Schubert 		sta->pending_eapol_rx = NULL;
1084780fb4a2SCy Schubert 		return;
1085780fb4a2SCy Schubert 	}
1086780fb4a2SCy Schubert 
1087*a90b9d01SCy Schubert 	sta->pending_eapol_rx->encrypted = encrypted;
1088780fb4a2SCy Schubert 	os_get_reltime(&sta->pending_eapol_rx->rx_time);
1089780fb4a2SCy Schubert }
1090780fb4a2SCy Schubert 
1091780fb4a2SCy Schubert 
ieee802_1x_check_encryption(struct sta_info * sta,enum frame_encryption encrypted,u8 type)1092*a90b9d01SCy Schubert static bool ieee802_1x_check_encryption(struct sta_info *sta,
1093*a90b9d01SCy Schubert 					enum frame_encryption encrypted,
1094*a90b9d01SCy Schubert 					u8 type)
1095*a90b9d01SCy Schubert {
1096*a90b9d01SCy Schubert 	if (encrypted != FRAME_NOT_ENCRYPTED)
1097*a90b9d01SCy Schubert 		return true;
1098*a90b9d01SCy Schubert 	if (type != IEEE802_1X_TYPE_EAP_PACKET &&
1099*a90b9d01SCy Schubert 	    type != IEEE802_1X_TYPE_EAPOL_START &&
1100*a90b9d01SCy Schubert 	    type != IEEE802_1X_TYPE_EAPOL_LOGOFF)
1101*a90b9d01SCy Schubert 		return true;
1102*a90b9d01SCy Schubert 	if (!(sta->flags & WLAN_STA_MFP))
1103*a90b9d01SCy Schubert 		return true;
1104*a90b9d01SCy Schubert 	return !wpa_auth_pairwise_set(sta->wpa_sm);
1105*a90b9d01SCy Schubert }
1106*a90b9d01SCy Schubert 
1107*a90b9d01SCy Schubert 
1108e28a4053SRui Paulo /**
1109e28a4053SRui Paulo  * ieee802_1x_receive - Process the EAPOL frames from the Supplicant
1110e28a4053SRui Paulo  * @hapd: hostapd BSS data
1111e28a4053SRui Paulo  * @sa: Source address (sender of the EAPOL frame)
1112e28a4053SRui Paulo  * @buf: EAPOL frame
1113e28a4053SRui Paulo  * @len: Length of buf in octets
1114*a90b9d01SCy Schubert  * @encrypted: Whether the frame was encrypted
1115e28a4053SRui Paulo  *
1116e28a4053SRui Paulo  * This function is called for each incoming EAPOL frame from the interface
1117e28a4053SRui Paulo  */
ieee802_1x_receive(struct hostapd_data * hapd,const u8 * sa,const u8 * buf,size_t len,enum frame_encryption encrypted)1118e28a4053SRui Paulo void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
1119*a90b9d01SCy Schubert 			size_t len, enum frame_encryption encrypted)
1120e28a4053SRui Paulo {
1121e28a4053SRui Paulo 	struct sta_info *sta;
1122e28a4053SRui Paulo 	struct ieee802_1x_hdr *hdr;
1123e28a4053SRui Paulo 	struct ieee802_1x_eapol_key *key;
1124e28a4053SRui Paulo 	u16 datalen;
1125e28a4053SRui Paulo 	struct rsn_pmksa_cache_entry *pmksa;
1126f05cddf9SRui Paulo 	int key_mgmt;
1127e28a4053SRui Paulo 
11285b9c547cSRui Paulo 	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen &&
1129e28a4053SRui Paulo 	    !hapd->conf->wps_state)
1130e28a4053SRui Paulo 		return;
1131e28a4053SRui Paulo 
1132*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR
1133*a90b9d01SCy Schubert 		   " (encrypted=%d)",
1134*a90b9d01SCy Schubert 		   (unsigned long) len, MAC2STR(sa), encrypted);
1135e28a4053SRui Paulo 	sta = ap_get_sta(hapd, sa);
1136f05cddf9SRui Paulo 	if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) &&
1137f05cddf9SRui Paulo 		     !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) {
1138c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG,
1139c1d255d3SCy Schubert 			   "IEEE 802.1X data frame from not associated/Pre-authenticating STA");
1140780fb4a2SCy Schubert 
1141780fb4a2SCy Schubert 		if (sta && (sta->flags & WLAN_STA_AUTH)) {
1142780fb4a2SCy Schubert 			wpa_printf(MSG_DEBUG, "Saving EAPOL frame from " MACSTR
1143780fb4a2SCy Schubert 				   " for later use", MAC2STR(sta->addr));
1144*a90b9d01SCy Schubert 			ieee802_1x_save_eapol(sta, buf, len, encrypted);
1145780fb4a2SCy Schubert 		}
1146780fb4a2SCy Schubert 
1147e28a4053SRui Paulo 		return;
1148e28a4053SRui Paulo 	}
1149e28a4053SRui Paulo 
1150e28a4053SRui Paulo 	if (len < sizeof(*hdr)) {
11515b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "   too short IEEE 802.1X packet");
1152e28a4053SRui Paulo 		return;
1153e28a4053SRui Paulo 	}
1154e28a4053SRui Paulo 
1155e28a4053SRui Paulo 	hdr = (struct ieee802_1x_hdr *) buf;
1156e28a4053SRui Paulo 	datalen = be_to_host16(hdr->length);
1157e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "   IEEE 802.1X: version=%d type=%d length=%d",
1158e28a4053SRui Paulo 		   hdr->version, hdr->type, datalen);
1159e28a4053SRui Paulo 
1160e28a4053SRui Paulo 	if (len - sizeof(*hdr) < datalen) {
1161c1d255d3SCy Schubert 		wpa_printf(MSG_INFO,
1162c1d255d3SCy Schubert 			   "   frame too short for this IEEE 802.1X packet");
1163e28a4053SRui Paulo 		if (sta->eapol_sm)
1164e28a4053SRui Paulo 			sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++;
1165e28a4053SRui Paulo 		return;
1166e28a4053SRui Paulo 	}
1167e28a4053SRui Paulo 	if (len - sizeof(*hdr) > datalen) {
1168c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG,
1169c1d255d3SCy Schubert 			   "   ignoring %lu extra octets after IEEE 802.1X packet",
1170e28a4053SRui Paulo 			   (unsigned long) len - sizeof(*hdr) - datalen);
1171e28a4053SRui Paulo 	}
1172e28a4053SRui Paulo 
1173e28a4053SRui Paulo 	if (sta->eapol_sm) {
1174e28a4053SRui Paulo 		sta->eapol_sm->dot1xAuthLastEapolFrameVersion = hdr->version;
1175e28a4053SRui Paulo 		sta->eapol_sm->dot1xAuthEapolFramesRx++;
1176e28a4053SRui Paulo 	}
1177e28a4053SRui Paulo 
1178e28a4053SRui Paulo 	key = (struct ieee802_1x_eapol_key *) (hdr + 1);
1179e28a4053SRui Paulo 	if (datalen >= sizeof(struct ieee802_1x_eapol_key) &&
1180e28a4053SRui Paulo 	    hdr->type == IEEE802_1X_TYPE_EAPOL_KEY &&
1181e28a4053SRui Paulo 	    (key->type == EAPOL_KEY_TYPE_WPA ||
1182e28a4053SRui Paulo 	     key->type == EAPOL_KEY_TYPE_RSN)) {
1183e28a4053SRui Paulo 		wpa_receive(hapd->wpa_auth, sta->wpa_sm, (u8 *) hdr,
1184e28a4053SRui Paulo 			    sizeof(*hdr) + datalen);
1185e28a4053SRui Paulo 		return;
1186e28a4053SRui Paulo 	}
1187e28a4053SRui Paulo 
11885b9c547cSRui Paulo 	if (!hapd->conf->ieee802_1x && !hapd->conf->osen &&
1189f05cddf9SRui Paulo 	    !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
1190c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG,
1191c1d255d3SCy Schubert 			   "IEEE 802.1X: Ignore EAPOL message - 802.1X not enabled and WPS not used");
1192e28a4053SRui Paulo 		return;
1193f05cddf9SRui Paulo 	}
1194f05cddf9SRui Paulo 
1195f05cddf9SRui Paulo 	key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
119685732ac8SCy Schubert 	if (key_mgmt != -1 &&
119785732ac8SCy Schubert 	    (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
119885732ac8SCy Schubert 	     key_mgmt == WPA_KEY_MGMT_DPP)) {
1199c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG,
1200c1d255d3SCy Schubert 			   "IEEE 802.1X: Ignore EAPOL message - STA is using PSK");
1201f05cddf9SRui Paulo 		return;
1202f05cddf9SRui Paulo 	}
1203e28a4053SRui Paulo 
1204*a90b9d01SCy Schubert 	if (!ieee802_1x_check_encryption(sta, encrypted, hdr->type)) {
1205*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
1206*a90b9d01SCy Schubert 			   "IEEE 802.1X: Discard unencrypted EAPOL message - encryption was expected");
1207*a90b9d01SCy Schubert 		return;
1208*a90b9d01SCy Schubert 	}
1209*a90b9d01SCy Schubert 
1210e28a4053SRui Paulo 	if (!sta->eapol_sm) {
1211e28a4053SRui Paulo 		sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
1212e28a4053SRui Paulo 		if (!sta->eapol_sm)
1213e28a4053SRui Paulo 			return;
1214e28a4053SRui Paulo 
1215e28a4053SRui Paulo #ifdef CONFIG_WPS
12165b9c547cSRui Paulo 		if (!hapd->conf->ieee802_1x && hapd->conf->wps_state) {
1217f05cddf9SRui Paulo 			u32 wflags = sta->flags & (WLAN_STA_WPS |
1218f05cddf9SRui Paulo 						   WLAN_STA_WPS2 |
1219f05cddf9SRui Paulo 						   WLAN_STA_MAYBE_WPS);
1220f05cddf9SRui Paulo 			if (wflags == WLAN_STA_MAYBE_WPS ||
1221f05cddf9SRui Paulo 			    wflags == (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) {
1222e28a4053SRui Paulo 				/*
1223f05cddf9SRui Paulo 				 * Delay EAPOL frame transmission until a
1224f05cddf9SRui Paulo 				 * possible WPS STA initiates the handshake
1225f05cddf9SRui Paulo 				 * with EAPOL-Start. Only allow the wait to be
1226f05cddf9SRui Paulo 				 * skipped if the STA is known to support WPS
1227f05cddf9SRui Paulo 				 * 2.0.
1228e28a4053SRui Paulo 				 */
1229c1d255d3SCy Schubert 				wpa_printf(MSG_DEBUG,
1230c1d255d3SCy Schubert 					   "WPS: Do not start EAPOL until EAPOL-Start is received");
1231e28a4053SRui Paulo 				sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
1232e28a4053SRui Paulo 			}
1233f05cddf9SRui Paulo 		}
1234e28a4053SRui Paulo #endif /* CONFIG_WPS */
1235e28a4053SRui Paulo 
1236c1d255d3SCy Schubert 		sta->eapol_sm->eap_if->portEnabled = true;
1237e28a4053SRui Paulo 	}
1238e28a4053SRui Paulo 
1239e28a4053SRui Paulo 	/* since we support version 1, we can ignore version field and proceed
1240e28a4053SRui Paulo 	 * as specified in version 1 standard [IEEE Std 802.1X-2001, 7.5.5] */
1241e28a4053SRui Paulo 	/* TODO: actually, we are not version 1 anymore.. However, Version 2
1242e28a4053SRui Paulo 	 * does not change frame contents, so should be ok to process frames
1243e28a4053SRui Paulo 	 * more or less identically. Some changes might be needed for
1244e28a4053SRui Paulo 	 * verification of fields. */
1245e28a4053SRui Paulo 
1246e28a4053SRui Paulo 	switch (hdr->type) {
1247e28a4053SRui Paulo 	case IEEE802_1X_TYPE_EAP_PACKET:
1248e28a4053SRui Paulo 		handle_eap(hapd, sta, (u8 *) (hdr + 1), datalen);
1249e28a4053SRui Paulo 		break;
1250e28a4053SRui Paulo 
1251e28a4053SRui Paulo 	case IEEE802_1X_TYPE_EAPOL_START:
1252e28a4053SRui Paulo 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
1253c1d255d3SCy Schubert 			       HOSTAPD_LEVEL_DEBUG,
1254c1d255d3SCy Schubert 			       "received EAPOL-Start from STA");
1255e28a4053SRui Paulo 		sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
1256e28a4053SRui Paulo 		pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
1257e28a4053SRui Paulo 		if (pmksa) {
1258e28a4053SRui Paulo 			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
1259c1d255d3SCy Schubert 				       HOSTAPD_LEVEL_DEBUG,
1260c1d255d3SCy Schubert 				       "cached PMKSA available - ignore it since STA sent EAPOL-Start");
1261e28a4053SRui Paulo 			wpa_auth_sta_clear_pmksa(sta->wpa_sm, pmksa);
1262e28a4053SRui Paulo 		}
1263c1d255d3SCy Schubert 		sta->eapol_sm->eapolStart = true;
1264e28a4053SRui Paulo 		sta->eapol_sm->dot1xAuthEapolStartFramesRx++;
1265f05cddf9SRui Paulo 		eap_server_clear_identity(sta->eapol_sm->eap);
1266e28a4053SRui Paulo 		wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
1267e28a4053SRui Paulo 		break;
1268e28a4053SRui Paulo 
1269e28a4053SRui Paulo 	case IEEE802_1X_TYPE_EAPOL_LOGOFF:
1270e28a4053SRui Paulo 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
1271c1d255d3SCy Schubert 			       HOSTAPD_LEVEL_DEBUG,
1272c1d255d3SCy Schubert 			       "received EAPOL-Logoff from STA");
1273e28a4053SRui Paulo 		sta->acct_terminate_cause =
1274e28a4053SRui Paulo 			RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
1275e28a4053SRui Paulo 		accounting_sta_stop(hapd, sta);
1276c1d255d3SCy Schubert 		sta->eapol_sm->eapolLogoff = true;
1277e28a4053SRui Paulo 		sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++;
1278f05cddf9SRui Paulo 		eap_server_clear_identity(sta->eapol_sm->eap);
1279e28a4053SRui Paulo 		break;
1280e28a4053SRui Paulo 
1281e28a4053SRui Paulo 	case IEEE802_1X_TYPE_EAPOL_KEY:
1282e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "   EAPOL-Key");
1283f05cddf9SRui Paulo 		if (!ap_sta_is_authorized(sta)) {
1284c1d255d3SCy Schubert 			wpa_printf(MSG_DEBUG,
1285c1d255d3SCy Schubert 				   "   Dropped key data from unauthorized Supplicant");
1286e28a4053SRui Paulo 			break;
1287e28a4053SRui Paulo 		}
1288e28a4053SRui Paulo 		break;
1289e28a4053SRui Paulo 
1290e28a4053SRui Paulo 	case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT:
1291e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "   EAPOL-Encapsulated-ASF-Alert");
1292e28a4053SRui Paulo 		/* TODO: implement support for this; show data */
1293e28a4053SRui Paulo 		break;
1294e28a4053SRui Paulo 
1295206b73d0SCy Schubert #ifdef CONFIG_MACSEC
1296206b73d0SCy Schubert 	case IEEE802_1X_TYPE_EAPOL_MKA:
1297206b73d0SCy Schubert 		wpa_printf(MSG_EXCESSIVE,
1298206b73d0SCy Schubert 			   "EAPOL type %d will be handled by MKA", hdr->type);
1299206b73d0SCy Schubert 		break;
1300206b73d0SCy Schubert #endif /* CONFIG_MACSEC */
1301206b73d0SCy Schubert 
1302e28a4053SRui Paulo 	default:
1303e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "   unknown IEEE 802.1X packet type");
1304e28a4053SRui Paulo 		sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++;
1305e28a4053SRui Paulo 		break;
1306e28a4053SRui Paulo 	}
1307e28a4053SRui Paulo 
1308e28a4053SRui Paulo 	eapol_auth_step(sta->eapol_sm);
1309e28a4053SRui Paulo }
1310e28a4053SRui Paulo 
1311e28a4053SRui Paulo 
1312e28a4053SRui Paulo /**
1313e28a4053SRui Paulo  * ieee802_1x_new_station - Start IEEE 802.1X authentication
1314e28a4053SRui Paulo  * @hapd: hostapd BSS data
1315e28a4053SRui Paulo  * @sta: The station
1316e28a4053SRui Paulo  *
1317e28a4053SRui Paulo  * This function is called to start IEEE 802.1X authentication when a new
1318e28a4053SRui Paulo  * station completes IEEE 802.11 association.
1319e28a4053SRui Paulo  */
ieee802_1x_new_station(struct hostapd_data * hapd,struct sta_info * sta)1320e28a4053SRui Paulo void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
1321e28a4053SRui Paulo {
1322e28a4053SRui Paulo 	struct rsn_pmksa_cache_entry *pmksa;
1323e28a4053SRui Paulo 	int reassoc = 1;
1324e28a4053SRui Paulo 	int force_1x = 0;
1325f05cddf9SRui Paulo 	int key_mgmt;
1326e28a4053SRui Paulo 
1327e28a4053SRui Paulo #ifdef CONFIG_WPS
13285b9c547cSRui Paulo 	if (hapd->conf->wps_state &&
13295b9c547cSRui Paulo 	    ((hapd->conf->wpa && (sta->flags & WLAN_STA_MAYBE_WPS)) ||
13305b9c547cSRui Paulo 	     (sta->flags & WLAN_STA_WPS))) {
1331e28a4053SRui Paulo 		/*
1332e28a4053SRui Paulo 		 * Need to enable IEEE 802.1X/EAPOL state machines for possible
1333e28a4053SRui Paulo 		 * WPS handshake even if IEEE 802.1X/EAPOL is not used for
1334e28a4053SRui Paulo 		 * authentication in this BSS.
1335e28a4053SRui Paulo 		 */
1336e28a4053SRui Paulo 		force_1x = 1;
1337e28a4053SRui Paulo 	}
1338e28a4053SRui Paulo #endif /* CONFIG_WPS */
1339e28a4053SRui Paulo 
13405b9c547cSRui Paulo 	if (!force_1x && !hapd->conf->ieee802_1x && !hapd->conf->osen) {
1341c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG,
1342c1d255d3SCy Schubert 			   "IEEE 802.1X: Ignore STA - 802.1X not enabled or forced for WPS");
1343f05cddf9SRui Paulo 		/*
1344f05cddf9SRui Paulo 		 * Clear any possible EAPOL authenticator state to support
1345f05cddf9SRui Paulo 		 * reassociation change from WPS to PSK.
1346f05cddf9SRui Paulo 		 */
1347780fb4a2SCy Schubert 		ieee802_1x_free_station(hapd, sta);
1348e28a4053SRui Paulo 		return;
1349f05cddf9SRui Paulo 	}
1350f05cddf9SRui Paulo 
1351f05cddf9SRui Paulo 	key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
135285732ac8SCy Schubert 	if (key_mgmt != -1 &&
135385732ac8SCy Schubert 	    (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
135485732ac8SCy Schubert 	     key_mgmt == WPA_KEY_MGMT_DPP)) {
1355f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK");
1356f05cddf9SRui Paulo 		/*
1357f05cddf9SRui Paulo 		 * Clear any possible EAPOL authenticator state to support
1358f05cddf9SRui Paulo 		 * reassociation change from WPA-EAP to PSK.
1359f05cddf9SRui Paulo 		 */
1360780fb4a2SCy Schubert 		ieee802_1x_free_station(hapd, sta);
1361f05cddf9SRui Paulo 		return;
1362f05cddf9SRui Paulo 	}
1363e28a4053SRui Paulo 
1364c1d255d3SCy Schubert 	if (!sta->eapol_sm) {
1365e28a4053SRui Paulo 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
1366e28a4053SRui Paulo 			       HOSTAPD_LEVEL_DEBUG, "start authentication");
1367e28a4053SRui Paulo 		sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
1368c1d255d3SCy Schubert 		if (!sta->eapol_sm) {
1369e28a4053SRui Paulo 			hostapd_logger(hapd, sta->addr,
1370e28a4053SRui Paulo 				       HOSTAPD_MODULE_IEEE8021X,
1371e28a4053SRui Paulo 				       HOSTAPD_LEVEL_INFO,
1372e28a4053SRui Paulo 				       "failed to allocate state machine");
1373e28a4053SRui Paulo 			return;
1374e28a4053SRui Paulo 		}
1375e28a4053SRui Paulo 		reassoc = 0;
1376e28a4053SRui Paulo 	}
1377e28a4053SRui Paulo 
1378e28a4053SRui Paulo #ifdef CONFIG_WPS
1379e28a4053SRui Paulo 	sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
13805b9c547cSRui Paulo 	if (!hapd->conf->ieee802_1x && hapd->conf->wps_state &&
13815b9c547cSRui Paulo 	    !(sta->flags & WLAN_STA_WPS2)) {
1382e28a4053SRui Paulo 		/*
1383f05cddf9SRui Paulo 		 * Delay EAPOL frame transmission until a possible WPS STA
1384f05cddf9SRui Paulo 		 * initiates the handshake with EAPOL-Start. Only allow the
1385f05cddf9SRui Paulo 		 * wait to be skipped if the STA is known to support WPS 2.0.
1386e28a4053SRui Paulo 		 */
1387c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG,
1388c1d255d3SCy Schubert 			   "WPS: Do not start EAPOL until EAPOL-Start is received");
1389e28a4053SRui Paulo 		sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
1390e28a4053SRui Paulo 	}
1391e28a4053SRui Paulo #endif /* CONFIG_WPS */
1392e28a4053SRui Paulo 
1393c1d255d3SCy Schubert 	sta->eapol_sm->eap_if->portEnabled = true;
1394e28a4053SRui Paulo 
139585732ac8SCy Schubert #ifdef CONFIG_IEEE80211R_AP
1396f05cddf9SRui Paulo 	if (sta->auth_alg == WLAN_AUTH_FT) {
1397f05cddf9SRui Paulo 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
1398f05cddf9SRui Paulo 			       HOSTAPD_LEVEL_DEBUG,
1399f05cddf9SRui Paulo 			       "PMK from FT - skip IEEE 802.1X/EAP");
1400f05cddf9SRui Paulo 		/* Setup EAPOL state machines to already authenticated state
1401f05cddf9SRui Paulo 		 * because of existing FT information from R0KH. */
1402c1d255d3SCy Schubert 		sta->eapol_sm->keyRun = true;
1403c1d255d3SCy Schubert 		sta->eapol_sm->eap_if->eapKeyAvailable = true;
1404f05cddf9SRui Paulo 		sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
1405f05cddf9SRui Paulo 		sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
1406c1d255d3SCy Schubert 		sta->eapol_sm->authSuccess = true;
1407c1d255d3SCy Schubert 		sta->eapol_sm->authFail = false;
1408c1d255d3SCy Schubert 		sta->eapol_sm->portValid = true;
1409f05cddf9SRui Paulo 		if (sta->eapol_sm->eap)
1410f05cddf9SRui Paulo 			eap_sm_notify_cached(sta->eapol_sm->eap);
141185732ac8SCy Schubert 		ap_sta_bind_vlan(hapd, sta);
1412f05cddf9SRui Paulo 		return;
1413f05cddf9SRui Paulo 	}
141485732ac8SCy Schubert #endif /* CONFIG_IEEE80211R_AP */
141585732ac8SCy Schubert 
141685732ac8SCy Schubert #ifdef CONFIG_FILS
141785732ac8SCy Schubert 	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
141885732ac8SCy Schubert 	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
141985732ac8SCy Schubert 	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
142085732ac8SCy Schubert 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
142185732ac8SCy Schubert 			       HOSTAPD_LEVEL_DEBUG,
142285732ac8SCy Schubert 			       "PMK from FILS - skip IEEE 802.1X/EAP");
142385732ac8SCy Schubert 		/* Setup EAPOL state machines to already authenticated state
142485732ac8SCy Schubert 		 * because of existing FILS information. */
1425c1d255d3SCy Schubert 		sta->eapol_sm->keyRun = true;
1426c1d255d3SCy Schubert 		sta->eapol_sm->eap_if->eapKeyAvailable = true;
142785732ac8SCy Schubert 		sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
142885732ac8SCy Schubert 		sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
1429c1d255d3SCy Schubert 		sta->eapol_sm->authSuccess = true;
1430c1d255d3SCy Schubert 		sta->eapol_sm->authFail = false;
1431c1d255d3SCy Schubert 		sta->eapol_sm->portValid = true;
143285732ac8SCy Schubert 		if (sta->eapol_sm->eap)
143385732ac8SCy Schubert 			eap_sm_notify_cached(sta->eapol_sm->eap);
14344bc52338SCy Schubert 		wpa_auth_set_ptk_rekey_timer(sta->wpa_sm);
143585732ac8SCy Schubert 		return;
143685732ac8SCy Schubert 	}
143785732ac8SCy Schubert #endif /* CONFIG_FILS */
1438f05cddf9SRui Paulo 
1439e28a4053SRui Paulo 	pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
1440e28a4053SRui Paulo 	if (pmksa) {
1441e28a4053SRui Paulo 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
1442e28a4053SRui Paulo 			       HOSTAPD_LEVEL_DEBUG,
1443e28a4053SRui Paulo 			       "PMK from PMKSA cache - skip IEEE 802.1X/EAP");
1444e28a4053SRui Paulo 		/* Setup EAPOL state machines to already authenticated state
1445e28a4053SRui Paulo 		 * because of existing PMKSA information in the cache. */
1446c1d255d3SCy Schubert 		sta->eapol_sm->keyRun = true;
1447c1d255d3SCy Schubert 		sta->eapol_sm->eap_if->eapKeyAvailable = true;
1448e28a4053SRui Paulo 		sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
1449e28a4053SRui Paulo 		sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
1450c1d255d3SCy Schubert 		sta->eapol_sm->authSuccess = true;
1451c1d255d3SCy Schubert 		sta->eapol_sm->authFail = false;
1452e28a4053SRui Paulo 		if (sta->eapol_sm->eap)
1453e28a4053SRui Paulo 			eap_sm_notify_cached(sta->eapol_sm->eap);
1454780fb4a2SCy Schubert 		pmksa_cache_to_eapol_data(hapd, pmksa, sta->eapol_sm);
1455325151a3SRui Paulo 		ap_sta_bind_vlan(hapd, sta);
1456e28a4053SRui Paulo 	} else {
1457e28a4053SRui Paulo 		if (reassoc) {
1458e28a4053SRui Paulo 			/*
1459e28a4053SRui Paulo 			 * Force EAPOL state machines to start
1460e28a4053SRui Paulo 			 * re-authentication without having to wait for the
1461e28a4053SRui Paulo 			 * Supplicant to send EAPOL-Start.
1462e28a4053SRui Paulo 			 */
1463c1d255d3SCy Schubert 			sta->eapol_sm->reAuthenticate = true;
1464e28a4053SRui Paulo 		}
1465e28a4053SRui Paulo 		eapol_auth_step(sta->eapol_sm);
1466e28a4053SRui Paulo 	}
1467e28a4053SRui Paulo }
1468e28a4053SRui Paulo 
1469e28a4053SRui Paulo 
ieee802_1x_free_station(struct hostapd_data * hapd,struct sta_info * sta)1470780fb4a2SCy Schubert void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta)
1471e28a4053SRui Paulo {
1472e28a4053SRui Paulo 	struct eapol_state_machine *sm = sta->eapol_sm;
1473e28a4053SRui Paulo 
1474780fb4a2SCy Schubert #ifdef CONFIG_HS20
1475780fb4a2SCy Schubert 	eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
1476780fb4a2SCy Schubert #endif /* CONFIG_HS20 */
1477780fb4a2SCy Schubert 
1478780fb4a2SCy Schubert 	if (sta->pending_eapol_rx) {
1479780fb4a2SCy Schubert 		wpabuf_free(sta->pending_eapol_rx->buf);
1480780fb4a2SCy Schubert 		os_free(sta->pending_eapol_rx);
1481780fb4a2SCy Schubert 		sta->pending_eapol_rx = NULL;
1482780fb4a2SCy Schubert 	}
1483780fb4a2SCy Schubert 
1484c1d255d3SCy Schubert 	if (!sm)
1485e28a4053SRui Paulo 		return;
1486e28a4053SRui Paulo 
1487e28a4053SRui Paulo 	sta->eapol_sm = NULL;
1488e28a4053SRui Paulo 
1489e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
1490e28a4053SRui Paulo 	radius_msg_free(sm->last_recv_radius);
1491e28a4053SRui Paulo 	radius_free_class(&sm->radius_class);
1492e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
1493e28a4053SRui Paulo 
1494e28a4053SRui Paulo 	eapol_auth_free(sm);
1495e28a4053SRui Paulo }
1496e28a4053SRui Paulo 
1497e28a4053SRui Paulo 
1498e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
ieee802_1x_decapsulate_radius(struct hostapd_data * hapd,struct sta_info * sta)1499e28a4053SRui Paulo static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd,
1500e28a4053SRui Paulo 					  struct sta_info *sta)
1501e28a4053SRui Paulo {
1502f05cddf9SRui Paulo 	struct wpabuf *eap;
1503f05cddf9SRui Paulo 	const struct eap_hdr *hdr;
1504e28a4053SRui Paulo 	int eap_type = -1;
1505e28a4053SRui Paulo 	char buf[64];
1506e28a4053SRui Paulo 	struct radius_msg *msg;
1507e28a4053SRui Paulo 	struct eapol_state_machine *sm = sta->eapol_sm;
1508e28a4053SRui Paulo 
1509c1d255d3SCy Schubert 	if (!sm || !sm->last_recv_radius) {
1510e28a4053SRui Paulo 		if (sm)
1511c1d255d3SCy Schubert 			sm->eap_if->aaaEapNoReq = true;
1512e28a4053SRui Paulo 		return;
1513e28a4053SRui Paulo 	}
1514e28a4053SRui Paulo 
1515e28a4053SRui Paulo 	msg = sm->last_recv_radius;
1516e28a4053SRui Paulo 
1517f05cddf9SRui Paulo 	eap = radius_msg_get_eap(msg);
1518c1d255d3SCy Schubert 	if (!eap) {
1519e28a4053SRui Paulo 		/* RFC 3579, Chap. 2.6.3:
1520e28a4053SRui Paulo 		 * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
1521e28a4053SRui Paulo 		 * attribute */
1522e28a4053SRui Paulo 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
1523c1d255d3SCy Schubert 			       HOSTAPD_LEVEL_WARNING,
1524c1d255d3SCy Schubert 			       "could not extract EAP-Message from RADIUS message");
1525c1d255d3SCy Schubert 		sm->eap_if->aaaEapNoReq = true;
1526e28a4053SRui Paulo 		return;
1527e28a4053SRui Paulo 	}
1528e28a4053SRui Paulo 
1529f05cddf9SRui Paulo 	if (wpabuf_len(eap) < sizeof(*hdr)) {
1530e28a4053SRui Paulo 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
1531c1d255d3SCy Schubert 			       HOSTAPD_LEVEL_WARNING,
1532c1d255d3SCy Schubert 			       "too short EAP packet received from authentication server");
1533f05cddf9SRui Paulo 		wpabuf_free(eap);
1534c1d255d3SCy Schubert 		sm->eap_if->aaaEapNoReq = true;
1535e28a4053SRui Paulo 		return;
1536e28a4053SRui Paulo 	}
1537e28a4053SRui Paulo 
1538f05cddf9SRui Paulo 	if (wpabuf_len(eap) > sizeof(*hdr))
1539f05cddf9SRui Paulo 		eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)];
1540e28a4053SRui Paulo 
1541f05cddf9SRui Paulo 	hdr = wpabuf_head(eap);
1542e28a4053SRui Paulo 	switch (hdr->code) {
1543e28a4053SRui Paulo 	case EAP_CODE_REQUEST:
1544e28a4053SRui Paulo 		if (eap_type >= 0)
1545e28a4053SRui Paulo 			sm->eap_type_authsrv = eap_type;
1546e28a4053SRui Paulo 		os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
15475b9c547cSRui Paulo 			    eap_server_get_name(0, eap_type), eap_type);
1548e28a4053SRui Paulo 		break;
1549e28a4053SRui Paulo 	case EAP_CODE_RESPONSE:
1550e28a4053SRui Paulo 		os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
15515b9c547cSRui Paulo 			    eap_server_get_name(0, eap_type), eap_type);
1552e28a4053SRui Paulo 		break;
1553e28a4053SRui Paulo 	case EAP_CODE_SUCCESS:
1554e28a4053SRui Paulo 		os_strlcpy(buf, "EAP Success", sizeof(buf));
1555e28a4053SRui Paulo 		break;
1556e28a4053SRui Paulo 	case EAP_CODE_FAILURE:
1557e28a4053SRui Paulo 		os_strlcpy(buf, "EAP Failure", sizeof(buf));
1558e28a4053SRui Paulo 		break;
1559e28a4053SRui Paulo 	default:
1560e28a4053SRui Paulo 		os_strlcpy(buf, "unknown EAP code", sizeof(buf));
1561e28a4053SRui Paulo 		break;
1562e28a4053SRui Paulo 	}
1563e28a4053SRui Paulo 	buf[sizeof(buf) - 1] = '\0';
1564e28a4053SRui Paulo 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
1565c1d255d3SCy Schubert 		       HOSTAPD_LEVEL_DEBUG,
1566c1d255d3SCy Schubert 		       "decapsulated EAP packet (code=%d id=%d len=%d) from RADIUS server: %s",
1567e28a4053SRui Paulo 		       hdr->code, hdr->identifier, be_to_host16(hdr->length),
1568e28a4053SRui Paulo 		       buf);
1569c1d255d3SCy Schubert 	sm->eap_if->aaaEapReq = true;
1570e28a4053SRui Paulo 
1571e28a4053SRui Paulo 	wpabuf_free(sm->eap_if->aaaEapReqData);
1572f05cddf9SRui Paulo 	sm->eap_if->aaaEapReqData = eap;
1573e28a4053SRui Paulo }
1574e28a4053SRui Paulo 
1575e28a4053SRui Paulo 
ieee802_1x_get_keys(struct hostapd_data * hapd,struct sta_info * sta,struct radius_msg * msg,struct radius_msg * req,const u8 * shared_secret,size_t shared_secret_len)1576e28a4053SRui Paulo static void ieee802_1x_get_keys(struct hostapd_data *hapd,
1577e28a4053SRui Paulo 				struct sta_info *sta, struct radius_msg *msg,
1578e28a4053SRui Paulo 				struct radius_msg *req,
1579e28a4053SRui Paulo 				const u8 *shared_secret,
1580e28a4053SRui Paulo 				size_t shared_secret_len)
1581e28a4053SRui Paulo {
1582e28a4053SRui Paulo 	struct radius_ms_mppe_keys *keys;
1583206b73d0SCy Schubert 	u8 *buf;
1584206b73d0SCy Schubert 	size_t len;
1585e28a4053SRui Paulo 	struct eapol_state_machine *sm = sta->eapol_sm;
1586c1d255d3SCy Schubert 
1587c1d255d3SCy Schubert 	if (!sm)
1588e28a4053SRui Paulo 		return;
1589e28a4053SRui Paulo 
1590e28a4053SRui Paulo 	keys = radius_msg_get_ms_keys(msg, req, shared_secret,
1591e28a4053SRui Paulo 				      shared_secret_len);
1592e28a4053SRui Paulo 
1593e28a4053SRui Paulo 	if (keys && keys->send && keys->recv) {
1594206b73d0SCy Schubert 		len = keys->send_len + keys->recv_len;
1595e28a4053SRui Paulo 		wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key",
1596e28a4053SRui Paulo 				keys->send, keys->send_len);
1597e28a4053SRui Paulo 		wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key",
1598e28a4053SRui Paulo 				keys->recv, keys->recv_len);
1599e28a4053SRui Paulo 
1600e28a4053SRui Paulo 		os_free(sm->eap_if->aaaEapKeyData);
1601e28a4053SRui Paulo 		sm->eap_if->aaaEapKeyData = os_malloc(len);
1602e28a4053SRui Paulo 		if (sm->eap_if->aaaEapKeyData) {
1603e28a4053SRui Paulo 			os_memcpy(sm->eap_if->aaaEapKeyData, keys->recv,
1604e28a4053SRui Paulo 				  keys->recv_len);
1605e28a4053SRui Paulo 			os_memcpy(sm->eap_if->aaaEapKeyData + keys->recv_len,
1606e28a4053SRui Paulo 				  keys->send, keys->send_len);
1607e28a4053SRui Paulo 			sm->eap_if->aaaEapKeyDataLen = len;
1608c1d255d3SCy Schubert 			sm->eap_if->aaaEapKeyAvailable = true;
1609e28a4053SRui Paulo 		}
16105b9c547cSRui Paulo 	} else {
16115b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
16125b9c547cSRui Paulo 			   "MS-MPPE: 1x_get_keys, could not get keys: %p  send: %p  recv: %p",
16135b9c547cSRui Paulo 			   keys, keys ? keys->send : NULL,
16145b9c547cSRui Paulo 			   keys ? keys->recv : NULL);
1615e28a4053SRui Paulo 	}
1616e28a4053SRui Paulo 
1617e28a4053SRui Paulo 	if (keys) {
1618e28a4053SRui Paulo 		os_free(keys->send);
1619e28a4053SRui Paulo 		os_free(keys->recv);
1620e28a4053SRui Paulo 		os_free(keys);
1621e28a4053SRui Paulo 	}
1622206b73d0SCy Schubert 
1623206b73d0SCy Schubert 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_EAP_KEY_NAME, &buf, &len,
1624206b73d0SCy Schubert 				    NULL) == 0) {
1625206b73d0SCy Schubert 		os_free(sm->eap_if->eapSessionId);
1626206b73d0SCy Schubert 		sm->eap_if->eapSessionId = os_memdup(buf, len);
1627206b73d0SCy Schubert 		if (sm->eap_if->eapSessionId) {
1628206b73d0SCy Schubert 			sm->eap_if->eapSessionIdLen = len;
1629206b73d0SCy Schubert 			wpa_hexdump(MSG_DEBUG, "EAP-Key Name",
1630206b73d0SCy Schubert 				    sm->eap_if->eapSessionId,
1631206b73d0SCy Schubert 				    sm->eap_if->eapSessionIdLen);
1632206b73d0SCy Schubert 		}
1633206b73d0SCy Schubert 	} else {
1634206b73d0SCy Schubert 		sm->eap_if->eapSessionIdLen = 0;
1635206b73d0SCy Schubert 	}
1636e28a4053SRui Paulo }
1637e28a4053SRui Paulo 
1638e28a4053SRui Paulo 
ieee802_1x_store_radius_class(struct hostapd_data * hapd,struct sta_info * sta,struct radius_msg * msg)1639e28a4053SRui Paulo static void ieee802_1x_store_radius_class(struct hostapd_data *hapd,
1640e28a4053SRui Paulo 					  struct sta_info *sta,
1641e28a4053SRui Paulo 					  struct radius_msg *msg)
1642e28a4053SRui Paulo {
1643325151a3SRui Paulo 	u8 *attr_class;
1644e28a4053SRui Paulo 	size_t class_len;
1645e28a4053SRui Paulo 	struct eapol_state_machine *sm = sta->eapol_sm;
1646e28a4053SRui Paulo 	int count, i;
1647e28a4053SRui Paulo 	struct radius_attr_data *nclass;
1648e28a4053SRui Paulo 	size_t nclass_count;
1649e28a4053SRui Paulo 
1650c1d255d3SCy Schubert 	if (!hapd->conf->radius->acct_server || !hapd->radius || !sm)
1651e28a4053SRui Paulo 		return;
1652e28a4053SRui Paulo 
1653e28a4053SRui Paulo 	radius_free_class(&sm->radius_class);
1654e28a4053SRui Paulo 	count = radius_msg_count_attr(msg, RADIUS_ATTR_CLASS, 1);
1655e28a4053SRui Paulo 	if (count <= 0)
1656e28a4053SRui Paulo 		return;
1657e28a4053SRui Paulo 
1658f05cddf9SRui Paulo 	nclass = os_calloc(count, sizeof(struct radius_attr_data));
1659c1d255d3SCy Schubert 	if (!nclass)
1660e28a4053SRui Paulo 		return;
1661e28a4053SRui Paulo 
1662e28a4053SRui Paulo 	nclass_count = 0;
1663e28a4053SRui Paulo 
1664325151a3SRui Paulo 	attr_class = NULL;
1665e28a4053SRui Paulo 	for (i = 0; i < count; i++) {
1666e28a4053SRui Paulo 		do {
1667e28a4053SRui Paulo 			if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS,
1668325151a3SRui Paulo 						    &attr_class, &class_len,
1669325151a3SRui Paulo 						    attr_class) < 0) {
1670e28a4053SRui Paulo 				i = count;
1671e28a4053SRui Paulo 				break;
1672e28a4053SRui Paulo 			}
1673e28a4053SRui Paulo 		} while (class_len < 1);
1674e28a4053SRui Paulo 
167585732ac8SCy Schubert 		nclass[nclass_count].data = os_memdup(attr_class, class_len);
1676c1d255d3SCy Schubert 		if (!nclass[nclass_count].data)
1677e28a4053SRui Paulo 			break;
1678e28a4053SRui Paulo 
1679e28a4053SRui Paulo 		nclass[nclass_count].len = class_len;
1680e28a4053SRui Paulo 		nclass_count++;
1681e28a4053SRui Paulo 	}
1682e28a4053SRui Paulo 
1683e28a4053SRui Paulo 	sm->radius_class.attr = nclass;
1684e28a4053SRui Paulo 	sm->radius_class.count = nclass_count;
1685c1d255d3SCy Schubert 	wpa_printf(MSG_DEBUG,
1686c1d255d3SCy Schubert 		   "IEEE 802.1X: Stored %lu RADIUS Class attributes for "
1687c1d255d3SCy Schubert 		   MACSTR,
1688e28a4053SRui Paulo 		   (unsigned long) sm->radius_class.count,
1689e28a4053SRui Paulo 		   MAC2STR(sta->addr));
1690e28a4053SRui Paulo }
1691e28a4053SRui Paulo 
1692e28a4053SRui Paulo 
1693e28a4053SRui Paulo /* Update sta->identity based on User-Name attribute in Access-Accept */
ieee802_1x_update_sta_identity(struct hostapd_data * hapd,struct sta_info * sta,struct radius_msg * msg)1694e28a4053SRui Paulo static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd,
1695e28a4053SRui Paulo 					   struct sta_info *sta,
1696e28a4053SRui Paulo 					   struct radius_msg *msg)
1697e28a4053SRui Paulo {
1698e28a4053SRui Paulo 	u8 *buf, *identity;
1699e28a4053SRui Paulo 	size_t len;
1700e28a4053SRui Paulo 	struct eapol_state_machine *sm = sta->eapol_sm;
1701e28a4053SRui Paulo 
1702c1d255d3SCy Schubert 	if (!sm)
1703e28a4053SRui Paulo 		return;
1704e28a4053SRui Paulo 
1705e28a4053SRui Paulo 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len,
1706e28a4053SRui Paulo 				    NULL) < 0)
1707e28a4053SRui Paulo 		return;
1708e28a4053SRui Paulo 
17095b9c547cSRui Paulo 	identity = (u8 *) dup_binstr(buf, len);
1710c1d255d3SCy Schubert 	if (!identity)
1711e28a4053SRui Paulo 		return;
1712e28a4053SRui Paulo 
1713e28a4053SRui Paulo 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
1714c1d255d3SCy Schubert 		       HOSTAPD_LEVEL_DEBUG,
1715c1d255d3SCy Schubert 		       "old identity '%s' updated with User-Name from Access-Accept '%s'",
1716e28a4053SRui Paulo 		       sm->identity ? (char *) sm->identity : "N/A",
1717e28a4053SRui Paulo 		       (char *) identity);
1718e28a4053SRui Paulo 
1719e28a4053SRui Paulo 	os_free(sm->identity);
1720e28a4053SRui Paulo 	sm->identity = identity;
1721e28a4053SRui Paulo 	sm->identity_len = len;
1722e28a4053SRui Paulo }
1723e28a4053SRui Paulo 
1724e28a4053SRui Paulo 
1725f05cddf9SRui Paulo /* Update CUI based on Chargeable-User-Identity attribute in Access-Accept */
ieee802_1x_update_sta_cui(struct hostapd_data * hapd,struct sta_info * sta,struct radius_msg * msg)1726f05cddf9SRui Paulo static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd,
1727f05cddf9SRui Paulo 				      struct sta_info *sta,
1728f05cddf9SRui Paulo 				      struct radius_msg *msg)
1729f05cddf9SRui Paulo {
1730f05cddf9SRui Paulo 	struct eapol_state_machine *sm = sta->eapol_sm;
1731f05cddf9SRui Paulo 	struct wpabuf *cui;
1732f05cddf9SRui Paulo 	u8 *buf;
1733f05cddf9SRui Paulo 	size_t len;
1734f05cddf9SRui Paulo 
1735c1d255d3SCy Schubert 	if (!sm)
1736f05cddf9SRui Paulo 		return;
1737f05cddf9SRui Paulo 
1738f05cddf9SRui Paulo 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
1739f05cddf9SRui Paulo 				    &buf, &len, NULL) < 0)
1740f05cddf9SRui Paulo 		return;
1741f05cddf9SRui Paulo 
1742f05cddf9SRui Paulo 	cui = wpabuf_alloc_copy(buf, len);
1743c1d255d3SCy Schubert 	if (!cui)
1744f05cddf9SRui Paulo 		return;
1745f05cddf9SRui Paulo 
1746f05cddf9SRui Paulo 	wpabuf_free(sm->radius_cui);
1747f05cddf9SRui Paulo 	sm->radius_cui = cui;
1748f05cddf9SRui Paulo }
1749f05cddf9SRui Paulo 
1750f05cddf9SRui Paulo 
17515b9c547cSRui Paulo #ifdef CONFIG_HS20
17525b9c547cSRui Paulo 
ieee802_1x_hs20_sub_rem(struct sta_info * sta,u8 * pos,size_t len)17535b9c547cSRui Paulo static void ieee802_1x_hs20_sub_rem(struct sta_info *sta, u8 *pos, size_t len)
17545b9c547cSRui Paulo {
17555b9c547cSRui Paulo 	sta->remediation = 1;
17565b9c547cSRui Paulo 	os_free(sta->remediation_url);
17575b9c547cSRui Paulo 	if (len > 2) {
17585b9c547cSRui Paulo 		sta->remediation_url = os_malloc(len);
17595b9c547cSRui Paulo 		if (!sta->remediation_url)
17605b9c547cSRui Paulo 			return;
17615b9c547cSRui Paulo 		sta->remediation_method = pos[0];
17625b9c547cSRui Paulo 		os_memcpy(sta->remediation_url, pos + 1, len - 1);
17635b9c547cSRui Paulo 		sta->remediation_url[len - 1] = '\0';
1764c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG,
1765c1d255d3SCy Schubert 			   "HS 2.0: Subscription remediation needed for "
1766c1d255d3SCy Schubert 			   MACSTR " - server method %u URL %s",
17675b9c547cSRui Paulo 			   MAC2STR(sta->addr), sta->remediation_method,
17685b9c547cSRui Paulo 			   sta->remediation_url);
17695b9c547cSRui Paulo 	} else {
17705b9c547cSRui Paulo 		sta->remediation_url = NULL;
1771c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG,
1772c1d255d3SCy Schubert 			   "HS 2.0: Subscription remediation needed for "
1773c1d255d3SCy Schubert 			   MACSTR, MAC2STR(sta->addr));
17745b9c547cSRui Paulo 	}
17755b9c547cSRui Paulo 	/* TODO: assign the STA into remediation VLAN or add filtering */
17765b9c547cSRui Paulo }
17775b9c547cSRui Paulo 
17785b9c547cSRui Paulo 
ieee802_1x_hs20_deauth_req(struct hostapd_data * hapd,struct sta_info * sta,const u8 * pos,size_t len)17795b9c547cSRui Paulo static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd,
1780*a90b9d01SCy Schubert 				       struct sta_info *sta, const u8 *pos,
17815b9c547cSRui Paulo 				       size_t len)
17825b9c547cSRui Paulo {
1783*a90b9d01SCy Schubert 	size_t url_len;
1784*a90b9d01SCy Schubert 	unsigned int timeout;
1785*a90b9d01SCy Schubert 
17865b9c547cSRui Paulo 	if (len < 3)
17875b9c547cSRui Paulo 		return; /* Malformed information */
1788*a90b9d01SCy Schubert 	url_len = len - 3;
17895b9c547cSRui Paulo 	sta->hs20_deauth_requested = 1;
1790*a90b9d01SCy Schubert 	sta->hs20_deauth_on_ack = url_len == 0;
1791c1d255d3SCy Schubert 	wpa_printf(MSG_DEBUG,
1792*a90b9d01SCy Schubert 		   "HS 2.0: Deauthentication request - Code %u  Re-auth Delay %u  URL length %zu",
1793*a90b9d01SCy Schubert 		   *pos, WPA_GET_LE16(pos + 1), url_len);
17945b9c547cSRui Paulo 	wpabuf_free(sta->hs20_deauth_req);
17955b9c547cSRui Paulo 	sta->hs20_deauth_req = wpabuf_alloc(len + 1);
17965b9c547cSRui Paulo 	if (sta->hs20_deauth_req) {
17975b9c547cSRui Paulo 		wpabuf_put_data(sta->hs20_deauth_req, pos, 3);
1798*a90b9d01SCy Schubert 		wpabuf_put_u8(sta->hs20_deauth_req, url_len);
1799*a90b9d01SCy Schubert 		wpabuf_put_data(sta->hs20_deauth_req, pos + 3, url_len);
18005b9c547cSRui Paulo 	}
1801*a90b9d01SCy Schubert 	timeout = hapd->conf->hs20_deauth_req_timeout;
1802*a90b9d01SCy Schubert 	/* If there is no URL, no need to provide time to fetch it. Use a short
1803*a90b9d01SCy Schubert 	 * timeout here to allow maximum time for completing 4-way handshake and
1804*a90b9d01SCy Schubert 	 * WNM-Notification delivery. Acknowledgement of the frame will result
1805*a90b9d01SCy Schubert 	 * in cutting this wait further. */
1806*a90b9d01SCy Schubert 	if (!url_len && timeout > 2)
1807*a90b9d01SCy Schubert 		timeout = 2;
1808*a90b9d01SCy Schubert 	ap_sta_session_timeout(hapd, sta, timeout);
18095b9c547cSRui Paulo }
18105b9c547cSRui Paulo 
18115b9c547cSRui Paulo 
ieee802_1x_hs20_session_info(struct hostapd_data * hapd,struct sta_info * sta,u8 * pos,size_t len,int session_timeout)18125b9c547cSRui Paulo static void ieee802_1x_hs20_session_info(struct hostapd_data *hapd,
18135b9c547cSRui Paulo 					 struct sta_info *sta, u8 *pos,
18145b9c547cSRui Paulo 					 size_t len, int session_timeout)
18155b9c547cSRui Paulo {
18165b9c547cSRui Paulo 	unsigned int swt;
18175b9c547cSRui Paulo 	int warning_time, beacon_int;
18185b9c547cSRui Paulo 
18195b9c547cSRui Paulo 	if (len < 1)
18205b9c547cSRui Paulo 		return; /* Malformed information */
18215b9c547cSRui Paulo 	os_free(sta->hs20_session_info_url);
18225b9c547cSRui Paulo 	sta->hs20_session_info_url = os_malloc(len);
1823c1d255d3SCy Schubert 	if (!sta->hs20_session_info_url)
18245b9c547cSRui Paulo 		return;
18255b9c547cSRui Paulo 	swt = pos[0];
18265b9c547cSRui Paulo 	os_memcpy(sta->hs20_session_info_url, pos + 1, len - 1);
18275b9c547cSRui Paulo 	sta->hs20_session_info_url[len - 1] = '\0';
1828c1d255d3SCy Schubert 	wpa_printf(MSG_DEBUG,
1829c1d255d3SCy Schubert 		   "HS 2.0: Session Information URL='%s' SWT=%u (session_timeout=%d)",
18305b9c547cSRui Paulo 		   sta->hs20_session_info_url, swt, session_timeout);
18315b9c547cSRui Paulo 	if (session_timeout < 0) {
1832c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG,
1833c1d255d3SCy Schubert 			   "HS 2.0: No Session-Timeout set - ignore session info URL");
18345b9c547cSRui Paulo 		return;
18355b9c547cSRui Paulo 	}
18365b9c547cSRui Paulo 	if (swt == 255)
18375b9c547cSRui Paulo 		swt = 1; /* Use one minute as the AP selected value */
18385b9c547cSRui Paulo 
18395b9c547cSRui Paulo 	if ((unsigned int) session_timeout < swt * 60)
18405b9c547cSRui Paulo 		warning_time = 0;
18415b9c547cSRui Paulo 	else
18425b9c547cSRui Paulo 		warning_time = session_timeout - swt * 60;
18435b9c547cSRui Paulo 
18445b9c547cSRui Paulo 	beacon_int = hapd->iconf->beacon_int;
18455b9c547cSRui Paulo 	if (beacon_int < 1)
18465b9c547cSRui Paulo 		beacon_int = 100; /* best guess */
18475b9c547cSRui Paulo 	sta->hs20_disassoc_timer = swt * 60 * 1000 / beacon_int * 125 / 128;
18485b9c547cSRui Paulo 	if (sta->hs20_disassoc_timer > 65535)
18495b9c547cSRui Paulo 		sta->hs20_disassoc_timer = 65535;
18505b9c547cSRui Paulo 
18515b9c547cSRui Paulo 	ap_sta_session_warning_timeout(hapd, sta, warning_time);
18525b9c547cSRui Paulo }
18535b9c547cSRui Paulo 
185485732ac8SCy Schubert 
ieee802_1x_hs20_t_c_filtering(struct hostapd_data * hapd,struct sta_info * sta,u8 * pos,size_t len)185585732ac8SCy Schubert static void ieee802_1x_hs20_t_c_filtering(struct hostapd_data *hapd,
185685732ac8SCy Schubert 					  struct sta_info *sta, u8 *pos,
185785732ac8SCy Schubert 					  size_t len)
185885732ac8SCy Schubert {
185985732ac8SCy Schubert 	if (len < 4)
186085732ac8SCy Schubert 		return; /* Malformed information */
186185732ac8SCy Schubert 	wpa_printf(MSG_DEBUG,
186285732ac8SCy Schubert 		   "HS 2.0: Terms and Conditions filtering %02x %02x %02x %02x",
186385732ac8SCy Schubert 		   pos[0], pos[1], pos[2], pos[3]);
186485732ac8SCy Schubert 	hs20_t_c_filtering(hapd, sta, pos[0] & BIT(0));
186585732ac8SCy Schubert }
186685732ac8SCy Schubert 
186785732ac8SCy Schubert 
ieee802_1x_hs20_t_c_url(struct hostapd_data * hapd,struct sta_info * sta,u8 * pos,size_t len)186885732ac8SCy Schubert static void ieee802_1x_hs20_t_c_url(struct hostapd_data *hapd,
186985732ac8SCy Schubert 				    struct sta_info *sta, u8 *pos, size_t len)
187085732ac8SCy Schubert {
187185732ac8SCy Schubert 	os_free(sta->t_c_url);
187285732ac8SCy Schubert 	sta->t_c_url = os_malloc(len + 1);
187385732ac8SCy Schubert 	if (!sta->t_c_url)
187485732ac8SCy Schubert 		return;
187585732ac8SCy Schubert 	os_memcpy(sta->t_c_url, pos, len);
187685732ac8SCy Schubert 	sta->t_c_url[len] = '\0';
187785732ac8SCy Schubert 	wpa_printf(MSG_DEBUG,
187885732ac8SCy Schubert 		   "HS 2.0: Terms and Conditions URL %s", sta->t_c_url);
187985732ac8SCy Schubert }
188085732ac8SCy Schubert 
18815b9c547cSRui Paulo #endif /* CONFIG_HS20 */
18825b9c547cSRui Paulo 
18835b9c547cSRui Paulo 
ieee802_1x_check_hs20(struct hostapd_data * hapd,struct sta_info * sta,struct radius_msg * msg,int session_timeout)18845b9c547cSRui Paulo static void ieee802_1x_check_hs20(struct hostapd_data *hapd,
18855b9c547cSRui Paulo 				  struct sta_info *sta,
18865b9c547cSRui Paulo 				  struct radius_msg *msg,
18875b9c547cSRui Paulo 				  int session_timeout)
18885b9c547cSRui Paulo {
18895b9c547cSRui Paulo #ifdef CONFIG_HS20
18905b9c547cSRui Paulo 	u8 *buf, *pos, *end, type, sublen;
18915b9c547cSRui Paulo 	size_t len;
18925b9c547cSRui Paulo 
18935b9c547cSRui Paulo 	buf = NULL;
18945b9c547cSRui Paulo 	sta->remediation = 0;
18955b9c547cSRui Paulo 	sta->hs20_deauth_requested = 0;
1896*a90b9d01SCy Schubert 	sta->hs20_deauth_on_ack = 0;
18975b9c547cSRui Paulo 
18985b9c547cSRui Paulo 	for (;;) {
18995b9c547cSRui Paulo 		if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
19005b9c547cSRui Paulo 					    &buf, &len, buf) < 0)
19015b9c547cSRui Paulo 			break;
19025b9c547cSRui Paulo 		if (len < 6)
19035b9c547cSRui Paulo 			continue;
19045b9c547cSRui Paulo 		pos = buf;
19055b9c547cSRui Paulo 		end = buf + len;
19065b9c547cSRui Paulo 		if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
19075b9c547cSRui Paulo 			continue;
19085b9c547cSRui Paulo 		pos += 4;
19095b9c547cSRui Paulo 
19105b9c547cSRui Paulo 		type = *pos++;
19115b9c547cSRui Paulo 		sublen = *pos++;
19125b9c547cSRui Paulo 		if (sublen < 2)
19135b9c547cSRui Paulo 			continue; /* invalid length */
19145b9c547cSRui Paulo 		sublen -= 2; /* skip header */
19155b9c547cSRui Paulo 		if (pos + sublen > end)
19165b9c547cSRui Paulo 			continue; /* invalid WFA VSA */
19175b9c547cSRui Paulo 
19185b9c547cSRui Paulo 		switch (type) {
19195b9c547cSRui Paulo 		case RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION:
19205b9c547cSRui Paulo 			ieee802_1x_hs20_sub_rem(sta, pos, sublen);
19215b9c547cSRui Paulo 			break;
19225b9c547cSRui Paulo 		case RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ:
19235b9c547cSRui Paulo 			ieee802_1x_hs20_deauth_req(hapd, sta, pos, sublen);
19245b9c547cSRui Paulo 			break;
19255b9c547cSRui Paulo 		case RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL:
19265b9c547cSRui Paulo 			ieee802_1x_hs20_session_info(hapd, sta, pos, sublen,
19275b9c547cSRui Paulo 						     session_timeout);
19285b9c547cSRui Paulo 			break;
192985732ac8SCy Schubert 		case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING:
193085732ac8SCy Schubert 			ieee802_1x_hs20_t_c_filtering(hapd, sta, pos, sublen);
193185732ac8SCy Schubert 			break;
193285732ac8SCy Schubert 		case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL:
193385732ac8SCy Schubert 			ieee802_1x_hs20_t_c_url(hapd, sta, pos, sublen);
193485732ac8SCy Schubert 			break;
19355b9c547cSRui Paulo 		}
19365b9c547cSRui Paulo 	}
19375b9c547cSRui Paulo #endif /* CONFIG_HS20 */
19385b9c547cSRui Paulo }
19395b9c547cSRui Paulo 
19405b9c547cSRui Paulo 
1941e28a4053SRui Paulo struct sta_id_search {
1942e28a4053SRui Paulo 	u8 identifier;
1943e28a4053SRui Paulo 	struct eapol_state_machine *sm;
1944e28a4053SRui Paulo };
1945e28a4053SRui Paulo 
1946e28a4053SRui Paulo 
ieee802_1x_select_radius_identifier(struct hostapd_data * hapd,struct sta_info * sta,void * ctx)1947e28a4053SRui Paulo static int ieee802_1x_select_radius_identifier(struct hostapd_data *hapd,
1948e28a4053SRui Paulo 					       struct sta_info *sta,
1949e28a4053SRui Paulo 					       void *ctx)
1950e28a4053SRui Paulo {
1951e28a4053SRui Paulo 	struct sta_id_search *id_search = ctx;
1952e28a4053SRui Paulo 	struct eapol_state_machine *sm = sta->eapol_sm;
1953e28a4053SRui Paulo 
1954e28a4053SRui Paulo 	if (sm && sm->radius_identifier >= 0 &&
1955e28a4053SRui Paulo 	    sm->radius_identifier == id_search->identifier) {
1956e28a4053SRui Paulo 		id_search->sm = sm;
1957e28a4053SRui Paulo 		return 1;
1958e28a4053SRui Paulo 	}
1959e28a4053SRui Paulo 	return 0;
1960e28a4053SRui Paulo }
1961e28a4053SRui Paulo 
1962e28a4053SRui Paulo 
1963e28a4053SRui Paulo static struct eapol_state_machine *
ieee802_1x_search_radius_identifier(struct hostapd_data * hapd,u8 identifier)1964e28a4053SRui Paulo ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier)
1965e28a4053SRui Paulo {
1966e28a4053SRui Paulo 	struct sta_id_search id_search;
1967c1d255d3SCy Schubert 
1968e28a4053SRui Paulo 	id_search.identifier = identifier;
1969e28a4053SRui Paulo 	id_search.sm = NULL;
1970e28a4053SRui Paulo 	ap_for_each_sta(hapd, ieee802_1x_select_radius_identifier, &id_search);
1971e28a4053SRui Paulo 	return id_search.sm;
1972e28a4053SRui Paulo }
1973e28a4053SRui Paulo 
1974e28a4053SRui Paulo 
19754bc52338SCy Schubert #ifndef CONFIG_NO_VLAN
ieee802_1x_update_vlan(struct radius_msg * msg,struct hostapd_data * hapd,struct sta_info * sta)19764bc52338SCy Schubert static int ieee802_1x_update_vlan(struct radius_msg *msg,
19774bc52338SCy Schubert 				  struct hostapd_data *hapd,
19784bc52338SCy Schubert 				  struct sta_info *sta)
19794bc52338SCy Schubert {
19804bc52338SCy Schubert 	struct vlan_description vlan_desc;
19814bc52338SCy Schubert 
19824bc52338SCy Schubert 	os_memset(&vlan_desc, 0, sizeof(vlan_desc));
19834bc52338SCy Schubert 	vlan_desc.notempty = !!radius_msg_get_vlanid(msg, &vlan_desc.untagged,
19844bc52338SCy Schubert 						     MAX_NUM_TAGGED_VLAN,
19854bc52338SCy Schubert 						     vlan_desc.tagged);
19864bc52338SCy Schubert 
19874bc52338SCy Schubert 	if (vlan_desc.notempty &&
19884bc52338SCy Schubert 	    !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
1989c1d255d3SCy Schubert 		sta->eapol_sm->authFail = true;
19904bc52338SCy Schubert 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
19914bc52338SCy Schubert 			       HOSTAPD_LEVEL_INFO,
19924bc52338SCy Schubert 			       "Invalid VLAN %d%s received from RADIUS server",
19934bc52338SCy Schubert 			       vlan_desc.untagged,
19944bc52338SCy Schubert 			       vlan_desc.tagged[0] ? "+" : "");
19954bc52338SCy Schubert 		os_memset(&vlan_desc, 0, sizeof(vlan_desc));
19964bc52338SCy Schubert 		ap_sta_set_vlan(hapd, sta, &vlan_desc);
19974bc52338SCy Schubert 		return -1;
19984bc52338SCy Schubert 	}
19994bc52338SCy Schubert 
20004bc52338SCy Schubert 	if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
20014bc52338SCy Schubert 	    !vlan_desc.notempty) {
2002c1d255d3SCy Schubert 		sta->eapol_sm->authFail = true;
20034bc52338SCy Schubert 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
20044bc52338SCy Schubert 			       HOSTAPD_LEVEL_INFO,
20054bc52338SCy Schubert 			       "authentication server did not include required VLAN ID in Access-Accept");
20064bc52338SCy Schubert 		return -1;
20074bc52338SCy Schubert 	}
20084bc52338SCy Schubert 
20094bc52338SCy Schubert 	return ap_sta_set_vlan(hapd, sta, &vlan_desc);
20104bc52338SCy Schubert }
20114bc52338SCy Schubert #endif /* CONFIG_NO_VLAN */
20124bc52338SCy Schubert 
20134bc52338SCy Schubert 
2014e28a4053SRui Paulo /**
2015e28a4053SRui Paulo  * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server
2016e28a4053SRui Paulo  * @msg: RADIUS response message
2017e28a4053SRui Paulo  * @req: RADIUS request message
2018e28a4053SRui Paulo  * @shared_secret: RADIUS shared secret
2019e28a4053SRui Paulo  * @shared_secret_len: Length of shared_secret in octets
2020e28a4053SRui Paulo  * @data: Context data (struct hostapd_data *)
2021e28a4053SRui Paulo  * Returns: Processing status
2022e28a4053SRui Paulo  */
2023e28a4053SRui Paulo static RadiusRxResult
ieee802_1x_receive_auth(struct radius_msg * msg,struct radius_msg * req,const u8 * shared_secret,size_t shared_secret_len,void * data)2024e28a4053SRui Paulo ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
2025e28a4053SRui Paulo 			const u8 *shared_secret, size_t shared_secret_len,
2026e28a4053SRui Paulo 			void *data)
2027e28a4053SRui Paulo {
2028e28a4053SRui Paulo 	struct hostapd_data *hapd = data;
2029e28a4053SRui Paulo 	struct sta_info *sta;
2030e28a4053SRui Paulo 	u32 session_timeout = 0, termination_action, acct_interim_interval;
2031780fb4a2SCy Schubert 	int session_timeout_set;
203285732ac8SCy Schubert 	u32 reason_code;
2033e28a4053SRui Paulo 	struct eapol_state_machine *sm;
2034e28a4053SRui Paulo 	int override_eapReq = 0;
2035e28a4053SRui Paulo 	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
2036e28a4053SRui Paulo 
2037e28a4053SRui Paulo 	sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier);
2038c1d255d3SCy Schubert 	if (!sm) {
2039c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG,
2040c1d255d3SCy Schubert 			   "IEEE 802.1X: Could not find matching station for this RADIUS message");
2041e28a4053SRui Paulo 		return RADIUS_RX_UNKNOWN;
2042e28a4053SRui Paulo 	}
2043e28a4053SRui Paulo 	sta = sm->sta;
2044e28a4053SRui Paulo 
2045*a90b9d01SCy Schubert 	if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 1)) {
2046c1d255d3SCy Schubert 		wpa_printf(MSG_INFO,
2047c1d255d3SCy Schubert 			   "Incoming RADIUS packet did not have correct Message-Authenticator - dropped");
2048e28a4053SRui Paulo 		return RADIUS_RX_INVALID_AUTHENTICATOR;
2049e28a4053SRui Paulo 	}
2050e28a4053SRui Paulo 
2051e28a4053SRui Paulo 	if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
2052e28a4053SRui Paulo 	    hdr->code != RADIUS_CODE_ACCESS_REJECT &&
2053e28a4053SRui Paulo 	    hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) {
20545b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "Unknown RADIUS message code");
2055e28a4053SRui Paulo 		return RADIUS_RX_UNKNOWN;
2056e28a4053SRui Paulo 	}
2057e28a4053SRui Paulo 
2058e28a4053SRui Paulo 	sm->radius_identifier = -1;
2059e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "RADIUS packet matching with station " MACSTR,
2060e28a4053SRui Paulo 		   MAC2STR(sta->addr));
2061e28a4053SRui Paulo 
2062e28a4053SRui Paulo 	radius_msg_free(sm->last_recv_radius);
2063e28a4053SRui Paulo 	sm->last_recv_radius = msg;
2064e28a4053SRui Paulo 
2065e28a4053SRui Paulo 	session_timeout_set =
2066e28a4053SRui Paulo 		!radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
2067e28a4053SRui Paulo 					   &session_timeout);
2068e28a4053SRui Paulo 	if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_TERMINATION_ACTION,
2069e28a4053SRui Paulo 				      &termination_action))
2070e28a4053SRui Paulo 		termination_action = RADIUS_TERMINATION_ACTION_DEFAULT;
2071e28a4053SRui Paulo 
2072e28a4053SRui Paulo 	if (hapd->conf->acct_interim_interval == 0 &&
2073e28a4053SRui Paulo 	    hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
2074e28a4053SRui Paulo 	    radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
2075e28a4053SRui Paulo 				      &acct_interim_interval) == 0) {
2076e28a4053SRui Paulo 		if (acct_interim_interval < 60) {
2077e28a4053SRui Paulo 			hostapd_logger(hapd, sta->addr,
2078e28a4053SRui Paulo 				       HOSTAPD_MODULE_IEEE8021X,
2079e28a4053SRui Paulo 				       HOSTAPD_LEVEL_INFO,
2080c1d255d3SCy Schubert 				       "ignored too small Acct-Interim-Interval %d",
2081e28a4053SRui Paulo 				       acct_interim_interval);
2082e28a4053SRui Paulo 		} else
2083e28a4053SRui Paulo 			sta->acct_interim_interval = acct_interim_interval;
2084e28a4053SRui Paulo 	}
2085e28a4053SRui Paulo 
2086e28a4053SRui Paulo 
2087e28a4053SRui Paulo 	switch (hdr->code) {
2088e28a4053SRui Paulo 	case RADIUS_CODE_ACCESS_ACCEPT:
2089e28a4053SRui Paulo #ifndef CONFIG_NO_VLAN
20904bc52338SCy Schubert 		if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED &&
20914bc52338SCy Schubert 		    ieee802_1x_update_vlan(msg, hapd, sta) < 0)
2092780fb4a2SCy Schubert 			break;
2093780fb4a2SCy Schubert 
2094780fb4a2SCy Schubert 		if (sta->vlan_id > 0) {
2095780fb4a2SCy Schubert 			hostapd_logger(hapd, sta->addr,
2096780fb4a2SCy Schubert 				       HOSTAPD_MODULE_RADIUS,
2097780fb4a2SCy Schubert 				       HOSTAPD_LEVEL_INFO,
2098780fb4a2SCy Schubert 				       "VLAN ID %d", sta->vlan_id);
2099780fb4a2SCy Schubert 		}
2100780fb4a2SCy Schubert 
2101325151a3SRui Paulo 		if ((sta->flags & WLAN_STA_ASSOC) &&
2102325151a3SRui Paulo 		    ap_sta_bind_vlan(hapd, sta) < 0)
2103e28a4053SRui Paulo 			break;
21044bc52338SCy Schubert #endif /* CONFIG_NO_VLAN */
2105e28a4053SRui Paulo 
21065b9c547cSRui Paulo 		sta->session_timeout_set = !!session_timeout_set;
210785732ac8SCy Schubert 		os_get_reltime(&sta->session_timeout);
210885732ac8SCy Schubert 		sta->session_timeout.sec += session_timeout;
21095b9c547cSRui Paulo 
2110e28a4053SRui Paulo 		/* RFC 3580, Ch. 3.17 */
2111e28a4053SRui Paulo 		if (session_timeout_set && termination_action ==
211285732ac8SCy Schubert 		    RADIUS_TERMINATION_ACTION_RADIUS_REQUEST)
2113e28a4053SRui Paulo 			sm->reAuthPeriod = session_timeout;
211485732ac8SCy Schubert 		else if (session_timeout_set)
2115e28a4053SRui Paulo 			ap_sta_session_timeout(hapd, sta, session_timeout);
211685732ac8SCy Schubert 		else
211785732ac8SCy Schubert 			ap_sta_no_session_timeout(hapd, sta);
2118e28a4053SRui Paulo 
2119c1d255d3SCy Schubert 		sm->eap_if->aaaSuccess = true;
2120e28a4053SRui Paulo 		override_eapReq = 1;
2121e28a4053SRui Paulo 		ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret,
2122e28a4053SRui Paulo 				    shared_secret_len);
2123e28a4053SRui Paulo 		ieee802_1x_store_radius_class(hapd, sta, msg);
2124e28a4053SRui Paulo 		ieee802_1x_update_sta_identity(hapd, sta, msg);
2125f05cddf9SRui Paulo 		ieee802_1x_update_sta_cui(hapd, sta, msg);
21265b9c547cSRui Paulo 		ieee802_1x_check_hs20(hapd, sta, msg,
21275b9c547cSRui Paulo 				      session_timeout_set ?
21285b9c547cSRui Paulo 				      (int) session_timeout : -1);
2129e28a4053SRui Paulo 		break;
2130e28a4053SRui Paulo 	case RADIUS_CODE_ACCESS_REJECT:
2131c1d255d3SCy Schubert 		sm->eap_if->aaaFail = true;
2132e28a4053SRui Paulo 		override_eapReq = 1;
213385732ac8SCy Schubert 		if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE,
213485732ac8SCy Schubert 					      &reason_code) == 0) {
213585732ac8SCy Schubert 			wpa_printf(MSG_DEBUG,
213685732ac8SCy Schubert 				   "RADIUS server indicated WLAN-Reason-Code %u in Access-Reject for "
213785732ac8SCy Schubert 				   MACSTR, reason_code, MAC2STR(sta->addr));
213885732ac8SCy Schubert 			sta->disconnect_reason_code = reason_code;
213985732ac8SCy Schubert 		}
2140e28a4053SRui Paulo 		break;
2141e28a4053SRui Paulo 	case RADIUS_CODE_ACCESS_CHALLENGE:
2142c1d255d3SCy Schubert 		sm->eap_if->aaaEapReq = true;
2143e28a4053SRui Paulo 		if (session_timeout_set) {
2144e28a4053SRui Paulo 			/* RFC 2869, Ch. 2.3.2; RFC 3580, Ch. 3.17 */
2145e28a4053SRui Paulo 			sm->eap_if->aaaMethodTimeout = session_timeout;
2146e28a4053SRui Paulo 			hostapd_logger(hapd, sm->addr,
2147e28a4053SRui Paulo 				       HOSTAPD_MODULE_IEEE8021X,
2148e28a4053SRui Paulo 				       HOSTAPD_LEVEL_DEBUG,
2149c1d255d3SCy Schubert 				       "using EAP timeout of %d seconds (from RADIUS)",
2150e28a4053SRui Paulo 				       sm->eap_if->aaaMethodTimeout);
2151e28a4053SRui Paulo 		} else {
2152e28a4053SRui Paulo 			/*
2153e28a4053SRui Paulo 			 * Use dynamic retransmission behavior per EAP
2154e28a4053SRui Paulo 			 * specification.
2155e28a4053SRui Paulo 			 */
2156e28a4053SRui Paulo 			sm->eap_if->aaaMethodTimeout = 0;
2157e28a4053SRui Paulo 		}
2158e28a4053SRui Paulo 		break;
2159e28a4053SRui Paulo 	}
2160e28a4053SRui Paulo 
2161e28a4053SRui Paulo 	ieee802_1x_decapsulate_radius(hapd, sta);
2162e28a4053SRui Paulo 	if (override_eapReq)
2163c1d255d3SCy Schubert 		sm->eap_if->aaaEapReq = false;
2164e28a4053SRui Paulo 
216585732ac8SCy Schubert #ifdef CONFIG_FILS
216685732ac8SCy Schubert #ifdef NEED_AP_MLME
2167c1d255d3SCy Schubert 	if (sta->flags &
2168c1d255d3SCy Schubert 	    (WLAN_STA_PENDING_FILS_ERP | WLAN_STA_PENDING_PASN_FILS_ERP)) {
216985732ac8SCy Schubert 		/* TODO: Add a PMKSA entry on success? */
217085732ac8SCy Schubert 		ieee802_11_finish_fils_auth(
217185732ac8SCy Schubert 			hapd, sta, hdr->code == RADIUS_CODE_ACCESS_ACCEPT,
217285732ac8SCy Schubert 			sm->eap_if->aaaEapReqData,
217385732ac8SCy Schubert 			sm->eap_if->aaaEapKeyData,
217485732ac8SCy Schubert 			sm->eap_if->aaaEapKeyDataLen);
217585732ac8SCy Schubert 	}
217685732ac8SCy Schubert #endif /* NEED_AP_MLME */
217785732ac8SCy Schubert #endif /* CONFIG_FILS */
217885732ac8SCy Schubert 
2179e28a4053SRui Paulo 	eapol_auth_step(sm);
2180e28a4053SRui Paulo 
2181e28a4053SRui Paulo 	return RADIUS_RX_QUEUED;
2182e28a4053SRui Paulo }
2183e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
2184e28a4053SRui Paulo 
2185e28a4053SRui Paulo 
ieee802_1x_abort_auth(struct hostapd_data * hapd,struct sta_info * sta)2186e28a4053SRui Paulo void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta)
2187e28a4053SRui Paulo {
2188e28a4053SRui Paulo 	struct eapol_state_machine *sm = sta->eapol_sm;
2189c1d255d3SCy Schubert 
2190c1d255d3SCy Schubert 	if (!sm)
2191e28a4053SRui Paulo 		return;
2192e28a4053SRui Paulo 
2193e28a4053SRui Paulo 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
2194e28a4053SRui Paulo 		       HOSTAPD_LEVEL_DEBUG, "aborting authentication");
2195e28a4053SRui Paulo 
2196e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
2197e28a4053SRui Paulo 	radius_msg_free(sm->last_recv_radius);
2198e28a4053SRui Paulo 	sm->last_recv_radius = NULL;
2199e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
2200e28a4053SRui Paulo 
2201e28a4053SRui Paulo 	if (sm->eap_if->eapTimeout) {
2202e28a4053SRui Paulo 		/*
2203e28a4053SRui Paulo 		 * Disconnect the STA since it did not reply to the last EAP
2204e28a4053SRui Paulo 		 * request and we cannot continue EAP processing (EAP-Failure
2205e28a4053SRui Paulo 		 * could only be sent if the EAP peer actually replied).
2206e28a4053SRui Paulo 		 */
2207f05cddf9SRui Paulo 		wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "EAP Timeout, STA " MACSTR,
2208f05cddf9SRui Paulo 			MAC2STR(sta->addr));
2209f05cddf9SRui Paulo 
2210c1d255d3SCy Schubert 		sm->eap_if->portEnabled = false;
2211e28a4053SRui Paulo 		ap_sta_disconnect(hapd, sta, sta->addr,
2212e28a4053SRui Paulo 				  WLAN_REASON_PREV_AUTH_NOT_VALID);
2213e28a4053SRui Paulo 	}
2214e28a4053SRui Paulo }
2215e28a4053SRui Paulo 
2216e28a4053SRui Paulo 
2217c1d255d3SCy Schubert #ifdef CONFIG_WEP
2218c1d255d3SCy Schubert 
ieee802_1x_rekey_broadcast(struct hostapd_data * hapd)2219e28a4053SRui Paulo static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd)
2220e28a4053SRui Paulo {
2221e28a4053SRui Paulo 	struct eapol_authenticator *eapol = hapd->eapol_auth;
2222e28a4053SRui Paulo 
2223e28a4053SRui Paulo 	if (hapd->conf->default_wep_key_len < 1)
2224e28a4053SRui Paulo 		return 0;
2225e28a4053SRui Paulo 
2226e28a4053SRui Paulo 	os_free(eapol->default_wep_key);
2227e28a4053SRui Paulo 	eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len);
2228c1d255d3SCy Schubert 	if (!eapol->default_wep_key ||
2229f05cddf9SRui Paulo 	    random_get_bytes(eapol->default_wep_key,
2230e28a4053SRui Paulo 			     hapd->conf->default_wep_key_len)) {
22315b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "Could not generate random WEP key");
2232e28a4053SRui Paulo 		os_free(eapol->default_wep_key);
2233e28a4053SRui Paulo 		eapol->default_wep_key = NULL;
2234e28a4053SRui Paulo 		return -1;
2235e28a4053SRui Paulo 	}
2236e28a4053SRui Paulo 
2237e28a4053SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "IEEE 802.1X: New default WEP key",
2238e28a4053SRui Paulo 			eapol->default_wep_key,
2239e28a4053SRui Paulo 			hapd->conf->default_wep_key_len);
2240e28a4053SRui Paulo 
2241e28a4053SRui Paulo 	return 0;
2242e28a4053SRui Paulo }
2243e28a4053SRui Paulo 
2244e28a4053SRui Paulo 
ieee802_1x_sta_key_available(struct hostapd_data * hapd,struct sta_info * sta,void * ctx)2245e28a4053SRui Paulo static int ieee802_1x_sta_key_available(struct hostapd_data *hapd,
2246e28a4053SRui Paulo 					struct sta_info *sta, void *ctx)
2247e28a4053SRui Paulo {
2248e28a4053SRui Paulo 	if (sta->eapol_sm) {
2249c1d255d3SCy Schubert 		sta->eapol_sm->eap_if->eapKeyAvailable = true;
2250e28a4053SRui Paulo 		eapol_auth_step(sta->eapol_sm);
2251e28a4053SRui Paulo 	}
2252e28a4053SRui Paulo 	return 0;
2253e28a4053SRui Paulo }
2254e28a4053SRui Paulo 
2255e28a4053SRui Paulo 
ieee802_1x_rekey(void * eloop_ctx,void * timeout_ctx)2256e28a4053SRui Paulo static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx)
2257e28a4053SRui Paulo {
2258e28a4053SRui Paulo 	struct hostapd_data *hapd = eloop_ctx;
2259e28a4053SRui Paulo 	struct eapol_authenticator *eapol = hapd->eapol_auth;
2260e28a4053SRui Paulo 
2261e28a4053SRui Paulo 	if (eapol->default_wep_key_idx >= 3)
2262e28a4053SRui Paulo 		eapol->default_wep_key_idx =
2263e28a4053SRui Paulo 			hapd->conf->individual_wep_key_len > 0 ? 1 : 0;
2264e28a4053SRui Paulo 	else
2265e28a4053SRui Paulo 		eapol->default_wep_key_idx++;
2266e28a4053SRui Paulo 
2267e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d",
2268e28a4053SRui Paulo 		   eapol->default_wep_key_idx);
2269e28a4053SRui Paulo 
2270e28a4053SRui Paulo 	if (ieee802_1x_rekey_broadcast(hapd)) {
2271e28a4053SRui Paulo 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
2272c1d255d3SCy Schubert 			       HOSTAPD_LEVEL_WARNING,
2273c1d255d3SCy Schubert 			       "failed to generate a new broadcast key");
2274e28a4053SRui Paulo 		os_free(eapol->default_wep_key);
2275e28a4053SRui Paulo 		eapol->default_wep_key = NULL;
2276e28a4053SRui Paulo 		return;
2277e28a4053SRui Paulo 	}
2278e28a4053SRui Paulo 
2279e28a4053SRui Paulo 	/* TODO: Could setup key for RX here, but change default TX keyid only
2280e28a4053SRui Paulo 	 * after new broadcast key has been sent to all stations. */
2281f05cddf9SRui Paulo 	if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
2282f05cddf9SRui Paulo 				broadcast_ether_addr,
2283c1d255d3SCy Schubert 				eapol->default_wep_key_idx, 0, 1, NULL, 0,
2284e28a4053SRui Paulo 				eapol->default_wep_key,
2285c1d255d3SCy Schubert 				hapd->conf->default_wep_key_len,
2286c1d255d3SCy Schubert 				KEY_FLAG_GROUP_RX_TX_DEFAULT)) {
2287e28a4053SRui Paulo 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
2288c1d255d3SCy Schubert 			       HOSTAPD_LEVEL_WARNING,
2289c1d255d3SCy Schubert 			       "failed to configure a new broadcast key");
2290e28a4053SRui Paulo 		os_free(eapol->default_wep_key);
2291e28a4053SRui Paulo 		eapol->default_wep_key = NULL;
2292e28a4053SRui Paulo 		return;
2293e28a4053SRui Paulo 	}
2294e28a4053SRui Paulo 
2295e28a4053SRui Paulo 	ap_for_each_sta(hapd, ieee802_1x_sta_key_available, NULL);
2296e28a4053SRui Paulo 
2297e28a4053SRui Paulo 	if (hapd->conf->wep_rekeying_period > 0) {
2298e28a4053SRui Paulo 		eloop_register_timeout(hapd->conf->wep_rekeying_period, 0,
2299e28a4053SRui Paulo 				       ieee802_1x_rekey, hapd, NULL);
2300e28a4053SRui Paulo 	}
2301e28a4053SRui Paulo }
2302e28a4053SRui Paulo 
2303c1d255d3SCy Schubert #endif /* CONFIG_WEP */
2304c1d255d3SCy Schubert 
2305e28a4053SRui Paulo 
ieee802_1x_eapol_send(void * ctx,void * sta_ctx,u8 type,const u8 * data,size_t datalen)2306e28a4053SRui Paulo static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type,
2307e28a4053SRui Paulo 				  const u8 *data, size_t datalen)
2308e28a4053SRui Paulo {
2309e28a4053SRui Paulo #ifdef CONFIG_WPS
2310e28a4053SRui Paulo 	struct sta_info *sta = sta_ctx;
2311e28a4053SRui Paulo 
2312e28a4053SRui Paulo 	if ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) ==
2313e28a4053SRui Paulo 	    WLAN_STA_MAYBE_WPS) {
2314e28a4053SRui Paulo 		const u8 *identity;
2315e28a4053SRui Paulo 		size_t identity_len;
2316e28a4053SRui Paulo 		struct eapol_state_machine *sm = sta->eapol_sm;
2317e28a4053SRui Paulo 
2318e28a4053SRui Paulo 		identity = eap_get_identity(sm->eap, &identity_len);
2319e28a4053SRui Paulo 		if (identity &&
2320e28a4053SRui Paulo 		    ((identity_len == WSC_ID_ENROLLEE_LEN &&
2321e28a4053SRui Paulo 		      os_memcmp(identity, WSC_ID_ENROLLEE,
2322e28a4053SRui Paulo 				WSC_ID_ENROLLEE_LEN) == 0) ||
2323e28a4053SRui Paulo 		     (identity_len == WSC_ID_REGISTRAR_LEN &&
2324e28a4053SRui Paulo 		      os_memcmp(identity, WSC_ID_REGISTRAR,
2325e28a4053SRui Paulo 				WSC_ID_REGISTRAR_LEN) == 0))) {
2326c1d255d3SCy Schubert 			wpa_printf(MSG_DEBUG,
2327c1d255d3SCy Schubert 				   "WPS: WLAN_STA_MAYBE_WPS -> WLAN_STA_WPS");
2328e28a4053SRui Paulo 			sta->flags |= WLAN_STA_WPS;
2329e28a4053SRui Paulo 		}
2330e28a4053SRui Paulo 	}
2331e28a4053SRui Paulo #endif /* CONFIG_WPS */
2332e28a4053SRui Paulo 
2333e28a4053SRui Paulo 	ieee802_1x_send(ctx, sta_ctx, type, data, datalen);
2334e28a4053SRui Paulo }
2335e28a4053SRui Paulo 
2336e28a4053SRui Paulo 
ieee802_1x_aaa_send(void * ctx,void * sta_ctx,const u8 * data,size_t datalen)2337e28a4053SRui Paulo static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx,
2338e28a4053SRui Paulo 				const u8 *data, size_t datalen)
2339e28a4053SRui Paulo {
2340e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
2341e28a4053SRui Paulo 	struct hostapd_data *hapd = ctx;
2342e28a4053SRui Paulo 	struct sta_info *sta = sta_ctx;
2343e28a4053SRui Paulo 
2344e28a4053SRui Paulo 	ieee802_1x_encapsulate_radius(hapd, sta, data, datalen);
2345e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
2346e28a4053SRui Paulo }
2347e28a4053SRui Paulo 
2348e28a4053SRui Paulo 
_ieee802_1x_finished(void * ctx,void * sta_ctx,int success,int preauth,int remediation,bool logoff)2349*a90b9d01SCy Schubert static bool _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
2350*a90b9d01SCy Schubert 				 int preauth, int remediation, bool logoff)
2351e28a4053SRui Paulo {
2352e28a4053SRui Paulo 	struct hostapd_data *hapd = ctx;
2353e28a4053SRui Paulo 	struct sta_info *sta = sta_ctx;
2354c1d255d3SCy Schubert 
2355*a90b9d01SCy Schubert 	if (preauth) {
2356e28a4053SRui Paulo 		rsn_preauth_finished(hapd, sta, success);
2357*a90b9d01SCy Schubert 		return false;
2358*a90b9d01SCy Schubert 	}
2359*a90b9d01SCy Schubert 
2360*a90b9d01SCy Schubert 	return ieee802_1x_finished(hapd, sta, success, remediation, logoff);
2361e28a4053SRui Paulo }
2362e28a4053SRui Paulo 
2363e28a4053SRui Paulo 
ieee802_1x_get_eap_user(void * ctx,const u8 * identity,size_t identity_len,int phase2,struct eap_user * user)2364e28a4053SRui Paulo static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
2365e28a4053SRui Paulo 				   size_t identity_len, int phase2,
2366e28a4053SRui Paulo 				   struct eap_user *user)
2367e28a4053SRui Paulo {
2368e28a4053SRui Paulo 	struct hostapd_data *hapd = ctx;
2369e28a4053SRui Paulo 	const struct hostapd_eap_user *eap_user;
2370f05cddf9SRui Paulo 	int i;
2371325151a3SRui Paulo 	int rv = -1;
2372e28a4053SRui Paulo 
2373f05cddf9SRui Paulo 	eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2);
2374c1d255d3SCy Schubert 	if (!eap_user)
2375325151a3SRui Paulo 		goto out;
2376e28a4053SRui Paulo 
2377e28a4053SRui Paulo 	os_memset(user, 0, sizeof(*user));
2378e28a4053SRui Paulo 	user->phase2 = phase2;
2379f05cddf9SRui Paulo 	for (i = 0; i < EAP_MAX_METHODS; i++) {
2380e28a4053SRui Paulo 		user->methods[i].vendor = eap_user->methods[i].vendor;
2381e28a4053SRui Paulo 		user->methods[i].method = eap_user->methods[i].method;
2382e28a4053SRui Paulo 	}
2383e28a4053SRui Paulo 
2384e28a4053SRui Paulo 	if (eap_user->password) {
238585732ac8SCy Schubert 		user->password = os_memdup(eap_user->password,
238685732ac8SCy Schubert 					   eap_user->password_len);
2387c1d255d3SCy Schubert 		if (!user->password)
2388325151a3SRui Paulo 			goto out;
2389e28a4053SRui Paulo 		user->password_len = eap_user->password_len;
2390f05cddf9SRui Paulo 		user->password_hash = eap_user->password_hash;
239185732ac8SCy Schubert 		if (eap_user->salt && eap_user->salt_len) {
239285732ac8SCy Schubert 			user->salt = os_memdup(eap_user->salt,
239385732ac8SCy Schubert 					       eap_user->salt_len);
239485732ac8SCy Schubert 			if (!user->salt)
239585732ac8SCy Schubert 				goto out;
239685732ac8SCy Schubert 			user->salt_len = eap_user->salt_len;
239785732ac8SCy Schubert 		}
2398e28a4053SRui Paulo 	}
2399e28a4053SRui Paulo 	user->force_version = eap_user->force_version;
24005b9c547cSRui Paulo 	user->macacl = eap_user->macacl;
2401e28a4053SRui Paulo 	user->ttls_auth = eap_user->ttls_auth;
24025b9c547cSRui Paulo 	user->remediation = eap_user->remediation;
2403325151a3SRui Paulo 	rv = 0;
2404e28a4053SRui Paulo 
2405325151a3SRui Paulo out:
2406325151a3SRui Paulo 	if (rv)
2407325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__);
2408325151a3SRui Paulo 
2409325151a3SRui Paulo 	return rv;
2410e28a4053SRui Paulo }
2411e28a4053SRui Paulo 
2412e28a4053SRui Paulo 
ieee802_1x_sta_entry_alive(void * ctx,const u8 * addr)2413e28a4053SRui Paulo static int ieee802_1x_sta_entry_alive(void *ctx, const u8 *addr)
2414e28a4053SRui Paulo {
2415e28a4053SRui Paulo 	struct hostapd_data *hapd = ctx;
2416e28a4053SRui Paulo 	struct sta_info *sta;
2417c1d255d3SCy Schubert 
2418e28a4053SRui Paulo 	sta = ap_get_sta(hapd, addr);
2419c1d255d3SCy Schubert 	if (!sta || !sta->eapol_sm)
2420e28a4053SRui Paulo 		return 0;
2421e28a4053SRui Paulo 	return 1;
2422e28a4053SRui Paulo }
2423e28a4053SRui Paulo 
2424e28a4053SRui Paulo 
ieee802_1x_logger(void * ctx,const u8 * addr,eapol_logger_level level,const char * txt)2425e28a4053SRui Paulo static void ieee802_1x_logger(void *ctx, const u8 *addr,
2426e28a4053SRui Paulo 			      eapol_logger_level level, const char *txt)
2427e28a4053SRui Paulo {
2428e28a4053SRui Paulo #ifndef CONFIG_NO_HOSTAPD_LOGGER
2429e28a4053SRui Paulo 	struct hostapd_data *hapd = ctx;
2430e28a4053SRui Paulo 	int hlevel;
2431e28a4053SRui Paulo 
2432e28a4053SRui Paulo 	switch (level) {
2433e28a4053SRui Paulo 	case EAPOL_LOGGER_WARNING:
2434e28a4053SRui Paulo 		hlevel = HOSTAPD_LEVEL_WARNING;
2435e28a4053SRui Paulo 		break;
2436e28a4053SRui Paulo 	case EAPOL_LOGGER_INFO:
2437e28a4053SRui Paulo 		hlevel = HOSTAPD_LEVEL_INFO;
2438e28a4053SRui Paulo 		break;
2439e28a4053SRui Paulo 	case EAPOL_LOGGER_DEBUG:
2440e28a4053SRui Paulo 	default:
2441e28a4053SRui Paulo 		hlevel = HOSTAPD_LEVEL_DEBUG;
2442e28a4053SRui Paulo 		break;
2443e28a4053SRui Paulo 	}
2444e28a4053SRui Paulo 
2445e28a4053SRui Paulo 	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE8021X, hlevel, "%s",
2446e28a4053SRui Paulo 		       txt);
2447e28a4053SRui Paulo #endif /* CONFIG_NO_HOSTAPD_LOGGER */
2448e28a4053SRui Paulo }
2449e28a4053SRui Paulo 
2450e28a4053SRui Paulo 
ieee802_1x_set_port_authorized(void * ctx,void * sta_ctx,int authorized)2451e28a4053SRui Paulo static void ieee802_1x_set_port_authorized(void *ctx, void *sta_ctx,
2452e28a4053SRui Paulo 					   int authorized)
2453e28a4053SRui Paulo {
2454e28a4053SRui Paulo 	struct hostapd_data *hapd = ctx;
2455e28a4053SRui Paulo 	struct sta_info *sta = sta_ctx;
2456c1d255d3SCy Schubert 
2457e28a4053SRui Paulo 	ieee802_1x_set_sta_authorized(hapd, sta, authorized);
2458e28a4053SRui Paulo }
2459e28a4053SRui Paulo 
2460e28a4053SRui Paulo 
_ieee802_1x_abort_auth(void * ctx,void * sta_ctx)2461e28a4053SRui Paulo static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx)
2462e28a4053SRui Paulo {
2463e28a4053SRui Paulo 	struct hostapd_data *hapd = ctx;
2464e28a4053SRui Paulo 	struct sta_info *sta = sta_ctx;
2465c1d255d3SCy Schubert 
2466e28a4053SRui Paulo 	ieee802_1x_abort_auth(hapd, sta);
2467e28a4053SRui Paulo }
2468e28a4053SRui Paulo 
2469e28a4053SRui Paulo 
2470c1d255d3SCy Schubert #ifdef CONFIG_WEP
_ieee802_1x_tx_key(void * ctx,void * sta_ctx)2471e28a4053SRui Paulo static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx)
2472e28a4053SRui Paulo {
2473325151a3SRui Paulo #ifndef CONFIG_FIPS
2474325151a3SRui Paulo #ifndef CONFIG_NO_RC4
2475e28a4053SRui Paulo 	struct hostapd_data *hapd = ctx;
2476e28a4053SRui Paulo 	struct sta_info *sta = sta_ctx;
2477c1d255d3SCy Schubert 
2478e28a4053SRui Paulo 	ieee802_1x_tx_key(hapd, sta);
2479325151a3SRui Paulo #endif /* CONFIG_NO_RC4 */
2480325151a3SRui Paulo #endif /* CONFIG_FIPS */
2481e28a4053SRui Paulo }
2482c1d255d3SCy Schubert #endif /* CONFIG_WEP */
2483e28a4053SRui Paulo 
2484e28a4053SRui Paulo 
ieee802_1x_eapol_event(void * ctx,void * sta_ctx,enum eapol_event type)2485e28a4053SRui Paulo static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx,
2486e28a4053SRui Paulo 				   enum eapol_event type)
2487e28a4053SRui Paulo {
2488e28a4053SRui Paulo 	/* struct hostapd_data *hapd = ctx; */
2489e28a4053SRui Paulo 	struct sta_info *sta = sta_ctx;
2490c1d255d3SCy Schubert 
2491e28a4053SRui Paulo 	switch (type) {
2492e28a4053SRui Paulo 	case EAPOL_AUTH_SM_CHANGE:
2493e28a4053SRui Paulo 		wpa_auth_sm_notify(sta->wpa_sm);
2494e28a4053SRui Paulo 		break;
2495e28a4053SRui Paulo 	case EAPOL_AUTH_REAUTHENTICATE:
2496e28a4053SRui Paulo 		wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
2497e28a4053SRui Paulo 		break;
2498e28a4053SRui Paulo 	}
2499e28a4053SRui Paulo }
2500e28a4053SRui Paulo 
2501e28a4053SRui Paulo 
25025b9c547cSRui Paulo #ifdef CONFIG_ERP
25035b9c547cSRui Paulo 
25045b9c547cSRui Paulo static struct eap_server_erp_key *
ieee802_1x_erp_get_key(void * ctx,const char * keyname)25055b9c547cSRui Paulo ieee802_1x_erp_get_key(void *ctx, const char *keyname)
25065b9c547cSRui Paulo {
25075b9c547cSRui Paulo 	struct hostapd_data *hapd = ctx;
25085b9c547cSRui Paulo 	struct eap_server_erp_key *erp;
25095b9c547cSRui Paulo 
25105b9c547cSRui Paulo 	dl_list_for_each(erp, &hapd->erp_keys, struct eap_server_erp_key,
25115b9c547cSRui Paulo 			 list) {
25125b9c547cSRui Paulo 		if (os_strcmp(erp->keyname_nai, keyname) == 0)
25135b9c547cSRui Paulo 			return erp;
25145b9c547cSRui Paulo 	}
25155b9c547cSRui Paulo 
25165b9c547cSRui Paulo 	return NULL;
25175b9c547cSRui Paulo }
25185b9c547cSRui Paulo 
25195b9c547cSRui Paulo 
ieee802_1x_erp_add_key(void * ctx,struct eap_server_erp_key * erp)25205b9c547cSRui Paulo static int ieee802_1x_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
25215b9c547cSRui Paulo {
25225b9c547cSRui Paulo 	struct hostapd_data *hapd = ctx;
25235b9c547cSRui Paulo 
25245b9c547cSRui Paulo 	dl_list_add(&hapd->erp_keys, &erp->list);
25255b9c547cSRui Paulo 	return 0;
25265b9c547cSRui Paulo }
25275b9c547cSRui Paulo 
25285b9c547cSRui Paulo #endif /* CONFIG_ERP */
25295b9c547cSRui Paulo 
25305b9c547cSRui Paulo 
ieee802_1x_init(struct hostapd_data * hapd)2531e28a4053SRui Paulo int ieee802_1x_init(struct hostapd_data *hapd)
2532e28a4053SRui Paulo {
2533e28a4053SRui Paulo 	struct eapol_auth_config conf;
2534e28a4053SRui Paulo 	struct eapol_auth_cb cb;
2535e28a4053SRui Paulo 
2536*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
2537*a90b9d01SCy Schubert 	if (!hostapd_mld_is_first_bss(hapd)) {
2538*a90b9d01SCy Schubert 		struct hostapd_data *first;
2539*a90b9d01SCy Schubert 
2540*a90b9d01SCy Schubert 		first = hostapd_mld_get_first_bss(hapd);
2541*a90b9d01SCy Schubert 		if (!first)
2542*a90b9d01SCy Schubert 			return -1;
2543*a90b9d01SCy Schubert 
2544*a90b9d01SCy Schubert 		if (!first->eapol_auth) {
2545*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
2546*a90b9d01SCy Schubert 				   "MLD: First BSS IEEE 802.1X state machine does not exist. Init on its behalf");
2547*a90b9d01SCy Schubert 
2548*a90b9d01SCy Schubert 			if (ieee802_1x_init(first))
2549*a90b9d01SCy Schubert 				return -1;
2550*a90b9d01SCy Schubert 		}
2551*a90b9d01SCy Schubert 
2552*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
2553*a90b9d01SCy Schubert 			   "MLD: Using IEEE 802.1X state machine of the first BSS");
2554*a90b9d01SCy Schubert 
2555*a90b9d01SCy Schubert 		hapd->eapol_auth = first->eapol_auth;
2556*a90b9d01SCy Schubert 		return 0;
2557*a90b9d01SCy Schubert 	}
2558*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
2559*a90b9d01SCy Schubert 
25605b9c547cSRui Paulo 	dl_list_init(&hapd->erp_keys);
25615b9c547cSRui Paulo 
2562e28a4053SRui Paulo 	os_memset(&conf, 0, sizeof(conf));
2563c1d255d3SCy Schubert 	conf.eap_cfg = hapd->eap_cfg;
2564e28a4053SRui Paulo 	conf.ctx = hapd;
2565e28a4053SRui Paulo 	conf.eap_reauth_period = hapd->conf->eap_reauth_period;
2566e28a4053SRui Paulo 	conf.wpa = hapd->conf->wpa;
2567c1d255d3SCy Schubert #ifdef CONFIG_WEP
2568e28a4053SRui Paulo 	conf.individual_wep_key_len = hapd->conf->individual_wep_key_len;
2569c1d255d3SCy Schubert #endif /* CONFIG_WEP */
2570e28a4053SRui Paulo 	conf.eap_req_id_text = hapd->conf->eap_req_id_text;
2571e28a4053SRui Paulo 	conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len;
25725b9c547cSRui Paulo 	conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start;
25735b9c547cSRui Paulo 	conf.erp_domain = hapd->conf->erp_domain;
2574*a90b9d01SCy Schubert #ifdef CONFIG_TESTING_OPTIONS
2575*a90b9d01SCy Schubert 	conf.eap_skip_prot_success = hapd->conf->eap_skip_prot_success;
2576*a90b9d01SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */
2577e28a4053SRui Paulo 
2578e28a4053SRui Paulo 	os_memset(&cb, 0, sizeof(cb));
2579e28a4053SRui Paulo 	cb.eapol_send = ieee802_1x_eapol_send;
2580e28a4053SRui Paulo 	cb.aaa_send = ieee802_1x_aaa_send;
2581e28a4053SRui Paulo 	cb.finished = _ieee802_1x_finished;
2582e28a4053SRui Paulo 	cb.get_eap_user = ieee802_1x_get_eap_user;
2583e28a4053SRui Paulo 	cb.sta_entry_alive = ieee802_1x_sta_entry_alive;
2584e28a4053SRui Paulo 	cb.logger = ieee802_1x_logger;
2585e28a4053SRui Paulo 	cb.set_port_authorized = ieee802_1x_set_port_authorized;
2586e28a4053SRui Paulo 	cb.abort_auth = _ieee802_1x_abort_auth;
2587c1d255d3SCy Schubert #ifdef CONFIG_WEP
2588e28a4053SRui Paulo 	cb.tx_key = _ieee802_1x_tx_key;
2589c1d255d3SCy Schubert #endif /* CONFIG_WEP */
2590e28a4053SRui Paulo 	cb.eapol_event = ieee802_1x_eapol_event;
25915b9c547cSRui Paulo #ifdef CONFIG_ERP
25925b9c547cSRui Paulo 	cb.erp_get_key = ieee802_1x_erp_get_key;
25935b9c547cSRui Paulo 	cb.erp_add_key = ieee802_1x_erp_add_key;
25945b9c547cSRui Paulo #endif /* CONFIG_ERP */
2595e28a4053SRui Paulo 
2596e28a4053SRui Paulo 	hapd->eapol_auth = eapol_auth_init(&conf, &cb);
2597c1d255d3SCy Schubert 	if (!hapd->eapol_auth)
2598e28a4053SRui Paulo 		return -1;
2599e28a4053SRui Paulo 
2600e28a4053SRui Paulo 	if ((hapd->conf->ieee802_1x || hapd->conf->wpa) &&
2601f05cddf9SRui Paulo 	    hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1))
2602e28a4053SRui Paulo 		return -1;
2603e28a4053SRui Paulo 
2604e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
2605e28a4053SRui Paulo 	if (radius_client_register(hapd->radius, RADIUS_AUTH,
2606e28a4053SRui Paulo 				   ieee802_1x_receive_auth, hapd))
2607e28a4053SRui Paulo 		return -1;
2608e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
2609e28a4053SRui Paulo 
2610c1d255d3SCy Schubert #ifdef CONFIG_WEP
2611e28a4053SRui Paulo 	if (hapd->conf->default_wep_key_len) {
2612c1d255d3SCy Schubert 		int i;
2613c1d255d3SCy Schubert 
2614e28a4053SRui Paulo 		for (i = 0; i < 4; i++)
2615f05cddf9SRui Paulo 			hostapd_drv_set_key(hapd->conf->iface, hapd,
2616c1d255d3SCy Schubert 					    WPA_ALG_NONE, NULL, i, 0, 0, NULL,
2617c1d255d3SCy Schubert 					    0, NULL, 0, KEY_FLAG_GROUP);
2618e28a4053SRui Paulo 
2619e28a4053SRui Paulo 		ieee802_1x_rekey(hapd, NULL);
2620e28a4053SRui Paulo 
2621c1d255d3SCy Schubert 		if (!hapd->eapol_auth->default_wep_key)
2622e28a4053SRui Paulo 			return -1;
2623e28a4053SRui Paulo 	}
2624c1d255d3SCy Schubert #endif /* CONFIG_WEP */
2625e28a4053SRui Paulo 
2626e28a4053SRui Paulo 	return 0;
2627e28a4053SRui Paulo }
2628e28a4053SRui Paulo 
2629e28a4053SRui Paulo 
ieee802_1x_erp_flush(struct hostapd_data * hapd)26305b9c547cSRui Paulo void ieee802_1x_erp_flush(struct hostapd_data *hapd)
26315b9c547cSRui Paulo {
26325b9c547cSRui Paulo 	struct eap_server_erp_key *erp;
26335b9c547cSRui Paulo 
26345b9c547cSRui Paulo 	while ((erp = dl_list_first(&hapd->erp_keys, struct eap_server_erp_key,
26355b9c547cSRui Paulo 				    list)) != NULL) {
26365b9c547cSRui Paulo 		dl_list_del(&erp->list);
26375b9c547cSRui Paulo 		bin_clear_free(erp, sizeof(*erp));
26385b9c547cSRui Paulo 	}
26395b9c547cSRui Paulo }
26405b9c547cSRui Paulo 
26415b9c547cSRui Paulo 
ieee802_1x_deinit(struct hostapd_data * hapd)2642e28a4053SRui Paulo void ieee802_1x_deinit(struct hostapd_data *hapd)
2643e28a4053SRui Paulo {
2644*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
2645*a90b9d01SCy Schubert 	if (!hostapd_mld_is_first_bss(hapd)) {
2646*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
2647*a90b9d01SCy Schubert 			   "MLD: Deinit IEEE 802.1X state machine of a non-first BSS");
2648*a90b9d01SCy Schubert 
2649*a90b9d01SCy Schubert 		hapd->eapol_auth = NULL;
2650*a90b9d01SCy Schubert 		return;
2651*a90b9d01SCy Schubert 	}
2652*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
2653*a90b9d01SCy Schubert 
2654c1d255d3SCy Schubert #ifdef CONFIG_WEP
2655e28a4053SRui Paulo 	eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
2656c1d255d3SCy Schubert #endif /* CONFIG_WEP */
2657e28a4053SRui Paulo 
2658780fb4a2SCy Schubert 	if (hapd->driver && hapd->drv_priv &&
2659e28a4053SRui Paulo 	    (hapd->conf->ieee802_1x || hapd->conf->wpa))
2660f05cddf9SRui Paulo 		hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
2661e28a4053SRui Paulo 
2662e28a4053SRui Paulo 	eapol_auth_deinit(hapd->eapol_auth);
2663e28a4053SRui Paulo 	hapd->eapol_auth = NULL;
26645b9c547cSRui Paulo 
26655b9c547cSRui Paulo 	ieee802_1x_erp_flush(hapd);
2666e28a4053SRui Paulo }
2667e28a4053SRui Paulo 
2668e28a4053SRui Paulo 
ieee802_1x_tx_status(struct hostapd_data * hapd,struct sta_info * sta,const u8 * buf,size_t len,int ack)2669e28a4053SRui Paulo int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
2670e28a4053SRui Paulo 			 const u8 *buf, size_t len, int ack)
2671e28a4053SRui Paulo {
2672e28a4053SRui Paulo 	struct ieee80211_hdr *hdr;
2673e28a4053SRui Paulo 	u8 *pos;
2674e28a4053SRui Paulo 	const unsigned char rfc1042_hdr[ETH_ALEN] =
2675e28a4053SRui Paulo 		{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
2676e28a4053SRui Paulo 
2677c1d255d3SCy Schubert 	if (!sta)
2678e28a4053SRui Paulo 		return -1;
2679f05cddf9SRui Paulo 	if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2)
2680e28a4053SRui Paulo 		return 0;
2681e28a4053SRui Paulo 
2682e28a4053SRui Paulo 	hdr = (struct ieee80211_hdr *) buf;
2683e28a4053SRui Paulo 	pos = (u8 *) (hdr + 1);
2684e28a4053SRui Paulo 	if (os_memcmp(pos, rfc1042_hdr, sizeof(rfc1042_hdr)) != 0)
2685e28a4053SRui Paulo 		return 0;
2686e28a4053SRui Paulo 	pos += sizeof(rfc1042_hdr);
2687e28a4053SRui Paulo 	if (WPA_GET_BE16(pos) != ETH_P_PAE)
2688e28a4053SRui Paulo 		return 0;
2689e28a4053SRui Paulo 	pos += 2;
2690e28a4053SRui Paulo 
2691f05cddf9SRui Paulo 	return ieee802_1x_eapol_tx_status(hapd, sta, pos, buf + len - pos,
2692f05cddf9SRui Paulo 					  ack);
2693f05cddf9SRui Paulo }
2694e28a4053SRui Paulo 
2695f05cddf9SRui Paulo 
ieee802_1x_eapol_tx_status(struct hostapd_data * hapd,struct sta_info * sta,const u8 * buf,int len,int ack)2696f05cddf9SRui Paulo int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
2697f05cddf9SRui Paulo 			       const u8 *buf, int len, int ack)
2698f05cddf9SRui Paulo {
2699f05cddf9SRui Paulo 	const struct ieee802_1x_hdr *xhdr =
2700f05cddf9SRui Paulo 		(const struct ieee802_1x_hdr *) buf;
2701f05cddf9SRui Paulo 	const u8 *pos = buf + sizeof(*xhdr);
2702f05cddf9SRui Paulo 	struct ieee802_1x_eapol_key *key;
2703f05cddf9SRui Paulo 
2704f05cddf9SRui Paulo 	if (len < (int) sizeof(*xhdr))
2705f05cddf9SRui Paulo 		return 0;
2706c1d255d3SCy Schubert 	wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
2707c1d255d3SCy Schubert 		   " TX status - version=%d type=%d length=%d - ack=%d",
2708e28a4053SRui Paulo 		   MAC2STR(sta->addr), xhdr->version, xhdr->type,
2709e28a4053SRui Paulo 		   be_to_host16(xhdr->length), ack);
2710e28a4053SRui Paulo 
271185732ac8SCy Schubert #ifdef CONFIG_WPS
271285732ac8SCy Schubert 	if (xhdr->type == IEEE802_1X_TYPE_EAP_PACKET && ack &&
271385732ac8SCy Schubert 	    (sta->flags & WLAN_STA_WPS) &&
271485732ac8SCy Schubert 	    ap_sta_pending_delayed_1x_auth_fail_disconnect(hapd, sta)) {
271585732ac8SCy Schubert 		wpa_printf(MSG_DEBUG,
271685732ac8SCy Schubert 			   "WPS: Indicate EAP completion on ACK for EAP-Failure");
271785732ac8SCy Schubert 		hostapd_wps_eap_completed(hapd);
271885732ac8SCy Schubert 	}
271985732ac8SCy Schubert #endif /* CONFIG_WPS */
272085732ac8SCy Schubert 
2721f05cddf9SRui Paulo 	if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY)
2722f05cddf9SRui Paulo 		return 0;
2723f05cddf9SRui Paulo 
2724f05cddf9SRui Paulo 	if (pos + sizeof(struct wpa_eapol_key) <= buf + len) {
2725f05cddf9SRui Paulo 		const struct wpa_eapol_key *wpa;
2726c1d255d3SCy Schubert 
2727f05cddf9SRui Paulo 		wpa = (const struct wpa_eapol_key *) pos;
2728f05cddf9SRui Paulo 		if (wpa->type == EAPOL_KEY_TYPE_RSN ||
2729f05cddf9SRui Paulo 		    wpa->type == EAPOL_KEY_TYPE_WPA)
2730f05cddf9SRui Paulo 			wpa_auth_eapol_key_tx_status(hapd->wpa_auth,
2731f05cddf9SRui Paulo 						     sta->wpa_sm, ack);
2732f05cddf9SRui Paulo 	}
2733f05cddf9SRui Paulo 
2734e28a4053SRui Paulo 	/* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant
2735e28a4053SRui Paulo 	 * or Authenticator state machines, but EAPOL-Key packets are not
2736f05cddf9SRui Paulo 	 * retransmitted in case of failure. Try to re-send failed EAPOL-Key
2737e28a4053SRui Paulo 	 * packets couple of times because otherwise STA keys become
2738e28a4053SRui Paulo 	 * unsynchronized with AP. */
2739f05cddf9SRui Paulo 	if (!ack && pos + sizeof(*key) <= buf + len) {
2740e28a4053SRui Paulo 		key = (struct ieee802_1x_eapol_key *) pos;
2741e28a4053SRui Paulo 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
2742c1d255d3SCy Schubert 			       HOSTAPD_LEVEL_DEBUG,
2743c1d255d3SCy Schubert 			       "did not Ack EAPOL-Key frame (%scast index=%d)",
2744e28a4053SRui Paulo 			       key->key_index & BIT(7) ? "uni" : "broad",
2745e28a4053SRui Paulo 			       key->key_index & ~BIT(7));
2746e28a4053SRui Paulo 		/* TODO: re-send EAPOL-Key couple of times (with short delay
2747e28a4053SRui Paulo 		 * between them?). If all attempt fail, report error and
2748e28a4053SRui Paulo 		 * deauthenticate STA so that it will get new keys when
2749e28a4053SRui Paulo 		 * authenticating again (e.g., after returning in range).
2750e28a4053SRui Paulo 		 * Separate limit/transmit state needed both for unicast and
2751e28a4053SRui Paulo 		 * broadcast keys(?) */
2752e28a4053SRui Paulo 	}
2753e28a4053SRui Paulo 	/* TODO: could move unicast key configuration from ieee802_1x_tx_key()
2754e28a4053SRui Paulo 	 * to here and change the key only if the EAPOL-Key packet was Acked.
2755e28a4053SRui Paulo 	 */
2756e28a4053SRui Paulo 
2757e28a4053SRui Paulo 	return 1;
2758e28a4053SRui Paulo }
2759e28a4053SRui Paulo 
2760e28a4053SRui Paulo 
ieee802_1x_get_identity(struct eapol_state_machine * sm,size_t * len)2761e28a4053SRui Paulo u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len)
2762e28a4053SRui Paulo {
2763c1d255d3SCy Schubert 	if (!sm || !sm->identity)
2764e28a4053SRui Paulo 		return NULL;
2765e28a4053SRui Paulo 
2766e28a4053SRui Paulo 	*len = sm->identity_len;
2767e28a4053SRui Paulo 	return sm->identity;
2768e28a4053SRui Paulo }
2769e28a4053SRui Paulo 
2770e28a4053SRui Paulo 
ieee802_1x_get_radius_class(struct eapol_state_machine * sm,size_t * len,int idx)2771e28a4053SRui Paulo u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
2772e28a4053SRui Paulo 				 int idx)
2773e28a4053SRui Paulo {
2774c1d255d3SCy Schubert 	if (!sm || !sm->radius_class.attr ||
2775e28a4053SRui Paulo 	    idx >= (int) sm->radius_class.count)
2776e28a4053SRui Paulo 		return NULL;
2777e28a4053SRui Paulo 
2778e28a4053SRui Paulo 	*len = sm->radius_class.attr[idx].len;
2779e28a4053SRui Paulo 	return sm->radius_class.attr[idx].data;
2780e28a4053SRui Paulo }
2781e28a4053SRui Paulo 
2782e28a4053SRui Paulo 
ieee802_1x_get_radius_cui(struct eapol_state_machine * sm)2783f05cddf9SRui Paulo struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm)
2784f05cddf9SRui Paulo {
2785c1d255d3SCy Schubert 	if (!sm)
2786f05cddf9SRui Paulo 		return NULL;
2787f05cddf9SRui Paulo 	return sm->radius_cui;
2788f05cddf9SRui Paulo }
2789f05cddf9SRui Paulo 
2790f05cddf9SRui Paulo 
ieee802_1x_get_key(struct eapol_state_machine * sm,size_t * len)2791e28a4053SRui Paulo const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len)
2792e28a4053SRui Paulo {
2793f05cddf9SRui Paulo 	*len = 0;
2794c1d255d3SCy Schubert 	if (!sm)
2795e28a4053SRui Paulo 		return NULL;
2796e28a4053SRui Paulo 
2797e28a4053SRui Paulo 	*len = sm->eap_if->eapKeyDataLen;
2798e28a4053SRui Paulo 	return sm->eap_if->eapKeyData;
2799e28a4053SRui Paulo }
2800e28a4053SRui Paulo 
2801e28a4053SRui Paulo 
2802206b73d0SCy Schubert #ifdef CONFIG_MACSEC
ieee802_1x_get_session_id(struct eapol_state_machine * sm,size_t * len)2803206b73d0SCy Schubert const u8 * ieee802_1x_get_session_id(struct eapol_state_machine *sm,
2804206b73d0SCy Schubert 				     size_t *len)
2805206b73d0SCy Schubert {
2806206b73d0SCy Schubert 	*len = 0;
2807206b73d0SCy Schubert 	if (!sm || !sm->eap_if)
2808206b73d0SCy Schubert 		return NULL;
2809206b73d0SCy Schubert 
2810206b73d0SCy Schubert 	*len = sm->eap_if->eapSessionIdLen;
2811206b73d0SCy Schubert 	return sm->eap_if->eapSessionId;
2812206b73d0SCy Schubert }
2813206b73d0SCy Schubert #endif /* CONFIG_MACSEC */
2814206b73d0SCy Schubert 
2815206b73d0SCy Schubert 
ieee802_1x_notify_port_enabled(struct eapol_state_machine * sm,bool enabled)2816e28a4053SRui Paulo void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
2817c1d255d3SCy Schubert 				    bool enabled)
2818e28a4053SRui Paulo {
2819c1d255d3SCy Schubert 	if (!sm)
2820e28a4053SRui Paulo 		return;
2821c1d255d3SCy Schubert 	sm->eap_if->portEnabled = enabled;
2822e28a4053SRui Paulo 	eapol_auth_step(sm);
2823e28a4053SRui Paulo }
2824e28a4053SRui Paulo 
2825e28a4053SRui Paulo 
ieee802_1x_notify_port_valid(struct eapol_state_machine * sm,bool valid)2826c1d255d3SCy Schubert void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, bool valid)
2827e28a4053SRui Paulo {
2828c1d255d3SCy Schubert 	if (!sm)
2829e28a4053SRui Paulo 		return;
2830c1d255d3SCy Schubert 	sm->portValid = valid;
2831e28a4053SRui Paulo 	eapol_auth_step(sm);
2832e28a4053SRui Paulo }
2833e28a4053SRui Paulo 
2834e28a4053SRui Paulo 
ieee802_1x_notify_pre_auth(struct eapol_state_machine * sm,bool pre_auth)2835c1d255d3SCy Schubert void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, bool pre_auth)
2836e28a4053SRui Paulo {
2837c1d255d3SCy Schubert 	if (!sm)
2838e28a4053SRui Paulo 		return;
2839e28a4053SRui Paulo 	if (pre_auth)
2840e28a4053SRui Paulo 		sm->flags |= EAPOL_SM_PREAUTH;
2841e28a4053SRui Paulo 	else
2842e28a4053SRui Paulo 		sm->flags &= ~EAPOL_SM_PREAUTH;
2843e28a4053SRui Paulo }
2844e28a4053SRui Paulo 
2845e28a4053SRui Paulo 
bool_txt(bool val)2846c1d255d3SCy Schubert static const char * bool_txt(bool val)
2847e28a4053SRui Paulo {
2848325151a3SRui Paulo 	return val ? "TRUE" : "FALSE";
2849e28a4053SRui Paulo }
2850e28a4053SRui Paulo 
2851e28a4053SRui Paulo 
ieee802_1x_get_mib(struct hostapd_data * hapd,char * buf,size_t buflen)2852e28a4053SRui Paulo int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
2853e28a4053SRui Paulo {
2854e28a4053SRui Paulo 	/* TODO */
2855e28a4053SRui Paulo 	return 0;
2856e28a4053SRui Paulo }
2857e28a4053SRui Paulo 
2858e28a4053SRui Paulo 
ieee802_1x_get_mib_sta(struct hostapd_data * hapd,struct sta_info * sta,char * buf,size_t buflen)2859e28a4053SRui Paulo int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
2860e28a4053SRui Paulo 			   char *buf, size_t buflen)
2861e28a4053SRui Paulo {
2862e28a4053SRui Paulo 	int len = 0, ret;
2863e28a4053SRui Paulo 	struct eapol_state_machine *sm = sta->eapol_sm;
28645b9c547cSRui Paulo 	struct os_reltime diff;
28655b9c547cSRui Paulo 	const char *name1;
28665b9c547cSRui Paulo 	const char *name2;
28674bc52338SCy Schubert 	char *identity_buf = NULL;
2868e28a4053SRui Paulo 
2869c1d255d3SCy Schubert 	if (!sm)
2870e28a4053SRui Paulo 		return 0;
2871e28a4053SRui Paulo 
2872e28a4053SRui Paulo 	ret = os_snprintf(buf + len, buflen - len,
2873e28a4053SRui Paulo 			  "dot1xPaePortNumber=%d\n"
2874e28a4053SRui Paulo 			  "dot1xPaePortProtocolVersion=%d\n"
2875e28a4053SRui Paulo 			  "dot1xPaePortCapabilities=1\n"
2876e28a4053SRui Paulo 			  "dot1xPaePortInitialize=%d\n"
2877e28a4053SRui Paulo 			  "dot1xPaePortReauthenticate=FALSE\n",
2878e28a4053SRui Paulo 			  sta->aid,
2879e28a4053SRui Paulo 			  EAPOL_VERSION,
2880e28a4053SRui Paulo 			  sm->initialize);
28815b9c547cSRui Paulo 	if (os_snprintf_error(buflen - len, ret))
2882e28a4053SRui Paulo 		return len;
2883e28a4053SRui Paulo 	len += ret;
2884e28a4053SRui Paulo 
2885e28a4053SRui Paulo 	/* dot1xAuthConfigTable */
2886e28a4053SRui Paulo 	ret = os_snprintf(buf + len, buflen - len,
2887e28a4053SRui Paulo 			  "dot1xAuthPaeState=%d\n"
2888e28a4053SRui Paulo 			  "dot1xAuthBackendAuthState=%d\n"
2889e28a4053SRui Paulo 			  "dot1xAuthAdminControlledDirections=%d\n"
2890e28a4053SRui Paulo 			  "dot1xAuthOperControlledDirections=%d\n"
2891e28a4053SRui Paulo 			  "dot1xAuthAuthControlledPortStatus=%d\n"
2892e28a4053SRui Paulo 			  "dot1xAuthAuthControlledPortControl=%d\n"
2893e28a4053SRui Paulo 			  "dot1xAuthQuietPeriod=%u\n"
2894e28a4053SRui Paulo 			  "dot1xAuthServerTimeout=%u\n"
2895e28a4053SRui Paulo 			  "dot1xAuthReAuthPeriod=%u\n"
2896e28a4053SRui Paulo 			  "dot1xAuthReAuthEnabled=%s\n"
2897e28a4053SRui Paulo 			  "dot1xAuthKeyTxEnabled=%s\n",
2898e28a4053SRui Paulo 			  sm->auth_pae_state + 1,
2899e28a4053SRui Paulo 			  sm->be_auth_state + 1,
2900e28a4053SRui Paulo 			  sm->adminControlledDirections,
2901e28a4053SRui Paulo 			  sm->operControlledDirections,
2902e28a4053SRui Paulo 			  sm->authPortStatus,
2903e28a4053SRui Paulo 			  sm->portControl,
2904e28a4053SRui Paulo 			  sm->quietPeriod,
2905e28a4053SRui Paulo 			  sm->serverTimeout,
2906e28a4053SRui Paulo 			  sm->reAuthPeriod,
2907e28a4053SRui Paulo 			  bool_txt(sm->reAuthEnabled),
2908e28a4053SRui Paulo 			  bool_txt(sm->keyTxEnabled));
29095b9c547cSRui Paulo 	if (os_snprintf_error(buflen - len, ret))
2910e28a4053SRui Paulo 		return len;
2911e28a4053SRui Paulo 	len += ret;
2912e28a4053SRui Paulo 
2913e28a4053SRui Paulo 	/* dot1xAuthStatsTable */
2914e28a4053SRui Paulo 	ret = os_snprintf(buf + len, buflen - len,
2915e28a4053SRui Paulo 			  "dot1xAuthEapolFramesRx=%u\n"
2916e28a4053SRui Paulo 			  "dot1xAuthEapolFramesTx=%u\n"
2917e28a4053SRui Paulo 			  "dot1xAuthEapolStartFramesRx=%u\n"
2918e28a4053SRui Paulo 			  "dot1xAuthEapolLogoffFramesRx=%u\n"
2919e28a4053SRui Paulo 			  "dot1xAuthEapolRespIdFramesRx=%u\n"
2920e28a4053SRui Paulo 			  "dot1xAuthEapolRespFramesRx=%u\n"
2921e28a4053SRui Paulo 			  "dot1xAuthEapolReqIdFramesTx=%u\n"
2922e28a4053SRui Paulo 			  "dot1xAuthEapolReqFramesTx=%u\n"
2923e28a4053SRui Paulo 			  "dot1xAuthInvalidEapolFramesRx=%u\n"
2924e28a4053SRui Paulo 			  "dot1xAuthEapLengthErrorFramesRx=%u\n"
2925e28a4053SRui Paulo 			  "dot1xAuthLastEapolFrameVersion=%u\n"
2926e28a4053SRui Paulo 			  "dot1xAuthLastEapolFrameSource=" MACSTR "\n",
2927e28a4053SRui Paulo 			  sm->dot1xAuthEapolFramesRx,
2928e28a4053SRui Paulo 			  sm->dot1xAuthEapolFramesTx,
2929e28a4053SRui Paulo 			  sm->dot1xAuthEapolStartFramesRx,
2930e28a4053SRui Paulo 			  sm->dot1xAuthEapolLogoffFramesRx,
2931e28a4053SRui Paulo 			  sm->dot1xAuthEapolRespIdFramesRx,
2932e28a4053SRui Paulo 			  sm->dot1xAuthEapolRespFramesRx,
2933e28a4053SRui Paulo 			  sm->dot1xAuthEapolReqIdFramesTx,
2934e28a4053SRui Paulo 			  sm->dot1xAuthEapolReqFramesTx,
2935e28a4053SRui Paulo 			  sm->dot1xAuthInvalidEapolFramesRx,
2936e28a4053SRui Paulo 			  sm->dot1xAuthEapLengthErrorFramesRx,
2937e28a4053SRui Paulo 			  sm->dot1xAuthLastEapolFrameVersion,
2938e28a4053SRui Paulo 			  MAC2STR(sm->addr));
29395b9c547cSRui Paulo 	if (os_snprintf_error(buflen - len, ret))
2940e28a4053SRui Paulo 		return len;
2941e28a4053SRui Paulo 	len += ret;
2942e28a4053SRui Paulo 
2943e28a4053SRui Paulo 	/* dot1xAuthDiagTable */
2944e28a4053SRui Paulo 	ret = os_snprintf(buf + len, buflen - len,
2945e28a4053SRui Paulo 			  "dot1xAuthEntersConnecting=%u\n"
2946e28a4053SRui Paulo 			  "dot1xAuthEapLogoffsWhileConnecting=%u\n"
2947e28a4053SRui Paulo 			  "dot1xAuthEntersAuthenticating=%u\n"
2948e28a4053SRui Paulo 			  "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n"
2949e28a4053SRui Paulo 			  "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n"
2950e28a4053SRui Paulo 			  "dot1xAuthAuthFailWhileAuthenticating=%u\n"
2951e28a4053SRui Paulo 			  "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n"
2952e28a4053SRui Paulo 			  "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n"
2953e28a4053SRui Paulo 			  "dot1xAuthAuthReauthsWhileAuthenticated=%u\n"
2954e28a4053SRui Paulo 			  "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n"
2955e28a4053SRui Paulo 			  "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n"
2956e28a4053SRui Paulo 			  "dot1xAuthBackendResponses=%u\n"
2957e28a4053SRui Paulo 			  "dot1xAuthBackendAccessChallenges=%u\n"
2958e28a4053SRui Paulo 			  "dot1xAuthBackendOtherRequestsToSupplicant=%u\n"
2959e28a4053SRui Paulo 			  "dot1xAuthBackendAuthSuccesses=%u\n"
2960e28a4053SRui Paulo 			  "dot1xAuthBackendAuthFails=%u\n",
2961e28a4053SRui Paulo 			  sm->authEntersConnecting,
2962e28a4053SRui Paulo 			  sm->authEapLogoffsWhileConnecting,
2963e28a4053SRui Paulo 			  sm->authEntersAuthenticating,
2964e28a4053SRui Paulo 			  sm->authAuthSuccessesWhileAuthenticating,
2965e28a4053SRui Paulo 			  sm->authAuthTimeoutsWhileAuthenticating,
2966e28a4053SRui Paulo 			  sm->authAuthFailWhileAuthenticating,
2967e28a4053SRui Paulo 			  sm->authAuthEapStartsWhileAuthenticating,
2968e28a4053SRui Paulo 			  sm->authAuthEapLogoffWhileAuthenticating,
2969e28a4053SRui Paulo 			  sm->authAuthReauthsWhileAuthenticated,
2970e28a4053SRui Paulo 			  sm->authAuthEapStartsWhileAuthenticated,
2971e28a4053SRui Paulo 			  sm->authAuthEapLogoffWhileAuthenticated,
2972e28a4053SRui Paulo 			  sm->backendResponses,
2973e28a4053SRui Paulo 			  sm->backendAccessChallenges,
2974e28a4053SRui Paulo 			  sm->backendOtherRequestsToSupplicant,
2975e28a4053SRui Paulo 			  sm->backendAuthSuccesses,
2976e28a4053SRui Paulo 			  sm->backendAuthFails);
29775b9c547cSRui Paulo 	if (os_snprintf_error(buflen - len, ret))
2978e28a4053SRui Paulo 		return len;
2979e28a4053SRui Paulo 	len += ret;
2980e28a4053SRui Paulo 
2981e28a4053SRui Paulo 	/* dot1xAuthSessionStatsTable */
29825b9c547cSRui Paulo 	os_reltime_age(&sta->acct_session_start, &diff);
29834bc52338SCy Schubert 	if (sm->eap && !sm->identity) {
29844bc52338SCy Schubert 		const u8 *id;
29854bc52338SCy Schubert 		size_t id_len;
29864bc52338SCy Schubert 
29874bc52338SCy Schubert 		id = eap_get_identity(sm->eap, &id_len);
29884bc52338SCy Schubert 		if (id)
29894bc52338SCy Schubert 			identity_buf = dup_binstr(id, id_len);
29904bc52338SCy Schubert 	}
2991e28a4053SRui Paulo 	ret = os_snprintf(buf + len, buflen - len,
2992e28a4053SRui Paulo 			  /* TODO: dot1xAuthSessionOctetsRx */
2993e28a4053SRui Paulo 			  /* TODO: dot1xAuthSessionOctetsTx */
2994e28a4053SRui Paulo 			  /* TODO: dot1xAuthSessionFramesRx */
2995e28a4053SRui Paulo 			  /* TODO: dot1xAuthSessionFramesTx */
2996780fb4a2SCy Schubert 			  "dot1xAuthSessionId=%016llX\n"
2997e28a4053SRui Paulo 			  "dot1xAuthSessionAuthenticMethod=%d\n"
2998e28a4053SRui Paulo 			  "dot1xAuthSessionTime=%u\n"
2999e28a4053SRui Paulo 			  "dot1xAuthSessionTerminateCause=999\n"
3000e28a4053SRui Paulo 			  "dot1xAuthSessionUserName=%s\n",
3001780fb4a2SCy Schubert 			  (unsigned long long) sta->acct_session_id,
3002e28a4053SRui Paulo 			  (wpa_key_mgmt_wpa_ieee8021x(
3003e28a4053SRui Paulo 				   wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
3004e28a4053SRui Paulo 			  1 : 2,
30055b9c547cSRui Paulo 			  (unsigned int) diff.sec,
30064bc52338SCy Schubert 			  sm->identity ? (char *) sm->identity :
30074bc52338SCy Schubert 					 (identity_buf ? identity_buf : "N/A"));
30084bc52338SCy Schubert 	os_free(identity_buf);
30095b9c547cSRui Paulo 	if (os_snprintf_error(buflen - len, ret))
30105b9c547cSRui Paulo 		return len;
30115b9c547cSRui Paulo 	len += ret;
30125b9c547cSRui Paulo 
3013780fb4a2SCy Schubert 	if (sm->acct_multi_session_id) {
30145b9c547cSRui Paulo 		ret = os_snprintf(buf + len, buflen - len,
3015780fb4a2SCy Schubert 				  "authMultiSessionId=%016llX\n",
3016780fb4a2SCy Schubert 				  (unsigned long long)
3017780fb4a2SCy Schubert 				  sm->acct_multi_session_id);
30185b9c547cSRui Paulo 		if (os_snprintf_error(buflen - len, ret))
30195b9c547cSRui Paulo 			return len;
30205b9c547cSRui Paulo 		len += ret;
30215b9c547cSRui Paulo 	}
30225b9c547cSRui Paulo 
30235b9c547cSRui Paulo 	name1 = eap_server_get_name(0, sm->eap_type_authsrv);
30245b9c547cSRui Paulo 	name2 = eap_server_get_name(0, sm->eap_type_supp);
30255b9c547cSRui Paulo 	ret = os_snprintf(buf + len, buflen - len,
30265b9c547cSRui Paulo 			  "last_eap_type_as=%d (%s)\n"
30275b9c547cSRui Paulo 			  "last_eap_type_sta=%d (%s)\n",
30285b9c547cSRui Paulo 			  sm->eap_type_authsrv, name1,
30295b9c547cSRui Paulo 			  sm->eap_type_supp, name2);
30305b9c547cSRui Paulo 	if (os_snprintf_error(buflen - len, ret))
3031e28a4053SRui Paulo 		return len;
3032e28a4053SRui Paulo 	len += ret;
3033e28a4053SRui Paulo 
3034e28a4053SRui Paulo 	return len;
3035e28a4053SRui Paulo }
3036e28a4053SRui Paulo 
3037e28a4053SRui Paulo 
3038780fb4a2SCy Schubert #ifdef CONFIG_HS20
ieee802_1x_wnm_notif_send(void * eloop_ctx,void * timeout_ctx)3039780fb4a2SCy Schubert static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
3040780fb4a2SCy Schubert {
3041780fb4a2SCy Schubert 	struct hostapd_data *hapd = eloop_ctx;
3042780fb4a2SCy Schubert 	struct sta_info *sta = timeout_ctx;
3043780fb4a2SCy Schubert 
3044780fb4a2SCy Schubert 	if (sta->remediation) {
3045780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
3046780fb4a2SCy Schubert 			   MACSTR " to indicate Subscription Remediation",
3047780fb4a2SCy Schubert 			   MAC2STR(sta->addr));
3048780fb4a2SCy Schubert 		hs20_send_wnm_notification(hapd, sta->addr,
3049780fb4a2SCy Schubert 					   sta->remediation_method,
3050780fb4a2SCy Schubert 					   sta->remediation_url);
3051780fb4a2SCy Schubert 		os_free(sta->remediation_url);
3052780fb4a2SCy Schubert 		sta->remediation_url = NULL;
3053780fb4a2SCy Schubert 	}
3054780fb4a2SCy Schubert 
3055780fb4a2SCy Schubert 	if (sta->hs20_deauth_req) {
3056780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
3057780fb4a2SCy Schubert 			   MACSTR " to indicate imminent deauthentication",
3058780fb4a2SCy Schubert 			   MAC2STR(sta->addr));
3059780fb4a2SCy Schubert 		hs20_send_wnm_notification_deauth_req(hapd, sta->addr,
3060780fb4a2SCy Schubert 						      sta->hs20_deauth_req);
3061780fb4a2SCy Schubert 	}
306285732ac8SCy Schubert 
306385732ac8SCy Schubert 	if (sta->hs20_t_c_filtering) {
306485732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
306585732ac8SCy Schubert 			   MACSTR " to indicate Terms and Conditions filtering",
306685732ac8SCy Schubert 			   MAC2STR(sta->addr));
306785732ac8SCy Schubert 		hs20_send_wnm_notification_t_c(hapd, sta->addr, sta->t_c_url);
306885732ac8SCy Schubert 		os_free(sta->t_c_url);
306985732ac8SCy Schubert 		sta->t_c_url = NULL;
307085732ac8SCy Schubert 	}
3071780fb4a2SCy Schubert }
3072780fb4a2SCy Schubert #endif /* CONFIG_HS20 */
3073780fb4a2SCy Schubert 
3074780fb4a2SCy Schubert 
ieee802_1x_finished(struct hostapd_data * hapd,struct sta_info * sta,int success,int remediation,bool logoff)3075*a90b9d01SCy Schubert static bool ieee802_1x_finished(struct hostapd_data *hapd,
30765b9c547cSRui Paulo 				struct sta_info *sta, int success,
3077*a90b9d01SCy Schubert 				int remediation, bool logoff)
3078e28a4053SRui Paulo {
3079e28a4053SRui Paulo 	const u8 *key;
3080e28a4053SRui Paulo 	size_t len;
3081e28a4053SRui Paulo 	/* TODO: get PMKLifetime from WPA parameters */
3082e28a4053SRui Paulo 	static const int dot11RSNAConfigPMKLifetime = 43200;
30835b9c547cSRui Paulo 	unsigned int session_timeout;
308485732ac8SCy Schubert 	struct os_reltime now, remaining;
30855b9c547cSRui Paulo 
30865b9c547cSRui Paulo #ifdef CONFIG_HS20
30875b9c547cSRui Paulo 	if (remediation && !sta->remediation) {
30885b9c547cSRui Paulo 		sta->remediation = 1;
30895b9c547cSRui Paulo 		os_free(sta->remediation_url);
30905b9c547cSRui Paulo 		sta->remediation_url =
30915b9c547cSRui Paulo 			os_strdup(hapd->conf->subscr_remediation_url);
30925b9c547cSRui Paulo 		sta->remediation_method = 1; /* SOAP-XML SPP */
30935b9c547cSRui Paulo 	}
30945b9c547cSRui Paulo 
309585732ac8SCy Schubert 	if (success && (sta->remediation || sta->hs20_deauth_req ||
309685732ac8SCy Schubert 			sta->hs20_t_c_filtering)) {
3097780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to "
3098780fb4a2SCy Schubert 			   MACSTR " in 100 ms", MAC2STR(sta->addr));
3099780fb4a2SCy Schubert 		eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
3100780fb4a2SCy Schubert 		eloop_register_timeout(0, 100000, ieee802_1x_wnm_notif_send,
3101780fb4a2SCy Schubert 				       hapd, sta);
31025b9c547cSRui Paulo 	}
31035b9c547cSRui Paulo #endif /* CONFIG_HS20 */
3104e28a4053SRui Paulo 
3105206b73d0SCy Schubert #ifdef CONFIG_MACSEC
3106206b73d0SCy Schubert 	ieee802_1x_notify_create_actor_hapd(hapd, sta);
3107206b73d0SCy Schubert #endif /* CONFIG_MACSEC */
3108206b73d0SCy Schubert 
3109e28a4053SRui Paulo 	key = ieee802_1x_get_key(sta->eapol_sm, &len);
311085732ac8SCy Schubert 	if (sta->session_timeout_set) {
311185732ac8SCy Schubert 		os_get_reltime(&now);
311285732ac8SCy Schubert 		os_reltime_sub(&sta->session_timeout, &now, &remaining);
311385732ac8SCy Schubert 		session_timeout = (remaining.sec > 0) ? remaining.sec : 1;
311485732ac8SCy Schubert 	} else {
31155b9c547cSRui Paulo 		session_timeout = dot11RSNAConfigPMKLifetime;
311685732ac8SCy Schubert 	}
31175b9c547cSRui Paulo 	if (success && key && len >= PMK_LEN && !sta->remediation &&
31185b9c547cSRui Paulo 	    !sta->hs20_deauth_requested &&
3119780fb4a2SCy Schubert 	    wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout,
3120e28a4053SRui Paulo 			       sta->eapol_sm) == 0) {
3121e28a4053SRui Paulo 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
3122e28a4053SRui Paulo 			       HOSTAPD_LEVEL_DEBUG,
3123e28a4053SRui Paulo 			       "Added PMKSA cache entry (IEEE 802.1X)");
3124e28a4053SRui Paulo 	}
3125e28a4053SRui Paulo 
3126f05cddf9SRui Paulo 	if (!success) {
3127e28a4053SRui Paulo 		/*
3128e28a4053SRui Paulo 		 * Many devices require deauthentication after WPS provisioning
3129e28a4053SRui Paulo 		 * and some may not be be able to do that themselves, so
3130f05cddf9SRui Paulo 		 * disconnect the client here. In addition, this may also
3131f05cddf9SRui Paulo 		 * benefit IEEE 802.1X/EAPOL authentication cases, too since
3132f05cddf9SRui Paulo 		 * the EAPOL PAE state machine would remain in HELD state for
3133f05cddf9SRui Paulo 		 * considerable amount of time and some EAP methods, like
3134f05cddf9SRui Paulo 		 * EAP-FAST with anonymous provisioning, may require another
3135f05cddf9SRui Paulo 		 * EAPOL authentication to be started to complete connection.
3136e28a4053SRui Paulo 		 */
3137*a90b9d01SCy Schubert 		ap_sta_delayed_1x_auth_fail_disconnect(hapd, sta,
3138*a90b9d01SCy Schubert 						       logoff ? 0 : 10);
3139*a90b9d01SCy Schubert 		if (logoff && sta->wpa_sm)
3140*a90b9d01SCy Schubert 			return true;
3141e28a4053SRui Paulo 	}
3142*a90b9d01SCy Schubert 
3143*a90b9d01SCy Schubert 	return false;
3144e28a4053SRui Paulo }
3145