1189251Ssam/* 2214734Srpaulo * RADIUS client 3214734Srpaulo * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam 11189251Ssam#include "common.h" 12189251Ssam#include "radius.h" 13189251Ssam#include "radius_client.h" 14189251Ssam#include "eloop.h" 15189251Ssam 16189251Ssam/* Defaults for RADIUS retransmit values (exponential backoff) */ 17189251Ssam 18214734Srpaulo/** 19214734Srpaulo * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds 20214734Srpaulo */ 21214734Srpaulo#define RADIUS_CLIENT_FIRST_WAIT 3 22189251Ssam 23214734Srpaulo/** 24214734Srpaulo * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds 25214734Srpaulo */ 26214734Srpaulo#define RADIUS_CLIENT_MAX_WAIT 120 27214734Srpaulo 28214734Srpaulo/** 29214734Srpaulo * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries 30214734Srpaulo * 31214734Srpaulo * Maximum number of retransmit attempts before the entry is removed from 32214734Srpaulo * retransmit list. 33214734Srpaulo */ 34214734Srpaulo#define RADIUS_CLIENT_MAX_RETRIES 10 35214734Srpaulo 36214734Srpaulo/** 37214734Srpaulo * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages 38214734Srpaulo * 39214734Srpaulo * Maximum number of entries in retransmit list (oldest entries will be 40214734Srpaulo * removed, if this limit is exceeded). 41214734Srpaulo */ 42214734Srpaulo#define RADIUS_CLIENT_MAX_ENTRIES 30 43214734Srpaulo 44214734Srpaulo/** 45214734Srpaulo * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point 46214734Srpaulo * 47214734Srpaulo * The number of failed retry attempts after which the RADIUS server will be 48214734Srpaulo * changed (if one of more backup servers are configured). 49214734Srpaulo */ 50214734Srpaulo#define RADIUS_CLIENT_NUM_FAILOVER 4 51214734Srpaulo 52214734Srpaulo 53214734Srpaulo/** 54214734Srpaulo * struct radius_rx_handler - RADIUS client RX handler 55214734Srpaulo * 56214734Srpaulo * This data structure is used internally inside the RADIUS client module to 57214734Srpaulo * store registered RX handlers. These handlers are registered by calls to 58214734Srpaulo * radius_client_register() and unregistered when the RADIUS client is 59214734Srpaulo * deinitialized with a call to radius_client_deinit(). 60214734Srpaulo */ 61189251Ssamstruct radius_rx_handler { 62214734Srpaulo /** 63214734Srpaulo * handler - Received RADIUS message handler 64214734Srpaulo */ 65189251Ssam RadiusRxResult (*handler)(struct radius_msg *msg, 66189251Ssam struct radius_msg *req, 67189251Ssam const u8 *shared_secret, 68189251Ssam size_t shared_secret_len, 69189251Ssam void *data); 70214734Srpaulo 71214734Srpaulo /** 72214734Srpaulo * data - Context data for the handler 73214734Srpaulo */ 74189251Ssam void *data; 75189251Ssam}; 76189251Ssam 77189251Ssam 78214734Srpaulo/** 79214734Srpaulo * struct radius_msg_list - RADIUS client message retransmit list 80214734Srpaulo * 81214734Srpaulo * This data structure is used internally inside the RADIUS client module to 82214734Srpaulo * store pending RADIUS requests that may still need to be retransmitted. 83214734Srpaulo */ 84189251Ssamstruct radius_msg_list { 85214734Srpaulo /** 86214734Srpaulo * addr - STA/client address 87214734Srpaulo * 88214734Srpaulo * This is used to find RADIUS messages for the same STA. 89214734Srpaulo */ 90214734Srpaulo u8 addr[ETH_ALEN]; 91214734Srpaulo 92214734Srpaulo /** 93214734Srpaulo * msg - RADIUS message 94214734Srpaulo */ 95189251Ssam struct radius_msg *msg; 96214734Srpaulo 97214734Srpaulo /** 98214734Srpaulo * msg_type - Message type 99214734Srpaulo */ 100189251Ssam RadiusType msg_type; 101214734Srpaulo 102214734Srpaulo /** 103214734Srpaulo * first_try - Time of the first transmission attempt 104214734Srpaulo */ 105189251Ssam os_time_t first_try; 106214734Srpaulo 107214734Srpaulo /** 108214734Srpaulo * next_try - Time for the next transmission attempt 109214734Srpaulo */ 110189251Ssam os_time_t next_try; 111214734Srpaulo 112214734Srpaulo /** 113214734Srpaulo * attempts - Number of transmission attempts 114214734Srpaulo */ 115189251Ssam int attempts; 116214734Srpaulo 117214734Srpaulo /** 118214734Srpaulo * next_wait - Next retransmission wait time in seconds 119214734Srpaulo */ 120189251Ssam int next_wait; 121214734Srpaulo 122214734Srpaulo /** 123214734Srpaulo * last_attempt - Time of the last transmission attempt 124214734Srpaulo */ 125189251Ssam struct os_time last_attempt; 126189251Ssam 127214734Srpaulo /** 128214734Srpaulo * shared_secret - Shared secret with the target RADIUS server 129214734Srpaulo */ 130214734Srpaulo const u8 *shared_secret; 131214734Srpaulo 132214734Srpaulo /** 133214734Srpaulo * shared_secret_len - shared_secret length in octets 134214734Srpaulo */ 135189251Ssam size_t shared_secret_len; 136189251Ssam 137189251Ssam /* TODO: server config with failover to backup server(s) */ 138189251Ssam 139214734Srpaulo /** 140214734Srpaulo * next - Next message in the list 141214734Srpaulo */ 142189251Ssam struct radius_msg_list *next; 143189251Ssam}; 144189251Ssam 145189251Ssam 146214734Srpaulo/** 147214734Srpaulo * struct radius_client_data - Internal RADIUS client data 148214734Srpaulo * 149214734Srpaulo * This data structure is used internally inside the RADIUS client module. 150214734Srpaulo * External users allocate this by calling radius_client_init() and free it by 151214734Srpaulo * calling radius_client_deinit(). The pointer to this opaque data is used in 152214734Srpaulo * calls to other functions as an identifier for the RADIUS client instance. 153214734Srpaulo */ 154189251Ssamstruct radius_client_data { 155214734Srpaulo /** 156214734Srpaulo * ctx - Context pointer for hostapd_logger() callbacks 157214734Srpaulo */ 158189251Ssam void *ctx; 159214734Srpaulo 160214734Srpaulo /** 161214734Srpaulo * conf - RADIUS client configuration (list of RADIUS servers to use) 162214734Srpaulo */ 163189251Ssam struct hostapd_radius_servers *conf; 164189251Ssam 165214734Srpaulo /** 166214734Srpaulo * auth_serv_sock - IPv4 socket for RADIUS authentication messages 167214734Srpaulo */ 168214734Srpaulo int auth_serv_sock; 169214734Srpaulo 170214734Srpaulo /** 171214734Srpaulo * acct_serv_sock - IPv4 socket for RADIUS accounting messages 172214734Srpaulo */ 173214734Srpaulo int acct_serv_sock; 174214734Srpaulo 175214734Srpaulo /** 176214734Srpaulo * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages 177214734Srpaulo */ 178189251Ssam int auth_serv_sock6; 179214734Srpaulo 180214734Srpaulo /** 181214734Srpaulo * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages 182214734Srpaulo */ 183189251Ssam int acct_serv_sock6; 184189251Ssam 185214734Srpaulo /** 186214734Srpaulo * auth_sock - Currently used socket for RADIUS authentication server 187214734Srpaulo */ 188214734Srpaulo int auth_sock; 189214734Srpaulo 190214734Srpaulo /** 191214734Srpaulo * acct_sock - Currently used socket for RADIUS accounting server 192214734Srpaulo */ 193214734Srpaulo int acct_sock; 194214734Srpaulo 195214734Srpaulo /** 196214734Srpaulo * auth_handlers - Authentication message handlers 197214734Srpaulo */ 198189251Ssam struct radius_rx_handler *auth_handlers; 199214734Srpaulo 200214734Srpaulo /** 201214734Srpaulo * num_auth_handlers - Number of handlers in auth_handlers 202214734Srpaulo */ 203189251Ssam size_t num_auth_handlers; 204214734Srpaulo 205214734Srpaulo /** 206214734Srpaulo * acct_handlers - Accounting message handlers 207214734Srpaulo */ 208189251Ssam struct radius_rx_handler *acct_handlers; 209214734Srpaulo 210214734Srpaulo /** 211214734Srpaulo * num_acct_handlers - Number of handlers in acct_handlers 212214734Srpaulo */ 213189251Ssam size_t num_acct_handlers; 214189251Ssam 215214734Srpaulo /** 216214734Srpaulo * msgs - Pending outgoing RADIUS messages 217214734Srpaulo */ 218189251Ssam struct radius_msg_list *msgs; 219214734Srpaulo 220214734Srpaulo /** 221214734Srpaulo * num_msgs - Number of pending messages in the msgs list 222214734Srpaulo */ 223189251Ssam size_t num_msgs; 224189251Ssam 225214734Srpaulo /** 226214734Srpaulo * next_radius_identifier - Next RADIUS message identifier to use 227214734Srpaulo */ 228189251Ssam u8 next_radius_identifier; 229189251Ssam}; 230189251Ssam 231189251Ssam 232189251Ssamstatic int 233189251Ssamradius_change_server(struct radius_client_data *radius, 234189251Ssam struct hostapd_radius_server *nserv, 235189251Ssam struct hostapd_radius_server *oserv, 236189251Ssam int sock, int sock6, int auth); 237189251Ssamstatic int radius_client_init_acct(struct radius_client_data *radius); 238189251Ssamstatic int radius_client_init_auth(struct radius_client_data *radius); 239189251Ssam 240189251Ssam 241189251Ssamstatic void radius_client_msg_free(struct radius_msg_list *req) 242189251Ssam{ 243189251Ssam radius_msg_free(req->msg); 244189251Ssam os_free(req); 245189251Ssam} 246189251Ssam 247189251Ssam 248214734Srpaulo/** 249214734Srpaulo * radius_client_register - Register a RADIUS client RX handler 250214734Srpaulo * @radius: RADIUS client context from radius_client_init() 251214734Srpaulo * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT) 252214734Srpaulo * @handler: Handler for received RADIUS messages 253214734Srpaulo * @data: Context pointer for handler callbacks 254214734Srpaulo * Returns: 0 on success, -1 on failure 255214734Srpaulo * 256214734Srpaulo * This function is used to register a handler for processing received RADIUS 257214734Srpaulo * authentication and accounting messages. The handler() callback function will 258214734Srpaulo * be called whenever a RADIUS message is received from the active server. 259214734Srpaulo * 260214734Srpaulo * There can be multiple registered RADIUS message handlers. The handlers will 261214734Srpaulo * be called in order until one of them indicates that it has processed or 262214734Srpaulo * queued the message. 263214734Srpaulo */ 264189251Ssamint radius_client_register(struct radius_client_data *radius, 265189251Ssam RadiusType msg_type, 266189251Ssam RadiusRxResult (*handler)(struct radius_msg *msg, 267189251Ssam struct radius_msg *req, 268189251Ssam const u8 *shared_secret, 269189251Ssam size_t shared_secret_len, 270189251Ssam void *data), 271189251Ssam void *data) 272189251Ssam{ 273189251Ssam struct radius_rx_handler **handlers, *newh; 274189251Ssam size_t *num; 275189251Ssam 276189251Ssam if (msg_type == RADIUS_ACCT) { 277189251Ssam handlers = &radius->acct_handlers; 278189251Ssam num = &radius->num_acct_handlers; 279189251Ssam } else { 280189251Ssam handlers = &radius->auth_handlers; 281189251Ssam num = &radius->num_auth_handlers; 282189251Ssam } 283189251Ssam 284252726Srpaulo newh = os_realloc_array(*handlers, *num + 1, 285252726Srpaulo sizeof(struct radius_rx_handler)); 286189251Ssam if (newh == NULL) 287189251Ssam return -1; 288189251Ssam 289189251Ssam newh[*num].handler = handler; 290189251Ssam newh[*num].data = data; 291189251Ssam (*num)++; 292189251Ssam *handlers = newh; 293189251Ssam 294189251Ssam return 0; 295189251Ssam} 296189251Ssam 297189251Ssam 298189251Ssamstatic void radius_client_handle_send_error(struct radius_client_data *radius, 299189251Ssam int s, RadiusType msg_type) 300189251Ssam{ 301189251Ssam#ifndef CONFIG_NATIVE_WINDOWS 302189251Ssam int _errno = errno; 303189251Ssam perror("send[RADIUS]"); 304189251Ssam if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || 305189251Ssam _errno == EBADF) { 306189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 307189251Ssam HOSTAPD_LEVEL_INFO, 308189251Ssam "Send failed - maybe interface status changed -" 309189251Ssam " try to connect again"); 310189251Ssam eloop_unregister_read_sock(s); 311189251Ssam close(s); 312189251Ssam if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) 313189251Ssam radius_client_init_acct(radius); 314189251Ssam else 315189251Ssam radius_client_init_auth(radius); 316189251Ssam } 317189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */ 318189251Ssam} 319189251Ssam 320189251Ssam 321189251Ssamstatic int radius_client_retransmit(struct radius_client_data *radius, 322189251Ssam struct radius_msg_list *entry, 323189251Ssam os_time_t now) 324189251Ssam{ 325189251Ssam struct hostapd_radius_servers *conf = radius->conf; 326189251Ssam int s; 327214734Srpaulo struct wpabuf *buf; 328189251Ssam 329189251Ssam if (entry->msg_type == RADIUS_ACCT || 330189251Ssam entry->msg_type == RADIUS_ACCT_INTERIM) { 331189251Ssam s = radius->acct_sock; 332189251Ssam if (entry->attempts == 0) 333189251Ssam conf->acct_server->requests++; 334189251Ssam else { 335189251Ssam conf->acct_server->timeouts++; 336189251Ssam conf->acct_server->retransmissions++; 337189251Ssam } 338189251Ssam } else { 339189251Ssam s = radius->auth_sock; 340189251Ssam if (entry->attempts == 0) 341189251Ssam conf->auth_server->requests++; 342189251Ssam else { 343189251Ssam conf->auth_server->timeouts++; 344189251Ssam conf->auth_server->retransmissions++; 345189251Ssam } 346189251Ssam } 347189251Ssam 348189251Ssam /* retransmit; remove entry if too many attempts */ 349189251Ssam entry->attempts++; 350189251Ssam hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, 351189251Ssam HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", 352214734Srpaulo radius_msg_get_hdr(entry->msg)->identifier); 353189251Ssam 354189251Ssam os_get_time(&entry->last_attempt); 355214734Srpaulo buf = radius_msg_get_buf(entry->msg); 356214734Srpaulo if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) 357189251Ssam radius_client_handle_send_error(radius, s, entry->msg_type); 358189251Ssam 359189251Ssam entry->next_try = now + entry->next_wait; 360189251Ssam entry->next_wait *= 2; 361189251Ssam if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) 362189251Ssam entry->next_wait = RADIUS_CLIENT_MAX_WAIT; 363189251Ssam if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) { 364189251Ssam printf("Removing un-ACKed RADIUS message due to too many " 365189251Ssam "failed retransmit attempts\n"); 366189251Ssam return 1; 367189251Ssam } 368189251Ssam 369189251Ssam return 0; 370189251Ssam} 371189251Ssam 372189251Ssam 373189251Ssamstatic void radius_client_timer(void *eloop_ctx, void *timeout_ctx) 374189251Ssam{ 375189251Ssam struct radius_client_data *radius = eloop_ctx; 376189251Ssam struct hostapd_radius_servers *conf = radius->conf; 377189251Ssam struct os_time now; 378189251Ssam os_time_t first; 379189251Ssam struct radius_msg_list *entry, *prev, *tmp; 380189251Ssam int auth_failover = 0, acct_failover = 0; 381189251Ssam char abuf[50]; 382189251Ssam 383189251Ssam entry = radius->msgs; 384189251Ssam if (!entry) 385189251Ssam return; 386189251Ssam 387189251Ssam os_get_time(&now); 388189251Ssam first = 0; 389189251Ssam 390189251Ssam prev = NULL; 391189251Ssam while (entry) { 392189251Ssam if (now.sec >= entry->next_try && 393189251Ssam radius_client_retransmit(radius, entry, now.sec)) { 394189251Ssam if (prev) 395189251Ssam prev->next = entry->next; 396189251Ssam else 397189251Ssam radius->msgs = entry->next; 398189251Ssam 399189251Ssam tmp = entry; 400189251Ssam entry = entry->next; 401189251Ssam radius_client_msg_free(tmp); 402189251Ssam radius->num_msgs--; 403189251Ssam continue; 404189251Ssam } 405189251Ssam 406189251Ssam if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) { 407189251Ssam if (entry->msg_type == RADIUS_ACCT || 408189251Ssam entry->msg_type == RADIUS_ACCT_INTERIM) 409189251Ssam acct_failover++; 410189251Ssam else 411189251Ssam auth_failover++; 412189251Ssam } 413189251Ssam 414189251Ssam if (first == 0 || entry->next_try < first) 415189251Ssam first = entry->next_try; 416189251Ssam 417189251Ssam prev = entry; 418189251Ssam entry = entry->next; 419189251Ssam } 420189251Ssam 421189251Ssam if (radius->msgs) { 422189251Ssam if (first < now.sec) 423189251Ssam first = now.sec; 424189251Ssam eloop_register_timeout(first - now.sec, 0, 425189251Ssam radius_client_timer, radius, NULL); 426189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 427189251Ssam HOSTAPD_LEVEL_DEBUG, "Next RADIUS client " 428189251Ssam "retransmit in %ld seconds", 429189251Ssam (long int) (first - now.sec)); 430189251Ssam } 431189251Ssam 432189251Ssam if (auth_failover && conf->num_auth_servers > 1) { 433189251Ssam struct hostapd_radius_server *next, *old; 434189251Ssam old = conf->auth_server; 435189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 436189251Ssam HOSTAPD_LEVEL_NOTICE, 437189251Ssam "No response from Authentication server " 438189251Ssam "%s:%d - failover", 439189251Ssam hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), 440189251Ssam old->port); 441189251Ssam 442189251Ssam for (entry = radius->msgs; entry; entry = entry->next) { 443189251Ssam if (entry->msg_type == RADIUS_AUTH) 444189251Ssam old->timeouts++; 445189251Ssam } 446189251Ssam 447189251Ssam next = old + 1; 448189251Ssam if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) 449189251Ssam next = conf->auth_servers; 450189251Ssam conf->auth_server = next; 451189251Ssam radius_change_server(radius, next, old, 452189251Ssam radius->auth_serv_sock, 453189251Ssam radius->auth_serv_sock6, 1); 454189251Ssam } 455189251Ssam 456189251Ssam if (acct_failover && conf->num_acct_servers > 1) { 457189251Ssam struct hostapd_radius_server *next, *old; 458189251Ssam old = conf->acct_server; 459189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 460189251Ssam HOSTAPD_LEVEL_NOTICE, 461189251Ssam "No response from Accounting server " 462189251Ssam "%s:%d - failover", 463189251Ssam hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), 464189251Ssam old->port); 465189251Ssam 466189251Ssam for (entry = radius->msgs; entry; entry = entry->next) { 467189251Ssam if (entry->msg_type == RADIUS_ACCT || 468189251Ssam entry->msg_type == RADIUS_ACCT_INTERIM) 469189251Ssam old->timeouts++; 470189251Ssam } 471189251Ssam 472189251Ssam next = old + 1; 473189251Ssam if (next > &conf->acct_servers[conf->num_acct_servers - 1]) 474189251Ssam next = conf->acct_servers; 475189251Ssam conf->acct_server = next; 476189251Ssam radius_change_server(radius, next, old, 477189251Ssam radius->acct_serv_sock, 478189251Ssam radius->acct_serv_sock6, 0); 479189251Ssam } 480189251Ssam} 481189251Ssam 482189251Ssam 483189251Ssamstatic void radius_client_update_timeout(struct radius_client_data *radius) 484189251Ssam{ 485189251Ssam struct os_time now; 486189251Ssam os_time_t first; 487189251Ssam struct radius_msg_list *entry; 488189251Ssam 489189251Ssam eloop_cancel_timeout(radius_client_timer, radius, NULL); 490189251Ssam 491189251Ssam if (radius->msgs == NULL) { 492189251Ssam return; 493189251Ssam } 494189251Ssam 495189251Ssam first = 0; 496189251Ssam for (entry = radius->msgs; entry; entry = entry->next) { 497189251Ssam if (first == 0 || entry->next_try < first) 498189251Ssam first = entry->next_try; 499189251Ssam } 500189251Ssam 501189251Ssam os_get_time(&now); 502189251Ssam if (first < now.sec) 503189251Ssam first = now.sec; 504189251Ssam eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, 505189251Ssam NULL); 506189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 507189251Ssam HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" 508252726Srpaulo " %ld seconds", (long int) (first - now.sec)); 509189251Ssam} 510189251Ssam 511189251Ssam 512189251Ssamstatic void radius_client_list_add(struct radius_client_data *radius, 513189251Ssam struct radius_msg *msg, 514214734Srpaulo RadiusType msg_type, 515214734Srpaulo const u8 *shared_secret, 516189251Ssam size_t shared_secret_len, const u8 *addr) 517189251Ssam{ 518189251Ssam struct radius_msg_list *entry, *prev; 519189251Ssam 520189251Ssam if (eloop_terminated()) { 521189251Ssam /* No point in adding entries to retransmit queue since event 522189251Ssam * loop has already been terminated. */ 523189251Ssam radius_msg_free(msg); 524189251Ssam return; 525189251Ssam } 526189251Ssam 527189251Ssam entry = os_zalloc(sizeof(*entry)); 528189251Ssam if (entry == NULL) { 529189251Ssam printf("Failed to add RADIUS packet into retransmit list\n"); 530189251Ssam radius_msg_free(msg); 531189251Ssam return; 532189251Ssam } 533189251Ssam 534189251Ssam if (addr) 535189251Ssam os_memcpy(entry->addr, addr, ETH_ALEN); 536189251Ssam entry->msg = msg; 537189251Ssam entry->msg_type = msg_type; 538189251Ssam entry->shared_secret = shared_secret; 539189251Ssam entry->shared_secret_len = shared_secret_len; 540189251Ssam os_get_time(&entry->last_attempt); 541189251Ssam entry->first_try = entry->last_attempt.sec; 542189251Ssam entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; 543189251Ssam entry->attempts = 1; 544189251Ssam entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; 545189251Ssam entry->next = radius->msgs; 546189251Ssam radius->msgs = entry; 547189251Ssam radius_client_update_timeout(radius); 548189251Ssam 549189251Ssam if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { 550189251Ssam printf("Removing the oldest un-ACKed RADIUS packet due to " 551189251Ssam "retransmit list limits.\n"); 552189251Ssam prev = NULL; 553189251Ssam while (entry->next) { 554189251Ssam prev = entry; 555189251Ssam entry = entry->next; 556189251Ssam } 557189251Ssam if (prev) { 558189251Ssam prev->next = NULL; 559189251Ssam radius_client_msg_free(entry); 560189251Ssam } 561189251Ssam } else 562189251Ssam radius->num_msgs++; 563189251Ssam} 564189251Ssam 565189251Ssam 566189251Ssamstatic void radius_client_list_del(struct radius_client_data *radius, 567189251Ssam RadiusType msg_type, const u8 *addr) 568189251Ssam{ 569189251Ssam struct radius_msg_list *entry, *prev, *tmp; 570189251Ssam 571189251Ssam if (addr == NULL) 572189251Ssam return; 573189251Ssam 574189251Ssam entry = radius->msgs; 575189251Ssam prev = NULL; 576189251Ssam while (entry) { 577189251Ssam if (entry->msg_type == msg_type && 578189251Ssam os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { 579189251Ssam if (prev) 580189251Ssam prev->next = entry->next; 581189251Ssam else 582189251Ssam radius->msgs = entry->next; 583189251Ssam tmp = entry; 584189251Ssam entry = entry->next; 585189251Ssam hostapd_logger(radius->ctx, addr, 586189251Ssam HOSTAPD_MODULE_RADIUS, 587189251Ssam HOSTAPD_LEVEL_DEBUG, 588189251Ssam "Removing matching RADIUS message"); 589189251Ssam radius_client_msg_free(tmp); 590189251Ssam radius->num_msgs--; 591189251Ssam continue; 592189251Ssam } 593189251Ssam prev = entry; 594189251Ssam entry = entry->next; 595189251Ssam } 596189251Ssam} 597189251Ssam 598189251Ssam 599214734Srpaulo/** 600214734Srpaulo * radius_client_send - Send a RADIUS request 601214734Srpaulo * @radius: RADIUS client context from radius_client_init() 602214734Srpaulo * @msg: RADIUS message to be sent 603214734Srpaulo * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM) 604214734Srpaulo * @addr: MAC address of the device related to this message or %NULL 605214734Srpaulo * Returns: 0 on success, -1 on failure 606214734Srpaulo * 607214734Srpaulo * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or 608214734Srpaulo * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference 609214734Srpaulo * between accounting and interim accounting messages is that the interim 610214734Srpaulo * message will override any pending interim accounting updates while a new 611214734Srpaulo * accounting message does not remove any pending messages. 612214734Srpaulo * 613214734Srpaulo * The message is added on the retransmission queue and will be retransmitted 614214734Srpaulo * automatically until a response is received or maximum number of retries 615214734Srpaulo * (RADIUS_CLIENT_MAX_RETRIES) is reached. 616214734Srpaulo * 617214734Srpaulo * The related device MAC address can be used to identify pending messages that 618214734Srpaulo * can be removed with radius_client_flush_auth() or with interim accounting 619214734Srpaulo * updates. 620214734Srpaulo */ 621189251Ssamint radius_client_send(struct radius_client_data *radius, 622189251Ssam struct radius_msg *msg, RadiusType msg_type, 623189251Ssam const u8 *addr) 624189251Ssam{ 625189251Ssam struct hostapd_radius_servers *conf = radius->conf; 626214734Srpaulo const u8 *shared_secret; 627189251Ssam size_t shared_secret_len; 628189251Ssam char *name; 629189251Ssam int s, res; 630214734Srpaulo struct wpabuf *buf; 631189251Ssam 632189251Ssam if (msg_type == RADIUS_ACCT_INTERIM) { 633189251Ssam /* Remove any pending interim acct update for the same STA. */ 634189251Ssam radius_client_list_del(radius, msg_type, addr); 635189251Ssam } 636189251Ssam 637189251Ssam if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { 638189251Ssam if (conf->acct_server == NULL) { 639189251Ssam hostapd_logger(radius->ctx, NULL, 640189251Ssam HOSTAPD_MODULE_RADIUS, 641189251Ssam HOSTAPD_LEVEL_INFO, 642189251Ssam "No accounting server configured"); 643189251Ssam return -1; 644189251Ssam } 645189251Ssam shared_secret = conf->acct_server->shared_secret; 646189251Ssam shared_secret_len = conf->acct_server->shared_secret_len; 647189251Ssam radius_msg_finish_acct(msg, shared_secret, shared_secret_len); 648189251Ssam name = "accounting"; 649189251Ssam s = radius->acct_sock; 650189251Ssam conf->acct_server->requests++; 651189251Ssam } else { 652189251Ssam if (conf->auth_server == NULL) { 653189251Ssam hostapd_logger(radius->ctx, NULL, 654189251Ssam HOSTAPD_MODULE_RADIUS, 655189251Ssam HOSTAPD_LEVEL_INFO, 656189251Ssam "No authentication server configured"); 657189251Ssam return -1; 658189251Ssam } 659189251Ssam shared_secret = conf->auth_server->shared_secret; 660189251Ssam shared_secret_len = conf->auth_server->shared_secret_len; 661189251Ssam radius_msg_finish(msg, shared_secret, shared_secret_len); 662189251Ssam name = "authentication"; 663189251Ssam s = radius->auth_sock; 664189251Ssam conf->auth_server->requests++; 665189251Ssam } 666189251Ssam 667189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 668189251Ssam HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s " 669189251Ssam "server", name); 670189251Ssam if (conf->msg_dumps) 671189251Ssam radius_msg_dump(msg); 672189251Ssam 673214734Srpaulo buf = radius_msg_get_buf(msg); 674214734Srpaulo res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0); 675189251Ssam if (res < 0) 676189251Ssam radius_client_handle_send_error(radius, s, msg_type); 677189251Ssam 678189251Ssam radius_client_list_add(radius, msg, msg_type, shared_secret, 679189251Ssam shared_secret_len, addr); 680189251Ssam 681252726Srpaulo return 0; 682189251Ssam} 683189251Ssam 684189251Ssam 685189251Ssamstatic void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) 686189251Ssam{ 687189251Ssam struct radius_client_data *radius = eloop_ctx; 688189251Ssam struct hostapd_radius_servers *conf = radius->conf; 689189251Ssam RadiusType msg_type = (RadiusType) sock_ctx; 690189251Ssam int len, roundtrip; 691189251Ssam unsigned char buf[3000]; 692189251Ssam struct radius_msg *msg; 693214734Srpaulo struct radius_hdr *hdr; 694189251Ssam struct radius_rx_handler *handlers; 695189251Ssam size_t num_handlers, i; 696189251Ssam struct radius_msg_list *req, *prev_req; 697189251Ssam struct os_time now; 698189251Ssam struct hostapd_radius_server *rconf; 699189251Ssam int invalid_authenticator = 0; 700189251Ssam 701189251Ssam if (msg_type == RADIUS_ACCT) { 702189251Ssam handlers = radius->acct_handlers; 703189251Ssam num_handlers = radius->num_acct_handlers; 704189251Ssam rconf = conf->acct_server; 705189251Ssam } else { 706189251Ssam handlers = radius->auth_handlers; 707189251Ssam num_handlers = radius->num_auth_handlers; 708189251Ssam rconf = conf->auth_server; 709189251Ssam } 710189251Ssam 711189251Ssam len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); 712189251Ssam if (len < 0) { 713189251Ssam perror("recv[RADIUS]"); 714189251Ssam return; 715189251Ssam } 716189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 717189251Ssam HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS " 718189251Ssam "server", len); 719189251Ssam if (len == sizeof(buf)) { 720189251Ssam printf("Possibly too long UDP frame for our buffer - " 721189251Ssam "dropping it\n"); 722189251Ssam return; 723189251Ssam } 724189251Ssam 725189251Ssam msg = radius_msg_parse(buf, len); 726189251Ssam if (msg == NULL) { 727189251Ssam printf("Parsing incoming RADIUS frame failed\n"); 728189251Ssam rconf->malformed_responses++; 729189251Ssam return; 730189251Ssam } 731214734Srpaulo hdr = radius_msg_get_hdr(msg); 732189251Ssam 733189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 734189251Ssam HOSTAPD_LEVEL_DEBUG, "Received RADIUS message"); 735189251Ssam if (conf->msg_dumps) 736189251Ssam radius_msg_dump(msg); 737189251Ssam 738214734Srpaulo switch (hdr->code) { 739189251Ssam case RADIUS_CODE_ACCESS_ACCEPT: 740189251Ssam rconf->access_accepts++; 741189251Ssam break; 742189251Ssam case RADIUS_CODE_ACCESS_REJECT: 743189251Ssam rconf->access_rejects++; 744189251Ssam break; 745189251Ssam case RADIUS_CODE_ACCESS_CHALLENGE: 746189251Ssam rconf->access_challenges++; 747189251Ssam break; 748189251Ssam case RADIUS_CODE_ACCOUNTING_RESPONSE: 749189251Ssam rconf->responses++; 750189251Ssam break; 751189251Ssam } 752189251Ssam 753189251Ssam prev_req = NULL; 754189251Ssam req = radius->msgs; 755189251Ssam while (req) { 756189251Ssam /* TODO: also match by src addr:port of the packet when using 757189251Ssam * alternative RADIUS servers (?) */ 758189251Ssam if ((req->msg_type == msg_type || 759189251Ssam (req->msg_type == RADIUS_ACCT_INTERIM && 760189251Ssam msg_type == RADIUS_ACCT)) && 761214734Srpaulo radius_msg_get_hdr(req->msg)->identifier == 762214734Srpaulo hdr->identifier) 763189251Ssam break; 764189251Ssam 765189251Ssam prev_req = req; 766189251Ssam req = req->next; 767189251Ssam } 768189251Ssam 769189251Ssam if (req == NULL) { 770189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 771189251Ssam HOSTAPD_LEVEL_DEBUG, 772189251Ssam "No matching RADIUS request found (type=%d " 773189251Ssam "id=%d) - dropping packet", 774214734Srpaulo msg_type, hdr->identifier); 775189251Ssam goto fail; 776189251Ssam } 777189251Ssam 778189251Ssam os_get_time(&now); 779189251Ssam roundtrip = (now.sec - req->last_attempt.sec) * 100 + 780189251Ssam (now.usec - req->last_attempt.usec) / 10000; 781189251Ssam hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, 782189251Ssam HOSTAPD_LEVEL_DEBUG, 783189251Ssam "Received RADIUS packet matched with a pending " 784189251Ssam "request, round trip time %d.%02d sec", 785189251Ssam roundtrip / 100, roundtrip % 100); 786189251Ssam rconf->round_trip_time = roundtrip; 787189251Ssam 788189251Ssam /* Remove ACKed RADIUS packet from retransmit list */ 789189251Ssam if (prev_req) 790189251Ssam prev_req->next = req->next; 791189251Ssam else 792189251Ssam radius->msgs = req->next; 793189251Ssam radius->num_msgs--; 794189251Ssam 795189251Ssam for (i = 0; i < num_handlers; i++) { 796189251Ssam RadiusRxResult res; 797189251Ssam res = handlers[i].handler(msg, req->msg, req->shared_secret, 798189251Ssam req->shared_secret_len, 799189251Ssam handlers[i].data); 800189251Ssam switch (res) { 801189251Ssam case RADIUS_RX_PROCESSED: 802189251Ssam radius_msg_free(msg); 803189251Ssam /* continue */ 804189251Ssam case RADIUS_RX_QUEUED: 805189251Ssam radius_client_msg_free(req); 806189251Ssam return; 807189251Ssam case RADIUS_RX_INVALID_AUTHENTICATOR: 808189251Ssam invalid_authenticator++; 809189251Ssam /* continue */ 810189251Ssam case RADIUS_RX_UNKNOWN: 811189251Ssam /* continue with next handler */ 812189251Ssam break; 813189251Ssam } 814189251Ssam } 815189251Ssam 816189251Ssam if (invalid_authenticator) 817189251Ssam rconf->bad_authenticators++; 818189251Ssam else 819189251Ssam rconf->unknown_types++; 820189251Ssam hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, 821189251Ssam HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found " 822189251Ssam "(type=%d code=%d id=%d)%s - dropping packet", 823214734Srpaulo msg_type, hdr->code, hdr->identifier, 824189251Ssam invalid_authenticator ? " [INVALID AUTHENTICATOR]" : 825189251Ssam ""); 826189251Ssam radius_client_msg_free(req); 827189251Ssam 828189251Ssam fail: 829189251Ssam radius_msg_free(msg); 830189251Ssam} 831189251Ssam 832189251Ssam 833214734Srpaulo/** 834214734Srpaulo * radius_client_get_id - Get an identifier for a new RADIUS message 835214734Srpaulo * @radius: RADIUS client context from radius_client_init() 836214734Srpaulo * Returns: Allocated identifier 837214734Srpaulo * 838214734Srpaulo * This function is used to fetch a unique (among pending requests) identifier 839214734Srpaulo * for a new RADIUS message. 840214734Srpaulo */ 841189251Ssamu8 radius_client_get_id(struct radius_client_data *radius) 842189251Ssam{ 843189251Ssam struct radius_msg_list *entry, *prev, *_remove; 844189251Ssam u8 id = radius->next_radius_identifier++; 845189251Ssam 846189251Ssam /* remove entries with matching id from retransmit list to avoid 847189251Ssam * using new reply from the RADIUS server with an old request */ 848189251Ssam entry = radius->msgs; 849189251Ssam prev = NULL; 850189251Ssam while (entry) { 851214734Srpaulo if (radius_msg_get_hdr(entry->msg)->identifier == id) { 852189251Ssam hostapd_logger(radius->ctx, entry->addr, 853189251Ssam HOSTAPD_MODULE_RADIUS, 854189251Ssam HOSTAPD_LEVEL_DEBUG, 855189251Ssam "Removing pending RADIUS message, " 856189251Ssam "since its id (%d) is reused", id); 857189251Ssam if (prev) 858189251Ssam prev->next = entry->next; 859189251Ssam else 860189251Ssam radius->msgs = entry->next; 861189251Ssam _remove = entry; 862189251Ssam } else { 863189251Ssam _remove = NULL; 864189251Ssam prev = entry; 865189251Ssam } 866189251Ssam entry = entry->next; 867189251Ssam 868189251Ssam if (_remove) 869189251Ssam radius_client_msg_free(_remove); 870189251Ssam } 871189251Ssam 872189251Ssam return id; 873189251Ssam} 874189251Ssam 875189251Ssam 876214734Srpaulo/** 877214734Srpaulo * radius_client_flush - Flush all pending RADIUS client messages 878214734Srpaulo * @radius: RADIUS client context from radius_client_init() 879214734Srpaulo * @only_auth: Whether only authentication messages are removed 880214734Srpaulo */ 881189251Ssamvoid radius_client_flush(struct radius_client_data *radius, int only_auth) 882189251Ssam{ 883189251Ssam struct radius_msg_list *entry, *prev, *tmp; 884189251Ssam 885189251Ssam if (!radius) 886189251Ssam return; 887189251Ssam 888189251Ssam prev = NULL; 889189251Ssam entry = radius->msgs; 890189251Ssam 891189251Ssam while (entry) { 892189251Ssam if (!only_auth || entry->msg_type == RADIUS_AUTH) { 893189251Ssam if (prev) 894189251Ssam prev->next = entry->next; 895189251Ssam else 896189251Ssam radius->msgs = entry->next; 897189251Ssam 898189251Ssam tmp = entry; 899189251Ssam entry = entry->next; 900189251Ssam radius_client_msg_free(tmp); 901189251Ssam radius->num_msgs--; 902189251Ssam } else { 903189251Ssam prev = entry; 904189251Ssam entry = entry->next; 905189251Ssam } 906189251Ssam } 907189251Ssam 908189251Ssam if (radius->msgs == NULL) 909189251Ssam eloop_cancel_timeout(radius_client_timer, radius, NULL); 910189251Ssam} 911189251Ssam 912189251Ssam 913189251Ssamstatic void radius_client_update_acct_msgs(struct radius_client_data *radius, 914214734Srpaulo const u8 *shared_secret, 915189251Ssam size_t shared_secret_len) 916189251Ssam{ 917189251Ssam struct radius_msg_list *entry; 918189251Ssam 919189251Ssam if (!radius) 920189251Ssam return; 921189251Ssam 922189251Ssam for (entry = radius->msgs; entry; entry = entry->next) { 923189251Ssam if (entry->msg_type == RADIUS_ACCT) { 924189251Ssam entry->shared_secret = shared_secret; 925189251Ssam entry->shared_secret_len = shared_secret_len; 926189251Ssam radius_msg_finish_acct(entry->msg, shared_secret, 927189251Ssam shared_secret_len); 928189251Ssam } 929189251Ssam } 930189251Ssam} 931189251Ssam 932189251Ssam 933189251Ssamstatic int 934189251Ssamradius_change_server(struct radius_client_data *radius, 935189251Ssam struct hostapd_radius_server *nserv, 936189251Ssam struct hostapd_radius_server *oserv, 937189251Ssam int sock, int sock6, int auth) 938189251Ssam{ 939189251Ssam struct sockaddr_in serv, claddr; 940189251Ssam#ifdef CONFIG_IPV6 941189251Ssam struct sockaddr_in6 serv6, claddr6; 942189251Ssam#endif /* CONFIG_IPV6 */ 943189251Ssam struct sockaddr *addr, *cl_addr; 944189251Ssam socklen_t addrlen, claddrlen; 945189251Ssam char abuf[50]; 946189251Ssam int sel_sock; 947189251Ssam struct radius_msg_list *entry; 948189251Ssam struct hostapd_radius_servers *conf = radius->conf; 949189251Ssam 950189251Ssam hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 951189251Ssam HOSTAPD_LEVEL_INFO, 952189251Ssam "%s server %s:%d", 953189251Ssam auth ? "Authentication" : "Accounting", 954189251Ssam hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), 955189251Ssam nserv->port); 956189251Ssam 957189251Ssam if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len || 958189251Ssam os_memcmp(nserv->shared_secret, oserv->shared_secret, 959189251Ssam nserv->shared_secret_len) != 0) { 960189251Ssam /* Pending RADIUS packets used different shared secret, so 961189251Ssam * they need to be modified. Update accounting message 962189251Ssam * authenticators here. Authentication messages are removed 963189251Ssam * since they would require more changes and the new RADIUS 964189251Ssam * server may not be prepared to receive them anyway due to 965189251Ssam * missing state information. Client will likely retry 966189251Ssam * authentication, so this should not be an issue. */ 967189251Ssam if (auth) 968189251Ssam radius_client_flush(radius, 1); 969189251Ssam else { 970189251Ssam radius_client_update_acct_msgs( 971189251Ssam radius, nserv->shared_secret, 972189251Ssam nserv->shared_secret_len); 973189251Ssam } 974189251Ssam } 975189251Ssam 976189251Ssam /* Reset retry counters for the new server */ 977189251Ssam for (entry = radius->msgs; entry; entry = entry->next) { 978189251Ssam if ((auth && entry->msg_type != RADIUS_AUTH) || 979189251Ssam (!auth && entry->msg_type != RADIUS_ACCT)) 980189251Ssam continue; 981189251Ssam entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; 982189251Ssam entry->attempts = 0; 983189251Ssam entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; 984189251Ssam } 985189251Ssam 986189251Ssam if (radius->msgs) { 987189251Ssam eloop_cancel_timeout(radius_client_timer, radius, NULL); 988189251Ssam eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, 989189251Ssam radius_client_timer, radius, NULL); 990189251Ssam } 991189251Ssam 992189251Ssam switch (nserv->addr.af) { 993189251Ssam case AF_INET: 994189251Ssam os_memset(&serv, 0, sizeof(serv)); 995189251Ssam serv.sin_family = AF_INET; 996189251Ssam serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr; 997189251Ssam serv.sin_port = htons(nserv->port); 998189251Ssam addr = (struct sockaddr *) &serv; 999189251Ssam addrlen = sizeof(serv); 1000189251Ssam sel_sock = sock; 1001189251Ssam break; 1002189251Ssam#ifdef CONFIG_IPV6 1003189251Ssam case AF_INET6: 1004189251Ssam os_memset(&serv6, 0, sizeof(serv6)); 1005189251Ssam serv6.sin6_family = AF_INET6; 1006189251Ssam os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6, 1007189251Ssam sizeof(struct in6_addr)); 1008189251Ssam serv6.sin6_port = htons(nserv->port); 1009189251Ssam addr = (struct sockaddr *) &serv6; 1010189251Ssam addrlen = sizeof(serv6); 1011189251Ssam sel_sock = sock6; 1012189251Ssam break; 1013189251Ssam#endif /* CONFIG_IPV6 */ 1014189251Ssam default: 1015189251Ssam return -1; 1016189251Ssam } 1017189251Ssam 1018189251Ssam if (conf->force_client_addr) { 1019189251Ssam switch (conf->client_addr.af) { 1020189251Ssam case AF_INET: 1021189251Ssam os_memset(&claddr, 0, sizeof(claddr)); 1022189251Ssam claddr.sin_family = AF_INET; 1023189251Ssam claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr; 1024189251Ssam claddr.sin_port = htons(0); 1025189251Ssam cl_addr = (struct sockaddr *) &claddr; 1026189251Ssam claddrlen = sizeof(claddr); 1027189251Ssam break; 1028189251Ssam#ifdef CONFIG_IPV6 1029189251Ssam case AF_INET6: 1030189251Ssam os_memset(&claddr6, 0, sizeof(claddr6)); 1031189251Ssam claddr6.sin6_family = AF_INET6; 1032189251Ssam os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6, 1033189251Ssam sizeof(struct in6_addr)); 1034189251Ssam claddr6.sin6_port = htons(0); 1035189251Ssam cl_addr = (struct sockaddr *) &claddr6; 1036189251Ssam claddrlen = sizeof(claddr6); 1037189251Ssam break; 1038189251Ssam#endif /* CONFIG_IPV6 */ 1039189251Ssam default: 1040189251Ssam return -1; 1041189251Ssam } 1042189251Ssam 1043189251Ssam if (bind(sel_sock, cl_addr, claddrlen) < 0) { 1044189251Ssam perror("bind[radius]"); 1045189251Ssam return -1; 1046189251Ssam } 1047189251Ssam } 1048189251Ssam 1049189251Ssam if (connect(sel_sock, addr, addrlen) < 0) { 1050189251Ssam perror("connect[radius]"); 1051189251Ssam return -1; 1052189251Ssam } 1053189251Ssam 1054189251Ssam#ifndef CONFIG_NATIVE_WINDOWS 1055189251Ssam switch (nserv->addr.af) { 1056189251Ssam case AF_INET: 1057189251Ssam claddrlen = sizeof(claddr); 1058189251Ssam getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen); 1059189251Ssam wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", 1060189251Ssam inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port)); 1061189251Ssam break; 1062189251Ssam#ifdef CONFIG_IPV6 1063189251Ssam case AF_INET6: { 1064189251Ssam claddrlen = sizeof(claddr6); 1065189251Ssam getsockname(sel_sock, (struct sockaddr *) &claddr6, 1066189251Ssam &claddrlen); 1067189251Ssam wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", 1068189251Ssam inet_ntop(AF_INET6, &claddr6.sin6_addr, 1069189251Ssam abuf, sizeof(abuf)), 1070189251Ssam ntohs(claddr6.sin6_port)); 1071189251Ssam break; 1072189251Ssam } 1073189251Ssam#endif /* CONFIG_IPV6 */ 1074189251Ssam } 1075189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */ 1076189251Ssam 1077189251Ssam if (auth) 1078189251Ssam radius->auth_sock = sel_sock; 1079189251Ssam else 1080189251Ssam radius->acct_sock = sel_sock; 1081189251Ssam 1082189251Ssam return 0; 1083189251Ssam} 1084189251Ssam 1085189251Ssam 1086189251Ssamstatic void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) 1087189251Ssam{ 1088189251Ssam struct radius_client_data *radius = eloop_ctx; 1089189251Ssam struct hostapd_radius_servers *conf = radius->conf; 1090189251Ssam struct hostapd_radius_server *oserv; 1091189251Ssam 1092189251Ssam if (radius->auth_sock >= 0 && conf->auth_servers && 1093189251Ssam conf->auth_server != conf->auth_servers) { 1094189251Ssam oserv = conf->auth_server; 1095189251Ssam conf->auth_server = conf->auth_servers; 1096189251Ssam radius_change_server(radius, conf->auth_server, oserv, 1097189251Ssam radius->auth_serv_sock, 1098189251Ssam radius->auth_serv_sock6, 1); 1099189251Ssam } 1100189251Ssam 1101189251Ssam if (radius->acct_sock >= 0 && conf->acct_servers && 1102189251Ssam conf->acct_server != conf->acct_servers) { 1103189251Ssam oserv = conf->acct_server; 1104189251Ssam conf->acct_server = conf->acct_servers; 1105189251Ssam radius_change_server(radius, conf->acct_server, oserv, 1106189251Ssam radius->acct_serv_sock, 1107189251Ssam radius->acct_serv_sock6, 0); 1108189251Ssam } 1109189251Ssam 1110189251Ssam if (conf->retry_primary_interval) 1111189251Ssam eloop_register_timeout(conf->retry_primary_interval, 0, 1112189251Ssam radius_retry_primary_timer, radius, 1113189251Ssam NULL); 1114189251Ssam} 1115189251Ssam 1116189251Ssam 1117209158Srpaulostatic int radius_client_disable_pmtu_discovery(int s) 1118209158Srpaulo{ 1119209158Srpaulo int r = -1; 1120209158Srpaulo#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) 1121209158Srpaulo /* Turn off Path MTU discovery on IPv4/UDP sockets. */ 1122209158Srpaulo int action = IP_PMTUDISC_DONT; 1123209158Srpaulo r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, 1124209158Srpaulo sizeof(action)); 1125209158Srpaulo if (r == -1) 1126209158Srpaulo wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: " 1127209158Srpaulo "%s", strerror(errno)); 1128209158Srpaulo#endif 1129209158Srpaulo return r; 1130209158Srpaulo} 1131209158Srpaulo 1132209158Srpaulo 1133189251Ssamstatic int radius_client_init_auth(struct radius_client_data *radius) 1134189251Ssam{ 1135189251Ssam struct hostapd_radius_servers *conf = radius->conf; 1136189251Ssam int ok = 0; 1137189251Ssam 1138189251Ssam radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); 1139189251Ssam if (radius->auth_serv_sock < 0) 1140189251Ssam perror("socket[PF_INET,SOCK_DGRAM]"); 1141209158Srpaulo else { 1142209158Srpaulo radius_client_disable_pmtu_discovery(radius->auth_serv_sock); 1143189251Ssam ok++; 1144209158Srpaulo } 1145189251Ssam 1146189251Ssam#ifdef CONFIG_IPV6 1147189251Ssam radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); 1148189251Ssam if (radius->auth_serv_sock6 < 0) 1149189251Ssam perror("socket[PF_INET6,SOCK_DGRAM]"); 1150189251Ssam else 1151189251Ssam ok++; 1152189251Ssam#endif /* CONFIG_IPV6 */ 1153189251Ssam 1154189251Ssam if (ok == 0) 1155189251Ssam return -1; 1156189251Ssam 1157189251Ssam radius_change_server(radius, conf->auth_server, NULL, 1158189251Ssam radius->auth_serv_sock, radius->auth_serv_sock6, 1159189251Ssam 1); 1160189251Ssam 1161189251Ssam if (radius->auth_serv_sock >= 0 && 1162189251Ssam eloop_register_read_sock(radius->auth_serv_sock, 1163189251Ssam radius_client_receive, radius, 1164189251Ssam (void *) RADIUS_AUTH)) { 1165189251Ssam printf("Could not register read socket for authentication " 1166189251Ssam "server\n"); 1167189251Ssam return -1; 1168189251Ssam } 1169189251Ssam 1170189251Ssam#ifdef CONFIG_IPV6 1171189251Ssam if (radius->auth_serv_sock6 >= 0 && 1172189251Ssam eloop_register_read_sock(radius->auth_serv_sock6, 1173189251Ssam radius_client_receive, radius, 1174189251Ssam (void *) RADIUS_AUTH)) { 1175189251Ssam printf("Could not register read socket for authentication " 1176189251Ssam "server\n"); 1177189251Ssam return -1; 1178189251Ssam } 1179189251Ssam#endif /* CONFIG_IPV6 */ 1180189251Ssam 1181189251Ssam return 0; 1182189251Ssam} 1183189251Ssam 1184189251Ssam 1185189251Ssamstatic int radius_client_init_acct(struct radius_client_data *radius) 1186189251Ssam{ 1187189251Ssam struct hostapd_radius_servers *conf = radius->conf; 1188189251Ssam int ok = 0; 1189189251Ssam 1190189251Ssam radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); 1191189251Ssam if (radius->acct_serv_sock < 0) 1192189251Ssam perror("socket[PF_INET,SOCK_DGRAM]"); 1193209158Srpaulo else { 1194209158Srpaulo radius_client_disable_pmtu_discovery(radius->acct_serv_sock); 1195189251Ssam ok++; 1196209158Srpaulo } 1197189251Ssam 1198189251Ssam#ifdef CONFIG_IPV6 1199189251Ssam radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); 1200189251Ssam if (radius->acct_serv_sock6 < 0) 1201189251Ssam perror("socket[PF_INET6,SOCK_DGRAM]"); 1202189251Ssam else 1203189251Ssam ok++; 1204189251Ssam#endif /* CONFIG_IPV6 */ 1205189251Ssam 1206189251Ssam if (ok == 0) 1207189251Ssam return -1; 1208189251Ssam 1209189251Ssam radius_change_server(radius, conf->acct_server, NULL, 1210189251Ssam radius->acct_serv_sock, radius->acct_serv_sock6, 1211189251Ssam 0); 1212189251Ssam 1213189251Ssam if (radius->acct_serv_sock >= 0 && 1214189251Ssam eloop_register_read_sock(radius->acct_serv_sock, 1215189251Ssam radius_client_receive, radius, 1216189251Ssam (void *) RADIUS_ACCT)) { 1217189251Ssam printf("Could not register read socket for accounting " 1218189251Ssam "server\n"); 1219189251Ssam return -1; 1220189251Ssam } 1221189251Ssam 1222189251Ssam#ifdef CONFIG_IPV6 1223189251Ssam if (radius->acct_serv_sock6 >= 0 && 1224189251Ssam eloop_register_read_sock(radius->acct_serv_sock6, 1225189251Ssam radius_client_receive, radius, 1226189251Ssam (void *) RADIUS_ACCT)) { 1227189251Ssam printf("Could not register read socket for accounting " 1228189251Ssam "server\n"); 1229189251Ssam return -1; 1230189251Ssam } 1231189251Ssam#endif /* CONFIG_IPV6 */ 1232189251Ssam 1233189251Ssam return 0; 1234189251Ssam} 1235189251Ssam 1236189251Ssam 1237214734Srpaulo/** 1238214734Srpaulo * radius_client_init - Initialize RADIUS client 1239214734Srpaulo * @ctx: Callback context to be used in hostapd_logger() calls 1240214734Srpaulo * @conf: RADIUS client configuration (RADIUS servers) 1241214734Srpaulo * Returns: Pointer to private RADIUS client context or %NULL on failure 1242214734Srpaulo * 1243214734Srpaulo * The caller is responsible for keeping the configuration data available for 1244214734Srpaulo * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is 1245214734Srpaulo * called for the returned context pointer. 1246214734Srpaulo */ 1247189251Ssamstruct radius_client_data * 1248189251Ssamradius_client_init(void *ctx, struct hostapd_radius_servers *conf) 1249189251Ssam{ 1250189251Ssam struct radius_client_data *radius; 1251189251Ssam 1252189251Ssam radius = os_zalloc(sizeof(struct radius_client_data)); 1253189251Ssam if (radius == NULL) 1254189251Ssam return NULL; 1255189251Ssam 1256189251Ssam radius->ctx = ctx; 1257189251Ssam radius->conf = conf; 1258189251Ssam radius->auth_serv_sock = radius->acct_serv_sock = 1259189251Ssam radius->auth_serv_sock6 = radius->acct_serv_sock6 = 1260189251Ssam radius->auth_sock = radius->acct_sock = -1; 1261189251Ssam 1262189251Ssam if (conf->auth_server && radius_client_init_auth(radius)) { 1263189251Ssam radius_client_deinit(radius); 1264189251Ssam return NULL; 1265189251Ssam } 1266189251Ssam 1267189251Ssam if (conf->acct_server && radius_client_init_acct(radius)) { 1268189251Ssam radius_client_deinit(radius); 1269189251Ssam return NULL; 1270189251Ssam } 1271189251Ssam 1272189251Ssam if (conf->retry_primary_interval) 1273189251Ssam eloop_register_timeout(conf->retry_primary_interval, 0, 1274189251Ssam radius_retry_primary_timer, radius, 1275189251Ssam NULL); 1276189251Ssam 1277189251Ssam return radius; 1278189251Ssam} 1279189251Ssam 1280189251Ssam 1281214734Srpaulo/** 1282214734Srpaulo * radius_client_deinit - Deinitialize RADIUS client 1283214734Srpaulo * @radius: RADIUS client context from radius_client_init() 1284214734Srpaulo */ 1285189251Ssamvoid radius_client_deinit(struct radius_client_data *radius) 1286189251Ssam{ 1287189251Ssam if (!radius) 1288189251Ssam return; 1289189251Ssam 1290189251Ssam if (radius->auth_serv_sock >= 0) 1291189251Ssam eloop_unregister_read_sock(radius->auth_serv_sock); 1292189251Ssam if (radius->acct_serv_sock >= 0) 1293189251Ssam eloop_unregister_read_sock(radius->acct_serv_sock); 1294209158Srpaulo#ifdef CONFIG_IPV6 1295209158Srpaulo if (radius->auth_serv_sock6 >= 0) 1296209158Srpaulo eloop_unregister_read_sock(radius->auth_serv_sock6); 1297209158Srpaulo if (radius->acct_serv_sock6 >= 0) 1298209158Srpaulo eloop_unregister_read_sock(radius->acct_serv_sock6); 1299209158Srpaulo#endif /* CONFIG_IPV6 */ 1300189251Ssam 1301189251Ssam eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL); 1302189251Ssam 1303189251Ssam radius_client_flush(radius, 0); 1304189251Ssam os_free(radius->auth_handlers); 1305189251Ssam os_free(radius->acct_handlers); 1306189251Ssam os_free(radius); 1307189251Ssam} 1308189251Ssam 1309189251Ssam 1310214734Srpaulo/** 1311214734Srpaulo * radius_client_flush_auth - Flush pending RADIUS messages for an address 1312214734Srpaulo * @radius: RADIUS client context from radius_client_init() 1313214734Srpaulo * @addr: MAC address of the related device 1314214734Srpaulo * 1315214734Srpaulo * This function can be used to remove pending RADIUS authentication messages 1316214734Srpaulo * that are related to a specific device. The addr parameter is matched with 1317214734Srpaulo * the one used in radius_client_send() call that was used to transmit the 1318214734Srpaulo * authentication request. 1319214734Srpaulo */ 1320214734Srpaulovoid radius_client_flush_auth(struct radius_client_data *radius, 1321214734Srpaulo const u8 *addr) 1322189251Ssam{ 1323189251Ssam struct radius_msg_list *entry, *prev, *tmp; 1324189251Ssam 1325189251Ssam prev = NULL; 1326189251Ssam entry = radius->msgs; 1327189251Ssam while (entry) { 1328189251Ssam if (entry->msg_type == RADIUS_AUTH && 1329189251Ssam os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { 1330189251Ssam hostapd_logger(radius->ctx, addr, 1331189251Ssam HOSTAPD_MODULE_RADIUS, 1332189251Ssam HOSTAPD_LEVEL_DEBUG, 1333189251Ssam "Removing pending RADIUS authentication" 1334189251Ssam " message for removed client"); 1335189251Ssam 1336189251Ssam if (prev) 1337189251Ssam prev->next = entry->next; 1338189251Ssam else 1339189251Ssam radius->msgs = entry->next; 1340189251Ssam 1341189251Ssam tmp = entry; 1342189251Ssam entry = entry->next; 1343189251Ssam radius_client_msg_free(tmp); 1344189251Ssam radius->num_msgs--; 1345189251Ssam continue; 1346189251Ssam } 1347189251Ssam 1348189251Ssam prev = entry; 1349189251Ssam entry = entry->next; 1350189251Ssam } 1351189251Ssam} 1352189251Ssam 1353189251Ssam 1354189251Ssamstatic int radius_client_dump_auth_server(char *buf, size_t buflen, 1355189251Ssam struct hostapd_radius_server *serv, 1356189251Ssam struct radius_client_data *cli) 1357189251Ssam{ 1358189251Ssam int pending = 0; 1359189251Ssam struct radius_msg_list *msg; 1360189251Ssam char abuf[50]; 1361189251Ssam 1362189251Ssam if (cli) { 1363189251Ssam for (msg = cli->msgs; msg; msg = msg->next) { 1364189251Ssam if (msg->msg_type == RADIUS_AUTH) 1365189251Ssam pending++; 1366189251Ssam } 1367189251Ssam } 1368189251Ssam 1369189251Ssam return os_snprintf(buf, buflen, 1370189251Ssam "radiusAuthServerIndex=%d\n" 1371189251Ssam "radiusAuthServerAddress=%s\n" 1372189251Ssam "radiusAuthClientServerPortNumber=%d\n" 1373189251Ssam "radiusAuthClientRoundTripTime=%d\n" 1374189251Ssam "radiusAuthClientAccessRequests=%u\n" 1375189251Ssam "radiusAuthClientAccessRetransmissions=%u\n" 1376189251Ssam "radiusAuthClientAccessAccepts=%u\n" 1377189251Ssam "radiusAuthClientAccessRejects=%u\n" 1378189251Ssam "radiusAuthClientAccessChallenges=%u\n" 1379189251Ssam "radiusAuthClientMalformedAccessResponses=%u\n" 1380189251Ssam "radiusAuthClientBadAuthenticators=%u\n" 1381189251Ssam "radiusAuthClientPendingRequests=%u\n" 1382189251Ssam "radiusAuthClientTimeouts=%u\n" 1383189251Ssam "radiusAuthClientUnknownTypes=%u\n" 1384189251Ssam "radiusAuthClientPacketsDropped=%u\n", 1385189251Ssam serv->index, 1386189251Ssam hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), 1387189251Ssam serv->port, 1388189251Ssam serv->round_trip_time, 1389189251Ssam serv->requests, 1390189251Ssam serv->retransmissions, 1391189251Ssam serv->access_accepts, 1392189251Ssam serv->access_rejects, 1393189251Ssam serv->access_challenges, 1394189251Ssam serv->malformed_responses, 1395189251Ssam serv->bad_authenticators, 1396189251Ssam pending, 1397189251Ssam serv->timeouts, 1398189251Ssam serv->unknown_types, 1399189251Ssam serv->packets_dropped); 1400189251Ssam} 1401189251Ssam 1402189251Ssam 1403189251Ssamstatic int radius_client_dump_acct_server(char *buf, size_t buflen, 1404189251Ssam struct hostapd_radius_server *serv, 1405189251Ssam struct radius_client_data *cli) 1406189251Ssam{ 1407189251Ssam int pending = 0; 1408189251Ssam struct radius_msg_list *msg; 1409189251Ssam char abuf[50]; 1410189251Ssam 1411189251Ssam if (cli) { 1412189251Ssam for (msg = cli->msgs; msg; msg = msg->next) { 1413189251Ssam if (msg->msg_type == RADIUS_ACCT || 1414189251Ssam msg->msg_type == RADIUS_ACCT_INTERIM) 1415189251Ssam pending++; 1416189251Ssam } 1417189251Ssam } 1418189251Ssam 1419189251Ssam return os_snprintf(buf, buflen, 1420189251Ssam "radiusAccServerIndex=%d\n" 1421189251Ssam "radiusAccServerAddress=%s\n" 1422189251Ssam "radiusAccClientServerPortNumber=%d\n" 1423189251Ssam "radiusAccClientRoundTripTime=%d\n" 1424189251Ssam "radiusAccClientRequests=%u\n" 1425189251Ssam "radiusAccClientRetransmissions=%u\n" 1426189251Ssam "radiusAccClientResponses=%u\n" 1427189251Ssam "radiusAccClientMalformedResponses=%u\n" 1428189251Ssam "radiusAccClientBadAuthenticators=%u\n" 1429189251Ssam "radiusAccClientPendingRequests=%u\n" 1430189251Ssam "radiusAccClientTimeouts=%u\n" 1431189251Ssam "radiusAccClientUnknownTypes=%u\n" 1432189251Ssam "radiusAccClientPacketsDropped=%u\n", 1433189251Ssam serv->index, 1434189251Ssam hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), 1435189251Ssam serv->port, 1436189251Ssam serv->round_trip_time, 1437189251Ssam serv->requests, 1438189251Ssam serv->retransmissions, 1439189251Ssam serv->responses, 1440189251Ssam serv->malformed_responses, 1441189251Ssam serv->bad_authenticators, 1442189251Ssam pending, 1443189251Ssam serv->timeouts, 1444189251Ssam serv->unknown_types, 1445189251Ssam serv->packets_dropped); 1446189251Ssam} 1447189251Ssam 1448189251Ssam 1449214734Srpaulo/** 1450214734Srpaulo * radius_client_get_mib - Get RADIUS client MIB information 1451214734Srpaulo * @radius: RADIUS client context from radius_client_init() 1452214734Srpaulo * @buf: Buffer for returning MIB data in text format 1453214734Srpaulo * @buflen: Maximum buf length in octets 1454214734Srpaulo * Returns: Number of octets written into the buffer 1455214734Srpaulo */ 1456189251Ssamint radius_client_get_mib(struct radius_client_data *radius, char *buf, 1457189251Ssam size_t buflen) 1458189251Ssam{ 1459189251Ssam struct hostapd_radius_servers *conf = radius->conf; 1460189251Ssam int i; 1461189251Ssam struct hostapd_radius_server *serv; 1462189251Ssam int count = 0; 1463189251Ssam 1464189251Ssam if (conf->auth_servers) { 1465189251Ssam for (i = 0; i < conf->num_auth_servers; i++) { 1466189251Ssam serv = &conf->auth_servers[i]; 1467189251Ssam count += radius_client_dump_auth_server( 1468189251Ssam buf + count, buflen - count, serv, 1469189251Ssam serv == conf->auth_server ? 1470189251Ssam radius : NULL); 1471189251Ssam } 1472189251Ssam } 1473189251Ssam 1474189251Ssam if (conf->acct_servers) { 1475189251Ssam for (i = 0; i < conf->num_acct_servers; i++) { 1476189251Ssam serv = &conf->acct_servers[i]; 1477189251Ssam count += radius_client_dump_acct_server( 1478189251Ssam buf + count, buflen - count, serv, 1479189251Ssam serv == conf->acct_server ? 1480189251Ssam radius : NULL); 1481189251Ssam } 1482189251Ssam } 1483189251Ssam 1484189251Ssam return count; 1485189251Ssam} 1486252726Srpaulo 1487252726Srpaulo 1488252726Srpaulovoid radius_client_reconfig(struct radius_client_data *radius, 1489252726Srpaulo struct hostapd_radius_servers *conf) 1490252726Srpaulo{ 1491252726Srpaulo if (radius) 1492252726Srpaulo radius->conf = conf; 1493252726Srpaulo} 1494