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