]> git.ipfire.org Git - thirdparty/hostap.git/blame - src/ap/ieee802_11_auth.c
Rename some src/ap files to avoid duplicate file names
[thirdparty/hostap.git] / src / ap / ieee802_11_auth.c
CommitLineData
6fc6879b
JM
1/*
2 * hostapd / IEEE 802.11 authentication (ACL)
6226e38d 3 * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
6fc6879b
JM
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.
1c6e69cc
JM
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.
6fc6879b
JM
18 */
19
6226e38d 20#include "utils/includes.h"
6fc6879b
JM
21
22#ifndef CONFIG_NATIVE_WINDOWS
23
6226e38d
JM
24#include "utils/common.h"
25#include "utils/eloop.h"
26#include "radius/radius.h"
27#include "radius/radius_client.h"
6fc6879b 28#include "hostapd.h"
6226e38d 29#include "ap_config.h"
6fc6879b
JM
30#include "ieee802_11.h"
31#include "ieee802_11_auth.h"
6fc6879b
JM
32
33#define RADIUS_ACL_TIMEOUT 30
34
35
36struct hostapd_cached_radius_acl {
37 time_t timestamp;
38 macaddr addr;
39 int accepted; /* HOSTAPD_ACL_* */
40 struct hostapd_cached_radius_acl *next;
41 u32 session_timeout;
42 u32 acct_interim_interval;
43 int vlan_id;
44};
45
46
47struct hostapd_acl_query_data {
48 time_t timestamp;
49 u8 radius_id;
50 macaddr addr;
51 u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
52 size_t auth_msg_len;
53 struct hostapd_acl_query_data *next;
54};
55
56
f88bd288 57#ifndef CONFIG_NO_RADIUS
6fc6879b
JM
58static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
59{
60 struct hostapd_cached_radius_acl *prev;
61
62 while (acl_cache) {
63 prev = acl_cache;
64 acl_cache = acl_cache->next;
65 os_free(prev);
66 }
67}
68
69
70static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
71 u32 *session_timeout,
72 u32 *acct_interim_interval, int *vlan_id)
73{
74 struct hostapd_cached_radius_acl *entry;
75 time_t now;
76
77 time(&now);
78 entry = hapd->acl_cache;
79
80 while (entry) {
81 if (os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
82 if (now - entry->timestamp > RADIUS_ACL_TIMEOUT)
83 return -1; /* entry has expired */
84 if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
b6745143
CZ
85 if (session_timeout)
86 *session_timeout =
87 entry->session_timeout;
88 if (acct_interim_interval)
89 *acct_interim_interval =
90 entry->acct_interim_interval;
6fc6879b
JM
91 if (vlan_id)
92 *vlan_id = entry->vlan_id;
93 return entry->accepted;
94 }
95
96 entry = entry->next;
97 }
98
99 return -1;
100}
f88bd288 101#endif /* CONFIG_NO_RADIUS */
6fc6879b
JM
102
103
104static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
105{
106 if (query == NULL)
107 return;
108 os_free(query->auth_msg);
109 os_free(query);
110}
111
112
f88bd288 113#ifndef CONFIG_NO_RADIUS
6fc6879b
JM
114static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
115 struct hostapd_acl_query_data *query)
116{
117 struct radius_msg *msg;
118 char buf[128];
119
120 query->radius_id = radius_client_get_id(hapd->radius);
121 msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
122 if (msg == NULL)
123 return -1;
124
125 radius_msg_make_authenticator(msg, addr, ETH_ALEN);
126
127 os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
128 if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
129 os_strlen(buf))) {
130 wpa_printf(MSG_DEBUG, "Could not add User-Name");
131 goto fail;
132 }
133
134 if (!radius_msg_add_attr_user_password(
135 msg, (u8 *) buf, os_strlen(buf),
136 hapd->conf->radius->auth_server->shared_secret,
137 hapd->conf->radius->auth_server->shared_secret_len)) {
138 wpa_printf(MSG_DEBUG, "Could not add User-Password");
139 goto fail;
140 }
141
142 if (hapd->conf->own_ip_addr.af == AF_INET &&
143 !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
144 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
145 wpa_printf(MSG_DEBUG, "Could not add NAS-IP-Address");
146 goto fail;
147 }
148
149#ifdef CONFIG_IPV6
150 if (hapd->conf->own_ip_addr.af == AF_INET6 &&
151 !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
152 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
153 wpa_printf(MSG_DEBUG, "Could not add NAS-IPv6-Address");
154 goto fail;
155 }
156#endif /* CONFIG_IPV6 */
157
158 if (hapd->conf->nas_identifier &&
159 !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
160 (u8 *) hapd->conf->nas_identifier,
161 os_strlen(hapd->conf->nas_identifier))) {
162 wpa_printf(MSG_DEBUG, "Could not add NAS-Identifier");
163 goto fail;
164 }
165
166 os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
167 MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
168 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
169 (u8 *) buf, os_strlen(buf))) {
170 wpa_printf(MSG_DEBUG, "Could not add Called-Station-Id");
171 goto fail;
172 }
173
174 os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
175 MAC2STR(addr));
176 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
177 (u8 *) buf, os_strlen(buf))) {
178 wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id");
179 goto fail;
180 }
181
182 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
183 RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
184 wpa_printf(MSG_DEBUG, "Could not add NAS-Port-Type");
185 goto fail;
186 }
187
188 os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
189 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
190 (u8 *) buf, os_strlen(buf))) {
191 wpa_printf(MSG_DEBUG, "Could not add Connect-Info");
192 goto fail;
193 }
194
195 radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr);
196 return 0;
197
198 fail:
199 radius_msg_free(msg);
6fc6879b
JM
200 return -1;
201}
f88bd288 202#endif /* CONFIG_NO_RADIUS */
6fc6879b
JM
203
204
1c6e69cc
JM
205/**
206 * hostapd_allowed_address - Check whether a specified STA can be authenticated
207 * @hapd: hostapd BSS data
208 * @addr: MAC address of the STA
209 * @msg: Authentication message
210 * @len: Length of msg in octets
211 * @session_timeout: Buffer for returning session timeout (from RADIUS)
212 * @acct_interim_interval: Buffer for returning account interval (from RADIUS)
213 * @vlan_id: Buffer for returning VLAN ID
214 * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
215 */
6fc6879b
JM
216int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
217 const u8 *msg, size_t len, u32 *session_timeout,
218 u32 *acct_interim_interval, int *vlan_id)
219{
b6745143
CZ
220 if (session_timeout)
221 *session_timeout = 0;
222 if (acct_interim_interval)
223 *acct_interim_interval = 0;
6fc6879b
JM
224 if (vlan_id)
225 *vlan_id = 0;
226
227 if (hostapd_maclist_found(hapd->conf->accept_mac,
271d2830 228 hapd->conf->num_accept_mac, addr, vlan_id))
6fc6879b
JM
229 return HOSTAPD_ACL_ACCEPT;
230
231 if (hostapd_maclist_found(hapd->conf->deny_mac,
271d2830 232 hapd->conf->num_deny_mac, addr, vlan_id))
6fc6879b
JM
233 return HOSTAPD_ACL_REJECT;
234
235 if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
236 return HOSTAPD_ACL_ACCEPT;
237 if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
238 return HOSTAPD_ACL_REJECT;
239
240 if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
f88bd288
JM
241#ifdef CONFIG_NO_RADIUS
242 return HOSTAPD_ACL_REJECT;
243#else /* CONFIG_NO_RADIUS */
6fc6879b
JM
244 struct hostapd_acl_query_data *query;
245
246 /* Check whether ACL cache has an entry for this station */
247 int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
248 acct_interim_interval,
249 vlan_id);
250 if (res == HOSTAPD_ACL_ACCEPT ||
251 res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
252 return res;
253 if (res == HOSTAPD_ACL_REJECT)
254 return HOSTAPD_ACL_REJECT;
255
256 query = hapd->acl_queries;
257 while (query) {
258 if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
259 /* pending query in RADIUS retransmit queue;
260 * do not generate a new one */
261 return HOSTAPD_ACL_PENDING;
262 }
263 query = query->next;
264 }
265
266 if (!hapd->conf->radius->auth_server)
267 return HOSTAPD_ACL_REJECT;
268
269 /* No entry in the cache - query external RADIUS server */
270 query = os_zalloc(sizeof(*query));
271 if (query == NULL) {
272 wpa_printf(MSG_ERROR, "malloc for query data failed");
273 return HOSTAPD_ACL_REJECT;
274 }
275 time(&query->timestamp);
276 os_memcpy(query->addr, addr, ETH_ALEN);
277 if (hostapd_radius_acl_query(hapd, addr, query)) {
278 wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
279 "for ACL query.");
280 hostapd_acl_query_free(query);
281 return HOSTAPD_ACL_REJECT;
282 }
283
284 query->auth_msg = os_malloc(len);
285 if (query->auth_msg == NULL) {
286 wpa_printf(MSG_ERROR, "Failed to allocate memory for "
287 "auth frame.");
288 hostapd_acl_query_free(query);
289 return HOSTAPD_ACL_REJECT;
290 }
291 os_memcpy(query->auth_msg, msg, len);
292 query->auth_msg_len = len;
293 query->next = hapd->acl_queries;
294 hapd->acl_queries = query;
295
296 /* Queued data will be processed in hostapd_acl_recv_radius()
297 * when RADIUS server replies to the sent Access-Request. */
298 return HOSTAPD_ACL_PENDING;
f88bd288 299#endif /* CONFIG_NO_RADIUS */
6fc6879b
JM
300 }
301
302 return HOSTAPD_ACL_REJECT;
303}
304
305
f88bd288 306#ifndef CONFIG_NO_RADIUS
6fc6879b
JM
307static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now)
308{
309 struct hostapd_cached_radius_acl *prev, *entry, *tmp;
310
311 prev = NULL;
312 entry = hapd->acl_cache;
313
314 while (entry) {
315 if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
316 wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
317 " has expired.", MAC2STR(entry->addr));
318 if (prev)
319 prev->next = entry->next;
320 else
321 hapd->acl_cache = entry->next;
6affdaee 322#ifdef CONFIG_DRIVER_RADIUS_ACL
2f3e0bd4 323 hapd->drv.set_radius_acl_expire(hapd, entry->addr);
6affdaee 324#endif /* CONFIG_DRIVER_RADIUS_ACL */
6fc6879b
JM
325 tmp = entry;
326 entry = entry->next;
327 os_free(tmp);
328 continue;
329 }
330
331 prev = entry;
332 entry = entry->next;
333 }
334}
335
336
337static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now)
338{
339 struct hostapd_acl_query_data *prev, *entry, *tmp;
340
341 prev = NULL;
342 entry = hapd->acl_queries;
343
344 while (entry) {
345 if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
346 wpa_printf(MSG_DEBUG, "ACL query for " MACSTR
347 " has expired.", MAC2STR(entry->addr));
348 if (prev)
349 prev->next = entry->next;
350 else
351 hapd->acl_queries = entry->next;
352
353 tmp = entry;
354 entry = entry->next;
355 hostapd_acl_query_free(tmp);
356 continue;
357 }
358
359 prev = entry;
360 entry = entry->next;
361 }
362}
363
364
1c6e69cc
JM
365/**
366 * hostapd_acl_expire - ACL cache expiration callback
367 * @eloop_ctx: struct hostapd_data *
368 * @timeout_ctx: Not used
369 */
6fc6879b
JM
370static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
371{
372 struct hostapd_data *hapd = eloop_ctx;
373 time_t now;
374
375 time(&now);
376 hostapd_acl_expire_cache(hapd, now);
377 hostapd_acl_expire_queries(hapd, now);
378
379 eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
380}
381
382
1c6e69cc
JM
383/**
384 * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
385 * @msg: RADIUS response message
386 * @req: RADIUS request message
387 * @shared_secret: RADIUS shared secret
388 * @shared_secret_len: Length of shared_secret in octets
389 * @data: Context data (struct hostapd_data *)
390 * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and
391 * was processed here) or RADIUS_RX_UNKNOWN if not.
392 */
6fc6879b
JM
393static RadiusRxResult
394hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
7d02e641 395 const u8 *shared_secret, size_t shared_secret_len,
6fc6879b
JM
396 void *data)
397{
398 struct hostapd_data *hapd = data;
399 struct hostapd_acl_query_data *query, *prev;
400 struct hostapd_cached_radius_acl *cache;
1489e11a 401 struct radius_hdr *hdr = radius_msg_get_hdr(msg);
6fc6879b
JM
402
403 query = hapd->acl_queries;
404 prev = NULL;
405 while (query) {
1489e11a 406 if (query->radius_id == hdr->identifier)
6fc6879b
JM
407 break;
408 prev = query;
409 query = query->next;
410 }
411 if (query == NULL)
412 return RADIUS_RX_UNKNOWN;
413
414 wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS "
415 "message (id=%d)", query->radius_id);
416
417 if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
418 wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have "
419 "correct authenticator - dropped\n");
420 return RADIUS_RX_INVALID_AUTHENTICATOR;
421 }
422
1489e11a
JM
423 if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
424 hdr->code != RADIUS_CODE_ACCESS_REJECT) {
6fc6879b 425 wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
1489e11a 426 "query", hdr->code);
6fc6879b
JM
427 return RADIUS_RX_UNKNOWN;
428 }
429
430 /* Insert Accept/Reject info into ACL cache */
431 cache = os_zalloc(sizeof(*cache));
432 if (cache == NULL) {
433 wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
434 goto done;
435 }
436 time(&cache->timestamp);
437 os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
1489e11a 438 if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
6fc6879b
JM
439 if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
440 &cache->session_timeout) == 0)
441 cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
442 else
443 cache->accepted = HOSTAPD_ACL_ACCEPT;
444
445 if (radius_msg_get_attr_int32(
446 msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
447 &cache->acct_interim_interval) == 0 &&
448 cache->acct_interim_interval < 60) {
449 wpa_printf(MSG_DEBUG, "Ignored too small "
450 "Acct-Interim-Interval %d for STA " MACSTR,
451 cache->acct_interim_interval,
452 MAC2STR(query->addr));
453 cache->acct_interim_interval = 0;
454 }
455
456 cache->vlan_id = radius_msg_get_vlanid(msg);
457 } else
458 cache->accepted = HOSTAPD_ACL_REJECT;
459 cache->next = hapd->acl_cache;
460 hapd->acl_cache = cache;
461
6affdaee 462#ifdef CONFIG_DRIVER_RADIUS_ACL
2f3e0bd4
JM
463 hapd->drv.set_radius_acl_auth(hapd, query->addr, cache->accepted,
464 cache->session_timeout);
6affdaee 465#else /* CONFIG_DRIVER_RADIUS_ACL */
fe6bdb77 466#ifdef NEED_AP_MLME
6fc6879b
JM
467 /* Re-send original authentication frame for 802.11 processing */
468 wpa_printf(MSG_DEBUG, "Re-sending authentication frame after "
469 "successful RADIUS ACL query");
f8b1f695 470 ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL);
fe6bdb77 471#endif /* NEED_AP_MLME */
6affdaee 472#endif /* CONFIG_DRIVER_RADIUS_ACL */
6fc6879b
JM
473
474 done:
475 if (prev == NULL)
476 hapd->acl_queries = query->next;
477 else
478 prev->next = query->next;
479
480 hostapd_acl_query_free(query);
481
482 return RADIUS_RX_PROCESSED;
483}
f88bd288 484#endif /* CONFIG_NO_RADIUS */
6fc6879b
JM
485
486
1c6e69cc
JM
487/**
488 * hostapd_acl_init: Initialize IEEE 802.11 ACL
489 * @hapd: hostapd BSS data
490 * Returns: 0 on success, -1 on failure
491 */
6fc6879b
JM
492int hostapd_acl_init(struct hostapd_data *hapd)
493{
f88bd288 494#ifndef CONFIG_NO_RADIUS
6fc6879b
JM
495 if (radius_client_register(hapd->radius, RADIUS_AUTH,
496 hostapd_acl_recv_radius, hapd))
497 return -1;
498
499 eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
f88bd288 500#endif /* CONFIG_NO_RADIUS */
6fc6879b
JM
501
502 return 0;
503}
504
505
1c6e69cc
JM
506/**
507 * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL
508 * @hapd: hostapd BSS data
509 */
6fc6879b
JM
510void hostapd_acl_deinit(struct hostapd_data *hapd)
511{
512 struct hostapd_acl_query_data *query, *prev;
513
f88bd288 514#ifndef CONFIG_NO_RADIUS
6fc6879b
JM
515 eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL);
516
517 hostapd_acl_cache_free(hapd->acl_cache);
f88bd288 518#endif /* CONFIG_NO_RADIUS */
6fc6879b
JM
519
520 query = hapd->acl_queries;
521 while (query) {
522 prev = query;
523 query = query->next;
524 hostapd_acl_query_free(prev);
525 }
526}
527
6fc6879b 528#endif /* CONFIG_NATIVE_WINDOWS */