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