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 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
     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 
     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 
    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
    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  */
    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
    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 
    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  */
    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
    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  */
    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  */
    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