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