xref: /netbsd-src/external/bsd/wpa/dist/src/ap/ieee802_11_auth.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*
2  * hostapd / IEEE 802.11 authentication (ACL)
3  * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  *
14  * Access control list for IEEE 802.11 authentication can uses statically
15  * configured ACL from configuration files or an external RADIUS server.
16  * Results from external RADIUS queries are cached to allow faster
17  * authentication frame processing.
18  */
19 
20 #include "utils/includes.h"
21 
22 #include "utils/common.h"
23 #include "utils/eloop.h"
24 #include "radius/radius.h"
25 #include "radius/radius_client.h"
26 #include "hostapd.h"
27 #include "ap_config.h"
28 #include "ap_drv_ops.h"
29 #include "ieee802_11.h"
30 #include "ieee802_11_auth.h"
31 
32 #define RADIUS_ACL_TIMEOUT 30
33 
34 
35 struct hostapd_cached_radius_acl {
36 	os_time_t timestamp;
37 	macaddr addr;
38 	int accepted; /* HOSTAPD_ACL_* */
39 	struct hostapd_cached_radius_acl *next;
40 	u32 session_timeout;
41 	u32 acct_interim_interval;
42 	int vlan_id;
43 };
44 
45 
46 struct hostapd_acl_query_data {
47 	os_time_t timestamp;
48 	u8 radius_id;
49 	macaddr addr;
50 	u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
51 	size_t auth_msg_len;
52 	struct hostapd_acl_query_data *next;
53 };
54 
55 
56 #ifndef CONFIG_NO_RADIUS
57 static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
58 {
59 	struct hostapd_cached_radius_acl *prev;
60 
61 	while (acl_cache) {
62 		prev = acl_cache;
63 		acl_cache = acl_cache->next;
64 		os_free(prev);
65 	}
66 }
67 
68 
69 static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
70 				 u32 *session_timeout,
71 				 u32 *acct_interim_interval, int *vlan_id)
72 {
73 	struct hostapd_cached_radius_acl *entry;
74 	struct os_time now;
75 
76 	os_get_time(&now);
77 	entry = hapd->acl_cache;
78 
79 	while (entry) {
80 		if (os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
81 			if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT)
82 				return -1; /* entry has expired */
83 			if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
84 				if (session_timeout)
85 					*session_timeout =
86 						entry->session_timeout;
87 			if (acct_interim_interval)
88 				*acct_interim_interval =
89 					entry->acct_interim_interval;
90 			if (vlan_id)
91 				*vlan_id = entry->vlan_id;
92 			return entry->accepted;
93 		}
94 
95 		entry = entry->next;
96 	}
97 
98 	return -1;
99 }
100 #endif /* CONFIG_NO_RADIUS */
101 
102 
103 static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
104 {
105 	if (query == NULL)
106 		return;
107 	os_free(query->auth_msg);
108 	os_free(query);
109 }
110 
111 
112 #ifndef CONFIG_NO_RADIUS
113 static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
114 				    struct hostapd_acl_query_data *query)
115 {
116 	struct radius_msg *msg;
117 	char buf[128];
118 
119 	query->radius_id = radius_client_get_id(hapd->radius);
120 	msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
121 	if (msg == NULL)
122 		return -1;
123 
124 	radius_msg_make_authenticator(msg, addr, ETH_ALEN);
125 
126 	os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
127 	if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
128 				 os_strlen(buf))) {
129 		wpa_printf(MSG_DEBUG, "Could not add User-Name");
130 		goto fail;
131 	}
132 
133 	if (!radius_msg_add_attr_user_password(
134 		    msg, (u8 *) buf, os_strlen(buf),
135 		    hapd->conf->radius->auth_server->shared_secret,
136 		    hapd->conf->radius->auth_server->shared_secret_len)) {
137 		wpa_printf(MSG_DEBUG, "Could not add User-Password");
138 		goto fail;
139 	}
140 
141 	if (hapd->conf->own_ip_addr.af == AF_INET &&
142 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
143 				 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
144 		wpa_printf(MSG_DEBUG, "Could not add NAS-IP-Address");
145 		goto fail;
146 	}
147 
148 #ifdef CONFIG_IPV6
149 	if (hapd->conf->own_ip_addr.af == AF_INET6 &&
150 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
151 				 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
152 		wpa_printf(MSG_DEBUG, "Could not add NAS-IPv6-Address");
153 		goto fail;
154 	}
155 #endif /* CONFIG_IPV6 */
156 
157 	if (hapd->conf->nas_identifier &&
158 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
159 				 (u8 *) hapd->conf->nas_identifier,
160 				 os_strlen(hapd->conf->nas_identifier))) {
161 		wpa_printf(MSG_DEBUG, "Could not add NAS-Identifier");
162 		goto fail;
163 	}
164 
165 	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
166 		    MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
167 	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
168 				 (u8 *) buf, os_strlen(buf))) {
169 		wpa_printf(MSG_DEBUG, "Could not add Called-Station-Id");
170 		goto fail;
171 	}
172 
173 	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
174 		    MAC2STR(addr));
175 	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
176 				 (u8 *) buf, os_strlen(buf))) {
177 		wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id");
178 		goto fail;
179 	}
180 
181 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
182 				       RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
183 		wpa_printf(MSG_DEBUG, "Could not add NAS-Port-Type");
184 		goto fail;
185 	}
186 
187 	os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
188 	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
189 				 (u8 *) buf, os_strlen(buf))) {
190 		wpa_printf(MSG_DEBUG, "Could not add Connect-Info");
191 		goto fail;
192 	}
193 
194 	if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
195 		goto fail;
196 	return 0;
197 
198  fail:
199 	radius_msg_free(msg);
200 	return -1;
201 }
202 #endif /* CONFIG_NO_RADIUS */
203 
204 
205 /**
206  * hostapd_allowed_address - Check whether a specified STA can be authenticated
207  * @hapd: hostapd BSS data
208  * @addr: MAC address of the STA
209  * @msg: Authentication message
210  * @len: Length of msg in octets
211  * @session_timeout: Buffer for returning session timeout (from RADIUS)
212  * @acct_interim_interval: Buffer for returning account interval (from RADIUS)
213  * @vlan_id: Buffer for returning VLAN ID
214  * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
215  */
216 int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
217 			    const u8 *msg, size_t len, u32 *session_timeout,
218 			    u32 *acct_interim_interval, int *vlan_id)
219 {
220 	if (session_timeout)
221 		*session_timeout = 0;
222 	if (acct_interim_interval)
223 		*acct_interim_interval = 0;
224 	if (vlan_id)
225 		*vlan_id = 0;
226 
227 	if (hostapd_maclist_found(hapd->conf->accept_mac,
228 				  hapd->conf->num_accept_mac, addr, vlan_id))
229 		return HOSTAPD_ACL_ACCEPT;
230 
231 	if (hostapd_maclist_found(hapd->conf->deny_mac,
232 				  hapd->conf->num_deny_mac, addr, vlan_id))
233 		return HOSTAPD_ACL_REJECT;
234 
235 	if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
236 		return HOSTAPD_ACL_ACCEPT;
237 	if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
238 		return HOSTAPD_ACL_REJECT;
239 
240 	if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
241 #ifdef CONFIG_NO_RADIUS
242 		return HOSTAPD_ACL_REJECT;
243 #else /* CONFIG_NO_RADIUS */
244 		struct hostapd_acl_query_data *query;
245 		struct os_time t;
246 
247 		/* Check whether ACL cache has an entry for this station */
248 		int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
249 						acct_interim_interval,
250 						vlan_id);
251 		if (res == HOSTAPD_ACL_ACCEPT ||
252 		    res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
253 			return res;
254 		if (res == HOSTAPD_ACL_REJECT)
255 			return HOSTAPD_ACL_REJECT;
256 
257 		query = hapd->acl_queries;
258 		while (query) {
259 			if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
260 				/* pending query in RADIUS retransmit queue;
261 				 * do not generate a new one */
262 				return HOSTAPD_ACL_PENDING;
263 			}
264 			query = query->next;
265 		}
266 
267 		if (!hapd->conf->radius->auth_server)
268 			return HOSTAPD_ACL_REJECT;
269 
270 		/* No entry in the cache - query external RADIUS server */
271 		query = os_zalloc(sizeof(*query));
272 		if (query == NULL) {
273 			wpa_printf(MSG_ERROR, "malloc for query data failed");
274 			return HOSTAPD_ACL_REJECT;
275 		}
276 		os_get_time(&t);
277 		query->timestamp = t.sec;
278 		os_memcpy(query->addr, addr, ETH_ALEN);
279 		if (hostapd_radius_acl_query(hapd, addr, query)) {
280 			wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
281 				   "for ACL query.");
282 			hostapd_acl_query_free(query);
283 			return HOSTAPD_ACL_REJECT;
284 		}
285 
286 		query->auth_msg = os_malloc(len);
287 		if (query->auth_msg == NULL) {
288 			wpa_printf(MSG_ERROR, "Failed to allocate memory for "
289 				   "auth frame.");
290 			hostapd_acl_query_free(query);
291 			return HOSTAPD_ACL_REJECT;
292 		}
293 		os_memcpy(query->auth_msg, msg, len);
294 		query->auth_msg_len = len;
295 		query->next = hapd->acl_queries;
296 		hapd->acl_queries = query;
297 
298 		/* Queued data will be processed in hostapd_acl_recv_radius()
299 		 * when RADIUS server replies to the sent Access-Request. */
300 		return HOSTAPD_ACL_PENDING;
301 #endif /* CONFIG_NO_RADIUS */
302 	}
303 
304 	return HOSTAPD_ACL_REJECT;
305 }
306 
307 
308 #ifndef CONFIG_NO_RADIUS
309 static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now)
310 {
311 	struct hostapd_cached_radius_acl *prev, *entry, *tmp;
312 
313 	prev = NULL;
314 	entry = hapd->acl_cache;
315 
316 	while (entry) {
317 		if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
318 			wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
319 				   " has expired.", MAC2STR(entry->addr));
320 			if (prev)
321 				prev->next = entry->next;
322 			else
323 				hapd->acl_cache = entry->next;
324 			hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
325 			tmp = entry;
326 			entry = entry->next;
327 			os_free(tmp);
328 			continue;
329 		}
330 
331 		prev = entry;
332 		entry = entry->next;
333 	}
334 }
335 
336 
337 static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
338 				       os_time_t now)
339 {
340 	struct hostapd_acl_query_data *prev, *entry, *tmp;
341 
342 	prev = NULL;
343 	entry = hapd->acl_queries;
344 
345 	while (entry) {
346 		if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
347 			wpa_printf(MSG_DEBUG, "ACL query for " MACSTR
348 				   " has expired.", MAC2STR(entry->addr));
349 			if (prev)
350 				prev->next = entry->next;
351 			else
352 				hapd->acl_queries = entry->next;
353 
354 			tmp = entry;
355 			entry = entry->next;
356 			hostapd_acl_query_free(tmp);
357 			continue;
358 		}
359 
360 		prev = entry;
361 		entry = entry->next;
362 	}
363 }
364 
365 
366 /**
367  * hostapd_acl_expire - ACL cache expiration callback
368  * @eloop_ctx: struct hostapd_data *
369  * @timeout_ctx: Not used
370  */
371 static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
372 {
373 	struct hostapd_data *hapd = eloop_ctx;
374 	struct os_time now;
375 
376 	os_get_time(&now);
377 	hostapd_acl_expire_cache(hapd, now.sec);
378 	hostapd_acl_expire_queries(hapd, now.sec);
379 
380 	eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
381 }
382 
383 
384 /**
385  * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
386  * @msg: RADIUS response message
387  * @req: RADIUS request message
388  * @shared_secret: RADIUS shared secret
389  * @shared_secret_len: Length of shared_secret in octets
390  * @data: Context data (struct hostapd_data *)
391  * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and
392  * was processed here) or RADIUS_RX_UNKNOWN if not.
393  */
394 static RadiusRxResult
395 hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
396 			const u8 *shared_secret, size_t shared_secret_len,
397 			void *data)
398 {
399 	struct hostapd_data *hapd = data;
400 	struct hostapd_acl_query_data *query, *prev;
401 	struct hostapd_cached_radius_acl *cache;
402 	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
403 	struct os_time t;
404 
405 	query = hapd->acl_queries;
406 	prev = NULL;
407 	while (query) {
408 		if (query->radius_id == hdr->identifier)
409 			break;
410 		prev = query;
411 		query = query->next;
412 	}
413 	if (query == NULL)
414 		return RADIUS_RX_UNKNOWN;
415 
416 	wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS "
417 		   "message (id=%d)", query->radius_id);
418 
419 	if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
420 		wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have "
421 			   "correct authenticator - dropped\n");
422 		return RADIUS_RX_INVALID_AUTHENTICATOR;
423 	}
424 
425 	if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
426 	    hdr->code != RADIUS_CODE_ACCESS_REJECT) {
427 		wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
428 			   "query", hdr->code);
429 		return RADIUS_RX_UNKNOWN;
430 	}
431 
432 	/* Insert Accept/Reject info into ACL cache */
433 	cache = os_zalloc(sizeof(*cache));
434 	if (cache == NULL) {
435 		wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
436 		goto done;
437 	}
438 	os_get_time(&t);
439 	cache->timestamp = t.sec;
440 	os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
441 	if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
442 		if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
443 					      &cache->session_timeout) == 0)
444 			cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
445 		else
446 			cache->accepted = HOSTAPD_ACL_ACCEPT;
447 
448 		if (radius_msg_get_attr_int32(
449 			    msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
450 			    &cache->acct_interim_interval) == 0 &&
451 		    cache->acct_interim_interval < 60) {
452 			wpa_printf(MSG_DEBUG, "Ignored too small "
453 				   "Acct-Interim-Interval %d for STA " MACSTR,
454 				   cache->acct_interim_interval,
455 				   MAC2STR(query->addr));
456 			cache->acct_interim_interval = 0;
457 		}
458 
459 		cache->vlan_id = radius_msg_get_vlanid(msg);
460 	} else
461 		cache->accepted = HOSTAPD_ACL_REJECT;
462 	cache->next = hapd->acl_cache;
463 	hapd->acl_cache = cache;
464 
465 #ifdef CONFIG_DRIVER_RADIUS_ACL
466 	hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted,
467 					cache->session_timeout);
468 #else /* CONFIG_DRIVER_RADIUS_ACL */
469 #ifdef NEED_AP_MLME
470 	/* Re-send original authentication frame for 802.11 processing */
471 	wpa_printf(MSG_DEBUG, "Re-sending authentication frame after "
472 		   "successful RADIUS ACL query");
473 	ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL);
474 #endif /* NEED_AP_MLME */
475 #endif /* CONFIG_DRIVER_RADIUS_ACL */
476 
477  done:
478 	if (prev == NULL)
479 		hapd->acl_queries = query->next;
480 	else
481 		prev->next = query->next;
482 
483 	hostapd_acl_query_free(query);
484 
485 	return RADIUS_RX_PROCESSED;
486 }
487 #endif /* CONFIG_NO_RADIUS */
488 
489 
490 /**
491  * hostapd_acl_init: Initialize IEEE 802.11 ACL
492  * @hapd: hostapd BSS data
493  * Returns: 0 on success, -1 on failure
494  */
495 int hostapd_acl_init(struct hostapd_data *hapd)
496 {
497 #ifndef CONFIG_NO_RADIUS
498 	if (radius_client_register(hapd->radius, RADIUS_AUTH,
499 				   hostapd_acl_recv_radius, hapd))
500 		return -1;
501 
502 	eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
503 #endif /* CONFIG_NO_RADIUS */
504 
505 	return 0;
506 }
507 
508 
509 /**
510  * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL
511  * @hapd: hostapd BSS data
512  */
513 void hostapd_acl_deinit(struct hostapd_data *hapd)
514 {
515 	struct hostapd_acl_query_data *query, *prev;
516 
517 #ifndef CONFIG_NO_RADIUS
518 	eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL);
519 
520 	hostapd_acl_cache_free(hapd->acl_cache);
521 #endif /* CONFIG_NO_RADIUS */
522 
523 	query = hapd->acl_queries;
524 	while (query) {
525 		prev = query;
526 		query = query->next;
527 		hostapd_acl_query_free(prev);
528 	}
529 }
530