blob: 0bcdb45339140396758138689e44968606c8cc4e [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;
229};
230
231
232static int
233radius_change_server(struct radius_client_data *radius,
234 struct hostapd_radius_server *nserv,
235 struct hostapd_radius_server *oserv,
236 int sock, int sock6, int auth);
237static int radius_client_init_acct(struct radius_client_data *radius);
238static int radius_client_init_auth(struct radius_client_data *radius);
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800239static void radius_client_auth_failover(struct radius_client_data *radius);
240static void radius_client_acct_failover(struct radius_client_data *radius);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700241
242
243static void radius_client_msg_free(struct radius_msg_list *req)
244{
245 radius_msg_free(req->msg);
246 os_free(req);
247}
248
249
250/**
251 * radius_client_register - Register a RADIUS client RX handler
252 * @radius: RADIUS client context from radius_client_init()
253 * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT)
254 * @handler: Handler for received RADIUS messages
255 * @data: Context pointer for handler callbacks
256 * Returns: 0 on success, -1 on failure
257 *
258 * This function is used to register a handler for processing received RADIUS
259 * authentication and accounting messages. The handler() callback function will
260 * be called whenever a RADIUS message is received from the active server.
261 *
262 * There can be multiple registered RADIUS message handlers. The handlers will
263 * be called in order until one of them indicates that it has processed or
264 * queued the message.
265 */
266int radius_client_register(struct radius_client_data *radius,
267 RadiusType msg_type,
268 RadiusRxResult (*handler)(struct radius_msg *msg,
269 struct radius_msg *req,
270 const u8 *shared_secret,
271 size_t shared_secret_len,
272 void *data),
273 void *data)
274{
275 struct radius_rx_handler **handlers, *newh;
276 size_t *num;
277
278 if (msg_type == RADIUS_ACCT) {
279 handlers = &radius->acct_handlers;
280 num = &radius->num_acct_handlers;
281 } else {
282 handlers = &radius->auth_handlers;
283 num = &radius->num_auth_handlers;
284 }
285
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700286 newh = os_realloc_array(*handlers, *num + 1,
287 sizeof(struct radius_rx_handler));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700288 if (newh == NULL)
289 return -1;
290
291 newh[*num].handler = handler;
292 newh[*num].data = data;
293 (*num)++;
294 *handlers = newh;
295
296 return 0;
297}
298
299
Dmitry Shmidt71757432014-06-02 13:50:35 -0700300/*
301 * Returns >0 if message queue was flushed (i.e., the message that triggered
302 * the error is not available anymore)
303 */
304static int radius_client_handle_send_error(struct radius_client_data *radius,
305 int s, RadiusType msg_type)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700306{
307#ifndef CONFIG_NATIVE_WINDOWS
308 int _errno = errno;
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800309 wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700310 if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
Dmitry Shmidt71757432014-06-02 13:50:35 -0700311 _errno == EBADF || _errno == ENETUNREACH) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700312 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
313 HOSTAPD_LEVEL_INFO,
314 "Send failed - maybe interface status changed -"
315 " try to connect again");
Dmitry Shmidt71757432014-06-02 13:50:35 -0700316 if (msg_type == RADIUS_ACCT ||
317 msg_type == RADIUS_ACCT_INTERIM) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700318 radius_client_init_acct(radius);
Dmitry Shmidt71757432014-06-02 13:50:35 -0700319 return 0;
320 } else {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700321 radius_client_init_auth(radius);
Dmitry Shmidt71757432014-06-02 13:50:35 -0700322 return 1;
323 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700324 }
325#endif /* CONFIG_NATIVE_WINDOWS */
Dmitry Shmidt71757432014-06-02 13:50:35 -0700326
327 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700328}
329
330
331static int radius_client_retransmit(struct radius_client_data *radius,
332 struct radius_msg_list *entry,
333 os_time_t now)
334{
335 struct hostapd_radius_servers *conf = radius->conf;
336 int s;
337 struct wpabuf *buf;
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800338 size_t prev_num_msgs;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700339
340 if (entry->msg_type == RADIUS_ACCT ||
341 entry->msg_type == RADIUS_ACCT_INTERIM) {
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800342 if (radius->acct_sock < 0)
343 radius_client_init_acct(radius);
344 if (radius->acct_sock < 0 && conf->num_acct_servers > 1) {
345 prev_num_msgs = radius->num_msgs;
346 radius_client_acct_failover(radius);
347 if (prev_num_msgs != radius->num_msgs)
348 return 0;
349 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700350 s = radius->acct_sock;
351 if (entry->attempts == 0)
352 conf->acct_server->requests++;
353 else {
354 conf->acct_server->timeouts++;
355 conf->acct_server->retransmissions++;
356 }
357 } else {
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800358 if (radius->auth_sock < 0)
359 radius_client_init_auth(radius);
360 if (radius->auth_sock < 0 && conf->num_auth_servers > 1) {
361 prev_num_msgs = radius->num_msgs;
362 radius_client_auth_failover(radius);
363 if (prev_num_msgs != radius->num_msgs)
364 return 0;
365 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700366 s = radius->auth_sock;
367 if (entry->attempts == 0)
368 conf->auth_server->requests++;
369 else {
370 conf->auth_server->timeouts++;
371 conf->auth_server->retransmissions++;
372 }
373 }
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800374 if (s < 0) {
375 wpa_printf(MSG_INFO,
376 "RADIUS: No valid socket for retransmission");
377 return 1;
378 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700379
380 /* retransmit; remove entry if too many attempts */
381 entry->attempts++;
382 hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,
383 HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
384 radius_msg_get_hdr(entry->msg)->identifier);
385
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800386 os_get_reltime(&entry->last_attempt);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700387 buf = radius_msg_get_buf(entry->msg);
Dmitry Shmidt71757432014-06-02 13:50:35 -0700388 if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
389 if (radius_client_handle_send_error(radius, s, entry->msg_type)
390 > 0)
391 return 0;
392 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700393
394 entry->next_try = now + entry->next_wait;
395 entry->next_wait *= 2;
396 if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
397 entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
398 if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800399 wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700400 return 1;
401 }
402
403 return 0;
404}
405
406
407static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
408{
409 struct radius_client_data *radius = eloop_ctx;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800410 struct os_reltime now;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700411 os_time_t first;
412 struct radius_msg_list *entry, *prev, *tmp;
413 int auth_failover = 0, acct_failover = 0;
Dmitry Shmidt71757432014-06-02 13:50:35 -0700414 size_t prev_num_msgs;
415 int s;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700416
417 entry = radius->msgs;
418 if (!entry)
419 return;
420
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800421 os_get_reltime(&now);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700422 first = 0;
423
424 prev = NULL;
425 while (entry) {
Dmitry Shmidt71757432014-06-02 13:50:35 -0700426 prev_num_msgs = radius->num_msgs;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700427 if (now.sec >= entry->next_try &&
428 radius_client_retransmit(radius, entry, now.sec)) {
429 if (prev)
430 prev->next = entry->next;
431 else
432 radius->msgs = entry->next;
433
434 tmp = entry;
435 entry = entry->next;
436 radius_client_msg_free(tmp);
437 radius->num_msgs--;
438 continue;
439 }
440
Dmitry Shmidt71757432014-06-02 13:50:35 -0700441 if (prev_num_msgs != radius->num_msgs) {
442 wpa_printf(MSG_DEBUG,
443 "RADIUS: Message removed from queue - restart from beginning");
444 entry = radius->msgs;
445 prev = NULL;
446 continue;
447 }
448
449 s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock :
450 radius->acct_sock;
451 if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER ||
452 (s < 0 && entry->attempts > 0)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700453 if (entry->msg_type == RADIUS_ACCT ||
454 entry->msg_type == RADIUS_ACCT_INTERIM)
455 acct_failover++;
456 else
457 auth_failover++;
458 }
459
460 if (first == 0 || entry->next_try < first)
461 first = entry->next_try;
462
463 prev = entry;
464 entry = entry->next;
465 }
466
467 if (radius->msgs) {
468 if (first < now.sec)
469 first = now.sec;
470 eloop_register_timeout(first - now.sec, 0,
471 radius_client_timer, radius, NULL);
472 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
473 HOSTAPD_LEVEL_DEBUG, "Next RADIUS client "
474 "retransmit in %ld seconds",
475 (long int) (first - now.sec));
476 }
477
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800478 if (auth_failover)
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800479 radius_client_auth_failover(radius);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700480
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800481 if (acct_failover)
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800482 radius_client_acct_failover(radius);
483}
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700484
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800485
486static void radius_client_auth_failover(struct radius_client_data *radius)
487{
488 struct hostapd_radius_servers *conf = radius->conf;
489 struct hostapd_radius_server *next, *old;
490 struct radius_msg_list *entry;
491 char abuf[50];
492
493 old = conf->auth_server;
494 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
495 HOSTAPD_LEVEL_NOTICE,
496 "No response from Authentication server %s:%d - failover",
497 hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
498 old->port);
499
500 for (entry = radius->msgs; entry; entry = entry->next) {
501 if (entry->msg_type == RADIUS_AUTH)
502 old->timeouts++;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700503 }
504
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800505 next = old + 1;
506 if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
507 next = conf->auth_servers;
508 conf->auth_server = next;
509 radius_change_server(radius, next, old,
510 radius->auth_serv_sock,
511 radius->auth_serv_sock6, 1);
512}
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700513
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700514
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800515static void radius_client_acct_failover(struct radius_client_data *radius)
516{
517 struct hostapd_radius_servers *conf = radius->conf;
518 struct hostapd_radius_server *next, *old;
519 struct radius_msg_list *entry;
520 char abuf[50];
521
522 old = conf->acct_server;
523 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
524 HOSTAPD_LEVEL_NOTICE,
525 "No response from Accounting server %s:%d - failover",
526 hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
527 old->port);
528
529 for (entry = radius->msgs; entry; entry = entry->next) {
530 if (entry->msg_type == RADIUS_ACCT ||
531 entry->msg_type == RADIUS_ACCT_INTERIM)
532 old->timeouts++;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700533 }
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800534
535 next = old + 1;
536 if (next > &conf->acct_servers[conf->num_acct_servers - 1])
537 next = conf->acct_servers;
538 conf->acct_server = next;
539 radius_change_server(radius, next, old,
540 radius->acct_serv_sock,
541 radius->acct_serv_sock6, 0);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700542}
543
544
545static void radius_client_update_timeout(struct radius_client_data *radius)
546{
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800547 struct os_reltime now;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700548 os_time_t first;
549 struct radius_msg_list *entry;
550
551 eloop_cancel_timeout(radius_client_timer, radius, NULL);
552
553 if (radius->msgs == NULL) {
554 return;
555 }
556
557 first = 0;
558 for (entry = radius->msgs; entry; entry = entry->next) {
559 if (first == 0 || entry->next_try < first)
560 first = entry->next_try;
561 }
562
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800563 os_get_reltime(&now);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700564 if (first < now.sec)
565 first = now.sec;
566 eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
567 NULL);
568 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
569 HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
Dmitry Shmidt04949592012-07-19 12:16:46 -0700570 " %ld seconds", (long int) (first - now.sec));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700571}
572
573
574static void radius_client_list_add(struct radius_client_data *radius,
575 struct radius_msg *msg,
576 RadiusType msg_type,
577 const u8 *shared_secret,
578 size_t shared_secret_len, const u8 *addr)
579{
580 struct radius_msg_list *entry, *prev;
581
582 if (eloop_terminated()) {
583 /* No point in adding entries to retransmit queue since event
584 * loop has already been terminated. */
585 radius_msg_free(msg);
586 return;
587 }
588
589 entry = os_zalloc(sizeof(*entry));
590 if (entry == NULL) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800591 wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700592 radius_msg_free(msg);
593 return;
594 }
595
596 if (addr)
597 os_memcpy(entry->addr, addr, ETH_ALEN);
598 entry->msg = msg;
599 entry->msg_type = msg_type;
600 entry->shared_secret = shared_secret;
601 entry->shared_secret_len = shared_secret_len;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800602 os_get_reltime(&entry->last_attempt);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700603 entry->first_try = entry->last_attempt.sec;
604 entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
605 entry->attempts = 1;
606 entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
607 entry->next = radius->msgs;
608 radius->msgs = entry;
609 radius_client_update_timeout(radius);
610
611 if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800612 wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700613 prev = NULL;
614 while (entry->next) {
615 prev = entry;
616 entry = entry->next;
617 }
618 if (prev) {
619 prev->next = NULL;
620 radius_client_msg_free(entry);
621 }
622 } else
623 radius->num_msgs++;
624}
625
626
627static void radius_client_list_del(struct radius_client_data *radius,
628 RadiusType msg_type, const u8 *addr)
629{
630 struct radius_msg_list *entry, *prev, *tmp;
631
632 if (addr == NULL)
633 return;
634
635 entry = radius->msgs;
636 prev = NULL;
637 while (entry) {
638 if (entry->msg_type == msg_type &&
639 os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
640 if (prev)
641 prev->next = entry->next;
642 else
643 radius->msgs = entry->next;
644 tmp = entry;
645 entry = entry->next;
646 hostapd_logger(radius->ctx, addr,
647 HOSTAPD_MODULE_RADIUS,
648 HOSTAPD_LEVEL_DEBUG,
649 "Removing matching RADIUS message");
650 radius_client_msg_free(tmp);
651 radius->num_msgs--;
652 continue;
653 }
654 prev = entry;
655 entry = entry->next;
656 }
657}
658
659
660/**
661 * radius_client_send - Send a RADIUS request
662 * @radius: RADIUS client context from radius_client_init()
663 * @msg: RADIUS message to be sent
664 * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM)
665 * @addr: MAC address of the device related to this message or %NULL
666 * Returns: 0 on success, -1 on failure
667 *
668 * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or
669 * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference
670 * between accounting and interim accounting messages is that the interim
671 * message will override any pending interim accounting updates while a new
672 * accounting message does not remove any pending messages.
673 *
674 * The message is added on the retransmission queue and will be retransmitted
675 * automatically until a response is received or maximum number of retries
676 * (RADIUS_CLIENT_MAX_RETRIES) is reached.
677 *
678 * The related device MAC address can be used to identify pending messages that
679 * can be removed with radius_client_flush_auth() or with interim accounting
680 * updates.
681 */
682int radius_client_send(struct radius_client_data *radius,
683 struct radius_msg *msg, RadiusType msg_type,
684 const u8 *addr)
685{
686 struct hostapd_radius_servers *conf = radius->conf;
687 const u8 *shared_secret;
688 size_t shared_secret_len;
689 char *name;
690 int s, res;
691 struct wpabuf *buf;
692
693 if (msg_type == RADIUS_ACCT_INTERIM) {
694 /* Remove any pending interim acct update for the same STA. */
695 radius_client_list_del(radius, msg_type, addr);
696 }
697
698 if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800699 if (conf->acct_server && radius->acct_sock < 0)
700 radius_client_init_acct(radius);
701
Dmitry Shmidt2f74e362015-01-21 13:19:05 -0800702 if (conf->acct_server == NULL || radius->acct_sock < 0 ||
703 conf->acct_server->shared_secret == NULL) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700704 hostapd_logger(radius->ctx, NULL,
705 HOSTAPD_MODULE_RADIUS,
706 HOSTAPD_LEVEL_INFO,
707 "No accounting server configured");
708 return -1;
709 }
710 shared_secret = conf->acct_server->shared_secret;
711 shared_secret_len = conf->acct_server->shared_secret_len;
712 radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
713 name = "accounting";
714 s = radius->acct_sock;
715 conf->acct_server->requests++;
716 } else {
Dmitry Shmidt203eadb2015-03-05 14:16:04 -0800717 if (conf->auth_server && radius->auth_sock < 0)
718 radius_client_init_auth(radius);
719
Dmitry Shmidt2f74e362015-01-21 13:19:05 -0800720 if (conf->auth_server == NULL || radius->auth_sock < 0 ||
721 conf->auth_server->shared_secret == NULL) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700722 hostapd_logger(radius->ctx, NULL,
723 HOSTAPD_MODULE_RADIUS,
724 HOSTAPD_LEVEL_INFO,
725 "No authentication server configured");
726 return -1;
727 }
728 shared_secret = conf->auth_server->shared_secret;
729 shared_secret_len = conf->auth_server->shared_secret_len;
730 radius_msg_finish(msg, shared_secret, shared_secret_len);
731 name = "authentication";
732 s = radius->auth_sock;
733 conf->auth_server->requests++;
734 }
735
736 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
737 HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s "
738 "server", name);
739 if (conf->msg_dumps)
740 radius_msg_dump(msg);
741
742 buf = radius_msg_get_buf(msg);
743 res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0);
744 if (res < 0)
745 radius_client_handle_send_error(radius, s, msg_type);
746
747 radius_client_list_add(radius, msg, msg_type, shared_secret,
748 shared_secret_len, addr);
749
Dmitry Shmidt04949592012-07-19 12:16:46 -0700750 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700751}
752
753
754static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
755{
756 struct radius_client_data *radius = eloop_ctx;
757 struct hostapd_radius_servers *conf = radius->conf;
758 RadiusType msg_type = (RadiusType) sock_ctx;
759 int len, roundtrip;
760 unsigned char buf[3000];
761 struct radius_msg *msg;
762 struct radius_hdr *hdr;
763 struct radius_rx_handler *handlers;
764 size_t num_handlers, i;
765 struct radius_msg_list *req, *prev_req;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800766 struct os_reltime now;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700767 struct hostapd_radius_server *rconf;
768 int invalid_authenticator = 0;
769
770 if (msg_type == RADIUS_ACCT) {
771 handlers = radius->acct_handlers;
772 num_handlers = radius->num_acct_handlers;
773 rconf = conf->acct_server;
774 } else {
775 handlers = radius->auth_handlers;
776 num_handlers = radius->num_auth_handlers;
777 rconf = conf->auth_server;
778 }
779
780 len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
781 if (len < 0) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800782 wpa_printf(MSG_INFO, "recv[RADIUS]: %s", strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700783 return;
784 }
785 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
786 HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
787 "server", len);
788 if (len == sizeof(buf)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800789 wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700790 return;
791 }
792
793 msg = radius_msg_parse(buf, len);
794 if (msg == NULL) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800795 wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700796 rconf->malformed_responses++;
797 return;
798 }
799 hdr = radius_msg_get_hdr(msg);
800
801 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
802 HOSTAPD_LEVEL_DEBUG, "Received RADIUS message");
803 if (conf->msg_dumps)
804 radius_msg_dump(msg);
805
806 switch (hdr->code) {
807 case RADIUS_CODE_ACCESS_ACCEPT:
808 rconf->access_accepts++;
809 break;
810 case RADIUS_CODE_ACCESS_REJECT:
811 rconf->access_rejects++;
812 break;
813 case RADIUS_CODE_ACCESS_CHALLENGE:
814 rconf->access_challenges++;
815 break;
816 case RADIUS_CODE_ACCOUNTING_RESPONSE:
817 rconf->responses++;
818 break;
819 }
820
821 prev_req = NULL;
822 req = radius->msgs;
823 while (req) {
824 /* TODO: also match by src addr:port of the packet when using
825 * alternative RADIUS servers (?) */
826 if ((req->msg_type == msg_type ||
827 (req->msg_type == RADIUS_ACCT_INTERIM &&
828 msg_type == RADIUS_ACCT)) &&
829 radius_msg_get_hdr(req->msg)->identifier ==
830 hdr->identifier)
831 break;
832
833 prev_req = req;
834 req = req->next;
835 }
836
837 if (req == NULL) {
838 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
839 HOSTAPD_LEVEL_DEBUG,
840 "No matching RADIUS request found (type=%d "
841 "id=%d) - dropping packet",
842 msg_type, hdr->identifier);
843 goto fail;
844 }
845
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800846 os_get_reltime(&now);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700847 roundtrip = (now.sec - req->last_attempt.sec) * 100 +
848 (now.usec - req->last_attempt.usec) / 10000;
849 hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
850 HOSTAPD_LEVEL_DEBUG,
851 "Received RADIUS packet matched with a pending "
852 "request, round trip time %d.%02d sec",
853 roundtrip / 100, roundtrip % 100);
854 rconf->round_trip_time = roundtrip;
855
856 /* Remove ACKed RADIUS packet from retransmit list */
857 if (prev_req)
858 prev_req->next = req->next;
859 else
860 radius->msgs = req->next;
861 radius->num_msgs--;
862
863 for (i = 0; i < num_handlers; i++) {
864 RadiusRxResult res;
865 res = handlers[i].handler(msg, req->msg, req->shared_secret,
866 req->shared_secret_len,
867 handlers[i].data);
868 switch (res) {
869 case RADIUS_RX_PROCESSED:
870 radius_msg_free(msg);
871 /* continue */
872 case RADIUS_RX_QUEUED:
873 radius_client_msg_free(req);
874 return;
875 case RADIUS_RX_INVALID_AUTHENTICATOR:
876 invalid_authenticator++;
877 /* continue */
878 case RADIUS_RX_UNKNOWN:
879 /* continue with next handler */
880 break;
881 }
882 }
883
884 if (invalid_authenticator)
885 rconf->bad_authenticators++;
886 else
887 rconf->unknown_types++;
888 hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
889 HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found "
890 "(type=%d code=%d id=%d)%s - dropping packet",
891 msg_type, hdr->code, hdr->identifier,
892 invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
893 "");
894 radius_client_msg_free(req);
895
896 fail:
897 radius_msg_free(msg);
898}
899
900
901/**
902 * radius_client_get_id - Get an identifier for a new RADIUS message
903 * @radius: RADIUS client context from radius_client_init()
904 * Returns: Allocated identifier
905 *
906 * This function is used to fetch a unique (among pending requests) identifier
907 * for a new RADIUS message.
908 */
909u8 radius_client_get_id(struct radius_client_data *radius)
910{
911 struct radius_msg_list *entry, *prev, *_remove;
912 u8 id = radius->next_radius_identifier++;
913
914 /* remove entries with matching id from retransmit list to avoid
915 * using new reply from the RADIUS server with an old request */
916 entry = radius->msgs;
917 prev = NULL;
918 while (entry) {
919 if (radius_msg_get_hdr(entry->msg)->identifier == id) {
920 hostapd_logger(radius->ctx, entry->addr,
921 HOSTAPD_MODULE_RADIUS,
922 HOSTAPD_LEVEL_DEBUG,
923 "Removing pending RADIUS message, "
924 "since its id (%d) is reused", id);
925 if (prev)
926 prev->next = entry->next;
927 else
928 radius->msgs = entry->next;
929 _remove = entry;
930 } else {
931 _remove = NULL;
932 prev = entry;
933 }
934 entry = entry->next;
935
936 if (_remove)
937 radius_client_msg_free(_remove);
938 }
939
940 return id;
941}
942
943
944/**
945 * radius_client_flush - Flush all pending RADIUS client messages
946 * @radius: RADIUS client context from radius_client_init()
947 * @only_auth: Whether only authentication messages are removed
948 */
949void radius_client_flush(struct radius_client_data *radius, int only_auth)
950{
951 struct radius_msg_list *entry, *prev, *tmp;
952
953 if (!radius)
954 return;
955
956 prev = NULL;
957 entry = radius->msgs;
958
959 while (entry) {
960 if (!only_auth || entry->msg_type == RADIUS_AUTH) {
961 if (prev)
962 prev->next = entry->next;
963 else
964 radius->msgs = entry->next;
965
966 tmp = entry;
967 entry = entry->next;
968 radius_client_msg_free(tmp);
969 radius->num_msgs--;
970 } else {
971 prev = entry;
972 entry = entry->next;
973 }
974 }
975
976 if (radius->msgs == NULL)
977 eloop_cancel_timeout(radius_client_timer, radius, NULL);
978}
979
980
981static void radius_client_update_acct_msgs(struct radius_client_data *radius,
982 const u8 *shared_secret,
983 size_t shared_secret_len)
984{
985 struct radius_msg_list *entry;
986
987 if (!radius)
988 return;
989
990 for (entry = radius->msgs; entry; entry = entry->next) {
991 if (entry->msg_type == RADIUS_ACCT) {
992 entry->shared_secret = shared_secret;
993 entry->shared_secret_len = shared_secret_len;
994 radius_msg_finish_acct(entry->msg, shared_secret,
995 shared_secret_len);
996 }
997 }
998}
999
1000
1001static int
1002radius_change_server(struct radius_client_data *radius,
1003 struct hostapd_radius_server *nserv,
1004 struct hostapd_radius_server *oserv,
1005 int sock, int sock6, int auth)
1006{
1007 struct sockaddr_in serv, claddr;
1008#ifdef CONFIG_IPV6
1009 struct sockaddr_in6 serv6, claddr6;
1010#endif /* CONFIG_IPV6 */
1011 struct sockaddr *addr, *cl_addr;
1012 socklen_t addrlen, claddrlen;
1013 char abuf[50];
1014 int sel_sock;
1015 struct radius_msg_list *entry;
1016 struct hostapd_radius_servers *conf = radius->conf;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001017 struct sockaddr_in disconnect_addr = {
1018 .sin_family = AF_UNSPEC,
1019 };
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001020
1021 hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
1022 HOSTAPD_LEVEL_INFO,
1023 "%s server %s:%d",
1024 auth ? "Authentication" : "Accounting",
1025 hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
1026 nserv->port);
1027
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001028 if (oserv && oserv == nserv) {
1029 /* Reconnect to same server, flush */
1030 if (auth)
1031 radius_client_flush(radius, 1);
1032 }
1033
Dmitry Shmidt71757432014-06-02 13:50:35 -07001034 if (oserv && oserv != nserv &&
1035 (nserv->shared_secret_len != oserv->shared_secret_len ||
1036 os_memcmp(nserv->shared_secret, oserv->shared_secret,
1037 nserv->shared_secret_len) != 0)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001038 /* Pending RADIUS packets used different shared secret, so
1039 * they need to be modified. Update accounting message
1040 * authenticators here. Authentication messages are removed
1041 * since they would require more changes and the new RADIUS
1042 * server may not be prepared to receive them anyway due to
1043 * missing state information. Client will likely retry
1044 * authentication, so this should not be an issue. */
1045 if (auth)
1046 radius_client_flush(radius, 1);
1047 else {
1048 radius_client_update_acct_msgs(
1049 radius, nserv->shared_secret,
1050 nserv->shared_secret_len);
1051 }
1052 }
1053
1054 /* Reset retry counters for the new server */
Dmitry Shmidt71757432014-06-02 13:50:35 -07001055 for (entry = radius->msgs; oserv && oserv != nserv && entry;
1056 entry = entry->next) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001057 if ((auth && entry->msg_type != RADIUS_AUTH) ||
1058 (!auth && entry->msg_type != RADIUS_ACCT))
1059 continue;
1060 entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
1061 entry->attempts = 0;
1062 entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
1063 }
1064
1065 if (radius->msgs) {
1066 eloop_cancel_timeout(radius_client_timer, radius, NULL);
1067 eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
1068 radius_client_timer, radius, NULL);
1069 }
1070
1071 switch (nserv->addr.af) {
1072 case AF_INET:
1073 os_memset(&serv, 0, sizeof(serv));
1074 serv.sin_family = AF_INET;
1075 serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr;
1076 serv.sin_port = htons(nserv->port);
1077 addr = (struct sockaddr *) &serv;
1078 addrlen = sizeof(serv);
1079 sel_sock = sock;
1080 break;
1081#ifdef CONFIG_IPV6
1082 case AF_INET6:
1083 os_memset(&serv6, 0, sizeof(serv6));
1084 serv6.sin6_family = AF_INET6;
1085 os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6,
1086 sizeof(struct in6_addr));
1087 serv6.sin6_port = htons(nserv->port);
1088 addr = (struct sockaddr *) &serv6;
1089 addrlen = sizeof(serv6);
1090 sel_sock = sock6;
1091 break;
1092#endif /* CONFIG_IPV6 */
1093 default:
1094 return -1;
1095 }
1096
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001097 if (sel_sock < 0) {
1098 wpa_printf(MSG_INFO,
1099 "RADIUS: No server socket available (af=%d sock=%d sock6=%d auth=%d",
1100 nserv->addr.af, sock, sock6, auth);
1101 return -1;
1102 }
1103
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001104 if (conf->force_client_addr) {
1105 switch (conf->client_addr.af) {
1106 case AF_INET:
1107 os_memset(&claddr, 0, sizeof(claddr));
1108 claddr.sin_family = AF_INET;
1109 claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr;
1110 claddr.sin_port = htons(0);
1111 cl_addr = (struct sockaddr *) &claddr;
1112 claddrlen = sizeof(claddr);
1113 break;
1114#ifdef CONFIG_IPV6
1115 case AF_INET6:
1116 os_memset(&claddr6, 0, sizeof(claddr6));
1117 claddr6.sin6_family = AF_INET6;
1118 os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6,
1119 sizeof(struct in6_addr));
1120 claddr6.sin6_port = htons(0);
1121 cl_addr = (struct sockaddr *) &claddr6;
1122 claddrlen = sizeof(claddr6);
1123 break;
1124#endif /* CONFIG_IPV6 */
1125 default:
1126 return -1;
1127 }
1128
1129 if (bind(sel_sock, cl_addr, claddrlen) < 0) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001130 wpa_printf(MSG_INFO, "bind[radius]: %s",
1131 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001132 return -1;
1133 }
1134 }
1135
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001136 /* Force a reconnect by disconnecting the socket first */
1137 if (connect(sel_sock, (struct sockaddr *) &disconnect_addr,
1138 sizeof(disconnect_addr)) < 0)
1139 wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno));
1140
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001141 if (connect(sel_sock, addr, addrlen) < 0) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001142 wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001143 return -1;
1144 }
1145
1146#ifndef CONFIG_NATIVE_WINDOWS
1147 switch (nserv->addr.af) {
1148 case AF_INET:
1149 claddrlen = sizeof(claddr);
Dmitry Shmidt661b4f72014-09-29 14:58:27 -07001150 if (getsockname(sel_sock, (struct sockaddr *) &claddr,
1151 &claddrlen) == 0) {
1152 wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
1153 inet_ntoa(claddr.sin_addr),
1154 ntohs(claddr.sin_port));
1155 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001156 break;
1157#ifdef CONFIG_IPV6
1158 case AF_INET6: {
1159 claddrlen = sizeof(claddr6);
Dmitry Shmidt661b4f72014-09-29 14:58:27 -07001160 if (getsockname(sel_sock, (struct sockaddr *) &claddr6,
1161 &claddrlen) == 0) {
1162 wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
1163 inet_ntop(AF_INET6, &claddr6.sin6_addr,
1164 abuf, sizeof(abuf)),
1165 ntohs(claddr6.sin6_port));
1166 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001167 break;
1168 }
1169#endif /* CONFIG_IPV6 */
1170 }
1171#endif /* CONFIG_NATIVE_WINDOWS */
1172
1173 if (auth)
1174 radius->auth_sock = sel_sock;
1175 else
1176 radius->acct_sock = sel_sock;
1177
1178 return 0;
1179}
1180
1181
1182static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
1183{
1184 struct radius_client_data *radius = eloop_ctx;
1185 struct hostapd_radius_servers *conf = radius->conf;
1186 struct hostapd_radius_server *oserv;
1187
1188 if (radius->auth_sock >= 0 && conf->auth_servers &&
1189 conf->auth_server != conf->auth_servers) {
1190 oserv = conf->auth_server;
1191 conf->auth_server = conf->auth_servers;
Dmitry Shmidt203eadb2015-03-05 14:16:04 -08001192 if (radius_change_server(radius, conf->auth_server, oserv,
1193 radius->auth_serv_sock,
1194 radius->auth_serv_sock6, 1) < 0) {
1195 conf->auth_server = oserv;
1196 radius_change_server(radius, oserv, conf->auth_server,
1197 radius->auth_serv_sock,
1198 radius->auth_serv_sock6, 1);
1199 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001200 }
1201
1202 if (radius->acct_sock >= 0 && conf->acct_servers &&
1203 conf->acct_server != conf->acct_servers) {
1204 oserv = conf->acct_server;
1205 conf->acct_server = conf->acct_servers;
Dmitry Shmidt203eadb2015-03-05 14:16:04 -08001206 if (radius_change_server(radius, conf->acct_server, oserv,
1207 radius->acct_serv_sock,
1208 radius->acct_serv_sock6, 0) < 0) {
1209 conf->acct_server = oserv;
1210 radius_change_server(radius, oserv, conf->acct_server,
1211 radius->acct_serv_sock,
1212 radius->acct_serv_sock6, 0);
1213 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001214 }
1215
1216 if (conf->retry_primary_interval)
1217 eloop_register_timeout(conf->retry_primary_interval, 0,
1218 radius_retry_primary_timer, radius,
1219 NULL);
1220}
1221
1222
1223static int radius_client_disable_pmtu_discovery(int s)
1224{
1225 int r = -1;
1226#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
1227 /* Turn off Path MTU discovery on IPv4/UDP sockets. */
1228 int action = IP_PMTUDISC_DONT;
1229 r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
1230 sizeof(action));
1231 if (r == -1)
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001232 wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s",
1233 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001234#endif
1235 return r;
1236}
1237
1238
Dmitry Shmidt71757432014-06-02 13:50:35 -07001239static void radius_close_auth_sockets(struct radius_client_data *radius)
1240{
1241 radius->auth_sock = -1;
1242
1243 if (radius->auth_serv_sock >= 0) {
1244 eloop_unregister_read_sock(radius->auth_serv_sock);
1245 close(radius->auth_serv_sock);
1246 radius->auth_serv_sock = -1;
1247 }
1248#ifdef CONFIG_IPV6
1249 if (radius->auth_serv_sock6 >= 0) {
1250 eloop_unregister_read_sock(radius->auth_serv_sock6);
1251 close(radius->auth_serv_sock6);
1252 radius->auth_serv_sock6 = -1;
1253 }
1254#endif /* CONFIG_IPV6 */
1255}
1256
1257
1258static void radius_close_acct_sockets(struct radius_client_data *radius)
1259{
1260 radius->acct_sock = -1;
1261
1262 if (radius->acct_serv_sock >= 0) {
1263 eloop_unregister_read_sock(radius->acct_serv_sock);
1264 close(radius->acct_serv_sock);
1265 radius->acct_serv_sock = -1;
1266 }
1267#ifdef CONFIG_IPV6
1268 if (radius->acct_serv_sock6 >= 0) {
1269 eloop_unregister_read_sock(radius->acct_serv_sock6);
1270 close(radius->acct_serv_sock6);
1271 radius->acct_serv_sock6 = -1;
1272 }
1273#endif /* CONFIG_IPV6 */
1274}
1275
1276
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001277static int radius_client_init_auth(struct radius_client_data *radius)
1278{
1279 struct hostapd_radius_servers *conf = radius->conf;
1280 int ok = 0;
1281
Dmitry Shmidt71757432014-06-02 13:50:35 -07001282 radius_close_auth_sockets(radius);
1283
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001284 radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
1285 if (radius->auth_serv_sock < 0)
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001286 wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
1287 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001288 else {
1289 radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
1290 ok++;
1291 }
1292
1293#ifdef CONFIG_IPV6
1294 radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
1295 if (radius->auth_serv_sock6 < 0)
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001296 wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
1297 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001298 else
1299 ok++;
1300#endif /* CONFIG_IPV6 */
1301
1302 if (ok == 0)
1303 return -1;
1304
1305 radius_change_server(radius, conf->auth_server, NULL,
1306 radius->auth_serv_sock, radius->auth_serv_sock6,
1307 1);
1308
1309 if (radius->auth_serv_sock >= 0 &&
1310 eloop_register_read_sock(radius->auth_serv_sock,
1311 radius_client_receive, radius,
1312 (void *) RADIUS_AUTH)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001313 wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
Dmitry Shmidt71757432014-06-02 13:50:35 -07001314 radius_close_auth_sockets(radius);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001315 return -1;
1316 }
1317
1318#ifdef CONFIG_IPV6
1319 if (radius->auth_serv_sock6 >= 0 &&
1320 eloop_register_read_sock(radius->auth_serv_sock6,
1321 radius_client_receive, radius,
1322 (void *) RADIUS_AUTH)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001323 wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
Dmitry Shmidt71757432014-06-02 13:50:35 -07001324 radius_close_auth_sockets(radius);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001325 return -1;
1326 }
1327#endif /* CONFIG_IPV6 */
1328
1329 return 0;
1330}
1331
1332
1333static int radius_client_init_acct(struct radius_client_data *radius)
1334{
1335 struct hostapd_radius_servers *conf = radius->conf;
1336 int ok = 0;
1337
Dmitry Shmidt71757432014-06-02 13:50:35 -07001338 radius_close_acct_sockets(radius);
1339
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001340 radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
1341 if (radius->acct_serv_sock < 0)
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001342 wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
1343 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001344 else {
1345 radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
1346 ok++;
1347 }
1348
1349#ifdef CONFIG_IPV6
1350 radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
1351 if (radius->acct_serv_sock6 < 0)
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001352 wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
1353 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001354 else
1355 ok++;
1356#endif /* CONFIG_IPV6 */
1357
1358 if (ok == 0)
1359 return -1;
1360
1361 radius_change_server(radius, conf->acct_server, NULL,
1362 radius->acct_serv_sock, radius->acct_serv_sock6,
1363 0);
1364
1365 if (radius->acct_serv_sock >= 0 &&
1366 eloop_register_read_sock(radius->acct_serv_sock,
1367 radius_client_receive, radius,
1368 (void *) RADIUS_ACCT)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001369 wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
Dmitry Shmidt71757432014-06-02 13:50:35 -07001370 radius_close_acct_sockets(radius);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001371 return -1;
1372 }
1373
1374#ifdef CONFIG_IPV6
1375 if (radius->acct_serv_sock6 >= 0 &&
1376 eloop_register_read_sock(radius->acct_serv_sock6,
1377 radius_client_receive, radius,
1378 (void *) RADIUS_ACCT)) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001379 wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
Dmitry Shmidt71757432014-06-02 13:50:35 -07001380 radius_close_acct_sockets(radius);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001381 return -1;
1382 }
1383#endif /* CONFIG_IPV6 */
1384
1385 return 0;
1386}
1387
1388
1389/**
1390 * radius_client_init - Initialize RADIUS client
1391 * @ctx: Callback context to be used in hostapd_logger() calls
1392 * @conf: RADIUS client configuration (RADIUS servers)
1393 * Returns: Pointer to private RADIUS client context or %NULL on failure
1394 *
1395 * The caller is responsible for keeping the configuration data available for
1396 * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is
1397 * called for the returned context pointer.
1398 */
1399struct radius_client_data *
1400radius_client_init(void *ctx, struct hostapd_radius_servers *conf)
1401{
1402 struct radius_client_data *radius;
1403
1404 radius = os_zalloc(sizeof(struct radius_client_data));
1405 if (radius == NULL)
1406 return NULL;
1407
1408 radius->ctx = ctx;
1409 radius->conf = conf;
1410 radius->auth_serv_sock = radius->acct_serv_sock =
1411 radius->auth_serv_sock6 = radius->acct_serv_sock6 =
1412 radius->auth_sock = radius->acct_sock = -1;
1413
1414 if (conf->auth_server && radius_client_init_auth(radius)) {
1415 radius_client_deinit(radius);
1416 return NULL;
1417 }
1418
1419 if (conf->acct_server && radius_client_init_acct(radius)) {
1420 radius_client_deinit(radius);
1421 return NULL;
1422 }
1423
1424 if (conf->retry_primary_interval)
1425 eloop_register_timeout(conf->retry_primary_interval, 0,
1426 radius_retry_primary_timer, radius,
1427 NULL);
1428
1429 return radius;
1430}
1431
1432
1433/**
1434 * radius_client_deinit - Deinitialize RADIUS client
1435 * @radius: RADIUS client context from radius_client_init()
1436 */
1437void radius_client_deinit(struct radius_client_data *radius)
1438{
1439 if (!radius)
1440 return;
1441
Dmitry Shmidt71757432014-06-02 13:50:35 -07001442 radius_close_auth_sockets(radius);
1443 radius_close_acct_sockets(radius);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001444
1445 eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
1446
1447 radius_client_flush(radius, 0);
1448 os_free(radius->auth_handlers);
1449 os_free(radius->acct_handlers);
1450 os_free(radius);
1451}
1452
1453
1454/**
1455 * radius_client_flush_auth - Flush pending RADIUS messages for an address
1456 * @radius: RADIUS client context from radius_client_init()
1457 * @addr: MAC address of the related device
1458 *
1459 * This function can be used to remove pending RADIUS authentication messages
1460 * that are related to a specific device. The addr parameter is matched with
1461 * the one used in radius_client_send() call that was used to transmit the
1462 * authentication request.
1463 */
1464void radius_client_flush_auth(struct radius_client_data *radius,
1465 const u8 *addr)
1466{
1467 struct radius_msg_list *entry, *prev, *tmp;
1468
1469 prev = NULL;
1470 entry = radius->msgs;
1471 while (entry) {
1472 if (entry->msg_type == RADIUS_AUTH &&
1473 os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
1474 hostapd_logger(radius->ctx, addr,
1475 HOSTAPD_MODULE_RADIUS,
1476 HOSTAPD_LEVEL_DEBUG,
1477 "Removing pending RADIUS authentication"
1478 " message for removed client");
1479
1480 if (prev)
1481 prev->next = entry->next;
1482 else
1483 radius->msgs = entry->next;
1484
1485 tmp = entry;
1486 entry = entry->next;
1487 radius_client_msg_free(tmp);
1488 radius->num_msgs--;
1489 continue;
1490 }
1491
1492 prev = entry;
1493 entry = entry->next;
1494 }
1495}
1496
1497
1498static int radius_client_dump_auth_server(char *buf, size_t buflen,
1499 struct hostapd_radius_server *serv,
1500 struct radius_client_data *cli)
1501{
1502 int pending = 0;
1503 struct radius_msg_list *msg;
1504 char abuf[50];
1505
1506 if (cli) {
1507 for (msg = cli->msgs; msg; msg = msg->next) {
1508 if (msg->msg_type == RADIUS_AUTH)
1509 pending++;
1510 }
1511 }
1512
1513 return os_snprintf(buf, buflen,
1514 "radiusAuthServerIndex=%d\n"
1515 "radiusAuthServerAddress=%s\n"
1516 "radiusAuthClientServerPortNumber=%d\n"
1517 "radiusAuthClientRoundTripTime=%d\n"
1518 "radiusAuthClientAccessRequests=%u\n"
1519 "radiusAuthClientAccessRetransmissions=%u\n"
1520 "radiusAuthClientAccessAccepts=%u\n"
1521 "radiusAuthClientAccessRejects=%u\n"
1522 "radiusAuthClientAccessChallenges=%u\n"
1523 "radiusAuthClientMalformedAccessResponses=%u\n"
1524 "radiusAuthClientBadAuthenticators=%u\n"
1525 "radiusAuthClientPendingRequests=%u\n"
1526 "radiusAuthClientTimeouts=%u\n"
1527 "radiusAuthClientUnknownTypes=%u\n"
1528 "radiusAuthClientPacketsDropped=%u\n",
1529 serv->index,
1530 hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
1531 serv->port,
1532 serv->round_trip_time,
1533 serv->requests,
1534 serv->retransmissions,
1535 serv->access_accepts,
1536 serv->access_rejects,
1537 serv->access_challenges,
1538 serv->malformed_responses,
1539 serv->bad_authenticators,
1540 pending,
1541 serv->timeouts,
1542 serv->unknown_types,
1543 serv->packets_dropped);
1544}
1545
1546
1547static int radius_client_dump_acct_server(char *buf, size_t buflen,
1548 struct hostapd_radius_server *serv,
1549 struct radius_client_data *cli)
1550{
1551 int pending = 0;
1552 struct radius_msg_list *msg;
1553 char abuf[50];
1554
1555 if (cli) {
1556 for (msg = cli->msgs; msg; msg = msg->next) {
1557 if (msg->msg_type == RADIUS_ACCT ||
1558 msg->msg_type == RADIUS_ACCT_INTERIM)
1559 pending++;
1560 }
1561 }
1562
1563 return os_snprintf(buf, buflen,
1564 "radiusAccServerIndex=%d\n"
1565 "radiusAccServerAddress=%s\n"
1566 "radiusAccClientServerPortNumber=%d\n"
1567 "radiusAccClientRoundTripTime=%d\n"
1568 "radiusAccClientRequests=%u\n"
1569 "radiusAccClientRetransmissions=%u\n"
1570 "radiusAccClientResponses=%u\n"
1571 "radiusAccClientMalformedResponses=%u\n"
1572 "radiusAccClientBadAuthenticators=%u\n"
1573 "radiusAccClientPendingRequests=%u\n"
1574 "radiusAccClientTimeouts=%u\n"
1575 "radiusAccClientUnknownTypes=%u\n"
1576 "radiusAccClientPacketsDropped=%u\n",
1577 serv->index,
1578 hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
1579 serv->port,
1580 serv->round_trip_time,
1581 serv->requests,
1582 serv->retransmissions,
1583 serv->responses,
1584 serv->malformed_responses,
1585 serv->bad_authenticators,
1586 pending,
1587 serv->timeouts,
1588 serv->unknown_types,
1589 serv->packets_dropped);
1590}
1591
1592
1593/**
1594 * radius_client_get_mib - Get RADIUS client MIB information
1595 * @radius: RADIUS client context from radius_client_init()
1596 * @buf: Buffer for returning MIB data in text format
1597 * @buflen: Maximum buf length in octets
1598 * Returns: Number of octets written into the buffer
1599 */
1600int radius_client_get_mib(struct radius_client_data *radius, char *buf,
1601 size_t buflen)
1602{
1603 struct hostapd_radius_servers *conf = radius->conf;
1604 int i;
1605 struct hostapd_radius_server *serv;
1606 int count = 0;
1607
1608 if (conf->auth_servers) {
1609 for (i = 0; i < conf->num_auth_servers; i++) {
1610 serv = &conf->auth_servers[i];
1611 count += radius_client_dump_auth_server(
1612 buf + count, buflen - count, serv,
1613 serv == conf->auth_server ?
1614 radius : NULL);
1615 }
1616 }
1617
1618 if (conf->acct_servers) {
1619 for (i = 0; i < conf->num_acct_servers; i++) {
1620 serv = &conf->acct_servers[i];
1621 count += radius_client_dump_acct_server(
1622 buf + count, buflen - count, serv,
1623 serv == conf->acct_server ?
1624 radius : NULL);
1625 }
1626 }
1627
1628 return count;
1629}
1630
1631
1632void radius_client_reconfig(struct radius_client_data *radius,
1633 struct hostapd_radius_servers *conf)
1634{
1635 if (radius)
1636 radius->conf = conf;
1637}