]> git.ipfire.org Git - thirdparty/hostap.git/blob - src/ap/ieee802_11_auth.c
51551002eadd2f5bfeb10b2dc8644d6f3959f6a8
[thirdparty/hostap.git] / src / ap / ieee802_11_auth.c
1 /*
2 * hostapd / IEEE 802.11 authentication (ACL)
3 * Copyright (c) 2003-2007, Jouni Malinen <j@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 "includes.h"
21
22 #ifndef CONFIG_NATIVE_WINDOWS
23
24 #include "common.h"
25 #include "hostapd.h"
26 #include "config.h"
27 #include "ieee802_11.h"
28 #include "ieee802_11_auth.h"
29 #include "radius/radius.h"
30 #include "radius/radius_client.h"
31 #include "eloop.h"
32
33 #define RADIUS_ACL_TIMEOUT 30
34
35
36 struct 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
47 struct 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
57 #ifndef CONFIG_NO_RADIUS
58 static 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
70 static 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)
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;
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 }
101 #endif /* CONFIG_NO_RADIUS */
102
103
104 static 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
113 #ifndef CONFIG_NO_RADIUS
114 static 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);
200 return -1;
201 }
202 #endif /* CONFIG_NO_RADIUS */
203
204
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 */
216 int 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 {
220 if (session_timeout)
221 *session_timeout = 0;
222 if (acct_interim_interval)
223 *acct_interim_interval = 0;
224 if (vlan_id)
225 *vlan_id = 0;
226
227 if (hostapd_maclist_found(hapd->conf->accept_mac,
228 hapd->conf->num_accept_mac, addr, vlan_id))
229 return HOSTAPD_ACL_ACCEPT;
230
231 if (hostapd_maclist_found(hapd->conf->deny_mac,
232 hapd->conf->num_deny_mac, addr, vlan_id))
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) {
241 #ifdef CONFIG_NO_RADIUS
242 return HOSTAPD_ACL_REJECT;
243 #else /* CONFIG_NO_RADIUS */
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;
299 #endif /* CONFIG_NO_RADIUS */
300 }
301
302 return HOSTAPD_ACL_REJECT;
303 }
304
305
306 #ifndef CONFIG_NO_RADIUS
307 static 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;
322 #ifdef CONFIG_DRIVER_RADIUS_ACL
323 hapd->drv.set_radius_acl_expire(hapd, entry->addr);
324 #endif /* CONFIG_DRIVER_RADIUS_ACL */
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
337 static 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
365 /**
366 * hostapd_acl_expire - ACL cache expiration callback
367 * @eloop_ctx: struct hostapd_data *
368 * @timeout_ctx: Not used
369 */
370 static 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
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 */
393 static RadiusRxResult
394 hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
395 const u8 *shared_secret, size_t shared_secret_len,
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;
401 struct radius_hdr *hdr = radius_msg_get_hdr(msg);
402
403 query = hapd->acl_queries;
404 prev = NULL;
405 while (query) {
406 if (query->radius_id == hdr->identifier)
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
423 if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
424 hdr->code != RADIUS_CODE_ACCESS_REJECT) {
425 wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
426 "query", hdr->code);
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));
438 if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
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
462 #ifdef CONFIG_DRIVER_RADIUS_ACL
463 hapd->drv.set_radius_acl_auth(hapd, query->addr, cache->accepted,
464 cache->session_timeout);
465 #else /* CONFIG_DRIVER_RADIUS_ACL */
466 #ifdef NEED_AP_MLME
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");
470 ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL);
471 #endif /* NEED_AP_MLME */
472 #endif /* CONFIG_DRIVER_RADIUS_ACL */
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 }
484 #endif /* CONFIG_NO_RADIUS */
485
486
487 /**
488 * hostapd_acl_init: Initialize IEEE 802.11 ACL
489 * @hapd: hostapd BSS data
490 * Returns: 0 on success, -1 on failure
491 */
492 int hostapd_acl_init(struct hostapd_data *hapd)
493 {
494 #ifndef CONFIG_NO_RADIUS
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);
500 #endif /* CONFIG_NO_RADIUS */
501
502 return 0;
503 }
504
505
506 /**
507 * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL
508 * @hapd: hostapd BSS data
509 */
510 void hostapd_acl_deinit(struct hostapd_data *hapd)
511 {
512 struct hostapd_acl_query_data *query, *prev;
513
514 #ifndef CONFIG_NO_RADIUS
515 eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL);
516
517 hostapd_acl_cache_free(hapd->acl_cache);
518 #endif /* CONFIG_NO_RADIUS */
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
528 #endif /* CONFIG_NATIVE_WINDOWS */