ieee802_11_auth.c revision d5e4923d04122f81300fa68fb07d64ede28fd44d
1/*
2 * hostapd / IEEE 802.11 authentication (ACL)
3 * Copyright (c) 2003-2012, 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_1x.h"
26#include "ieee802_11_auth.h"
27
28#define RADIUS_ACL_TIMEOUT 30
29
30
31struct hostapd_cached_radius_acl {
32	os_time_t timestamp;
33	macaddr addr;
34	int accepted; /* HOSTAPD_ACL_* */
35	struct hostapd_cached_radius_acl *next;
36	u32 session_timeout;
37	u32 acct_interim_interval;
38	int vlan_id;
39	struct hostapd_sta_wpa_psk_short *psk;
40	char *identity;
41	char *radius_cui;
42};
43
44
45struct hostapd_acl_query_data {
46	os_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
56static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e)
57{
58	os_free(e->identity);
59	os_free(e->radius_cui);
60	hostapd_free_psk_list(e->psk);
61	os_free(e);
62}
63
64
65static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
66{
67	struct hostapd_cached_radius_acl *prev;
68
69	while (acl_cache) {
70		prev = acl_cache;
71		acl_cache = acl_cache->next;
72		hostapd_acl_cache_free_entry(prev);
73	}
74}
75
76
77static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
78			  struct hostapd_sta_wpa_psk_short *src)
79{
80	struct hostapd_sta_wpa_psk_short **copy_to;
81	struct hostapd_sta_wpa_psk_short *copy_from;
82
83	/* Copy PSK linked list */
84	copy_to = psk;
85	copy_from = src;
86	while (copy_from && copy_to) {
87		*copy_to = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
88		if (*copy_to == NULL)
89			break;
90		os_memcpy(*copy_to, copy_from,
91			  sizeof(struct hostapd_sta_wpa_psk_short));
92		copy_from = copy_from->next;
93		copy_to = &((*copy_to)->next);
94	}
95	if (copy_to)
96		*copy_to = NULL;
97}
98
99
100static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
101				 u32 *session_timeout,
102				 u32 *acct_interim_interval, int *vlan_id,
103				 struct hostapd_sta_wpa_psk_short **psk,
104				 char **identity, char **radius_cui)
105{
106	struct hostapd_cached_radius_acl *entry;
107	struct os_time now;
108
109	os_get_time(&now);
110
111	for (entry = hapd->acl_cache; entry; entry = entry->next) {
112		if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0)
113			continue;
114
115		if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT)
116			return -1; /* entry has expired */
117		if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
118			if (session_timeout)
119				*session_timeout = entry->session_timeout;
120		if (acct_interim_interval)
121			*acct_interim_interval =
122				entry->acct_interim_interval;
123		if (vlan_id)
124			*vlan_id = entry->vlan_id;
125		copy_psk_list(psk, entry->psk);
126		if (identity) {
127			if (entry->identity)
128				*identity = os_strdup(entry->identity);
129			else
130				*identity = NULL;
131		}
132		if (radius_cui) {
133			if (entry->radius_cui)
134				*radius_cui = os_strdup(entry->radius_cui);
135			else
136				*radius_cui = NULL;
137		}
138		return entry->accepted;
139	}
140
141	return -1;
142}
143#endif /* CONFIG_NO_RADIUS */
144
145
146static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
147{
148	if (query == NULL)
149		return;
150	os_free(query->auth_msg);
151	os_free(query);
152}
153
154
155#ifndef CONFIG_NO_RADIUS
156static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
157				    struct hostapd_acl_query_data *query)
158{
159	struct radius_msg *msg;
160	char buf[128];
161
162	query->radius_id = radius_client_get_id(hapd->radius);
163	msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
164	if (msg == NULL)
165		return -1;
166
167	radius_msg_make_authenticator(msg, addr, ETH_ALEN);
168
169	os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
170	if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
171				 os_strlen(buf))) {
172		wpa_printf(MSG_DEBUG, "Could not add User-Name");
173		goto fail;
174	}
175
176	if (!radius_msg_add_attr_user_password(
177		    msg, (u8 *) buf, os_strlen(buf),
178		    hapd->conf->radius->auth_server->shared_secret,
179		    hapd->conf->radius->auth_server->shared_secret_len)) {
180		wpa_printf(MSG_DEBUG, "Could not add User-Password");
181		goto fail;
182	}
183
184	if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr,
185				   NULL, msg) < 0)
186		goto fail;
187
188	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
189		    MAC2STR(addr));
190	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
191				 (u8 *) buf, os_strlen(buf))) {
192		wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id");
193		goto fail;
194	}
195
196	os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
197	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
198				 (u8 *) buf, os_strlen(buf))) {
199		wpa_printf(MSG_DEBUG, "Could not add Connect-Info");
200		goto fail;
201	}
202
203	if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
204		goto fail;
205	return 0;
206
207 fail:
208	radius_msg_free(msg);
209	return -1;
210}
211#endif /* CONFIG_NO_RADIUS */
212
213
214/**
215 * hostapd_allowed_address - Check whether a specified STA can be authenticated
216 * @hapd: hostapd BSS data
217 * @addr: MAC address of the STA
218 * @msg: Authentication message
219 * @len: Length of msg in octets
220 * @session_timeout: Buffer for returning session timeout (from RADIUS)
221 * @acct_interim_interval: Buffer for returning account interval (from RADIUS)
222 * @vlan_id: Buffer for returning VLAN ID
223 * @psk: Linked list buffer for returning WPA PSK
224 * @identity: Buffer for returning identity (from RADIUS)
225 * @radius_cui: Buffer for returning CUI (from RADIUS)
226 * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
227 *
228 * The caller is responsible for freeing the returned *identity and *radius_cui
229 * values with os_free().
230 */
231int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
232			    const u8 *msg, size_t len, u32 *session_timeout,
233			    u32 *acct_interim_interval, int *vlan_id,
234			    struct hostapd_sta_wpa_psk_short **psk,
235			    char **identity, char **radius_cui)
236{
237	if (session_timeout)
238		*session_timeout = 0;
239	if (acct_interim_interval)
240		*acct_interim_interval = 0;
241	if (vlan_id)
242		*vlan_id = 0;
243	if (psk)
244		*psk = NULL;
245	if (identity)
246		*identity = NULL;
247	if (radius_cui)
248		*radius_cui = NULL;
249
250	if (hostapd_maclist_found(hapd->conf->accept_mac,
251				  hapd->conf->num_accept_mac, addr, vlan_id))
252		return HOSTAPD_ACL_ACCEPT;
253
254	if (hostapd_maclist_found(hapd->conf->deny_mac,
255				  hapd->conf->num_deny_mac, addr, vlan_id))
256		return HOSTAPD_ACL_REJECT;
257
258	if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
259		return HOSTAPD_ACL_ACCEPT;
260	if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
261		return HOSTAPD_ACL_REJECT;
262
263	if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
264#ifdef CONFIG_NO_RADIUS
265		return HOSTAPD_ACL_REJECT;
266#else /* CONFIG_NO_RADIUS */
267		struct hostapd_acl_query_data *query;
268		struct os_time t;
269
270		/* Check whether ACL cache has an entry for this station */
271		int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
272						acct_interim_interval,
273						vlan_id, psk,
274						identity, radius_cui);
275		if (res == HOSTAPD_ACL_ACCEPT ||
276		    res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
277			return res;
278		if (res == HOSTAPD_ACL_REJECT)
279			return HOSTAPD_ACL_REJECT;
280
281		query = hapd->acl_queries;
282		while (query) {
283			if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
284				/* pending query in RADIUS retransmit queue;
285				 * do not generate a new one */
286				if (identity) {
287					os_free(*identity);
288					*identity = NULL;
289				}
290				if (radius_cui) {
291					os_free(*radius_cui);
292					*radius_cui = NULL;
293				}
294				return HOSTAPD_ACL_PENDING;
295			}
296			query = query->next;
297		}
298
299		if (!hapd->conf->radius->auth_server)
300			return HOSTAPD_ACL_REJECT;
301
302		/* No entry in the cache - query external RADIUS server */
303		query = os_zalloc(sizeof(*query));
304		if (query == NULL) {
305			wpa_printf(MSG_ERROR, "malloc for query data failed");
306			return HOSTAPD_ACL_REJECT;
307		}
308		os_get_time(&t);
309		query->timestamp = t.sec;
310		os_memcpy(query->addr, addr, ETH_ALEN);
311		if (hostapd_radius_acl_query(hapd, addr, query)) {
312			wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
313				   "for ACL query.");
314			hostapd_acl_query_free(query);
315			return HOSTAPD_ACL_REJECT;
316		}
317
318		query->auth_msg = os_malloc(len);
319		if (query->auth_msg == NULL) {
320			wpa_printf(MSG_ERROR, "Failed to allocate memory for "
321				   "auth frame.");
322			hostapd_acl_query_free(query);
323			return HOSTAPD_ACL_REJECT;
324		}
325		os_memcpy(query->auth_msg, msg, len);
326		query->auth_msg_len = len;
327		query->next = hapd->acl_queries;
328		hapd->acl_queries = query;
329
330		/* Queued data will be processed in hostapd_acl_recv_radius()
331		 * when RADIUS server replies to the sent Access-Request. */
332		return HOSTAPD_ACL_PENDING;
333#endif /* CONFIG_NO_RADIUS */
334	}
335
336	return HOSTAPD_ACL_REJECT;
337}
338
339
340#ifndef CONFIG_NO_RADIUS
341static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now)
342{
343	struct hostapd_cached_radius_acl *prev, *entry, *tmp;
344
345	prev = NULL;
346	entry = hapd->acl_cache;
347
348	while (entry) {
349		if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
350			wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
351				   " has expired.", MAC2STR(entry->addr));
352			if (prev)
353				prev->next = entry->next;
354			else
355				hapd->acl_cache = entry->next;
356			hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
357			tmp = entry;
358			entry = entry->next;
359			hostapd_acl_cache_free_entry(tmp);
360			continue;
361		}
362
363		prev = entry;
364		entry = entry->next;
365	}
366}
367
368
369static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
370				       os_time_t now)
371{
372	struct hostapd_acl_query_data *prev, *entry, *tmp;
373
374	prev = NULL;
375	entry = hapd->acl_queries;
376
377	while (entry) {
378		if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
379			wpa_printf(MSG_DEBUG, "ACL query for " MACSTR
380				   " has expired.", MAC2STR(entry->addr));
381			if (prev)
382				prev->next = entry->next;
383			else
384				hapd->acl_queries = entry->next;
385
386			tmp = entry;
387			entry = entry->next;
388			hostapd_acl_query_free(tmp);
389			continue;
390		}
391
392		prev = entry;
393		entry = entry->next;
394	}
395}
396
397
398/**
399 * hostapd_acl_expire - ACL cache expiration callback
400 * @eloop_ctx: struct hostapd_data *
401 * @timeout_ctx: Not used
402 */
403static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
404{
405	struct hostapd_data *hapd = eloop_ctx;
406	struct os_time now;
407
408	os_get_time(&now);
409	hostapd_acl_expire_cache(hapd, now.sec);
410	hostapd_acl_expire_queries(hapd, now.sec);
411
412	eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
413}
414
415
416static void decode_tunnel_passwords(struct hostapd_data *hapd,
417				    const u8 *shared_secret,
418				    size_t shared_secret_len,
419				    struct radius_msg *msg,
420				    struct radius_msg *req,
421				    struct hostapd_cached_radius_acl *cache)
422{
423	int passphraselen;
424	char *passphrase, *strpassphrase;
425	size_t i;
426	struct hostapd_sta_wpa_psk_short *psk;
427
428	/*
429	 * Decode all tunnel passwords as PSK and save them into a linked list.
430	 */
431	for (i = 0; ; i++) {
432		passphrase = radius_msg_get_tunnel_password(
433			msg, &passphraselen, shared_secret, shared_secret_len,
434			req, i);
435		/*
436		 * Passphrase is NULL iff there is no i-th Tunnel-Password
437		 * attribute in msg.
438		 */
439		if (passphrase == NULL)
440			break;
441		/*
442		 * passphrase does not contain the NULL termination.
443		 * Add it here as pbkdf2_sha1() requires it.
444		 */
445		strpassphrase = os_zalloc(passphraselen + 1);
446		psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
447		if (strpassphrase && psk) {
448			os_memcpy(strpassphrase, passphrase, passphraselen);
449			pbkdf2_sha1(strpassphrase,
450				    hapd->conf->ssid.ssid,
451				    hapd->conf->ssid.ssid_len, 4096,
452				    psk->psk, PMK_LEN);
453			psk->next = cache->psk;
454			cache->psk = psk;
455			psk = NULL;
456		}
457		os_free(strpassphrase);
458		os_free(psk);
459		os_free(passphrase);
460	}
461}
462
463
464/**
465 * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
466 * @msg: RADIUS response message
467 * @req: RADIUS request message
468 * @shared_secret: RADIUS shared secret
469 * @shared_secret_len: Length of shared_secret in octets
470 * @data: Context data (struct hostapd_data *)
471 * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and
472 * was processed here) or RADIUS_RX_UNKNOWN if not.
473 */
474static RadiusRxResult
475hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
476			const u8 *shared_secret, size_t shared_secret_len,
477			void *data)
478{
479	struct hostapd_data *hapd = data;
480	struct hostapd_acl_query_data *query, *prev;
481	struct hostapd_cached_radius_acl *cache;
482	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
483	struct os_time t;
484
485	query = hapd->acl_queries;
486	prev = NULL;
487	while (query) {
488		if (query->radius_id == hdr->identifier)
489			break;
490		prev = query;
491		query = query->next;
492	}
493	if (query == NULL)
494		return RADIUS_RX_UNKNOWN;
495
496	wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS "
497		   "message (id=%d)", query->radius_id);
498
499	if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
500		wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have "
501			   "correct authenticator - dropped\n");
502		return RADIUS_RX_INVALID_AUTHENTICATOR;
503	}
504
505	if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
506	    hdr->code != RADIUS_CODE_ACCESS_REJECT) {
507		wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
508			   "query", hdr->code);
509		return RADIUS_RX_UNKNOWN;
510	}
511
512	/* Insert Accept/Reject info into ACL cache */
513	cache = os_zalloc(sizeof(*cache));
514	if (cache == NULL) {
515		wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
516		goto done;
517	}
518	os_get_time(&t);
519	cache->timestamp = t.sec;
520	os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
521	if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
522		u8 *buf;
523		size_t len;
524
525		if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
526					      &cache->session_timeout) == 0)
527			cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
528		else
529			cache->accepted = HOSTAPD_ACL_ACCEPT;
530
531		if (radius_msg_get_attr_int32(
532			    msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
533			    &cache->acct_interim_interval) == 0 &&
534		    cache->acct_interim_interval < 60) {
535			wpa_printf(MSG_DEBUG, "Ignored too small "
536				   "Acct-Interim-Interval %d for STA " MACSTR,
537				   cache->acct_interim_interval,
538				   MAC2STR(query->addr));
539			cache->acct_interim_interval = 0;
540		}
541
542		cache->vlan_id = radius_msg_get_vlanid(msg);
543
544		decode_tunnel_passwords(hapd, shared_secret, shared_secret_len,
545					msg, req, cache);
546
547		if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
548					    &buf, &len, NULL) == 0) {
549			cache->identity = os_zalloc(len + 1);
550			if (cache->identity)
551				os_memcpy(cache->identity, buf, len);
552		}
553		if (radius_msg_get_attr_ptr(
554			    msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
555			    &buf, &len, NULL) == 0) {
556			cache->radius_cui = os_zalloc(len + 1);
557			if (cache->radius_cui)
558				os_memcpy(cache->radius_cui, buf, len);
559		}
560
561		if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED &&
562		    !cache->psk)
563			cache->accepted = HOSTAPD_ACL_REJECT;
564	} else
565		cache->accepted = HOSTAPD_ACL_REJECT;
566	cache->next = hapd->acl_cache;
567	hapd->acl_cache = cache;
568
569#ifdef CONFIG_DRIVER_RADIUS_ACL
570	hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted,
571					cache->session_timeout);
572#else /* CONFIG_DRIVER_RADIUS_ACL */
573#ifdef NEED_AP_MLME
574	/* Re-send original authentication frame for 802.11 processing */
575	wpa_printf(MSG_DEBUG, "Re-sending authentication frame after "
576		   "successful RADIUS ACL query");
577	ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL);
578#endif /* NEED_AP_MLME */
579#endif /* CONFIG_DRIVER_RADIUS_ACL */
580
581 done:
582	if (prev == NULL)
583		hapd->acl_queries = query->next;
584	else
585		prev->next = query->next;
586
587	hostapd_acl_query_free(query);
588
589	return RADIUS_RX_PROCESSED;
590}
591#endif /* CONFIG_NO_RADIUS */
592
593
594/**
595 * hostapd_acl_init: Initialize IEEE 802.11 ACL
596 * @hapd: hostapd BSS data
597 * Returns: 0 on success, -1 on failure
598 */
599int hostapd_acl_init(struct hostapd_data *hapd)
600{
601#ifndef CONFIG_NO_RADIUS
602	if (radius_client_register(hapd->radius, RADIUS_AUTH,
603				   hostapd_acl_recv_radius, hapd))
604		return -1;
605
606	eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
607#endif /* CONFIG_NO_RADIUS */
608
609	return 0;
610}
611
612
613/**
614 * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL
615 * @hapd: hostapd BSS data
616 */
617void hostapd_acl_deinit(struct hostapd_data *hapd)
618{
619	struct hostapd_acl_query_data *query, *prev;
620
621#ifndef CONFIG_NO_RADIUS
622	eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL);
623
624	hostapd_acl_cache_free(hapd->acl_cache);
625#endif /* CONFIG_NO_RADIUS */
626
627	query = hapd->acl_queries;
628	while (query) {
629		prev = query;
630		query = query->next;
631		hostapd_acl_query_free(prev);
632	}
633}
634
635
636void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk)
637{
638	while (psk) {
639		struct hostapd_sta_wpa_psk_short *prev = psk;
640		psk = psk->next;
641		os_free(prev);
642	}
643}
644