/src/hostap/src/radius/radius_client.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * RADIUS client |
3 | | * Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi> |
4 | | * |
5 | | * This software may be distributed under the terms of the BSD license. |
6 | | * See README for more details. |
7 | | */ |
8 | | |
9 | | #include "includes.h" |
10 | | #include <fcntl.h> |
11 | | #include <net/if.h> |
12 | | |
13 | | #include "common.h" |
14 | | #include "eloop.h" |
15 | | #include "crypto/tls.h" |
16 | | #include "radius.h" |
17 | | #include "radius_client.h" |
18 | | |
19 | | /* Defaults for RADIUS retransmit values (exponential backoff) */ |
20 | | |
21 | | /** |
22 | | * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds |
23 | | */ |
24 | 0 | #define RADIUS_CLIENT_FIRST_WAIT 3 |
25 | | |
26 | | /** |
27 | | * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds |
28 | | */ |
29 | 0 | #define RADIUS_CLIENT_MAX_WAIT 120 |
30 | | |
31 | | /** |
32 | | * RADIUS_CLIENT_MAX_FAILOVER - RADIUS client maximum retries |
33 | | * |
34 | | * Maximum number of server failovers before the entry is removed from |
35 | | * retransmit list. |
36 | | */ |
37 | 0 | #define RADIUS_CLIENT_MAX_FAILOVER 3 |
38 | | |
39 | | /** |
40 | | * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages |
41 | | * |
42 | | * Maximum number of entries in retransmit list (oldest entries will be |
43 | | * removed, if this limit is exceeded). |
44 | | */ |
45 | 0 | #define RADIUS_CLIENT_MAX_ENTRIES 30 |
46 | | |
47 | | /** |
48 | | * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point |
49 | | * |
50 | | * The number of failed retry attempts after which the RADIUS server will be |
51 | | * changed (if one of more backup servers are configured). |
52 | | */ |
53 | 0 | #define RADIUS_CLIENT_NUM_FAILOVER 4 |
54 | | |
55 | | |
56 | | /** |
57 | | * struct radius_rx_handler - RADIUS client RX handler |
58 | | * |
59 | | * This data structure is used internally inside the RADIUS client module to |
60 | | * store registered RX handlers. These handlers are registered by calls to |
61 | | * radius_client_register() and unregistered when the RADIUS client is |
62 | | * deinitialized with a call to radius_client_deinit(). |
63 | | */ |
64 | | struct radius_rx_handler { |
65 | | /** |
66 | | * handler - Received RADIUS message handler |
67 | | */ |
68 | | RadiusRxResult (*handler)(struct radius_msg *msg, |
69 | | struct radius_msg *req, |
70 | | const u8 *shared_secret, |
71 | | size_t shared_secret_len, |
72 | | void *data); |
73 | | |
74 | | /** |
75 | | * data - Context data for the handler |
76 | | */ |
77 | | void *data; |
78 | | }; |
79 | | |
80 | | |
81 | | /** |
82 | | * struct radius_msg_list - RADIUS client message retransmit list |
83 | | * |
84 | | * This data structure is used internally inside the RADIUS client module to |
85 | | * store pending RADIUS requests that may still need to be retransmitted. |
86 | | */ |
87 | | struct radius_msg_list { |
88 | | /** |
89 | | * addr - STA/client address |
90 | | * |
91 | | * This is used to find RADIUS messages for the same STA. |
92 | | */ |
93 | | u8 addr[ETH_ALEN]; |
94 | | |
95 | | /** |
96 | | * msg - RADIUS message |
97 | | */ |
98 | | struct radius_msg *msg; |
99 | | |
100 | | /** |
101 | | * msg_type - Message type |
102 | | */ |
103 | | RadiusType msg_type; |
104 | | |
105 | | /** |
106 | | * first_try - Time of the first transmission attempt |
107 | | */ |
108 | | os_time_t first_try; |
109 | | |
110 | | /** |
111 | | * next_try - Time for the next transmission attempt |
112 | | */ |
113 | | os_time_t next_try; |
114 | | |
115 | | /** |
116 | | * attempts - Number of transmission attempts for one server |
117 | | */ |
118 | | int attempts; |
119 | | |
120 | | /** |
121 | | * accu_attempts - Number of accumulated attempts |
122 | | */ |
123 | | int accu_attempts; |
124 | | |
125 | | /** |
126 | | * next_wait - Next retransmission wait time in seconds |
127 | | */ |
128 | | int next_wait; |
129 | | |
130 | | /** |
131 | | * last_attempt - Time of the last transmission attempt |
132 | | */ |
133 | | struct os_reltime last_attempt; |
134 | | |
135 | | /** |
136 | | * shared_secret - Shared secret with the target RADIUS server |
137 | | */ |
138 | | const u8 *shared_secret; |
139 | | |
140 | | /** |
141 | | * shared_secret_len - shared_secret length in octets |
142 | | */ |
143 | | size_t shared_secret_len; |
144 | | |
145 | | /* TODO: server config with failover to backup server(s) */ |
146 | | |
147 | | /** |
148 | | * next - Next message in the list |
149 | | */ |
150 | | struct radius_msg_list *next; |
151 | | }; |
152 | | |
153 | | |
154 | | /** |
155 | | * struct radius_client_data - Internal RADIUS client data |
156 | | * |
157 | | * This data structure is used internally inside the RADIUS client module. |
158 | | * External users allocate this by calling radius_client_init() and free it by |
159 | | * calling radius_client_deinit(). The pointer to this opaque data is used in |
160 | | * calls to other functions as an identifier for the RADIUS client instance. |
161 | | */ |
162 | | struct radius_client_data { |
163 | | /** |
164 | | * ctx - Context pointer for hostapd_logger() callbacks |
165 | | */ |
166 | | void *ctx; |
167 | | |
168 | | /** |
169 | | * conf - RADIUS client configuration (list of RADIUS servers to use) |
170 | | */ |
171 | | struct hostapd_radius_servers *conf; |
172 | | |
173 | | /** |
174 | | * auth_sock - Currently used socket for RADIUS authentication server |
175 | | */ |
176 | | int auth_sock; |
177 | | |
178 | | /** |
179 | | * auth_tls - Whether current authentication connection uses TLS |
180 | | */ |
181 | | bool auth_tls; |
182 | | |
183 | | /** |
184 | | * auth_tls_ready - Whether authentication TLS is ready |
185 | | */ |
186 | | bool auth_tls_ready; |
187 | | |
188 | | /** |
189 | | * acct_sock - Currently used socket for RADIUS accounting server |
190 | | */ |
191 | | int acct_sock; |
192 | | |
193 | | /** |
194 | | * acct_tls - Whether current accounting connection uses TLS |
195 | | */ |
196 | | bool acct_tls; |
197 | | |
198 | | /** |
199 | | * acct_tls_ready - Whether accounting TLS is ready |
200 | | */ |
201 | | bool acct_tls_ready; |
202 | | |
203 | | /** |
204 | | * auth_handlers - Authentication message handlers |
205 | | */ |
206 | | struct radius_rx_handler *auth_handlers; |
207 | | |
208 | | /** |
209 | | * num_auth_handlers - Number of handlers in auth_handlers |
210 | | */ |
211 | | size_t num_auth_handlers; |
212 | | |
213 | | /** |
214 | | * acct_handlers - Accounting message handlers |
215 | | */ |
216 | | struct radius_rx_handler *acct_handlers; |
217 | | |
218 | | /** |
219 | | * num_acct_handlers - Number of handlers in acct_handlers |
220 | | */ |
221 | | size_t num_acct_handlers; |
222 | | |
223 | | /** |
224 | | * msgs - Pending outgoing RADIUS messages |
225 | | */ |
226 | | struct radius_msg_list *msgs; |
227 | | |
228 | | /** |
229 | | * num_msgs - Number of pending messages in the msgs list |
230 | | */ |
231 | | size_t num_msgs; |
232 | | |
233 | | /** |
234 | | * next_radius_identifier - Next RADIUS message identifier to use |
235 | | */ |
236 | | u8 next_radius_identifier; |
237 | | |
238 | | /** |
239 | | * interim_error_cb - Interim accounting error callback |
240 | | */ |
241 | | void (*interim_error_cb)(const u8 *addr, void *ctx); |
242 | | |
243 | | /** |
244 | | * interim_error_cb_ctx - interim_error_cb() context data |
245 | | */ |
246 | | void *interim_error_cb_ctx; |
247 | | |
248 | | #ifdef CONFIG_RADIUS_TLS |
249 | | void *tls_ctx; |
250 | | struct tls_connection *auth_tls_conn; |
251 | | struct tls_connection *acct_tls_conn; |
252 | | #endif /* CONFIG_RADIUS_TLS */ |
253 | | }; |
254 | | |
255 | | |
256 | | static int |
257 | | radius_change_server(struct radius_client_data *radius, |
258 | | struct hostapd_radius_server *nserv, |
259 | | struct hostapd_radius_server *oserv, |
260 | | int auth); |
261 | | static int radius_client_init_acct(struct radius_client_data *radius); |
262 | | static int radius_client_init_auth(struct radius_client_data *radius); |
263 | | static void radius_client_auth_failover(struct radius_client_data *radius); |
264 | | static void radius_client_acct_failover(struct radius_client_data *radius); |
265 | | |
266 | | |
267 | | static void radius_client_msg_free(struct radius_msg_list *req) |
268 | 0 | { |
269 | 0 | radius_msg_free(req->msg); |
270 | 0 | os_free(req); |
271 | 0 | } |
272 | | |
273 | | |
274 | | /** |
275 | | * radius_client_register - Register a RADIUS client RX handler |
276 | | * @radius: RADIUS client context from radius_client_init() |
277 | | * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT) |
278 | | * @handler: Handler for received RADIUS messages |
279 | | * @data: Context pointer for handler callbacks |
280 | | * Returns: 0 on success, -1 on failure |
281 | | * |
282 | | * This function is used to register a handler for processing received RADIUS |
283 | | * authentication and accounting messages. The handler() callback function will |
284 | | * be called whenever a RADIUS message is received from the active server. |
285 | | * |
286 | | * There can be multiple registered RADIUS message handlers. The handlers will |
287 | | * be called in order until one of them indicates that it has processed or |
288 | | * queued the message. |
289 | | */ |
290 | | int radius_client_register(struct radius_client_data *radius, |
291 | | RadiusType msg_type, |
292 | | RadiusRxResult (*handler)(struct radius_msg *msg, |
293 | | struct radius_msg *req, |
294 | | const u8 *shared_secret, |
295 | | size_t shared_secret_len, |
296 | | void *data), |
297 | | void *data) |
298 | 0 | { |
299 | 0 | struct radius_rx_handler **handlers, *newh; |
300 | 0 | size_t *num; |
301 | |
|
302 | 0 | if (msg_type == RADIUS_ACCT) { |
303 | 0 | handlers = &radius->acct_handlers; |
304 | 0 | num = &radius->num_acct_handlers; |
305 | 0 | } else { |
306 | 0 | handlers = &radius->auth_handlers; |
307 | 0 | num = &radius->num_auth_handlers; |
308 | 0 | } |
309 | |
|
310 | 0 | newh = os_realloc_array(*handlers, *num + 1, |
311 | 0 | sizeof(struct radius_rx_handler)); |
312 | 0 | if (newh == NULL) |
313 | 0 | return -1; |
314 | | |
315 | 0 | newh[*num].handler = handler; |
316 | 0 | newh[*num].data = data; |
317 | 0 | (*num)++; |
318 | 0 | *handlers = newh; |
319 | |
|
320 | 0 | return 0; |
321 | 0 | } |
322 | | |
323 | | |
324 | | /** |
325 | | * radius_client_set_interim_erro_cb - Register an interim acct error callback |
326 | | * @radius: RADIUS client context from radius_client_init() |
327 | | * @addr: Station address from the failed message |
328 | | * @cb: Handler for interim accounting errors |
329 | | * @ctx: Context pointer for handler callbacks |
330 | | * |
331 | | * This function is used to register a handler for processing failed |
332 | | * transmission attempts of interim accounting update messages. |
333 | | */ |
334 | | void radius_client_set_interim_error_cb(struct radius_client_data *radius, |
335 | | void (*cb)(const u8 *addr, void *ctx), |
336 | | void *ctx) |
337 | 0 | { |
338 | 0 | radius->interim_error_cb = cb; |
339 | 0 | radius->interim_error_cb_ctx = ctx; |
340 | 0 | } |
341 | | |
342 | | |
343 | | /* |
344 | | * Returns >0 if message queue was flushed (i.e., the message that triggered |
345 | | * the error is not available anymore) |
346 | | */ |
347 | | static int radius_client_handle_send_error(struct radius_client_data *radius, |
348 | | int s, RadiusType msg_type) |
349 | 0 | { |
350 | 0 | #ifndef CONFIG_NATIVE_WINDOWS |
351 | 0 | int _errno = errno; |
352 | 0 | wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno)); |
353 | 0 | if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || |
354 | 0 | _errno == EBADF || _errno == ENETUNREACH || _errno == EACCES) { |
355 | 0 | hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, |
356 | 0 | HOSTAPD_LEVEL_INFO, |
357 | 0 | "Send failed - maybe interface status changed -" |
358 | 0 | " try to connect again"); |
359 | 0 | if (msg_type == RADIUS_ACCT || |
360 | 0 | msg_type == RADIUS_ACCT_INTERIM) { |
361 | 0 | radius_client_init_acct(radius); |
362 | 0 | return 0; |
363 | 0 | } else { |
364 | 0 | radius_client_init_auth(radius); |
365 | 0 | return 1; |
366 | 0 | } |
367 | 0 | } |
368 | 0 | #endif /* CONFIG_NATIVE_WINDOWS */ |
369 | | |
370 | 0 | return 0; |
371 | 0 | } |
372 | | |
373 | | |
374 | | static int radius_client_retransmit(struct radius_client_data *radius, |
375 | | struct radius_msg_list *entry, |
376 | | os_time_t now) |
377 | 0 | { |
378 | 0 | struct hostapd_radius_servers *conf = radius->conf; |
379 | 0 | int s; |
380 | 0 | struct wpabuf *buf; |
381 | 0 | size_t prev_num_msgs; |
382 | 0 | u8 *acct_delay_time; |
383 | 0 | size_t acct_delay_time_len; |
384 | 0 | int num_servers; |
385 | | #ifdef CONFIG_RADIUS_TLS |
386 | | struct wpabuf *out = NULL; |
387 | | struct tls_connection *conn = NULL; |
388 | | bool acct = false; |
389 | | #endif /* CONFIG_RADIUS_TLS */ |
390 | |
|
391 | 0 | if (entry->msg_type == RADIUS_ACCT || |
392 | 0 | entry->msg_type == RADIUS_ACCT_INTERIM) { |
393 | | #ifdef CONFIG_RADIUS_TLS |
394 | | acct = true; |
395 | | if (radius->acct_tls) |
396 | | conn = radius->acct_tls_conn; |
397 | | #endif /* CONFIG_RADIUS_TLS */ |
398 | 0 | num_servers = conf->num_acct_servers; |
399 | 0 | if (radius->acct_sock < 0) |
400 | 0 | radius_client_init_acct(radius); |
401 | 0 | if (radius->acct_sock < 0 && conf->num_acct_servers > 1) { |
402 | 0 | prev_num_msgs = radius->num_msgs; |
403 | 0 | radius_client_acct_failover(radius); |
404 | 0 | if (prev_num_msgs != radius->num_msgs) |
405 | 0 | return 0; |
406 | 0 | } |
407 | 0 | s = radius->acct_sock; |
408 | 0 | if (entry->attempts == 0) |
409 | 0 | conf->acct_server->requests++; |
410 | 0 | else { |
411 | 0 | conf->acct_server->timeouts++; |
412 | 0 | conf->acct_server->retransmissions++; |
413 | 0 | } |
414 | 0 | } else { |
415 | | #ifdef CONFIG_RADIUS_TLS |
416 | | if (radius->auth_tls) |
417 | | conn = radius->auth_tls_conn; |
418 | | #endif /* CONFIG_RADIUS_TLS */ |
419 | 0 | num_servers = conf->num_auth_servers; |
420 | 0 | if (radius->auth_sock < 0) |
421 | 0 | radius_client_init_auth(radius); |
422 | 0 | if (radius->auth_sock < 0 && conf->num_auth_servers > 1) { |
423 | 0 | prev_num_msgs = radius->num_msgs; |
424 | 0 | radius_client_auth_failover(radius); |
425 | 0 | if (prev_num_msgs != radius->num_msgs) |
426 | 0 | return 0; |
427 | 0 | } |
428 | 0 | s = radius->auth_sock; |
429 | 0 | if (entry->attempts == 0) |
430 | 0 | conf->auth_server->requests++; |
431 | 0 | else { |
432 | 0 | conf->auth_server->timeouts++; |
433 | 0 | conf->auth_server->retransmissions++; |
434 | 0 | } |
435 | 0 | } |
436 | | |
437 | 0 | if (entry->msg_type == RADIUS_ACCT_INTERIM) { |
438 | 0 | wpa_printf(MSG_DEBUG, |
439 | 0 | "RADIUS: Failed to transmit interim accounting update to " |
440 | 0 | MACSTR " - drop message and request a new update", |
441 | 0 | MAC2STR(entry->addr)); |
442 | 0 | if (radius->interim_error_cb) |
443 | 0 | radius->interim_error_cb(entry->addr, |
444 | 0 | radius->interim_error_cb_ctx); |
445 | 0 | return 1; |
446 | 0 | } |
447 | | |
448 | 0 | if (s < 0) { |
449 | 0 | wpa_printf(MSG_INFO, |
450 | 0 | "RADIUS: No valid socket for retransmission"); |
451 | 0 | return 1; |
452 | 0 | } |
453 | | |
454 | | #ifdef CONFIG_RADIUS_TLS |
455 | | if ((acct && radius->acct_tls && !radius->acct_tls_ready) || |
456 | | (!acct && radius->auth_tls && !radius->auth_tls_ready)) { |
457 | | wpa_printf(MSG_DEBUG, |
458 | | "RADIUS: TLS connection not yet ready for TX"); |
459 | | goto not_ready; |
460 | | } |
461 | | #endif /* CONFIG_RADIUS_TLS */ |
462 | | |
463 | 0 | if (entry->msg_type == RADIUS_ACCT && |
464 | 0 | radius_msg_get_attr_ptr(entry->msg, RADIUS_ATTR_ACCT_DELAY_TIME, |
465 | 0 | &acct_delay_time, &acct_delay_time_len, |
466 | 0 | NULL) == 0 && |
467 | 0 | acct_delay_time_len == 4) { |
468 | 0 | struct radius_hdr *hdr; |
469 | 0 | u32 delay_time; |
470 | | |
471 | | /* |
472 | | * Need to assign a new identifier since attribute contents |
473 | | * changes. |
474 | | */ |
475 | 0 | hdr = radius_msg_get_hdr(entry->msg); |
476 | 0 | hdr->identifier = radius_client_get_id(radius); |
477 | | |
478 | | /* Update Acct-Delay-Time to show wait time in queue */ |
479 | 0 | delay_time = now - entry->first_try; |
480 | 0 | WPA_PUT_BE32(acct_delay_time, delay_time); |
481 | |
|
482 | 0 | wpa_printf(MSG_DEBUG, |
483 | 0 | "RADIUS: Updated Acct-Delay-Time to %u for retransmission", |
484 | 0 | delay_time); |
485 | 0 | if (radius_msg_finish_acct(entry->msg, entry->shared_secret, |
486 | 0 | entry->shared_secret_len) < 0) { |
487 | 0 | wpa_printf(MSG_INFO, "Failed to build RADIUS message"); |
488 | 0 | return -1; |
489 | 0 | } |
490 | 0 | if (radius->conf->msg_dumps) |
491 | 0 | radius_msg_dump(entry->msg); |
492 | 0 | } |
493 | | |
494 | | /* retransmit; remove entry if too many attempts */ |
495 | 0 | if (entry->accu_attempts >= RADIUS_CLIENT_MAX_FAILOVER * |
496 | 0 | RADIUS_CLIENT_NUM_FAILOVER * num_servers) { |
497 | 0 | wpa_printf(MSG_INFO, |
498 | 0 | "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); |
499 | 0 | return 1; |
500 | 0 | } |
501 | | |
502 | 0 | entry->attempts++; |
503 | 0 | entry->accu_attempts++; |
504 | 0 | hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, |
505 | 0 | HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", |
506 | 0 | radius_msg_get_hdr(entry->msg)->identifier); |
507 | |
|
508 | 0 | os_get_reltime(&entry->last_attempt); |
509 | 0 | buf = radius_msg_get_buf(entry->msg); |
510 | | #ifdef CONFIG_RADIUS_TLS |
511 | | if (conn) { |
512 | | out = tls_connection_encrypt(radius->tls_ctx, conn, buf); |
513 | | if (!out) { |
514 | | wpa_printf(MSG_INFO, |
515 | | "RADIUS: Failed to encrypt RADIUS message (TLS)"); |
516 | | return -1; |
517 | | } |
518 | | wpa_printf(MSG_DEBUG, |
519 | | "RADIUS: TLS encryption of %zu bytes of plaintext to %zu bytes of ciphertext", |
520 | | wpabuf_len(buf), wpabuf_len(out)); |
521 | | buf = out; |
522 | | } |
523 | | #endif /* CONFIG_RADIUS_TLS */ |
524 | |
|
525 | 0 | wpa_printf(MSG_DEBUG, "RADIUS: Send %zu bytes to the server", |
526 | 0 | wpabuf_len(buf)); |
527 | 0 | if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { |
528 | 0 | if (radius_client_handle_send_error(radius, s, entry->msg_type) |
529 | 0 | > 0) { |
530 | | #ifdef CONFIG_RADIUS_TLS |
531 | | wpabuf_free(out); |
532 | | #endif /* CONFIG_RADIUS_TLS */ |
533 | 0 | return 0; |
534 | 0 | } |
535 | 0 | } |
536 | | #ifdef CONFIG_RADIUS_TLS |
537 | | wpabuf_free(out); |
538 | | |
539 | | not_ready: |
540 | | #endif /* CONFIG_RADIUS_TLS */ |
541 | | |
542 | 0 | entry->next_try = now + entry->next_wait; |
543 | 0 | entry->next_wait *= 2; |
544 | 0 | if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) |
545 | 0 | entry->next_wait = RADIUS_CLIENT_MAX_WAIT; |
546 | |
|
547 | 0 | return 0; |
548 | 0 | } |
549 | | |
550 | | |
551 | | static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) |
552 | 0 | { |
553 | 0 | struct radius_client_data *radius = eloop_ctx; |
554 | 0 | struct os_reltime now; |
555 | 0 | os_time_t first; |
556 | 0 | struct radius_msg_list *entry, *prev, *tmp; |
557 | 0 | int auth_failover = 0, acct_failover = 0; |
558 | 0 | size_t prev_num_msgs; |
559 | 0 | int s; |
560 | |
|
561 | 0 | entry = radius->msgs; |
562 | 0 | if (!entry) |
563 | 0 | return; |
564 | | |
565 | 0 | os_get_reltime(&now); |
566 | |
|
567 | 0 | while (entry) { |
568 | 0 | if (now.sec >= entry->next_try) { |
569 | 0 | s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock : |
570 | 0 | radius->acct_sock; |
571 | 0 | if (entry->attempts >= RADIUS_CLIENT_NUM_FAILOVER || |
572 | 0 | (s < 0 && entry->attempts > 0)) { |
573 | 0 | if (entry->msg_type == RADIUS_ACCT || |
574 | 0 | entry->msg_type == RADIUS_ACCT_INTERIM) |
575 | 0 | acct_failover++; |
576 | 0 | else |
577 | 0 | auth_failover++; |
578 | 0 | } |
579 | 0 | } |
580 | 0 | entry = entry->next; |
581 | 0 | } |
582 | |
|
583 | 0 | if (auth_failover) |
584 | 0 | radius_client_auth_failover(radius); |
585 | |
|
586 | 0 | if (acct_failover) |
587 | 0 | radius_client_acct_failover(radius); |
588 | |
|
589 | 0 | entry = radius->msgs; |
590 | 0 | first = 0; |
591 | |
|
592 | 0 | prev = NULL; |
593 | 0 | while (entry) { |
594 | 0 | prev_num_msgs = radius->num_msgs; |
595 | 0 | if (now.sec >= entry->next_try && |
596 | 0 | radius_client_retransmit(radius, entry, now.sec)) { |
597 | 0 | if (prev) |
598 | 0 | prev->next = entry->next; |
599 | 0 | else |
600 | 0 | radius->msgs = entry->next; |
601 | |
|
602 | 0 | tmp = entry; |
603 | 0 | entry = entry->next; |
604 | 0 | radius_client_msg_free(tmp); |
605 | 0 | radius->num_msgs--; |
606 | 0 | continue; |
607 | 0 | } |
608 | | |
609 | 0 | if (prev_num_msgs != radius->num_msgs) { |
610 | 0 | wpa_printf(MSG_DEBUG, |
611 | 0 | "RADIUS: Message removed from queue - restart from beginning"); |
612 | 0 | entry = radius->msgs; |
613 | 0 | prev = NULL; |
614 | 0 | continue; |
615 | 0 | } |
616 | | |
617 | 0 | if (first == 0 || entry->next_try < first) |
618 | 0 | first = entry->next_try; |
619 | |
|
620 | 0 | prev = entry; |
621 | 0 | entry = entry->next; |
622 | 0 | } |
623 | |
|
624 | 0 | if (radius->msgs) { |
625 | 0 | if (first < now.sec) |
626 | 0 | first = now.sec; |
627 | 0 | eloop_cancel_timeout(radius_client_timer, radius, NULL); |
628 | 0 | eloop_register_timeout(first - now.sec, 0, |
629 | 0 | radius_client_timer, radius, NULL); |
630 | 0 | hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, |
631 | 0 | HOSTAPD_LEVEL_DEBUG, "Next RADIUS client " |
632 | 0 | "retransmit in %ld seconds", |
633 | 0 | (long int) (first - now.sec)); |
634 | 0 | } |
635 | 0 | } |
636 | | |
637 | | |
638 | | static void radius_client_auth_failover(struct radius_client_data *radius) |
639 | 0 | { |
640 | 0 | struct hostapd_radius_servers *conf = radius->conf; |
641 | 0 | struct hostapd_radius_server *next, *old; |
642 | 0 | struct radius_msg_list *entry; |
643 | 0 | char abuf[50]; |
644 | |
|
645 | 0 | old = conf->auth_server; |
646 | 0 | hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, |
647 | 0 | HOSTAPD_LEVEL_NOTICE, |
648 | 0 | "No response from Authentication server %s:%d - failover", |
649 | 0 | hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), |
650 | 0 | old->port); |
651 | |
|
652 | 0 | for (entry = radius->msgs; entry; entry = entry->next) { |
653 | 0 | if (entry->msg_type == RADIUS_AUTH) |
654 | 0 | old->timeouts++; |
655 | 0 | } |
656 | |
|
657 | 0 | next = old + 1; |
658 | 0 | if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) |
659 | 0 | next = conf->auth_servers; |
660 | 0 | conf->auth_server = next; |
661 | 0 | radius_change_server(radius, next, old, 1); |
662 | 0 | } |
663 | | |
664 | | |
665 | | static void radius_client_acct_failover(struct radius_client_data *radius) |
666 | 0 | { |
667 | 0 | struct hostapd_radius_servers *conf = radius->conf; |
668 | 0 | struct hostapd_radius_server *next, *old; |
669 | 0 | struct radius_msg_list *entry; |
670 | 0 | char abuf[50]; |
671 | |
|
672 | 0 | old = conf->acct_server; |
673 | 0 | hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, |
674 | 0 | HOSTAPD_LEVEL_NOTICE, |
675 | 0 | "No response from Accounting server %s:%d - failover", |
676 | 0 | hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), |
677 | 0 | old->port); |
678 | |
|
679 | 0 | for (entry = radius->msgs; entry; entry = entry->next) { |
680 | 0 | if (entry->msg_type == RADIUS_ACCT || |
681 | 0 | entry->msg_type == RADIUS_ACCT_INTERIM) |
682 | 0 | old->timeouts++; |
683 | 0 | } |
684 | |
|
685 | 0 | next = old + 1; |
686 | 0 | if (next > &conf->acct_servers[conf->num_acct_servers - 1]) |
687 | 0 | next = conf->acct_servers; |
688 | 0 | conf->acct_server = next; |
689 | 0 | radius_change_server(radius, next, old, 0); |
690 | 0 | } |
691 | | |
692 | | |
693 | | static void radius_client_update_timeout(struct radius_client_data *radius) |
694 | 0 | { |
695 | 0 | struct os_reltime now; |
696 | 0 | os_time_t first; |
697 | 0 | struct radius_msg_list *entry; |
698 | |
|
699 | 0 | eloop_cancel_timeout(radius_client_timer, radius, NULL); |
700 | |
|
701 | 0 | if (radius->msgs == NULL) { |
702 | 0 | return; |
703 | 0 | } |
704 | | |
705 | 0 | first = 0; |
706 | 0 | for (entry = radius->msgs; entry; entry = entry->next) { |
707 | 0 | if (first == 0 || entry->next_try < first) |
708 | 0 | first = entry->next_try; |
709 | 0 | } |
710 | |
|
711 | 0 | os_get_reltime(&now); |
712 | 0 | if (first < now.sec) |
713 | 0 | first = now.sec; |
714 | 0 | eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, |
715 | 0 | NULL); |
716 | 0 | hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, |
717 | 0 | HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" |
718 | 0 | " %ld seconds", (long int) (first - now.sec)); |
719 | 0 | } |
720 | | |
721 | | |
722 | | static void radius_client_list_add(struct radius_client_data *radius, |
723 | | struct radius_msg *msg, |
724 | | RadiusType msg_type, |
725 | | const u8 *shared_secret, |
726 | | size_t shared_secret_len, const u8 *addr) |
727 | 0 | { |
728 | 0 | struct radius_msg_list *entry, *prev; |
729 | |
|
730 | 0 | if (eloop_terminated()) { |
731 | | /* No point in adding entries to retransmit queue since event |
732 | | * loop has already been terminated. */ |
733 | 0 | radius_msg_free(msg); |
734 | 0 | return; |
735 | 0 | } |
736 | | |
737 | 0 | entry = os_zalloc(sizeof(*entry)); |
738 | 0 | if (entry == NULL) { |
739 | 0 | wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list"); |
740 | 0 | radius_msg_free(msg); |
741 | 0 | return; |
742 | 0 | } |
743 | | |
744 | 0 | if (addr) |
745 | 0 | os_memcpy(entry->addr, addr, ETH_ALEN); |
746 | 0 | entry->msg = msg; |
747 | 0 | entry->msg_type = msg_type; |
748 | 0 | entry->shared_secret = shared_secret; |
749 | 0 | entry->shared_secret_len = shared_secret_len; |
750 | 0 | os_get_reltime(&entry->last_attempt); |
751 | 0 | entry->first_try = entry->last_attempt.sec; |
752 | 0 | entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; |
753 | 0 | entry->attempts = 1; |
754 | 0 | entry->accu_attempts = 1; |
755 | 0 | entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; |
756 | 0 | if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) |
757 | 0 | entry->next_wait = RADIUS_CLIENT_MAX_WAIT; |
758 | 0 | entry->next = radius->msgs; |
759 | 0 | radius->msgs = entry; |
760 | 0 | radius_client_update_timeout(radius); |
761 | |
|
762 | 0 | if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { |
763 | 0 | wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits"); |
764 | 0 | prev = NULL; |
765 | 0 | while (entry->next) { |
766 | 0 | prev = entry; |
767 | 0 | entry = entry->next; |
768 | 0 | } |
769 | 0 | if (prev) { |
770 | 0 | prev->next = NULL; |
771 | 0 | radius_client_msg_free(entry); |
772 | 0 | } |
773 | 0 | } else |
774 | 0 | radius->num_msgs++; |
775 | 0 | } |
776 | | |
777 | | |
778 | | static int radius_client_disable_pmtu_discovery(int s) |
779 | 0 | { |
780 | 0 | int r = -1; |
781 | 0 | #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) |
782 | | /* Turn off Path MTU discovery on IPv4/UDP sockets. */ |
783 | 0 | int action = IP_PMTUDISC_DONT; |
784 | 0 | r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, |
785 | 0 | sizeof(action)); |
786 | 0 | if (r == -1) |
787 | 0 | wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s", |
788 | 0 | strerror(errno)); |
789 | 0 | #endif |
790 | 0 | return r; |
791 | 0 | } |
792 | | |
793 | | |
794 | | static void radius_close_auth_socket(struct radius_client_data *radius) |
795 | 0 | { |
796 | 0 | if (radius->auth_sock >= 0) { |
797 | | #ifdef CONFIG_RADIUS_TLS |
798 | | if (radius->conf->auth_server->tls) |
799 | | eloop_unregister_sock(radius->auth_sock, |
800 | | EVENT_TYPE_WRITE); |
801 | | #endif /* CONFIG_RADIUS_TLS */ |
802 | 0 | eloop_unregister_read_sock(radius->auth_sock); |
803 | 0 | close(radius->auth_sock); |
804 | 0 | radius->auth_sock = -1; |
805 | 0 | } |
806 | 0 | } |
807 | | |
808 | | |
809 | | static void radius_close_acct_socket(struct radius_client_data *radius) |
810 | 0 | { |
811 | 0 | if (radius->acct_sock >= 0) { |
812 | | #ifdef CONFIG_RADIUS_TLS |
813 | | if (radius->conf->acct_server->tls) |
814 | | eloop_unregister_sock(radius->acct_sock, |
815 | | EVENT_TYPE_WRITE); |
816 | | #endif /* CONFIG_RADIUS_TLS */ |
817 | 0 | eloop_unregister_read_sock(radius->acct_sock); |
818 | 0 | close(radius->acct_sock); |
819 | 0 | radius->acct_sock = -1; |
820 | 0 | } |
821 | 0 | } |
822 | | |
823 | | |
824 | | /** |
825 | | * radius_client_send - Send a RADIUS request |
826 | | * @radius: RADIUS client context from radius_client_init() |
827 | | * @msg: RADIUS message to be sent |
828 | | * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM) |
829 | | * @addr: MAC address of the device related to this message or %NULL |
830 | | * Returns: 0 on success, -1 on failure |
831 | | * |
832 | | * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or |
833 | | * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference |
834 | | * between accounting and interim accounting messages is that the interim |
835 | | * message will not be retransmitted. Instead, a callback is used to indicate |
836 | | * that the transmission failed for the specific station @addr so that a new |
837 | | * interim accounting update message can be generated with up-to-date session |
838 | | * data instead of trying to resend old information. |
839 | | * |
840 | | * The message is added on the retransmission queue and will be retransmitted |
841 | | * automatically until a response is received or maximum number of retries |
842 | | * (RADIUS_CLIENT_MAX_FAILOVER * RADIUS_CLIENT_NUM_FAILOVER) is reached. No |
843 | | * such retries are used with RADIUS_ACCT_INTERIM, i.e., such a pending message |
844 | | * is removed from the queue automatically on transmission failure. |
845 | | * |
846 | | * The related device MAC address can be used to identify pending messages that |
847 | | * can be removed with radius_client_flush_auth(). |
848 | | */ |
849 | | int radius_client_send(struct radius_client_data *radius, |
850 | | struct radius_msg *msg, RadiusType msg_type, |
851 | | const u8 *addr) |
852 | 0 | { |
853 | 0 | struct hostapd_radius_servers *conf = radius->conf; |
854 | 0 | const u8 *shared_secret; |
855 | 0 | size_t shared_secret_len; |
856 | 0 | char *name; |
857 | 0 | int s, res; |
858 | 0 | struct wpabuf *buf; |
859 | | #ifdef CONFIG_RADIUS_TLS |
860 | | struct wpabuf *out = NULL; |
861 | | struct tls_connection *conn = NULL; |
862 | | bool acct = false; |
863 | | #endif /* CONFIG_RADIUS_TLS */ |
864 | |
|
865 | 0 | if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { |
866 | | #ifdef CONFIG_RADIUS_TLS |
867 | | acct = true; |
868 | | if (radius->acct_tls) |
869 | | conn = radius->acct_tls_conn; |
870 | | #endif /* CONFIG_RADIUS_TLS */ |
871 | 0 | if (conf->acct_server && radius->acct_sock < 0) |
872 | 0 | radius_client_init_acct(radius); |
873 | |
|
874 | 0 | if (conf->acct_server == NULL || radius->acct_sock < 0 || |
875 | 0 | conf->acct_server->shared_secret == NULL) { |
876 | 0 | hostapd_logger(radius->ctx, NULL, |
877 | 0 | HOSTAPD_MODULE_RADIUS, |
878 | 0 | HOSTAPD_LEVEL_INFO, |
879 | 0 | "No accounting server configured"); |
880 | 0 | return -1; |
881 | 0 | } |
882 | 0 | shared_secret = conf->acct_server->shared_secret; |
883 | 0 | shared_secret_len = conf->acct_server->shared_secret_len; |
884 | 0 | if (radius_msg_finish_acct(msg, shared_secret, |
885 | 0 | shared_secret_len) < 0) { |
886 | 0 | hostapd_logger(radius->ctx, NULL, |
887 | 0 | HOSTAPD_MODULE_RADIUS, |
888 | 0 | HOSTAPD_LEVEL_INFO, |
889 | 0 | "Failed to build RADIUS accounting message"); |
890 | 0 | return -1; |
891 | 0 | } |
892 | 0 | name = "accounting"; |
893 | 0 | s = radius->acct_sock; |
894 | 0 | conf->acct_server->requests++; |
895 | 0 | } else { |
896 | | #ifdef CONFIG_RADIUS_TLS |
897 | | if (radius->auth_tls) |
898 | | conn = radius->auth_tls_conn; |
899 | | #endif /* CONFIG_RADIUS_TLS */ |
900 | 0 | if (conf->auth_server && radius->auth_sock < 0) |
901 | 0 | radius_client_init_auth(radius); |
902 | |
|
903 | 0 | if (conf->auth_server == NULL || radius->auth_sock < 0 || |
904 | 0 | conf->auth_server->shared_secret == NULL) { |
905 | 0 | hostapd_logger(radius->ctx, NULL, |
906 | 0 | HOSTAPD_MODULE_RADIUS, |
907 | 0 | HOSTAPD_LEVEL_INFO, |
908 | 0 | "No authentication server configured"); |
909 | 0 | return -1; |
910 | 0 | } |
911 | 0 | shared_secret = conf->auth_server->shared_secret; |
912 | 0 | shared_secret_len = conf->auth_server->shared_secret_len; |
913 | 0 | if (radius_msg_finish(msg, shared_secret, shared_secret_len) < |
914 | 0 | 0) { |
915 | 0 | hostapd_logger(radius->ctx, NULL, |
916 | 0 | HOSTAPD_MODULE_RADIUS, |
917 | 0 | HOSTAPD_LEVEL_INFO, |
918 | 0 | "Failed to build RADIUS authentication message"); |
919 | 0 | return -1; |
920 | 0 | } |
921 | 0 | name = "authentication"; |
922 | 0 | s = radius->auth_sock; |
923 | 0 | conf->auth_server->requests++; |
924 | 0 | } |
925 | | |
926 | 0 | hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, |
927 | 0 | HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s " |
928 | 0 | "server", name); |
929 | 0 | if (conf->msg_dumps) |
930 | 0 | radius_msg_dump(msg); |
931 | |
|
932 | | #ifdef CONFIG_RADIUS_TLS |
933 | | if ((acct && radius->acct_tls && !radius->acct_tls_ready) || |
934 | | (!acct && radius->auth_tls && !radius->auth_tls_ready)) { |
935 | | wpa_printf(MSG_DEBUG, |
936 | | "RADIUS: TLS connection not yet ready for TX"); |
937 | | goto skip_send; |
938 | | } |
939 | | #endif /* CONFIG_RADIUS_TLS */ |
940 | |
|
941 | 0 | buf = radius_msg_get_buf(msg); |
942 | | #ifdef CONFIG_RADIUS_TLS |
943 | | if (conn) { |
944 | | out = tls_connection_encrypt(radius->tls_ctx, conn, buf); |
945 | | if (!out) { |
946 | | wpa_printf(MSG_INFO, |
947 | | "RADIUS: Failed to encrypt RADIUS message (TLS)"); |
948 | | return -1; |
949 | | } |
950 | | wpa_printf(MSG_DEBUG, |
951 | | "RADIUS: TLS encryption of %zu bytes of plaintext to %zu bytes of ciphertext", |
952 | | wpabuf_len(buf), wpabuf_len(out)); |
953 | | buf = out; |
954 | | } |
955 | | #endif /* CONFIG_RADIUS_TLS */ |
956 | 0 | wpa_printf(MSG_DEBUG, "RADIUS: Send %zu bytes to the server", |
957 | 0 | wpabuf_len(buf)); |
958 | 0 | res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0); |
959 | | #ifdef CONFIG_RADIUS_TLS |
960 | | wpabuf_free(out); |
961 | | #endif /* CONFIG_RADIUS_TLS */ |
962 | 0 | if (res < 0) |
963 | 0 | radius_client_handle_send_error(radius, s, msg_type); |
964 | |
|
965 | | #ifdef CONFIG_RADIUS_TLS |
966 | | skip_send: |
967 | | #endif /* CONFIG_RADIUS_TLS */ |
968 | 0 | radius_client_list_add(radius, msg, msg_type, shared_secret, |
969 | 0 | shared_secret_len, addr); |
970 | |
|
971 | 0 | return 0; |
972 | 0 | } |
973 | | |
974 | | |
975 | | #ifdef CONFIG_RADIUS_TLS |
976 | | |
977 | | static void radius_client_close_tcp(struct radius_client_data *radius, |
978 | | int sock, RadiusType msg_type) |
979 | | { |
980 | | wpa_printf(MSG_DEBUG, "RADIUS: Closing TCP connection (sock %d)", |
981 | | sock); |
982 | | if (msg_type == RADIUS_ACCT) { |
983 | | radius->acct_tls_ready = false; |
984 | | radius_close_acct_socket(radius); |
985 | | } else { |
986 | | radius->auth_tls_ready = false; |
987 | | radius_close_auth_socket(radius); |
988 | | } |
989 | | } |
990 | | |
991 | | |
992 | | static void |
993 | | radius_client_process_tls_handshake(struct radius_client_data *radius, |
994 | | int sock, RadiusType msg_type, |
995 | | u8 *buf, size_t len) |
996 | | { |
997 | | struct wpabuf *in, *out = NULL, *appl; |
998 | | struct tls_connection *conn; |
999 | | int res; |
1000 | | bool ready = false; |
1001 | | |
1002 | | wpa_printf(MSG_DEBUG, |
1003 | | "RADIUS: Process %zu bytes of received TLS handshake message", |
1004 | | len); |
1005 | | |
1006 | | if (msg_type == RADIUS_ACCT) |
1007 | | conn = radius->acct_tls_conn; |
1008 | | else |
1009 | | conn = radius->auth_tls_conn; |
1010 | | |
1011 | | in = wpabuf_alloc_copy(buf, len); |
1012 | | if (!in) |
1013 | | return; |
1014 | | |
1015 | | appl = NULL; |
1016 | | out = tls_connection_handshake(radius->tls_ctx, conn, in, &appl); |
1017 | | wpabuf_free(in); |
1018 | | if (!out) { |
1019 | | wpa_printf(MSG_DEBUG, |
1020 | | "RADIUS: Could not generate TLS handshake data"); |
1021 | | goto fail; |
1022 | | } |
1023 | | |
1024 | | if (tls_connection_get_failed(radius->tls_ctx, conn)) { |
1025 | | wpa_printf(MSG_INFO, "RADIUS: TLS handshake failed"); |
1026 | | goto fail; |
1027 | | } |
1028 | | |
1029 | | if (tls_connection_established(radius->tls_ctx, conn)) { |
1030 | | wpa_printf(MSG_DEBUG, |
1031 | | "RADIUS: TLS connection established (sock=%d)", |
1032 | | sock); |
1033 | | if (msg_type == RADIUS_ACCT) |
1034 | | radius->acct_tls_ready = true; |
1035 | | else |
1036 | | radius->auth_tls_ready = true; |
1037 | | ready = true; |
1038 | | } |
1039 | | |
1040 | | wpa_printf(MSG_DEBUG, "RADIUS: Sending %zu bytes of TLS handshake", |
1041 | | wpabuf_len(out)); |
1042 | | res = send(sock, wpabuf_head(out), wpabuf_len(out), 0); |
1043 | | if (res < 0) { |
1044 | | wpa_printf(MSG_INFO, "RADIUS: send: %s", strerror(errno)); |
1045 | | goto fail; |
1046 | | } |
1047 | | if ((size_t) res != wpabuf_len(out)) { |
1048 | | wpa_printf(MSG_INFO, |
1049 | | "RADIUS: Could not send all data for TLS handshake: only %d bytes sent", |
1050 | | res); |
1051 | | goto fail; |
1052 | | } |
1053 | | wpabuf_free(out); |
1054 | | |
1055 | | if (ready) { |
1056 | | struct radius_msg_list *entry, *prev, *tmp; |
1057 | | struct os_reltime now; |
1058 | | |
1059 | | /* Send all pending message of matching type since the TLS |
1060 | | * tunnel has now been established. */ |
1061 | | |
1062 | | os_get_reltime(&now); |
1063 | | |
1064 | | entry = radius->msgs; |
1065 | | prev = NULL; |
1066 | | while (entry) { |
1067 | | if (entry->msg_type != msg_type) { |
1068 | | prev = entry; |
1069 | | entry = entry->next; |
1070 | | continue; |
1071 | | } |
1072 | | |
1073 | | if (radius_client_retransmit(radius, entry, now.sec)) { |
1074 | | if (prev) |
1075 | | prev->next = entry->next; |
1076 | | else |
1077 | | radius->msgs = entry->next; |
1078 | | |
1079 | | tmp = entry; |
1080 | | entry = entry->next; |
1081 | | radius_client_msg_free(tmp); |
1082 | | radius->num_msgs--; |
1083 | | continue; |
1084 | | } |
1085 | | |
1086 | | prev = entry; |
1087 | | entry = entry->next; |
1088 | | } |
1089 | | } |
1090 | | |
1091 | | return; |
1092 | | |
1093 | | fail: |
1094 | | wpabuf_free(out); |
1095 | | tls_connection_deinit(radius->tls_ctx, conn); |
1096 | | if (msg_type == RADIUS_ACCT) |
1097 | | radius->acct_tls_conn = NULL; |
1098 | | else |
1099 | | radius->auth_tls_conn = NULL; |
1100 | | radius_client_close_tcp(radius, sock, msg_type); |
1101 | | } |
1102 | | |
1103 | | #endif /* CONFIG_RADIUS_TLS */ |
1104 | | |
1105 | | |
1106 | | static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) |
1107 | 0 | { |
1108 | 0 | struct radius_client_data *radius = eloop_ctx; |
1109 | 0 | struct hostapd_radius_servers *conf = radius->conf; |
1110 | 0 | RadiusType msg_type = (uintptr_t) sock_ctx; |
1111 | 0 | int len, roundtrip; |
1112 | 0 | unsigned char buf[RADIUS_MAX_MSG_LEN]; |
1113 | 0 | struct msghdr msghdr = {0}; |
1114 | 0 | struct iovec iov; |
1115 | 0 | struct radius_msg *msg; |
1116 | 0 | struct radius_hdr *hdr; |
1117 | 0 | struct radius_rx_handler *handlers; |
1118 | 0 | size_t num_handlers, i; |
1119 | 0 | struct radius_msg_list *req, *prev_req, *r; |
1120 | 0 | struct os_reltime now; |
1121 | 0 | struct hostapd_radius_server *rconf; |
1122 | 0 | int invalid_authenticator = 0; |
1123 | | #ifdef CONFIG_RADIUS_TLS |
1124 | | struct tls_connection *conn = NULL; |
1125 | | bool tls, tls_ready; |
1126 | | #endif /* CONFIG_RADIUS_TLS */ |
1127 | |
|
1128 | 0 | if (msg_type == RADIUS_ACCT) { |
1129 | | #ifdef CONFIG_RADIUS_TLS |
1130 | | if (radius->acct_tls) |
1131 | | conn = radius->acct_tls_conn; |
1132 | | tls = radius->acct_tls; |
1133 | | tls_ready = radius->acct_tls_ready; |
1134 | | #endif /* CONFIG_RADIUS_TLS */ |
1135 | 0 | handlers = radius->acct_handlers; |
1136 | 0 | num_handlers = radius->num_acct_handlers; |
1137 | 0 | rconf = conf->acct_server; |
1138 | 0 | } else { |
1139 | | #ifdef CONFIG_RADIUS_TLS |
1140 | | if (radius->auth_tls) |
1141 | | conn = radius->auth_tls_conn; |
1142 | | tls = radius->auth_tls; |
1143 | | tls_ready = radius->auth_tls_ready; |
1144 | | #endif /* CONFIG_RADIUS_TLS */ |
1145 | 0 | handlers = radius->auth_handlers; |
1146 | 0 | num_handlers = radius->num_auth_handlers; |
1147 | 0 | rconf = conf->auth_server; |
1148 | 0 | } |
1149 | |
|
1150 | 0 | iov.iov_base = buf; |
1151 | 0 | iov.iov_len = RADIUS_MAX_MSG_LEN; |
1152 | 0 | msghdr.msg_iov = &iov; |
1153 | 0 | msghdr.msg_iovlen = 1; |
1154 | 0 | msghdr.msg_flags = 0; |
1155 | 0 | len = recvmsg(sock, &msghdr, MSG_DONTWAIT); |
1156 | 0 | if (len < 0) { |
1157 | 0 | wpa_printf(MSG_INFO, "recvmsg[RADIUS]: %s", strerror(errno)); |
1158 | 0 | return; |
1159 | 0 | } |
1160 | | #ifdef CONFIG_RADIUS_TLS |
1161 | | if (tls && len == 0) { |
1162 | | wpa_printf(MSG_DEBUG, "RADIUS: No TCP data available"); |
1163 | | goto close_tcp; |
1164 | | } |
1165 | | |
1166 | | if (tls && !tls_ready) { |
1167 | | radius_client_process_tls_handshake(radius, sock, msg_type, |
1168 | | buf, len); |
1169 | | return; |
1170 | | } |
1171 | | |
1172 | | if (conn) { |
1173 | | struct wpabuf *out, *in; |
1174 | | |
1175 | | in = wpabuf_alloc_copy(buf, len); |
1176 | | if (!in) |
1177 | | return; |
1178 | | wpa_printf(MSG_DEBUG, |
1179 | | "RADIUS: Process %d bytes of encrypted TLS data", |
1180 | | len); |
1181 | | out = tls_connection_decrypt(radius->tls_ctx, conn, in); |
1182 | | wpabuf_free(in); |
1183 | | if (!out) { |
1184 | | wpa_printf(MSG_INFO, |
1185 | | "RADIUS: Failed to decrypt TLS data"); |
1186 | | goto close_tcp; |
1187 | | } |
1188 | | if (wpabuf_len(out) == 0) { |
1189 | | wpa_printf(MSG_DEBUG, |
1190 | | "RADIUS: Full message not yet received - continue waiting for additional TLS data"); |
1191 | | wpabuf_free(out); |
1192 | | return; |
1193 | | } |
1194 | | if (wpabuf_len(out) > RADIUS_MAX_MSG_LEN) { |
1195 | | wpa_printf(MSG_INFO, |
1196 | | "RADIUS: Too long RADIUS message from TLS: %zu", |
1197 | | wpabuf_len(out)); |
1198 | | wpabuf_free(out); |
1199 | | goto close_tcp; |
1200 | | } |
1201 | | os_memcpy(buf, wpabuf_head(out), wpabuf_len(out)); |
1202 | | len = wpabuf_len(out); |
1203 | | wpabuf_free(out); |
1204 | | } |
1205 | | #endif /* CONFIG_RADIUS_TLS */ |
1206 | | |
1207 | 0 | hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, |
1208 | 0 | HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS " |
1209 | 0 | "server", len); |
1210 | |
|
1211 | 0 | if (msghdr.msg_flags & MSG_TRUNC) { |
1212 | 0 | wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it"); |
1213 | 0 | return; |
1214 | 0 | } |
1215 | | |
1216 | 0 | msg = radius_msg_parse(buf, len); |
1217 | 0 | if (msg == NULL) { |
1218 | 0 | wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed"); |
1219 | 0 | rconf->malformed_responses++; |
1220 | 0 | return; |
1221 | 0 | } |
1222 | 0 | hdr = radius_msg_get_hdr(msg); |
1223 | |
|
1224 | 0 | hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, |
1225 | 0 | HOSTAPD_LEVEL_DEBUG, "Received RADIUS message"); |
1226 | 0 | if (conf->msg_dumps) |
1227 | 0 | radius_msg_dump(msg); |
1228 | |
|
1229 | 0 | switch (hdr->code) { |
1230 | 0 | case RADIUS_CODE_ACCESS_ACCEPT: |
1231 | 0 | rconf->access_accepts++; |
1232 | 0 | break; |
1233 | 0 | case RADIUS_CODE_ACCESS_REJECT: |
1234 | 0 | rconf->access_rejects++; |
1235 | 0 | break; |
1236 | 0 | case RADIUS_CODE_ACCESS_CHALLENGE: |
1237 | 0 | rconf->access_challenges++; |
1238 | 0 | break; |
1239 | 0 | case RADIUS_CODE_ACCOUNTING_RESPONSE: |
1240 | 0 | rconf->responses++; |
1241 | 0 | break; |
1242 | 0 | } |
1243 | | |
1244 | 0 | req = radius->msgs; |
1245 | 0 | while (req) { |
1246 | | /* TODO: also match by src addr:port of the packet when using |
1247 | | * alternative RADIUS servers (?) */ |
1248 | 0 | if ((req->msg_type == msg_type || |
1249 | 0 | (req->msg_type == RADIUS_ACCT_INTERIM && |
1250 | 0 | msg_type == RADIUS_ACCT)) && |
1251 | 0 | radius_msg_get_hdr(req->msg)->identifier == |
1252 | 0 | hdr->identifier) |
1253 | 0 | break; |
1254 | | |
1255 | 0 | req = req->next; |
1256 | 0 | } |
1257 | |
|
1258 | 0 | if (req == NULL) { |
1259 | 0 | hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, |
1260 | 0 | HOSTAPD_LEVEL_DEBUG, |
1261 | 0 | "No matching RADIUS request found (type=%d " |
1262 | 0 | "id=%d) - dropping packet", |
1263 | 0 | msg_type, hdr->identifier); |
1264 | 0 | goto fail; |
1265 | 0 | } |
1266 | | |
1267 | 0 | os_get_reltime(&now); |
1268 | 0 | roundtrip = (now.sec - req->last_attempt.sec) * 100 + |
1269 | 0 | (now.usec - req->last_attempt.usec) / 10000; |
1270 | 0 | hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, |
1271 | 0 | HOSTAPD_LEVEL_DEBUG, |
1272 | 0 | "Received RADIUS packet matched with a pending " |
1273 | 0 | "request, round trip time %d.%02d sec", |
1274 | 0 | roundtrip / 100, roundtrip % 100); |
1275 | 0 | rconf->round_trip_time = roundtrip; |
1276 | |
|
1277 | 0 | for (i = 0; i < num_handlers; i++) { |
1278 | 0 | RadiusRxResult res; |
1279 | 0 | res = handlers[i].handler(msg, req->msg, req->shared_secret, |
1280 | 0 | req->shared_secret_len, |
1281 | 0 | handlers[i].data); |
1282 | 0 | switch (res) { |
1283 | 0 | case RADIUS_RX_PROCESSED: |
1284 | 0 | radius_msg_free(msg); |
1285 | | /* fall through */ |
1286 | 0 | case RADIUS_RX_QUEUED: |
1287 | | /* Remove ACKed RADIUS packet from retransmit list */ |
1288 | 0 | prev_req = NULL; |
1289 | 0 | for (r = radius->msgs; r; r = r->next) { |
1290 | 0 | if (r == req) |
1291 | 0 | break; |
1292 | 0 | prev_req = r; |
1293 | 0 | } |
1294 | 0 | if (prev_req) |
1295 | 0 | prev_req->next = req->next; |
1296 | 0 | else |
1297 | 0 | radius->msgs = req->next; |
1298 | 0 | radius->num_msgs--; |
1299 | |
|
1300 | 0 | radius_client_msg_free(req); |
1301 | 0 | return; |
1302 | 0 | case RADIUS_RX_INVALID_AUTHENTICATOR: |
1303 | 0 | invalid_authenticator++; |
1304 | | /* fall through */ |
1305 | 0 | case RADIUS_RX_UNKNOWN: |
1306 | | /* continue with next handler */ |
1307 | 0 | break; |
1308 | 0 | } |
1309 | 0 | } |
1310 | | |
1311 | 0 | if (invalid_authenticator) |
1312 | 0 | rconf->bad_authenticators++; |
1313 | 0 | else |
1314 | 0 | rconf->unknown_types++; |
1315 | 0 | hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, |
1316 | 0 | HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found " |
1317 | 0 | "(type=%d code=%d id=%d)%s - dropping packet", |
1318 | 0 | msg_type, hdr->code, hdr->identifier, |
1319 | 0 | invalid_authenticator ? " [INVALID AUTHENTICATOR]" : |
1320 | 0 | ""); |
1321 | |
|
1322 | 0 | fail: |
1323 | 0 | radius_msg_free(msg); |
1324 | 0 | return; |
1325 | |
|
1326 | | #ifdef CONFIG_RADIUS_TLS |
1327 | | close_tcp: |
1328 | | radius_client_close_tcp(radius, sock, msg_type); |
1329 | | #endif /* CONFIG_RADIUS_TLS */ |
1330 | 0 | } |
1331 | | |
1332 | | |
1333 | | #ifdef CONFIG_RADIUS_TLS |
1334 | | static void radius_client_write_ready(int sock, void *eloop_ctx, void *sock_ctx) |
1335 | | { |
1336 | | struct radius_client_data *radius = eloop_ctx; |
1337 | | RadiusType msg_type = (uintptr_t) sock_ctx; |
1338 | | struct tls_connection *conn = NULL; |
1339 | | struct wpabuf *in, *out = NULL, *appl; |
1340 | | int res = -1; |
1341 | | struct tls_connection_params params; |
1342 | | struct hostapd_radius_server *server; |
1343 | | |
1344 | | wpa_printf(MSG_DEBUG, "RADIUS: TCP connection established - start TLS handshake (sock=%d)", |
1345 | | sock); |
1346 | | |
1347 | | if (msg_type == RADIUS_ACCT) { |
1348 | | eloop_unregister_sock(sock, EVENT_TYPE_WRITE); |
1349 | | eloop_register_read_sock(sock, radius_client_receive, radius, |
1350 | | (void *) RADIUS_ACCT); |
1351 | | if (radius->acct_tls_conn) { |
1352 | | wpa_printf(MSG_DEBUG, |
1353 | | "RADIUS: Deinit previously used TLS connection"); |
1354 | | tls_connection_deinit(radius->tls_ctx, |
1355 | | radius->acct_tls_conn); |
1356 | | radius->acct_tls_conn = NULL; |
1357 | | } |
1358 | | server = radius->conf->acct_server; |
1359 | | } else { |
1360 | | eloop_unregister_sock(sock, EVENT_TYPE_WRITE); |
1361 | | eloop_register_read_sock(sock, radius_client_receive, radius, |
1362 | | (void *) RADIUS_AUTH); |
1363 | | if (radius->auth_tls_conn) { |
1364 | | wpa_printf(MSG_DEBUG, |
1365 | | "RADIUS: Deinit previously used TLS connection"); |
1366 | | tls_connection_deinit(radius->tls_ctx, |
1367 | | radius->auth_tls_conn); |
1368 | | radius->auth_tls_conn = NULL; |
1369 | | } |
1370 | | server = radius->conf->auth_server; |
1371 | | } |
1372 | | |
1373 | | if (!server) |
1374 | | goto fail; |
1375 | | |
1376 | | conn = tls_connection_init(radius->tls_ctx); |
1377 | | if (!conn) { |
1378 | | wpa_printf(MSG_INFO, |
1379 | | "RADIUS: Failed to initiate TLS connection"); |
1380 | | goto fail; |
1381 | | } |
1382 | | |
1383 | | os_memset(¶ms, 0, sizeof(params)); |
1384 | | params.ca_cert = server->ca_cert; |
1385 | | params.client_cert = server->client_cert; |
1386 | | params.private_key = server->private_key; |
1387 | | params.private_key_passwd = server->private_key_passwd; |
1388 | | params.flags = TLS_CONN_DISABLE_TLSv1_0 | TLS_CONN_DISABLE_TLSv1_1; |
1389 | | if (tls_connection_set_params(radius->tls_ctx, conn, ¶ms)) { |
1390 | | wpa_printf(MSG_INFO, |
1391 | | "RADIUS: Failed to set TLS connection parameters"); |
1392 | | goto fail; |
1393 | | } |
1394 | | |
1395 | | in = NULL; |
1396 | | appl = NULL; |
1397 | | out = tls_connection_handshake(radius->tls_ctx, conn, in, &appl); |
1398 | | if (!out) { |
1399 | | wpa_printf(MSG_DEBUG, |
1400 | | "RADIUS: Could not generate TLS handshake data"); |
1401 | | goto fail; |
1402 | | } |
1403 | | |
1404 | | if (tls_connection_get_failed(radius->tls_ctx, conn)) { |
1405 | | wpa_printf(MSG_INFO, "RADIUS: TLS handshake failed"); |
1406 | | goto fail; |
1407 | | } |
1408 | | |
1409 | | wpa_printf(MSG_DEBUG, "RADIUS: Sending %zu bytes of TLS handshake", |
1410 | | wpabuf_len(out)); |
1411 | | res = send(sock, wpabuf_head(out), wpabuf_len(out), 0); |
1412 | | if (res < 0) { |
1413 | | wpa_printf(MSG_INFO, "RADIUS: send: %s", strerror(errno)); |
1414 | | goto fail; |
1415 | | } |
1416 | | if ((size_t) res != wpabuf_len(out)) { |
1417 | | wpa_printf(MSG_INFO, |
1418 | | "RADIUS: Could not send all data for TLS handshake: only %d bytes sent", |
1419 | | res); |
1420 | | goto fail; |
1421 | | } |
1422 | | wpabuf_free(out); |
1423 | | |
1424 | | if (msg_type == RADIUS_ACCT) |
1425 | | radius->acct_tls_conn = conn; |
1426 | | else |
1427 | | radius->auth_tls_conn = conn; |
1428 | | return; |
1429 | | |
1430 | | fail: |
1431 | | wpa_printf(MSG_INFO, "RADIUS: Failed to perform TLS handshake"); |
1432 | | tls_connection_deinit(radius->tls_ctx, conn); |
1433 | | wpabuf_free(out); |
1434 | | radius_client_close_tcp(radius, sock, msg_type); |
1435 | | } |
1436 | | #endif /* CONFIG_RADIUS_TLS */ |
1437 | | |
1438 | | |
1439 | | /** |
1440 | | * radius_client_get_id - Get an identifier for a new RADIUS message |
1441 | | * @radius: RADIUS client context from radius_client_init() |
1442 | | * Returns: Allocated identifier |
1443 | | * |
1444 | | * This function is used to fetch a unique (among pending requests) identifier |
1445 | | * for a new RADIUS message. |
1446 | | */ |
1447 | | u8 radius_client_get_id(struct radius_client_data *radius) |
1448 | 0 | { |
1449 | 0 | struct radius_msg_list *entry, *prev, *_remove; |
1450 | 0 | u8 id = radius->next_radius_identifier++; |
1451 | | |
1452 | | /* remove entries with matching id from retransmit list to avoid |
1453 | | * using new reply from the RADIUS server with an old request */ |
1454 | 0 | entry = radius->msgs; |
1455 | 0 | prev = NULL; |
1456 | 0 | while (entry) { |
1457 | 0 | if (radius_msg_get_hdr(entry->msg)->identifier == id) { |
1458 | 0 | hostapd_logger(radius->ctx, entry->addr, |
1459 | 0 | HOSTAPD_MODULE_RADIUS, |
1460 | 0 | HOSTAPD_LEVEL_DEBUG, |
1461 | 0 | "Removing pending RADIUS message, " |
1462 | 0 | "since its id (%d) is reused", id); |
1463 | 0 | if (prev) |
1464 | 0 | prev->next = entry->next; |
1465 | 0 | else |
1466 | 0 | radius->msgs = entry->next; |
1467 | 0 | _remove = entry; |
1468 | 0 | } else { |
1469 | 0 | _remove = NULL; |
1470 | 0 | prev = entry; |
1471 | 0 | } |
1472 | 0 | entry = entry->next; |
1473 | |
|
1474 | 0 | if (_remove) |
1475 | 0 | radius_client_msg_free(_remove); |
1476 | 0 | } |
1477 | |
|
1478 | 0 | return id; |
1479 | 0 | } |
1480 | | |
1481 | | |
1482 | | /** |
1483 | | * radius_client_flush - Flush all pending RADIUS client messages |
1484 | | * @radius: RADIUS client context from radius_client_init() |
1485 | | * @only_auth: Whether only authentication messages are removed |
1486 | | */ |
1487 | | void radius_client_flush(struct radius_client_data *radius, int only_auth) |
1488 | 0 | { |
1489 | 0 | struct radius_msg_list *entry, *prev, *tmp; |
1490 | |
|
1491 | 0 | if (!radius) |
1492 | 0 | return; |
1493 | | |
1494 | 0 | prev = NULL; |
1495 | 0 | entry = radius->msgs; |
1496 | |
|
1497 | 0 | while (entry) { |
1498 | 0 | if (!only_auth || entry->msg_type == RADIUS_AUTH) { |
1499 | 0 | if (prev) |
1500 | 0 | prev->next = entry->next; |
1501 | 0 | else |
1502 | 0 | radius->msgs = entry->next; |
1503 | |
|
1504 | 0 | tmp = entry; |
1505 | 0 | entry = entry->next; |
1506 | 0 | radius_client_msg_free(tmp); |
1507 | 0 | radius->num_msgs--; |
1508 | 0 | } else { |
1509 | 0 | prev = entry; |
1510 | 0 | entry = entry->next; |
1511 | 0 | } |
1512 | 0 | } |
1513 | |
|
1514 | 0 | if (radius->msgs == NULL) |
1515 | 0 | eloop_cancel_timeout(radius_client_timer, radius, NULL); |
1516 | 0 | } |
1517 | | |
1518 | | |
1519 | | static void radius_client_update_acct_msgs(struct radius_client_data *radius, |
1520 | | const u8 *shared_secret, |
1521 | | size_t shared_secret_len) |
1522 | 0 | { |
1523 | 0 | struct radius_msg_list *entry; |
1524 | |
|
1525 | 0 | if (!radius) |
1526 | 0 | return; |
1527 | | |
1528 | 0 | for (entry = radius->msgs; entry; entry = entry->next) { |
1529 | 0 | if (entry->msg_type == RADIUS_ACCT) { |
1530 | 0 | entry->shared_secret = shared_secret; |
1531 | 0 | entry->shared_secret_len = shared_secret_len; |
1532 | 0 | if (radius_msg_finish_acct(entry->msg, shared_secret, |
1533 | 0 | shared_secret_len) < 0) |
1534 | 0 | wpa_printf(MSG_INFO, |
1535 | 0 | "RADIUS: Failed to update accounting message"); |
1536 | 0 | } |
1537 | 0 | } |
1538 | 0 | } |
1539 | | |
1540 | | |
1541 | | static int |
1542 | | radius_change_server(struct radius_client_data *radius, |
1543 | | struct hostapd_radius_server *nserv, |
1544 | | struct hostapd_radius_server *oserv, |
1545 | | int auth) |
1546 | 0 | { |
1547 | 0 | struct sockaddr_in serv, claddr; |
1548 | 0 | #ifdef CONFIG_IPV6 |
1549 | 0 | struct sockaddr_in6 serv6, claddr6; |
1550 | 0 | #endif /* CONFIG_IPV6 */ |
1551 | 0 | struct sockaddr *addr, *cl_addr; |
1552 | 0 | socklen_t addrlen, claddrlen; |
1553 | 0 | char abuf[50]; |
1554 | 0 | int sel_sock; |
1555 | 0 | struct radius_msg_list *entry; |
1556 | 0 | struct hostapd_radius_servers *conf = radius->conf; |
1557 | 0 | int type = SOCK_DGRAM; |
1558 | 0 | bool tls = nserv->tls; |
1559 | |
|
1560 | 0 | if (tls) { |
1561 | | #ifdef CONFIG_RADIUS_TLS |
1562 | | type = SOCK_STREAM; |
1563 | | #else /* CONFIG_RADIUS_TLS */ |
1564 | 0 | wpa_printf(MSG_ERROR, "RADIUS: TLS not supported"); |
1565 | 0 | return -1; |
1566 | 0 | #endif /* CONFIG_RADIUS_TLS */ |
1567 | 0 | } |
1568 | | |
1569 | 0 | hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, |
1570 | 0 | HOSTAPD_LEVEL_INFO, |
1571 | 0 | "%s server %s:%d", |
1572 | 0 | auth ? "Authentication" : "Accounting", |
1573 | 0 | hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), |
1574 | 0 | nserv->port); |
1575 | |
|
1576 | 0 | if (oserv && oserv == nserv) { |
1577 | | /* Reconnect to same server, flush */ |
1578 | 0 | if (auth) |
1579 | 0 | radius_client_flush(radius, 1); |
1580 | 0 | } |
1581 | |
|
1582 | 0 | if (oserv && oserv != nserv && |
1583 | 0 | (nserv->shared_secret_len != oserv->shared_secret_len || |
1584 | 0 | os_memcmp(nserv->shared_secret, oserv->shared_secret, |
1585 | 0 | nserv->shared_secret_len) != 0)) { |
1586 | | /* Pending RADIUS packets used different shared secret, so |
1587 | | * they need to be modified. Update accounting message |
1588 | | * authenticators here. Authentication messages are removed |
1589 | | * since they would require more changes and the new RADIUS |
1590 | | * server may not be prepared to receive them anyway due to |
1591 | | * missing state information. Client will likely retry |
1592 | | * authentication, so this should not be an issue. */ |
1593 | 0 | if (auth) |
1594 | 0 | radius_client_flush(radius, 1); |
1595 | 0 | else { |
1596 | 0 | radius_client_update_acct_msgs( |
1597 | 0 | radius, nserv->shared_secret, |
1598 | 0 | nserv->shared_secret_len); |
1599 | 0 | } |
1600 | 0 | } |
1601 | | |
1602 | | /* Reset retry counters */ |
1603 | 0 | for (entry = radius->msgs; oserv && entry; entry = entry->next) { |
1604 | 0 | if ((auth && entry->msg_type != RADIUS_AUTH) || |
1605 | 0 | (!auth && entry->msg_type != RADIUS_ACCT)) |
1606 | 0 | continue; |
1607 | 0 | entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; |
1608 | 0 | entry->attempts = 0; |
1609 | 0 | entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; |
1610 | 0 | } |
1611 | |
|
1612 | 0 | if (radius->msgs) { |
1613 | 0 | eloop_cancel_timeout(radius_client_timer, radius, NULL); |
1614 | 0 | eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, |
1615 | 0 | radius_client_timer, radius, NULL); |
1616 | 0 | } |
1617 | |
|
1618 | 0 | switch (nserv->addr.af) { |
1619 | 0 | case AF_INET: |
1620 | 0 | os_memset(&serv, 0, sizeof(serv)); |
1621 | 0 | serv.sin_family = AF_INET; |
1622 | 0 | serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr; |
1623 | 0 | serv.sin_port = htons(nserv->port); |
1624 | 0 | addr = (struct sockaddr *) &serv; |
1625 | 0 | addrlen = sizeof(serv); |
1626 | 0 | sel_sock = socket(PF_INET, type, 0); |
1627 | 0 | if (sel_sock >= 0) |
1628 | 0 | radius_client_disable_pmtu_discovery(sel_sock); |
1629 | 0 | break; |
1630 | 0 | #ifdef CONFIG_IPV6 |
1631 | 0 | case AF_INET6: |
1632 | 0 | os_memset(&serv6, 0, sizeof(serv6)); |
1633 | 0 | serv6.sin6_family = AF_INET6; |
1634 | 0 | os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6, |
1635 | 0 | sizeof(struct in6_addr)); |
1636 | 0 | serv6.sin6_port = htons(nserv->port); |
1637 | 0 | addr = (struct sockaddr *) &serv6; |
1638 | 0 | addrlen = sizeof(serv6); |
1639 | 0 | sel_sock = socket(PF_INET6, type, 0); |
1640 | 0 | break; |
1641 | 0 | #endif /* CONFIG_IPV6 */ |
1642 | 0 | default: |
1643 | 0 | return -1; |
1644 | 0 | } |
1645 | | |
1646 | 0 | if (sel_sock < 0) { |
1647 | 0 | wpa_printf(MSG_INFO, |
1648 | 0 | "RADIUS: Failed to open server socket (af=%d auth=%d)", |
1649 | 0 | nserv->addr.af, auth); |
1650 | 0 | return -1; |
1651 | 0 | } |
1652 | | |
1653 | | #ifdef CONFIG_RADIUS_TLS |
1654 | | if (tls && fcntl(sel_sock, F_SETFL, O_NONBLOCK) != 0) { |
1655 | | wpa_printf(MSG_DEBUG, "RADIUS: fnctl(O_NONBLOCK) failed: %s", |
1656 | | strerror(errno)); |
1657 | | close(sel_sock); |
1658 | | return -1; |
1659 | | } |
1660 | | #endif /* CONFIG_RADIUS_TLS */ |
1661 | | |
1662 | 0 | #ifdef __linux__ |
1663 | 0 | if (conf->force_client_dev && conf->force_client_dev[0]) { |
1664 | 0 | if (setsockopt(sel_sock, SOL_SOCKET, SO_BINDTODEVICE, |
1665 | 0 | conf->force_client_dev, |
1666 | 0 | os_strlen(conf->force_client_dev)) < 0) { |
1667 | 0 | wpa_printf(MSG_ERROR, |
1668 | 0 | "RADIUS: setsockopt[SO_BINDTODEVICE]: %s", |
1669 | 0 | strerror(errno)); |
1670 | | /* Probably not a critical error; continue on and hope |
1671 | | * for the best. */ |
1672 | 0 | } else { |
1673 | 0 | wpa_printf(MSG_DEBUG, |
1674 | 0 | "RADIUS: Bound client socket to device: %s", |
1675 | 0 | conf->force_client_dev); |
1676 | 0 | } |
1677 | 0 | } |
1678 | 0 | #endif /* __linux__ */ |
1679 | |
|
1680 | 0 | if (conf->force_client_addr) { |
1681 | 0 | switch (conf->client_addr.af) { |
1682 | 0 | case AF_INET: |
1683 | 0 | os_memset(&claddr, 0, sizeof(claddr)); |
1684 | 0 | claddr.sin_family = AF_INET; |
1685 | 0 | claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr; |
1686 | 0 | claddr.sin_port = htons(0); |
1687 | 0 | cl_addr = (struct sockaddr *) &claddr; |
1688 | 0 | claddrlen = sizeof(claddr); |
1689 | 0 | break; |
1690 | 0 | #ifdef CONFIG_IPV6 |
1691 | 0 | case AF_INET6: |
1692 | 0 | os_memset(&claddr6, 0, sizeof(claddr6)); |
1693 | 0 | claddr6.sin6_family = AF_INET6; |
1694 | 0 | os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6, |
1695 | 0 | sizeof(struct in6_addr)); |
1696 | 0 | claddr6.sin6_port = htons(0); |
1697 | 0 | cl_addr = (struct sockaddr *) &claddr6; |
1698 | 0 | claddrlen = sizeof(claddr6); |
1699 | 0 | break; |
1700 | 0 | #endif /* CONFIG_IPV6 */ |
1701 | 0 | default: |
1702 | 0 | close(sel_sock); |
1703 | 0 | return -1; |
1704 | 0 | } |
1705 | | |
1706 | 0 | if (bind(sel_sock, cl_addr, claddrlen) < 0) { |
1707 | 0 | wpa_printf(MSG_INFO, "bind[radius]: %s", |
1708 | 0 | strerror(errno)); |
1709 | 0 | close(sel_sock); |
1710 | 0 | return -2; |
1711 | 0 | } |
1712 | 0 | } |
1713 | | |
1714 | 0 | if (connect(sel_sock, addr, addrlen) < 0) { |
1715 | 0 | if (nserv->tls && errno == EINPROGRESS) { |
1716 | 0 | wpa_printf(MSG_DEBUG, |
1717 | 0 | "RADIUS: TCP connection establishment in progress (sock %d)", |
1718 | 0 | sel_sock); |
1719 | 0 | } else { |
1720 | 0 | wpa_printf(MSG_INFO, "connect[radius]: %s", |
1721 | 0 | strerror(errno)); |
1722 | 0 | close(sel_sock); |
1723 | 0 | return -2; |
1724 | 0 | } |
1725 | 0 | } |
1726 | | |
1727 | 0 | #ifndef CONFIG_NATIVE_WINDOWS |
1728 | 0 | switch (nserv->addr.af) { |
1729 | 0 | case AF_INET: |
1730 | 0 | claddrlen = sizeof(claddr); |
1731 | 0 | if (getsockname(sel_sock, (struct sockaddr *) &claddr, |
1732 | 0 | &claddrlen) == 0) { |
1733 | 0 | wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", |
1734 | 0 | inet_ntoa(claddr.sin_addr), |
1735 | 0 | ntohs(claddr.sin_port)); |
1736 | 0 | } |
1737 | 0 | break; |
1738 | 0 | #ifdef CONFIG_IPV6 |
1739 | 0 | case AF_INET6: { |
1740 | 0 | claddrlen = sizeof(claddr6); |
1741 | 0 | if (getsockname(sel_sock, (struct sockaddr *) &claddr6, |
1742 | 0 | &claddrlen) == 0) { |
1743 | 0 | wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", |
1744 | 0 | inet_ntop(AF_INET6, &claddr6.sin6_addr, |
1745 | 0 | abuf, sizeof(abuf)), |
1746 | 0 | ntohs(claddr6.sin6_port)); |
1747 | 0 | } |
1748 | 0 | break; |
1749 | 0 | } |
1750 | 0 | #endif /* CONFIG_IPV6 */ |
1751 | 0 | } |
1752 | 0 | #endif /* CONFIG_NATIVE_WINDOWS */ |
1753 | | |
1754 | 0 | if (auth) { |
1755 | 0 | radius_close_auth_socket(radius); |
1756 | 0 | radius->auth_sock = sel_sock; |
1757 | 0 | } else { |
1758 | 0 | radius_close_acct_socket(radius); |
1759 | 0 | radius->acct_sock = sel_sock; |
1760 | 0 | } |
1761 | |
|
1762 | 0 | if (!tls) |
1763 | 0 | eloop_register_read_sock(sel_sock, radius_client_receive, |
1764 | 0 | radius, |
1765 | 0 | auth ? (void *) RADIUS_AUTH : |
1766 | 0 | (void *) RADIUS_ACCT); |
1767 | | #ifdef CONFIG_RADIUS_TLS |
1768 | | if (tls) |
1769 | | eloop_register_sock(sel_sock, EVENT_TYPE_WRITE, |
1770 | | radius_client_write_ready, radius, |
1771 | | auth ? (void *) RADIUS_AUTH : |
1772 | | (void *) RADIUS_ACCT); |
1773 | | #endif /* CONFIG_RADIUS_TLS */ |
1774 | |
|
1775 | 0 | if (auth) { |
1776 | 0 | radius->auth_tls = nserv->tls; |
1777 | 0 | radius->auth_tls_ready = false; |
1778 | 0 | } else { |
1779 | 0 | radius->acct_tls = nserv->tls; |
1780 | 0 | radius->acct_tls_ready = false; |
1781 | 0 | } |
1782 | |
|
1783 | 0 | return 0; |
1784 | 0 | } |
1785 | | |
1786 | | |
1787 | | static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) |
1788 | 0 | { |
1789 | 0 | struct radius_client_data *radius = eloop_ctx; |
1790 | 0 | struct hostapd_radius_servers *conf = radius->conf; |
1791 | 0 | struct hostapd_radius_server *oserv; |
1792 | |
|
1793 | 0 | if (radius->auth_sock >= 0 && conf->auth_servers && |
1794 | 0 | conf->auth_server != conf->auth_servers) { |
1795 | 0 | oserv = conf->auth_server; |
1796 | 0 | conf->auth_server = conf->auth_servers; |
1797 | 0 | if (radius_change_server(radius, conf->auth_server, oserv, |
1798 | 0 | 1) < 0) { |
1799 | 0 | conf->auth_server = oserv; |
1800 | 0 | radius_change_server(radius, oserv, conf->auth_server, |
1801 | 0 | 1); |
1802 | 0 | } |
1803 | 0 | } |
1804 | |
|
1805 | 0 | if (radius->acct_sock >= 0 && conf->acct_servers && |
1806 | 0 | conf->acct_server != conf->acct_servers) { |
1807 | 0 | oserv = conf->acct_server; |
1808 | 0 | conf->acct_server = conf->acct_servers; |
1809 | 0 | if (radius_change_server(radius, conf->acct_server, oserv, |
1810 | 0 | 0) < 0) { |
1811 | 0 | conf->acct_server = oserv; |
1812 | 0 | radius_change_server(radius, oserv, conf->acct_server, |
1813 | 0 | 0); |
1814 | 0 | } |
1815 | 0 | } |
1816 | |
|
1817 | 0 | if (conf->retry_primary_interval) |
1818 | 0 | eloop_register_timeout(conf->retry_primary_interval, 0, |
1819 | 0 | radius_retry_primary_timer, radius, |
1820 | 0 | NULL); |
1821 | 0 | } |
1822 | | |
1823 | | |
1824 | | static int radius_client_init_auth(struct radius_client_data *radius) |
1825 | 0 | { |
1826 | 0 | radius_close_auth_socket(radius); |
1827 | 0 | return radius_change_server(radius, radius->conf->auth_server, NULL, 1); |
1828 | 0 | } |
1829 | | |
1830 | | |
1831 | | static int radius_client_init_acct(struct radius_client_data *radius) |
1832 | 0 | { |
1833 | 0 | radius_close_acct_socket(radius); |
1834 | 0 | return radius_change_server(radius, radius->conf->acct_server, NULL, 0); |
1835 | 0 | } |
1836 | | |
1837 | | |
1838 | | #ifdef CONFIG_RADIUS_TLS |
1839 | | static void radius_tls_event_cb(void *ctx, enum tls_event ev, |
1840 | | union tls_event_data *data) |
1841 | | { |
1842 | | wpa_printf(MSG_DEBUG, "RADIUS: TLS event %d", ev); |
1843 | | } |
1844 | | #endif /* CONFIG_RADIUS_TLS */ |
1845 | | |
1846 | | |
1847 | | /** |
1848 | | * radius_client_init - Initialize RADIUS client |
1849 | | * @ctx: Callback context to be used in hostapd_logger() calls |
1850 | | * @conf: RADIUS client configuration (RADIUS servers) |
1851 | | * Returns: Pointer to private RADIUS client context or %NULL on failure |
1852 | | * |
1853 | | * The caller is responsible for keeping the configuration data available for |
1854 | | * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is |
1855 | | * called for the returned context pointer. |
1856 | | */ |
1857 | | struct radius_client_data * |
1858 | | radius_client_init(void *ctx, struct hostapd_radius_servers *conf) |
1859 | 0 | { |
1860 | 0 | struct radius_client_data *radius; |
1861 | |
|
1862 | 0 | radius = os_zalloc(sizeof(struct radius_client_data)); |
1863 | 0 | if (radius == NULL) |
1864 | 0 | return NULL; |
1865 | | |
1866 | 0 | radius->ctx = ctx; |
1867 | 0 | radius->conf = conf; |
1868 | 0 | radius->auth_sock = radius->acct_sock = -1; |
1869 | |
|
1870 | 0 | if (conf->auth_server && radius_client_init_auth(radius) == -1) { |
1871 | 0 | radius_client_deinit(radius); |
1872 | 0 | return NULL; |
1873 | 0 | } |
1874 | | |
1875 | 0 | if (conf->acct_server && radius_client_init_acct(radius) == -1) { |
1876 | 0 | radius_client_deinit(radius); |
1877 | 0 | return NULL; |
1878 | 0 | } |
1879 | | |
1880 | 0 | if (conf->retry_primary_interval) |
1881 | 0 | eloop_register_timeout(conf->retry_primary_interval, 0, |
1882 | 0 | radius_retry_primary_timer, radius, |
1883 | 0 | NULL); |
1884 | |
|
1885 | | #ifdef CONFIG_RADIUS_TLS |
1886 | | if ((conf->auth_server && conf->auth_server->tls) || |
1887 | | (conf->acct_server && conf->acct_server->tls)) { |
1888 | | struct tls_config tls_conf; |
1889 | | |
1890 | | os_memset(&tls_conf, 0, sizeof(tls_conf)); |
1891 | | tls_conf.event_cb = radius_tls_event_cb; |
1892 | | radius->tls_ctx = tls_init(&tls_conf); |
1893 | | if (!radius->tls_ctx) { |
1894 | | radius_client_deinit(radius); |
1895 | | return NULL; |
1896 | | } |
1897 | | } |
1898 | | #endif /* CONFIG_RADIUS_TLS */ |
1899 | | |
1900 | |
|
1901 | 0 | return radius; |
1902 | 0 | } |
1903 | | |
1904 | | |
1905 | | /** |
1906 | | * radius_client_deinit - Deinitialize RADIUS client |
1907 | | * @radius: RADIUS client context from radius_client_init() |
1908 | | */ |
1909 | | void radius_client_deinit(struct radius_client_data *radius) |
1910 | 0 | { |
1911 | 0 | if (!radius) |
1912 | 0 | return; |
1913 | | |
1914 | 0 | radius_close_auth_socket(radius); |
1915 | 0 | radius_close_acct_socket(radius); |
1916 | |
|
1917 | 0 | eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL); |
1918 | |
|
1919 | 0 | radius_client_flush(radius, 0); |
1920 | 0 | os_free(radius->auth_handlers); |
1921 | 0 | os_free(radius->acct_handlers); |
1922 | | #ifdef CONFIG_RADIUS_TLS |
1923 | | if (radius->tls_ctx) { |
1924 | | tls_connection_deinit(radius->tls_ctx, radius->auth_tls_conn); |
1925 | | tls_connection_deinit(radius->tls_ctx, radius->acct_tls_conn); |
1926 | | tls_deinit(radius->tls_ctx); |
1927 | | } |
1928 | | #endif /* CONFIG_RADIUS_TLS */ |
1929 | 0 | os_free(radius); |
1930 | 0 | } |
1931 | | |
1932 | | |
1933 | | /** |
1934 | | * radius_client_flush_auth - Flush pending RADIUS messages for an address |
1935 | | * @radius: RADIUS client context from radius_client_init() |
1936 | | * @addr: MAC address of the related device |
1937 | | * |
1938 | | * This function can be used to remove pending RADIUS authentication messages |
1939 | | * that are related to a specific device. The addr parameter is matched with |
1940 | | * the one used in radius_client_send() call that was used to transmit the |
1941 | | * authentication request. |
1942 | | */ |
1943 | | void radius_client_flush_auth(struct radius_client_data *radius, |
1944 | | const u8 *addr) |
1945 | 0 | { |
1946 | 0 | struct radius_msg_list *entry, *prev, *tmp; |
1947 | |
|
1948 | 0 | prev = NULL; |
1949 | 0 | entry = radius->msgs; |
1950 | 0 | while (entry) { |
1951 | 0 | if (entry->msg_type == RADIUS_AUTH && |
1952 | 0 | ether_addr_equal(entry->addr, addr)) { |
1953 | 0 | hostapd_logger(radius->ctx, addr, |
1954 | 0 | HOSTAPD_MODULE_RADIUS, |
1955 | 0 | HOSTAPD_LEVEL_DEBUG, |
1956 | 0 | "Removing pending RADIUS authentication" |
1957 | 0 | " message for removed client"); |
1958 | |
|
1959 | 0 | if (prev) |
1960 | 0 | prev->next = entry->next; |
1961 | 0 | else |
1962 | 0 | radius->msgs = entry->next; |
1963 | |
|
1964 | 0 | tmp = entry; |
1965 | 0 | entry = entry->next; |
1966 | 0 | radius_client_msg_free(tmp); |
1967 | 0 | radius->num_msgs--; |
1968 | 0 | continue; |
1969 | 0 | } |
1970 | | |
1971 | 0 | prev = entry; |
1972 | 0 | entry = entry->next; |
1973 | 0 | } |
1974 | 0 | } |
1975 | | |
1976 | | |
1977 | | static int radius_client_dump_auth_server(char *buf, size_t buflen, |
1978 | | struct hostapd_radius_server *serv, |
1979 | | struct radius_client_data *cli) |
1980 | 0 | { |
1981 | 0 | int pending = 0; |
1982 | 0 | struct radius_msg_list *msg; |
1983 | 0 | char abuf[50]; |
1984 | |
|
1985 | 0 | if (cli) { |
1986 | 0 | for (msg = cli->msgs; msg; msg = msg->next) { |
1987 | 0 | if (msg->msg_type == RADIUS_AUTH) |
1988 | 0 | pending++; |
1989 | 0 | } |
1990 | 0 | } |
1991 | |
|
1992 | 0 | return os_snprintf(buf, buflen, |
1993 | 0 | "radiusAuthServerIndex=%d\n" |
1994 | 0 | "radiusAuthServerAddress=%s\n" |
1995 | 0 | "radiusAuthClientServerPortNumber=%d\n" |
1996 | 0 | "radiusAuthClientRoundTripTime=%d\n" |
1997 | 0 | "radiusAuthClientAccessRequests=%u\n" |
1998 | 0 | "radiusAuthClientAccessRetransmissions=%u\n" |
1999 | 0 | "radiusAuthClientAccessAccepts=%u\n" |
2000 | 0 | "radiusAuthClientAccessRejects=%u\n" |
2001 | 0 | "radiusAuthClientAccessChallenges=%u\n" |
2002 | 0 | "radiusAuthClientMalformedAccessResponses=%u\n" |
2003 | 0 | "radiusAuthClientBadAuthenticators=%u\n" |
2004 | 0 | "radiusAuthClientPendingRequests=%u\n" |
2005 | 0 | "radiusAuthClientTimeouts=%u\n" |
2006 | 0 | "radiusAuthClientUnknownTypes=%u\n" |
2007 | 0 | "radiusAuthClientPacketsDropped=%u\n", |
2008 | 0 | serv->index, |
2009 | 0 | hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), |
2010 | 0 | serv->port, |
2011 | 0 | serv->round_trip_time, |
2012 | 0 | serv->requests, |
2013 | 0 | serv->retransmissions, |
2014 | 0 | serv->access_accepts, |
2015 | 0 | serv->access_rejects, |
2016 | 0 | serv->access_challenges, |
2017 | 0 | serv->malformed_responses, |
2018 | 0 | serv->bad_authenticators, |
2019 | 0 | pending, |
2020 | 0 | serv->timeouts, |
2021 | 0 | serv->unknown_types, |
2022 | 0 | serv->packets_dropped); |
2023 | 0 | } |
2024 | | |
2025 | | |
2026 | | static int radius_client_dump_acct_server(char *buf, size_t buflen, |
2027 | | struct hostapd_radius_server *serv, |
2028 | | struct radius_client_data *cli) |
2029 | 0 | { |
2030 | 0 | int pending = 0; |
2031 | 0 | struct radius_msg_list *msg; |
2032 | 0 | char abuf[50]; |
2033 | |
|
2034 | 0 | if (cli) { |
2035 | 0 | for (msg = cli->msgs; msg; msg = msg->next) { |
2036 | 0 | if (msg->msg_type == RADIUS_ACCT || |
2037 | 0 | msg->msg_type == RADIUS_ACCT_INTERIM) |
2038 | 0 | pending++; |
2039 | 0 | } |
2040 | 0 | } |
2041 | |
|
2042 | 0 | return os_snprintf(buf, buflen, |
2043 | 0 | "radiusAccServerIndex=%d\n" |
2044 | 0 | "radiusAccServerAddress=%s\n" |
2045 | 0 | "radiusAccClientServerPortNumber=%d\n" |
2046 | 0 | "radiusAccClientRoundTripTime=%d\n" |
2047 | 0 | "radiusAccClientRequests=%u\n" |
2048 | 0 | "radiusAccClientRetransmissions=%u\n" |
2049 | 0 | "radiusAccClientResponses=%u\n" |
2050 | 0 | "radiusAccClientMalformedResponses=%u\n" |
2051 | 0 | "radiusAccClientBadAuthenticators=%u\n" |
2052 | 0 | "radiusAccClientPendingRequests=%u\n" |
2053 | 0 | "radiusAccClientTimeouts=%u\n" |
2054 | 0 | "radiusAccClientUnknownTypes=%u\n" |
2055 | 0 | "radiusAccClientPacketsDropped=%u\n", |
2056 | 0 | serv->index, |
2057 | 0 | hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), |
2058 | 0 | serv->port, |
2059 | 0 | serv->round_trip_time, |
2060 | 0 | serv->requests, |
2061 | 0 | serv->retransmissions, |
2062 | 0 | serv->responses, |
2063 | 0 | serv->malformed_responses, |
2064 | 0 | serv->bad_authenticators, |
2065 | 0 | pending, |
2066 | 0 | serv->timeouts, |
2067 | 0 | serv->unknown_types, |
2068 | 0 | serv->packets_dropped); |
2069 | 0 | } |
2070 | | |
2071 | | |
2072 | | /** |
2073 | | * radius_client_get_mib - Get RADIUS client MIB information |
2074 | | * @radius: RADIUS client context from radius_client_init() |
2075 | | * @buf: Buffer for returning MIB data in text format |
2076 | | * @buflen: Maximum buf length in octets |
2077 | | * Returns: Number of octets written into the buffer |
2078 | | */ |
2079 | | int radius_client_get_mib(struct radius_client_data *radius, char *buf, |
2080 | | size_t buflen) |
2081 | 0 | { |
2082 | 0 | struct hostapd_radius_servers *conf; |
2083 | 0 | int i; |
2084 | 0 | struct hostapd_radius_server *serv; |
2085 | 0 | int count = 0; |
2086 | |
|
2087 | 0 | if (!radius) |
2088 | 0 | return 0; |
2089 | | |
2090 | 0 | conf = radius->conf; |
2091 | |
|
2092 | 0 | if (conf->auth_servers) { |
2093 | 0 | for (i = 0; i < conf->num_auth_servers; i++) { |
2094 | 0 | serv = &conf->auth_servers[i]; |
2095 | 0 | count += radius_client_dump_auth_server( |
2096 | 0 | buf + count, buflen - count, serv, |
2097 | 0 | serv == conf->auth_server ? |
2098 | 0 | radius : NULL); |
2099 | 0 | } |
2100 | 0 | } |
2101 | |
|
2102 | 0 | if (conf->acct_servers) { |
2103 | 0 | for (i = 0; i < conf->num_acct_servers; i++) { |
2104 | 0 | serv = &conf->acct_servers[i]; |
2105 | 0 | count += radius_client_dump_acct_server( |
2106 | 0 | buf + count, buflen - count, serv, |
2107 | 0 | serv == conf->acct_server ? |
2108 | 0 | radius : NULL); |
2109 | 0 | } |
2110 | 0 | } |
2111 | |
|
2112 | 0 | return count; |
2113 | 0 | } |
2114 | | |
2115 | | |
2116 | | void radius_client_reconfig(struct radius_client_data *radius, |
2117 | | struct hostapd_radius_servers *conf) |
2118 | 0 | { |
2119 | 0 | if (radius) |
2120 | 0 | radius->conf = conf; |
2121 | 0 | } |