xref: /freebsd/contrib/wpa/src/radius/radius_server.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
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 */
227c1d255d3SCy Schubert 
228c1d255d3SCy 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 
db_table_exists(sqlite3 * db,const char * name)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 
db_table_create_sim_provisioning(sqlite3 * db)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 
srv_log(struct radius_session * sess,const char * fmt,...)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 *
radius_server_get_client(struct radius_server_data * data,struct in_addr * addr,int ipv6)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 *
radius_server_get_session(struct radius_client * client,unsigned int sess_id)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 
radius_server_session_free(struct radius_server_data * data,struct radius_session * sess)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 
radius_server_session_remove(struct radius_server_data * data,struct radius_session * sess)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 
radius_server_session_remove_timeout(void * eloop_ctx,void * timeout_ctx)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 
radius_server_session_timeout(void * eloop_ctx,void * timeout_ctx)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 *
radius_server_new_session(struct radius_server_data * data,struct radius_client * client)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
radius_server_testing_options_tls(struct radius_session * sess,const char * tls,struct eap_session_data * eap_conf)4775b9c547cSRui Paulo static void radius_server_testing_options_tls(struct radius_session *sess,
4785b9c547cSRui Paulo 					      const char *tls,
479c1d255d3SCy 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 
radius_server_testing_options(struct radius_session * sess,struct eap_session_data * eap_conf)5235b9c547cSRui Paulo static void radius_server_testing_options(struct radius_session *sess,
524c1d255d3SCy 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 *
radius_server_erp_find_key(struct radius_server_data * data,const char * keyname)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 *
radius_server_get_new_session(struct radius_server_data * data,struct radius_client * client,struct radius_msg * msg,const char * from_addr)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;
567c1d255d3SCy 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
585c1d255d3SCy 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 
644c1d255d3SCy Schubert 	os_memset(&eap_sess, 0, sizeof(eap_sess));
645c1d255d3SCy Schubert 	radius_server_testing_options(sess, &eap_sess);
64639beb93cSSam Leffler 	sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
647c1d255d3SCy 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);
655c1d255d3SCy Schubert 	sess->eap_if->eapRestart = true;
656c1d255d3SCy 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
radius_srv_hs20_t_c_pending(struct radius_session * sess)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 
radius_server_add_session(struct radius_session * sess)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 
db_update_last_msk(struct radius_session * sess,const char * msk)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 
radius_server_is_sim_method(struct radius_session * sess)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 
radius_server_hs20_missing_sim_pps(struct radius_msg * request)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 
radius_server_sim_provisioning_session(struct radius_session * sess,const u8 * hash)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 *
radius_server_encapsulate_eap(struct radius_server_data * data,struct radius_client * client,struct radius_session * sess,struct radius_msg * request)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) {
907c1d255d3SCy Schubert 		sess->eap_if->eapFail = false;
90839beb93cSSam Leffler 		code = RADIUS_CODE_ACCESS_REJECT;
90939beb93cSSam Leffler 	} else if (sess->eap_if->eapSuccess) {
910c1d255d3SCy Schubert 		sess->eap_if->eapSuccess = false;
91139beb93cSSam Leffler 		code = RADIUS_CODE_ACCESS_ACCEPT;
91239beb93cSSam Leffler 	} else {
913c1d255d3SCy 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 
923*a90b9d01SCy Schubert 	if (!radius_msg_add_msg_auth(msg)) {
924*a90b9d01SCy Schubert 		radius_msg_free(msg);
925*a90b9d01SCy Schubert 		return NULL;
926*a90b9d01SCy Schubert 	}
927*a90b9d01SCy Schubert 
92839beb93cSSam Leffler 	sess_id = htonl(sess->sess_id);
92939beb93cSSam Leffler 	if (code == RADIUS_CODE_ACCESS_CHALLENGE &&
93039beb93cSSam Leffler 	    !radius_msg_add_attr(msg, RADIUS_ATTR_STATE,
93139beb93cSSam Leffler 				 (u8 *) &sess_id, sizeof(sess_id))) {
93239beb93cSSam Leffler 		RADIUS_DEBUG("Failed to add State attribute");
93339beb93cSSam Leffler 	}
93439beb93cSSam Leffler 
93539beb93cSSam Leffler 	if (sess->eap_if->eapReqData &&
93639beb93cSSam Leffler 	    !radius_msg_add_eap(msg, wpabuf_head(sess->eap_if->eapReqData),
93739beb93cSSam Leffler 				wpabuf_len(sess->eap_if->eapReqData))) {
93839beb93cSSam Leffler 		RADIUS_DEBUG("Failed to add EAP-Message attribute");
93939beb93cSSam Leffler 	}
94039beb93cSSam Leffler 
94139beb93cSSam Leffler 	if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) {
94239beb93cSSam Leffler 		int len;
943f05cddf9SRui Paulo #ifdef CONFIG_RADIUS_TEST
94485732ac8SCy Schubert 		char buf[2 * 64 + 1];
94585732ac8SCy Schubert 
94685732ac8SCy Schubert 		len = sess->eap_if->eapKeyDataLen;
94785732ac8SCy Schubert 		if (len > 64)
94885732ac8SCy Schubert 			len = 64;
94985732ac8SCy Schubert 		len = wpa_snprintf_hex(buf, sizeof(buf),
95085732ac8SCy Schubert 				       sess->eap_if->eapKeyData, len);
95185732ac8SCy Schubert 		buf[len] = '\0';
95285732ac8SCy Schubert 
953f05cddf9SRui Paulo 		if (data->dump_msk_file) {
954f05cddf9SRui Paulo 			FILE *f;
95585732ac8SCy Schubert 
956f05cddf9SRui Paulo 			f = fopen(data->dump_msk_file, "a");
957f05cddf9SRui Paulo 			if (f) {
958f05cddf9SRui Paulo 				len = sess->eap_if->eapKeyDataLen;
959f05cddf9SRui Paulo 				if (len > 64)
960f05cddf9SRui Paulo 					len = 64;
961f05cddf9SRui Paulo 				len = wpa_snprintf_hex(
962f05cddf9SRui Paulo 					buf, sizeof(buf),
963f05cddf9SRui Paulo 					sess->eap_if->eapKeyData, len);
964f05cddf9SRui Paulo 				buf[len] = '\0';
965f05cddf9SRui Paulo 				fprintf(f, "%s\n", buf);
966f05cddf9SRui Paulo 				fclose(f);
967f05cddf9SRui Paulo 			}
968f05cddf9SRui Paulo 		}
96985732ac8SCy Schubert 
97085732ac8SCy Schubert 		db_update_last_msk(sess, buf);
971f05cddf9SRui Paulo #endif /* CONFIG_RADIUS_TEST */
97239beb93cSSam Leffler 		if (sess->eap_if->eapKeyDataLen > 64) {
97339beb93cSSam Leffler 			len = 32;
97439beb93cSSam Leffler 		} else {
97539beb93cSSam Leffler 			len = sess->eap_if->eapKeyDataLen / 2;
97639beb93cSSam Leffler 		}
977e28a4053SRui Paulo 		if (!radius_msg_add_mppe_keys(msg, hdr->authenticator,
97839beb93cSSam Leffler 					      (u8 *) client->shared_secret,
97939beb93cSSam Leffler 					      client->shared_secret_len,
98039beb93cSSam Leffler 					      sess->eap_if->eapKeyData + len,
98139beb93cSSam Leffler 					      len, sess->eap_if->eapKeyData,
98239beb93cSSam Leffler 					      len)) {
98339beb93cSSam Leffler 			RADIUS_DEBUG("Failed to add MPPE key attributes");
98439beb93cSSam Leffler 		}
985206b73d0SCy Schubert 
986206b73d0SCy Schubert 		if (sess->eap_if->eapSessionId &&
987206b73d0SCy Schubert 		    !radius_msg_add_attr(msg, RADIUS_ATTR_EAP_KEY_NAME,
988206b73d0SCy Schubert 					 sess->eap_if->eapSessionId,
989206b73d0SCy Schubert 					 sess->eap_if->eapSessionIdLen)) {
990206b73d0SCy Schubert 			RADIUS_DEBUG("Failed to add EAP-Key-Name attribute");
991206b73d0SCy Schubert 		}
99239beb93cSSam Leffler 	}
99339beb93cSSam Leffler 
9945b9c547cSRui Paulo #ifdef CONFIG_HS20
9955b9c547cSRui Paulo 	if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation &&
9965b9c547cSRui Paulo 	    data->subscr_remediation_url) {
9975b9c547cSRui Paulo 		u8 *buf;
9985b9c547cSRui Paulo 		size_t url_len = os_strlen(data->subscr_remediation_url);
9995b9c547cSRui Paulo 		buf = os_malloc(1 + url_len);
10005b9c547cSRui Paulo 		if (buf == NULL) {
10015b9c547cSRui Paulo 			radius_msg_free(msg);
10025b9c547cSRui Paulo 			return NULL;
10035b9c547cSRui Paulo 		}
10045b9c547cSRui Paulo 		buf[0] = data->subscr_remediation_method;
10055b9c547cSRui Paulo 		os_memcpy(&buf[1], data->subscr_remediation_url, url_len);
10065b9c547cSRui Paulo 		if (!radius_msg_add_wfa(
10075b9c547cSRui Paulo 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
10085b9c547cSRui Paulo 			    buf, 1 + url_len)) {
10095b9c547cSRui Paulo 			RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
10105b9c547cSRui Paulo 		}
10115b9c547cSRui Paulo 		os_free(buf);
10125b9c547cSRui Paulo 	} else if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation) {
10135b9c547cSRui Paulo 		u8 buf[1];
10145b9c547cSRui Paulo 		if (!radius_msg_add_wfa(
10155b9c547cSRui Paulo 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
10165b9c547cSRui Paulo 			    buf, 0)) {
10175b9c547cSRui Paulo 			RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
10185b9c547cSRui Paulo 		}
10194bc52338SCy Schubert 	} else if (code == RADIUS_CODE_ACCESS_ACCEPT &&
10204bc52338SCy Schubert 		   data->hs20_sim_provisioning_url &&
10214bc52338SCy Schubert 		   radius_server_is_sim_method(sess) &&
10224bc52338SCy Schubert 		   radius_server_hs20_missing_sim_pps(request)) {
10234bc52338SCy Schubert 		u8 *buf, *pos, hash[HS20_MOBILE_ID_HASH_LEN];
10244bc52338SCy Schubert 		size_t prefix_len, url_len;
10254bc52338SCy Schubert 
10264bc52338SCy Schubert 		RADIUS_DEBUG("Device needs HS 2.0 SIM provisioning");
10274bc52338SCy Schubert 
10284bc52338SCy Schubert 		if (os_get_random(hash, HS20_MOBILE_ID_HASH_LEN) < 0) {
10294bc52338SCy Schubert 			radius_msg_free(msg);
10304bc52338SCy Schubert 			return NULL;
10314bc52338SCy Schubert 		}
10324bc52338SCy Schubert 		RADIUS_DUMP("hotspot2dot0-mobile-identifier-hash",
10334bc52338SCy Schubert 			    hash, HS20_MOBILE_ID_HASH_LEN);
10344bc52338SCy Schubert 
10354bc52338SCy Schubert 		if (radius_server_sim_provisioning_session(sess, hash) < 0) {
10364bc52338SCy Schubert 			radius_msg_free(msg);
10374bc52338SCy Schubert 			return NULL;
10384bc52338SCy Schubert 		}
10394bc52338SCy Schubert 
10404bc52338SCy Schubert 		prefix_len = os_strlen(data->hs20_sim_provisioning_url);
10414bc52338SCy Schubert 		url_len = prefix_len + 2 * HS20_MOBILE_ID_HASH_LEN;
10424bc52338SCy Schubert 		buf = os_malloc(1 + url_len + 1);
10434bc52338SCy Schubert 		if (!buf) {
10444bc52338SCy Schubert 			radius_msg_free(msg);
10454bc52338SCy Schubert 			return NULL;
10464bc52338SCy Schubert 		}
10474bc52338SCy Schubert 		pos = buf;
10484bc52338SCy Schubert 		*pos++ = data->subscr_remediation_method;
10494bc52338SCy Schubert 		os_memcpy(pos, data->hs20_sim_provisioning_url, prefix_len);
10504bc52338SCy Schubert 		pos += prefix_len;
10514bc52338SCy Schubert 		wpa_snprintf_hex((char *) pos, 2 * HS20_MOBILE_ID_HASH_LEN + 1,
10524bc52338SCy Schubert 				 hash, HS20_MOBILE_ID_HASH_LEN);
10534bc52338SCy Schubert 		RADIUS_DEBUG("HS 2.0 subscription remediation URL: %s",
10544bc52338SCy Schubert 			     (char *) &buf[1]);
10554bc52338SCy Schubert 		if (!radius_msg_add_wfa(
10564bc52338SCy Schubert 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
10574bc52338SCy Schubert 			    buf, 1 + url_len)) {
10584bc52338SCy Schubert 			RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
10594bc52338SCy Schubert 		}
10604bc52338SCy Schubert 		os_free(buf);
10615b9c547cSRui Paulo 	}
106285732ac8SCy Schubert 
106385732ac8SCy Schubert 	if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) {
106485732ac8SCy Schubert 		u8 buf[4] = { 0x01, 0x00, 0x00, 0x00 }; /* E=1 */
106585732ac8SCy Schubert 		const char *url = data->t_c_server_url, *pos;
106685732ac8SCy Schubert 		char *url2, *end2, *pos2;
106785732ac8SCy Schubert 		size_t url_len;
106885732ac8SCy Schubert 
106985732ac8SCy Schubert 		if (!radius_msg_add_wfa(
107085732ac8SCy Schubert 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING,
107185732ac8SCy Schubert 			    buf, sizeof(buf))) {
107285732ac8SCy Schubert 			RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering");
107385732ac8SCy Schubert 			radius_msg_free(msg);
107485732ac8SCy Schubert 			return NULL;
107585732ac8SCy Schubert 		}
107685732ac8SCy Schubert 
107785732ac8SCy Schubert 		if (!url) {
107885732ac8SCy Schubert 			RADIUS_DEBUG("No t_c_server_url configured");
107985732ac8SCy Schubert 			radius_msg_free(msg);
108085732ac8SCy Schubert 			return NULL;
108185732ac8SCy Schubert 		}
108285732ac8SCy Schubert 
108385732ac8SCy Schubert 		pos = os_strstr(url, "@1@");
108485732ac8SCy Schubert 		if (!pos) {
108585732ac8SCy Schubert 			RADIUS_DEBUG("No @1@ macro in t_c_server_url");
108685732ac8SCy Schubert 			radius_msg_free(msg);
108785732ac8SCy Schubert 			return NULL;
108885732ac8SCy Schubert 		}
108985732ac8SCy Schubert 
109085732ac8SCy Schubert 		url_len = os_strlen(url) + ETH_ALEN * 3 - 1 - 3;
109185732ac8SCy Schubert 		url2 = os_malloc(url_len + 1);
109285732ac8SCy Schubert 		if (!url2) {
109385732ac8SCy Schubert 			RADIUS_DEBUG("Failed to allocate room for T&C Server URL");
109485732ac8SCy Schubert 			os_free(url2);
109585732ac8SCy Schubert 			radius_msg_free(msg);
109685732ac8SCy Schubert 			return NULL;
109785732ac8SCy Schubert 		}
109885732ac8SCy Schubert 		pos2 = url2;
109985732ac8SCy Schubert 		end2 = url2 + url_len + 1;
110085732ac8SCy Schubert 		os_memcpy(pos2, url, pos - url);
110185732ac8SCy Schubert 		pos2 += pos - url;
110285732ac8SCy Schubert 		os_snprintf(pos2, end2 - pos2, MACSTR, MAC2STR(sess->mac_addr));
110385732ac8SCy Schubert 		pos2 += ETH_ALEN * 3 - 1;
110485732ac8SCy Schubert 		os_memcpy(pos2, pos + 3, os_strlen(pos + 3));
110585732ac8SCy Schubert 		if (!radius_msg_add_wfa(msg,
110685732ac8SCy Schubert 					RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL,
110785732ac8SCy Schubert 					(const u8 *) url2, url_len)) {
110885732ac8SCy Schubert 			RADIUS_DEBUG("Failed to add WFA-HS20-T-C-URL");
110985732ac8SCy Schubert 			os_free(url2);
111085732ac8SCy Schubert 			radius_msg_free(msg);
111185732ac8SCy Schubert 			return NULL;
111285732ac8SCy Schubert 		}
111385732ac8SCy Schubert 		os_free(url2);
111485732ac8SCy Schubert 
111585732ac8SCy Schubert 		radius_srv_hs20_t_c_pending(sess);
111685732ac8SCy Schubert 	}
11175b9c547cSRui Paulo #endif /* CONFIG_HS20 */
11185b9c547cSRui Paulo 
111939beb93cSSam Leffler 	if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
112039beb93cSSam Leffler 		RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
112139beb93cSSam Leffler 		radius_msg_free(msg);
112239beb93cSSam Leffler 		return NULL;
112339beb93cSSam Leffler 	}
112439beb93cSSam Leffler 
11255b9c547cSRui Paulo 	if (code == RADIUS_CODE_ACCESS_ACCEPT) {
11265b9c547cSRui Paulo 		struct hostapd_radius_attr *attr;
11275b9c547cSRui Paulo 		for (attr = sess->accept_attr; attr; attr = attr->next) {
11285b9c547cSRui Paulo 			if (!radius_msg_add_attr(msg, attr->type,
11295b9c547cSRui Paulo 						 wpabuf_head(attr->val),
11305b9c547cSRui Paulo 						 wpabuf_len(attr->val))) {
11315b9c547cSRui Paulo 				wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
11325b9c547cSRui Paulo 				radius_msg_free(msg);
11335b9c547cSRui Paulo 				return NULL;
11345b9c547cSRui Paulo 			}
11355b9c547cSRui Paulo 		}
11365b9c547cSRui Paulo 	}
11375b9c547cSRui Paulo 
113885732ac8SCy Schubert 	if (code == RADIUS_CODE_ACCESS_REJECT) {
113985732ac8SCy Schubert 		if (radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE,
114085732ac8SCy Schubert 					      reason) < 0) {
114185732ac8SCy Schubert 			RADIUS_DEBUG("Failed to add WLAN-Reason-Code attribute");
114285732ac8SCy Schubert 			radius_msg_free(msg);
114385732ac8SCy Schubert 			return NULL;
114485732ac8SCy Schubert 		}
114585732ac8SCy Schubert 	}
114685732ac8SCy Schubert 
11475b9c547cSRui Paulo 	if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
11485b9c547cSRui Paulo 				  client->shared_secret_len,
11495b9c547cSRui Paulo 				  hdr->authenticator) < 0) {
11505b9c547cSRui Paulo 		RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
11515b9c547cSRui Paulo 	}
11525b9c547cSRui Paulo 
115385732ac8SCy Schubert 	if (code == RADIUS_CODE_ACCESS_ACCEPT)
115485732ac8SCy Schubert 		radius_server_add_session(sess);
115585732ac8SCy Schubert 
11565b9c547cSRui Paulo 	return msg;
11575b9c547cSRui Paulo }
11585b9c547cSRui Paulo 
11595b9c547cSRui Paulo 
11605b9c547cSRui Paulo static struct radius_msg *
radius_server_macacl(struct radius_server_data * data,struct radius_client * client,struct radius_session * sess,struct radius_msg * request)11615b9c547cSRui Paulo radius_server_macacl(struct radius_server_data *data,
11625b9c547cSRui Paulo 		     struct radius_client *client,
11635b9c547cSRui Paulo 		     struct radius_session *sess,
11645b9c547cSRui Paulo 		     struct radius_msg *request)
11655b9c547cSRui Paulo {
11665b9c547cSRui Paulo 	struct radius_msg *msg;
11675b9c547cSRui Paulo 	int code;
11685b9c547cSRui Paulo 	struct radius_hdr *hdr = radius_msg_get_hdr(request);
11695b9c547cSRui Paulo 	u8 *pw;
11705b9c547cSRui Paulo 	size_t pw_len;
11715b9c547cSRui Paulo 
11725b9c547cSRui Paulo 	code = RADIUS_CODE_ACCESS_ACCEPT;
11735b9c547cSRui Paulo 
11745b9c547cSRui Paulo 	if (radius_msg_get_attr_ptr(request, RADIUS_ATTR_USER_PASSWORD, &pw,
11755b9c547cSRui Paulo 				    &pw_len, NULL) < 0) {
11765b9c547cSRui Paulo 		RADIUS_DEBUG("Could not get User-Password");
11775b9c547cSRui Paulo 		code = RADIUS_CODE_ACCESS_REJECT;
11785b9c547cSRui Paulo 	} else {
11795b9c547cSRui Paulo 		int res;
11805b9c547cSRui Paulo 		struct eap_user tmp;
11815b9c547cSRui Paulo 
11825b9c547cSRui Paulo 		os_memset(&tmp, 0, sizeof(tmp));
11835b9c547cSRui Paulo 		res = data->get_eap_user(data->conf_ctx, (u8 *) sess->username,
11845b9c547cSRui Paulo 					 os_strlen(sess->username), 0, &tmp);
11855b9c547cSRui Paulo 		if (res || !tmp.macacl || tmp.password == NULL) {
11865b9c547cSRui Paulo 			RADIUS_DEBUG("No MAC ACL user entry");
11875b9c547cSRui Paulo 			bin_clear_free(tmp.password, tmp.password_len);
11885b9c547cSRui Paulo 			code = RADIUS_CODE_ACCESS_REJECT;
11895b9c547cSRui Paulo 		} else {
11905b9c547cSRui Paulo 			u8 buf[128];
11915b9c547cSRui Paulo 			res = radius_user_password_hide(
11925b9c547cSRui Paulo 				request, tmp.password, tmp.password_len,
11935b9c547cSRui Paulo 				(u8 *) client->shared_secret,
11945b9c547cSRui Paulo 				client->shared_secret_len,
11955b9c547cSRui Paulo 				buf, sizeof(buf));
11965b9c547cSRui Paulo 			bin_clear_free(tmp.password, tmp.password_len);
11975b9c547cSRui Paulo 
11985b9c547cSRui Paulo 			if (res < 0 || pw_len != (size_t) res ||
11995b9c547cSRui Paulo 			    os_memcmp_const(pw, buf, res) != 0) {
12005b9c547cSRui Paulo 				RADIUS_DEBUG("Incorrect User-Password");
12015b9c547cSRui Paulo 				code = RADIUS_CODE_ACCESS_REJECT;
12025b9c547cSRui Paulo 			}
12035b9c547cSRui Paulo 		}
12045b9c547cSRui Paulo 	}
12055b9c547cSRui Paulo 
12065b9c547cSRui Paulo 	msg = radius_msg_new(code, hdr->identifier);
12075b9c547cSRui Paulo 	if (msg == NULL) {
12085b9c547cSRui Paulo 		RADIUS_DEBUG("Failed to allocate reply message");
12095b9c547cSRui Paulo 		return NULL;
12105b9c547cSRui Paulo 	}
12115b9c547cSRui Paulo 
1212*a90b9d01SCy Schubert 	if (!radius_msg_add_msg_auth(msg)) {
1213*a90b9d01SCy Schubert 		radius_msg_free(msg);
1214*a90b9d01SCy Schubert 		return NULL;
1215*a90b9d01SCy Schubert 	}
1216*a90b9d01SCy Schubert 
12175b9c547cSRui Paulo 	if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
12185b9c547cSRui Paulo 		RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
12195b9c547cSRui Paulo 		radius_msg_free(msg);
12205b9c547cSRui Paulo 		return NULL;
12215b9c547cSRui Paulo 	}
12225b9c547cSRui Paulo 
12235b9c547cSRui Paulo 	if (code == RADIUS_CODE_ACCESS_ACCEPT) {
12245b9c547cSRui Paulo 		struct hostapd_radius_attr *attr;
12255b9c547cSRui Paulo 		for (attr = sess->accept_attr; attr; attr = attr->next) {
12265b9c547cSRui Paulo 			if (!radius_msg_add_attr(msg, attr->type,
12275b9c547cSRui Paulo 						 wpabuf_head(attr->val),
12285b9c547cSRui Paulo 						 wpabuf_len(attr->val))) {
12295b9c547cSRui Paulo 				wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
12305b9c547cSRui Paulo 				radius_msg_free(msg);
12315b9c547cSRui Paulo 				return NULL;
12325b9c547cSRui Paulo 			}
12335b9c547cSRui Paulo 		}
12345b9c547cSRui Paulo 	}
12355b9c547cSRui Paulo 
123639beb93cSSam Leffler 	if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
123739beb93cSSam Leffler 				  client->shared_secret_len,
1238e28a4053SRui Paulo 				  hdr->authenticator) < 0) {
123939beb93cSSam Leffler 		RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
124039beb93cSSam Leffler 	}
124139beb93cSSam Leffler 
124239beb93cSSam Leffler 	return msg;
124339beb93cSSam Leffler }
124439beb93cSSam Leffler 
124539beb93cSSam Leffler 
radius_server_reject(struct radius_server_data * data,struct radius_client * client,struct radius_msg * request,struct sockaddr * from,socklen_t fromlen,const char * from_addr,int from_port)124639beb93cSSam Leffler static int radius_server_reject(struct radius_server_data *data,
124739beb93cSSam Leffler 				struct radius_client *client,
124839beb93cSSam Leffler 				struct radius_msg *request,
124939beb93cSSam Leffler 				struct sockaddr *from, socklen_t fromlen,
125039beb93cSSam Leffler 				const char *from_addr, int from_port)
125139beb93cSSam Leffler {
125239beb93cSSam Leffler 	struct radius_msg *msg;
125339beb93cSSam Leffler 	int ret = 0;
125439beb93cSSam Leffler 	struct eap_hdr eapfail;
1255e28a4053SRui Paulo 	struct wpabuf *buf;
1256e28a4053SRui Paulo 	struct radius_hdr *hdr = radius_msg_get_hdr(request);
125739beb93cSSam Leffler 
125839beb93cSSam Leffler 	RADIUS_DEBUG("Reject invalid request from %s:%d",
125939beb93cSSam Leffler 		     from_addr, from_port);
126039beb93cSSam Leffler 
1261e28a4053SRui Paulo 	msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, hdr->identifier);
126239beb93cSSam Leffler 	if (msg == NULL) {
126339beb93cSSam Leffler 		return -1;
126439beb93cSSam Leffler 	}
126539beb93cSSam Leffler 
1266*a90b9d01SCy Schubert 	if (!radius_msg_add_msg_auth(msg)) {
1267*a90b9d01SCy Schubert 		radius_msg_free(msg);
1268*a90b9d01SCy Schubert 		return -1;
1269*a90b9d01SCy Schubert 	}
1270*a90b9d01SCy Schubert 
127139beb93cSSam Leffler 	os_memset(&eapfail, 0, sizeof(eapfail));
127239beb93cSSam Leffler 	eapfail.code = EAP_CODE_FAILURE;
127339beb93cSSam Leffler 	eapfail.identifier = 0;
127439beb93cSSam Leffler 	eapfail.length = host_to_be16(sizeof(eapfail));
127539beb93cSSam Leffler 
127639beb93cSSam Leffler 	if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) {
127739beb93cSSam Leffler 		RADIUS_DEBUG("Failed to add EAP-Message attribute");
127839beb93cSSam Leffler 	}
127939beb93cSSam Leffler 
128039beb93cSSam Leffler 	if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
128139beb93cSSam Leffler 		RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
128239beb93cSSam Leffler 		radius_msg_free(msg);
128339beb93cSSam Leffler 		return -1;
128439beb93cSSam Leffler 	}
128539beb93cSSam Leffler 
128639beb93cSSam Leffler 	if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
128739beb93cSSam Leffler 				  client->shared_secret_len,
1288e28a4053SRui Paulo 				  hdr->authenticator) <
1289e28a4053SRui Paulo 	    0) {
129039beb93cSSam Leffler 		RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
129139beb93cSSam Leffler 	}
129239beb93cSSam Leffler 
129339beb93cSSam Leffler 	if (wpa_debug_level <= MSG_MSGDUMP) {
129439beb93cSSam Leffler 		radius_msg_dump(msg);
129539beb93cSSam Leffler 	}
129639beb93cSSam Leffler 
129739beb93cSSam Leffler 	data->counters.access_rejects++;
129839beb93cSSam Leffler 	client->counters.access_rejects++;
1299e28a4053SRui Paulo 	buf = radius_msg_get_buf(msg);
1300e28a4053SRui Paulo 	if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
130139beb93cSSam Leffler 		   (struct sockaddr *) from, sizeof(*from)) < 0) {
13025b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno));
130339beb93cSSam Leffler 		ret = -1;
130439beb93cSSam Leffler 	}
130539beb93cSSam Leffler 
130639beb93cSSam Leffler 	radius_msg_free(msg);
130739beb93cSSam Leffler 
130839beb93cSSam Leffler 	return ret;
130939beb93cSSam Leffler }
131039beb93cSSam Leffler 
131139beb93cSSam Leffler 
radius_server_hs20_t_c_check(struct radius_session * sess,struct radius_msg * msg)131285732ac8SCy Schubert static void radius_server_hs20_t_c_check(struct radius_session *sess,
131385732ac8SCy Schubert 					 struct radius_msg *msg)
131485732ac8SCy Schubert {
131585732ac8SCy Schubert #ifdef CONFIG_HS20
131685732ac8SCy Schubert 	u8 *buf, *pos, *end, type, sublen, *timestamp = NULL;
131785732ac8SCy Schubert 	size_t len;
131885732ac8SCy Schubert 
131985732ac8SCy Schubert 	buf = NULL;
132085732ac8SCy Schubert 	for (;;) {
132185732ac8SCy Schubert 		if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
132285732ac8SCy Schubert 					    &buf, &len, buf) < 0)
132385732ac8SCy Schubert 			break;
132485732ac8SCy Schubert 		if (len < 6)
132585732ac8SCy Schubert 			continue;
132685732ac8SCy Schubert 		pos = buf;
132785732ac8SCy Schubert 		end = buf + len;
132885732ac8SCy Schubert 		if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
132985732ac8SCy Schubert 			continue;
133085732ac8SCy Schubert 		pos += 4;
133185732ac8SCy Schubert 
133285732ac8SCy Schubert 		type = *pos++;
133385732ac8SCy Schubert 		sublen = *pos++;
133485732ac8SCy Schubert 		if (sublen < 2)
133585732ac8SCy Schubert 			continue; /* invalid length */
133685732ac8SCy Schubert 		sublen -= 2; /* skip header */
133785732ac8SCy Schubert 		if (pos + sublen > end)
133885732ac8SCy Schubert 			continue; /* invalid WFA VSA */
133985732ac8SCy Schubert 
134085732ac8SCy Schubert 		if (type == RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP && len >= 4) {
134185732ac8SCy Schubert 			timestamp = pos;
134285732ac8SCy Schubert 			break;
134385732ac8SCy Schubert 		}
134485732ac8SCy Schubert 	}
134585732ac8SCy Schubert 
134685732ac8SCy Schubert 	if (!timestamp)
134785732ac8SCy Schubert 		return;
134885732ac8SCy Schubert 	RADIUS_DEBUG("HS20-Timestamp: %u", WPA_GET_BE32(timestamp));
134985732ac8SCy Schubert 	if (sess->t_c_timestamp != WPA_GET_BE32(timestamp)) {
135085732ac8SCy Schubert 		RADIUS_DEBUG("Last read T&C timestamp does not match HS20-Timestamp --> require filtering");
135185732ac8SCy Schubert 		sess->t_c_filtering = 1;
135285732ac8SCy Schubert 	}
135385732ac8SCy Schubert #endif /* CONFIG_HS20 */
135485732ac8SCy Schubert }
135585732ac8SCy Schubert 
135685732ac8SCy Schubert 
radius_server_request(struct radius_server_data * data,struct radius_msg * msg,struct sockaddr * from,socklen_t fromlen,struct radius_client * client,const char * from_addr,int from_port,struct radius_session * force_sess)135739beb93cSSam Leffler static int radius_server_request(struct radius_server_data *data,
135839beb93cSSam Leffler 				 struct radius_msg *msg,
135939beb93cSSam Leffler 				 struct sockaddr *from, socklen_t fromlen,
136039beb93cSSam Leffler 				 struct radius_client *client,
136139beb93cSSam Leffler 				 const char *from_addr, int from_port,
136239beb93cSSam Leffler 				 struct radius_session *force_sess)
136339beb93cSSam Leffler {
1364f05cddf9SRui Paulo 	struct wpabuf *eap = NULL;
136539beb93cSSam Leffler 	int res, state_included = 0;
136639beb93cSSam Leffler 	u8 statebuf[4];
136739beb93cSSam Leffler 	unsigned int state;
136839beb93cSSam Leffler 	struct radius_session *sess;
136939beb93cSSam Leffler 	struct radius_msg *reply;
13703157ba21SRui Paulo 	int is_complete = 0;
137139beb93cSSam Leffler 
137239beb93cSSam Leffler 	if (force_sess)
137339beb93cSSam Leffler 		sess = force_sess;
137439beb93cSSam Leffler 	else {
137539beb93cSSam Leffler 		res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf,
137639beb93cSSam Leffler 					  sizeof(statebuf));
137739beb93cSSam Leffler 		state_included = res >= 0;
137839beb93cSSam Leffler 		if (res == sizeof(statebuf)) {
137939beb93cSSam Leffler 			state = WPA_GET_BE32(statebuf);
138039beb93cSSam Leffler 			sess = radius_server_get_session(client, state);
138139beb93cSSam Leffler 		} else {
138239beb93cSSam Leffler 			sess = NULL;
138339beb93cSSam Leffler 		}
138439beb93cSSam Leffler 	}
138539beb93cSSam Leffler 
138639beb93cSSam Leffler 	if (sess) {
138739beb93cSSam Leffler 		RADIUS_DEBUG("Request for session 0x%x", sess->sess_id);
138839beb93cSSam Leffler 	} else if (state_included) {
138939beb93cSSam Leffler 		RADIUS_DEBUG("State attribute included but no session found");
139039beb93cSSam Leffler 		radius_server_reject(data, client, msg, from, fromlen,
139139beb93cSSam Leffler 				     from_addr, from_port);
139239beb93cSSam Leffler 		return -1;
139339beb93cSSam Leffler 	} else {
13945b9c547cSRui Paulo 		sess = radius_server_get_new_session(data, client, msg,
13955b9c547cSRui Paulo 						     from_addr);
139639beb93cSSam Leffler 		if (sess == NULL) {
139739beb93cSSam Leffler 			RADIUS_DEBUG("Could not create a new session");
139839beb93cSSam Leffler 			radius_server_reject(data, client, msg, from, fromlen,
139939beb93cSSam Leffler 					     from_addr, from_port);
140039beb93cSSam Leffler 			return -1;
140139beb93cSSam Leffler 		}
140239beb93cSSam Leffler 	}
140339beb93cSSam Leffler 
140439beb93cSSam Leffler 	if (sess->last_from_port == from_port &&
1405e28a4053SRui Paulo 	    sess->last_identifier == radius_msg_get_hdr(msg)->identifier &&
1406e28a4053SRui Paulo 	    os_memcmp(sess->last_authenticator,
1407e28a4053SRui Paulo 		      radius_msg_get_hdr(msg)->authenticator, 16) == 0) {
140839beb93cSSam Leffler 		RADIUS_DEBUG("Duplicate message from %s", from_addr);
140939beb93cSSam Leffler 		data->counters.dup_access_requests++;
141039beb93cSSam Leffler 		client->counters.dup_access_requests++;
141139beb93cSSam Leffler 
141239beb93cSSam Leffler 		if (sess->last_reply) {
1413e28a4053SRui Paulo 			struct wpabuf *buf;
1414e28a4053SRui Paulo 			buf = radius_msg_get_buf(sess->last_reply);
1415e28a4053SRui Paulo 			res = sendto(data->auth_sock, wpabuf_head(buf),
1416e28a4053SRui Paulo 				     wpabuf_len(buf), 0,
141739beb93cSSam Leffler 				     (struct sockaddr *) from, fromlen);
141839beb93cSSam Leffler 			if (res < 0) {
14195b9c547cSRui Paulo 				wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
14205b9c547cSRui Paulo 					   strerror(errno));
142139beb93cSSam Leffler 			}
142239beb93cSSam Leffler 			return 0;
142339beb93cSSam Leffler 		}
142439beb93cSSam Leffler 
142539beb93cSSam Leffler 		RADIUS_DEBUG("No previous reply available for duplicate "
142639beb93cSSam Leffler 			     "message");
142739beb93cSSam Leffler 		return -1;
142839beb93cSSam Leffler 	}
142939beb93cSSam Leffler 
1430f05cddf9SRui Paulo 	eap = radius_msg_get_eap(msg);
14315b9c547cSRui Paulo 	if (eap == NULL && sess->macacl) {
14325b9c547cSRui Paulo 		reply = radius_server_macacl(data, client, sess, msg);
14335b9c547cSRui Paulo 		if (reply == NULL)
14345b9c547cSRui Paulo 			return -1;
14355b9c547cSRui Paulo 		goto send_reply;
14365b9c547cSRui Paulo 	}
143739beb93cSSam Leffler 	if (eap == NULL) {
143839beb93cSSam Leffler 		RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
143939beb93cSSam Leffler 			     from_addr);
144039beb93cSSam Leffler 		data->counters.packets_dropped++;
144139beb93cSSam Leffler 		client->counters.packets_dropped++;
144239beb93cSSam Leffler 		return -1;
144339beb93cSSam Leffler 	}
144439beb93cSSam Leffler 
1445f05cddf9SRui Paulo 	RADIUS_DUMP("Received EAP data", wpabuf_head(eap), wpabuf_len(eap));
144639beb93cSSam Leffler 
144739beb93cSSam Leffler 	/* FIX: if Code is Request, Success, or Failure, send Access-Reject;
144839beb93cSSam Leffler 	 * RFC3579 Sect. 2.6.2.
144939beb93cSSam Leffler 	 * Include EAP-Response/Nak with no preferred method if
145039beb93cSSam Leffler 	 * code == request.
145139beb93cSSam Leffler 	 * If code is not 1-4, discard the packet silently.
145239beb93cSSam Leffler 	 * Or is this already done by the EAP state machine? */
145339beb93cSSam Leffler 
145439beb93cSSam Leffler 	wpabuf_free(sess->eap_if->eapRespData);
1455f05cddf9SRui Paulo 	sess->eap_if->eapRespData = eap;
1456c1d255d3SCy Schubert 	sess->eap_if->eapResp = true;
145739beb93cSSam Leffler 	eap_server_sm_step(sess->eap);
145839beb93cSSam Leffler 
145939beb93cSSam Leffler 	if ((sess->eap_if->eapReq || sess->eap_if->eapSuccess ||
146039beb93cSSam Leffler 	     sess->eap_if->eapFail) && sess->eap_if->eapReqData) {
146139beb93cSSam Leffler 		RADIUS_DUMP("EAP data from the state machine",
146239beb93cSSam Leffler 			    wpabuf_head(sess->eap_if->eapReqData),
146339beb93cSSam Leffler 			    wpabuf_len(sess->eap_if->eapReqData));
146439beb93cSSam Leffler 	} else if (sess->eap_if->eapFail) {
146539beb93cSSam Leffler 		RADIUS_DEBUG("No EAP data from the state machine, but eapFail "
146639beb93cSSam Leffler 			     "set");
146739beb93cSSam Leffler 	} else if (eap_sm_method_pending(sess->eap)) {
146839beb93cSSam Leffler 		radius_msg_free(sess->last_msg);
146939beb93cSSam Leffler 		sess->last_msg = msg;
147039beb93cSSam Leffler 		sess->last_from_port = from_port;
147139beb93cSSam Leffler 		os_free(sess->last_from_addr);
147239beb93cSSam Leffler 		sess->last_from_addr = os_strdup(from_addr);
147339beb93cSSam Leffler 		sess->last_fromlen = fromlen;
147439beb93cSSam Leffler 		os_memcpy(&sess->last_from, from, fromlen);
147539beb93cSSam Leffler 		return -2;
147639beb93cSSam Leffler 	} else {
147739beb93cSSam Leffler 		RADIUS_DEBUG("No EAP data from the state machine - ignore this"
147839beb93cSSam Leffler 			     " Access-Request silently (assuming it was a "
147939beb93cSSam Leffler 			     "duplicate)");
148039beb93cSSam Leffler 		data->counters.packets_dropped++;
148139beb93cSSam Leffler 		client->counters.packets_dropped++;
148239beb93cSSam Leffler 		return -1;
148339beb93cSSam Leffler 	}
148439beb93cSSam Leffler 
14853157ba21SRui Paulo 	if (sess->eap_if->eapSuccess || sess->eap_if->eapFail)
14863157ba21SRui Paulo 		is_complete = 1;
148785732ac8SCy Schubert 	if (sess->eap_if->eapFail) {
14885b9c547cSRui Paulo 		srv_log(sess, "EAP authentication failed");
148985732ac8SCy Schubert 		db_update_last_msk(sess, "FAIL");
149085732ac8SCy Schubert 	} else if (sess->eap_if->eapSuccess) {
14915b9c547cSRui Paulo 		srv_log(sess, "EAP authentication succeeded");
149285732ac8SCy Schubert 	}
149385732ac8SCy Schubert 
149485732ac8SCy Schubert 	if (sess->eap_if->eapSuccess)
149585732ac8SCy Schubert 		radius_server_hs20_t_c_check(sess, msg);
14963157ba21SRui Paulo 
149739beb93cSSam Leffler 	reply = radius_server_encapsulate_eap(data, client, sess, msg);
149839beb93cSSam Leffler 
14995b9c547cSRui Paulo send_reply:
150039beb93cSSam Leffler 	if (reply) {
1501e28a4053SRui Paulo 		struct wpabuf *buf;
1502e28a4053SRui Paulo 		struct radius_hdr *hdr;
1503e28a4053SRui Paulo 
150439beb93cSSam Leffler 		RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port);
150539beb93cSSam Leffler 		if (wpa_debug_level <= MSG_MSGDUMP) {
150639beb93cSSam Leffler 			radius_msg_dump(reply);
150739beb93cSSam Leffler 		}
150839beb93cSSam Leffler 
1509e28a4053SRui Paulo 		switch (radius_msg_get_hdr(reply)->code) {
151039beb93cSSam Leffler 		case RADIUS_CODE_ACCESS_ACCEPT:
15115b9c547cSRui Paulo 			srv_log(sess, "Sending Access-Accept");
151239beb93cSSam Leffler 			data->counters.access_accepts++;
151339beb93cSSam Leffler 			client->counters.access_accepts++;
151439beb93cSSam Leffler 			break;
151539beb93cSSam Leffler 		case RADIUS_CODE_ACCESS_REJECT:
15165b9c547cSRui Paulo 			srv_log(sess, "Sending Access-Reject");
151739beb93cSSam Leffler 			data->counters.access_rejects++;
151839beb93cSSam Leffler 			client->counters.access_rejects++;
151939beb93cSSam Leffler 			break;
152039beb93cSSam Leffler 		case RADIUS_CODE_ACCESS_CHALLENGE:
152139beb93cSSam Leffler 			data->counters.access_challenges++;
152239beb93cSSam Leffler 			client->counters.access_challenges++;
152339beb93cSSam Leffler 			break;
152439beb93cSSam Leffler 		}
1525e28a4053SRui Paulo 		buf = radius_msg_get_buf(reply);
1526e28a4053SRui Paulo 		res = sendto(data->auth_sock, wpabuf_head(buf),
1527e28a4053SRui Paulo 			     wpabuf_len(buf), 0,
152839beb93cSSam Leffler 			     (struct sockaddr *) from, fromlen);
152939beb93cSSam Leffler 		if (res < 0) {
15305b9c547cSRui Paulo 			wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
15315b9c547cSRui Paulo 				   strerror(errno));
153239beb93cSSam Leffler 		}
153339beb93cSSam Leffler 		radius_msg_free(sess->last_reply);
153439beb93cSSam Leffler 		sess->last_reply = reply;
153539beb93cSSam Leffler 		sess->last_from_port = from_port;
1536e28a4053SRui Paulo 		hdr = radius_msg_get_hdr(msg);
1537e28a4053SRui Paulo 		sess->last_identifier = hdr->identifier;
1538e28a4053SRui Paulo 		os_memcpy(sess->last_authenticator, hdr->authenticator, 16);
153939beb93cSSam Leffler 	} else {
154039beb93cSSam Leffler 		data->counters.packets_dropped++;
154139beb93cSSam Leffler 		client->counters.packets_dropped++;
154239beb93cSSam Leffler 	}
154339beb93cSSam Leffler 
15443157ba21SRui Paulo 	if (is_complete) {
154539beb93cSSam Leffler 		RADIUS_DEBUG("Removing completed session 0x%x after timeout",
154639beb93cSSam Leffler 			     sess->sess_id);
154739beb93cSSam Leffler 		eloop_cancel_timeout(radius_server_session_remove_timeout,
154839beb93cSSam Leffler 				     data, sess);
154985732ac8SCy Schubert 		eloop_register_timeout(RADIUS_SESSION_MAINTAIN, 0,
155039beb93cSSam Leffler 				       radius_server_session_remove_timeout,
155139beb93cSSam Leffler 				       data, sess);
155239beb93cSSam Leffler 	}
155339beb93cSSam Leffler 
155439beb93cSSam Leffler 	return 0;
155539beb93cSSam Leffler }
155639beb93cSSam Leffler 
155739beb93cSSam Leffler 
155885732ac8SCy Schubert static void
radius_server_receive_disconnect_resp(struct radius_server_data * data,struct radius_client * client,struct radius_msg * msg,int ack)155985732ac8SCy Schubert radius_server_receive_disconnect_resp(struct radius_server_data *data,
156085732ac8SCy Schubert 				      struct radius_client *client,
156185732ac8SCy Schubert 				      struct radius_msg *msg, int ack)
156285732ac8SCy Schubert {
156385732ac8SCy Schubert 	struct radius_hdr *hdr;
156485732ac8SCy Schubert 
156585732ac8SCy Schubert 	if (!client->pending_dac_disconnect_req) {
156685732ac8SCy Schubert 		RADIUS_DEBUG("Ignore unexpected Disconnect response");
156785732ac8SCy Schubert 		radius_msg_free(msg);
156885732ac8SCy Schubert 		return;
156985732ac8SCy Schubert 	}
157085732ac8SCy Schubert 
157185732ac8SCy Schubert 	hdr = radius_msg_get_hdr(msg);
157285732ac8SCy Schubert 	if (hdr->identifier != client->pending_dac_disconnect_id) {
157385732ac8SCy Schubert 		RADIUS_DEBUG("Ignore unexpected Disconnect response with unexpected identifier %u (expected %u)",
157485732ac8SCy Schubert 			     hdr->identifier,
157585732ac8SCy Schubert 			     client->pending_dac_disconnect_id);
157685732ac8SCy Schubert 		radius_msg_free(msg);
157785732ac8SCy Schubert 		return;
157885732ac8SCy Schubert 	}
157985732ac8SCy Schubert 
158085732ac8SCy Schubert 	if (radius_msg_verify(msg, (const u8 *) client->shared_secret,
158185732ac8SCy Schubert 			      client->shared_secret_len,
158285732ac8SCy Schubert 			      client->pending_dac_disconnect_req, 0)) {
158385732ac8SCy Schubert 		RADIUS_DEBUG("Ignore Disconnect response with invalid authenticator");
158485732ac8SCy Schubert 		radius_msg_free(msg);
158585732ac8SCy Schubert 		return;
158685732ac8SCy Schubert 	}
158785732ac8SCy Schubert 
158885732ac8SCy Schubert 	RADIUS_DEBUG("Disconnect-%s received for " MACSTR,
158985732ac8SCy Schubert 		     ack ? "ACK" : "NAK",
159085732ac8SCy Schubert 		     MAC2STR(client->pending_dac_disconnect_addr));
159185732ac8SCy Schubert 
159285732ac8SCy Schubert 	radius_msg_free(msg);
159385732ac8SCy Schubert 	radius_msg_free(client->pending_dac_disconnect_req);
159485732ac8SCy Schubert 	client->pending_dac_disconnect_req = NULL;
159585732ac8SCy Schubert }
159685732ac8SCy Schubert 
159785732ac8SCy Schubert 
radius_server_receive_coa_resp(struct radius_server_data * data,struct radius_client * client,struct radius_msg * msg,int ack)159885732ac8SCy Schubert static void radius_server_receive_coa_resp(struct radius_server_data *data,
159985732ac8SCy Schubert 					   struct radius_client *client,
160085732ac8SCy Schubert 					   struct radius_msg *msg, int ack)
160185732ac8SCy Schubert {
160285732ac8SCy Schubert 	struct radius_hdr *hdr;
160385732ac8SCy Schubert #ifdef CONFIG_SQLITE
160485732ac8SCy Schubert 	char addrtxt[3 * ETH_ALEN];
160585732ac8SCy Schubert 	char *sql;
160685732ac8SCy Schubert 	int res;
160785732ac8SCy Schubert #endif /* CONFIG_SQLITE */
160885732ac8SCy Schubert 
160985732ac8SCy Schubert 	if (!client->pending_dac_coa_req) {
161085732ac8SCy Schubert 		RADIUS_DEBUG("Ignore unexpected CoA response");
161185732ac8SCy Schubert 		radius_msg_free(msg);
161285732ac8SCy Schubert 		return;
161385732ac8SCy Schubert 	}
161485732ac8SCy Schubert 
161585732ac8SCy Schubert 	hdr = radius_msg_get_hdr(msg);
161685732ac8SCy Schubert 	if (hdr->identifier != client->pending_dac_coa_id) {
161785732ac8SCy Schubert 		RADIUS_DEBUG("Ignore unexpected CoA response with unexpected identifier %u (expected %u)",
161885732ac8SCy Schubert 			     hdr->identifier,
161985732ac8SCy Schubert 			     client->pending_dac_coa_id);
162085732ac8SCy Schubert 		radius_msg_free(msg);
162185732ac8SCy Schubert 		return;
162285732ac8SCy Schubert 	}
162385732ac8SCy Schubert 
162485732ac8SCy Schubert 	if (radius_msg_verify(msg, (const u8 *) client->shared_secret,
162585732ac8SCy Schubert 			      client->shared_secret_len,
162685732ac8SCy Schubert 			      client->pending_dac_coa_req, 0)) {
162785732ac8SCy Schubert 		RADIUS_DEBUG("Ignore CoA response with invalid authenticator");
162885732ac8SCy Schubert 		radius_msg_free(msg);
162985732ac8SCy Schubert 		return;
163085732ac8SCy Schubert 	}
163185732ac8SCy Schubert 
163285732ac8SCy Schubert 	RADIUS_DEBUG("CoA-%s received for " MACSTR,
163385732ac8SCy Schubert 		     ack ? "ACK" : "NAK",
163485732ac8SCy Schubert 		     MAC2STR(client->pending_dac_coa_addr));
163585732ac8SCy Schubert 
163685732ac8SCy Schubert 	radius_msg_free(msg);
163785732ac8SCy Schubert 	radius_msg_free(client->pending_dac_coa_req);
163885732ac8SCy Schubert 	client->pending_dac_coa_req = NULL;
163985732ac8SCy Schubert 
164085732ac8SCy Schubert #ifdef CONFIG_SQLITE
164185732ac8SCy Schubert 	if (!data->db)
164285732ac8SCy Schubert 		return;
164385732ac8SCy Schubert 
164485732ac8SCy Schubert 	os_snprintf(addrtxt, sizeof(addrtxt), MACSTR,
164585732ac8SCy Schubert 		    MAC2STR(client->pending_dac_coa_addr));
164685732ac8SCy Schubert 
164785732ac8SCy Schubert 	if (ack) {
164885732ac8SCy 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",
164985732ac8SCy Schubert 				      addrtxt);
165085732ac8SCy Schubert 	} else {
165185732ac8SCy Schubert 		sql = sqlite3_mprintf("UPDATE current_sessions SET waiting_coa_ack=0 WHERE mac_addr=%Q",
165285732ac8SCy Schubert 				      addrtxt);
165385732ac8SCy Schubert 	}
165485732ac8SCy Schubert 	if (!sql)
165585732ac8SCy Schubert 		return;
165685732ac8SCy Schubert 
165785732ac8SCy Schubert 	res = sqlite3_exec(data->db, sql, NULL, NULL, NULL);
165885732ac8SCy Schubert 	sqlite3_free(sql);
165985732ac8SCy Schubert 	if (res != SQLITE_OK) {
166085732ac8SCy Schubert 		RADIUS_ERROR("Failed to update current_sessions entry: %s",
166185732ac8SCy Schubert 			     sqlite3_errmsg(data->db));
166285732ac8SCy Schubert 		return;
166385732ac8SCy Schubert 	}
166485732ac8SCy Schubert #endif /* CONFIG_SQLITE */
166585732ac8SCy Schubert }
166685732ac8SCy Schubert 
166785732ac8SCy Schubert 
radius_server_receive_auth(int sock,void * eloop_ctx,void * sock_ctx)166839beb93cSSam Leffler static void radius_server_receive_auth(int sock, void *eloop_ctx,
166939beb93cSSam Leffler 				       void *sock_ctx)
167039beb93cSSam Leffler {
167139beb93cSSam Leffler 	struct radius_server_data *data = eloop_ctx;
167239beb93cSSam Leffler 	u8 *buf = NULL;
16733157ba21SRui Paulo 	union {
16743157ba21SRui Paulo 		struct sockaddr_storage ss;
16753157ba21SRui Paulo 		struct sockaddr_in sin;
16763157ba21SRui Paulo #ifdef CONFIG_IPV6
16773157ba21SRui Paulo 		struct sockaddr_in6 sin6;
16783157ba21SRui Paulo #endif /* CONFIG_IPV6 */
16793157ba21SRui Paulo 	} from;
168039beb93cSSam Leffler 	socklen_t fromlen;
168139beb93cSSam Leffler 	int len;
168239beb93cSSam Leffler 	struct radius_client *client = NULL;
168339beb93cSSam Leffler 	struct radius_msg *msg = NULL;
168439beb93cSSam Leffler 	char abuf[50];
168539beb93cSSam Leffler 	int from_port = 0;
168639beb93cSSam Leffler 
168739beb93cSSam Leffler 	buf = os_malloc(RADIUS_MAX_MSG_LEN);
168839beb93cSSam Leffler 	if (buf == NULL) {
168939beb93cSSam Leffler 		goto fail;
169039beb93cSSam Leffler 	}
169139beb93cSSam Leffler 
169239beb93cSSam Leffler 	fromlen = sizeof(from);
169339beb93cSSam Leffler 	len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
16943157ba21SRui Paulo 		       (struct sockaddr *) &from.ss, &fromlen);
169539beb93cSSam Leffler 	if (len < 0) {
16965b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s",
16975b9c547cSRui Paulo 			   strerror(errno));
169839beb93cSSam Leffler 		goto fail;
169939beb93cSSam Leffler 	}
170039beb93cSSam Leffler 
170139beb93cSSam Leffler #ifdef CONFIG_IPV6
170239beb93cSSam Leffler 	if (data->ipv6) {
17033157ba21SRui Paulo 		if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf,
17043157ba21SRui Paulo 			      sizeof(abuf)) == NULL)
170539beb93cSSam Leffler 			abuf[0] = '\0';
17063157ba21SRui Paulo 		from_port = ntohs(from.sin6.sin6_port);
170739beb93cSSam Leffler 		RADIUS_DEBUG("Received %d bytes from %s:%d",
170839beb93cSSam Leffler 			     len, abuf, from_port);
170939beb93cSSam Leffler 
171039beb93cSSam Leffler 		client = radius_server_get_client(data,
171139beb93cSSam Leffler 						  (struct in_addr *)
17123157ba21SRui Paulo 						  &from.sin6.sin6_addr, 1);
171339beb93cSSam Leffler 	}
171439beb93cSSam Leffler #endif /* CONFIG_IPV6 */
171539beb93cSSam Leffler 
171639beb93cSSam Leffler 	if (!data->ipv6) {
17173157ba21SRui Paulo 		os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
17183157ba21SRui Paulo 		from_port = ntohs(from.sin.sin_port);
171939beb93cSSam Leffler 		RADIUS_DEBUG("Received %d bytes from %s:%d",
172039beb93cSSam Leffler 			     len, abuf, from_port);
172139beb93cSSam Leffler 
17223157ba21SRui Paulo 		client = radius_server_get_client(data, &from.sin.sin_addr, 0);
172339beb93cSSam Leffler 	}
172439beb93cSSam Leffler 
172539beb93cSSam Leffler 	RADIUS_DUMP("Received data", buf, len);
172639beb93cSSam Leffler 
172739beb93cSSam Leffler 	if (client == NULL) {
172839beb93cSSam Leffler 		RADIUS_DEBUG("Unknown client %s - packet ignored", abuf);
172939beb93cSSam Leffler 		data->counters.invalid_requests++;
173039beb93cSSam Leffler 		goto fail;
173139beb93cSSam Leffler 	}
173239beb93cSSam Leffler 
173339beb93cSSam Leffler 	msg = radius_msg_parse(buf, len);
173439beb93cSSam Leffler 	if (msg == NULL) {
173539beb93cSSam Leffler 		RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
173639beb93cSSam Leffler 		data->counters.malformed_access_requests++;
173739beb93cSSam Leffler 		client->counters.malformed_access_requests++;
173839beb93cSSam Leffler 		goto fail;
173939beb93cSSam Leffler 	}
174039beb93cSSam Leffler 
174139beb93cSSam Leffler 	os_free(buf);
174239beb93cSSam Leffler 	buf = NULL;
174339beb93cSSam Leffler 
174439beb93cSSam Leffler 	if (wpa_debug_level <= MSG_MSGDUMP) {
174539beb93cSSam Leffler 		radius_msg_dump(msg);
174639beb93cSSam Leffler 	}
174739beb93cSSam Leffler 
174885732ac8SCy Schubert 	if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_ACK) {
174985732ac8SCy Schubert 		radius_server_receive_disconnect_resp(data, client, msg, 1);
175085732ac8SCy Schubert 		return;
175185732ac8SCy Schubert 	}
175285732ac8SCy Schubert 
175385732ac8SCy Schubert 	if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_NAK) {
175485732ac8SCy Schubert 		radius_server_receive_disconnect_resp(data, client, msg, 0);
175585732ac8SCy Schubert 		return;
175685732ac8SCy Schubert 	}
175785732ac8SCy Schubert 
175885732ac8SCy Schubert 	if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_ACK) {
175985732ac8SCy Schubert 		radius_server_receive_coa_resp(data, client, msg, 1);
176085732ac8SCy Schubert 		return;
176185732ac8SCy Schubert 	}
176285732ac8SCy Schubert 
176385732ac8SCy Schubert 	if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_NAK) {
176485732ac8SCy Schubert 		radius_server_receive_coa_resp(data, client, msg, 0);
176585732ac8SCy Schubert 		return;
176685732ac8SCy Schubert 	}
176785732ac8SCy Schubert 
1768e28a4053SRui Paulo 	if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCESS_REQUEST) {
1769e28a4053SRui Paulo 		RADIUS_DEBUG("Unexpected RADIUS code %d",
1770e28a4053SRui Paulo 			     radius_msg_get_hdr(msg)->code);
177139beb93cSSam Leffler 		data->counters.unknown_types++;
177239beb93cSSam Leffler 		client->counters.unknown_types++;
177339beb93cSSam Leffler 		goto fail;
177439beb93cSSam Leffler 	}
177539beb93cSSam Leffler 
177639beb93cSSam Leffler 	data->counters.access_requests++;
177739beb93cSSam Leffler 	client->counters.access_requests++;
177839beb93cSSam Leffler 
177939beb93cSSam Leffler 	if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret,
178039beb93cSSam Leffler 				       client->shared_secret_len, NULL)) {
178139beb93cSSam Leffler 		RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf);
178239beb93cSSam Leffler 		data->counters.bad_authenticators++;
178339beb93cSSam Leffler 		client->counters.bad_authenticators++;
178439beb93cSSam Leffler 		goto fail;
178539beb93cSSam Leffler 	}
178639beb93cSSam Leffler 
178739beb93cSSam Leffler 	if (radius_server_request(data, msg, (struct sockaddr *) &from,
178839beb93cSSam Leffler 				  fromlen, client, abuf, from_port, NULL) ==
178939beb93cSSam Leffler 	    -2)
179039beb93cSSam Leffler 		return; /* msg was stored with the session */
179139beb93cSSam Leffler 
179239beb93cSSam Leffler fail:
179339beb93cSSam Leffler 	radius_msg_free(msg);
179439beb93cSSam Leffler 	os_free(buf);
179539beb93cSSam Leffler }
179639beb93cSSam Leffler 
179739beb93cSSam Leffler 
radius_server_receive_acct(int sock,void * eloop_ctx,void * sock_ctx)17985b9c547cSRui Paulo static void radius_server_receive_acct(int sock, void *eloop_ctx,
17995b9c547cSRui Paulo 				       void *sock_ctx)
18005b9c547cSRui Paulo {
18015b9c547cSRui Paulo 	struct radius_server_data *data = eloop_ctx;
18025b9c547cSRui Paulo 	u8 *buf = NULL;
18035b9c547cSRui Paulo 	union {
18045b9c547cSRui Paulo 		struct sockaddr_storage ss;
18055b9c547cSRui Paulo 		struct sockaddr_in sin;
18065b9c547cSRui Paulo #ifdef CONFIG_IPV6
18075b9c547cSRui Paulo 		struct sockaddr_in6 sin6;
18085b9c547cSRui Paulo #endif /* CONFIG_IPV6 */
18095b9c547cSRui Paulo 	} from;
18105b9c547cSRui Paulo 	socklen_t fromlen;
18115b9c547cSRui Paulo 	int len, res;
18125b9c547cSRui Paulo 	struct radius_client *client = NULL;
18135b9c547cSRui Paulo 	struct radius_msg *msg = NULL, *resp = NULL;
18145b9c547cSRui Paulo 	char abuf[50];
18155b9c547cSRui Paulo 	int from_port = 0;
18165b9c547cSRui Paulo 	struct radius_hdr *hdr;
18175b9c547cSRui Paulo 	struct wpabuf *rbuf;
18185b9c547cSRui Paulo 
18195b9c547cSRui Paulo 	buf = os_malloc(RADIUS_MAX_MSG_LEN);
18205b9c547cSRui Paulo 	if (buf == NULL) {
18215b9c547cSRui Paulo 		goto fail;
18225b9c547cSRui Paulo 	}
18235b9c547cSRui Paulo 
18245b9c547cSRui Paulo 	fromlen = sizeof(from);
18255b9c547cSRui Paulo 	len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
18265b9c547cSRui Paulo 		       (struct sockaddr *) &from.ss, &fromlen);
18275b9c547cSRui Paulo 	if (len < 0) {
18285b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s",
18295b9c547cSRui Paulo 			   strerror(errno));
18305b9c547cSRui Paulo 		goto fail;
18315b9c547cSRui Paulo 	}
18325b9c547cSRui Paulo 
18335b9c547cSRui Paulo #ifdef CONFIG_IPV6
18345b9c547cSRui Paulo 	if (data->ipv6) {
18355b9c547cSRui Paulo 		if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf,
18365b9c547cSRui Paulo 			      sizeof(abuf)) == NULL)
18375b9c547cSRui Paulo 			abuf[0] = '\0';
18385b9c547cSRui Paulo 		from_port = ntohs(from.sin6.sin6_port);
18395b9c547cSRui Paulo 		RADIUS_DEBUG("Received %d bytes from %s:%d",
18405b9c547cSRui Paulo 			     len, abuf, from_port);
18415b9c547cSRui Paulo 
18425b9c547cSRui Paulo 		client = radius_server_get_client(data,
18435b9c547cSRui Paulo 						  (struct in_addr *)
18445b9c547cSRui Paulo 						  &from.sin6.sin6_addr, 1);
18455b9c547cSRui Paulo 	}
18465b9c547cSRui Paulo #endif /* CONFIG_IPV6 */
18475b9c547cSRui Paulo 
18485b9c547cSRui Paulo 	if (!data->ipv6) {
18495b9c547cSRui Paulo 		os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
18505b9c547cSRui Paulo 		from_port = ntohs(from.sin.sin_port);
18515b9c547cSRui Paulo 		RADIUS_DEBUG("Received %d bytes from %s:%d",
18525b9c547cSRui Paulo 			     len, abuf, from_port);
18535b9c547cSRui Paulo 
18545b9c547cSRui Paulo 		client = radius_server_get_client(data, &from.sin.sin_addr, 0);
18555b9c547cSRui Paulo 	}
18565b9c547cSRui Paulo 
18575b9c547cSRui Paulo 	RADIUS_DUMP("Received data", buf, len);
18585b9c547cSRui Paulo 
18595b9c547cSRui Paulo 	if (client == NULL) {
18605b9c547cSRui Paulo 		RADIUS_DEBUG("Unknown client %s - packet ignored", abuf);
18615b9c547cSRui Paulo 		data->counters.invalid_acct_requests++;
18625b9c547cSRui Paulo 		goto fail;
18635b9c547cSRui Paulo 	}
18645b9c547cSRui Paulo 
18655b9c547cSRui Paulo 	msg = radius_msg_parse(buf, len);
18665b9c547cSRui Paulo 	if (msg == NULL) {
18675b9c547cSRui Paulo 		RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
18685b9c547cSRui Paulo 		data->counters.malformed_acct_requests++;
18695b9c547cSRui Paulo 		client->counters.malformed_acct_requests++;
18705b9c547cSRui Paulo 		goto fail;
18715b9c547cSRui Paulo 	}
18725b9c547cSRui Paulo 
18735b9c547cSRui Paulo 	os_free(buf);
18745b9c547cSRui Paulo 	buf = NULL;
18755b9c547cSRui Paulo 
18765b9c547cSRui Paulo 	if (wpa_debug_level <= MSG_MSGDUMP) {
18775b9c547cSRui Paulo 		radius_msg_dump(msg);
18785b9c547cSRui Paulo 	}
18795b9c547cSRui Paulo 
18805b9c547cSRui Paulo 	if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_REQUEST) {
18815b9c547cSRui Paulo 		RADIUS_DEBUG("Unexpected RADIUS code %d",
18825b9c547cSRui Paulo 			     radius_msg_get_hdr(msg)->code);
18835b9c547cSRui Paulo 		data->counters.unknown_acct_types++;
18845b9c547cSRui Paulo 		client->counters.unknown_acct_types++;
18855b9c547cSRui Paulo 		goto fail;
18865b9c547cSRui Paulo 	}
18875b9c547cSRui Paulo 
18885b9c547cSRui Paulo 	data->counters.acct_requests++;
18895b9c547cSRui Paulo 	client->counters.acct_requests++;
18905b9c547cSRui Paulo 
18915b9c547cSRui Paulo 	if (radius_msg_verify_acct_req(msg, (u8 *) client->shared_secret,
18925b9c547cSRui Paulo 				       client->shared_secret_len)) {
18935b9c547cSRui Paulo 		RADIUS_DEBUG("Invalid Authenticator from %s", abuf);
18945b9c547cSRui Paulo 		data->counters.acct_bad_authenticators++;
18955b9c547cSRui Paulo 		client->counters.acct_bad_authenticators++;
18965b9c547cSRui Paulo 		goto fail;
18975b9c547cSRui Paulo 	}
18985b9c547cSRui Paulo 
18995b9c547cSRui Paulo 	/* TODO: Write accounting information to a file or database */
19005b9c547cSRui Paulo 
19015b9c547cSRui Paulo 	hdr = radius_msg_get_hdr(msg);
19025b9c547cSRui Paulo 
19035b9c547cSRui Paulo 	resp = radius_msg_new(RADIUS_CODE_ACCOUNTING_RESPONSE, hdr->identifier);
19045b9c547cSRui Paulo 	if (resp == NULL)
19055b9c547cSRui Paulo 		goto fail;
19065b9c547cSRui Paulo 
19075b9c547cSRui Paulo 	radius_msg_finish_acct_resp(resp, (u8 *) client->shared_secret,
19085b9c547cSRui Paulo 				    client->shared_secret_len,
19095b9c547cSRui Paulo 				    hdr->authenticator);
19105b9c547cSRui Paulo 
19115b9c547cSRui Paulo 	RADIUS_DEBUG("Reply to %s:%d", abuf, from_port);
19125b9c547cSRui Paulo 	if (wpa_debug_level <= MSG_MSGDUMP) {
19135b9c547cSRui Paulo 		radius_msg_dump(resp);
19145b9c547cSRui Paulo 	}
19155b9c547cSRui Paulo 	rbuf = radius_msg_get_buf(resp);
19165b9c547cSRui Paulo 	data->counters.acct_responses++;
19175b9c547cSRui Paulo 	client->counters.acct_responses++;
19185b9c547cSRui Paulo 	res = sendto(data->acct_sock, wpabuf_head(rbuf), wpabuf_len(rbuf), 0,
19195b9c547cSRui Paulo 		     (struct sockaddr *) &from.ss, fromlen);
19205b9c547cSRui Paulo 	if (res < 0) {
19215b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
19225b9c547cSRui Paulo 			   strerror(errno));
19235b9c547cSRui Paulo 	}
19245b9c547cSRui Paulo 
19255b9c547cSRui Paulo fail:
19265b9c547cSRui Paulo 	radius_msg_free(resp);
19275b9c547cSRui Paulo 	radius_msg_free(msg);
19285b9c547cSRui Paulo 	os_free(buf);
19295b9c547cSRui Paulo }
19305b9c547cSRui Paulo 
19315b9c547cSRui Paulo 
radius_server_disable_pmtu_discovery(int s)19323157ba21SRui Paulo static int radius_server_disable_pmtu_discovery(int s)
19333157ba21SRui Paulo {
19343157ba21SRui Paulo 	int r = -1;
19353157ba21SRui Paulo #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
19363157ba21SRui Paulo 	/* Turn off Path MTU discovery on IPv4/UDP sockets. */
19373157ba21SRui Paulo 	int action = IP_PMTUDISC_DONT;
19383157ba21SRui Paulo 	r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
19393157ba21SRui Paulo 		       sizeof(action));
19403157ba21SRui Paulo 	if (r == -1)
19413157ba21SRui Paulo 		wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
19423157ba21SRui Paulo 			   "%s", strerror(errno));
19433157ba21SRui Paulo #endif
19443157ba21SRui Paulo 	return r;
19453157ba21SRui Paulo }
19463157ba21SRui Paulo 
19473157ba21SRui Paulo 
radius_server_open_socket(int port)194839beb93cSSam Leffler static int radius_server_open_socket(int port)
194939beb93cSSam Leffler {
195039beb93cSSam Leffler 	int s;
195139beb93cSSam Leffler 	struct sockaddr_in addr;
195239beb93cSSam Leffler 
195339beb93cSSam Leffler 	s = socket(PF_INET, SOCK_DGRAM, 0);
195439beb93cSSam Leffler 	if (s < 0) {
19555b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: socket: %s", strerror(errno));
195639beb93cSSam Leffler 		return -1;
195739beb93cSSam Leffler 	}
195839beb93cSSam Leffler 
19593157ba21SRui Paulo 	radius_server_disable_pmtu_discovery(s);
19603157ba21SRui Paulo 
196139beb93cSSam Leffler 	os_memset(&addr, 0, sizeof(addr));
196239beb93cSSam Leffler 	addr.sin_family = AF_INET;
196339beb93cSSam Leffler 	addr.sin_port = htons(port);
196439beb93cSSam Leffler 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
19655b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
196639beb93cSSam Leffler 		close(s);
196739beb93cSSam Leffler 		return -1;
196839beb93cSSam Leffler 	}
196939beb93cSSam Leffler 
197039beb93cSSam Leffler 	return s;
197139beb93cSSam Leffler }
197239beb93cSSam Leffler 
197339beb93cSSam Leffler 
197439beb93cSSam Leffler #ifdef CONFIG_IPV6
radius_server_open_socket6(int port)197539beb93cSSam Leffler static int radius_server_open_socket6(int port)
197639beb93cSSam Leffler {
197739beb93cSSam Leffler 	int s;
197839beb93cSSam Leffler 	struct sockaddr_in6 addr;
197939beb93cSSam Leffler 
198039beb93cSSam Leffler 	s = socket(PF_INET6, SOCK_DGRAM, 0);
198139beb93cSSam Leffler 	if (s < 0) {
19825b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: socket[IPv6]: %s",
19835b9c547cSRui Paulo 			   strerror(errno));
198439beb93cSSam Leffler 		return -1;
198539beb93cSSam Leffler 	}
198639beb93cSSam Leffler 
198739beb93cSSam Leffler 	os_memset(&addr, 0, sizeof(addr));
198839beb93cSSam Leffler 	addr.sin6_family = AF_INET6;
198939beb93cSSam Leffler 	os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
199039beb93cSSam Leffler 	addr.sin6_port = htons(port);
199139beb93cSSam Leffler 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
19925b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
199339beb93cSSam Leffler 		close(s);
199439beb93cSSam Leffler 		return -1;
199539beb93cSSam Leffler 	}
199639beb93cSSam Leffler 
199739beb93cSSam Leffler 	return s;
199839beb93cSSam Leffler }
199939beb93cSSam Leffler #endif /* CONFIG_IPV6 */
200039beb93cSSam Leffler 
200139beb93cSSam Leffler 
radius_server_free_sessions(struct radius_server_data * data,struct radius_session * sessions)200239beb93cSSam Leffler static void radius_server_free_sessions(struct radius_server_data *data,
200339beb93cSSam Leffler 					struct radius_session *sessions)
200439beb93cSSam Leffler {
200539beb93cSSam Leffler 	struct radius_session *session, *prev;
200639beb93cSSam Leffler 
200739beb93cSSam Leffler 	session = sessions;
200839beb93cSSam Leffler 	while (session) {
200939beb93cSSam Leffler 		prev = session;
201039beb93cSSam Leffler 		session = session->next;
201139beb93cSSam Leffler 		radius_server_session_free(data, prev);
201239beb93cSSam Leffler 	}
201339beb93cSSam Leffler }
201439beb93cSSam Leffler 
201539beb93cSSam Leffler 
radius_server_free_clients(struct radius_server_data * data,struct radius_client * clients)201639beb93cSSam Leffler static void radius_server_free_clients(struct radius_server_data *data,
201739beb93cSSam Leffler 				       struct radius_client *clients)
201839beb93cSSam Leffler {
201939beb93cSSam Leffler 	struct radius_client *client, *prev;
202039beb93cSSam Leffler 
202139beb93cSSam Leffler 	client = clients;
202239beb93cSSam Leffler 	while (client) {
202339beb93cSSam Leffler 		prev = client;
202439beb93cSSam Leffler 		client = client->next;
202539beb93cSSam Leffler 
202639beb93cSSam Leffler 		radius_server_free_sessions(data, prev->sessions);
202739beb93cSSam Leffler 		os_free(prev->shared_secret);
202885732ac8SCy Schubert 		radius_msg_free(prev->pending_dac_coa_req);
202985732ac8SCy Schubert 		radius_msg_free(prev->pending_dac_disconnect_req);
203039beb93cSSam Leffler 		os_free(prev);
203139beb93cSSam Leffler 	}
203239beb93cSSam Leffler }
203339beb93cSSam Leffler 
203439beb93cSSam Leffler 
203539beb93cSSam Leffler static struct radius_client *
radius_server_read_clients(const char * client_file,int ipv6)203639beb93cSSam Leffler radius_server_read_clients(const char *client_file, int ipv6)
203739beb93cSSam Leffler {
203839beb93cSSam Leffler 	FILE *f;
203939beb93cSSam Leffler 	const int buf_size = 1024;
204039beb93cSSam Leffler 	char *buf, *pos;
204139beb93cSSam Leffler 	struct radius_client *clients, *tail, *entry;
204239beb93cSSam Leffler 	int line = 0, mask, failed = 0, i;
204339beb93cSSam Leffler 	struct in_addr addr;
204439beb93cSSam Leffler #ifdef CONFIG_IPV6
204539beb93cSSam Leffler 	struct in6_addr addr6;
204639beb93cSSam Leffler #endif /* CONFIG_IPV6 */
204739beb93cSSam Leffler 	unsigned int val;
204839beb93cSSam Leffler 
204939beb93cSSam Leffler 	f = fopen(client_file, "r");
205039beb93cSSam Leffler 	if (f == NULL) {
205139beb93cSSam Leffler 		RADIUS_ERROR("Could not open client file '%s'", client_file);
205239beb93cSSam Leffler 		return NULL;
205339beb93cSSam Leffler 	}
205439beb93cSSam Leffler 
205539beb93cSSam Leffler 	buf = os_malloc(buf_size);
205639beb93cSSam Leffler 	if (buf == NULL) {
205739beb93cSSam Leffler 		fclose(f);
205839beb93cSSam Leffler 		return NULL;
205939beb93cSSam Leffler 	}
206039beb93cSSam Leffler 
206139beb93cSSam Leffler 	clients = tail = NULL;
206239beb93cSSam Leffler 	while (fgets(buf, buf_size, f)) {
206339beb93cSSam Leffler 		/* Configuration file format:
206439beb93cSSam Leffler 		 * 192.168.1.0/24 secret
206539beb93cSSam Leffler 		 * 192.168.1.2 secret
206639beb93cSSam Leffler 		 * fe80::211:22ff:fe33:4455/64 secretipv6
206739beb93cSSam Leffler 		 */
206839beb93cSSam Leffler 		line++;
206939beb93cSSam Leffler 		buf[buf_size - 1] = '\0';
207039beb93cSSam Leffler 		pos = buf;
207139beb93cSSam Leffler 		while (*pos != '\0' && *pos != '\n')
207239beb93cSSam Leffler 			pos++;
207339beb93cSSam Leffler 		if (*pos == '\n')
207439beb93cSSam Leffler 			*pos = '\0';
207539beb93cSSam Leffler 		if (*buf == '\0' || *buf == '#')
207639beb93cSSam Leffler 			continue;
207739beb93cSSam Leffler 
207839beb93cSSam Leffler 		pos = buf;
207939beb93cSSam Leffler 		while ((*pos >= '0' && *pos <= '9') || *pos == '.' ||
208039beb93cSSam Leffler 		       (*pos >= 'a' && *pos <= 'f') || *pos == ':' ||
208139beb93cSSam Leffler 		       (*pos >= 'A' && *pos <= 'F')) {
208239beb93cSSam Leffler 			pos++;
208339beb93cSSam Leffler 		}
208439beb93cSSam Leffler 
208539beb93cSSam Leffler 		if (*pos == '\0') {
208639beb93cSSam Leffler 			failed = 1;
208739beb93cSSam Leffler 			break;
208839beb93cSSam Leffler 		}
208939beb93cSSam Leffler 
209039beb93cSSam Leffler 		if (*pos == '/') {
209139beb93cSSam Leffler 			char *end;
209239beb93cSSam Leffler 			*pos++ = '\0';
209339beb93cSSam Leffler 			mask = strtol(pos, &end, 10);
209439beb93cSSam Leffler 			if ((pos == end) ||
209539beb93cSSam Leffler 			    (mask < 0 || mask > (ipv6 ? 128 : 32))) {
209639beb93cSSam Leffler 				failed = 1;
209739beb93cSSam Leffler 				break;
209839beb93cSSam Leffler 			}
209939beb93cSSam Leffler 			pos = end;
210039beb93cSSam Leffler 		} else {
210139beb93cSSam Leffler 			mask = ipv6 ? 128 : 32;
210239beb93cSSam Leffler 			*pos++ = '\0';
210339beb93cSSam Leffler 		}
210439beb93cSSam Leffler 
210539beb93cSSam Leffler 		if (!ipv6 && inet_aton(buf, &addr) == 0) {
210639beb93cSSam Leffler 			failed = 1;
210739beb93cSSam Leffler 			break;
210839beb93cSSam Leffler 		}
210939beb93cSSam Leffler #ifdef CONFIG_IPV6
211039beb93cSSam Leffler 		if (ipv6 && inet_pton(AF_INET6, buf, &addr6) <= 0) {
211139beb93cSSam Leffler 			if (inet_pton(AF_INET, buf, &addr) <= 0) {
211239beb93cSSam Leffler 				failed = 1;
211339beb93cSSam Leffler 				break;
211439beb93cSSam Leffler 			}
211539beb93cSSam Leffler 			/* Convert IPv4 address to IPv6 */
211639beb93cSSam Leffler 			if (mask <= 32)
211739beb93cSSam Leffler 				mask += (128 - 32);
211839beb93cSSam Leffler 			os_memset(addr6.s6_addr, 0, 10);
211939beb93cSSam Leffler 			addr6.s6_addr[10] = 0xff;
212039beb93cSSam Leffler 			addr6.s6_addr[11] = 0xff;
212139beb93cSSam Leffler 			os_memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr,
212239beb93cSSam Leffler 				  4);
212339beb93cSSam Leffler 		}
212439beb93cSSam Leffler #endif /* CONFIG_IPV6 */
212539beb93cSSam Leffler 
212639beb93cSSam Leffler 		while (*pos == ' ' || *pos == '\t') {
212739beb93cSSam Leffler 			pos++;
212839beb93cSSam Leffler 		}
212939beb93cSSam Leffler 
213039beb93cSSam Leffler 		if (*pos == '\0') {
213139beb93cSSam Leffler 			failed = 1;
213239beb93cSSam Leffler 			break;
213339beb93cSSam Leffler 		}
213439beb93cSSam Leffler 
213539beb93cSSam Leffler 		entry = os_zalloc(sizeof(*entry));
213639beb93cSSam Leffler 		if (entry == NULL) {
213739beb93cSSam Leffler 			failed = 1;
213839beb93cSSam Leffler 			break;
213939beb93cSSam Leffler 		}
214039beb93cSSam Leffler 		entry->shared_secret = os_strdup(pos);
214139beb93cSSam Leffler 		if (entry->shared_secret == NULL) {
214239beb93cSSam Leffler 			failed = 1;
214339beb93cSSam Leffler 			os_free(entry);
214439beb93cSSam Leffler 			break;
214539beb93cSSam Leffler 		}
214639beb93cSSam Leffler 		entry->shared_secret_len = os_strlen(entry->shared_secret);
214739beb93cSSam Leffler 		if (!ipv6) {
21485b9c547cSRui Paulo 			entry->addr.s_addr = addr.s_addr;
214939beb93cSSam Leffler 			val = 0;
215039beb93cSSam Leffler 			for (i = 0; i < mask; i++)
21514bc52338SCy Schubert 				val |= 1U << (31 - i);
215239beb93cSSam Leffler 			entry->mask.s_addr = htonl(val);
215339beb93cSSam Leffler 		}
215439beb93cSSam Leffler #ifdef CONFIG_IPV6
215539beb93cSSam Leffler 		if (ipv6) {
215639beb93cSSam Leffler 			int offset = mask / 8;
215739beb93cSSam Leffler 
215839beb93cSSam Leffler 			os_memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16);
215939beb93cSSam Leffler 			os_memset(entry->mask6.s6_addr, 0xff, offset);
216039beb93cSSam Leffler 			val = 0;
216139beb93cSSam Leffler 			for (i = 0; i < (mask % 8); i++)
216239beb93cSSam Leffler 				val |= 1 << (7 - i);
216339beb93cSSam Leffler 			if (offset < 16)
216439beb93cSSam Leffler 				entry->mask6.s6_addr[offset] = val;
216539beb93cSSam Leffler 		}
216639beb93cSSam Leffler #endif /* CONFIG_IPV6 */
216739beb93cSSam Leffler 
216839beb93cSSam Leffler 		if (tail == NULL) {
216939beb93cSSam Leffler 			clients = tail = entry;
217039beb93cSSam Leffler 		} else {
217139beb93cSSam Leffler 			tail->next = entry;
217239beb93cSSam Leffler 			tail = entry;
217339beb93cSSam Leffler 		}
217439beb93cSSam Leffler 	}
217539beb93cSSam Leffler 
217639beb93cSSam Leffler 	if (failed) {
217739beb93cSSam Leffler 		RADIUS_ERROR("Invalid line %d in '%s'", line, client_file);
217839beb93cSSam Leffler 		radius_server_free_clients(NULL, clients);
217939beb93cSSam Leffler 		clients = NULL;
218039beb93cSSam Leffler 	}
218139beb93cSSam Leffler 
218239beb93cSSam Leffler 	os_free(buf);
218339beb93cSSam Leffler 	fclose(f);
218439beb93cSSam Leffler 
218539beb93cSSam Leffler 	return clients;
218639beb93cSSam Leffler }
218739beb93cSSam Leffler 
218839beb93cSSam Leffler 
2189e28a4053SRui Paulo /**
2190e28a4053SRui Paulo  * radius_server_init - Initialize RADIUS server
2191e28a4053SRui Paulo  * @conf: Configuration for the RADIUS server
2192e28a4053SRui Paulo  * Returns: Pointer to private RADIUS server context or %NULL on failure
2193e28a4053SRui Paulo  *
2194e28a4053SRui Paulo  * This initializes a RADIUS server instance and returns a context pointer that
2195e28a4053SRui Paulo  * will be used in other calls to the RADIUS server module. The server can be
2196e28a4053SRui Paulo  * deinitialize by calling radius_server_deinit().
2197e28a4053SRui Paulo  */
219839beb93cSSam Leffler struct radius_server_data *
radius_server_init(struct radius_server_conf * conf)219939beb93cSSam Leffler radius_server_init(struct radius_server_conf *conf)
220039beb93cSSam Leffler {
220139beb93cSSam Leffler 	struct radius_server_data *data;
220239beb93cSSam Leffler 
220339beb93cSSam Leffler #ifndef CONFIG_IPV6
220439beb93cSSam Leffler 	if (conf->ipv6) {
22055b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "RADIUS server compiled without IPv6 support");
220639beb93cSSam Leffler 		return NULL;
220739beb93cSSam Leffler 	}
220839beb93cSSam Leffler #endif /* CONFIG_IPV6 */
220939beb93cSSam Leffler 
221039beb93cSSam Leffler 	data = os_zalloc(sizeof(*data));
221139beb93cSSam Leffler 	if (data == NULL)
221239beb93cSSam Leffler 		return NULL;
221339beb93cSSam Leffler 
2214c1d255d3SCy Schubert 	data->eap_cfg = conf->eap_cfg;
2215206b73d0SCy Schubert 	data->auth_sock = -1;
2216206b73d0SCy Schubert 	data->acct_sock = -1;
22175b9c547cSRui Paulo 	dl_list_init(&data->erp_keys);
22185b9c547cSRui Paulo 	os_get_reltime(&data->start_time);
221939beb93cSSam Leffler 	data->conf_ctx = conf->conf_ctx;
2220c1d255d3SCy Schubert 	conf->eap_cfg->backend_auth = true;
2221c1d255d3SCy Schubert 	conf->eap_cfg->eap_server = 1;
222239beb93cSSam Leffler 	data->ipv6 = conf->ipv6;
222339beb93cSSam Leffler 	data->get_eap_user = conf->get_eap_user;
222439beb93cSSam Leffler 	if (conf->eap_req_id_text) {
222539beb93cSSam Leffler 		data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len);
2226c1d255d3SCy Schubert 		if (!data->eap_req_id_text)
2227c1d255d3SCy Schubert 			goto fail;
222839beb93cSSam Leffler 		os_memcpy(data->eap_req_id_text, conf->eap_req_id_text,
222939beb93cSSam Leffler 			  conf->eap_req_id_text_len);
223039beb93cSSam Leffler 		data->eap_req_id_text_len = conf->eap_req_id_text_len;
223139beb93cSSam Leffler 	}
22325b9c547cSRui Paulo 	data->erp_domain = conf->erp_domain;
22335b9c547cSRui Paulo 
22345b9c547cSRui Paulo 	if (conf->subscr_remediation_url) {
22355b9c547cSRui Paulo 		data->subscr_remediation_url =
22365b9c547cSRui Paulo 			os_strdup(conf->subscr_remediation_url);
2237c1d255d3SCy Schubert 		if (!data->subscr_remediation_url)
2238c1d255d3SCy Schubert 			goto fail;
22395b9c547cSRui Paulo 	}
22405b9c547cSRui Paulo 	data->subscr_remediation_method = conf->subscr_remediation_method;
2241c1d255d3SCy Schubert 	if (conf->hs20_sim_provisioning_url) {
22424bc52338SCy Schubert 		data->hs20_sim_provisioning_url =
22434bc52338SCy Schubert 			os_strdup(conf->hs20_sim_provisioning_url);
2244c1d255d3SCy Schubert 		if (!data->hs20_sim_provisioning_url)
2245c1d255d3SCy Schubert 			goto fail;
2246c1d255d3SCy Schubert 	}
22475b9c547cSRui Paulo 
2248c1d255d3SCy Schubert 	if (conf->t_c_server_url) {
224985732ac8SCy Schubert 		data->t_c_server_url = os_strdup(conf->t_c_server_url);
2250c1d255d3SCy Schubert 		if (!data->t_c_server_url)
2251c1d255d3SCy Schubert 			goto fail;
2252c1d255d3SCy Schubert 	}
225385732ac8SCy Schubert 
22545b9c547cSRui Paulo #ifdef CONFIG_SQLITE
22555b9c547cSRui Paulo 	if (conf->sqlite_file) {
22565b9c547cSRui Paulo 		if (sqlite3_open(conf->sqlite_file, &data->db)) {
22575b9c547cSRui Paulo 			RADIUS_ERROR("Could not open SQLite file '%s'",
22585b9c547cSRui Paulo 				     conf->sqlite_file);
2259c1d255d3SCy Schubert 			goto fail;
22605b9c547cSRui Paulo 		}
22615b9c547cSRui Paulo 	}
22625b9c547cSRui Paulo #endif /* CONFIG_SQLITE */
226339beb93cSSam Leffler 
2264f05cddf9SRui Paulo #ifdef CONFIG_RADIUS_TEST
2265f05cddf9SRui Paulo 	if (conf->dump_msk_file)
2266f05cddf9SRui Paulo 		data->dump_msk_file = os_strdup(conf->dump_msk_file);
2267f05cddf9SRui Paulo #endif /* CONFIG_RADIUS_TEST */
2268f05cddf9SRui Paulo 
226939beb93cSSam Leffler 	data->clients = radius_server_read_clients(conf->client_file,
227039beb93cSSam Leffler 						   conf->ipv6);
227139beb93cSSam Leffler 	if (data->clients == NULL) {
22725b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "No RADIUS clients configured");
2273c1d255d3SCy Schubert 		goto fail;
227439beb93cSSam Leffler 	}
227539beb93cSSam Leffler 
227639beb93cSSam Leffler #ifdef CONFIG_IPV6
227739beb93cSSam Leffler 	if (conf->ipv6)
227839beb93cSSam Leffler 		data->auth_sock = radius_server_open_socket6(conf->auth_port);
227939beb93cSSam Leffler 	else
228039beb93cSSam Leffler #endif /* CONFIG_IPV6 */
228139beb93cSSam Leffler 	data->auth_sock = radius_server_open_socket(conf->auth_port);
228239beb93cSSam Leffler 	if (data->auth_sock < 0) {
22835b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS authentication server");
2284c1d255d3SCy Schubert 		goto fail;
228539beb93cSSam Leffler 	}
228639beb93cSSam Leffler 	if (eloop_register_read_sock(data->auth_sock,
228739beb93cSSam Leffler 				     radius_server_receive_auth,
228839beb93cSSam Leffler 				     data, NULL)) {
2289c1d255d3SCy Schubert 		goto fail;
229039beb93cSSam Leffler 	}
229139beb93cSSam Leffler 
22925b9c547cSRui Paulo 	if (conf->acct_port) {
22935b9c547cSRui Paulo #ifdef CONFIG_IPV6
22945b9c547cSRui Paulo 		if (conf->ipv6)
22955b9c547cSRui Paulo 			data->acct_sock = radius_server_open_socket6(
22965b9c547cSRui Paulo 				conf->acct_port);
22975b9c547cSRui Paulo 		else
22985b9c547cSRui Paulo #endif /* CONFIG_IPV6 */
22995b9c547cSRui Paulo 		data->acct_sock = radius_server_open_socket(conf->acct_port);
23005b9c547cSRui Paulo 		if (data->acct_sock < 0) {
23015b9c547cSRui Paulo 			wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS accounting server");
2302c1d255d3SCy Schubert 			goto fail;
23035b9c547cSRui Paulo 		}
23045b9c547cSRui Paulo 		if (eloop_register_read_sock(data->acct_sock,
23055b9c547cSRui Paulo 					     radius_server_receive_acct,
2306c1d255d3SCy Schubert 					     data, NULL))
2307c1d255d3SCy Schubert 			goto fail;
23085b9c547cSRui Paulo 	} else {
23095b9c547cSRui Paulo 		data->acct_sock = -1;
23105b9c547cSRui Paulo 	}
23115b9c547cSRui Paulo 
231239beb93cSSam Leffler 	return data;
2313c1d255d3SCy Schubert fail:
2314c1d255d3SCy Schubert 	radius_server_deinit(data);
2315c1d255d3SCy Schubert 	return NULL;
231639beb93cSSam Leffler }
231739beb93cSSam Leffler 
231839beb93cSSam Leffler 
2319e28a4053SRui Paulo /**
23205b9c547cSRui Paulo  * radius_server_erp_flush - Flush all ERP keys
23215b9c547cSRui Paulo  * @data: RADIUS server context from radius_server_init()
23225b9c547cSRui Paulo  */
radius_server_erp_flush(struct radius_server_data * data)23235b9c547cSRui Paulo void radius_server_erp_flush(struct radius_server_data *data)
23245b9c547cSRui Paulo {
23255b9c547cSRui Paulo 	struct eap_server_erp_key *erp;
23265b9c547cSRui Paulo 
23275b9c547cSRui Paulo 	if (data == NULL)
23285b9c547cSRui Paulo 		return;
23295b9c547cSRui Paulo 	while ((erp = dl_list_first(&data->erp_keys, struct eap_server_erp_key,
23305b9c547cSRui Paulo 				    list)) != NULL) {
23315b9c547cSRui Paulo 		dl_list_del(&erp->list);
23325b9c547cSRui Paulo 		bin_clear_free(erp, sizeof(*erp));
23335b9c547cSRui Paulo 	}
23345b9c547cSRui Paulo }
23355b9c547cSRui Paulo 
23365b9c547cSRui Paulo 
23375b9c547cSRui Paulo /**
2338e28a4053SRui Paulo  * radius_server_deinit - Deinitialize RADIUS server
2339e28a4053SRui Paulo  * @data: RADIUS server context from radius_server_init()
2340e28a4053SRui Paulo  */
radius_server_deinit(struct radius_server_data * data)234139beb93cSSam Leffler void radius_server_deinit(struct radius_server_data *data)
234239beb93cSSam Leffler {
234339beb93cSSam Leffler 	if (data == NULL)
234439beb93cSSam Leffler 		return;
234539beb93cSSam Leffler 
234639beb93cSSam Leffler 	if (data->auth_sock >= 0) {
234739beb93cSSam Leffler 		eloop_unregister_read_sock(data->auth_sock);
234839beb93cSSam Leffler 		close(data->auth_sock);
234939beb93cSSam Leffler 	}
235039beb93cSSam Leffler 
23515b9c547cSRui Paulo 	if (data->acct_sock >= 0) {
23525b9c547cSRui Paulo 		eloop_unregister_read_sock(data->acct_sock);
23535b9c547cSRui Paulo 		close(data->acct_sock);
23545b9c547cSRui Paulo 	}
23555b9c547cSRui Paulo 
235639beb93cSSam Leffler 	radius_server_free_clients(data, data->clients);
235739beb93cSSam Leffler 
235839beb93cSSam Leffler 	os_free(data->eap_req_id_text);
2359f05cddf9SRui Paulo #ifdef CONFIG_RADIUS_TEST
2360f05cddf9SRui Paulo 	os_free(data->dump_msk_file);
2361f05cddf9SRui Paulo #endif /* CONFIG_RADIUS_TEST */
23625b9c547cSRui Paulo 	os_free(data->subscr_remediation_url);
23634bc52338SCy Schubert 	os_free(data->hs20_sim_provisioning_url);
236485732ac8SCy Schubert 	os_free(data->t_c_server_url);
23655b9c547cSRui Paulo 
23665b9c547cSRui Paulo #ifdef CONFIG_SQLITE
23675b9c547cSRui Paulo 	if (data->db)
23685b9c547cSRui Paulo 		sqlite3_close(data->db);
23695b9c547cSRui Paulo #endif /* CONFIG_SQLITE */
23705b9c547cSRui Paulo 
23715b9c547cSRui Paulo 	radius_server_erp_flush(data);
23725b9c547cSRui Paulo 
237339beb93cSSam Leffler 	os_free(data);
237439beb93cSSam Leffler }
237539beb93cSSam Leffler 
237639beb93cSSam Leffler 
2377e28a4053SRui Paulo /**
2378e28a4053SRui Paulo  * radius_server_get_mib - Get RADIUS server MIB information
2379e28a4053SRui Paulo  * @data: RADIUS server context from radius_server_init()
2380e28a4053SRui Paulo  * @buf: Buffer for returning the MIB data in text format
2381e28a4053SRui Paulo  * @buflen: buf length in octets
2382e28a4053SRui Paulo  * Returns: Number of octets written into buf
2383e28a4053SRui Paulo  */
radius_server_get_mib(struct radius_server_data * data,char * buf,size_t buflen)238439beb93cSSam Leffler int radius_server_get_mib(struct radius_server_data *data, char *buf,
238539beb93cSSam Leffler 			  size_t buflen)
238639beb93cSSam Leffler {
238739beb93cSSam Leffler 	int ret, uptime;
238839beb93cSSam Leffler 	unsigned int idx;
238939beb93cSSam Leffler 	char *end, *pos;
23905b9c547cSRui Paulo 	struct os_reltime now;
239139beb93cSSam Leffler 	struct radius_client *cli;
239239beb93cSSam Leffler 
239339beb93cSSam Leffler 	/* RFC 2619 - RADIUS Authentication Server MIB */
239439beb93cSSam Leffler 
239539beb93cSSam Leffler 	if (data == NULL || buflen == 0)
239639beb93cSSam Leffler 		return 0;
239739beb93cSSam Leffler 
239839beb93cSSam Leffler 	pos = buf;
239939beb93cSSam Leffler 	end = buf + buflen;
240039beb93cSSam Leffler 
24015b9c547cSRui Paulo 	os_get_reltime(&now);
240239beb93cSSam Leffler 	uptime = (now.sec - data->start_time.sec) * 100 +
240339beb93cSSam Leffler 		((now.usec - data->start_time.usec) / 10000) % 100;
240439beb93cSSam Leffler 	ret = os_snprintf(pos, end - pos,
240539beb93cSSam Leffler 			  "RADIUS-AUTH-SERVER-MIB\n"
240639beb93cSSam Leffler 			  "radiusAuthServIdent=hostapd\n"
240739beb93cSSam Leffler 			  "radiusAuthServUpTime=%d\n"
240839beb93cSSam Leffler 			  "radiusAuthServResetTime=0\n"
240939beb93cSSam Leffler 			  "radiusAuthServConfigReset=4\n",
241039beb93cSSam Leffler 			  uptime);
24115b9c547cSRui Paulo 	if (os_snprintf_error(end - pos, ret)) {
241239beb93cSSam Leffler 		*pos = '\0';
241339beb93cSSam Leffler 		return pos - buf;
241439beb93cSSam Leffler 	}
241539beb93cSSam Leffler 	pos += ret;
241639beb93cSSam Leffler 
241739beb93cSSam Leffler 	ret = os_snprintf(pos, end - pos,
241839beb93cSSam Leffler 			  "radiusAuthServTotalAccessRequests=%u\n"
241939beb93cSSam Leffler 			  "radiusAuthServTotalInvalidRequests=%u\n"
242039beb93cSSam Leffler 			  "radiusAuthServTotalDupAccessRequests=%u\n"
242139beb93cSSam Leffler 			  "radiusAuthServTotalAccessAccepts=%u\n"
242239beb93cSSam Leffler 			  "radiusAuthServTotalAccessRejects=%u\n"
242339beb93cSSam Leffler 			  "radiusAuthServTotalAccessChallenges=%u\n"
242439beb93cSSam Leffler 			  "radiusAuthServTotalMalformedAccessRequests=%u\n"
242539beb93cSSam Leffler 			  "radiusAuthServTotalBadAuthenticators=%u\n"
242639beb93cSSam Leffler 			  "radiusAuthServTotalPacketsDropped=%u\n"
24275b9c547cSRui Paulo 			  "radiusAuthServTotalUnknownTypes=%u\n"
24285b9c547cSRui Paulo 			  "radiusAccServTotalRequests=%u\n"
24295b9c547cSRui Paulo 			  "radiusAccServTotalInvalidRequests=%u\n"
24305b9c547cSRui Paulo 			  "radiusAccServTotalResponses=%u\n"
24315b9c547cSRui Paulo 			  "radiusAccServTotalMalformedRequests=%u\n"
24325b9c547cSRui Paulo 			  "radiusAccServTotalBadAuthenticators=%u\n"
24335b9c547cSRui Paulo 			  "radiusAccServTotalUnknownTypes=%u\n",
243439beb93cSSam Leffler 			  data->counters.access_requests,
243539beb93cSSam Leffler 			  data->counters.invalid_requests,
243639beb93cSSam Leffler 			  data->counters.dup_access_requests,
243739beb93cSSam Leffler 			  data->counters.access_accepts,
243839beb93cSSam Leffler 			  data->counters.access_rejects,
243939beb93cSSam Leffler 			  data->counters.access_challenges,
244039beb93cSSam Leffler 			  data->counters.malformed_access_requests,
244139beb93cSSam Leffler 			  data->counters.bad_authenticators,
244239beb93cSSam Leffler 			  data->counters.packets_dropped,
24435b9c547cSRui Paulo 			  data->counters.unknown_types,
24445b9c547cSRui Paulo 			  data->counters.acct_requests,
24455b9c547cSRui Paulo 			  data->counters.invalid_acct_requests,
24465b9c547cSRui Paulo 			  data->counters.acct_responses,
24475b9c547cSRui Paulo 			  data->counters.malformed_acct_requests,
24485b9c547cSRui Paulo 			  data->counters.acct_bad_authenticators,
24495b9c547cSRui Paulo 			  data->counters.unknown_acct_types);
24505b9c547cSRui Paulo 	if (os_snprintf_error(end - pos, ret)) {
245139beb93cSSam Leffler 		*pos = '\0';
245239beb93cSSam Leffler 		return pos - buf;
245339beb93cSSam Leffler 	}
245439beb93cSSam Leffler 	pos += ret;
245539beb93cSSam Leffler 
245639beb93cSSam Leffler 	for (cli = data->clients, idx = 0; cli; cli = cli->next, idx++) {
245739beb93cSSam Leffler 		char abuf[50], mbuf[50];
245839beb93cSSam Leffler #ifdef CONFIG_IPV6
245939beb93cSSam Leffler 		if (data->ipv6) {
246039beb93cSSam Leffler 			if (inet_ntop(AF_INET6, &cli->addr6, abuf,
246139beb93cSSam Leffler 				      sizeof(abuf)) == NULL)
246239beb93cSSam Leffler 				abuf[0] = '\0';
24635b9c547cSRui Paulo 			if (inet_ntop(AF_INET6, &cli->mask6, mbuf,
246439beb93cSSam Leffler 				      sizeof(mbuf)) == NULL)
246539beb93cSSam Leffler 				mbuf[0] = '\0';
246639beb93cSSam Leffler 		}
246739beb93cSSam Leffler #endif /* CONFIG_IPV6 */
246839beb93cSSam Leffler 		if (!data->ipv6) {
246939beb93cSSam Leffler 			os_strlcpy(abuf, inet_ntoa(cli->addr), sizeof(abuf));
247039beb93cSSam Leffler 			os_strlcpy(mbuf, inet_ntoa(cli->mask), sizeof(mbuf));
247139beb93cSSam Leffler 		}
247239beb93cSSam Leffler 
247339beb93cSSam Leffler 		ret = os_snprintf(pos, end - pos,
247439beb93cSSam Leffler 				  "radiusAuthClientIndex=%u\n"
247539beb93cSSam Leffler 				  "radiusAuthClientAddress=%s/%s\n"
247639beb93cSSam Leffler 				  "radiusAuthServAccessRequests=%u\n"
247739beb93cSSam Leffler 				  "radiusAuthServDupAccessRequests=%u\n"
247839beb93cSSam Leffler 				  "radiusAuthServAccessAccepts=%u\n"
247939beb93cSSam Leffler 				  "radiusAuthServAccessRejects=%u\n"
248039beb93cSSam Leffler 				  "radiusAuthServAccessChallenges=%u\n"
248139beb93cSSam Leffler 				  "radiusAuthServMalformedAccessRequests=%u\n"
248239beb93cSSam Leffler 				  "radiusAuthServBadAuthenticators=%u\n"
248339beb93cSSam Leffler 				  "radiusAuthServPacketsDropped=%u\n"
24845b9c547cSRui Paulo 				  "radiusAuthServUnknownTypes=%u\n"
24855b9c547cSRui Paulo 				  "radiusAccServTotalRequests=%u\n"
24865b9c547cSRui Paulo 				  "radiusAccServTotalInvalidRequests=%u\n"
24875b9c547cSRui Paulo 				  "radiusAccServTotalResponses=%u\n"
24885b9c547cSRui Paulo 				  "radiusAccServTotalMalformedRequests=%u\n"
24895b9c547cSRui Paulo 				  "radiusAccServTotalBadAuthenticators=%u\n"
24905b9c547cSRui Paulo 				  "radiusAccServTotalUnknownTypes=%u\n",
249139beb93cSSam Leffler 				  idx,
249239beb93cSSam Leffler 				  abuf, mbuf,
249339beb93cSSam Leffler 				  cli->counters.access_requests,
249439beb93cSSam Leffler 				  cli->counters.dup_access_requests,
249539beb93cSSam Leffler 				  cli->counters.access_accepts,
249639beb93cSSam Leffler 				  cli->counters.access_rejects,
249739beb93cSSam Leffler 				  cli->counters.access_challenges,
249839beb93cSSam Leffler 				  cli->counters.malformed_access_requests,
249939beb93cSSam Leffler 				  cli->counters.bad_authenticators,
250039beb93cSSam Leffler 				  cli->counters.packets_dropped,
25015b9c547cSRui Paulo 				  cli->counters.unknown_types,
25025b9c547cSRui Paulo 				  cli->counters.acct_requests,
25035b9c547cSRui Paulo 				  cli->counters.invalid_acct_requests,
25045b9c547cSRui Paulo 				  cli->counters.acct_responses,
25055b9c547cSRui Paulo 				  cli->counters.malformed_acct_requests,
25065b9c547cSRui Paulo 				  cli->counters.acct_bad_authenticators,
25075b9c547cSRui Paulo 				  cli->counters.unknown_acct_types);
25085b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret)) {
250939beb93cSSam Leffler 			*pos = '\0';
251039beb93cSSam Leffler 			return pos - buf;
251139beb93cSSam Leffler 		}
251239beb93cSSam Leffler 		pos += ret;
251339beb93cSSam Leffler 	}
251439beb93cSSam Leffler 
251539beb93cSSam Leffler 	return pos - buf;
251639beb93cSSam Leffler }
251739beb93cSSam Leffler 
251839beb93cSSam Leffler 
radius_server_get_eap_user(void * ctx,const u8 * identity,size_t identity_len,int phase2,struct eap_user * user)251939beb93cSSam Leffler static int radius_server_get_eap_user(void *ctx, const u8 *identity,
252039beb93cSSam Leffler 				      size_t identity_len, int phase2,
252139beb93cSSam Leffler 				      struct eap_user *user)
252239beb93cSSam Leffler {
252339beb93cSSam Leffler 	struct radius_session *sess = ctx;
252439beb93cSSam Leffler 	struct radius_server_data *data = sess->server;
25255b9c547cSRui Paulo 	int ret;
252639beb93cSSam Leffler 
25275b9c547cSRui Paulo 	ret = data->get_eap_user(data->conf_ctx, identity, identity_len,
252839beb93cSSam Leffler 				 phase2, user);
25295b9c547cSRui Paulo 	if (ret == 0 && user) {
25305b9c547cSRui Paulo 		sess->accept_attr = user->accept_attr;
25315b9c547cSRui Paulo 		sess->remediation = user->remediation;
25325b9c547cSRui Paulo 		sess->macacl = user->macacl;
253385732ac8SCy Schubert 		sess->t_c_timestamp = user->t_c_timestamp;
25345b9c547cSRui Paulo 	}
2535325151a3SRui Paulo 
2536325151a3SRui Paulo 	if (ret) {
2537325151a3SRui Paulo 		RADIUS_DEBUG("%s: User-Name not found from user database",
2538325151a3SRui Paulo 			     __func__);
2539325151a3SRui Paulo 	}
2540325151a3SRui Paulo 
25415b9c547cSRui Paulo 	return ret;
254239beb93cSSam Leffler }
254339beb93cSSam Leffler 
254439beb93cSSam Leffler 
radius_server_get_eap_req_id_text(void * ctx,size_t * len)254539beb93cSSam Leffler static const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len)
254639beb93cSSam Leffler {
254739beb93cSSam Leffler 	struct radius_session *sess = ctx;
254839beb93cSSam Leffler 	struct radius_server_data *data = sess->server;
254939beb93cSSam Leffler 	*len = data->eap_req_id_text_len;
255039beb93cSSam Leffler 	return data->eap_req_id_text;
255139beb93cSSam Leffler }
255239beb93cSSam Leffler 
255339beb93cSSam Leffler 
radius_server_log_msg(void * ctx,const char * msg)25545b9c547cSRui Paulo static void radius_server_log_msg(void *ctx, const char *msg)
25555b9c547cSRui Paulo {
25565b9c547cSRui Paulo 	struct radius_session *sess = ctx;
25575b9c547cSRui Paulo 	srv_log(sess, "EAP: %s", msg);
25585b9c547cSRui Paulo }
25595b9c547cSRui Paulo 
25605b9c547cSRui Paulo 
25615b9c547cSRui Paulo #ifdef CONFIG_ERP
25625b9c547cSRui Paulo 
radius_server_get_erp_domain(void * ctx)25635b9c547cSRui Paulo static const char * radius_server_get_erp_domain(void *ctx)
25645b9c547cSRui Paulo {
25655b9c547cSRui Paulo 	struct radius_session *sess = ctx;
25665b9c547cSRui Paulo 	struct radius_server_data *data = sess->server;
25675b9c547cSRui Paulo 
25685b9c547cSRui Paulo 	return data->erp_domain;
25695b9c547cSRui Paulo }
25705b9c547cSRui Paulo 
25715b9c547cSRui Paulo 
25725b9c547cSRui Paulo static struct eap_server_erp_key *
radius_server_erp_get_key(void * ctx,const char * keyname)25735b9c547cSRui Paulo radius_server_erp_get_key(void *ctx, const char *keyname)
25745b9c547cSRui Paulo {
25755b9c547cSRui Paulo 	struct radius_session *sess = ctx;
25765b9c547cSRui Paulo 	struct radius_server_data *data = sess->server;
25775b9c547cSRui Paulo 
25784bc52338SCy Schubert 	return radius_server_erp_find_key(data, keyname);
25795b9c547cSRui Paulo }
25805b9c547cSRui Paulo 
25815b9c547cSRui Paulo 
radius_server_erp_add_key(void * ctx,struct eap_server_erp_key * erp)25825b9c547cSRui Paulo static int radius_server_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
25835b9c547cSRui Paulo {
25845b9c547cSRui Paulo 	struct radius_session *sess = ctx;
25855b9c547cSRui Paulo 	struct radius_server_data *data = sess->server;
25865b9c547cSRui Paulo 
25875b9c547cSRui Paulo 	dl_list_add(&data->erp_keys, &erp->list);
25885b9c547cSRui Paulo 	return 0;
25895b9c547cSRui Paulo }
25905b9c547cSRui Paulo 
25915b9c547cSRui Paulo #endif /* CONFIG_ERP */
25925b9c547cSRui Paulo 
25935b9c547cSRui Paulo 
2594325151a3SRui Paulo static const struct eapol_callbacks radius_server_eapol_cb =
259539beb93cSSam Leffler {
259639beb93cSSam Leffler 	.get_eap_user = radius_server_get_eap_user,
259739beb93cSSam Leffler 	.get_eap_req_id_text = radius_server_get_eap_req_id_text,
25985b9c547cSRui Paulo 	.log_msg = radius_server_log_msg,
25995b9c547cSRui Paulo #ifdef CONFIG_ERP
26005b9c547cSRui Paulo 	.get_erp_send_reauth_start = NULL,
26015b9c547cSRui Paulo 	.get_erp_domain = radius_server_get_erp_domain,
26025b9c547cSRui Paulo 	.erp_get_key = radius_server_erp_get_key,
26035b9c547cSRui Paulo 	.erp_add_key = radius_server_erp_add_key,
26045b9c547cSRui Paulo #endif /* CONFIG_ERP */
260539beb93cSSam Leffler };
260639beb93cSSam Leffler 
260739beb93cSSam Leffler 
2608e28a4053SRui Paulo /**
2609e28a4053SRui Paulo  * radius_server_eap_pending_cb - Pending EAP data notification
2610e28a4053SRui Paulo  * @data: RADIUS server context from radius_server_init()
2611e28a4053SRui Paulo  * @ctx: Pending EAP context pointer
2612e28a4053SRui Paulo  *
2613e28a4053SRui Paulo  * This function is used to notify EAP server module that a pending operation
2614e28a4053SRui Paulo  * has been completed and processing of the EAP session can proceed.
2615e28a4053SRui Paulo  */
radius_server_eap_pending_cb(struct radius_server_data * data,void * ctx)261639beb93cSSam Leffler void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx)
261739beb93cSSam Leffler {
261839beb93cSSam Leffler 	struct radius_client *cli;
261939beb93cSSam Leffler 	struct radius_session *s, *sess = NULL;
262039beb93cSSam Leffler 	struct radius_msg *msg;
262139beb93cSSam Leffler 
262239beb93cSSam Leffler 	if (data == NULL)
262339beb93cSSam Leffler 		return;
262439beb93cSSam Leffler 
262539beb93cSSam Leffler 	for (cli = data->clients; cli; cli = cli->next) {
262639beb93cSSam Leffler 		for (s = cli->sessions; s; s = s->next) {
262739beb93cSSam Leffler 			if (s->eap == ctx && s->last_msg) {
262839beb93cSSam Leffler 				sess = s;
262939beb93cSSam Leffler 				break;
263039beb93cSSam Leffler 			}
263139beb93cSSam Leffler 		}
263239beb93cSSam Leffler 		if (sess)
263339beb93cSSam Leffler 			break;
263439beb93cSSam Leffler 	}
263539beb93cSSam Leffler 
263639beb93cSSam Leffler 	if (sess == NULL) {
263739beb93cSSam Leffler 		RADIUS_DEBUG("No session matched callback ctx");
263839beb93cSSam Leffler 		return;
263939beb93cSSam Leffler 	}
264039beb93cSSam Leffler 
264139beb93cSSam Leffler 	msg = sess->last_msg;
264239beb93cSSam Leffler 	sess->last_msg = NULL;
264339beb93cSSam Leffler 	eap_sm_pending_cb(sess->eap);
264439beb93cSSam Leffler 	if (radius_server_request(data, msg,
264539beb93cSSam Leffler 				  (struct sockaddr *) &sess->last_from,
264639beb93cSSam Leffler 				  sess->last_fromlen, cli,
264739beb93cSSam Leffler 				  sess->last_from_addr,
264839beb93cSSam Leffler 				  sess->last_from_port, sess) == -2)
264939beb93cSSam Leffler 		return; /* msg was stored with the session */
265039beb93cSSam Leffler 
265139beb93cSSam Leffler 	radius_msg_free(msg);
265239beb93cSSam Leffler }
265385732ac8SCy Schubert 
265485732ac8SCy Schubert 
265585732ac8SCy Schubert #ifdef CONFIG_SQLITE
265685732ac8SCy Schubert 
265785732ac8SCy Schubert struct db_session_fields {
265885732ac8SCy Schubert 	char *identity;
265985732ac8SCy Schubert 	char *nas;
266085732ac8SCy Schubert 	int hs20_t_c_filtering;
266185732ac8SCy Schubert 	int waiting_coa_ack;
266285732ac8SCy Schubert 	int coa_ack_received;
266385732ac8SCy Schubert };
266485732ac8SCy Schubert 
266585732ac8SCy Schubert 
get_db_session_fields(void * ctx,int argc,char * argv[],char * col[])266685732ac8SCy Schubert static int get_db_session_fields(void *ctx, int argc, char *argv[], char *col[])
266785732ac8SCy Schubert {
266885732ac8SCy Schubert 	struct db_session_fields *fields = ctx;
266985732ac8SCy Schubert 	int i;
267085732ac8SCy Schubert 
267185732ac8SCy Schubert 	for (i = 0; i < argc; i++) {
267285732ac8SCy Schubert 		if (!argv[i])
267385732ac8SCy Schubert 			continue;
267485732ac8SCy Schubert 
267585732ac8SCy Schubert 		RADIUS_DEBUG("Session DB: %s=%s", col[i], argv[i]);
267685732ac8SCy Schubert 
267785732ac8SCy Schubert 		if (os_strcmp(col[i], "identity") == 0) {
267885732ac8SCy Schubert 			os_free(fields->identity);
267985732ac8SCy Schubert 			fields->identity = os_strdup(argv[i]);
268085732ac8SCy Schubert 		} else if (os_strcmp(col[i], "nas") == 0) {
268185732ac8SCy Schubert 			os_free(fields->nas);
268285732ac8SCy Schubert 			fields->nas = os_strdup(argv[i]);
268385732ac8SCy Schubert 		} else if (os_strcmp(col[i], "hs20_t_c_filtering") == 0) {
268485732ac8SCy Schubert 			fields->hs20_t_c_filtering = atoi(argv[i]);
268585732ac8SCy Schubert 		} else if (os_strcmp(col[i], "waiting_coa_ack") == 0) {
268685732ac8SCy Schubert 			fields->waiting_coa_ack = atoi(argv[i]);
268785732ac8SCy Schubert 		} else if (os_strcmp(col[i], "coa_ack_received") == 0) {
268885732ac8SCy Schubert 			fields->coa_ack_received = atoi(argv[i]);
268985732ac8SCy Schubert 		}
269085732ac8SCy Schubert 	}
269185732ac8SCy Schubert 
269285732ac8SCy Schubert 	return 0;
269385732ac8SCy Schubert }
269485732ac8SCy Schubert 
269585732ac8SCy Schubert 
free_db_session_fields(struct db_session_fields * fields)269685732ac8SCy Schubert static void free_db_session_fields(struct db_session_fields *fields)
269785732ac8SCy Schubert {
269885732ac8SCy Schubert 	os_free(fields->identity);
269985732ac8SCy Schubert 	fields->identity = NULL;
270085732ac8SCy Schubert 	os_free(fields->nas);
270185732ac8SCy Schubert 	fields->nas = NULL;
270285732ac8SCy Schubert }
270385732ac8SCy Schubert 
270485732ac8SCy Schubert #endif /* CONFIG_SQLITE */
270585732ac8SCy Schubert 
270685732ac8SCy Schubert 
radius_server_dac_request(struct radius_server_data * data,const char * req)270785732ac8SCy Schubert int radius_server_dac_request(struct radius_server_data *data, const char *req)
270885732ac8SCy Schubert {
270985732ac8SCy Schubert #ifdef CONFIG_SQLITE
271085732ac8SCy Schubert 	char *sql;
271185732ac8SCy Schubert 	int res;
271285732ac8SCy Schubert 	int disconnect;
271385732ac8SCy Schubert 	const char *pos = req;
271485732ac8SCy Schubert 	u8 addr[ETH_ALEN];
271585732ac8SCy Schubert 	char addrtxt[3 * ETH_ALEN];
271685732ac8SCy Schubert 	int t_c_clear = 0;
271785732ac8SCy Schubert 	struct db_session_fields fields;
271885732ac8SCy Schubert 	struct sockaddr_in das;
271985732ac8SCy Schubert 	struct radius_client *client;
272085732ac8SCy Schubert 	struct radius_msg *msg;
272185732ac8SCy Schubert 	struct wpabuf *buf;
272285732ac8SCy Schubert 	u8 identifier;
272385732ac8SCy Schubert 	struct os_time now;
272485732ac8SCy Schubert 
272585732ac8SCy Schubert 	if (!data)
272685732ac8SCy Schubert 		return -1;
272785732ac8SCy Schubert 
272885732ac8SCy Schubert 	/* req: <disconnect|coa> <MAC Address> [t_c_clear] */
272985732ac8SCy Schubert 
273085732ac8SCy Schubert 	if (os_strncmp(pos, "disconnect ", 11) == 0) {
273185732ac8SCy Schubert 		disconnect = 1;
273285732ac8SCy Schubert 		pos += 11;
273385732ac8SCy Schubert 	} else if (os_strncmp(req, "coa ", 4) == 0) {
273485732ac8SCy Schubert 		disconnect = 0;
273585732ac8SCy Schubert 		pos += 4;
273685732ac8SCy Schubert 	} else {
273785732ac8SCy Schubert 		return -1;
273885732ac8SCy Schubert 	}
273985732ac8SCy Schubert 
274085732ac8SCy Schubert 	if (hwaddr_aton(pos, addr))
274185732ac8SCy Schubert 		return -1;
274285732ac8SCy Schubert 	pos = os_strchr(pos, ' ');
274385732ac8SCy Schubert 	if (pos) {
274485732ac8SCy Schubert 		if (os_strstr(pos, "t_c_clear"))
274585732ac8SCy Schubert 			t_c_clear = 1;
274685732ac8SCy Schubert 	}
274785732ac8SCy Schubert 
274885732ac8SCy Schubert 	if (!disconnect && !t_c_clear) {
274985732ac8SCy Schubert 		RADIUS_ERROR("DAC request for CoA without any authorization change");
275085732ac8SCy Schubert 		return -1;
275185732ac8SCy Schubert 	}
275285732ac8SCy Schubert 
275385732ac8SCy Schubert 	if (!data->db) {
275485732ac8SCy Schubert 		RADIUS_ERROR("SQLite database not in use");
275585732ac8SCy Schubert 		return -1;
275685732ac8SCy Schubert 	}
275785732ac8SCy Schubert 
275885732ac8SCy Schubert 	os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, MAC2STR(addr));
275985732ac8SCy Schubert 
276085732ac8SCy Schubert 	sql = sqlite3_mprintf("SELECT * FROM current_sessions WHERE mac_addr=%Q",
276185732ac8SCy Schubert 			      addrtxt);
276285732ac8SCy Schubert 	if (!sql)
276385732ac8SCy Schubert 		return -1;
276485732ac8SCy Schubert 
276585732ac8SCy Schubert 	os_memset(&fields, 0, sizeof(fields));
276685732ac8SCy Schubert 	res = sqlite3_exec(data->db, sql, get_db_session_fields, &fields, NULL);
276785732ac8SCy Schubert 	sqlite3_free(sql);
276885732ac8SCy Schubert 	if (res != SQLITE_OK) {
276985732ac8SCy Schubert 		RADIUS_ERROR("Failed to find matching current_sessions entry from sqlite database: %s",
277085732ac8SCy Schubert 			     sqlite3_errmsg(data->db));
277185732ac8SCy Schubert 		free_db_session_fields(&fields);
277285732ac8SCy Schubert 		return -1;
277385732ac8SCy Schubert 	}
277485732ac8SCy Schubert 
277585732ac8SCy Schubert 	if (!fields.nas) {
277685732ac8SCy Schubert 		RADIUS_ERROR("No NAS information found from current_sessions");
277785732ac8SCy Schubert 		free_db_session_fields(&fields);
277885732ac8SCy Schubert 		return -1;
277985732ac8SCy Schubert 	}
278085732ac8SCy Schubert 
278185732ac8SCy Schubert 	os_memset(&das, 0, sizeof(das));
278285732ac8SCy Schubert 	das.sin_family = AF_INET;
278385732ac8SCy Schubert 	das.sin_addr.s_addr = inet_addr(fields.nas);
278485732ac8SCy Schubert 	das.sin_port = htons(3799);
278585732ac8SCy Schubert 
278685732ac8SCy Schubert 	free_db_session_fields(&fields);
278785732ac8SCy Schubert 
278885732ac8SCy Schubert 	client = radius_server_get_client(data, &das.sin_addr, 0);
278985732ac8SCy Schubert 	if (!client) {
279085732ac8SCy Schubert 		RADIUS_ERROR("No NAS information available to protect the packet");
279185732ac8SCy Schubert 		return -1;
279285732ac8SCy Schubert 	}
279385732ac8SCy Schubert 
279485732ac8SCy Schubert 	identifier = client->next_dac_identifier++;
279585732ac8SCy Schubert 
279685732ac8SCy Schubert 	msg = radius_msg_new(disconnect ? RADIUS_CODE_DISCONNECT_REQUEST :
279785732ac8SCy Schubert 			     RADIUS_CODE_COA_REQUEST, identifier);
279885732ac8SCy Schubert 	if (!msg)
279985732ac8SCy Schubert 		return -1;
280085732ac8SCy Schubert 
280185732ac8SCy Schubert 	os_snprintf(addrtxt, sizeof(addrtxt), RADIUS_802_1X_ADDR_FORMAT,
280285732ac8SCy Schubert 		    MAC2STR(addr));
280385732ac8SCy Schubert 	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
280485732ac8SCy Schubert 				 (u8 *) addrtxt, os_strlen(addrtxt))) {
280585732ac8SCy Schubert 		RADIUS_ERROR("Could not add Calling-Station-Id");
280685732ac8SCy Schubert 		radius_msg_free(msg);
280785732ac8SCy Schubert 		return -1;
280885732ac8SCy Schubert 	}
280985732ac8SCy Schubert 
281085732ac8SCy Schubert 	if (!disconnect && t_c_clear) {
281185732ac8SCy Schubert 		u8 val[4] = { 0x00, 0x00, 0x00, 0x00 }; /* E=0 */
281285732ac8SCy Schubert 
281385732ac8SCy Schubert 		if (!radius_msg_add_wfa(
281485732ac8SCy Schubert 			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING,
281585732ac8SCy Schubert 			    val, sizeof(val))) {
281685732ac8SCy Schubert 			RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering");
281785732ac8SCy Schubert 			radius_msg_free(msg);
281885732ac8SCy Schubert 			return -1;
281985732ac8SCy Schubert 		}
282085732ac8SCy Schubert 	}
282185732ac8SCy Schubert 
282285732ac8SCy Schubert 	os_get_time(&now);
282385732ac8SCy Schubert 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
282485732ac8SCy Schubert 				       now.sec)) {
282585732ac8SCy Schubert 		RADIUS_ERROR("Failed to add Event-Timestamp attribute");
282685732ac8SCy Schubert 		radius_msg_free(msg);
282785732ac8SCy Schubert 		return -1;
282885732ac8SCy Schubert 	}
282985732ac8SCy Schubert 
283085732ac8SCy Schubert 	radius_msg_finish_acct(msg, (u8 *) client->shared_secret,
283185732ac8SCy Schubert 			       client->shared_secret_len);
283285732ac8SCy Schubert 
283385732ac8SCy Schubert 	if (wpa_debug_level <= MSG_MSGDUMP)
283485732ac8SCy Schubert 		radius_msg_dump(msg);
283585732ac8SCy Schubert 
283685732ac8SCy Schubert 	buf = radius_msg_get_buf(msg);
283785732ac8SCy Schubert 	if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
283885732ac8SCy Schubert 		   (struct sockaddr *) &das, sizeof(das)) < 0) {
283985732ac8SCy Schubert 		RADIUS_ERROR("Failed to send packet - sendto: %s",
284085732ac8SCy Schubert 			     strerror(errno));
284185732ac8SCy Schubert 		radius_msg_free(msg);
284285732ac8SCy Schubert 		return -1;
284385732ac8SCy Schubert 	}
284485732ac8SCy Schubert 
284585732ac8SCy Schubert 	if (disconnect) {
284685732ac8SCy Schubert 		radius_msg_free(client->pending_dac_disconnect_req);
284785732ac8SCy Schubert 		client->pending_dac_disconnect_req = msg;
284885732ac8SCy Schubert 		client->pending_dac_disconnect_id = identifier;
284985732ac8SCy Schubert 		os_memcpy(client->pending_dac_disconnect_addr, addr, ETH_ALEN);
285085732ac8SCy Schubert 	} else {
285185732ac8SCy Schubert 		radius_msg_free(client->pending_dac_coa_req);
285285732ac8SCy Schubert 		client->pending_dac_coa_req = msg;
285385732ac8SCy Schubert 		client->pending_dac_coa_id = identifier;
285485732ac8SCy Schubert 		os_memcpy(client->pending_dac_coa_addr, addr, ETH_ALEN);
285585732ac8SCy Schubert 	}
285685732ac8SCy Schubert 
285785732ac8SCy Schubert 	return 0;
285885732ac8SCy Schubert #else /* CONFIG_SQLITE */
285985732ac8SCy Schubert 	return -1;
286085732ac8SCy Schubert #endif /* CONFIG_SQLITE */
286185732ac8SCy Schubert }
2862