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