5ae3ac33719e2764b7cbffaf60e5b3b4a2368d6e
[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 "hostapd.h"
25 #include "config.h"
26 #include "ieee802_11.h"
27 #include "ieee802_11_auth.h"
28 #include "radius/radius.h"
29 #include "radius/radius_client.h"
30 #include "eloop.h"
31 #ifdef CONFIG_DRIVER_RADIUS_ACL
32 #include "driver_i.h"
33 #endif /* CONFIG_DRIVER_RADIUS_ACL */
34
35 #define RADIUS_ACL_TIMEOUT 30
36
37
38 struct hostapd_cached_radius_acl {
39         time_t timestamp;
40         macaddr addr;
41         int accepted; /* HOSTAPD_ACL_* */
42         struct hostapd_cached_radius_acl *next;
43         u32 session_timeout;
44         u32 acct_interim_interval;
45         int vlan_id;
46 };
47
48
49 struct hostapd_acl_query_data {
50         time_t timestamp;
51         u8 radius_id;
52         macaddr addr;
53         u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
54         size_t auth_msg_len;
55         struct hostapd_acl_query_data *next;
56 };
57
58
59 #ifndef CONFIG_NO_RADIUS
60 static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
61 {
62         struct hostapd_cached_radius_acl *prev;
63
64         while (acl_cache) {
65                 prev = acl_cache;
66                 acl_cache = acl_cache->next;
67                 os_free(prev);
68         }
69 }
70
71
72 static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
73                                  u32 *session_timeout,
74                                  u32 *acct_interim_interval, int *vlan_id)
75 {
76         struct hostapd_cached_radius_acl *entry;
77         time_t now;
78
79         time(&now);
80         entry = hapd->acl_cache;
81
82         while (entry) {
83                 if (os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
84                         if (now - entry->timestamp > RADIUS_ACL_TIMEOUT)
85                                 return -1; /* entry has expired */
86                         if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
87                                 if (session_timeout)
88                                         *session_timeout =
89                                                 entry->session_timeout;
90                         if (acct_interim_interval)
91                                 *acct_interim_interval =
92                                         entry->acct_interim_interval;
93                         if (vlan_id)
94                                 *vlan_id = entry->vlan_id;
95                         return entry->accepted;
96                 }
97
98                 entry = entry->next;
99         }
100
101         return -1;
102 }
103 #endif /* CONFIG_NO_RADIUS */
104
105
106 static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
107 {
108         if (query == NULL)
109                 return;
110         os_free(query->auth_msg);
111         os_free(query);
112 }
113
114
115 #ifndef CONFIG_NO_RADIUS
116 static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
117                                     struct hostapd_acl_query_data *query)
118 {
119         struct radius_msg *msg;
120         char buf[128];
121
122         query->radius_id = radius_client_get_id(hapd->radius);
123         msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
124         if (msg == NULL)
125                 return -1;
126
127         radius_msg_make_authenticator(msg, addr, ETH_ALEN);
128
129         os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
130         if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
131                                  os_strlen(buf))) {
132                 wpa_printf(MSG_DEBUG, "Could not add User-Name");
133                 goto fail;
134         }
135
136         if (!radius_msg_add_attr_user_password(
137                     msg, (u8 *) buf, os_strlen(buf),
138                     hapd->conf->radius->auth_server->shared_secret,
139                     hapd->conf->radius->auth_server->shared_secret_len)) {
140                 wpa_printf(MSG_DEBUG, "Could not add User-Password");
141                 goto fail;
142         }
143
144         if (hapd->conf->own_ip_addr.af == AF_INET &&
145             !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
146                                  (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
147                 wpa_printf(MSG_DEBUG, "Could not add NAS-IP-Address");
148                 goto fail;
149         }
150
151 #ifdef CONFIG_IPV6
152         if (hapd->conf->own_ip_addr.af == AF_INET6 &&
153             !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
154                                  (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
155                 wpa_printf(MSG_DEBUG, "Could not add NAS-IPv6-Address");
156                 goto fail;
157         }
158 #endif /* CONFIG_IPV6 */
159
160         if (hapd->conf->nas_identifier &&
161             !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
162                                  (u8 *) hapd->conf->nas_identifier,
163                                  os_strlen(hapd->conf->nas_identifier))) {
164                 wpa_printf(MSG_DEBUG, "Could not add NAS-Identifier");
165                 goto fail;
166         }
167
168         os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
169                     MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
170         if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
171                                  (u8 *) buf, os_strlen(buf))) {
172                 wpa_printf(MSG_DEBUG, "Could not add Called-Station-Id");
173                 goto fail;
174         }
175
176         os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
177                     MAC2STR(addr));
178         if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
179                                  (u8 *) buf, os_strlen(buf))) {
180                 wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id");
181                 goto fail;
182         }
183
184         if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
185                                        RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
186                 wpa_printf(MSG_DEBUG, "Could not add NAS-Port-Type");
187                 goto fail;
188         }
189
190         os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
191         if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
192                                  (u8 *) buf, os_strlen(buf))) {
193                 wpa_printf(MSG_DEBUG, "Could not add Connect-Info");
194                 goto fail;
195         }
196
197         radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr);
198         return 0;
199
200  fail:
201         radius_msg_free(msg);
202         os_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
405         query = hapd->acl_queries;
406         prev = NULL;
407         while (query) {
408                 if (query->radius_id == msg->hdr->identifier)
409                         break;
410                 prev = query;
411                 query = query->next;
412         }
413         if (query == NULL)
414                 return RADIUS_RX_UNKNOWN;
415
416         wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS "
417                    "message (id=%d)", query->radius_id);
418
419         if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
420                 wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have "
421                            "correct authenticator - dropped\n");
422                 return RADIUS_RX_INVALID_AUTHENTICATOR;
423         }
424
425         if (msg->hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
426             msg->hdr->code != RADIUS_CODE_ACCESS_REJECT) {
427                 wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
428                            "query", msg->hdr->code);
429                 return RADIUS_RX_UNKNOWN;
430         }
431
432         /* Insert Accept/Reject info into ACL cache */
433         cache = os_zalloc(sizeof(*cache));
434         if (cache == NULL) {
435                 wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
436                 goto done;
437         }
438         time(&cache->timestamp);
439         os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
440         if (msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
441                 if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
442                                               &cache->session_timeout) == 0)
443                         cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
444                 else
445                         cache->accepted = HOSTAPD_ACL_ACCEPT;
446
447                 if (radius_msg_get_attr_int32(
448                             msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
449                             &cache->acct_interim_interval) == 0 &&
450                     cache->acct_interim_interval < 60) {
451                         wpa_printf(MSG_DEBUG, "Ignored too small "
452                                    "Acct-Interim-Interval %d for STA " MACSTR,
453                                    cache->acct_interim_interval,
454                                    MAC2STR(query->addr));
455                         cache->acct_interim_interval = 0;
456                 }
457
458                 cache->vlan_id = radius_msg_get_vlanid(msg);
459         } else
460                 cache->accepted = HOSTAPD_ACL_REJECT;
461         cache->next = hapd->acl_cache;
462         hapd->acl_cache = cache;
463
464 #ifdef CONFIG_DRIVER_RADIUS_ACL
465         hostapd_set_radius_acl_auth(hapd, query->addr, cache->accepted,
466                                     cache->session_timeout);
467 #else /* CONFIG_DRIVER_RADIUS_ACL */
468 #ifdef NEED_MLME
469         /* Re-send original authentication frame for 802.11 processing */
470         wpa_printf(MSG_DEBUG, "Re-sending authentication frame after "
471                    "successful RADIUS ACL query");
472         ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len,
473                         WLAN_FC_STYPE_AUTH, NULL);
474 #endif /* NEED_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
532 int hostapd_acl_reconfig(struct hostapd_data *hapd,
533                          struct hostapd_config *oldconf)
534 {
535         if (!hapd->radius_client_reconfigured)
536                 return 0;
537
538         hostapd_acl_deinit(hapd);
539         return hostapd_acl_init(hapd);
540 }
541
542 #endif /* CONFIG_NATIVE_WINDOWS */