ieee802_11_auth.c revision c5ec7f57ead87efa365800228aa0b09a12d9e6c4
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
30struct 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
43struct 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
54static 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
66static 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
105static 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
115static 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 */
219int 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
317static 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
345static 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 */
379static 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 */
402static RadiusRxResult
403hostapd_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 */
532int 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 */
550void 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