radius_client.c revision 214734
1189251Ssam/* 2214734Srpaulo * RADIUS client 3214734Srpaulo * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> 4189251Ssam * 5189251Ssam * This program is free software; you can redistribute it and/or modify 6189251Ssam * it under the terms of the GNU General Public License version 2 as 7189251Ssam * published by the Free Software Foundation. 8189251Ssam * 9189251Ssam * Alternatively, this software may be distributed under the terms of BSD 10189251Ssam * license. 11189251Ssam * 12189251Ssam * See README and COPYING for more details. 13189251Ssam */ 14189251Ssam 15189251Ssam#include "includes.h" 16189251Ssam 17189251Ssam#include "common.h" 18189251Ssam#include "radius.h" 19189251Ssam#include "radius_client.h" 20189251Ssam#include "eloop.h" 21189251Ssam 22189251Ssam/* Defaults for RADIUS retransmit values (exponential backoff) */ 23189251Ssam 24214734Srpaulo/** 25214734Srpaulo * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds 26214734Srpaulo */ 27214734Srpaulo#define RADIUS_CLIENT_FIRST_WAIT 3 28189251Ssam 29214734Srpaulo/** 30214734Srpaulo * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds 31214734Srpaulo */ 32214734Srpaulo#define RADIUS_CLIENT_MAX_WAIT 120 33214734Srpaulo 34214734Srpaulo/** 35214734Srpaulo * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries 36214734Srpaulo * 37214734Srpaulo * Maximum number of retransmit attempts before the entry is removed from 38214734Srpaulo * retransmit list. 39214734Srpaulo */ 40214734Srpaulo#define RADIUS_CLIENT_MAX_RETRIES 10 41214734Srpaulo 42214734Srpaulo/** 43214734Srpaulo * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages 44214734Srpaulo * 45214734Srpaulo * Maximum number of entries in retransmit list (oldest entries will be 46214734Srpaulo * removed, if this limit is exceeded). 47214734Srpaulo */ 48214734Srpaulo#define RADIUS_CLIENT_MAX_ENTRIES 30 49214734Srpaulo 50214734Srpaulo/** 51214734Srpaulo * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point 52214734Srpaulo * 53214734Srpaulo * The number of failed retry attempts after which the RADIUS server will be 54214734Srpaulo * changed (if one of more backup servers are configured). 55214734Srpaulo */ 56214734Srpaulo#define RADIUS_CLIENT_NUM_FAILOVER 4 57214734Srpaulo 58214734Srpaulo 59214734Srpaulo/** 60214734Srpaulo * struct radius_rx_handler - RADIUS client RX handler 61214734Srpaulo * 62214734Srpaulo * This data structure is used internally inside the RADIUS client module to 63214734Srpaulo * store registered RX handlers. These handlers are registered by calls to 64214734Srpaulo * radius_client_register() and unregistered when the RADIUS client is 65214734Srpaulo * deinitialized with a call to radius_client_deinit(). 66214734Srpaulo */ 67189251Ssamstruct radius_rx_handler { 68214734Srpaulo /** 69214734Srpaulo * handler - Received RADIUS message handler 70214734Srpaulo */ 71189251Ssam RadiusRxResult (*handler)(struct radius_msg *msg, 72189251Ssam struct radius_msg *req, 73189251Ssam const u8 *shared_secret, 74189251Ssam size_t shared_secret_len, 75189251Ssam void *data); 76214734Srpaulo 77214734Srpaulo /** 78214734Srpaulo * data - Context data for the handler 79214734Srpaulo */ 80189251Ssam void *data; 81189251Ssam}; 82189251Ssam 83189251Ssam 84214734Srpaulo/** 85214734Srpaulo * struct radius_msg_list - RADIUS client message retransmit list 86214734Srpaulo * 87214734Srpaulo * This data structure is used internally inside the RADIUS client module to 88214734Srpaulo * store pending RADIUS requests that may still need to be retransmitted. 89214734Srpaulo */ 90189251Ssamstruct radius_msg_list { 91214734Srpaulo /** 92214734Srpaulo * addr - STA/client address 93214734Srpaulo * 94214734Srpaulo * This is used to find RADIUS messages for the same STA. 95214734Srpaulo */ 96214734Srpaulo u8 addr[ETH_ALEN]; 97214734Srpaulo 98214734Srpaulo /** 99214734Srpaulo * msg - RADIUS message 100214734Srpaulo */ 101189251Ssam struct radius_msg *msg; 102214734Srpaulo 103214734Srpaulo /** 104214734Srpaulo * msg_type - Message type 105214734Srpaulo */ 106189251Ssam RadiusType msg_type; 107214734Srpaulo 108214734Srpaulo /** 109214734Srpaulo * first_try - Time of the first transmission attempt 110214734Srpaulo */ 111189251Ssam os_time_t first_try; 112214734Srpaulo 113214734Srpaulo /** 114214734Srpaulo * next_try - Time for the next transmission attempt 115214734Srpaulo */ 116189251Ssam os_time_t next_try; 117214734Srpaulo 118214734Srpaulo /** 119214734Srpaulo * attempts - Number of transmission attempts 120214734Srpaulo */ 121189251Ssam int attempts; 122214734Srpaulo 123214734Srpaulo /** 124214734Srpaulo * next_wait - Next retransmission wait time in seconds 125214734Srpaulo */ 126189251Ssam int next_wait; 127214734Srpaulo 128214734Srpaulo /** 129214734Srpaulo * last_attempt - Time of the last transmission attempt 130214734Srpaulo */ 131189251Ssam struct os_time last_attempt; 132189251Ssam 133214734Srpaulo /** 134214734Srpaulo * shared_secret - Shared secret with the target RADIUS server 135214734Srpaulo */ 136214734Srpaulo const u8 *shared_secret; 137214734Srpaulo 138214734Srpaulo /** 139214734Srpaulo * shared_secret_len - shared_secret length in octets 140214734Srpaulo */ 141189251Ssam size_t shared_secret_len; 142189251Ssam 143189251Ssam /* TODO: server config with failover to backup server(s) */ 144189251Ssam 145214734Srpaulo /** 146214734Srpaulo * next - Next message in the list 147214734Srpaulo */ 148189251Ssam struct radius_msg_list *next; 149189251Ssam}; 150189251Ssam 151189251Ssam 152214734Srpaulo/** 153214734Srpaulo * struct radius_client_data - Internal RADIUS client data 154214734Srpaulo * 155214734Srpaulo * This data structure is used internally inside the RADIUS client module. 156214734Srpaulo * External users allocate this by calling radius_client_init() and free it by 157214734Srpaulo * calling radius_client_deinit(). The pointer to this opaque data is used in 158214734Srpaulo * calls to other functions as an identifier for the RADIUS client instance. 159214734Srpaulo */ 160189251Ssamstruct radius_client_data { 161214734Srpaulo /** 162214734Srpaulo * ctx - Context pointer for hostapd_logger() callbacks 163214734Srpaulo */ 164189251Ssam void *ctx; 165214734Srpaulo 166214734Srpaulo /** 167214734Srpaulo * conf - RADIUS client configuration (list of RADIUS servers to use) 168214734Srpaulo */ 169189251Ssam struct hostapd_radius_servers *conf; 170189251Ssam 171214734Srpaulo /** 172214734Srpaulo * auth_serv_sock - IPv4 socket for RADIUS authentication messages 173214734Srpaulo */ 174214734Srpaulo int auth_serv_sock; 175214734Srpaulo 176214734Srpaulo /** 177214734Srpaulo * acct_serv_sock - IPv4 socket for RADIUS accounting messages 178214734Srpaulo */ 179214734Srpaulo int acct_serv_sock; 180214734Srpaulo 181214734Srpaulo /** 182214734Srpaulo * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages 183214734Srpaulo */ 184189251Ssam int auth_serv_sock6; 185214734Srpaulo 186214734Srpaulo /** 187214734Srpaulo * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages 188214734Srpaulo */ 189189251Ssam int acct_serv_sock6; 190189251Ssam 191214734Srpaulo /** 192214734Srpaulo * auth_sock - Currently used socket for RADIUS authentication server 193214734Srpaulo */ 194214734Srpaulo int auth_sock; 195214734Srpaulo 196214734Srpaulo /** 197214734Srpaulo * acct_sock - Currently used socket for RADIUS accounting server 198214734Srpaulo */ 199214734Srpaulo int acct_sock; 200214734Srpaulo 201214734Srpaulo /** 202214734Srpaulo * auth_handlers - Authentication message handlers 203214734Srpaulo */ 204189251Ssam struct radius_rx_handler *auth_handlers; 205214734Srpaulo 206214734Srpaulo /** 207214734Srpaulo * num_auth_handlers - Number of handlers in auth_handlers 208214734Srpaulo */ 209189251Ssam size_t num_auth_handlers; 210214734Srpaulo 211214734Srpaulo /** 212214734Srpaulo * acct_handlers - Accounting message handlers 213214734Srpaulo */ 214189251Ssam struct radius_rx_handler *acct_handlers; 215214734Srpaulo 216214734Srpaulo /** 217214734Srpaulo * num_acct_handlers - Number of handlers in acct_handlers 218214734Srpaulo */ 219189251Ssam size_t num_acct_handlers; 220189251Ssam 221214734Srpaulo /** 222214734Srpaulo * msgs - Pending outgoing RADIUS messages 223214734Srpaulo */ 224189251Ssam struct radius_msg_list *msgs; 225214734Srpaulo 226214734Srpaulo /** 227214734Srpaulo * num_msgs - Number of pending messages in the msgs list 228214734Srpaulo */ 229189251Ssam size_t num_msgs; 230189251Ssam 231214734Srpaulo /** 232214734Srpaulo * next_radius_identifier - Next RADIUS message identifier to use 233214734Srpaulo */ 234189251Ssam u8 next_radius_identifier; 235189251Ssam}; 236189251Ssam 237189251Ssam 238189251Ssamstatic int 239189251Ssamradius_change_server(struct radius_client_data *radius, 240189251Ssam struct hostapd_radius_server *nserv, 241189251Ssam struct hostapd_radius_server *oserv, 242189251Ssam int sock, int sock6, int auth); 243189251Ssamstatic int radius_client_init_acct(struct radius_client_data *radius); 244189251Ssamstatic int radius_client_init_auth(struct radius_client_data *radius); 245189251Ssam 246189251Ssam 247189251Ssamstatic void radius_client_msg_free(struct radius_msg_list *req) 248189251Ssam{ 249189251Ssam radius_msg_free(req->msg); 250189251Ssam os_free(req); 251189251Ssam} 252189251Ssam 253189251Ssam 254214734Srpaulo/** 255214734Srpaulo * radius_client_register - Register a RADIUS client RX handler 256214734Srpaulo * @radius: RADIUS client context from radius_client_init() 257214734Srpaulo * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT) 258214734Srpaulo * @handler: Handler for received RADIUS messages 259214734Srpaulo * @data: Context pointer for handler callbacks 260214734Srpaulo * Returns: 0 on success, -1 on failure 261214734Srpaulo * 262214734Srpaulo * This function is used to register a handler for processing received RADIUS 263214734Srpaulo * authentication and accounting messages. The handler() callback function will 264214734Srpaulo * be called whenever a RADIUS message is received from the active server. 265214734Srpaulo * 266214734Srpaulo * There can be multiple registered RADIUS message handlers. The handlers will 267214734Srpaulo * be called in order until one of them indicates that it has processed or 268214734Srpaulo * queued the message. 269214734Srpaulo */ 270189251Ssamint radius_client_register(struct radius_client_data *radius, 271189251Ssam RadiusType msg_type, 272189251Ssam RadiusRxResult (*handler)(struct radius_msg *msg, 273189251Ssam struct radius_msg *req, 274189251Ssam const u8 *shared_secret, 275189251Ssam size_t shared_secret_len, 276189251Ssam void *data), 277189251Ssam void *data) 278189251Ssam{ 279189251Ssam struct radius_rx_handler **handlers, *newh; 280189251Ssam size_t *num; 281189251Ssam 282189251Ssam if (msg_type == RADIUS_ACCT) { 283189251Ssam handlers = &radius->acct_handlers; 284189251Ssam num = &radius->num_acct_handlers; 285189251Ssam } else { 286189251Ssam handlers = &radius->auth_handlers; 287189251Ssam num = &radius->num_auth_handlers; 288189251Ssam } 289189251Ssam 290189251Ssam newh = os_realloc(*handlers, 291189251Ssam (*num + 1) * sizeof(struct radius_rx_handler)); 292189251Ssam if (newh == NULL) 293189251Ssam return -1; 294189251Ssam 295189251Ssam newh[*num].handler = handler; 296189251Ssam newh[*num].data = data; 297189251Ssam (*num)++; 298189251Ssam *handlers = newh; 299189251Ssam 300189251Ssam return 0; 301189251Ssam} 302189251Ssam 303189251Ssam 304189251Ssamstatic void radius_client_handle_send_error(struct radius_client_data *radius, 305189251Ssam int s, RadiusType msg_type) 306189251Ssam{ 307189251Ssam#ifndef CONFIG_NATIVE_WINDOWS 308189251Ssam int _errno = errno; 309189251Ssam perror("send[RADIUS]"); 310189251Ssam if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || 311189251Ssam _errno == EBADF) { 312189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 313189251Ssam HOSTAPD_LEVEL_INFO, 314189251Ssam "Send failed - maybe interface status changed -" 315189251Ssam " try to connect again"); 316189251Ssam eloop_unregister_read_sock(s); 317189251Ssam close(s); 318189251Ssam if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) 319189251Ssam radius_client_init_acct(radius); 320189251Ssam else 321189251Ssam radius_client_init_auth(radius); 322189251Ssam } 323189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */ 324189251Ssam} 325189251Ssam 326189251Ssam 327189251Ssamstatic int radius_client_retransmit(struct radius_client_data *radius, 328189251Ssam struct radius_msg_list *entry, 329189251Ssam os_time_t now) 330189251Ssam{ 331189251Ssam struct hostapd_radius_servers *conf = radius->conf; 332189251Ssam int s; 333214734Srpaulo struct wpabuf *buf; 334189251Ssam 335189251Ssam if (entry->msg_type == RADIUS_ACCT || 336189251Ssam entry->msg_type == RADIUS_ACCT_INTERIM) { 337189251Ssam s = radius->acct_sock; 338189251Ssam if (entry->attempts == 0) 339189251Ssam conf->acct_server->requests++; 340189251Ssam else { 341189251Ssam conf->acct_server->timeouts++; 342189251Ssam conf->acct_server->retransmissions++; 343189251Ssam } 344189251Ssam } else { 345189251Ssam s = radius->auth_sock; 346189251Ssam if (entry->attempts == 0) 347189251Ssam conf->auth_server->requests++; 348189251Ssam else { 349189251Ssam conf->auth_server->timeouts++; 350189251Ssam conf->auth_server->retransmissions++; 351189251Ssam } 352189251Ssam } 353189251Ssam 354189251Ssam /* retransmit; remove entry if too many attempts */ 355189251Ssam entry->attempts++; 356189251Ssam hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, 357189251Ssam HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", 358214734Srpaulo radius_msg_get_hdr(entry->msg)->identifier); 359189251Ssam 360189251Ssam os_get_time(&entry->last_attempt); 361214734Srpaulo buf = radius_msg_get_buf(entry->msg); 362214734Srpaulo if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) 363189251Ssam radius_client_handle_send_error(radius, s, entry->msg_type); 364189251Ssam 365189251Ssam entry->next_try = now + entry->next_wait; 366189251Ssam entry->next_wait *= 2; 367189251Ssam if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) 368189251Ssam entry->next_wait = RADIUS_CLIENT_MAX_WAIT; 369189251Ssam if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) { 370189251Ssam printf("Removing un-ACKed RADIUS message due to too many " 371189251Ssam "failed retransmit attempts\n"); 372189251Ssam return 1; 373189251Ssam } 374189251Ssam 375189251Ssam return 0; 376189251Ssam} 377189251Ssam 378189251Ssam 379189251Ssamstatic void radius_client_timer(void *eloop_ctx, void *timeout_ctx) 380189251Ssam{ 381189251Ssam struct radius_client_data *radius = eloop_ctx; 382189251Ssam struct hostapd_radius_servers *conf = radius->conf; 383189251Ssam struct os_time now; 384189251Ssam os_time_t first; 385189251Ssam struct radius_msg_list *entry, *prev, *tmp; 386189251Ssam int auth_failover = 0, acct_failover = 0; 387189251Ssam char abuf[50]; 388189251Ssam 389189251Ssam entry = radius->msgs; 390189251Ssam if (!entry) 391189251Ssam return; 392189251Ssam 393189251Ssam os_get_time(&now); 394189251Ssam first = 0; 395189251Ssam 396189251Ssam prev = NULL; 397189251Ssam while (entry) { 398189251Ssam if (now.sec >= entry->next_try && 399189251Ssam radius_client_retransmit(radius, entry, now.sec)) { 400189251Ssam if (prev) 401189251Ssam prev->next = entry->next; 402189251Ssam else 403189251Ssam radius->msgs = entry->next; 404189251Ssam 405189251Ssam tmp = entry; 406189251Ssam entry = entry->next; 407189251Ssam radius_client_msg_free(tmp); 408189251Ssam radius->num_msgs--; 409189251Ssam continue; 410189251Ssam } 411189251Ssam 412189251Ssam if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) { 413189251Ssam if (entry->msg_type == RADIUS_ACCT || 414189251Ssam entry->msg_type == RADIUS_ACCT_INTERIM) 415189251Ssam acct_failover++; 416189251Ssam else 417189251Ssam auth_failover++; 418189251Ssam } 419189251Ssam 420189251Ssam if (first == 0 || entry->next_try < first) 421189251Ssam first = entry->next_try; 422189251Ssam 423189251Ssam prev = entry; 424189251Ssam entry = entry->next; 425189251Ssam } 426189251Ssam 427189251Ssam if (radius->msgs) { 428189251Ssam if (first < now.sec) 429189251Ssam first = now.sec; 430189251Ssam eloop_register_timeout(first - now.sec, 0, 431189251Ssam radius_client_timer, radius, NULL); 432189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 433189251Ssam HOSTAPD_LEVEL_DEBUG, "Next RADIUS client " 434189251Ssam "retransmit in %ld seconds", 435189251Ssam (long int) (first - now.sec)); 436189251Ssam } 437189251Ssam 438189251Ssam if (auth_failover && conf->num_auth_servers > 1) { 439189251Ssam struct hostapd_radius_server *next, *old; 440189251Ssam old = conf->auth_server; 441189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 442189251Ssam HOSTAPD_LEVEL_NOTICE, 443189251Ssam "No response from Authentication server " 444189251Ssam "%s:%d - failover", 445189251Ssam hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), 446189251Ssam old->port); 447189251Ssam 448189251Ssam for (entry = radius->msgs; entry; entry = entry->next) { 449189251Ssam if (entry->msg_type == RADIUS_AUTH) 450189251Ssam old->timeouts++; 451189251Ssam } 452189251Ssam 453189251Ssam next = old + 1; 454189251Ssam if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) 455189251Ssam next = conf->auth_servers; 456189251Ssam conf->auth_server = next; 457189251Ssam radius_change_server(radius, next, old, 458189251Ssam radius->auth_serv_sock, 459189251Ssam radius->auth_serv_sock6, 1); 460189251Ssam } 461189251Ssam 462189251Ssam if (acct_failover && conf->num_acct_servers > 1) { 463189251Ssam struct hostapd_radius_server *next, *old; 464189251Ssam old = conf->acct_server; 465189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 466189251Ssam HOSTAPD_LEVEL_NOTICE, 467189251Ssam "No response from Accounting server " 468189251Ssam "%s:%d - failover", 469189251Ssam hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), 470189251Ssam old->port); 471189251Ssam 472189251Ssam for (entry = radius->msgs; entry; entry = entry->next) { 473189251Ssam if (entry->msg_type == RADIUS_ACCT || 474189251Ssam entry->msg_type == RADIUS_ACCT_INTERIM) 475189251Ssam old->timeouts++; 476189251Ssam } 477189251Ssam 478189251Ssam next = old + 1; 479189251Ssam if (next > &conf->acct_servers[conf->num_acct_servers - 1]) 480189251Ssam next = conf->acct_servers; 481189251Ssam conf->acct_server = next; 482189251Ssam radius_change_server(radius, next, old, 483189251Ssam radius->acct_serv_sock, 484189251Ssam radius->acct_serv_sock6, 0); 485189251Ssam } 486189251Ssam} 487189251Ssam 488189251Ssam 489189251Ssamstatic void radius_client_update_timeout(struct radius_client_data *radius) 490189251Ssam{ 491189251Ssam struct os_time now; 492189251Ssam os_time_t first; 493189251Ssam struct radius_msg_list *entry; 494189251Ssam 495189251Ssam eloop_cancel_timeout(radius_client_timer, radius, NULL); 496189251Ssam 497189251Ssam if (radius->msgs == NULL) { 498189251Ssam return; 499189251Ssam } 500189251Ssam 501189251Ssam first = 0; 502189251Ssam for (entry = radius->msgs; entry; entry = entry->next) { 503189251Ssam if (first == 0 || entry->next_try < first) 504189251Ssam first = entry->next_try; 505189251Ssam } 506189251Ssam 507189251Ssam os_get_time(&now); 508189251Ssam if (first < now.sec) 509189251Ssam first = now.sec; 510189251Ssam eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, 511189251Ssam NULL); 512189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 513189251Ssam HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" 514189251Ssam " %ld seconds\n", (long int) (first - now.sec)); 515189251Ssam} 516189251Ssam 517189251Ssam 518189251Ssamstatic void radius_client_list_add(struct radius_client_data *radius, 519189251Ssam struct radius_msg *msg, 520214734Srpaulo RadiusType msg_type, 521214734Srpaulo const u8 *shared_secret, 522189251Ssam size_t shared_secret_len, const u8 *addr) 523189251Ssam{ 524189251Ssam struct radius_msg_list *entry, *prev; 525189251Ssam 526189251Ssam if (eloop_terminated()) { 527189251Ssam /* No point in adding entries to retransmit queue since event 528189251Ssam * loop has already been terminated. */ 529189251Ssam radius_msg_free(msg); 530189251Ssam return; 531189251Ssam } 532189251Ssam 533189251Ssam entry = os_zalloc(sizeof(*entry)); 534189251Ssam if (entry == NULL) { 535189251Ssam printf("Failed to add RADIUS packet into retransmit list\n"); 536189251Ssam radius_msg_free(msg); 537189251Ssam return; 538189251Ssam } 539189251Ssam 540189251Ssam if (addr) 541189251Ssam os_memcpy(entry->addr, addr, ETH_ALEN); 542189251Ssam entry->msg = msg; 543189251Ssam entry->msg_type = msg_type; 544189251Ssam entry->shared_secret = shared_secret; 545189251Ssam entry->shared_secret_len = shared_secret_len; 546189251Ssam os_get_time(&entry->last_attempt); 547189251Ssam entry->first_try = entry->last_attempt.sec; 548189251Ssam entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; 549189251Ssam entry->attempts = 1; 550189251Ssam entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; 551189251Ssam entry->next = radius->msgs; 552189251Ssam radius->msgs = entry; 553189251Ssam radius_client_update_timeout(radius); 554189251Ssam 555189251Ssam if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { 556189251Ssam printf("Removing the oldest un-ACKed RADIUS packet due to " 557189251Ssam "retransmit list limits.\n"); 558189251Ssam prev = NULL; 559189251Ssam while (entry->next) { 560189251Ssam prev = entry; 561189251Ssam entry = entry->next; 562189251Ssam } 563189251Ssam if (prev) { 564189251Ssam prev->next = NULL; 565189251Ssam radius_client_msg_free(entry); 566189251Ssam } 567189251Ssam } else 568189251Ssam radius->num_msgs++; 569189251Ssam} 570189251Ssam 571189251Ssam 572189251Ssamstatic void radius_client_list_del(struct radius_client_data *radius, 573189251Ssam RadiusType msg_type, const u8 *addr) 574189251Ssam{ 575189251Ssam struct radius_msg_list *entry, *prev, *tmp; 576189251Ssam 577189251Ssam if (addr == NULL) 578189251Ssam return; 579189251Ssam 580189251Ssam entry = radius->msgs; 581189251Ssam prev = NULL; 582189251Ssam while (entry) { 583189251Ssam if (entry->msg_type == msg_type && 584189251Ssam os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { 585189251Ssam if (prev) 586189251Ssam prev->next = entry->next; 587189251Ssam else 588189251Ssam radius->msgs = entry->next; 589189251Ssam tmp = entry; 590189251Ssam entry = entry->next; 591189251Ssam hostapd_logger(radius->ctx, addr, 592189251Ssam HOSTAPD_MODULE_RADIUS, 593189251Ssam HOSTAPD_LEVEL_DEBUG, 594189251Ssam "Removing matching RADIUS message"); 595189251Ssam radius_client_msg_free(tmp); 596189251Ssam radius->num_msgs--; 597189251Ssam continue; 598189251Ssam } 599189251Ssam prev = entry; 600189251Ssam entry = entry->next; 601189251Ssam } 602189251Ssam} 603189251Ssam 604189251Ssam 605214734Srpaulo/** 606214734Srpaulo * radius_client_send - Send a RADIUS request 607214734Srpaulo * @radius: RADIUS client context from radius_client_init() 608214734Srpaulo * @msg: RADIUS message to be sent 609214734Srpaulo * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM) 610214734Srpaulo * @addr: MAC address of the device related to this message or %NULL 611214734Srpaulo * Returns: 0 on success, -1 on failure 612214734Srpaulo * 613214734Srpaulo * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or 614214734Srpaulo * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference 615214734Srpaulo * between accounting and interim accounting messages is that the interim 616214734Srpaulo * message will override any pending interim accounting updates while a new 617214734Srpaulo * accounting message does not remove any pending messages. 618214734Srpaulo * 619214734Srpaulo * The message is added on the retransmission queue and will be retransmitted 620214734Srpaulo * automatically until a response is received or maximum number of retries 621214734Srpaulo * (RADIUS_CLIENT_MAX_RETRIES) is reached. 622214734Srpaulo * 623214734Srpaulo * The related device MAC address can be used to identify pending messages that 624214734Srpaulo * can be removed with radius_client_flush_auth() or with interim accounting 625214734Srpaulo * updates. 626214734Srpaulo */ 627189251Ssamint radius_client_send(struct radius_client_data *radius, 628189251Ssam struct radius_msg *msg, RadiusType msg_type, 629189251Ssam const u8 *addr) 630189251Ssam{ 631189251Ssam struct hostapd_radius_servers *conf = radius->conf; 632214734Srpaulo const u8 *shared_secret; 633189251Ssam size_t shared_secret_len; 634189251Ssam char *name; 635189251Ssam int s, res; 636214734Srpaulo struct wpabuf *buf; 637189251Ssam 638189251Ssam if (msg_type == RADIUS_ACCT_INTERIM) { 639189251Ssam /* Remove any pending interim acct update for the same STA. */ 640189251Ssam radius_client_list_del(radius, msg_type, addr); 641189251Ssam } 642189251Ssam 643189251Ssam if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { 644189251Ssam if (conf->acct_server == NULL) { 645189251Ssam hostapd_logger(radius->ctx, NULL, 646189251Ssam HOSTAPD_MODULE_RADIUS, 647189251Ssam HOSTAPD_LEVEL_INFO, 648189251Ssam "No accounting server configured"); 649189251Ssam return -1; 650189251Ssam } 651189251Ssam shared_secret = conf->acct_server->shared_secret; 652189251Ssam shared_secret_len = conf->acct_server->shared_secret_len; 653189251Ssam radius_msg_finish_acct(msg, shared_secret, shared_secret_len); 654189251Ssam name = "accounting"; 655189251Ssam s = radius->acct_sock; 656189251Ssam conf->acct_server->requests++; 657189251Ssam } else { 658189251Ssam if (conf->auth_server == NULL) { 659189251Ssam hostapd_logger(radius->ctx, NULL, 660189251Ssam HOSTAPD_MODULE_RADIUS, 661189251Ssam HOSTAPD_LEVEL_INFO, 662189251Ssam "No authentication server configured"); 663189251Ssam return -1; 664189251Ssam } 665189251Ssam shared_secret = conf->auth_server->shared_secret; 666189251Ssam shared_secret_len = conf->auth_server->shared_secret_len; 667189251Ssam radius_msg_finish(msg, shared_secret, shared_secret_len); 668189251Ssam name = "authentication"; 669189251Ssam s = radius->auth_sock; 670189251Ssam conf->auth_server->requests++; 671189251Ssam } 672189251Ssam 673189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 674189251Ssam HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s " 675189251Ssam "server", name); 676189251Ssam if (conf->msg_dumps) 677189251Ssam radius_msg_dump(msg); 678189251Ssam 679214734Srpaulo buf = radius_msg_get_buf(msg); 680214734Srpaulo res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0); 681189251Ssam if (res < 0) 682189251Ssam radius_client_handle_send_error(radius, s, msg_type); 683189251Ssam 684189251Ssam radius_client_list_add(radius, msg, msg_type, shared_secret, 685189251Ssam shared_secret_len, addr); 686189251Ssam 687189251Ssam return res; 688189251Ssam} 689189251Ssam 690189251Ssam 691189251Ssamstatic void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) 692189251Ssam{ 693189251Ssam struct radius_client_data *radius = eloop_ctx; 694189251Ssam struct hostapd_radius_servers *conf = radius->conf; 695189251Ssam RadiusType msg_type = (RadiusType) sock_ctx; 696189251Ssam int len, roundtrip; 697189251Ssam unsigned char buf[3000]; 698189251Ssam struct radius_msg *msg; 699214734Srpaulo struct radius_hdr *hdr; 700189251Ssam struct radius_rx_handler *handlers; 701189251Ssam size_t num_handlers, i; 702189251Ssam struct radius_msg_list *req, *prev_req; 703189251Ssam struct os_time now; 704189251Ssam struct hostapd_radius_server *rconf; 705189251Ssam int invalid_authenticator = 0; 706189251Ssam 707189251Ssam if (msg_type == RADIUS_ACCT) { 708189251Ssam handlers = radius->acct_handlers; 709189251Ssam num_handlers = radius->num_acct_handlers; 710189251Ssam rconf = conf->acct_server; 711189251Ssam } else { 712189251Ssam handlers = radius->auth_handlers; 713189251Ssam num_handlers = radius->num_auth_handlers; 714189251Ssam rconf = conf->auth_server; 715189251Ssam } 716189251Ssam 717189251Ssam len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); 718189251Ssam if (len < 0) { 719189251Ssam perror("recv[RADIUS]"); 720189251Ssam return; 721189251Ssam } 722189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 723189251Ssam HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS " 724189251Ssam "server", len); 725189251Ssam if (len == sizeof(buf)) { 726189251Ssam printf("Possibly too long UDP frame for our buffer - " 727189251Ssam "dropping it\n"); 728189251Ssam return; 729189251Ssam } 730189251Ssam 731189251Ssam msg = radius_msg_parse(buf, len); 732189251Ssam if (msg == NULL) { 733189251Ssam printf("Parsing incoming RADIUS frame failed\n"); 734189251Ssam rconf->malformed_responses++; 735189251Ssam return; 736189251Ssam } 737214734Srpaulo hdr = radius_msg_get_hdr(msg); 738189251Ssam 739189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 740189251Ssam HOSTAPD_LEVEL_DEBUG, "Received RADIUS message"); 741189251Ssam if (conf->msg_dumps) 742189251Ssam radius_msg_dump(msg); 743189251Ssam 744214734Srpaulo switch (hdr->code) { 745189251Ssam case RADIUS_CODE_ACCESS_ACCEPT: 746189251Ssam rconf->access_accepts++; 747189251Ssam break; 748189251Ssam case RADIUS_CODE_ACCESS_REJECT: 749189251Ssam rconf->access_rejects++; 750189251Ssam break; 751189251Ssam case RADIUS_CODE_ACCESS_CHALLENGE: 752189251Ssam rconf->access_challenges++; 753189251Ssam break; 754189251Ssam case RADIUS_CODE_ACCOUNTING_RESPONSE: 755189251Ssam rconf->responses++; 756189251Ssam break; 757189251Ssam } 758189251Ssam 759189251Ssam prev_req = NULL; 760189251Ssam req = radius->msgs; 761189251Ssam while (req) { 762189251Ssam /* TODO: also match by src addr:port of the packet when using 763189251Ssam * alternative RADIUS servers (?) */ 764189251Ssam if ((req->msg_type == msg_type || 765189251Ssam (req->msg_type == RADIUS_ACCT_INTERIM && 766189251Ssam msg_type == RADIUS_ACCT)) && 767214734Srpaulo radius_msg_get_hdr(req->msg)->identifier == 768214734Srpaulo hdr->identifier) 769189251Ssam break; 770189251Ssam 771189251Ssam prev_req = req; 772189251Ssam req = req->next; 773189251Ssam } 774189251Ssam 775189251Ssam if (req == NULL) { 776189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 777189251Ssam HOSTAPD_LEVEL_DEBUG, 778189251Ssam "No matching RADIUS request found (type=%d " 779189251Ssam "id=%d) - dropping packet", 780214734Srpaulo msg_type, hdr->identifier); 781189251Ssam goto fail; 782189251Ssam } 783189251Ssam 784189251Ssam os_get_time(&now); 785189251Ssam roundtrip = (now.sec - req->last_attempt.sec) * 100 + 786189251Ssam (now.usec - req->last_attempt.usec) / 10000; 787189251Ssam hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, 788189251Ssam HOSTAPD_LEVEL_DEBUG, 789189251Ssam "Received RADIUS packet matched with a pending " 790189251Ssam "request, round trip time %d.%02d sec", 791189251Ssam roundtrip / 100, roundtrip % 100); 792189251Ssam rconf->round_trip_time = roundtrip; 793189251Ssam 794189251Ssam /* Remove ACKed RADIUS packet from retransmit list */ 795189251Ssam if (prev_req) 796189251Ssam prev_req->next = req->next; 797189251Ssam else 798189251Ssam radius->msgs = req->next; 799189251Ssam radius->num_msgs--; 800189251Ssam 801189251Ssam for (i = 0; i < num_handlers; i++) { 802189251Ssam RadiusRxResult res; 803189251Ssam res = handlers[i].handler(msg, req->msg, req->shared_secret, 804189251Ssam req->shared_secret_len, 805189251Ssam handlers[i].data); 806189251Ssam switch (res) { 807189251Ssam case RADIUS_RX_PROCESSED: 808189251Ssam radius_msg_free(msg); 809189251Ssam /* continue */ 810189251Ssam case RADIUS_RX_QUEUED: 811189251Ssam radius_client_msg_free(req); 812189251Ssam return; 813189251Ssam case RADIUS_RX_INVALID_AUTHENTICATOR: 814189251Ssam invalid_authenticator++; 815189251Ssam /* continue */ 816189251Ssam case RADIUS_RX_UNKNOWN: 817189251Ssam /* continue with next handler */ 818189251Ssam break; 819189251Ssam } 820189251Ssam } 821189251Ssam 822189251Ssam if (invalid_authenticator) 823189251Ssam rconf->bad_authenticators++; 824189251Ssam else 825189251Ssam rconf->unknown_types++; 826189251Ssam hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, 827189251Ssam HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found " 828189251Ssam "(type=%d code=%d id=%d)%s - dropping packet", 829214734Srpaulo msg_type, hdr->code, hdr->identifier, 830189251Ssam invalid_authenticator ? " [INVALID AUTHENTICATOR]" : 831189251Ssam ""); 832189251Ssam radius_client_msg_free(req); 833189251Ssam 834189251Ssam fail: 835189251Ssam radius_msg_free(msg); 836189251Ssam} 837189251Ssam 838189251Ssam 839214734Srpaulo/** 840214734Srpaulo * radius_client_get_id - Get an identifier for a new RADIUS message 841214734Srpaulo * @radius: RADIUS client context from radius_client_init() 842214734Srpaulo * Returns: Allocated identifier 843214734Srpaulo * 844214734Srpaulo * This function is used to fetch a unique (among pending requests) identifier 845214734Srpaulo * for a new RADIUS message. 846214734Srpaulo */ 847189251Ssamu8 radius_client_get_id(struct radius_client_data *radius) 848189251Ssam{ 849189251Ssam struct radius_msg_list *entry, *prev, *_remove; 850189251Ssam u8 id = radius->next_radius_identifier++; 851189251Ssam 852189251Ssam /* remove entries with matching id from retransmit list to avoid 853189251Ssam * using new reply from the RADIUS server with an old request */ 854189251Ssam entry = radius->msgs; 855189251Ssam prev = NULL; 856189251Ssam while (entry) { 857214734Srpaulo if (radius_msg_get_hdr(entry->msg)->identifier == id) { 858189251Ssam hostapd_logger(radius->ctx, entry->addr, 859189251Ssam HOSTAPD_MODULE_RADIUS, 860189251Ssam HOSTAPD_LEVEL_DEBUG, 861189251Ssam "Removing pending RADIUS message, " 862189251Ssam "since its id (%d) is reused", id); 863189251Ssam if (prev) 864189251Ssam prev->next = entry->next; 865189251Ssam else 866189251Ssam radius->msgs = entry->next; 867189251Ssam _remove = entry; 868189251Ssam } else { 869189251Ssam _remove = NULL; 870189251Ssam prev = entry; 871189251Ssam } 872189251Ssam entry = entry->next; 873189251Ssam 874189251Ssam if (_remove) 875189251Ssam radius_client_msg_free(_remove); 876189251Ssam } 877189251Ssam 878189251Ssam return id; 879189251Ssam} 880189251Ssam 881189251Ssam 882214734Srpaulo/** 883214734Srpaulo * radius_client_flush - Flush all pending RADIUS client messages 884214734Srpaulo * @radius: RADIUS client context from radius_client_init() 885214734Srpaulo * @only_auth: Whether only authentication messages are removed 886214734Srpaulo */ 887189251Ssamvoid radius_client_flush(struct radius_client_data *radius, int only_auth) 888189251Ssam{ 889189251Ssam struct radius_msg_list *entry, *prev, *tmp; 890189251Ssam 891189251Ssam if (!radius) 892189251Ssam return; 893189251Ssam 894189251Ssam prev = NULL; 895189251Ssam entry = radius->msgs; 896189251Ssam 897189251Ssam while (entry) { 898189251Ssam if (!only_auth || entry->msg_type == RADIUS_AUTH) { 899189251Ssam if (prev) 900189251Ssam prev->next = entry->next; 901189251Ssam else 902189251Ssam radius->msgs = entry->next; 903189251Ssam 904189251Ssam tmp = entry; 905189251Ssam entry = entry->next; 906189251Ssam radius_client_msg_free(tmp); 907189251Ssam radius->num_msgs--; 908189251Ssam } else { 909189251Ssam prev = entry; 910189251Ssam entry = entry->next; 911189251Ssam } 912189251Ssam } 913189251Ssam 914189251Ssam if (radius->msgs == NULL) 915189251Ssam eloop_cancel_timeout(radius_client_timer, radius, NULL); 916189251Ssam} 917189251Ssam 918189251Ssam 919189251Ssamstatic void radius_client_update_acct_msgs(struct radius_client_data *radius, 920214734Srpaulo const u8 *shared_secret, 921189251Ssam size_t shared_secret_len) 922189251Ssam{ 923189251Ssam struct radius_msg_list *entry; 924189251Ssam 925189251Ssam if (!radius) 926189251Ssam return; 927189251Ssam 928189251Ssam for (entry = radius->msgs; entry; entry = entry->next) { 929189251Ssam if (entry->msg_type == RADIUS_ACCT) { 930189251Ssam entry->shared_secret = shared_secret; 931189251Ssam entry->shared_secret_len = shared_secret_len; 932189251Ssam radius_msg_finish_acct(entry->msg, shared_secret, 933189251Ssam shared_secret_len); 934189251Ssam } 935189251Ssam } 936189251Ssam} 937189251Ssam 938189251Ssam 939189251Ssamstatic int 940189251Ssamradius_change_server(struct radius_client_data *radius, 941189251Ssam struct hostapd_radius_server *nserv, 942189251Ssam struct hostapd_radius_server *oserv, 943189251Ssam int sock, int sock6, int auth) 944189251Ssam{ 945189251Ssam struct sockaddr_in serv, claddr; 946189251Ssam#ifdef CONFIG_IPV6 947189251Ssam struct sockaddr_in6 serv6, claddr6; 948189251Ssam#endif /* CONFIG_IPV6 */ 949189251Ssam struct sockaddr *addr, *cl_addr; 950189251Ssam socklen_t addrlen, claddrlen; 951189251Ssam char abuf[50]; 952189251Ssam int sel_sock; 953189251Ssam struct radius_msg_list *entry; 954189251Ssam struct hostapd_radius_servers *conf = radius->conf; 955189251Ssam 956189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 957189251Ssam HOSTAPD_LEVEL_INFO, 958189251Ssam "%s server %s:%d", 959189251Ssam auth ? "Authentication" : "Accounting", 960189251Ssam hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), 961189251Ssam nserv->port); 962189251Ssam 963189251Ssam if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len || 964189251Ssam os_memcmp(nserv->shared_secret, oserv->shared_secret, 965189251Ssam nserv->shared_secret_len) != 0) { 966189251Ssam /* Pending RADIUS packets used different shared secret, so 967189251Ssam * they need to be modified. Update accounting message 968189251Ssam * authenticators here. Authentication messages are removed 969189251Ssam * since they would require more changes and the new RADIUS 970189251Ssam * server may not be prepared to receive them anyway due to 971189251Ssam * missing state information. Client will likely retry 972189251Ssam * authentication, so this should not be an issue. */ 973189251Ssam if (auth) 974189251Ssam radius_client_flush(radius, 1); 975189251Ssam else { 976189251Ssam radius_client_update_acct_msgs( 977189251Ssam radius, nserv->shared_secret, 978189251Ssam nserv->shared_secret_len); 979189251Ssam } 980189251Ssam } 981189251Ssam 982189251Ssam /* Reset retry counters for the new server */ 983189251Ssam for (entry = radius->msgs; entry; entry = entry->next) { 984189251Ssam if ((auth && entry->msg_type != RADIUS_AUTH) || 985189251Ssam (!auth && entry->msg_type != RADIUS_ACCT)) 986189251Ssam continue; 987189251Ssam entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; 988189251Ssam entry->attempts = 0; 989189251Ssam entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; 990189251Ssam } 991189251Ssam 992189251Ssam if (radius->msgs) { 993189251Ssam eloop_cancel_timeout(radius_client_timer, radius, NULL); 994189251Ssam eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, 995189251Ssam radius_client_timer, radius, NULL); 996189251Ssam } 997189251Ssam 998189251Ssam switch (nserv->addr.af) { 999189251Ssam case AF_INET: 1000189251Ssam os_memset(&serv, 0, sizeof(serv)); 1001189251Ssam serv.sin_family = AF_INET; 1002189251Ssam serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr; 1003189251Ssam serv.sin_port = htons(nserv->port); 1004189251Ssam addr = (struct sockaddr *) &serv; 1005189251Ssam addrlen = sizeof(serv); 1006189251Ssam sel_sock = sock; 1007189251Ssam break; 1008189251Ssam#ifdef CONFIG_IPV6 1009189251Ssam case AF_INET6: 1010189251Ssam os_memset(&serv6, 0, sizeof(serv6)); 1011189251Ssam serv6.sin6_family = AF_INET6; 1012189251Ssam os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6, 1013189251Ssam sizeof(struct in6_addr)); 1014189251Ssam serv6.sin6_port = htons(nserv->port); 1015189251Ssam addr = (struct sockaddr *) &serv6; 1016189251Ssam addrlen = sizeof(serv6); 1017189251Ssam sel_sock = sock6; 1018189251Ssam break; 1019189251Ssam#endif /* CONFIG_IPV6 */ 1020189251Ssam default: 1021189251Ssam return -1; 1022189251Ssam } 1023189251Ssam 1024189251Ssam if (conf->force_client_addr) { 1025189251Ssam switch (conf->client_addr.af) { 1026189251Ssam case AF_INET: 1027189251Ssam os_memset(&claddr, 0, sizeof(claddr)); 1028189251Ssam claddr.sin_family = AF_INET; 1029189251Ssam claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr; 1030189251Ssam claddr.sin_port = htons(0); 1031189251Ssam cl_addr = (struct sockaddr *) &claddr; 1032189251Ssam claddrlen = sizeof(claddr); 1033189251Ssam break; 1034189251Ssam#ifdef CONFIG_IPV6 1035189251Ssam case AF_INET6: 1036189251Ssam os_memset(&claddr6, 0, sizeof(claddr6)); 1037189251Ssam claddr6.sin6_family = AF_INET6; 1038189251Ssam os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6, 1039189251Ssam sizeof(struct in6_addr)); 1040189251Ssam claddr6.sin6_port = htons(0); 1041189251Ssam cl_addr = (struct sockaddr *) &claddr6; 1042189251Ssam claddrlen = sizeof(claddr6); 1043189251Ssam break; 1044189251Ssam#endif /* CONFIG_IPV6 */ 1045189251Ssam default: 1046189251Ssam return -1; 1047189251Ssam } 1048189251Ssam 1049189251Ssam if (bind(sel_sock, cl_addr, claddrlen) < 0) { 1050189251Ssam perror("bind[radius]"); 1051189251Ssam return -1; 1052189251Ssam } 1053189251Ssam } 1054189251Ssam 1055189251Ssam if (connect(sel_sock, addr, addrlen) < 0) { 1056189251Ssam perror("connect[radius]"); 1057189251Ssam return -1; 1058189251Ssam } 1059189251Ssam 1060189251Ssam#ifndef CONFIG_NATIVE_WINDOWS 1061189251Ssam switch (nserv->addr.af) { 1062189251Ssam case AF_INET: 1063189251Ssam claddrlen = sizeof(claddr); 1064189251Ssam getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen); 1065189251Ssam wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", 1066189251Ssam inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port)); 1067189251Ssam break; 1068189251Ssam#ifdef CONFIG_IPV6 1069189251Ssam case AF_INET6: { 1070189251Ssam claddrlen = sizeof(claddr6); 1071189251Ssam getsockname(sel_sock, (struct sockaddr *) &claddr6, 1072189251Ssam &claddrlen); 1073189251Ssam wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", 1074189251Ssam inet_ntop(AF_INET6, &claddr6.sin6_addr, 1075189251Ssam abuf, sizeof(abuf)), 1076189251Ssam ntohs(claddr6.sin6_port)); 1077189251Ssam break; 1078189251Ssam } 1079189251Ssam#endif /* CONFIG_IPV6 */ 1080189251Ssam } 1081189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */ 1082189251Ssam 1083189251Ssam if (auth) 1084189251Ssam radius->auth_sock = sel_sock; 1085189251Ssam else 1086189251Ssam radius->acct_sock = sel_sock; 1087189251Ssam 1088189251Ssam return 0; 1089189251Ssam} 1090189251Ssam 1091189251Ssam 1092189251Ssamstatic void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) 1093189251Ssam{ 1094189251Ssam struct radius_client_data *radius = eloop_ctx; 1095189251Ssam struct hostapd_radius_servers *conf = radius->conf; 1096189251Ssam struct hostapd_radius_server *oserv; 1097189251Ssam 1098189251Ssam if (radius->auth_sock >= 0 && conf->auth_servers && 1099189251Ssam conf->auth_server != conf->auth_servers) { 1100189251Ssam oserv = conf->auth_server; 1101189251Ssam conf->auth_server = conf->auth_servers; 1102189251Ssam radius_change_server(radius, conf->auth_server, oserv, 1103189251Ssam radius->auth_serv_sock, 1104189251Ssam radius->auth_serv_sock6, 1); 1105189251Ssam } 1106189251Ssam 1107189251Ssam if (radius->acct_sock >= 0 && conf->acct_servers && 1108189251Ssam conf->acct_server != conf->acct_servers) { 1109189251Ssam oserv = conf->acct_server; 1110189251Ssam conf->acct_server = conf->acct_servers; 1111189251Ssam radius_change_server(radius, conf->acct_server, oserv, 1112189251Ssam radius->acct_serv_sock, 1113189251Ssam radius->acct_serv_sock6, 0); 1114189251Ssam } 1115189251Ssam 1116189251Ssam if (conf->retry_primary_interval) 1117189251Ssam eloop_register_timeout(conf->retry_primary_interval, 0, 1118189251Ssam radius_retry_primary_timer, radius, 1119189251Ssam NULL); 1120189251Ssam} 1121189251Ssam 1122189251Ssam 1123209158Srpaulostatic int radius_client_disable_pmtu_discovery(int s) 1124209158Srpaulo{ 1125209158Srpaulo int r = -1; 1126209158Srpaulo#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) 1127209158Srpaulo /* Turn off Path MTU discovery on IPv4/UDP sockets. */ 1128209158Srpaulo int action = IP_PMTUDISC_DONT; 1129209158Srpaulo r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, 1130209158Srpaulo sizeof(action)); 1131209158Srpaulo if (r == -1) 1132209158Srpaulo wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: " 1133209158Srpaulo "%s", strerror(errno)); 1134209158Srpaulo#endif 1135209158Srpaulo return r; 1136209158Srpaulo} 1137209158Srpaulo 1138209158Srpaulo 1139189251Ssamstatic int radius_client_init_auth(struct radius_client_data *radius) 1140189251Ssam{ 1141189251Ssam struct hostapd_radius_servers *conf = radius->conf; 1142189251Ssam int ok = 0; 1143189251Ssam 1144189251Ssam radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); 1145189251Ssam if (radius->auth_serv_sock < 0) 1146189251Ssam perror("socket[PF_INET,SOCK_DGRAM]"); 1147209158Srpaulo else { 1148209158Srpaulo radius_client_disable_pmtu_discovery(radius->auth_serv_sock); 1149189251Ssam ok++; 1150209158Srpaulo } 1151189251Ssam 1152189251Ssam#ifdef CONFIG_IPV6 1153189251Ssam radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); 1154189251Ssam if (radius->auth_serv_sock6 < 0) 1155189251Ssam perror("socket[PF_INET6,SOCK_DGRAM]"); 1156189251Ssam else 1157189251Ssam ok++; 1158189251Ssam#endif /* CONFIG_IPV6 */ 1159189251Ssam 1160189251Ssam if (ok == 0) 1161189251Ssam return -1; 1162189251Ssam 1163189251Ssam radius_change_server(radius, conf->auth_server, NULL, 1164189251Ssam radius->auth_serv_sock, radius->auth_serv_sock6, 1165189251Ssam 1); 1166189251Ssam 1167189251Ssam if (radius->auth_serv_sock >= 0 && 1168189251Ssam eloop_register_read_sock(radius->auth_serv_sock, 1169189251Ssam radius_client_receive, radius, 1170189251Ssam (void *) RADIUS_AUTH)) { 1171189251Ssam printf("Could not register read socket for authentication " 1172189251Ssam "server\n"); 1173189251Ssam return -1; 1174189251Ssam } 1175189251Ssam 1176189251Ssam#ifdef CONFIG_IPV6 1177189251Ssam if (radius->auth_serv_sock6 >= 0 && 1178189251Ssam eloop_register_read_sock(radius->auth_serv_sock6, 1179189251Ssam radius_client_receive, radius, 1180189251Ssam (void *) RADIUS_AUTH)) { 1181189251Ssam printf("Could not register read socket for authentication " 1182189251Ssam "server\n"); 1183189251Ssam return -1; 1184189251Ssam } 1185189251Ssam#endif /* CONFIG_IPV6 */ 1186189251Ssam 1187189251Ssam return 0; 1188189251Ssam} 1189189251Ssam 1190189251Ssam 1191189251Ssamstatic int radius_client_init_acct(struct radius_client_data *radius) 1192189251Ssam{ 1193189251Ssam struct hostapd_radius_servers *conf = radius->conf; 1194189251Ssam int ok = 0; 1195189251Ssam 1196189251Ssam radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); 1197189251Ssam if (radius->acct_serv_sock < 0) 1198189251Ssam perror("socket[PF_INET,SOCK_DGRAM]"); 1199209158Srpaulo else { 1200209158Srpaulo radius_client_disable_pmtu_discovery(radius->acct_serv_sock); 1201189251Ssam ok++; 1202209158Srpaulo } 1203189251Ssam 1204189251Ssam#ifdef CONFIG_IPV6 1205189251Ssam radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); 1206189251Ssam if (radius->acct_serv_sock6 < 0) 1207189251Ssam perror("socket[PF_INET6,SOCK_DGRAM]"); 1208189251Ssam else 1209189251Ssam ok++; 1210189251Ssam#endif /* CONFIG_IPV6 */ 1211189251Ssam 1212189251Ssam if (ok == 0) 1213189251Ssam return -1; 1214189251Ssam 1215189251Ssam radius_change_server(radius, conf->acct_server, NULL, 1216189251Ssam radius->acct_serv_sock, radius->acct_serv_sock6, 1217189251Ssam 0); 1218189251Ssam 1219189251Ssam if (radius->acct_serv_sock >= 0 && 1220189251Ssam eloop_register_read_sock(radius->acct_serv_sock, 1221189251Ssam radius_client_receive, radius, 1222189251Ssam (void *) RADIUS_ACCT)) { 1223189251Ssam printf("Could not register read socket for accounting " 1224189251Ssam "server\n"); 1225189251Ssam return -1; 1226189251Ssam } 1227189251Ssam 1228189251Ssam#ifdef CONFIG_IPV6 1229189251Ssam if (radius->acct_serv_sock6 >= 0 && 1230189251Ssam eloop_register_read_sock(radius->acct_serv_sock6, 1231189251Ssam radius_client_receive, radius, 1232189251Ssam (void *) RADIUS_ACCT)) { 1233189251Ssam printf("Could not register read socket for accounting " 1234189251Ssam "server\n"); 1235189251Ssam return -1; 1236189251Ssam } 1237189251Ssam#endif /* CONFIG_IPV6 */ 1238189251Ssam 1239189251Ssam return 0; 1240189251Ssam} 1241189251Ssam 1242189251Ssam 1243214734Srpaulo/** 1244214734Srpaulo * radius_client_init - Initialize RADIUS client 1245214734Srpaulo * @ctx: Callback context to be used in hostapd_logger() calls 1246214734Srpaulo * @conf: RADIUS client configuration (RADIUS servers) 1247214734Srpaulo * Returns: Pointer to private RADIUS client context or %NULL on failure 1248214734Srpaulo * 1249214734Srpaulo * The caller is responsible for keeping the configuration data available for 1250214734Srpaulo * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is 1251214734Srpaulo * called for the returned context pointer. 1252214734Srpaulo */ 1253189251Ssamstruct radius_client_data * 1254189251Ssamradius_client_init(void *ctx, struct hostapd_radius_servers *conf) 1255189251Ssam{ 1256189251Ssam struct radius_client_data *radius; 1257189251Ssam 1258189251Ssam radius = os_zalloc(sizeof(struct radius_client_data)); 1259189251Ssam if (radius == NULL) 1260189251Ssam return NULL; 1261189251Ssam 1262189251Ssam radius->ctx = ctx; 1263189251Ssam radius->conf = conf; 1264189251Ssam radius->auth_serv_sock = radius->acct_serv_sock = 1265189251Ssam radius->auth_serv_sock6 = radius->acct_serv_sock6 = 1266189251Ssam radius->auth_sock = radius->acct_sock = -1; 1267189251Ssam 1268189251Ssam if (conf->auth_server && radius_client_init_auth(radius)) { 1269189251Ssam radius_client_deinit(radius); 1270189251Ssam return NULL; 1271189251Ssam } 1272189251Ssam 1273189251Ssam if (conf->acct_server && radius_client_init_acct(radius)) { 1274189251Ssam radius_client_deinit(radius); 1275189251Ssam return NULL; 1276189251Ssam } 1277189251Ssam 1278189251Ssam if (conf->retry_primary_interval) 1279189251Ssam eloop_register_timeout(conf->retry_primary_interval, 0, 1280189251Ssam radius_retry_primary_timer, radius, 1281189251Ssam NULL); 1282189251Ssam 1283189251Ssam return radius; 1284189251Ssam} 1285189251Ssam 1286189251Ssam 1287214734Srpaulo/** 1288214734Srpaulo * radius_client_deinit - Deinitialize RADIUS client 1289214734Srpaulo * @radius: RADIUS client context from radius_client_init() 1290214734Srpaulo */ 1291189251Ssamvoid radius_client_deinit(struct radius_client_data *radius) 1292189251Ssam{ 1293189251Ssam if (!radius) 1294189251Ssam return; 1295189251Ssam 1296189251Ssam if (radius->auth_serv_sock >= 0) 1297189251Ssam eloop_unregister_read_sock(radius->auth_serv_sock); 1298189251Ssam if (radius->acct_serv_sock >= 0) 1299189251Ssam eloop_unregister_read_sock(radius->acct_serv_sock); 1300209158Srpaulo#ifdef CONFIG_IPV6 1301209158Srpaulo if (radius->auth_serv_sock6 >= 0) 1302209158Srpaulo eloop_unregister_read_sock(radius->auth_serv_sock6); 1303209158Srpaulo if (radius->acct_serv_sock6 >= 0) 1304209158Srpaulo eloop_unregister_read_sock(radius->acct_serv_sock6); 1305209158Srpaulo#endif /* CONFIG_IPV6 */ 1306189251Ssam 1307189251Ssam eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL); 1308189251Ssam 1309189251Ssam radius_client_flush(radius, 0); 1310189251Ssam os_free(radius->auth_handlers); 1311189251Ssam os_free(radius->acct_handlers); 1312189251Ssam os_free(radius); 1313189251Ssam} 1314189251Ssam 1315189251Ssam 1316214734Srpaulo/** 1317214734Srpaulo * radius_client_flush_auth - Flush pending RADIUS messages for an address 1318214734Srpaulo * @radius: RADIUS client context from radius_client_init() 1319214734Srpaulo * @addr: MAC address of the related device 1320214734Srpaulo * 1321214734Srpaulo * This function can be used to remove pending RADIUS authentication messages 1322214734Srpaulo * that are related to a specific device. The addr parameter is matched with 1323214734Srpaulo * the one used in radius_client_send() call that was used to transmit the 1324214734Srpaulo * authentication request. 1325214734Srpaulo */ 1326214734Srpaulovoid radius_client_flush_auth(struct radius_client_data *radius, 1327214734Srpaulo const u8 *addr) 1328189251Ssam{ 1329189251Ssam struct radius_msg_list *entry, *prev, *tmp; 1330189251Ssam 1331189251Ssam prev = NULL; 1332189251Ssam entry = radius->msgs; 1333189251Ssam while (entry) { 1334189251Ssam if (entry->msg_type == RADIUS_AUTH && 1335189251Ssam os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { 1336189251Ssam hostapd_logger(radius->ctx, addr, 1337189251Ssam HOSTAPD_MODULE_RADIUS, 1338189251Ssam HOSTAPD_LEVEL_DEBUG, 1339189251Ssam "Removing pending RADIUS authentication" 1340189251Ssam " message for removed client"); 1341189251Ssam 1342189251Ssam if (prev) 1343189251Ssam prev->next = entry->next; 1344189251Ssam else 1345189251Ssam radius->msgs = entry->next; 1346189251Ssam 1347189251Ssam tmp = entry; 1348189251Ssam entry = entry->next; 1349189251Ssam radius_client_msg_free(tmp); 1350189251Ssam radius->num_msgs--; 1351189251Ssam continue; 1352189251Ssam } 1353189251Ssam 1354189251Ssam prev = entry; 1355189251Ssam entry = entry->next; 1356189251Ssam } 1357189251Ssam} 1358189251Ssam 1359189251Ssam 1360189251Ssamstatic int radius_client_dump_auth_server(char *buf, size_t buflen, 1361189251Ssam struct hostapd_radius_server *serv, 1362189251Ssam struct radius_client_data *cli) 1363189251Ssam{ 1364189251Ssam int pending = 0; 1365189251Ssam struct radius_msg_list *msg; 1366189251Ssam char abuf[50]; 1367189251Ssam 1368189251Ssam if (cli) { 1369189251Ssam for (msg = cli->msgs; msg; msg = msg->next) { 1370189251Ssam if (msg->msg_type == RADIUS_AUTH) 1371189251Ssam pending++; 1372189251Ssam } 1373189251Ssam } 1374189251Ssam 1375189251Ssam return os_snprintf(buf, buflen, 1376189251Ssam "radiusAuthServerIndex=%d\n" 1377189251Ssam "radiusAuthServerAddress=%s\n" 1378189251Ssam "radiusAuthClientServerPortNumber=%d\n" 1379189251Ssam "radiusAuthClientRoundTripTime=%d\n" 1380189251Ssam "radiusAuthClientAccessRequests=%u\n" 1381189251Ssam "radiusAuthClientAccessRetransmissions=%u\n" 1382189251Ssam "radiusAuthClientAccessAccepts=%u\n" 1383189251Ssam "radiusAuthClientAccessRejects=%u\n" 1384189251Ssam "radiusAuthClientAccessChallenges=%u\n" 1385189251Ssam "radiusAuthClientMalformedAccessResponses=%u\n" 1386189251Ssam "radiusAuthClientBadAuthenticators=%u\n" 1387189251Ssam "radiusAuthClientPendingRequests=%u\n" 1388189251Ssam "radiusAuthClientTimeouts=%u\n" 1389189251Ssam "radiusAuthClientUnknownTypes=%u\n" 1390189251Ssam "radiusAuthClientPacketsDropped=%u\n", 1391189251Ssam serv->index, 1392189251Ssam hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), 1393189251Ssam serv->port, 1394189251Ssam serv->round_trip_time, 1395189251Ssam serv->requests, 1396189251Ssam serv->retransmissions, 1397189251Ssam serv->access_accepts, 1398189251Ssam serv->access_rejects, 1399189251Ssam serv->access_challenges, 1400189251Ssam serv->malformed_responses, 1401189251Ssam serv->bad_authenticators, 1402189251Ssam pending, 1403189251Ssam serv->timeouts, 1404189251Ssam serv->unknown_types, 1405189251Ssam serv->packets_dropped); 1406189251Ssam} 1407189251Ssam 1408189251Ssam 1409189251Ssamstatic int radius_client_dump_acct_server(char *buf, size_t buflen, 1410189251Ssam struct hostapd_radius_server *serv, 1411189251Ssam struct radius_client_data *cli) 1412189251Ssam{ 1413189251Ssam int pending = 0; 1414189251Ssam struct radius_msg_list *msg; 1415189251Ssam char abuf[50]; 1416189251Ssam 1417189251Ssam if (cli) { 1418189251Ssam for (msg = cli->msgs; msg; msg = msg->next) { 1419189251Ssam if (msg->msg_type == RADIUS_ACCT || 1420189251Ssam msg->msg_type == RADIUS_ACCT_INTERIM) 1421189251Ssam pending++; 1422189251Ssam } 1423189251Ssam } 1424189251Ssam 1425189251Ssam return os_snprintf(buf, buflen, 1426189251Ssam "radiusAccServerIndex=%d\n" 1427189251Ssam "radiusAccServerAddress=%s\n" 1428189251Ssam "radiusAccClientServerPortNumber=%d\n" 1429189251Ssam "radiusAccClientRoundTripTime=%d\n" 1430189251Ssam "radiusAccClientRequests=%u\n" 1431189251Ssam "radiusAccClientRetransmissions=%u\n" 1432189251Ssam "radiusAccClientResponses=%u\n" 1433189251Ssam "radiusAccClientMalformedResponses=%u\n" 1434189251Ssam "radiusAccClientBadAuthenticators=%u\n" 1435189251Ssam "radiusAccClientPendingRequests=%u\n" 1436189251Ssam "radiusAccClientTimeouts=%u\n" 1437189251Ssam "radiusAccClientUnknownTypes=%u\n" 1438189251Ssam "radiusAccClientPacketsDropped=%u\n", 1439189251Ssam serv->index, 1440189251Ssam hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), 1441189251Ssam serv->port, 1442189251Ssam serv->round_trip_time, 1443189251Ssam serv->requests, 1444189251Ssam serv->retransmissions, 1445189251Ssam serv->responses, 1446189251Ssam serv->malformed_responses, 1447189251Ssam serv->bad_authenticators, 1448189251Ssam pending, 1449189251Ssam serv->timeouts, 1450189251Ssam serv->unknown_types, 1451189251Ssam serv->packets_dropped); 1452189251Ssam} 1453189251Ssam 1454189251Ssam 1455214734Srpaulo/** 1456214734Srpaulo * radius_client_get_mib - Get RADIUS client MIB information 1457214734Srpaulo * @radius: RADIUS client context from radius_client_init() 1458214734Srpaulo * @buf: Buffer for returning MIB data in text format 1459214734Srpaulo * @buflen: Maximum buf length in octets 1460214734Srpaulo * Returns: Number of octets written into the buffer 1461214734Srpaulo */ 1462189251Ssamint radius_client_get_mib(struct radius_client_data *radius, char *buf, 1463189251Ssam size_t buflen) 1464189251Ssam{ 1465189251Ssam struct hostapd_radius_servers *conf = radius->conf; 1466189251Ssam int i; 1467189251Ssam struct hostapd_radius_server *serv; 1468189251Ssam int count = 0; 1469189251Ssam 1470189251Ssam if (conf->auth_servers) { 1471189251Ssam for (i = 0; i < conf->num_auth_servers; i++) { 1472189251Ssam serv = &conf->auth_servers[i]; 1473189251Ssam count += radius_client_dump_auth_server( 1474189251Ssam buf + count, buflen - count, serv, 1475189251Ssam serv == conf->auth_server ? 1476189251Ssam radius : NULL); 1477189251Ssam } 1478189251Ssam } 1479189251Ssam 1480189251Ssam if (conf->acct_servers) { 1481189251Ssam for (i = 0; i < conf->num_acct_servers; i++) { 1482189251Ssam serv = &conf->acct_servers[i]; 1483189251Ssam count += radius_client_dump_acct_server( 1484189251Ssam buf + count, buflen - count, serv, 1485189251Ssam serv == conf->acct_server ? 1486189251Ssam radius : NULL); 1487189251Ssam } 1488189251Ssam } 1489189251Ssam 1490189251Ssam return count; 1491189251Ssam} 1492