xref: /freebsd/contrib/wpa/src/radius/radius_client.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
139beb93cSSam Leffler /*
2e28a4053SRui Paulo  * RADIUS client
3*a90b9d01SCy Schubert  * Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
939beb93cSSam Leffler #include "includes.h"
10*a90b9d01SCy Schubert #include <fcntl.h>
11c1d255d3SCy Schubert #include <net/if.h>
1239beb93cSSam Leffler 
1339beb93cSSam Leffler #include "common.h"
14*a90b9d01SCy Schubert #include "eloop.h"
15*a90b9d01SCy Schubert #include "crypto/tls.h"
1639beb93cSSam Leffler #include "radius.h"
1739beb93cSSam Leffler #include "radius_client.h"
1839beb93cSSam Leffler 
1939beb93cSSam Leffler /* Defaults for RADIUS retransmit values (exponential backoff) */
20e28a4053SRui Paulo 
21e28a4053SRui Paulo /**
22e28a4053SRui Paulo  * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds
23e28a4053SRui Paulo  */
24e28a4053SRui Paulo #define RADIUS_CLIENT_FIRST_WAIT 3
25e28a4053SRui Paulo 
26e28a4053SRui Paulo /**
27e28a4053SRui Paulo  * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds
28e28a4053SRui Paulo  */
29e28a4053SRui Paulo #define RADIUS_CLIENT_MAX_WAIT 120
30e28a4053SRui Paulo 
31e28a4053SRui Paulo /**
324bc52338SCy Schubert  * RADIUS_CLIENT_MAX_FAILOVER - RADIUS client maximum retries
33e28a4053SRui Paulo  *
344bc52338SCy Schubert  * Maximum number of server failovers before the entry is removed from
35e28a4053SRui Paulo  * retransmit list.
36e28a4053SRui Paulo  */
374bc52338SCy Schubert #define RADIUS_CLIENT_MAX_FAILOVER 3
38e28a4053SRui Paulo 
39e28a4053SRui Paulo /**
40e28a4053SRui Paulo  * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages
41e28a4053SRui Paulo  *
42e28a4053SRui Paulo  * Maximum number of entries in retransmit list (oldest entries will be
43e28a4053SRui Paulo  * removed, if this limit is exceeded).
44e28a4053SRui Paulo  */
45e28a4053SRui Paulo #define RADIUS_CLIENT_MAX_ENTRIES 30
46e28a4053SRui Paulo 
47e28a4053SRui Paulo /**
48e28a4053SRui Paulo  * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point
49e28a4053SRui Paulo  *
50e28a4053SRui Paulo  * The number of failed retry attempts after which the RADIUS server will be
51e28a4053SRui Paulo  * changed (if one of more backup servers are configured).
52e28a4053SRui Paulo  */
53e28a4053SRui Paulo #define RADIUS_CLIENT_NUM_FAILOVER 4
5439beb93cSSam Leffler 
5539beb93cSSam Leffler 
56e28a4053SRui Paulo /**
57e28a4053SRui Paulo  * struct radius_rx_handler - RADIUS client RX handler
58e28a4053SRui Paulo  *
59e28a4053SRui Paulo  * This data structure is used internally inside the RADIUS client module to
60e28a4053SRui Paulo  * store registered RX handlers. These handlers are registered by calls to
61e28a4053SRui Paulo  * radius_client_register() and unregistered when the RADIUS client is
62e28a4053SRui Paulo  * deinitialized with a call to radius_client_deinit().
63e28a4053SRui Paulo  */
6439beb93cSSam Leffler struct radius_rx_handler {
65e28a4053SRui Paulo 	/**
66e28a4053SRui Paulo 	 * handler - Received RADIUS message handler
67e28a4053SRui Paulo 	 */
6839beb93cSSam Leffler 	RadiusRxResult (*handler)(struct radius_msg *msg,
6939beb93cSSam Leffler 				  struct radius_msg *req,
7039beb93cSSam Leffler 				  const u8 *shared_secret,
7139beb93cSSam Leffler 				  size_t shared_secret_len,
7239beb93cSSam Leffler 				  void *data);
73e28a4053SRui Paulo 
74e28a4053SRui Paulo 	/**
75e28a4053SRui Paulo 	 * data - Context data for the handler
76e28a4053SRui Paulo 	 */
7739beb93cSSam Leffler 	void *data;
7839beb93cSSam Leffler };
7939beb93cSSam Leffler 
8039beb93cSSam Leffler 
81e28a4053SRui Paulo /**
82e28a4053SRui Paulo  * struct radius_msg_list - RADIUS client message retransmit list
83e28a4053SRui Paulo  *
84e28a4053SRui Paulo  * This data structure is used internally inside the RADIUS client module to
85e28a4053SRui Paulo  * store pending RADIUS requests that may still need to be retransmitted.
86e28a4053SRui Paulo  */
8739beb93cSSam Leffler struct radius_msg_list {
88e28a4053SRui Paulo 	/**
89e28a4053SRui Paulo 	 * addr - STA/client address
90e28a4053SRui Paulo 	 *
91e28a4053SRui Paulo 	 * This is used to find RADIUS messages for the same STA.
92e28a4053SRui Paulo 	 */
93e28a4053SRui Paulo 	u8 addr[ETH_ALEN];
94e28a4053SRui Paulo 
95e28a4053SRui Paulo 	/**
96e28a4053SRui Paulo 	 * msg - RADIUS message
97e28a4053SRui Paulo 	 */
9839beb93cSSam Leffler 	struct radius_msg *msg;
99e28a4053SRui Paulo 
100e28a4053SRui Paulo 	/**
101e28a4053SRui Paulo 	 * msg_type - Message type
102e28a4053SRui Paulo 	 */
10339beb93cSSam Leffler 	RadiusType msg_type;
104e28a4053SRui Paulo 
105e28a4053SRui Paulo 	/**
106e28a4053SRui Paulo 	 * first_try - Time of the first transmission attempt
107e28a4053SRui Paulo 	 */
10839beb93cSSam Leffler 	os_time_t first_try;
109e28a4053SRui Paulo 
110e28a4053SRui Paulo 	/**
111e28a4053SRui Paulo 	 * next_try - Time for the next transmission attempt
112e28a4053SRui Paulo 	 */
11339beb93cSSam Leffler 	os_time_t next_try;
114e28a4053SRui Paulo 
115e28a4053SRui Paulo 	/**
1164bc52338SCy Schubert 	 * attempts - Number of transmission attempts for one server
117e28a4053SRui Paulo 	 */
11839beb93cSSam Leffler 	int attempts;
119e28a4053SRui Paulo 
120e28a4053SRui Paulo 	/**
1214bc52338SCy Schubert 	 * accu_attempts - Number of accumulated attempts
1224bc52338SCy Schubert 	 */
1234bc52338SCy Schubert 	int accu_attempts;
1244bc52338SCy Schubert 
1254bc52338SCy Schubert 	/**
126e28a4053SRui Paulo 	 * next_wait - Next retransmission wait time in seconds
127e28a4053SRui Paulo 	 */
12839beb93cSSam Leffler 	int next_wait;
129e28a4053SRui Paulo 
130e28a4053SRui Paulo 	/**
131e28a4053SRui Paulo 	 * last_attempt - Time of the last transmission attempt
132e28a4053SRui Paulo 	 */
1335b9c547cSRui Paulo 	struct os_reltime last_attempt;
13439beb93cSSam Leffler 
135e28a4053SRui Paulo 	/**
136e28a4053SRui Paulo 	 * shared_secret - Shared secret with the target RADIUS server
137e28a4053SRui Paulo 	 */
138e28a4053SRui Paulo 	const u8 *shared_secret;
139e28a4053SRui Paulo 
140e28a4053SRui Paulo 	/**
141e28a4053SRui Paulo 	 * shared_secret_len - shared_secret length in octets
142e28a4053SRui Paulo 	 */
14339beb93cSSam Leffler 	size_t shared_secret_len;
14439beb93cSSam Leffler 
14539beb93cSSam Leffler 	/* TODO: server config with failover to backup server(s) */
14639beb93cSSam Leffler 
147e28a4053SRui Paulo 	/**
148e28a4053SRui Paulo 	 * next - Next message in the list
149e28a4053SRui Paulo 	 */
15039beb93cSSam Leffler 	struct radius_msg_list *next;
15139beb93cSSam Leffler };
15239beb93cSSam Leffler 
15339beb93cSSam Leffler 
154e28a4053SRui Paulo /**
155e28a4053SRui Paulo  * struct radius_client_data - Internal RADIUS client data
156e28a4053SRui Paulo  *
157e28a4053SRui Paulo  * This data structure is used internally inside the RADIUS client module.
158e28a4053SRui Paulo  * External users allocate this by calling radius_client_init() and free it by
159e28a4053SRui Paulo  * calling radius_client_deinit(). The pointer to this opaque data is used in
160e28a4053SRui Paulo  * calls to other functions as an identifier for the RADIUS client instance.
161e28a4053SRui Paulo  */
16239beb93cSSam Leffler struct radius_client_data {
163e28a4053SRui Paulo 	/**
164e28a4053SRui Paulo 	 * ctx - Context pointer for hostapd_logger() callbacks
165e28a4053SRui Paulo 	 */
16639beb93cSSam Leffler 	void *ctx;
167e28a4053SRui Paulo 
168e28a4053SRui Paulo 	/**
169e28a4053SRui Paulo 	 * conf - RADIUS client configuration (list of RADIUS servers to use)
170e28a4053SRui Paulo 	 */
17139beb93cSSam Leffler 	struct hostapd_radius_servers *conf;
17239beb93cSSam Leffler 
173e28a4053SRui Paulo 	/**
174e28a4053SRui Paulo 	 * auth_sock - Currently used socket for RADIUS authentication server
175e28a4053SRui Paulo 	 */
176e28a4053SRui Paulo 	int auth_sock;
177e28a4053SRui Paulo 
178e28a4053SRui Paulo 	/**
179*a90b9d01SCy Schubert 	 * auth_tls - Whether current authentication connection uses TLS
180*a90b9d01SCy Schubert 	 */
181*a90b9d01SCy Schubert 	bool auth_tls;
182*a90b9d01SCy Schubert 
183*a90b9d01SCy Schubert 	/**
184*a90b9d01SCy Schubert 	 * auth_tls_ready - Whether authentication TLS is ready
185*a90b9d01SCy Schubert 	 */
186*a90b9d01SCy Schubert 	bool auth_tls_ready;
187*a90b9d01SCy Schubert 
188*a90b9d01SCy Schubert 	/**
189e28a4053SRui Paulo 	 * acct_sock - Currently used socket for RADIUS accounting server
190e28a4053SRui Paulo 	 */
191e28a4053SRui Paulo 	int acct_sock;
192e28a4053SRui Paulo 
193e28a4053SRui Paulo 	/**
194*a90b9d01SCy Schubert 	 * acct_tls - Whether current accounting connection uses TLS
195*a90b9d01SCy Schubert 	 */
196*a90b9d01SCy Schubert 	bool acct_tls;
197*a90b9d01SCy Schubert 
198*a90b9d01SCy Schubert 	/**
199*a90b9d01SCy Schubert 	 * acct_tls_ready - Whether accounting TLS is ready
200*a90b9d01SCy Schubert 	 */
201*a90b9d01SCy Schubert 	bool acct_tls_ready;
202*a90b9d01SCy Schubert 
203*a90b9d01SCy Schubert 	/**
204e28a4053SRui Paulo 	 * auth_handlers - Authentication message handlers
205e28a4053SRui Paulo 	 */
20639beb93cSSam Leffler 	struct radius_rx_handler *auth_handlers;
207e28a4053SRui Paulo 
208e28a4053SRui Paulo 	/**
209e28a4053SRui Paulo 	 * num_auth_handlers - Number of handlers in auth_handlers
210e28a4053SRui Paulo 	 */
21139beb93cSSam Leffler 	size_t num_auth_handlers;
212e28a4053SRui Paulo 
213e28a4053SRui Paulo 	/**
214e28a4053SRui Paulo 	 * acct_handlers - Accounting message handlers
215e28a4053SRui Paulo 	 */
21639beb93cSSam Leffler 	struct radius_rx_handler *acct_handlers;
217e28a4053SRui Paulo 
218e28a4053SRui Paulo 	/**
219e28a4053SRui Paulo 	 * num_acct_handlers - Number of handlers in acct_handlers
220e28a4053SRui Paulo 	 */
22139beb93cSSam Leffler 	size_t num_acct_handlers;
22239beb93cSSam Leffler 
223e28a4053SRui Paulo 	/**
224e28a4053SRui Paulo 	 * msgs - Pending outgoing RADIUS messages
225e28a4053SRui Paulo 	 */
22639beb93cSSam Leffler 	struct radius_msg_list *msgs;
227e28a4053SRui Paulo 
228e28a4053SRui Paulo 	/**
229e28a4053SRui Paulo 	 * num_msgs - Number of pending messages in the msgs list
230e28a4053SRui Paulo 	 */
23139beb93cSSam Leffler 	size_t num_msgs;
23239beb93cSSam Leffler 
233e28a4053SRui Paulo 	/**
234e28a4053SRui Paulo 	 * next_radius_identifier - Next RADIUS message identifier to use
235e28a4053SRui Paulo 	 */
23639beb93cSSam Leffler 	u8 next_radius_identifier;
237780fb4a2SCy Schubert 
238780fb4a2SCy Schubert 	/**
239780fb4a2SCy Schubert 	 * interim_error_cb - Interim accounting error callback
240780fb4a2SCy Schubert 	 */
241780fb4a2SCy Schubert 	void (*interim_error_cb)(const u8 *addr, void *ctx);
242780fb4a2SCy Schubert 
243780fb4a2SCy Schubert 	/**
244780fb4a2SCy Schubert 	 * interim_error_cb_ctx - interim_error_cb() context data
245780fb4a2SCy Schubert 	 */
246780fb4a2SCy Schubert 	void *interim_error_cb_ctx;
247*a90b9d01SCy Schubert 
248*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
249*a90b9d01SCy Schubert 	void *tls_ctx;
250*a90b9d01SCy Schubert 	struct tls_connection *auth_tls_conn;
251*a90b9d01SCy Schubert 	struct tls_connection *acct_tls_conn;
252*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
25339beb93cSSam Leffler };
25439beb93cSSam Leffler 
25539beb93cSSam Leffler 
25639beb93cSSam Leffler static int
25739beb93cSSam Leffler radius_change_server(struct radius_client_data *radius,
25839beb93cSSam Leffler 		     struct hostapd_radius_server *nserv,
25939beb93cSSam Leffler 		     struct hostapd_radius_server *oserv,
260*a90b9d01SCy Schubert 		     int auth);
26139beb93cSSam Leffler static int radius_client_init_acct(struct radius_client_data *radius);
26239beb93cSSam Leffler static int radius_client_init_auth(struct radius_client_data *radius);
2635b9c547cSRui Paulo static void radius_client_auth_failover(struct radius_client_data *radius);
2645b9c547cSRui Paulo static void radius_client_acct_failover(struct radius_client_data *radius);
26539beb93cSSam Leffler 
26639beb93cSSam Leffler 
radius_client_msg_free(struct radius_msg_list * req)26739beb93cSSam Leffler static void radius_client_msg_free(struct radius_msg_list *req)
26839beb93cSSam Leffler {
26939beb93cSSam Leffler 	radius_msg_free(req->msg);
27039beb93cSSam Leffler 	os_free(req);
27139beb93cSSam Leffler }
27239beb93cSSam Leffler 
27339beb93cSSam Leffler 
274e28a4053SRui Paulo /**
275e28a4053SRui Paulo  * radius_client_register - Register a RADIUS client RX handler
276e28a4053SRui Paulo  * @radius: RADIUS client context from radius_client_init()
277e28a4053SRui Paulo  * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT)
278e28a4053SRui Paulo  * @handler: Handler for received RADIUS messages
279e28a4053SRui Paulo  * @data: Context pointer for handler callbacks
280e28a4053SRui Paulo  * Returns: 0 on success, -1 on failure
281e28a4053SRui Paulo  *
282e28a4053SRui Paulo  * This function is used to register a handler for processing received RADIUS
283e28a4053SRui Paulo  * authentication and accounting messages. The handler() callback function will
284e28a4053SRui Paulo  * be called whenever a RADIUS message is received from the active server.
285e28a4053SRui Paulo  *
286e28a4053SRui Paulo  * There can be multiple registered RADIUS message handlers. The handlers will
287e28a4053SRui Paulo  * be called in order until one of them indicates that it has processed or
288e28a4053SRui Paulo  * queued the message.
289e28a4053SRui Paulo  */
radius_client_register(struct radius_client_data * radius,RadiusType msg_type,RadiusRxResult (* handler)(struct radius_msg * msg,struct radius_msg * req,const u8 * shared_secret,size_t shared_secret_len,void * data),void * data)29039beb93cSSam Leffler int radius_client_register(struct radius_client_data *radius,
29139beb93cSSam Leffler 			   RadiusType msg_type,
29239beb93cSSam Leffler 			   RadiusRxResult (*handler)(struct radius_msg *msg,
29339beb93cSSam Leffler 						     struct radius_msg *req,
29439beb93cSSam Leffler 						     const u8 *shared_secret,
29539beb93cSSam Leffler 						     size_t shared_secret_len,
29639beb93cSSam Leffler 						     void *data),
29739beb93cSSam Leffler 			   void *data)
29839beb93cSSam Leffler {
29939beb93cSSam Leffler 	struct radius_rx_handler **handlers, *newh;
30039beb93cSSam Leffler 	size_t *num;
30139beb93cSSam Leffler 
30239beb93cSSam Leffler 	if (msg_type == RADIUS_ACCT) {
30339beb93cSSam Leffler 		handlers = &radius->acct_handlers;
30439beb93cSSam Leffler 		num = &radius->num_acct_handlers;
30539beb93cSSam Leffler 	} else {
30639beb93cSSam Leffler 		handlers = &radius->auth_handlers;
30739beb93cSSam Leffler 		num = &radius->num_auth_handlers;
30839beb93cSSam Leffler 	}
30939beb93cSSam Leffler 
310f05cddf9SRui Paulo 	newh = os_realloc_array(*handlers, *num + 1,
311f05cddf9SRui Paulo 				sizeof(struct radius_rx_handler));
31239beb93cSSam Leffler 	if (newh == NULL)
31339beb93cSSam Leffler 		return -1;
31439beb93cSSam Leffler 
31539beb93cSSam Leffler 	newh[*num].handler = handler;
31639beb93cSSam Leffler 	newh[*num].data = data;
31739beb93cSSam Leffler 	(*num)++;
31839beb93cSSam Leffler 	*handlers = newh;
31939beb93cSSam Leffler 
32039beb93cSSam Leffler 	return 0;
32139beb93cSSam Leffler }
32239beb93cSSam Leffler 
32339beb93cSSam Leffler 
324780fb4a2SCy Schubert /**
325780fb4a2SCy Schubert  * radius_client_set_interim_erro_cb - Register an interim acct error callback
326780fb4a2SCy Schubert  * @radius: RADIUS client context from radius_client_init()
327780fb4a2SCy Schubert  * @addr: Station address from the failed message
328780fb4a2SCy Schubert  * @cb: Handler for interim accounting errors
329780fb4a2SCy Schubert  * @ctx: Context pointer for handler callbacks
330780fb4a2SCy Schubert  *
331780fb4a2SCy Schubert  * This function is used to register a handler for processing failed
332780fb4a2SCy Schubert  * transmission attempts of interim accounting update messages.
333780fb4a2SCy Schubert  */
radius_client_set_interim_error_cb(struct radius_client_data * radius,void (* cb)(const u8 * addr,void * ctx),void * ctx)334780fb4a2SCy Schubert void radius_client_set_interim_error_cb(struct radius_client_data *radius,
335780fb4a2SCy Schubert 					void (*cb)(const u8 *addr, void *ctx),
336780fb4a2SCy Schubert 					void *ctx)
337780fb4a2SCy Schubert {
338780fb4a2SCy Schubert 	radius->interim_error_cb = cb;
339780fb4a2SCy Schubert 	radius->interim_error_cb_ctx = ctx;
340780fb4a2SCy Schubert }
341780fb4a2SCy Schubert 
342780fb4a2SCy Schubert 
3435b9c547cSRui Paulo /*
3445b9c547cSRui Paulo  * Returns >0 if message queue was flushed (i.e., the message that triggered
3455b9c547cSRui Paulo  * the error is not available anymore)
3465b9c547cSRui Paulo  */
radius_client_handle_send_error(struct radius_client_data * radius,int s,RadiusType msg_type)3475b9c547cSRui Paulo static int radius_client_handle_send_error(struct radius_client_data *radius,
34839beb93cSSam Leffler 					   int s, RadiusType msg_type)
34939beb93cSSam Leffler {
35039beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS
35139beb93cSSam Leffler 	int _errno = errno;
3525b9c547cSRui Paulo 	wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno));
35339beb93cSSam Leffler 	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
354780fb4a2SCy Schubert 	    _errno == EBADF || _errno == ENETUNREACH || _errno == EACCES) {
35539beb93cSSam Leffler 		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
35639beb93cSSam Leffler 			       HOSTAPD_LEVEL_INFO,
35739beb93cSSam Leffler 			       "Send failed - maybe interface status changed -"
35839beb93cSSam Leffler 			       " try to connect again");
3595b9c547cSRui Paulo 		if (msg_type == RADIUS_ACCT ||
3605b9c547cSRui Paulo 		    msg_type == RADIUS_ACCT_INTERIM) {
36139beb93cSSam Leffler 			radius_client_init_acct(radius);
3625b9c547cSRui Paulo 			return 0;
3635b9c547cSRui Paulo 		} else {
36439beb93cSSam Leffler 			radius_client_init_auth(radius);
3655b9c547cSRui Paulo 			return 1;
3665b9c547cSRui Paulo 		}
36739beb93cSSam Leffler 	}
36839beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */
3695b9c547cSRui Paulo 
3705b9c547cSRui Paulo 	return 0;
37139beb93cSSam Leffler }
37239beb93cSSam Leffler 
37339beb93cSSam Leffler 
radius_client_retransmit(struct radius_client_data * radius,struct radius_msg_list * entry,os_time_t now)37439beb93cSSam Leffler static int radius_client_retransmit(struct radius_client_data *radius,
37539beb93cSSam Leffler 				    struct radius_msg_list *entry,
37639beb93cSSam Leffler 				    os_time_t now)
37739beb93cSSam Leffler {
37839beb93cSSam Leffler 	struct hostapd_radius_servers *conf = radius->conf;
37939beb93cSSam Leffler 	int s;
380e28a4053SRui Paulo 	struct wpabuf *buf;
3815b9c547cSRui Paulo 	size_t prev_num_msgs;
382780fb4a2SCy Schubert 	u8 *acct_delay_time;
383780fb4a2SCy Schubert 	size_t acct_delay_time_len;
3844bc52338SCy Schubert 	int num_servers;
385*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
386*a90b9d01SCy Schubert 	struct wpabuf *out = NULL;
387*a90b9d01SCy Schubert 	struct tls_connection *conn = NULL;
388*a90b9d01SCy Schubert 	bool acct = false;
389*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
39039beb93cSSam Leffler 
39139beb93cSSam Leffler 	if (entry->msg_type == RADIUS_ACCT ||
39239beb93cSSam Leffler 	    entry->msg_type == RADIUS_ACCT_INTERIM) {
393*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
394*a90b9d01SCy Schubert 		acct = true;
395*a90b9d01SCy Schubert 		if (radius->acct_tls)
396*a90b9d01SCy Schubert 			conn = radius->acct_tls_conn;
397*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
3984bc52338SCy Schubert 		num_servers = conf->num_acct_servers;
3995b9c547cSRui Paulo 		if (radius->acct_sock < 0)
4005b9c547cSRui Paulo 			radius_client_init_acct(radius);
4015b9c547cSRui Paulo 		if (radius->acct_sock < 0 && conf->num_acct_servers > 1) {
4025b9c547cSRui Paulo 			prev_num_msgs = radius->num_msgs;
4035b9c547cSRui Paulo 			radius_client_acct_failover(radius);
4045b9c547cSRui Paulo 			if (prev_num_msgs != radius->num_msgs)
4055b9c547cSRui Paulo 				return 0;
4065b9c547cSRui Paulo 		}
40739beb93cSSam Leffler 		s = radius->acct_sock;
40839beb93cSSam Leffler 		if (entry->attempts == 0)
40939beb93cSSam Leffler 			conf->acct_server->requests++;
41039beb93cSSam Leffler 		else {
41139beb93cSSam Leffler 			conf->acct_server->timeouts++;
41239beb93cSSam Leffler 			conf->acct_server->retransmissions++;
41339beb93cSSam Leffler 		}
41439beb93cSSam Leffler 	} else {
415*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
416*a90b9d01SCy Schubert 		if (radius->auth_tls)
417*a90b9d01SCy Schubert 			conn = radius->auth_tls_conn;
418*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
4194bc52338SCy Schubert 		num_servers = conf->num_auth_servers;
4205b9c547cSRui Paulo 		if (radius->auth_sock < 0)
4215b9c547cSRui Paulo 			radius_client_init_auth(radius);
4225b9c547cSRui Paulo 		if (radius->auth_sock < 0 && conf->num_auth_servers > 1) {
4235b9c547cSRui Paulo 			prev_num_msgs = radius->num_msgs;
4245b9c547cSRui Paulo 			radius_client_auth_failover(radius);
4255b9c547cSRui Paulo 			if (prev_num_msgs != radius->num_msgs)
4265b9c547cSRui Paulo 				return 0;
4275b9c547cSRui Paulo 		}
42839beb93cSSam Leffler 		s = radius->auth_sock;
42939beb93cSSam Leffler 		if (entry->attempts == 0)
43039beb93cSSam Leffler 			conf->auth_server->requests++;
43139beb93cSSam Leffler 		else {
43239beb93cSSam Leffler 			conf->auth_server->timeouts++;
43339beb93cSSam Leffler 			conf->auth_server->retransmissions++;
43439beb93cSSam Leffler 		}
43539beb93cSSam Leffler 	}
436780fb4a2SCy Schubert 
437780fb4a2SCy Schubert 	if (entry->msg_type == RADIUS_ACCT_INTERIM) {
438780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG,
439780fb4a2SCy Schubert 			   "RADIUS: Failed to transmit interim accounting update to "
440780fb4a2SCy Schubert 			   MACSTR " - drop message and request a new update",
441780fb4a2SCy Schubert 			   MAC2STR(entry->addr));
442780fb4a2SCy Schubert 		if (radius->interim_error_cb)
443780fb4a2SCy Schubert 			radius->interim_error_cb(entry->addr,
444780fb4a2SCy Schubert 						 radius->interim_error_cb_ctx);
445780fb4a2SCy Schubert 		return 1;
446780fb4a2SCy Schubert 	}
447780fb4a2SCy Schubert 
4485b9c547cSRui Paulo 	if (s < 0) {
4495b9c547cSRui Paulo 		wpa_printf(MSG_INFO,
4505b9c547cSRui Paulo 			   "RADIUS: No valid socket for retransmission");
4515b9c547cSRui Paulo 		return 1;
4525b9c547cSRui Paulo 	}
45339beb93cSSam Leffler 
454*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
455*a90b9d01SCy Schubert 	if ((acct && radius->acct_tls && !radius->acct_tls_ready) ||
456*a90b9d01SCy Schubert 	    (!acct && radius->auth_tls && !radius->auth_tls_ready)) {
457*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
458*a90b9d01SCy Schubert 			   "RADIUS: TLS connection not yet ready for TX");
459*a90b9d01SCy Schubert 		goto not_ready;
460*a90b9d01SCy Schubert 	}
461*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
462*a90b9d01SCy Schubert 
463780fb4a2SCy Schubert 	if (entry->msg_type == RADIUS_ACCT &&
464780fb4a2SCy Schubert 	    radius_msg_get_attr_ptr(entry->msg, RADIUS_ATTR_ACCT_DELAY_TIME,
465780fb4a2SCy Schubert 				    &acct_delay_time, &acct_delay_time_len,
466780fb4a2SCy Schubert 				    NULL) == 0 &&
467780fb4a2SCy Schubert 	    acct_delay_time_len == 4) {
468780fb4a2SCy Schubert 		struct radius_hdr *hdr;
469780fb4a2SCy Schubert 		u32 delay_time;
470780fb4a2SCy Schubert 
471780fb4a2SCy Schubert 		/*
472780fb4a2SCy Schubert 		 * Need to assign a new identifier since attribute contents
473780fb4a2SCy Schubert 		 * changes.
474780fb4a2SCy Schubert 		 */
475780fb4a2SCy Schubert 		hdr = radius_msg_get_hdr(entry->msg);
476780fb4a2SCy Schubert 		hdr->identifier = radius_client_get_id(radius);
477780fb4a2SCy Schubert 
478780fb4a2SCy Schubert 		/* Update Acct-Delay-Time to show wait time in queue */
479780fb4a2SCy Schubert 		delay_time = now - entry->first_try;
480780fb4a2SCy Schubert 		WPA_PUT_BE32(acct_delay_time, delay_time);
481780fb4a2SCy Schubert 
482780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG,
483780fb4a2SCy Schubert 			   "RADIUS: Updated Acct-Delay-Time to %u for retransmission",
484780fb4a2SCy Schubert 			   delay_time);
485780fb4a2SCy Schubert 		radius_msg_finish_acct(entry->msg, entry->shared_secret,
486780fb4a2SCy Schubert 				       entry->shared_secret_len);
487780fb4a2SCy Schubert 		if (radius->conf->msg_dumps)
488780fb4a2SCy Schubert 			radius_msg_dump(entry->msg);
489780fb4a2SCy Schubert 	}
490780fb4a2SCy Schubert 
49139beb93cSSam Leffler 	/* retransmit; remove entry if too many attempts */
492c1d255d3SCy Schubert 	if (entry->accu_attempts >= RADIUS_CLIENT_MAX_FAILOVER *
4934bc52338SCy Schubert 	    RADIUS_CLIENT_NUM_FAILOVER * num_servers) {
4944bc52338SCy Schubert 		wpa_printf(MSG_INFO,
4954bc52338SCy Schubert 			   "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts");
4964bc52338SCy Schubert 		return 1;
4974bc52338SCy Schubert 	}
4984bc52338SCy Schubert 
49939beb93cSSam Leffler 	entry->attempts++;
5004bc52338SCy Schubert 	entry->accu_attempts++;
50139beb93cSSam Leffler 	hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,
50239beb93cSSam Leffler 		       HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
503e28a4053SRui Paulo 		       radius_msg_get_hdr(entry->msg)->identifier);
50439beb93cSSam Leffler 
5055b9c547cSRui Paulo 	os_get_reltime(&entry->last_attempt);
506e28a4053SRui Paulo 	buf = radius_msg_get_buf(entry->msg);
507*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
508*a90b9d01SCy Schubert 	if (conn) {
509*a90b9d01SCy Schubert 		out = tls_connection_encrypt(radius->tls_ctx, conn, buf);
510*a90b9d01SCy Schubert 		if (!out) {
511*a90b9d01SCy Schubert 			wpa_printf(MSG_INFO,
512*a90b9d01SCy Schubert 				   "RADIUS: Failed to encrypt RADIUS message (TLS)");
513*a90b9d01SCy Schubert 			return -1;
514*a90b9d01SCy Schubert 		}
515*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
516*a90b9d01SCy Schubert 			   "RADIUS: TLS encryption of %zu bytes of plaintext to %zu bytes of ciphertext",
517*a90b9d01SCy Schubert 			   wpabuf_len(buf), wpabuf_len(out));
518*a90b9d01SCy Schubert 		buf = out;
519*a90b9d01SCy Schubert 	}
520*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
521*a90b9d01SCy Schubert 
522*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG, "RADIUS: Send %zu bytes to the server",
523*a90b9d01SCy Schubert 		   wpabuf_len(buf));
5245b9c547cSRui Paulo 	if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
5255b9c547cSRui Paulo 		if (radius_client_handle_send_error(radius, s, entry->msg_type)
526*a90b9d01SCy Schubert 		    > 0) {
527*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
528*a90b9d01SCy Schubert 			wpabuf_free(out);
529*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
5305b9c547cSRui Paulo 			return 0;
5315b9c547cSRui Paulo 		}
532*a90b9d01SCy Schubert 	}
533*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
534*a90b9d01SCy Schubert 	wpabuf_free(out);
535*a90b9d01SCy Schubert 
536*a90b9d01SCy Schubert not_ready:
537*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
53839beb93cSSam Leffler 
53939beb93cSSam Leffler 	entry->next_try = now + entry->next_wait;
54039beb93cSSam Leffler 	entry->next_wait *= 2;
54139beb93cSSam Leffler 	if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
54239beb93cSSam Leffler 		entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
54339beb93cSSam Leffler 
54439beb93cSSam Leffler 	return 0;
54539beb93cSSam Leffler }
54639beb93cSSam Leffler 
54739beb93cSSam Leffler 
radius_client_timer(void * eloop_ctx,void * timeout_ctx)54839beb93cSSam Leffler static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
54939beb93cSSam Leffler {
55039beb93cSSam Leffler 	struct radius_client_data *radius = eloop_ctx;
5515b9c547cSRui Paulo 	struct os_reltime now;
55239beb93cSSam Leffler 	os_time_t first;
55339beb93cSSam Leffler 	struct radius_msg_list *entry, *prev, *tmp;
55439beb93cSSam Leffler 	int auth_failover = 0, acct_failover = 0;
5555b9c547cSRui Paulo 	size_t prev_num_msgs;
5565b9c547cSRui Paulo 	int s;
55739beb93cSSam Leffler 
55839beb93cSSam Leffler 	entry = radius->msgs;
55939beb93cSSam Leffler 	if (!entry)
56039beb93cSSam Leffler 		return;
56139beb93cSSam Leffler 
5625b9c547cSRui Paulo 	os_get_reltime(&now);
5634bc52338SCy Schubert 
5644bc52338SCy Schubert 	while (entry) {
5654bc52338SCy Schubert 		if (now.sec >= entry->next_try) {
5664bc52338SCy Schubert 			s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock :
5674bc52338SCy Schubert 				radius->acct_sock;
568c1d255d3SCy Schubert 			if (entry->attempts >= RADIUS_CLIENT_NUM_FAILOVER ||
5694bc52338SCy Schubert 			    (s < 0 && entry->attempts > 0)) {
5704bc52338SCy Schubert 				if (entry->msg_type == RADIUS_ACCT ||
5714bc52338SCy Schubert 				    entry->msg_type == RADIUS_ACCT_INTERIM)
5724bc52338SCy Schubert 					acct_failover++;
5734bc52338SCy Schubert 				else
5744bc52338SCy Schubert 					auth_failover++;
5754bc52338SCy Schubert 			}
5764bc52338SCy Schubert 		}
5774bc52338SCy Schubert 		entry = entry->next;
5784bc52338SCy Schubert 	}
5794bc52338SCy Schubert 
5804bc52338SCy Schubert 	if (auth_failover)
5814bc52338SCy Schubert 		radius_client_auth_failover(radius);
5824bc52338SCy Schubert 
5834bc52338SCy Schubert 	if (acct_failover)
5844bc52338SCy Schubert 		radius_client_acct_failover(radius);
5854bc52338SCy Schubert 
5864bc52338SCy Schubert 	entry = radius->msgs;
58739beb93cSSam Leffler 	first = 0;
58839beb93cSSam Leffler 
58939beb93cSSam Leffler 	prev = NULL;
59039beb93cSSam Leffler 	while (entry) {
5915b9c547cSRui Paulo 		prev_num_msgs = radius->num_msgs;
59239beb93cSSam Leffler 		if (now.sec >= entry->next_try &&
59339beb93cSSam Leffler 		    radius_client_retransmit(radius, entry, now.sec)) {
59439beb93cSSam Leffler 			if (prev)
59539beb93cSSam Leffler 				prev->next = entry->next;
59639beb93cSSam Leffler 			else
59739beb93cSSam Leffler 				radius->msgs = entry->next;
59839beb93cSSam Leffler 
59939beb93cSSam Leffler 			tmp = entry;
60039beb93cSSam Leffler 			entry = entry->next;
60139beb93cSSam Leffler 			radius_client_msg_free(tmp);
60239beb93cSSam Leffler 			radius->num_msgs--;
60339beb93cSSam Leffler 			continue;
60439beb93cSSam Leffler 		}
60539beb93cSSam Leffler 
6065b9c547cSRui Paulo 		if (prev_num_msgs != radius->num_msgs) {
6075b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG,
6085b9c547cSRui Paulo 				   "RADIUS: Message removed from queue - restart from beginning");
6095b9c547cSRui Paulo 			entry = radius->msgs;
6105b9c547cSRui Paulo 			prev = NULL;
6115b9c547cSRui Paulo 			continue;
6125b9c547cSRui Paulo 		}
6135b9c547cSRui Paulo 
61439beb93cSSam Leffler 		if (first == 0 || entry->next_try < first)
61539beb93cSSam Leffler 			first = entry->next_try;
61639beb93cSSam Leffler 
61739beb93cSSam Leffler 		prev = entry;
61839beb93cSSam Leffler 		entry = entry->next;
61939beb93cSSam Leffler 	}
62039beb93cSSam Leffler 
62139beb93cSSam Leffler 	if (radius->msgs) {
62239beb93cSSam Leffler 		if (first < now.sec)
62339beb93cSSam Leffler 			first = now.sec;
6244bc52338SCy Schubert 		eloop_cancel_timeout(radius_client_timer, radius, NULL);
62539beb93cSSam Leffler 		eloop_register_timeout(first - now.sec, 0,
62639beb93cSSam Leffler 				       radius_client_timer, radius, NULL);
62739beb93cSSam Leffler 		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
62839beb93cSSam Leffler 			       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client "
62939beb93cSSam Leffler 			       "retransmit in %ld seconds",
63039beb93cSSam Leffler 			       (long int) (first - now.sec));
63139beb93cSSam Leffler 	}
6325b9c547cSRui Paulo }
6335b9c547cSRui Paulo 
6345b9c547cSRui Paulo 
radius_client_auth_failover(struct radius_client_data * radius)6355b9c547cSRui Paulo static void radius_client_auth_failover(struct radius_client_data *radius)
6365b9c547cSRui Paulo {
6375b9c547cSRui Paulo 	struct hostapd_radius_servers *conf = radius->conf;
63839beb93cSSam Leffler 	struct hostapd_radius_server *next, *old;
6395b9c547cSRui Paulo 	struct radius_msg_list *entry;
6405b9c547cSRui Paulo 	char abuf[50];
6415b9c547cSRui Paulo 
64239beb93cSSam Leffler 	old = conf->auth_server;
64339beb93cSSam Leffler 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
64439beb93cSSam Leffler 		       HOSTAPD_LEVEL_NOTICE,
6455b9c547cSRui Paulo 		       "No response from Authentication server %s:%d - failover",
64639beb93cSSam Leffler 		       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
64739beb93cSSam Leffler 		       old->port);
64839beb93cSSam Leffler 
64939beb93cSSam Leffler 	for (entry = radius->msgs; entry; entry = entry->next) {
65039beb93cSSam Leffler 		if (entry->msg_type == RADIUS_AUTH)
65139beb93cSSam Leffler 			old->timeouts++;
65239beb93cSSam Leffler 	}
65339beb93cSSam Leffler 
65439beb93cSSam Leffler 	next = old + 1;
65539beb93cSSam Leffler 	if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
65639beb93cSSam Leffler 		next = conf->auth_servers;
65739beb93cSSam Leffler 	conf->auth_server = next;
658*a90b9d01SCy Schubert 	radius_change_server(radius, next, old, 1);
65939beb93cSSam Leffler }
66039beb93cSSam Leffler 
6615b9c547cSRui Paulo 
radius_client_acct_failover(struct radius_client_data * radius)6625b9c547cSRui Paulo static void radius_client_acct_failover(struct radius_client_data *radius)
6635b9c547cSRui Paulo {
6645b9c547cSRui Paulo 	struct hostapd_radius_servers *conf = radius->conf;
66539beb93cSSam Leffler 	struct hostapd_radius_server *next, *old;
6665b9c547cSRui Paulo 	struct radius_msg_list *entry;
6675b9c547cSRui Paulo 	char abuf[50];
6685b9c547cSRui Paulo 
66939beb93cSSam Leffler 	old = conf->acct_server;
67039beb93cSSam Leffler 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
67139beb93cSSam Leffler 		       HOSTAPD_LEVEL_NOTICE,
6725b9c547cSRui Paulo 		       "No response from Accounting server %s:%d - failover",
67339beb93cSSam Leffler 		       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
67439beb93cSSam Leffler 		       old->port);
67539beb93cSSam Leffler 
67639beb93cSSam Leffler 	for (entry = radius->msgs; entry; entry = entry->next) {
67739beb93cSSam Leffler 		if (entry->msg_type == RADIUS_ACCT ||
67839beb93cSSam Leffler 		    entry->msg_type == RADIUS_ACCT_INTERIM)
67939beb93cSSam Leffler 			old->timeouts++;
68039beb93cSSam Leffler 	}
68139beb93cSSam Leffler 
68239beb93cSSam Leffler 	next = old + 1;
68339beb93cSSam Leffler 	if (next > &conf->acct_servers[conf->num_acct_servers - 1])
68439beb93cSSam Leffler 		next = conf->acct_servers;
68539beb93cSSam Leffler 	conf->acct_server = next;
686*a90b9d01SCy Schubert 	radius_change_server(radius, next, old, 0);
68739beb93cSSam Leffler }
68839beb93cSSam Leffler 
68939beb93cSSam Leffler 
radius_client_update_timeout(struct radius_client_data * radius)69039beb93cSSam Leffler static void radius_client_update_timeout(struct radius_client_data *radius)
69139beb93cSSam Leffler {
6925b9c547cSRui Paulo 	struct os_reltime now;
69339beb93cSSam Leffler 	os_time_t first;
69439beb93cSSam Leffler 	struct radius_msg_list *entry;
69539beb93cSSam Leffler 
69639beb93cSSam Leffler 	eloop_cancel_timeout(radius_client_timer, radius, NULL);
69739beb93cSSam Leffler 
69839beb93cSSam Leffler 	if (radius->msgs == NULL) {
69939beb93cSSam Leffler 		return;
70039beb93cSSam Leffler 	}
70139beb93cSSam Leffler 
70239beb93cSSam Leffler 	first = 0;
70339beb93cSSam Leffler 	for (entry = radius->msgs; entry; entry = entry->next) {
70439beb93cSSam Leffler 		if (first == 0 || entry->next_try < first)
70539beb93cSSam Leffler 			first = entry->next_try;
70639beb93cSSam Leffler 	}
70739beb93cSSam Leffler 
7085b9c547cSRui Paulo 	os_get_reltime(&now);
70939beb93cSSam Leffler 	if (first < now.sec)
71039beb93cSSam Leffler 		first = now.sec;
71139beb93cSSam Leffler 	eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
71239beb93cSSam Leffler 			       NULL);
71339beb93cSSam Leffler 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
71439beb93cSSam Leffler 		       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
715f05cddf9SRui Paulo 		       " %ld seconds", (long int) (first - now.sec));
71639beb93cSSam Leffler }
71739beb93cSSam Leffler 
71839beb93cSSam Leffler 
radius_client_list_add(struct radius_client_data * radius,struct radius_msg * msg,RadiusType msg_type,const u8 * shared_secret,size_t shared_secret_len,const u8 * addr)71939beb93cSSam Leffler static void radius_client_list_add(struct radius_client_data *radius,
72039beb93cSSam Leffler 				   struct radius_msg *msg,
721e28a4053SRui Paulo 				   RadiusType msg_type,
722e28a4053SRui Paulo 				   const u8 *shared_secret,
72339beb93cSSam Leffler 				   size_t shared_secret_len, const u8 *addr)
72439beb93cSSam Leffler {
72539beb93cSSam Leffler 	struct radius_msg_list *entry, *prev;
72639beb93cSSam Leffler 
72739beb93cSSam Leffler 	if (eloop_terminated()) {
72839beb93cSSam Leffler 		/* No point in adding entries to retransmit queue since event
72939beb93cSSam Leffler 		 * loop has already been terminated. */
73039beb93cSSam Leffler 		radius_msg_free(msg);
73139beb93cSSam Leffler 		return;
73239beb93cSSam Leffler 	}
73339beb93cSSam Leffler 
73439beb93cSSam Leffler 	entry = os_zalloc(sizeof(*entry));
73539beb93cSSam Leffler 	if (entry == NULL) {
7365b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list");
73739beb93cSSam Leffler 		radius_msg_free(msg);
73839beb93cSSam Leffler 		return;
73939beb93cSSam Leffler 	}
74039beb93cSSam Leffler 
74139beb93cSSam Leffler 	if (addr)
74239beb93cSSam Leffler 		os_memcpy(entry->addr, addr, ETH_ALEN);
74339beb93cSSam Leffler 	entry->msg = msg;
74439beb93cSSam Leffler 	entry->msg_type = msg_type;
74539beb93cSSam Leffler 	entry->shared_secret = shared_secret;
74639beb93cSSam Leffler 	entry->shared_secret_len = shared_secret_len;
7475b9c547cSRui Paulo 	os_get_reltime(&entry->last_attempt);
74839beb93cSSam Leffler 	entry->first_try = entry->last_attempt.sec;
74939beb93cSSam Leffler 	entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
75039beb93cSSam Leffler 	entry->attempts = 1;
7514bc52338SCy Schubert 	entry->accu_attempts = 1;
75239beb93cSSam Leffler 	entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
7534bc52338SCy Schubert 	if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
7544bc52338SCy Schubert 		entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
75539beb93cSSam Leffler 	entry->next = radius->msgs;
75639beb93cSSam Leffler 	radius->msgs = entry;
75739beb93cSSam Leffler 	radius_client_update_timeout(radius);
75839beb93cSSam Leffler 
75939beb93cSSam Leffler 	if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
7605b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits");
76139beb93cSSam Leffler 		prev = NULL;
76239beb93cSSam Leffler 		while (entry->next) {
76339beb93cSSam Leffler 			prev = entry;
76439beb93cSSam Leffler 			entry = entry->next;
76539beb93cSSam Leffler 		}
76639beb93cSSam Leffler 		if (prev) {
76739beb93cSSam Leffler 			prev->next = NULL;
76839beb93cSSam Leffler 			radius_client_msg_free(entry);
76939beb93cSSam Leffler 		}
77039beb93cSSam Leffler 	} else
77139beb93cSSam Leffler 		radius->num_msgs++;
77239beb93cSSam Leffler }
77339beb93cSSam Leffler 
77439beb93cSSam Leffler 
radius_client_disable_pmtu_discovery(int s)775*a90b9d01SCy Schubert static int radius_client_disable_pmtu_discovery(int s)
776*a90b9d01SCy Schubert {
777*a90b9d01SCy Schubert 	int r = -1;
778*a90b9d01SCy Schubert #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
779*a90b9d01SCy Schubert 	/* Turn off Path MTU discovery on IPv4/UDP sockets. */
780*a90b9d01SCy Schubert 	int action = IP_PMTUDISC_DONT;
781*a90b9d01SCy Schubert 	r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
782*a90b9d01SCy Schubert 		       sizeof(action));
783*a90b9d01SCy Schubert 	if (r == -1)
784*a90b9d01SCy Schubert 		wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s",
785*a90b9d01SCy Schubert 			   strerror(errno));
786*a90b9d01SCy Schubert #endif
787*a90b9d01SCy Schubert 	return r;
788*a90b9d01SCy Schubert }
789*a90b9d01SCy Schubert 
790*a90b9d01SCy Schubert 
radius_close_auth_socket(struct radius_client_data * radius)791*a90b9d01SCy Schubert static void radius_close_auth_socket(struct radius_client_data *radius)
792*a90b9d01SCy Schubert {
793*a90b9d01SCy Schubert 	if (radius->auth_sock >= 0) {
794*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
795*a90b9d01SCy Schubert 		if (radius->conf->auth_server->tls)
796*a90b9d01SCy Schubert 			eloop_unregister_sock(radius->auth_sock,
797*a90b9d01SCy Schubert 					      EVENT_TYPE_WRITE);
798*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
799*a90b9d01SCy Schubert 		eloop_unregister_read_sock(radius->auth_sock);
800*a90b9d01SCy Schubert 		close(radius->auth_sock);
801*a90b9d01SCy Schubert 		radius->auth_sock = -1;
802*a90b9d01SCy Schubert 	}
803*a90b9d01SCy Schubert }
804*a90b9d01SCy Schubert 
805*a90b9d01SCy Schubert 
radius_close_acct_socket(struct radius_client_data * radius)806*a90b9d01SCy Schubert static void radius_close_acct_socket(struct radius_client_data *radius)
807*a90b9d01SCy Schubert {
808*a90b9d01SCy Schubert 	if (radius->acct_sock >= 0) {
809*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
810*a90b9d01SCy Schubert 		if (radius->conf->acct_server->tls)
811*a90b9d01SCy Schubert 			eloop_unregister_sock(radius->acct_sock,
812*a90b9d01SCy Schubert 					      EVENT_TYPE_WRITE);
813*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
814*a90b9d01SCy Schubert 		eloop_unregister_read_sock(radius->acct_sock);
815*a90b9d01SCy Schubert 		close(radius->acct_sock);
816*a90b9d01SCy Schubert 		radius->acct_sock = -1;
817*a90b9d01SCy Schubert 	}
818*a90b9d01SCy Schubert }
819*a90b9d01SCy Schubert 
820*a90b9d01SCy Schubert 
821e28a4053SRui Paulo /**
822e28a4053SRui Paulo  * radius_client_send - Send a RADIUS request
823e28a4053SRui Paulo  * @radius: RADIUS client context from radius_client_init()
824e28a4053SRui Paulo  * @msg: RADIUS message to be sent
825e28a4053SRui Paulo  * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM)
826e28a4053SRui Paulo  * @addr: MAC address of the device related to this message or %NULL
827e28a4053SRui Paulo  * Returns: 0 on success, -1 on failure
828e28a4053SRui Paulo  *
829e28a4053SRui Paulo  * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or
830e28a4053SRui Paulo  * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference
831e28a4053SRui Paulo  * between accounting and interim accounting messages is that the interim
832780fb4a2SCy Schubert  * message will not be retransmitted. Instead, a callback is used to indicate
833780fb4a2SCy Schubert  * that the transmission failed for the specific station @addr so that a new
834780fb4a2SCy Schubert  * interim accounting update message can be generated with up-to-date session
835780fb4a2SCy Schubert  * data instead of trying to resend old information.
836e28a4053SRui Paulo  *
837e28a4053SRui Paulo  * The message is added on the retransmission queue and will be retransmitted
838e28a4053SRui Paulo  * automatically until a response is received or maximum number of retries
8394bc52338SCy Schubert  * (RADIUS_CLIENT_MAX_FAILOVER * RADIUS_CLIENT_NUM_FAILOVER) is reached. No
8404bc52338SCy Schubert  * such retries are used with RADIUS_ACCT_INTERIM, i.e., such a pending message
8414bc52338SCy Schubert  * is removed from the queue automatically on transmission failure.
842e28a4053SRui Paulo  *
843e28a4053SRui Paulo  * The related device MAC address can be used to identify pending messages that
844780fb4a2SCy Schubert  * can be removed with radius_client_flush_auth().
845e28a4053SRui Paulo  */
radius_client_send(struct radius_client_data * radius,struct radius_msg * msg,RadiusType msg_type,const u8 * addr)84639beb93cSSam Leffler int radius_client_send(struct radius_client_data *radius,
84739beb93cSSam Leffler 		       struct radius_msg *msg, RadiusType msg_type,
84839beb93cSSam Leffler 		       const u8 *addr)
84939beb93cSSam Leffler {
85039beb93cSSam Leffler 	struct hostapd_radius_servers *conf = radius->conf;
851e28a4053SRui Paulo 	const u8 *shared_secret;
85239beb93cSSam Leffler 	size_t shared_secret_len;
85339beb93cSSam Leffler 	char *name;
85439beb93cSSam Leffler 	int s, res;
855e28a4053SRui Paulo 	struct wpabuf *buf;
856*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
857*a90b9d01SCy Schubert 	struct wpabuf *out = NULL;
858*a90b9d01SCy Schubert 	struct tls_connection *conn = NULL;
859*a90b9d01SCy Schubert 	bool acct = false;
860*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
86139beb93cSSam Leffler 
86239beb93cSSam Leffler 	if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
863*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
864*a90b9d01SCy Schubert 		acct = true;
865*a90b9d01SCy Schubert 		if (radius->acct_tls)
866*a90b9d01SCy Schubert 			conn = radius->acct_tls_conn;
867*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
8685b9c547cSRui Paulo 		if (conf->acct_server && radius->acct_sock < 0)
8695b9c547cSRui Paulo 			radius_client_init_acct(radius);
8705b9c547cSRui Paulo 
8715b9c547cSRui Paulo 		if (conf->acct_server == NULL || radius->acct_sock < 0 ||
8725b9c547cSRui Paulo 		    conf->acct_server->shared_secret == NULL) {
87339beb93cSSam Leffler 			hostapd_logger(radius->ctx, NULL,
87439beb93cSSam Leffler 				       HOSTAPD_MODULE_RADIUS,
87539beb93cSSam Leffler 				       HOSTAPD_LEVEL_INFO,
87639beb93cSSam Leffler 				       "No accounting server configured");
87739beb93cSSam Leffler 			return -1;
87839beb93cSSam Leffler 		}
87939beb93cSSam Leffler 		shared_secret = conf->acct_server->shared_secret;
88039beb93cSSam Leffler 		shared_secret_len = conf->acct_server->shared_secret_len;
88139beb93cSSam Leffler 		radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
88239beb93cSSam Leffler 		name = "accounting";
88339beb93cSSam Leffler 		s = radius->acct_sock;
88439beb93cSSam Leffler 		conf->acct_server->requests++;
88539beb93cSSam Leffler 	} else {
886*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
887*a90b9d01SCy Schubert 		if (radius->auth_tls)
888*a90b9d01SCy Schubert 			conn = radius->auth_tls_conn;
889*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
8905b9c547cSRui Paulo 		if (conf->auth_server && radius->auth_sock < 0)
8915b9c547cSRui Paulo 			radius_client_init_auth(radius);
8925b9c547cSRui Paulo 
8935b9c547cSRui Paulo 		if (conf->auth_server == NULL || radius->auth_sock < 0 ||
8945b9c547cSRui Paulo 		    conf->auth_server->shared_secret == NULL) {
89539beb93cSSam Leffler 			hostapd_logger(radius->ctx, NULL,
89639beb93cSSam Leffler 				       HOSTAPD_MODULE_RADIUS,
89739beb93cSSam Leffler 				       HOSTAPD_LEVEL_INFO,
89839beb93cSSam Leffler 				       "No authentication server configured");
89939beb93cSSam Leffler 			return -1;
90039beb93cSSam Leffler 		}
90139beb93cSSam Leffler 		shared_secret = conf->auth_server->shared_secret;
90239beb93cSSam Leffler 		shared_secret_len = conf->auth_server->shared_secret_len;
90339beb93cSSam Leffler 		radius_msg_finish(msg, shared_secret, shared_secret_len);
90439beb93cSSam Leffler 		name = "authentication";
90539beb93cSSam Leffler 		s = radius->auth_sock;
90639beb93cSSam Leffler 		conf->auth_server->requests++;
90739beb93cSSam Leffler 	}
90839beb93cSSam Leffler 
90939beb93cSSam Leffler 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
91039beb93cSSam Leffler 		       HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s "
91139beb93cSSam Leffler 		       "server", name);
91239beb93cSSam Leffler 	if (conf->msg_dumps)
91339beb93cSSam Leffler 		radius_msg_dump(msg);
91439beb93cSSam Leffler 
915*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
916*a90b9d01SCy Schubert 	if ((acct && radius->acct_tls && !radius->acct_tls_ready) ||
917*a90b9d01SCy Schubert 	    (!acct && radius->auth_tls && !radius->auth_tls_ready)) {
918*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
919*a90b9d01SCy Schubert 			   "RADIUS: TLS connection not yet ready for TX");
920*a90b9d01SCy Schubert 		goto skip_send;
921*a90b9d01SCy Schubert 	}
922*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
923*a90b9d01SCy Schubert 
924e28a4053SRui Paulo 	buf = radius_msg_get_buf(msg);
925*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
926*a90b9d01SCy Schubert 	if (conn) {
927*a90b9d01SCy Schubert 		out = tls_connection_encrypt(radius->tls_ctx, conn, buf);
928*a90b9d01SCy Schubert 		if (!out) {
929*a90b9d01SCy Schubert 			wpa_printf(MSG_INFO,
930*a90b9d01SCy Schubert 				   "RADIUS: Failed to encrypt RADIUS message (TLS)");
931*a90b9d01SCy Schubert 			return -1;
932*a90b9d01SCy Schubert 		}
933*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
934*a90b9d01SCy Schubert 			   "RADIUS: TLS encryption of %zu bytes of plaintext to %zu bytes of ciphertext",
935*a90b9d01SCy Schubert 			   wpabuf_len(buf), wpabuf_len(out));
936*a90b9d01SCy Schubert 		buf = out;
937*a90b9d01SCy Schubert 	}
938*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
939*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG, "RADIUS: Send %zu bytes to the server",
940*a90b9d01SCy Schubert 		   wpabuf_len(buf));
941e28a4053SRui Paulo 	res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0);
942*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
943*a90b9d01SCy Schubert 	wpabuf_free(out);
944*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
94539beb93cSSam Leffler 	if (res < 0)
94639beb93cSSam Leffler 		radius_client_handle_send_error(radius, s, msg_type);
94739beb93cSSam Leffler 
948*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
949*a90b9d01SCy Schubert skip_send:
950*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
95139beb93cSSam Leffler 	radius_client_list_add(radius, msg, msg_type, shared_secret,
95239beb93cSSam Leffler 			       shared_secret_len, addr);
95339beb93cSSam Leffler 
954f05cddf9SRui Paulo 	return 0;
95539beb93cSSam Leffler }
95639beb93cSSam Leffler 
95739beb93cSSam Leffler 
958*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
959*a90b9d01SCy Schubert 
radius_client_close_tcp(struct radius_client_data * radius,int sock,RadiusType msg_type)960*a90b9d01SCy Schubert static void radius_client_close_tcp(struct radius_client_data *radius,
961*a90b9d01SCy Schubert 				    int sock, RadiusType msg_type)
962*a90b9d01SCy Schubert {
963*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG, "RADIUS: Closing TCP connection (sock %d)",
964*a90b9d01SCy Schubert 		   sock);
965*a90b9d01SCy Schubert 	if (msg_type == RADIUS_ACCT) {
966*a90b9d01SCy Schubert 		radius->acct_tls_ready = false;
967*a90b9d01SCy Schubert 		radius_close_acct_socket(radius);
968*a90b9d01SCy Schubert 	} else {
969*a90b9d01SCy Schubert 		radius->auth_tls_ready = false;
970*a90b9d01SCy Schubert 		radius_close_auth_socket(radius);
971*a90b9d01SCy Schubert 	}
972*a90b9d01SCy Schubert }
973*a90b9d01SCy Schubert 
974*a90b9d01SCy Schubert 
975*a90b9d01SCy Schubert static void
radius_client_process_tls_handshake(struct radius_client_data * radius,int sock,RadiusType msg_type,u8 * buf,size_t len)976*a90b9d01SCy Schubert radius_client_process_tls_handshake(struct radius_client_data *radius,
977*a90b9d01SCy Schubert 				    int sock, RadiusType msg_type,
978*a90b9d01SCy Schubert 				    u8 *buf, size_t len)
979*a90b9d01SCy Schubert {
980*a90b9d01SCy Schubert 	struct wpabuf *in, *out = NULL, *appl;
981*a90b9d01SCy Schubert 	struct tls_connection *conn;
982*a90b9d01SCy Schubert 	int res;
983*a90b9d01SCy Schubert 	bool ready = false;
984*a90b9d01SCy Schubert 
985*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG,
986*a90b9d01SCy Schubert 		   "RADIUS: Process %zu bytes of received TLS handshake message",
987*a90b9d01SCy Schubert 		   len);
988*a90b9d01SCy Schubert 
989*a90b9d01SCy Schubert 	if (msg_type == RADIUS_ACCT)
990*a90b9d01SCy Schubert 		conn = radius->acct_tls_conn;
991*a90b9d01SCy Schubert 	else
992*a90b9d01SCy Schubert 		conn = radius->auth_tls_conn;
993*a90b9d01SCy Schubert 
994*a90b9d01SCy Schubert 	in = wpabuf_alloc_copy(buf, len);
995*a90b9d01SCy Schubert 	if (!in)
996*a90b9d01SCy Schubert 		return;
997*a90b9d01SCy Schubert 
998*a90b9d01SCy Schubert 	appl = NULL;
999*a90b9d01SCy Schubert 	out = tls_connection_handshake(radius->tls_ctx, conn, in, &appl);
1000*a90b9d01SCy Schubert 	wpabuf_free(in);
1001*a90b9d01SCy Schubert 	if (!out) {
1002*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
1003*a90b9d01SCy Schubert 			   "RADIUS: Could not generate TLS handshake data");
1004*a90b9d01SCy Schubert 		goto fail;
1005*a90b9d01SCy Schubert 	}
1006*a90b9d01SCy Schubert 
1007*a90b9d01SCy Schubert 	if (tls_connection_get_failed(radius->tls_ctx, conn)) {
1008*a90b9d01SCy Schubert 		wpa_printf(MSG_INFO, "RADIUS: TLS handshake failed");
1009*a90b9d01SCy Schubert 		goto fail;
1010*a90b9d01SCy Schubert 	}
1011*a90b9d01SCy Schubert 
1012*a90b9d01SCy Schubert 	if (tls_connection_established(radius->tls_ctx, conn)) {
1013*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
1014*a90b9d01SCy Schubert 			   "RADIUS: TLS connection established (sock=%d)",
1015*a90b9d01SCy Schubert 			   sock);
1016*a90b9d01SCy Schubert 		if (msg_type == RADIUS_ACCT)
1017*a90b9d01SCy Schubert 			radius->acct_tls_ready = true;
1018*a90b9d01SCy Schubert 		else
1019*a90b9d01SCy Schubert 			radius->auth_tls_ready = true;
1020*a90b9d01SCy Schubert 		ready = true;
1021*a90b9d01SCy Schubert 	}
1022*a90b9d01SCy Schubert 
1023*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG, "RADIUS: Sending %zu bytes of TLS handshake",
1024*a90b9d01SCy Schubert 		   wpabuf_len(out));
1025*a90b9d01SCy Schubert 	res = send(sock, wpabuf_head(out), wpabuf_len(out), 0);
1026*a90b9d01SCy Schubert 	if (res < 0) {
1027*a90b9d01SCy Schubert 		wpa_printf(MSG_INFO, "RADIUS: send: %s", strerror(errno));
1028*a90b9d01SCy Schubert 		goto fail;
1029*a90b9d01SCy Schubert 	}
1030*a90b9d01SCy Schubert 	if ((size_t) res != wpabuf_len(out)) {
1031*a90b9d01SCy Schubert 		wpa_printf(MSG_INFO,
1032*a90b9d01SCy Schubert 			   "RADIUS: Could not send all data for TLS handshake: only %d bytes sent",
1033*a90b9d01SCy Schubert 			   res);
1034*a90b9d01SCy Schubert 		goto fail;
1035*a90b9d01SCy Schubert 	}
1036*a90b9d01SCy Schubert 	wpabuf_free(out);
1037*a90b9d01SCy Schubert 
1038*a90b9d01SCy Schubert 	if (ready) {
1039*a90b9d01SCy Schubert 		struct radius_msg_list *entry, *prev, *tmp;
1040*a90b9d01SCy Schubert 		struct os_reltime now;
1041*a90b9d01SCy Schubert 
1042*a90b9d01SCy Schubert 		/* Send all pending message of matching type since the TLS
1043*a90b9d01SCy Schubert 		 * tunnel has now been established. */
1044*a90b9d01SCy Schubert 
1045*a90b9d01SCy Schubert 		os_get_reltime(&now);
1046*a90b9d01SCy Schubert 
1047*a90b9d01SCy Schubert 		entry = radius->msgs;
1048*a90b9d01SCy Schubert 		prev = NULL;
1049*a90b9d01SCy Schubert 		while (entry) {
1050*a90b9d01SCy Schubert 			if (entry->msg_type != msg_type) {
1051*a90b9d01SCy Schubert 				prev = entry;
1052*a90b9d01SCy Schubert 				entry = entry->next;
1053*a90b9d01SCy Schubert 				continue;
1054*a90b9d01SCy Schubert 			}
1055*a90b9d01SCy Schubert 
1056*a90b9d01SCy Schubert 			if (radius_client_retransmit(radius, entry, now.sec)) {
1057*a90b9d01SCy Schubert 				if (prev)
1058*a90b9d01SCy Schubert 					prev->next = entry->next;
1059*a90b9d01SCy Schubert 				else
1060*a90b9d01SCy Schubert 					radius->msgs = entry->next;
1061*a90b9d01SCy Schubert 
1062*a90b9d01SCy Schubert 				tmp = entry;
1063*a90b9d01SCy Schubert 				entry = entry->next;
1064*a90b9d01SCy Schubert 				radius_client_msg_free(tmp);
1065*a90b9d01SCy Schubert 				radius->num_msgs--;
1066*a90b9d01SCy Schubert 				continue;
1067*a90b9d01SCy Schubert 			}
1068*a90b9d01SCy Schubert 
1069*a90b9d01SCy Schubert 			prev = entry;
1070*a90b9d01SCy Schubert 			entry = entry->next;
1071*a90b9d01SCy Schubert 		}
1072*a90b9d01SCy Schubert 	}
1073*a90b9d01SCy Schubert 
1074*a90b9d01SCy Schubert 	return;
1075*a90b9d01SCy Schubert 
1076*a90b9d01SCy Schubert fail:
1077*a90b9d01SCy Schubert 	wpabuf_free(out);
1078*a90b9d01SCy Schubert 	tls_connection_deinit(radius->tls_ctx, conn);
1079*a90b9d01SCy Schubert 	if (msg_type == RADIUS_ACCT)
1080*a90b9d01SCy Schubert 		radius->acct_tls_conn = NULL;
1081*a90b9d01SCy Schubert 	else
1082*a90b9d01SCy Schubert 		radius->auth_tls_conn = NULL;
1083*a90b9d01SCy Schubert 	radius_client_close_tcp(radius, sock, msg_type);
1084*a90b9d01SCy Schubert }
1085*a90b9d01SCy Schubert 
1086*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
1087*a90b9d01SCy Schubert 
1088*a90b9d01SCy Schubert 
radius_client_receive(int sock,void * eloop_ctx,void * sock_ctx)108939beb93cSSam Leffler static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
109039beb93cSSam Leffler {
109139beb93cSSam Leffler 	struct radius_client_data *radius = eloop_ctx;
109239beb93cSSam Leffler 	struct hostapd_radius_servers *conf = radius->conf;
10934b72b91aSCy Schubert 	RadiusType msg_type = (uintptr_t) sock_ctx;
109439beb93cSSam Leffler 	int len, roundtrip;
1095c1d255d3SCy Schubert 	unsigned char buf[RADIUS_MAX_MSG_LEN];
1096c1d255d3SCy Schubert 	struct msghdr msghdr = {0};
1097c1d255d3SCy Schubert 	struct iovec iov;
109839beb93cSSam Leffler 	struct radius_msg *msg;
1099e28a4053SRui Paulo 	struct radius_hdr *hdr;
110039beb93cSSam Leffler 	struct radius_rx_handler *handlers;
110139beb93cSSam Leffler 	size_t num_handlers, i;
110239beb93cSSam Leffler 	struct radius_msg_list *req, *prev_req;
11035b9c547cSRui Paulo 	struct os_reltime now;
110439beb93cSSam Leffler 	struct hostapd_radius_server *rconf;
110539beb93cSSam Leffler 	int invalid_authenticator = 0;
1106*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
1107*a90b9d01SCy Schubert 	struct tls_connection *conn = NULL;
1108*a90b9d01SCy Schubert 	bool tls, tls_ready;
1109*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
111039beb93cSSam Leffler 
111139beb93cSSam Leffler 	if (msg_type == RADIUS_ACCT) {
1112*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
1113*a90b9d01SCy Schubert 		if (radius->acct_tls)
1114*a90b9d01SCy Schubert 			conn = radius->acct_tls_conn;
1115*a90b9d01SCy Schubert 		tls = radius->acct_tls;
1116*a90b9d01SCy Schubert 		tls_ready = radius->acct_tls_ready;
1117*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
111839beb93cSSam Leffler 		handlers = radius->acct_handlers;
111939beb93cSSam Leffler 		num_handlers = radius->num_acct_handlers;
112039beb93cSSam Leffler 		rconf = conf->acct_server;
112139beb93cSSam Leffler 	} else {
1122*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
1123*a90b9d01SCy Schubert 		if (radius->auth_tls)
1124*a90b9d01SCy Schubert 			conn = radius->auth_tls_conn;
1125*a90b9d01SCy Schubert 		tls = radius->auth_tls;
1126*a90b9d01SCy Schubert 		tls_ready = radius->auth_tls_ready;
1127*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
112839beb93cSSam Leffler 		handlers = radius->auth_handlers;
112939beb93cSSam Leffler 		num_handlers = radius->num_auth_handlers;
113039beb93cSSam Leffler 		rconf = conf->auth_server;
113139beb93cSSam Leffler 	}
113239beb93cSSam Leffler 
1133c1d255d3SCy Schubert 	iov.iov_base = buf;
1134c1d255d3SCy Schubert 	iov.iov_len = RADIUS_MAX_MSG_LEN;
1135c1d255d3SCy Schubert 	msghdr.msg_iov = &iov;
1136c1d255d3SCy Schubert 	msghdr.msg_iovlen = 1;
1137c1d255d3SCy Schubert 	msghdr.msg_flags = 0;
1138c1d255d3SCy Schubert 	len = recvmsg(sock, &msghdr, MSG_DONTWAIT);
113939beb93cSSam Leffler 	if (len < 0) {
1140c1d255d3SCy Schubert 		wpa_printf(MSG_INFO, "recvmsg[RADIUS]: %s", strerror(errno));
114139beb93cSSam Leffler 		return;
114239beb93cSSam Leffler 	}
1143*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
1144*a90b9d01SCy Schubert 	if (tls && len == 0) {
1145*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "RADIUS: No TCP data available");
1146*a90b9d01SCy Schubert 		goto close_tcp;
1147*a90b9d01SCy Schubert 	}
1148*a90b9d01SCy Schubert 
1149*a90b9d01SCy Schubert 	if (tls && !tls_ready) {
1150*a90b9d01SCy Schubert 		radius_client_process_tls_handshake(radius, sock, msg_type,
1151*a90b9d01SCy Schubert 						    buf, len);
1152*a90b9d01SCy Schubert 		return;
1153*a90b9d01SCy Schubert 	}
1154*a90b9d01SCy Schubert 
1155*a90b9d01SCy Schubert 	if (conn) {
1156*a90b9d01SCy Schubert 		struct wpabuf *out, *in;
1157*a90b9d01SCy Schubert 
1158*a90b9d01SCy Schubert 		in = wpabuf_alloc_copy(buf, len);
1159*a90b9d01SCy Schubert 		if (!in)
1160*a90b9d01SCy Schubert 			return;
1161*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
1162*a90b9d01SCy Schubert 			   "RADIUS: Process %d bytes of encrypted TLS data",
1163*a90b9d01SCy Schubert 			   len);
1164*a90b9d01SCy Schubert 		out = tls_connection_decrypt(radius->tls_ctx, conn, in);
1165*a90b9d01SCy Schubert 		wpabuf_free(in);
1166*a90b9d01SCy Schubert 		if (!out) {
1167*a90b9d01SCy Schubert 			wpa_printf(MSG_INFO,
1168*a90b9d01SCy Schubert 				   "RADIUS: Failed to decrypt TLS data");
1169*a90b9d01SCy Schubert 			goto close_tcp;
1170*a90b9d01SCy Schubert 		}
1171*a90b9d01SCy Schubert 		if (wpabuf_len(out) == 0) {
1172*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
1173*a90b9d01SCy Schubert 				   "RADIUS: Full message not yet received - continue waiting for additional TLS data");
1174*a90b9d01SCy Schubert 			wpabuf_free(out);
1175*a90b9d01SCy Schubert 			return;
1176*a90b9d01SCy Schubert 		}
1177*a90b9d01SCy Schubert 		if (wpabuf_len(out) > RADIUS_MAX_MSG_LEN) {
1178*a90b9d01SCy Schubert 			wpa_printf(MSG_INFO,
1179*a90b9d01SCy Schubert 				   "RADIUS: Too long RADIUS message from TLS: %zu",
1180*a90b9d01SCy Schubert 				   wpabuf_len(out));
1181*a90b9d01SCy Schubert 			wpabuf_free(out);
1182*a90b9d01SCy Schubert 			goto close_tcp;
1183*a90b9d01SCy Schubert 		}
1184*a90b9d01SCy Schubert 		os_memcpy(buf, wpabuf_head(out), wpabuf_len(out));
1185*a90b9d01SCy Schubert 		len = wpabuf_len(out);
1186*a90b9d01SCy Schubert 		wpabuf_free(out);
1187*a90b9d01SCy Schubert 	}
1188*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
1189c1d255d3SCy Schubert 
119039beb93cSSam Leffler 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
119139beb93cSSam Leffler 		       HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
119239beb93cSSam Leffler 		       "server", len);
1193c1d255d3SCy Schubert 
1194c1d255d3SCy Schubert 	if (msghdr.msg_flags & MSG_TRUNC) {
11955b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it");
119639beb93cSSam Leffler 		return;
119739beb93cSSam Leffler 	}
119839beb93cSSam Leffler 
119939beb93cSSam Leffler 	msg = radius_msg_parse(buf, len);
120039beb93cSSam Leffler 	if (msg == NULL) {
12015b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed");
120239beb93cSSam Leffler 		rconf->malformed_responses++;
120339beb93cSSam Leffler 		return;
120439beb93cSSam Leffler 	}
1205e28a4053SRui Paulo 	hdr = radius_msg_get_hdr(msg);
120639beb93cSSam Leffler 
120739beb93cSSam Leffler 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
120839beb93cSSam Leffler 		       HOSTAPD_LEVEL_DEBUG, "Received RADIUS message");
120939beb93cSSam Leffler 	if (conf->msg_dumps)
121039beb93cSSam Leffler 		radius_msg_dump(msg);
121139beb93cSSam Leffler 
1212e28a4053SRui Paulo 	switch (hdr->code) {
121339beb93cSSam Leffler 	case RADIUS_CODE_ACCESS_ACCEPT:
121439beb93cSSam Leffler 		rconf->access_accepts++;
121539beb93cSSam Leffler 		break;
121639beb93cSSam Leffler 	case RADIUS_CODE_ACCESS_REJECT:
121739beb93cSSam Leffler 		rconf->access_rejects++;
121839beb93cSSam Leffler 		break;
121939beb93cSSam Leffler 	case RADIUS_CODE_ACCESS_CHALLENGE:
122039beb93cSSam Leffler 		rconf->access_challenges++;
122139beb93cSSam Leffler 		break;
122239beb93cSSam Leffler 	case RADIUS_CODE_ACCOUNTING_RESPONSE:
122339beb93cSSam Leffler 		rconf->responses++;
122439beb93cSSam Leffler 		break;
122539beb93cSSam Leffler 	}
122639beb93cSSam Leffler 
122739beb93cSSam Leffler 	prev_req = NULL;
122839beb93cSSam Leffler 	req = radius->msgs;
122939beb93cSSam Leffler 	while (req) {
123039beb93cSSam Leffler 		/* TODO: also match by src addr:port of the packet when using
123139beb93cSSam Leffler 		 * alternative RADIUS servers (?) */
123239beb93cSSam Leffler 		if ((req->msg_type == msg_type ||
123339beb93cSSam Leffler 		     (req->msg_type == RADIUS_ACCT_INTERIM &&
123439beb93cSSam Leffler 		      msg_type == RADIUS_ACCT)) &&
1235e28a4053SRui Paulo 		    radius_msg_get_hdr(req->msg)->identifier ==
1236e28a4053SRui Paulo 		    hdr->identifier)
123739beb93cSSam Leffler 			break;
123839beb93cSSam Leffler 
123939beb93cSSam Leffler 		prev_req = req;
124039beb93cSSam Leffler 		req = req->next;
124139beb93cSSam Leffler 	}
124239beb93cSSam Leffler 
124339beb93cSSam Leffler 	if (req == NULL) {
124439beb93cSSam Leffler 		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
124539beb93cSSam Leffler 			       HOSTAPD_LEVEL_DEBUG,
124639beb93cSSam Leffler 			       "No matching RADIUS request found (type=%d "
124739beb93cSSam Leffler 			       "id=%d) - dropping packet",
1248e28a4053SRui Paulo 			       msg_type, hdr->identifier);
124939beb93cSSam Leffler 		goto fail;
125039beb93cSSam Leffler 	}
125139beb93cSSam Leffler 
12525b9c547cSRui Paulo 	os_get_reltime(&now);
125339beb93cSSam Leffler 	roundtrip = (now.sec - req->last_attempt.sec) * 100 +
125439beb93cSSam Leffler 		(now.usec - req->last_attempt.usec) / 10000;
125539beb93cSSam Leffler 	hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
125639beb93cSSam Leffler 		       HOSTAPD_LEVEL_DEBUG,
125739beb93cSSam Leffler 		       "Received RADIUS packet matched with a pending "
125839beb93cSSam Leffler 		       "request, round trip time %d.%02d sec",
125939beb93cSSam Leffler 		       roundtrip / 100, roundtrip % 100);
126039beb93cSSam Leffler 	rconf->round_trip_time = roundtrip;
126139beb93cSSam Leffler 
126239beb93cSSam Leffler 	/* Remove ACKed RADIUS packet from retransmit list */
126339beb93cSSam Leffler 	if (prev_req)
126439beb93cSSam Leffler 		prev_req->next = req->next;
126539beb93cSSam Leffler 	else
126639beb93cSSam Leffler 		radius->msgs = req->next;
126739beb93cSSam Leffler 	radius->num_msgs--;
126839beb93cSSam Leffler 
126939beb93cSSam Leffler 	for (i = 0; i < num_handlers; i++) {
127039beb93cSSam Leffler 		RadiusRxResult res;
127139beb93cSSam Leffler 		res = handlers[i].handler(msg, req->msg, req->shared_secret,
127239beb93cSSam Leffler 					  req->shared_secret_len,
127339beb93cSSam Leffler 					  handlers[i].data);
127439beb93cSSam Leffler 		switch (res) {
127539beb93cSSam Leffler 		case RADIUS_RX_PROCESSED:
127639beb93cSSam Leffler 			radius_msg_free(msg);
127785732ac8SCy Schubert 			/* fall through */
127839beb93cSSam Leffler 		case RADIUS_RX_QUEUED:
127939beb93cSSam Leffler 			radius_client_msg_free(req);
128039beb93cSSam Leffler 			return;
128139beb93cSSam Leffler 		case RADIUS_RX_INVALID_AUTHENTICATOR:
128239beb93cSSam Leffler 			invalid_authenticator++;
128385732ac8SCy Schubert 			/* fall through */
128439beb93cSSam Leffler 		case RADIUS_RX_UNKNOWN:
128539beb93cSSam Leffler 			/* continue with next handler */
128639beb93cSSam Leffler 			break;
128739beb93cSSam Leffler 		}
128839beb93cSSam Leffler 	}
128939beb93cSSam Leffler 
129039beb93cSSam Leffler 	if (invalid_authenticator)
129139beb93cSSam Leffler 		rconf->bad_authenticators++;
129239beb93cSSam Leffler 	else
129339beb93cSSam Leffler 		rconf->unknown_types++;
129439beb93cSSam Leffler 	hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
129539beb93cSSam Leffler 		       HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found "
129639beb93cSSam Leffler 		       "(type=%d code=%d id=%d)%s - dropping packet",
1297e28a4053SRui Paulo 		       msg_type, hdr->code, hdr->identifier,
129839beb93cSSam Leffler 		       invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
129939beb93cSSam Leffler 		       "");
130039beb93cSSam Leffler 	radius_client_msg_free(req);
130139beb93cSSam Leffler 
130239beb93cSSam Leffler  fail:
130339beb93cSSam Leffler 	radius_msg_free(msg);
1304*a90b9d01SCy Schubert 	return;
1305*a90b9d01SCy Schubert 
1306*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
1307*a90b9d01SCy Schubert close_tcp:
1308*a90b9d01SCy Schubert 	radius_client_close_tcp(radius, sock, msg_type);
1309*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
131039beb93cSSam Leffler }
131139beb93cSSam Leffler 
131239beb93cSSam Leffler 
1313*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
radius_client_write_ready(int sock,void * eloop_ctx,void * sock_ctx)1314*a90b9d01SCy Schubert static void radius_client_write_ready(int sock, void *eloop_ctx, void *sock_ctx)
1315*a90b9d01SCy Schubert {
1316*a90b9d01SCy Schubert 	struct radius_client_data *radius = eloop_ctx;
1317*a90b9d01SCy Schubert 	RadiusType msg_type = (uintptr_t) sock_ctx;
1318*a90b9d01SCy Schubert 	struct tls_connection *conn = NULL;
1319*a90b9d01SCy Schubert 	struct wpabuf *in, *out = NULL, *appl;
1320*a90b9d01SCy Schubert 	int res = -1;
1321*a90b9d01SCy Schubert 	struct tls_connection_params params;
1322*a90b9d01SCy Schubert 	struct hostapd_radius_server *server;
1323*a90b9d01SCy Schubert 
1324*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG, "RADIUS: TCP connection established - start TLS handshake (sock=%d)",
1325*a90b9d01SCy Schubert 		   sock);
1326*a90b9d01SCy Schubert 
1327*a90b9d01SCy Schubert 	if (msg_type == RADIUS_ACCT) {
1328*a90b9d01SCy Schubert 		eloop_unregister_sock(sock, EVENT_TYPE_WRITE);
1329*a90b9d01SCy Schubert 		eloop_register_read_sock(sock, radius_client_receive, radius,
1330*a90b9d01SCy Schubert 					 (void *) RADIUS_ACCT);
1331*a90b9d01SCy Schubert 		if (radius->acct_tls_conn) {
1332*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
1333*a90b9d01SCy Schubert 				   "RADIUS: Deinit previously used TLS connection");
1334*a90b9d01SCy Schubert 			tls_connection_deinit(radius->tls_ctx,
1335*a90b9d01SCy Schubert 					      radius->acct_tls_conn);
1336*a90b9d01SCy Schubert 			radius->acct_tls_conn = NULL;
1337*a90b9d01SCy Schubert 		}
1338*a90b9d01SCy Schubert 		server = radius->conf->acct_server;
1339*a90b9d01SCy Schubert 	} else {
1340*a90b9d01SCy Schubert 		eloop_unregister_sock(sock, EVENT_TYPE_WRITE);
1341*a90b9d01SCy Schubert 		eloop_register_read_sock(sock, radius_client_receive, radius,
1342*a90b9d01SCy Schubert 					 (void *) RADIUS_AUTH);
1343*a90b9d01SCy Schubert 		if (radius->auth_tls_conn) {
1344*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
1345*a90b9d01SCy Schubert 				   "RADIUS: Deinit previously used TLS connection");
1346*a90b9d01SCy Schubert 			tls_connection_deinit(radius->tls_ctx,
1347*a90b9d01SCy Schubert 					      radius->auth_tls_conn);
1348*a90b9d01SCy Schubert 			radius->auth_tls_conn = NULL;
1349*a90b9d01SCy Schubert 		}
1350*a90b9d01SCy Schubert 		server = radius->conf->auth_server;
1351*a90b9d01SCy Schubert 	}
1352*a90b9d01SCy Schubert 
1353*a90b9d01SCy Schubert 	if (!server)
1354*a90b9d01SCy Schubert 		goto fail;
1355*a90b9d01SCy Schubert 
1356*a90b9d01SCy Schubert 	conn = tls_connection_init(radius->tls_ctx);
1357*a90b9d01SCy Schubert 	if (!conn) {
1358*a90b9d01SCy Schubert 		wpa_printf(MSG_INFO,
1359*a90b9d01SCy Schubert 			   "RADIUS: Failed to initiate TLS connection");
1360*a90b9d01SCy Schubert 		goto fail;
1361*a90b9d01SCy Schubert 	}
1362*a90b9d01SCy Schubert 
1363*a90b9d01SCy Schubert 	os_memset(&params, 0, sizeof(params));
1364*a90b9d01SCy Schubert 	params.ca_cert = server->ca_cert;
1365*a90b9d01SCy Schubert 	params.client_cert = server->client_cert;
1366*a90b9d01SCy Schubert 	params.private_key = server->private_key;
1367*a90b9d01SCy Schubert 	params.private_key_passwd = server->private_key_passwd;
1368*a90b9d01SCy Schubert 	params.flags = TLS_CONN_DISABLE_TLSv1_0 | TLS_CONN_DISABLE_TLSv1_1;
1369*a90b9d01SCy Schubert 	if (tls_connection_set_params(radius->tls_ctx, conn, &params)) {
1370*a90b9d01SCy Schubert 		wpa_printf(MSG_INFO,
1371*a90b9d01SCy Schubert 			   "RADIUS: Failed to set TLS connection parameters");
1372*a90b9d01SCy Schubert 		goto fail;
1373*a90b9d01SCy Schubert 	}
1374*a90b9d01SCy Schubert 
1375*a90b9d01SCy Schubert 	in = NULL;
1376*a90b9d01SCy Schubert 	appl = NULL;
1377*a90b9d01SCy Schubert 	out = tls_connection_handshake(radius->tls_ctx, conn, in, &appl);
1378*a90b9d01SCy Schubert 	if (!out) {
1379*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
1380*a90b9d01SCy Schubert 			   "RADIUS: Could not generate TLS handshake data");
1381*a90b9d01SCy Schubert 		goto fail;
1382*a90b9d01SCy Schubert 	}
1383*a90b9d01SCy Schubert 
1384*a90b9d01SCy Schubert 	if (tls_connection_get_failed(radius->tls_ctx, conn)) {
1385*a90b9d01SCy Schubert 		wpa_printf(MSG_INFO, "RADIUS: TLS handshake failed");
1386*a90b9d01SCy Schubert 		goto fail;
1387*a90b9d01SCy Schubert 	}
1388*a90b9d01SCy Schubert 
1389*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG, "RADIUS: Sending %zu bytes of TLS handshake",
1390*a90b9d01SCy Schubert 		   wpabuf_len(out));
1391*a90b9d01SCy Schubert 	res = send(sock, wpabuf_head(out), wpabuf_len(out), 0);
1392*a90b9d01SCy Schubert 	if (res < 0) {
1393*a90b9d01SCy Schubert 		wpa_printf(MSG_INFO, "RADIUS: send: %s", strerror(errno));
1394*a90b9d01SCy Schubert 		goto fail;
1395*a90b9d01SCy Schubert 	}
1396*a90b9d01SCy Schubert 	if ((size_t) res != wpabuf_len(out)) {
1397*a90b9d01SCy Schubert 		wpa_printf(MSG_INFO,
1398*a90b9d01SCy Schubert 			   "RADIUS: Could not send all data for TLS handshake: only %d bytes sent",
1399*a90b9d01SCy Schubert 			   res);
1400*a90b9d01SCy Schubert 		goto fail;
1401*a90b9d01SCy Schubert 	}
1402*a90b9d01SCy Schubert 	wpabuf_free(out);
1403*a90b9d01SCy Schubert 
1404*a90b9d01SCy Schubert 	if (msg_type == RADIUS_ACCT)
1405*a90b9d01SCy Schubert 		radius->acct_tls_conn = conn;
1406*a90b9d01SCy Schubert 	else
1407*a90b9d01SCy Schubert 		radius->auth_tls_conn = conn;
1408*a90b9d01SCy Schubert 	return;
1409*a90b9d01SCy Schubert 
1410*a90b9d01SCy Schubert fail:
1411*a90b9d01SCy Schubert 	wpa_printf(MSG_INFO, "RADIUS: Failed to perform TLS handshake");
1412*a90b9d01SCy Schubert 	tls_connection_deinit(radius->tls_ctx, conn);
1413*a90b9d01SCy Schubert 	wpabuf_free(out);
1414*a90b9d01SCy Schubert 	radius_client_close_tcp(radius, sock, msg_type);
1415*a90b9d01SCy Schubert }
1416*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
1417*a90b9d01SCy Schubert 
1418*a90b9d01SCy Schubert 
1419e28a4053SRui Paulo /**
1420e28a4053SRui Paulo  * radius_client_get_id - Get an identifier for a new RADIUS message
1421e28a4053SRui Paulo  * @radius: RADIUS client context from radius_client_init()
1422e28a4053SRui Paulo  * Returns: Allocated identifier
1423e28a4053SRui Paulo  *
1424e28a4053SRui Paulo  * This function is used to fetch a unique (among pending requests) identifier
1425e28a4053SRui Paulo  * for a new RADIUS message.
1426e28a4053SRui Paulo  */
radius_client_get_id(struct radius_client_data * radius)142739beb93cSSam Leffler u8 radius_client_get_id(struct radius_client_data *radius)
142839beb93cSSam Leffler {
142939beb93cSSam Leffler 	struct radius_msg_list *entry, *prev, *_remove;
143039beb93cSSam Leffler 	u8 id = radius->next_radius_identifier++;
143139beb93cSSam Leffler 
143239beb93cSSam Leffler 	/* remove entries with matching id from retransmit list to avoid
143339beb93cSSam Leffler 	 * using new reply from the RADIUS server with an old request */
143439beb93cSSam Leffler 	entry = radius->msgs;
143539beb93cSSam Leffler 	prev = NULL;
143639beb93cSSam Leffler 	while (entry) {
1437e28a4053SRui Paulo 		if (radius_msg_get_hdr(entry->msg)->identifier == id) {
143839beb93cSSam Leffler 			hostapd_logger(radius->ctx, entry->addr,
143939beb93cSSam Leffler 				       HOSTAPD_MODULE_RADIUS,
144039beb93cSSam Leffler 				       HOSTAPD_LEVEL_DEBUG,
144139beb93cSSam Leffler 				       "Removing pending RADIUS message, "
144239beb93cSSam Leffler 				       "since its id (%d) is reused", id);
144339beb93cSSam Leffler 			if (prev)
144439beb93cSSam Leffler 				prev->next = entry->next;
144539beb93cSSam Leffler 			else
144639beb93cSSam Leffler 				radius->msgs = entry->next;
144739beb93cSSam Leffler 			_remove = entry;
144839beb93cSSam Leffler 		} else {
144939beb93cSSam Leffler 			_remove = NULL;
145039beb93cSSam Leffler 			prev = entry;
145139beb93cSSam Leffler 		}
145239beb93cSSam Leffler 		entry = entry->next;
145339beb93cSSam Leffler 
145439beb93cSSam Leffler 		if (_remove)
145539beb93cSSam Leffler 			radius_client_msg_free(_remove);
145639beb93cSSam Leffler 	}
145739beb93cSSam Leffler 
145839beb93cSSam Leffler 	return id;
145939beb93cSSam Leffler }
146039beb93cSSam Leffler 
146139beb93cSSam Leffler 
1462e28a4053SRui Paulo /**
1463e28a4053SRui Paulo  * radius_client_flush - Flush all pending RADIUS client messages
1464e28a4053SRui Paulo  * @radius: RADIUS client context from radius_client_init()
1465e28a4053SRui Paulo  * @only_auth: Whether only authentication messages are removed
1466e28a4053SRui Paulo  */
radius_client_flush(struct radius_client_data * radius,int only_auth)146739beb93cSSam Leffler void radius_client_flush(struct radius_client_data *radius, int only_auth)
146839beb93cSSam Leffler {
146939beb93cSSam Leffler 	struct radius_msg_list *entry, *prev, *tmp;
147039beb93cSSam Leffler 
147139beb93cSSam Leffler 	if (!radius)
147239beb93cSSam Leffler 		return;
147339beb93cSSam Leffler 
147439beb93cSSam Leffler 	prev = NULL;
147539beb93cSSam Leffler 	entry = radius->msgs;
147639beb93cSSam Leffler 
147739beb93cSSam Leffler 	while (entry) {
147839beb93cSSam Leffler 		if (!only_auth || entry->msg_type == RADIUS_AUTH) {
147939beb93cSSam Leffler 			if (prev)
148039beb93cSSam Leffler 				prev->next = entry->next;
148139beb93cSSam Leffler 			else
148239beb93cSSam Leffler 				radius->msgs = entry->next;
148339beb93cSSam Leffler 
148439beb93cSSam Leffler 			tmp = entry;
148539beb93cSSam Leffler 			entry = entry->next;
148639beb93cSSam Leffler 			radius_client_msg_free(tmp);
148739beb93cSSam Leffler 			radius->num_msgs--;
148839beb93cSSam Leffler 		} else {
148939beb93cSSam Leffler 			prev = entry;
149039beb93cSSam Leffler 			entry = entry->next;
149139beb93cSSam Leffler 		}
149239beb93cSSam Leffler 	}
149339beb93cSSam Leffler 
149439beb93cSSam Leffler 	if (radius->msgs == NULL)
149539beb93cSSam Leffler 		eloop_cancel_timeout(radius_client_timer, radius, NULL);
149639beb93cSSam Leffler }
149739beb93cSSam Leffler 
149839beb93cSSam Leffler 
radius_client_update_acct_msgs(struct radius_client_data * radius,const u8 * shared_secret,size_t shared_secret_len)149939beb93cSSam Leffler static void radius_client_update_acct_msgs(struct radius_client_data *radius,
1500e28a4053SRui Paulo 					   const u8 *shared_secret,
150139beb93cSSam Leffler 					   size_t shared_secret_len)
150239beb93cSSam Leffler {
150339beb93cSSam Leffler 	struct radius_msg_list *entry;
150439beb93cSSam Leffler 
150539beb93cSSam Leffler 	if (!radius)
150639beb93cSSam Leffler 		return;
150739beb93cSSam Leffler 
150839beb93cSSam Leffler 	for (entry = radius->msgs; entry; entry = entry->next) {
150939beb93cSSam Leffler 		if (entry->msg_type == RADIUS_ACCT) {
151039beb93cSSam Leffler 			entry->shared_secret = shared_secret;
151139beb93cSSam Leffler 			entry->shared_secret_len = shared_secret_len;
151239beb93cSSam Leffler 			radius_msg_finish_acct(entry->msg, shared_secret,
151339beb93cSSam Leffler 					       shared_secret_len);
151439beb93cSSam Leffler 		}
151539beb93cSSam Leffler 	}
151639beb93cSSam Leffler }
151739beb93cSSam Leffler 
151839beb93cSSam Leffler 
151939beb93cSSam Leffler static int
radius_change_server(struct radius_client_data * radius,struct hostapd_radius_server * nserv,struct hostapd_radius_server * oserv,int auth)152039beb93cSSam Leffler radius_change_server(struct radius_client_data *radius,
152139beb93cSSam Leffler 		     struct hostapd_radius_server *nserv,
152239beb93cSSam Leffler 		     struct hostapd_radius_server *oserv,
1523*a90b9d01SCy Schubert 		     int auth)
152439beb93cSSam Leffler {
152539beb93cSSam Leffler 	struct sockaddr_in serv, claddr;
152639beb93cSSam Leffler #ifdef CONFIG_IPV6
152739beb93cSSam Leffler 	struct sockaddr_in6 serv6, claddr6;
152839beb93cSSam Leffler #endif /* CONFIG_IPV6 */
152939beb93cSSam Leffler 	struct sockaddr *addr, *cl_addr;
153039beb93cSSam Leffler 	socklen_t addrlen, claddrlen;
153139beb93cSSam Leffler 	char abuf[50];
153239beb93cSSam Leffler 	int sel_sock;
153339beb93cSSam Leffler 	struct radius_msg_list *entry;
153439beb93cSSam Leffler 	struct hostapd_radius_servers *conf = radius->conf;
1535*a90b9d01SCy Schubert 	int type = SOCK_DGRAM;
1536*a90b9d01SCy Schubert 	bool tls = nserv->tls;
1537*a90b9d01SCy Schubert 
1538*a90b9d01SCy Schubert 	if (tls) {
1539*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
1540*a90b9d01SCy Schubert 		type = SOCK_STREAM;
1541*a90b9d01SCy Schubert #else /* CONFIG_RADIUS_TLS */
1542*a90b9d01SCy Schubert 		wpa_printf(MSG_ERROR, "RADIUS: TLS not supported");
1543*a90b9d01SCy Schubert 		return -1;
1544*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
1545*a90b9d01SCy Schubert 	}
154639beb93cSSam Leffler 
154739beb93cSSam Leffler 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
154839beb93cSSam Leffler 		       HOSTAPD_LEVEL_INFO,
154939beb93cSSam Leffler 		       "%s server %s:%d",
155039beb93cSSam Leffler 		       auth ? "Authentication" : "Accounting",
155139beb93cSSam Leffler 		       hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
155239beb93cSSam Leffler 		       nserv->port);
155339beb93cSSam Leffler 
1554780fb4a2SCy Schubert 	if (oserv && oserv == nserv) {
1555780fb4a2SCy Schubert 		/* Reconnect to same server, flush */
1556780fb4a2SCy Schubert 		if (auth)
1557780fb4a2SCy Schubert 			radius_client_flush(radius, 1);
1558780fb4a2SCy Schubert 	}
1559780fb4a2SCy Schubert 
15605b9c547cSRui Paulo 	if (oserv && oserv != nserv &&
15615b9c547cSRui Paulo 	    (nserv->shared_secret_len != oserv->shared_secret_len ||
156239beb93cSSam Leffler 	     os_memcmp(nserv->shared_secret, oserv->shared_secret,
15635b9c547cSRui Paulo 		       nserv->shared_secret_len) != 0)) {
156439beb93cSSam Leffler 		/* Pending RADIUS packets used different shared secret, so
156539beb93cSSam Leffler 		 * they need to be modified. Update accounting message
156639beb93cSSam Leffler 		 * authenticators here. Authentication messages are removed
156739beb93cSSam Leffler 		 * since they would require more changes and the new RADIUS
156839beb93cSSam Leffler 		 * server may not be prepared to receive them anyway due to
156939beb93cSSam Leffler 		 * missing state information. Client will likely retry
157039beb93cSSam Leffler 		 * authentication, so this should not be an issue. */
157139beb93cSSam Leffler 		if (auth)
157239beb93cSSam Leffler 			radius_client_flush(radius, 1);
157339beb93cSSam Leffler 		else {
157439beb93cSSam Leffler 			radius_client_update_acct_msgs(
157539beb93cSSam Leffler 				radius, nserv->shared_secret,
157639beb93cSSam Leffler 				nserv->shared_secret_len);
157739beb93cSSam Leffler 		}
157839beb93cSSam Leffler 	}
157939beb93cSSam Leffler 
15804bc52338SCy Schubert 	/* Reset retry counters */
15814bc52338SCy Schubert 	for (entry = radius->msgs; oserv && entry; entry = entry->next) {
158239beb93cSSam Leffler 		if ((auth && entry->msg_type != RADIUS_AUTH) ||
158339beb93cSSam Leffler 		    (!auth && entry->msg_type != RADIUS_ACCT))
158439beb93cSSam Leffler 			continue;
158539beb93cSSam Leffler 		entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
1586c1d255d3SCy Schubert 		entry->attempts = 0;
158739beb93cSSam Leffler 		entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
158839beb93cSSam Leffler 	}
158939beb93cSSam Leffler 
159039beb93cSSam Leffler 	if (radius->msgs) {
159139beb93cSSam Leffler 		eloop_cancel_timeout(radius_client_timer, radius, NULL);
159239beb93cSSam Leffler 		eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
159339beb93cSSam Leffler 				       radius_client_timer, radius, NULL);
159439beb93cSSam Leffler 	}
159539beb93cSSam Leffler 
159639beb93cSSam Leffler 	switch (nserv->addr.af) {
159739beb93cSSam Leffler 	case AF_INET:
159839beb93cSSam Leffler 		os_memset(&serv, 0, sizeof(serv));
159939beb93cSSam Leffler 		serv.sin_family = AF_INET;
160039beb93cSSam Leffler 		serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr;
160139beb93cSSam Leffler 		serv.sin_port = htons(nserv->port);
160239beb93cSSam Leffler 		addr = (struct sockaddr *) &serv;
160339beb93cSSam Leffler 		addrlen = sizeof(serv);
1604*a90b9d01SCy Schubert 		sel_sock = socket(PF_INET, type, 0);
1605*a90b9d01SCy Schubert 		if (sel_sock >= 0)
1606*a90b9d01SCy Schubert 			radius_client_disable_pmtu_discovery(sel_sock);
160739beb93cSSam Leffler 		break;
160839beb93cSSam Leffler #ifdef CONFIG_IPV6
160939beb93cSSam Leffler 	case AF_INET6:
161039beb93cSSam Leffler 		os_memset(&serv6, 0, sizeof(serv6));
161139beb93cSSam Leffler 		serv6.sin6_family = AF_INET6;
161239beb93cSSam Leffler 		os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6,
161339beb93cSSam Leffler 			  sizeof(struct in6_addr));
161439beb93cSSam Leffler 		serv6.sin6_port = htons(nserv->port);
161539beb93cSSam Leffler 		addr = (struct sockaddr *) &serv6;
161639beb93cSSam Leffler 		addrlen = sizeof(serv6);
1617*a90b9d01SCy Schubert 		sel_sock = socket(PF_INET6, type, 0);
161839beb93cSSam Leffler 		break;
161939beb93cSSam Leffler #endif /* CONFIG_IPV6 */
162039beb93cSSam Leffler 	default:
162139beb93cSSam Leffler 		return -1;
162239beb93cSSam Leffler 	}
162339beb93cSSam Leffler 
16245b9c547cSRui Paulo 	if (sel_sock < 0) {
16255b9c547cSRui Paulo 		wpa_printf(MSG_INFO,
1626*a90b9d01SCy Schubert 			   "RADIUS: Failed to open server socket (af=%d auth=%d)",
1627*a90b9d01SCy Schubert 			   nserv->addr.af, auth);
16285b9c547cSRui Paulo 		return -1;
16295b9c547cSRui Paulo 	}
16305b9c547cSRui Paulo 
1631*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
1632*a90b9d01SCy Schubert 	if (tls && fcntl(sel_sock, F_SETFL, O_NONBLOCK) != 0) {
1633*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG, "RADIUS: fnctl(O_NONBLOCK) failed: %s",
1634*a90b9d01SCy Schubert 			   strerror(errno));
1635*a90b9d01SCy Schubert 		close(sel_sock);
1636*a90b9d01SCy Schubert 		return -1;
1637*a90b9d01SCy Schubert 	}
1638*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
1639c1d255d3SCy Schubert 
1640c1d255d3SCy Schubert #ifdef __linux__
1641c1d255d3SCy Schubert 	if (conf->force_client_dev && conf->force_client_dev[0]) {
1642c1d255d3SCy Schubert 		if (setsockopt(sel_sock, SOL_SOCKET, SO_BINDTODEVICE,
1643c1d255d3SCy Schubert 			       conf->force_client_dev,
1644c1d255d3SCy Schubert 			       os_strlen(conf->force_client_dev)) < 0) {
1645c1d255d3SCy Schubert 			wpa_printf(MSG_ERROR,
1646c1d255d3SCy Schubert 				   "RADIUS: setsockopt[SO_BINDTODEVICE]: %s",
1647c1d255d3SCy Schubert 				   strerror(errno));
1648c1d255d3SCy Schubert 			/* Probably not a critical error; continue on and hope
1649c1d255d3SCy Schubert 			 * for the best. */
1650c1d255d3SCy Schubert 		} else {
1651c1d255d3SCy Schubert 			wpa_printf(MSG_DEBUG,
1652c1d255d3SCy Schubert 				   "RADIUS: Bound client socket to device: %s",
1653c1d255d3SCy Schubert 				   conf->force_client_dev);
1654c1d255d3SCy Schubert 		}
1655c1d255d3SCy Schubert 	}
1656c1d255d3SCy Schubert #endif /* __linux__ */
1657c1d255d3SCy Schubert 
165839beb93cSSam Leffler 	if (conf->force_client_addr) {
165939beb93cSSam Leffler 		switch (conf->client_addr.af) {
166039beb93cSSam Leffler 		case AF_INET:
166139beb93cSSam Leffler 			os_memset(&claddr, 0, sizeof(claddr));
166239beb93cSSam Leffler 			claddr.sin_family = AF_INET;
166339beb93cSSam Leffler 			claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr;
166439beb93cSSam Leffler 			claddr.sin_port = htons(0);
166539beb93cSSam Leffler 			cl_addr = (struct sockaddr *) &claddr;
166639beb93cSSam Leffler 			claddrlen = sizeof(claddr);
166739beb93cSSam Leffler 			break;
166839beb93cSSam Leffler #ifdef CONFIG_IPV6
166939beb93cSSam Leffler 		case AF_INET6:
167039beb93cSSam Leffler 			os_memset(&claddr6, 0, sizeof(claddr6));
167139beb93cSSam Leffler 			claddr6.sin6_family = AF_INET6;
167239beb93cSSam Leffler 			os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6,
167339beb93cSSam Leffler 				  sizeof(struct in6_addr));
167439beb93cSSam Leffler 			claddr6.sin6_port = htons(0);
167539beb93cSSam Leffler 			cl_addr = (struct sockaddr *) &claddr6;
167639beb93cSSam Leffler 			claddrlen = sizeof(claddr6);
167739beb93cSSam Leffler 			break;
167839beb93cSSam Leffler #endif /* CONFIG_IPV6 */
167939beb93cSSam Leffler 		default:
1680*a90b9d01SCy Schubert 			close(sel_sock);
168139beb93cSSam Leffler 			return -1;
168239beb93cSSam Leffler 		}
168339beb93cSSam Leffler 
168439beb93cSSam Leffler 		if (bind(sel_sock, cl_addr, claddrlen) < 0) {
16855b9c547cSRui Paulo 			wpa_printf(MSG_INFO, "bind[radius]: %s",
16865b9c547cSRui Paulo 				   strerror(errno));
1687*a90b9d01SCy Schubert 			close(sel_sock);
1688*a90b9d01SCy Schubert 			return -2;
168939beb93cSSam Leffler 		}
169039beb93cSSam Leffler 	}
169139beb93cSSam Leffler 
169239beb93cSSam Leffler 	if (connect(sel_sock, addr, addrlen) < 0) {
1693*a90b9d01SCy Schubert 		if (nserv->tls && errno == EINPROGRESS) {
1694*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
1695*a90b9d01SCy Schubert 				   "RADIUS: TCP connection establishment in progress (sock %d)",
1696*a90b9d01SCy Schubert 				   sel_sock);
1697*a90b9d01SCy Schubert 		} else {
1698*a90b9d01SCy Schubert 			wpa_printf(MSG_INFO, "connect[radius]: %s",
1699*a90b9d01SCy Schubert 				   strerror(errno));
1700*a90b9d01SCy Schubert 			close(sel_sock);
1701*a90b9d01SCy Schubert 			return -2;
1702*a90b9d01SCy Schubert 		}
170339beb93cSSam Leffler 	}
170439beb93cSSam Leffler 
170539beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS
170639beb93cSSam Leffler 	switch (nserv->addr.af) {
170739beb93cSSam Leffler 	case AF_INET:
170839beb93cSSam Leffler 		claddrlen = sizeof(claddr);
17095b9c547cSRui Paulo 		if (getsockname(sel_sock, (struct sockaddr *) &claddr,
17105b9c547cSRui Paulo 				&claddrlen) == 0) {
171139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
17125b9c547cSRui Paulo 				   inet_ntoa(claddr.sin_addr),
17135b9c547cSRui Paulo 				   ntohs(claddr.sin_port));
17145b9c547cSRui Paulo 		}
171539beb93cSSam Leffler 		break;
171639beb93cSSam Leffler #ifdef CONFIG_IPV6
171739beb93cSSam Leffler 	case AF_INET6: {
171839beb93cSSam Leffler 		claddrlen = sizeof(claddr6);
17195b9c547cSRui Paulo 		if (getsockname(sel_sock, (struct sockaddr *) &claddr6,
17205b9c547cSRui Paulo 				&claddrlen) == 0) {
172139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
172239beb93cSSam Leffler 				   inet_ntop(AF_INET6, &claddr6.sin6_addr,
172339beb93cSSam Leffler 					     abuf, sizeof(abuf)),
172439beb93cSSam Leffler 				   ntohs(claddr6.sin6_port));
17255b9c547cSRui Paulo 		}
172639beb93cSSam Leffler 		break;
172739beb93cSSam Leffler 	}
172839beb93cSSam Leffler #endif /* CONFIG_IPV6 */
172939beb93cSSam Leffler 	}
173039beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */
173139beb93cSSam Leffler 
1732*a90b9d01SCy Schubert 	if (auth) {
1733*a90b9d01SCy Schubert 		radius_close_auth_socket(radius);
173439beb93cSSam Leffler 		radius->auth_sock = sel_sock;
1735*a90b9d01SCy Schubert 	} else {
1736*a90b9d01SCy Schubert 		radius_close_acct_socket(radius);
173739beb93cSSam Leffler 		radius->acct_sock = sel_sock;
1738*a90b9d01SCy Schubert 	}
1739*a90b9d01SCy Schubert 
1740*a90b9d01SCy Schubert 	if (!tls)
1741*a90b9d01SCy Schubert 		eloop_register_read_sock(sel_sock, radius_client_receive,
1742*a90b9d01SCy Schubert 					 radius,
1743*a90b9d01SCy Schubert 					 auth ? (void *) RADIUS_AUTH :
1744*a90b9d01SCy Schubert 					 (void *) RADIUS_ACCT);
1745*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
1746*a90b9d01SCy Schubert 	if (tls)
1747*a90b9d01SCy Schubert 		eloop_register_sock(sel_sock, EVENT_TYPE_WRITE,
1748*a90b9d01SCy Schubert 				    radius_client_write_ready, radius,
1749*a90b9d01SCy Schubert 				    auth ? (void *) RADIUS_AUTH :
1750*a90b9d01SCy Schubert 				    (void *) RADIUS_ACCT);
1751*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
1752*a90b9d01SCy Schubert 
1753*a90b9d01SCy Schubert 	if (auth) {
1754*a90b9d01SCy Schubert 		radius->auth_tls = nserv->tls;
1755*a90b9d01SCy Schubert 		radius->auth_tls_ready = false;
1756*a90b9d01SCy Schubert 	} else {
1757*a90b9d01SCy Schubert 		radius->acct_tls = nserv->tls;
1758*a90b9d01SCy Schubert 		radius->acct_tls_ready = false;
1759*a90b9d01SCy Schubert 	}
176039beb93cSSam Leffler 
176139beb93cSSam Leffler 	return 0;
176239beb93cSSam Leffler }
176339beb93cSSam Leffler 
176439beb93cSSam Leffler 
radius_retry_primary_timer(void * eloop_ctx,void * timeout_ctx)176539beb93cSSam Leffler static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
176639beb93cSSam Leffler {
176739beb93cSSam Leffler 	struct radius_client_data *radius = eloop_ctx;
176839beb93cSSam Leffler 	struct hostapd_radius_servers *conf = radius->conf;
176939beb93cSSam Leffler 	struct hostapd_radius_server *oserv;
177039beb93cSSam Leffler 
177139beb93cSSam Leffler 	if (radius->auth_sock >= 0 && conf->auth_servers &&
177239beb93cSSam Leffler 	    conf->auth_server != conf->auth_servers) {
177339beb93cSSam Leffler 		oserv = conf->auth_server;
177439beb93cSSam Leffler 		conf->auth_server = conf->auth_servers;
17755b9c547cSRui Paulo 		if (radius_change_server(radius, conf->auth_server, oserv,
1776*a90b9d01SCy Schubert 					 1) < 0) {
17775b9c547cSRui Paulo 			conf->auth_server = oserv;
17785b9c547cSRui Paulo 			radius_change_server(radius, oserv, conf->auth_server,
1779*a90b9d01SCy Schubert 					     1);
178039beb93cSSam Leffler 		}
17815b9c547cSRui Paulo 	}
178239beb93cSSam Leffler 
178339beb93cSSam Leffler 	if (radius->acct_sock >= 0 && conf->acct_servers &&
178439beb93cSSam Leffler 	    conf->acct_server != conf->acct_servers) {
178539beb93cSSam Leffler 		oserv = conf->acct_server;
178639beb93cSSam Leffler 		conf->acct_server = conf->acct_servers;
17875b9c547cSRui Paulo 		if (radius_change_server(radius, conf->acct_server, oserv,
1788*a90b9d01SCy Schubert 					 0) < 0) {
17895b9c547cSRui Paulo 			conf->acct_server = oserv;
17905b9c547cSRui Paulo 			radius_change_server(radius, oserv, conf->acct_server,
1791*a90b9d01SCy Schubert 					     0);
179239beb93cSSam Leffler 		}
17935b9c547cSRui Paulo 	}
179439beb93cSSam Leffler 
179539beb93cSSam Leffler 	if (conf->retry_primary_interval)
179639beb93cSSam Leffler 		eloop_register_timeout(conf->retry_primary_interval, 0,
179739beb93cSSam Leffler 				       radius_retry_primary_timer, radius,
179839beb93cSSam Leffler 				       NULL);
179939beb93cSSam Leffler }
180039beb93cSSam Leffler 
180139beb93cSSam Leffler 
radius_client_init_auth(struct radius_client_data * radius)180239beb93cSSam Leffler static int radius_client_init_auth(struct radius_client_data *radius)
180339beb93cSSam Leffler {
1804*a90b9d01SCy Schubert 	radius_close_auth_socket(radius);
1805*a90b9d01SCy Schubert 	return radius_change_server(radius, radius->conf->auth_server, NULL, 1);
180639beb93cSSam Leffler }
180739beb93cSSam Leffler 
180839beb93cSSam Leffler 
radius_client_init_acct(struct radius_client_data * radius)180939beb93cSSam Leffler static int radius_client_init_acct(struct radius_client_data *radius)
181039beb93cSSam Leffler {
1811*a90b9d01SCy Schubert 	radius_close_acct_socket(radius);
1812*a90b9d01SCy Schubert 	return radius_change_server(radius, radius->conf->acct_server, NULL, 0);
18133157ba21SRui Paulo }
181439beb93cSSam Leffler 
181539beb93cSSam Leffler 
1816*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
radius_tls_event_cb(void * ctx,enum tls_event ev,union tls_event_data * data)1817*a90b9d01SCy Schubert static void radius_tls_event_cb(void *ctx, enum tls_event ev,
1818*a90b9d01SCy Schubert 				union tls_event_data *data)
1819*a90b9d01SCy Schubert {
1820*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG, "RADIUS: TLS event %d", ev);
182139beb93cSSam Leffler }
1822*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
182339beb93cSSam Leffler 
182439beb93cSSam Leffler 
1825e28a4053SRui Paulo /**
1826e28a4053SRui Paulo  * radius_client_init - Initialize RADIUS client
1827e28a4053SRui Paulo  * @ctx: Callback context to be used in hostapd_logger() calls
1828e28a4053SRui Paulo  * @conf: RADIUS client configuration (RADIUS servers)
1829e28a4053SRui Paulo  * Returns: Pointer to private RADIUS client context or %NULL on failure
1830e28a4053SRui Paulo  *
1831e28a4053SRui Paulo  * The caller is responsible for keeping the configuration data available for
1832e28a4053SRui Paulo  * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is
1833e28a4053SRui Paulo  * called for the returned context pointer.
1834e28a4053SRui Paulo  */
183539beb93cSSam Leffler struct radius_client_data *
radius_client_init(void * ctx,struct hostapd_radius_servers * conf)183639beb93cSSam Leffler radius_client_init(void *ctx, struct hostapd_radius_servers *conf)
183739beb93cSSam Leffler {
183839beb93cSSam Leffler 	struct radius_client_data *radius;
183939beb93cSSam Leffler 
184039beb93cSSam Leffler 	radius = os_zalloc(sizeof(struct radius_client_data));
184139beb93cSSam Leffler 	if (radius == NULL)
184239beb93cSSam Leffler 		return NULL;
184339beb93cSSam Leffler 
184439beb93cSSam Leffler 	radius->ctx = ctx;
184539beb93cSSam Leffler 	radius->conf = conf;
184639beb93cSSam Leffler 	radius->auth_sock = radius->acct_sock = -1;
184739beb93cSSam Leffler 
1848*a90b9d01SCy Schubert 	if (conf->auth_server && radius_client_init_auth(radius) == -1) {
184939beb93cSSam Leffler 		radius_client_deinit(radius);
185039beb93cSSam Leffler 		return NULL;
185139beb93cSSam Leffler 	}
185239beb93cSSam Leffler 
1853*a90b9d01SCy Schubert 	if (conf->acct_server && radius_client_init_acct(radius) == -1) {
185439beb93cSSam Leffler 		radius_client_deinit(radius);
185539beb93cSSam Leffler 		return NULL;
185639beb93cSSam Leffler 	}
185739beb93cSSam Leffler 
185839beb93cSSam Leffler 	if (conf->retry_primary_interval)
185939beb93cSSam Leffler 		eloop_register_timeout(conf->retry_primary_interval, 0,
186039beb93cSSam Leffler 				       radius_retry_primary_timer, radius,
186139beb93cSSam Leffler 				       NULL);
186239beb93cSSam Leffler 
1863*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
1864*a90b9d01SCy Schubert 	if ((conf->auth_server && conf->auth_server->tls) ||
1865*a90b9d01SCy Schubert 	    (conf->acct_server && conf->acct_server->tls)) {
1866*a90b9d01SCy Schubert 		struct tls_config tls_conf;
1867*a90b9d01SCy Schubert 
1868*a90b9d01SCy Schubert 		os_memset(&tls_conf, 0, sizeof(tls_conf));
1869*a90b9d01SCy Schubert 		tls_conf.event_cb = radius_tls_event_cb;
1870*a90b9d01SCy Schubert 		radius->tls_ctx = tls_init(&tls_conf);
1871*a90b9d01SCy Schubert 		if (!radius->tls_ctx) {
1872*a90b9d01SCy Schubert 			radius_client_deinit(radius);
1873*a90b9d01SCy Schubert 			return NULL;
1874*a90b9d01SCy Schubert 		}
1875*a90b9d01SCy Schubert 	}
1876*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
1877*a90b9d01SCy Schubert 
1878*a90b9d01SCy Schubert 
187939beb93cSSam Leffler 	return radius;
188039beb93cSSam Leffler }
188139beb93cSSam Leffler 
188239beb93cSSam Leffler 
1883e28a4053SRui Paulo /**
1884e28a4053SRui Paulo  * radius_client_deinit - Deinitialize RADIUS client
1885e28a4053SRui Paulo  * @radius: RADIUS client context from radius_client_init()
1886e28a4053SRui Paulo  */
radius_client_deinit(struct radius_client_data * radius)188739beb93cSSam Leffler void radius_client_deinit(struct radius_client_data *radius)
188839beb93cSSam Leffler {
188939beb93cSSam Leffler 	if (!radius)
189039beb93cSSam Leffler 		return;
189139beb93cSSam Leffler 
1892*a90b9d01SCy Schubert 	radius_close_auth_socket(radius);
1893*a90b9d01SCy Schubert 	radius_close_acct_socket(radius);
189439beb93cSSam Leffler 
189539beb93cSSam Leffler 	eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
189639beb93cSSam Leffler 
189739beb93cSSam Leffler 	radius_client_flush(radius, 0);
189839beb93cSSam Leffler 	os_free(radius->auth_handlers);
189939beb93cSSam Leffler 	os_free(radius->acct_handlers);
1900*a90b9d01SCy Schubert #ifdef CONFIG_RADIUS_TLS
1901*a90b9d01SCy Schubert 	if (radius->tls_ctx) {
1902*a90b9d01SCy Schubert 		tls_connection_deinit(radius->tls_ctx, radius->auth_tls_conn);
1903*a90b9d01SCy Schubert 		tls_connection_deinit(radius->tls_ctx, radius->acct_tls_conn);
1904*a90b9d01SCy Schubert 		tls_deinit(radius->tls_ctx);
1905*a90b9d01SCy Schubert 	}
1906*a90b9d01SCy Schubert #endif /* CONFIG_RADIUS_TLS */
190739beb93cSSam Leffler 	os_free(radius);
190839beb93cSSam Leffler }
190939beb93cSSam Leffler 
191039beb93cSSam Leffler 
1911e28a4053SRui Paulo /**
1912e28a4053SRui Paulo  * radius_client_flush_auth - Flush pending RADIUS messages for an address
1913e28a4053SRui Paulo  * @radius: RADIUS client context from radius_client_init()
1914e28a4053SRui Paulo  * @addr: MAC address of the related device
1915e28a4053SRui Paulo  *
1916e28a4053SRui Paulo  * This function can be used to remove pending RADIUS authentication messages
1917e28a4053SRui Paulo  * that are related to a specific device. The addr parameter is matched with
1918e28a4053SRui Paulo  * the one used in radius_client_send() call that was used to transmit the
1919e28a4053SRui Paulo  * authentication request.
1920e28a4053SRui Paulo  */
radius_client_flush_auth(struct radius_client_data * radius,const u8 * addr)1921e28a4053SRui Paulo void radius_client_flush_auth(struct radius_client_data *radius,
1922e28a4053SRui Paulo 			      const u8 *addr)
192339beb93cSSam Leffler {
192439beb93cSSam Leffler 	struct radius_msg_list *entry, *prev, *tmp;
192539beb93cSSam Leffler 
192639beb93cSSam Leffler 	prev = NULL;
192739beb93cSSam Leffler 	entry = radius->msgs;
192839beb93cSSam Leffler 	while (entry) {
192939beb93cSSam Leffler 		if (entry->msg_type == RADIUS_AUTH &&
1930*a90b9d01SCy Schubert 		    ether_addr_equal(entry->addr, addr)) {
193139beb93cSSam Leffler 			hostapd_logger(radius->ctx, addr,
193239beb93cSSam Leffler 				       HOSTAPD_MODULE_RADIUS,
193339beb93cSSam Leffler 				       HOSTAPD_LEVEL_DEBUG,
193439beb93cSSam Leffler 				       "Removing pending RADIUS authentication"
193539beb93cSSam Leffler 				       " message for removed client");
193639beb93cSSam Leffler 
193739beb93cSSam Leffler 			if (prev)
193839beb93cSSam Leffler 				prev->next = entry->next;
193939beb93cSSam Leffler 			else
194039beb93cSSam Leffler 				radius->msgs = entry->next;
194139beb93cSSam Leffler 
194239beb93cSSam Leffler 			tmp = entry;
194339beb93cSSam Leffler 			entry = entry->next;
194439beb93cSSam Leffler 			radius_client_msg_free(tmp);
194539beb93cSSam Leffler 			radius->num_msgs--;
194639beb93cSSam Leffler 			continue;
194739beb93cSSam Leffler 		}
194839beb93cSSam Leffler 
194939beb93cSSam Leffler 		prev = entry;
195039beb93cSSam Leffler 		entry = entry->next;
195139beb93cSSam Leffler 	}
195239beb93cSSam Leffler }
195339beb93cSSam Leffler 
195439beb93cSSam Leffler 
radius_client_dump_auth_server(char * buf,size_t buflen,struct hostapd_radius_server * serv,struct radius_client_data * cli)195539beb93cSSam Leffler static int radius_client_dump_auth_server(char *buf, size_t buflen,
195639beb93cSSam Leffler 					  struct hostapd_radius_server *serv,
195739beb93cSSam Leffler 					  struct radius_client_data *cli)
195839beb93cSSam Leffler {
195939beb93cSSam Leffler 	int pending = 0;
196039beb93cSSam Leffler 	struct radius_msg_list *msg;
196139beb93cSSam Leffler 	char abuf[50];
196239beb93cSSam Leffler 
196339beb93cSSam Leffler 	if (cli) {
196439beb93cSSam Leffler 		for (msg = cli->msgs; msg; msg = msg->next) {
196539beb93cSSam Leffler 			if (msg->msg_type == RADIUS_AUTH)
196639beb93cSSam Leffler 				pending++;
196739beb93cSSam Leffler 		}
196839beb93cSSam Leffler 	}
196939beb93cSSam Leffler 
197039beb93cSSam Leffler 	return os_snprintf(buf, buflen,
197139beb93cSSam Leffler 			   "radiusAuthServerIndex=%d\n"
197239beb93cSSam Leffler 			   "radiusAuthServerAddress=%s\n"
197339beb93cSSam Leffler 			   "radiusAuthClientServerPortNumber=%d\n"
197439beb93cSSam Leffler 			   "radiusAuthClientRoundTripTime=%d\n"
197539beb93cSSam Leffler 			   "radiusAuthClientAccessRequests=%u\n"
197639beb93cSSam Leffler 			   "radiusAuthClientAccessRetransmissions=%u\n"
197739beb93cSSam Leffler 			   "radiusAuthClientAccessAccepts=%u\n"
197839beb93cSSam Leffler 			   "radiusAuthClientAccessRejects=%u\n"
197939beb93cSSam Leffler 			   "radiusAuthClientAccessChallenges=%u\n"
198039beb93cSSam Leffler 			   "radiusAuthClientMalformedAccessResponses=%u\n"
198139beb93cSSam Leffler 			   "radiusAuthClientBadAuthenticators=%u\n"
198239beb93cSSam Leffler 			   "radiusAuthClientPendingRequests=%u\n"
198339beb93cSSam Leffler 			   "radiusAuthClientTimeouts=%u\n"
198439beb93cSSam Leffler 			   "radiusAuthClientUnknownTypes=%u\n"
198539beb93cSSam Leffler 			   "radiusAuthClientPacketsDropped=%u\n",
198639beb93cSSam Leffler 			   serv->index,
198739beb93cSSam Leffler 			   hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
198839beb93cSSam Leffler 			   serv->port,
198939beb93cSSam Leffler 			   serv->round_trip_time,
199039beb93cSSam Leffler 			   serv->requests,
199139beb93cSSam Leffler 			   serv->retransmissions,
199239beb93cSSam Leffler 			   serv->access_accepts,
199339beb93cSSam Leffler 			   serv->access_rejects,
199439beb93cSSam Leffler 			   serv->access_challenges,
199539beb93cSSam Leffler 			   serv->malformed_responses,
199639beb93cSSam Leffler 			   serv->bad_authenticators,
199739beb93cSSam Leffler 			   pending,
199839beb93cSSam Leffler 			   serv->timeouts,
199939beb93cSSam Leffler 			   serv->unknown_types,
200039beb93cSSam Leffler 			   serv->packets_dropped);
200139beb93cSSam Leffler }
200239beb93cSSam Leffler 
200339beb93cSSam Leffler 
radius_client_dump_acct_server(char * buf,size_t buflen,struct hostapd_radius_server * serv,struct radius_client_data * cli)200439beb93cSSam Leffler static int radius_client_dump_acct_server(char *buf, size_t buflen,
200539beb93cSSam Leffler 					  struct hostapd_radius_server *serv,
200639beb93cSSam Leffler 					  struct radius_client_data *cli)
200739beb93cSSam Leffler {
200839beb93cSSam Leffler 	int pending = 0;
200939beb93cSSam Leffler 	struct radius_msg_list *msg;
201039beb93cSSam Leffler 	char abuf[50];
201139beb93cSSam Leffler 
201239beb93cSSam Leffler 	if (cli) {
201339beb93cSSam Leffler 		for (msg = cli->msgs; msg; msg = msg->next) {
201439beb93cSSam Leffler 			if (msg->msg_type == RADIUS_ACCT ||
201539beb93cSSam Leffler 			    msg->msg_type == RADIUS_ACCT_INTERIM)
201639beb93cSSam Leffler 				pending++;
201739beb93cSSam Leffler 		}
201839beb93cSSam Leffler 	}
201939beb93cSSam Leffler 
202039beb93cSSam Leffler 	return os_snprintf(buf, buflen,
202139beb93cSSam Leffler 			   "radiusAccServerIndex=%d\n"
202239beb93cSSam Leffler 			   "radiusAccServerAddress=%s\n"
202339beb93cSSam Leffler 			   "radiusAccClientServerPortNumber=%d\n"
202439beb93cSSam Leffler 			   "radiusAccClientRoundTripTime=%d\n"
202539beb93cSSam Leffler 			   "radiusAccClientRequests=%u\n"
202639beb93cSSam Leffler 			   "radiusAccClientRetransmissions=%u\n"
202739beb93cSSam Leffler 			   "radiusAccClientResponses=%u\n"
202839beb93cSSam Leffler 			   "radiusAccClientMalformedResponses=%u\n"
202939beb93cSSam Leffler 			   "radiusAccClientBadAuthenticators=%u\n"
203039beb93cSSam Leffler 			   "radiusAccClientPendingRequests=%u\n"
203139beb93cSSam Leffler 			   "radiusAccClientTimeouts=%u\n"
203239beb93cSSam Leffler 			   "radiusAccClientUnknownTypes=%u\n"
203339beb93cSSam Leffler 			   "radiusAccClientPacketsDropped=%u\n",
203439beb93cSSam Leffler 			   serv->index,
203539beb93cSSam Leffler 			   hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
203639beb93cSSam Leffler 			   serv->port,
203739beb93cSSam Leffler 			   serv->round_trip_time,
203839beb93cSSam Leffler 			   serv->requests,
203939beb93cSSam Leffler 			   serv->retransmissions,
204039beb93cSSam Leffler 			   serv->responses,
204139beb93cSSam Leffler 			   serv->malformed_responses,
204239beb93cSSam Leffler 			   serv->bad_authenticators,
204339beb93cSSam Leffler 			   pending,
204439beb93cSSam Leffler 			   serv->timeouts,
204539beb93cSSam Leffler 			   serv->unknown_types,
204639beb93cSSam Leffler 			   serv->packets_dropped);
204739beb93cSSam Leffler }
204839beb93cSSam Leffler 
204939beb93cSSam Leffler 
2050e28a4053SRui Paulo /**
2051e28a4053SRui Paulo  * radius_client_get_mib - Get RADIUS client MIB information
2052e28a4053SRui Paulo  * @radius: RADIUS client context from radius_client_init()
2053e28a4053SRui Paulo  * @buf: Buffer for returning MIB data in text format
2054e28a4053SRui Paulo  * @buflen: Maximum buf length in octets
2055e28a4053SRui Paulo  * Returns: Number of octets written into the buffer
2056e28a4053SRui Paulo  */
radius_client_get_mib(struct radius_client_data * radius,char * buf,size_t buflen)205739beb93cSSam Leffler int radius_client_get_mib(struct radius_client_data *radius, char *buf,
205839beb93cSSam Leffler 			  size_t buflen)
205939beb93cSSam Leffler {
2060780fb4a2SCy Schubert 	struct hostapd_radius_servers *conf;
206139beb93cSSam Leffler 	int i;
206239beb93cSSam Leffler 	struct hostapd_radius_server *serv;
206339beb93cSSam Leffler 	int count = 0;
206439beb93cSSam Leffler 
2065780fb4a2SCy Schubert 	if (!radius)
2066780fb4a2SCy Schubert 		return 0;
2067780fb4a2SCy Schubert 
2068780fb4a2SCy Schubert 	conf = radius->conf;
2069780fb4a2SCy Schubert 
207039beb93cSSam Leffler 	if (conf->auth_servers) {
207139beb93cSSam Leffler 		for (i = 0; i < conf->num_auth_servers; i++) {
207239beb93cSSam Leffler 			serv = &conf->auth_servers[i];
207339beb93cSSam Leffler 			count += radius_client_dump_auth_server(
207439beb93cSSam Leffler 				buf + count, buflen - count, serv,
207539beb93cSSam Leffler 				serv == conf->auth_server ?
207639beb93cSSam Leffler 				radius : NULL);
207739beb93cSSam Leffler 		}
207839beb93cSSam Leffler 	}
207939beb93cSSam Leffler 
208039beb93cSSam Leffler 	if (conf->acct_servers) {
208139beb93cSSam Leffler 		for (i = 0; i < conf->num_acct_servers; i++) {
208239beb93cSSam Leffler 			serv = &conf->acct_servers[i];
208339beb93cSSam Leffler 			count += radius_client_dump_acct_server(
208439beb93cSSam Leffler 				buf + count, buflen - count, serv,
208539beb93cSSam Leffler 				serv == conf->acct_server ?
208639beb93cSSam Leffler 				radius : NULL);
208739beb93cSSam Leffler 		}
208839beb93cSSam Leffler 	}
208939beb93cSSam Leffler 
209039beb93cSSam Leffler 	return count;
209139beb93cSSam Leffler }
2092f05cddf9SRui Paulo 
2093f05cddf9SRui Paulo 
radius_client_reconfig(struct radius_client_data * radius,struct hostapd_radius_servers * conf)2094f05cddf9SRui Paulo void radius_client_reconfig(struct radius_client_data *radius,
2095f05cddf9SRui Paulo 			    struct hostapd_radius_servers *conf)
2096f05cddf9SRui Paulo {
2097f05cddf9SRui Paulo 	if (radius)
2098f05cddf9SRui Paulo 		radius->conf = conf;
2099f05cddf9SRui Paulo }
2100