• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
hostapd_acl_cache_free(struct hostapd_cached_radius_acl * acl_cache)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 
hostapd_acl_cache_get(struct hostapd_data * hapd,const u8 * addr,u32 * session_timeout,u32 * acct_interim_interval,int * vlan_id)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 
hostapd_acl_query_free(struct hostapd_acl_query_data * query)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
hostapd_radius_acl_query(struct hostapd_data * hapd,const u8 * addr,struct hostapd_acl_query_data * query)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 	radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr);
195 	return 0;
196 
197  fail:
198 	radius_msg_free(msg);
199 	return -1;
200 }
201 #endif /* CONFIG_NO_RADIUS */
202 
203 
204 /**
205  * hostapd_allowed_address - Check whether a specified STA can be authenticated
206  * @hapd: hostapd BSS data
207  * @addr: MAC address of the STA
208  * @msg: Authentication message
209  * @len: Length of msg in octets
210  * @session_timeout: Buffer for returning session timeout (from RADIUS)
211  * @acct_interim_interval: Buffer for returning account interval (from RADIUS)
212  * @vlan_id: Buffer for returning VLAN ID
213  * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
214  */
hostapd_allowed_address(struct hostapd_data * hapd,const u8 * addr,const u8 * msg,size_t len,u32 * session_timeout,u32 * acct_interim_interval,int * vlan_id)215 int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
216 			    const u8 *msg, size_t len, u32 *session_timeout,
217 			    u32 *acct_interim_interval, int *vlan_id)
218 {
219 	if (session_timeout)
220 		*session_timeout = 0;
221 	if (acct_interim_interval)
222 		*acct_interim_interval = 0;
223 	if (vlan_id)
224 		*vlan_id = 0;
225 
226 	if (hostapd_maclist_found(hapd->conf->accept_mac,
227 				  hapd->conf->num_accept_mac, addr, vlan_id))
228 		return HOSTAPD_ACL_ACCEPT;
229 
230 	if (hostapd_maclist_found(hapd->conf->deny_mac,
231 				  hapd->conf->num_deny_mac, addr, vlan_id))
232 		return HOSTAPD_ACL_REJECT;
233 
234 	if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
235 		return HOSTAPD_ACL_ACCEPT;
236 	if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
237 		return HOSTAPD_ACL_REJECT;
238 
239 	if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
240 #ifdef CONFIG_NO_RADIUS
241 		return HOSTAPD_ACL_REJECT;
242 #else /* CONFIG_NO_RADIUS */
243 		struct hostapd_acl_query_data *query;
244 
245 		/* Check whether ACL cache has an entry for this station */
246 		int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
247 						acct_interim_interval,
248 						vlan_id);
249 		if (res == HOSTAPD_ACL_ACCEPT ||
250 		    res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
251 			return res;
252 		if (res == HOSTAPD_ACL_REJECT)
253 			return HOSTAPD_ACL_REJECT;
254 
255 		query = hapd->acl_queries;
256 		while (query) {
257 			if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
258 				/* pending query in RADIUS retransmit queue;
259 				 * do not generate a new one */
260 				return HOSTAPD_ACL_PENDING;
261 			}
262 			query = query->next;
263 		}
264 
265 		if (!hapd->conf->radius->auth_server)
266 			return HOSTAPD_ACL_REJECT;
267 
268 		/* No entry in the cache - query external RADIUS server */
269 		query = os_zalloc(sizeof(*query));
270 		if (query == NULL) {
271 			wpa_printf(MSG_ERROR, "malloc for query data failed");
272 			return HOSTAPD_ACL_REJECT;
273 		}
274 		time(&query->timestamp);
275 		os_memcpy(query->addr, addr, ETH_ALEN);
276 		if (hostapd_radius_acl_query(hapd, addr, query)) {
277 			wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
278 				   "for ACL query.");
279 			hostapd_acl_query_free(query);
280 			return HOSTAPD_ACL_REJECT;
281 		}
282 
283 		query->auth_msg = os_malloc(len);
284 		if (query->auth_msg == NULL) {
285 			wpa_printf(MSG_ERROR, "Failed to allocate memory for "
286 				   "auth frame.");
287 			hostapd_acl_query_free(query);
288 			return HOSTAPD_ACL_REJECT;
289 		}
290 		os_memcpy(query->auth_msg, msg, len);
291 		query->auth_msg_len = len;
292 		query->next = hapd->acl_queries;
293 		hapd->acl_queries = query;
294 
295 		/* Queued data will be processed in hostapd_acl_recv_radius()
296 		 * when RADIUS server replies to the sent Access-Request. */
297 		return HOSTAPD_ACL_PENDING;
298 #endif /* CONFIG_NO_RADIUS */
299 	}
300 
301 	return HOSTAPD_ACL_REJECT;
302 }
303 
304 
305 #ifndef CONFIG_NO_RADIUS
hostapd_acl_expire_cache(struct hostapd_data * hapd,os_time_t now)306 static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now)
307 {
308 	struct hostapd_cached_radius_acl *prev, *entry, *tmp;
309 
310 	prev = NULL;
311 	entry = hapd->acl_cache;
312 
313 	while (entry) {
314 		if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
315 			wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
316 				   " has expired.", MAC2STR(entry->addr));
317 			if (prev)
318 				prev->next = entry->next;
319 			else
320 				hapd->acl_cache = entry->next;
321 			hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
322 			tmp = entry;
323 			entry = entry->next;
324 			os_free(tmp);
325 			continue;
326 		}
327 
328 		prev = entry;
329 		entry = entry->next;
330 	}
331 }
332 
333 
hostapd_acl_expire_queries(struct hostapd_data * hapd,os_time_t now)334 static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
335 				       os_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  */
hostapd_acl_expire(void * eloop_ctx,void * timeout_ctx)368 static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
369 {
370 	struct hostapd_data *hapd = eloop_ctx;
371 	struct os_time now;
372 
373 	os_get_time(&now);
374 	hostapd_acl_expire_cache(hapd, now.sec);
375 	hostapd_acl_expire_queries(hapd, now.sec);
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
hostapd_acl_recv_radius(struct radius_msg * msg,struct radius_msg * req,const u8 * shared_secret,size_t shared_secret_len,void * data)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 	hostapd_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  */
hostapd_acl_init(struct hostapd_data * hapd)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  */
hostapd_acl_deinit(struct hostapd_data * hapd)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