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