xref: /freebsd/contrib/wpa/src/radius/radius_server.c (revision c1d255d3ffdbe447de3ab875bf4e7d7accc5bfc5)
139beb93cSSam Leffler /*
2e28a4053SRui Paulo  * RADIUS authentication server
34bc52338SCy Schubert  * Copyright (c) 2005-2009, 2011-2019, 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 /**
2985732ac8SCy Schubert  * RADIUS_SESSION_MAINTAIN - Completed session expiration timeout in seconds
3085732ac8SCy Schubert  */
3185732ac8SCy Schubert #define RADIUS_SESSION_MAINTAIN 5
3285732ac8SCy Schubert 
3385732ac8SCy Schubert /**
34e28a4053SRui Paulo  * RADIUS_MAX_SESSION - Maximum number of active sessions
35e28a4053SRui Paulo  */
3685732ac8SCy Schubert #define RADIUS_MAX_SESSION 1000
37e28a4053SRui Paulo 
38325151a3SRui Paulo static const struct eapol_callbacks radius_server_eapol_cb;
3939beb93cSSam Leffler 
4039beb93cSSam Leffler struct radius_client;
4139beb93cSSam Leffler struct radius_server_data;
4239beb93cSSam Leffler 
43e28a4053SRui Paulo /**
44e28a4053SRui Paulo  * struct radius_server_counters - RADIUS server statistics counters
45e28a4053SRui Paulo  */
4639beb93cSSam Leffler struct radius_server_counters {
4739beb93cSSam Leffler 	u32 access_requests;
4839beb93cSSam Leffler 	u32 invalid_requests;
4939beb93cSSam Leffler 	u32 dup_access_requests;
5039beb93cSSam Leffler 	u32 access_accepts;
5139beb93cSSam Leffler 	u32 access_rejects;
5239beb93cSSam Leffler 	u32 access_challenges;
5339beb93cSSam Leffler 	u32 malformed_access_requests;
5439beb93cSSam Leffler 	u32 bad_authenticators;
5539beb93cSSam Leffler 	u32 packets_dropped;
5639beb93cSSam Leffler 	u32 unknown_types;
575b9c547cSRui Paulo 
585b9c547cSRui Paulo 	u32 acct_requests;
595b9c547cSRui Paulo 	u32 invalid_acct_requests;
605b9c547cSRui Paulo 	u32 acct_responses;
615b9c547cSRui Paulo 	u32 malformed_acct_requests;
625b9c547cSRui Paulo 	u32 acct_bad_authenticators;
635b9c547cSRui Paulo 	u32 unknown_acct_types;
6439beb93cSSam Leffler };
6539beb93cSSam Leffler 
66e28a4053SRui Paulo /**
67e28a4053SRui Paulo  * struct radius_session - Internal RADIUS server data for a session
68e28a4053SRui Paulo  */
6939beb93cSSam Leffler struct radius_session {
7039beb93cSSam Leffler 	struct radius_session *next;
7139beb93cSSam Leffler 	struct radius_client *client;
7239beb93cSSam Leffler 	struct radius_server_data *server;
7339beb93cSSam Leffler 	unsigned int sess_id;
7439beb93cSSam Leffler 	struct eap_sm *eap;
7539beb93cSSam Leffler 	struct eap_eapol_interface *eap_if;
765b9c547cSRui Paulo 	char *username; /* from User-Name attribute */
775b9c547cSRui Paulo 	char *nas_ip;
7885732ac8SCy Schubert 	u8 mac_addr[ETH_ALEN]; /* from Calling-Station-Id attribute */
7939beb93cSSam Leffler 
8039beb93cSSam Leffler 	struct radius_msg *last_msg;
8139beb93cSSam Leffler 	char *last_from_addr;
8239beb93cSSam Leffler 	int last_from_port;
8339beb93cSSam Leffler 	struct sockaddr_storage last_from;
8439beb93cSSam Leffler 	socklen_t last_fromlen;
8539beb93cSSam Leffler 	u8 last_identifier;
8639beb93cSSam Leffler 	struct radius_msg *last_reply;
8739beb93cSSam Leffler 	u8 last_authenticator[16];
885b9c547cSRui Paulo 
895b9c547cSRui Paulo 	unsigned int remediation:1;
905b9c547cSRui Paulo 	unsigned int macacl:1;
9185732ac8SCy Schubert 	unsigned int t_c_filtering:1;
925b9c547cSRui Paulo 
935b9c547cSRui Paulo 	struct hostapd_radius_attr *accept_attr;
9485732ac8SCy Schubert 
9585732ac8SCy Schubert 	u32 t_c_timestamp; /* Last read T&C timestamp from user DB */
9639beb93cSSam Leffler };
9739beb93cSSam Leffler 
98e28a4053SRui Paulo /**
99e28a4053SRui Paulo  * struct radius_client - Internal RADIUS server data for a client
100e28a4053SRui Paulo  */
10139beb93cSSam Leffler struct radius_client {
10239beb93cSSam Leffler 	struct radius_client *next;
10339beb93cSSam Leffler 	struct in_addr addr;
10439beb93cSSam Leffler 	struct in_addr mask;
10539beb93cSSam Leffler #ifdef CONFIG_IPV6
10639beb93cSSam Leffler 	struct in6_addr addr6;
10739beb93cSSam Leffler 	struct in6_addr mask6;
10839beb93cSSam Leffler #endif /* CONFIG_IPV6 */
10939beb93cSSam Leffler 	char *shared_secret;
11039beb93cSSam Leffler 	int shared_secret_len;
11139beb93cSSam Leffler 	struct radius_session *sessions;
11239beb93cSSam Leffler 	struct radius_server_counters counters;
11385732ac8SCy Schubert 
11485732ac8SCy Schubert 	u8 next_dac_identifier;
11585732ac8SCy Schubert 	struct radius_msg *pending_dac_coa_req;
11685732ac8SCy Schubert 	u8 pending_dac_coa_id;
11785732ac8SCy Schubert 	u8 pending_dac_coa_addr[ETH_ALEN];
11885732ac8SCy Schubert 	struct radius_msg *pending_dac_disconnect_req;
11985732ac8SCy Schubert 	u8 pending_dac_disconnect_id;
12085732ac8SCy Schubert 	u8 pending_dac_disconnect_addr[ETH_ALEN];
12139beb93cSSam Leffler };
12239beb93cSSam Leffler 
123e28a4053SRui Paulo /**
124e28a4053SRui Paulo  * struct radius_server_data - Internal RADIUS server data
125e28a4053SRui Paulo  */
12639beb93cSSam Leffler struct radius_server_data {
127e28a4053SRui Paulo 	/**
128e28a4053SRui Paulo 	 * auth_sock - Socket for RADIUS authentication messages
129e28a4053SRui Paulo 	 */
13039beb93cSSam Leffler 	int auth_sock;
131e28a4053SRui Paulo 
132e28a4053SRui Paulo 	/**
1335b9c547cSRui Paulo 	 * acct_sock - Socket for RADIUS accounting messages
1345b9c547cSRui Paulo 	 */
1355b9c547cSRui Paulo 	int acct_sock;
1365b9c547cSRui Paulo 
1375b9c547cSRui Paulo 	/**
138e28a4053SRui Paulo 	 * clients - List of authorized RADIUS clients
139e28a4053SRui Paulo 	 */
14039beb93cSSam Leffler 	struct radius_client *clients;
141e28a4053SRui Paulo 
142e28a4053SRui Paulo 	/**
143e28a4053SRui Paulo 	 * next_sess_id - Next session identifier
144e28a4053SRui Paulo 	 */
14539beb93cSSam Leffler 	unsigned int next_sess_id;
146e28a4053SRui Paulo 
147e28a4053SRui Paulo 	/**
148e28a4053SRui Paulo 	 * conf_ctx - Context pointer for callbacks
149e28a4053SRui Paulo 	 *
150e28a4053SRui Paulo 	 * This is used as the ctx argument in get_eap_user() calls.
151e28a4053SRui Paulo 	 */
15239beb93cSSam Leffler 	void *conf_ctx;
153e28a4053SRui Paulo 
154e28a4053SRui Paulo 	/**
155e28a4053SRui Paulo 	 * num_sess - Number of active sessions
156e28a4053SRui Paulo 	 */
15739beb93cSSam Leffler 	int num_sess;
158e28a4053SRui Paulo 
1595b9c547cSRui Paulo 	const char *erp_domain;
1605b9c547cSRui Paulo 
1615b9c547cSRui Paulo 	struct dl_list erp_keys; /* struct eap_server_erp_key */
1625b9c547cSRui Paulo 
163e28a4053SRui Paulo 	/**
164e28a4053SRui Paulo 	 * ipv6 - Whether to enable IPv6 support in the RADIUS server
165e28a4053SRui Paulo 	 */
16639beb93cSSam Leffler 	int ipv6;
167e28a4053SRui Paulo 
168e28a4053SRui Paulo 	/**
169e28a4053SRui Paulo 	 * start_time - Timestamp of server start
170e28a4053SRui Paulo 	 */
1715b9c547cSRui Paulo 	struct os_reltime start_time;
172e28a4053SRui Paulo 
173e28a4053SRui Paulo 	/**
174e28a4053SRui Paulo 	 * counters - Statistics counters for server operations
175e28a4053SRui Paulo 	 *
176e28a4053SRui Paulo 	 * These counters are the sum over all clients.
177e28a4053SRui Paulo 	 */
17839beb93cSSam Leffler 	struct radius_server_counters counters;
179e28a4053SRui Paulo 
180e28a4053SRui Paulo 	/**
181e28a4053SRui Paulo 	 * get_eap_user - Callback for fetching EAP user information
182e28a4053SRui Paulo 	 * @ctx: Context data from conf_ctx
183e28a4053SRui Paulo 	 * @identity: User identity
184e28a4053SRui Paulo 	 * @identity_len: identity buffer length in octets
185e28a4053SRui Paulo 	 * @phase2: Whether this is for Phase 2 identity
186e28a4053SRui Paulo 	 * @user: Data structure for filling in the user information
187e28a4053SRui Paulo 	 * Returns: 0 on success, -1 on failure
188e28a4053SRui Paulo 	 *
189e28a4053SRui Paulo 	 * This is used to fetch information from user database. The callback
190e28a4053SRui Paulo 	 * will fill in information about allowed EAP methods and the user
191e28a4053SRui Paulo 	 * password. The password field will be an allocated copy of the
192e28a4053SRui Paulo 	 * password data and RADIUS server will free it after use.
193e28a4053SRui Paulo 	 */
19439beb93cSSam Leffler 	int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
19539beb93cSSam Leffler 			    int phase2, struct eap_user *user);
196e28a4053SRui Paulo 
197e28a4053SRui Paulo 	/**
198e28a4053SRui Paulo 	 * eap_req_id_text - Optional data for EAP-Request/Identity
199e28a4053SRui Paulo 	 *
200e28a4053SRui Paulo 	 * This can be used to configure an optional, displayable message that
201e28a4053SRui Paulo 	 * will be sent in EAP-Request/Identity. This string can contain an
202e28a4053SRui Paulo 	 * ASCII-0 character (nul) to separate network infromation per RFC
203e28a4053SRui Paulo 	 * 4284. The actual string length is explicit provided in
204e28a4053SRui Paulo 	 * eap_req_id_text_len since nul character will not be used as a string
205e28a4053SRui Paulo 	 * terminator.
206e28a4053SRui Paulo 	 */
20739beb93cSSam Leffler 	char *eap_req_id_text;
208e28a4053SRui Paulo 
209e28a4053SRui Paulo 	/**
210e28a4053SRui Paulo 	 * eap_req_id_text_len - Length of eap_req_id_text buffer in octets
211e28a4053SRui Paulo 	 */
21239beb93cSSam Leffler 	size_t eap_req_id_text_len;
213e28a4053SRui Paulo 
214f05cddf9SRui Paulo #ifdef CONFIG_RADIUS_TEST
215f05cddf9SRui Paulo 	char *dump_msk_file;
216f05cddf9SRui Paulo #endif /* CONFIG_RADIUS_TEST */
2175b9c547cSRui Paulo 
2185b9c547cSRui Paulo 	char *subscr_remediation_url;
2195b9c547cSRui Paulo 	u8 subscr_remediation_method;
2204bc52338SCy Schubert 	char *hs20_sim_provisioning_url;
2215b9c547cSRui Paulo 
22285732ac8SCy Schubert 	char *t_c_server_url;
22385732ac8SCy Schubert 
2245b9c547cSRui Paulo #ifdef CONFIG_SQLITE
2255b9c547cSRui Paulo 	sqlite3 *db;
2265b9c547cSRui Paulo #endif /* CONFIG_SQLITE */
227*c1d255d3SCy Schubert 
228*c1d255d3SCy Schubert 	const struct eap_config *eap_cfg;
22939beb93cSSam Leffler };
23039beb93cSSam Leffler 
23139beb93cSSam Leffler 
23239beb93cSSam Leffler #define RADIUS_DEBUG(args...) \
23339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RADIUS SRV: " args)
23439beb93cSSam Leffler #define RADIUS_ERROR(args...) \
23539beb93cSSam Leffler wpa_printf(MSG_ERROR, "RADIUS SRV: " args)
23639beb93cSSam Leffler #define RADIUS_DUMP(args...) \
23739beb93cSSam Leffler wpa_hexdump(MSG_MSGDUMP, "RADIUS SRV: " args)
23839beb93cSSam Leffler #define RADIUS_DUMP_ASCII(args...) \
23939beb93cSSam Leffler wpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args)
24039beb93cSSam Leffler 
24139beb93cSSam Leffler 
24239beb93cSSam Leffler static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx);
2433157ba21SRui Paulo static void radius_server_session_remove_timeout(void *eloop_ctx,
2443157ba21SRui Paulo 						 void *timeout_ctx);
24539beb93cSSam Leffler 
2464bc52338SCy Schubert #ifdef CONFIG_SQLITE
2474bc52338SCy Schubert #ifdef CONFIG_HS20
2484bc52338SCy Schubert 
2494bc52338SCy Schubert static int db_table_exists(sqlite3 *db, const char *name)
2504bc52338SCy Schubert {
2514bc52338SCy Schubert 	char cmd[128];
2524bc52338SCy Schubert 
2534bc52338SCy Schubert 	os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
2544bc52338SCy Schubert 	return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
2554bc52338SCy Schubert }
2564bc52338SCy Schubert 
2574bc52338SCy Schubert 
2584bc52338SCy Schubert static int db_table_create_sim_provisioning(sqlite3 *db)
2594bc52338SCy Schubert {
2604bc52338SCy Schubert 	char *err = NULL;
2614bc52338SCy Schubert 	const char *sql =
2624bc52338SCy Schubert 		"CREATE TABLE sim_provisioning("
2634bc52338SCy Schubert 		" mobile_identifier_hash TEXT PRIMARY KEY,"
2644bc52338SCy Schubert 		" imsi TEXT,"
2654bc52338SCy Schubert 		" mac_addr TEXT,"
2664bc52338SCy Schubert 		" eap_method TEXT,"
2674bc52338SCy Schubert 		" timestamp TEXT"
2684bc52338SCy Schubert 		");";
2694bc52338SCy Schubert 
2704bc52338SCy Schubert 	RADIUS_DEBUG("Adding database table for SIM provisioning information");
2714bc52338SCy Schubert 	if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
2724bc52338SCy Schubert 		RADIUS_ERROR("SQLite error: %s", err);
2734bc52338SCy Schubert 		sqlite3_free(err);
2744bc52338SCy Schubert 		return -1;
2754bc52338SCy Schubert 	}
2764bc52338SCy Schubert 
2774bc52338SCy Schubert 	return 0;
2784bc52338SCy Schubert }
2794bc52338SCy Schubert 
2804bc52338SCy Schubert #endif /* CONFIG_HS20 */
2814bc52338SCy Schubert #endif /* CONFIG_SQLITE */
2824bc52338SCy Schubert 
2834bc52338SCy Schubert 
2845b9c547cSRui Paulo void srv_log(struct radius_session *sess, const char *fmt, ...)
2855b9c547cSRui Paulo PRINTF_FORMAT(2, 3);
2865b9c547cSRui Paulo 
2875b9c547cSRui Paulo void srv_log(struct radius_session *sess, const char *fmt, ...)
2885b9c547cSRui Paulo {
2895b9c547cSRui Paulo 	va_list ap;
2905b9c547cSRui Paulo 	char *buf;
2915b9c547cSRui Paulo 	int buflen;
2925b9c547cSRui Paulo 
2935b9c547cSRui Paulo 	va_start(ap, fmt);
2945b9c547cSRui Paulo 	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
2955b9c547cSRui Paulo 	va_end(ap);
2965b9c547cSRui Paulo 
2975b9c547cSRui Paulo 	buf = os_malloc(buflen);
2985b9c547cSRui Paulo 	if (buf == NULL)
2995b9c547cSRui Paulo 		return;
3005b9c547cSRui Paulo 	va_start(ap, fmt);
3015b9c547cSRui Paulo 	vsnprintf(buf, buflen, fmt, ap);
3025b9c547cSRui Paulo 	va_end(ap);
3035b9c547cSRui Paulo 
3045b9c547cSRui Paulo 	RADIUS_DEBUG("[0x%x %s] %s", sess->sess_id, sess->nas_ip, buf);
3055b9c547cSRui Paulo 
3065b9c547cSRui Paulo #ifdef CONFIG_SQLITE
3075b9c547cSRui Paulo 	if (sess->server->db) {
3085b9c547cSRui Paulo 		char *sql;
3095b9c547cSRui Paulo 		sql = sqlite3_mprintf("INSERT INTO authlog"
3105b9c547cSRui Paulo 				      "(timestamp,session,nas_ip,username,note)"
3115b9c547cSRui Paulo 				      " VALUES ("
3125b9c547cSRui Paulo 				      "strftime('%%Y-%%m-%%d %%H:%%M:%%f',"
3135b9c547cSRui Paulo 				      "'now'),%u,%Q,%Q,%Q)",
3145b9c547cSRui Paulo 				      sess->sess_id, sess->nas_ip,
3155b9c547cSRui Paulo 				      sess->username, buf);
3165b9c547cSRui Paulo 		if (sql) {
3175b9c547cSRui Paulo 			if (sqlite3_exec(sess->server->db, sql, NULL, NULL,
3185b9c547cSRui Paulo 					 NULL) != SQLITE_OK) {
3195b9c547cSRui Paulo 				RADIUS_ERROR("Failed to add authlog entry into sqlite database: %s",
3205b9c547cSRui Paulo 					     sqlite3_errmsg(sess->server->db));
3215b9c547cSRui Paulo 			}
3225b9c547cSRui Paulo 			sqlite3_free(sql);
3235b9c547cSRui Paulo 		}
3245b9c547cSRui Paulo 	}
3255b9c547cSRui Paulo #endif /* CONFIG_SQLITE */
3265b9c547cSRui Paulo 
3275b9c547cSRui Paulo 	os_free(buf);
3285b9c547cSRui Paulo }
3295b9c547cSRui Paulo 
33039beb93cSSam Leffler 
33139beb93cSSam Leffler static struct radius_client *
33239beb93cSSam Leffler radius_server_get_client(struct radius_server_data *data, struct in_addr *addr,
33339beb93cSSam Leffler 			 int ipv6)
33439beb93cSSam Leffler {
33539beb93cSSam Leffler 	struct radius_client *client = data->clients;
33639beb93cSSam Leffler 
33739beb93cSSam Leffler 	while (client) {
33839beb93cSSam Leffler #ifdef CONFIG_IPV6
33939beb93cSSam Leffler 		if (ipv6) {
34039beb93cSSam Leffler 			struct in6_addr *addr6;
34139beb93cSSam Leffler 			int i;
34239beb93cSSam Leffler 
34339beb93cSSam Leffler 			addr6 = (struct in6_addr *) addr;
34439beb93cSSam Leffler 			for (i = 0; i < 16; i++) {
34539beb93cSSam Leffler 				if ((addr6->s6_addr[i] &
34639beb93cSSam Leffler 				     client->mask6.s6_addr[i]) !=
34739beb93cSSam Leffler 				    (client->addr6.s6_addr[i] &
34839beb93cSSam Leffler 				     client->mask6.s6_addr[i])) {
34939beb93cSSam Leffler 					i = 17;
35039beb93cSSam Leffler 					break;
35139beb93cSSam Leffler 				}
35239beb93cSSam Leffler 			}
35339beb93cSSam Leffler 			if (i == 16) {
35439beb93cSSam Leffler 				break;
35539beb93cSSam Leffler 			}
35639beb93cSSam Leffler 		}
35739beb93cSSam Leffler #endif /* CONFIG_IPV6 */
35839beb93cSSam Leffler 		if (!ipv6 && (client->addr.s_addr & client->mask.s_addr) ==
35939beb93cSSam Leffler 		    (addr->s_addr & client->mask.s_addr)) {
36039beb93cSSam Leffler 			break;
36139beb93cSSam Leffler 		}
36239beb93cSSam Leffler 
36339beb93cSSam Leffler 		client = client->next;
36439beb93cSSam Leffler 	}
36539beb93cSSam Leffler 
36639beb93cSSam Leffler 	return client;
36739beb93cSSam Leffler }
36839beb93cSSam Leffler 
36939beb93cSSam Leffler 
37039beb93cSSam Leffler static struct radius_session *
37139beb93cSSam Leffler radius_server_get_session(struct radius_client *client, unsigned int sess_id)
37239beb93cSSam Leffler {
37339beb93cSSam Leffler 	struct radius_session *sess = client->sessions;
37439beb93cSSam Leffler 
37539beb93cSSam Leffler 	while (sess) {
37639beb93cSSam Leffler 		if (sess->sess_id == sess_id) {
37739beb93cSSam Leffler 			break;
37839beb93cSSam Leffler 		}
37939beb93cSSam Leffler 		sess = sess->next;
38039beb93cSSam Leffler 	}
38139beb93cSSam Leffler 
38239beb93cSSam Leffler 	return sess;
38339beb93cSSam Leffler }
38439beb93cSSam Leffler 
38539beb93cSSam Leffler 
38639beb93cSSam Leffler static void radius_server_session_free(struct radius_server_data *data,
38739beb93cSSam Leffler 				       struct radius_session *sess)
38839beb93cSSam Leffler {
38939beb93cSSam Leffler 	eloop_cancel_timeout(radius_server_session_timeout, data, sess);
3903157ba21SRui Paulo 	eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess);
39139beb93cSSam Leffler 	eap_server_sm_deinit(sess->eap);
39239beb93cSSam Leffler 	radius_msg_free(sess->last_msg);
39339beb93cSSam Leffler 	os_free(sess->last_from_addr);
39439beb93cSSam Leffler 	radius_msg_free(sess->last_reply);
3955b9c547cSRui Paulo 	os_free(sess->username);
3965b9c547cSRui Paulo 	os_free(sess->nas_ip);
39739beb93cSSam Leffler 	os_free(sess);
39839beb93cSSam Leffler 	data->num_sess--;
39939beb93cSSam Leffler }
40039beb93cSSam Leffler 
40139beb93cSSam Leffler 
40239beb93cSSam Leffler static void radius_server_session_remove(struct radius_server_data *data,
40339beb93cSSam Leffler 					 struct radius_session *sess)
40439beb93cSSam Leffler {
40539beb93cSSam Leffler 	struct radius_client *client = sess->client;
40639beb93cSSam Leffler 	struct radius_session *session, *prev;
40739beb93cSSam Leffler 
40839beb93cSSam Leffler 	eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess);
40939beb93cSSam Leffler 
41039beb93cSSam Leffler 	prev = NULL;
41139beb93cSSam Leffler 	session = client->sessions;
41239beb93cSSam Leffler 	while (session) {
41339beb93cSSam Leffler 		if (session == sess) {
41439beb93cSSam Leffler 			if (prev == NULL) {
41539beb93cSSam Leffler 				client->sessions = sess->next;
41639beb93cSSam Leffler 			} else {
41739beb93cSSam Leffler 				prev->next = sess->next;
41839beb93cSSam Leffler 			}
41939beb93cSSam Leffler 			radius_server_session_free(data, sess);
42039beb93cSSam Leffler 			break;
42139beb93cSSam Leffler 		}
42239beb93cSSam Leffler 		prev = session;
42339beb93cSSam Leffler 		session = session->next;
42439beb93cSSam Leffler 	}
42539beb93cSSam Leffler }
42639beb93cSSam Leffler 
42739beb93cSSam Leffler 
42839beb93cSSam Leffler static void radius_server_session_remove_timeout(void *eloop_ctx,
42939beb93cSSam Leffler 						 void *timeout_ctx)
43039beb93cSSam Leffler {
43139beb93cSSam Leffler 	struct radius_server_data *data = eloop_ctx;
43239beb93cSSam Leffler 	struct radius_session *sess = timeout_ctx;
43339beb93cSSam Leffler 	RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id);
43439beb93cSSam Leffler 	radius_server_session_remove(data, sess);
43539beb93cSSam Leffler }
43639beb93cSSam Leffler 
43739beb93cSSam Leffler 
43839beb93cSSam Leffler static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx)
43939beb93cSSam Leffler {
44039beb93cSSam Leffler 	struct radius_server_data *data = eloop_ctx;
44139beb93cSSam Leffler 	struct radius_session *sess = timeout_ctx;
44239beb93cSSam Leffler 
44339beb93cSSam Leffler 	RADIUS_DEBUG("Timing out authentication session 0x%x", sess->sess_id);
44439beb93cSSam Leffler 	radius_server_session_remove(data, sess);
44539beb93cSSam Leffler }
44639beb93cSSam Leffler 
44739beb93cSSam Leffler 
44839beb93cSSam Leffler static struct radius_session *
44939beb93cSSam Leffler radius_server_new_session(struct radius_server_data *data,
45039beb93cSSam Leffler 			  struct radius_client *client)
45139beb93cSSam Leffler {
45239beb93cSSam Leffler 	struct radius_session *sess;
45339beb93cSSam Leffler 
45439beb93cSSam Leffler 	if (data->num_sess >= RADIUS_MAX_SESSION) {
45539beb93cSSam Leffler 		RADIUS_DEBUG("Maximum number of existing session - no room "
45639beb93cSSam Leffler 			     "for a new session");
45739beb93cSSam Leffler 		return NULL;
45839beb93cSSam Leffler 	}
45939beb93cSSam Leffler 
46039beb93cSSam Leffler 	sess = os_zalloc(sizeof(*sess));
46139beb93cSSam Leffler 	if (sess == NULL)
46239beb93cSSam Leffler 		return NULL;
46339beb93cSSam Leffler 
46439beb93cSSam Leffler 	sess->server = data;
46539beb93cSSam Leffler 	sess->client = client;
46639beb93cSSam Leffler 	sess->sess_id = data->next_sess_id++;
46739beb93cSSam Leffler 	sess->next = client->sessions;
46839beb93cSSam Leffler 	client->sessions = sess;
46939beb93cSSam Leffler 	eloop_register_timeout(RADIUS_SESSION_TIMEOUT, 0,
47039beb93cSSam Leffler 			       radius_server_session_timeout, data, sess);
47139beb93cSSam Leffler 	data->num_sess++;
47239beb93cSSam Leffler 	return sess;
47339beb93cSSam Leffler }
47439beb93cSSam Leffler 
47539beb93cSSam Leffler 
4765b9c547cSRui Paulo #ifdef CONFIG_TESTING_OPTIONS
4775b9c547cSRui Paulo static void radius_server_testing_options_tls(struct radius_session *sess,
4785b9c547cSRui Paulo 					      const char *tls,
479*c1d255d3SCy Schubert 					      struct eap_session_data *eap_conf)
4805b9c547cSRui Paulo {
4815b9c547cSRui Paulo 	int test = atoi(tls);
4825b9c547cSRui Paulo 
4835b9c547cSRui Paulo 	switch (test) {
4845b9c547cSRui Paulo 	case 1:
4855b9c547cSRui Paulo 		srv_log(sess, "TLS test - break VerifyData");
4865b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_BREAK_VERIFY_DATA;
4875b9c547cSRui Paulo 		break;
4885b9c547cSRui Paulo 	case 2:
4895b9c547cSRui Paulo 		srv_log(sess, "TLS test - break ServerKeyExchange ServerParams hash");
4905b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_HASH;
4915b9c547cSRui Paulo 		break;
4925b9c547cSRui Paulo 	case 3:
4935b9c547cSRui Paulo 		srv_log(sess, "TLS test - break ServerKeyExchange ServerParams Signature");
4945b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_SIGNATURE;
4955b9c547cSRui Paulo 		break;
4965b9c547cSRui Paulo 	case 4:
4975b9c547cSRui Paulo 		srv_log(sess, "TLS test - RSA-DHE using a short 511-bit prime");
4985b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_DHE_PRIME_511B;
4995b9c547cSRui Paulo 		break;
5005b9c547cSRui Paulo 	case 5:
5015b9c547cSRui Paulo 		srv_log(sess, "TLS test - RSA-DHE using a short 767-bit prime");
5025b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_DHE_PRIME_767B;
5035b9c547cSRui Paulo 		break;
5045b9c547cSRui Paulo 	case 6:
5055b9c547cSRui Paulo 		srv_log(sess, "TLS test - RSA-DHE using a bogus 15 \"prime\"");
5065b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_DHE_PRIME_15;
5075b9c547cSRui Paulo 		break;
5085b9c547cSRui Paulo 	case 7:
5095b9c547cSRui Paulo 		srv_log(sess, "TLS test - RSA-DHE using a short 58-bit prime in long container");
5105b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_DHE_PRIME_58B;
5115b9c547cSRui Paulo 		break;
5125b9c547cSRui Paulo 	case 8:
5135b9c547cSRui Paulo 		srv_log(sess, "TLS test - RSA-DHE using a non-prime");
5145b9c547cSRui Paulo 		eap_conf->tls_test_flags = TLS_DHE_NON_PRIME;
5155b9c547cSRui Paulo 		break;
5165b9c547cSRui Paulo 	default:
5175b9c547cSRui Paulo 		srv_log(sess, "Unrecognized TLS test");
5185b9c547cSRui Paulo 		break;
5195b9c547cSRui Paulo 	}
5205b9c547cSRui Paulo }
5215b9c547cSRui Paulo #endif /* CONFIG_TESTING_OPTIONS */
5225b9c547cSRui Paulo 
5235b9c547cSRui Paulo static void radius_server_testing_options(struct radius_session *sess,
524*c1d255d3SCy Schubert 					  struct eap_session_data *eap_conf)
5255b9c547cSRui Paulo {
5265b9c547cSRui Paulo #ifdef CONFIG_TESTING_OPTIONS
5275b9c547cSRui Paulo 	const char *pos;
5285b9c547cSRui Paulo 
5295b9c547cSRui Paulo 	pos = os_strstr(sess->username, "@test-");
5305b9c547cSRui Paulo 	if (pos == NULL)
5315b9c547cSRui Paulo 		return;
5325b9c547cSRui Paulo 	pos += 6;
5335b9c547cSRui Paulo 	if (os_strncmp(pos, "tls-", 4) == 0)
5345b9c547cSRui Paulo 		radius_server_testing_options_tls(sess, pos + 4, eap_conf);
5355b9c547cSRui Paulo 	else
5365b9c547cSRui Paulo 		srv_log(sess, "Unrecognized test: %s", pos);
5375b9c547cSRui Paulo #endif /* CONFIG_TESTING_OPTIONS */
5385b9c547cSRui Paulo }
5395b9c547cSRui Paulo 
5405b9c547cSRui Paulo 
5414bc52338SCy Schubert #ifdef CONFIG_ERP
5424bc52338SCy Schubert static struct eap_server_erp_key *
5434bc52338SCy Schubert radius_server_erp_find_key(struct radius_server_data *data, const char *keyname)
5444bc52338SCy Schubert {
5454bc52338SCy Schubert 	struct eap_server_erp_key *erp;
5464bc52338SCy Schubert 
5474bc52338SCy Schubert 	dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key,
5484bc52338SCy Schubert 			 list) {
5494bc52338SCy Schubert 		if (os_strcmp(erp->keyname_nai, keyname) == 0)
5504bc52338SCy Schubert 			return erp;
5514bc52338SCy Schubert 	}
5524bc52338SCy Schubert 
5534bc52338SCy Schubert 	return NULL;
5544bc52338SCy Schubert }
5554bc52338SCy Schubert #endif /* CONFIG_ERP */
5564bc52338SCy Schubert 
5574bc52338SCy Schubert 
55839beb93cSSam Leffler static struct radius_session *
55939beb93cSSam Leffler radius_server_get_new_session(struct radius_server_data *data,
56039beb93cSSam Leffler 			      struct radius_client *client,
5615b9c547cSRui Paulo 			      struct radius_msg *msg, const char *from_addr)
56239beb93cSSam Leffler {
56385732ac8SCy Schubert 	u8 *user, *id;
56485732ac8SCy Schubert 	size_t user_len, id_len;
56539beb93cSSam Leffler 	int res;
56639beb93cSSam Leffler 	struct radius_session *sess;
567*c1d255d3SCy Schubert 	struct eap_session_data eap_sess;
5684bc52338SCy Schubert 	struct eap_user *tmp;
56939beb93cSSam Leffler 
57039beb93cSSam Leffler 	RADIUS_DEBUG("Creating a new session");
57139beb93cSSam Leffler 
5725b9c547cSRui Paulo 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &user,
5735b9c547cSRui Paulo 				    &user_len, NULL) < 0) {
57439beb93cSSam Leffler 		RADIUS_DEBUG("Could not get User-Name");
57539beb93cSSam Leffler 		return NULL;
57639beb93cSSam Leffler 	}
57739beb93cSSam Leffler 	RADIUS_DUMP_ASCII("User-Name", user, user_len);
57839beb93cSSam Leffler 
5794bc52338SCy Schubert 	tmp = os_zalloc(sizeof(*tmp));
5804bc52338SCy Schubert 	if (!tmp)
5814bc52338SCy Schubert 		return NULL;
58239beb93cSSam Leffler 
5834bc52338SCy Schubert 	res = data->get_eap_user(data->conf_ctx, user, user_len, 0, tmp);
5844bc52338SCy Schubert #ifdef CONFIG_ERP
585*c1d255d3SCy Schubert 	if (res != 0 && data->eap_cfg->erp) {
5864bc52338SCy Schubert 		char *username;
5874bc52338SCy Schubert 
5884bc52338SCy Schubert 		username = os_zalloc(user_len + 1);
5894bc52338SCy Schubert 		if (username) {
5904bc52338SCy Schubert 			os_memcpy(username, user, user_len);
5914bc52338SCy Schubert 			if (radius_server_erp_find_key(data, username))
5924bc52338SCy Schubert 				res = 0;
5934bc52338SCy Schubert 			os_free(username);
5944bc52338SCy Schubert 		}
5954bc52338SCy Schubert 	}
5964bc52338SCy Schubert #endif /* CONFIG_ERP */
5975b9c547cSRui Paulo 	if (res != 0) {
5985b9c547cSRui Paulo 		RADIUS_DEBUG("User-Name not found from user database");
5994bc52338SCy Schubert 		eap_user_free(tmp);
6005b9c547cSRui Paulo 		return NULL;
6015b9c547cSRui Paulo 	}
6025b9c547cSRui Paulo 
60339beb93cSSam Leffler 	RADIUS_DEBUG("Matching user entry found");
60439beb93cSSam Leffler 	sess = radius_server_new_session(data, client);
60539beb93cSSam Leffler 	if (sess == NULL) {
60639beb93cSSam Leffler 		RADIUS_DEBUG("Failed to create a new session");
6074bc52338SCy Schubert 		eap_user_free(tmp);
60839beb93cSSam Leffler 		return NULL;
60939beb93cSSam Leffler 	}
6104bc52338SCy Schubert 	sess->accept_attr = tmp->accept_attr;
6114bc52338SCy Schubert 	sess->macacl = tmp->macacl;
6124bc52338SCy Schubert 	eap_user_free(tmp);
6135b9c547cSRui Paulo 
6145b9c547cSRui Paulo 	sess->username = os_malloc(user_len * 4 + 1);
6155b9c547cSRui Paulo 	if (sess->username == NULL) {
61685732ac8SCy Schubert 		radius_server_session_remove(data, sess);
61739beb93cSSam Leffler 		return NULL;
61839beb93cSSam Leffler 	}
6195b9c547cSRui Paulo 	printf_encode(sess->username, user_len * 4 + 1, user, user_len);
6205b9c547cSRui Paulo 
6215b9c547cSRui Paulo 	sess->nas_ip = os_strdup(from_addr);
6225b9c547cSRui Paulo 	if (sess->nas_ip == NULL) {
62385732ac8SCy Schubert 		radius_server_session_remove(data, sess);
6245b9c547cSRui Paulo 		return NULL;
6255b9c547cSRui Paulo 	}
6265b9c547cSRui Paulo 
62785732ac8SCy Schubert 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, &id,
62885732ac8SCy Schubert 				    &id_len, NULL) == 0) {
62985732ac8SCy Schubert 		char buf[3 * ETH_ALEN];
63085732ac8SCy Schubert 
63185732ac8SCy Schubert 		os_memset(buf, 0, sizeof(buf));
63285732ac8SCy Schubert 		if (id_len >= sizeof(buf))
63385732ac8SCy Schubert 			id_len = sizeof(buf) - 1;
63485732ac8SCy Schubert 		os_memcpy(buf, id, id_len);
63585732ac8SCy Schubert 		if (hwaddr_aton2(buf, sess->mac_addr) < 0)
63685732ac8SCy Schubert 			os_memset(sess->mac_addr, 0, ETH_ALEN);
63785732ac8SCy Schubert 		else
63885732ac8SCy Schubert 			RADIUS_DEBUG("Calling-Station-Id: " MACSTR,
63985732ac8SCy Schubert 				     MAC2STR(sess->mac_addr));
64085732ac8SCy Schubert 	}
64185732ac8SCy Schubert 
6425b9c547cSRui Paulo 	srv_log(sess, "New session created");
64339beb93cSSam Leffler 
644*c1d255d3SCy Schubert 	os_memset(&eap_sess, 0, sizeof(eap_sess));
645*c1d255d3SCy Schubert 	radius_server_testing_options(sess, &eap_sess);
64639beb93cSSam Leffler 	sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
647*c1d255d3SCy Schubert 				       data->eap_cfg, &eap_sess);
64839beb93cSSam Leffler 	if (sess->eap == NULL) {
64939beb93cSSam Leffler 		RADIUS_DEBUG("Failed to initialize EAP state machine for the "
65039beb93cSSam Leffler 			     "new session");
65185732ac8SCy Schubert 		radius_server_session_remove(data, sess);
65239beb93cSSam Leffler 		return NULL;
65339beb93cSSam Leffler 	}
65439beb93cSSam Leffler 	sess->eap_if = eap_get_interface(sess->eap);
655*c1d255d3SCy Schubert 	sess->eap_if->eapRestart = true;
656*c1d255d3SCy Schubert 	sess->eap_if->portEnabled = true;
65739beb93cSSam Leffler 
65839beb93cSSam Leffler 	RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id);
65939beb93cSSam Leffler 
66039beb93cSSam Leffler 	return sess;
66139beb93cSSam Leffler }
66239beb93cSSam Leffler 
66339beb93cSSam Leffler 
66485732ac8SCy Schubert #ifdef CONFIG_HS20
66585732ac8SCy Schubert static void radius_srv_hs20_t_c_pending(struct radius_session *sess)
66685732ac8SCy Schubert {
66785732ac8SCy Schubert #ifdef CONFIG_SQLITE
66885732ac8SCy Schubert 	char *sql;
66985732ac8SCy Schubert 	char addr[3 * ETH_ALEN], *id_str;
67085732ac8SCy Schubert 	const u8 *id;
67185732ac8SCy Schubert 	size_t id_len;
67285732ac8SCy Schubert 
67385732ac8SCy Schubert 	if (!sess->server->db || !sess->eap ||
67485732ac8SCy Schubert 	    is_zero_ether_addr(sess->mac_addr))
67585732ac8SCy Schubert 		return;
67685732ac8SCy Schubert 
67785732ac8SCy Schubert 	os_snprintf(addr, sizeof(addr), MACSTR, MAC2STR(sess->mac_addr));
67885732ac8SCy Schubert 
67985732ac8SCy Schubert 	id = eap_get_identity(sess->eap, &id_len);
68085732ac8SCy Schubert 	if (!id)
68185732ac8SCy Schubert 		return;
68285732ac8SCy Schubert 	id_str = os_malloc(id_len + 1);
68385732ac8SCy Schubert 	if (!id_str)
68485732ac8SCy Schubert 		return;
68585732ac8SCy Schubert 	os_memcpy(id_str, id, id_len);
68685732ac8SCy Schubert 	id_str[id_len] = '\0';
68785732ac8SCy Schubert 
68885732ac8SCy Schubert 	sql = sqlite3_mprintf("INSERT OR REPLACE INTO pending_tc (mac_addr,identity) VALUES (%Q,%Q)",
68985732ac8SCy Schubert 			      addr, id_str);
69085732ac8SCy Schubert 	os_free(id_str);
69185732ac8SCy Schubert 	if (!sql)
69285732ac8SCy Schubert 		return;
69385732ac8SCy Schubert 
69485732ac8SCy Schubert 	if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) !=
69585732ac8SCy Schubert 	    SQLITE_OK) {
69685732ac8SCy Schubert 		RADIUS_ERROR("Failed to add pending_tc entry into sqlite database: %s",
69785732ac8SCy Schubert 			     sqlite3_errmsg(sess->server->db));
69885732ac8SCy Schubert 	}
69985732ac8SCy Schubert 	sqlite3_free(sql);
70085732ac8SCy Schubert #endif /* CONFIG_SQLITE */
70185732ac8SCy Schubert }
70285732ac8SCy Schubert #endif /* CONFIG_HS20 */
70385732ac8SCy Schubert 
70485732ac8SCy Schubert 
70585732ac8SCy Schubert static void radius_server_add_session(struct radius_session *sess)
70685732ac8SCy Schubert {
70785732ac8SCy Schubert #ifdef CONFIG_SQLITE
70885732ac8SCy Schubert 	char *sql;
70985732ac8SCy Schubert 	char addr_txt[ETH_ALEN * 3];
71085732ac8SCy Schubert 	struct os_time now;
71185732ac8SCy Schubert 
71285732ac8SCy Schubert 	if (!sess->server->db)
71385732ac8SCy Schubert 		return;
71485732ac8SCy Schubert 
71585732ac8SCy Schubert 
71685732ac8SCy Schubert 	os_snprintf(addr_txt, sizeof(addr_txt), MACSTR,
71785732ac8SCy Schubert 		    MAC2STR(sess->mac_addr));
71885732ac8SCy Schubert 
71985732ac8SCy Schubert 	os_get_time(&now);
72085732ac8SCy 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)",
72185732ac8SCy Schubert 			      addr_txt, sess->username, now.sec,
72285732ac8SCy Schubert 			      sess->nas_ip, sess->t_c_filtering);
72385732ac8SCy Schubert 	if (sql) {
72485732ac8SCy Schubert 			if (sqlite3_exec(sess->server->db, sql, NULL, NULL,
72585732ac8SCy Schubert 					 NULL) != SQLITE_OK) {
72685732ac8SCy Schubert 				RADIUS_ERROR("Failed to add current_sessions entry into sqlite database: %s",
72785732ac8SCy Schubert 					     sqlite3_errmsg(sess->server->db));
72885732ac8SCy Schubert 			}
72985732ac8SCy Schubert 			sqlite3_free(sql);
73085732ac8SCy Schubert 	}
73185732ac8SCy Schubert #endif /* CONFIG_SQLITE */
73285732ac8SCy Schubert }
73385732ac8SCy Schubert 
73485732ac8SCy Schubert 
73585732ac8SCy Schubert static void db_update_last_msk(struct radius_session *sess, const char *msk)
73685732ac8SCy Schubert {
73785732ac8SCy Schubert #ifdef CONFIG_RADIUS_TEST
73885732ac8SCy Schubert #ifdef CONFIG_SQLITE
73985732ac8SCy Schubert 	char *sql = NULL;
74085732ac8SCy Schubert 	char *id_str = NULL;
74185732ac8SCy Schubert 	const u8 *id;
74285732ac8SCy Schubert 	size_t id_len;
74385732ac8SCy Schubert 	const char *serial_num;
74485732ac8SCy Schubert 
74585732ac8SCy Schubert 	if (!sess->server->db)
74685732ac8SCy Schubert 		return;
74785732ac8SCy Schubert 
74885732ac8SCy Schubert 	serial_num = eap_get_serial_num(sess->eap);
74985732ac8SCy Schubert 	if (serial_num) {
75085732ac8SCy Schubert 		id_len = 5 + os_strlen(serial_num) + 1;
75185732ac8SCy Schubert 		id_str = os_malloc(id_len);
75285732ac8SCy Schubert 		if (!id_str)
75385732ac8SCy Schubert 			return;
75485732ac8SCy Schubert 		os_snprintf(id_str, id_len, "cert-%s", serial_num);
75585732ac8SCy Schubert 	} else {
75685732ac8SCy Schubert 		id = eap_get_identity(sess->eap, &id_len);
75785732ac8SCy Schubert 		if (!id)
75885732ac8SCy Schubert 			return;
75985732ac8SCy Schubert 		id_str = os_malloc(id_len + 1);
76085732ac8SCy Schubert 		if (!id_str)
76185732ac8SCy Schubert 			return;
76285732ac8SCy Schubert 		os_memcpy(id_str, id, id_len);
76385732ac8SCy Schubert 		id_str[id_len] = '\0';
76485732ac8SCy Schubert 	}
76585732ac8SCy Schubert 
76685732ac8SCy Schubert 	sql = sqlite3_mprintf("UPDATE users SET last_msk=%Q WHERE identity=%Q",
76785732ac8SCy Schubert 			      msk, id_str);
76885732ac8SCy Schubert 	os_free(id_str);
76985732ac8SCy Schubert 	if (!sql)
77085732ac8SCy Schubert 		return;
77185732ac8SCy Schubert 
77285732ac8SCy Schubert 	if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) !=
77385732ac8SCy Schubert 	    SQLITE_OK) {
77485732ac8SCy Schubert 		RADIUS_DEBUG("Failed to update last_msk: %s",
77585732ac8SCy Schubert 			     sqlite3_errmsg(sess->server->db));
77685732ac8SCy Schubert 	}
77785732ac8SCy Schubert 	sqlite3_free(sql);
77885732ac8SCy Schubert #endif /* CONFIG_SQLITE */
77985732ac8SCy Schubert #endif /* CONFIG_RADIUS_TEST */
78085732ac8SCy Schubert }
78185732ac8SCy Schubert 
78285732ac8SCy Schubert 
7834bc52338SCy Schubert #ifdef CONFIG_HS20
7844bc52338SCy Schubert 
7854bc52338SCy Schubert static int radius_server_is_sim_method(struct radius_session *sess)
7864bc52338SCy Schubert {
7874bc52338SCy Schubert 	const char *name;
7884bc52338SCy Schubert 
7894bc52338SCy Schubert 	name = eap_get_method(sess->eap);
7904bc52338SCy Schubert 	return name &&
7914bc52338SCy Schubert 		(os_strcmp(name, "SIM") == 0 ||
7924bc52338SCy Schubert 		 os_strcmp(name, "AKA") == 0 ||
7934bc52338SCy Schubert 		 os_strcmp(name, "AKA'") == 0);
7944bc52338SCy Schubert }
7954bc52338SCy Schubert 
7964bc52338SCy Schubert 
7974bc52338SCy Schubert static int radius_server_hs20_missing_sim_pps(struct radius_msg *request)
7984bc52338SCy Schubert {
7994bc52338SCy Schubert 	u8 *buf, *pos, *end, type, sublen;
8004bc52338SCy Schubert 	size_t len;
8014bc52338SCy Schubert 
8024bc52338SCy Schubert 	buf = NULL;
8034bc52338SCy Schubert 	for (;;) {
8044bc52338SCy Schubert 		if (radius_msg_get_attr_ptr(request,
8054bc52338SCy Schubert 					    RADIUS_ATTR_VENDOR_SPECIFIC,
8064bc52338SCy Schubert 					    &buf, &len, buf) < 0)
8074bc52338SCy Schubert 			return 0;
8084bc52338SCy Schubert 		if (len < 6)
8094bc52338SCy Schubert 			continue;
8104bc52338SCy Schubert 		pos = buf;
8114bc52338SCy Schubert 		end = buf + len;
8124bc52338SCy Schubert 		if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
8134bc52338SCy Schubert 			continue;
8144bc52338SCy Schubert 		pos += 4;
8154bc52338SCy Schubert 
8164bc52338SCy Schubert 		type = *pos++;
8174bc52338SCy Schubert 		sublen = *pos++;
8184bc52338SCy Schubert 		if (sublen < 2)
8194bc52338SCy Schubert 			continue; /* invalid length */
8204bc52338SCy Schubert 		sublen -= 2; /* skip header */
8214bc52338SCy Schubert 		if (pos + sublen > end)
8224bc52338SCy Schubert 			continue; /* invalid WFA VSA */
8234bc52338SCy Schubert 
8244bc52338SCy Schubert 		if (type != RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION)
8254bc52338SCy Schubert 			continue;
8264bc52338SCy Schubert 
8274bc52338SCy Schubert 		RADIUS_DUMP("HS2.0 mobile device version", pos, sublen);
8284bc52338SCy Schubert 		if (sublen < 1 + 2)
8294bc52338SCy Schubert 			continue;
8304bc52338SCy Schubert 		if (pos[0] == 0)
8314bc52338SCy Schubert 			continue; /* Release 1 STA does not support provisioning
8324bc52338SCy Schubert 
8334bc52338SCy Schubert 				   */
8344bc52338SCy Schubert 		/* UpdateIdentifier 0 indicates no PPS MO */
8354bc52338SCy Schubert 		return WPA_GET_BE16(pos + 1) == 0;
8364bc52338SCy Schubert 	}
8374bc52338SCy Schubert }
8384bc52338SCy Schubert 
8394bc52338SCy Schubert 
8404bc52338SCy Schubert #define HS20_MOBILE_ID_HASH_LEN 16
8414bc52338SCy Schubert 
8424bc52338SCy Schubert static int radius_server_sim_provisioning_session(struct radius_session *sess,
8434bc52338SCy Schubert 						  const u8 *hash)
8444bc52338SCy Schubert {
8454bc52338SCy Schubert #ifdef CONFIG_SQLITE
8464bc52338SCy Schubert 	char *sql;
8474bc52338SCy Schubert 	char addr_txt[ETH_ALEN * 3];
8484bc52338SCy Schubert 	char hash_txt[2 * HS20_MOBILE_ID_HASH_LEN + 1];
8494bc52338SCy Schubert 	struct os_time now;
8504bc52338SCy Schubert 	int res;
8514bc52338SCy Schubert 	const char *imsi, *eap_method;
8524bc52338SCy Schubert 
8534bc52338SCy Schubert 	if (!sess->server->db ||
8544bc52338SCy Schubert 	    (!db_table_exists(sess->server->db, "sim_provisioning") &&
8554bc52338SCy Schubert 	     db_table_create_sim_provisioning(sess->server->db) < 0))
8564bc52338SCy Schubert 		return -1;
8574bc52338SCy Schubert 
8584bc52338SCy Schubert 	imsi = eap_get_imsi(sess->eap);
8594bc52338SCy Schubert 	if (!imsi)
8604bc52338SCy Schubert 		return -1;
8614bc52338SCy Schubert 
8624bc52338SCy Schubert 	eap_method = eap_get_method(sess->eap);
8634bc52338SCy Schubert 	if (!eap_method)
8644bc52338SCy Schubert 		return -1;
8654bc52338SCy Schubert 
8664bc52338SCy Schubert 	os_snprintf(addr_txt, sizeof(addr_txt), MACSTR,
8674bc52338SCy Schubert 		    MAC2STR(sess->mac_addr));
8684bc52338SCy Schubert 	wpa_snprintf_hex(hash_txt, sizeof(hash_txt), hash,
8694bc52338SCy Schubert 			 HS20_MOBILE_ID_HASH_LEN);
8704bc52338SCy Schubert 
8714bc52338SCy Schubert 	os_get_time(&now);
8724bc52338SCy Schubert 	sql = sqlite3_mprintf("INSERT INTO sim_provisioning(mobile_identifier_hash,imsi,mac_addr,eap_method,timestamp) VALUES (%Q,%Q,%Q,%Q,%u)",
8734bc52338SCy Schubert 			      hash_txt, imsi, addr_txt, eap_method, now.sec);
8744bc52338SCy Schubert 	if (!sql)
8754bc52338SCy Schubert 		return -1;
8764bc52338SCy Schubert 
8774bc52338SCy Schubert 	if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) !=
8784bc52338SCy Schubert 	    SQLITE_OK) {
8794bc52338SCy Schubert 		RADIUS_ERROR("Failed to add SIM provisioning entry into sqlite database: %s",
8804bc52338SCy Schubert 			     sqlite3_errmsg(sess->server->db));
8814bc52338SCy Schubert 		res = -1;
8824bc52338SCy Schubert 	} else {
8834bc52338SCy Schubert 		res = 0;
8844bc52338SCy Schubert 	}
8854bc52338SCy Schubert 	sqlite3_free(sql);
8864bc52338SCy Schubert 	return res;
8874bc52338SCy Schubert #endif /* CONFIG_SQLITE */
8884bc52338SCy Schubert 	return -1;
8894bc52338SCy Schubert }
8904bc52338SCy Schubert 
8914bc52338SCy Schubert #endif /* CONFIG_HS20 */
8924bc52338SCy Schubert 
8934bc52338SCy Schubert 
89439beb93cSSam Leffler static struct radius_msg *
89539beb93cSSam Leffler radius_server_encapsulate_eap(struct radius_server_data *data,
89639beb93cSSam Leffler 			      struct radius_client *client,
89739beb93cSSam Leffler 			      struct radius_session *sess,
89839beb93cSSam Leffler 			      struct radius_msg *request)
89939beb93cSSam Leffler {
90039beb93cSSam Leffler 	struct radius_msg *msg;
90139beb93cSSam Leffler 	int code;
90239beb93cSSam Leffler 	unsigned int sess_id;
903e28a4053SRui Paulo 	struct radius_hdr *hdr = radius_msg_get_hdr(request);
90485732ac8SCy Schubert 	u16 reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED;
90539beb93cSSam Leffler 
90639beb93cSSam Leffler 	if (sess->eap_if->eapFail) {
907*c1d255d3SCy Schubert 		sess->eap_if->eapFail = false;
90839beb93cSSam Leffler 		code = RADIUS_CODE_ACCESS_REJECT;
90939beb93cSSam Leffler 	} else if (sess->eap_if->eapSuccess) {
910*c1d255d3SCy Schubert 		sess->eap_if->eapSuccess = false;
91139beb93cSSam Leffler 		code = RADIUS_CODE_ACCESS_ACCEPT;
91239beb93cSSam Leffler 	} else {
913*c1d255d3SCy Schubert 		sess->eap_if->eapReq = false;
91439beb93cSSam Leffler 		code = RADIUS_CODE_ACCESS_CHALLENGE;
91539beb93cSSam Leffler 	}
91639beb93cSSam Leffler 
917e28a4053SRui Paulo 	msg = radius_msg_new(code, hdr->identifier);
91839beb93cSSam Leffler 	if (msg == NULL) {
91939beb93cSSam Leffler 		RADIUS_DEBUG("Failed to allocate reply message");
92039beb93cSSam Leffler 		return NULL;
92139beb93cSSam Leffler 	}
92239beb93cSSam Leffler 
92339beb93cSSam Leffler 	sess_id = htonl(sess->sess_id);
92439beb93cSSam Leffler 	if (code == RADIUS_CODE_ACCESS_CHALLENGE &&
92539beb93cSSam Leffler 	    !radius_msg_add_attr(msg, RADIUS_ATTR_STATE,
92639beb93cSSam Leffler 				 (u8 *) &sess_id, sizeof(sess_id))) {
92739beb93cSSam Leffler 		RADIUS_DEBUG("Failed to add State attribute");
92839beb93cSSam Leffler 	}
92939beb93cSSam Leffler 
93039beb93cSSam Leffler 	if (sess->eap_if->eapReqData &&
93139beb93cSSam Leffler 	    !radius_msg_add_eap(msg, wpabuf_head(sess->eap_if->eapReqData),
93239beb93cSSam Leffler 				wpabuf_len(sess->eap_if->eapReqData))) {
93339beb93cSSam Leffler 		RADIUS_DEBUG("Failed to add EAP-Message attribute");
93439beb93cSSam Leffler 	}
93539beb93cSSam Leffler 
93639beb93cSSam Leffler 	if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) {
93739beb93cSSam Leffler 		int len;
938f05cddf9SRui Paulo #ifdef CONFIG_RADIUS_TEST
93985732ac8SCy Schubert 		char buf[2 * 64 + 1];
94085732ac8SCy Schubert 
94185732ac8SCy Schubert 		len = sess->eap_if->eapKeyDataLen;
94285732ac8SCy Schubert 		if (len > 64)
94385732ac8SCy Schubert 			len = 64;
94485732ac8SCy Schubert 		len = wpa_snprintf_hex(buf, sizeof(buf),
94585732ac8SCy Schubert 				       sess->eap_if->eapKeyData, len);
94685732ac8SCy Schubert 		buf[len] = '\0';
94785732ac8SCy Schubert 
948f05cddf9SRui Paulo 		if (data->dump_msk_file) {
949f05cddf9SRui Paulo 			FILE *f;
95085732ac8SCy Schubert 
951f05cddf9SRui Paulo 			f = fopen(data->dump_msk_file, "a");
952f05cddf9SRui Paulo 			if (f) {
953f05cddf9SRui Paulo 				len = sess->eap_if->eapKeyDataLen;
954f05cddf9SRui Paulo 				if (len > 64)
955f05cddf9SRui Paulo 					len = 64;
956f05cddf9SRui Paulo 				len = wpa_snprintf_hex(
957f05cddf9SRui Paulo 					buf, sizeof(buf),
958f05cddf9SRui Paulo 					sess->eap_if->eapKeyData, len);
959f05cddf9SRui Paulo 				buf[len] = '\0';
960f05cddf9SRui Paulo 				fprintf(f, "%s\n", buf);
961f05cddf9SRui Paulo 				fclose(f);
962f05cddf9SRui Paulo 			}
963f05cddf9SRui Paulo 		}
96485732ac8SCy Schubert 
96585732ac8SCy Schubert 		db_update_last_msk(sess, buf);
966f05cddf9SRui Paulo #endif /* CONFIG_RADIUS_TEST */
96739beb93cSSam Leffler 		if (sess->eap_if->eapKeyDataLen > 64) {
96839beb93cSSam Leffler 			len = 32;
96939beb93cSSam Leffler 		} else {
97039beb93cSSam Leffler 			len = sess->eap_if->eapKeyDataLen / 2;
97139beb93cSSam Leffler 		}
972e28a4053SRui Paulo 		if (!radius_msg_add_mppe_keys(msg, hdr->authenticator,
97339beb93cSSam Leffler 					      (u8 *) client->shared_secret,
97439beb93cSSam Leffler 					      client->shared_secret_len,
97539beb93cSSam Leffler 					      sess->eap_if->eapKeyData + len,
97639beb93cSSam Leffler 					      len, sess->eap_if->eapKeyData,
97739beb93cSSam Leffler 					      len)) {
97839beb93cSSam Leffler 			RADIUS_DEBUG("Failed to add MPPE key attributes");
97939beb93cSSam Leffler 		}
980206b73d0SCy Schubert 
981206b73d0SCy Schubert 		if (sess->eap_if->eapSessionId &&
982206b73d0SCy Schubert 		    !radius_msg_add_attr(msg, RADIUS_ATTR_EAP_KEY_NAME,
983206b73d0SCy Schubert 					 sess->eap_if->eapSessionId,
984206b73d0SCy Schubert 					 sess->eap_if->eapSessionIdLen)) {
985206b73d0SCy Schubert 			RADIUS_DEBUG("Failed to add EAP-Key-Name attribute");
986206b73d0SCy Schubert 		}
98739beb93cSSam Leffler 	}
98839beb93cSSam Leffler 
9895b9c547cSRui Paulo #ifdef CONFIG_HS20
9905b9c547cSRui Paulo 	if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation &&
9915b9c547cSRui Paulo 	    data->subscr_remediation_url) {
9925b9c547cSRui Paulo 		u8 *buf;
9935b9c547cSRui Paulo 		size_t url_len = os_strlen(data->subscr_remediation_url);
9945b9c547cSRui Paulo 		buf = os_malloc(1 + url_len);
9955b9c547cSRui Paulo 		if (buf == NULL) {
9965b9c547cSRui Paulo 			radius_msg_free(msg);
9975b9c547cSRui Paulo 			return NULL;
9985b9c547cSRui Paulo 		}
9995b9c547cSRui Paulo 		buf[0] = data->subscr_remediation_method;
10005b9c547cSRui Paulo 		os_memcpy(&buf[1], data->subscr_remediation_url, url_len);
10015b9c547cSRui Paulo 		if (!radius_msg_add_wfa(
10025b9c547cSRui Paulo 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
10035b9c547cSRui Paulo 			    buf, 1 + url_len)) {
10045b9c547cSRui Paulo 			RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
10055b9c547cSRui Paulo 		}
10065b9c547cSRui Paulo 		os_free(buf);
10075b9c547cSRui Paulo 	} else if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation) {
10085b9c547cSRui Paulo 		u8 buf[1];
10095b9c547cSRui Paulo 		if (!radius_msg_add_wfa(
10105b9c547cSRui Paulo 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
10115b9c547cSRui Paulo 			    buf, 0)) {
10125b9c547cSRui Paulo 			RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
10135b9c547cSRui Paulo 		}
10144bc52338SCy Schubert 	} else if (code == RADIUS_CODE_ACCESS_ACCEPT &&
10154bc52338SCy Schubert 		   data->hs20_sim_provisioning_url &&
10164bc52338SCy Schubert 		   radius_server_is_sim_method(sess) &&
10174bc52338SCy Schubert 		   radius_server_hs20_missing_sim_pps(request)) {
10184bc52338SCy Schubert 		u8 *buf, *pos, hash[HS20_MOBILE_ID_HASH_LEN];
10194bc52338SCy Schubert 		size_t prefix_len, url_len;
10204bc52338SCy Schubert 
10214bc52338SCy Schubert 		RADIUS_DEBUG("Device needs HS 2.0 SIM provisioning");
10224bc52338SCy Schubert 
10234bc52338SCy Schubert 		if (os_get_random(hash, HS20_MOBILE_ID_HASH_LEN) < 0) {
10244bc52338SCy Schubert 			radius_msg_free(msg);
10254bc52338SCy Schubert 			return NULL;
10264bc52338SCy Schubert 		}
10274bc52338SCy Schubert 		RADIUS_DUMP("hotspot2dot0-mobile-identifier-hash",
10284bc52338SCy Schubert 			    hash, HS20_MOBILE_ID_HASH_LEN);
10294bc52338SCy Schubert 
10304bc52338SCy Schubert 		if (radius_server_sim_provisioning_session(sess, hash) < 0) {
10314bc52338SCy Schubert 			radius_msg_free(msg);
10324bc52338SCy Schubert 			return NULL;
10334bc52338SCy Schubert 		}
10344bc52338SCy Schubert 
10354bc52338SCy Schubert 		prefix_len = os_strlen(data->hs20_sim_provisioning_url);
10364bc52338SCy Schubert 		url_len = prefix_len + 2 * HS20_MOBILE_ID_HASH_LEN;
10374bc52338SCy Schubert 		buf = os_malloc(1 + url_len + 1);
10384bc52338SCy Schubert 		if (!buf) {
10394bc52338SCy Schubert 			radius_msg_free(msg);
10404bc52338SCy Schubert 			return NULL;
10414bc52338SCy Schubert 		}
10424bc52338SCy Schubert 		pos = buf;
10434bc52338SCy Schubert 		*pos++ = data->subscr_remediation_method;
10444bc52338SCy Schubert 		os_memcpy(pos, data->hs20_sim_provisioning_url, prefix_len);
10454bc52338SCy Schubert 		pos += prefix_len;
10464bc52338SCy Schubert 		wpa_snprintf_hex((char *) pos, 2 * HS20_MOBILE_ID_HASH_LEN + 1,
10474bc52338SCy Schubert 				 hash, HS20_MOBILE_ID_HASH_LEN);
10484bc52338SCy Schubert 		RADIUS_DEBUG("HS 2.0 subscription remediation URL: %s",
10494bc52338SCy Schubert 			     (char *) &buf[1]);
10504bc52338SCy Schubert 		if (!radius_msg_add_wfa(
10514bc52338SCy Schubert 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
10524bc52338SCy Schubert 			    buf, 1 + url_len)) {
10534bc52338SCy Schubert 			RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
10544bc52338SCy Schubert 		}
10554bc52338SCy Schubert 		os_free(buf);
10565b9c547cSRui Paulo 	}
105785732ac8SCy Schubert 
105885732ac8SCy Schubert 	if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) {
105985732ac8SCy Schubert 		u8 buf[4] = { 0x01, 0x00, 0x00, 0x00 }; /* E=1 */
106085732ac8SCy Schubert 		const char *url = data->t_c_server_url, *pos;
106185732ac8SCy Schubert 		char *url2, *end2, *pos2;
106285732ac8SCy Schubert 		size_t url_len;
106385732ac8SCy Schubert 
106485732ac8SCy Schubert 		if (!radius_msg_add_wfa(
106585732ac8SCy Schubert 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING,
106685732ac8SCy Schubert 			    buf, sizeof(buf))) {
106785732ac8SCy Schubert 			RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering");
106885732ac8SCy Schubert 			radius_msg_free(msg);
106985732ac8SCy Schubert 			return NULL;
107085732ac8SCy Schubert 		}
107185732ac8SCy Schubert 
107285732ac8SCy Schubert 		if (!url) {
107385732ac8SCy Schubert 			RADIUS_DEBUG("No t_c_server_url configured");
107485732ac8SCy Schubert 			radius_msg_free(msg);
107585732ac8SCy Schubert 			return NULL;
107685732ac8SCy Schubert 		}
107785732ac8SCy Schubert 
107885732ac8SCy Schubert 		pos = os_strstr(url, "@1@");
107985732ac8SCy Schubert 		if (!pos) {
108085732ac8SCy Schubert 			RADIUS_DEBUG("No @1@ macro in t_c_server_url");
108185732ac8SCy Schubert 			radius_msg_free(msg);
108285732ac8SCy Schubert 			return NULL;
108385732ac8SCy Schubert 		}
108485732ac8SCy Schubert 
108585732ac8SCy Schubert 		url_len = os_strlen(url) + ETH_ALEN * 3 - 1 - 3;
108685732ac8SCy Schubert 		url2 = os_malloc(url_len + 1);
108785732ac8SCy Schubert 		if (!url2) {
108885732ac8SCy Schubert 			RADIUS_DEBUG("Failed to allocate room for T&C Server URL");
108985732ac8SCy Schubert 			os_free(url2);
109085732ac8SCy Schubert 			radius_msg_free(msg);
109185732ac8SCy Schubert 			return NULL;
109285732ac8SCy Schubert 		}
109385732ac8SCy Schubert 		pos2 = url2;
109485732ac8SCy Schubert 		end2 = url2 + url_len + 1;
109585732ac8SCy Schubert 		os_memcpy(pos2, url, pos - url);
109685732ac8SCy Schubert 		pos2 += pos - url;
109785732ac8SCy Schubert 		os_snprintf(pos2, end2 - pos2, MACSTR, MAC2STR(sess->mac_addr));
109885732ac8SCy Schubert 		pos2 += ETH_ALEN * 3 - 1;
109985732ac8SCy Schubert 		os_memcpy(pos2, pos + 3, os_strlen(pos + 3));
110085732ac8SCy Schubert 		if (!radius_msg_add_wfa(msg,
110185732ac8SCy Schubert 					RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL,
110285732ac8SCy Schubert 					(const u8 *) url2, url_len)) {
110385732ac8SCy Schubert 			RADIUS_DEBUG("Failed to add WFA-HS20-T-C-URL");
110485732ac8SCy Schubert 			os_free(url2);
110585732ac8SCy Schubert 			radius_msg_free(msg);
110685732ac8SCy Schubert 			return NULL;
110785732ac8SCy Schubert 		}
110885732ac8SCy Schubert 		os_free(url2);
110985732ac8SCy Schubert 
111085732ac8SCy Schubert 		radius_srv_hs20_t_c_pending(sess);
111185732ac8SCy Schubert 	}
11125b9c547cSRui Paulo #endif /* CONFIG_HS20 */
11135b9c547cSRui Paulo 
111439beb93cSSam Leffler 	if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
111539beb93cSSam Leffler 		RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
111639beb93cSSam Leffler 		radius_msg_free(msg);
111739beb93cSSam Leffler 		return NULL;
111839beb93cSSam Leffler 	}
111939beb93cSSam Leffler 
11205b9c547cSRui Paulo 	if (code == RADIUS_CODE_ACCESS_ACCEPT) {
11215b9c547cSRui Paulo 		struct hostapd_radius_attr *attr;
11225b9c547cSRui Paulo 		for (attr = sess->accept_attr; attr; attr = attr->next) {
11235b9c547cSRui Paulo 			if (!radius_msg_add_attr(msg, attr->type,
11245b9c547cSRui Paulo 						 wpabuf_head(attr->val),
11255b9c547cSRui Paulo 						 wpabuf_len(attr->val))) {
11265b9c547cSRui Paulo 				wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
11275b9c547cSRui Paulo 				radius_msg_free(msg);
11285b9c547cSRui Paulo 				return NULL;
11295b9c547cSRui Paulo 			}
11305b9c547cSRui Paulo 		}
11315b9c547cSRui Paulo 	}
11325b9c547cSRui Paulo 
113385732ac8SCy Schubert 	if (code == RADIUS_CODE_ACCESS_REJECT) {
113485732ac8SCy Schubert 		if (radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE,
113585732ac8SCy Schubert 					      reason) < 0) {
113685732ac8SCy Schubert 			RADIUS_DEBUG("Failed to add WLAN-Reason-Code attribute");
113785732ac8SCy Schubert 			radius_msg_free(msg);
113885732ac8SCy Schubert 			return NULL;
113985732ac8SCy Schubert 		}
114085732ac8SCy Schubert 	}
114185732ac8SCy Schubert 
11425b9c547cSRui Paulo 	if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
11435b9c547cSRui Paulo 				  client->shared_secret_len,
11445b9c547cSRui Paulo 				  hdr->authenticator) < 0) {
11455b9c547cSRui Paulo 		RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
11465b9c547cSRui Paulo 	}
11475b9c547cSRui Paulo 
114885732ac8SCy Schubert 	if (code == RADIUS_CODE_ACCESS_ACCEPT)
114985732ac8SCy Schubert 		radius_server_add_session(sess);
115085732ac8SCy Schubert 
11515b9c547cSRui Paulo 	return msg;
11525b9c547cSRui Paulo }
11535b9c547cSRui Paulo 
11545b9c547cSRui Paulo 
11555b9c547cSRui Paulo static struct radius_msg *
11565b9c547cSRui Paulo radius_server_macacl(struct radius_server_data *data,
11575b9c547cSRui Paulo 		     struct radius_client *client,
11585b9c547cSRui Paulo 		     struct radius_session *sess,
11595b9c547cSRui Paulo 		     struct radius_msg *request)
11605b9c547cSRui Paulo {
11615b9c547cSRui Paulo 	struct radius_msg *msg;
11625b9c547cSRui Paulo 	int code;
11635b9c547cSRui Paulo 	struct radius_hdr *hdr = radius_msg_get_hdr(request);
11645b9c547cSRui Paulo 	u8 *pw;
11655b9c547cSRui Paulo 	size_t pw_len;
11665b9c547cSRui Paulo 
11675b9c547cSRui Paulo 	code = RADIUS_CODE_ACCESS_ACCEPT;
11685b9c547cSRui Paulo 
11695b9c547cSRui Paulo 	if (radius_msg_get_attr_ptr(request, RADIUS_ATTR_USER_PASSWORD, &pw,
11705b9c547cSRui Paulo 				    &pw_len, NULL) < 0) {
11715b9c547cSRui Paulo 		RADIUS_DEBUG("Could not get User-Password");
11725b9c547cSRui Paulo 		code = RADIUS_CODE_ACCESS_REJECT;
11735b9c547cSRui Paulo 	} else {
11745b9c547cSRui Paulo 		int res;
11755b9c547cSRui Paulo 		struct eap_user tmp;
11765b9c547cSRui Paulo 
11775b9c547cSRui Paulo 		os_memset(&tmp, 0, sizeof(tmp));
11785b9c547cSRui Paulo 		res = data->get_eap_user(data->conf_ctx, (u8 *) sess->username,
11795b9c547cSRui Paulo 					 os_strlen(sess->username), 0, &tmp);
11805b9c547cSRui Paulo 		if (res || !tmp.macacl || tmp.password == NULL) {
11815b9c547cSRui Paulo 			RADIUS_DEBUG("No MAC ACL user entry");
11825b9c547cSRui Paulo 			bin_clear_free(tmp.password, tmp.password_len);
11835b9c547cSRui Paulo 			code = RADIUS_CODE_ACCESS_REJECT;
11845b9c547cSRui Paulo 		} else {
11855b9c547cSRui Paulo 			u8 buf[128];
11865b9c547cSRui Paulo 			res = radius_user_password_hide(
11875b9c547cSRui Paulo 				request, tmp.password, tmp.password_len,
11885b9c547cSRui Paulo 				(u8 *) client->shared_secret,
11895b9c547cSRui Paulo 				client->shared_secret_len,
11905b9c547cSRui Paulo 				buf, sizeof(buf));
11915b9c547cSRui Paulo 			bin_clear_free(tmp.password, tmp.password_len);
11925b9c547cSRui Paulo 
11935b9c547cSRui Paulo 			if (res < 0 || pw_len != (size_t) res ||
11945b9c547cSRui Paulo 			    os_memcmp_const(pw, buf, res) != 0) {
11955b9c547cSRui Paulo 				RADIUS_DEBUG("Incorrect User-Password");
11965b9c547cSRui Paulo 				code = RADIUS_CODE_ACCESS_REJECT;
11975b9c547cSRui Paulo 			}
11985b9c547cSRui Paulo 		}
11995b9c547cSRui Paulo 	}
12005b9c547cSRui Paulo 
12015b9c547cSRui Paulo 	msg = radius_msg_new(code, hdr->identifier);
12025b9c547cSRui Paulo 	if (msg == NULL) {
12035b9c547cSRui Paulo 		RADIUS_DEBUG("Failed to allocate reply message");
12045b9c547cSRui Paulo 		return NULL;
12055b9c547cSRui Paulo 	}
12065b9c547cSRui Paulo 
12075b9c547cSRui Paulo 	if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
12085b9c547cSRui Paulo 		RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
12095b9c547cSRui Paulo 		radius_msg_free(msg);
12105b9c547cSRui Paulo 		return NULL;
12115b9c547cSRui Paulo 	}
12125b9c547cSRui Paulo 
12135b9c547cSRui Paulo 	if (code == RADIUS_CODE_ACCESS_ACCEPT) {
12145b9c547cSRui Paulo 		struct hostapd_radius_attr *attr;
12155b9c547cSRui Paulo 		for (attr = sess->accept_attr; attr; attr = attr->next) {
12165b9c547cSRui Paulo 			if (!radius_msg_add_attr(msg, attr->type,
12175b9c547cSRui Paulo 						 wpabuf_head(attr->val),
12185b9c547cSRui Paulo 						 wpabuf_len(attr->val))) {
12195b9c547cSRui Paulo 				wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
12205b9c547cSRui Paulo 				radius_msg_free(msg);
12215b9c547cSRui Paulo 				return NULL;
12225b9c547cSRui Paulo 			}
12235b9c547cSRui Paulo 		}
12245b9c547cSRui Paulo 	}
12255b9c547cSRui Paulo 
122639beb93cSSam Leffler 	if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
122739beb93cSSam Leffler 				  client->shared_secret_len,
1228e28a4053SRui Paulo 				  hdr->authenticator) < 0) {
122939beb93cSSam Leffler 		RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
123039beb93cSSam Leffler 	}
123139beb93cSSam Leffler 
123239beb93cSSam Leffler 	return msg;
123339beb93cSSam Leffler }
123439beb93cSSam Leffler 
123539beb93cSSam Leffler 
123639beb93cSSam Leffler static int radius_server_reject(struct radius_server_data *data,
123739beb93cSSam Leffler 				struct radius_client *client,
123839beb93cSSam Leffler 				struct radius_msg *request,
123939beb93cSSam Leffler 				struct sockaddr *from, socklen_t fromlen,
124039beb93cSSam Leffler 				const char *from_addr, int from_port)
124139beb93cSSam Leffler {
124239beb93cSSam Leffler 	struct radius_msg *msg;
124339beb93cSSam Leffler 	int ret = 0;
124439beb93cSSam Leffler 	struct eap_hdr eapfail;
1245e28a4053SRui Paulo 	struct wpabuf *buf;
1246e28a4053SRui Paulo 	struct radius_hdr *hdr = radius_msg_get_hdr(request);
124739beb93cSSam Leffler 
124839beb93cSSam Leffler 	RADIUS_DEBUG("Reject invalid request from %s:%d",
124939beb93cSSam Leffler 		     from_addr, from_port);
125039beb93cSSam Leffler 
1251e28a4053SRui Paulo 	msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, hdr->identifier);
125239beb93cSSam Leffler 	if (msg == NULL) {
125339beb93cSSam Leffler 		return -1;
125439beb93cSSam Leffler 	}
125539beb93cSSam Leffler 
125639beb93cSSam Leffler 	os_memset(&eapfail, 0, sizeof(eapfail));
125739beb93cSSam Leffler 	eapfail.code = EAP_CODE_FAILURE;
125839beb93cSSam Leffler 	eapfail.identifier = 0;
125939beb93cSSam Leffler 	eapfail.length = host_to_be16(sizeof(eapfail));
126039beb93cSSam Leffler 
126139beb93cSSam Leffler 	if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) {
126239beb93cSSam Leffler 		RADIUS_DEBUG("Failed to add EAP-Message attribute");
126339beb93cSSam Leffler 	}
126439beb93cSSam Leffler 
126539beb93cSSam Leffler 	if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
126639beb93cSSam Leffler 		RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
126739beb93cSSam Leffler 		radius_msg_free(msg);
126839beb93cSSam Leffler 		return -1;
126939beb93cSSam Leffler 	}
127039beb93cSSam Leffler 
127139beb93cSSam Leffler 	if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
127239beb93cSSam Leffler 				  client->shared_secret_len,
1273e28a4053SRui Paulo 				  hdr->authenticator) <
1274e28a4053SRui Paulo 	    0) {
127539beb93cSSam Leffler 		RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
127639beb93cSSam Leffler 	}
127739beb93cSSam Leffler 
127839beb93cSSam Leffler 	if (wpa_debug_level <= MSG_MSGDUMP) {
127939beb93cSSam Leffler 		radius_msg_dump(msg);
128039beb93cSSam Leffler 	}
128139beb93cSSam Leffler 
128239beb93cSSam Leffler 	data->counters.access_rejects++;
128339beb93cSSam Leffler 	client->counters.access_rejects++;
1284e28a4053SRui Paulo 	buf = radius_msg_get_buf(msg);
1285e28a4053SRui Paulo 	if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
128639beb93cSSam Leffler 		   (struct sockaddr *) from, sizeof(*from)) < 0) {
12875b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno));
128839beb93cSSam Leffler 		ret = -1;
128939beb93cSSam Leffler 	}
129039beb93cSSam Leffler 
129139beb93cSSam Leffler 	radius_msg_free(msg);
129239beb93cSSam Leffler 
129339beb93cSSam Leffler 	return ret;
129439beb93cSSam Leffler }
129539beb93cSSam Leffler 
129639beb93cSSam Leffler 
129785732ac8SCy Schubert static void radius_server_hs20_t_c_check(struct radius_session *sess,
129885732ac8SCy Schubert 					 struct radius_msg *msg)
129985732ac8SCy Schubert {
130085732ac8SCy Schubert #ifdef CONFIG_HS20
130185732ac8SCy Schubert 	u8 *buf, *pos, *end, type, sublen, *timestamp = NULL;
130285732ac8SCy Schubert 	size_t len;
130385732ac8SCy Schubert 
130485732ac8SCy Schubert 	buf = NULL;
130585732ac8SCy Schubert 	for (;;) {
130685732ac8SCy Schubert 		if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
130785732ac8SCy Schubert 					    &buf, &len, buf) < 0)
130885732ac8SCy Schubert 			break;
130985732ac8SCy Schubert 		if (len < 6)
131085732ac8SCy Schubert 			continue;
131185732ac8SCy Schubert 		pos = buf;
131285732ac8SCy Schubert 		end = buf + len;
131385732ac8SCy Schubert 		if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
131485732ac8SCy Schubert 			continue;
131585732ac8SCy Schubert 		pos += 4;
131685732ac8SCy Schubert 
131785732ac8SCy Schubert 		type = *pos++;
131885732ac8SCy Schubert 		sublen = *pos++;
131985732ac8SCy Schubert 		if (sublen < 2)
132085732ac8SCy Schubert 			continue; /* invalid length */
132185732ac8SCy Schubert 		sublen -= 2; /* skip header */
132285732ac8SCy Schubert 		if (pos + sublen > end)
132385732ac8SCy Schubert 			continue; /* invalid WFA VSA */
132485732ac8SCy Schubert 
132585732ac8SCy Schubert 		if (type == RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP && len >= 4) {
132685732ac8SCy Schubert 			timestamp = pos;
132785732ac8SCy Schubert 			break;
132885732ac8SCy Schubert 		}
132985732ac8SCy Schubert 	}
133085732ac8SCy Schubert 
133185732ac8SCy Schubert 	if (!timestamp)
133285732ac8SCy Schubert 		return;
133385732ac8SCy Schubert 	RADIUS_DEBUG("HS20-Timestamp: %u", WPA_GET_BE32(timestamp));
133485732ac8SCy Schubert 	if (sess->t_c_timestamp != WPA_GET_BE32(timestamp)) {
133585732ac8SCy Schubert 		RADIUS_DEBUG("Last read T&C timestamp does not match HS20-Timestamp --> require filtering");
133685732ac8SCy Schubert 		sess->t_c_filtering = 1;
133785732ac8SCy Schubert 	}
133885732ac8SCy Schubert #endif /* CONFIG_HS20 */
133985732ac8SCy Schubert }
134085732ac8SCy Schubert 
134185732ac8SCy Schubert 
134239beb93cSSam Leffler static int radius_server_request(struct radius_server_data *data,
134339beb93cSSam Leffler 				 struct radius_msg *msg,
134439beb93cSSam Leffler 				 struct sockaddr *from, socklen_t fromlen,
134539beb93cSSam Leffler 				 struct radius_client *client,
134639beb93cSSam Leffler 				 const char *from_addr, int from_port,
134739beb93cSSam Leffler 				 struct radius_session *force_sess)
134839beb93cSSam Leffler {
1349f05cddf9SRui Paulo 	struct wpabuf *eap = NULL;
135039beb93cSSam Leffler 	int res, state_included = 0;
135139beb93cSSam Leffler 	u8 statebuf[4];
135239beb93cSSam Leffler 	unsigned int state;
135339beb93cSSam Leffler 	struct radius_session *sess;
135439beb93cSSam Leffler 	struct radius_msg *reply;
13553157ba21SRui Paulo 	int is_complete = 0;
135639beb93cSSam Leffler 
135739beb93cSSam Leffler 	if (force_sess)
135839beb93cSSam Leffler 		sess = force_sess;
135939beb93cSSam Leffler 	else {
136039beb93cSSam Leffler 		res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf,
136139beb93cSSam Leffler 					  sizeof(statebuf));
136239beb93cSSam Leffler 		state_included = res >= 0;
136339beb93cSSam Leffler 		if (res == sizeof(statebuf)) {
136439beb93cSSam Leffler 			state = WPA_GET_BE32(statebuf);
136539beb93cSSam Leffler 			sess = radius_server_get_session(client, state);
136639beb93cSSam Leffler 		} else {
136739beb93cSSam Leffler 			sess = NULL;
136839beb93cSSam Leffler 		}
136939beb93cSSam Leffler 	}
137039beb93cSSam Leffler 
137139beb93cSSam Leffler 	if (sess) {
137239beb93cSSam Leffler 		RADIUS_DEBUG("Request for session 0x%x", sess->sess_id);
137339beb93cSSam Leffler 	} else if (state_included) {
137439beb93cSSam Leffler 		RADIUS_DEBUG("State attribute included but no session found");
137539beb93cSSam Leffler 		radius_server_reject(data, client, msg, from, fromlen,
137639beb93cSSam Leffler 				     from_addr, from_port);
137739beb93cSSam Leffler 		return -1;
137839beb93cSSam Leffler 	} else {
13795b9c547cSRui Paulo 		sess = radius_server_get_new_session(data, client, msg,
13805b9c547cSRui Paulo 						     from_addr);
138139beb93cSSam Leffler 		if (sess == NULL) {
138239beb93cSSam Leffler 			RADIUS_DEBUG("Could not create a new session");
138339beb93cSSam Leffler 			radius_server_reject(data, client, msg, from, fromlen,
138439beb93cSSam Leffler 					     from_addr, from_port);
138539beb93cSSam Leffler 			return -1;
138639beb93cSSam Leffler 		}
138739beb93cSSam Leffler 	}
138839beb93cSSam Leffler 
138939beb93cSSam Leffler 	if (sess->last_from_port == from_port &&
1390e28a4053SRui Paulo 	    sess->last_identifier == radius_msg_get_hdr(msg)->identifier &&
1391e28a4053SRui Paulo 	    os_memcmp(sess->last_authenticator,
1392e28a4053SRui Paulo 		      radius_msg_get_hdr(msg)->authenticator, 16) == 0) {
139339beb93cSSam Leffler 		RADIUS_DEBUG("Duplicate message from %s", from_addr);
139439beb93cSSam Leffler 		data->counters.dup_access_requests++;
139539beb93cSSam Leffler 		client->counters.dup_access_requests++;
139639beb93cSSam Leffler 
139739beb93cSSam Leffler 		if (sess->last_reply) {
1398e28a4053SRui Paulo 			struct wpabuf *buf;
1399e28a4053SRui Paulo 			buf = radius_msg_get_buf(sess->last_reply);
1400e28a4053SRui Paulo 			res = sendto(data->auth_sock, wpabuf_head(buf),
1401e28a4053SRui Paulo 				     wpabuf_len(buf), 0,
140239beb93cSSam Leffler 				     (struct sockaddr *) from, fromlen);
140339beb93cSSam Leffler 			if (res < 0) {
14045b9c547cSRui Paulo 				wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
14055b9c547cSRui Paulo 					   strerror(errno));
140639beb93cSSam Leffler 			}
140739beb93cSSam Leffler 			return 0;
140839beb93cSSam Leffler 		}
140939beb93cSSam Leffler 
141039beb93cSSam Leffler 		RADIUS_DEBUG("No previous reply available for duplicate "
141139beb93cSSam Leffler 			     "message");
141239beb93cSSam Leffler 		return -1;
141339beb93cSSam Leffler 	}
141439beb93cSSam Leffler 
1415f05cddf9SRui Paulo 	eap = radius_msg_get_eap(msg);
14165b9c547cSRui Paulo 	if (eap == NULL && sess->macacl) {
14175b9c547cSRui Paulo 		reply = radius_server_macacl(data, client, sess, msg);
14185b9c547cSRui Paulo 		if (reply == NULL)
14195b9c547cSRui Paulo 			return -1;
14205b9c547cSRui Paulo 		goto send_reply;
14215b9c547cSRui Paulo 	}
142239beb93cSSam Leffler 	if (eap == NULL) {
142339beb93cSSam Leffler 		RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
142439beb93cSSam Leffler 			     from_addr);
142539beb93cSSam Leffler 		data->counters.packets_dropped++;
142639beb93cSSam Leffler 		client->counters.packets_dropped++;
142739beb93cSSam Leffler 		return -1;
142839beb93cSSam Leffler 	}
142939beb93cSSam Leffler 
1430f05cddf9SRui Paulo 	RADIUS_DUMP("Received EAP data", wpabuf_head(eap), wpabuf_len(eap));
143139beb93cSSam Leffler 
143239beb93cSSam Leffler 	/* FIX: if Code is Request, Success, or Failure, send Access-Reject;
143339beb93cSSam Leffler 	 * RFC3579 Sect. 2.6.2.
143439beb93cSSam Leffler 	 * Include EAP-Response/Nak with no preferred method if
143539beb93cSSam Leffler 	 * code == request.
143639beb93cSSam Leffler 	 * If code is not 1-4, discard the packet silently.
143739beb93cSSam Leffler 	 * Or is this already done by the EAP state machine? */
143839beb93cSSam Leffler 
143939beb93cSSam Leffler 	wpabuf_free(sess->eap_if->eapRespData);
1440f05cddf9SRui Paulo 	sess->eap_if->eapRespData = eap;
1441*c1d255d3SCy Schubert 	sess->eap_if->eapResp = true;
144239beb93cSSam Leffler 	eap_server_sm_step(sess->eap);
144339beb93cSSam Leffler 
144439beb93cSSam Leffler 	if ((sess->eap_if->eapReq || sess->eap_if->eapSuccess ||
144539beb93cSSam Leffler 	     sess->eap_if->eapFail) && sess->eap_if->eapReqData) {
144639beb93cSSam Leffler 		RADIUS_DUMP("EAP data from the state machine",
144739beb93cSSam Leffler 			    wpabuf_head(sess->eap_if->eapReqData),
144839beb93cSSam Leffler 			    wpabuf_len(sess->eap_if->eapReqData));
144939beb93cSSam Leffler 	} else if (sess->eap_if->eapFail) {
145039beb93cSSam Leffler 		RADIUS_DEBUG("No EAP data from the state machine, but eapFail "
145139beb93cSSam Leffler 			     "set");
145239beb93cSSam Leffler 	} else if (eap_sm_method_pending(sess->eap)) {
145339beb93cSSam Leffler 		radius_msg_free(sess->last_msg);
145439beb93cSSam Leffler 		sess->last_msg = msg;
145539beb93cSSam Leffler 		sess->last_from_port = from_port;
145639beb93cSSam Leffler 		os_free(sess->last_from_addr);
145739beb93cSSam Leffler 		sess->last_from_addr = os_strdup(from_addr);
145839beb93cSSam Leffler 		sess->last_fromlen = fromlen;
145939beb93cSSam Leffler 		os_memcpy(&sess->last_from, from, fromlen);
146039beb93cSSam Leffler 		return -2;
146139beb93cSSam Leffler 	} else {
146239beb93cSSam Leffler 		RADIUS_DEBUG("No EAP data from the state machine - ignore this"
146339beb93cSSam Leffler 			     " Access-Request silently (assuming it was a "
146439beb93cSSam Leffler 			     "duplicate)");
146539beb93cSSam Leffler 		data->counters.packets_dropped++;
146639beb93cSSam Leffler 		client->counters.packets_dropped++;
146739beb93cSSam Leffler 		return -1;
146839beb93cSSam Leffler 	}
146939beb93cSSam Leffler 
14703157ba21SRui Paulo 	if (sess->eap_if->eapSuccess || sess->eap_if->eapFail)
14713157ba21SRui Paulo 		is_complete = 1;
147285732ac8SCy Schubert 	if (sess->eap_if->eapFail) {
14735b9c547cSRui Paulo 		srv_log(sess, "EAP authentication failed");
147485732ac8SCy Schubert 		db_update_last_msk(sess, "FAIL");
147585732ac8SCy Schubert 	} else if (sess->eap_if->eapSuccess) {
14765b9c547cSRui Paulo 		srv_log(sess, "EAP authentication succeeded");
147785732ac8SCy Schubert 	}
147885732ac8SCy Schubert 
147985732ac8SCy Schubert 	if (sess->eap_if->eapSuccess)
148085732ac8SCy Schubert 		radius_server_hs20_t_c_check(sess, msg);
14813157ba21SRui Paulo 
148239beb93cSSam Leffler 	reply = radius_server_encapsulate_eap(data, client, sess, msg);
148339beb93cSSam Leffler 
14845b9c547cSRui Paulo send_reply:
148539beb93cSSam Leffler 	if (reply) {
1486e28a4053SRui Paulo 		struct wpabuf *buf;
1487e28a4053SRui Paulo 		struct radius_hdr *hdr;
1488e28a4053SRui Paulo 
148939beb93cSSam Leffler 		RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port);
149039beb93cSSam Leffler 		if (wpa_debug_level <= MSG_MSGDUMP) {
149139beb93cSSam Leffler 			radius_msg_dump(reply);
149239beb93cSSam Leffler 		}
149339beb93cSSam Leffler 
1494e28a4053SRui Paulo 		switch (radius_msg_get_hdr(reply)->code) {
149539beb93cSSam Leffler 		case RADIUS_CODE_ACCESS_ACCEPT:
14965b9c547cSRui Paulo 			srv_log(sess, "Sending Access-Accept");
149739beb93cSSam Leffler 			data->counters.access_accepts++;
149839beb93cSSam Leffler 			client->counters.access_accepts++;
149939beb93cSSam Leffler 			break;
150039beb93cSSam Leffler 		case RADIUS_CODE_ACCESS_REJECT:
15015b9c547cSRui Paulo 			srv_log(sess, "Sending Access-Reject");
150239beb93cSSam Leffler 			data->counters.access_rejects++;
150339beb93cSSam Leffler 			client->counters.access_rejects++;
150439beb93cSSam Leffler 			break;
150539beb93cSSam Leffler 		case RADIUS_CODE_ACCESS_CHALLENGE:
150639beb93cSSam Leffler 			data->counters.access_challenges++;
150739beb93cSSam Leffler 			client->counters.access_challenges++;
150839beb93cSSam Leffler 			break;
150939beb93cSSam Leffler 		}
1510e28a4053SRui Paulo 		buf = radius_msg_get_buf(reply);
1511e28a4053SRui Paulo 		res = sendto(data->auth_sock, wpabuf_head(buf),
1512e28a4053SRui Paulo 			     wpabuf_len(buf), 0,
151339beb93cSSam Leffler 			     (struct sockaddr *) from, fromlen);
151439beb93cSSam Leffler 		if (res < 0) {
15155b9c547cSRui Paulo 			wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
15165b9c547cSRui Paulo 				   strerror(errno));
151739beb93cSSam Leffler 		}
151839beb93cSSam Leffler 		radius_msg_free(sess->last_reply);
151939beb93cSSam Leffler 		sess->last_reply = reply;
152039beb93cSSam Leffler 		sess->last_from_port = from_port;
1521e28a4053SRui Paulo 		hdr = radius_msg_get_hdr(msg);
1522e28a4053SRui Paulo 		sess->last_identifier = hdr->identifier;
1523e28a4053SRui Paulo 		os_memcpy(sess->last_authenticator, hdr->authenticator, 16);
152439beb93cSSam Leffler 	} else {
152539beb93cSSam Leffler 		data->counters.packets_dropped++;
152639beb93cSSam Leffler 		client->counters.packets_dropped++;
152739beb93cSSam Leffler 	}
152839beb93cSSam Leffler 
15293157ba21SRui Paulo 	if (is_complete) {
153039beb93cSSam Leffler 		RADIUS_DEBUG("Removing completed session 0x%x after timeout",
153139beb93cSSam Leffler 			     sess->sess_id);
153239beb93cSSam Leffler 		eloop_cancel_timeout(radius_server_session_remove_timeout,
153339beb93cSSam Leffler 				     data, sess);
153485732ac8SCy Schubert 		eloop_register_timeout(RADIUS_SESSION_MAINTAIN, 0,
153539beb93cSSam Leffler 				       radius_server_session_remove_timeout,
153639beb93cSSam Leffler 				       data, sess);
153739beb93cSSam Leffler 	}
153839beb93cSSam Leffler 
153939beb93cSSam Leffler 	return 0;
154039beb93cSSam Leffler }
154139beb93cSSam Leffler 
154239beb93cSSam Leffler 
154385732ac8SCy Schubert static void
154485732ac8SCy Schubert radius_server_receive_disconnect_resp(struct radius_server_data *data,
154585732ac8SCy Schubert 				      struct radius_client *client,
154685732ac8SCy Schubert 				      struct radius_msg *msg, int ack)
154785732ac8SCy Schubert {
154885732ac8SCy Schubert 	struct radius_hdr *hdr;
154985732ac8SCy Schubert 
155085732ac8SCy Schubert 	if (!client->pending_dac_disconnect_req) {
155185732ac8SCy Schubert 		RADIUS_DEBUG("Ignore unexpected Disconnect response");
155285732ac8SCy Schubert 		radius_msg_free(msg);
155385732ac8SCy Schubert 		return;
155485732ac8SCy Schubert 	}
155585732ac8SCy Schubert 
155685732ac8SCy Schubert 	hdr = radius_msg_get_hdr(msg);
155785732ac8SCy Schubert 	if (hdr->identifier != client->pending_dac_disconnect_id) {
155885732ac8SCy Schubert 		RADIUS_DEBUG("Ignore unexpected Disconnect response with unexpected identifier %u (expected %u)",
155985732ac8SCy Schubert 			     hdr->identifier,
156085732ac8SCy Schubert 			     client->pending_dac_disconnect_id);
156185732ac8SCy Schubert 		radius_msg_free(msg);
156285732ac8SCy Schubert 		return;
156385732ac8SCy Schubert 	}
156485732ac8SCy Schubert 
156585732ac8SCy Schubert 	if (radius_msg_verify(msg, (const u8 *) client->shared_secret,
156685732ac8SCy Schubert 			      client->shared_secret_len,
156785732ac8SCy Schubert 			      client->pending_dac_disconnect_req, 0)) {
156885732ac8SCy Schubert 		RADIUS_DEBUG("Ignore Disconnect response with invalid authenticator");
156985732ac8SCy Schubert 		radius_msg_free(msg);
157085732ac8SCy Schubert 		return;
157185732ac8SCy Schubert 	}
157285732ac8SCy Schubert 
157385732ac8SCy Schubert 	RADIUS_DEBUG("Disconnect-%s received for " MACSTR,
157485732ac8SCy Schubert 		     ack ? "ACK" : "NAK",
157585732ac8SCy Schubert 		     MAC2STR(client->pending_dac_disconnect_addr));
157685732ac8SCy Schubert 
157785732ac8SCy Schubert 	radius_msg_free(msg);
157885732ac8SCy Schubert 	radius_msg_free(client->pending_dac_disconnect_req);
157985732ac8SCy Schubert 	client->pending_dac_disconnect_req = NULL;
158085732ac8SCy Schubert }
158185732ac8SCy Schubert 
158285732ac8SCy Schubert 
158385732ac8SCy Schubert static void radius_server_receive_coa_resp(struct radius_server_data *data,
158485732ac8SCy Schubert 					   struct radius_client *client,
158585732ac8SCy Schubert 					   struct radius_msg *msg, int ack)
158685732ac8SCy Schubert {
158785732ac8SCy Schubert 	struct radius_hdr *hdr;
158885732ac8SCy Schubert #ifdef CONFIG_SQLITE
158985732ac8SCy Schubert 	char addrtxt[3 * ETH_ALEN];
159085732ac8SCy Schubert 	char *sql;
159185732ac8SCy Schubert 	int res;
159285732ac8SCy Schubert #endif /* CONFIG_SQLITE */
159385732ac8SCy Schubert 
159485732ac8SCy Schubert 	if (!client->pending_dac_coa_req) {
159585732ac8SCy Schubert 		RADIUS_DEBUG("Ignore unexpected CoA response");
159685732ac8SCy Schubert 		radius_msg_free(msg);
159785732ac8SCy Schubert 		return;
159885732ac8SCy Schubert 	}
159985732ac8SCy Schubert 
160085732ac8SCy Schubert 	hdr = radius_msg_get_hdr(msg);
160185732ac8SCy Schubert 	if (hdr->identifier != client->pending_dac_coa_id) {
160285732ac8SCy Schubert 		RADIUS_DEBUG("Ignore unexpected CoA response with unexpected identifier %u (expected %u)",
160385732ac8SCy Schubert 			     hdr->identifier,
160485732ac8SCy Schubert 			     client->pending_dac_coa_id);
160585732ac8SCy Schubert 		radius_msg_free(msg);
160685732ac8SCy Schubert 		return;
160785732ac8SCy Schubert 	}
160885732ac8SCy Schubert 
160985732ac8SCy Schubert 	if (radius_msg_verify(msg, (const u8 *) client->shared_secret,
161085732ac8SCy Schubert 			      client->shared_secret_len,
161185732ac8SCy Schubert 			      client->pending_dac_coa_req, 0)) {
161285732ac8SCy Schubert 		RADIUS_DEBUG("Ignore CoA response with invalid authenticator");
161385732ac8SCy Schubert 		radius_msg_free(msg);
161485732ac8SCy Schubert 		return;
161585732ac8SCy Schubert 	}
161685732ac8SCy Schubert 
161785732ac8SCy Schubert 	RADIUS_DEBUG("CoA-%s received for " MACSTR,
161885732ac8SCy Schubert 		     ack ? "ACK" : "NAK",
161985732ac8SCy Schubert 		     MAC2STR(client->pending_dac_coa_addr));
162085732ac8SCy Schubert 
162185732ac8SCy Schubert 	radius_msg_free(msg);
162285732ac8SCy Schubert 	radius_msg_free(client->pending_dac_coa_req);
162385732ac8SCy Schubert 	client->pending_dac_coa_req = NULL;
162485732ac8SCy Schubert 
162585732ac8SCy Schubert #ifdef CONFIG_SQLITE
162685732ac8SCy Schubert 	if (!data->db)
162785732ac8SCy Schubert 		return;
162885732ac8SCy Schubert 
162985732ac8SCy Schubert 	os_snprintf(addrtxt, sizeof(addrtxt), MACSTR,
163085732ac8SCy Schubert 		    MAC2STR(client->pending_dac_coa_addr));
163185732ac8SCy Schubert 
163285732ac8SCy Schubert 	if (ack) {
163385732ac8SCy 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",
163485732ac8SCy Schubert 				      addrtxt);
163585732ac8SCy Schubert 	} else {
163685732ac8SCy Schubert 		sql = sqlite3_mprintf("UPDATE current_sessions SET waiting_coa_ack=0 WHERE mac_addr=%Q",
163785732ac8SCy Schubert 				      addrtxt);
163885732ac8SCy Schubert 	}
163985732ac8SCy Schubert 	if (!sql)
164085732ac8SCy Schubert 		return;
164185732ac8SCy Schubert 
164285732ac8SCy Schubert 	res = sqlite3_exec(data->db, sql, NULL, NULL, NULL);
164385732ac8SCy Schubert 	sqlite3_free(sql);
164485732ac8SCy Schubert 	if (res != SQLITE_OK) {
164585732ac8SCy Schubert 		RADIUS_ERROR("Failed to update current_sessions entry: %s",
164685732ac8SCy Schubert 			     sqlite3_errmsg(data->db));
164785732ac8SCy Schubert 		return;
164885732ac8SCy Schubert 	}
164985732ac8SCy Schubert #endif /* CONFIG_SQLITE */
165085732ac8SCy Schubert }
165185732ac8SCy Schubert 
165285732ac8SCy Schubert 
165339beb93cSSam Leffler static void radius_server_receive_auth(int sock, void *eloop_ctx,
165439beb93cSSam Leffler 				       void *sock_ctx)
165539beb93cSSam Leffler {
165639beb93cSSam Leffler 	struct radius_server_data *data = eloop_ctx;
165739beb93cSSam Leffler 	u8 *buf = NULL;
16583157ba21SRui Paulo 	union {
16593157ba21SRui Paulo 		struct sockaddr_storage ss;
16603157ba21SRui Paulo 		struct sockaddr_in sin;
16613157ba21SRui Paulo #ifdef CONFIG_IPV6
16623157ba21SRui Paulo 		struct sockaddr_in6 sin6;
16633157ba21SRui Paulo #endif /* CONFIG_IPV6 */
16643157ba21SRui Paulo 	} from;
166539beb93cSSam Leffler 	socklen_t fromlen;
166639beb93cSSam Leffler 	int len;
166739beb93cSSam Leffler 	struct radius_client *client = NULL;
166839beb93cSSam Leffler 	struct radius_msg *msg = NULL;
166939beb93cSSam Leffler 	char abuf[50];
167039beb93cSSam Leffler 	int from_port = 0;
167139beb93cSSam Leffler 
167239beb93cSSam Leffler 	buf = os_malloc(RADIUS_MAX_MSG_LEN);
167339beb93cSSam Leffler 	if (buf == NULL) {
167439beb93cSSam Leffler 		goto fail;
167539beb93cSSam Leffler 	}
167639beb93cSSam Leffler 
167739beb93cSSam Leffler 	fromlen = sizeof(from);
167839beb93cSSam Leffler 	len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
16793157ba21SRui Paulo 		       (struct sockaddr *) &from.ss, &fromlen);
168039beb93cSSam Leffler 	if (len < 0) {
16815b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s",
16825b9c547cSRui Paulo 			   strerror(errno));
168339beb93cSSam Leffler 		goto fail;
168439beb93cSSam Leffler 	}
168539beb93cSSam Leffler 
168639beb93cSSam Leffler #ifdef CONFIG_IPV6
168739beb93cSSam Leffler 	if (data->ipv6) {
16883157ba21SRui Paulo 		if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf,
16893157ba21SRui Paulo 			      sizeof(abuf)) == NULL)
169039beb93cSSam Leffler 			abuf[0] = '\0';
16913157ba21SRui Paulo 		from_port = ntohs(from.sin6.sin6_port);
169239beb93cSSam Leffler 		RADIUS_DEBUG("Received %d bytes from %s:%d",
169339beb93cSSam Leffler 			     len, abuf, from_port);
169439beb93cSSam Leffler 
169539beb93cSSam Leffler 		client = radius_server_get_client(data,
169639beb93cSSam Leffler 						  (struct in_addr *)
16973157ba21SRui Paulo 						  &from.sin6.sin6_addr, 1);
169839beb93cSSam Leffler 	}
169939beb93cSSam Leffler #endif /* CONFIG_IPV6 */
170039beb93cSSam Leffler 
170139beb93cSSam Leffler 	if (!data->ipv6) {
17023157ba21SRui Paulo 		os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
17033157ba21SRui Paulo 		from_port = ntohs(from.sin.sin_port);
170439beb93cSSam Leffler 		RADIUS_DEBUG("Received %d bytes from %s:%d",
170539beb93cSSam Leffler 			     len, abuf, from_port);
170639beb93cSSam Leffler 
17073157ba21SRui Paulo 		client = radius_server_get_client(data, &from.sin.sin_addr, 0);
170839beb93cSSam Leffler 	}
170939beb93cSSam Leffler 
171039beb93cSSam Leffler 	RADIUS_DUMP("Received data", buf, len);
171139beb93cSSam Leffler 
171239beb93cSSam Leffler 	if (client == NULL) {
171339beb93cSSam Leffler 		RADIUS_DEBUG("Unknown client %s - packet ignored", abuf);
171439beb93cSSam Leffler 		data->counters.invalid_requests++;
171539beb93cSSam Leffler 		goto fail;
171639beb93cSSam Leffler 	}
171739beb93cSSam Leffler 
171839beb93cSSam Leffler 	msg = radius_msg_parse(buf, len);
171939beb93cSSam Leffler 	if (msg == NULL) {
172039beb93cSSam Leffler 		RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
172139beb93cSSam Leffler 		data->counters.malformed_access_requests++;
172239beb93cSSam Leffler 		client->counters.malformed_access_requests++;
172339beb93cSSam Leffler 		goto fail;
172439beb93cSSam Leffler 	}
172539beb93cSSam Leffler 
172639beb93cSSam Leffler 	os_free(buf);
172739beb93cSSam Leffler 	buf = NULL;
172839beb93cSSam Leffler 
172939beb93cSSam Leffler 	if (wpa_debug_level <= MSG_MSGDUMP) {
173039beb93cSSam Leffler 		radius_msg_dump(msg);
173139beb93cSSam Leffler 	}
173239beb93cSSam Leffler 
173385732ac8SCy Schubert 	if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_ACK) {
173485732ac8SCy Schubert 		radius_server_receive_disconnect_resp(data, client, msg, 1);
173585732ac8SCy Schubert 		return;
173685732ac8SCy Schubert 	}
173785732ac8SCy Schubert 
173885732ac8SCy Schubert 	if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_NAK) {
173985732ac8SCy Schubert 		radius_server_receive_disconnect_resp(data, client, msg, 0);
174085732ac8SCy Schubert 		return;
174185732ac8SCy Schubert 	}
174285732ac8SCy Schubert 
174385732ac8SCy Schubert 	if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_ACK) {
174485732ac8SCy Schubert 		radius_server_receive_coa_resp(data, client, msg, 1);
174585732ac8SCy Schubert 		return;
174685732ac8SCy Schubert 	}
174785732ac8SCy Schubert 
174885732ac8SCy Schubert 	if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_NAK) {
174985732ac8SCy Schubert 		radius_server_receive_coa_resp(data, client, msg, 0);
175085732ac8SCy Schubert 		return;
175185732ac8SCy Schubert 	}
175285732ac8SCy Schubert 
1753e28a4053SRui Paulo 	if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCESS_REQUEST) {
1754e28a4053SRui Paulo 		RADIUS_DEBUG("Unexpected RADIUS code %d",
1755e28a4053SRui Paulo 			     radius_msg_get_hdr(msg)->code);
175639beb93cSSam Leffler 		data->counters.unknown_types++;
175739beb93cSSam Leffler 		client->counters.unknown_types++;
175839beb93cSSam Leffler 		goto fail;
175939beb93cSSam Leffler 	}
176039beb93cSSam Leffler 
176139beb93cSSam Leffler 	data->counters.access_requests++;
176239beb93cSSam Leffler 	client->counters.access_requests++;
176339beb93cSSam Leffler 
176439beb93cSSam Leffler 	if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret,
176539beb93cSSam Leffler 				       client->shared_secret_len, NULL)) {
176639beb93cSSam Leffler 		RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf);
176739beb93cSSam Leffler 		data->counters.bad_authenticators++;
176839beb93cSSam Leffler 		client->counters.bad_authenticators++;
176939beb93cSSam Leffler 		goto fail;
177039beb93cSSam Leffler 	}
177139beb93cSSam Leffler 
177239beb93cSSam Leffler 	if (radius_server_request(data, msg, (struct sockaddr *) &from,
177339beb93cSSam Leffler 				  fromlen, client, abuf, from_port, NULL) ==
177439beb93cSSam Leffler 	    -2)
177539beb93cSSam Leffler 		return; /* msg was stored with the session */
177639beb93cSSam Leffler 
177739beb93cSSam Leffler fail:
177839beb93cSSam Leffler 	radius_msg_free(msg);
177939beb93cSSam Leffler 	os_free(buf);
178039beb93cSSam Leffler }
178139beb93cSSam Leffler 
178239beb93cSSam Leffler 
17835b9c547cSRui Paulo static void radius_server_receive_acct(int sock, void *eloop_ctx,
17845b9c547cSRui Paulo 				       void *sock_ctx)
17855b9c547cSRui Paulo {
17865b9c547cSRui Paulo 	struct radius_server_data *data = eloop_ctx;
17875b9c547cSRui Paulo 	u8 *buf = NULL;
17885b9c547cSRui Paulo 	union {
17895b9c547cSRui Paulo 		struct sockaddr_storage ss;
17905b9c547cSRui Paulo 		struct sockaddr_in sin;
17915b9c547cSRui Paulo #ifdef CONFIG_IPV6
17925b9c547cSRui Paulo 		struct sockaddr_in6 sin6;
17935b9c547cSRui Paulo #endif /* CONFIG_IPV6 */
17945b9c547cSRui Paulo 	} from;
17955b9c547cSRui Paulo 	socklen_t fromlen;
17965b9c547cSRui Paulo 	int len, res;
17975b9c547cSRui Paulo 	struct radius_client *client = NULL;
17985b9c547cSRui Paulo 	struct radius_msg *msg = NULL, *resp = NULL;
17995b9c547cSRui Paulo 	char abuf[50];
18005b9c547cSRui Paulo 	int from_port = 0;
18015b9c547cSRui Paulo 	struct radius_hdr *hdr;
18025b9c547cSRui Paulo 	struct wpabuf *rbuf;
18035b9c547cSRui Paulo 
18045b9c547cSRui Paulo 	buf = os_malloc(RADIUS_MAX_MSG_LEN);
18055b9c547cSRui Paulo 	if (buf == NULL) {
18065b9c547cSRui Paulo 		goto fail;
18075b9c547cSRui Paulo 	}
18085b9c547cSRui Paulo 
18095b9c547cSRui Paulo 	fromlen = sizeof(from);
18105b9c547cSRui Paulo 	len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
18115b9c547cSRui Paulo 		       (struct sockaddr *) &from.ss, &fromlen);
18125b9c547cSRui Paulo 	if (len < 0) {
18135b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s",
18145b9c547cSRui Paulo 			   strerror(errno));
18155b9c547cSRui Paulo 		goto fail;
18165b9c547cSRui Paulo 	}
18175b9c547cSRui Paulo 
18185b9c547cSRui Paulo #ifdef CONFIG_IPV6
18195b9c547cSRui Paulo 	if (data->ipv6) {
18205b9c547cSRui Paulo 		if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf,
18215b9c547cSRui Paulo 			      sizeof(abuf)) == NULL)
18225b9c547cSRui Paulo 			abuf[0] = '\0';
18235b9c547cSRui Paulo 		from_port = ntohs(from.sin6.sin6_port);
18245b9c547cSRui Paulo 		RADIUS_DEBUG("Received %d bytes from %s:%d",
18255b9c547cSRui Paulo 			     len, abuf, from_port);
18265b9c547cSRui Paulo 
18275b9c547cSRui Paulo 		client = radius_server_get_client(data,
18285b9c547cSRui Paulo 						  (struct in_addr *)
18295b9c547cSRui Paulo 						  &from.sin6.sin6_addr, 1);
18305b9c547cSRui Paulo 	}
18315b9c547cSRui Paulo #endif /* CONFIG_IPV6 */
18325b9c547cSRui Paulo 
18335b9c547cSRui Paulo 	if (!data->ipv6) {
18345b9c547cSRui Paulo 		os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
18355b9c547cSRui Paulo 		from_port = ntohs(from.sin.sin_port);
18365b9c547cSRui Paulo 		RADIUS_DEBUG("Received %d bytes from %s:%d",
18375b9c547cSRui Paulo 			     len, abuf, from_port);
18385b9c547cSRui Paulo 
18395b9c547cSRui Paulo 		client = radius_server_get_client(data, &from.sin.sin_addr, 0);
18405b9c547cSRui Paulo 	}
18415b9c547cSRui Paulo 
18425b9c547cSRui Paulo 	RADIUS_DUMP("Received data", buf, len);
18435b9c547cSRui Paulo 
18445b9c547cSRui Paulo 	if (client == NULL) {
18455b9c547cSRui Paulo 		RADIUS_DEBUG("Unknown client %s - packet ignored", abuf);
18465b9c547cSRui Paulo 		data->counters.invalid_acct_requests++;
18475b9c547cSRui Paulo 		goto fail;
18485b9c547cSRui Paulo 	}
18495b9c547cSRui Paulo 
18505b9c547cSRui Paulo 	msg = radius_msg_parse(buf, len);
18515b9c547cSRui Paulo 	if (msg == NULL) {
18525b9c547cSRui Paulo 		RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
18535b9c547cSRui Paulo 		data->counters.malformed_acct_requests++;
18545b9c547cSRui Paulo 		client->counters.malformed_acct_requests++;
18555b9c547cSRui Paulo 		goto fail;
18565b9c547cSRui Paulo 	}
18575b9c547cSRui Paulo 
18585b9c547cSRui Paulo 	os_free(buf);
18595b9c547cSRui Paulo 	buf = NULL;
18605b9c547cSRui Paulo 
18615b9c547cSRui Paulo 	if (wpa_debug_level <= MSG_MSGDUMP) {
18625b9c547cSRui Paulo 		radius_msg_dump(msg);
18635b9c547cSRui Paulo 	}
18645b9c547cSRui Paulo 
18655b9c547cSRui Paulo 	if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_REQUEST) {
18665b9c547cSRui Paulo 		RADIUS_DEBUG("Unexpected RADIUS code %d",
18675b9c547cSRui Paulo 			     radius_msg_get_hdr(msg)->code);
18685b9c547cSRui Paulo 		data->counters.unknown_acct_types++;
18695b9c547cSRui Paulo 		client->counters.unknown_acct_types++;
18705b9c547cSRui Paulo 		goto fail;
18715b9c547cSRui Paulo 	}
18725b9c547cSRui Paulo 
18735b9c547cSRui Paulo 	data->counters.acct_requests++;
18745b9c547cSRui Paulo 	client->counters.acct_requests++;
18755b9c547cSRui Paulo 
18765b9c547cSRui Paulo 	if (radius_msg_verify_acct_req(msg, (u8 *) client->shared_secret,
18775b9c547cSRui Paulo 				       client->shared_secret_len)) {
18785b9c547cSRui Paulo 		RADIUS_DEBUG("Invalid Authenticator from %s", abuf);
18795b9c547cSRui Paulo 		data->counters.acct_bad_authenticators++;
18805b9c547cSRui Paulo 		client->counters.acct_bad_authenticators++;
18815b9c547cSRui Paulo 		goto fail;
18825b9c547cSRui Paulo 	}
18835b9c547cSRui Paulo 
18845b9c547cSRui Paulo 	/* TODO: Write accounting information to a file or database */
18855b9c547cSRui Paulo 
18865b9c547cSRui Paulo 	hdr = radius_msg_get_hdr(msg);
18875b9c547cSRui Paulo 
18885b9c547cSRui Paulo 	resp = radius_msg_new(RADIUS_CODE_ACCOUNTING_RESPONSE, hdr->identifier);
18895b9c547cSRui Paulo 	if (resp == NULL)
18905b9c547cSRui Paulo 		goto fail;
18915b9c547cSRui Paulo 
18925b9c547cSRui Paulo 	radius_msg_finish_acct_resp(resp, (u8 *) client->shared_secret,
18935b9c547cSRui Paulo 				    client->shared_secret_len,
18945b9c547cSRui Paulo 				    hdr->authenticator);
18955b9c547cSRui Paulo 
18965b9c547cSRui Paulo 	RADIUS_DEBUG("Reply to %s:%d", abuf, from_port);
18975b9c547cSRui Paulo 	if (wpa_debug_level <= MSG_MSGDUMP) {
18985b9c547cSRui Paulo 		radius_msg_dump(resp);
18995b9c547cSRui Paulo 	}
19005b9c547cSRui Paulo 	rbuf = radius_msg_get_buf(resp);
19015b9c547cSRui Paulo 	data->counters.acct_responses++;
19025b9c547cSRui Paulo 	client->counters.acct_responses++;
19035b9c547cSRui Paulo 	res = sendto(data->acct_sock, wpabuf_head(rbuf), wpabuf_len(rbuf), 0,
19045b9c547cSRui Paulo 		     (struct sockaddr *) &from.ss, fromlen);
19055b9c547cSRui Paulo 	if (res < 0) {
19065b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
19075b9c547cSRui Paulo 			   strerror(errno));
19085b9c547cSRui Paulo 	}
19095b9c547cSRui Paulo 
19105b9c547cSRui Paulo fail:
19115b9c547cSRui Paulo 	radius_msg_free(resp);
19125b9c547cSRui Paulo 	radius_msg_free(msg);
19135b9c547cSRui Paulo 	os_free(buf);
19145b9c547cSRui Paulo }
19155b9c547cSRui Paulo 
19165b9c547cSRui Paulo 
19173157ba21SRui Paulo static int radius_server_disable_pmtu_discovery(int s)
19183157ba21SRui Paulo {
19193157ba21SRui Paulo 	int r = -1;
19203157ba21SRui Paulo #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
19213157ba21SRui Paulo 	/* Turn off Path MTU discovery on IPv4/UDP sockets. */
19223157ba21SRui Paulo 	int action = IP_PMTUDISC_DONT;
19233157ba21SRui Paulo 	r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
19243157ba21SRui Paulo 		       sizeof(action));
19253157ba21SRui Paulo 	if (r == -1)
19263157ba21SRui Paulo 		wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
19273157ba21SRui Paulo 			   "%s", strerror(errno));
19283157ba21SRui Paulo #endif
19293157ba21SRui Paulo 	return r;
19303157ba21SRui Paulo }
19313157ba21SRui Paulo 
19323157ba21SRui Paulo 
193339beb93cSSam Leffler static int radius_server_open_socket(int port)
193439beb93cSSam Leffler {
193539beb93cSSam Leffler 	int s;
193639beb93cSSam Leffler 	struct sockaddr_in addr;
193739beb93cSSam Leffler 
193839beb93cSSam Leffler 	s = socket(PF_INET, SOCK_DGRAM, 0);
193939beb93cSSam Leffler 	if (s < 0) {
19405b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: socket: %s", strerror(errno));
194139beb93cSSam Leffler 		return -1;
194239beb93cSSam Leffler 	}
194339beb93cSSam Leffler 
19443157ba21SRui Paulo 	radius_server_disable_pmtu_discovery(s);
19453157ba21SRui Paulo 
194639beb93cSSam Leffler 	os_memset(&addr, 0, sizeof(addr));
194739beb93cSSam Leffler 	addr.sin_family = AF_INET;
194839beb93cSSam Leffler 	addr.sin_port = htons(port);
194939beb93cSSam Leffler 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
19505b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
195139beb93cSSam Leffler 		close(s);
195239beb93cSSam Leffler 		return -1;
195339beb93cSSam Leffler 	}
195439beb93cSSam Leffler 
195539beb93cSSam Leffler 	return s;
195639beb93cSSam Leffler }
195739beb93cSSam Leffler 
195839beb93cSSam Leffler 
195939beb93cSSam Leffler #ifdef CONFIG_IPV6
196039beb93cSSam Leffler static int radius_server_open_socket6(int port)
196139beb93cSSam Leffler {
196239beb93cSSam Leffler 	int s;
196339beb93cSSam Leffler 	struct sockaddr_in6 addr;
196439beb93cSSam Leffler 
196539beb93cSSam Leffler 	s = socket(PF_INET6, SOCK_DGRAM, 0);
196639beb93cSSam Leffler 	if (s < 0) {
19675b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: socket[IPv6]: %s",
19685b9c547cSRui Paulo 			   strerror(errno));
196939beb93cSSam Leffler 		return -1;
197039beb93cSSam Leffler 	}
197139beb93cSSam Leffler 
197239beb93cSSam Leffler 	os_memset(&addr, 0, sizeof(addr));
197339beb93cSSam Leffler 	addr.sin6_family = AF_INET6;
197439beb93cSSam Leffler 	os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
197539beb93cSSam Leffler 	addr.sin6_port = htons(port);
197639beb93cSSam Leffler 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
19775b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
197839beb93cSSam Leffler 		close(s);
197939beb93cSSam Leffler 		return -1;
198039beb93cSSam Leffler 	}
198139beb93cSSam Leffler 
198239beb93cSSam Leffler 	return s;
198339beb93cSSam Leffler }
198439beb93cSSam Leffler #endif /* CONFIG_IPV6 */
198539beb93cSSam Leffler 
198639beb93cSSam Leffler 
198739beb93cSSam Leffler static void radius_server_free_sessions(struct radius_server_data *data,
198839beb93cSSam Leffler 					struct radius_session *sessions)
198939beb93cSSam Leffler {
199039beb93cSSam Leffler 	struct radius_session *session, *prev;
199139beb93cSSam Leffler 
199239beb93cSSam Leffler 	session = sessions;
199339beb93cSSam Leffler 	while (session) {
199439beb93cSSam Leffler 		prev = session;
199539beb93cSSam Leffler 		session = session->next;
199639beb93cSSam Leffler 		radius_server_session_free(data, prev);
199739beb93cSSam Leffler 	}
199839beb93cSSam Leffler }
199939beb93cSSam Leffler 
200039beb93cSSam Leffler 
200139beb93cSSam Leffler static void radius_server_free_clients(struct radius_server_data *data,
200239beb93cSSam Leffler 				       struct radius_client *clients)
200339beb93cSSam Leffler {
200439beb93cSSam Leffler 	struct radius_client *client, *prev;
200539beb93cSSam Leffler 
200639beb93cSSam Leffler 	client = clients;
200739beb93cSSam Leffler 	while (client) {
200839beb93cSSam Leffler 		prev = client;
200939beb93cSSam Leffler 		client = client->next;
201039beb93cSSam Leffler 
201139beb93cSSam Leffler 		radius_server_free_sessions(data, prev->sessions);
201239beb93cSSam Leffler 		os_free(prev->shared_secret);
201385732ac8SCy Schubert 		radius_msg_free(prev->pending_dac_coa_req);
201485732ac8SCy Schubert 		radius_msg_free(prev->pending_dac_disconnect_req);
201539beb93cSSam Leffler 		os_free(prev);
201639beb93cSSam Leffler 	}
201739beb93cSSam Leffler }
201839beb93cSSam Leffler 
201939beb93cSSam Leffler 
202039beb93cSSam Leffler static struct radius_client *
202139beb93cSSam Leffler radius_server_read_clients(const char *client_file, int ipv6)
202239beb93cSSam Leffler {
202339beb93cSSam Leffler 	FILE *f;
202439beb93cSSam Leffler 	const int buf_size = 1024;
202539beb93cSSam Leffler 	char *buf, *pos;
202639beb93cSSam Leffler 	struct radius_client *clients, *tail, *entry;
202739beb93cSSam Leffler 	int line = 0, mask, failed = 0, i;
202839beb93cSSam Leffler 	struct in_addr addr;
202939beb93cSSam Leffler #ifdef CONFIG_IPV6
203039beb93cSSam Leffler 	struct in6_addr addr6;
203139beb93cSSam Leffler #endif /* CONFIG_IPV6 */
203239beb93cSSam Leffler 	unsigned int val;
203339beb93cSSam Leffler 
203439beb93cSSam Leffler 	f = fopen(client_file, "r");
203539beb93cSSam Leffler 	if (f == NULL) {
203639beb93cSSam Leffler 		RADIUS_ERROR("Could not open client file '%s'", client_file);
203739beb93cSSam Leffler 		return NULL;
203839beb93cSSam Leffler 	}
203939beb93cSSam Leffler 
204039beb93cSSam Leffler 	buf = os_malloc(buf_size);
204139beb93cSSam Leffler 	if (buf == NULL) {
204239beb93cSSam Leffler 		fclose(f);
204339beb93cSSam Leffler 		return NULL;
204439beb93cSSam Leffler 	}
204539beb93cSSam Leffler 
204639beb93cSSam Leffler 	clients = tail = NULL;
204739beb93cSSam Leffler 	while (fgets(buf, buf_size, f)) {
204839beb93cSSam Leffler 		/* Configuration file format:
204939beb93cSSam Leffler 		 * 192.168.1.0/24 secret
205039beb93cSSam Leffler 		 * 192.168.1.2 secret
205139beb93cSSam Leffler 		 * fe80::211:22ff:fe33:4455/64 secretipv6
205239beb93cSSam Leffler 		 */
205339beb93cSSam Leffler 		line++;
205439beb93cSSam Leffler 		buf[buf_size - 1] = '\0';
205539beb93cSSam Leffler 		pos = buf;
205639beb93cSSam Leffler 		while (*pos != '\0' && *pos != '\n')
205739beb93cSSam Leffler 			pos++;
205839beb93cSSam Leffler 		if (*pos == '\n')
205939beb93cSSam Leffler 			*pos = '\0';
206039beb93cSSam Leffler 		if (*buf == '\0' || *buf == '#')
206139beb93cSSam Leffler 			continue;
206239beb93cSSam Leffler 
206339beb93cSSam Leffler 		pos = buf;
206439beb93cSSam Leffler 		while ((*pos >= '0' && *pos <= '9') || *pos == '.' ||
206539beb93cSSam Leffler 		       (*pos >= 'a' && *pos <= 'f') || *pos == ':' ||
206639beb93cSSam Leffler 		       (*pos >= 'A' && *pos <= 'F')) {
206739beb93cSSam Leffler 			pos++;
206839beb93cSSam Leffler 		}
206939beb93cSSam Leffler 
207039beb93cSSam Leffler 		if (*pos == '\0') {
207139beb93cSSam Leffler 			failed = 1;
207239beb93cSSam Leffler 			break;
207339beb93cSSam Leffler 		}
207439beb93cSSam Leffler 
207539beb93cSSam Leffler 		if (*pos == '/') {
207639beb93cSSam Leffler 			char *end;
207739beb93cSSam Leffler 			*pos++ = '\0';
207839beb93cSSam Leffler 			mask = strtol(pos, &end, 10);
207939beb93cSSam Leffler 			if ((pos == end) ||
208039beb93cSSam Leffler 			    (mask < 0 || mask > (ipv6 ? 128 : 32))) {
208139beb93cSSam Leffler 				failed = 1;
208239beb93cSSam Leffler 				break;
208339beb93cSSam Leffler 			}
208439beb93cSSam Leffler 			pos = end;
208539beb93cSSam Leffler 		} else {
208639beb93cSSam Leffler 			mask = ipv6 ? 128 : 32;
208739beb93cSSam Leffler 			*pos++ = '\0';
208839beb93cSSam Leffler 		}
208939beb93cSSam Leffler 
209039beb93cSSam Leffler 		if (!ipv6 && inet_aton(buf, &addr) == 0) {
209139beb93cSSam Leffler 			failed = 1;
209239beb93cSSam Leffler 			break;
209339beb93cSSam Leffler 		}
209439beb93cSSam Leffler #ifdef CONFIG_IPV6
209539beb93cSSam Leffler 		if (ipv6 && inet_pton(AF_INET6, buf, &addr6) <= 0) {
209639beb93cSSam Leffler 			if (inet_pton(AF_INET, buf, &addr) <= 0) {
209739beb93cSSam Leffler 				failed = 1;
209839beb93cSSam Leffler 				break;
209939beb93cSSam Leffler 			}
210039beb93cSSam Leffler 			/* Convert IPv4 address to IPv6 */
210139beb93cSSam Leffler 			if (mask <= 32)
210239beb93cSSam Leffler 				mask += (128 - 32);
210339beb93cSSam Leffler 			os_memset(addr6.s6_addr, 0, 10);
210439beb93cSSam Leffler 			addr6.s6_addr[10] = 0xff;
210539beb93cSSam Leffler 			addr6.s6_addr[11] = 0xff;
210639beb93cSSam Leffler 			os_memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr,
210739beb93cSSam Leffler 				  4);
210839beb93cSSam Leffler 		}
210939beb93cSSam Leffler #endif /* CONFIG_IPV6 */
211039beb93cSSam Leffler 
211139beb93cSSam Leffler 		while (*pos == ' ' || *pos == '\t') {
211239beb93cSSam Leffler 			pos++;
211339beb93cSSam Leffler 		}
211439beb93cSSam Leffler 
211539beb93cSSam Leffler 		if (*pos == '\0') {
211639beb93cSSam Leffler 			failed = 1;
211739beb93cSSam Leffler 			break;
211839beb93cSSam Leffler 		}
211939beb93cSSam Leffler 
212039beb93cSSam Leffler 		entry = os_zalloc(sizeof(*entry));
212139beb93cSSam Leffler 		if (entry == NULL) {
212239beb93cSSam Leffler 			failed = 1;
212339beb93cSSam Leffler 			break;
212439beb93cSSam Leffler 		}
212539beb93cSSam Leffler 		entry->shared_secret = os_strdup(pos);
212639beb93cSSam Leffler 		if (entry->shared_secret == NULL) {
212739beb93cSSam Leffler 			failed = 1;
212839beb93cSSam Leffler 			os_free(entry);
212939beb93cSSam Leffler 			break;
213039beb93cSSam Leffler 		}
213139beb93cSSam Leffler 		entry->shared_secret_len = os_strlen(entry->shared_secret);
213239beb93cSSam Leffler 		if (!ipv6) {
21335b9c547cSRui Paulo 			entry->addr.s_addr = addr.s_addr;
213439beb93cSSam Leffler 			val = 0;
213539beb93cSSam Leffler 			for (i = 0; i < mask; i++)
21364bc52338SCy Schubert 				val |= 1U << (31 - i);
213739beb93cSSam Leffler 			entry->mask.s_addr = htonl(val);
213839beb93cSSam Leffler 		}
213939beb93cSSam Leffler #ifdef CONFIG_IPV6
214039beb93cSSam Leffler 		if (ipv6) {
214139beb93cSSam Leffler 			int offset = mask / 8;
214239beb93cSSam Leffler 
214339beb93cSSam Leffler 			os_memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16);
214439beb93cSSam Leffler 			os_memset(entry->mask6.s6_addr, 0xff, offset);
214539beb93cSSam Leffler 			val = 0;
214639beb93cSSam Leffler 			for (i = 0; i < (mask % 8); i++)
214739beb93cSSam Leffler 				val |= 1 << (7 - i);
214839beb93cSSam Leffler 			if (offset < 16)
214939beb93cSSam Leffler 				entry->mask6.s6_addr[offset] = val;
215039beb93cSSam Leffler 		}
215139beb93cSSam Leffler #endif /* CONFIG_IPV6 */
215239beb93cSSam Leffler 
215339beb93cSSam Leffler 		if (tail == NULL) {
215439beb93cSSam Leffler 			clients = tail = entry;
215539beb93cSSam Leffler 		} else {
215639beb93cSSam Leffler 			tail->next = entry;
215739beb93cSSam Leffler 			tail = entry;
215839beb93cSSam Leffler 		}
215939beb93cSSam Leffler 	}
216039beb93cSSam Leffler 
216139beb93cSSam Leffler 	if (failed) {
216239beb93cSSam Leffler 		RADIUS_ERROR("Invalid line %d in '%s'", line, client_file);
216339beb93cSSam Leffler 		radius_server_free_clients(NULL, clients);
216439beb93cSSam Leffler 		clients = NULL;
216539beb93cSSam Leffler 	}
216639beb93cSSam Leffler 
216739beb93cSSam Leffler 	os_free(buf);
216839beb93cSSam Leffler 	fclose(f);
216939beb93cSSam Leffler 
217039beb93cSSam Leffler 	return clients;
217139beb93cSSam Leffler }
217239beb93cSSam Leffler 
217339beb93cSSam Leffler 
2174e28a4053SRui Paulo /**
2175e28a4053SRui Paulo  * radius_server_init - Initialize RADIUS server
2176e28a4053SRui Paulo  * @conf: Configuration for the RADIUS server
2177e28a4053SRui Paulo  * Returns: Pointer to private RADIUS server context or %NULL on failure
2178e28a4053SRui Paulo  *
2179e28a4053SRui Paulo  * This initializes a RADIUS server instance and returns a context pointer that
2180e28a4053SRui Paulo  * will be used in other calls to the RADIUS server module. The server can be
2181e28a4053SRui Paulo  * deinitialize by calling radius_server_deinit().
2182e28a4053SRui Paulo  */
218339beb93cSSam Leffler struct radius_server_data *
218439beb93cSSam Leffler radius_server_init(struct radius_server_conf *conf)
218539beb93cSSam Leffler {
218639beb93cSSam Leffler 	struct radius_server_data *data;
218739beb93cSSam Leffler 
218839beb93cSSam Leffler #ifndef CONFIG_IPV6
218939beb93cSSam Leffler 	if (conf->ipv6) {
21905b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "RADIUS server compiled without IPv6 support");
219139beb93cSSam Leffler 		return NULL;
219239beb93cSSam Leffler 	}
219339beb93cSSam Leffler #endif /* CONFIG_IPV6 */
219439beb93cSSam Leffler 
219539beb93cSSam Leffler 	data = os_zalloc(sizeof(*data));
219639beb93cSSam Leffler 	if (data == NULL)
219739beb93cSSam Leffler 		return NULL;
219839beb93cSSam Leffler 
2199*c1d255d3SCy Schubert 	data->eap_cfg = conf->eap_cfg;
2200206b73d0SCy Schubert 	data->auth_sock = -1;
2201206b73d0SCy Schubert 	data->acct_sock = -1;
22025b9c547cSRui Paulo 	dl_list_init(&data->erp_keys);
22035b9c547cSRui Paulo 	os_get_reltime(&data->start_time);
220439beb93cSSam Leffler 	data->conf_ctx = conf->conf_ctx;
2205*c1d255d3SCy Schubert 	conf->eap_cfg->backend_auth = true;
2206*c1d255d3SCy Schubert 	conf->eap_cfg->eap_server = 1;
220739beb93cSSam Leffler 	data->ipv6 = conf->ipv6;
220839beb93cSSam Leffler 	data->get_eap_user = conf->get_eap_user;
220939beb93cSSam Leffler 	if (conf->eap_req_id_text) {
221039beb93cSSam Leffler 		data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len);
2211*c1d255d3SCy Schubert 		if (!data->eap_req_id_text)
2212*c1d255d3SCy Schubert 			goto fail;
221339beb93cSSam Leffler 		os_memcpy(data->eap_req_id_text, conf->eap_req_id_text,
221439beb93cSSam Leffler 			  conf->eap_req_id_text_len);
221539beb93cSSam Leffler 		data->eap_req_id_text_len = conf->eap_req_id_text_len;
221639beb93cSSam Leffler 	}
22175b9c547cSRui Paulo 	data->erp_domain = conf->erp_domain;
22185b9c547cSRui Paulo 
22195b9c547cSRui Paulo 	if (conf->subscr_remediation_url) {
22205b9c547cSRui Paulo 		data->subscr_remediation_url =
22215b9c547cSRui Paulo 			os_strdup(conf->subscr_remediation_url);
2222*c1d255d3SCy Schubert 		if (!data->subscr_remediation_url)
2223*c1d255d3SCy Schubert 			goto fail;
22245b9c547cSRui Paulo 	}
22255b9c547cSRui Paulo 	data->subscr_remediation_method = conf->subscr_remediation_method;
2226*c1d255d3SCy Schubert 	if (conf->hs20_sim_provisioning_url) {
22274bc52338SCy Schubert 		data->hs20_sim_provisioning_url =
22284bc52338SCy Schubert 			os_strdup(conf->hs20_sim_provisioning_url);
2229*c1d255d3SCy Schubert 		if (!data->hs20_sim_provisioning_url)
2230*c1d255d3SCy Schubert 			goto fail;
2231*c1d255d3SCy Schubert 	}
22325b9c547cSRui Paulo 
2233*c1d255d3SCy Schubert 	if (conf->t_c_server_url) {
223485732ac8SCy Schubert 		data->t_c_server_url = os_strdup(conf->t_c_server_url);
2235*c1d255d3SCy Schubert 		if (!data->t_c_server_url)
2236*c1d255d3SCy Schubert 			goto fail;
2237*c1d255d3SCy Schubert 	}
223885732ac8SCy Schubert 
22395b9c547cSRui Paulo #ifdef CONFIG_SQLITE
22405b9c547cSRui Paulo 	if (conf->sqlite_file) {
22415b9c547cSRui Paulo 		if (sqlite3_open(conf->sqlite_file, &data->db)) {
22425b9c547cSRui Paulo 			RADIUS_ERROR("Could not open SQLite file '%s'",
22435b9c547cSRui Paulo 				     conf->sqlite_file);
2244*c1d255d3SCy Schubert 			goto fail;
22455b9c547cSRui Paulo 		}
22465b9c547cSRui Paulo 	}
22475b9c547cSRui Paulo #endif /* CONFIG_SQLITE */
224839beb93cSSam Leffler 
2249f05cddf9SRui Paulo #ifdef CONFIG_RADIUS_TEST
2250f05cddf9SRui Paulo 	if (conf->dump_msk_file)
2251f05cddf9SRui Paulo 		data->dump_msk_file = os_strdup(conf->dump_msk_file);
2252f05cddf9SRui Paulo #endif /* CONFIG_RADIUS_TEST */
2253f05cddf9SRui Paulo 
225439beb93cSSam Leffler 	data->clients = radius_server_read_clients(conf->client_file,
225539beb93cSSam Leffler 						   conf->ipv6);
225639beb93cSSam Leffler 	if (data->clients == NULL) {
22575b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "No RADIUS clients configured");
2258*c1d255d3SCy Schubert 		goto fail;
225939beb93cSSam Leffler 	}
226039beb93cSSam Leffler 
226139beb93cSSam Leffler #ifdef CONFIG_IPV6
226239beb93cSSam Leffler 	if (conf->ipv6)
226339beb93cSSam Leffler 		data->auth_sock = radius_server_open_socket6(conf->auth_port);
226439beb93cSSam Leffler 	else
226539beb93cSSam Leffler #endif /* CONFIG_IPV6 */
226639beb93cSSam Leffler 	data->auth_sock = radius_server_open_socket(conf->auth_port);
226739beb93cSSam Leffler 	if (data->auth_sock < 0) {
22685b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS authentication server");
2269*c1d255d3SCy Schubert 		goto fail;
227039beb93cSSam Leffler 	}
227139beb93cSSam Leffler 	if (eloop_register_read_sock(data->auth_sock,
227239beb93cSSam Leffler 				     radius_server_receive_auth,
227339beb93cSSam Leffler 				     data, NULL)) {
2274*c1d255d3SCy Schubert 		goto fail;
227539beb93cSSam Leffler 	}
227639beb93cSSam Leffler 
22775b9c547cSRui Paulo 	if (conf->acct_port) {
22785b9c547cSRui Paulo #ifdef CONFIG_IPV6
22795b9c547cSRui Paulo 		if (conf->ipv6)
22805b9c547cSRui Paulo 			data->acct_sock = radius_server_open_socket6(
22815b9c547cSRui Paulo 				conf->acct_port);
22825b9c547cSRui Paulo 		else
22835b9c547cSRui Paulo #endif /* CONFIG_IPV6 */
22845b9c547cSRui Paulo 		data->acct_sock = radius_server_open_socket(conf->acct_port);
22855b9c547cSRui Paulo 		if (data->acct_sock < 0) {
22865b9c547cSRui Paulo 			wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS accounting server");
2287*c1d255d3SCy Schubert 			goto fail;
22885b9c547cSRui Paulo 		}
22895b9c547cSRui Paulo 		if (eloop_register_read_sock(data->acct_sock,
22905b9c547cSRui Paulo 					     radius_server_receive_acct,
2291*c1d255d3SCy Schubert 					     data, NULL))
2292*c1d255d3SCy Schubert 			goto fail;
22935b9c547cSRui Paulo 	} else {
22945b9c547cSRui Paulo 		data->acct_sock = -1;
22955b9c547cSRui Paulo 	}
22965b9c547cSRui Paulo 
229739beb93cSSam Leffler 	return data;
2298*c1d255d3SCy Schubert fail:
2299*c1d255d3SCy Schubert 	radius_server_deinit(data);
2300*c1d255d3SCy Schubert 	return NULL;
230139beb93cSSam Leffler }
230239beb93cSSam Leffler 
230339beb93cSSam Leffler 
2304e28a4053SRui Paulo /**
23055b9c547cSRui Paulo  * radius_server_erp_flush - Flush all ERP keys
23065b9c547cSRui Paulo  * @data: RADIUS server context from radius_server_init()
23075b9c547cSRui Paulo  */
23085b9c547cSRui Paulo void radius_server_erp_flush(struct radius_server_data *data)
23095b9c547cSRui Paulo {
23105b9c547cSRui Paulo 	struct eap_server_erp_key *erp;
23115b9c547cSRui Paulo 
23125b9c547cSRui Paulo 	if (data == NULL)
23135b9c547cSRui Paulo 		return;
23145b9c547cSRui Paulo 	while ((erp = dl_list_first(&data->erp_keys, struct eap_server_erp_key,
23155b9c547cSRui Paulo 				    list)) != NULL) {
23165b9c547cSRui Paulo 		dl_list_del(&erp->list);
23175b9c547cSRui Paulo 		bin_clear_free(erp, sizeof(*erp));
23185b9c547cSRui Paulo 	}
23195b9c547cSRui Paulo }
23205b9c547cSRui Paulo 
23215b9c547cSRui Paulo 
23225b9c547cSRui Paulo /**
2323e28a4053SRui Paulo  * radius_server_deinit - Deinitialize RADIUS server
2324e28a4053SRui Paulo  * @data: RADIUS server context from radius_server_init()
2325e28a4053SRui Paulo  */
232639beb93cSSam Leffler void radius_server_deinit(struct radius_server_data *data)
232739beb93cSSam Leffler {
232839beb93cSSam Leffler 	if (data == NULL)
232939beb93cSSam Leffler 		return;
233039beb93cSSam Leffler 
233139beb93cSSam Leffler 	if (data->auth_sock >= 0) {
233239beb93cSSam Leffler 		eloop_unregister_read_sock(data->auth_sock);
233339beb93cSSam Leffler 		close(data->auth_sock);
233439beb93cSSam Leffler 	}
233539beb93cSSam Leffler 
23365b9c547cSRui Paulo 	if (data->acct_sock >= 0) {
23375b9c547cSRui Paulo 		eloop_unregister_read_sock(data->acct_sock);
23385b9c547cSRui Paulo 		close(data->acct_sock);
23395b9c547cSRui Paulo 	}
23405b9c547cSRui Paulo 
234139beb93cSSam Leffler 	radius_server_free_clients(data, data->clients);
234239beb93cSSam Leffler 
234339beb93cSSam Leffler 	os_free(data->eap_req_id_text);
2344f05cddf9SRui Paulo #ifdef CONFIG_RADIUS_TEST
2345f05cddf9SRui Paulo 	os_free(data->dump_msk_file);
2346f05cddf9SRui Paulo #endif /* CONFIG_RADIUS_TEST */
23475b9c547cSRui Paulo 	os_free(data->subscr_remediation_url);
23484bc52338SCy Schubert 	os_free(data->hs20_sim_provisioning_url);
234985732ac8SCy Schubert 	os_free(data->t_c_server_url);
23505b9c547cSRui Paulo 
23515b9c547cSRui Paulo #ifdef CONFIG_SQLITE
23525b9c547cSRui Paulo 	if (data->db)
23535b9c547cSRui Paulo 		sqlite3_close(data->db);
23545b9c547cSRui Paulo #endif /* CONFIG_SQLITE */
23555b9c547cSRui Paulo 
23565b9c547cSRui Paulo 	radius_server_erp_flush(data);
23575b9c547cSRui Paulo 
235839beb93cSSam Leffler 	os_free(data);
235939beb93cSSam Leffler }
236039beb93cSSam Leffler 
236139beb93cSSam Leffler 
2362e28a4053SRui Paulo /**
2363e28a4053SRui Paulo  * radius_server_get_mib - Get RADIUS server MIB information
2364e28a4053SRui Paulo  * @data: RADIUS server context from radius_server_init()
2365e28a4053SRui Paulo  * @buf: Buffer for returning the MIB data in text format
2366e28a4053SRui Paulo  * @buflen: buf length in octets
2367e28a4053SRui Paulo  * Returns: Number of octets written into buf
2368e28a4053SRui Paulo  */
236939beb93cSSam Leffler int radius_server_get_mib(struct radius_server_data *data, char *buf,
237039beb93cSSam Leffler 			  size_t buflen)
237139beb93cSSam Leffler {
237239beb93cSSam Leffler 	int ret, uptime;
237339beb93cSSam Leffler 	unsigned int idx;
237439beb93cSSam Leffler 	char *end, *pos;
23755b9c547cSRui Paulo 	struct os_reltime now;
237639beb93cSSam Leffler 	struct radius_client *cli;
237739beb93cSSam Leffler 
237839beb93cSSam Leffler 	/* RFC 2619 - RADIUS Authentication Server MIB */
237939beb93cSSam Leffler 
238039beb93cSSam Leffler 	if (data == NULL || buflen == 0)
238139beb93cSSam Leffler 		return 0;
238239beb93cSSam Leffler 
238339beb93cSSam Leffler 	pos = buf;
238439beb93cSSam Leffler 	end = buf + buflen;
238539beb93cSSam Leffler 
23865b9c547cSRui Paulo 	os_get_reltime(&now);
238739beb93cSSam Leffler 	uptime = (now.sec - data->start_time.sec) * 100 +
238839beb93cSSam Leffler 		((now.usec - data->start_time.usec) / 10000) % 100;
238939beb93cSSam Leffler 	ret = os_snprintf(pos, end - pos,
239039beb93cSSam Leffler 			  "RADIUS-AUTH-SERVER-MIB\n"
239139beb93cSSam Leffler 			  "radiusAuthServIdent=hostapd\n"
239239beb93cSSam Leffler 			  "radiusAuthServUpTime=%d\n"
239339beb93cSSam Leffler 			  "radiusAuthServResetTime=0\n"
239439beb93cSSam Leffler 			  "radiusAuthServConfigReset=4\n",
239539beb93cSSam Leffler 			  uptime);
23965b9c547cSRui Paulo 	if (os_snprintf_error(end - pos, ret)) {
239739beb93cSSam Leffler 		*pos = '\0';
239839beb93cSSam Leffler 		return pos - buf;
239939beb93cSSam Leffler 	}
240039beb93cSSam Leffler 	pos += ret;
240139beb93cSSam Leffler 
240239beb93cSSam Leffler 	ret = os_snprintf(pos, end - pos,
240339beb93cSSam Leffler 			  "radiusAuthServTotalAccessRequests=%u\n"
240439beb93cSSam Leffler 			  "radiusAuthServTotalInvalidRequests=%u\n"
240539beb93cSSam Leffler 			  "radiusAuthServTotalDupAccessRequests=%u\n"
240639beb93cSSam Leffler 			  "radiusAuthServTotalAccessAccepts=%u\n"
240739beb93cSSam Leffler 			  "radiusAuthServTotalAccessRejects=%u\n"
240839beb93cSSam Leffler 			  "radiusAuthServTotalAccessChallenges=%u\n"
240939beb93cSSam Leffler 			  "radiusAuthServTotalMalformedAccessRequests=%u\n"
241039beb93cSSam Leffler 			  "radiusAuthServTotalBadAuthenticators=%u\n"
241139beb93cSSam Leffler 			  "radiusAuthServTotalPacketsDropped=%u\n"
24125b9c547cSRui Paulo 			  "radiusAuthServTotalUnknownTypes=%u\n"
24135b9c547cSRui Paulo 			  "radiusAccServTotalRequests=%u\n"
24145b9c547cSRui Paulo 			  "radiusAccServTotalInvalidRequests=%u\n"
24155b9c547cSRui Paulo 			  "radiusAccServTotalResponses=%u\n"
24165b9c547cSRui Paulo 			  "radiusAccServTotalMalformedRequests=%u\n"
24175b9c547cSRui Paulo 			  "radiusAccServTotalBadAuthenticators=%u\n"
24185b9c547cSRui Paulo 			  "radiusAccServTotalUnknownTypes=%u\n",
241939beb93cSSam Leffler 			  data->counters.access_requests,
242039beb93cSSam Leffler 			  data->counters.invalid_requests,
242139beb93cSSam Leffler 			  data->counters.dup_access_requests,
242239beb93cSSam Leffler 			  data->counters.access_accepts,
242339beb93cSSam Leffler 			  data->counters.access_rejects,
242439beb93cSSam Leffler 			  data->counters.access_challenges,
242539beb93cSSam Leffler 			  data->counters.malformed_access_requests,
242639beb93cSSam Leffler 			  data->counters.bad_authenticators,
242739beb93cSSam Leffler 			  data->counters.packets_dropped,
24285b9c547cSRui Paulo 			  data->counters.unknown_types,
24295b9c547cSRui Paulo 			  data->counters.acct_requests,
24305b9c547cSRui Paulo 			  data->counters.invalid_acct_requests,
24315b9c547cSRui Paulo 			  data->counters.acct_responses,
24325b9c547cSRui Paulo 			  data->counters.malformed_acct_requests,
24335b9c547cSRui Paulo 			  data->counters.acct_bad_authenticators,
24345b9c547cSRui Paulo 			  data->counters.unknown_acct_types);
24355b9c547cSRui Paulo 	if (os_snprintf_error(end - pos, ret)) {
243639beb93cSSam Leffler 		*pos = '\0';
243739beb93cSSam Leffler 		return pos - buf;
243839beb93cSSam Leffler 	}
243939beb93cSSam Leffler 	pos += ret;
244039beb93cSSam Leffler 
244139beb93cSSam Leffler 	for (cli = data->clients, idx = 0; cli; cli = cli->next, idx++) {
244239beb93cSSam Leffler 		char abuf[50], mbuf[50];
244339beb93cSSam Leffler #ifdef CONFIG_IPV6
244439beb93cSSam Leffler 		if (data->ipv6) {
244539beb93cSSam Leffler 			if (inet_ntop(AF_INET6, &cli->addr6, abuf,
244639beb93cSSam Leffler 				      sizeof(abuf)) == NULL)
244739beb93cSSam Leffler 				abuf[0] = '\0';
24485b9c547cSRui Paulo 			if (inet_ntop(AF_INET6, &cli->mask6, mbuf,
244939beb93cSSam Leffler 				      sizeof(mbuf)) == NULL)
245039beb93cSSam Leffler 				mbuf[0] = '\0';
245139beb93cSSam Leffler 		}
245239beb93cSSam Leffler #endif /* CONFIG_IPV6 */
245339beb93cSSam Leffler 		if (!data->ipv6) {
245439beb93cSSam Leffler 			os_strlcpy(abuf, inet_ntoa(cli->addr), sizeof(abuf));
245539beb93cSSam Leffler 			os_strlcpy(mbuf, inet_ntoa(cli->mask), sizeof(mbuf));
245639beb93cSSam Leffler 		}
245739beb93cSSam Leffler 
245839beb93cSSam Leffler 		ret = os_snprintf(pos, end - pos,
245939beb93cSSam Leffler 				  "radiusAuthClientIndex=%u\n"
246039beb93cSSam Leffler 				  "radiusAuthClientAddress=%s/%s\n"
246139beb93cSSam Leffler 				  "radiusAuthServAccessRequests=%u\n"
246239beb93cSSam Leffler 				  "radiusAuthServDupAccessRequests=%u\n"
246339beb93cSSam Leffler 				  "radiusAuthServAccessAccepts=%u\n"
246439beb93cSSam Leffler 				  "radiusAuthServAccessRejects=%u\n"
246539beb93cSSam Leffler 				  "radiusAuthServAccessChallenges=%u\n"
246639beb93cSSam Leffler 				  "radiusAuthServMalformedAccessRequests=%u\n"
246739beb93cSSam Leffler 				  "radiusAuthServBadAuthenticators=%u\n"
246839beb93cSSam Leffler 				  "radiusAuthServPacketsDropped=%u\n"
24695b9c547cSRui Paulo 				  "radiusAuthServUnknownTypes=%u\n"
24705b9c547cSRui Paulo 				  "radiusAccServTotalRequests=%u\n"
24715b9c547cSRui Paulo 				  "radiusAccServTotalInvalidRequests=%u\n"
24725b9c547cSRui Paulo 				  "radiusAccServTotalResponses=%u\n"
24735b9c547cSRui Paulo 				  "radiusAccServTotalMalformedRequests=%u\n"
24745b9c547cSRui Paulo 				  "radiusAccServTotalBadAuthenticators=%u\n"
24755b9c547cSRui Paulo 				  "radiusAccServTotalUnknownTypes=%u\n",
247639beb93cSSam Leffler 				  idx,
247739beb93cSSam Leffler 				  abuf, mbuf,
247839beb93cSSam Leffler 				  cli->counters.access_requests,
247939beb93cSSam Leffler 				  cli->counters.dup_access_requests,
248039beb93cSSam Leffler 				  cli->counters.access_accepts,
248139beb93cSSam Leffler 				  cli->counters.access_rejects,
248239beb93cSSam Leffler 				  cli->counters.access_challenges,
248339beb93cSSam Leffler 				  cli->counters.malformed_access_requests,
248439beb93cSSam Leffler 				  cli->counters.bad_authenticators,
248539beb93cSSam Leffler 				  cli->counters.packets_dropped,
24865b9c547cSRui Paulo 				  cli->counters.unknown_types,
24875b9c547cSRui Paulo 				  cli->counters.acct_requests,
24885b9c547cSRui Paulo 				  cli->counters.invalid_acct_requests,
24895b9c547cSRui Paulo 				  cli->counters.acct_responses,
24905b9c547cSRui Paulo 				  cli->counters.malformed_acct_requests,
24915b9c547cSRui Paulo 				  cli->counters.acct_bad_authenticators,
24925b9c547cSRui Paulo 				  cli->counters.unknown_acct_types);
24935b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret)) {
249439beb93cSSam Leffler 			*pos = '\0';
249539beb93cSSam Leffler 			return pos - buf;
249639beb93cSSam Leffler 		}
249739beb93cSSam Leffler 		pos += ret;
249839beb93cSSam Leffler 	}
249939beb93cSSam Leffler 
250039beb93cSSam Leffler 	return pos - buf;
250139beb93cSSam Leffler }
250239beb93cSSam Leffler 
250339beb93cSSam Leffler 
250439beb93cSSam Leffler static int radius_server_get_eap_user(void *ctx, const u8 *identity,
250539beb93cSSam Leffler 				      size_t identity_len, int phase2,
250639beb93cSSam Leffler 				      struct eap_user *user)
250739beb93cSSam Leffler {
250839beb93cSSam Leffler 	struct radius_session *sess = ctx;
250939beb93cSSam Leffler 	struct radius_server_data *data = sess->server;
25105b9c547cSRui Paulo 	int ret;
251139beb93cSSam Leffler 
25125b9c547cSRui Paulo 	ret = data->get_eap_user(data->conf_ctx, identity, identity_len,
251339beb93cSSam Leffler 				 phase2, user);
25145b9c547cSRui Paulo 	if (ret == 0 && user) {
25155b9c547cSRui Paulo 		sess->accept_attr = user->accept_attr;
25165b9c547cSRui Paulo 		sess->remediation = user->remediation;
25175b9c547cSRui Paulo 		sess->macacl = user->macacl;
251885732ac8SCy Schubert 		sess->t_c_timestamp = user->t_c_timestamp;
25195b9c547cSRui Paulo 	}
2520325151a3SRui Paulo 
2521325151a3SRui Paulo 	if (ret) {
2522325151a3SRui Paulo 		RADIUS_DEBUG("%s: User-Name not found from user database",
2523325151a3SRui Paulo 			     __func__);
2524325151a3SRui Paulo 	}
2525325151a3SRui Paulo 
25265b9c547cSRui Paulo 	return ret;
252739beb93cSSam Leffler }
252839beb93cSSam Leffler 
252939beb93cSSam Leffler 
253039beb93cSSam Leffler static const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len)
253139beb93cSSam Leffler {
253239beb93cSSam Leffler 	struct radius_session *sess = ctx;
253339beb93cSSam Leffler 	struct radius_server_data *data = sess->server;
253439beb93cSSam Leffler 	*len = data->eap_req_id_text_len;
253539beb93cSSam Leffler 	return data->eap_req_id_text;
253639beb93cSSam Leffler }
253739beb93cSSam Leffler 
253839beb93cSSam Leffler 
25395b9c547cSRui Paulo static void radius_server_log_msg(void *ctx, const char *msg)
25405b9c547cSRui Paulo {
25415b9c547cSRui Paulo 	struct radius_session *sess = ctx;
25425b9c547cSRui Paulo 	srv_log(sess, "EAP: %s", msg);
25435b9c547cSRui Paulo }
25445b9c547cSRui Paulo 
25455b9c547cSRui Paulo 
25465b9c547cSRui Paulo #ifdef CONFIG_ERP
25475b9c547cSRui Paulo 
25485b9c547cSRui Paulo static const char * radius_server_get_erp_domain(void *ctx)
25495b9c547cSRui Paulo {
25505b9c547cSRui Paulo 	struct radius_session *sess = ctx;
25515b9c547cSRui Paulo 	struct radius_server_data *data = sess->server;
25525b9c547cSRui Paulo 
25535b9c547cSRui Paulo 	return data->erp_domain;
25545b9c547cSRui Paulo }
25555b9c547cSRui Paulo 
25565b9c547cSRui Paulo 
25575b9c547cSRui Paulo static struct eap_server_erp_key *
25585b9c547cSRui Paulo radius_server_erp_get_key(void *ctx, const char *keyname)
25595b9c547cSRui Paulo {
25605b9c547cSRui Paulo 	struct radius_session *sess = ctx;
25615b9c547cSRui Paulo 	struct radius_server_data *data = sess->server;
25625b9c547cSRui Paulo 
25634bc52338SCy Schubert 	return radius_server_erp_find_key(data, keyname);
25645b9c547cSRui Paulo }
25655b9c547cSRui Paulo 
25665b9c547cSRui Paulo 
25675b9c547cSRui Paulo static int radius_server_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
25685b9c547cSRui Paulo {
25695b9c547cSRui Paulo 	struct radius_session *sess = ctx;
25705b9c547cSRui Paulo 	struct radius_server_data *data = sess->server;
25715b9c547cSRui Paulo 
25725b9c547cSRui Paulo 	dl_list_add(&data->erp_keys, &erp->list);
25735b9c547cSRui Paulo 	return 0;
25745b9c547cSRui Paulo }
25755b9c547cSRui Paulo 
25765b9c547cSRui Paulo #endif /* CONFIG_ERP */
25775b9c547cSRui Paulo 
25785b9c547cSRui Paulo 
2579325151a3SRui Paulo static const struct eapol_callbacks radius_server_eapol_cb =
258039beb93cSSam Leffler {
258139beb93cSSam Leffler 	.get_eap_user = radius_server_get_eap_user,
258239beb93cSSam Leffler 	.get_eap_req_id_text = radius_server_get_eap_req_id_text,
25835b9c547cSRui Paulo 	.log_msg = radius_server_log_msg,
25845b9c547cSRui Paulo #ifdef CONFIG_ERP
25855b9c547cSRui Paulo 	.get_erp_send_reauth_start = NULL,
25865b9c547cSRui Paulo 	.get_erp_domain = radius_server_get_erp_domain,
25875b9c547cSRui Paulo 	.erp_get_key = radius_server_erp_get_key,
25885b9c547cSRui Paulo 	.erp_add_key = radius_server_erp_add_key,
25895b9c547cSRui Paulo #endif /* CONFIG_ERP */
259039beb93cSSam Leffler };
259139beb93cSSam Leffler 
259239beb93cSSam Leffler 
2593e28a4053SRui Paulo /**
2594e28a4053SRui Paulo  * radius_server_eap_pending_cb - Pending EAP data notification
2595e28a4053SRui Paulo  * @data: RADIUS server context from radius_server_init()
2596e28a4053SRui Paulo  * @ctx: Pending EAP context pointer
2597e28a4053SRui Paulo  *
2598e28a4053SRui Paulo  * This function is used to notify EAP server module that a pending operation
2599e28a4053SRui Paulo  * has been completed and processing of the EAP session can proceed.
2600e28a4053SRui Paulo  */
260139beb93cSSam Leffler void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx)
260239beb93cSSam Leffler {
260339beb93cSSam Leffler 	struct radius_client *cli;
260439beb93cSSam Leffler 	struct radius_session *s, *sess = NULL;
260539beb93cSSam Leffler 	struct radius_msg *msg;
260639beb93cSSam Leffler 
260739beb93cSSam Leffler 	if (data == NULL)
260839beb93cSSam Leffler 		return;
260939beb93cSSam Leffler 
261039beb93cSSam Leffler 	for (cli = data->clients; cli; cli = cli->next) {
261139beb93cSSam Leffler 		for (s = cli->sessions; s; s = s->next) {
261239beb93cSSam Leffler 			if (s->eap == ctx && s->last_msg) {
261339beb93cSSam Leffler 				sess = s;
261439beb93cSSam Leffler 				break;
261539beb93cSSam Leffler 			}
261639beb93cSSam Leffler 		}
261739beb93cSSam Leffler 		if (sess)
261839beb93cSSam Leffler 			break;
261939beb93cSSam Leffler 	}
262039beb93cSSam Leffler 
262139beb93cSSam Leffler 	if (sess == NULL) {
262239beb93cSSam Leffler 		RADIUS_DEBUG("No session matched callback ctx");
262339beb93cSSam Leffler 		return;
262439beb93cSSam Leffler 	}
262539beb93cSSam Leffler 
262639beb93cSSam Leffler 	msg = sess->last_msg;
262739beb93cSSam Leffler 	sess->last_msg = NULL;
262839beb93cSSam Leffler 	eap_sm_pending_cb(sess->eap);
262939beb93cSSam Leffler 	if (radius_server_request(data, msg,
263039beb93cSSam Leffler 				  (struct sockaddr *) &sess->last_from,
263139beb93cSSam Leffler 				  sess->last_fromlen, cli,
263239beb93cSSam Leffler 				  sess->last_from_addr,
263339beb93cSSam Leffler 				  sess->last_from_port, sess) == -2)
263439beb93cSSam Leffler 		return; /* msg was stored with the session */
263539beb93cSSam Leffler 
263639beb93cSSam Leffler 	radius_msg_free(msg);
263739beb93cSSam Leffler }
263885732ac8SCy Schubert 
263985732ac8SCy Schubert 
264085732ac8SCy Schubert #ifdef CONFIG_SQLITE
264185732ac8SCy Schubert 
264285732ac8SCy Schubert struct db_session_fields {
264385732ac8SCy Schubert 	char *identity;
264485732ac8SCy Schubert 	char *nas;
264585732ac8SCy Schubert 	int hs20_t_c_filtering;
264685732ac8SCy Schubert 	int waiting_coa_ack;
264785732ac8SCy Schubert 	int coa_ack_received;
264885732ac8SCy Schubert };
264985732ac8SCy Schubert 
265085732ac8SCy Schubert 
265185732ac8SCy Schubert static int get_db_session_fields(void *ctx, int argc, char *argv[], char *col[])
265285732ac8SCy Schubert {
265385732ac8SCy Schubert 	struct db_session_fields *fields = ctx;
265485732ac8SCy Schubert 	int i;
265585732ac8SCy Schubert 
265685732ac8SCy Schubert 	for (i = 0; i < argc; i++) {
265785732ac8SCy Schubert 		if (!argv[i])
265885732ac8SCy Schubert 			continue;
265985732ac8SCy Schubert 
266085732ac8SCy Schubert 		RADIUS_DEBUG("Session DB: %s=%s", col[i], argv[i]);
266185732ac8SCy Schubert 
266285732ac8SCy Schubert 		if (os_strcmp(col[i], "identity") == 0) {
266385732ac8SCy Schubert 			os_free(fields->identity);
266485732ac8SCy Schubert 			fields->identity = os_strdup(argv[i]);
266585732ac8SCy Schubert 		} else if (os_strcmp(col[i], "nas") == 0) {
266685732ac8SCy Schubert 			os_free(fields->nas);
266785732ac8SCy Schubert 			fields->nas = os_strdup(argv[i]);
266885732ac8SCy Schubert 		} else if (os_strcmp(col[i], "hs20_t_c_filtering") == 0) {
266985732ac8SCy Schubert 			fields->hs20_t_c_filtering = atoi(argv[i]);
267085732ac8SCy Schubert 		} else if (os_strcmp(col[i], "waiting_coa_ack") == 0) {
267185732ac8SCy Schubert 			fields->waiting_coa_ack = atoi(argv[i]);
267285732ac8SCy Schubert 		} else if (os_strcmp(col[i], "coa_ack_received") == 0) {
267385732ac8SCy Schubert 			fields->coa_ack_received = atoi(argv[i]);
267485732ac8SCy Schubert 		}
267585732ac8SCy Schubert 	}
267685732ac8SCy Schubert 
267785732ac8SCy Schubert 	return 0;
267885732ac8SCy Schubert }
267985732ac8SCy Schubert 
268085732ac8SCy Schubert 
268185732ac8SCy Schubert static void free_db_session_fields(struct db_session_fields *fields)
268285732ac8SCy Schubert {
268385732ac8SCy Schubert 	os_free(fields->identity);
268485732ac8SCy Schubert 	fields->identity = NULL;
268585732ac8SCy Schubert 	os_free(fields->nas);
268685732ac8SCy Schubert 	fields->nas = NULL;
268785732ac8SCy Schubert }
268885732ac8SCy Schubert 
268985732ac8SCy Schubert #endif /* CONFIG_SQLITE */
269085732ac8SCy Schubert 
269185732ac8SCy Schubert 
269285732ac8SCy Schubert int radius_server_dac_request(struct radius_server_data *data, const char *req)
269385732ac8SCy Schubert {
269485732ac8SCy Schubert #ifdef CONFIG_SQLITE
269585732ac8SCy Schubert 	char *sql;
269685732ac8SCy Schubert 	int res;
269785732ac8SCy Schubert 	int disconnect;
269885732ac8SCy Schubert 	const char *pos = req;
269985732ac8SCy Schubert 	u8 addr[ETH_ALEN];
270085732ac8SCy Schubert 	char addrtxt[3 * ETH_ALEN];
270185732ac8SCy Schubert 	int t_c_clear = 0;
270285732ac8SCy Schubert 	struct db_session_fields fields;
270385732ac8SCy Schubert 	struct sockaddr_in das;
270485732ac8SCy Schubert 	struct radius_client *client;
270585732ac8SCy Schubert 	struct radius_msg *msg;
270685732ac8SCy Schubert 	struct wpabuf *buf;
270785732ac8SCy Schubert 	u8 identifier;
270885732ac8SCy Schubert 	struct os_time now;
270985732ac8SCy Schubert 
271085732ac8SCy Schubert 	if (!data)
271185732ac8SCy Schubert 		return -1;
271285732ac8SCy Schubert 
271385732ac8SCy Schubert 	/* req: <disconnect|coa> <MAC Address> [t_c_clear] */
271485732ac8SCy Schubert 
271585732ac8SCy Schubert 	if (os_strncmp(pos, "disconnect ", 11) == 0) {
271685732ac8SCy Schubert 		disconnect = 1;
271785732ac8SCy Schubert 		pos += 11;
271885732ac8SCy Schubert 	} else if (os_strncmp(req, "coa ", 4) == 0) {
271985732ac8SCy Schubert 		disconnect = 0;
272085732ac8SCy Schubert 		pos += 4;
272185732ac8SCy Schubert 	} else {
272285732ac8SCy Schubert 		return -1;
272385732ac8SCy Schubert 	}
272485732ac8SCy Schubert 
272585732ac8SCy Schubert 	if (hwaddr_aton(pos, addr))
272685732ac8SCy Schubert 		return -1;
272785732ac8SCy Schubert 	pos = os_strchr(pos, ' ');
272885732ac8SCy Schubert 	if (pos) {
272985732ac8SCy Schubert 		if (os_strstr(pos, "t_c_clear"))
273085732ac8SCy Schubert 			t_c_clear = 1;
273185732ac8SCy Schubert 	}
273285732ac8SCy Schubert 
273385732ac8SCy Schubert 	if (!disconnect && !t_c_clear) {
273485732ac8SCy Schubert 		RADIUS_ERROR("DAC request for CoA without any authorization change");
273585732ac8SCy Schubert 		return -1;
273685732ac8SCy Schubert 	}
273785732ac8SCy Schubert 
273885732ac8SCy Schubert 	if (!data->db) {
273985732ac8SCy Schubert 		RADIUS_ERROR("SQLite database not in use");
274085732ac8SCy Schubert 		return -1;
274185732ac8SCy Schubert 	}
274285732ac8SCy Schubert 
274385732ac8SCy Schubert 	os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, MAC2STR(addr));
274485732ac8SCy Schubert 
274585732ac8SCy Schubert 	sql = sqlite3_mprintf("SELECT * FROM current_sessions WHERE mac_addr=%Q",
274685732ac8SCy Schubert 			      addrtxt);
274785732ac8SCy Schubert 	if (!sql)
274885732ac8SCy Schubert 		return -1;
274985732ac8SCy Schubert 
275085732ac8SCy Schubert 	os_memset(&fields, 0, sizeof(fields));
275185732ac8SCy Schubert 	res = sqlite3_exec(data->db, sql, get_db_session_fields, &fields, NULL);
275285732ac8SCy Schubert 	sqlite3_free(sql);
275385732ac8SCy Schubert 	if (res != SQLITE_OK) {
275485732ac8SCy Schubert 		RADIUS_ERROR("Failed to find matching current_sessions entry from sqlite database: %s",
275585732ac8SCy Schubert 			     sqlite3_errmsg(data->db));
275685732ac8SCy Schubert 		free_db_session_fields(&fields);
275785732ac8SCy Schubert 		return -1;
275885732ac8SCy Schubert 	}
275985732ac8SCy Schubert 
276085732ac8SCy Schubert 	if (!fields.nas) {
276185732ac8SCy Schubert 		RADIUS_ERROR("No NAS information found from current_sessions");
276285732ac8SCy Schubert 		free_db_session_fields(&fields);
276385732ac8SCy Schubert 		return -1;
276485732ac8SCy Schubert 	}
276585732ac8SCy Schubert 
276685732ac8SCy Schubert 	os_memset(&das, 0, sizeof(das));
276785732ac8SCy Schubert 	das.sin_family = AF_INET;
276885732ac8SCy Schubert 	das.sin_addr.s_addr = inet_addr(fields.nas);
276985732ac8SCy Schubert 	das.sin_port = htons(3799);
277085732ac8SCy Schubert 
277185732ac8SCy Schubert 	free_db_session_fields(&fields);
277285732ac8SCy Schubert 
277385732ac8SCy Schubert 	client = radius_server_get_client(data, &das.sin_addr, 0);
277485732ac8SCy Schubert 	if (!client) {
277585732ac8SCy Schubert 		RADIUS_ERROR("No NAS information available to protect the packet");
277685732ac8SCy Schubert 		return -1;
277785732ac8SCy Schubert 	}
277885732ac8SCy Schubert 
277985732ac8SCy Schubert 	identifier = client->next_dac_identifier++;
278085732ac8SCy Schubert 
278185732ac8SCy Schubert 	msg = radius_msg_new(disconnect ? RADIUS_CODE_DISCONNECT_REQUEST :
278285732ac8SCy Schubert 			     RADIUS_CODE_COA_REQUEST, identifier);
278385732ac8SCy Schubert 	if (!msg)
278485732ac8SCy Schubert 		return -1;
278585732ac8SCy Schubert 
278685732ac8SCy Schubert 	os_snprintf(addrtxt, sizeof(addrtxt), RADIUS_802_1X_ADDR_FORMAT,
278785732ac8SCy Schubert 		    MAC2STR(addr));
278885732ac8SCy Schubert 	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
278985732ac8SCy Schubert 				 (u8 *) addrtxt, os_strlen(addrtxt))) {
279085732ac8SCy Schubert 		RADIUS_ERROR("Could not add Calling-Station-Id");
279185732ac8SCy Schubert 		radius_msg_free(msg);
279285732ac8SCy Schubert 		return -1;
279385732ac8SCy Schubert 	}
279485732ac8SCy Schubert 
279585732ac8SCy Schubert 	if (!disconnect && t_c_clear) {
279685732ac8SCy Schubert 		u8 val[4] = { 0x00, 0x00, 0x00, 0x00 }; /* E=0 */
279785732ac8SCy Schubert 
279885732ac8SCy Schubert 		if (!radius_msg_add_wfa(
279985732ac8SCy Schubert 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING,
280085732ac8SCy Schubert 			    val, sizeof(val))) {
280185732ac8SCy Schubert 			RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering");
280285732ac8SCy Schubert 			radius_msg_free(msg);
280385732ac8SCy Schubert 			return -1;
280485732ac8SCy Schubert 		}
280585732ac8SCy Schubert 	}
280685732ac8SCy Schubert 
280785732ac8SCy Schubert 	os_get_time(&now);
280885732ac8SCy Schubert 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
280985732ac8SCy Schubert 				       now.sec)) {
281085732ac8SCy Schubert 		RADIUS_ERROR("Failed to add Event-Timestamp attribute");
281185732ac8SCy Schubert 		radius_msg_free(msg);
281285732ac8SCy Schubert 		return -1;
281385732ac8SCy Schubert 	}
281485732ac8SCy Schubert 
281585732ac8SCy Schubert 	radius_msg_finish_acct(msg, (u8 *) client->shared_secret,
281685732ac8SCy Schubert 			       client->shared_secret_len);
281785732ac8SCy Schubert 
281885732ac8SCy Schubert 	if (wpa_debug_level <= MSG_MSGDUMP)
281985732ac8SCy Schubert 		radius_msg_dump(msg);
282085732ac8SCy Schubert 
282185732ac8SCy Schubert 	buf = radius_msg_get_buf(msg);
282285732ac8SCy Schubert 	if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
282385732ac8SCy Schubert 		   (struct sockaddr *) &das, sizeof(das)) < 0) {
282485732ac8SCy Schubert 		RADIUS_ERROR("Failed to send packet - sendto: %s",
282585732ac8SCy Schubert 			     strerror(errno));
282685732ac8SCy Schubert 		radius_msg_free(msg);
282785732ac8SCy Schubert 		return -1;
282885732ac8SCy Schubert 	}
282985732ac8SCy Schubert 
283085732ac8SCy Schubert 	if (disconnect) {
283185732ac8SCy Schubert 		radius_msg_free(client->pending_dac_disconnect_req);
283285732ac8SCy Schubert 		client->pending_dac_disconnect_req = msg;
283385732ac8SCy Schubert 		client->pending_dac_disconnect_id = identifier;
283485732ac8SCy Schubert 		os_memcpy(client->pending_dac_disconnect_addr, addr, ETH_ALEN);
283585732ac8SCy Schubert 	} else {
283685732ac8SCy Schubert 		radius_msg_free(client->pending_dac_coa_req);
283785732ac8SCy Schubert 		client->pending_dac_coa_req = msg;
283885732ac8SCy Schubert 		client->pending_dac_coa_id = identifier;
283985732ac8SCy Schubert 		os_memcpy(client->pending_dac_coa_addr, addr, ETH_ALEN);
284085732ac8SCy Schubert 	}
284185732ac8SCy Schubert 
284285732ac8SCy Schubert 	return 0;
284385732ac8SCy Schubert #else /* CONFIG_SQLITE */
284485732ac8SCy Schubert 	return -1;
284585732ac8SCy Schubert #endif /* CONFIG_SQLITE */
284685732ac8SCy Schubert }
2847