blob: a87ee745eb28446734069f070d493d98396b1f14 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * RADIUS client
Dmitry Shmidt203eadb2015-03-05 14:16:04 -08003 * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "radius.h"
13#include "radius_client.h"
14#include "eloop.h"
15
16/* Defaults for RADIUS retransmit values (exponential backoff) */
17
18/**
19 * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds
20 */
21#define RADIUS_CLIENT_FIRST_WAIT 3
22
23/**
24 * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds
25 */
26#define RADIUS_CLIENT_MAX_WAIT 120
27
28/**
29 * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries
30 *
31 * Maximum number of retransmit attempts before the entry is removed from
32 * retransmit list.
33 */
34#define RADIUS_CLIENT_MAX_RETRIES 10
35
36/**
37 * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages
38 *
39 * Maximum number of entries in retransmit list (oldest entries will be
40 * removed, if this limit is exceeded).
41 */
42#define RADIUS_CLIENT_MAX_ENTRIES 30
43
44/**
45 * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point
46 *
47 * The number of failed retry attempts after which the RADIUS server will be
48 * changed (if one of more backup servers are configured).
49 */
50#define RADIUS_CLIENT_NUM_FAILOVER 4
51
52
53/**
54 * struct radius_rx_handler - RADIUS client RX handler
55 *
56 * This data structure is used internally inside the RADIUS client module to
57 * store registered RX handlers. These handlers are registered by calls to
58 * radius_client_register() and unregistered when the RADIUS client is
59 * deinitialized with a call to radius_client_deinit().
60 */
61struct radius_rx_handler {
62 /**
63 * handler - Received RADIUS message handler
64 */
65 RadiusRxResult (*handler)(struct radius_msg *msg,
66 struct radius_msg *req,
67 const u8 *shared_secret,
68 size_t shared_secret_len,
69 void *data);
70
71 /**
72 * data - Context data for the handler
73 */
74 void *data;
75};
76
77
78/**
79 * struct radius_msg_list - RADIUS client message retransmit list
80 *
81 * This data structure is used internally inside the RADIUS client module to
82 * store pending RADIUS requests that may still need to be retransmitted.
83 */
84struct radius_msg_list {
85 /**
86 * addr - STA/client address
87 *
88 * This is used to find RADIUS messages for the same STA.
89 */
90 u8 addr[ETH_ALEN];
91
92 /**
93 * msg - RADIUS message
94 */
95 struct radius_msg *msg;
96
97 /**
98 * msg_type - Message type
99 */
100 RadiusType msg_type;
101
102 /**
103 * first_try - Time of the first transmission attempt
104 */
105 os_time_t first_try;
106
107 /**
108 * next_try - Time for the next transmission attempt
109 */
110 os_time_t next_try;
111
112 /**
113 * attempts - Number of transmission attempts
114 */
115 int attempts;
116
117 /**
118 * next_wait - Next retransmission wait time in seconds
119 */
120 int next_wait;
121
122 /**
123 * last_attempt - Time of the last transmission attempt
124 */
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800125 struct os_reltime last_attempt;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700126
127 /**
128 * shared_secret - Shared secret with the target RADIUS server
129 */
130 const u8 *shared_secret;
131
132 /**
133 * shared_secret_len - shared_secret length in octets
134 */
135 size_t shared_secret_len;
136
137 /* TODO: server config with failover to backup server(s) */
138
139 /**
140 * next - Next message in the list
141 */
142 struct radius_msg_list *next;
143};
144
145
146/**
147 * struct radius_client_data - Internal RADIUS client data
148 *
149 * This data structure is used internally inside the RADIUS client module.
150 * External users allocate this by calling radius_client_init() and free it by
151 * calling radius_client_deinit(). The pointer to this opaque data is used in
152 * calls to other functions as an identifier for the RADIUS client instance.
153 */
154struct radius_client_data {
155 /**
156 * ctx - Context pointer for hostapd_logger() callbacks
157 */
158 void *ctx;
159
160 /**
161 * conf - RADIUS client configuration (list of RADIUS servers to use)
162 */
163 struct hostapd_radius_servers *conf;
164
165 /**
166 * auth_serv_sock - IPv4 socket for RADIUS authentication messages
167 */
168 int auth_serv_sock;
169
170 /**
171 * acct_serv_sock - IPv4 socket for RADIUS accounting messages
172 */
173 int acct_serv_sock;
174
175 /**
176 * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages
177 */
178 int auth_serv_sock6;
179
180 /**
181 * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages
182 */
183 int acct_serv_sock6;
184
185 /**
186 * auth_sock - Currently used socket for RADIUS authentication server
187 */
188 int auth_sock;
189
190 /**
191 * acct_sock - Currently used socket for RADIUS accounting server
192 */
193 int acct_sock;
194
195 /**
196 * auth_handlers - Authentication message handlers
197 */
198 struct radius_rx_handler *auth_handlers;
199
200 /**
201 * num_auth_handlers - Number of handlers in auth_handlers
202 */
203 size_t num_auth_handlers;
204
205 /**
206 * acct_handlers - Accounting message handlers
207 */
208 struct radius_rx_handler *acct_handlers;
209
210 /**
211 * num_acct_handlers - Number of handlers in acct_handlers
212 */
213 size_t num_acct_handlers;
214
215 /**
216 * msgs - Pending outgoing RADIUS messages
217 */
218 struct radius_msg_list *msgs;
219
220 /**
221 * num_msgs - Number of pending messages in the msgs list
222 */
223 size_t num_msgs;
224
225 /**
226 * next_radius_identifier - Next RADIUS message identifier to use
227 */
228 u8 next_radius_identifier;
Dmitry Shmidt9c175262016-03-03 10:20:07 -0800229
230 /**
231 * interim_error_cb - Interim accounting error callback
232 */
233 void (*interim_error_cb)(const u8 *addr, void *ctx);
234
235 /**
236 * interim_error_cb_ctx - interim_error_cb() context data
237 */
238 void *interim_error_cb_ctx;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700239};
240
241
242static int
243radius_change_server(struct radius_client_data *radius,
244 struct hostapd_radius_server *nserv,
245 struct hostapd_radius_server *oserv,
246 int sock, int sock6, int auth);
247static int radius_client_init_acct(struct radius_client_data *radius);
248static int radius_client_init_auth(struct radius_client_data *radius);
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800249static void radius_client_auth_failover(struct radius_client_data *radius);
250static void radius_client_acct_failover(struct radius_client_data *radius);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700251
252
253static void radius_client_msg_free(struct radius_msg_list *req)
254{
255 radius_msg_free(req->msg);
256 os_free(req);
257}
258
259
260/**
261 * radius_client_register - Register a RADIUS client RX handler
262 * @radius: RADIUS client context from radius_client_init()
263 * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT)
264 * @handler: Handler for received RADIUS messages
265 * @data: Context pointer for handler callbacks
266 * Returns: 0 on success, -1 on failure
267 *
268 * This function is used to register a handler for processing received RADIUS
269 * authentication and accounting messages. The handler() callback function will
270 * be called whenever a RADIUS message is received from the active server.
271 *
272 * There can be multiple registered RADIUS message handlers. The handlers will
273 * be called in order until one of them indicates that it has processed or
274 * queued the message.
275 */
276int radius_client_register(struct radius_client_data *radius,
277 RadiusType msg_type,
278 RadiusRxResult (*handler)(struct radius_msg *msg,
279 struct radius_msg *req,
280 const u8 *shared_secret,
281 size_t shared_secret_len,
282 void *data),
283 void *data)
284{
285 struct radius_rx_handler **handlers, *newh;
286 size_t *num;
287
288 if (msg_type == RADIUS_ACCT) {
289 handlers = &radius->acct_handlers;
290 num = &radius->num_acct_handlers;
291 } else {
292 handlers = &radius->auth_handlers;
293 num = &radius->num_auth_handlers;
294 }
295
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700296 newh = os_realloc_array(*handlers, *num + 1,
297 sizeof(struct radius_rx_handler));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700298 if (newh == NULL)
299 return -1;
300
301 newh[*num].handler = handler;
302 newh[*num].data = data;
303 (*num)++;
304 *handlers = newh;
305
306 return 0;
307}
308
309
Dmitry Shmidt9c175262016-03-03 10:20:07 -0800310/**
311 * radius_client_set_interim_erro_cb - Register an interim acct error callback
312 * @radius: RADIUS client context from radius_client_init()
313 * @addr: Station address from the failed message
314 * @cb: Handler for interim accounting errors
315 * @ctx: Context pointer for handler callbacks
316 *
317 * This function is used to register a handler for processing failed
318 * transmission attempts of interim accounting update messages.
319 */
320void radius_client_set_interim_error_cb(struct radius_client_data *radius,
321 void (*cb)(const u8 *addr, void *ctx),
322 void *ctx)
323{
324 radius->interim_error_cb = cb;
325 radius->interim_error_cb_ctx = ctx;
326}
327
328
Dmitry Shmidt71757432014-06-02 13:50:35 -0700329/*
330 * Returns >0 if message queue was flushed (i.e., the message that triggered
331 * the error is not available anymore)
332 */
333static int radius_client_handle_send_error(struct radius_client_data *radius,
334 int s, RadiusType msg_type)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700335{
336#ifndef CONFIG_NATIVE_WINDOWS
337 int _errno = errno;
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800338 wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700339 if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
Dmitry Shmidt014a3ff2015-12-28 13:27:49 -0800340 _errno == EBADF || _errno == ENETUNREACH || _errno == EACCES) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700341 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
342 HOSTAPD_LEVEL_INFO,
343 "Send failed - maybe interface status changed -"
344 " try to connect again");
Dmitry Shmidt71757432014-06-02 13:50:35 -0700345 if (msg_type == RADIUS_ACCT ||
346 msg_type == RADIUS_ACCT_INTERIM) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700347 radius_client_init_acct(radius);
Dmitry Shmidt71757432014-06-02 13:50:35 -0700348 return 0;
349 } else {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700350 radius_client_init_auth(radius);
Dmitry Shmidt71757432014-06-02 13:50:35 -0700351 return 1;
352 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700353 }
354#endif /* CONFIG_NATIVE_WINDOWS */
Dmitry Shmidt71757432014-06-02 13:50:35 -0700355
356 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700357}
358
359
360static int radius_client_retransmit(struct radius_client_data *radius,
361 struct radius_msg_list *entry,
362 os_time_t now)
363{
364 struct hostapd_radius_servers *conf = radius->conf;
365 int s;
366 struct wpabuf *buf;
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800367 size_t prev_num_msgs;
Dmitry Shmidt9c175262016-03-03 10:20:07 -0800368 u8 *acct_delay_time;
369 size_t acct_delay_time_len;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700370
371 if (entry->msg_type == RADIUS_ACCT ||
372 entry->msg_type == RADIUS_ACCT_INTERIM) {
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800373 if (radius->acct_sock < 0)
374 radius_client_init_acct(radius);
375 if (radius->acct_sock < 0 && conf->num_acct_servers > 1) {
376 prev_num_msgs = radius->num_msgs;
377 radius_client_acct_failover(radius);
378 if (prev_num_msgs != radius->num_msgs)
379 return 0;
380 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700381 s = radius->acct_sock;
382 if (entry->attempts == 0)
383 conf->acct_server->requests++;
384 else {
385 conf->acct_server->timeouts++;
386 conf->acct_server->retransmissions++;
387 }
388 } else {
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800389 if (radius->auth_sock < 0)
390 radius_client_init_auth(radius);
391 if (radius->auth_sock < 0 && conf->num_auth_servers > 1) {
392 prev_num_msgs = radius->num_msgs;
393 radius_client_auth_failover(radius);
394 if (prev_num_msgs != radius->num_msgs)
395 return 0;
396 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700397 s = radius->auth_sock;
398 if (entry->attempts == 0)
399 conf->auth_server->requests++;
400 else {
401 conf->auth_server->timeouts++;
402 conf->auth_server->retransmissions++;
403 }
404 }
Dmitry Shmidt9c175262016-03-03 10:20:07 -0800405
406 if (entry->msg_type == RADIUS_ACCT_INTERIM) {
407 wpa_printf(MSG_DEBUG,
408 "RADIUS: Failed to transmit interim accounting update to "
409 MACSTR " - drop message and request a new update",
410 MAC2STR(entry->addr));
411 if (radius->interim_error_cb)
412 radius->interim_error_cb(entry->addr,
413 radius->interim_error_cb_ctx);
414 return 1;
415 }
416
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800417 if (s < 0) {
418 wpa_printf(MSG_INFO,
419 "RADIUS: No valid socket for retransmission");
420 return 1;
421 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700422
Dmitry Shmidt9c175262016-03-03 10:20:07 -0800423 if (entry->msg_type == RADIUS_ACCT &&
424 radius_msg_get_attr_ptr(entry->msg, RADIUS_ATTR_ACCT_DELAY_TIME,
425 &acct_delay_time, &acct_delay_time_len,
426 NULL) == 0 &&
427 acct_delay_time_len == 4) {
428 struct radius_hdr *hdr;
429 u32 delay_time;
430
431 /*
432 * Need to assign a new identifier since attribute contents
433 * changes.
434 */
435 hdr = radius_msg_get_hdr(entry->msg);
436 hdr->identifier = radius_client_get_id(radius);
437
438 /* Update Acct-Delay-Time to show wait time in queue */
439 delay_time = now - entry->first_try;
440 WPA_PUT_BE32(acct_delay_time, delay_time);
441
442 wpa_printf(MSG_DEBUG,
443 "RADIUS: Updated Acct-Delay-Time to %u for retransmission",
444 delay_time);
445 radius_msg_finish_acct(entry->msg, entry->shared_secret,
446 entry->shared_secret_len);
447 if (radius->conf->msg_dumps)
448 radius_msg_dump(entry->msg);
449 }
450
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700451 /* retransmit; remove entry if too many attempts */
452 entry->attempts++;
453 hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,
454 HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
455 radius_msg_get_hdr(entry->msg)->identifier);
456
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800457 os_get_reltime(&entry->last_attempt);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700458 buf = radius_msg_get_buf(entry->msg);
Dmitry Shmidt71757432014-06-02 13:50:35 -0700459 if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
460 if (radius_client_handle_send_error(radius, s, entry->msg_type)
461 > 0)
462 return 0;
463 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700464
465 entry->next_try = now + entry->next_wait;
466 entry->next_wait *= 2;
467 if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
468 entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
469 if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800470 wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700471 return 1;
472 }
473
474 return 0;
475}
476
477
478static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
479{
480 struct radius_client_data *radius = eloop_ctx;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800481 struct os_reltime now;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700482 os_time_t first;
483 struct radius_msg_list *entry, *prev, *tmp;
484 int auth_failover = 0, acct_failover = 0;
Dmitry Shmidt71757432014-06-02 13:50:35 -0700485 size_t prev_num_msgs;
486 int s;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700487
488 entry = radius->msgs;
489 if (!entry)
490 return;
491
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800492 os_get_reltime(&now);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700493 first = 0;
494
495 prev = NULL;
496 while (entry) {
Dmitry Shmidt71757432014-06-02 13:50:35 -0700497 prev_num_msgs = radius->num_msgs;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700498 if (now.sec >= entry->next_try &&
499 radius_client_retransmit(radius, entry, now.sec)) {
500 if (prev)
501 prev->next = entry->next;
502 else
503 radius->msgs = entry->next;
504
505 tmp = entry;
506 entry = entry->next;
507 radius_client_msg_free(tmp);
508 radius->num_msgs--;
509 continue;
510 }
511
Dmitry Shmidt71757432014-06-02 13:50:35 -0700512 if (prev_num_msgs != radius->num_msgs) {
513 wpa_printf(MSG_DEBUG,
514 "RADIUS: Message removed from queue - restart from beginning");
515 entry = radius->msgs;
516 prev = NULL;
517 continue;
518 }
519
520 s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock :
521 radius->acct_sock;
522 if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER ||
523 (s < 0 && entry->attempts > 0)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700524 if (entry->msg_type == RADIUS_ACCT ||
525 entry->msg_type == RADIUS_ACCT_INTERIM)
526 acct_failover++;
527 else
528 auth_failover++;
529 }
530
531 if (first == 0 || entry->next_try < first)
532 first = entry->next_try;
533
534 prev = entry;
535 entry = entry->next;
536 }
537
538 if (radius->msgs) {
539 if (first < now.sec)
540 first = now.sec;
541 eloop_register_timeout(first - now.sec, 0,
542 radius_client_timer, radius, NULL);
543 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
544 HOSTAPD_LEVEL_DEBUG, "Next RADIUS client "
545 "retransmit in %ld seconds",
546 (long int) (first - now.sec));
547 }
548
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800549 if (auth_failover)
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800550 radius_client_auth_failover(radius);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700551
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800552 if (acct_failover)
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800553 radius_client_acct_failover(radius);
554}
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700555
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800556
557static void radius_client_auth_failover(struct radius_client_data *radius)
558{
559 struct hostapd_radius_servers *conf = radius->conf;
560 struct hostapd_radius_server *next, *old;
561 struct radius_msg_list *entry;
562 char abuf[50];
563
564 old = conf->auth_server;
565 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
566 HOSTAPD_LEVEL_NOTICE,
567 "No response from Authentication server %s:%d - failover",
568 hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
569 old->port);
570
571 for (entry = radius->msgs; entry; entry = entry->next) {
572 if (entry->msg_type == RADIUS_AUTH)
573 old->timeouts++;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700574 }
575
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800576 next = old + 1;
577 if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
578 next = conf->auth_servers;
579 conf->auth_server = next;
580 radius_change_server(radius, next, old,
581 radius->auth_serv_sock,
582 radius->auth_serv_sock6, 1);
583}
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700584
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700585
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800586static void radius_client_acct_failover(struct radius_client_data *radius)
587{
588 struct hostapd_radius_servers *conf = radius->conf;
589 struct hostapd_radius_server *next, *old;
590 struct radius_msg_list *entry;
591 char abuf[50];
592
593 old = conf->acct_server;
594 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
595 HOSTAPD_LEVEL_NOTICE,
596 "No response from Accounting server %s:%d - failover",
597 hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
598 old->port);
599
600 for (entry = radius->msgs; entry; entry = entry->next) {
601 if (entry->msg_type == RADIUS_ACCT ||
602 entry->msg_type == RADIUS_ACCT_INTERIM)
603 old->timeouts++;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700604 }
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800605
606 next = old + 1;
607 if (next > &conf->acct_servers[conf->num_acct_servers - 1])
608 next = conf->acct_servers;
609 conf->acct_server = next;
610 radius_change_server(radius, next, old,
611 radius->acct_serv_sock,
612 radius->acct_serv_sock6, 0);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700613}
614
615
616static void radius_client_update_timeout(struct radius_client_data *radius)
617{
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800618 struct os_reltime now;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700619 os_time_t first;
620 struct radius_msg_list *entry;
621
622 eloop_cancel_timeout(radius_client_timer, radius, NULL);
623
624 if (radius->msgs == NULL) {
625 return;
626 }
627
628 first = 0;
629 for (entry = radius->msgs; entry; entry = entry->next) {
630 if (first == 0 || entry->next_try < first)
631 first = entry->next_try;
632 }
633
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800634 os_get_reltime(&now);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700635 if (first < now.sec)
636 first = now.sec;
637 eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
638 NULL);
639 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
640 HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
Dmitry Shmidt04949592012-07-19 12:16:46 -0700641 " %ld seconds", (long int) (first - now.sec));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700642}
643
644
645static void radius_client_list_add(struct radius_client_data *radius,
646 struct radius_msg *msg,
647 RadiusType msg_type,
648 const u8 *shared_secret,
649 size_t shared_secret_len, const u8 *addr)
650{
651 struct radius_msg_list *entry, *prev;
652
653 if (eloop_terminated()) {
654 /* No point in adding entries to retransmit queue since event
655 * loop has already been terminated. */
656 radius_msg_free(msg);
657 return;
658 }
659
660 entry = os_zalloc(sizeof(*entry));
661 if (entry == NULL) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800662 wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700663 radius_msg_free(msg);
664 return;
665 }
666
667 if (addr)
668 os_memcpy(entry->addr, addr, ETH_ALEN);
669 entry->msg = msg;
670 entry->msg_type = msg_type;
671 entry->shared_secret = shared_secret;
672 entry->shared_secret_len = shared_secret_len;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800673 os_get_reltime(&entry->last_attempt);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700674 entry->first_try = entry->last_attempt.sec;
675 entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
676 entry->attempts = 1;
677 entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
678 entry->next = radius->msgs;
679 radius->msgs = entry;
680 radius_client_update_timeout(radius);
681
682 if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800683 wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700684 prev = NULL;
685 while (entry->next) {
686 prev = entry;
687 entry = entry->next;
688 }
689 if (prev) {
690 prev->next = NULL;
691 radius_client_msg_free(entry);
692 }
693 } else
694 radius->num_msgs++;
695}
696
697
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700698/**
699 * radius_client_send - Send a RADIUS request
700 * @radius: RADIUS client context from radius_client_init()
701 * @msg: RADIUS message to be sent
702 * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM)
703 * @addr: MAC address of the device related to this message or %NULL
704 * Returns: 0 on success, -1 on failure
705 *
706 * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or
707 * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference
708 * between accounting and interim accounting messages is that the interim
Dmitry Shmidt9c175262016-03-03 10:20:07 -0800709 * message will not be retransmitted. Instead, a callback is used to indicate
710 * that the transmission failed for the specific station @addr so that a new
711 * interim accounting update message can be generated with up-to-date session
712 * data instead of trying to resend old information.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700713 *
714 * The message is added on the retransmission queue and will be retransmitted
715 * automatically until a response is received or maximum number of retries
Dmitry Shmidt9c175262016-03-03 10:20:07 -0800716 * (RADIUS_CLIENT_MAX_RETRIES) is reached. No such retries are used with
717 * RADIUS_ACCT_INTERIM, i.e., such a pending message is removed from the queue
718 * automatically on transmission failure.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700719 *
720 * The related device MAC address can be used to identify pending messages that
Dmitry Shmidt9c175262016-03-03 10:20:07 -0800721 * can be removed with radius_client_flush_auth().
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700722 */
723int radius_client_send(struct radius_client_data *radius,
724 struct radius_msg *msg, RadiusType msg_type,
725 const u8 *addr)
726{
727 struct hostapd_radius_servers *conf = radius->conf;
728 const u8 *shared_secret;
729 size_t shared_secret_len;
730 char *name;
731 int s, res;
732 struct wpabuf *buf;
733
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700734 if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800735 if (conf->acct_server && radius->acct_sock < 0)
736 radius_client_init_acct(radius);
737
Dmitry Shmidt2f74e362015-01-21 13:19:05 -0800738 if (conf->acct_server == NULL || radius->acct_sock < 0 ||
739 conf->acct_server->shared_secret == NULL) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700740 hostapd_logger(radius->ctx, NULL,
741 HOSTAPD_MODULE_RADIUS,
742 HOSTAPD_LEVEL_INFO,
743 "No accounting server configured");
744 return -1;
745 }
746 shared_secret = conf->acct_server->shared_secret;
747 shared_secret_len = conf->acct_server->shared_secret_len;
748 radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
749 name = "accounting";
750 s = radius->acct_sock;
751 conf->acct_server->requests++;
752 } else {
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800753 if (conf->auth_server && radius->auth_sock < 0)
754 radius_client_init_auth(radius);
755
Dmitry Shmidt2f74e362015-01-21 13:19:05 -0800756 if (conf->auth_server == NULL || radius->auth_sock < 0 ||
757 conf->auth_server->shared_secret == NULL) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700758 hostapd_logger(radius->ctx, NULL,
759 HOSTAPD_MODULE_RADIUS,
760 HOSTAPD_LEVEL_INFO,
761 "No authentication server configured");
762 return -1;
763 }
764 shared_secret = conf->auth_server->shared_secret;
765 shared_secret_len = conf->auth_server->shared_secret_len;
766 radius_msg_finish(msg, shared_secret, shared_secret_len);
767 name = "authentication";
768 s = radius->auth_sock;
769 conf->auth_server->requests++;
770 }
771
772 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
773 HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s "
774 "server", name);
775 if (conf->msg_dumps)
776 radius_msg_dump(msg);
777
778 buf = radius_msg_get_buf(msg);
779 res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0);
780 if (res < 0)
781 radius_client_handle_send_error(radius, s, msg_type);
782
783 radius_client_list_add(radius, msg, msg_type, shared_secret,
784 shared_secret_len, addr);
785
Dmitry Shmidt04949592012-07-19 12:16:46 -0700786 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700787}
788
789
790static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
791{
792 struct radius_client_data *radius = eloop_ctx;
793 struct hostapd_radius_servers *conf = radius->conf;
794 RadiusType msg_type = (RadiusType) sock_ctx;
795 int len, roundtrip;
796 unsigned char buf[3000];
797 struct radius_msg *msg;
798 struct radius_hdr *hdr;
799 struct radius_rx_handler *handlers;
800 size_t num_handlers, i;
801 struct radius_msg_list *req, *prev_req;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800802 struct os_reltime now;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700803 struct hostapd_radius_server *rconf;
804 int invalid_authenticator = 0;
805
806 if (msg_type == RADIUS_ACCT) {
807 handlers = radius->acct_handlers;
808 num_handlers = radius->num_acct_handlers;
809 rconf = conf->acct_server;
810 } else {
811 handlers = radius->auth_handlers;
812 num_handlers = radius->num_auth_handlers;
813 rconf = conf->auth_server;
814 }
815
816 len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
817 if (len < 0) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800818 wpa_printf(MSG_INFO, "recv[RADIUS]: %s", strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700819 return;
820 }
821 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
822 HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
823 "server", len);
824 if (len == sizeof(buf)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800825 wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700826 return;
827 }
828
829 msg = radius_msg_parse(buf, len);
830 if (msg == NULL) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800831 wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700832 rconf->malformed_responses++;
833 return;
834 }
835 hdr = radius_msg_get_hdr(msg);
836
837 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
838 HOSTAPD_LEVEL_DEBUG, "Received RADIUS message");
839 if (conf->msg_dumps)
840 radius_msg_dump(msg);
841
842 switch (hdr->code) {
843 case RADIUS_CODE_ACCESS_ACCEPT:
844 rconf->access_accepts++;
845 break;
846 case RADIUS_CODE_ACCESS_REJECT:
847 rconf->access_rejects++;
848 break;
849 case RADIUS_CODE_ACCESS_CHALLENGE:
850 rconf->access_challenges++;
851 break;
852 case RADIUS_CODE_ACCOUNTING_RESPONSE:
853 rconf->responses++;
854 break;
855 }
856
857 prev_req = NULL;
858 req = radius->msgs;
859 while (req) {
860 /* TODO: also match by src addr:port of the packet when using
861 * alternative RADIUS servers (?) */
862 if ((req->msg_type == msg_type ||
863 (req->msg_type == RADIUS_ACCT_INTERIM &&
864 msg_type == RADIUS_ACCT)) &&
865 radius_msg_get_hdr(req->msg)->identifier ==
866 hdr->identifier)
867 break;
868
869 prev_req = req;
870 req = req->next;
871 }
872
873 if (req == NULL) {
874 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
875 HOSTAPD_LEVEL_DEBUG,
876 "No matching RADIUS request found (type=%d "
877 "id=%d) - dropping packet",
878 msg_type, hdr->identifier);
879 goto fail;
880 }
881
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800882 os_get_reltime(&now);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700883 roundtrip = (now.sec - req->last_attempt.sec) * 100 +
884 (now.usec - req->last_attempt.usec) / 10000;
885 hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
886 HOSTAPD_LEVEL_DEBUG,
887 "Received RADIUS packet matched with a pending "
888 "request, round trip time %d.%02d sec",
889 roundtrip / 100, roundtrip % 100);
890 rconf->round_trip_time = roundtrip;
891
892 /* Remove ACKed RADIUS packet from retransmit list */
893 if (prev_req)
894 prev_req->next = req->next;
895 else
896 radius->msgs = req->next;
897 radius->num_msgs--;
898
899 for (i = 0; i < num_handlers; i++) {
900 RadiusRxResult res;
901 res = handlers[i].handler(msg, req->msg, req->shared_secret,
902 req->shared_secret_len,
903 handlers[i].data);
904 switch (res) {
905 case RADIUS_RX_PROCESSED:
906 radius_msg_free(msg);
Roshan Pius3a1667e2018-07-03 15:17:14 -0700907 /* fall through */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700908 case RADIUS_RX_QUEUED:
909 radius_client_msg_free(req);
910 return;
911 case RADIUS_RX_INVALID_AUTHENTICATOR:
912 invalid_authenticator++;
Roshan Pius3a1667e2018-07-03 15:17:14 -0700913 /* fall through */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700914 case RADIUS_RX_UNKNOWN:
915 /* continue with next handler */
916 break;
917 }
918 }
919
920 if (invalid_authenticator)
921 rconf->bad_authenticators++;
922 else
923 rconf->unknown_types++;
924 hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
925 HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found "
926 "(type=%d code=%d id=%d)%s - dropping packet",
927 msg_type, hdr->code, hdr->identifier,
928 invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
929 "");
930 radius_client_msg_free(req);
931
932 fail:
933 radius_msg_free(msg);
934}
935
936
937/**
938 * radius_client_get_id - Get an identifier for a new RADIUS message
939 * @radius: RADIUS client context from radius_client_init()
940 * Returns: Allocated identifier
941 *
942 * This function is used to fetch a unique (among pending requests) identifier
943 * for a new RADIUS message.
944 */
945u8 radius_client_get_id(struct radius_client_data *radius)
946{
947 struct radius_msg_list *entry, *prev, *_remove;
948 u8 id = radius->next_radius_identifier++;
949
950 /* remove entries with matching id from retransmit list to avoid
951 * using new reply from the RADIUS server with an old request */
952 entry = radius->msgs;
953 prev = NULL;
954 while (entry) {
955 if (radius_msg_get_hdr(entry->msg)->identifier == id) {
956 hostapd_logger(radius->ctx, entry->addr,
957 HOSTAPD_MODULE_RADIUS,
958 HOSTAPD_LEVEL_DEBUG,
959 "Removing pending RADIUS message, "
960 "since its id (%d) is reused", id);
961 if (prev)
962 prev->next = entry->next;
963 else
964 radius->msgs = entry->next;
965 _remove = entry;
966 } else {
967 _remove = NULL;
968 prev = entry;
969 }
970 entry = entry->next;
971
972 if (_remove)
973 radius_client_msg_free(_remove);
974 }
975
976 return id;
977}
978
979
980/**
981 * radius_client_flush - Flush all pending RADIUS client messages
982 * @radius: RADIUS client context from radius_client_init()
983 * @only_auth: Whether only authentication messages are removed
984 */
985void radius_client_flush(struct radius_client_data *radius, int only_auth)
986{
987 struct radius_msg_list *entry, *prev, *tmp;
988
989 if (!radius)
990 return;
991
992 prev = NULL;
993 entry = radius->msgs;
994
995 while (entry) {
996 if (!only_auth || entry->msg_type == RADIUS_AUTH) {
997 if (prev)
998 prev->next = entry->next;
999 else
1000 radius->msgs = entry->next;
1001
1002 tmp = entry;
1003 entry = entry->next;
1004 radius_client_msg_free(tmp);
1005 radius->num_msgs--;
1006 } else {
1007 prev = entry;
1008 entry = entry->next;
1009 }
1010 }
1011
1012 if (radius->msgs == NULL)
1013 eloop_cancel_timeout(radius_client_timer, radius, NULL);
1014}
1015
1016
1017static void radius_client_update_acct_msgs(struct radius_client_data *radius,
1018 const u8 *shared_secret,
1019 size_t shared_secret_len)
1020{
1021 struct radius_msg_list *entry;
1022
1023 if (!radius)
1024 return;
1025
1026 for (entry = radius->msgs; entry; entry = entry->next) {
1027 if (entry->msg_type == RADIUS_ACCT) {
1028 entry->shared_secret = shared_secret;
1029 entry->shared_secret_len = shared_secret_len;
1030 radius_msg_finish_acct(entry->msg, shared_secret,
1031 shared_secret_len);
1032 }
1033 }
1034}
1035
1036
1037static int
1038radius_change_server(struct radius_client_data *radius,
1039 struct hostapd_radius_server *nserv,
1040 struct hostapd_radius_server *oserv,
1041 int sock, int sock6, int auth)
1042{
1043 struct sockaddr_in serv, claddr;
1044#ifdef CONFIG_IPV6
1045 struct sockaddr_in6 serv6, claddr6;
1046#endif /* CONFIG_IPV6 */
1047 struct sockaddr *addr, *cl_addr;
1048 socklen_t addrlen, claddrlen;
1049 char abuf[50];
1050 int sel_sock;
1051 struct radius_msg_list *entry;
1052 struct hostapd_radius_servers *conf = radius->conf;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001053 struct sockaddr_in disconnect_addr = {
1054 .sin_family = AF_UNSPEC,
1055 };
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001056
1057 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
1058 HOSTAPD_LEVEL_INFO,
1059 "%s server %s:%d",
1060 auth ? "Authentication" : "Accounting",
1061 hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
1062 nserv->port);
1063
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001064 if (oserv && oserv == nserv) {
1065 /* Reconnect to same server, flush */
1066 if (auth)
1067 radius_client_flush(radius, 1);
1068 }
1069
Dmitry Shmidt71757432014-06-02 13:50:35 -07001070 if (oserv && oserv != nserv &&
1071 (nserv->shared_secret_len != oserv->shared_secret_len ||
1072 os_memcmp(nserv->shared_secret, oserv->shared_secret,
1073 nserv->shared_secret_len) != 0)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001074 /* Pending RADIUS packets used different shared secret, so
1075 * they need to be modified. Update accounting message
1076 * authenticators here. Authentication messages are removed
1077 * since they would require more changes and the new RADIUS
1078 * server may not be prepared to receive them anyway due to
1079 * missing state information. Client will likely retry
1080 * authentication, so this should not be an issue. */
1081 if (auth)
1082 radius_client_flush(radius, 1);
1083 else {
1084 radius_client_update_acct_msgs(
1085 radius, nserv->shared_secret,
1086 nserv->shared_secret_len);
1087 }
1088 }
1089
1090 /* Reset retry counters for the new server */
Dmitry Shmidt71757432014-06-02 13:50:35 -07001091 for (entry = radius->msgs; oserv && oserv != nserv && entry;
1092 entry = entry->next) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001093 if ((auth && entry->msg_type != RADIUS_AUTH) ||
1094 (!auth && entry->msg_type != RADIUS_ACCT))
1095 continue;
1096 entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
1097 entry->attempts = 0;
1098 entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
1099 }
1100
1101 if (radius->msgs) {
1102 eloop_cancel_timeout(radius_client_timer, radius, NULL);
1103 eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
1104 radius_client_timer, radius, NULL);
1105 }
1106
1107 switch (nserv->addr.af) {
1108 case AF_INET:
1109 os_memset(&serv, 0, sizeof(serv));
1110 serv.sin_family = AF_INET;
1111 serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr;
1112 serv.sin_port = htons(nserv->port);
1113 addr = (struct sockaddr *) &serv;
1114 addrlen = sizeof(serv);
1115 sel_sock = sock;
1116 break;
1117#ifdef CONFIG_IPV6
1118 case AF_INET6:
1119 os_memset(&serv6, 0, sizeof(serv6));
1120 serv6.sin6_family = AF_INET6;
1121 os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6,
1122 sizeof(struct in6_addr));
1123 serv6.sin6_port = htons(nserv->port);
1124 addr = (struct sockaddr *) &serv6;
1125 addrlen = sizeof(serv6);
1126 sel_sock = sock6;
1127 break;
1128#endif /* CONFIG_IPV6 */
1129 default:
1130 return -1;
1131 }
1132
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001133 if (sel_sock < 0) {
1134 wpa_printf(MSG_INFO,
1135 "RADIUS: No server socket available (af=%d sock=%d sock6=%d auth=%d",
1136 nserv->addr.af, sock, sock6, auth);
1137 return -1;
1138 }
1139
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001140 if (conf->force_client_addr) {
1141 switch (conf->client_addr.af) {
1142 case AF_INET:
1143 os_memset(&claddr, 0, sizeof(claddr));
1144 claddr.sin_family = AF_INET;
1145 claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr;
1146 claddr.sin_port = htons(0);
1147 cl_addr = (struct sockaddr *) &claddr;
1148 claddrlen = sizeof(claddr);
1149 break;
1150#ifdef CONFIG_IPV6
1151 case AF_INET6:
1152 os_memset(&claddr6, 0, sizeof(claddr6));
1153 claddr6.sin6_family = AF_INET6;
1154 os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6,
1155 sizeof(struct in6_addr));
1156 claddr6.sin6_port = htons(0);
1157 cl_addr = (struct sockaddr *) &claddr6;
1158 claddrlen = sizeof(claddr6);
1159 break;
1160#endif /* CONFIG_IPV6 */
1161 default:
1162 return -1;
1163 }
1164
1165 if (bind(sel_sock, cl_addr, claddrlen) < 0) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001166 wpa_printf(MSG_INFO, "bind[radius]: %s",
1167 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001168 return -1;
1169 }
1170 }
1171
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001172 /* Force a reconnect by disconnecting the socket first */
1173 if (connect(sel_sock, (struct sockaddr *) &disconnect_addr,
1174 sizeof(disconnect_addr)) < 0)
1175 wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno));
1176
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001177 if (connect(sel_sock, addr, addrlen) < 0) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001178 wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001179 return -1;
1180 }
1181
1182#ifndef CONFIG_NATIVE_WINDOWS
1183 switch (nserv->addr.af) {
1184 case AF_INET:
1185 claddrlen = sizeof(claddr);
Dmitry Shmidt661b4f72014-09-29 14:58:27 -07001186 if (getsockname(sel_sock, (struct sockaddr *) &claddr,
1187 &claddrlen) == 0) {
1188 wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
1189 inet_ntoa(claddr.sin_addr),
1190 ntohs(claddr.sin_port));
1191 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001192 break;
1193#ifdef CONFIG_IPV6
1194 case AF_INET6: {
1195 claddrlen = sizeof(claddr6);
Dmitry Shmidt661b4f72014-09-29 14:58:27 -07001196 if (getsockname(sel_sock, (struct sockaddr *) &claddr6,
1197 &claddrlen) == 0) {
1198 wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
1199 inet_ntop(AF_INET6, &claddr6.sin6_addr,
1200 abuf, sizeof(abuf)),
1201 ntohs(claddr6.sin6_port));
1202 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001203 break;
1204 }
1205#endif /* CONFIG_IPV6 */
1206 }
1207#endif /* CONFIG_NATIVE_WINDOWS */
1208
1209 if (auth)
1210 radius->auth_sock = sel_sock;
1211 else
1212 radius->acct_sock = sel_sock;
1213
1214 return 0;
1215}
1216
1217
1218static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
1219{
1220 struct radius_client_data *radius = eloop_ctx;
1221 struct hostapd_radius_servers *conf = radius->conf;
1222 struct hostapd_radius_server *oserv;
1223
1224 if (radius->auth_sock >= 0 && conf->auth_servers &&
1225 conf->auth_server != conf->auth_servers) {
1226 oserv = conf->auth_server;
1227 conf->auth_server = conf->auth_servers;
Dmitry Shmidt203eadb2015-03-05 14:16:04 -08001228 if (radius_change_server(radius, conf->auth_server, oserv,
1229 radius->auth_serv_sock,
1230 radius->auth_serv_sock6, 1) < 0) {
1231 conf->auth_server = oserv;
1232 radius_change_server(radius, oserv, conf->auth_server,
1233 radius->auth_serv_sock,
1234 radius->auth_serv_sock6, 1);
1235 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001236 }
1237
1238 if (radius->acct_sock >= 0 && conf->acct_servers &&
1239 conf->acct_server != conf->acct_servers) {
1240 oserv = conf->acct_server;
1241 conf->acct_server = conf->acct_servers;
Dmitry Shmidt203eadb2015-03-05 14:16:04 -08001242 if (radius_change_server(radius, conf->acct_server, oserv,
1243 radius->acct_serv_sock,
1244 radius->acct_serv_sock6, 0) < 0) {
1245 conf->acct_server = oserv;
1246 radius_change_server(radius, oserv, conf->acct_server,
1247 radius->acct_serv_sock,
1248 radius->acct_serv_sock6, 0);
1249 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001250 }
1251
1252 if (conf->retry_primary_interval)
1253 eloop_register_timeout(conf->retry_primary_interval, 0,
1254 radius_retry_primary_timer, radius,
1255 NULL);
1256}
1257
1258
1259static int radius_client_disable_pmtu_discovery(int s)
1260{
1261 int r = -1;
1262#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
1263 /* Turn off Path MTU discovery on IPv4/UDP sockets. */
1264 int action = IP_PMTUDISC_DONT;
1265 r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
1266 sizeof(action));
1267 if (r == -1)
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001268 wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s",
1269 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001270#endif
1271 return r;
1272}
1273
1274
Dmitry Shmidt71757432014-06-02 13:50:35 -07001275static void radius_close_auth_sockets(struct radius_client_data *radius)
1276{
1277 radius->auth_sock = -1;
1278
1279 if (radius->auth_serv_sock >= 0) {
1280 eloop_unregister_read_sock(radius->auth_serv_sock);
1281 close(radius->auth_serv_sock);
1282 radius->auth_serv_sock = -1;
1283 }
1284#ifdef CONFIG_IPV6
1285 if (radius->auth_serv_sock6 >= 0) {
1286 eloop_unregister_read_sock(radius->auth_serv_sock6);
1287 close(radius->auth_serv_sock6);
1288 radius->auth_serv_sock6 = -1;
1289 }
1290#endif /* CONFIG_IPV6 */
1291}
1292
1293
1294static void radius_close_acct_sockets(struct radius_client_data *radius)
1295{
1296 radius->acct_sock = -1;
1297
1298 if (radius->acct_serv_sock >= 0) {
1299 eloop_unregister_read_sock(radius->acct_serv_sock);
1300 close(radius->acct_serv_sock);
1301 radius->acct_serv_sock = -1;
1302 }
1303#ifdef CONFIG_IPV6
1304 if (radius->acct_serv_sock6 >= 0) {
1305 eloop_unregister_read_sock(radius->acct_serv_sock6);
1306 close(radius->acct_serv_sock6);
1307 radius->acct_serv_sock6 = -1;
1308 }
1309#endif /* CONFIG_IPV6 */
1310}
1311
1312
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001313static int radius_client_init_auth(struct radius_client_data *radius)
1314{
1315 struct hostapd_radius_servers *conf = radius->conf;
1316 int ok = 0;
1317
Dmitry Shmidt71757432014-06-02 13:50:35 -07001318 radius_close_auth_sockets(radius);
1319
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001320 radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
1321 if (radius->auth_serv_sock < 0)
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001322 wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
1323 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001324 else {
1325 radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
1326 ok++;
1327 }
1328
1329#ifdef CONFIG_IPV6
1330 radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
1331 if (radius->auth_serv_sock6 < 0)
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001332 wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
1333 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001334 else
1335 ok++;
1336#endif /* CONFIG_IPV6 */
1337
1338 if (ok == 0)
1339 return -1;
1340
1341 radius_change_server(radius, conf->auth_server, NULL,
1342 radius->auth_serv_sock, radius->auth_serv_sock6,
1343 1);
1344
1345 if (radius->auth_serv_sock >= 0 &&
1346 eloop_register_read_sock(radius->auth_serv_sock,
1347 radius_client_receive, radius,
1348 (void *) RADIUS_AUTH)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001349 wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
Dmitry Shmidt71757432014-06-02 13:50:35 -07001350 radius_close_auth_sockets(radius);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001351 return -1;
1352 }
1353
1354#ifdef CONFIG_IPV6
1355 if (radius->auth_serv_sock6 >= 0 &&
1356 eloop_register_read_sock(radius->auth_serv_sock6,
1357 radius_client_receive, radius,
1358 (void *) RADIUS_AUTH)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001359 wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
Dmitry Shmidt71757432014-06-02 13:50:35 -07001360 radius_close_auth_sockets(radius);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001361 return -1;
1362 }
1363#endif /* CONFIG_IPV6 */
1364
1365 return 0;
1366}
1367
1368
1369static int radius_client_init_acct(struct radius_client_data *radius)
1370{
1371 struct hostapd_radius_servers *conf = radius->conf;
1372 int ok = 0;
1373
Dmitry Shmidt71757432014-06-02 13:50:35 -07001374 radius_close_acct_sockets(radius);
1375
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001376 radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
1377 if (radius->acct_serv_sock < 0)
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001378 wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
1379 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001380 else {
1381 radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
1382 ok++;
1383 }
1384
1385#ifdef CONFIG_IPV6
1386 radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
1387 if (radius->acct_serv_sock6 < 0)
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001388 wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
1389 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001390 else
1391 ok++;
1392#endif /* CONFIG_IPV6 */
1393
1394 if (ok == 0)
1395 return -1;
1396
1397 radius_change_server(radius, conf->acct_server, NULL,
1398 radius->acct_serv_sock, radius->acct_serv_sock6,
1399 0);
1400
1401 if (radius->acct_serv_sock >= 0 &&
1402 eloop_register_read_sock(radius->acct_serv_sock,
1403 radius_client_receive, radius,
1404 (void *) RADIUS_ACCT)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001405 wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
Dmitry Shmidt71757432014-06-02 13:50:35 -07001406 radius_close_acct_sockets(radius);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001407 return -1;
1408 }
1409
1410#ifdef CONFIG_IPV6
1411 if (radius->acct_serv_sock6 >= 0 &&
1412 eloop_register_read_sock(radius->acct_serv_sock6,
1413 radius_client_receive, radius,
1414 (void *) RADIUS_ACCT)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001415 wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
Dmitry Shmidt71757432014-06-02 13:50:35 -07001416 radius_close_acct_sockets(radius);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001417 return -1;
1418 }
1419#endif /* CONFIG_IPV6 */
1420
1421 return 0;
1422}
1423
1424
1425/**
1426 * radius_client_init - Initialize RADIUS client
1427 * @ctx: Callback context to be used in hostapd_logger() calls
1428 * @conf: RADIUS client configuration (RADIUS servers)
1429 * Returns: Pointer to private RADIUS client context or %NULL on failure
1430 *
1431 * The caller is responsible for keeping the configuration data available for
1432 * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is
1433 * called for the returned context pointer.
1434 */
1435struct radius_client_data *
1436radius_client_init(void *ctx, struct hostapd_radius_servers *conf)
1437{
1438 struct radius_client_data *radius;
1439
1440 radius = os_zalloc(sizeof(struct radius_client_data));
1441 if (radius == NULL)
1442 return NULL;
1443
1444 radius->ctx = ctx;
1445 radius->conf = conf;
1446 radius->auth_serv_sock = radius->acct_serv_sock =
1447 radius->auth_serv_sock6 = radius->acct_serv_sock6 =
1448 radius->auth_sock = radius->acct_sock = -1;
1449
1450 if (conf->auth_server && radius_client_init_auth(radius)) {
1451 radius_client_deinit(radius);
1452 return NULL;
1453 }
1454
1455 if (conf->acct_server && radius_client_init_acct(radius)) {
1456 radius_client_deinit(radius);
1457 return NULL;
1458 }
1459
1460 if (conf->retry_primary_interval)
1461 eloop_register_timeout(conf->retry_primary_interval, 0,
1462 radius_retry_primary_timer, radius,
1463 NULL);
1464
1465 return radius;
1466}
1467
1468
1469/**
1470 * radius_client_deinit - Deinitialize RADIUS client
1471 * @radius: RADIUS client context from radius_client_init()
1472 */
1473void radius_client_deinit(struct radius_client_data *radius)
1474{
1475 if (!radius)
1476 return;
1477
Dmitry Shmidt71757432014-06-02 13:50:35 -07001478 radius_close_auth_sockets(radius);
1479 radius_close_acct_sockets(radius);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001480
1481 eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
1482
1483 radius_client_flush(radius, 0);
1484 os_free(radius->auth_handlers);
1485 os_free(radius->acct_handlers);
1486 os_free(radius);
1487}
1488
1489
1490/**
1491 * radius_client_flush_auth - Flush pending RADIUS messages for an address
1492 * @radius: RADIUS client context from radius_client_init()
1493 * @addr: MAC address of the related device
1494 *
1495 * This function can be used to remove pending RADIUS authentication messages
1496 * that are related to a specific device. The addr parameter is matched with
1497 * the one used in radius_client_send() call that was used to transmit the
1498 * authentication request.
1499 */
1500void radius_client_flush_auth(struct radius_client_data *radius,
1501 const u8 *addr)
1502{
1503 struct radius_msg_list *entry, *prev, *tmp;
1504
1505 prev = NULL;
1506 entry = radius->msgs;
1507 while (entry) {
1508 if (entry->msg_type == RADIUS_AUTH &&
1509 os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
1510 hostapd_logger(radius->ctx, addr,
1511 HOSTAPD_MODULE_RADIUS,
1512 HOSTAPD_LEVEL_DEBUG,
1513 "Removing pending RADIUS authentication"
1514 " message for removed client");
1515
1516 if (prev)
1517 prev->next = entry->next;
1518 else
1519 radius->msgs = entry->next;
1520
1521 tmp = entry;
1522 entry = entry->next;
1523 radius_client_msg_free(tmp);
1524 radius->num_msgs--;
1525 continue;
1526 }
1527
1528 prev = entry;
1529 entry = entry->next;
1530 }
1531}
1532
1533
1534static int radius_client_dump_auth_server(char *buf, size_t buflen,
1535 struct hostapd_radius_server *serv,
1536 struct radius_client_data *cli)
1537{
1538 int pending = 0;
1539 struct radius_msg_list *msg;
1540 char abuf[50];
1541
1542 if (cli) {
1543 for (msg = cli->msgs; msg; msg = msg->next) {
1544 if (msg->msg_type == RADIUS_AUTH)
1545 pending++;
1546 }
1547 }
1548
1549 return os_snprintf(buf, buflen,
1550 "radiusAuthServerIndex=%d\n"
1551 "radiusAuthServerAddress=%s\n"
1552 "radiusAuthClientServerPortNumber=%d\n"
1553 "radiusAuthClientRoundTripTime=%d\n"
1554 "radiusAuthClientAccessRequests=%u\n"
1555 "radiusAuthClientAccessRetransmissions=%u\n"
1556 "radiusAuthClientAccessAccepts=%u\n"
1557 "radiusAuthClientAccessRejects=%u\n"
1558 "radiusAuthClientAccessChallenges=%u\n"
1559 "radiusAuthClientMalformedAccessResponses=%u\n"
1560 "radiusAuthClientBadAuthenticators=%u\n"
1561 "radiusAuthClientPendingRequests=%u\n"
1562 "radiusAuthClientTimeouts=%u\n"
1563 "radiusAuthClientUnknownTypes=%u\n"
1564 "radiusAuthClientPacketsDropped=%u\n",
1565 serv->index,
1566 hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
1567 serv->port,
1568 serv->round_trip_time,
1569 serv->requests,
1570 serv->retransmissions,
1571 serv->access_accepts,
1572 serv->access_rejects,
1573 serv->access_challenges,
1574 serv->malformed_responses,
1575 serv->bad_authenticators,
1576 pending,
1577 serv->timeouts,
1578 serv->unknown_types,
1579 serv->packets_dropped);
1580}
1581
1582
1583static int radius_client_dump_acct_server(char *buf, size_t buflen,
1584 struct hostapd_radius_server *serv,
1585 struct radius_client_data *cli)
1586{
1587 int pending = 0;
1588 struct radius_msg_list *msg;
1589 char abuf[50];
1590
1591 if (cli) {
1592 for (msg = cli->msgs; msg; msg = msg->next) {
1593 if (msg->msg_type == RADIUS_ACCT ||
1594 msg->msg_type == RADIUS_ACCT_INTERIM)
1595 pending++;
1596 }
1597 }
1598
1599 return os_snprintf(buf, buflen,
1600 "radiusAccServerIndex=%d\n"
1601 "radiusAccServerAddress=%s\n"
1602 "radiusAccClientServerPortNumber=%d\n"
1603 "radiusAccClientRoundTripTime=%d\n"
1604 "radiusAccClientRequests=%u\n"
1605 "radiusAccClientRetransmissions=%u\n"
1606 "radiusAccClientResponses=%u\n"
1607 "radiusAccClientMalformedResponses=%u\n"
1608 "radiusAccClientBadAuthenticators=%u\n"
1609 "radiusAccClientPendingRequests=%u\n"
1610 "radiusAccClientTimeouts=%u\n"
1611 "radiusAccClientUnknownTypes=%u\n"
1612 "radiusAccClientPacketsDropped=%u\n",
1613 serv->index,
1614 hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
1615 serv->port,
1616 serv->round_trip_time,
1617 serv->requests,
1618 serv->retransmissions,
1619 serv->responses,
1620 serv->malformed_responses,
1621 serv->bad_authenticators,
1622 pending,
1623 serv->timeouts,
1624 serv->unknown_types,
1625 serv->packets_dropped);
1626}
1627
1628
1629/**
1630 * radius_client_get_mib - Get RADIUS client MIB information
1631 * @radius: RADIUS client context from radius_client_init()
1632 * @buf: Buffer for returning MIB data in text format
1633 * @buflen: Maximum buf length in octets
1634 * Returns: Number of octets written into the buffer
1635 */
1636int radius_client_get_mib(struct radius_client_data *radius, char *buf,
1637 size_t buflen)
1638{
Dmitry Shmidt7d175302016-09-06 13:11:34 -07001639 struct hostapd_radius_servers *conf;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001640 int i;
1641 struct hostapd_radius_server *serv;
1642 int count = 0;
1643
Dmitry Shmidt7d175302016-09-06 13:11:34 -07001644 if (!radius)
1645 return 0;
1646
1647 conf = radius->conf;
1648
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001649 if (conf->auth_servers) {
1650 for (i = 0; i < conf->num_auth_servers; i++) {
1651 serv = &conf->auth_servers[i];
1652 count += radius_client_dump_auth_server(
1653 buf + count, buflen - count, serv,
1654 serv == conf->auth_server ?
1655 radius : NULL);
1656 }
1657 }
1658
1659 if (conf->acct_servers) {
1660 for (i = 0; i < conf->num_acct_servers; i++) {
1661 serv = &conf->acct_servers[i];
1662 count += radius_client_dump_acct_server(
1663 buf + count, buflen - count, serv,
1664 serv == conf->acct_server ?
1665 radius : NULL);
1666 }
1667 }
1668
1669 return count;
1670}
1671
1672
1673void radius_client_reconfig(struct radius_client_data *radius,
1674 struct hostapd_radius_servers *conf)
1675{
1676 if (radius)
1677 radius->conf = conf;
1678}