139beb93cSSam Leffler /* 2e28a4053SRui Paulo * RADIUS client 3e28a4053SRui Paulo * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> 439beb93cSSam Leffler * 5*f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 6*f05cddf9SRui Paulo * See README for more details. 739beb93cSSam Leffler */ 839beb93cSSam Leffler 939beb93cSSam Leffler #include "includes.h" 1039beb93cSSam Leffler 1139beb93cSSam Leffler #include "common.h" 1239beb93cSSam Leffler #include "radius.h" 1339beb93cSSam Leffler #include "radius_client.h" 1439beb93cSSam Leffler #include "eloop.h" 1539beb93cSSam Leffler 1639beb93cSSam Leffler /* Defaults for RADIUS retransmit values (exponential backoff) */ 17e28a4053SRui Paulo 18e28a4053SRui Paulo /** 19e28a4053SRui Paulo * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds 20e28a4053SRui Paulo */ 21e28a4053SRui Paulo #define RADIUS_CLIENT_FIRST_WAIT 3 22e28a4053SRui Paulo 23e28a4053SRui Paulo /** 24e28a4053SRui Paulo * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds 25e28a4053SRui Paulo */ 26e28a4053SRui Paulo #define RADIUS_CLIENT_MAX_WAIT 120 27e28a4053SRui Paulo 28e28a4053SRui Paulo /** 29e28a4053SRui Paulo * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries 30e28a4053SRui Paulo * 31e28a4053SRui Paulo * Maximum number of retransmit attempts before the entry is removed from 32e28a4053SRui Paulo * retransmit list. 33e28a4053SRui Paulo */ 34e28a4053SRui Paulo #define RADIUS_CLIENT_MAX_RETRIES 10 35e28a4053SRui Paulo 36e28a4053SRui Paulo /** 37e28a4053SRui Paulo * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages 38e28a4053SRui Paulo * 39e28a4053SRui Paulo * Maximum number of entries in retransmit list (oldest entries will be 40e28a4053SRui Paulo * removed, if this limit is exceeded). 41e28a4053SRui Paulo */ 42e28a4053SRui Paulo #define RADIUS_CLIENT_MAX_ENTRIES 30 43e28a4053SRui Paulo 44e28a4053SRui Paulo /** 45e28a4053SRui Paulo * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point 46e28a4053SRui Paulo * 47e28a4053SRui Paulo * The number of failed retry attempts after which the RADIUS server will be 48e28a4053SRui Paulo * changed (if one of more backup servers are configured). 49e28a4053SRui Paulo */ 50e28a4053SRui Paulo #define RADIUS_CLIENT_NUM_FAILOVER 4 5139beb93cSSam Leffler 5239beb93cSSam Leffler 53e28a4053SRui Paulo /** 54e28a4053SRui Paulo * struct radius_rx_handler - RADIUS client RX handler 55e28a4053SRui Paulo * 56e28a4053SRui Paulo * This data structure is used internally inside the RADIUS client module to 57e28a4053SRui Paulo * store registered RX handlers. These handlers are registered by calls to 58e28a4053SRui Paulo * radius_client_register() and unregistered when the RADIUS client is 59e28a4053SRui Paulo * deinitialized with a call to radius_client_deinit(). 60e28a4053SRui Paulo */ 6139beb93cSSam Leffler struct radius_rx_handler { 62e28a4053SRui Paulo /** 63e28a4053SRui Paulo * handler - Received RADIUS message handler 64e28a4053SRui Paulo */ 6539beb93cSSam Leffler RadiusRxResult (*handler)(struct radius_msg *msg, 6639beb93cSSam Leffler struct radius_msg *req, 6739beb93cSSam Leffler const u8 *shared_secret, 6839beb93cSSam Leffler size_t shared_secret_len, 6939beb93cSSam Leffler void *data); 70e28a4053SRui Paulo 71e28a4053SRui Paulo /** 72e28a4053SRui Paulo * data - Context data for the handler 73e28a4053SRui Paulo */ 7439beb93cSSam Leffler void *data; 7539beb93cSSam Leffler }; 7639beb93cSSam Leffler 7739beb93cSSam Leffler 78e28a4053SRui Paulo /** 79e28a4053SRui Paulo * struct radius_msg_list - RADIUS client message retransmit list 80e28a4053SRui Paulo * 81e28a4053SRui Paulo * This data structure is used internally inside the RADIUS client module to 82e28a4053SRui Paulo * store pending RADIUS requests that may still need to be retransmitted. 83e28a4053SRui Paulo */ 8439beb93cSSam Leffler struct radius_msg_list { 85e28a4053SRui Paulo /** 86e28a4053SRui Paulo * addr - STA/client address 87e28a4053SRui Paulo * 88e28a4053SRui Paulo * This is used to find RADIUS messages for the same STA. 89e28a4053SRui Paulo */ 90e28a4053SRui Paulo u8 addr[ETH_ALEN]; 91e28a4053SRui Paulo 92e28a4053SRui Paulo /** 93e28a4053SRui Paulo * msg - RADIUS message 94e28a4053SRui Paulo */ 9539beb93cSSam Leffler struct radius_msg *msg; 96e28a4053SRui Paulo 97e28a4053SRui Paulo /** 98e28a4053SRui Paulo * msg_type - Message type 99e28a4053SRui Paulo */ 10039beb93cSSam Leffler RadiusType msg_type; 101e28a4053SRui Paulo 102e28a4053SRui Paulo /** 103e28a4053SRui Paulo * first_try - Time of the first transmission attempt 104e28a4053SRui Paulo */ 10539beb93cSSam Leffler os_time_t first_try; 106e28a4053SRui Paulo 107e28a4053SRui Paulo /** 108e28a4053SRui Paulo * next_try - Time for the next transmission attempt 109e28a4053SRui Paulo */ 11039beb93cSSam Leffler os_time_t next_try; 111e28a4053SRui Paulo 112e28a4053SRui Paulo /** 113e28a4053SRui Paulo * attempts - Number of transmission attempts 114e28a4053SRui Paulo */ 11539beb93cSSam Leffler int attempts; 116e28a4053SRui Paulo 117e28a4053SRui Paulo /** 118e28a4053SRui Paulo * next_wait - Next retransmission wait time in seconds 119e28a4053SRui Paulo */ 12039beb93cSSam Leffler int next_wait; 121e28a4053SRui Paulo 122e28a4053SRui Paulo /** 123e28a4053SRui Paulo * last_attempt - Time of the last transmission attempt 124e28a4053SRui Paulo */ 12539beb93cSSam Leffler struct os_time last_attempt; 12639beb93cSSam Leffler 127e28a4053SRui Paulo /** 128e28a4053SRui Paulo * shared_secret - Shared secret with the target RADIUS server 129e28a4053SRui Paulo */ 130e28a4053SRui Paulo const u8 *shared_secret; 131e28a4053SRui Paulo 132e28a4053SRui Paulo /** 133e28a4053SRui Paulo * shared_secret_len - shared_secret length in octets 134e28a4053SRui Paulo */ 13539beb93cSSam Leffler size_t shared_secret_len; 13639beb93cSSam Leffler 13739beb93cSSam Leffler /* TODO: server config with failover to backup server(s) */ 13839beb93cSSam Leffler 139e28a4053SRui Paulo /** 140e28a4053SRui Paulo * next - Next message in the list 141e28a4053SRui Paulo */ 14239beb93cSSam Leffler struct radius_msg_list *next; 14339beb93cSSam Leffler }; 14439beb93cSSam Leffler 14539beb93cSSam Leffler 146e28a4053SRui Paulo /** 147e28a4053SRui Paulo * struct radius_client_data - Internal RADIUS client data 148e28a4053SRui Paulo * 149e28a4053SRui Paulo * This data structure is used internally inside the RADIUS client module. 150e28a4053SRui Paulo * External users allocate this by calling radius_client_init() and free it by 151e28a4053SRui Paulo * calling radius_client_deinit(). The pointer to this opaque data is used in 152e28a4053SRui Paulo * calls to other functions as an identifier for the RADIUS client instance. 153e28a4053SRui Paulo */ 15439beb93cSSam Leffler struct radius_client_data { 155e28a4053SRui Paulo /** 156e28a4053SRui Paulo * ctx - Context pointer for hostapd_logger() callbacks 157e28a4053SRui Paulo */ 15839beb93cSSam Leffler void *ctx; 159e28a4053SRui Paulo 160e28a4053SRui Paulo /** 161e28a4053SRui Paulo * conf - RADIUS client configuration (list of RADIUS servers to use) 162e28a4053SRui Paulo */ 16339beb93cSSam Leffler struct hostapd_radius_servers *conf; 16439beb93cSSam Leffler 165e28a4053SRui Paulo /** 166e28a4053SRui Paulo * auth_serv_sock - IPv4 socket for RADIUS authentication messages 167e28a4053SRui Paulo */ 168e28a4053SRui Paulo int auth_serv_sock; 16939beb93cSSam Leffler 170e28a4053SRui Paulo /** 171e28a4053SRui Paulo * acct_serv_sock - IPv4 socket for RADIUS accounting messages 172e28a4053SRui Paulo */ 173e28a4053SRui Paulo int acct_serv_sock; 174e28a4053SRui Paulo 175e28a4053SRui Paulo /** 176e28a4053SRui Paulo * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages 177e28a4053SRui Paulo */ 178e28a4053SRui Paulo int auth_serv_sock6; 179e28a4053SRui Paulo 180e28a4053SRui Paulo /** 181e28a4053SRui Paulo * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages 182e28a4053SRui Paulo */ 183e28a4053SRui Paulo int acct_serv_sock6; 184e28a4053SRui Paulo 185e28a4053SRui Paulo /** 186e28a4053SRui Paulo * auth_sock - Currently used socket for RADIUS authentication server 187e28a4053SRui Paulo */ 188e28a4053SRui Paulo int auth_sock; 189e28a4053SRui Paulo 190e28a4053SRui Paulo /** 191e28a4053SRui Paulo * acct_sock - Currently used socket for RADIUS accounting server 192e28a4053SRui Paulo */ 193e28a4053SRui Paulo int acct_sock; 194e28a4053SRui Paulo 195e28a4053SRui Paulo /** 196e28a4053SRui Paulo * auth_handlers - Authentication message handlers 197e28a4053SRui Paulo */ 19839beb93cSSam Leffler struct radius_rx_handler *auth_handlers; 199e28a4053SRui Paulo 200e28a4053SRui Paulo /** 201e28a4053SRui Paulo * num_auth_handlers - Number of handlers in auth_handlers 202e28a4053SRui Paulo */ 20339beb93cSSam Leffler size_t num_auth_handlers; 204e28a4053SRui Paulo 205e28a4053SRui Paulo /** 206e28a4053SRui Paulo * acct_handlers - Accounting message handlers 207e28a4053SRui Paulo */ 20839beb93cSSam Leffler struct radius_rx_handler *acct_handlers; 209e28a4053SRui Paulo 210e28a4053SRui Paulo /** 211e28a4053SRui Paulo * num_acct_handlers - Number of handlers in acct_handlers 212e28a4053SRui Paulo */ 21339beb93cSSam Leffler size_t num_acct_handlers; 21439beb93cSSam Leffler 215e28a4053SRui Paulo /** 216e28a4053SRui Paulo * msgs - Pending outgoing RADIUS messages 217e28a4053SRui Paulo */ 21839beb93cSSam Leffler struct radius_msg_list *msgs; 219e28a4053SRui Paulo 220e28a4053SRui Paulo /** 221e28a4053SRui Paulo * num_msgs - Number of pending messages in the msgs list 222e28a4053SRui Paulo */ 22339beb93cSSam Leffler size_t num_msgs; 22439beb93cSSam Leffler 225e28a4053SRui Paulo /** 226e28a4053SRui Paulo * next_radius_identifier - Next RADIUS message identifier to use 227e28a4053SRui Paulo */ 22839beb93cSSam Leffler u8 next_radius_identifier; 22939beb93cSSam Leffler }; 23039beb93cSSam Leffler 23139beb93cSSam Leffler 23239beb93cSSam Leffler static int 23339beb93cSSam Leffler radius_change_server(struct radius_client_data *radius, 23439beb93cSSam Leffler struct hostapd_radius_server *nserv, 23539beb93cSSam Leffler struct hostapd_radius_server *oserv, 23639beb93cSSam Leffler int sock, int sock6, int auth); 23739beb93cSSam Leffler static int radius_client_init_acct(struct radius_client_data *radius); 23839beb93cSSam Leffler static int radius_client_init_auth(struct radius_client_data *radius); 23939beb93cSSam Leffler 24039beb93cSSam Leffler 24139beb93cSSam Leffler static void radius_client_msg_free(struct radius_msg_list *req) 24239beb93cSSam Leffler { 24339beb93cSSam Leffler radius_msg_free(req->msg); 24439beb93cSSam Leffler os_free(req); 24539beb93cSSam Leffler } 24639beb93cSSam Leffler 24739beb93cSSam Leffler 248e28a4053SRui Paulo /** 249e28a4053SRui Paulo * radius_client_register - Register a RADIUS client RX handler 250e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 251e28a4053SRui Paulo * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT) 252e28a4053SRui Paulo * @handler: Handler for received RADIUS messages 253e28a4053SRui Paulo * @data: Context pointer for handler callbacks 254e28a4053SRui Paulo * Returns: 0 on success, -1 on failure 255e28a4053SRui Paulo * 256e28a4053SRui Paulo * This function is used to register a handler for processing received RADIUS 257e28a4053SRui Paulo * authentication and accounting messages. The handler() callback function will 258e28a4053SRui Paulo * be called whenever a RADIUS message is received from the active server. 259e28a4053SRui Paulo * 260e28a4053SRui Paulo * There can be multiple registered RADIUS message handlers. The handlers will 261e28a4053SRui Paulo * be called in order until one of them indicates that it has processed or 262e28a4053SRui Paulo * queued the message. 263e28a4053SRui Paulo */ 26439beb93cSSam Leffler int radius_client_register(struct radius_client_data *radius, 26539beb93cSSam Leffler RadiusType msg_type, 26639beb93cSSam Leffler RadiusRxResult (*handler)(struct radius_msg *msg, 26739beb93cSSam Leffler struct radius_msg *req, 26839beb93cSSam Leffler const u8 *shared_secret, 26939beb93cSSam Leffler size_t shared_secret_len, 27039beb93cSSam Leffler void *data), 27139beb93cSSam Leffler void *data) 27239beb93cSSam Leffler { 27339beb93cSSam Leffler struct radius_rx_handler **handlers, *newh; 27439beb93cSSam Leffler size_t *num; 27539beb93cSSam Leffler 27639beb93cSSam Leffler if (msg_type == RADIUS_ACCT) { 27739beb93cSSam Leffler handlers = &radius->acct_handlers; 27839beb93cSSam Leffler num = &radius->num_acct_handlers; 27939beb93cSSam Leffler } else { 28039beb93cSSam Leffler handlers = &radius->auth_handlers; 28139beb93cSSam Leffler num = &radius->num_auth_handlers; 28239beb93cSSam Leffler } 28339beb93cSSam Leffler 284*f05cddf9SRui Paulo newh = os_realloc_array(*handlers, *num + 1, 285*f05cddf9SRui Paulo sizeof(struct radius_rx_handler)); 28639beb93cSSam Leffler if (newh == NULL) 28739beb93cSSam Leffler return -1; 28839beb93cSSam Leffler 28939beb93cSSam Leffler newh[*num].handler = handler; 29039beb93cSSam Leffler newh[*num].data = data; 29139beb93cSSam Leffler (*num)++; 29239beb93cSSam Leffler *handlers = newh; 29339beb93cSSam Leffler 29439beb93cSSam Leffler return 0; 29539beb93cSSam Leffler } 29639beb93cSSam Leffler 29739beb93cSSam Leffler 29839beb93cSSam Leffler static void radius_client_handle_send_error(struct radius_client_data *radius, 29939beb93cSSam Leffler int s, RadiusType msg_type) 30039beb93cSSam Leffler { 30139beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS 30239beb93cSSam Leffler int _errno = errno; 30339beb93cSSam Leffler perror("send[RADIUS]"); 30439beb93cSSam Leffler if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || 30539beb93cSSam Leffler _errno == EBADF) { 30639beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 30739beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 30839beb93cSSam Leffler "Send failed - maybe interface status changed -" 30939beb93cSSam Leffler " try to connect again"); 31039beb93cSSam Leffler eloop_unregister_read_sock(s); 31139beb93cSSam Leffler close(s); 31239beb93cSSam Leffler if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) 31339beb93cSSam Leffler radius_client_init_acct(radius); 31439beb93cSSam Leffler else 31539beb93cSSam Leffler radius_client_init_auth(radius); 31639beb93cSSam Leffler } 31739beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */ 31839beb93cSSam Leffler } 31939beb93cSSam Leffler 32039beb93cSSam Leffler 32139beb93cSSam Leffler static int radius_client_retransmit(struct radius_client_data *radius, 32239beb93cSSam Leffler struct radius_msg_list *entry, 32339beb93cSSam Leffler os_time_t now) 32439beb93cSSam Leffler { 32539beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 32639beb93cSSam Leffler int s; 327e28a4053SRui Paulo struct wpabuf *buf; 32839beb93cSSam Leffler 32939beb93cSSam Leffler if (entry->msg_type == RADIUS_ACCT || 33039beb93cSSam Leffler entry->msg_type == RADIUS_ACCT_INTERIM) { 33139beb93cSSam Leffler s = radius->acct_sock; 33239beb93cSSam Leffler if (entry->attempts == 0) 33339beb93cSSam Leffler conf->acct_server->requests++; 33439beb93cSSam Leffler else { 33539beb93cSSam Leffler conf->acct_server->timeouts++; 33639beb93cSSam Leffler conf->acct_server->retransmissions++; 33739beb93cSSam Leffler } 33839beb93cSSam Leffler } else { 33939beb93cSSam Leffler s = radius->auth_sock; 34039beb93cSSam Leffler if (entry->attempts == 0) 34139beb93cSSam Leffler conf->auth_server->requests++; 34239beb93cSSam Leffler else { 34339beb93cSSam Leffler conf->auth_server->timeouts++; 34439beb93cSSam Leffler conf->auth_server->retransmissions++; 34539beb93cSSam Leffler } 34639beb93cSSam Leffler } 34739beb93cSSam Leffler 34839beb93cSSam Leffler /* retransmit; remove entry if too many attempts */ 34939beb93cSSam Leffler entry->attempts++; 35039beb93cSSam Leffler hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, 35139beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", 352e28a4053SRui Paulo radius_msg_get_hdr(entry->msg)->identifier); 35339beb93cSSam Leffler 35439beb93cSSam Leffler os_get_time(&entry->last_attempt); 355e28a4053SRui Paulo buf = radius_msg_get_buf(entry->msg); 356e28a4053SRui Paulo if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) 35739beb93cSSam Leffler radius_client_handle_send_error(radius, s, entry->msg_type); 35839beb93cSSam Leffler 35939beb93cSSam Leffler entry->next_try = now + entry->next_wait; 36039beb93cSSam Leffler entry->next_wait *= 2; 36139beb93cSSam Leffler if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) 36239beb93cSSam Leffler entry->next_wait = RADIUS_CLIENT_MAX_WAIT; 36339beb93cSSam Leffler if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) { 36439beb93cSSam Leffler printf("Removing un-ACKed RADIUS message due to too many " 36539beb93cSSam Leffler "failed retransmit attempts\n"); 36639beb93cSSam Leffler return 1; 36739beb93cSSam Leffler } 36839beb93cSSam Leffler 36939beb93cSSam Leffler return 0; 37039beb93cSSam Leffler } 37139beb93cSSam Leffler 37239beb93cSSam Leffler 37339beb93cSSam Leffler static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) 37439beb93cSSam Leffler { 37539beb93cSSam Leffler struct radius_client_data *radius = eloop_ctx; 37639beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 37739beb93cSSam Leffler struct os_time now; 37839beb93cSSam Leffler os_time_t first; 37939beb93cSSam Leffler struct radius_msg_list *entry, *prev, *tmp; 38039beb93cSSam Leffler int auth_failover = 0, acct_failover = 0; 38139beb93cSSam Leffler char abuf[50]; 38239beb93cSSam Leffler 38339beb93cSSam Leffler entry = radius->msgs; 38439beb93cSSam Leffler if (!entry) 38539beb93cSSam Leffler return; 38639beb93cSSam Leffler 38739beb93cSSam Leffler os_get_time(&now); 38839beb93cSSam Leffler first = 0; 38939beb93cSSam Leffler 39039beb93cSSam Leffler prev = NULL; 39139beb93cSSam Leffler while (entry) { 39239beb93cSSam Leffler if (now.sec >= entry->next_try && 39339beb93cSSam Leffler radius_client_retransmit(radius, entry, now.sec)) { 39439beb93cSSam Leffler if (prev) 39539beb93cSSam Leffler prev->next = entry->next; 39639beb93cSSam Leffler else 39739beb93cSSam Leffler radius->msgs = entry->next; 39839beb93cSSam Leffler 39939beb93cSSam Leffler tmp = entry; 40039beb93cSSam Leffler entry = entry->next; 40139beb93cSSam Leffler radius_client_msg_free(tmp); 40239beb93cSSam Leffler radius->num_msgs--; 40339beb93cSSam Leffler continue; 40439beb93cSSam Leffler } 40539beb93cSSam Leffler 40639beb93cSSam Leffler if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) { 40739beb93cSSam Leffler if (entry->msg_type == RADIUS_ACCT || 40839beb93cSSam Leffler entry->msg_type == RADIUS_ACCT_INTERIM) 40939beb93cSSam Leffler acct_failover++; 41039beb93cSSam Leffler else 41139beb93cSSam Leffler auth_failover++; 41239beb93cSSam Leffler } 41339beb93cSSam Leffler 41439beb93cSSam Leffler if (first == 0 || entry->next_try < first) 41539beb93cSSam Leffler first = entry->next_try; 41639beb93cSSam Leffler 41739beb93cSSam Leffler prev = entry; 41839beb93cSSam Leffler entry = entry->next; 41939beb93cSSam Leffler } 42039beb93cSSam Leffler 42139beb93cSSam Leffler if (radius->msgs) { 42239beb93cSSam Leffler if (first < now.sec) 42339beb93cSSam Leffler first = now.sec; 42439beb93cSSam Leffler eloop_register_timeout(first - now.sec, 0, 42539beb93cSSam Leffler radius_client_timer, radius, NULL); 42639beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 42739beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Next RADIUS client " 42839beb93cSSam Leffler "retransmit in %ld seconds", 42939beb93cSSam Leffler (long int) (first - now.sec)); 43039beb93cSSam Leffler } 43139beb93cSSam Leffler 43239beb93cSSam Leffler if (auth_failover && conf->num_auth_servers > 1) { 43339beb93cSSam Leffler struct hostapd_radius_server *next, *old; 43439beb93cSSam Leffler old = conf->auth_server; 43539beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 43639beb93cSSam Leffler HOSTAPD_LEVEL_NOTICE, 43739beb93cSSam Leffler "No response from Authentication server " 43839beb93cSSam Leffler "%s:%d - failover", 43939beb93cSSam Leffler hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), 44039beb93cSSam Leffler old->port); 44139beb93cSSam Leffler 44239beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 44339beb93cSSam Leffler if (entry->msg_type == RADIUS_AUTH) 44439beb93cSSam Leffler old->timeouts++; 44539beb93cSSam Leffler } 44639beb93cSSam Leffler 44739beb93cSSam Leffler next = old + 1; 44839beb93cSSam Leffler if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) 44939beb93cSSam Leffler next = conf->auth_servers; 45039beb93cSSam Leffler conf->auth_server = next; 45139beb93cSSam Leffler radius_change_server(radius, next, old, 45239beb93cSSam Leffler radius->auth_serv_sock, 45339beb93cSSam Leffler radius->auth_serv_sock6, 1); 45439beb93cSSam Leffler } 45539beb93cSSam Leffler 45639beb93cSSam Leffler if (acct_failover && conf->num_acct_servers > 1) { 45739beb93cSSam Leffler struct hostapd_radius_server *next, *old; 45839beb93cSSam Leffler old = conf->acct_server; 45939beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 46039beb93cSSam Leffler HOSTAPD_LEVEL_NOTICE, 46139beb93cSSam Leffler "No response from Accounting server " 46239beb93cSSam Leffler "%s:%d - failover", 46339beb93cSSam Leffler hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), 46439beb93cSSam Leffler old->port); 46539beb93cSSam Leffler 46639beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 46739beb93cSSam Leffler if (entry->msg_type == RADIUS_ACCT || 46839beb93cSSam Leffler entry->msg_type == RADIUS_ACCT_INTERIM) 46939beb93cSSam Leffler old->timeouts++; 47039beb93cSSam Leffler } 47139beb93cSSam Leffler 47239beb93cSSam Leffler next = old + 1; 47339beb93cSSam Leffler if (next > &conf->acct_servers[conf->num_acct_servers - 1]) 47439beb93cSSam Leffler next = conf->acct_servers; 47539beb93cSSam Leffler conf->acct_server = next; 47639beb93cSSam Leffler radius_change_server(radius, next, old, 47739beb93cSSam Leffler radius->acct_serv_sock, 47839beb93cSSam Leffler radius->acct_serv_sock6, 0); 47939beb93cSSam Leffler } 48039beb93cSSam Leffler } 48139beb93cSSam Leffler 48239beb93cSSam Leffler 48339beb93cSSam Leffler static void radius_client_update_timeout(struct radius_client_data *radius) 48439beb93cSSam Leffler { 48539beb93cSSam Leffler struct os_time now; 48639beb93cSSam Leffler os_time_t first; 48739beb93cSSam Leffler struct radius_msg_list *entry; 48839beb93cSSam Leffler 48939beb93cSSam Leffler eloop_cancel_timeout(radius_client_timer, radius, NULL); 49039beb93cSSam Leffler 49139beb93cSSam Leffler if (radius->msgs == NULL) { 49239beb93cSSam Leffler return; 49339beb93cSSam Leffler } 49439beb93cSSam Leffler 49539beb93cSSam Leffler first = 0; 49639beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 49739beb93cSSam Leffler if (first == 0 || entry->next_try < first) 49839beb93cSSam Leffler first = entry->next_try; 49939beb93cSSam Leffler } 50039beb93cSSam Leffler 50139beb93cSSam Leffler os_get_time(&now); 50239beb93cSSam Leffler if (first < now.sec) 50339beb93cSSam Leffler first = now.sec; 50439beb93cSSam Leffler eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, 50539beb93cSSam Leffler NULL); 50639beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 50739beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" 508*f05cddf9SRui Paulo " %ld seconds", (long int) (first - now.sec)); 50939beb93cSSam Leffler } 51039beb93cSSam Leffler 51139beb93cSSam Leffler 51239beb93cSSam Leffler static void radius_client_list_add(struct radius_client_data *radius, 51339beb93cSSam Leffler struct radius_msg *msg, 514e28a4053SRui Paulo RadiusType msg_type, 515e28a4053SRui Paulo const u8 *shared_secret, 51639beb93cSSam Leffler size_t shared_secret_len, const u8 *addr) 51739beb93cSSam Leffler { 51839beb93cSSam Leffler struct radius_msg_list *entry, *prev; 51939beb93cSSam Leffler 52039beb93cSSam Leffler if (eloop_terminated()) { 52139beb93cSSam Leffler /* No point in adding entries to retransmit queue since event 52239beb93cSSam Leffler * loop has already been terminated. */ 52339beb93cSSam Leffler radius_msg_free(msg); 52439beb93cSSam Leffler return; 52539beb93cSSam Leffler } 52639beb93cSSam Leffler 52739beb93cSSam Leffler entry = os_zalloc(sizeof(*entry)); 52839beb93cSSam Leffler if (entry == NULL) { 52939beb93cSSam Leffler printf("Failed to add RADIUS packet into retransmit list\n"); 53039beb93cSSam Leffler radius_msg_free(msg); 53139beb93cSSam Leffler return; 53239beb93cSSam Leffler } 53339beb93cSSam Leffler 53439beb93cSSam Leffler if (addr) 53539beb93cSSam Leffler os_memcpy(entry->addr, addr, ETH_ALEN); 53639beb93cSSam Leffler entry->msg = msg; 53739beb93cSSam Leffler entry->msg_type = msg_type; 53839beb93cSSam Leffler entry->shared_secret = shared_secret; 53939beb93cSSam Leffler entry->shared_secret_len = shared_secret_len; 54039beb93cSSam Leffler os_get_time(&entry->last_attempt); 54139beb93cSSam Leffler entry->first_try = entry->last_attempt.sec; 54239beb93cSSam Leffler entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; 54339beb93cSSam Leffler entry->attempts = 1; 54439beb93cSSam Leffler entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; 54539beb93cSSam Leffler entry->next = radius->msgs; 54639beb93cSSam Leffler radius->msgs = entry; 54739beb93cSSam Leffler radius_client_update_timeout(radius); 54839beb93cSSam Leffler 54939beb93cSSam Leffler if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { 55039beb93cSSam Leffler printf("Removing the oldest un-ACKed RADIUS packet due to " 55139beb93cSSam Leffler "retransmit list limits.\n"); 55239beb93cSSam Leffler prev = NULL; 55339beb93cSSam Leffler while (entry->next) { 55439beb93cSSam Leffler prev = entry; 55539beb93cSSam Leffler entry = entry->next; 55639beb93cSSam Leffler } 55739beb93cSSam Leffler if (prev) { 55839beb93cSSam Leffler prev->next = NULL; 55939beb93cSSam Leffler radius_client_msg_free(entry); 56039beb93cSSam Leffler } 56139beb93cSSam Leffler } else 56239beb93cSSam Leffler radius->num_msgs++; 56339beb93cSSam Leffler } 56439beb93cSSam Leffler 56539beb93cSSam Leffler 56639beb93cSSam Leffler static void radius_client_list_del(struct radius_client_data *radius, 56739beb93cSSam Leffler RadiusType msg_type, const u8 *addr) 56839beb93cSSam Leffler { 56939beb93cSSam Leffler struct radius_msg_list *entry, *prev, *tmp; 57039beb93cSSam Leffler 57139beb93cSSam Leffler if (addr == NULL) 57239beb93cSSam Leffler return; 57339beb93cSSam Leffler 57439beb93cSSam Leffler entry = radius->msgs; 57539beb93cSSam Leffler prev = NULL; 57639beb93cSSam Leffler while (entry) { 57739beb93cSSam Leffler if (entry->msg_type == msg_type && 57839beb93cSSam Leffler os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { 57939beb93cSSam Leffler if (prev) 58039beb93cSSam Leffler prev->next = entry->next; 58139beb93cSSam Leffler else 58239beb93cSSam Leffler radius->msgs = entry->next; 58339beb93cSSam Leffler tmp = entry; 58439beb93cSSam Leffler entry = entry->next; 58539beb93cSSam Leffler hostapd_logger(radius->ctx, addr, 58639beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 58739beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 58839beb93cSSam Leffler "Removing matching RADIUS message"); 58939beb93cSSam Leffler radius_client_msg_free(tmp); 59039beb93cSSam Leffler radius->num_msgs--; 59139beb93cSSam Leffler continue; 59239beb93cSSam Leffler } 59339beb93cSSam Leffler prev = entry; 59439beb93cSSam Leffler entry = entry->next; 59539beb93cSSam Leffler } 59639beb93cSSam Leffler } 59739beb93cSSam Leffler 59839beb93cSSam Leffler 599e28a4053SRui Paulo /** 600e28a4053SRui Paulo * radius_client_send - Send a RADIUS request 601e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 602e28a4053SRui Paulo * @msg: RADIUS message to be sent 603e28a4053SRui Paulo * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM) 604e28a4053SRui Paulo * @addr: MAC address of the device related to this message or %NULL 605e28a4053SRui Paulo * Returns: 0 on success, -1 on failure 606e28a4053SRui Paulo * 607e28a4053SRui Paulo * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or 608e28a4053SRui Paulo * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference 609e28a4053SRui Paulo * between accounting and interim accounting messages is that the interim 610e28a4053SRui Paulo * message will override any pending interim accounting updates while a new 611e28a4053SRui Paulo * accounting message does not remove any pending messages. 612e28a4053SRui Paulo * 613e28a4053SRui Paulo * The message is added on the retransmission queue and will be retransmitted 614e28a4053SRui Paulo * automatically until a response is received or maximum number of retries 615e28a4053SRui Paulo * (RADIUS_CLIENT_MAX_RETRIES) is reached. 616e28a4053SRui Paulo * 617e28a4053SRui Paulo * The related device MAC address can be used to identify pending messages that 618e28a4053SRui Paulo * can be removed with radius_client_flush_auth() or with interim accounting 619e28a4053SRui Paulo * updates. 620e28a4053SRui Paulo */ 62139beb93cSSam Leffler int radius_client_send(struct radius_client_data *radius, 62239beb93cSSam Leffler struct radius_msg *msg, RadiusType msg_type, 62339beb93cSSam Leffler const u8 *addr) 62439beb93cSSam Leffler { 62539beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 626e28a4053SRui Paulo const u8 *shared_secret; 62739beb93cSSam Leffler size_t shared_secret_len; 62839beb93cSSam Leffler char *name; 62939beb93cSSam Leffler int s, res; 630e28a4053SRui Paulo struct wpabuf *buf; 63139beb93cSSam Leffler 63239beb93cSSam Leffler if (msg_type == RADIUS_ACCT_INTERIM) { 63339beb93cSSam Leffler /* Remove any pending interim acct update for the same STA. */ 63439beb93cSSam Leffler radius_client_list_del(radius, msg_type, addr); 63539beb93cSSam Leffler } 63639beb93cSSam Leffler 63739beb93cSSam Leffler if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { 63839beb93cSSam Leffler if (conf->acct_server == NULL) { 63939beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, 64039beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 64139beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 64239beb93cSSam Leffler "No accounting server configured"); 64339beb93cSSam Leffler return -1; 64439beb93cSSam Leffler } 64539beb93cSSam Leffler shared_secret = conf->acct_server->shared_secret; 64639beb93cSSam Leffler shared_secret_len = conf->acct_server->shared_secret_len; 64739beb93cSSam Leffler radius_msg_finish_acct(msg, shared_secret, shared_secret_len); 64839beb93cSSam Leffler name = "accounting"; 64939beb93cSSam Leffler s = radius->acct_sock; 65039beb93cSSam Leffler conf->acct_server->requests++; 65139beb93cSSam Leffler } else { 65239beb93cSSam Leffler if (conf->auth_server == NULL) { 65339beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, 65439beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 65539beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 65639beb93cSSam Leffler "No authentication server configured"); 65739beb93cSSam Leffler return -1; 65839beb93cSSam Leffler } 65939beb93cSSam Leffler shared_secret = conf->auth_server->shared_secret; 66039beb93cSSam Leffler shared_secret_len = conf->auth_server->shared_secret_len; 66139beb93cSSam Leffler radius_msg_finish(msg, shared_secret, shared_secret_len); 66239beb93cSSam Leffler name = "authentication"; 66339beb93cSSam Leffler s = radius->auth_sock; 66439beb93cSSam Leffler conf->auth_server->requests++; 66539beb93cSSam Leffler } 66639beb93cSSam Leffler 66739beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 66839beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s " 66939beb93cSSam Leffler "server", name); 67039beb93cSSam Leffler if (conf->msg_dumps) 67139beb93cSSam Leffler radius_msg_dump(msg); 67239beb93cSSam Leffler 673e28a4053SRui Paulo buf = radius_msg_get_buf(msg); 674e28a4053SRui Paulo res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0); 67539beb93cSSam Leffler if (res < 0) 67639beb93cSSam Leffler radius_client_handle_send_error(radius, s, msg_type); 67739beb93cSSam Leffler 67839beb93cSSam Leffler radius_client_list_add(radius, msg, msg_type, shared_secret, 67939beb93cSSam Leffler shared_secret_len, addr); 68039beb93cSSam Leffler 681*f05cddf9SRui Paulo return 0; 68239beb93cSSam Leffler } 68339beb93cSSam Leffler 68439beb93cSSam Leffler 68539beb93cSSam Leffler static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) 68639beb93cSSam Leffler { 68739beb93cSSam Leffler struct radius_client_data *radius = eloop_ctx; 68839beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 68939beb93cSSam Leffler RadiusType msg_type = (RadiusType) sock_ctx; 69039beb93cSSam Leffler int len, roundtrip; 69139beb93cSSam Leffler unsigned char buf[3000]; 69239beb93cSSam Leffler struct radius_msg *msg; 693e28a4053SRui Paulo struct radius_hdr *hdr; 69439beb93cSSam Leffler struct radius_rx_handler *handlers; 69539beb93cSSam Leffler size_t num_handlers, i; 69639beb93cSSam Leffler struct radius_msg_list *req, *prev_req; 69739beb93cSSam Leffler struct os_time now; 69839beb93cSSam Leffler struct hostapd_radius_server *rconf; 69939beb93cSSam Leffler int invalid_authenticator = 0; 70039beb93cSSam Leffler 70139beb93cSSam Leffler if (msg_type == RADIUS_ACCT) { 70239beb93cSSam Leffler handlers = radius->acct_handlers; 70339beb93cSSam Leffler num_handlers = radius->num_acct_handlers; 70439beb93cSSam Leffler rconf = conf->acct_server; 70539beb93cSSam Leffler } else { 70639beb93cSSam Leffler handlers = radius->auth_handlers; 70739beb93cSSam Leffler num_handlers = radius->num_auth_handlers; 70839beb93cSSam Leffler rconf = conf->auth_server; 70939beb93cSSam Leffler } 71039beb93cSSam Leffler 71139beb93cSSam Leffler len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); 71239beb93cSSam Leffler if (len < 0) { 71339beb93cSSam Leffler perror("recv[RADIUS]"); 71439beb93cSSam Leffler return; 71539beb93cSSam Leffler } 71639beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 71739beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS " 71839beb93cSSam Leffler "server", len); 71939beb93cSSam Leffler if (len == sizeof(buf)) { 72039beb93cSSam Leffler printf("Possibly too long UDP frame for our buffer - " 72139beb93cSSam Leffler "dropping it\n"); 72239beb93cSSam Leffler return; 72339beb93cSSam Leffler } 72439beb93cSSam Leffler 72539beb93cSSam Leffler msg = radius_msg_parse(buf, len); 72639beb93cSSam Leffler if (msg == NULL) { 72739beb93cSSam Leffler printf("Parsing incoming RADIUS frame failed\n"); 72839beb93cSSam Leffler rconf->malformed_responses++; 72939beb93cSSam Leffler return; 73039beb93cSSam Leffler } 731e28a4053SRui Paulo hdr = radius_msg_get_hdr(msg); 73239beb93cSSam Leffler 73339beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 73439beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Received RADIUS message"); 73539beb93cSSam Leffler if (conf->msg_dumps) 73639beb93cSSam Leffler radius_msg_dump(msg); 73739beb93cSSam Leffler 738e28a4053SRui Paulo switch (hdr->code) { 73939beb93cSSam Leffler case RADIUS_CODE_ACCESS_ACCEPT: 74039beb93cSSam Leffler rconf->access_accepts++; 74139beb93cSSam Leffler break; 74239beb93cSSam Leffler case RADIUS_CODE_ACCESS_REJECT: 74339beb93cSSam Leffler rconf->access_rejects++; 74439beb93cSSam Leffler break; 74539beb93cSSam Leffler case RADIUS_CODE_ACCESS_CHALLENGE: 74639beb93cSSam Leffler rconf->access_challenges++; 74739beb93cSSam Leffler break; 74839beb93cSSam Leffler case RADIUS_CODE_ACCOUNTING_RESPONSE: 74939beb93cSSam Leffler rconf->responses++; 75039beb93cSSam Leffler break; 75139beb93cSSam Leffler } 75239beb93cSSam Leffler 75339beb93cSSam Leffler prev_req = NULL; 75439beb93cSSam Leffler req = radius->msgs; 75539beb93cSSam Leffler while (req) { 75639beb93cSSam Leffler /* TODO: also match by src addr:port of the packet when using 75739beb93cSSam Leffler * alternative RADIUS servers (?) */ 75839beb93cSSam Leffler if ((req->msg_type == msg_type || 75939beb93cSSam Leffler (req->msg_type == RADIUS_ACCT_INTERIM && 76039beb93cSSam Leffler msg_type == RADIUS_ACCT)) && 761e28a4053SRui Paulo radius_msg_get_hdr(req->msg)->identifier == 762e28a4053SRui Paulo hdr->identifier) 76339beb93cSSam Leffler break; 76439beb93cSSam Leffler 76539beb93cSSam Leffler prev_req = req; 76639beb93cSSam Leffler req = req->next; 76739beb93cSSam Leffler } 76839beb93cSSam Leffler 76939beb93cSSam Leffler if (req == NULL) { 77039beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 77139beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 77239beb93cSSam Leffler "No matching RADIUS request found (type=%d " 77339beb93cSSam Leffler "id=%d) - dropping packet", 774e28a4053SRui Paulo msg_type, hdr->identifier); 77539beb93cSSam Leffler goto fail; 77639beb93cSSam Leffler } 77739beb93cSSam Leffler 77839beb93cSSam Leffler os_get_time(&now); 77939beb93cSSam Leffler roundtrip = (now.sec - req->last_attempt.sec) * 100 + 78039beb93cSSam Leffler (now.usec - req->last_attempt.usec) / 10000; 78139beb93cSSam Leffler hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, 78239beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 78339beb93cSSam Leffler "Received RADIUS packet matched with a pending " 78439beb93cSSam Leffler "request, round trip time %d.%02d sec", 78539beb93cSSam Leffler roundtrip / 100, roundtrip % 100); 78639beb93cSSam Leffler rconf->round_trip_time = roundtrip; 78739beb93cSSam Leffler 78839beb93cSSam Leffler /* Remove ACKed RADIUS packet from retransmit list */ 78939beb93cSSam Leffler if (prev_req) 79039beb93cSSam Leffler prev_req->next = req->next; 79139beb93cSSam Leffler else 79239beb93cSSam Leffler radius->msgs = req->next; 79339beb93cSSam Leffler radius->num_msgs--; 79439beb93cSSam Leffler 79539beb93cSSam Leffler for (i = 0; i < num_handlers; i++) { 79639beb93cSSam Leffler RadiusRxResult res; 79739beb93cSSam Leffler res = handlers[i].handler(msg, req->msg, req->shared_secret, 79839beb93cSSam Leffler req->shared_secret_len, 79939beb93cSSam Leffler handlers[i].data); 80039beb93cSSam Leffler switch (res) { 80139beb93cSSam Leffler case RADIUS_RX_PROCESSED: 80239beb93cSSam Leffler radius_msg_free(msg); 80339beb93cSSam Leffler /* continue */ 80439beb93cSSam Leffler case RADIUS_RX_QUEUED: 80539beb93cSSam Leffler radius_client_msg_free(req); 80639beb93cSSam Leffler return; 80739beb93cSSam Leffler case RADIUS_RX_INVALID_AUTHENTICATOR: 80839beb93cSSam Leffler invalid_authenticator++; 80939beb93cSSam Leffler /* continue */ 81039beb93cSSam Leffler case RADIUS_RX_UNKNOWN: 81139beb93cSSam Leffler /* continue with next handler */ 81239beb93cSSam Leffler break; 81339beb93cSSam Leffler } 81439beb93cSSam Leffler } 81539beb93cSSam Leffler 81639beb93cSSam Leffler if (invalid_authenticator) 81739beb93cSSam Leffler rconf->bad_authenticators++; 81839beb93cSSam Leffler else 81939beb93cSSam Leffler rconf->unknown_types++; 82039beb93cSSam Leffler hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, 82139beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found " 82239beb93cSSam Leffler "(type=%d code=%d id=%d)%s - dropping packet", 823e28a4053SRui Paulo msg_type, hdr->code, hdr->identifier, 82439beb93cSSam Leffler invalid_authenticator ? " [INVALID AUTHENTICATOR]" : 82539beb93cSSam Leffler ""); 82639beb93cSSam Leffler radius_client_msg_free(req); 82739beb93cSSam Leffler 82839beb93cSSam Leffler fail: 82939beb93cSSam Leffler radius_msg_free(msg); 83039beb93cSSam Leffler } 83139beb93cSSam Leffler 83239beb93cSSam Leffler 833e28a4053SRui Paulo /** 834e28a4053SRui Paulo * radius_client_get_id - Get an identifier for a new RADIUS message 835e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 836e28a4053SRui Paulo * Returns: Allocated identifier 837e28a4053SRui Paulo * 838e28a4053SRui Paulo * This function is used to fetch a unique (among pending requests) identifier 839e28a4053SRui Paulo * for a new RADIUS message. 840e28a4053SRui Paulo */ 84139beb93cSSam Leffler u8 radius_client_get_id(struct radius_client_data *radius) 84239beb93cSSam Leffler { 84339beb93cSSam Leffler struct radius_msg_list *entry, *prev, *_remove; 84439beb93cSSam Leffler u8 id = radius->next_radius_identifier++; 84539beb93cSSam Leffler 84639beb93cSSam Leffler /* remove entries with matching id from retransmit list to avoid 84739beb93cSSam Leffler * using new reply from the RADIUS server with an old request */ 84839beb93cSSam Leffler entry = radius->msgs; 84939beb93cSSam Leffler prev = NULL; 85039beb93cSSam Leffler while (entry) { 851e28a4053SRui Paulo if (radius_msg_get_hdr(entry->msg)->identifier == id) { 85239beb93cSSam Leffler hostapd_logger(radius->ctx, entry->addr, 85339beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 85439beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 85539beb93cSSam Leffler "Removing pending RADIUS message, " 85639beb93cSSam Leffler "since its id (%d) is reused", id); 85739beb93cSSam Leffler if (prev) 85839beb93cSSam Leffler prev->next = entry->next; 85939beb93cSSam Leffler else 86039beb93cSSam Leffler radius->msgs = entry->next; 86139beb93cSSam Leffler _remove = entry; 86239beb93cSSam Leffler } else { 86339beb93cSSam Leffler _remove = NULL; 86439beb93cSSam Leffler prev = entry; 86539beb93cSSam Leffler } 86639beb93cSSam Leffler entry = entry->next; 86739beb93cSSam Leffler 86839beb93cSSam Leffler if (_remove) 86939beb93cSSam Leffler radius_client_msg_free(_remove); 87039beb93cSSam Leffler } 87139beb93cSSam Leffler 87239beb93cSSam Leffler return id; 87339beb93cSSam Leffler } 87439beb93cSSam Leffler 87539beb93cSSam Leffler 876e28a4053SRui Paulo /** 877e28a4053SRui Paulo * radius_client_flush - Flush all pending RADIUS client messages 878e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 879e28a4053SRui Paulo * @only_auth: Whether only authentication messages are removed 880e28a4053SRui Paulo */ 88139beb93cSSam Leffler void radius_client_flush(struct radius_client_data *radius, int only_auth) 88239beb93cSSam Leffler { 88339beb93cSSam Leffler struct radius_msg_list *entry, *prev, *tmp; 88439beb93cSSam Leffler 88539beb93cSSam Leffler if (!radius) 88639beb93cSSam Leffler return; 88739beb93cSSam Leffler 88839beb93cSSam Leffler prev = NULL; 88939beb93cSSam Leffler entry = radius->msgs; 89039beb93cSSam Leffler 89139beb93cSSam Leffler while (entry) { 89239beb93cSSam Leffler if (!only_auth || entry->msg_type == RADIUS_AUTH) { 89339beb93cSSam Leffler if (prev) 89439beb93cSSam Leffler prev->next = entry->next; 89539beb93cSSam Leffler else 89639beb93cSSam Leffler radius->msgs = entry->next; 89739beb93cSSam Leffler 89839beb93cSSam Leffler tmp = entry; 89939beb93cSSam Leffler entry = entry->next; 90039beb93cSSam Leffler radius_client_msg_free(tmp); 90139beb93cSSam Leffler radius->num_msgs--; 90239beb93cSSam Leffler } else { 90339beb93cSSam Leffler prev = entry; 90439beb93cSSam Leffler entry = entry->next; 90539beb93cSSam Leffler } 90639beb93cSSam Leffler } 90739beb93cSSam Leffler 90839beb93cSSam Leffler if (radius->msgs == NULL) 90939beb93cSSam Leffler eloop_cancel_timeout(radius_client_timer, radius, NULL); 91039beb93cSSam Leffler } 91139beb93cSSam Leffler 91239beb93cSSam Leffler 91339beb93cSSam Leffler static void radius_client_update_acct_msgs(struct radius_client_data *radius, 914e28a4053SRui Paulo const u8 *shared_secret, 91539beb93cSSam Leffler size_t shared_secret_len) 91639beb93cSSam Leffler { 91739beb93cSSam Leffler struct radius_msg_list *entry; 91839beb93cSSam Leffler 91939beb93cSSam Leffler if (!radius) 92039beb93cSSam Leffler return; 92139beb93cSSam Leffler 92239beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 92339beb93cSSam Leffler if (entry->msg_type == RADIUS_ACCT) { 92439beb93cSSam Leffler entry->shared_secret = shared_secret; 92539beb93cSSam Leffler entry->shared_secret_len = shared_secret_len; 92639beb93cSSam Leffler radius_msg_finish_acct(entry->msg, shared_secret, 92739beb93cSSam Leffler shared_secret_len); 92839beb93cSSam Leffler } 92939beb93cSSam Leffler } 93039beb93cSSam Leffler } 93139beb93cSSam Leffler 93239beb93cSSam Leffler 93339beb93cSSam Leffler static int 93439beb93cSSam Leffler radius_change_server(struct radius_client_data *radius, 93539beb93cSSam Leffler struct hostapd_radius_server *nserv, 93639beb93cSSam Leffler struct hostapd_radius_server *oserv, 93739beb93cSSam Leffler int sock, int sock6, int auth) 93839beb93cSSam Leffler { 93939beb93cSSam Leffler struct sockaddr_in serv, claddr; 94039beb93cSSam Leffler #ifdef CONFIG_IPV6 94139beb93cSSam Leffler struct sockaddr_in6 serv6, claddr6; 94239beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 94339beb93cSSam Leffler struct sockaddr *addr, *cl_addr; 94439beb93cSSam Leffler socklen_t addrlen, claddrlen; 94539beb93cSSam Leffler char abuf[50]; 94639beb93cSSam Leffler int sel_sock; 94739beb93cSSam Leffler struct radius_msg_list *entry; 94839beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 94939beb93cSSam Leffler 95039beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 95139beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 95239beb93cSSam Leffler "%s server %s:%d", 95339beb93cSSam Leffler auth ? "Authentication" : "Accounting", 95439beb93cSSam Leffler hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), 95539beb93cSSam Leffler nserv->port); 95639beb93cSSam Leffler 95739beb93cSSam Leffler if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len || 95839beb93cSSam Leffler os_memcmp(nserv->shared_secret, oserv->shared_secret, 95939beb93cSSam Leffler nserv->shared_secret_len) != 0) { 96039beb93cSSam Leffler /* Pending RADIUS packets used different shared secret, so 96139beb93cSSam Leffler * they need to be modified. Update accounting message 96239beb93cSSam Leffler * authenticators here. Authentication messages are removed 96339beb93cSSam Leffler * since they would require more changes and the new RADIUS 96439beb93cSSam Leffler * server may not be prepared to receive them anyway due to 96539beb93cSSam Leffler * missing state information. Client will likely retry 96639beb93cSSam Leffler * authentication, so this should not be an issue. */ 96739beb93cSSam Leffler if (auth) 96839beb93cSSam Leffler radius_client_flush(radius, 1); 96939beb93cSSam Leffler else { 97039beb93cSSam Leffler radius_client_update_acct_msgs( 97139beb93cSSam Leffler radius, nserv->shared_secret, 97239beb93cSSam Leffler nserv->shared_secret_len); 97339beb93cSSam Leffler } 97439beb93cSSam Leffler } 97539beb93cSSam Leffler 97639beb93cSSam Leffler /* Reset retry counters for the new server */ 97739beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 97839beb93cSSam Leffler if ((auth && entry->msg_type != RADIUS_AUTH) || 97939beb93cSSam Leffler (!auth && entry->msg_type != RADIUS_ACCT)) 98039beb93cSSam Leffler continue; 98139beb93cSSam Leffler entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; 98239beb93cSSam Leffler entry->attempts = 0; 98339beb93cSSam Leffler entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; 98439beb93cSSam Leffler } 98539beb93cSSam Leffler 98639beb93cSSam Leffler if (radius->msgs) { 98739beb93cSSam Leffler eloop_cancel_timeout(radius_client_timer, radius, NULL); 98839beb93cSSam Leffler eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, 98939beb93cSSam Leffler radius_client_timer, radius, NULL); 99039beb93cSSam Leffler } 99139beb93cSSam Leffler 99239beb93cSSam Leffler switch (nserv->addr.af) { 99339beb93cSSam Leffler case AF_INET: 99439beb93cSSam Leffler os_memset(&serv, 0, sizeof(serv)); 99539beb93cSSam Leffler serv.sin_family = AF_INET; 99639beb93cSSam Leffler serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr; 99739beb93cSSam Leffler serv.sin_port = htons(nserv->port); 99839beb93cSSam Leffler addr = (struct sockaddr *) &serv; 99939beb93cSSam Leffler addrlen = sizeof(serv); 100039beb93cSSam Leffler sel_sock = sock; 100139beb93cSSam Leffler break; 100239beb93cSSam Leffler #ifdef CONFIG_IPV6 100339beb93cSSam Leffler case AF_INET6: 100439beb93cSSam Leffler os_memset(&serv6, 0, sizeof(serv6)); 100539beb93cSSam Leffler serv6.sin6_family = AF_INET6; 100639beb93cSSam Leffler os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6, 100739beb93cSSam Leffler sizeof(struct in6_addr)); 100839beb93cSSam Leffler serv6.sin6_port = htons(nserv->port); 100939beb93cSSam Leffler addr = (struct sockaddr *) &serv6; 101039beb93cSSam Leffler addrlen = sizeof(serv6); 101139beb93cSSam Leffler sel_sock = sock6; 101239beb93cSSam Leffler break; 101339beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 101439beb93cSSam Leffler default: 101539beb93cSSam Leffler return -1; 101639beb93cSSam Leffler } 101739beb93cSSam Leffler 101839beb93cSSam Leffler if (conf->force_client_addr) { 101939beb93cSSam Leffler switch (conf->client_addr.af) { 102039beb93cSSam Leffler case AF_INET: 102139beb93cSSam Leffler os_memset(&claddr, 0, sizeof(claddr)); 102239beb93cSSam Leffler claddr.sin_family = AF_INET; 102339beb93cSSam Leffler claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr; 102439beb93cSSam Leffler claddr.sin_port = htons(0); 102539beb93cSSam Leffler cl_addr = (struct sockaddr *) &claddr; 102639beb93cSSam Leffler claddrlen = sizeof(claddr); 102739beb93cSSam Leffler break; 102839beb93cSSam Leffler #ifdef CONFIG_IPV6 102939beb93cSSam Leffler case AF_INET6: 103039beb93cSSam Leffler os_memset(&claddr6, 0, sizeof(claddr6)); 103139beb93cSSam Leffler claddr6.sin6_family = AF_INET6; 103239beb93cSSam Leffler os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6, 103339beb93cSSam Leffler sizeof(struct in6_addr)); 103439beb93cSSam Leffler claddr6.sin6_port = htons(0); 103539beb93cSSam Leffler cl_addr = (struct sockaddr *) &claddr6; 103639beb93cSSam Leffler claddrlen = sizeof(claddr6); 103739beb93cSSam Leffler break; 103839beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 103939beb93cSSam Leffler default: 104039beb93cSSam Leffler return -1; 104139beb93cSSam Leffler } 104239beb93cSSam Leffler 104339beb93cSSam Leffler if (bind(sel_sock, cl_addr, claddrlen) < 0) { 104439beb93cSSam Leffler perror("bind[radius]"); 104539beb93cSSam Leffler return -1; 104639beb93cSSam Leffler } 104739beb93cSSam Leffler } 104839beb93cSSam Leffler 104939beb93cSSam Leffler if (connect(sel_sock, addr, addrlen) < 0) { 105039beb93cSSam Leffler perror("connect[radius]"); 105139beb93cSSam Leffler return -1; 105239beb93cSSam Leffler } 105339beb93cSSam Leffler 105439beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS 105539beb93cSSam Leffler switch (nserv->addr.af) { 105639beb93cSSam Leffler case AF_INET: 105739beb93cSSam Leffler claddrlen = sizeof(claddr); 105839beb93cSSam Leffler getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen); 105939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", 106039beb93cSSam Leffler inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port)); 106139beb93cSSam Leffler break; 106239beb93cSSam Leffler #ifdef CONFIG_IPV6 106339beb93cSSam Leffler case AF_INET6: { 106439beb93cSSam Leffler claddrlen = sizeof(claddr6); 106539beb93cSSam Leffler getsockname(sel_sock, (struct sockaddr *) &claddr6, 106639beb93cSSam Leffler &claddrlen); 106739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", 106839beb93cSSam Leffler inet_ntop(AF_INET6, &claddr6.sin6_addr, 106939beb93cSSam Leffler abuf, sizeof(abuf)), 107039beb93cSSam Leffler ntohs(claddr6.sin6_port)); 107139beb93cSSam Leffler break; 107239beb93cSSam Leffler } 107339beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 107439beb93cSSam Leffler } 107539beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */ 107639beb93cSSam Leffler 107739beb93cSSam Leffler if (auth) 107839beb93cSSam Leffler radius->auth_sock = sel_sock; 107939beb93cSSam Leffler else 108039beb93cSSam Leffler radius->acct_sock = sel_sock; 108139beb93cSSam Leffler 108239beb93cSSam Leffler return 0; 108339beb93cSSam Leffler } 108439beb93cSSam Leffler 108539beb93cSSam Leffler 108639beb93cSSam Leffler static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) 108739beb93cSSam Leffler { 108839beb93cSSam Leffler struct radius_client_data *radius = eloop_ctx; 108939beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 109039beb93cSSam Leffler struct hostapd_radius_server *oserv; 109139beb93cSSam Leffler 109239beb93cSSam Leffler if (radius->auth_sock >= 0 && conf->auth_servers && 109339beb93cSSam Leffler conf->auth_server != conf->auth_servers) { 109439beb93cSSam Leffler oserv = conf->auth_server; 109539beb93cSSam Leffler conf->auth_server = conf->auth_servers; 109639beb93cSSam Leffler radius_change_server(radius, conf->auth_server, oserv, 109739beb93cSSam Leffler radius->auth_serv_sock, 109839beb93cSSam Leffler radius->auth_serv_sock6, 1); 109939beb93cSSam Leffler } 110039beb93cSSam Leffler 110139beb93cSSam Leffler if (radius->acct_sock >= 0 && conf->acct_servers && 110239beb93cSSam Leffler conf->acct_server != conf->acct_servers) { 110339beb93cSSam Leffler oserv = conf->acct_server; 110439beb93cSSam Leffler conf->acct_server = conf->acct_servers; 110539beb93cSSam Leffler radius_change_server(radius, conf->acct_server, oserv, 110639beb93cSSam Leffler radius->acct_serv_sock, 110739beb93cSSam Leffler radius->acct_serv_sock6, 0); 110839beb93cSSam Leffler } 110939beb93cSSam Leffler 111039beb93cSSam Leffler if (conf->retry_primary_interval) 111139beb93cSSam Leffler eloop_register_timeout(conf->retry_primary_interval, 0, 111239beb93cSSam Leffler radius_retry_primary_timer, radius, 111339beb93cSSam Leffler NULL); 111439beb93cSSam Leffler } 111539beb93cSSam Leffler 111639beb93cSSam Leffler 11173157ba21SRui Paulo static int radius_client_disable_pmtu_discovery(int s) 11183157ba21SRui Paulo { 11193157ba21SRui Paulo int r = -1; 11203157ba21SRui Paulo #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) 11213157ba21SRui Paulo /* Turn off Path MTU discovery on IPv4/UDP sockets. */ 11223157ba21SRui Paulo int action = IP_PMTUDISC_DONT; 11233157ba21SRui Paulo r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, 11243157ba21SRui Paulo sizeof(action)); 11253157ba21SRui Paulo if (r == -1) 11263157ba21SRui Paulo wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: " 11273157ba21SRui Paulo "%s", strerror(errno)); 11283157ba21SRui Paulo #endif 11293157ba21SRui Paulo return r; 11303157ba21SRui Paulo } 11313157ba21SRui Paulo 11323157ba21SRui Paulo 113339beb93cSSam Leffler static int radius_client_init_auth(struct radius_client_data *radius) 113439beb93cSSam Leffler { 113539beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 113639beb93cSSam Leffler int ok = 0; 113739beb93cSSam Leffler 113839beb93cSSam Leffler radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); 113939beb93cSSam Leffler if (radius->auth_serv_sock < 0) 114039beb93cSSam Leffler perror("socket[PF_INET,SOCK_DGRAM]"); 11413157ba21SRui Paulo else { 11423157ba21SRui Paulo radius_client_disable_pmtu_discovery(radius->auth_serv_sock); 114339beb93cSSam Leffler ok++; 11443157ba21SRui Paulo } 114539beb93cSSam Leffler 114639beb93cSSam Leffler #ifdef CONFIG_IPV6 114739beb93cSSam Leffler radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); 114839beb93cSSam Leffler if (radius->auth_serv_sock6 < 0) 114939beb93cSSam Leffler perror("socket[PF_INET6,SOCK_DGRAM]"); 115039beb93cSSam Leffler else 115139beb93cSSam Leffler ok++; 115239beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 115339beb93cSSam Leffler 115439beb93cSSam Leffler if (ok == 0) 115539beb93cSSam Leffler return -1; 115639beb93cSSam Leffler 115739beb93cSSam Leffler radius_change_server(radius, conf->auth_server, NULL, 115839beb93cSSam Leffler radius->auth_serv_sock, radius->auth_serv_sock6, 115939beb93cSSam Leffler 1); 116039beb93cSSam Leffler 116139beb93cSSam Leffler if (radius->auth_serv_sock >= 0 && 116239beb93cSSam Leffler eloop_register_read_sock(radius->auth_serv_sock, 116339beb93cSSam Leffler radius_client_receive, radius, 116439beb93cSSam Leffler (void *) RADIUS_AUTH)) { 116539beb93cSSam Leffler printf("Could not register read socket for authentication " 116639beb93cSSam Leffler "server\n"); 116739beb93cSSam Leffler return -1; 116839beb93cSSam Leffler } 116939beb93cSSam Leffler 117039beb93cSSam Leffler #ifdef CONFIG_IPV6 117139beb93cSSam Leffler if (radius->auth_serv_sock6 >= 0 && 117239beb93cSSam Leffler eloop_register_read_sock(radius->auth_serv_sock6, 117339beb93cSSam Leffler radius_client_receive, radius, 117439beb93cSSam Leffler (void *) RADIUS_AUTH)) { 117539beb93cSSam Leffler printf("Could not register read socket for authentication " 117639beb93cSSam Leffler "server\n"); 117739beb93cSSam Leffler return -1; 117839beb93cSSam Leffler } 117939beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 118039beb93cSSam Leffler 118139beb93cSSam Leffler return 0; 118239beb93cSSam Leffler } 118339beb93cSSam Leffler 118439beb93cSSam Leffler 118539beb93cSSam Leffler static int radius_client_init_acct(struct radius_client_data *radius) 118639beb93cSSam Leffler { 118739beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 118839beb93cSSam Leffler int ok = 0; 118939beb93cSSam Leffler 119039beb93cSSam Leffler radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); 119139beb93cSSam Leffler if (radius->acct_serv_sock < 0) 119239beb93cSSam Leffler perror("socket[PF_INET,SOCK_DGRAM]"); 11933157ba21SRui Paulo else { 11943157ba21SRui Paulo radius_client_disable_pmtu_discovery(radius->acct_serv_sock); 119539beb93cSSam Leffler ok++; 11963157ba21SRui Paulo } 119739beb93cSSam Leffler 119839beb93cSSam Leffler #ifdef CONFIG_IPV6 119939beb93cSSam Leffler radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); 120039beb93cSSam Leffler if (radius->acct_serv_sock6 < 0) 120139beb93cSSam Leffler perror("socket[PF_INET6,SOCK_DGRAM]"); 120239beb93cSSam Leffler else 120339beb93cSSam Leffler ok++; 120439beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 120539beb93cSSam Leffler 120639beb93cSSam Leffler if (ok == 0) 120739beb93cSSam Leffler return -1; 120839beb93cSSam Leffler 120939beb93cSSam Leffler radius_change_server(radius, conf->acct_server, NULL, 121039beb93cSSam Leffler radius->acct_serv_sock, radius->acct_serv_sock6, 121139beb93cSSam Leffler 0); 121239beb93cSSam Leffler 121339beb93cSSam Leffler if (radius->acct_serv_sock >= 0 && 121439beb93cSSam Leffler eloop_register_read_sock(radius->acct_serv_sock, 121539beb93cSSam Leffler radius_client_receive, radius, 121639beb93cSSam Leffler (void *) RADIUS_ACCT)) { 121739beb93cSSam Leffler printf("Could not register read socket for accounting " 121839beb93cSSam Leffler "server\n"); 121939beb93cSSam Leffler return -1; 122039beb93cSSam Leffler } 122139beb93cSSam Leffler 122239beb93cSSam Leffler #ifdef CONFIG_IPV6 122339beb93cSSam Leffler if (radius->acct_serv_sock6 >= 0 && 122439beb93cSSam Leffler eloop_register_read_sock(radius->acct_serv_sock6, 122539beb93cSSam Leffler radius_client_receive, radius, 122639beb93cSSam Leffler (void *) RADIUS_ACCT)) { 122739beb93cSSam Leffler printf("Could not register read socket for accounting " 122839beb93cSSam Leffler "server\n"); 122939beb93cSSam Leffler return -1; 123039beb93cSSam Leffler } 123139beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 123239beb93cSSam Leffler 123339beb93cSSam Leffler return 0; 123439beb93cSSam Leffler } 123539beb93cSSam Leffler 123639beb93cSSam Leffler 1237e28a4053SRui Paulo /** 1238e28a4053SRui Paulo * radius_client_init - Initialize RADIUS client 1239e28a4053SRui Paulo * @ctx: Callback context to be used in hostapd_logger() calls 1240e28a4053SRui Paulo * @conf: RADIUS client configuration (RADIUS servers) 1241e28a4053SRui Paulo * Returns: Pointer to private RADIUS client context or %NULL on failure 1242e28a4053SRui Paulo * 1243e28a4053SRui Paulo * The caller is responsible for keeping the configuration data available for 1244e28a4053SRui Paulo * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is 1245e28a4053SRui Paulo * called for the returned context pointer. 1246e28a4053SRui Paulo */ 124739beb93cSSam Leffler struct radius_client_data * 124839beb93cSSam Leffler radius_client_init(void *ctx, struct hostapd_radius_servers *conf) 124939beb93cSSam Leffler { 125039beb93cSSam Leffler struct radius_client_data *radius; 125139beb93cSSam Leffler 125239beb93cSSam Leffler radius = os_zalloc(sizeof(struct radius_client_data)); 125339beb93cSSam Leffler if (radius == NULL) 125439beb93cSSam Leffler return NULL; 125539beb93cSSam Leffler 125639beb93cSSam Leffler radius->ctx = ctx; 125739beb93cSSam Leffler radius->conf = conf; 125839beb93cSSam Leffler radius->auth_serv_sock = radius->acct_serv_sock = 125939beb93cSSam Leffler radius->auth_serv_sock6 = radius->acct_serv_sock6 = 126039beb93cSSam Leffler radius->auth_sock = radius->acct_sock = -1; 126139beb93cSSam Leffler 126239beb93cSSam Leffler if (conf->auth_server && radius_client_init_auth(radius)) { 126339beb93cSSam Leffler radius_client_deinit(radius); 126439beb93cSSam Leffler return NULL; 126539beb93cSSam Leffler } 126639beb93cSSam Leffler 126739beb93cSSam Leffler if (conf->acct_server && radius_client_init_acct(radius)) { 126839beb93cSSam Leffler radius_client_deinit(radius); 126939beb93cSSam Leffler return NULL; 127039beb93cSSam Leffler } 127139beb93cSSam Leffler 127239beb93cSSam Leffler if (conf->retry_primary_interval) 127339beb93cSSam Leffler eloop_register_timeout(conf->retry_primary_interval, 0, 127439beb93cSSam Leffler radius_retry_primary_timer, radius, 127539beb93cSSam Leffler NULL); 127639beb93cSSam Leffler 127739beb93cSSam Leffler return radius; 127839beb93cSSam Leffler } 127939beb93cSSam Leffler 128039beb93cSSam Leffler 1281e28a4053SRui Paulo /** 1282e28a4053SRui Paulo * radius_client_deinit - Deinitialize RADIUS client 1283e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 1284e28a4053SRui Paulo */ 128539beb93cSSam Leffler void radius_client_deinit(struct radius_client_data *radius) 128639beb93cSSam Leffler { 128739beb93cSSam Leffler if (!radius) 128839beb93cSSam Leffler return; 128939beb93cSSam Leffler 129039beb93cSSam Leffler if (radius->auth_serv_sock >= 0) 129139beb93cSSam Leffler eloop_unregister_read_sock(radius->auth_serv_sock); 129239beb93cSSam Leffler if (radius->acct_serv_sock >= 0) 129339beb93cSSam Leffler eloop_unregister_read_sock(radius->acct_serv_sock); 12943157ba21SRui Paulo #ifdef CONFIG_IPV6 12953157ba21SRui Paulo if (radius->auth_serv_sock6 >= 0) 12963157ba21SRui Paulo eloop_unregister_read_sock(radius->auth_serv_sock6); 12973157ba21SRui Paulo if (radius->acct_serv_sock6 >= 0) 12983157ba21SRui Paulo eloop_unregister_read_sock(radius->acct_serv_sock6); 12993157ba21SRui Paulo #endif /* CONFIG_IPV6 */ 130039beb93cSSam Leffler 130139beb93cSSam Leffler eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL); 130239beb93cSSam Leffler 130339beb93cSSam Leffler radius_client_flush(radius, 0); 130439beb93cSSam Leffler os_free(radius->auth_handlers); 130539beb93cSSam Leffler os_free(radius->acct_handlers); 130639beb93cSSam Leffler os_free(radius); 130739beb93cSSam Leffler } 130839beb93cSSam Leffler 130939beb93cSSam Leffler 1310e28a4053SRui Paulo /** 1311e28a4053SRui Paulo * radius_client_flush_auth - Flush pending RADIUS messages for an address 1312e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 1313e28a4053SRui Paulo * @addr: MAC address of the related device 1314e28a4053SRui Paulo * 1315e28a4053SRui Paulo * This function can be used to remove pending RADIUS authentication messages 1316e28a4053SRui Paulo * that are related to a specific device. The addr parameter is matched with 1317e28a4053SRui Paulo * the one used in radius_client_send() call that was used to transmit the 1318e28a4053SRui Paulo * authentication request. 1319e28a4053SRui Paulo */ 1320e28a4053SRui Paulo void radius_client_flush_auth(struct radius_client_data *radius, 1321e28a4053SRui Paulo const u8 *addr) 132239beb93cSSam Leffler { 132339beb93cSSam Leffler struct radius_msg_list *entry, *prev, *tmp; 132439beb93cSSam Leffler 132539beb93cSSam Leffler prev = NULL; 132639beb93cSSam Leffler entry = radius->msgs; 132739beb93cSSam Leffler while (entry) { 132839beb93cSSam Leffler if (entry->msg_type == RADIUS_AUTH && 132939beb93cSSam Leffler os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { 133039beb93cSSam Leffler hostapd_logger(radius->ctx, addr, 133139beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 133239beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 133339beb93cSSam Leffler "Removing pending RADIUS authentication" 133439beb93cSSam Leffler " message for removed client"); 133539beb93cSSam Leffler 133639beb93cSSam Leffler if (prev) 133739beb93cSSam Leffler prev->next = entry->next; 133839beb93cSSam Leffler else 133939beb93cSSam Leffler radius->msgs = entry->next; 134039beb93cSSam Leffler 134139beb93cSSam Leffler tmp = entry; 134239beb93cSSam Leffler entry = entry->next; 134339beb93cSSam Leffler radius_client_msg_free(tmp); 134439beb93cSSam Leffler radius->num_msgs--; 134539beb93cSSam Leffler continue; 134639beb93cSSam Leffler } 134739beb93cSSam Leffler 134839beb93cSSam Leffler prev = entry; 134939beb93cSSam Leffler entry = entry->next; 135039beb93cSSam Leffler } 135139beb93cSSam Leffler } 135239beb93cSSam Leffler 135339beb93cSSam Leffler 135439beb93cSSam Leffler static int radius_client_dump_auth_server(char *buf, size_t buflen, 135539beb93cSSam Leffler struct hostapd_radius_server *serv, 135639beb93cSSam Leffler struct radius_client_data *cli) 135739beb93cSSam Leffler { 135839beb93cSSam Leffler int pending = 0; 135939beb93cSSam Leffler struct radius_msg_list *msg; 136039beb93cSSam Leffler char abuf[50]; 136139beb93cSSam Leffler 136239beb93cSSam Leffler if (cli) { 136339beb93cSSam Leffler for (msg = cli->msgs; msg; msg = msg->next) { 136439beb93cSSam Leffler if (msg->msg_type == RADIUS_AUTH) 136539beb93cSSam Leffler pending++; 136639beb93cSSam Leffler } 136739beb93cSSam Leffler } 136839beb93cSSam Leffler 136939beb93cSSam Leffler return os_snprintf(buf, buflen, 137039beb93cSSam Leffler "radiusAuthServerIndex=%d\n" 137139beb93cSSam Leffler "radiusAuthServerAddress=%s\n" 137239beb93cSSam Leffler "radiusAuthClientServerPortNumber=%d\n" 137339beb93cSSam Leffler "radiusAuthClientRoundTripTime=%d\n" 137439beb93cSSam Leffler "radiusAuthClientAccessRequests=%u\n" 137539beb93cSSam Leffler "radiusAuthClientAccessRetransmissions=%u\n" 137639beb93cSSam Leffler "radiusAuthClientAccessAccepts=%u\n" 137739beb93cSSam Leffler "radiusAuthClientAccessRejects=%u\n" 137839beb93cSSam Leffler "radiusAuthClientAccessChallenges=%u\n" 137939beb93cSSam Leffler "radiusAuthClientMalformedAccessResponses=%u\n" 138039beb93cSSam Leffler "radiusAuthClientBadAuthenticators=%u\n" 138139beb93cSSam Leffler "radiusAuthClientPendingRequests=%u\n" 138239beb93cSSam Leffler "radiusAuthClientTimeouts=%u\n" 138339beb93cSSam Leffler "radiusAuthClientUnknownTypes=%u\n" 138439beb93cSSam Leffler "radiusAuthClientPacketsDropped=%u\n", 138539beb93cSSam Leffler serv->index, 138639beb93cSSam Leffler hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), 138739beb93cSSam Leffler serv->port, 138839beb93cSSam Leffler serv->round_trip_time, 138939beb93cSSam Leffler serv->requests, 139039beb93cSSam Leffler serv->retransmissions, 139139beb93cSSam Leffler serv->access_accepts, 139239beb93cSSam Leffler serv->access_rejects, 139339beb93cSSam Leffler serv->access_challenges, 139439beb93cSSam Leffler serv->malformed_responses, 139539beb93cSSam Leffler serv->bad_authenticators, 139639beb93cSSam Leffler pending, 139739beb93cSSam Leffler serv->timeouts, 139839beb93cSSam Leffler serv->unknown_types, 139939beb93cSSam Leffler serv->packets_dropped); 140039beb93cSSam Leffler } 140139beb93cSSam Leffler 140239beb93cSSam Leffler 140339beb93cSSam Leffler static int radius_client_dump_acct_server(char *buf, size_t buflen, 140439beb93cSSam Leffler struct hostapd_radius_server *serv, 140539beb93cSSam Leffler struct radius_client_data *cli) 140639beb93cSSam Leffler { 140739beb93cSSam Leffler int pending = 0; 140839beb93cSSam Leffler struct radius_msg_list *msg; 140939beb93cSSam Leffler char abuf[50]; 141039beb93cSSam Leffler 141139beb93cSSam Leffler if (cli) { 141239beb93cSSam Leffler for (msg = cli->msgs; msg; msg = msg->next) { 141339beb93cSSam Leffler if (msg->msg_type == RADIUS_ACCT || 141439beb93cSSam Leffler msg->msg_type == RADIUS_ACCT_INTERIM) 141539beb93cSSam Leffler pending++; 141639beb93cSSam Leffler } 141739beb93cSSam Leffler } 141839beb93cSSam Leffler 141939beb93cSSam Leffler return os_snprintf(buf, buflen, 142039beb93cSSam Leffler "radiusAccServerIndex=%d\n" 142139beb93cSSam Leffler "radiusAccServerAddress=%s\n" 142239beb93cSSam Leffler "radiusAccClientServerPortNumber=%d\n" 142339beb93cSSam Leffler "radiusAccClientRoundTripTime=%d\n" 142439beb93cSSam Leffler "radiusAccClientRequests=%u\n" 142539beb93cSSam Leffler "radiusAccClientRetransmissions=%u\n" 142639beb93cSSam Leffler "radiusAccClientResponses=%u\n" 142739beb93cSSam Leffler "radiusAccClientMalformedResponses=%u\n" 142839beb93cSSam Leffler "radiusAccClientBadAuthenticators=%u\n" 142939beb93cSSam Leffler "radiusAccClientPendingRequests=%u\n" 143039beb93cSSam Leffler "radiusAccClientTimeouts=%u\n" 143139beb93cSSam Leffler "radiusAccClientUnknownTypes=%u\n" 143239beb93cSSam Leffler "radiusAccClientPacketsDropped=%u\n", 143339beb93cSSam Leffler serv->index, 143439beb93cSSam Leffler hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), 143539beb93cSSam Leffler serv->port, 143639beb93cSSam Leffler serv->round_trip_time, 143739beb93cSSam Leffler serv->requests, 143839beb93cSSam Leffler serv->retransmissions, 143939beb93cSSam Leffler serv->responses, 144039beb93cSSam Leffler serv->malformed_responses, 144139beb93cSSam Leffler serv->bad_authenticators, 144239beb93cSSam Leffler pending, 144339beb93cSSam Leffler serv->timeouts, 144439beb93cSSam Leffler serv->unknown_types, 144539beb93cSSam Leffler serv->packets_dropped); 144639beb93cSSam Leffler } 144739beb93cSSam Leffler 144839beb93cSSam Leffler 1449e28a4053SRui Paulo /** 1450e28a4053SRui Paulo * radius_client_get_mib - Get RADIUS client MIB information 1451e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 1452e28a4053SRui Paulo * @buf: Buffer for returning MIB data in text format 1453e28a4053SRui Paulo * @buflen: Maximum buf length in octets 1454e28a4053SRui Paulo * Returns: Number of octets written into the buffer 1455e28a4053SRui Paulo */ 145639beb93cSSam Leffler int radius_client_get_mib(struct radius_client_data *radius, char *buf, 145739beb93cSSam Leffler size_t buflen) 145839beb93cSSam Leffler { 145939beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 146039beb93cSSam Leffler int i; 146139beb93cSSam Leffler struct hostapd_radius_server *serv; 146239beb93cSSam Leffler int count = 0; 146339beb93cSSam Leffler 146439beb93cSSam Leffler if (conf->auth_servers) { 146539beb93cSSam Leffler for (i = 0; i < conf->num_auth_servers; i++) { 146639beb93cSSam Leffler serv = &conf->auth_servers[i]; 146739beb93cSSam Leffler count += radius_client_dump_auth_server( 146839beb93cSSam Leffler buf + count, buflen - count, serv, 146939beb93cSSam Leffler serv == conf->auth_server ? 147039beb93cSSam Leffler radius : NULL); 147139beb93cSSam Leffler } 147239beb93cSSam Leffler } 147339beb93cSSam Leffler 147439beb93cSSam Leffler if (conf->acct_servers) { 147539beb93cSSam Leffler for (i = 0; i < conf->num_acct_servers; i++) { 147639beb93cSSam Leffler serv = &conf->acct_servers[i]; 147739beb93cSSam Leffler count += radius_client_dump_acct_server( 147839beb93cSSam Leffler buf + count, buflen - count, serv, 147939beb93cSSam Leffler serv == conf->acct_server ? 148039beb93cSSam Leffler radius : NULL); 148139beb93cSSam Leffler } 148239beb93cSSam Leffler } 148339beb93cSSam Leffler 148439beb93cSSam Leffler return count; 148539beb93cSSam Leffler } 1486*f05cddf9SRui Paulo 1487*f05cddf9SRui Paulo 1488*f05cddf9SRui Paulo void radius_client_reconfig(struct radius_client_data *radius, 1489*f05cddf9SRui Paulo struct hostapd_radius_servers *conf) 1490*f05cddf9SRui Paulo { 1491*f05cddf9SRui Paulo if (radius) 1492*f05cddf9SRui Paulo radius->conf = conf; 1493*f05cddf9SRui Paulo } 1494