Home | History | Annotate | Download | only in ap
      1 /*
      2  * hostapd / IEEE 802.11 authentication (ACL)
      3  * Copyright (c) 2003-2009, Jouni Malinen <j (at) 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
     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 
     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 
    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
    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  */
    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
    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 
    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  */
    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
    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  */
    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  */
    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