Use generic driver events for TX status and RX reporting
[mech_eap.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         os_free(msg);
204         return -1;
205 }
206 #endif /* CONFIG_NO_RADIUS */
207
208
209 /**
210  * hostapd_allowed_address - Check whether a specified STA can be authenticated
211  * @hapd: hostapd BSS data
212  * @addr: MAC address of the STA
213  * @msg: Authentication message
214  * @len: Length of msg in octets
215  * @session_timeout: Buffer for returning session timeout (from RADIUS)
216  * @acct_interim_interval: Buffer for returning account interval (from RADIUS)
217  * @vlan_id: Buffer for returning VLAN ID
218  * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
219  */
220 int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
221                             const u8 *msg, size_t len, u32 *session_timeout,
222                             u32 *acct_interim_interval, int *vlan_id)
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
231         if (hostapd_maclist_found(hapd->conf->accept_mac,
232                                   hapd->conf->num_accept_mac, addr, vlan_id))
233                 return HOSTAPD_ACL_ACCEPT;
234
235         if (hostapd_maclist_found(hapd->conf->deny_mac,
236                                   hapd->conf->num_deny_mac, addr, vlan_id))
237                 return HOSTAPD_ACL_REJECT;
238
239         if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
240                 return HOSTAPD_ACL_ACCEPT;
241         if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
242                 return HOSTAPD_ACL_REJECT;
243
244         if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
245 #ifdef CONFIG_NO_RADIUS
246                 return HOSTAPD_ACL_REJECT;
247 #else /* CONFIG_NO_RADIUS */
248                 struct hostapd_acl_query_data *query;
249
250                 /* Check whether ACL cache has an entry for this station */
251                 int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
252                                                 acct_interim_interval,
253                                                 vlan_id);
254                 if (res == HOSTAPD_ACL_ACCEPT ||
255                     res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
256                         return res;
257                 if (res == HOSTAPD_ACL_REJECT)
258                         return HOSTAPD_ACL_REJECT;
259
260                 query = hapd->acl_queries;
261                 while (query) {
262                         if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
263                                 /* pending query in RADIUS retransmit queue;
264                                  * do not generate a new one */
265                                 return HOSTAPD_ACL_PENDING;
266                         }
267                         query = query->next;
268                 }
269
270                 if (!hapd->conf->radius->auth_server)
271                         return HOSTAPD_ACL_REJECT;
272
273                 /* No entry in the cache - query external RADIUS server */
274                 query = os_zalloc(sizeof(*query));
275                 if (query == NULL) {
276                         wpa_printf(MSG_ERROR, "malloc for query data failed");
277                         return HOSTAPD_ACL_REJECT;
278                 }
279                 time(&query->timestamp);
280                 os_memcpy(query->addr, addr, ETH_ALEN);
281                 if (hostapd_radius_acl_query(hapd, addr, query)) {
282                         wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
283                                    "for ACL query.");
284                         hostapd_acl_query_free(query);
285                         return HOSTAPD_ACL_REJECT;
286                 }
287
288                 query->auth_msg = os_malloc(len);
289                 if (query->auth_msg == NULL) {
290                         wpa_printf(MSG_ERROR, "Failed to allocate memory for "
291                                    "auth frame.");
292                         hostapd_acl_query_free(query);
293                         return HOSTAPD_ACL_REJECT;
294                 }
295                 os_memcpy(query->auth_msg, msg, len);
296                 query->auth_msg_len = len;
297                 query->next = hapd->acl_queries;
298                 hapd->acl_queries = query;
299
300                 /* Queued data will be processed in hostapd_acl_recv_radius()
301                  * when RADIUS server replies to the sent Access-Request. */
302                 return HOSTAPD_ACL_PENDING;
303 #endif /* CONFIG_NO_RADIUS */
304         }
305
306         return HOSTAPD_ACL_REJECT;
307 }
308
309
310 #ifndef CONFIG_NO_RADIUS
311 static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now)
312 {
313         struct hostapd_cached_radius_acl *prev, *entry, *tmp;
314
315         prev = NULL;
316         entry = hapd->acl_cache;
317
318         while (entry) {
319                 if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
320                         wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
321                                    " has expired.", MAC2STR(entry->addr));
322                         if (prev)
323                                 prev->next = entry->next;
324                         else
325                                 hapd->acl_cache = entry->next;
326 #ifdef CONFIG_DRIVER_RADIUS_ACL
327                         hostapd_set_radius_acl_expire(hapd, entry->addr);
328 #endif /* CONFIG_DRIVER_RADIUS_ACL */
329                         tmp = entry;
330                         entry = entry->next;
331                         os_free(tmp);
332                         continue;
333                 }
334
335                 prev = entry;
336                 entry = entry->next;
337         }
338 }
339
340
341 static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now)
342 {
343         struct hostapd_acl_query_data *prev, *entry, *tmp;
344
345         prev = NULL;
346         entry = hapd->acl_queries;
347
348         while (entry) {
349                 if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
350                         wpa_printf(MSG_DEBUG, "ACL query for " MACSTR
351                                    " has expired.", MAC2STR(entry->addr));
352                         if (prev)
353                                 prev->next = entry->next;
354                         else
355                                 hapd->acl_queries = entry->next;
356
357                         tmp = entry;
358                         entry = entry->next;
359                         hostapd_acl_query_free(tmp);
360                         continue;
361                 }
362
363                 prev = entry;
364                 entry = entry->next;
365         }
366 }
367
368
369 /**
370  * hostapd_acl_expire - ACL cache expiration callback
371  * @eloop_ctx: struct hostapd_data *
372  * @timeout_ctx: Not used
373  */
374 static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
375 {
376         struct hostapd_data *hapd = eloop_ctx;
377         time_t now;
378
379         time(&now);
380         hostapd_acl_expire_cache(hapd, now);
381         hostapd_acl_expire_queries(hapd, now);
382
383         eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
384 }
385
386
387 /**
388  * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
389  * @msg: RADIUS response message
390  * @req: RADIUS request message
391  * @shared_secret: RADIUS shared secret
392  * @shared_secret_len: Length of shared_secret in octets
393  * @data: Context data (struct hostapd_data *)
394  * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and
395  * was processed here) or RADIUS_RX_UNKNOWN if not.
396  */
397 static RadiusRxResult
398 hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
399                         const u8 *shared_secret, size_t shared_secret_len,
400                         void *data)
401 {
402         struct hostapd_data *hapd = data;
403         struct hostapd_acl_query_data *query, *prev;
404         struct hostapd_cached_radius_acl *cache;
405
406         query = hapd->acl_queries;
407         prev = NULL;
408         while (query) {
409                 if (query->radius_id == msg->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 (msg->hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
427             msg->hdr->code != RADIUS_CODE_ACCESS_REJECT) {
428                 wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
429                            "query", msg->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 (msg->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 */