xref: /freebsd/contrib/wpa/src/ap/ieee802_11_auth.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
1e28a4053SRui Paulo /*
2e28a4053SRui Paulo  * hostapd / IEEE 802.11 authentication (ACL)
3*a90b9d01SCy Schubert  * Copyright (c) 2003-2022, 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  * Access control list for IEEE 802.11 authentication can uses statically
9e28a4053SRui Paulo  * configured ACL from configuration files or an external RADIUS server.
10e28a4053SRui Paulo  * Results from external RADIUS queries are cached to allow faster
11e28a4053SRui Paulo  * authentication frame processing.
12e28a4053SRui Paulo  */
13e28a4053SRui Paulo 
14e28a4053SRui Paulo #include "utils/includes.h"
15e28a4053SRui Paulo 
16e28a4053SRui Paulo #include "utils/common.h"
17e28a4053SRui Paulo #include "utils/eloop.h"
18e28a4053SRui Paulo #include "radius/radius.h"
19e28a4053SRui Paulo #include "radius/radius_client.h"
20e28a4053SRui Paulo #include "hostapd.h"
21e28a4053SRui Paulo #include "ap_config.h"
22f05cddf9SRui Paulo #include "ap_drv_ops.h"
23*a90b9d01SCy Schubert #include "sta_info.h"
24*a90b9d01SCy Schubert #include "wpa_auth.h"
25e28a4053SRui Paulo #include "ieee802_11.h"
26f05cddf9SRui Paulo #include "ieee802_1x.h"
27e28a4053SRui Paulo #include "ieee802_11_auth.h"
28e28a4053SRui Paulo 
29e28a4053SRui Paulo #define RADIUS_ACL_TIMEOUT 30
30e28a4053SRui Paulo 
31e28a4053SRui Paulo 
32e28a4053SRui Paulo struct hostapd_cached_radius_acl {
335b9c547cSRui Paulo 	struct os_reltime timestamp;
34e28a4053SRui Paulo 	macaddr addr;
35e28a4053SRui Paulo 	int accepted; /* HOSTAPD_ACL_* */
36e28a4053SRui Paulo 	struct hostapd_cached_radius_acl *next;
37c1d255d3SCy Schubert 	struct radius_sta info;
38e28a4053SRui Paulo };
39e28a4053SRui Paulo 
40e28a4053SRui Paulo 
41e28a4053SRui Paulo struct hostapd_acl_query_data {
425b9c547cSRui Paulo 	struct os_reltime timestamp;
43e28a4053SRui Paulo 	u8 radius_id;
44e28a4053SRui Paulo 	macaddr addr;
45e28a4053SRui Paulo 	u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
46e28a4053SRui Paulo 	size_t auth_msg_len;
47e28a4053SRui Paulo 	struct hostapd_acl_query_data *next;
48*a90b9d01SCy Schubert 	bool radius_psk;
49*a90b9d01SCy Schubert 	int akm;
50*a90b9d01SCy Schubert 	u8 *anonce;
51*a90b9d01SCy Schubert 	u8 *eapol;
52*a90b9d01SCy Schubert 	size_t eapol_len;
53e28a4053SRui Paulo };
54e28a4053SRui Paulo 
55e28a4053SRui Paulo 
56e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl * e)57f05cddf9SRui Paulo static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e)
58f05cddf9SRui Paulo {
59c1d255d3SCy Schubert 	os_free(e->info.identity);
60c1d255d3SCy Schubert 	os_free(e->info.radius_cui);
61c1d255d3SCy Schubert 	hostapd_free_psk_list(e->info.psk);
62f05cddf9SRui Paulo 	os_free(e);
63f05cddf9SRui Paulo }
64f05cddf9SRui Paulo 
65f05cddf9SRui Paulo 
hostapd_acl_cache_free(struct hostapd_cached_radius_acl * acl_cache)66e28a4053SRui Paulo static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
67e28a4053SRui Paulo {
68e28a4053SRui Paulo 	struct hostapd_cached_radius_acl *prev;
69e28a4053SRui Paulo 
70e28a4053SRui Paulo 	while (acl_cache) {
71e28a4053SRui Paulo 		prev = acl_cache;
72e28a4053SRui Paulo 		acl_cache = acl_cache->next;
73f05cddf9SRui Paulo 		hostapd_acl_cache_free_entry(prev);
74e28a4053SRui Paulo 	}
75e28a4053SRui Paulo }
76e28a4053SRui Paulo 
77e28a4053SRui Paulo 
hostapd_acl_cache_get(struct hostapd_data * hapd,const u8 * addr,struct radius_sta * out)78e28a4053SRui Paulo static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
79c1d255d3SCy Schubert 				 struct radius_sta *out)
80e28a4053SRui Paulo {
81e28a4053SRui Paulo 	struct hostapd_cached_radius_acl *entry;
825b9c547cSRui Paulo 	struct os_reltime now;
83e28a4053SRui Paulo 
845b9c547cSRui Paulo 	os_get_reltime(&now);
85e28a4053SRui Paulo 
86f05cddf9SRui Paulo 	for (entry = hapd->acl_cache; entry; entry = entry->next) {
87*a90b9d01SCy Schubert 		if (!ether_addr_equal(entry->addr, addr))
88f05cddf9SRui Paulo 			continue;
89f05cddf9SRui Paulo 
905b9c547cSRui Paulo 		if (os_reltime_expired(&now, &entry->timestamp,
915b9c547cSRui Paulo 				       RADIUS_ACL_TIMEOUT))
92e28a4053SRui Paulo 			return -1; /* entry has expired */
93c1d255d3SCy Schubert 		*out = entry->info;
94c1d255d3SCy Schubert 
95f05cddf9SRui Paulo 		return entry->accepted;
96e28a4053SRui Paulo 	}
97e28a4053SRui Paulo 
98e28a4053SRui Paulo 	return -1;
99e28a4053SRui Paulo }
100e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
101e28a4053SRui Paulo 
102e28a4053SRui Paulo 
hostapd_acl_query_free(struct hostapd_acl_query_data * query)103e28a4053SRui Paulo static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
104e28a4053SRui Paulo {
105*a90b9d01SCy Schubert 	if (!query)
106e28a4053SRui Paulo 		return;
107e28a4053SRui Paulo 	os_free(query->auth_msg);
108*a90b9d01SCy Schubert 	os_free(query->anonce);
109*a90b9d01SCy Schubert 	os_free(query->eapol);
110e28a4053SRui Paulo 	os_free(query);
111e28a4053SRui Paulo }
112e28a4053SRui Paulo 
113e28a4053SRui Paulo 
114e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
hostapd_radius_acl_query(struct hostapd_data * hapd,const u8 * addr,struct hostapd_acl_query_data * query)115e28a4053SRui Paulo static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
116e28a4053SRui Paulo 				    struct hostapd_acl_query_data *query)
117e28a4053SRui Paulo {
118e28a4053SRui Paulo 	struct radius_msg *msg;
119e28a4053SRui Paulo 	char buf[128];
120e28a4053SRui Paulo 
121e28a4053SRui Paulo 	query->radius_id = radius_client_get_id(hapd->radius);
122e28a4053SRui Paulo 	msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
123*a90b9d01SCy Schubert 	if (!msg)
124e28a4053SRui Paulo 		return -1;
125e28a4053SRui Paulo 
126780fb4a2SCy Schubert 	if (radius_msg_make_authenticator(msg) < 0) {
127780fb4a2SCy Schubert 		wpa_printf(MSG_INFO, "Could not make Request Authenticator");
128780fb4a2SCy Schubert 		goto fail;
129780fb4a2SCy Schubert 	}
130e28a4053SRui Paulo 
131*a90b9d01SCy Schubert 	if (!radius_msg_add_msg_auth(msg))
132*a90b9d01SCy Schubert 		goto fail;
133*a90b9d01SCy Schubert 
134e28a4053SRui Paulo 	os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
135e28a4053SRui Paulo 	if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
136e28a4053SRui Paulo 				 os_strlen(buf))) {
137e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "Could not add User-Name");
138e28a4053SRui Paulo 		goto fail;
139e28a4053SRui Paulo 	}
140e28a4053SRui Paulo 
141e28a4053SRui Paulo 	if (!radius_msg_add_attr_user_password(
142e28a4053SRui Paulo 		    msg, (u8 *) buf, os_strlen(buf),
143e28a4053SRui Paulo 		    hapd->conf->radius->auth_server->shared_secret,
144e28a4053SRui Paulo 		    hapd->conf->radius->auth_server->shared_secret_len)) {
145e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "Could not add User-Password");
146e28a4053SRui Paulo 		goto fail;
147e28a4053SRui Paulo 	}
148e28a4053SRui Paulo 
149f05cddf9SRui Paulo 	if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr,
150f05cddf9SRui Paulo 				   NULL, msg) < 0)
151e28a4053SRui Paulo 		goto fail;
152e28a4053SRui Paulo 
153e28a4053SRui Paulo 	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
154e28a4053SRui Paulo 		    MAC2STR(addr));
155e28a4053SRui Paulo 	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
156e28a4053SRui Paulo 				 (u8 *) buf, os_strlen(buf))) {
157e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id");
158e28a4053SRui Paulo 		goto fail;
159e28a4053SRui Paulo 	}
160e28a4053SRui Paulo 
161e28a4053SRui Paulo 	os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
162e28a4053SRui Paulo 	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
163e28a4053SRui Paulo 				 (u8 *) buf, os_strlen(buf))) {
164e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "Could not add Connect-Info");
165e28a4053SRui Paulo 		goto fail;
166e28a4053SRui Paulo 	}
167e28a4053SRui Paulo 
168*a90b9d01SCy Schubert 	if (query->akm &&
169*a90b9d01SCy Schubert 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE,
170*a90b9d01SCy Schubert 				       wpa_akm_to_suite(query->akm))) {
171*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "Could not add WLAN-AKM-Suite");
172*a90b9d01SCy Schubert 		goto fail;
173*a90b9d01SCy Schubert 	}
174*a90b9d01SCy Schubert 
175*a90b9d01SCy Schubert 	if (query->anonce &&
176*a90b9d01SCy Schubert 	    !radius_msg_add_ext_vs(msg, RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5,
177*a90b9d01SCy Schubert 				   RADIUS_VENDOR_ID_FREERADIUS,
178*a90b9d01SCy Schubert 				   RADIUS_VENDOR_ATTR_FREERADIUS_802_1X_ANONCE,
179*a90b9d01SCy Schubert 				   query->anonce, WPA_NONCE_LEN)) {
180*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "Could not add FreeRADIUS-802.1X-Anonce");
181*a90b9d01SCy Schubert 		goto fail;
182*a90b9d01SCy Schubert 	}
183*a90b9d01SCy Schubert 
184*a90b9d01SCy Schubert 	if (query->eapol &&
185*a90b9d01SCy Schubert 	    !radius_msg_add_ext_vs(msg, RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5,
186*a90b9d01SCy Schubert 				   RADIUS_VENDOR_ID_FREERADIUS,
187*a90b9d01SCy Schubert 				   RADIUS_VENDOR_ATTR_FREERADIUS_802_1X_EAPOL_KEY_MSG,
188*a90b9d01SCy Schubert 				   query->eapol, query->eapol_len)) {
189*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "Could not add FreeRADIUS-802.1X-EAPoL-Key-Msg");
190*a90b9d01SCy Schubert 		goto fail;
191*a90b9d01SCy Schubert 	}
192*a90b9d01SCy Schubert 
193f05cddf9SRui Paulo 	if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
194f05cddf9SRui Paulo 		goto fail;
195e28a4053SRui Paulo 	return 0;
196e28a4053SRui Paulo 
197e28a4053SRui Paulo  fail:
198e28a4053SRui Paulo 	radius_msg_free(msg);
199e28a4053SRui Paulo 	return -1;
200e28a4053SRui Paulo }
201e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
202e28a4053SRui Paulo 
203e28a4053SRui Paulo 
204e28a4053SRui Paulo /**
205780fb4a2SCy Schubert  * hostapd_check_acl - Check a specified STA against accept/deny ACLs
206780fb4a2SCy Schubert  * @hapd: hostapd BSS data
207780fb4a2SCy Schubert  * @addr: MAC address of the STA
208780fb4a2SCy Schubert  * @vlan_id: Buffer for returning VLAN ID
209780fb4a2SCy Schubert  * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
210780fb4a2SCy Schubert  */
hostapd_check_acl(struct hostapd_data * hapd,const u8 * addr,struct vlan_description * vlan_id)211780fb4a2SCy Schubert int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
212780fb4a2SCy Schubert 		      struct vlan_description *vlan_id)
213780fb4a2SCy Schubert {
214780fb4a2SCy Schubert 	if (hostapd_maclist_found(hapd->conf->accept_mac,
215780fb4a2SCy Schubert 				  hapd->conf->num_accept_mac, addr, vlan_id))
216780fb4a2SCy Schubert 		return HOSTAPD_ACL_ACCEPT;
217780fb4a2SCy Schubert 
218780fb4a2SCy Schubert 	if (hostapd_maclist_found(hapd->conf->deny_mac,
219780fb4a2SCy Schubert 				  hapd->conf->num_deny_mac, addr, vlan_id))
220780fb4a2SCy Schubert 		return HOSTAPD_ACL_REJECT;
221780fb4a2SCy Schubert 
222780fb4a2SCy Schubert 	if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
223780fb4a2SCy Schubert 		return HOSTAPD_ACL_ACCEPT;
224780fb4a2SCy Schubert 	if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
225780fb4a2SCy Schubert 		return HOSTAPD_ACL_REJECT;
226780fb4a2SCy Schubert 
227780fb4a2SCy Schubert 	return HOSTAPD_ACL_PENDING;
228780fb4a2SCy Schubert }
229780fb4a2SCy Schubert 
230780fb4a2SCy Schubert 
231780fb4a2SCy Schubert /**
232e28a4053SRui Paulo  * hostapd_allowed_address - Check whether a specified STA can be authenticated
233e28a4053SRui Paulo  * @hapd: hostapd BSS data
234e28a4053SRui Paulo  * @addr: MAC address of the STA
235e28a4053SRui Paulo  * @msg: Authentication message
236e28a4053SRui Paulo  * @len: Length of msg in octets
237c1d255d3SCy Schubert  * @out.session_timeout: Buffer for returning session timeout (from RADIUS)
238c1d255d3SCy Schubert  * @out.acct_interim_interval: Buffer for returning account interval (from
239c1d255d3SCy Schubert  *	RADIUS)
240c1d255d3SCy Schubert  * @out.vlan_id: Buffer for returning VLAN ID
241c1d255d3SCy Schubert  * @out.psk: Linked list buffer for returning WPA PSK
242c1d255d3SCy Schubert  * @out.identity: Buffer for returning identity (from RADIUS)
243c1d255d3SCy Schubert  * @out.radius_cui: Buffer for returning CUI (from RADIUS)
24485732ac8SCy Schubert  * @is_probe_req: Whether this query for a Probe Request frame
245e28a4053SRui Paulo  * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
246f05cddf9SRui Paulo  *
247c1d255d3SCy Schubert  * The caller is responsible for properly cloning the returned out->identity and
248c1d255d3SCy Schubert  * out->radius_cui and out->psk values.
249e28a4053SRui Paulo  */
hostapd_allowed_address(struct hostapd_data * hapd,const u8 * addr,const u8 * msg,size_t len,struct radius_sta * out,int is_probe_req)250e28a4053SRui Paulo int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
251c1d255d3SCy Schubert 			    const u8 *msg, size_t len, struct radius_sta *out,
25285732ac8SCy Schubert 			    int is_probe_req)
253e28a4053SRui Paulo {
254780fb4a2SCy Schubert 	int res;
255780fb4a2SCy Schubert 
256c1d255d3SCy Schubert 	os_memset(out, 0, sizeof(*out));
257e28a4053SRui Paulo 
258c1d255d3SCy Schubert 	res = hostapd_check_acl(hapd, addr, &out->vlan_id);
259780fb4a2SCy Schubert 	if (res != HOSTAPD_ACL_PENDING)
260780fb4a2SCy Schubert 		return res;
261e28a4053SRui Paulo 
262e28a4053SRui Paulo 	if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
263e28a4053SRui Paulo #ifdef CONFIG_NO_RADIUS
264e28a4053SRui Paulo 		return HOSTAPD_ACL_REJECT;
265e28a4053SRui Paulo #else /* CONFIG_NO_RADIUS */
266e28a4053SRui Paulo 		struct hostapd_acl_query_data *query;
267e28a4053SRui Paulo 
26885732ac8SCy Schubert 		if (is_probe_req) {
26985732ac8SCy Schubert 			/* Skip RADIUS queries for Probe Request frames to avoid
27085732ac8SCy Schubert 			 * excessive load on the authentication server. */
27185732ac8SCy Schubert 			return HOSTAPD_ACL_ACCEPT;
27285732ac8SCy Schubert 		};
27385732ac8SCy Schubert 
2744bc52338SCy Schubert 		if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
275c1d255d3SCy Schubert 			os_memset(&out->vlan_id, 0, sizeof(out->vlan_id));
2764bc52338SCy Schubert 
277e28a4053SRui Paulo 		/* Check whether ACL cache has an entry for this station */
278c1d255d3SCy Schubert 		res = hostapd_acl_cache_get(hapd, addr, out);
279e28a4053SRui Paulo 		if (res == HOSTAPD_ACL_ACCEPT ||
280e28a4053SRui Paulo 		    res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
281e28a4053SRui Paulo 			return res;
282e28a4053SRui Paulo 		if (res == HOSTAPD_ACL_REJECT)
283e28a4053SRui Paulo 			return HOSTAPD_ACL_REJECT;
284e28a4053SRui Paulo 
285e28a4053SRui Paulo 		query = hapd->acl_queries;
286e28a4053SRui Paulo 		while (query) {
287*a90b9d01SCy Schubert 			if (ether_addr_equal(query->addr, addr)) {
288e28a4053SRui Paulo 				/* pending query in RADIUS retransmit queue;
289e28a4053SRui Paulo 				 * do not generate a new one */
290e28a4053SRui Paulo 				return HOSTAPD_ACL_PENDING;
291e28a4053SRui Paulo 			}
292e28a4053SRui Paulo 			query = query->next;
293e28a4053SRui Paulo 		}
294e28a4053SRui Paulo 
295e28a4053SRui Paulo 		if (!hapd->conf->radius->auth_server)
296e28a4053SRui Paulo 			return HOSTAPD_ACL_REJECT;
297e28a4053SRui Paulo 
298e28a4053SRui Paulo 		/* No entry in the cache - query external RADIUS server */
299e28a4053SRui Paulo 		query = os_zalloc(sizeof(*query));
300*a90b9d01SCy Schubert 		if (!query) {
301e28a4053SRui Paulo 			wpa_printf(MSG_ERROR, "malloc for query data failed");
302e28a4053SRui Paulo 			return HOSTAPD_ACL_REJECT;
303e28a4053SRui Paulo 		}
3045b9c547cSRui Paulo 		os_get_reltime(&query->timestamp);
305e28a4053SRui Paulo 		os_memcpy(query->addr, addr, ETH_ALEN);
306e28a4053SRui Paulo 		if (hostapd_radius_acl_query(hapd, addr, query)) {
307*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
308*a90b9d01SCy Schubert 				   "Failed to send Access-Request for ACL query.");
309e28a4053SRui Paulo 			hostapd_acl_query_free(query);
310e28a4053SRui Paulo 			return HOSTAPD_ACL_REJECT;
311e28a4053SRui Paulo 		}
312e28a4053SRui Paulo 
31385732ac8SCy Schubert 		query->auth_msg = os_memdup(msg, len);
314*a90b9d01SCy Schubert 		if (!query->auth_msg) {
315*a90b9d01SCy Schubert 			wpa_printf(MSG_ERROR,
316*a90b9d01SCy Schubert 				   "Failed to allocate memory for auth frame.");
317e28a4053SRui Paulo 			hostapd_acl_query_free(query);
318e28a4053SRui Paulo 			return HOSTAPD_ACL_REJECT;
319e28a4053SRui Paulo 		}
320e28a4053SRui Paulo 		query->auth_msg_len = len;
321e28a4053SRui Paulo 		query->next = hapd->acl_queries;
322e28a4053SRui Paulo 		hapd->acl_queries = query;
323e28a4053SRui Paulo 
324e28a4053SRui Paulo 		/* Queued data will be processed in hostapd_acl_recv_radius()
325e28a4053SRui Paulo 		 * when RADIUS server replies to the sent Access-Request. */
326e28a4053SRui Paulo 		return HOSTAPD_ACL_PENDING;
327e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
328e28a4053SRui Paulo 	}
329e28a4053SRui Paulo 
330e28a4053SRui Paulo 	return HOSTAPD_ACL_REJECT;
331e28a4053SRui Paulo }
332e28a4053SRui Paulo 
333e28a4053SRui Paulo 
334e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
hostapd_acl_expire_cache(struct hostapd_data * hapd,struct os_reltime * now)3355b9c547cSRui Paulo static void hostapd_acl_expire_cache(struct hostapd_data *hapd,
3365b9c547cSRui Paulo 				     struct os_reltime *now)
337e28a4053SRui Paulo {
338e28a4053SRui Paulo 	struct hostapd_cached_radius_acl *prev, *entry, *tmp;
339e28a4053SRui Paulo 
340e28a4053SRui Paulo 	prev = NULL;
341e28a4053SRui Paulo 	entry = hapd->acl_cache;
342e28a4053SRui Paulo 
343e28a4053SRui Paulo 	while (entry) {
3445b9c547cSRui Paulo 		if (os_reltime_expired(now, &entry->timestamp,
3455b9c547cSRui Paulo 				       RADIUS_ACL_TIMEOUT)) {
346e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
347e28a4053SRui Paulo 				   " has expired.", MAC2STR(entry->addr));
348e28a4053SRui Paulo 			if (prev)
349e28a4053SRui Paulo 				prev->next = entry->next;
350e28a4053SRui Paulo 			else
351e28a4053SRui Paulo 				hapd->acl_cache = entry->next;
352f05cddf9SRui Paulo 			hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
353e28a4053SRui Paulo 			tmp = entry;
354e28a4053SRui Paulo 			entry = entry->next;
355f05cddf9SRui Paulo 			hostapd_acl_cache_free_entry(tmp);
356e28a4053SRui Paulo 			continue;
357e28a4053SRui Paulo 		}
358e28a4053SRui Paulo 
359e28a4053SRui Paulo 		prev = entry;
360e28a4053SRui Paulo 		entry = entry->next;
361e28a4053SRui Paulo 	}
362e28a4053SRui Paulo }
363e28a4053SRui Paulo 
364e28a4053SRui Paulo 
hostapd_acl_expire_queries(struct hostapd_data * hapd,struct os_reltime * now)365f05cddf9SRui Paulo static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
3665b9c547cSRui Paulo 				       struct os_reltime *now)
367e28a4053SRui Paulo {
368e28a4053SRui Paulo 	struct hostapd_acl_query_data *prev, *entry, *tmp;
369e28a4053SRui Paulo 
370e28a4053SRui Paulo 	prev = NULL;
371e28a4053SRui Paulo 	entry = hapd->acl_queries;
372e28a4053SRui Paulo 
373e28a4053SRui Paulo 	while (entry) {
3745b9c547cSRui Paulo 		if (os_reltime_expired(now, &entry->timestamp,
3755b9c547cSRui Paulo 				       RADIUS_ACL_TIMEOUT)) {
376e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "ACL query for " MACSTR
377e28a4053SRui Paulo 				   " has expired.", MAC2STR(entry->addr));
378e28a4053SRui Paulo 			if (prev)
379e28a4053SRui Paulo 				prev->next = entry->next;
380e28a4053SRui Paulo 			else
381e28a4053SRui Paulo 				hapd->acl_queries = entry->next;
382e28a4053SRui Paulo 
383e28a4053SRui Paulo 			tmp = entry;
384e28a4053SRui Paulo 			entry = entry->next;
385e28a4053SRui Paulo 			hostapd_acl_query_free(tmp);
386e28a4053SRui Paulo 			continue;
387e28a4053SRui Paulo 		}
388e28a4053SRui Paulo 
389e28a4053SRui Paulo 		prev = entry;
390e28a4053SRui Paulo 		entry = entry->next;
391e28a4053SRui Paulo 	}
392e28a4053SRui Paulo }
393e28a4053SRui Paulo 
394e28a4053SRui Paulo 
395e28a4053SRui Paulo /**
396e28a4053SRui Paulo  * hostapd_acl_expire - ACL cache expiration callback
397325151a3SRui Paulo  * @hapd: struct hostapd_data *
398e28a4053SRui Paulo  */
hostapd_acl_expire(struct hostapd_data * hapd)399325151a3SRui Paulo void hostapd_acl_expire(struct hostapd_data *hapd)
400e28a4053SRui Paulo {
4015b9c547cSRui Paulo 	struct os_reltime now;
402e28a4053SRui Paulo 
4035b9c547cSRui Paulo 	os_get_reltime(&now);
4045b9c547cSRui Paulo 	hostapd_acl_expire_cache(hapd, &now);
4055b9c547cSRui Paulo 	hostapd_acl_expire_queries(hapd, &now);
406e28a4053SRui Paulo }
407e28a4053SRui Paulo 
408e28a4053SRui Paulo 
decode_tunnel_passwords(struct hostapd_data * hapd,const u8 * shared_secret,size_t shared_secret_len,struct radius_msg * msg,struct radius_msg * req,struct hostapd_cached_radius_acl * cache)409f05cddf9SRui Paulo static void decode_tunnel_passwords(struct hostapd_data *hapd,
410f05cddf9SRui Paulo 				    const u8 *shared_secret,
411f05cddf9SRui Paulo 				    size_t shared_secret_len,
412f05cddf9SRui Paulo 				    struct radius_msg *msg,
413f05cddf9SRui Paulo 				    struct radius_msg *req,
414f05cddf9SRui Paulo 				    struct hostapd_cached_radius_acl *cache)
415f05cddf9SRui Paulo {
416f05cddf9SRui Paulo 	int passphraselen;
417780fb4a2SCy Schubert 	char *passphrase;
418f05cddf9SRui Paulo 	size_t i;
419f05cddf9SRui Paulo 	struct hostapd_sta_wpa_psk_short *psk;
420f05cddf9SRui Paulo 
421f05cddf9SRui Paulo 	/*
422f05cddf9SRui Paulo 	 * Decode all tunnel passwords as PSK and save them into a linked list.
423f05cddf9SRui Paulo 	 */
424f05cddf9SRui Paulo 	for (i = 0; ; i++) {
425f05cddf9SRui Paulo 		passphrase = radius_msg_get_tunnel_password(
426f05cddf9SRui Paulo 			msg, &passphraselen, shared_secret, shared_secret_len,
427f05cddf9SRui Paulo 			req, i);
428f05cddf9SRui Paulo 		/*
429f05cddf9SRui Paulo 		 * Passphrase is NULL iff there is no i-th Tunnel-Password
430f05cddf9SRui Paulo 		 * attribute in msg.
431f05cddf9SRui Paulo 		 */
432*a90b9d01SCy Schubert 		if (!passphrase)
433f05cddf9SRui Paulo 			break;
434780fb4a2SCy Schubert 
435780fb4a2SCy Schubert 		/*
436780fb4a2SCy Schubert 		 * Passphase should be 8..63 chars (to be hashed with SSID)
437780fb4a2SCy Schubert 		 * or 64 chars hex string (no separate hashing with SSID).
438780fb4a2SCy Schubert 		 */
439780fb4a2SCy Schubert 
440780fb4a2SCy Schubert 		if (passphraselen < MIN_PASSPHRASE_LEN ||
441780fb4a2SCy Schubert 		    passphraselen > MAX_PASSPHRASE_LEN + 1)
442780fb4a2SCy Schubert 			goto free_pass;
443780fb4a2SCy Schubert 
444f05cddf9SRui Paulo 		/*
445f05cddf9SRui Paulo 		 * passphrase does not contain the NULL termination.
446f05cddf9SRui Paulo 		 * Add it here as pbkdf2_sha1() requires it.
447f05cddf9SRui Paulo 		 */
448f05cddf9SRui Paulo 		psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
449780fb4a2SCy Schubert 		if (psk) {
450780fb4a2SCy Schubert 			if ((passphraselen == MAX_PASSPHRASE_LEN + 1) &&
451780fb4a2SCy Schubert 			    (hexstr2bin(passphrase, psk->psk, PMK_LEN) < 0)) {
452780fb4a2SCy Schubert 				hostapd_logger(hapd, cache->addr,
453780fb4a2SCy Schubert 					       HOSTAPD_MODULE_RADIUS,
454780fb4a2SCy Schubert 					       HOSTAPD_LEVEL_WARNING,
455780fb4a2SCy Schubert 					       "invalid hex string (%d chars) in Tunnel-Password",
456780fb4a2SCy Schubert 					       passphraselen);
457780fb4a2SCy Schubert 				goto skip;
458780fb4a2SCy Schubert 			} else if (passphraselen <= MAX_PASSPHRASE_LEN) {
459780fb4a2SCy Schubert 				os_memcpy(psk->passphrase, passphrase,
460780fb4a2SCy Schubert 					  passphraselen);
461780fb4a2SCy Schubert 				psk->is_passphrase = 1;
462780fb4a2SCy Schubert 			}
463c1d255d3SCy Schubert 			psk->next = cache->info.psk;
464c1d255d3SCy Schubert 			cache->info.psk = psk;
465f05cddf9SRui Paulo 			psk = NULL;
466f05cddf9SRui Paulo 		}
467780fb4a2SCy Schubert skip:
468f05cddf9SRui Paulo 		os_free(psk);
469780fb4a2SCy Schubert free_pass:
470f05cddf9SRui Paulo 		os_free(passphrase);
471f05cddf9SRui Paulo 	}
472f05cddf9SRui Paulo }
473f05cddf9SRui Paulo 
474f05cddf9SRui Paulo 
475e28a4053SRui Paulo /**
476e28a4053SRui Paulo  * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
477e28a4053SRui Paulo  * @msg: RADIUS response message
478e28a4053SRui Paulo  * @req: RADIUS request message
479e28a4053SRui Paulo  * @shared_secret: RADIUS shared secret
480e28a4053SRui Paulo  * @shared_secret_len: Length of shared_secret in octets
481e28a4053SRui Paulo  * @data: Context data (struct hostapd_data *)
482e28a4053SRui Paulo  * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and
483e28a4053SRui Paulo  * was processed here) or RADIUS_RX_UNKNOWN if not.
484e28a4053SRui Paulo  */
485e28a4053SRui Paulo static RadiusRxResult
hostapd_acl_recv_radius(struct radius_msg * msg,struct radius_msg * req,const u8 * shared_secret,size_t shared_secret_len,void * data)486e28a4053SRui Paulo hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
487e28a4053SRui Paulo 			const u8 *shared_secret, size_t shared_secret_len,
488e28a4053SRui Paulo 			void *data)
489e28a4053SRui Paulo {
490e28a4053SRui Paulo 	struct hostapd_data *hapd = data;
491e28a4053SRui Paulo 	struct hostapd_acl_query_data *query, *prev;
492e28a4053SRui Paulo 	struct hostapd_cached_radius_acl *cache;
493c1d255d3SCy Schubert 	struct radius_sta *info;
494e28a4053SRui Paulo 	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
495e28a4053SRui Paulo 
496e28a4053SRui Paulo 	query = hapd->acl_queries;
497e28a4053SRui Paulo 	prev = NULL;
498e28a4053SRui Paulo 	while (query) {
499e28a4053SRui Paulo 		if (query->radius_id == hdr->identifier)
500e28a4053SRui Paulo 			break;
501e28a4053SRui Paulo 		prev = query;
502e28a4053SRui Paulo 		query = query->next;
503e28a4053SRui Paulo 	}
504*a90b9d01SCy Schubert 	if (!query)
505e28a4053SRui Paulo 		return RADIUS_RX_UNKNOWN;
506e28a4053SRui Paulo 
507*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG,
508*a90b9d01SCy Schubert 		   "Found matching Access-Request for RADIUS message (id=%d)",
509*a90b9d01SCy Schubert 		   query->radius_id);
510e28a4053SRui Paulo 
511*a90b9d01SCy Schubert 	if (radius_msg_verify(
512*a90b9d01SCy Schubert 		    msg, shared_secret, shared_secret_len, req,
513*a90b9d01SCy Schubert 		    hapd->conf->radius_require_message_authenticator)) {
514*a90b9d01SCy Schubert 		wpa_printf(MSG_INFO,
515*a90b9d01SCy Schubert 			   "Incoming RADIUS packet did not have correct authenticator - dropped");
516e28a4053SRui Paulo 		return RADIUS_RX_INVALID_AUTHENTICATOR;
517e28a4053SRui Paulo 	}
518e28a4053SRui Paulo 
519e28a4053SRui Paulo 	if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
520e28a4053SRui Paulo 	    hdr->code != RADIUS_CODE_ACCESS_REJECT) {
521*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
522*a90b9d01SCy Schubert 			   "Unknown RADIUS message code %d to ACL query",
523*a90b9d01SCy Schubert 			   hdr->code);
524e28a4053SRui Paulo 		return RADIUS_RX_UNKNOWN;
525e28a4053SRui Paulo 	}
526e28a4053SRui Paulo 
527e28a4053SRui Paulo 	/* Insert Accept/Reject info into ACL cache */
528e28a4053SRui Paulo 	cache = os_zalloc(sizeof(*cache));
529*a90b9d01SCy Schubert 	if (!cache) {
530e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
531e28a4053SRui Paulo 		goto done;
532e28a4053SRui Paulo 	}
5335b9c547cSRui Paulo 	os_get_reltime(&cache->timestamp);
534e28a4053SRui Paulo 	os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
535c1d255d3SCy Schubert 	info = &cache->info;
536e28a4053SRui Paulo 	if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
537f05cddf9SRui Paulo 		u8 *buf;
538f05cddf9SRui Paulo 		size_t len;
539f05cddf9SRui Paulo 
540e28a4053SRui Paulo 		if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
541c1d255d3SCy Schubert 					      &info->session_timeout) == 0)
542e28a4053SRui Paulo 			cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
543e28a4053SRui Paulo 		else
544e28a4053SRui Paulo 			cache->accepted = HOSTAPD_ACL_ACCEPT;
545e28a4053SRui Paulo 
546e28a4053SRui Paulo 		if (radius_msg_get_attr_int32(
547e28a4053SRui Paulo 			    msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
548c1d255d3SCy Schubert 			    &info->acct_interim_interval) == 0 &&
549c1d255d3SCy Schubert 		    info->acct_interim_interval < 60) {
550*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
551*a90b9d01SCy Schubert 				   "Ignored too small Acct-Interim-Interval %d for STA "
552*a90b9d01SCy Schubert 				   MACSTR,
553c1d255d3SCy Schubert 				   info->acct_interim_interval,
554e28a4053SRui Paulo 				   MAC2STR(query->addr));
555c1d255d3SCy Schubert 			info->acct_interim_interval = 0;
556e28a4053SRui Paulo 		}
557e28a4053SRui Paulo 
5584bc52338SCy Schubert 		if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED)
559c1d255d3SCy Schubert 			info->vlan_id.notempty = !!radius_msg_get_vlanid(
560c1d255d3SCy Schubert 				msg, &info->vlan_id.untagged,
561c1d255d3SCy Schubert 				MAX_NUM_TAGGED_VLAN, info->vlan_id.tagged);
562f05cddf9SRui Paulo 
563f05cddf9SRui Paulo 		decode_tunnel_passwords(hapd, shared_secret, shared_secret_len,
564f05cddf9SRui Paulo 					msg, req, cache);
565f05cddf9SRui Paulo 
566f05cddf9SRui Paulo 		if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
567f05cddf9SRui Paulo 					    &buf, &len, NULL) == 0) {
568c1d255d3SCy Schubert 			info->identity = os_zalloc(len + 1);
569c1d255d3SCy Schubert 			if (info->identity)
570c1d255d3SCy Schubert 				os_memcpy(info->identity, buf, len);
571f05cddf9SRui Paulo 		}
572f05cddf9SRui Paulo 		if (radius_msg_get_attr_ptr(
573f05cddf9SRui Paulo 			    msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
574f05cddf9SRui Paulo 			    &buf, &len, NULL) == 0) {
575c1d255d3SCy Schubert 			info->radius_cui = os_zalloc(len + 1);
576c1d255d3SCy Schubert 			if (info->radius_cui)
577c1d255d3SCy Schubert 				os_memcpy(info->radius_cui, buf, len);
578f05cddf9SRui Paulo 		}
579f05cddf9SRui Paulo 
580f05cddf9SRui Paulo 		if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED &&
581c1d255d3SCy Schubert 		    !info->psk)
582f05cddf9SRui Paulo 			cache->accepted = HOSTAPD_ACL_REJECT;
583325151a3SRui Paulo 
584c1d255d3SCy Schubert 		if (info->vlan_id.notempty &&
585c1d255d3SCy Schubert 		    !hostapd_vlan_valid(hapd->conf->vlan, &info->vlan_id)) {
586325151a3SRui Paulo 			hostapd_logger(hapd, query->addr,
587325151a3SRui Paulo 				       HOSTAPD_MODULE_RADIUS,
588325151a3SRui Paulo 				       HOSTAPD_LEVEL_INFO,
589780fb4a2SCy Schubert 				       "Invalid VLAN %d%s received from RADIUS server",
590c1d255d3SCy Schubert 				       info->vlan_id.untagged,
591c1d255d3SCy Schubert 				       info->vlan_id.tagged[0] ? "+" : "");
592c1d255d3SCy Schubert 			os_memset(&info->vlan_id, 0, sizeof(info->vlan_id));
593325151a3SRui Paulo 		}
594325151a3SRui Paulo 		if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
595c1d255d3SCy Schubert 		    !info->vlan_id.notempty)
596325151a3SRui Paulo 			cache->accepted = HOSTAPD_ACL_REJECT;
597e28a4053SRui Paulo 	} else
598e28a4053SRui Paulo 		cache->accepted = HOSTAPD_ACL_REJECT;
599e28a4053SRui Paulo 	cache->next = hapd->acl_cache;
600e28a4053SRui Paulo 	hapd->acl_cache = cache;
601e28a4053SRui Paulo 
602*a90b9d01SCy Schubert 	if (query->radius_psk) {
603*a90b9d01SCy Schubert 		struct sta_info *sta;
604*a90b9d01SCy Schubert 		bool success = cache->accepted == HOSTAPD_ACL_ACCEPT ||
605*a90b9d01SCy Schubert 			cache->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT;
606*a90b9d01SCy Schubert 
607*a90b9d01SCy Schubert 		sta = ap_get_sta(hapd, query->addr);
608*a90b9d01SCy Schubert 		if (!sta || !sta->wpa_sm) {
609*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
610*a90b9d01SCy Schubert 				   "No STA/SM entry found for the RADIUS PSK response");
611*a90b9d01SCy Schubert 			goto done;
612*a90b9d01SCy Schubert 		}
613*a90b9d01SCy Schubert #ifdef NEED_AP_MLME
614*a90b9d01SCy Schubert 		if (success &&
615*a90b9d01SCy Schubert 		    (ieee802_11_set_radius_info(hapd, sta, cache->accepted,
616*a90b9d01SCy Schubert 						info) < 0 ||
617*a90b9d01SCy Schubert 		     ap_sta_bind_vlan(hapd, sta) < 0))
618*a90b9d01SCy Schubert 			success = false;
619*a90b9d01SCy Schubert #endif /* NEED_AP_MLME */
620*a90b9d01SCy Schubert 		wpa_auth_sta_radius_psk_resp(sta->wpa_sm, success);
621*a90b9d01SCy Schubert 	} else {
622e28a4053SRui Paulo #ifdef CONFIG_DRIVER_RADIUS_ACL
623*a90b9d01SCy Schubert 		hostapd_drv_set_radius_acl_auth(hapd, query->addr,
624*a90b9d01SCy Schubert 						cache->accepted,
625c1d255d3SCy Schubert 						info->session_timeout);
626e28a4053SRui Paulo #else /* CONFIG_DRIVER_RADIUS_ACL */
627e28a4053SRui Paulo #ifdef NEED_AP_MLME
628*a90b9d01SCy Schubert 		/* Re-send original authentication frame for 802.11 processing
629*a90b9d01SCy Schubert 		 */
630*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
631*a90b9d01SCy Schubert 			   "Re-sending authentication frame after successful RADIUS ACL query");
632*a90b9d01SCy Schubert 		ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len,
633*a90b9d01SCy Schubert 				NULL);
634e28a4053SRui Paulo #endif /* NEED_AP_MLME */
635e28a4053SRui Paulo #endif /* CONFIG_DRIVER_RADIUS_ACL */
636*a90b9d01SCy Schubert 	}
637e28a4053SRui Paulo 
638e28a4053SRui Paulo  done:
639*a90b9d01SCy Schubert 	if (!prev)
640e28a4053SRui Paulo 		hapd->acl_queries = query->next;
641e28a4053SRui Paulo 	else
642e28a4053SRui Paulo 		prev->next = query->next;
643e28a4053SRui Paulo 
644e28a4053SRui Paulo 	hostapd_acl_query_free(query);
645e28a4053SRui Paulo 
646e28a4053SRui Paulo 	return RADIUS_RX_PROCESSED;
647e28a4053SRui Paulo }
648e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
649e28a4053SRui Paulo 
650e28a4053SRui Paulo 
651e28a4053SRui Paulo /**
652e28a4053SRui Paulo  * hostapd_acl_init: Initialize IEEE 802.11 ACL
653e28a4053SRui Paulo  * @hapd: hostapd BSS data
654e28a4053SRui Paulo  * Returns: 0 on success, -1 on failure
655e28a4053SRui Paulo  */
hostapd_acl_init(struct hostapd_data * hapd)656e28a4053SRui Paulo int hostapd_acl_init(struct hostapd_data *hapd)
657e28a4053SRui Paulo {
658e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
659e28a4053SRui Paulo 	if (radius_client_register(hapd->radius, RADIUS_AUTH,
660e28a4053SRui Paulo 				   hostapd_acl_recv_radius, hapd))
661e28a4053SRui Paulo 		return -1;
662e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
663e28a4053SRui Paulo 
664e28a4053SRui Paulo 	return 0;
665e28a4053SRui Paulo }
666e28a4053SRui Paulo 
667e28a4053SRui Paulo 
668e28a4053SRui Paulo /**
669e28a4053SRui Paulo  * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL
670e28a4053SRui Paulo  * @hapd: hostapd BSS data
671e28a4053SRui Paulo  */
hostapd_acl_deinit(struct hostapd_data * hapd)672e28a4053SRui Paulo void hostapd_acl_deinit(struct hostapd_data *hapd)
673e28a4053SRui Paulo {
674e28a4053SRui Paulo 	struct hostapd_acl_query_data *query, *prev;
675e28a4053SRui Paulo 
676e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
677e28a4053SRui Paulo 	hostapd_acl_cache_free(hapd->acl_cache);
67885732ac8SCy Schubert 	hapd->acl_cache = NULL;
679e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
680e28a4053SRui Paulo 
681e28a4053SRui Paulo 	query = hapd->acl_queries;
68285732ac8SCy Schubert 	hapd->acl_queries = NULL;
683e28a4053SRui Paulo 	while (query) {
684e28a4053SRui Paulo 		prev = query;
685e28a4053SRui Paulo 		query = query->next;
686e28a4053SRui Paulo 		hostapd_acl_query_free(prev);
687e28a4053SRui Paulo 	}
688e28a4053SRui Paulo }
689f05cddf9SRui Paulo 
690f05cddf9SRui Paulo 
hostapd_copy_psk_list(struct hostapd_sta_wpa_psk_short ** psk,struct hostapd_sta_wpa_psk_short * src)691c1d255d3SCy Schubert void hostapd_copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
692c1d255d3SCy Schubert 			   struct hostapd_sta_wpa_psk_short *src)
693c1d255d3SCy Schubert {
694c1d255d3SCy Schubert 	if (!psk)
695c1d255d3SCy Schubert 		return;
696c1d255d3SCy Schubert 
697c1d255d3SCy Schubert 	if (src)
698c1d255d3SCy Schubert 		src->ref++;
699c1d255d3SCy Schubert 
700c1d255d3SCy Schubert 	*psk = src;
701c1d255d3SCy Schubert }
702c1d255d3SCy Schubert 
703c1d255d3SCy Schubert 
hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short * psk)704f05cddf9SRui Paulo void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk)
705f05cddf9SRui Paulo {
706780fb4a2SCy Schubert 	if (psk && psk->ref) {
707780fb4a2SCy Schubert 		/* This will be freed when the last reference is dropped. */
708780fb4a2SCy Schubert 		psk->ref--;
709780fb4a2SCy Schubert 		return;
710780fb4a2SCy Schubert 	}
711780fb4a2SCy Schubert 
712f05cddf9SRui Paulo 	while (psk) {
713f05cddf9SRui Paulo 		struct hostapd_sta_wpa_psk_short *prev = psk;
714f05cddf9SRui Paulo 		psk = psk->next;
715*a90b9d01SCy Schubert 		bin_clear_free(prev, sizeof(*prev));
716f05cddf9SRui Paulo 	}
717f05cddf9SRui Paulo }
718*a90b9d01SCy Schubert 
719*a90b9d01SCy Schubert 
720*a90b9d01SCy Schubert #ifndef CONFIG_NO_RADIUS
hostapd_acl_req_radius_psk(struct hostapd_data * hapd,const u8 * addr,int key_mgmt,const u8 * anonce,const u8 * eapol,size_t eapol_len)721*a90b9d01SCy Schubert void hostapd_acl_req_radius_psk(struct hostapd_data *hapd, const u8 *addr,
722*a90b9d01SCy Schubert 				int key_mgmt, const u8 *anonce,
723*a90b9d01SCy Schubert 				const u8 *eapol, size_t eapol_len)
724*a90b9d01SCy Schubert {
725*a90b9d01SCy Schubert 	struct hostapd_acl_query_data *query;
726*a90b9d01SCy Schubert 
727*a90b9d01SCy Schubert 	query = os_zalloc(sizeof(*query));
728*a90b9d01SCy Schubert 	if (!query)
729*a90b9d01SCy Schubert 		return;
730*a90b9d01SCy Schubert 
731*a90b9d01SCy Schubert 	query->radius_psk = true;
732*a90b9d01SCy Schubert 	query->akm = key_mgmt;
733*a90b9d01SCy Schubert 	os_get_reltime(&query->timestamp);
734*a90b9d01SCy Schubert 	os_memcpy(query->addr, addr, ETH_ALEN);
735*a90b9d01SCy Schubert 	if (anonce)
736*a90b9d01SCy Schubert 		query->anonce = os_memdup(anonce, WPA_NONCE_LEN);
737*a90b9d01SCy Schubert 	if (eapol) {
738*a90b9d01SCy Schubert 		query->eapol = os_memdup(eapol, eapol_len);
739*a90b9d01SCy Schubert 		query->eapol_len = eapol_len;
740*a90b9d01SCy Schubert 	}
741*a90b9d01SCy Schubert 	if (hostapd_radius_acl_query(hapd, addr, query)) {
742*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
743*a90b9d01SCy Schubert 			   "Failed to send Access-Request for RADIUS PSK/ACL query");
744*a90b9d01SCy Schubert 		hostapd_acl_query_free(query);
745*a90b9d01SCy Schubert 		return;
746*a90b9d01SCy Schubert 	}
747*a90b9d01SCy Schubert 
748*a90b9d01SCy Schubert 	query->next = hapd->acl_queries;
749*a90b9d01SCy Schubert 	hapd->acl_queries = query;
750*a90b9d01SCy Schubert }
751*a90b9d01SCy Schubert #endif /* CONFIG_NO_RADIUS */
752