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