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(¶ms, 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, ¶ms)) {
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