139beb93cSSam Leffler /* 2*e28a4053SRui Paulo * RADIUS client 3*e28a4053SRui Paulo * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> 439beb93cSSam Leffler * 539beb93cSSam Leffler * This program is free software; you can redistribute it and/or modify 639beb93cSSam Leffler * it under the terms of the GNU General Public License version 2 as 739beb93cSSam Leffler * published by the Free Software Foundation. 839beb93cSSam Leffler * 939beb93cSSam Leffler * Alternatively, this software may be distributed under the terms of BSD 1039beb93cSSam Leffler * license. 1139beb93cSSam Leffler * 1239beb93cSSam Leffler * See README and COPYING for more details. 1339beb93cSSam Leffler */ 1439beb93cSSam Leffler 1539beb93cSSam Leffler #include "includes.h" 1639beb93cSSam Leffler 1739beb93cSSam Leffler #include "common.h" 1839beb93cSSam Leffler #include "radius.h" 1939beb93cSSam Leffler #include "radius_client.h" 2039beb93cSSam Leffler #include "eloop.h" 2139beb93cSSam Leffler 2239beb93cSSam Leffler /* Defaults for RADIUS retransmit values (exponential backoff) */ 23*e28a4053SRui Paulo 24*e28a4053SRui Paulo /** 25*e28a4053SRui Paulo * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds 26*e28a4053SRui Paulo */ 27*e28a4053SRui Paulo #define RADIUS_CLIENT_FIRST_WAIT 3 28*e28a4053SRui Paulo 29*e28a4053SRui Paulo /** 30*e28a4053SRui Paulo * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds 31*e28a4053SRui Paulo */ 32*e28a4053SRui Paulo #define RADIUS_CLIENT_MAX_WAIT 120 33*e28a4053SRui Paulo 34*e28a4053SRui Paulo /** 35*e28a4053SRui Paulo * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries 36*e28a4053SRui Paulo * 37*e28a4053SRui Paulo * Maximum number of retransmit attempts before the entry is removed from 38*e28a4053SRui Paulo * retransmit list. 39*e28a4053SRui Paulo */ 40*e28a4053SRui Paulo #define RADIUS_CLIENT_MAX_RETRIES 10 41*e28a4053SRui Paulo 42*e28a4053SRui Paulo /** 43*e28a4053SRui Paulo * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages 44*e28a4053SRui Paulo * 45*e28a4053SRui Paulo * Maximum number of entries in retransmit list (oldest entries will be 46*e28a4053SRui Paulo * removed, if this limit is exceeded). 47*e28a4053SRui Paulo */ 48*e28a4053SRui Paulo #define RADIUS_CLIENT_MAX_ENTRIES 30 49*e28a4053SRui Paulo 50*e28a4053SRui Paulo /** 51*e28a4053SRui Paulo * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point 52*e28a4053SRui Paulo * 53*e28a4053SRui Paulo * The number of failed retry attempts after which the RADIUS server will be 54*e28a4053SRui Paulo * changed (if one of more backup servers are configured). 55*e28a4053SRui Paulo */ 56*e28a4053SRui Paulo #define RADIUS_CLIENT_NUM_FAILOVER 4 5739beb93cSSam Leffler 5839beb93cSSam Leffler 59*e28a4053SRui Paulo /** 60*e28a4053SRui Paulo * struct radius_rx_handler - RADIUS client RX handler 61*e28a4053SRui Paulo * 62*e28a4053SRui Paulo * This data structure is used internally inside the RADIUS client module to 63*e28a4053SRui Paulo * store registered RX handlers. These handlers are registered by calls to 64*e28a4053SRui Paulo * radius_client_register() and unregistered when the RADIUS client is 65*e28a4053SRui Paulo * deinitialized with a call to radius_client_deinit(). 66*e28a4053SRui Paulo */ 6739beb93cSSam Leffler struct radius_rx_handler { 68*e28a4053SRui Paulo /** 69*e28a4053SRui Paulo * handler - Received RADIUS message handler 70*e28a4053SRui Paulo */ 7139beb93cSSam Leffler RadiusRxResult (*handler)(struct radius_msg *msg, 7239beb93cSSam Leffler struct radius_msg *req, 7339beb93cSSam Leffler const u8 *shared_secret, 7439beb93cSSam Leffler size_t shared_secret_len, 7539beb93cSSam Leffler void *data); 76*e28a4053SRui Paulo 77*e28a4053SRui Paulo /** 78*e28a4053SRui Paulo * data - Context data for the handler 79*e28a4053SRui Paulo */ 8039beb93cSSam Leffler void *data; 8139beb93cSSam Leffler }; 8239beb93cSSam Leffler 8339beb93cSSam Leffler 84*e28a4053SRui Paulo /** 85*e28a4053SRui Paulo * struct radius_msg_list - RADIUS client message retransmit list 86*e28a4053SRui Paulo * 87*e28a4053SRui Paulo * This data structure is used internally inside the RADIUS client module to 88*e28a4053SRui Paulo * store pending RADIUS requests that may still need to be retransmitted. 89*e28a4053SRui Paulo */ 9039beb93cSSam Leffler struct radius_msg_list { 91*e28a4053SRui Paulo /** 92*e28a4053SRui Paulo * addr - STA/client address 93*e28a4053SRui Paulo * 94*e28a4053SRui Paulo * This is used to find RADIUS messages for the same STA. 95*e28a4053SRui Paulo */ 96*e28a4053SRui Paulo u8 addr[ETH_ALEN]; 97*e28a4053SRui Paulo 98*e28a4053SRui Paulo /** 99*e28a4053SRui Paulo * msg - RADIUS message 100*e28a4053SRui Paulo */ 10139beb93cSSam Leffler struct radius_msg *msg; 102*e28a4053SRui Paulo 103*e28a4053SRui Paulo /** 104*e28a4053SRui Paulo * msg_type - Message type 105*e28a4053SRui Paulo */ 10639beb93cSSam Leffler RadiusType msg_type; 107*e28a4053SRui Paulo 108*e28a4053SRui Paulo /** 109*e28a4053SRui Paulo * first_try - Time of the first transmission attempt 110*e28a4053SRui Paulo */ 11139beb93cSSam Leffler os_time_t first_try; 112*e28a4053SRui Paulo 113*e28a4053SRui Paulo /** 114*e28a4053SRui Paulo * next_try - Time for the next transmission attempt 115*e28a4053SRui Paulo */ 11639beb93cSSam Leffler os_time_t next_try; 117*e28a4053SRui Paulo 118*e28a4053SRui Paulo /** 119*e28a4053SRui Paulo * attempts - Number of transmission attempts 120*e28a4053SRui Paulo */ 12139beb93cSSam Leffler int attempts; 122*e28a4053SRui Paulo 123*e28a4053SRui Paulo /** 124*e28a4053SRui Paulo * next_wait - Next retransmission wait time in seconds 125*e28a4053SRui Paulo */ 12639beb93cSSam Leffler int next_wait; 127*e28a4053SRui Paulo 128*e28a4053SRui Paulo /** 129*e28a4053SRui Paulo * last_attempt - Time of the last transmission attempt 130*e28a4053SRui Paulo */ 13139beb93cSSam Leffler struct os_time last_attempt; 13239beb93cSSam Leffler 133*e28a4053SRui Paulo /** 134*e28a4053SRui Paulo * shared_secret - Shared secret with the target RADIUS server 135*e28a4053SRui Paulo */ 136*e28a4053SRui Paulo const u8 *shared_secret; 137*e28a4053SRui Paulo 138*e28a4053SRui Paulo /** 139*e28a4053SRui Paulo * shared_secret_len - shared_secret length in octets 140*e28a4053SRui Paulo */ 14139beb93cSSam Leffler size_t shared_secret_len; 14239beb93cSSam Leffler 14339beb93cSSam Leffler /* TODO: server config with failover to backup server(s) */ 14439beb93cSSam Leffler 145*e28a4053SRui Paulo /** 146*e28a4053SRui Paulo * next - Next message in the list 147*e28a4053SRui Paulo */ 14839beb93cSSam Leffler struct radius_msg_list *next; 14939beb93cSSam Leffler }; 15039beb93cSSam Leffler 15139beb93cSSam Leffler 152*e28a4053SRui Paulo /** 153*e28a4053SRui Paulo * struct radius_client_data - Internal RADIUS client data 154*e28a4053SRui Paulo * 155*e28a4053SRui Paulo * This data structure is used internally inside the RADIUS client module. 156*e28a4053SRui Paulo * External users allocate this by calling radius_client_init() and free it by 157*e28a4053SRui Paulo * calling radius_client_deinit(). The pointer to this opaque data is used in 158*e28a4053SRui Paulo * calls to other functions as an identifier for the RADIUS client instance. 159*e28a4053SRui Paulo */ 16039beb93cSSam Leffler struct radius_client_data { 161*e28a4053SRui Paulo /** 162*e28a4053SRui Paulo * ctx - Context pointer for hostapd_logger() callbacks 163*e28a4053SRui Paulo */ 16439beb93cSSam Leffler void *ctx; 165*e28a4053SRui Paulo 166*e28a4053SRui Paulo /** 167*e28a4053SRui Paulo * conf - RADIUS client configuration (list of RADIUS servers to use) 168*e28a4053SRui Paulo */ 16939beb93cSSam Leffler struct hostapd_radius_servers *conf; 17039beb93cSSam Leffler 171*e28a4053SRui Paulo /** 172*e28a4053SRui Paulo * auth_serv_sock - IPv4 socket for RADIUS authentication messages 173*e28a4053SRui Paulo */ 174*e28a4053SRui Paulo int auth_serv_sock; 17539beb93cSSam Leffler 176*e28a4053SRui Paulo /** 177*e28a4053SRui Paulo * acct_serv_sock - IPv4 socket for RADIUS accounting messages 178*e28a4053SRui Paulo */ 179*e28a4053SRui Paulo int acct_serv_sock; 180*e28a4053SRui Paulo 181*e28a4053SRui Paulo /** 182*e28a4053SRui Paulo * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages 183*e28a4053SRui Paulo */ 184*e28a4053SRui Paulo int auth_serv_sock6; 185*e28a4053SRui Paulo 186*e28a4053SRui Paulo /** 187*e28a4053SRui Paulo * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages 188*e28a4053SRui Paulo */ 189*e28a4053SRui Paulo int acct_serv_sock6; 190*e28a4053SRui Paulo 191*e28a4053SRui Paulo /** 192*e28a4053SRui Paulo * auth_sock - Currently used socket for RADIUS authentication server 193*e28a4053SRui Paulo */ 194*e28a4053SRui Paulo int auth_sock; 195*e28a4053SRui Paulo 196*e28a4053SRui Paulo /** 197*e28a4053SRui Paulo * acct_sock - Currently used socket for RADIUS accounting server 198*e28a4053SRui Paulo */ 199*e28a4053SRui Paulo int acct_sock; 200*e28a4053SRui Paulo 201*e28a4053SRui Paulo /** 202*e28a4053SRui Paulo * auth_handlers - Authentication message handlers 203*e28a4053SRui Paulo */ 20439beb93cSSam Leffler struct radius_rx_handler *auth_handlers; 205*e28a4053SRui Paulo 206*e28a4053SRui Paulo /** 207*e28a4053SRui Paulo * num_auth_handlers - Number of handlers in auth_handlers 208*e28a4053SRui Paulo */ 20939beb93cSSam Leffler size_t num_auth_handlers; 210*e28a4053SRui Paulo 211*e28a4053SRui Paulo /** 212*e28a4053SRui Paulo * acct_handlers - Accounting message handlers 213*e28a4053SRui Paulo */ 21439beb93cSSam Leffler struct radius_rx_handler *acct_handlers; 215*e28a4053SRui Paulo 216*e28a4053SRui Paulo /** 217*e28a4053SRui Paulo * num_acct_handlers - Number of handlers in acct_handlers 218*e28a4053SRui Paulo */ 21939beb93cSSam Leffler size_t num_acct_handlers; 22039beb93cSSam Leffler 221*e28a4053SRui Paulo /** 222*e28a4053SRui Paulo * msgs - Pending outgoing RADIUS messages 223*e28a4053SRui Paulo */ 22439beb93cSSam Leffler struct radius_msg_list *msgs; 225*e28a4053SRui Paulo 226*e28a4053SRui Paulo /** 227*e28a4053SRui Paulo * num_msgs - Number of pending messages in the msgs list 228*e28a4053SRui Paulo */ 22939beb93cSSam Leffler size_t num_msgs; 23039beb93cSSam Leffler 231*e28a4053SRui Paulo /** 232*e28a4053SRui Paulo * next_radius_identifier - Next RADIUS message identifier to use 233*e28a4053SRui Paulo */ 23439beb93cSSam Leffler u8 next_radius_identifier; 23539beb93cSSam Leffler }; 23639beb93cSSam Leffler 23739beb93cSSam Leffler 23839beb93cSSam Leffler static int 23939beb93cSSam Leffler radius_change_server(struct radius_client_data *radius, 24039beb93cSSam Leffler struct hostapd_radius_server *nserv, 24139beb93cSSam Leffler struct hostapd_radius_server *oserv, 24239beb93cSSam Leffler int sock, int sock6, int auth); 24339beb93cSSam Leffler static int radius_client_init_acct(struct radius_client_data *radius); 24439beb93cSSam Leffler static int radius_client_init_auth(struct radius_client_data *radius); 24539beb93cSSam Leffler 24639beb93cSSam Leffler 24739beb93cSSam Leffler static void radius_client_msg_free(struct radius_msg_list *req) 24839beb93cSSam Leffler { 24939beb93cSSam Leffler radius_msg_free(req->msg); 25039beb93cSSam Leffler os_free(req); 25139beb93cSSam Leffler } 25239beb93cSSam Leffler 25339beb93cSSam Leffler 254*e28a4053SRui Paulo /** 255*e28a4053SRui Paulo * radius_client_register - Register a RADIUS client RX handler 256*e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 257*e28a4053SRui Paulo * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT) 258*e28a4053SRui Paulo * @handler: Handler for received RADIUS messages 259*e28a4053SRui Paulo * @data: Context pointer for handler callbacks 260*e28a4053SRui Paulo * Returns: 0 on success, -1 on failure 261*e28a4053SRui Paulo * 262*e28a4053SRui Paulo * This function is used to register a handler for processing received RADIUS 263*e28a4053SRui Paulo * authentication and accounting messages. The handler() callback function will 264*e28a4053SRui Paulo * be called whenever a RADIUS message is received from the active server. 265*e28a4053SRui Paulo * 266*e28a4053SRui Paulo * There can be multiple registered RADIUS message handlers. The handlers will 267*e28a4053SRui Paulo * be called in order until one of them indicates that it has processed or 268*e28a4053SRui Paulo * queued the message. 269*e28a4053SRui Paulo */ 27039beb93cSSam Leffler int radius_client_register(struct radius_client_data *radius, 27139beb93cSSam Leffler RadiusType msg_type, 27239beb93cSSam Leffler RadiusRxResult (*handler)(struct radius_msg *msg, 27339beb93cSSam Leffler struct radius_msg *req, 27439beb93cSSam Leffler const u8 *shared_secret, 27539beb93cSSam Leffler size_t shared_secret_len, 27639beb93cSSam Leffler void *data), 27739beb93cSSam Leffler void *data) 27839beb93cSSam Leffler { 27939beb93cSSam Leffler struct radius_rx_handler **handlers, *newh; 28039beb93cSSam Leffler size_t *num; 28139beb93cSSam Leffler 28239beb93cSSam Leffler if (msg_type == RADIUS_ACCT) { 28339beb93cSSam Leffler handlers = &radius->acct_handlers; 28439beb93cSSam Leffler num = &radius->num_acct_handlers; 28539beb93cSSam Leffler } else { 28639beb93cSSam Leffler handlers = &radius->auth_handlers; 28739beb93cSSam Leffler num = &radius->num_auth_handlers; 28839beb93cSSam Leffler } 28939beb93cSSam Leffler 29039beb93cSSam Leffler newh = os_realloc(*handlers, 29139beb93cSSam Leffler (*num + 1) * sizeof(struct radius_rx_handler)); 29239beb93cSSam Leffler if (newh == NULL) 29339beb93cSSam Leffler return -1; 29439beb93cSSam Leffler 29539beb93cSSam Leffler newh[*num].handler = handler; 29639beb93cSSam Leffler newh[*num].data = data; 29739beb93cSSam Leffler (*num)++; 29839beb93cSSam Leffler *handlers = newh; 29939beb93cSSam Leffler 30039beb93cSSam Leffler return 0; 30139beb93cSSam Leffler } 30239beb93cSSam Leffler 30339beb93cSSam Leffler 30439beb93cSSam Leffler static void radius_client_handle_send_error(struct radius_client_data *radius, 30539beb93cSSam Leffler int s, RadiusType msg_type) 30639beb93cSSam Leffler { 30739beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS 30839beb93cSSam Leffler int _errno = errno; 30939beb93cSSam Leffler perror("send[RADIUS]"); 31039beb93cSSam Leffler if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || 31139beb93cSSam Leffler _errno == EBADF) { 31239beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 31339beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 31439beb93cSSam Leffler "Send failed - maybe interface status changed -" 31539beb93cSSam Leffler " try to connect again"); 31639beb93cSSam Leffler eloop_unregister_read_sock(s); 31739beb93cSSam Leffler close(s); 31839beb93cSSam Leffler if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) 31939beb93cSSam Leffler radius_client_init_acct(radius); 32039beb93cSSam Leffler else 32139beb93cSSam Leffler radius_client_init_auth(radius); 32239beb93cSSam Leffler } 32339beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */ 32439beb93cSSam Leffler } 32539beb93cSSam Leffler 32639beb93cSSam Leffler 32739beb93cSSam Leffler static int radius_client_retransmit(struct radius_client_data *radius, 32839beb93cSSam Leffler struct radius_msg_list *entry, 32939beb93cSSam Leffler os_time_t now) 33039beb93cSSam Leffler { 33139beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 33239beb93cSSam Leffler int s; 333*e28a4053SRui Paulo struct wpabuf *buf; 33439beb93cSSam Leffler 33539beb93cSSam Leffler if (entry->msg_type == RADIUS_ACCT || 33639beb93cSSam Leffler entry->msg_type == RADIUS_ACCT_INTERIM) { 33739beb93cSSam Leffler s = radius->acct_sock; 33839beb93cSSam Leffler if (entry->attempts == 0) 33939beb93cSSam Leffler conf->acct_server->requests++; 34039beb93cSSam Leffler else { 34139beb93cSSam Leffler conf->acct_server->timeouts++; 34239beb93cSSam Leffler conf->acct_server->retransmissions++; 34339beb93cSSam Leffler } 34439beb93cSSam Leffler } else { 34539beb93cSSam Leffler s = radius->auth_sock; 34639beb93cSSam Leffler if (entry->attempts == 0) 34739beb93cSSam Leffler conf->auth_server->requests++; 34839beb93cSSam Leffler else { 34939beb93cSSam Leffler conf->auth_server->timeouts++; 35039beb93cSSam Leffler conf->auth_server->retransmissions++; 35139beb93cSSam Leffler } 35239beb93cSSam Leffler } 35339beb93cSSam Leffler 35439beb93cSSam Leffler /* retransmit; remove entry if too many attempts */ 35539beb93cSSam Leffler entry->attempts++; 35639beb93cSSam Leffler hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, 35739beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", 358*e28a4053SRui Paulo radius_msg_get_hdr(entry->msg)->identifier); 35939beb93cSSam Leffler 36039beb93cSSam Leffler os_get_time(&entry->last_attempt); 361*e28a4053SRui Paulo buf = radius_msg_get_buf(entry->msg); 362*e28a4053SRui Paulo if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) 36339beb93cSSam Leffler radius_client_handle_send_error(radius, s, entry->msg_type); 36439beb93cSSam Leffler 36539beb93cSSam Leffler entry->next_try = now + entry->next_wait; 36639beb93cSSam Leffler entry->next_wait *= 2; 36739beb93cSSam Leffler if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) 36839beb93cSSam Leffler entry->next_wait = RADIUS_CLIENT_MAX_WAIT; 36939beb93cSSam Leffler if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) { 37039beb93cSSam Leffler printf("Removing un-ACKed RADIUS message due to too many " 37139beb93cSSam Leffler "failed retransmit attempts\n"); 37239beb93cSSam Leffler return 1; 37339beb93cSSam Leffler } 37439beb93cSSam Leffler 37539beb93cSSam Leffler return 0; 37639beb93cSSam Leffler } 37739beb93cSSam Leffler 37839beb93cSSam Leffler 37939beb93cSSam Leffler static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) 38039beb93cSSam Leffler { 38139beb93cSSam Leffler struct radius_client_data *radius = eloop_ctx; 38239beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 38339beb93cSSam Leffler struct os_time now; 38439beb93cSSam Leffler os_time_t first; 38539beb93cSSam Leffler struct radius_msg_list *entry, *prev, *tmp; 38639beb93cSSam Leffler int auth_failover = 0, acct_failover = 0; 38739beb93cSSam Leffler char abuf[50]; 38839beb93cSSam Leffler 38939beb93cSSam Leffler entry = radius->msgs; 39039beb93cSSam Leffler if (!entry) 39139beb93cSSam Leffler return; 39239beb93cSSam Leffler 39339beb93cSSam Leffler os_get_time(&now); 39439beb93cSSam Leffler first = 0; 39539beb93cSSam Leffler 39639beb93cSSam Leffler prev = NULL; 39739beb93cSSam Leffler while (entry) { 39839beb93cSSam Leffler if (now.sec >= entry->next_try && 39939beb93cSSam Leffler radius_client_retransmit(radius, entry, now.sec)) { 40039beb93cSSam Leffler if (prev) 40139beb93cSSam Leffler prev->next = entry->next; 40239beb93cSSam Leffler else 40339beb93cSSam Leffler radius->msgs = entry->next; 40439beb93cSSam Leffler 40539beb93cSSam Leffler tmp = entry; 40639beb93cSSam Leffler entry = entry->next; 40739beb93cSSam Leffler radius_client_msg_free(tmp); 40839beb93cSSam Leffler radius->num_msgs--; 40939beb93cSSam Leffler continue; 41039beb93cSSam Leffler } 41139beb93cSSam Leffler 41239beb93cSSam Leffler if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) { 41339beb93cSSam Leffler if (entry->msg_type == RADIUS_ACCT || 41439beb93cSSam Leffler entry->msg_type == RADIUS_ACCT_INTERIM) 41539beb93cSSam Leffler acct_failover++; 41639beb93cSSam Leffler else 41739beb93cSSam Leffler auth_failover++; 41839beb93cSSam Leffler } 41939beb93cSSam Leffler 42039beb93cSSam Leffler if (first == 0 || entry->next_try < first) 42139beb93cSSam Leffler first = entry->next_try; 42239beb93cSSam Leffler 42339beb93cSSam Leffler prev = entry; 42439beb93cSSam Leffler entry = entry->next; 42539beb93cSSam Leffler } 42639beb93cSSam Leffler 42739beb93cSSam Leffler if (radius->msgs) { 42839beb93cSSam Leffler if (first < now.sec) 42939beb93cSSam Leffler first = now.sec; 43039beb93cSSam Leffler eloop_register_timeout(first - now.sec, 0, 43139beb93cSSam Leffler radius_client_timer, radius, NULL); 43239beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 43339beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Next RADIUS client " 43439beb93cSSam Leffler "retransmit in %ld seconds", 43539beb93cSSam Leffler (long int) (first - now.sec)); 43639beb93cSSam Leffler } 43739beb93cSSam Leffler 43839beb93cSSam Leffler if (auth_failover && conf->num_auth_servers > 1) { 43939beb93cSSam Leffler struct hostapd_radius_server *next, *old; 44039beb93cSSam Leffler old = conf->auth_server; 44139beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 44239beb93cSSam Leffler HOSTAPD_LEVEL_NOTICE, 44339beb93cSSam Leffler "No response from Authentication server " 44439beb93cSSam Leffler "%s:%d - failover", 44539beb93cSSam Leffler hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), 44639beb93cSSam Leffler old->port); 44739beb93cSSam Leffler 44839beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 44939beb93cSSam Leffler if (entry->msg_type == RADIUS_AUTH) 45039beb93cSSam Leffler old->timeouts++; 45139beb93cSSam Leffler } 45239beb93cSSam Leffler 45339beb93cSSam Leffler next = old + 1; 45439beb93cSSam Leffler if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) 45539beb93cSSam Leffler next = conf->auth_servers; 45639beb93cSSam Leffler conf->auth_server = next; 45739beb93cSSam Leffler radius_change_server(radius, next, old, 45839beb93cSSam Leffler radius->auth_serv_sock, 45939beb93cSSam Leffler radius->auth_serv_sock6, 1); 46039beb93cSSam Leffler } 46139beb93cSSam Leffler 46239beb93cSSam Leffler if (acct_failover && conf->num_acct_servers > 1) { 46339beb93cSSam Leffler struct hostapd_radius_server *next, *old; 46439beb93cSSam Leffler old = conf->acct_server; 46539beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 46639beb93cSSam Leffler HOSTAPD_LEVEL_NOTICE, 46739beb93cSSam Leffler "No response from Accounting server " 46839beb93cSSam Leffler "%s:%d - failover", 46939beb93cSSam Leffler hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), 47039beb93cSSam Leffler old->port); 47139beb93cSSam Leffler 47239beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 47339beb93cSSam Leffler if (entry->msg_type == RADIUS_ACCT || 47439beb93cSSam Leffler entry->msg_type == RADIUS_ACCT_INTERIM) 47539beb93cSSam Leffler old->timeouts++; 47639beb93cSSam Leffler } 47739beb93cSSam Leffler 47839beb93cSSam Leffler next = old + 1; 47939beb93cSSam Leffler if (next > &conf->acct_servers[conf->num_acct_servers - 1]) 48039beb93cSSam Leffler next = conf->acct_servers; 48139beb93cSSam Leffler conf->acct_server = next; 48239beb93cSSam Leffler radius_change_server(radius, next, old, 48339beb93cSSam Leffler radius->acct_serv_sock, 48439beb93cSSam Leffler radius->acct_serv_sock6, 0); 48539beb93cSSam Leffler } 48639beb93cSSam Leffler } 48739beb93cSSam Leffler 48839beb93cSSam Leffler 48939beb93cSSam Leffler static void radius_client_update_timeout(struct radius_client_data *radius) 49039beb93cSSam Leffler { 49139beb93cSSam Leffler struct os_time now; 49239beb93cSSam Leffler os_time_t first; 49339beb93cSSam Leffler struct radius_msg_list *entry; 49439beb93cSSam Leffler 49539beb93cSSam Leffler eloop_cancel_timeout(radius_client_timer, radius, NULL); 49639beb93cSSam Leffler 49739beb93cSSam Leffler if (radius->msgs == NULL) { 49839beb93cSSam Leffler return; 49939beb93cSSam Leffler } 50039beb93cSSam Leffler 50139beb93cSSam Leffler first = 0; 50239beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 50339beb93cSSam Leffler if (first == 0 || entry->next_try < first) 50439beb93cSSam Leffler first = entry->next_try; 50539beb93cSSam Leffler } 50639beb93cSSam Leffler 50739beb93cSSam Leffler os_get_time(&now); 50839beb93cSSam Leffler if (first < now.sec) 50939beb93cSSam Leffler first = now.sec; 51039beb93cSSam Leffler eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, 51139beb93cSSam Leffler NULL); 51239beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 51339beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" 51439beb93cSSam Leffler " %ld seconds\n", (long int) (first - now.sec)); 51539beb93cSSam Leffler } 51639beb93cSSam Leffler 51739beb93cSSam Leffler 51839beb93cSSam Leffler static void radius_client_list_add(struct radius_client_data *radius, 51939beb93cSSam Leffler struct radius_msg *msg, 520*e28a4053SRui Paulo RadiusType msg_type, 521*e28a4053SRui Paulo const u8 *shared_secret, 52239beb93cSSam Leffler size_t shared_secret_len, const u8 *addr) 52339beb93cSSam Leffler { 52439beb93cSSam Leffler struct radius_msg_list *entry, *prev; 52539beb93cSSam Leffler 52639beb93cSSam Leffler if (eloop_terminated()) { 52739beb93cSSam Leffler /* No point in adding entries to retransmit queue since event 52839beb93cSSam Leffler * loop has already been terminated. */ 52939beb93cSSam Leffler radius_msg_free(msg); 53039beb93cSSam Leffler return; 53139beb93cSSam Leffler } 53239beb93cSSam Leffler 53339beb93cSSam Leffler entry = os_zalloc(sizeof(*entry)); 53439beb93cSSam Leffler if (entry == NULL) { 53539beb93cSSam Leffler printf("Failed to add RADIUS packet into retransmit list\n"); 53639beb93cSSam Leffler radius_msg_free(msg); 53739beb93cSSam Leffler return; 53839beb93cSSam Leffler } 53939beb93cSSam Leffler 54039beb93cSSam Leffler if (addr) 54139beb93cSSam Leffler os_memcpy(entry->addr, addr, ETH_ALEN); 54239beb93cSSam Leffler entry->msg = msg; 54339beb93cSSam Leffler entry->msg_type = msg_type; 54439beb93cSSam Leffler entry->shared_secret = shared_secret; 54539beb93cSSam Leffler entry->shared_secret_len = shared_secret_len; 54639beb93cSSam Leffler os_get_time(&entry->last_attempt); 54739beb93cSSam Leffler entry->first_try = entry->last_attempt.sec; 54839beb93cSSam Leffler entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; 54939beb93cSSam Leffler entry->attempts = 1; 55039beb93cSSam Leffler entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; 55139beb93cSSam Leffler entry->next = radius->msgs; 55239beb93cSSam Leffler radius->msgs = entry; 55339beb93cSSam Leffler radius_client_update_timeout(radius); 55439beb93cSSam Leffler 55539beb93cSSam Leffler if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { 55639beb93cSSam Leffler printf("Removing the oldest un-ACKed RADIUS packet due to " 55739beb93cSSam Leffler "retransmit list limits.\n"); 55839beb93cSSam Leffler prev = NULL; 55939beb93cSSam Leffler while (entry->next) { 56039beb93cSSam Leffler prev = entry; 56139beb93cSSam Leffler entry = entry->next; 56239beb93cSSam Leffler } 56339beb93cSSam Leffler if (prev) { 56439beb93cSSam Leffler prev->next = NULL; 56539beb93cSSam Leffler radius_client_msg_free(entry); 56639beb93cSSam Leffler } 56739beb93cSSam Leffler } else 56839beb93cSSam Leffler radius->num_msgs++; 56939beb93cSSam Leffler } 57039beb93cSSam Leffler 57139beb93cSSam Leffler 57239beb93cSSam Leffler static void radius_client_list_del(struct radius_client_data *radius, 57339beb93cSSam Leffler RadiusType msg_type, const u8 *addr) 57439beb93cSSam Leffler { 57539beb93cSSam Leffler struct radius_msg_list *entry, *prev, *tmp; 57639beb93cSSam Leffler 57739beb93cSSam Leffler if (addr == NULL) 57839beb93cSSam Leffler return; 57939beb93cSSam Leffler 58039beb93cSSam Leffler entry = radius->msgs; 58139beb93cSSam Leffler prev = NULL; 58239beb93cSSam Leffler while (entry) { 58339beb93cSSam Leffler if (entry->msg_type == msg_type && 58439beb93cSSam Leffler os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { 58539beb93cSSam Leffler if (prev) 58639beb93cSSam Leffler prev->next = entry->next; 58739beb93cSSam Leffler else 58839beb93cSSam Leffler radius->msgs = entry->next; 58939beb93cSSam Leffler tmp = entry; 59039beb93cSSam Leffler entry = entry->next; 59139beb93cSSam Leffler hostapd_logger(radius->ctx, addr, 59239beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 59339beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 59439beb93cSSam Leffler "Removing matching RADIUS message"); 59539beb93cSSam Leffler radius_client_msg_free(tmp); 59639beb93cSSam Leffler radius->num_msgs--; 59739beb93cSSam Leffler continue; 59839beb93cSSam Leffler } 59939beb93cSSam Leffler prev = entry; 60039beb93cSSam Leffler entry = entry->next; 60139beb93cSSam Leffler } 60239beb93cSSam Leffler } 60339beb93cSSam Leffler 60439beb93cSSam Leffler 605*e28a4053SRui Paulo /** 606*e28a4053SRui Paulo * radius_client_send - Send a RADIUS request 607*e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 608*e28a4053SRui Paulo * @msg: RADIUS message to be sent 609*e28a4053SRui Paulo * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM) 610*e28a4053SRui Paulo * @addr: MAC address of the device related to this message or %NULL 611*e28a4053SRui Paulo * Returns: 0 on success, -1 on failure 612*e28a4053SRui Paulo * 613*e28a4053SRui Paulo * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or 614*e28a4053SRui Paulo * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference 615*e28a4053SRui Paulo * between accounting and interim accounting messages is that the interim 616*e28a4053SRui Paulo * message will override any pending interim accounting updates while a new 617*e28a4053SRui Paulo * accounting message does not remove any pending messages. 618*e28a4053SRui Paulo * 619*e28a4053SRui Paulo * The message is added on the retransmission queue and will be retransmitted 620*e28a4053SRui Paulo * automatically until a response is received or maximum number of retries 621*e28a4053SRui Paulo * (RADIUS_CLIENT_MAX_RETRIES) is reached. 622*e28a4053SRui Paulo * 623*e28a4053SRui Paulo * The related device MAC address can be used to identify pending messages that 624*e28a4053SRui Paulo * can be removed with radius_client_flush_auth() or with interim accounting 625*e28a4053SRui Paulo * updates. 626*e28a4053SRui Paulo */ 62739beb93cSSam Leffler int radius_client_send(struct radius_client_data *radius, 62839beb93cSSam Leffler struct radius_msg *msg, RadiusType msg_type, 62939beb93cSSam Leffler const u8 *addr) 63039beb93cSSam Leffler { 63139beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 632*e28a4053SRui Paulo const u8 *shared_secret; 63339beb93cSSam Leffler size_t shared_secret_len; 63439beb93cSSam Leffler char *name; 63539beb93cSSam Leffler int s, res; 636*e28a4053SRui Paulo struct wpabuf *buf; 63739beb93cSSam Leffler 63839beb93cSSam Leffler if (msg_type == RADIUS_ACCT_INTERIM) { 63939beb93cSSam Leffler /* Remove any pending interim acct update for the same STA. */ 64039beb93cSSam Leffler radius_client_list_del(radius, msg_type, addr); 64139beb93cSSam Leffler } 64239beb93cSSam Leffler 64339beb93cSSam Leffler if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { 64439beb93cSSam Leffler if (conf->acct_server == NULL) { 64539beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, 64639beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 64739beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 64839beb93cSSam Leffler "No accounting server configured"); 64939beb93cSSam Leffler return -1; 65039beb93cSSam Leffler } 65139beb93cSSam Leffler shared_secret = conf->acct_server->shared_secret; 65239beb93cSSam Leffler shared_secret_len = conf->acct_server->shared_secret_len; 65339beb93cSSam Leffler radius_msg_finish_acct(msg, shared_secret, shared_secret_len); 65439beb93cSSam Leffler name = "accounting"; 65539beb93cSSam Leffler s = radius->acct_sock; 65639beb93cSSam Leffler conf->acct_server->requests++; 65739beb93cSSam Leffler } else { 65839beb93cSSam Leffler if (conf->auth_server == NULL) { 65939beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, 66039beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 66139beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 66239beb93cSSam Leffler "No authentication server configured"); 66339beb93cSSam Leffler return -1; 66439beb93cSSam Leffler } 66539beb93cSSam Leffler shared_secret = conf->auth_server->shared_secret; 66639beb93cSSam Leffler shared_secret_len = conf->auth_server->shared_secret_len; 66739beb93cSSam Leffler radius_msg_finish(msg, shared_secret, shared_secret_len); 66839beb93cSSam Leffler name = "authentication"; 66939beb93cSSam Leffler s = radius->auth_sock; 67039beb93cSSam Leffler conf->auth_server->requests++; 67139beb93cSSam Leffler } 67239beb93cSSam Leffler 67339beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 67439beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s " 67539beb93cSSam Leffler "server", name); 67639beb93cSSam Leffler if (conf->msg_dumps) 67739beb93cSSam Leffler radius_msg_dump(msg); 67839beb93cSSam Leffler 679*e28a4053SRui Paulo buf = radius_msg_get_buf(msg); 680*e28a4053SRui Paulo res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0); 68139beb93cSSam Leffler if (res < 0) 68239beb93cSSam Leffler radius_client_handle_send_error(radius, s, msg_type); 68339beb93cSSam Leffler 68439beb93cSSam Leffler radius_client_list_add(radius, msg, msg_type, shared_secret, 68539beb93cSSam Leffler shared_secret_len, addr); 68639beb93cSSam Leffler 68739beb93cSSam Leffler return res; 68839beb93cSSam Leffler } 68939beb93cSSam Leffler 69039beb93cSSam Leffler 69139beb93cSSam Leffler static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) 69239beb93cSSam Leffler { 69339beb93cSSam Leffler struct radius_client_data *radius = eloop_ctx; 69439beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 69539beb93cSSam Leffler RadiusType msg_type = (RadiusType) sock_ctx; 69639beb93cSSam Leffler int len, roundtrip; 69739beb93cSSam Leffler unsigned char buf[3000]; 69839beb93cSSam Leffler struct radius_msg *msg; 699*e28a4053SRui Paulo struct radius_hdr *hdr; 70039beb93cSSam Leffler struct radius_rx_handler *handlers; 70139beb93cSSam Leffler size_t num_handlers, i; 70239beb93cSSam Leffler struct radius_msg_list *req, *prev_req; 70339beb93cSSam Leffler struct os_time now; 70439beb93cSSam Leffler struct hostapd_radius_server *rconf; 70539beb93cSSam Leffler int invalid_authenticator = 0; 70639beb93cSSam Leffler 70739beb93cSSam Leffler if (msg_type == RADIUS_ACCT) { 70839beb93cSSam Leffler handlers = radius->acct_handlers; 70939beb93cSSam Leffler num_handlers = radius->num_acct_handlers; 71039beb93cSSam Leffler rconf = conf->acct_server; 71139beb93cSSam Leffler } else { 71239beb93cSSam Leffler handlers = radius->auth_handlers; 71339beb93cSSam Leffler num_handlers = radius->num_auth_handlers; 71439beb93cSSam Leffler rconf = conf->auth_server; 71539beb93cSSam Leffler } 71639beb93cSSam Leffler 71739beb93cSSam Leffler len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); 71839beb93cSSam Leffler if (len < 0) { 71939beb93cSSam Leffler perror("recv[RADIUS]"); 72039beb93cSSam Leffler return; 72139beb93cSSam Leffler } 72239beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 72339beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS " 72439beb93cSSam Leffler "server", len); 72539beb93cSSam Leffler if (len == sizeof(buf)) { 72639beb93cSSam Leffler printf("Possibly too long UDP frame for our buffer - " 72739beb93cSSam Leffler "dropping it\n"); 72839beb93cSSam Leffler return; 72939beb93cSSam Leffler } 73039beb93cSSam Leffler 73139beb93cSSam Leffler msg = radius_msg_parse(buf, len); 73239beb93cSSam Leffler if (msg == NULL) { 73339beb93cSSam Leffler printf("Parsing incoming RADIUS frame failed\n"); 73439beb93cSSam Leffler rconf->malformed_responses++; 73539beb93cSSam Leffler return; 73639beb93cSSam Leffler } 737*e28a4053SRui Paulo hdr = radius_msg_get_hdr(msg); 73839beb93cSSam Leffler 73939beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 74039beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Received RADIUS message"); 74139beb93cSSam Leffler if (conf->msg_dumps) 74239beb93cSSam Leffler radius_msg_dump(msg); 74339beb93cSSam Leffler 744*e28a4053SRui Paulo switch (hdr->code) { 74539beb93cSSam Leffler case RADIUS_CODE_ACCESS_ACCEPT: 74639beb93cSSam Leffler rconf->access_accepts++; 74739beb93cSSam Leffler break; 74839beb93cSSam Leffler case RADIUS_CODE_ACCESS_REJECT: 74939beb93cSSam Leffler rconf->access_rejects++; 75039beb93cSSam Leffler break; 75139beb93cSSam Leffler case RADIUS_CODE_ACCESS_CHALLENGE: 75239beb93cSSam Leffler rconf->access_challenges++; 75339beb93cSSam Leffler break; 75439beb93cSSam Leffler case RADIUS_CODE_ACCOUNTING_RESPONSE: 75539beb93cSSam Leffler rconf->responses++; 75639beb93cSSam Leffler break; 75739beb93cSSam Leffler } 75839beb93cSSam Leffler 75939beb93cSSam Leffler prev_req = NULL; 76039beb93cSSam Leffler req = radius->msgs; 76139beb93cSSam Leffler while (req) { 76239beb93cSSam Leffler /* TODO: also match by src addr:port of the packet when using 76339beb93cSSam Leffler * alternative RADIUS servers (?) */ 76439beb93cSSam Leffler if ((req->msg_type == msg_type || 76539beb93cSSam Leffler (req->msg_type == RADIUS_ACCT_INTERIM && 76639beb93cSSam Leffler msg_type == RADIUS_ACCT)) && 767*e28a4053SRui Paulo radius_msg_get_hdr(req->msg)->identifier == 768*e28a4053SRui Paulo hdr->identifier) 76939beb93cSSam Leffler break; 77039beb93cSSam Leffler 77139beb93cSSam Leffler prev_req = req; 77239beb93cSSam Leffler req = req->next; 77339beb93cSSam Leffler } 77439beb93cSSam Leffler 77539beb93cSSam Leffler if (req == NULL) { 77639beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 77739beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 77839beb93cSSam Leffler "No matching RADIUS request found (type=%d " 77939beb93cSSam Leffler "id=%d) - dropping packet", 780*e28a4053SRui Paulo msg_type, hdr->identifier); 78139beb93cSSam Leffler goto fail; 78239beb93cSSam Leffler } 78339beb93cSSam Leffler 78439beb93cSSam Leffler os_get_time(&now); 78539beb93cSSam Leffler roundtrip = (now.sec - req->last_attempt.sec) * 100 + 78639beb93cSSam Leffler (now.usec - req->last_attempt.usec) / 10000; 78739beb93cSSam Leffler hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, 78839beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 78939beb93cSSam Leffler "Received RADIUS packet matched with a pending " 79039beb93cSSam Leffler "request, round trip time %d.%02d sec", 79139beb93cSSam Leffler roundtrip / 100, roundtrip % 100); 79239beb93cSSam Leffler rconf->round_trip_time = roundtrip; 79339beb93cSSam Leffler 79439beb93cSSam Leffler /* Remove ACKed RADIUS packet from retransmit list */ 79539beb93cSSam Leffler if (prev_req) 79639beb93cSSam Leffler prev_req->next = req->next; 79739beb93cSSam Leffler else 79839beb93cSSam Leffler radius->msgs = req->next; 79939beb93cSSam Leffler radius->num_msgs--; 80039beb93cSSam Leffler 80139beb93cSSam Leffler for (i = 0; i < num_handlers; i++) { 80239beb93cSSam Leffler RadiusRxResult res; 80339beb93cSSam Leffler res = handlers[i].handler(msg, req->msg, req->shared_secret, 80439beb93cSSam Leffler req->shared_secret_len, 80539beb93cSSam Leffler handlers[i].data); 80639beb93cSSam Leffler switch (res) { 80739beb93cSSam Leffler case RADIUS_RX_PROCESSED: 80839beb93cSSam Leffler radius_msg_free(msg); 80939beb93cSSam Leffler /* continue */ 81039beb93cSSam Leffler case RADIUS_RX_QUEUED: 81139beb93cSSam Leffler radius_client_msg_free(req); 81239beb93cSSam Leffler return; 81339beb93cSSam Leffler case RADIUS_RX_INVALID_AUTHENTICATOR: 81439beb93cSSam Leffler invalid_authenticator++; 81539beb93cSSam Leffler /* continue */ 81639beb93cSSam Leffler case RADIUS_RX_UNKNOWN: 81739beb93cSSam Leffler /* continue with next handler */ 81839beb93cSSam Leffler break; 81939beb93cSSam Leffler } 82039beb93cSSam Leffler } 82139beb93cSSam Leffler 82239beb93cSSam Leffler if (invalid_authenticator) 82339beb93cSSam Leffler rconf->bad_authenticators++; 82439beb93cSSam Leffler else 82539beb93cSSam Leffler rconf->unknown_types++; 82639beb93cSSam Leffler hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, 82739beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found " 82839beb93cSSam Leffler "(type=%d code=%d id=%d)%s - dropping packet", 829*e28a4053SRui Paulo msg_type, hdr->code, hdr->identifier, 83039beb93cSSam Leffler invalid_authenticator ? " [INVALID AUTHENTICATOR]" : 83139beb93cSSam Leffler ""); 83239beb93cSSam Leffler radius_client_msg_free(req); 83339beb93cSSam Leffler 83439beb93cSSam Leffler fail: 83539beb93cSSam Leffler radius_msg_free(msg); 83639beb93cSSam Leffler } 83739beb93cSSam Leffler 83839beb93cSSam Leffler 839*e28a4053SRui Paulo /** 840*e28a4053SRui Paulo * radius_client_get_id - Get an identifier for a new RADIUS message 841*e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 842*e28a4053SRui Paulo * Returns: Allocated identifier 843*e28a4053SRui Paulo * 844*e28a4053SRui Paulo * This function is used to fetch a unique (among pending requests) identifier 845*e28a4053SRui Paulo * for a new RADIUS message. 846*e28a4053SRui Paulo */ 84739beb93cSSam Leffler u8 radius_client_get_id(struct radius_client_data *radius) 84839beb93cSSam Leffler { 84939beb93cSSam Leffler struct radius_msg_list *entry, *prev, *_remove; 85039beb93cSSam Leffler u8 id = radius->next_radius_identifier++; 85139beb93cSSam Leffler 85239beb93cSSam Leffler /* remove entries with matching id from retransmit list to avoid 85339beb93cSSam Leffler * using new reply from the RADIUS server with an old request */ 85439beb93cSSam Leffler entry = radius->msgs; 85539beb93cSSam Leffler prev = NULL; 85639beb93cSSam Leffler while (entry) { 857*e28a4053SRui Paulo if (radius_msg_get_hdr(entry->msg)->identifier == id) { 85839beb93cSSam Leffler hostapd_logger(radius->ctx, entry->addr, 85939beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 86039beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 86139beb93cSSam Leffler "Removing pending RADIUS message, " 86239beb93cSSam Leffler "since its id (%d) is reused", id); 86339beb93cSSam Leffler if (prev) 86439beb93cSSam Leffler prev->next = entry->next; 86539beb93cSSam Leffler else 86639beb93cSSam Leffler radius->msgs = entry->next; 86739beb93cSSam Leffler _remove = entry; 86839beb93cSSam Leffler } else { 86939beb93cSSam Leffler _remove = NULL; 87039beb93cSSam Leffler prev = entry; 87139beb93cSSam Leffler } 87239beb93cSSam Leffler entry = entry->next; 87339beb93cSSam Leffler 87439beb93cSSam Leffler if (_remove) 87539beb93cSSam Leffler radius_client_msg_free(_remove); 87639beb93cSSam Leffler } 87739beb93cSSam Leffler 87839beb93cSSam Leffler return id; 87939beb93cSSam Leffler } 88039beb93cSSam Leffler 88139beb93cSSam Leffler 882*e28a4053SRui Paulo /** 883*e28a4053SRui Paulo * radius_client_flush - Flush all pending RADIUS client messages 884*e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 885*e28a4053SRui Paulo * @only_auth: Whether only authentication messages are removed 886*e28a4053SRui Paulo */ 88739beb93cSSam Leffler void radius_client_flush(struct radius_client_data *radius, int only_auth) 88839beb93cSSam Leffler { 88939beb93cSSam Leffler struct radius_msg_list *entry, *prev, *tmp; 89039beb93cSSam Leffler 89139beb93cSSam Leffler if (!radius) 89239beb93cSSam Leffler return; 89339beb93cSSam Leffler 89439beb93cSSam Leffler prev = NULL; 89539beb93cSSam Leffler entry = radius->msgs; 89639beb93cSSam Leffler 89739beb93cSSam Leffler while (entry) { 89839beb93cSSam Leffler if (!only_auth || entry->msg_type == RADIUS_AUTH) { 89939beb93cSSam Leffler if (prev) 90039beb93cSSam Leffler prev->next = entry->next; 90139beb93cSSam Leffler else 90239beb93cSSam Leffler radius->msgs = entry->next; 90339beb93cSSam Leffler 90439beb93cSSam Leffler tmp = entry; 90539beb93cSSam Leffler entry = entry->next; 90639beb93cSSam Leffler radius_client_msg_free(tmp); 90739beb93cSSam Leffler radius->num_msgs--; 90839beb93cSSam Leffler } else { 90939beb93cSSam Leffler prev = entry; 91039beb93cSSam Leffler entry = entry->next; 91139beb93cSSam Leffler } 91239beb93cSSam Leffler } 91339beb93cSSam Leffler 91439beb93cSSam Leffler if (radius->msgs == NULL) 91539beb93cSSam Leffler eloop_cancel_timeout(radius_client_timer, radius, NULL); 91639beb93cSSam Leffler } 91739beb93cSSam Leffler 91839beb93cSSam Leffler 91939beb93cSSam Leffler static void radius_client_update_acct_msgs(struct radius_client_data *radius, 920*e28a4053SRui Paulo const u8 *shared_secret, 92139beb93cSSam Leffler size_t shared_secret_len) 92239beb93cSSam Leffler { 92339beb93cSSam Leffler struct radius_msg_list *entry; 92439beb93cSSam Leffler 92539beb93cSSam Leffler if (!radius) 92639beb93cSSam Leffler return; 92739beb93cSSam Leffler 92839beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 92939beb93cSSam Leffler if (entry->msg_type == RADIUS_ACCT) { 93039beb93cSSam Leffler entry->shared_secret = shared_secret; 93139beb93cSSam Leffler entry->shared_secret_len = shared_secret_len; 93239beb93cSSam Leffler radius_msg_finish_acct(entry->msg, shared_secret, 93339beb93cSSam Leffler shared_secret_len); 93439beb93cSSam Leffler } 93539beb93cSSam Leffler } 93639beb93cSSam Leffler } 93739beb93cSSam Leffler 93839beb93cSSam Leffler 93939beb93cSSam Leffler static int 94039beb93cSSam Leffler radius_change_server(struct radius_client_data *radius, 94139beb93cSSam Leffler struct hostapd_radius_server *nserv, 94239beb93cSSam Leffler struct hostapd_radius_server *oserv, 94339beb93cSSam Leffler int sock, int sock6, int auth) 94439beb93cSSam Leffler { 94539beb93cSSam Leffler struct sockaddr_in serv, claddr; 94639beb93cSSam Leffler #ifdef CONFIG_IPV6 94739beb93cSSam Leffler struct sockaddr_in6 serv6, claddr6; 94839beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 94939beb93cSSam Leffler struct sockaddr *addr, *cl_addr; 95039beb93cSSam Leffler socklen_t addrlen, claddrlen; 95139beb93cSSam Leffler char abuf[50]; 95239beb93cSSam Leffler int sel_sock; 95339beb93cSSam Leffler struct radius_msg_list *entry; 95439beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 95539beb93cSSam Leffler 95639beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 95739beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 95839beb93cSSam Leffler "%s server %s:%d", 95939beb93cSSam Leffler auth ? "Authentication" : "Accounting", 96039beb93cSSam Leffler hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), 96139beb93cSSam Leffler nserv->port); 96239beb93cSSam Leffler 96339beb93cSSam Leffler if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len || 96439beb93cSSam Leffler os_memcmp(nserv->shared_secret, oserv->shared_secret, 96539beb93cSSam Leffler nserv->shared_secret_len) != 0) { 96639beb93cSSam Leffler /* Pending RADIUS packets used different shared secret, so 96739beb93cSSam Leffler * they need to be modified. Update accounting message 96839beb93cSSam Leffler * authenticators here. Authentication messages are removed 96939beb93cSSam Leffler * since they would require more changes and the new RADIUS 97039beb93cSSam Leffler * server may not be prepared to receive them anyway due to 97139beb93cSSam Leffler * missing state information. Client will likely retry 97239beb93cSSam Leffler * authentication, so this should not be an issue. */ 97339beb93cSSam Leffler if (auth) 97439beb93cSSam Leffler radius_client_flush(radius, 1); 97539beb93cSSam Leffler else { 97639beb93cSSam Leffler radius_client_update_acct_msgs( 97739beb93cSSam Leffler radius, nserv->shared_secret, 97839beb93cSSam Leffler nserv->shared_secret_len); 97939beb93cSSam Leffler } 98039beb93cSSam Leffler } 98139beb93cSSam Leffler 98239beb93cSSam Leffler /* Reset retry counters for the new server */ 98339beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 98439beb93cSSam Leffler if ((auth && entry->msg_type != RADIUS_AUTH) || 98539beb93cSSam Leffler (!auth && entry->msg_type != RADIUS_ACCT)) 98639beb93cSSam Leffler continue; 98739beb93cSSam Leffler entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; 98839beb93cSSam Leffler entry->attempts = 0; 98939beb93cSSam Leffler entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; 99039beb93cSSam Leffler } 99139beb93cSSam Leffler 99239beb93cSSam Leffler if (radius->msgs) { 99339beb93cSSam Leffler eloop_cancel_timeout(radius_client_timer, radius, NULL); 99439beb93cSSam Leffler eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, 99539beb93cSSam Leffler radius_client_timer, radius, NULL); 99639beb93cSSam Leffler } 99739beb93cSSam Leffler 99839beb93cSSam Leffler switch (nserv->addr.af) { 99939beb93cSSam Leffler case AF_INET: 100039beb93cSSam Leffler os_memset(&serv, 0, sizeof(serv)); 100139beb93cSSam Leffler serv.sin_family = AF_INET; 100239beb93cSSam Leffler serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr; 100339beb93cSSam Leffler serv.sin_port = htons(nserv->port); 100439beb93cSSam Leffler addr = (struct sockaddr *) &serv; 100539beb93cSSam Leffler addrlen = sizeof(serv); 100639beb93cSSam Leffler sel_sock = sock; 100739beb93cSSam Leffler break; 100839beb93cSSam Leffler #ifdef CONFIG_IPV6 100939beb93cSSam Leffler case AF_INET6: 101039beb93cSSam Leffler os_memset(&serv6, 0, sizeof(serv6)); 101139beb93cSSam Leffler serv6.sin6_family = AF_INET6; 101239beb93cSSam Leffler os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6, 101339beb93cSSam Leffler sizeof(struct in6_addr)); 101439beb93cSSam Leffler serv6.sin6_port = htons(nserv->port); 101539beb93cSSam Leffler addr = (struct sockaddr *) &serv6; 101639beb93cSSam Leffler addrlen = sizeof(serv6); 101739beb93cSSam Leffler sel_sock = sock6; 101839beb93cSSam Leffler break; 101939beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 102039beb93cSSam Leffler default: 102139beb93cSSam Leffler return -1; 102239beb93cSSam Leffler } 102339beb93cSSam Leffler 102439beb93cSSam Leffler if (conf->force_client_addr) { 102539beb93cSSam Leffler switch (conf->client_addr.af) { 102639beb93cSSam Leffler case AF_INET: 102739beb93cSSam Leffler os_memset(&claddr, 0, sizeof(claddr)); 102839beb93cSSam Leffler claddr.sin_family = AF_INET; 102939beb93cSSam Leffler claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr; 103039beb93cSSam Leffler claddr.sin_port = htons(0); 103139beb93cSSam Leffler cl_addr = (struct sockaddr *) &claddr; 103239beb93cSSam Leffler claddrlen = sizeof(claddr); 103339beb93cSSam Leffler break; 103439beb93cSSam Leffler #ifdef CONFIG_IPV6 103539beb93cSSam Leffler case AF_INET6: 103639beb93cSSam Leffler os_memset(&claddr6, 0, sizeof(claddr6)); 103739beb93cSSam Leffler claddr6.sin6_family = AF_INET6; 103839beb93cSSam Leffler os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6, 103939beb93cSSam Leffler sizeof(struct in6_addr)); 104039beb93cSSam Leffler claddr6.sin6_port = htons(0); 104139beb93cSSam Leffler cl_addr = (struct sockaddr *) &claddr6; 104239beb93cSSam Leffler claddrlen = sizeof(claddr6); 104339beb93cSSam Leffler break; 104439beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 104539beb93cSSam Leffler default: 104639beb93cSSam Leffler return -1; 104739beb93cSSam Leffler } 104839beb93cSSam Leffler 104939beb93cSSam Leffler if (bind(sel_sock, cl_addr, claddrlen) < 0) { 105039beb93cSSam Leffler perror("bind[radius]"); 105139beb93cSSam Leffler return -1; 105239beb93cSSam Leffler } 105339beb93cSSam Leffler } 105439beb93cSSam Leffler 105539beb93cSSam Leffler if (connect(sel_sock, addr, addrlen) < 0) { 105639beb93cSSam Leffler perror("connect[radius]"); 105739beb93cSSam Leffler return -1; 105839beb93cSSam Leffler } 105939beb93cSSam Leffler 106039beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS 106139beb93cSSam Leffler switch (nserv->addr.af) { 106239beb93cSSam Leffler case AF_INET: 106339beb93cSSam Leffler claddrlen = sizeof(claddr); 106439beb93cSSam Leffler getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen); 106539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", 106639beb93cSSam Leffler inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port)); 106739beb93cSSam Leffler break; 106839beb93cSSam Leffler #ifdef CONFIG_IPV6 106939beb93cSSam Leffler case AF_INET6: { 107039beb93cSSam Leffler claddrlen = sizeof(claddr6); 107139beb93cSSam Leffler getsockname(sel_sock, (struct sockaddr *) &claddr6, 107239beb93cSSam Leffler &claddrlen); 107339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", 107439beb93cSSam Leffler inet_ntop(AF_INET6, &claddr6.sin6_addr, 107539beb93cSSam Leffler abuf, sizeof(abuf)), 107639beb93cSSam Leffler ntohs(claddr6.sin6_port)); 107739beb93cSSam Leffler break; 107839beb93cSSam Leffler } 107939beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 108039beb93cSSam Leffler } 108139beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */ 108239beb93cSSam Leffler 108339beb93cSSam Leffler if (auth) 108439beb93cSSam Leffler radius->auth_sock = sel_sock; 108539beb93cSSam Leffler else 108639beb93cSSam Leffler radius->acct_sock = sel_sock; 108739beb93cSSam Leffler 108839beb93cSSam Leffler return 0; 108939beb93cSSam Leffler } 109039beb93cSSam Leffler 109139beb93cSSam Leffler 109239beb93cSSam Leffler static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) 109339beb93cSSam Leffler { 109439beb93cSSam Leffler struct radius_client_data *radius = eloop_ctx; 109539beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 109639beb93cSSam Leffler struct hostapd_radius_server *oserv; 109739beb93cSSam Leffler 109839beb93cSSam Leffler if (radius->auth_sock >= 0 && conf->auth_servers && 109939beb93cSSam Leffler conf->auth_server != conf->auth_servers) { 110039beb93cSSam Leffler oserv = conf->auth_server; 110139beb93cSSam Leffler conf->auth_server = conf->auth_servers; 110239beb93cSSam Leffler radius_change_server(radius, conf->auth_server, oserv, 110339beb93cSSam Leffler radius->auth_serv_sock, 110439beb93cSSam Leffler radius->auth_serv_sock6, 1); 110539beb93cSSam Leffler } 110639beb93cSSam Leffler 110739beb93cSSam Leffler if (radius->acct_sock >= 0 && conf->acct_servers && 110839beb93cSSam Leffler conf->acct_server != conf->acct_servers) { 110939beb93cSSam Leffler oserv = conf->acct_server; 111039beb93cSSam Leffler conf->acct_server = conf->acct_servers; 111139beb93cSSam Leffler radius_change_server(radius, conf->acct_server, oserv, 111239beb93cSSam Leffler radius->acct_serv_sock, 111339beb93cSSam Leffler radius->acct_serv_sock6, 0); 111439beb93cSSam Leffler } 111539beb93cSSam Leffler 111639beb93cSSam Leffler if (conf->retry_primary_interval) 111739beb93cSSam Leffler eloop_register_timeout(conf->retry_primary_interval, 0, 111839beb93cSSam Leffler radius_retry_primary_timer, radius, 111939beb93cSSam Leffler NULL); 112039beb93cSSam Leffler } 112139beb93cSSam Leffler 112239beb93cSSam Leffler 11233157ba21SRui Paulo static int radius_client_disable_pmtu_discovery(int s) 11243157ba21SRui Paulo { 11253157ba21SRui Paulo int r = -1; 11263157ba21SRui Paulo #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) 11273157ba21SRui Paulo /* Turn off Path MTU discovery on IPv4/UDP sockets. */ 11283157ba21SRui Paulo int action = IP_PMTUDISC_DONT; 11293157ba21SRui Paulo r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, 11303157ba21SRui Paulo sizeof(action)); 11313157ba21SRui Paulo if (r == -1) 11323157ba21SRui Paulo wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: " 11333157ba21SRui Paulo "%s", strerror(errno)); 11343157ba21SRui Paulo #endif 11353157ba21SRui Paulo return r; 11363157ba21SRui Paulo } 11373157ba21SRui Paulo 11383157ba21SRui Paulo 113939beb93cSSam Leffler static int radius_client_init_auth(struct radius_client_data *radius) 114039beb93cSSam Leffler { 114139beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 114239beb93cSSam Leffler int ok = 0; 114339beb93cSSam Leffler 114439beb93cSSam Leffler radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); 114539beb93cSSam Leffler if (radius->auth_serv_sock < 0) 114639beb93cSSam Leffler perror("socket[PF_INET,SOCK_DGRAM]"); 11473157ba21SRui Paulo else { 11483157ba21SRui Paulo radius_client_disable_pmtu_discovery(radius->auth_serv_sock); 114939beb93cSSam Leffler ok++; 11503157ba21SRui Paulo } 115139beb93cSSam Leffler 115239beb93cSSam Leffler #ifdef CONFIG_IPV6 115339beb93cSSam Leffler radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); 115439beb93cSSam Leffler if (radius->auth_serv_sock6 < 0) 115539beb93cSSam Leffler perror("socket[PF_INET6,SOCK_DGRAM]"); 115639beb93cSSam Leffler else 115739beb93cSSam Leffler ok++; 115839beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 115939beb93cSSam Leffler 116039beb93cSSam Leffler if (ok == 0) 116139beb93cSSam Leffler return -1; 116239beb93cSSam Leffler 116339beb93cSSam Leffler radius_change_server(radius, conf->auth_server, NULL, 116439beb93cSSam Leffler radius->auth_serv_sock, radius->auth_serv_sock6, 116539beb93cSSam Leffler 1); 116639beb93cSSam Leffler 116739beb93cSSam Leffler if (radius->auth_serv_sock >= 0 && 116839beb93cSSam Leffler eloop_register_read_sock(radius->auth_serv_sock, 116939beb93cSSam Leffler radius_client_receive, radius, 117039beb93cSSam Leffler (void *) RADIUS_AUTH)) { 117139beb93cSSam Leffler printf("Could not register read socket for authentication " 117239beb93cSSam Leffler "server\n"); 117339beb93cSSam Leffler return -1; 117439beb93cSSam Leffler } 117539beb93cSSam Leffler 117639beb93cSSam Leffler #ifdef CONFIG_IPV6 117739beb93cSSam Leffler if (radius->auth_serv_sock6 >= 0 && 117839beb93cSSam Leffler eloop_register_read_sock(radius->auth_serv_sock6, 117939beb93cSSam Leffler radius_client_receive, radius, 118039beb93cSSam Leffler (void *) RADIUS_AUTH)) { 118139beb93cSSam Leffler printf("Could not register read socket for authentication " 118239beb93cSSam Leffler "server\n"); 118339beb93cSSam Leffler return -1; 118439beb93cSSam Leffler } 118539beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 118639beb93cSSam Leffler 118739beb93cSSam Leffler return 0; 118839beb93cSSam Leffler } 118939beb93cSSam Leffler 119039beb93cSSam Leffler 119139beb93cSSam Leffler static int radius_client_init_acct(struct radius_client_data *radius) 119239beb93cSSam Leffler { 119339beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 119439beb93cSSam Leffler int ok = 0; 119539beb93cSSam Leffler 119639beb93cSSam Leffler radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); 119739beb93cSSam Leffler if (radius->acct_serv_sock < 0) 119839beb93cSSam Leffler perror("socket[PF_INET,SOCK_DGRAM]"); 11993157ba21SRui Paulo else { 12003157ba21SRui Paulo radius_client_disable_pmtu_discovery(radius->acct_serv_sock); 120139beb93cSSam Leffler ok++; 12023157ba21SRui Paulo } 120339beb93cSSam Leffler 120439beb93cSSam Leffler #ifdef CONFIG_IPV6 120539beb93cSSam Leffler radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); 120639beb93cSSam Leffler if (radius->acct_serv_sock6 < 0) 120739beb93cSSam Leffler perror("socket[PF_INET6,SOCK_DGRAM]"); 120839beb93cSSam Leffler else 120939beb93cSSam Leffler ok++; 121039beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 121139beb93cSSam Leffler 121239beb93cSSam Leffler if (ok == 0) 121339beb93cSSam Leffler return -1; 121439beb93cSSam Leffler 121539beb93cSSam Leffler radius_change_server(radius, conf->acct_server, NULL, 121639beb93cSSam Leffler radius->acct_serv_sock, radius->acct_serv_sock6, 121739beb93cSSam Leffler 0); 121839beb93cSSam Leffler 121939beb93cSSam Leffler if (radius->acct_serv_sock >= 0 && 122039beb93cSSam Leffler eloop_register_read_sock(radius->acct_serv_sock, 122139beb93cSSam Leffler radius_client_receive, radius, 122239beb93cSSam Leffler (void *) RADIUS_ACCT)) { 122339beb93cSSam Leffler printf("Could not register read socket for accounting " 122439beb93cSSam Leffler "server\n"); 122539beb93cSSam Leffler return -1; 122639beb93cSSam Leffler } 122739beb93cSSam Leffler 122839beb93cSSam Leffler #ifdef CONFIG_IPV6 122939beb93cSSam Leffler if (radius->acct_serv_sock6 >= 0 && 123039beb93cSSam Leffler eloop_register_read_sock(radius->acct_serv_sock6, 123139beb93cSSam Leffler radius_client_receive, radius, 123239beb93cSSam Leffler (void *) RADIUS_ACCT)) { 123339beb93cSSam Leffler printf("Could not register read socket for accounting " 123439beb93cSSam Leffler "server\n"); 123539beb93cSSam Leffler return -1; 123639beb93cSSam Leffler } 123739beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 123839beb93cSSam Leffler 123939beb93cSSam Leffler return 0; 124039beb93cSSam Leffler } 124139beb93cSSam Leffler 124239beb93cSSam Leffler 1243*e28a4053SRui Paulo /** 1244*e28a4053SRui Paulo * radius_client_init - Initialize RADIUS client 1245*e28a4053SRui Paulo * @ctx: Callback context to be used in hostapd_logger() calls 1246*e28a4053SRui Paulo * @conf: RADIUS client configuration (RADIUS servers) 1247*e28a4053SRui Paulo * Returns: Pointer to private RADIUS client context or %NULL on failure 1248*e28a4053SRui Paulo * 1249*e28a4053SRui Paulo * The caller is responsible for keeping the configuration data available for 1250*e28a4053SRui Paulo * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is 1251*e28a4053SRui Paulo * called for the returned context pointer. 1252*e28a4053SRui Paulo */ 125339beb93cSSam Leffler struct radius_client_data * 125439beb93cSSam Leffler radius_client_init(void *ctx, struct hostapd_radius_servers *conf) 125539beb93cSSam Leffler { 125639beb93cSSam Leffler struct radius_client_data *radius; 125739beb93cSSam Leffler 125839beb93cSSam Leffler radius = os_zalloc(sizeof(struct radius_client_data)); 125939beb93cSSam Leffler if (radius == NULL) 126039beb93cSSam Leffler return NULL; 126139beb93cSSam Leffler 126239beb93cSSam Leffler radius->ctx = ctx; 126339beb93cSSam Leffler radius->conf = conf; 126439beb93cSSam Leffler radius->auth_serv_sock = radius->acct_serv_sock = 126539beb93cSSam Leffler radius->auth_serv_sock6 = radius->acct_serv_sock6 = 126639beb93cSSam Leffler radius->auth_sock = radius->acct_sock = -1; 126739beb93cSSam Leffler 126839beb93cSSam Leffler if (conf->auth_server && radius_client_init_auth(radius)) { 126939beb93cSSam Leffler radius_client_deinit(radius); 127039beb93cSSam Leffler return NULL; 127139beb93cSSam Leffler } 127239beb93cSSam Leffler 127339beb93cSSam Leffler if (conf->acct_server && radius_client_init_acct(radius)) { 127439beb93cSSam Leffler radius_client_deinit(radius); 127539beb93cSSam Leffler return NULL; 127639beb93cSSam Leffler } 127739beb93cSSam Leffler 127839beb93cSSam Leffler if (conf->retry_primary_interval) 127939beb93cSSam Leffler eloop_register_timeout(conf->retry_primary_interval, 0, 128039beb93cSSam Leffler radius_retry_primary_timer, radius, 128139beb93cSSam Leffler NULL); 128239beb93cSSam Leffler 128339beb93cSSam Leffler return radius; 128439beb93cSSam Leffler } 128539beb93cSSam Leffler 128639beb93cSSam Leffler 1287*e28a4053SRui Paulo /** 1288*e28a4053SRui Paulo * radius_client_deinit - Deinitialize RADIUS client 1289*e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 1290*e28a4053SRui Paulo */ 129139beb93cSSam Leffler void radius_client_deinit(struct radius_client_data *radius) 129239beb93cSSam Leffler { 129339beb93cSSam Leffler if (!radius) 129439beb93cSSam Leffler return; 129539beb93cSSam Leffler 129639beb93cSSam Leffler if (radius->auth_serv_sock >= 0) 129739beb93cSSam Leffler eloop_unregister_read_sock(radius->auth_serv_sock); 129839beb93cSSam Leffler if (radius->acct_serv_sock >= 0) 129939beb93cSSam Leffler eloop_unregister_read_sock(radius->acct_serv_sock); 13003157ba21SRui Paulo #ifdef CONFIG_IPV6 13013157ba21SRui Paulo if (radius->auth_serv_sock6 >= 0) 13023157ba21SRui Paulo eloop_unregister_read_sock(radius->auth_serv_sock6); 13033157ba21SRui Paulo if (radius->acct_serv_sock6 >= 0) 13043157ba21SRui Paulo eloop_unregister_read_sock(radius->acct_serv_sock6); 13053157ba21SRui Paulo #endif /* CONFIG_IPV6 */ 130639beb93cSSam Leffler 130739beb93cSSam Leffler eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL); 130839beb93cSSam Leffler 130939beb93cSSam Leffler radius_client_flush(radius, 0); 131039beb93cSSam Leffler os_free(radius->auth_handlers); 131139beb93cSSam Leffler os_free(radius->acct_handlers); 131239beb93cSSam Leffler os_free(radius); 131339beb93cSSam Leffler } 131439beb93cSSam Leffler 131539beb93cSSam Leffler 1316*e28a4053SRui Paulo /** 1317*e28a4053SRui Paulo * radius_client_flush_auth - Flush pending RADIUS messages for an address 1318*e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 1319*e28a4053SRui Paulo * @addr: MAC address of the related device 1320*e28a4053SRui Paulo * 1321*e28a4053SRui Paulo * This function can be used to remove pending RADIUS authentication messages 1322*e28a4053SRui Paulo * that are related to a specific device. The addr parameter is matched with 1323*e28a4053SRui Paulo * the one used in radius_client_send() call that was used to transmit the 1324*e28a4053SRui Paulo * authentication request. 1325*e28a4053SRui Paulo */ 1326*e28a4053SRui Paulo void radius_client_flush_auth(struct radius_client_data *radius, 1327*e28a4053SRui Paulo const u8 *addr) 132839beb93cSSam Leffler { 132939beb93cSSam Leffler struct radius_msg_list *entry, *prev, *tmp; 133039beb93cSSam Leffler 133139beb93cSSam Leffler prev = NULL; 133239beb93cSSam Leffler entry = radius->msgs; 133339beb93cSSam Leffler while (entry) { 133439beb93cSSam Leffler if (entry->msg_type == RADIUS_AUTH && 133539beb93cSSam Leffler os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { 133639beb93cSSam Leffler hostapd_logger(radius->ctx, addr, 133739beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 133839beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 133939beb93cSSam Leffler "Removing pending RADIUS authentication" 134039beb93cSSam Leffler " message for removed client"); 134139beb93cSSam Leffler 134239beb93cSSam Leffler if (prev) 134339beb93cSSam Leffler prev->next = entry->next; 134439beb93cSSam Leffler else 134539beb93cSSam Leffler radius->msgs = entry->next; 134639beb93cSSam Leffler 134739beb93cSSam Leffler tmp = entry; 134839beb93cSSam Leffler entry = entry->next; 134939beb93cSSam Leffler radius_client_msg_free(tmp); 135039beb93cSSam Leffler radius->num_msgs--; 135139beb93cSSam Leffler continue; 135239beb93cSSam Leffler } 135339beb93cSSam Leffler 135439beb93cSSam Leffler prev = entry; 135539beb93cSSam Leffler entry = entry->next; 135639beb93cSSam Leffler } 135739beb93cSSam Leffler } 135839beb93cSSam Leffler 135939beb93cSSam Leffler 136039beb93cSSam Leffler static int radius_client_dump_auth_server(char *buf, size_t buflen, 136139beb93cSSam Leffler struct hostapd_radius_server *serv, 136239beb93cSSam Leffler struct radius_client_data *cli) 136339beb93cSSam Leffler { 136439beb93cSSam Leffler int pending = 0; 136539beb93cSSam Leffler struct radius_msg_list *msg; 136639beb93cSSam Leffler char abuf[50]; 136739beb93cSSam Leffler 136839beb93cSSam Leffler if (cli) { 136939beb93cSSam Leffler for (msg = cli->msgs; msg; msg = msg->next) { 137039beb93cSSam Leffler if (msg->msg_type == RADIUS_AUTH) 137139beb93cSSam Leffler pending++; 137239beb93cSSam Leffler } 137339beb93cSSam Leffler } 137439beb93cSSam Leffler 137539beb93cSSam Leffler return os_snprintf(buf, buflen, 137639beb93cSSam Leffler "radiusAuthServerIndex=%d\n" 137739beb93cSSam Leffler "radiusAuthServerAddress=%s\n" 137839beb93cSSam Leffler "radiusAuthClientServerPortNumber=%d\n" 137939beb93cSSam Leffler "radiusAuthClientRoundTripTime=%d\n" 138039beb93cSSam Leffler "radiusAuthClientAccessRequests=%u\n" 138139beb93cSSam Leffler "radiusAuthClientAccessRetransmissions=%u\n" 138239beb93cSSam Leffler "radiusAuthClientAccessAccepts=%u\n" 138339beb93cSSam Leffler "radiusAuthClientAccessRejects=%u\n" 138439beb93cSSam Leffler "radiusAuthClientAccessChallenges=%u\n" 138539beb93cSSam Leffler "radiusAuthClientMalformedAccessResponses=%u\n" 138639beb93cSSam Leffler "radiusAuthClientBadAuthenticators=%u\n" 138739beb93cSSam Leffler "radiusAuthClientPendingRequests=%u\n" 138839beb93cSSam Leffler "radiusAuthClientTimeouts=%u\n" 138939beb93cSSam Leffler "radiusAuthClientUnknownTypes=%u\n" 139039beb93cSSam Leffler "radiusAuthClientPacketsDropped=%u\n", 139139beb93cSSam Leffler serv->index, 139239beb93cSSam Leffler hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), 139339beb93cSSam Leffler serv->port, 139439beb93cSSam Leffler serv->round_trip_time, 139539beb93cSSam Leffler serv->requests, 139639beb93cSSam Leffler serv->retransmissions, 139739beb93cSSam Leffler serv->access_accepts, 139839beb93cSSam Leffler serv->access_rejects, 139939beb93cSSam Leffler serv->access_challenges, 140039beb93cSSam Leffler serv->malformed_responses, 140139beb93cSSam Leffler serv->bad_authenticators, 140239beb93cSSam Leffler pending, 140339beb93cSSam Leffler serv->timeouts, 140439beb93cSSam Leffler serv->unknown_types, 140539beb93cSSam Leffler serv->packets_dropped); 140639beb93cSSam Leffler } 140739beb93cSSam Leffler 140839beb93cSSam Leffler 140939beb93cSSam Leffler static int radius_client_dump_acct_server(char *buf, size_t buflen, 141039beb93cSSam Leffler struct hostapd_radius_server *serv, 141139beb93cSSam Leffler struct radius_client_data *cli) 141239beb93cSSam Leffler { 141339beb93cSSam Leffler int pending = 0; 141439beb93cSSam Leffler struct radius_msg_list *msg; 141539beb93cSSam Leffler char abuf[50]; 141639beb93cSSam Leffler 141739beb93cSSam Leffler if (cli) { 141839beb93cSSam Leffler for (msg = cli->msgs; msg; msg = msg->next) { 141939beb93cSSam Leffler if (msg->msg_type == RADIUS_ACCT || 142039beb93cSSam Leffler msg->msg_type == RADIUS_ACCT_INTERIM) 142139beb93cSSam Leffler pending++; 142239beb93cSSam Leffler } 142339beb93cSSam Leffler } 142439beb93cSSam Leffler 142539beb93cSSam Leffler return os_snprintf(buf, buflen, 142639beb93cSSam Leffler "radiusAccServerIndex=%d\n" 142739beb93cSSam Leffler "radiusAccServerAddress=%s\n" 142839beb93cSSam Leffler "radiusAccClientServerPortNumber=%d\n" 142939beb93cSSam Leffler "radiusAccClientRoundTripTime=%d\n" 143039beb93cSSam Leffler "radiusAccClientRequests=%u\n" 143139beb93cSSam Leffler "radiusAccClientRetransmissions=%u\n" 143239beb93cSSam Leffler "radiusAccClientResponses=%u\n" 143339beb93cSSam Leffler "radiusAccClientMalformedResponses=%u\n" 143439beb93cSSam Leffler "radiusAccClientBadAuthenticators=%u\n" 143539beb93cSSam Leffler "radiusAccClientPendingRequests=%u\n" 143639beb93cSSam Leffler "radiusAccClientTimeouts=%u\n" 143739beb93cSSam Leffler "radiusAccClientUnknownTypes=%u\n" 143839beb93cSSam Leffler "radiusAccClientPacketsDropped=%u\n", 143939beb93cSSam Leffler serv->index, 144039beb93cSSam Leffler hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), 144139beb93cSSam Leffler serv->port, 144239beb93cSSam Leffler serv->round_trip_time, 144339beb93cSSam Leffler serv->requests, 144439beb93cSSam Leffler serv->retransmissions, 144539beb93cSSam Leffler serv->responses, 144639beb93cSSam Leffler serv->malformed_responses, 144739beb93cSSam Leffler serv->bad_authenticators, 144839beb93cSSam Leffler pending, 144939beb93cSSam Leffler serv->timeouts, 145039beb93cSSam Leffler serv->unknown_types, 145139beb93cSSam Leffler serv->packets_dropped); 145239beb93cSSam Leffler } 145339beb93cSSam Leffler 145439beb93cSSam Leffler 1455*e28a4053SRui Paulo /** 1456*e28a4053SRui Paulo * radius_client_get_mib - Get RADIUS client MIB information 1457*e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 1458*e28a4053SRui Paulo * @buf: Buffer for returning MIB data in text format 1459*e28a4053SRui Paulo * @buflen: Maximum buf length in octets 1460*e28a4053SRui Paulo * Returns: Number of octets written into the buffer 1461*e28a4053SRui Paulo */ 146239beb93cSSam Leffler int radius_client_get_mib(struct radius_client_data *radius, char *buf, 146339beb93cSSam Leffler size_t buflen) 146439beb93cSSam Leffler { 146539beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 146639beb93cSSam Leffler int i; 146739beb93cSSam Leffler struct hostapd_radius_server *serv; 146839beb93cSSam Leffler int count = 0; 146939beb93cSSam Leffler 147039beb93cSSam Leffler if (conf->auth_servers) { 147139beb93cSSam Leffler for (i = 0; i < conf->num_auth_servers; i++) { 147239beb93cSSam Leffler serv = &conf->auth_servers[i]; 147339beb93cSSam Leffler count += radius_client_dump_auth_server( 147439beb93cSSam Leffler buf + count, buflen - count, serv, 147539beb93cSSam Leffler serv == conf->auth_server ? 147639beb93cSSam Leffler radius : NULL); 147739beb93cSSam Leffler } 147839beb93cSSam Leffler } 147939beb93cSSam Leffler 148039beb93cSSam Leffler if (conf->acct_servers) { 148139beb93cSSam Leffler for (i = 0; i < conf->num_acct_servers; i++) { 148239beb93cSSam Leffler serv = &conf->acct_servers[i]; 148339beb93cSSam Leffler count += radius_client_dump_acct_server( 148439beb93cSSam Leffler buf + count, buflen - count, serv, 148539beb93cSSam Leffler serv == conf->acct_server ? 148639beb93cSSam Leffler radius : NULL); 148739beb93cSSam Leffler } 148839beb93cSSam Leffler } 148939beb93cSSam Leffler 149039beb93cSSam Leffler return count; 149139beb93cSSam Leffler } 1492