xref: /freebsd/contrib/wpa/src/radius/radius_server.c (revision 85732ac8bccbc0adcf5a261ea1ffec8ca7b3a92d)
139beb93cSSam Leffler /*
2e28a4053SRui Paulo  * RADIUS authentication server
35b9c547cSRui Paulo  * Copyright (c) 2005-2009, 2011-2014, 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"
1039beb93cSSam Leffler #include <net/if.h>
115b9c547cSRui Paulo #ifdef CONFIG_SQLITE
125b9c547cSRui Paulo #include <sqlite3.h>
135b9c547cSRui Paulo #endif /* CONFIG_SQLITE */
1439beb93cSSam Leffler 
1539beb93cSSam Leffler #include "common.h"
1639beb93cSSam Leffler #include "radius.h"
1739beb93cSSam Leffler #include "eloop.h"
1839beb93cSSam Leffler #include "eap_server/eap.h"
195b9c547cSRui Paulo #include "ap/ap_config.h"
205b9c547cSRui Paulo #include "crypto/tls.h"
2139beb93cSSam Leffler #include "radius_server.h"
2239beb93cSSam Leffler 
23e28a4053SRui Paulo /**
24e28a4053SRui Paulo  * RADIUS_SESSION_TIMEOUT - Session timeout in seconds
25e28a4053SRui Paulo  */
2639beb93cSSam Leffler #define RADIUS_SESSION_TIMEOUT 60
27e28a4053SRui Paulo 
28e28a4053SRui Paulo /**
29*85732ac8SCy Schubert  * RADIUS_SESSION_MAINTAIN - Completed session expiration timeout in seconds
30*85732ac8SCy Schubert  */
31*85732ac8SCy Schubert #define RADIUS_SESSION_MAINTAIN 5
32*85732ac8SCy Schubert 
33*85732ac8SCy Schubert /**
34e28a4053SRui Paulo  * RADIUS_MAX_SESSION - Maximum number of active sessions
35e28a4053SRui Paulo  */
36*85732ac8SCy Schubert #define RADIUS_MAX_SESSION 1000
37e28a4053SRui Paulo 
38e28a4053SRui Paulo /**
39e28a4053SRui Paulo  * RADIUS_MAX_MSG_LEN - Maximum message length for incoming RADIUS messages
40e28a4053SRui Paulo  */
4139beb93cSSam Leffler #define RADIUS_MAX_MSG_LEN 3000
4239beb93cSSam Leffler 
43325151a3SRui Paulo static const struct eapol_callbacks radius_server_eapol_cb;
4439beb93cSSam Leffler 
4539beb93cSSam Leffler struct radius_client;
4639beb93cSSam Leffler struct radius_server_data;
4739beb93cSSam Leffler 
48e28a4053SRui Paulo /**
49e28a4053SRui Paulo  * struct radius_server_counters - RADIUS server statistics counters
50e28a4053SRui Paulo  */
5139beb93cSSam Leffler struct radius_server_counters {
5239beb93cSSam Leffler 	u32 access_requests;
5339beb93cSSam Leffler 	u32 invalid_requests;
5439beb93cSSam Leffler 	u32 dup_access_requests;
5539beb93cSSam Leffler 	u32 access_accepts;
5639beb93cSSam Leffler 	u32 access_rejects;
5739beb93cSSam Leffler 	u32 access_challenges;
5839beb93cSSam Leffler 	u32 malformed_access_requests;
5939beb93cSSam Leffler 	u32 bad_authenticators;
6039beb93cSSam Leffler 	u32 packets_dropped;
6139beb93cSSam Leffler 	u32 unknown_types;
625b9c547cSRui Paulo 
635b9c547cSRui Paulo 	u32 acct_requests;
645b9c547cSRui Paulo 	u32 invalid_acct_requests;
655b9c547cSRui Paulo 	u32 acct_responses;
665b9c547cSRui Paulo 	u32 malformed_acct_requests;
675b9c547cSRui Paulo 	u32 acct_bad_authenticators;
685b9c547cSRui Paulo 	u32 unknown_acct_types;
6939beb93cSSam Leffler };
7039beb93cSSam Leffler 
71e28a4053SRui Paulo /**
72e28a4053SRui Paulo  * struct radius_session - Internal RADIUS server data for a session
73e28a4053SRui Paulo  */
7439beb93cSSam Leffler struct radius_session {
7539beb93cSSam Leffler 	struct radius_session *next;
7639beb93cSSam Leffler 	struct radius_client *client;
7739beb93cSSam Leffler 	struct radius_server_data *server;
7839beb93cSSam Leffler 	unsigned int sess_id;
7939beb93cSSam Leffler 	struct eap_sm *eap;
8039beb93cSSam Leffler 	struct eap_eapol_interface *eap_if;
815b9c547cSRui Paulo 	char *username; /* from User-Name attribute */
825b9c547cSRui Paulo 	char *nas_ip;
83*85732ac8SCy Schubert 	u8 mac_addr[ETH_ALEN]; /* from Calling-Station-Id attribute */
8439beb93cSSam Leffler 
8539beb93cSSam Leffler 	struct radius_msg *last_msg;
8639beb93cSSam Leffler 	char *last_from_addr;
8739beb93cSSam Leffler 	int last_from_port;
8839beb93cSSam Leffler 	struct sockaddr_storage last_from;
8939beb93cSSam Leffler 	socklen_t last_fromlen;
9039beb93cSSam Leffler 	u8 last_identifier;
9139beb93cSSam Leffler 	struct radius_msg *last_reply;
9239beb93cSSam Leffler 	u8 last_authenticator[16];
935b9c547cSRui Paulo 
945b9c547cSRui Paulo 	unsigned int remediation:1;
955b9c547cSRui Paulo 	unsigned int macacl:1;
96*85732ac8SCy Schubert 	unsigned int t_c_filtering:1;
975b9c547cSRui Paulo 
985b9c547cSRui Paulo 	struct hostapd_radius_attr *accept_attr;
99*85732ac8SCy Schubert 
100*85732ac8SCy Schubert 	u32 t_c_timestamp; /* Last read T&C timestamp from user DB */
10139beb93cSSam Leffler };
10239beb93cSSam Leffler 
103e28a4053SRui Paulo /**
104e28a4053SRui Paulo  * struct radius_client - Internal RADIUS server data for a client
105e28a4053SRui Paulo  */
10639beb93cSSam Leffler struct radius_client {
10739beb93cSSam Leffler 	struct radius_client *next;
10839beb93cSSam Leffler 	struct in_addr addr;
10939beb93cSSam Leffler 	struct in_addr mask;
11039beb93cSSam Leffler #ifdef CONFIG_IPV6
11139beb93cSSam Leffler 	struct in6_addr addr6;
11239beb93cSSam Leffler 	struct in6_addr mask6;
11339beb93cSSam Leffler #endif /* CONFIG_IPV6 */
11439beb93cSSam Leffler 	char *shared_secret;
11539beb93cSSam Leffler 	int shared_secret_len;
11639beb93cSSam Leffler 	struct radius_session *sessions;
11739beb93cSSam Leffler 	struct radius_server_counters counters;
118*85732ac8SCy Schubert 
119*85732ac8SCy Schubert 	u8 next_dac_identifier;
120*85732ac8SCy Schubert 	struct radius_msg *pending_dac_coa_req;
121*85732ac8SCy Schubert 	u8 pending_dac_coa_id;
122*85732ac8SCy Schubert 	u8 pending_dac_coa_addr[ETH_ALEN];
123*85732ac8SCy Schubert 	struct radius_msg *pending_dac_disconnect_req;
124*85732ac8SCy Schubert 	u8 pending_dac_disconnect_id;
125*85732ac8SCy Schubert 	u8 pending_dac_disconnect_addr[ETH_ALEN];
12639beb93cSSam Leffler };
12739beb93cSSam Leffler 
128e28a4053SRui Paulo /**
129e28a4053SRui Paulo  * struct radius_server_data - Internal RADIUS server data
130e28a4053SRui Paulo  */
13139beb93cSSam Leffler struct radius_server_data {
132e28a4053SRui Paulo 	/**
133e28a4053SRui Paulo 	 * auth_sock - Socket for RADIUS authentication messages
134e28a4053SRui Paulo 	 */
13539beb93cSSam Leffler 	int auth_sock;
136e28a4053SRui Paulo 
137e28a4053SRui Paulo 	/**
1385b9c547cSRui Paulo 	 * acct_sock - Socket for RADIUS accounting messages
1395b9c547cSRui Paulo 	 */
1405b9c547cSRui Paulo 	int acct_sock;
1415b9c547cSRui Paulo 
1425b9c547cSRui Paulo 	/**
143e28a4053SRui Paulo 	 * clients - List of authorized RADIUS clients
144e28a4053SRui Paulo 	 */
14539beb93cSSam Leffler 	struct radius_client *clients;
146e28a4053SRui Paulo 
147e28a4053SRui Paulo 	/**
148e28a4053SRui Paulo 	 * next_sess_id - Next session identifier
149e28a4053SRui Paulo 	 */
15039beb93cSSam Leffler 	unsigned int next_sess_id;
151e28a4053SRui Paulo 
152e28a4053SRui Paulo 	/**
153e28a4053SRui Paulo 	 * conf_ctx - Context pointer for callbacks
154e28a4053SRui Paulo 	 *
155e28a4053SRui Paulo 	 * This is used as the ctx argument in get_eap_user() calls.
156e28a4053SRui Paulo 	 */
15739beb93cSSam Leffler 	void *conf_ctx;
158e28a4053SRui Paulo 
159e28a4053SRui Paulo 	/**
160e28a4053SRui Paulo 	 * num_sess - Number of active sessions
161e28a4053SRui Paulo 	 */
16239beb93cSSam Leffler 	int num_sess;
163e28a4053SRui Paulo 
164e28a4053SRui Paulo 	/**
165e28a4053SRui Paulo 	 * eap_sim_db_priv - EAP-SIM/AKA database context
166e28a4053SRui Paulo 	 *
167e28a4053SRui Paulo 	 * This is passed to the EAP-SIM/AKA server implementation as a
168e28a4053SRui Paulo 	 * callback context.
169e28a4053SRui Paulo 	 */
17039beb93cSSam Leffler 	void *eap_sim_db_priv;
171e28a4053SRui Paulo 
172e28a4053SRui Paulo 	/**
173e28a4053SRui Paulo 	 * ssl_ctx - TLS context
174e28a4053SRui Paulo 	 *
175e28a4053SRui Paulo 	 * This is passed to the EAP server implementation as a callback
176e28a4053SRui Paulo 	 * context for TLS operations.
177e28a4053SRui Paulo 	 */
17839beb93cSSam Leffler 	void *ssl_ctx;
179e28a4053SRui Paulo 
180e28a4053SRui Paulo 	/**
181e28a4053SRui Paulo 	 * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST
182e28a4053SRui Paulo 	 *
183e28a4053SRui Paulo 	 * This parameter is used to set a key for EAP-FAST to encrypt the
184e28a4053SRui Paulo 	 * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If
185e28a4053SRui Paulo 	 * set, must point to a 16-octet key.
186e28a4053SRui Paulo 	 */
18739beb93cSSam Leffler 	u8 *pac_opaque_encr_key;
188e28a4053SRui Paulo 
189e28a4053SRui Paulo 	/**
190e28a4053SRui Paulo 	 * eap_fast_a_id - EAP-FAST authority identity (A-ID)
191e28a4053SRui Paulo 	 *
192e28a4053SRui Paulo 	 * If EAP-FAST is not used, this can be set to %NULL. In theory, this
193e28a4053SRui Paulo 	 * is a variable length field, but due to some existing implementations
194e28a4053SRui Paulo 	 * requiring A-ID to be 16 octets in length, it is recommended to use
195e28a4053SRui Paulo 	 * that length for the field to provide interoperability with deployed
196e28a4053SRui Paulo 	 * peer implementations.
197e28a4053SRui Paulo 	 */
19839beb93cSSam Leffler 	u8 *eap_fast_a_id;
199e28a4053SRui Paulo 
200e28a4053SRui Paulo 	/**
201e28a4053SRui Paulo 	 * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets
202e28a4053SRui Paulo 	 */
20339beb93cSSam Leffler 	size_t eap_fast_a_id_len;
204e28a4053SRui Paulo 
205e28a4053SRui Paulo 	/**
206e28a4053SRui Paulo 	 * eap_fast_a_id_info - EAP-FAST authority identifier information
207e28a4053SRui Paulo 	 *
208e28a4053SRui Paulo 	 * This A-ID-Info contains a user-friendly name for the A-ID. For
209e28a4053SRui Paulo 	 * example, this could be the enterprise and server names in
210e28a4053SRui Paulo 	 * human-readable format. This field is encoded as UTF-8. If EAP-FAST
211e28a4053SRui Paulo 	 * is not used, this can be set to %NULL.
212e28a4053SRui Paulo 	 */
21339beb93cSSam Leffler 	char *eap_fast_a_id_info;
214e28a4053SRui Paulo 
215e28a4053SRui Paulo 	/**
216e28a4053SRui Paulo 	 * eap_fast_prov - EAP-FAST provisioning modes
217e28a4053SRui Paulo 	 *
218e28a4053SRui Paulo 	 * 0 = provisioning disabled, 1 = only anonymous provisioning allowed,
219e28a4053SRui Paulo 	 * 2 = only authenticated provisioning allowed, 3 = both provisioning
220e28a4053SRui Paulo 	 * modes allowed.
221e28a4053SRui Paulo 	 */
22239beb93cSSam Leffler 	int eap_fast_prov;
223e28a4053SRui Paulo 
224e28a4053SRui Paulo 	/**
225e28a4053SRui Paulo 	 * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds
226e28a4053SRui Paulo 	 *
227e28a4053SRui Paulo 	 * This is the hard limit on how long a provisioned PAC-Key can be
228e28a4053SRui Paulo 	 * used.
229e28a4053SRui Paulo 	 */
23039beb93cSSam Leffler 	int pac_key_lifetime;
231e28a4053SRui Paulo 
232e28a4053SRui Paulo 	/**
233e28a4053SRui Paulo 	 * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds
234e28a4053SRui Paulo 	 *
235e28a4053SRui Paulo 	 * This is a soft limit on the PAC-Key. The server will automatically
236e28a4053SRui Paulo 	 * generate a new PAC-Key when this number of seconds (or fewer) of the
237e28a4053SRui Paulo 	 * lifetime remains.
238e28a4053SRui Paulo 	 */
23939beb93cSSam Leffler 	int pac_key_refresh_time;
240e28a4053SRui Paulo 
241e28a4053SRui Paulo 	/**
242e28a4053SRui Paulo 	 * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
243e28a4053SRui Paulo 	 *
244e28a4053SRui Paulo 	 * This controls whether the protected success/failure indication
245e28a4053SRui Paulo 	 * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA.
246e28a4053SRui Paulo 	 */
24739beb93cSSam Leffler 	int eap_sim_aka_result_ind;
248e28a4053SRui Paulo 
249e28a4053SRui Paulo 	/**
250e28a4053SRui Paulo 	 * tnc - Trusted Network Connect (TNC)
251e28a4053SRui Paulo 	 *
252e28a4053SRui Paulo 	 * This controls whether TNC is enabled and will be required before the
253e28a4053SRui Paulo 	 * peer is allowed to connect. Note: This is only used with EAP-TTLS
254e28a4053SRui Paulo 	 * and EAP-FAST. If any other EAP method is enabled, the peer will be
255e28a4053SRui Paulo 	 * allowed to connect without TNC.
256e28a4053SRui Paulo 	 */
25739beb93cSSam Leffler 	int tnc;
258e28a4053SRui Paulo 
259e28a4053SRui Paulo 	/**
260f05cddf9SRui Paulo 	 * pwd_group - The D-H group assigned for EAP-pwd
261f05cddf9SRui Paulo 	 *
262f05cddf9SRui Paulo 	 * If EAP-pwd is not used it can be set to zero.
263f05cddf9SRui Paulo 	 */
264f05cddf9SRui Paulo 	u16 pwd_group;
265f05cddf9SRui Paulo 
266f05cddf9SRui Paulo 	/**
2675b9c547cSRui Paulo 	 * server_id - Server identity
2685b9c547cSRui Paulo 	 */
2695b9c547cSRui Paulo 	const char *server_id;
2705b9c547cSRui Paulo 
2715b9c547cSRui Paulo 	/**
2725b9c547cSRui Paulo 	 * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
2735b9c547cSRui Paulo 	 *
2745b9c547cSRui Paulo 	 * This controls whether the authentication server derives ERP key
2755b9c547cSRui Paulo 	 * hierarchy (rRK and rIK) from full EAP authentication and allows
2765b9c547cSRui Paulo 	 * these keys to be used to perform ERP to derive rMSK instead of full
2775b9c547cSRui Paulo 	 * EAP authentication to derive MSK.
2785b9c547cSRui Paulo 	 */
2795b9c547cSRui Paulo 	int erp;
2805b9c547cSRui Paulo 
2815b9c547cSRui Paulo 	const char *erp_domain;
2825b9c547cSRui Paulo 
2835b9c547cSRui Paulo 	struct dl_list erp_keys; /* struct eap_server_erp_key */
2845b9c547cSRui Paulo 
285325151a3SRui Paulo 	unsigned int tls_session_lifetime;
286325151a3SRui Paulo 
287*85732ac8SCy Schubert 	unsigned int tls_flags;
288*85732ac8SCy Schubert 
2895b9c547cSRui Paulo 	/**
290e28a4053SRui Paulo 	 * wps - Wi-Fi Protected Setup context
291e28a4053SRui Paulo 	 *
292e28a4053SRui Paulo 	 * If WPS is used with an external RADIUS server (which is quite
293e28a4053SRui Paulo 	 * unlikely configuration), this is used to provide a pointer to WPS
294e28a4053SRui Paulo 	 * context data. Normally, this can be set to %NULL.
295e28a4053SRui Paulo 	 */
29639beb93cSSam Leffler 	struct wps_context *wps;
297e28a4053SRui Paulo 
298e28a4053SRui Paulo 	/**
299e28a4053SRui Paulo 	 * ipv6 - Whether to enable IPv6 support in the RADIUS server
300e28a4053SRui Paulo 	 */
30139beb93cSSam Leffler 	int ipv6;
302e28a4053SRui Paulo 
303e28a4053SRui Paulo 	/**
304e28a4053SRui Paulo 	 * start_time - Timestamp of server start
305e28a4053SRui Paulo 	 */
3065b9c547cSRui Paulo 	struct os_reltime start_time;
307e28a4053SRui Paulo 
308e28a4053SRui Paulo 	/**
309e28a4053SRui Paulo 	 * counters - Statistics counters for server operations
310e28a4053SRui Paulo 	 *
311e28a4053SRui Paulo 	 * These counters are the sum over all clients.
312e28a4053SRui Paulo 	 */
31339beb93cSSam Leffler 	struct radius_server_counters counters;
314e28a4053SRui Paulo 
315e28a4053SRui Paulo 	/**
316e28a4053SRui Paulo 	 * get_eap_user - Callback for fetching EAP user information
317e28a4053SRui Paulo 	 * @ctx: Context data from conf_ctx
318e28a4053SRui Paulo 	 * @identity: User identity
319e28a4053SRui Paulo 	 * @identity_len: identity buffer length in octets
320e28a4053SRui Paulo 	 * @phase2: Whether this is for Phase 2 identity
321e28a4053SRui Paulo 	 * @user: Data structure for filling in the user information
322e28a4053SRui Paulo 	 * Returns: 0 on success, -1 on failure
323e28a4053SRui Paulo 	 *
324e28a4053SRui Paulo 	 * This is used to fetch information from user database. The callback
325e28a4053SRui Paulo 	 * will fill in information about allowed EAP methods and the user
326e28a4053SRui Paulo 	 * password. The password field will be an allocated copy of the
327e28a4053SRui Paulo 	 * password data and RADIUS server will free it after use.
328e28a4053SRui Paulo 	 */
32939beb93cSSam Leffler 	int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
33039beb93cSSam Leffler 			    int phase2, struct eap_user *user);
331e28a4053SRui Paulo 
332e28a4053SRui Paulo 	/**
333e28a4053SRui Paulo 	 * eap_req_id_text - Optional data for EAP-Request/Identity
334e28a4053SRui Paulo 	 *
335e28a4053SRui Paulo 	 * This can be used to configure an optional, displayable message that
336e28a4053SRui Paulo 	 * will be sent in EAP-Request/Identity. This string can contain an
337e28a4053SRui Paulo 	 * ASCII-0 character (nul) to separate network infromation per RFC
338e28a4053SRui Paulo 	 * 4284. The actual string length is explicit provided in
339e28a4053SRui Paulo 	 * eap_req_id_text_len since nul character will not be used as a string
340e28a4053SRui Paulo 	 * terminator.
341e28a4053SRui Paulo 	 */
34239beb93cSSam Leffler 	char *eap_req_id_text;
343e28a4053SRui Paulo 
344e28a4053SRui Paulo 	/**
345e28a4053SRui Paulo 	 * eap_req_id_text_len - Length of eap_req_id_text buffer in octets
346e28a4053SRui Paulo 	 */
34739beb93cSSam Leffler 	size_t eap_req_id_text_len;
348e28a4053SRui Paulo 
349e28a4053SRui Paulo 	/*
350e28a4053SRui Paulo 	 * msg_ctx - Context data for wpa_msg() calls
351e28a4053SRui Paulo 	 */
352e28a4053SRui Paulo 	void *msg_ctx;
353f05cddf9SRui Paulo 
354f05cddf9SRui Paulo #ifdef CONFIG_RADIUS_TEST
355f05cddf9SRui Paulo 	char *dump_msk_file;
356f05cddf9SRui Paulo #endif /* CONFIG_RADIUS_TEST */
3575b9c547cSRui Paulo 
3585b9c547cSRui Paulo 	char *subscr_remediation_url;
3595b9c547cSRui Paulo 	u8 subscr_remediation_method;
3605b9c547cSRui Paulo 
361*85732ac8SCy Schubert 	char *t_c_server_url;
362*85732ac8SCy Schubert 
3635b9c547cSRui Paulo #ifdef CONFIG_SQLITE
3645b9c547cSRui Paulo 	sqlite3 *db;
3655b9c547cSRui Paulo #endif /* CONFIG_SQLITE */
36639beb93cSSam Leffler };
36739beb93cSSam Leffler 
36839beb93cSSam Leffler 
36939beb93cSSam Leffler #define RADIUS_DEBUG(args...) \
37039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RADIUS SRV: " args)
37139beb93cSSam Leffler #define RADIUS_ERROR(args...) \
37239beb93cSSam Leffler wpa_printf(MSG_ERROR, "RADIUS SRV: " args)
37339beb93cSSam Leffler #define RADIUS_DUMP(args...) \
37439beb93cSSam Leffler wpa_hexdump(MSG_MSGDUMP, "RADIUS SRV: " args)
37539beb93cSSam Leffler #define RADIUS_DUMP_ASCII(args...) \
37639beb93cSSam Leffler wpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args)
37739beb93cSSam Leffler 
37839beb93cSSam Leffler 
37939beb93cSSam Leffler static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx);
3803157ba21SRui Paulo static void radius_server_session_remove_timeout(void *eloop_ctx,
3813157ba21SRui Paulo 						 void *timeout_ctx);
38239beb93cSSam Leffler 
3835b9c547cSRui Paulo void srv_log(struct radius_session *sess, const char *fmt, ...)
3845b9c547cSRui Paulo PRINTF_FORMAT(2, 3);
3855b9c547cSRui Paulo 
3865b9c547cSRui Paulo void srv_log(struct radius_session *sess, const char *fmt, ...)
3875b9c547cSRui Paulo {
3885b9c547cSRui Paulo 	va_list ap;
3895b9c547cSRui Paulo 	char *buf;
3905b9c547cSRui Paulo 	int buflen;
3915b9c547cSRui Paulo 
3925b9c547cSRui Paulo 	va_start(ap, fmt);
3935b9c547cSRui Paulo 	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
3945b9c547cSRui Paulo 	va_end(ap);
3955b9c547cSRui Paulo 
3965b9c547cSRui Paulo 	buf = os_malloc(buflen);
3975b9c547cSRui Paulo 	if (buf == NULL)
3985b9c547cSRui Paulo 		return;
3995b9c547cSRui Paulo 	va_start(ap, fmt);
4005b9c547cSRui Paulo 	vsnprintf(buf, buflen, fmt, ap);
4015b9c547cSRui Paulo 	va_end(ap);
4025b9c547cSRui Paulo 
4035b9c547cSRui Paulo 	RADIUS_DEBUG("[0x%x %s] %s", sess->sess_id, sess->nas_ip, buf);
4045b9c547cSRui Paulo 
4055b9c547cSRui Paulo #ifdef CONFIG_SQLITE
4065b9c547cSRui Paulo 	if (sess->server->db) {
4075b9c547cSRui Paulo 		char *sql;
4085b9c547cSRui Paulo 		sql = sqlite3_mprintf("INSERT INTO authlog"
4095b9c547cSRui Paulo 				      "(timestamp,session,nas_ip,username,note)"
4105b9c547cSRui Paulo 				      " VALUES ("
4115b9c547cSRui Paulo 				      "strftime('%%Y-%%m-%%d %%H:%%M:%%f',"
4125b9c547cSRui Paulo 				      "'now'),%u,%Q,%Q,%Q)",
4135b9c547cSRui Paulo 				      sess->sess_id, sess->nas_ip,
4145b9c547cSRui Paulo 				      sess->username, buf);
4155b9c547cSRui Paulo 		if (sql) {
4165b9c547cSRui Paulo 			if (sqlite3_exec(sess->server->db, sql, NULL, NULL,
4175b9c547cSRui Paulo 					 NULL) != SQLITE_OK) {
4185b9c547cSRui Paulo 				RADIUS_ERROR("Failed to add authlog entry into sqlite database: %s",
4195b9c547cSRui Paulo 					     sqlite3_errmsg(sess->server->db));
4205b9c547cSRui Paulo 			}
4215b9c547cSRui Paulo 			sqlite3_free(sql);
4225b9c547cSRui Paulo 		}
4235b9c547cSRui Paulo 	}
4245b9c547cSRui Paulo #endif /* CONFIG_SQLITE */
4255b9c547cSRui Paulo 
4265b9c547cSRui Paulo 	os_free(buf);
4275b9c547cSRui Paulo }
4285b9c547cSRui Paulo 
42939beb93cSSam Leffler 
43039beb93cSSam Leffler static struct radius_client *
43139beb93cSSam Leffler radius_server_get_client(struct radius_server_data *data, struct in_addr *addr,
43239beb93cSSam Leffler 			 int ipv6)
43339beb93cSSam Leffler {
43439beb93cSSam Leffler 	struct radius_client *client = data->clients;
43539beb93cSSam Leffler 
43639beb93cSSam Leffler 	while (client) {
43739beb93cSSam Leffler #ifdef CONFIG_IPV6
43839beb93cSSam Leffler 		if (ipv6) {
43939beb93cSSam Leffler 			struct in6_addr *addr6;
44039beb93cSSam Leffler 			int i;
44139beb93cSSam Leffler 
44239beb93cSSam Leffler 			addr6 = (struct in6_addr *) addr;
44339beb93cSSam Leffler 			for (i = 0; i < 16; i++) {
44439beb93cSSam Leffler 				if ((addr6->s6_addr[i] &
44539beb93cSSam Leffler 				     client->mask6.s6_addr[i]) !=
44639beb93cSSam Leffler 				    (client->addr6.s6_addr[i] &
44739beb93cSSam Leffler 				     client->mask6.s6_addr[i])) {
44839beb93cSSam Leffler 					i = 17;
44939beb93cSSam Leffler 					break;
45039beb93cSSam Leffler 				}
45139beb93cSSam Leffler 			}
45239beb93cSSam Leffler 			if (i == 16) {
45339beb93cSSam Leffler 				break;
45439beb93cSSam Leffler 			}
45539beb93cSSam Leffler 		}
45639beb93cSSam Leffler #endif /* CONFIG_IPV6 */
45739beb93cSSam Leffler 		if (!ipv6 && (client->addr.s_addr & client->mask.s_addr) ==
45839beb93cSSam Leffler 		    (addr->s_addr & client->mask.s_addr)) {
45939beb93cSSam Leffler 			break;
46039beb93cSSam Leffler 		}
46139beb93cSSam Leffler 
46239beb93cSSam Leffler 		client = client->next;
46339beb93cSSam Leffler 	}
46439beb93cSSam Leffler 
46539beb93cSSam Leffler 	return client;
46639beb93cSSam Leffler }
46739beb93cSSam Leffler 
46839beb93cSSam Leffler 
46939beb93cSSam Leffler static struct radius_session *
47039beb93cSSam Leffler radius_server_get_session(struct radius_client *client, unsigned int sess_id)
47139beb93cSSam Leffler {
47239beb93cSSam Leffler 	struct radius_session *sess = client->sessions;
47339beb93cSSam Leffler 
47439beb93cSSam Leffler 	while (sess) {
47539beb93cSSam Leffler 		if (sess->sess_id == sess_id) {
47639beb93cSSam Leffler 			break;
47739beb93cSSam Leffler 		}
47839beb93cSSam Leffler 		sess = sess->next;
47939beb93cSSam Leffler 	}
48039beb93cSSam Leffler 
48139beb93cSSam Leffler 	return sess;
48239beb93cSSam Leffler }
48339beb93cSSam Leffler 
48439beb93cSSam Leffler 
48539beb93cSSam Leffler static void radius_server_session_free(struct radius_server_data *data,
48639beb93cSSam Leffler 				       struct radius_session *sess)
48739beb93cSSam Leffler {
48839beb93cSSam Leffler 	eloop_cancel_timeout(radius_server_session_timeout, data, sess);
4893157ba21SRui Paulo 	eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess);
49039beb93cSSam Leffler 	eap_server_sm_deinit(sess->eap);
49139beb93cSSam Leffler 	radius_msg_free(sess->last_msg);
49239beb93cSSam Leffler 	os_free(sess->last_from_addr);
49339beb93cSSam Leffler 	radius_msg_free(sess->last_reply);
4945b9c547cSRui Paulo 	os_free(sess->username);
4955b9c547cSRui Paulo 	os_free(sess->nas_ip);
49639beb93cSSam Leffler 	os_free(sess);
49739beb93cSSam Leffler 	data->num_sess--;
49839beb93cSSam Leffler }
49939beb93cSSam Leffler 
50039beb93cSSam Leffler 
50139beb93cSSam Leffler static void radius_server_session_remove(struct radius_server_data *data,
50239beb93cSSam Leffler 					 struct radius_session *sess)
50339beb93cSSam Leffler {
50439beb93cSSam Leffler 	struct radius_client *client = sess->client;
50539beb93cSSam Leffler 	struct radius_session *session, *prev;
50639beb93cSSam Leffler 
50739beb93cSSam Leffler 	eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess);
50839beb93cSSam Leffler 
50939beb93cSSam Leffler 	prev = NULL;
51039beb93cSSam Leffler 	session = client->sessions;
51139beb93cSSam Leffler 	while (session) {
51239beb93cSSam Leffler 		if (session == sess) {
51339beb93cSSam Leffler 			if (prev == NULL) {
51439beb93cSSam Leffler 				client->sessions = sess->next;
51539beb93cSSam Leffler 			} else {
51639beb93cSSam Leffler 				prev->next = sess->next;
51739beb93cSSam Leffler 			}
51839beb93cSSam Leffler 			radius_server_session_free(data, sess);
51939beb93cSSam Leffler 			break;
52039beb93cSSam Leffler 		}
52139beb93cSSam Leffler 		prev = session;
52239beb93cSSam Leffler 		session = session->next;
52339beb93cSSam Leffler 	}
52439beb93cSSam Leffler }
52539beb93cSSam Leffler 
52639beb93cSSam Leffler 
52739beb93cSSam Leffler static void radius_server_session_remove_timeout(void *eloop_ctx,
52839beb93cSSam Leffler 						 void *timeout_ctx)
52939beb93cSSam Leffler {
53039beb93cSSam Leffler 	struct radius_server_data *data = eloop_ctx;
53139beb93cSSam Leffler 	struct radius_session *sess = timeout_ctx;
53239beb93cSSam Leffler 	RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id);
53339beb93cSSam Leffler 	radius_server_session_remove(data, sess);
53439beb93cSSam Leffler }
53539beb93cSSam Leffler 
53639beb93cSSam Leffler 
53739beb93cSSam Leffler static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx)
53839beb93cSSam Leffler {
53939beb93cSSam Leffler 	struct radius_server_data *data = eloop_ctx;
54039beb93cSSam Leffler 	struct radius_session *sess = timeout_ctx;
54139beb93cSSam Leffler 
54239beb93cSSam Leffler 	RADIUS_DEBUG("Timing out authentication session 0x%x", sess->sess_id);
54339beb93cSSam Leffler 	radius_server_session_remove(data, sess);
54439beb93cSSam Leffler }
54539beb93cSSam Leffler 
54639beb93cSSam Leffler 
54739beb93cSSam Leffler static struct radius_session *
54839beb93cSSam Leffler radius_server_new_session(struct radius_server_data *data,
54939beb93cSSam Leffler 			  struct radius_client *client)
55039beb93cSSam Leffler {
55139beb93cSSam Leffler 	struct radius_session *sess;
55239beb93cSSam Leffler 
55339beb93cSSam Leffler 	if (data->num_sess >= RADIUS_MAX_SESSION) {
55439beb93cSSam Leffler 		RADIUS_DEBUG("Maximum number of existing session - no room "
55539beb93cSSam Leffler 			     "for a new session");
55639beb93cSSam Leffler 		return NULL;
55739beb93cSSam Leffler 	}
55839beb93cSSam Leffler 
55939beb93cSSam Leffler 	sess = os_zalloc(sizeof(*sess));
56039beb93cSSam Leffler 	if (sess == NULL)
56139beb93cSSam Leffler 		return NULL;
56239beb93cSSam Leffler 
56339beb93cSSam Leffler 	sess->server = data;
56439beb93cSSam Leffler 	sess->client = client;
56539beb93cSSam Leffler 	sess->sess_id = data->next_sess_id++;
56639beb93cSSam Leffler 	sess->next = client->sessions;
56739beb93cSSam Leffler 	client->sessions = sess;
56839beb93cSSam Leffler 	eloop_register_timeout(RADIUS_SESSION_TIMEOUT, 0,
56939beb93cSSam Leffler 			       radius_server_session_timeout, data, sess);
57039beb93cSSam Leffler 	data->num_sess++;
57139beb93cSSam Leffler 	return sess;
57239beb93cSSam Leffler }
57339beb93cSSam Leffler 
57439beb93cSSam Leffler 
5755b9c547cSRui Paulo #ifdef CONFIG_TESTING_OPTIONS
5765b9c547cSRui Paulo static void radius_server_testing_options_tls(struct radius_session *sess,
5775b9c547cSRui Paulo 					      const char *tls,
5785b9c547cSRui Paulo 					      struct eap_config *eap_conf)
5795b9c547cSRui Paulo {
5805b9c547cSRui Paulo 	int test = atoi(tls);
5815b9c547cSRui Paulo 
5825b9c547cSRui Paulo 	switch (test) {
5835b9c547cSRui Paulo 	case 1:
5845b9c547cSRui Paulo 		srv_log(sess, "TLS test - break VerifyData");
5855b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_BREAK_VERIFY_DATA;
5865b9c547cSRui Paulo 		break;
5875b9c547cSRui Paulo 	case 2:
5885b9c547cSRui Paulo 		srv_log(sess, "TLS test - break ServerKeyExchange ServerParams hash");
5895b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_HASH;
5905b9c547cSRui Paulo 		break;
5915b9c547cSRui Paulo 	case 3:
5925b9c547cSRui Paulo 		srv_log(sess, "TLS test - break ServerKeyExchange ServerParams Signature");
5935b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_SIGNATURE;
5945b9c547cSRui Paulo 		break;
5955b9c547cSRui Paulo 	case 4:
5965b9c547cSRui Paulo 		srv_log(sess, "TLS test - RSA-DHE using a short 511-bit prime");
5975b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_DHE_PRIME_511B;
5985b9c547cSRui Paulo 		break;
5995b9c547cSRui Paulo 	case 5:
6005b9c547cSRui Paulo 		srv_log(sess, "TLS test - RSA-DHE using a short 767-bit prime");
6015b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_DHE_PRIME_767B;
6025b9c547cSRui Paulo 		break;
6035b9c547cSRui Paulo 	case 6:
6045b9c547cSRui Paulo 		srv_log(sess, "TLS test - RSA-DHE using a bogus 15 \"prime\"");
6055b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_DHE_PRIME_15;
6065b9c547cSRui Paulo 		break;
6075b9c547cSRui Paulo 	case 7:
6085b9c547cSRui Paulo 		srv_log(sess, "TLS test - RSA-DHE using a short 58-bit prime in long container");
6095b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_DHE_PRIME_58B;
6105b9c547cSRui Paulo 		break;
6115b9c547cSRui Paulo 	case 8:
6125b9c547cSRui Paulo 		srv_log(sess, "TLS test - RSA-DHE using a non-prime");
6135b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_DHE_NON_PRIME;
6145b9c547cSRui Paulo 		break;
6155b9c547cSRui Paulo 	default:
6165b9c547cSRui Paulo 		srv_log(sess, "Unrecognized TLS test");
6175b9c547cSRui Paulo 		break;
6185b9c547cSRui Paulo 	}
6195b9c547cSRui Paulo }
6205b9c547cSRui Paulo #endif /* CONFIG_TESTING_OPTIONS */
6215b9c547cSRui Paulo 
6225b9c547cSRui Paulo static void radius_server_testing_options(struct radius_session *sess,
6235b9c547cSRui Paulo 					  struct eap_config *eap_conf)
6245b9c547cSRui Paulo {
6255b9c547cSRui Paulo #ifdef CONFIG_TESTING_OPTIONS
6265b9c547cSRui Paulo 	const char *pos;
6275b9c547cSRui Paulo 
6285b9c547cSRui Paulo 	pos = os_strstr(sess->username, "@test-");
6295b9c547cSRui Paulo 	if (pos == NULL)
6305b9c547cSRui Paulo 		return;
6315b9c547cSRui Paulo 	pos += 6;
6325b9c547cSRui Paulo 	if (os_strncmp(pos, "tls-", 4) == 0)
6335b9c547cSRui Paulo 		radius_server_testing_options_tls(sess, pos + 4, eap_conf);
6345b9c547cSRui Paulo 	else
6355b9c547cSRui Paulo 		srv_log(sess, "Unrecognized test: %s", pos);
6365b9c547cSRui Paulo #endif /* CONFIG_TESTING_OPTIONS */
6375b9c547cSRui Paulo }
6385b9c547cSRui Paulo 
6395b9c547cSRui Paulo 
64039beb93cSSam Leffler static struct radius_session *
64139beb93cSSam Leffler radius_server_get_new_session(struct radius_server_data *data,
64239beb93cSSam Leffler 			      struct radius_client *client,
6435b9c547cSRui Paulo 			      struct radius_msg *msg, const char *from_addr)
64439beb93cSSam Leffler {
645*85732ac8SCy Schubert 	u8 *user, *id;
646*85732ac8SCy Schubert 	size_t user_len, id_len;
64739beb93cSSam Leffler 	int res;
64839beb93cSSam Leffler 	struct radius_session *sess;
64939beb93cSSam Leffler 	struct eap_config eap_conf;
6505b9c547cSRui Paulo 	struct eap_user tmp;
65139beb93cSSam Leffler 
65239beb93cSSam Leffler 	RADIUS_DEBUG("Creating a new session");
65339beb93cSSam Leffler 
6545b9c547cSRui Paulo 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &user,
6555b9c547cSRui Paulo 				    &user_len, NULL) < 0) {
65639beb93cSSam Leffler 		RADIUS_DEBUG("Could not get User-Name");
65739beb93cSSam Leffler 		return NULL;
65839beb93cSSam Leffler 	}
65939beb93cSSam Leffler 	RADIUS_DUMP_ASCII("User-Name", user, user_len);
66039beb93cSSam Leffler 
6615b9c547cSRui Paulo 	os_memset(&tmp, 0, sizeof(tmp));
6625b9c547cSRui Paulo 	res = data->get_eap_user(data->conf_ctx, user, user_len, 0, &tmp);
6635b9c547cSRui Paulo 	bin_clear_free(tmp.password, tmp.password_len);
66439beb93cSSam Leffler 
6655b9c547cSRui Paulo 	if (res != 0) {
6665b9c547cSRui Paulo 		RADIUS_DEBUG("User-Name not found from user database");
6675b9c547cSRui Paulo 		return NULL;
6685b9c547cSRui Paulo 	}
6695b9c547cSRui Paulo 
67039beb93cSSam Leffler 	RADIUS_DEBUG("Matching user entry found");
67139beb93cSSam Leffler 	sess = radius_server_new_session(data, client);
67239beb93cSSam Leffler 	if (sess == NULL) {
67339beb93cSSam Leffler 		RADIUS_DEBUG("Failed to create a new session");
67439beb93cSSam Leffler 		return NULL;
67539beb93cSSam Leffler 	}
6765b9c547cSRui Paulo 	sess->accept_attr = tmp.accept_attr;
6775b9c547cSRui Paulo 	sess->macacl = tmp.macacl;
6785b9c547cSRui Paulo 
6795b9c547cSRui Paulo 	sess->username = os_malloc(user_len * 4 + 1);
6805b9c547cSRui Paulo 	if (sess->username == NULL) {
681*85732ac8SCy Schubert 		radius_server_session_remove(data, sess);
68239beb93cSSam Leffler 		return NULL;
68339beb93cSSam Leffler 	}
6845b9c547cSRui Paulo 	printf_encode(sess->username, user_len * 4 + 1, user, user_len);
6855b9c547cSRui Paulo 
6865b9c547cSRui Paulo 	sess->nas_ip = os_strdup(from_addr);
6875b9c547cSRui Paulo 	if (sess->nas_ip == NULL) {
688*85732ac8SCy Schubert 		radius_server_session_remove(data, sess);
6895b9c547cSRui Paulo 		return NULL;
6905b9c547cSRui Paulo 	}
6915b9c547cSRui Paulo 
692*85732ac8SCy Schubert 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, &id,
693*85732ac8SCy Schubert 				    &id_len, NULL) == 0) {
694*85732ac8SCy Schubert 		char buf[3 * ETH_ALEN];
695*85732ac8SCy Schubert 
696*85732ac8SCy Schubert 		os_memset(buf, 0, sizeof(buf));
697*85732ac8SCy Schubert 		if (id_len >= sizeof(buf))
698*85732ac8SCy Schubert 			id_len = sizeof(buf) - 1;
699*85732ac8SCy Schubert 		os_memcpy(buf, id, id_len);
700*85732ac8SCy Schubert 		if (hwaddr_aton2(buf, sess->mac_addr) < 0)
701*85732ac8SCy Schubert 			os_memset(sess->mac_addr, 0, ETH_ALEN);
702*85732ac8SCy Schubert 		else
703*85732ac8SCy Schubert 			RADIUS_DEBUG("Calling-Station-Id: " MACSTR,
704*85732ac8SCy Schubert 				     MAC2STR(sess->mac_addr));
705*85732ac8SCy Schubert 	}
706*85732ac8SCy Schubert 
7075b9c547cSRui Paulo 	srv_log(sess, "New session created");
70839beb93cSSam Leffler 
70939beb93cSSam Leffler 	os_memset(&eap_conf, 0, sizeof(eap_conf));
71039beb93cSSam Leffler 	eap_conf.ssl_ctx = data->ssl_ctx;
711e28a4053SRui Paulo 	eap_conf.msg_ctx = data->msg_ctx;
71239beb93cSSam Leffler 	eap_conf.eap_sim_db_priv = data->eap_sim_db_priv;
71339beb93cSSam Leffler 	eap_conf.backend_auth = TRUE;
71439beb93cSSam Leffler 	eap_conf.eap_server = 1;
71539beb93cSSam Leffler 	eap_conf.pac_opaque_encr_key = data->pac_opaque_encr_key;
71639beb93cSSam Leffler 	eap_conf.eap_fast_a_id = data->eap_fast_a_id;
71739beb93cSSam Leffler 	eap_conf.eap_fast_a_id_len = data->eap_fast_a_id_len;
71839beb93cSSam Leffler 	eap_conf.eap_fast_a_id_info = data->eap_fast_a_id_info;
71939beb93cSSam Leffler 	eap_conf.eap_fast_prov = data->eap_fast_prov;
72039beb93cSSam Leffler 	eap_conf.pac_key_lifetime = data->pac_key_lifetime;
72139beb93cSSam Leffler 	eap_conf.pac_key_refresh_time = data->pac_key_refresh_time;
72239beb93cSSam Leffler 	eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind;
72339beb93cSSam Leffler 	eap_conf.tnc = data->tnc;
72439beb93cSSam Leffler 	eap_conf.wps = data->wps;
725f05cddf9SRui Paulo 	eap_conf.pwd_group = data->pwd_group;
7265b9c547cSRui Paulo 	eap_conf.server_id = (const u8 *) data->server_id;
7275b9c547cSRui Paulo 	eap_conf.server_id_len = os_strlen(data->server_id);
7285b9c547cSRui Paulo 	eap_conf.erp = data->erp;
729325151a3SRui Paulo 	eap_conf.tls_session_lifetime = data->tls_session_lifetime;
730*85732ac8SCy Schubert 	eap_conf.tls_flags = data->tls_flags;
7315b9c547cSRui Paulo 	radius_server_testing_options(sess, &eap_conf);
73239beb93cSSam Leffler 	sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
73339beb93cSSam Leffler 				       &eap_conf);
73439beb93cSSam Leffler 	if (sess->eap == NULL) {
73539beb93cSSam Leffler 		RADIUS_DEBUG("Failed to initialize EAP state machine for the "
73639beb93cSSam Leffler 			     "new session");
737*85732ac8SCy Schubert 		radius_server_session_remove(data, sess);
73839beb93cSSam Leffler 		return NULL;
73939beb93cSSam Leffler 	}
74039beb93cSSam Leffler 	sess->eap_if = eap_get_interface(sess->eap);
74139beb93cSSam Leffler 	sess->eap_if->eapRestart = TRUE;
74239beb93cSSam Leffler 	sess->eap_if->portEnabled = TRUE;
74339beb93cSSam Leffler 
74439beb93cSSam Leffler 	RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id);
74539beb93cSSam Leffler 
74639beb93cSSam Leffler 	return sess;
74739beb93cSSam Leffler }
74839beb93cSSam Leffler 
74939beb93cSSam Leffler 
750*85732ac8SCy Schubert #ifdef CONFIG_HS20
751*85732ac8SCy Schubert static void radius_srv_hs20_t_c_pending(struct radius_session *sess)
752*85732ac8SCy Schubert {
753*85732ac8SCy Schubert #ifdef CONFIG_SQLITE
754*85732ac8SCy Schubert 	char *sql;
755*85732ac8SCy Schubert 	char addr[3 * ETH_ALEN], *id_str;
756*85732ac8SCy Schubert 	const u8 *id;
757*85732ac8SCy Schubert 	size_t id_len;
758*85732ac8SCy Schubert 
759*85732ac8SCy Schubert 	if (!sess->server->db || !sess->eap ||
760*85732ac8SCy Schubert 	    is_zero_ether_addr(sess->mac_addr))
761*85732ac8SCy Schubert 		return;
762*85732ac8SCy Schubert 
763*85732ac8SCy Schubert 	os_snprintf(addr, sizeof(addr), MACSTR, MAC2STR(sess->mac_addr));
764*85732ac8SCy Schubert 
765*85732ac8SCy Schubert 	id = eap_get_identity(sess->eap, &id_len);
766*85732ac8SCy Schubert 	if (!id)
767*85732ac8SCy Schubert 		return;
768*85732ac8SCy Schubert 	id_str = os_malloc(id_len + 1);
769*85732ac8SCy Schubert 	if (!id_str)
770*85732ac8SCy Schubert 		return;
771*85732ac8SCy Schubert 	os_memcpy(id_str, id, id_len);
772*85732ac8SCy Schubert 	id_str[id_len] = '\0';
773*85732ac8SCy Schubert 
774*85732ac8SCy Schubert 	sql = sqlite3_mprintf("INSERT OR REPLACE INTO pending_tc (mac_addr,identity) VALUES (%Q,%Q)",
775*85732ac8SCy Schubert 			      addr, id_str);
776*85732ac8SCy Schubert 	os_free(id_str);
777*85732ac8SCy Schubert 	if (!sql)
778*85732ac8SCy Schubert 		return;
779*85732ac8SCy Schubert 
780*85732ac8SCy Schubert 	if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) !=
781*85732ac8SCy Schubert 	    SQLITE_OK) {
782*85732ac8SCy Schubert 		RADIUS_ERROR("Failed to add pending_tc entry into sqlite database: %s",
783*85732ac8SCy Schubert 			     sqlite3_errmsg(sess->server->db));
784*85732ac8SCy Schubert 	}
785*85732ac8SCy Schubert 	sqlite3_free(sql);
786*85732ac8SCy Schubert #endif /* CONFIG_SQLITE */
787*85732ac8SCy Schubert }
788*85732ac8SCy Schubert #endif /* CONFIG_HS20 */
789*85732ac8SCy Schubert 
790*85732ac8SCy Schubert 
791*85732ac8SCy Schubert static void radius_server_add_session(struct radius_session *sess)
792*85732ac8SCy Schubert {
793*85732ac8SCy Schubert #ifdef CONFIG_SQLITE
794*85732ac8SCy Schubert 	char *sql;
795*85732ac8SCy Schubert 	char addr_txt[ETH_ALEN * 3];
796*85732ac8SCy Schubert 	struct os_time now;
797*85732ac8SCy Schubert 
798*85732ac8SCy Schubert 	if (!sess->server->db)
799*85732ac8SCy Schubert 		return;
800*85732ac8SCy Schubert 
801*85732ac8SCy Schubert 
802*85732ac8SCy Schubert 	os_snprintf(addr_txt, sizeof(addr_txt), MACSTR,
803*85732ac8SCy Schubert 		    MAC2STR(sess->mac_addr));
804*85732ac8SCy Schubert 
805*85732ac8SCy Schubert 	os_get_time(&now);
806*85732ac8SCy Schubert 	sql = sqlite3_mprintf("INSERT OR REPLACE INTO current_sessions(mac_addr,identity,start_time,nas,hs20_t_c_filtering) VALUES (%Q,%Q,%d,%Q,%u)",
807*85732ac8SCy Schubert 			      addr_txt, sess->username, now.sec,
808*85732ac8SCy Schubert 			      sess->nas_ip, sess->t_c_filtering);
809*85732ac8SCy Schubert 	if (sql) {
810*85732ac8SCy Schubert 			if (sqlite3_exec(sess->server->db, sql, NULL, NULL,
811*85732ac8SCy Schubert 					 NULL) != SQLITE_OK) {
812*85732ac8SCy Schubert 				RADIUS_ERROR("Failed to add current_sessions entry into sqlite database: %s",
813*85732ac8SCy Schubert 					     sqlite3_errmsg(sess->server->db));
814*85732ac8SCy Schubert 			}
815*85732ac8SCy Schubert 			sqlite3_free(sql);
816*85732ac8SCy Schubert 	}
817*85732ac8SCy Schubert #endif /* CONFIG_SQLITE */
818*85732ac8SCy Schubert }
819*85732ac8SCy Schubert 
820*85732ac8SCy Schubert 
821*85732ac8SCy Schubert static void db_update_last_msk(struct radius_session *sess, const char *msk)
822*85732ac8SCy Schubert {
823*85732ac8SCy Schubert #ifdef CONFIG_RADIUS_TEST
824*85732ac8SCy Schubert #ifdef CONFIG_SQLITE
825*85732ac8SCy Schubert 	char *sql = NULL;
826*85732ac8SCy Schubert 	char *id_str = NULL;
827*85732ac8SCy Schubert 	const u8 *id;
828*85732ac8SCy Schubert 	size_t id_len;
829*85732ac8SCy Schubert 	const char *serial_num;
830*85732ac8SCy Schubert 
831*85732ac8SCy Schubert 	if (!sess->server->db)
832*85732ac8SCy Schubert 		return;
833*85732ac8SCy Schubert 
834*85732ac8SCy Schubert 	serial_num = eap_get_serial_num(sess->eap);
835*85732ac8SCy Schubert 	if (serial_num) {
836*85732ac8SCy Schubert 		id_len = 5 + os_strlen(serial_num) + 1;
837*85732ac8SCy Schubert 		id_str = os_malloc(id_len);
838*85732ac8SCy Schubert 		if (!id_str)
839*85732ac8SCy Schubert 			return;
840*85732ac8SCy Schubert 		os_snprintf(id_str, id_len, "cert-%s", serial_num);
841*85732ac8SCy Schubert 	} else {
842*85732ac8SCy Schubert 		id = eap_get_identity(sess->eap, &id_len);
843*85732ac8SCy Schubert 		if (!id)
844*85732ac8SCy Schubert 			return;
845*85732ac8SCy Schubert 		id_str = os_malloc(id_len + 1);
846*85732ac8SCy Schubert 		if (!id_str)
847*85732ac8SCy Schubert 			return;
848*85732ac8SCy Schubert 		os_memcpy(id_str, id, id_len);
849*85732ac8SCy Schubert 		id_str[id_len] = '\0';
850*85732ac8SCy Schubert 	}
851*85732ac8SCy Schubert 
852*85732ac8SCy Schubert 	sql = sqlite3_mprintf("UPDATE users SET last_msk=%Q WHERE identity=%Q",
853*85732ac8SCy Schubert 			      msk, id_str);
854*85732ac8SCy Schubert 	os_free(id_str);
855*85732ac8SCy Schubert 	if (!sql)
856*85732ac8SCy Schubert 		return;
857*85732ac8SCy Schubert 
858*85732ac8SCy Schubert 	if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) !=
859*85732ac8SCy Schubert 	    SQLITE_OK) {
860*85732ac8SCy Schubert 		RADIUS_DEBUG("Failed to update last_msk: %s",
861*85732ac8SCy Schubert 			     sqlite3_errmsg(sess->server->db));
862*85732ac8SCy Schubert 	}
863*85732ac8SCy Schubert 	sqlite3_free(sql);
864*85732ac8SCy Schubert #endif /* CONFIG_SQLITE */
865*85732ac8SCy Schubert #endif /* CONFIG_RADIUS_TEST */
866*85732ac8SCy Schubert }
867*85732ac8SCy Schubert 
868*85732ac8SCy Schubert 
86939beb93cSSam Leffler static struct radius_msg *
87039beb93cSSam Leffler radius_server_encapsulate_eap(struct radius_server_data *data,
87139beb93cSSam Leffler 			      struct radius_client *client,
87239beb93cSSam Leffler 			      struct radius_session *sess,
87339beb93cSSam Leffler 			      struct radius_msg *request)
87439beb93cSSam Leffler {
87539beb93cSSam Leffler 	struct radius_msg *msg;
87639beb93cSSam Leffler 	int code;
87739beb93cSSam Leffler 	unsigned int sess_id;
878e28a4053SRui Paulo 	struct radius_hdr *hdr = radius_msg_get_hdr(request);
879*85732ac8SCy Schubert 	u16 reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED;
88039beb93cSSam Leffler 
88139beb93cSSam Leffler 	if (sess->eap_if->eapFail) {
88239beb93cSSam Leffler 		sess->eap_if->eapFail = FALSE;
88339beb93cSSam Leffler 		code = RADIUS_CODE_ACCESS_REJECT;
88439beb93cSSam Leffler 	} else if (sess->eap_if->eapSuccess) {
88539beb93cSSam Leffler 		sess->eap_if->eapSuccess = FALSE;
88639beb93cSSam Leffler 		code = RADIUS_CODE_ACCESS_ACCEPT;
88739beb93cSSam Leffler 	} else {
88839beb93cSSam Leffler 		sess->eap_if->eapReq = FALSE;
88939beb93cSSam Leffler 		code = RADIUS_CODE_ACCESS_CHALLENGE;
89039beb93cSSam Leffler 	}
89139beb93cSSam Leffler 
892e28a4053SRui Paulo 	msg = radius_msg_new(code, hdr->identifier);
89339beb93cSSam Leffler 	if (msg == NULL) {
89439beb93cSSam Leffler 		RADIUS_DEBUG("Failed to allocate reply message");
89539beb93cSSam Leffler 		return NULL;
89639beb93cSSam Leffler 	}
89739beb93cSSam Leffler 
89839beb93cSSam Leffler 	sess_id = htonl(sess->sess_id);
89939beb93cSSam Leffler 	if (code == RADIUS_CODE_ACCESS_CHALLENGE &&
90039beb93cSSam Leffler 	    !radius_msg_add_attr(msg, RADIUS_ATTR_STATE,
90139beb93cSSam Leffler 				 (u8 *) &sess_id, sizeof(sess_id))) {
90239beb93cSSam Leffler 		RADIUS_DEBUG("Failed to add State attribute");
90339beb93cSSam Leffler 	}
90439beb93cSSam Leffler 
90539beb93cSSam Leffler 	if (sess->eap_if->eapReqData &&
90639beb93cSSam Leffler 	    !radius_msg_add_eap(msg, wpabuf_head(sess->eap_if->eapReqData),
90739beb93cSSam Leffler 				wpabuf_len(sess->eap_if->eapReqData))) {
90839beb93cSSam Leffler 		RADIUS_DEBUG("Failed to add EAP-Message attribute");
90939beb93cSSam Leffler 	}
91039beb93cSSam Leffler 
91139beb93cSSam Leffler 	if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) {
91239beb93cSSam Leffler 		int len;
913f05cddf9SRui Paulo #ifdef CONFIG_RADIUS_TEST
914*85732ac8SCy Schubert 		char buf[2 * 64 + 1];
915*85732ac8SCy Schubert 
916*85732ac8SCy Schubert 		len = sess->eap_if->eapKeyDataLen;
917*85732ac8SCy Schubert 		if (len > 64)
918*85732ac8SCy Schubert 			len = 64;
919*85732ac8SCy Schubert 		len = wpa_snprintf_hex(buf, sizeof(buf),
920*85732ac8SCy Schubert 				       sess->eap_if->eapKeyData, len);
921*85732ac8SCy Schubert 		buf[len] = '\0';
922*85732ac8SCy Schubert 
923f05cddf9SRui Paulo 		if (data->dump_msk_file) {
924f05cddf9SRui Paulo 			FILE *f;
925*85732ac8SCy Schubert 
926f05cddf9SRui Paulo 			f = fopen(data->dump_msk_file, "a");
927f05cddf9SRui Paulo 			if (f) {
928f05cddf9SRui Paulo 				len = sess->eap_if->eapKeyDataLen;
929f05cddf9SRui Paulo 				if (len > 64)
930f05cddf9SRui Paulo 					len = 64;
931f05cddf9SRui Paulo 				len = wpa_snprintf_hex(
932f05cddf9SRui Paulo 					buf, sizeof(buf),
933f05cddf9SRui Paulo 					sess->eap_if->eapKeyData, len);
934f05cddf9SRui Paulo 				buf[len] = '\0';
935f05cddf9SRui Paulo 				fprintf(f, "%s\n", buf);
936f05cddf9SRui Paulo 				fclose(f);
937f05cddf9SRui Paulo 			}
938f05cddf9SRui Paulo 		}
939*85732ac8SCy Schubert 
940*85732ac8SCy Schubert 		db_update_last_msk(sess, buf);
941f05cddf9SRui Paulo #endif /* CONFIG_RADIUS_TEST */
94239beb93cSSam Leffler 		if (sess->eap_if->eapKeyDataLen > 64) {
94339beb93cSSam Leffler 			len = 32;
94439beb93cSSam Leffler 		} else {
94539beb93cSSam Leffler 			len = sess->eap_if->eapKeyDataLen / 2;
94639beb93cSSam Leffler 		}
947e28a4053SRui Paulo 		if (!radius_msg_add_mppe_keys(msg, hdr->authenticator,
94839beb93cSSam Leffler 					      (u8 *) client->shared_secret,
94939beb93cSSam Leffler 					      client->shared_secret_len,
95039beb93cSSam Leffler 					      sess->eap_if->eapKeyData + len,
95139beb93cSSam Leffler 					      len, sess->eap_if->eapKeyData,
95239beb93cSSam Leffler 					      len)) {
95339beb93cSSam Leffler 			RADIUS_DEBUG("Failed to add MPPE key attributes");
95439beb93cSSam Leffler 		}
95539beb93cSSam Leffler 	}
95639beb93cSSam Leffler 
9575b9c547cSRui Paulo #ifdef CONFIG_HS20
9585b9c547cSRui Paulo 	if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation &&
9595b9c547cSRui Paulo 	    data->subscr_remediation_url) {
9605b9c547cSRui Paulo 		u8 *buf;
9615b9c547cSRui Paulo 		size_t url_len = os_strlen(data->subscr_remediation_url);
9625b9c547cSRui Paulo 		buf = os_malloc(1 + url_len);
9635b9c547cSRui Paulo 		if (buf == NULL) {
9645b9c547cSRui Paulo 			radius_msg_free(msg);
9655b9c547cSRui Paulo 			return NULL;
9665b9c547cSRui Paulo 		}
9675b9c547cSRui Paulo 		buf[0] = data->subscr_remediation_method;
9685b9c547cSRui Paulo 		os_memcpy(&buf[1], data->subscr_remediation_url, url_len);
9695b9c547cSRui Paulo 		if (!radius_msg_add_wfa(
9705b9c547cSRui Paulo 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
9715b9c547cSRui Paulo 			    buf, 1 + url_len)) {
9725b9c547cSRui Paulo 			RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
9735b9c547cSRui Paulo 		}
9745b9c547cSRui Paulo 		os_free(buf);
9755b9c547cSRui Paulo 	} else if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation) {
9765b9c547cSRui Paulo 		u8 buf[1];
9775b9c547cSRui Paulo 		if (!radius_msg_add_wfa(
9785b9c547cSRui Paulo 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
9795b9c547cSRui Paulo 			    buf, 0)) {
9805b9c547cSRui Paulo 			RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
9815b9c547cSRui Paulo 		}
9825b9c547cSRui Paulo 	}
983*85732ac8SCy Schubert 
984*85732ac8SCy Schubert 	if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) {
985*85732ac8SCy Schubert 		u8 buf[4] = { 0x01, 0x00, 0x00, 0x00 }; /* E=1 */
986*85732ac8SCy Schubert 		const char *url = data->t_c_server_url, *pos;
987*85732ac8SCy Schubert 		char *url2, *end2, *pos2;
988*85732ac8SCy Schubert 		size_t url_len;
989*85732ac8SCy Schubert 
990*85732ac8SCy Schubert 		if (!radius_msg_add_wfa(
991*85732ac8SCy Schubert 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING,
992*85732ac8SCy Schubert 			    buf, sizeof(buf))) {
993*85732ac8SCy Schubert 			RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering");
994*85732ac8SCy Schubert 			radius_msg_free(msg);
995*85732ac8SCy Schubert 			return NULL;
996*85732ac8SCy Schubert 		}
997*85732ac8SCy Schubert 
998*85732ac8SCy Schubert 		if (!url) {
999*85732ac8SCy Schubert 			RADIUS_DEBUG("No t_c_server_url configured");
1000*85732ac8SCy Schubert 			radius_msg_free(msg);
1001*85732ac8SCy Schubert 			return NULL;
1002*85732ac8SCy Schubert 		}
1003*85732ac8SCy Schubert 
1004*85732ac8SCy Schubert 		pos = os_strstr(url, "@1@");
1005*85732ac8SCy Schubert 		if (!pos) {
1006*85732ac8SCy Schubert 			RADIUS_DEBUG("No @1@ macro in t_c_server_url");
1007*85732ac8SCy Schubert 			radius_msg_free(msg);
1008*85732ac8SCy Schubert 			return NULL;
1009*85732ac8SCy Schubert 		}
1010*85732ac8SCy Schubert 
1011*85732ac8SCy Schubert 		url_len = os_strlen(url) + ETH_ALEN * 3 - 1 - 3;
1012*85732ac8SCy Schubert 		url2 = os_malloc(url_len + 1);
1013*85732ac8SCy Schubert 		if (!url2) {
1014*85732ac8SCy Schubert 			RADIUS_DEBUG("Failed to allocate room for T&C Server URL");
1015*85732ac8SCy Schubert 			os_free(url2);
1016*85732ac8SCy Schubert 			radius_msg_free(msg);
1017*85732ac8SCy Schubert 			return NULL;
1018*85732ac8SCy Schubert 		}
1019*85732ac8SCy Schubert 		pos2 = url2;
1020*85732ac8SCy Schubert 		end2 = url2 + url_len + 1;
1021*85732ac8SCy Schubert 		os_memcpy(pos2, url, pos - url);
1022*85732ac8SCy Schubert 		pos2 += pos - url;
1023*85732ac8SCy Schubert 		os_snprintf(pos2, end2 - pos2, MACSTR, MAC2STR(sess->mac_addr));
1024*85732ac8SCy Schubert 		pos2 += ETH_ALEN * 3 - 1;
1025*85732ac8SCy Schubert 		os_memcpy(pos2, pos + 3, os_strlen(pos + 3));
1026*85732ac8SCy Schubert 		if (!radius_msg_add_wfa(msg,
1027*85732ac8SCy Schubert 					RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL,
1028*85732ac8SCy Schubert 					(const u8 *) url2, url_len)) {
1029*85732ac8SCy Schubert 			RADIUS_DEBUG("Failed to add WFA-HS20-T-C-URL");
1030*85732ac8SCy Schubert 			os_free(url2);
1031*85732ac8SCy Schubert 			radius_msg_free(msg);
1032*85732ac8SCy Schubert 			return NULL;
1033*85732ac8SCy Schubert 		}
1034*85732ac8SCy Schubert 		os_free(url2);
1035*85732ac8SCy Schubert 
1036*85732ac8SCy Schubert 		radius_srv_hs20_t_c_pending(sess);
1037*85732ac8SCy Schubert 	}
10385b9c547cSRui Paulo #endif /* CONFIG_HS20 */
10395b9c547cSRui Paulo 
104039beb93cSSam Leffler 	if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
104139beb93cSSam Leffler 		RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
104239beb93cSSam Leffler 		radius_msg_free(msg);
104339beb93cSSam Leffler 		return NULL;
104439beb93cSSam Leffler 	}
104539beb93cSSam Leffler 
10465b9c547cSRui Paulo 	if (code == RADIUS_CODE_ACCESS_ACCEPT) {
10475b9c547cSRui Paulo 		struct hostapd_radius_attr *attr;
10485b9c547cSRui Paulo 		for (attr = sess->accept_attr; attr; attr = attr->next) {
10495b9c547cSRui Paulo 			if (!radius_msg_add_attr(msg, attr->type,
10505b9c547cSRui Paulo 						 wpabuf_head(attr->val),
10515b9c547cSRui Paulo 						 wpabuf_len(attr->val))) {
10525b9c547cSRui Paulo 				wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
10535b9c547cSRui Paulo 				radius_msg_free(msg);
10545b9c547cSRui Paulo 				return NULL;
10555b9c547cSRui Paulo 			}
10565b9c547cSRui Paulo 		}
10575b9c547cSRui Paulo 	}
10585b9c547cSRui Paulo 
1059*85732ac8SCy Schubert 	if (code == RADIUS_CODE_ACCESS_REJECT) {
1060*85732ac8SCy Schubert 		if (radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE,
1061*85732ac8SCy Schubert 					      reason) < 0) {
1062*85732ac8SCy Schubert 			RADIUS_DEBUG("Failed to add WLAN-Reason-Code attribute");
1063*85732ac8SCy Schubert 			radius_msg_free(msg);
1064*85732ac8SCy Schubert 			return NULL;
1065*85732ac8SCy Schubert 		}
1066*85732ac8SCy Schubert 	}
1067*85732ac8SCy Schubert 
10685b9c547cSRui Paulo 	if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
10695b9c547cSRui Paulo 				  client->shared_secret_len,
10705b9c547cSRui Paulo 				  hdr->authenticator) < 0) {
10715b9c547cSRui Paulo 		RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
10725b9c547cSRui Paulo 	}
10735b9c547cSRui Paulo 
1074*85732ac8SCy Schubert 	if (code == RADIUS_CODE_ACCESS_ACCEPT)
1075*85732ac8SCy Schubert 		radius_server_add_session(sess);
1076*85732ac8SCy Schubert 
10775b9c547cSRui Paulo 	return msg;
10785b9c547cSRui Paulo }
10795b9c547cSRui Paulo 
10805b9c547cSRui Paulo 
10815b9c547cSRui Paulo static struct radius_msg *
10825b9c547cSRui Paulo radius_server_macacl(struct radius_server_data *data,
10835b9c547cSRui Paulo 		     struct radius_client *client,
10845b9c547cSRui Paulo 		     struct radius_session *sess,
10855b9c547cSRui Paulo 		     struct radius_msg *request)
10865b9c547cSRui Paulo {
10875b9c547cSRui Paulo 	struct radius_msg *msg;
10885b9c547cSRui Paulo 	int code;
10895b9c547cSRui Paulo 	struct radius_hdr *hdr = radius_msg_get_hdr(request);
10905b9c547cSRui Paulo 	u8 *pw;
10915b9c547cSRui Paulo 	size_t pw_len;
10925b9c547cSRui Paulo 
10935b9c547cSRui Paulo 	code = RADIUS_CODE_ACCESS_ACCEPT;
10945b9c547cSRui Paulo 
10955b9c547cSRui Paulo 	if (radius_msg_get_attr_ptr(request, RADIUS_ATTR_USER_PASSWORD, &pw,
10965b9c547cSRui Paulo 				    &pw_len, NULL) < 0) {
10975b9c547cSRui Paulo 		RADIUS_DEBUG("Could not get User-Password");
10985b9c547cSRui Paulo 		code = RADIUS_CODE_ACCESS_REJECT;
10995b9c547cSRui Paulo 	} else {
11005b9c547cSRui Paulo 		int res;
11015b9c547cSRui Paulo 		struct eap_user tmp;
11025b9c547cSRui Paulo 
11035b9c547cSRui Paulo 		os_memset(&tmp, 0, sizeof(tmp));
11045b9c547cSRui Paulo 		res = data->get_eap_user(data->conf_ctx, (u8 *) sess->username,
11055b9c547cSRui Paulo 					 os_strlen(sess->username), 0, &tmp);
11065b9c547cSRui Paulo 		if (res || !tmp.macacl || tmp.password == NULL) {
11075b9c547cSRui Paulo 			RADIUS_DEBUG("No MAC ACL user entry");
11085b9c547cSRui Paulo 			bin_clear_free(tmp.password, tmp.password_len);
11095b9c547cSRui Paulo 			code = RADIUS_CODE_ACCESS_REJECT;
11105b9c547cSRui Paulo 		} else {
11115b9c547cSRui Paulo 			u8 buf[128];
11125b9c547cSRui Paulo 			res = radius_user_password_hide(
11135b9c547cSRui Paulo 				request, tmp.password, tmp.password_len,
11145b9c547cSRui Paulo 				(u8 *) client->shared_secret,
11155b9c547cSRui Paulo 				client->shared_secret_len,
11165b9c547cSRui Paulo 				buf, sizeof(buf));
11175b9c547cSRui Paulo 			bin_clear_free(tmp.password, tmp.password_len);
11185b9c547cSRui Paulo 
11195b9c547cSRui Paulo 			if (res < 0 || pw_len != (size_t) res ||
11205b9c547cSRui Paulo 			    os_memcmp_const(pw, buf, res) != 0) {
11215b9c547cSRui Paulo 				RADIUS_DEBUG("Incorrect User-Password");
11225b9c547cSRui Paulo 				code = RADIUS_CODE_ACCESS_REJECT;
11235b9c547cSRui Paulo 			}
11245b9c547cSRui Paulo 		}
11255b9c547cSRui Paulo 	}
11265b9c547cSRui Paulo 
11275b9c547cSRui Paulo 	msg = radius_msg_new(code, hdr->identifier);
11285b9c547cSRui Paulo 	if (msg == NULL) {
11295b9c547cSRui Paulo 		RADIUS_DEBUG("Failed to allocate reply message");
11305b9c547cSRui Paulo 		return NULL;
11315b9c547cSRui Paulo 	}
11325b9c547cSRui Paulo 
11335b9c547cSRui Paulo 	if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
11345b9c547cSRui Paulo 		RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
11355b9c547cSRui Paulo 		radius_msg_free(msg);
11365b9c547cSRui Paulo 		return NULL;
11375b9c547cSRui Paulo 	}
11385b9c547cSRui Paulo 
11395b9c547cSRui Paulo 	if (code == RADIUS_CODE_ACCESS_ACCEPT) {
11405b9c547cSRui Paulo 		struct hostapd_radius_attr *attr;
11415b9c547cSRui Paulo 		for (attr = sess->accept_attr; attr; attr = attr->next) {
11425b9c547cSRui Paulo 			if (!radius_msg_add_attr(msg, attr->type,
11435b9c547cSRui Paulo 						 wpabuf_head(attr->val),
11445b9c547cSRui Paulo 						 wpabuf_len(attr->val))) {
11455b9c547cSRui Paulo 				wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
11465b9c547cSRui Paulo 				radius_msg_free(msg);
11475b9c547cSRui Paulo 				return NULL;
11485b9c547cSRui Paulo 			}
11495b9c547cSRui Paulo 		}
11505b9c547cSRui Paulo 	}
11515b9c547cSRui Paulo 
115239beb93cSSam Leffler 	if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
115339beb93cSSam Leffler 				  client->shared_secret_len,
1154e28a4053SRui Paulo 				  hdr->authenticator) < 0) {
115539beb93cSSam Leffler 		RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
115639beb93cSSam Leffler 	}
115739beb93cSSam Leffler 
115839beb93cSSam Leffler 	return msg;
115939beb93cSSam Leffler }
116039beb93cSSam Leffler 
116139beb93cSSam Leffler 
116239beb93cSSam Leffler static int radius_server_reject(struct radius_server_data *data,
116339beb93cSSam Leffler 				struct radius_client *client,
116439beb93cSSam Leffler 				struct radius_msg *request,
116539beb93cSSam Leffler 				struct sockaddr *from, socklen_t fromlen,
116639beb93cSSam Leffler 				const char *from_addr, int from_port)
116739beb93cSSam Leffler {
116839beb93cSSam Leffler 	struct radius_msg *msg;
116939beb93cSSam Leffler 	int ret = 0;
117039beb93cSSam Leffler 	struct eap_hdr eapfail;
1171e28a4053SRui Paulo 	struct wpabuf *buf;
1172e28a4053SRui Paulo 	struct radius_hdr *hdr = radius_msg_get_hdr(request);
117339beb93cSSam Leffler 
117439beb93cSSam Leffler 	RADIUS_DEBUG("Reject invalid request from %s:%d",
117539beb93cSSam Leffler 		     from_addr, from_port);
117639beb93cSSam Leffler 
1177e28a4053SRui Paulo 	msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, hdr->identifier);
117839beb93cSSam Leffler 	if (msg == NULL) {
117939beb93cSSam Leffler 		return -1;
118039beb93cSSam Leffler 	}
118139beb93cSSam Leffler 
118239beb93cSSam Leffler 	os_memset(&eapfail, 0, sizeof(eapfail));
118339beb93cSSam Leffler 	eapfail.code = EAP_CODE_FAILURE;
118439beb93cSSam Leffler 	eapfail.identifier = 0;
118539beb93cSSam Leffler 	eapfail.length = host_to_be16(sizeof(eapfail));
118639beb93cSSam Leffler 
118739beb93cSSam Leffler 	if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) {
118839beb93cSSam Leffler 		RADIUS_DEBUG("Failed to add EAP-Message attribute");
118939beb93cSSam Leffler 	}
119039beb93cSSam Leffler 
119139beb93cSSam Leffler 	if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
119239beb93cSSam Leffler 		RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
119339beb93cSSam Leffler 		radius_msg_free(msg);
119439beb93cSSam Leffler 		return -1;
119539beb93cSSam Leffler 	}
119639beb93cSSam Leffler 
119739beb93cSSam Leffler 	if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
119839beb93cSSam Leffler 				  client->shared_secret_len,
1199e28a4053SRui Paulo 				  hdr->authenticator) <
1200e28a4053SRui Paulo 	    0) {
120139beb93cSSam Leffler 		RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
120239beb93cSSam Leffler 	}
120339beb93cSSam Leffler 
120439beb93cSSam Leffler 	if (wpa_debug_level <= MSG_MSGDUMP) {
120539beb93cSSam Leffler 		radius_msg_dump(msg);
120639beb93cSSam Leffler 	}
120739beb93cSSam Leffler 
120839beb93cSSam Leffler 	data->counters.access_rejects++;
120939beb93cSSam Leffler 	client->counters.access_rejects++;
1210e28a4053SRui Paulo 	buf = radius_msg_get_buf(msg);
1211e28a4053SRui Paulo 	if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
121239beb93cSSam Leffler 		   (struct sockaddr *) from, sizeof(*from)) < 0) {
12135b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno));
121439beb93cSSam Leffler 		ret = -1;
121539beb93cSSam Leffler 	}
121639beb93cSSam Leffler 
121739beb93cSSam Leffler 	radius_msg_free(msg);
121839beb93cSSam Leffler 
121939beb93cSSam Leffler 	return ret;
122039beb93cSSam Leffler }
122139beb93cSSam Leffler 
122239beb93cSSam Leffler 
1223*85732ac8SCy Schubert static void radius_server_hs20_t_c_check(struct radius_session *sess,
1224*85732ac8SCy Schubert 					 struct radius_msg *msg)
1225*85732ac8SCy Schubert {
1226*85732ac8SCy Schubert #ifdef CONFIG_HS20
1227*85732ac8SCy Schubert 	u8 *buf, *pos, *end, type, sublen, *timestamp = NULL;
1228*85732ac8SCy Schubert 	size_t len;
1229*85732ac8SCy Schubert 
1230*85732ac8SCy Schubert 	buf = NULL;
1231*85732ac8SCy Schubert 	for (;;) {
1232*85732ac8SCy Schubert 		if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
1233*85732ac8SCy Schubert 					    &buf, &len, buf) < 0)
1234*85732ac8SCy Schubert 			break;
1235*85732ac8SCy Schubert 		if (len < 6)
1236*85732ac8SCy Schubert 			continue;
1237*85732ac8SCy Schubert 		pos = buf;
1238*85732ac8SCy Schubert 		end = buf + len;
1239*85732ac8SCy Schubert 		if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
1240*85732ac8SCy Schubert 			continue;
1241*85732ac8SCy Schubert 		pos += 4;
1242*85732ac8SCy Schubert 
1243*85732ac8SCy Schubert 		type = *pos++;
1244*85732ac8SCy Schubert 		sublen = *pos++;
1245*85732ac8SCy Schubert 		if (sublen < 2)
1246*85732ac8SCy Schubert 			continue; /* invalid length */
1247*85732ac8SCy Schubert 		sublen -= 2; /* skip header */
1248*85732ac8SCy Schubert 		if (pos + sublen > end)
1249*85732ac8SCy Schubert 			continue; /* invalid WFA VSA */
1250*85732ac8SCy Schubert 
1251*85732ac8SCy Schubert 		if (type == RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP && len >= 4) {
1252*85732ac8SCy Schubert 			timestamp = pos;
1253*85732ac8SCy Schubert 			break;
1254*85732ac8SCy Schubert 		}
1255*85732ac8SCy Schubert 	}
1256*85732ac8SCy Schubert 
1257*85732ac8SCy Schubert 	if (!timestamp)
1258*85732ac8SCy Schubert 		return;
1259*85732ac8SCy Schubert 	RADIUS_DEBUG("HS20-Timestamp: %u", WPA_GET_BE32(timestamp));
1260*85732ac8SCy Schubert 	if (sess->t_c_timestamp != WPA_GET_BE32(timestamp)) {
1261*85732ac8SCy Schubert 		RADIUS_DEBUG("Last read T&C timestamp does not match HS20-Timestamp --> require filtering");
1262*85732ac8SCy Schubert 		sess->t_c_filtering = 1;
1263*85732ac8SCy Schubert 	}
1264*85732ac8SCy Schubert #endif /* CONFIG_HS20 */
1265*85732ac8SCy Schubert }
1266*85732ac8SCy Schubert 
1267*85732ac8SCy Schubert 
126839beb93cSSam Leffler static int radius_server_request(struct radius_server_data *data,
126939beb93cSSam Leffler 				 struct radius_msg *msg,
127039beb93cSSam Leffler 				 struct sockaddr *from, socklen_t fromlen,
127139beb93cSSam Leffler 				 struct radius_client *client,
127239beb93cSSam Leffler 				 const char *from_addr, int from_port,
127339beb93cSSam Leffler 				 struct radius_session *force_sess)
127439beb93cSSam Leffler {
1275f05cddf9SRui Paulo 	struct wpabuf *eap = NULL;
127639beb93cSSam Leffler 	int res, state_included = 0;
127739beb93cSSam Leffler 	u8 statebuf[4];
127839beb93cSSam Leffler 	unsigned int state;
127939beb93cSSam Leffler 	struct radius_session *sess;
128039beb93cSSam Leffler 	struct radius_msg *reply;
12813157ba21SRui Paulo 	int is_complete = 0;
128239beb93cSSam Leffler 
128339beb93cSSam Leffler 	if (force_sess)
128439beb93cSSam Leffler 		sess = force_sess;
128539beb93cSSam Leffler 	else {
128639beb93cSSam Leffler 		res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf,
128739beb93cSSam Leffler 					  sizeof(statebuf));
128839beb93cSSam Leffler 		state_included = res >= 0;
128939beb93cSSam Leffler 		if (res == sizeof(statebuf)) {
129039beb93cSSam Leffler 			state = WPA_GET_BE32(statebuf);
129139beb93cSSam Leffler 			sess = radius_server_get_session(client, state);
129239beb93cSSam Leffler 		} else {
129339beb93cSSam Leffler 			sess = NULL;
129439beb93cSSam Leffler 		}
129539beb93cSSam Leffler 	}
129639beb93cSSam Leffler 
129739beb93cSSam Leffler 	if (sess) {
129839beb93cSSam Leffler 		RADIUS_DEBUG("Request for session 0x%x", sess->sess_id);
129939beb93cSSam Leffler 	} else if (state_included) {
130039beb93cSSam Leffler 		RADIUS_DEBUG("State attribute included but no session found");
130139beb93cSSam Leffler 		radius_server_reject(data, client, msg, from, fromlen,
130239beb93cSSam Leffler 				     from_addr, from_port);
130339beb93cSSam Leffler 		return -1;
130439beb93cSSam Leffler 	} else {
13055b9c547cSRui Paulo 		sess = radius_server_get_new_session(data, client, msg,
13065b9c547cSRui Paulo 						     from_addr);
130739beb93cSSam Leffler 		if (sess == NULL) {
130839beb93cSSam Leffler 			RADIUS_DEBUG("Could not create a new session");
130939beb93cSSam Leffler 			radius_server_reject(data, client, msg, from, fromlen,
131039beb93cSSam Leffler 					     from_addr, from_port);
131139beb93cSSam Leffler 			return -1;
131239beb93cSSam Leffler 		}
131339beb93cSSam Leffler 	}
131439beb93cSSam Leffler 
131539beb93cSSam Leffler 	if (sess->last_from_port == from_port &&
1316e28a4053SRui Paulo 	    sess->last_identifier == radius_msg_get_hdr(msg)->identifier &&
1317e28a4053SRui Paulo 	    os_memcmp(sess->last_authenticator,
1318e28a4053SRui Paulo 		      radius_msg_get_hdr(msg)->authenticator, 16) == 0) {
131939beb93cSSam Leffler 		RADIUS_DEBUG("Duplicate message from %s", from_addr);
132039beb93cSSam Leffler 		data->counters.dup_access_requests++;
132139beb93cSSam Leffler 		client->counters.dup_access_requests++;
132239beb93cSSam Leffler 
132339beb93cSSam Leffler 		if (sess->last_reply) {
1324e28a4053SRui Paulo 			struct wpabuf *buf;
1325e28a4053SRui Paulo 			buf = radius_msg_get_buf(sess->last_reply);
1326e28a4053SRui Paulo 			res = sendto(data->auth_sock, wpabuf_head(buf),
1327e28a4053SRui Paulo 				     wpabuf_len(buf), 0,
132839beb93cSSam Leffler 				     (struct sockaddr *) from, fromlen);
132939beb93cSSam Leffler 			if (res < 0) {
13305b9c547cSRui Paulo 				wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
13315b9c547cSRui Paulo 					   strerror(errno));
133239beb93cSSam Leffler 			}
133339beb93cSSam Leffler 			return 0;
133439beb93cSSam Leffler 		}
133539beb93cSSam Leffler 
133639beb93cSSam Leffler 		RADIUS_DEBUG("No previous reply available for duplicate "
133739beb93cSSam Leffler 			     "message");
133839beb93cSSam Leffler 		return -1;
133939beb93cSSam Leffler 	}
134039beb93cSSam Leffler 
1341f05cddf9SRui Paulo 	eap = radius_msg_get_eap(msg);
13425b9c547cSRui Paulo 	if (eap == NULL && sess->macacl) {
13435b9c547cSRui Paulo 		reply = radius_server_macacl(data, client, sess, msg);
13445b9c547cSRui Paulo 		if (reply == NULL)
13455b9c547cSRui Paulo 			return -1;
13465b9c547cSRui Paulo 		goto send_reply;
13475b9c547cSRui Paulo 	}
134839beb93cSSam Leffler 	if (eap == NULL) {
134939beb93cSSam Leffler 		RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
135039beb93cSSam Leffler 			     from_addr);
135139beb93cSSam Leffler 		data->counters.packets_dropped++;
135239beb93cSSam Leffler 		client->counters.packets_dropped++;
135339beb93cSSam Leffler 		return -1;
135439beb93cSSam Leffler 	}
135539beb93cSSam Leffler 
1356f05cddf9SRui Paulo 	RADIUS_DUMP("Received EAP data", wpabuf_head(eap), wpabuf_len(eap));
135739beb93cSSam Leffler 
135839beb93cSSam Leffler 	/* FIX: if Code is Request, Success, or Failure, send Access-Reject;
135939beb93cSSam Leffler 	 * RFC3579 Sect. 2.6.2.
136039beb93cSSam Leffler 	 * Include EAP-Response/Nak with no preferred method if
136139beb93cSSam Leffler 	 * code == request.
136239beb93cSSam Leffler 	 * If code is not 1-4, discard the packet silently.
136339beb93cSSam Leffler 	 * Or is this already done by the EAP state machine? */
136439beb93cSSam Leffler 
136539beb93cSSam Leffler 	wpabuf_free(sess->eap_if->eapRespData);
1366f05cddf9SRui Paulo 	sess->eap_if->eapRespData = eap;
136739beb93cSSam Leffler 	sess->eap_if->eapResp = TRUE;
136839beb93cSSam Leffler 	eap_server_sm_step(sess->eap);
136939beb93cSSam Leffler 
137039beb93cSSam Leffler 	if ((sess->eap_if->eapReq || sess->eap_if->eapSuccess ||
137139beb93cSSam Leffler 	     sess->eap_if->eapFail) && sess->eap_if->eapReqData) {
137239beb93cSSam Leffler 		RADIUS_DUMP("EAP data from the state machine",
137339beb93cSSam Leffler 			    wpabuf_head(sess->eap_if->eapReqData),
137439beb93cSSam Leffler 			    wpabuf_len(sess->eap_if->eapReqData));
137539beb93cSSam Leffler 	} else if (sess->eap_if->eapFail) {
137639beb93cSSam Leffler 		RADIUS_DEBUG("No EAP data from the state machine, but eapFail "
137739beb93cSSam Leffler 			     "set");
137839beb93cSSam Leffler 	} else if (eap_sm_method_pending(sess->eap)) {
137939beb93cSSam Leffler 		radius_msg_free(sess->last_msg);
138039beb93cSSam Leffler 		sess->last_msg = msg;
138139beb93cSSam Leffler 		sess->last_from_port = from_port;
138239beb93cSSam Leffler 		os_free(sess->last_from_addr);
138339beb93cSSam Leffler 		sess->last_from_addr = os_strdup(from_addr);
138439beb93cSSam Leffler 		sess->last_fromlen = fromlen;
138539beb93cSSam Leffler 		os_memcpy(&sess->last_from, from, fromlen);
138639beb93cSSam Leffler 		return -2;
138739beb93cSSam Leffler 	} else {
138839beb93cSSam Leffler 		RADIUS_DEBUG("No EAP data from the state machine - ignore this"
138939beb93cSSam Leffler 			     " Access-Request silently (assuming it was a "
139039beb93cSSam Leffler 			     "duplicate)");
139139beb93cSSam Leffler 		data->counters.packets_dropped++;
139239beb93cSSam Leffler 		client->counters.packets_dropped++;
139339beb93cSSam Leffler 		return -1;
139439beb93cSSam Leffler 	}
139539beb93cSSam Leffler 
13963157ba21SRui Paulo 	if (sess->eap_if->eapSuccess || sess->eap_if->eapFail)
13973157ba21SRui Paulo 		is_complete = 1;
1398*85732ac8SCy Schubert 	if (sess->eap_if->eapFail) {
13995b9c547cSRui Paulo 		srv_log(sess, "EAP authentication failed");
1400*85732ac8SCy Schubert 		db_update_last_msk(sess, "FAIL");
1401*85732ac8SCy Schubert 	} else if (sess->eap_if->eapSuccess) {
14025b9c547cSRui Paulo 		srv_log(sess, "EAP authentication succeeded");
1403*85732ac8SCy Schubert 	}
1404*85732ac8SCy Schubert 
1405*85732ac8SCy Schubert 	if (sess->eap_if->eapSuccess)
1406*85732ac8SCy Schubert 		radius_server_hs20_t_c_check(sess, msg);
14073157ba21SRui Paulo 
140839beb93cSSam Leffler 	reply = radius_server_encapsulate_eap(data, client, sess, msg);
140939beb93cSSam Leffler 
14105b9c547cSRui Paulo send_reply:
141139beb93cSSam Leffler 	if (reply) {
1412e28a4053SRui Paulo 		struct wpabuf *buf;
1413e28a4053SRui Paulo 		struct radius_hdr *hdr;
1414e28a4053SRui Paulo 
141539beb93cSSam Leffler 		RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port);
141639beb93cSSam Leffler 		if (wpa_debug_level <= MSG_MSGDUMP) {
141739beb93cSSam Leffler 			radius_msg_dump(reply);
141839beb93cSSam Leffler 		}
141939beb93cSSam Leffler 
1420e28a4053SRui Paulo 		switch (radius_msg_get_hdr(reply)->code) {
142139beb93cSSam Leffler 		case RADIUS_CODE_ACCESS_ACCEPT:
14225b9c547cSRui Paulo 			srv_log(sess, "Sending Access-Accept");
142339beb93cSSam Leffler 			data->counters.access_accepts++;
142439beb93cSSam Leffler 			client->counters.access_accepts++;
142539beb93cSSam Leffler 			break;
142639beb93cSSam Leffler 		case RADIUS_CODE_ACCESS_REJECT:
14275b9c547cSRui Paulo 			srv_log(sess, "Sending Access-Reject");
142839beb93cSSam Leffler 			data->counters.access_rejects++;
142939beb93cSSam Leffler 			client->counters.access_rejects++;
143039beb93cSSam Leffler 			break;
143139beb93cSSam Leffler 		case RADIUS_CODE_ACCESS_CHALLENGE:
143239beb93cSSam Leffler 			data->counters.access_challenges++;
143339beb93cSSam Leffler 			client->counters.access_challenges++;
143439beb93cSSam Leffler 			break;
143539beb93cSSam Leffler 		}
1436e28a4053SRui Paulo 		buf = radius_msg_get_buf(reply);
1437e28a4053SRui Paulo 		res = sendto(data->auth_sock, wpabuf_head(buf),
1438e28a4053SRui Paulo 			     wpabuf_len(buf), 0,
143939beb93cSSam Leffler 			     (struct sockaddr *) from, fromlen);
144039beb93cSSam Leffler 		if (res < 0) {
14415b9c547cSRui Paulo 			wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
14425b9c547cSRui Paulo 				   strerror(errno));
144339beb93cSSam Leffler 		}
144439beb93cSSam Leffler 		radius_msg_free(sess->last_reply);
144539beb93cSSam Leffler 		sess->last_reply = reply;
144639beb93cSSam Leffler 		sess->last_from_port = from_port;
1447e28a4053SRui Paulo 		hdr = radius_msg_get_hdr(msg);
1448e28a4053SRui Paulo 		sess->last_identifier = hdr->identifier;
1449e28a4053SRui Paulo 		os_memcpy(sess->last_authenticator, hdr->authenticator, 16);
145039beb93cSSam Leffler 	} else {
145139beb93cSSam Leffler 		data->counters.packets_dropped++;
145239beb93cSSam Leffler 		client->counters.packets_dropped++;
145339beb93cSSam Leffler 	}
145439beb93cSSam Leffler 
14553157ba21SRui Paulo 	if (is_complete) {
145639beb93cSSam Leffler 		RADIUS_DEBUG("Removing completed session 0x%x after timeout",
145739beb93cSSam Leffler 			     sess->sess_id);
145839beb93cSSam Leffler 		eloop_cancel_timeout(radius_server_session_remove_timeout,
145939beb93cSSam Leffler 				     data, sess);
1460*85732ac8SCy Schubert 		eloop_register_timeout(RADIUS_SESSION_MAINTAIN, 0,
146139beb93cSSam Leffler 				       radius_server_session_remove_timeout,
146239beb93cSSam Leffler 				       data, sess);
146339beb93cSSam Leffler 	}
146439beb93cSSam Leffler 
146539beb93cSSam Leffler 	return 0;
146639beb93cSSam Leffler }
146739beb93cSSam Leffler 
146839beb93cSSam Leffler 
1469*85732ac8SCy Schubert static void
1470*85732ac8SCy Schubert radius_server_receive_disconnect_resp(struct radius_server_data *data,
1471*85732ac8SCy Schubert 				      struct radius_client *client,
1472*85732ac8SCy Schubert 				      struct radius_msg *msg, int ack)
1473*85732ac8SCy Schubert {
1474*85732ac8SCy Schubert 	struct radius_hdr *hdr;
1475*85732ac8SCy Schubert 
1476*85732ac8SCy Schubert 	if (!client->pending_dac_disconnect_req) {
1477*85732ac8SCy Schubert 		RADIUS_DEBUG("Ignore unexpected Disconnect response");
1478*85732ac8SCy Schubert 		radius_msg_free(msg);
1479*85732ac8SCy Schubert 		return;
1480*85732ac8SCy Schubert 	}
1481*85732ac8SCy Schubert 
1482*85732ac8SCy Schubert 	hdr = radius_msg_get_hdr(msg);
1483*85732ac8SCy Schubert 	if (hdr->identifier != client->pending_dac_disconnect_id) {
1484*85732ac8SCy Schubert 		RADIUS_DEBUG("Ignore unexpected Disconnect response with unexpected identifier %u (expected %u)",
1485*85732ac8SCy Schubert 			     hdr->identifier,
1486*85732ac8SCy Schubert 			     client->pending_dac_disconnect_id);
1487*85732ac8SCy Schubert 		radius_msg_free(msg);
1488*85732ac8SCy Schubert 		return;
1489*85732ac8SCy Schubert 	}
1490*85732ac8SCy Schubert 
1491*85732ac8SCy Schubert 	if (radius_msg_verify(msg, (const u8 *) client->shared_secret,
1492*85732ac8SCy Schubert 			      client->shared_secret_len,
1493*85732ac8SCy Schubert 			      client->pending_dac_disconnect_req, 0)) {
1494*85732ac8SCy Schubert 		RADIUS_DEBUG("Ignore Disconnect response with invalid authenticator");
1495*85732ac8SCy Schubert 		radius_msg_free(msg);
1496*85732ac8SCy Schubert 		return;
1497*85732ac8SCy Schubert 	}
1498*85732ac8SCy Schubert 
1499*85732ac8SCy Schubert 	RADIUS_DEBUG("Disconnect-%s received for " MACSTR,
1500*85732ac8SCy Schubert 		     ack ? "ACK" : "NAK",
1501*85732ac8SCy Schubert 		     MAC2STR(client->pending_dac_disconnect_addr));
1502*85732ac8SCy Schubert 
1503*85732ac8SCy Schubert 	radius_msg_free(msg);
1504*85732ac8SCy Schubert 	radius_msg_free(client->pending_dac_disconnect_req);
1505*85732ac8SCy Schubert 	client->pending_dac_disconnect_req = NULL;
1506*85732ac8SCy Schubert }
1507*85732ac8SCy Schubert 
1508*85732ac8SCy Schubert 
1509*85732ac8SCy Schubert static void radius_server_receive_coa_resp(struct radius_server_data *data,
1510*85732ac8SCy Schubert 					   struct radius_client *client,
1511*85732ac8SCy Schubert 					   struct radius_msg *msg, int ack)
1512*85732ac8SCy Schubert {
1513*85732ac8SCy Schubert 	struct radius_hdr *hdr;
1514*85732ac8SCy Schubert #ifdef CONFIG_SQLITE
1515*85732ac8SCy Schubert 	char addrtxt[3 * ETH_ALEN];
1516*85732ac8SCy Schubert 	char *sql;
1517*85732ac8SCy Schubert 	int res;
1518*85732ac8SCy Schubert #endif /* CONFIG_SQLITE */
1519*85732ac8SCy Schubert 
1520*85732ac8SCy Schubert 	if (!client->pending_dac_coa_req) {
1521*85732ac8SCy Schubert 		RADIUS_DEBUG("Ignore unexpected CoA response");
1522*85732ac8SCy Schubert 		radius_msg_free(msg);
1523*85732ac8SCy Schubert 		return;
1524*85732ac8SCy Schubert 	}
1525*85732ac8SCy Schubert 
1526*85732ac8SCy Schubert 	hdr = radius_msg_get_hdr(msg);
1527*85732ac8SCy Schubert 	if (hdr->identifier != client->pending_dac_coa_id) {
1528*85732ac8SCy Schubert 		RADIUS_DEBUG("Ignore unexpected CoA response with unexpected identifier %u (expected %u)",
1529*85732ac8SCy Schubert 			     hdr->identifier,
1530*85732ac8SCy Schubert 			     client->pending_dac_coa_id);
1531*85732ac8SCy Schubert 		radius_msg_free(msg);
1532*85732ac8SCy Schubert 		return;
1533*85732ac8SCy Schubert 	}
1534*85732ac8SCy Schubert 
1535*85732ac8SCy Schubert 	if (radius_msg_verify(msg, (const u8 *) client->shared_secret,
1536*85732ac8SCy Schubert 			      client->shared_secret_len,
1537*85732ac8SCy Schubert 			      client->pending_dac_coa_req, 0)) {
1538*85732ac8SCy Schubert 		RADIUS_DEBUG("Ignore CoA response with invalid authenticator");
1539*85732ac8SCy Schubert 		radius_msg_free(msg);
1540*85732ac8SCy Schubert 		return;
1541*85732ac8SCy Schubert 	}
1542*85732ac8SCy Schubert 
1543*85732ac8SCy Schubert 	RADIUS_DEBUG("CoA-%s received for " MACSTR,
1544*85732ac8SCy Schubert 		     ack ? "ACK" : "NAK",
1545*85732ac8SCy Schubert 		     MAC2STR(client->pending_dac_coa_addr));
1546*85732ac8SCy Schubert 
1547*85732ac8SCy Schubert 	radius_msg_free(msg);
1548*85732ac8SCy Schubert 	radius_msg_free(client->pending_dac_coa_req);
1549*85732ac8SCy Schubert 	client->pending_dac_coa_req = NULL;
1550*85732ac8SCy Schubert 
1551*85732ac8SCy Schubert #ifdef CONFIG_SQLITE
1552*85732ac8SCy Schubert 	if (!data->db)
1553*85732ac8SCy Schubert 		return;
1554*85732ac8SCy Schubert 
1555*85732ac8SCy Schubert 	os_snprintf(addrtxt, sizeof(addrtxt), MACSTR,
1556*85732ac8SCy Schubert 		    MAC2STR(client->pending_dac_coa_addr));
1557*85732ac8SCy Schubert 
1558*85732ac8SCy Schubert 	if (ack) {
1559*85732ac8SCy Schubert 		sql = sqlite3_mprintf("UPDATE current_sessions SET hs20_t_c_filtering=0, waiting_coa_ack=0, coa_ack_received=1 WHERE mac_addr=%Q",
1560*85732ac8SCy Schubert 				      addrtxt);
1561*85732ac8SCy Schubert 	} else {
1562*85732ac8SCy Schubert 		sql = sqlite3_mprintf("UPDATE current_sessions SET waiting_coa_ack=0 WHERE mac_addr=%Q",
1563*85732ac8SCy Schubert 				      addrtxt);
1564*85732ac8SCy Schubert 	}
1565*85732ac8SCy Schubert 	if (!sql)
1566*85732ac8SCy Schubert 		return;
1567*85732ac8SCy Schubert 
1568*85732ac8SCy Schubert 	res = sqlite3_exec(data->db, sql, NULL, NULL, NULL);
1569*85732ac8SCy Schubert 	sqlite3_free(sql);
1570*85732ac8SCy Schubert 	if (res != SQLITE_OK) {
1571*85732ac8SCy Schubert 		RADIUS_ERROR("Failed to update current_sessions entry: %s",
1572*85732ac8SCy Schubert 			     sqlite3_errmsg(data->db));
1573*85732ac8SCy Schubert 		return;
1574*85732ac8SCy Schubert 	}
1575*85732ac8SCy Schubert #endif /* CONFIG_SQLITE */
1576*85732ac8SCy Schubert }
1577*85732ac8SCy Schubert 
1578*85732ac8SCy Schubert 
157939beb93cSSam Leffler static void radius_server_receive_auth(int sock, void *eloop_ctx,
158039beb93cSSam Leffler 				       void *sock_ctx)
158139beb93cSSam Leffler {
158239beb93cSSam Leffler 	struct radius_server_data *data = eloop_ctx;
158339beb93cSSam Leffler 	u8 *buf = NULL;
15843157ba21SRui Paulo 	union {
15853157ba21SRui Paulo 		struct sockaddr_storage ss;
15863157ba21SRui Paulo 		struct sockaddr_in sin;
15873157ba21SRui Paulo #ifdef CONFIG_IPV6
15883157ba21SRui Paulo 		struct sockaddr_in6 sin6;
15893157ba21SRui Paulo #endif /* CONFIG_IPV6 */
15903157ba21SRui Paulo 	} from;
159139beb93cSSam Leffler 	socklen_t fromlen;
159239beb93cSSam Leffler 	int len;
159339beb93cSSam Leffler 	struct radius_client *client = NULL;
159439beb93cSSam Leffler 	struct radius_msg *msg = NULL;
159539beb93cSSam Leffler 	char abuf[50];
159639beb93cSSam Leffler 	int from_port = 0;
159739beb93cSSam Leffler 
159839beb93cSSam Leffler 	buf = os_malloc(RADIUS_MAX_MSG_LEN);
159939beb93cSSam Leffler 	if (buf == NULL) {
160039beb93cSSam Leffler 		goto fail;
160139beb93cSSam Leffler 	}
160239beb93cSSam Leffler 
160339beb93cSSam Leffler 	fromlen = sizeof(from);
160439beb93cSSam Leffler 	len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
16053157ba21SRui Paulo 		       (struct sockaddr *) &from.ss, &fromlen);
160639beb93cSSam Leffler 	if (len < 0) {
16075b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s",
16085b9c547cSRui Paulo 			   strerror(errno));
160939beb93cSSam Leffler 		goto fail;
161039beb93cSSam Leffler 	}
161139beb93cSSam Leffler 
161239beb93cSSam Leffler #ifdef CONFIG_IPV6
161339beb93cSSam Leffler 	if (data->ipv6) {
16143157ba21SRui Paulo 		if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf,
16153157ba21SRui Paulo 			      sizeof(abuf)) == NULL)
161639beb93cSSam Leffler 			abuf[0] = '\0';
16173157ba21SRui Paulo 		from_port = ntohs(from.sin6.sin6_port);
161839beb93cSSam Leffler 		RADIUS_DEBUG("Received %d bytes from %s:%d",
161939beb93cSSam Leffler 			     len, abuf, from_port);
162039beb93cSSam Leffler 
162139beb93cSSam Leffler 		client = radius_server_get_client(data,
162239beb93cSSam Leffler 						  (struct in_addr *)
16233157ba21SRui Paulo 						  &from.sin6.sin6_addr, 1);
162439beb93cSSam Leffler 	}
162539beb93cSSam Leffler #endif /* CONFIG_IPV6 */
162639beb93cSSam Leffler 
162739beb93cSSam Leffler 	if (!data->ipv6) {
16283157ba21SRui Paulo 		os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
16293157ba21SRui Paulo 		from_port = ntohs(from.sin.sin_port);
163039beb93cSSam Leffler 		RADIUS_DEBUG("Received %d bytes from %s:%d",
163139beb93cSSam Leffler 			     len, abuf, from_port);
163239beb93cSSam Leffler 
16333157ba21SRui Paulo 		client = radius_server_get_client(data, &from.sin.sin_addr, 0);
163439beb93cSSam Leffler 	}
163539beb93cSSam Leffler 
163639beb93cSSam Leffler 	RADIUS_DUMP("Received data", buf, len);
163739beb93cSSam Leffler 
163839beb93cSSam Leffler 	if (client == NULL) {
163939beb93cSSam Leffler 		RADIUS_DEBUG("Unknown client %s - packet ignored", abuf);
164039beb93cSSam Leffler 		data->counters.invalid_requests++;
164139beb93cSSam Leffler 		goto fail;
164239beb93cSSam Leffler 	}
164339beb93cSSam Leffler 
164439beb93cSSam Leffler 	msg = radius_msg_parse(buf, len);
164539beb93cSSam Leffler 	if (msg == NULL) {
164639beb93cSSam Leffler 		RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
164739beb93cSSam Leffler 		data->counters.malformed_access_requests++;
164839beb93cSSam Leffler 		client->counters.malformed_access_requests++;
164939beb93cSSam Leffler 		goto fail;
165039beb93cSSam Leffler 	}
165139beb93cSSam Leffler 
165239beb93cSSam Leffler 	os_free(buf);
165339beb93cSSam Leffler 	buf = NULL;
165439beb93cSSam Leffler 
165539beb93cSSam Leffler 	if (wpa_debug_level <= MSG_MSGDUMP) {
165639beb93cSSam Leffler 		radius_msg_dump(msg);
165739beb93cSSam Leffler 	}
165839beb93cSSam Leffler 
1659*85732ac8SCy Schubert 	if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_ACK) {
1660*85732ac8SCy Schubert 		radius_server_receive_disconnect_resp(data, client, msg, 1);
1661*85732ac8SCy Schubert 		return;
1662*85732ac8SCy Schubert 	}
1663*85732ac8SCy Schubert 
1664*85732ac8SCy Schubert 	if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_NAK) {
1665*85732ac8SCy Schubert 		radius_server_receive_disconnect_resp(data, client, msg, 0);
1666*85732ac8SCy Schubert 		return;
1667*85732ac8SCy Schubert 	}
1668*85732ac8SCy Schubert 
1669*85732ac8SCy Schubert 	if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_ACK) {
1670*85732ac8SCy Schubert 		radius_server_receive_coa_resp(data, client, msg, 1);
1671*85732ac8SCy Schubert 		return;
1672*85732ac8SCy Schubert 	}
1673*85732ac8SCy Schubert 
1674*85732ac8SCy Schubert 	if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_NAK) {
1675*85732ac8SCy Schubert 		radius_server_receive_coa_resp(data, client, msg, 0);
1676*85732ac8SCy Schubert 		return;
1677*85732ac8SCy Schubert 	}
1678*85732ac8SCy Schubert 
1679e28a4053SRui Paulo 	if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCESS_REQUEST) {
1680e28a4053SRui Paulo 		RADIUS_DEBUG("Unexpected RADIUS code %d",
1681e28a4053SRui Paulo 			     radius_msg_get_hdr(msg)->code);
168239beb93cSSam Leffler 		data->counters.unknown_types++;
168339beb93cSSam Leffler 		client->counters.unknown_types++;
168439beb93cSSam Leffler 		goto fail;
168539beb93cSSam Leffler 	}
168639beb93cSSam Leffler 
168739beb93cSSam Leffler 	data->counters.access_requests++;
168839beb93cSSam Leffler 	client->counters.access_requests++;
168939beb93cSSam Leffler 
169039beb93cSSam Leffler 	if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret,
169139beb93cSSam Leffler 				       client->shared_secret_len, NULL)) {
169239beb93cSSam Leffler 		RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf);
169339beb93cSSam Leffler 		data->counters.bad_authenticators++;
169439beb93cSSam Leffler 		client->counters.bad_authenticators++;
169539beb93cSSam Leffler 		goto fail;
169639beb93cSSam Leffler 	}
169739beb93cSSam Leffler 
169839beb93cSSam Leffler 	if (radius_server_request(data, msg, (struct sockaddr *) &from,
169939beb93cSSam Leffler 				  fromlen, client, abuf, from_port, NULL) ==
170039beb93cSSam Leffler 	    -2)
170139beb93cSSam Leffler 		return; /* msg was stored with the session */
170239beb93cSSam Leffler 
170339beb93cSSam Leffler fail:
170439beb93cSSam Leffler 	radius_msg_free(msg);
170539beb93cSSam Leffler 	os_free(buf);
170639beb93cSSam Leffler }
170739beb93cSSam Leffler 
170839beb93cSSam Leffler 
17095b9c547cSRui Paulo static void radius_server_receive_acct(int sock, void *eloop_ctx,
17105b9c547cSRui Paulo 				       void *sock_ctx)
17115b9c547cSRui Paulo {
17125b9c547cSRui Paulo 	struct radius_server_data *data = eloop_ctx;
17135b9c547cSRui Paulo 	u8 *buf = NULL;
17145b9c547cSRui Paulo 	union {
17155b9c547cSRui Paulo 		struct sockaddr_storage ss;
17165b9c547cSRui Paulo 		struct sockaddr_in sin;
17175b9c547cSRui Paulo #ifdef CONFIG_IPV6
17185b9c547cSRui Paulo 		struct sockaddr_in6 sin6;
17195b9c547cSRui Paulo #endif /* CONFIG_IPV6 */
17205b9c547cSRui Paulo 	} from;
17215b9c547cSRui Paulo 	socklen_t fromlen;
17225b9c547cSRui Paulo 	int len, res;
17235b9c547cSRui Paulo 	struct radius_client *client = NULL;
17245b9c547cSRui Paulo 	struct radius_msg *msg = NULL, *resp = NULL;
17255b9c547cSRui Paulo 	char abuf[50];
17265b9c547cSRui Paulo 	int from_port = 0;
17275b9c547cSRui Paulo 	struct radius_hdr *hdr;
17285b9c547cSRui Paulo 	struct wpabuf *rbuf;
17295b9c547cSRui Paulo 
17305b9c547cSRui Paulo 	buf = os_malloc(RADIUS_MAX_MSG_LEN);
17315b9c547cSRui Paulo 	if (buf == NULL) {
17325b9c547cSRui Paulo 		goto fail;
17335b9c547cSRui Paulo 	}
17345b9c547cSRui Paulo 
17355b9c547cSRui Paulo 	fromlen = sizeof(from);
17365b9c547cSRui Paulo 	len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
17375b9c547cSRui Paulo 		       (struct sockaddr *) &from.ss, &fromlen);
17385b9c547cSRui Paulo 	if (len < 0) {
17395b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s",
17405b9c547cSRui Paulo 			   strerror(errno));
17415b9c547cSRui Paulo 		goto fail;
17425b9c547cSRui Paulo 	}
17435b9c547cSRui Paulo 
17445b9c547cSRui Paulo #ifdef CONFIG_IPV6
17455b9c547cSRui Paulo 	if (data->ipv6) {
17465b9c547cSRui Paulo 		if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf,
17475b9c547cSRui Paulo 			      sizeof(abuf)) == NULL)
17485b9c547cSRui Paulo 			abuf[0] = '\0';
17495b9c547cSRui Paulo 		from_port = ntohs(from.sin6.sin6_port);
17505b9c547cSRui Paulo 		RADIUS_DEBUG("Received %d bytes from %s:%d",
17515b9c547cSRui Paulo 			     len, abuf, from_port);
17525b9c547cSRui Paulo 
17535b9c547cSRui Paulo 		client = radius_server_get_client(data,
17545b9c547cSRui Paulo 						  (struct in_addr *)
17555b9c547cSRui Paulo 						  &from.sin6.sin6_addr, 1);
17565b9c547cSRui Paulo 	}
17575b9c547cSRui Paulo #endif /* CONFIG_IPV6 */
17585b9c547cSRui Paulo 
17595b9c547cSRui Paulo 	if (!data->ipv6) {
17605b9c547cSRui Paulo 		os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
17615b9c547cSRui Paulo 		from_port = ntohs(from.sin.sin_port);
17625b9c547cSRui Paulo 		RADIUS_DEBUG("Received %d bytes from %s:%d",
17635b9c547cSRui Paulo 			     len, abuf, from_port);
17645b9c547cSRui Paulo 
17655b9c547cSRui Paulo 		client = radius_server_get_client(data, &from.sin.sin_addr, 0);
17665b9c547cSRui Paulo 	}
17675b9c547cSRui Paulo 
17685b9c547cSRui Paulo 	RADIUS_DUMP("Received data", buf, len);
17695b9c547cSRui Paulo 
17705b9c547cSRui Paulo 	if (client == NULL) {
17715b9c547cSRui Paulo 		RADIUS_DEBUG("Unknown client %s - packet ignored", abuf);
17725b9c547cSRui Paulo 		data->counters.invalid_acct_requests++;
17735b9c547cSRui Paulo 		goto fail;
17745b9c547cSRui Paulo 	}
17755b9c547cSRui Paulo 
17765b9c547cSRui Paulo 	msg = radius_msg_parse(buf, len);
17775b9c547cSRui Paulo 	if (msg == NULL) {
17785b9c547cSRui Paulo 		RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
17795b9c547cSRui Paulo 		data->counters.malformed_acct_requests++;
17805b9c547cSRui Paulo 		client->counters.malformed_acct_requests++;
17815b9c547cSRui Paulo 		goto fail;
17825b9c547cSRui Paulo 	}
17835b9c547cSRui Paulo 
17845b9c547cSRui Paulo 	os_free(buf);
17855b9c547cSRui Paulo 	buf = NULL;
17865b9c547cSRui Paulo 
17875b9c547cSRui Paulo 	if (wpa_debug_level <= MSG_MSGDUMP) {
17885b9c547cSRui Paulo 		radius_msg_dump(msg);
17895b9c547cSRui Paulo 	}
17905b9c547cSRui Paulo 
17915b9c547cSRui Paulo 	if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_REQUEST) {
17925b9c547cSRui Paulo 		RADIUS_DEBUG("Unexpected RADIUS code %d",
17935b9c547cSRui Paulo 			     radius_msg_get_hdr(msg)->code);
17945b9c547cSRui Paulo 		data->counters.unknown_acct_types++;
17955b9c547cSRui Paulo 		client->counters.unknown_acct_types++;
17965b9c547cSRui Paulo 		goto fail;
17975b9c547cSRui Paulo 	}
17985b9c547cSRui Paulo 
17995b9c547cSRui Paulo 	data->counters.acct_requests++;
18005b9c547cSRui Paulo 	client->counters.acct_requests++;
18015b9c547cSRui Paulo 
18025b9c547cSRui Paulo 	if (radius_msg_verify_acct_req(msg, (u8 *) client->shared_secret,
18035b9c547cSRui Paulo 				       client->shared_secret_len)) {
18045b9c547cSRui Paulo 		RADIUS_DEBUG("Invalid Authenticator from %s", abuf);
18055b9c547cSRui Paulo 		data->counters.acct_bad_authenticators++;
18065b9c547cSRui Paulo 		client->counters.acct_bad_authenticators++;
18075b9c547cSRui Paulo 		goto fail;
18085b9c547cSRui Paulo 	}
18095b9c547cSRui Paulo 
18105b9c547cSRui Paulo 	/* TODO: Write accounting information to a file or database */
18115b9c547cSRui Paulo 
18125b9c547cSRui Paulo 	hdr = radius_msg_get_hdr(msg);
18135b9c547cSRui Paulo 
18145b9c547cSRui Paulo 	resp = radius_msg_new(RADIUS_CODE_ACCOUNTING_RESPONSE, hdr->identifier);
18155b9c547cSRui Paulo 	if (resp == NULL)
18165b9c547cSRui Paulo 		goto fail;
18175b9c547cSRui Paulo 
18185b9c547cSRui Paulo 	radius_msg_finish_acct_resp(resp, (u8 *) client->shared_secret,
18195b9c547cSRui Paulo 				    client->shared_secret_len,
18205b9c547cSRui Paulo 				    hdr->authenticator);
18215b9c547cSRui Paulo 
18225b9c547cSRui Paulo 	RADIUS_DEBUG("Reply to %s:%d", abuf, from_port);
18235b9c547cSRui Paulo 	if (wpa_debug_level <= MSG_MSGDUMP) {
18245b9c547cSRui Paulo 		radius_msg_dump(resp);
18255b9c547cSRui Paulo 	}
18265b9c547cSRui Paulo 	rbuf = radius_msg_get_buf(resp);
18275b9c547cSRui Paulo 	data->counters.acct_responses++;
18285b9c547cSRui Paulo 	client->counters.acct_responses++;
18295b9c547cSRui Paulo 	res = sendto(data->acct_sock, wpabuf_head(rbuf), wpabuf_len(rbuf), 0,
18305b9c547cSRui Paulo 		     (struct sockaddr *) &from.ss, fromlen);
18315b9c547cSRui Paulo 	if (res < 0) {
18325b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
18335b9c547cSRui Paulo 			   strerror(errno));
18345b9c547cSRui Paulo 	}
18355b9c547cSRui Paulo 
18365b9c547cSRui Paulo fail:
18375b9c547cSRui Paulo 	radius_msg_free(resp);
18385b9c547cSRui Paulo 	radius_msg_free(msg);
18395b9c547cSRui Paulo 	os_free(buf);
18405b9c547cSRui Paulo }
18415b9c547cSRui Paulo 
18425b9c547cSRui Paulo 
18433157ba21SRui Paulo static int radius_server_disable_pmtu_discovery(int s)
18443157ba21SRui Paulo {
18453157ba21SRui Paulo 	int r = -1;
18463157ba21SRui Paulo #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
18473157ba21SRui Paulo 	/* Turn off Path MTU discovery on IPv4/UDP sockets. */
18483157ba21SRui Paulo 	int action = IP_PMTUDISC_DONT;
18493157ba21SRui Paulo 	r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
18503157ba21SRui Paulo 		       sizeof(action));
18513157ba21SRui Paulo 	if (r == -1)
18523157ba21SRui Paulo 		wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
18533157ba21SRui Paulo 			   "%s", strerror(errno));
18543157ba21SRui Paulo #endif
18553157ba21SRui Paulo 	return r;
18563157ba21SRui Paulo }
18573157ba21SRui Paulo 
18583157ba21SRui Paulo 
185939beb93cSSam Leffler static int radius_server_open_socket(int port)
186039beb93cSSam Leffler {
186139beb93cSSam Leffler 	int s;
186239beb93cSSam Leffler 	struct sockaddr_in addr;
186339beb93cSSam Leffler 
186439beb93cSSam Leffler 	s = socket(PF_INET, SOCK_DGRAM, 0);
186539beb93cSSam Leffler 	if (s < 0) {
18665b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: socket: %s", strerror(errno));
186739beb93cSSam Leffler 		return -1;
186839beb93cSSam Leffler 	}
186939beb93cSSam Leffler 
18703157ba21SRui Paulo 	radius_server_disable_pmtu_discovery(s);
18713157ba21SRui Paulo 
187239beb93cSSam Leffler 	os_memset(&addr, 0, sizeof(addr));
187339beb93cSSam Leffler 	addr.sin_family = AF_INET;
187439beb93cSSam Leffler 	addr.sin_port = htons(port);
187539beb93cSSam Leffler 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
18765b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
187739beb93cSSam Leffler 		close(s);
187839beb93cSSam Leffler 		return -1;
187939beb93cSSam Leffler 	}
188039beb93cSSam Leffler 
188139beb93cSSam Leffler 	return s;
188239beb93cSSam Leffler }
188339beb93cSSam Leffler 
188439beb93cSSam Leffler 
188539beb93cSSam Leffler #ifdef CONFIG_IPV6
188639beb93cSSam Leffler static int radius_server_open_socket6(int port)
188739beb93cSSam Leffler {
188839beb93cSSam Leffler 	int s;
188939beb93cSSam Leffler 	struct sockaddr_in6 addr;
189039beb93cSSam Leffler 
189139beb93cSSam Leffler 	s = socket(PF_INET6, SOCK_DGRAM, 0);
189239beb93cSSam Leffler 	if (s < 0) {
18935b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: socket[IPv6]: %s",
18945b9c547cSRui Paulo 			   strerror(errno));
189539beb93cSSam Leffler 		return -1;
189639beb93cSSam Leffler 	}
189739beb93cSSam Leffler 
189839beb93cSSam Leffler 	os_memset(&addr, 0, sizeof(addr));
189939beb93cSSam Leffler 	addr.sin6_family = AF_INET6;
190039beb93cSSam Leffler 	os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
190139beb93cSSam Leffler 	addr.sin6_port = htons(port);
190239beb93cSSam Leffler 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
19035b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
190439beb93cSSam Leffler 		close(s);
190539beb93cSSam Leffler 		return -1;
190639beb93cSSam Leffler 	}
190739beb93cSSam Leffler 
190839beb93cSSam Leffler 	return s;
190939beb93cSSam Leffler }
191039beb93cSSam Leffler #endif /* CONFIG_IPV6 */
191139beb93cSSam Leffler 
191239beb93cSSam Leffler 
191339beb93cSSam Leffler static void radius_server_free_sessions(struct radius_server_data *data,
191439beb93cSSam Leffler 					struct radius_session *sessions)
191539beb93cSSam Leffler {
191639beb93cSSam Leffler 	struct radius_session *session, *prev;
191739beb93cSSam Leffler 
191839beb93cSSam Leffler 	session = sessions;
191939beb93cSSam Leffler 	while (session) {
192039beb93cSSam Leffler 		prev = session;
192139beb93cSSam Leffler 		session = session->next;
192239beb93cSSam Leffler 		radius_server_session_free(data, prev);
192339beb93cSSam Leffler 	}
192439beb93cSSam Leffler }
192539beb93cSSam Leffler 
192639beb93cSSam Leffler 
192739beb93cSSam Leffler static void radius_server_free_clients(struct radius_server_data *data,
192839beb93cSSam Leffler 				       struct radius_client *clients)
192939beb93cSSam Leffler {
193039beb93cSSam Leffler 	struct radius_client *client, *prev;
193139beb93cSSam Leffler 
193239beb93cSSam Leffler 	client = clients;
193339beb93cSSam Leffler 	while (client) {
193439beb93cSSam Leffler 		prev = client;
193539beb93cSSam Leffler 		client = client->next;
193639beb93cSSam Leffler 
193739beb93cSSam Leffler 		radius_server_free_sessions(data, prev->sessions);
193839beb93cSSam Leffler 		os_free(prev->shared_secret);
1939*85732ac8SCy Schubert 		radius_msg_free(prev->pending_dac_coa_req);
1940*85732ac8SCy Schubert 		radius_msg_free(prev->pending_dac_disconnect_req);
194139beb93cSSam Leffler 		os_free(prev);
194239beb93cSSam Leffler 	}
194339beb93cSSam Leffler }
194439beb93cSSam Leffler 
194539beb93cSSam Leffler 
194639beb93cSSam Leffler static struct radius_client *
194739beb93cSSam Leffler radius_server_read_clients(const char *client_file, int ipv6)
194839beb93cSSam Leffler {
194939beb93cSSam Leffler 	FILE *f;
195039beb93cSSam Leffler 	const int buf_size = 1024;
195139beb93cSSam Leffler 	char *buf, *pos;
195239beb93cSSam Leffler 	struct radius_client *clients, *tail, *entry;
195339beb93cSSam Leffler 	int line = 0, mask, failed = 0, i;
195439beb93cSSam Leffler 	struct in_addr addr;
195539beb93cSSam Leffler #ifdef CONFIG_IPV6
195639beb93cSSam Leffler 	struct in6_addr addr6;
195739beb93cSSam Leffler #endif /* CONFIG_IPV6 */
195839beb93cSSam Leffler 	unsigned int val;
195939beb93cSSam Leffler 
196039beb93cSSam Leffler 	f = fopen(client_file, "r");
196139beb93cSSam Leffler 	if (f == NULL) {
196239beb93cSSam Leffler 		RADIUS_ERROR("Could not open client file '%s'", client_file);
196339beb93cSSam Leffler 		return NULL;
196439beb93cSSam Leffler 	}
196539beb93cSSam Leffler 
196639beb93cSSam Leffler 	buf = os_malloc(buf_size);
196739beb93cSSam Leffler 	if (buf == NULL) {
196839beb93cSSam Leffler 		fclose(f);
196939beb93cSSam Leffler 		return NULL;
197039beb93cSSam Leffler 	}
197139beb93cSSam Leffler 
197239beb93cSSam Leffler 	clients = tail = NULL;
197339beb93cSSam Leffler 	while (fgets(buf, buf_size, f)) {
197439beb93cSSam Leffler 		/* Configuration file format:
197539beb93cSSam Leffler 		 * 192.168.1.0/24 secret
197639beb93cSSam Leffler 		 * 192.168.1.2 secret
197739beb93cSSam Leffler 		 * fe80::211:22ff:fe33:4455/64 secretipv6
197839beb93cSSam Leffler 		 */
197939beb93cSSam Leffler 		line++;
198039beb93cSSam Leffler 		buf[buf_size - 1] = '\0';
198139beb93cSSam Leffler 		pos = buf;
198239beb93cSSam Leffler 		while (*pos != '\0' && *pos != '\n')
198339beb93cSSam Leffler 			pos++;
198439beb93cSSam Leffler 		if (*pos == '\n')
198539beb93cSSam Leffler 			*pos = '\0';
198639beb93cSSam Leffler 		if (*buf == '\0' || *buf == '#')
198739beb93cSSam Leffler 			continue;
198839beb93cSSam Leffler 
198939beb93cSSam Leffler 		pos = buf;
199039beb93cSSam Leffler 		while ((*pos >= '0' && *pos <= '9') || *pos == '.' ||
199139beb93cSSam Leffler 		       (*pos >= 'a' && *pos <= 'f') || *pos == ':' ||
199239beb93cSSam Leffler 		       (*pos >= 'A' && *pos <= 'F')) {
199339beb93cSSam Leffler 			pos++;
199439beb93cSSam Leffler 		}
199539beb93cSSam Leffler 
199639beb93cSSam Leffler 		if (*pos == '\0') {
199739beb93cSSam Leffler 			failed = 1;
199839beb93cSSam Leffler 			break;
199939beb93cSSam Leffler 		}
200039beb93cSSam Leffler 
200139beb93cSSam Leffler 		if (*pos == '/') {
200239beb93cSSam Leffler 			char *end;
200339beb93cSSam Leffler 			*pos++ = '\0';
200439beb93cSSam Leffler 			mask = strtol(pos, &end, 10);
200539beb93cSSam Leffler 			if ((pos == end) ||
200639beb93cSSam Leffler 			    (mask < 0 || mask > (ipv6 ? 128 : 32))) {
200739beb93cSSam Leffler 				failed = 1;
200839beb93cSSam Leffler 				break;
200939beb93cSSam Leffler 			}
201039beb93cSSam Leffler 			pos = end;
201139beb93cSSam Leffler 		} else {
201239beb93cSSam Leffler 			mask = ipv6 ? 128 : 32;
201339beb93cSSam Leffler 			*pos++ = '\0';
201439beb93cSSam Leffler 		}
201539beb93cSSam Leffler 
201639beb93cSSam Leffler 		if (!ipv6 && inet_aton(buf, &addr) == 0) {
201739beb93cSSam Leffler 			failed = 1;
201839beb93cSSam Leffler 			break;
201939beb93cSSam Leffler 		}
202039beb93cSSam Leffler #ifdef CONFIG_IPV6
202139beb93cSSam Leffler 		if (ipv6 && inet_pton(AF_INET6, buf, &addr6) <= 0) {
202239beb93cSSam Leffler 			if (inet_pton(AF_INET, buf, &addr) <= 0) {
202339beb93cSSam Leffler 				failed = 1;
202439beb93cSSam Leffler 				break;
202539beb93cSSam Leffler 			}
202639beb93cSSam Leffler 			/* Convert IPv4 address to IPv6 */
202739beb93cSSam Leffler 			if (mask <= 32)
202839beb93cSSam Leffler 				mask += (128 - 32);
202939beb93cSSam Leffler 			os_memset(addr6.s6_addr, 0, 10);
203039beb93cSSam Leffler 			addr6.s6_addr[10] = 0xff;
203139beb93cSSam Leffler 			addr6.s6_addr[11] = 0xff;
203239beb93cSSam Leffler 			os_memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr,
203339beb93cSSam Leffler 				  4);
203439beb93cSSam Leffler 		}
203539beb93cSSam Leffler #endif /* CONFIG_IPV6 */
203639beb93cSSam Leffler 
203739beb93cSSam Leffler 		while (*pos == ' ' || *pos == '\t') {
203839beb93cSSam Leffler 			pos++;
203939beb93cSSam Leffler 		}
204039beb93cSSam Leffler 
204139beb93cSSam Leffler 		if (*pos == '\0') {
204239beb93cSSam Leffler 			failed = 1;
204339beb93cSSam Leffler 			break;
204439beb93cSSam Leffler 		}
204539beb93cSSam Leffler 
204639beb93cSSam Leffler 		entry = os_zalloc(sizeof(*entry));
204739beb93cSSam Leffler 		if (entry == NULL) {
204839beb93cSSam Leffler 			failed = 1;
204939beb93cSSam Leffler 			break;
205039beb93cSSam Leffler 		}
205139beb93cSSam Leffler 		entry->shared_secret = os_strdup(pos);
205239beb93cSSam Leffler 		if (entry->shared_secret == NULL) {
205339beb93cSSam Leffler 			failed = 1;
205439beb93cSSam Leffler 			os_free(entry);
205539beb93cSSam Leffler 			break;
205639beb93cSSam Leffler 		}
205739beb93cSSam Leffler 		entry->shared_secret_len = os_strlen(entry->shared_secret);
205839beb93cSSam Leffler 		if (!ipv6) {
20595b9c547cSRui Paulo 			entry->addr.s_addr = addr.s_addr;
206039beb93cSSam Leffler 			val = 0;
206139beb93cSSam Leffler 			for (i = 0; i < mask; i++)
206239beb93cSSam Leffler 				val |= 1 << (31 - i);
206339beb93cSSam Leffler 			entry->mask.s_addr = htonl(val);
206439beb93cSSam Leffler 		}
206539beb93cSSam Leffler #ifdef CONFIG_IPV6
206639beb93cSSam Leffler 		if (ipv6) {
206739beb93cSSam Leffler 			int offset = mask / 8;
206839beb93cSSam Leffler 
206939beb93cSSam Leffler 			os_memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16);
207039beb93cSSam Leffler 			os_memset(entry->mask6.s6_addr, 0xff, offset);
207139beb93cSSam Leffler 			val = 0;
207239beb93cSSam Leffler 			for (i = 0; i < (mask % 8); i++)
207339beb93cSSam Leffler 				val |= 1 << (7 - i);
207439beb93cSSam Leffler 			if (offset < 16)
207539beb93cSSam Leffler 				entry->mask6.s6_addr[offset] = val;
207639beb93cSSam Leffler 		}
207739beb93cSSam Leffler #endif /* CONFIG_IPV6 */
207839beb93cSSam Leffler 
207939beb93cSSam Leffler 		if (tail == NULL) {
208039beb93cSSam Leffler 			clients = tail = entry;
208139beb93cSSam Leffler 		} else {
208239beb93cSSam Leffler 			tail->next = entry;
208339beb93cSSam Leffler 			tail = entry;
208439beb93cSSam Leffler 		}
208539beb93cSSam Leffler 	}
208639beb93cSSam Leffler 
208739beb93cSSam Leffler 	if (failed) {
208839beb93cSSam Leffler 		RADIUS_ERROR("Invalid line %d in '%s'", line, client_file);
208939beb93cSSam Leffler 		radius_server_free_clients(NULL, clients);
209039beb93cSSam Leffler 		clients = NULL;
209139beb93cSSam Leffler 	}
209239beb93cSSam Leffler 
209339beb93cSSam Leffler 	os_free(buf);
209439beb93cSSam Leffler 	fclose(f);
209539beb93cSSam Leffler 
209639beb93cSSam Leffler 	return clients;
209739beb93cSSam Leffler }
209839beb93cSSam Leffler 
209939beb93cSSam Leffler 
2100e28a4053SRui Paulo /**
2101e28a4053SRui Paulo  * radius_server_init - Initialize RADIUS server
2102e28a4053SRui Paulo  * @conf: Configuration for the RADIUS server
2103e28a4053SRui Paulo  * Returns: Pointer to private RADIUS server context or %NULL on failure
2104e28a4053SRui Paulo  *
2105e28a4053SRui Paulo  * This initializes a RADIUS server instance and returns a context pointer that
2106e28a4053SRui Paulo  * will be used in other calls to the RADIUS server module. The server can be
2107e28a4053SRui Paulo  * deinitialize by calling radius_server_deinit().
2108e28a4053SRui Paulo  */
210939beb93cSSam Leffler struct radius_server_data *
211039beb93cSSam Leffler radius_server_init(struct radius_server_conf *conf)
211139beb93cSSam Leffler {
211239beb93cSSam Leffler 	struct radius_server_data *data;
211339beb93cSSam Leffler 
211439beb93cSSam Leffler #ifndef CONFIG_IPV6
211539beb93cSSam Leffler 	if (conf->ipv6) {
21165b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "RADIUS server compiled without IPv6 support");
211739beb93cSSam Leffler 		return NULL;
211839beb93cSSam Leffler 	}
211939beb93cSSam Leffler #endif /* CONFIG_IPV6 */
212039beb93cSSam Leffler 
212139beb93cSSam Leffler 	data = os_zalloc(sizeof(*data));
212239beb93cSSam Leffler 	if (data == NULL)
212339beb93cSSam Leffler 		return NULL;
212439beb93cSSam Leffler 
21255b9c547cSRui Paulo 	dl_list_init(&data->erp_keys);
21265b9c547cSRui Paulo 	os_get_reltime(&data->start_time);
212739beb93cSSam Leffler 	data->conf_ctx = conf->conf_ctx;
212839beb93cSSam Leffler 	data->eap_sim_db_priv = conf->eap_sim_db_priv;
212939beb93cSSam Leffler 	data->ssl_ctx = conf->ssl_ctx;
2130e28a4053SRui Paulo 	data->msg_ctx = conf->msg_ctx;
213139beb93cSSam Leffler 	data->ipv6 = conf->ipv6;
213239beb93cSSam Leffler 	if (conf->pac_opaque_encr_key) {
213339beb93cSSam Leffler 		data->pac_opaque_encr_key = os_malloc(16);
2134325151a3SRui Paulo 		if (data->pac_opaque_encr_key) {
2135325151a3SRui Paulo 			os_memcpy(data->pac_opaque_encr_key,
2136325151a3SRui Paulo 				  conf->pac_opaque_encr_key, 16);
2137325151a3SRui Paulo 		}
213839beb93cSSam Leffler 	}
213939beb93cSSam Leffler 	if (conf->eap_fast_a_id) {
214039beb93cSSam Leffler 		data->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
214139beb93cSSam Leffler 		if (data->eap_fast_a_id) {
214239beb93cSSam Leffler 			os_memcpy(data->eap_fast_a_id, conf->eap_fast_a_id,
214339beb93cSSam Leffler 				  conf->eap_fast_a_id_len);
214439beb93cSSam Leffler 			data->eap_fast_a_id_len = conf->eap_fast_a_id_len;
214539beb93cSSam Leffler 		}
214639beb93cSSam Leffler 	}
214739beb93cSSam Leffler 	if (conf->eap_fast_a_id_info)
214839beb93cSSam Leffler 		data->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info);
214939beb93cSSam Leffler 	data->eap_fast_prov = conf->eap_fast_prov;
215039beb93cSSam Leffler 	data->pac_key_lifetime = conf->pac_key_lifetime;
215139beb93cSSam Leffler 	data->pac_key_refresh_time = conf->pac_key_refresh_time;
215239beb93cSSam Leffler 	data->get_eap_user = conf->get_eap_user;
215339beb93cSSam Leffler 	data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
215439beb93cSSam Leffler 	data->tnc = conf->tnc;
215539beb93cSSam Leffler 	data->wps = conf->wps;
2156f05cddf9SRui Paulo 	data->pwd_group = conf->pwd_group;
21575b9c547cSRui Paulo 	data->server_id = conf->server_id;
215839beb93cSSam Leffler 	if (conf->eap_req_id_text) {
215939beb93cSSam Leffler 		data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len);
216039beb93cSSam Leffler 		if (data->eap_req_id_text) {
216139beb93cSSam Leffler 			os_memcpy(data->eap_req_id_text, conf->eap_req_id_text,
216239beb93cSSam Leffler 				  conf->eap_req_id_text_len);
216339beb93cSSam Leffler 			data->eap_req_id_text_len = conf->eap_req_id_text_len;
216439beb93cSSam Leffler 		}
216539beb93cSSam Leffler 	}
21665b9c547cSRui Paulo 	data->erp = conf->erp;
21675b9c547cSRui Paulo 	data->erp_domain = conf->erp_domain;
2168325151a3SRui Paulo 	data->tls_session_lifetime = conf->tls_session_lifetime;
2169*85732ac8SCy Schubert 	data->tls_flags = conf->tls_flags;
21705b9c547cSRui Paulo 
21715b9c547cSRui Paulo 	if (conf->subscr_remediation_url) {
21725b9c547cSRui Paulo 		data->subscr_remediation_url =
21735b9c547cSRui Paulo 			os_strdup(conf->subscr_remediation_url);
21745b9c547cSRui Paulo 	}
21755b9c547cSRui Paulo 	data->subscr_remediation_method = conf->subscr_remediation_method;
21765b9c547cSRui Paulo 
2177*85732ac8SCy Schubert 	if (conf->t_c_server_url)
2178*85732ac8SCy Schubert 		data->t_c_server_url = os_strdup(conf->t_c_server_url);
2179*85732ac8SCy Schubert 
21805b9c547cSRui Paulo #ifdef CONFIG_SQLITE
21815b9c547cSRui Paulo 	if (conf->sqlite_file) {
21825b9c547cSRui Paulo 		if (sqlite3_open(conf->sqlite_file, &data->db)) {
21835b9c547cSRui Paulo 			RADIUS_ERROR("Could not open SQLite file '%s'",
21845b9c547cSRui Paulo 				     conf->sqlite_file);
21855b9c547cSRui Paulo 			radius_server_deinit(data);
21865b9c547cSRui Paulo 			return NULL;
21875b9c547cSRui Paulo 		}
21885b9c547cSRui Paulo 	}
21895b9c547cSRui Paulo #endif /* CONFIG_SQLITE */
219039beb93cSSam Leffler 
2191f05cddf9SRui Paulo #ifdef CONFIG_RADIUS_TEST
2192f05cddf9SRui Paulo 	if (conf->dump_msk_file)
2193f05cddf9SRui Paulo 		data->dump_msk_file = os_strdup(conf->dump_msk_file);
2194f05cddf9SRui Paulo #endif /* CONFIG_RADIUS_TEST */
2195f05cddf9SRui Paulo 
219639beb93cSSam Leffler 	data->clients = radius_server_read_clients(conf->client_file,
219739beb93cSSam Leffler 						   conf->ipv6);
219839beb93cSSam Leffler 	if (data->clients == NULL) {
21995b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "No RADIUS clients configured");
220039beb93cSSam Leffler 		radius_server_deinit(data);
220139beb93cSSam Leffler 		return NULL;
220239beb93cSSam Leffler 	}
220339beb93cSSam Leffler 
220439beb93cSSam Leffler #ifdef CONFIG_IPV6
220539beb93cSSam Leffler 	if (conf->ipv6)
220639beb93cSSam Leffler 		data->auth_sock = radius_server_open_socket6(conf->auth_port);
220739beb93cSSam Leffler 	else
220839beb93cSSam Leffler #endif /* CONFIG_IPV6 */
220939beb93cSSam Leffler 	data->auth_sock = radius_server_open_socket(conf->auth_port);
221039beb93cSSam Leffler 	if (data->auth_sock < 0) {
22115b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS authentication server");
221239beb93cSSam Leffler 		radius_server_deinit(data);
221339beb93cSSam Leffler 		return NULL;
221439beb93cSSam Leffler 	}
221539beb93cSSam Leffler 	if (eloop_register_read_sock(data->auth_sock,
221639beb93cSSam Leffler 				     radius_server_receive_auth,
221739beb93cSSam Leffler 				     data, NULL)) {
221839beb93cSSam Leffler 		radius_server_deinit(data);
221939beb93cSSam Leffler 		return NULL;
222039beb93cSSam Leffler 	}
222139beb93cSSam Leffler 
22225b9c547cSRui Paulo 	if (conf->acct_port) {
22235b9c547cSRui Paulo #ifdef CONFIG_IPV6
22245b9c547cSRui Paulo 		if (conf->ipv6)
22255b9c547cSRui Paulo 			data->acct_sock = radius_server_open_socket6(
22265b9c547cSRui Paulo 				conf->acct_port);
22275b9c547cSRui Paulo 		else
22285b9c547cSRui Paulo #endif /* CONFIG_IPV6 */
22295b9c547cSRui Paulo 		data->acct_sock = radius_server_open_socket(conf->acct_port);
22305b9c547cSRui Paulo 		if (data->acct_sock < 0) {
22315b9c547cSRui Paulo 			wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS accounting server");
22325b9c547cSRui Paulo 			radius_server_deinit(data);
22335b9c547cSRui Paulo 			return NULL;
22345b9c547cSRui Paulo 		}
22355b9c547cSRui Paulo 		if (eloop_register_read_sock(data->acct_sock,
22365b9c547cSRui Paulo 					     radius_server_receive_acct,
22375b9c547cSRui Paulo 					     data, NULL)) {
22385b9c547cSRui Paulo 			radius_server_deinit(data);
22395b9c547cSRui Paulo 			return NULL;
22405b9c547cSRui Paulo 		}
22415b9c547cSRui Paulo 	} else {
22425b9c547cSRui Paulo 		data->acct_sock = -1;
22435b9c547cSRui Paulo 	}
22445b9c547cSRui Paulo 
224539beb93cSSam Leffler 	return data;
224639beb93cSSam Leffler }
224739beb93cSSam Leffler 
224839beb93cSSam Leffler 
2249e28a4053SRui Paulo /**
22505b9c547cSRui Paulo  * radius_server_erp_flush - Flush all ERP keys
22515b9c547cSRui Paulo  * @data: RADIUS server context from radius_server_init()
22525b9c547cSRui Paulo  */
22535b9c547cSRui Paulo void radius_server_erp_flush(struct radius_server_data *data)
22545b9c547cSRui Paulo {
22555b9c547cSRui Paulo 	struct eap_server_erp_key *erp;
22565b9c547cSRui Paulo 
22575b9c547cSRui Paulo 	if (data == NULL)
22585b9c547cSRui Paulo 		return;
22595b9c547cSRui Paulo 	while ((erp = dl_list_first(&data->erp_keys, struct eap_server_erp_key,
22605b9c547cSRui Paulo 				    list)) != NULL) {
22615b9c547cSRui Paulo 		dl_list_del(&erp->list);
22625b9c547cSRui Paulo 		bin_clear_free(erp, sizeof(*erp));
22635b9c547cSRui Paulo 	}
22645b9c547cSRui Paulo }
22655b9c547cSRui Paulo 
22665b9c547cSRui Paulo 
22675b9c547cSRui Paulo /**
2268e28a4053SRui Paulo  * radius_server_deinit - Deinitialize RADIUS server
2269e28a4053SRui Paulo  * @data: RADIUS server context from radius_server_init()
2270e28a4053SRui Paulo  */
227139beb93cSSam Leffler void radius_server_deinit(struct radius_server_data *data)
227239beb93cSSam Leffler {
227339beb93cSSam Leffler 	if (data == NULL)
227439beb93cSSam Leffler 		return;
227539beb93cSSam Leffler 
227639beb93cSSam Leffler 	if (data->auth_sock >= 0) {
227739beb93cSSam Leffler 		eloop_unregister_read_sock(data->auth_sock);
227839beb93cSSam Leffler 		close(data->auth_sock);
227939beb93cSSam Leffler 	}
228039beb93cSSam Leffler 
22815b9c547cSRui Paulo 	if (data->acct_sock >= 0) {
22825b9c547cSRui Paulo 		eloop_unregister_read_sock(data->acct_sock);
22835b9c547cSRui Paulo 		close(data->acct_sock);
22845b9c547cSRui Paulo 	}
22855b9c547cSRui Paulo 
228639beb93cSSam Leffler 	radius_server_free_clients(data, data->clients);
228739beb93cSSam Leffler 
228839beb93cSSam Leffler 	os_free(data->pac_opaque_encr_key);
228939beb93cSSam Leffler 	os_free(data->eap_fast_a_id);
229039beb93cSSam Leffler 	os_free(data->eap_fast_a_id_info);
229139beb93cSSam Leffler 	os_free(data->eap_req_id_text);
2292f05cddf9SRui Paulo #ifdef CONFIG_RADIUS_TEST
2293f05cddf9SRui Paulo 	os_free(data->dump_msk_file);
2294f05cddf9SRui Paulo #endif /* CONFIG_RADIUS_TEST */
22955b9c547cSRui Paulo 	os_free(data->subscr_remediation_url);
2296*85732ac8SCy Schubert 	os_free(data->t_c_server_url);
22975b9c547cSRui Paulo 
22985b9c547cSRui Paulo #ifdef CONFIG_SQLITE
22995b9c547cSRui Paulo 	if (data->db)
23005b9c547cSRui Paulo 		sqlite3_close(data->db);
23015b9c547cSRui Paulo #endif /* CONFIG_SQLITE */
23025b9c547cSRui Paulo 
23035b9c547cSRui Paulo 	radius_server_erp_flush(data);
23045b9c547cSRui Paulo 
230539beb93cSSam Leffler 	os_free(data);
230639beb93cSSam Leffler }
230739beb93cSSam Leffler 
230839beb93cSSam Leffler 
2309e28a4053SRui Paulo /**
2310e28a4053SRui Paulo  * radius_server_get_mib - Get RADIUS server MIB information
2311e28a4053SRui Paulo  * @data: RADIUS server context from radius_server_init()
2312e28a4053SRui Paulo  * @buf: Buffer for returning the MIB data in text format
2313e28a4053SRui Paulo  * @buflen: buf length in octets
2314e28a4053SRui Paulo  * Returns: Number of octets written into buf
2315e28a4053SRui Paulo  */
231639beb93cSSam Leffler int radius_server_get_mib(struct radius_server_data *data, char *buf,
231739beb93cSSam Leffler 			  size_t buflen)
231839beb93cSSam Leffler {
231939beb93cSSam Leffler 	int ret, uptime;
232039beb93cSSam Leffler 	unsigned int idx;
232139beb93cSSam Leffler 	char *end, *pos;
23225b9c547cSRui Paulo 	struct os_reltime now;
232339beb93cSSam Leffler 	struct radius_client *cli;
232439beb93cSSam Leffler 
232539beb93cSSam Leffler 	/* RFC 2619 - RADIUS Authentication Server MIB */
232639beb93cSSam Leffler 
232739beb93cSSam Leffler 	if (data == NULL || buflen == 0)
232839beb93cSSam Leffler 		return 0;
232939beb93cSSam Leffler 
233039beb93cSSam Leffler 	pos = buf;
233139beb93cSSam Leffler 	end = buf + buflen;
233239beb93cSSam Leffler 
23335b9c547cSRui Paulo 	os_get_reltime(&now);
233439beb93cSSam Leffler 	uptime = (now.sec - data->start_time.sec) * 100 +
233539beb93cSSam Leffler 		((now.usec - data->start_time.usec) / 10000) % 100;
233639beb93cSSam Leffler 	ret = os_snprintf(pos, end - pos,
233739beb93cSSam Leffler 			  "RADIUS-AUTH-SERVER-MIB\n"
233839beb93cSSam Leffler 			  "radiusAuthServIdent=hostapd\n"
233939beb93cSSam Leffler 			  "radiusAuthServUpTime=%d\n"
234039beb93cSSam Leffler 			  "radiusAuthServResetTime=0\n"
234139beb93cSSam Leffler 			  "radiusAuthServConfigReset=4\n",
234239beb93cSSam Leffler 			  uptime);
23435b9c547cSRui Paulo 	if (os_snprintf_error(end - pos, ret)) {
234439beb93cSSam Leffler 		*pos = '\0';
234539beb93cSSam Leffler 		return pos - buf;
234639beb93cSSam Leffler 	}
234739beb93cSSam Leffler 	pos += ret;
234839beb93cSSam Leffler 
234939beb93cSSam Leffler 	ret = os_snprintf(pos, end - pos,
235039beb93cSSam Leffler 			  "radiusAuthServTotalAccessRequests=%u\n"
235139beb93cSSam Leffler 			  "radiusAuthServTotalInvalidRequests=%u\n"
235239beb93cSSam Leffler 			  "radiusAuthServTotalDupAccessRequests=%u\n"
235339beb93cSSam Leffler 			  "radiusAuthServTotalAccessAccepts=%u\n"
235439beb93cSSam Leffler 			  "radiusAuthServTotalAccessRejects=%u\n"
235539beb93cSSam Leffler 			  "radiusAuthServTotalAccessChallenges=%u\n"
235639beb93cSSam Leffler 			  "radiusAuthServTotalMalformedAccessRequests=%u\n"
235739beb93cSSam Leffler 			  "radiusAuthServTotalBadAuthenticators=%u\n"
235839beb93cSSam Leffler 			  "radiusAuthServTotalPacketsDropped=%u\n"
23595b9c547cSRui Paulo 			  "radiusAuthServTotalUnknownTypes=%u\n"
23605b9c547cSRui Paulo 			  "radiusAccServTotalRequests=%u\n"
23615b9c547cSRui Paulo 			  "radiusAccServTotalInvalidRequests=%u\n"
23625b9c547cSRui Paulo 			  "radiusAccServTotalResponses=%u\n"
23635b9c547cSRui Paulo 			  "radiusAccServTotalMalformedRequests=%u\n"
23645b9c547cSRui Paulo 			  "radiusAccServTotalBadAuthenticators=%u\n"
23655b9c547cSRui Paulo 			  "radiusAccServTotalUnknownTypes=%u\n",
236639beb93cSSam Leffler 			  data->counters.access_requests,
236739beb93cSSam Leffler 			  data->counters.invalid_requests,
236839beb93cSSam Leffler 			  data->counters.dup_access_requests,
236939beb93cSSam Leffler 			  data->counters.access_accepts,
237039beb93cSSam Leffler 			  data->counters.access_rejects,
237139beb93cSSam Leffler 			  data->counters.access_challenges,
237239beb93cSSam Leffler 			  data->counters.malformed_access_requests,
237339beb93cSSam Leffler 			  data->counters.bad_authenticators,
237439beb93cSSam Leffler 			  data->counters.packets_dropped,
23755b9c547cSRui Paulo 			  data->counters.unknown_types,
23765b9c547cSRui Paulo 			  data->counters.acct_requests,
23775b9c547cSRui Paulo 			  data->counters.invalid_acct_requests,
23785b9c547cSRui Paulo 			  data->counters.acct_responses,
23795b9c547cSRui Paulo 			  data->counters.malformed_acct_requests,
23805b9c547cSRui Paulo 			  data->counters.acct_bad_authenticators,
23815b9c547cSRui Paulo 			  data->counters.unknown_acct_types);
23825b9c547cSRui Paulo 	if (os_snprintf_error(end - pos, ret)) {
238339beb93cSSam Leffler 		*pos = '\0';
238439beb93cSSam Leffler 		return pos - buf;
238539beb93cSSam Leffler 	}
238639beb93cSSam Leffler 	pos += ret;
238739beb93cSSam Leffler 
238839beb93cSSam Leffler 	for (cli = data->clients, idx = 0; cli; cli = cli->next, idx++) {
238939beb93cSSam Leffler 		char abuf[50], mbuf[50];
239039beb93cSSam Leffler #ifdef CONFIG_IPV6
239139beb93cSSam Leffler 		if (data->ipv6) {
239239beb93cSSam Leffler 			if (inet_ntop(AF_INET6, &cli->addr6, abuf,
239339beb93cSSam Leffler 				      sizeof(abuf)) == NULL)
239439beb93cSSam Leffler 				abuf[0] = '\0';
23955b9c547cSRui Paulo 			if (inet_ntop(AF_INET6, &cli->mask6, mbuf,
239639beb93cSSam Leffler 				      sizeof(mbuf)) == NULL)
239739beb93cSSam Leffler 				mbuf[0] = '\0';
239839beb93cSSam Leffler 		}
239939beb93cSSam Leffler #endif /* CONFIG_IPV6 */
240039beb93cSSam Leffler 		if (!data->ipv6) {
240139beb93cSSam Leffler 			os_strlcpy(abuf, inet_ntoa(cli->addr), sizeof(abuf));
240239beb93cSSam Leffler 			os_strlcpy(mbuf, inet_ntoa(cli->mask), sizeof(mbuf));
240339beb93cSSam Leffler 		}
240439beb93cSSam Leffler 
240539beb93cSSam Leffler 		ret = os_snprintf(pos, end - pos,
240639beb93cSSam Leffler 				  "radiusAuthClientIndex=%u\n"
240739beb93cSSam Leffler 				  "radiusAuthClientAddress=%s/%s\n"
240839beb93cSSam Leffler 				  "radiusAuthServAccessRequests=%u\n"
240939beb93cSSam Leffler 				  "radiusAuthServDupAccessRequests=%u\n"
241039beb93cSSam Leffler 				  "radiusAuthServAccessAccepts=%u\n"
241139beb93cSSam Leffler 				  "radiusAuthServAccessRejects=%u\n"
241239beb93cSSam Leffler 				  "radiusAuthServAccessChallenges=%u\n"
241339beb93cSSam Leffler 				  "radiusAuthServMalformedAccessRequests=%u\n"
241439beb93cSSam Leffler 				  "radiusAuthServBadAuthenticators=%u\n"
241539beb93cSSam Leffler 				  "radiusAuthServPacketsDropped=%u\n"
24165b9c547cSRui Paulo 				  "radiusAuthServUnknownTypes=%u\n"
24175b9c547cSRui Paulo 				  "radiusAccServTotalRequests=%u\n"
24185b9c547cSRui Paulo 				  "radiusAccServTotalInvalidRequests=%u\n"
24195b9c547cSRui Paulo 				  "radiusAccServTotalResponses=%u\n"
24205b9c547cSRui Paulo 				  "radiusAccServTotalMalformedRequests=%u\n"
24215b9c547cSRui Paulo 				  "radiusAccServTotalBadAuthenticators=%u\n"
24225b9c547cSRui Paulo 				  "radiusAccServTotalUnknownTypes=%u\n",
242339beb93cSSam Leffler 				  idx,
242439beb93cSSam Leffler 				  abuf, mbuf,
242539beb93cSSam Leffler 				  cli->counters.access_requests,
242639beb93cSSam Leffler 				  cli->counters.dup_access_requests,
242739beb93cSSam Leffler 				  cli->counters.access_accepts,
242839beb93cSSam Leffler 				  cli->counters.access_rejects,
242939beb93cSSam Leffler 				  cli->counters.access_challenges,
243039beb93cSSam Leffler 				  cli->counters.malformed_access_requests,
243139beb93cSSam Leffler 				  cli->counters.bad_authenticators,
243239beb93cSSam Leffler 				  cli->counters.packets_dropped,
24335b9c547cSRui Paulo 				  cli->counters.unknown_types,
24345b9c547cSRui Paulo 				  cli->counters.acct_requests,
24355b9c547cSRui Paulo 				  cli->counters.invalid_acct_requests,
24365b9c547cSRui Paulo 				  cli->counters.acct_responses,
24375b9c547cSRui Paulo 				  cli->counters.malformed_acct_requests,
24385b9c547cSRui Paulo 				  cli->counters.acct_bad_authenticators,
24395b9c547cSRui Paulo 				  cli->counters.unknown_acct_types);
24405b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret)) {
244139beb93cSSam Leffler 			*pos = '\0';
244239beb93cSSam Leffler 			return pos - buf;
244339beb93cSSam Leffler 		}
244439beb93cSSam Leffler 		pos += ret;
244539beb93cSSam Leffler 	}
244639beb93cSSam Leffler 
244739beb93cSSam Leffler 	return pos - buf;
244839beb93cSSam Leffler }
244939beb93cSSam Leffler 
245039beb93cSSam Leffler 
245139beb93cSSam Leffler static int radius_server_get_eap_user(void *ctx, const u8 *identity,
245239beb93cSSam Leffler 				      size_t identity_len, int phase2,
245339beb93cSSam Leffler 				      struct eap_user *user)
245439beb93cSSam Leffler {
245539beb93cSSam Leffler 	struct radius_session *sess = ctx;
245639beb93cSSam Leffler 	struct radius_server_data *data = sess->server;
24575b9c547cSRui Paulo 	int ret;
245839beb93cSSam Leffler 
24595b9c547cSRui Paulo 	ret = data->get_eap_user(data->conf_ctx, identity, identity_len,
246039beb93cSSam Leffler 				 phase2, user);
24615b9c547cSRui Paulo 	if (ret == 0 && user) {
24625b9c547cSRui Paulo 		sess->accept_attr = user->accept_attr;
24635b9c547cSRui Paulo 		sess->remediation = user->remediation;
24645b9c547cSRui Paulo 		sess->macacl = user->macacl;
2465*85732ac8SCy Schubert 		sess->t_c_timestamp = user->t_c_timestamp;
24665b9c547cSRui Paulo 	}
2467325151a3SRui Paulo 
2468325151a3SRui Paulo 	if (ret) {
2469325151a3SRui Paulo 		RADIUS_DEBUG("%s: User-Name not found from user database",
2470325151a3SRui Paulo 			     __func__);
2471325151a3SRui Paulo 	}
2472325151a3SRui Paulo 
24735b9c547cSRui Paulo 	return ret;
247439beb93cSSam Leffler }
247539beb93cSSam Leffler 
247639beb93cSSam Leffler 
247739beb93cSSam Leffler static const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len)
247839beb93cSSam Leffler {
247939beb93cSSam Leffler 	struct radius_session *sess = ctx;
248039beb93cSSam Leffler 	struct radius_server_data *data = sess->server;
248139beb93cSSam Leffler 	*len = data->eap_req_id_text_len;
248239beb93cSSam Leffler 	return data->eap_req_id_text;
248339beb93cSSam Leffler }
248439beb93cSSam Leffler 
248539beb93cSSam Leffler 
24865b9c547cSRui Paulo static void radius_server_log_msg(void *ctx, const char *msg)
24875b9c547cSRui Paulo {
24885b9c547cSRui Paulo 	struct radius_session *sess = ctx;
24895b9c547cSRui Paulo 	srv_log(sess, "EAP: %s", msg);
24905b9c547cSRui Paulo }
24915b9c547cSRui Paulo 
24925b9c547cSRui Paulo 
24935b9c547cSRui Paulo #ifdef CONFIG_ERP
24945b9c547cSRui Paulo 
24955b9c547cSRui Paulo static const char * radius_server_get_erp_domain(void *ctx)
24965b9c547cSRui Paulo {
24975b9c547cSRui Paulo 	struct radius_session *sess = ctx;
24985b9c547cSRui Paulo 	struct radius_server_data *data = sess->server;
24995b9c547cSRui Paulo 
25005b9c547cSRui Paulo 	return data->erp_domain;
25015b9c547cSRui Paulo }
25025b9c547cSRui Paulo 
25035b9c547cSRui Paulo 
25045b9c547cSRui Paulo static struct eap_server_erp_key *
25055b9c547cSRui Paulo radius_server_erp_get_key(void *ctx, const char *keyname)
25065b9c547cSRui Paulo {
25075b9c547cSRui Paulo 	struct radius_session *sess = ctx;
25085b9c547cSRui Paulo 	struct radius_server_data *data = sess->server;
25095b9c547cSRui Paulo 	struct eap_server_erp_key *erp;
25105b9c547cSRui Paulo 
25115b9c547cSRui Paulo 	dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key,
25125b9c547cSRui Paulo 			 list) {
25135b9c547cSRui Paulo 		if (os_strcmp(erp->keyname_nai, keyname) == 0)
25145b9c547cSRui Paulo 			return erp;
25155b9c547cSRui Paulo 	}
25165b9c547cSRui Paulo 
25175b9c547cSRui Paulo 	return NULL;
25185b9c547cSRui Paulo }
25195b9c547cSRui Paulo 
25205b9c547cSRui Paulo 
25215b9c547cSRui Paulo static int radius_server_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
25225b9c547cSRui Paulo {
25235b9c547cSRui Paulo 	struct radius_session *sess = ctx;
25245b9c547cSRui Paulo 	struct radius_server_data *data = sess->server;
25255b9c547cSRui Paulo 
25265b9c547cSRui Paulo 	dl_list_add(&data->erp_keys, &erp->list);
25275b9c547cSRui Paulo 	return 0;
25285b9c547cSRui Paulo }
25295b9c547cSRui Paulo 
25305b9c547cSRui Paulo #endif /* CONFIG_ERP */
25315b9c547cSRui Paulo 
25325b9c547cSRui Paulo 
2533325151a3SRui Paulo static const struct eapol_callbacks radius_server_eapol_cb =
253439beb93cSSam Leffler {
253539beb93cSSam Leffler 	.get_eap_user = radius_server_get_eap_user,
253639beb93cSSam Leffler 	.get_eap_req_id_text = radius_server_get_eap_req_id_text,
25375b9c547cSRui Paulo 	.log_msg = radius_server_log_msg,
25385b9c547cSRui Paulo #ifdef CONFIG_ERP
25395b9c547cSRui Paulo 	.get_erp_send_reauth_start = NULL,
25405b9c547cSRui Paulo 	.get_erp_domain = radius_server_get_erp_domain,
25415b9c547cSRui Paulo 	.erp_get_key = radius_server_erp_get_key,
25425b9c547cSRui Paulo 	.erp_add_key = radius_server_erp_add_key,
25435b9c547cSRui Paulo #endif /* CONFIG_ERP */
254439beb93cSSam Leffler };
254539beb93cSSam Leffler 
254639beb93cSSam Leffler 
2547e28a4053SRui Paulo /**
2548e28a4053SRui Paulo  * radius_server_eap_pending_cb - Pending EAP data notification
2549e28a4053SRui Paulo  * @data: RADIUS server context from radius_server_init()
2550e28a4053SRui Paulo  * @ctx: Pending EAP context pointer
2551e28a4053SRui Paulo  *
2552e28a4053SRui Paulo  * This function is used to notify EAP server module that a pending operation
2553e28a4053SRui Paulo  * has been completed and processing of the EAP session can proceed.
2554e28a4053SRui Paulo  */
255539beb93cSSam Leffler void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx)
255639beb93cSSam Leffler {
255739beb93cSSam Leffler 	struct radius_client *cli;
255839beb93cSSam Leffler 	struct radius_session *s, *sess = NULL;
255939beb93cSSam Leffler 	struct radius_msg *msg;
256039beb93cSSam Leffler 
256139beb93cSSam Leffler 	if (data == NULL)
256239beb93cSSam Leffler 		return;
256339beb93cSSam Leffler 
256439beb93cSSam Leffler 	for (cli = data->clients; cli; cli = cli->next) {
256539beb93cSSam Leffler 		for (s = cli->sessions; s; s = s->next) {
256639beb93cSSam Leffler 			if (s->eap == ctx && s->last_msg) {
256739beb93cSSam Leffler 				sess = s;
256839beb93cSSam Leffler 				break;
256939beb93cSSam Leffler 			}
257039beb93cSSam Leffler 		}
257139beb93cSSam Leffler 		if (sess)
257239beb93cSSam Leffler 			break;
257339beb93cSSam Leffler 	}
257439beb93cSSam Leffler 
257539beb93cSSam Leffler 	if (sess == NULL) {
257639beb93cSSam Leffler 		RADIUS_DEBUG("No session matched callback ctx");
257739beb93cSSam Leffler 		return;
257839beb93cSSam Leffler 	}
257939beb93cSSam Leffler 
258039beb93cSSam Leffler 	msg = sess->last_msg;
258139beb93cSSam Leffler 	sess->last_msg = NULL;
258239beb93cSSam Leffler 	eap_sm_pending_cb(sess->eap);
258339beb93cSSam Leffler 	if (radius_server_request(data, msg,
258439beb93cSSam Leffler 				  (struct sockaddr *) &sess->last_from,
258539beb93cSSam Leffler 				  sess->last_fromlen, cli,
258639beb93cSSam Leffler 				  sess->last_from_addr,
258739beb93cSSam Leffler 				  sess->last_from_port, sess) == -2)
258839beb93cSSam Leffler 		return; /* msg was stored with the session */
258939beb93cSSam Leffler 
259039beb93cSSam Leffler 	radius_msg_free(msg);
259139beb93cSSam Leffler }
2592*85732ac8SCy Schubert 
2593*85732ac8SCy Schubert 
2594*85732ac8SCy Schubert #ifdef CONFIG_SQLITE
2595*85732ac8SCy Schubert 
2596*85732ac8SCy Schubert struct db_session_fields {
2597*85732ac8SCy Schubert 	char *identity;
2598*85732ac8SCy Schubert 	char *nas;
2599*85732ac8SCy Schubert 	int hs20_t_c_filtering;
2600*85732ac8SCy Schubert 	int waiting_coa_ack;
2601*85732ac8SCy Schubert 	int coa_ack_received;
2602*85732ac8SCy Schubert };
2603*85732ac8SCy Schubert 
2604*85732ac8SCy Schubert 
2605*85732ac8SCy Schubert static int get_db_session_fields(void *ctx, int argc, char *argv[], char *col[])
2606*85732ac8SCy Schubert {
2607*85732ac8SCy Schubert 	struct db_session_fields *fields = ctx;
2608*85732ac8SCy Schubert 	int i;
2609*85732ac8SCy Schubert 
2610*85732ac8SCy Schubert 	for (i = 0; i < argc; i++) {
2611*85732ac8SCy Schubert 		if (!argv[i])
2612*85732ac8SCy Schubert 			continue;
2613*85732ac8SCy Schubert 
2614*85732ac8SCy Schubert 		RADIUS_DEBUG("Session DB: %s=%s", col[i], argv[i]);
2615*85732ac8SCy Schubert 
2616*85732ac8SCy Schubert 		if (os_strcmp(col[i], "identity") == 0) {
2617*85732ac8SCy Schubert 			os_free(fields->identity);
2618*85732ac8SCy Schubert 			fields->identity = os_strdup(argv[i]);
2619*85732ac8SCy Schubert 		} else if (os_strcmp(col[i], "nas") == 0) {
2620*85732ac8SCy Schubert 			os_free(fields->nas);
2621*85732ac8SCy Schubert 			fields->nas = os_strdup(argv[i]);
2622*85732ac8SCy Schubert 		} else if (os_strcmp(col[i], "hs20_t_c_filtering") == 0) {
2623*85732ac8SCy Schubert 			fields->hs20_t_c_filtering = atoi(argv[i]);
2624*85732ac8SCy Schubert 		} else if (os_strcmp(col[i], "waiting_coa_ack") == 0) {
2625*85732ac8SCy Schubert 			fields->waiting_coa_ack = atoi(argv[i]);
2626*85732ac8SCy Schubert 		} else if (os_strcmp(col[i], "coa_ack_received") == 0) {
2627*85732ac8SCy Schubert 			fields->coa_ack_received = atoi(argv[i]);
2628*85732ac8SCy Schubert 		}
2629*85732ac8SCy Schubert 	}
2630*85732ac8SCy Schubert 
2631*85732ac8SCy Schubert 	return 0;
2632*85732ac8SCy Schubert }
2633*85732ac8SCy Schubert 
2634*85732ac8SCy Schubert 
2635*85732ac8SCy Schubert static void free_db_session_fields(struct db_session_fields *fields)
2636*85732ac8SCy Schubert {
2637*85732ac8SCy Schubert 	os_free(fields->identity);
2638*85732ac8SCy Schubert 	fields->identity = NULL;
2639*85732ac8SCy Schubert 	os_free(fields->nas);
2640*85732ac8SCy Schubert 	fields->nas = NULL;
2641*85732ac8SCy Schubert }
2642*85732ac8SCy Schubert 
2643*85732ac8SCy Schubert #endif /* CONFIG_SQLITE */
2644*85732ac8SCy Schubert 
2645*85732ac8SCy Schubert 
2646*85732ac8SCy Schubert int radius_server_dac_request(struct radius_server_data *data, const char *req)
2647*85732ac8SCy Schubert {
2648*85732ac8SCy Schubert #ifdef CONFIG_SQLITE
2649*85732ac8SCy Schubert 	char *sql;
2650*85732ac8SCy Schubert 	int res;
2651*85732ac8SCy Schubert 	int disconnect;
2652*85732ac8SCy Schubert 	const char *pos = req;
2653*85732ac8SCy Schubert 	u8 addr[ETH_ALEN];
2654*85732ac8SCy Schubert 	char addrtxt[3 * ETH_ALEN];
2655*85732ac8SCy Schubert 	int t_c_clear = 0;
2656*85732ac8SCy Schubert 	struct db_session_fields fields;
2657*85732ac8SCy Schubert 	struct sockaddr_in das;
2658*85732ac8SCy Schubert 	struct radius_client *client;
2659*85732ac8SCy Schubert 	struct radius_msg *msg;
2660*85732ac8SCy Schubert 	struct wpabuf *buf;
2661*85732ac8SCy Schubert 	u8 identifier;
2662*85732ac8SCy Schubert 	struct os_time now;
2663*85732ac8SCy Schubert 
2664*85732ac8SCy Schubert 	if (!data)
2665*85732ac8SCy Schubert 		return -1;
2666*85732ac8SCy Schubert 
2667*85732ac8SCy Schubert 	/* req: <disconnect|coa> <MAC Address> [t_c_clear] */
2668*85732ac8SCy Schubert 
2669*85732ac8SCy Schubert 	if (os_strncmp(pos, "disconnect ", 11) == 0) {
2670*85732ac8SCy Schubert 		disconnect = 1;
2671*85732ac8SCy Schubert 		pos += 11;
2672*85732ac8SCy Schubert 	} else if (os_strncmp(req, "coa ", 4) == 0) {
2673*85732ac8SCy Schubert 		disconnect = 0;
2674*85732ac8SCy Schubert 		pos += 4;
2675*85732ac8SCy Schubert 	} else {
2676*85732ac8SCy Schubert 		return -1;
2677*85732ac8SCy Schubert 	}
2678*85732ac8SCy Schubert 
2679*85732ac8SCy Schubert 	if (hwaddr_aton(pos, addr))
2680*85732ac8SCy Schubert 		return -1;
2681*85732ac8SCy Schubert 	pos = os_strchr(pos, ' ');
2682*85732ac8SCy Schubert 	if (pos) {
2683*85732ac8SCy Schubert 		if (os_strstr(pos, "t_c_clear"))
2684*85732ac8SCy Schubert 			t_c_clear = 1;
2685*85732ac8SCy Schubert 	}
2686*85732ac8SCy Schubert 
2687*85732ac8SCy Schubert 	if (!disconnect && !t_c_clear) {
2688*85732ac8SCy Schubert 		RADIUS_ERROR("DAC request for CoA without any authorization change");
2689*85732ac8SCy Schubert 		return -1;
2690*85732ac8SCy Schubert 	}
2691*85732ac8SCy Schubert 
2692*85732ac8SCy Schubert 	if (!data->db) {
2693*85732ac8SCy Schubert 		RADIUS_ERROR("SQLite database not in use");
2694*85732ac8SCy Schubert 		return -1;
2695*85732ac8SCy Schubert 	}
2696*85732ac8SCy Schubert 
2697*85732ac8SCy Schubert 	os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, MAC2STR(addr));
2698*85732ac8SCy Schubert 
2699*85732ac8SCy Schubert 	sql = sqlite3_mprintf("SELECT * FROM current_sessions WHERE mac_addr=%Q",
2700*85732ac8SCy Schubert 			      addrtxt);
2701*85732ac8SCy Schubert 	if (!sql)
2702*85732ac8SCy Schubert 		return -1;
2703*85732ac8SCy Schubert 
2704*85732ac8SCy Schubert 	os_memset(&fields, 0, sizeof(fields));
2705*85732ac8SCy Schubert 	res = sqlite3_exec(data->db, sql, get_db_session_fields, &fields, NULL);
2706*85732ac8SCy Schubert 	sqlite3_free(sql);
2707*85732ac8SCy Schubert 	if (res != SQLITE_OK) {
2708*85732ac8SCy Schubert 		RADIUS_ERROR("Failed to find matching current_sessions entry from sqlite database: %s",
2709*85732ac8SCy Schubert 			     sqlite3_errmsg(data->db));
2710*85732ac8SCy Schubert 		free_db_session_fields(&fields);
2711*85732ac8SCy Schubert 		return -1;
2712*85732ac8SCy Schubert 	}
2713*85732ac8SCy Schubert 
2714*85732ac8SCy Schubert 	if (!fields.nas) {
2715*85732ac8SCy Schubert 		RADIUS_ERROR("No NAS information found from current_sessions");
2716*85732ac8SCy Schubert 		free_db_session_fields(&fields);
2717*85732ac8SCy Schubert 		return -1;
2718*85732ac8SCy Schubert 	}
2719*85732ac8SCy Schubert 
2720*85732ac8SCy Schubert 	os_memset(&das, 0, sizeof(das));
2721*85732ac8SCy Schubert 	das.sin_family = AF_INET;
2722*85732ac8SCy Schubert 	das.sin_addr.s_addr = inet_addr(fields.nas);
2723*85732ac8SCy Schubert 	das.sin_port = htons(3799);
2724*85732ac8SCy Schubert 
2725*85732ac8SCy Schubert 	free_db_session_fields(&fields);
2726*85732ac8SCy Schubert 
2727*85732ac8SCy Schubert 	client = radius_server_get_client(data, &das.sin_addr, 0);
2728*85732ac8SCy Schubert 	if (!client) {
2729*85732ac8SCy Schubert 		RADIUS_ERROR("No NAS information available to protect the packet");
2730*85732ac8SCy Schubert 		return -1;
2731*85732ac8SCy Schubert 	}
2732*85732ac8SCy Schubert 
2733*85732ac8SCy Schubert 	identifier = client->next_dac_identifier++;
2734*85732ac8SCy Schubert 
2735*85732ac8SCy Schubert 	msg = radius_msg_new(disconnect ? RADIUS_CODE_DISCONNECT_REQUEST :
2736*85732ac8SCy Schubert 			     RADIUS_CODE_COA_REQUEST, identifier);
2737*85732ac8SCy Schubert 	if (!msg)
2738*85732ac8SCy Schubert 		return -1;
2739*85732ac8SCy Schubert 
2740*85732ac8SCy Schubert 	os_snprintf(addrtxt, sizeof(addrtxt), RADIUS_802_1X_ADDR_FORMAT,
2741*85732ac8SCy Schubert 		    MAC2STR(addr));
2742*85732ac8SCy Schubert 	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
2743*85732ac8SCy Schubert 				 (u8 *) addrtxt, os_strlen(addrtxt))) {
2744*85732ac8SCy Schubert 		RADIUS_ERROR("Could not add Calling-Station-Id");
2745*85732ac8SCy Schubert 		radius_msg_free(msg);
2746*85732ac8SCy Schubert 		return -1;
2747*85732ac8SCy Schubert 	}
2748*85732ac8SCy Schubert 
2749*85732ac8SCy Schubert 	if (!disconnect && t_c_clear) {
2750*85732ac8SCy Schubert 		u8 val[4] = { 0x00, 0x00, 0x00, 0x00 }; /* E=0 */
2751*85732ac8SCy Schubert 
2752*85732ac8SCy Schubert 		if (!radius_msg_add_wfa(
2753*85732ac8SCy Schubert 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING,
2754*85732ac8SCy Schubert 			    val, sizeof(val))) {
2755*85732ac8SCy Schubert 			RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering");
2756*85732ac8SCy Schubert 			radius_msg_free(msg);
2757*85732ac8SCy Schubert 			return -1;
2758*85732ac8SCy Schubert 		}
2759*85732ac8SCy Schubert 	}
2760*85732ac8SCy Schubert 
2761*85732ac8SCy Schubert 	os_get_time(&now);
2762*85732ac8SCy Schubert 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
2763*85732ac8SCy Schubert 				       now.sec)) {
2764*85732ac8SCy Schubert 		RADIUS_ERROR("Failed to add Event-Timestamp attribute");
2765*85732ac8SCy Schubert 		radius_msg_free(msg);
2766*85732ac8SCy Schubert 		return -1;
2767*85732ac8SCy Schubert 	}
2768*85732ac8SCy Schubert 
2769*85732ac8SCy Schubert 	radius_msg_finish_acct(msg, (u8 *) client->shared_secret,
2770*85732ac8SCy Schubert 			       client->shared_secret_len);
2771*85732ac8SCy Schubert 
2772*85732ac8SCy Schubert 	if (wpa_debug_level <= MSG_MSGDUMP)
2773*85732ac8SCy Schubert 		radius_msg_dump(msg);
2774*85732ac8SCy Schubert 
2775*85732ac8SCy Schubert 	buf = radius_msg_get_buf(msg);
2776*85732ac8SCy Schubert 	if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
2777*85732ac8SCy Schubert 		   (struct sockaddr *) &das, sizeof(das)) < 0) {
2778*85732ac8SCy Schubert 		RADIUS_ERROR("Failed to send packet - sendto: %s",
2779*85732ac8SCy Schubert 			     strerror(errno));
2780*85732ac8SCy Schubert 		radius_msg_free(msg);
2781*85732ac8SCy Schubert 		return -1;
2782*85732ac8SCy Schubert 	}
2783*85732ac8SCy Schubert 
2784*85732ac8SCy Schubert 	if (disconnect) {
2785*85732ac8SCy Schubert 		radius_msg_free(client->pending_dac_disconnect_req);
2786*85732ac8SCy Schubert 		client->pending_dac_disconnect_req = msg;
2787*85732ac8SCy Schubert 		client->pending_dac_disconnect_id = identifier;
2788*85732ac8SCy Schubert 		os_memcpy(client->pending_dac_disconnect_addr, addr, ETH_ALEN);
2789*85732ac8SCy Schubert 	} else {
2790*85732ac8SCy Schubert 		radius_msg_free(client->pending_dac_coa_req);
2791*85732ac8SCy Schubert 		client->pending_dac_coa_req = msg;
2792*85732ac8SCy Schubert 		client->pending_dac_coa_id = identifier;
2793*85732ac8SCy Schubert 		os_memcpy(client->pending_dac_coa_addr, addr, ETH_ALEN);
2794*85732ac8SCy Schubert 	}
2795*85732ac8SCy Schubert 
2796*85732ac8SCy Schubert 	return 0;
2797*85732ac8SCy Schubert #else /* CONFIG_SQLITE */
2798*85732ac8SCy Schubert 	return -1;
2799*85732ac8SCy Schubert #endif /* CONFIG_SQLITE */
2800*85732ac8SCy Schubert }
2801