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