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