1189251Ssam/* 2214734Srpaulo * RADIUS authentication server 3214734Srpaulo * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> 4189251Ssam * 5189251Ssam * This program is free software; you can redistribute it and/or modify 6189251Ssam * it under the terms of the GNU General Public License version 2 as 7189251Ssam * published by the Free Software Foundation. 8189251Ssam * 9189251Ssam * Alternatively, this software may be distributed under the terms of BSD 10189251Ssam * license. 11189251Ssam * 12189251Ssam * See README and COPYING for more details. 13189251Ssam */ 14189251Ssam 15189251Ssam#include "includes.h" 16189251Ssam#include <net/if.h> 17189251Ssam 18189251Ssam#include "common.h" 19189251Ssam#include "radius.h" 20189251Ssam#include "eloop.h" 21189251Ssam#include "eap_server/eap.h" 22189251Ssam#include "radius_server.h" 23189251Ssam 24214734Srpaulo/** 25214734Srpaulo * RADIUS_SESSION_TIMEOUT - Session timeout in seconds 26214734Srpaulo */ 27189251Ssam#define RADIUS_SESSION_TIMEOUT 60 28214734Srpaulo 29214734Srpaulo/** 30214734Srpaulo * RADIUS_MAX_SESSION - Maximum number of active sessions 31214734Srpaulo */ 32189251Ssam#define RADIUS_MAX_SESSION 100 33214734Srpaulo 34214734Srpaulo/** 35214734Srpaulo * RADIUS_MAX_MSG_LEN - Maximum message length for incoming RADIUS messages 36214734Srpaulo */ 37189251Ssam#define RADIUS_MAX_MSG_LEN 3000 38189251Ssam 39189251Ssamstatic struct eapol_callbacks radius_server_eapol_cb; 40189251Ssam 41189251Ssamstruct radius_client; 42189251Ssamstruct radius_server_data; 43189251Ssam 44214734Srpaulo/** 45214734Srpaulo * struct radius_server_counters - RADIUS server statistics counters 46214734Srpaulo */ 47189251Ssamstruct radius_server_counters { 48189251Ssam u32 access_requests; 49189251Ssam u32 invalid_requests; 50189251Ssam u32 dup_access_requests; 51189251Ssam u32 access_accepts; 52189251Ssam u32 access_rejects; 53189251Ssam u32 access_challenges; 54189251Ssam u32 malformed_access_requests; 55189251Ssam u32 bad_authenticators; 56189251Ssam u32 packets_dropped; 57189251Ssam u32 unknown_types; 58189251Ssam}; 59189251Ssam 60214734Srpaulo/** 61214734Srpaulo * struct radius_session - Internal RADIUS server data for a session 62214734Srpaulo */ 63189251Ssamstruct radius_session { 64189251Ssam struct radius_session *next; 65189251Ssam struct radius_client *client; 66189251Ssam struct radius_server_data *server; 67189251Ssam unsigned int sess_id; 68189251Ssam struct eap_sm *eap; 69189251Ssam struct eap_eapol_interface *eap_if; 70189251Ssam 71189251Ssam struct radius_msg *last_msg; 72189251Ssam char *last_from_addr; 73189251Ssam int last_from_port; 74189251Ssam struct sockaddr_storage last_from; 75189251Ssam socklen_t last_fromlen; 76189251Ssam u8 last_identifier; 77189251Ssam struct radius_msg *last_reply; 78189251Ssam u8 last_authenticator[16]; 79189251Ssam}; 80189251Ssam 81214734Srpaulo/** 82214734Srpaulo * struct radius_client - Internal RADIUS server data for a client 83214734Srpaulo */ 84189251Ssamstruct radius_client { 85189251Ssam struct radius_client *next; 86189251Ssam struct in_addr addr; 87189251Ssam struct in_addr mask; 88189251Ssam#ifdef CONFIG_IPV6 89189251Ssam struct in6_addr addr6; 90189251Ssam struct in6_addr mask6; 91189251Ssam#endif /* CONFIG_IPV6 */ 92189251Ssam char *shared_secret; 93189251Ssam int shared_secret_len; 94189251Ssam struct radius_session *sessions; 95189251Ssam struct radius_server_counters counters; 96189251Ssam}; 97189251Ssam 98214734Srpaulo/** 99214734Srpaulo * struct radius_server_data - Internal RADIUS server data 100214734Srpaulo */ 101189251Ssamstruct radius_server_data { 102214734Srpaulo /** 103214734Srpaulo * auth_sock - Socket for RADIUS authentication messages 104214734Srpaulo */ 105189251Ssam int auth_sock; 106214734Srpaulo 107214734Srpaulo /** 108214734Srpaulo * clients - List of authorized RADIUS clients 109214734Srpaulo */ 110189251Ssam struct radius_client *clients; 111214734Srpaulo 112214734Srpaulo /** 113214734Srpaulo * next_sess_id - Next session identifier 114214734Srpaulo */ 115189251Ssam unsigned int next_sess_id; 116214734Srpaulo 117214734Srpaulo /** 118214734Srpaulo * conf_ctx - Context pointer for callbacks 119214734Srpaulo * 120214734Srpaulo * This is used as the ctx argument in get_eap_user() calls. 121214734Srpaulo */ 122189251Ssam void *conf_ctx; 123214734Srpaulo 124214734Srpaulo /** 125214734Srpaulo * num_sess - Number of active sessions 126214734Srpaulo */ 127189251Ssam int num_sess; 128214734Srpaulo 129214734Srpaulo /** 130214734Srpaulo * eap_sim_db_priv - EAP-SIM/AKA database context 131214734Srpaulo * 132214734Srpaulo * This is passed to the EAP-SIM/AKA server implementation as a 133214734Srpaulo * callback context. 134214734Srpaulo */ 135189251Ssam void *eap_sim_db_priv; 136214734Srpaulo 137214734Srpaulo /** 138214734Srpaulo * ssl_ctx - TLS context 139214734Srpaulo * 140214734Srpaulo * This is passed to the EAP server implementation as a callback 141214734Srpaulo * context for TLS operations. 142214734Srpaulo */ 143189251Ssam void *ssl_ctx; 144214734Srpaulo 145214734Srpaulo /** 146214734Srpaulo * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST 147214734Srpaulo * 148214734Srpaulo * This parameter is used to set a key for EAP-FAST to encrypt the 149214734Srpaulo * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If 150214734Srpaulo * set, must point to a 16-octet key. 151214734Srpaulo */ 152189251Ssam u8 *pac_opaque_encr_key; 153214734Srpaulo 154214734Srpaulo /** 155214734Srpaulo * eap_fast_a_id - EAP-FAST authority identity (A-ID) 156214734Srpaulo * 157214734Srpaulo * If EAP-FAST is not used, this can be set to %NULL. In theory, this 158214734Srpaulo * is a variable length field, but due to some existing implementations 159214734Srpaulo * requiring A-ID to be 16 octets in length, it is recommended to use 160214734Srpaulo * that length for the field to provide interoperability with deployed 161214734Srpaulo * peer implementations. 162214734Srpaulo */ 163189251Ssam u8 *eap_fast_a_id; 164214734Srpaulo 165214734Srpaulo /** 166214734Srpaulo * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets 167214734Srpaulo */ 168189251Ssam size_t eap_fast_a_id_len; 169214734Srpaulo 170214734Srpaulo /** 171214734Srpaulo * eap_fast_a_id_info - EAP-FAST authority identifier information 172214734Srpaulo * 173214734Srpaulo * This A-ID-Info contains a user-friendly name for the A-ID. For 174214734Srpaulo * example, this could be the enterprise and server names in 175214734Srpaulo * human-readable format. This field is encoded as UTF-8. If EAP-FAST 176214734Srpaulo * is not used, this can be set to %NULL. 177214734Srpaulo */ 178189251Ssam char *eap_fast_a_id_info; 179214734Srpaulo 180214734Srpaulo /** 181214734Srpaulo * eap_fast_prov - EAP-FAST provisioning modes 182214734Srpaulo * 183214734Srpaulo * 0 = provisioning disabled, 1 = only anonymous provisioning allowed, 184214734Srpaulo * 2 = only authenticated provisioning allowed, 3 = both provisioning 185214734Srpaulo * modes allowed. 186214734Srpaulo */ 187189251Ssam int eap_fast_prov; 188214734Srpaulo 189214734Srpaulo /** 190214734Srpaulo * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds 191214734Srpaulo * 192214734Srpaulo * This is the hard limit on how long a provisioned PAC-Key can be 193214734Srpaulo * used. 194214734Srpaulo */ 195189251Ssam int pac_key_lifetime; 196214734Srpaulo 197214734Srpaulo /** 198214734Srpaulo * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds 199214734Srpaulo * 200214734Srpaulo * This is a soft limit on the PAC-Key. The server will automatically 201214734Srpaulo * generate a new PAC-Key when this number of seconds (or fewer) of the 202214734Srpaulo * lifetime remains. 203214734Srpaulo */ 204189251Ssam int pac_key_refresh_time; 205214734Srpaulo 206214734Srpaulo /** 207214734Srpaulo * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication 208214734Srpaulo * 209214734Srpaulo * This controls whether the protected success/failure indication 210214734Srpaulo * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA. 211214734Srpaulo */ 212189251Ssam int eap_sim_aka_result_ind; 213214734Srpaulo 214214734Srpaulo /** 215214734Srpaulo * tnc - Trusted Network Connect (TNC) 216214734Srpaulo * 217214734Srpaulo * This controls whether TNC is enabled and will be required before the 218214734Srpaulo * peer is allowed to connect. Note: This is only used with EAP-TTLS 219214734Srpaulo * and EAP-FAST. If any other EAP method is enabled, the peer will be 220214734Srpaulo * allowed to connect without TNC. 221214734Srpaulo */ 222189251Ssam int tnc; 223214734Srpaulo 224214734Srpaulo /** 225214734Srpaulo * wps - Wi-Fi Protected Setup context 226214734Srpaulo * 227214734Srpaulo * If WPS is used with an external RADIUS server (which is quite 228214734Srpaulo * unlikely configuration), this is used to provide a pointer to WPS 229214734Srpaulo * context data. Normally, this can be set to %NULL. 230214734Srpaulo */ 231189251Ssam struct wps_context *wps; 232214734Srpaulo 233214734Srpaulo /** 234214734Srpaulo * ipv6 - Whether to enable IPv6 support in the RADIUS server 235214734Srpaulo */ 236189251Ssam int ipv6; 237214734Srpaulo 238214734Srpaulo /** 239214734Srpaulo * start_time - Timestamp of server start 240214734Srpaulo */ 241189251Ssam struct os_time start_time; 242214734Srpaulo 243214734Srpaulo /** 244214734Srpaulo * counters - Statistics counters for server operations 245214734Srpaulo * 246214734Srpaulo * These counters are the sum over all clients. 247214734Srpaulo */ 248189251Ssam struct radius_server_counters counters; 249214734Srpaulo 250214734Srpaulo /** 251214734Srpaulo * get_eap_user - Callback for fetching EAP user information 252214734Srpaulo * @ctx: Context data from conf_ctx 253214734Srpaulo * @identity: User identity 254214734Srpaulo * @identity_len: identity buffer length in octets 255214734Srpaulo * @phase2: Whether this is for Phase 2 identity 256214734Srpaulo * @user: Data structure for filling in the user information 257214734Srpaulo * Returns: 0 on success, -1 on failure 258214734Srpaulo * 259214734Srpaulo * This is used to fetch information from user database. The callback 260214734Srpaulo * will fill in information about allowed EAP methods and the user 261214734Srpaulo * password. The password field will be an allocated copy of the 262214734Srpaulo * password data and RADIUS server will free it after use. 263214734Srpaulo */ 264189251Ssam int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, 265189251Ssam int phase2, struct eap_user *user); 266214734Srpaulo 267214734Srpaulo /** 268214734Srpaulo * eap_req_id_text - Optional data for EAP-Request/Identity 269214734Srpaulo * 270214734Srpaulo * This can be used to configure an optional, displayable message that 271214734Srpaulo * will be sent in EAP-Request/Identity. This string can contain an 272214734Srpaulo * ASCII-0 character (nul) to separate network infromation per RFC 273214734Srpaulo * 4284. The actual string length is explicit provided in 274214734Srpaulo * eap_req_id_text_len since nul character will not be used as a string 275214734Srpaulo * terminator. 276214734Srpaulo */ 277189251Ssam char *eap_req_id_text; 278214734Srpaulo 279214734Srpaulo /** 280214734Srpaulo * eap_req_id_text_len - Length of eap_req_id_text buffer in octets 281214734Srpaulo */ 282189251Ssam size_t eap_req_id_text_len; 283214734Srpaulo 284214734Srpaulo /* 285214734Srpaulo * msg_ctx - Context data for wpa_msg() calls 286214734Srpaulo */ 287214734Srpaulo void *msg_ctx; 288189251Ssam}; 289189251Ssam 290189251Ssam 291189251Ssamextern int wpa_debug_level; 292189251Ssam 293189251Ssam#define RADIUS_DEBUG(args...) \ 294189251Ssamwpa_printf(MSG_DEBUG, "RADIUS SRV: " args) 295189251Ssam#define RADIUS_ERROR(args...) \ 296189251Ssamwpa_printf(MSG_ERROR, "RADIUS SRV: " args) 297189251Ssam#define RADIUS_DUMP(args...) \ 298189251Ssamwpa_hexdump(MSG_MSGDUMP, "RADIUS SRV: " args) 299189251Ssam#define RADIUS_DUMP_ASCII(args...) \ 300189251Ssamwpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args) 301189251Ssam 302189251Ssam 303189251Ssamstatic void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx); 304209158Srpaulostatic void radius_server_session_remove_timeout(void *eloop_ctx, 305209158Srpaulo void *timeout_ctx); 306189251Ssam 307189251Ssam 308189251Ssamstatic struct radius_client * 309189251Ssamradius_server_get_client(struct radius_server_data *data, struct in_addr *addr, 310189251Ssam int ipv6) 311189251Ssam{ 312189251Ssam struct radius_client *client = data->clients; 313189251Ssam 314189251Ssam while (client) { 315189251Ssam#ifdef CONFIG_IPV6 316189251Ssam if (ipv6) { 317189251Ssam struct in6_addr *addr6; 318189251Ssam int i; 319189251Ssam 320189251Ssam addr6 = (struct in6_addr *) addr; 321189251Ssam for (i = 0; i < 16; i++) { 322189251Ssam if ((addr6->s6_addr[i] & 323189251Ssam client->mask6.s6_addr[i]) != 324189251Ssam (client->addr6.s6_addr[i] & 325189251Ssam client->mask6.s6_addr[i])) { 326189251Ssam i = 17; 327189251Ssam break; 328189251Ssam } 329189251Ssam } 330189251Ssam if (i == 16) { 331189251Ssam break; 332189251Ssam } 333189251Ssam } 334189251Ssam#endif /* CONFIG_IPV6 */ 335189251Ssam if (!ipv6 && (client->addr.s_addr & client->mask.s_addr) == 336189251Ssam (addr->s_addr & client->mask.s_addr)) { 337189251Ssam break; 338189251Ssam } 339189251Ssam 340189251Ssam client = client->next; 341189251Ssam } 342189251Ssam 343189251Ssam return client; 344189251Ssam} 345189251Ssam 346189251Ssam 347189251Ssamstatic struct radius_session * 348189251Ssamradius_server_get_session(struct radius_client *client, unsigned int sess_id) 349189251Ssam{ 350189251Ssam struct radius_session *sess = client->sessions; 351189251Ssam 352189251Ssam while (sess) { 353189251Ssam if (sess->sess_id == sess_id) { 354189251Ssam break; 355189251Ssam } 356189251Ssam sess = sess->next; 357189251Ssam } 358189251Ssam 359189251Ssam return sess; 360189251Ssam} 361189251Ssam 362189251Ssam 363189251Ssamstatic void radius_server_session_free(struct radius_server_data *data, 364189251Ssam struct radius_session *sess) 365189251Ssam{ 366189251Ssam eloop_cancel_timeout(radius_server_session_timeout, data, sess); 367209158Srpaulo eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess); 368189251Ssam eap_server_sm_deinit(sess->eap); 369214734Srpaulo radius_msg_free(sess->last_msg); 370189251Ssam os_free(sess->last_from_addr); 371214734Srpaulo radius_msg_free(sess->last_reply); 372189251Ssam os_free(sess); 373189251Ssam data->num_sess--; 374189251Ssam} 375189251Ssam 376189251Ssam 377189251Ssamstatic void radius_server_session_remove(struct radius_server_data *data, 378189251Ssam struct radius_session *sess) 379189251Ssam{ 380189251Ssam struct radius_client *client = sess->client; 381189251Ssam struct radius_session *session, *prev; 382189251Ssam 383189251Ssam eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess); 384189251Ssam 385189251Ssam prev = NULL; 386189251Ssam session = client->sessions; 387189251Ssam while (session) { 388189251Ssam if (session == sess) { 389189251Ssam if (prev == NULL) { 390189251Ssam client->sessions = sess->next; 391189251Ssam } else { 392189251Ssam prev->next = sess->next; 393189251Ssam } 394189251Ssam radius_server_session_free(data, sess); 395189251Ssam break; 396189251Ssam } 397189251Ssam prev = session; 398189251Ssam session = session->next; 399189251Ssam } 400189251Ssam} 401189251Ssam 402189251Ssam 403189251Ssamstatic void radius_server_session_remove_timeout(void *eloop_ctx, 404189251Ssam void *timeout_ctx) 405189251Ssam{ 406189251Ssam struct radius_server_data *data = eloop_ctx; 407189251Ssam struct radius_session *sess = timeout_ctx; 408189251Ssam RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id); 409189251Ssam radius_server_session_remove(data, sess); 410189251Ssam} 411189251Ssam 412189251Ssam 413189251Ssamstatic void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx) 414189251Ssam{ 415189251Ssam struct radius_server_data *data = eloop_ctx; 416189251Ssam struct radius_session *sess = timeout_ctx; 417189251Ssam 418189251Ssam RADIUS_DEBUG("Timing out authentication session 0x%x", sess->sess_id); 419189251Ssam radius_server_session_remove(data, sess); 420189251Ssam} 421189251Ssam 422189251Ssam 423189251Ssamstatic struct radius_session * 424189251Ssamradius_server_new_session(struct radius_server_data *data, 425189251Ssam struct radius_client *client) 426189251Ssam{ 427189251Ssam struct radius_session *sess; 428189251Ssam 429189251Ssam if (data->num_sess >= RADIUS_MAX_SESSION) { 430189251Ssam RADIUS_DEBUG("Maximum number of existing session - no room " 431189251Ssam "for a new session"); 432189251Ssam return NULL; 433189251Ssam } 434189251Ssam 435189251Ssam sess = os_zalloc(sizeof(*sess)); 436189251Ssam if (sess == NULL) 437189251Ssam return NULL; 438189251Ssam 439189251Ssam sess->server = data; 440189251Ssam sess->client = client; 441189251Ssam sess->sess_id = data->next_sess_id++; 442189251Ssam sess->next = client->sessions; 443189251Ssam client->sessions = sess; 444189251Ssam eloop_register_timeout(RADIUS_SESSION_TIMEOUT, 0, 445189251Ssam radius_server_session_timeout, data, sess); 446189251Ssam data->num_sess++; 447189251Ssam return sess; 448189251Ssam} 449189251Ssam 450189251Ssam 451189251Ssamstatic struct radius_session * 452189251Ssamradius_server_get_new_session(struct radius_server_data *data, 453189251Ssam struct radius_client *client, 454189251Ssam struct radius_msg *msg) 455189251Ssam{ 456189251Ssam u8 *user; 457189251Ssam size_t user_len; 458189251Ssam int res; 459189251Ssam struct radius_session *sess; 460189251Ssam struct eap_config eap_conf; 461189251Ssam 462189251Ssam RADIUS_DEBUG("Creating a new session"); 463189251Ssam 464189251Ssam user = os_malloc(256); 465189251Ssam if (user == NULL) { 466189251Ssam return NULL; 467189251Ssam } 468189251Ssam res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256); 469189251Ssam if (res < 0 || res > 256) { 470189251Ssam RADIUS_DEBUG("Could not get User-Name"); 471189251Ssam os_free(user); 472189251Ssam return NULL; 473189251Ssam } 474189251Ssam user_len = res; 475189251Ssam RADIUS_DUMP_ASCII("User-Name", user, user_len); 476189251Ssam 477189251Ssam res = data->get_eap_user(data->conf_ctx, user, user_len, 0, NULL); 478189251Ssam os_free(user); 479189251Ssam 480189251Ssam if (res == 0) { 481189251Ssam RADIUS_DEBUG("Matching user entry found"); 482189251Ssam sess = radius_server_new_session(data, client); 483189251Ssam if (sess == NULL) { 484189251Ssam RADIUS_DEBUG("Failed to create a new session"); 485189251Ssam return NULL; 486189251Ssam } 487189251Ssam } else { 488189251Ssam RADIUS_DEBUG("User-Name not found from user database"); 489189251Ssam return NULL; 490189251Ssam } 491189251Ssam 492189251Ssam os_memset(&eap_conf, 0, sizeof(eap_conf)); 493189251Ssam eap_conf.ssl_ctx = data->ssl_ctx; 494214734Srpaulo eap_conf.msg_ctx = data->msg_ctx; 495189251Ssam eap_conf.eap_sim_db_priv = data->eap_sim_db_priv; 496189251Ssam eap_conf.backend_auth = TRUE; 497189251Ssam eap_conf.eap_server = 1; 498189251Ssam eap_conf.pac_opaque_encr_key = data->pac_opaque_encr_key; 499189251Ssam eap_conf.eap_fast_a_id = data->eap_fast_a_id; 500189251Ssam eap_conf.eap_fast_a_id_len = data->eap_fast_a_id_len; 501189251Ssam eap_conf.eap_fast_a_id_info = data->eap_fast_a_id_info; 502189251Ssam eap_conf.eap_fast_prov = data->eap_fast_prov; 503189251Ssam eap_conf.pac_key_lifetime = data->pac_key_lifetime; 504189251Ssam eap_conf.pac_key_refresh_time = data->pac_key_refresh_time; 505189251Ssam eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind; 506189251Ssam eap_conf.tnc = data->tnc; 507189251Ssam eap_conf.wps = data->wps; 508189251Ssam sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, 509189251Ssam &eap_conf); 510189251Ssam if (sess->eap == NULL) { 511189251Ssam RADIUS_DEBUG("Failed to initialize EAP state machine for the " 512189251Ssam "new session"); 513189251Ssam radius_server_session_free(data, sess); 514189251Ssam return NULL; 515189251Ssam } 516189251Ssam sess->eap_if = eap_get_interface(sess->eap); 517189251Ssam sess->eap_if->eapRestart = TRUE; 518189251Ssam sess->eap_if->portEnabled = TRUE; 519189251Ssam 520189251Ssam RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id); 521189251Ssam 522189251Ssam return sess; 523189251Ssam} 524189251Ssam 525189251Ssam 526189251Ssamstatic struct radius_msg * 527189251Ssamradius_server_encapsulate_eap(struct radius_server_data *data, 528189251Ssam struct radius_client *client, 529189251Ssam struct radius_session *sess, 530189251Ssam struct radius_msg *request) 531189251Ssam{ 532189251Ssam struct radius_msg *msg; 533189251Ssam int code; 534189251Ssam unsigned int sess_id; 535214734Srpaulo struct radius_hdr *hdr = radius_msg_get_hdr(request); 536189251Ssam 537189251Ssam if (sess->eap_if->eapFail) { 538189251Ssam sess->eap_if->eapFail = FALSE; 539189251Ssam code = RADIUS_CODE_ACCESS_REJECT; 540189251Ssam } else if (sess->eap_if->eapSuccess) { 541189251Ssam sess->eap_if->eapSuccess = FALSE; 542189251Ssam code = RADIUS_CODE_ACCESS_ACCEPT; 543189251Ssam } else { 544189251Ssam sess->eap_if->eapReq = FALSE; 545189251Ssam code = RADIUS_CODE_ACCESS_CHALLENGE; 546189251Ssam } 547189251Ssam 548214734Srpaulo msg = radius_msg_new(code, hdr->identifier); 549189251Ssam if (msg == NULL) { 550189251Ssam RADIUS_DEBUG("Failed to allocate reply message"); 551189251Ssam return NULL; 552189251Ssam } 553189251Ssam 554189251Ssam sess_id = htonl(sess->sess_id); 555189251Ssam if (code == RADIUS_CODE_ACCESS_CHALLENGE && 556189251Ssam !radius_msg_add_attr(msg, RADIUS_ATTR_STATE, 557189251Ssam (u8 *) &sess_id, sizeof(sess_id))) { 558189251Ssam RADIUS_DEBUG("Failed to add State attribute"); 559189251Ssam } 560189251Ssam 561189251Ssam if (sess->eap_if->eapReqData && 562189251Ssam !radius_msg_add_eap(msg, wpabuf_head(sess->eap_if->eapReqData), 563189251Ssam wpabuf_len(sess->eap_if->eapReqData))) { 564189251Ssam RADIUS_DEBUG("Failed to add EAP-Message attribute"); 565189251Ssam } 566189251Ssam 567189251Ssam if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) { 568189251Ssam int len; 569189251Ssam if (sess->eap_if->eapKeyDataLen > 64) { 570189251Ssam len = 32; 571189251Ssam } else { 572189251Ssam len = sess->eap_if->eapKeyDataLen / 2; 573189251Ssam } 574214734Srpaulo if (!radius_msg_add_mppe_keys(msg, hdr->authenticator, 575189251Ssam (u8 *) client->shared_secret, 576189251Ssam client->shared_secret_len, 577189251Ssam sess->eap_if->eapKeyData + len, 578189251Ssam len, sess->eap_if->eapKeyData, 579189251Ssam len)) { 580189251Ssam RADIUS_DEBUG("Failed to add MPPE key attributes"); 581189251Ssam } 582189251Ssam } 583189251Ssam 584189251Ssam if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { 585189251Ssam RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); 586189251Ssam radius_msg_free(msg); 587189251Ssam return NULL; 588189251Ssam } 589189251Ssam 590189251Ssam if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, 591189251Ssam client->shared_secret_len, 592214734Srpaulo hdr->authenticator) < 0) { 593189251Ssam RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); 594189251Ssam } 595189251Ssam 596189251Ssam return msg; 597189251Ssam} 598189251Ssam 599189251Ssam 600189251Ssamstatic int radius_server_reject(struct radius_server_data *data, 601189251Ssam struct radius_client *client, 602189251Ssam struct radius_msg *request, 603189251Ssam struct sockaddr *from, socklen_t fromlen, 604189251Ssam const char *from_addr, int from_port) 605189251Ssam{ 606189251Ssam struct radius_msg *msg; 607189251Ssam int ret = 0; 608189251Ssam struct eap_hdr eapfail; 609214734Srpaulo struct wpabuf *buf; 610214734Srpaulo struct radius_hdr *hdr = radius_msg_get_hdr(request); 611189251Ssam 612189251Ssam RADIUS_DEBUG("Reject invalid request from %s:%d", 613189251Ssam from_addr, from_port); 614189251Ssam 615214734Srpaulo msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, hdr->identifier); 616189251Ssam if (msg == NULL) { 617189251Ssam return -1; 618189251Ssam } 619189251Ssam 620189251Ssam os_memset(&eapfail, 0, sizeof(eapfail)); 621189251Ssam eapfail.code = EAP_CODE_FAILURE; 622189251Ssam eapfail.identifier = 0; 623189251Ssam eapfail.length = host_to_be16(sizeof(eapfail)); 624189251Ssam 625189251Ssam if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) { 626189251Ssam RADIUS_DEBUG("Failed to add EAP-Message attribute"); 627189251Ssam } 628189251Ssam 629189251Ssam if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { 630189251Ssam RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); 631189251Ssam radius_msg_free(msg); 632189251Ssam return -1; 633189251Ssam } 634189251Ssam 635189251Ssam if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, 636189251Ssam client->shared_secret_len, 637214734Srpaulo hdr->authenticator) < 638214734Srpaulo 0) { 639189251Ssam RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); 640189251Ssam } 641189251Ssam 642189251Ssam if (wpa_debug_level <= MSG_MSGDUMP) { 643189251Ssam radius_msg_dump(msg); 644189251Ssam } 645189251Ssam 646189251Ssam data->counters.access_rejects++; 647189251Ssam client->counters.access_rejects++; 648214734Srpaulo buf = radius_msg_get_buf(msg); 649214734Srpaulo if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0, 650189251Ssam (struct sockaddr *) from, sizeof(*from)) < 0) { 651189251Ssam perror("sendto[RADIUS SRV]"); 652189251Ssam ret = -1; 653189251Ssam } 654189251Ssam 655189251Ssam radius_msg_free(msg); 656189251Ssam 657189251Ssam return ret; 658189251Ssam} 659189251Ssam 660189251Ssam 661189251Ssamstatic int radius_server_request(struct radius_server_data *data, 662189251Ssam struct radius_msg *msg, 663189251Ssam struct sockaddr *from, socklen_t fromlen, 664189251Ssam struct radius_client *client, 665189251Ssam const char *from_addr, int from_port, 666189251Ssam struct radius_session *force_sess) 667189251Ssam{ 668189251Ssam u8 *eap = NULL; 669189251Ssam size_t eap_len; 670189251Ssam int res, state_included = 0; 671189251Ssam u8 statebuf[4]; 672189251Ssam unsigned int state; 673189251Ssam struct radius_session *sess; 674189251Ssam struct radius_msg *reply; 675209158Srpaulo int is_complete = 0; 676189251Ssam 677189251Ssam if (force_sess) 678189251Ssam sess = force_sess; 679189251Ssam else { 680189251Ssam res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf, 681189251Ssam sizeof(statebuf)); 682189251Ssam state_included = res >= 0; 683189251Ssam if (res == sizeof(statebuf)) { 684189251Ssam state = WPA_GET_BE32(statebuf); 685189251Ssam sess = radius_server_get_session(client, state); 686189251Ssam } else { 687189251Ssam sess = NULL; 688189251Ssam } 689189251Ssam } 690189251Ssam 691189251Ssam if (sess) { 692189251Ssam RADIUS_DEBUG("Request for session 0x%x", sess->sess_id); 693189251Ssam } else if (state_included) { 694189251Ssam RADIUS_DEBUG("State attribute included but no session found"); 695189251Ssam radius_server_reject(data, client, msg, from, fromlen, 696189251Ssam from_addr, from_port); 697189251Ssam return -1; 698189251Ssam } else { 699189251Ssam sess = radius_server_get_new_session(data, client, msg); 700189251Ssam if (sess == NULL) { 701189251Ssam RADIUS_DEBUG("Could not create a new session"); 702189251Ssam radius_server_reject(data, client, msg, from, fromlen, 703189251Ssam from_addr, from_port); 704189251Ssam return -1; 705189251Ssam } 706189251Ssam } 707189251Ssam 708189251Ssam if (sess->last_from_port == from_port && 709214734Srpaulo sess->last_identifier == radius_msg_get_hdr(msg)->identifier && 710214734Srpaulo os_memcmp(sess->last_authenticator, 711214734Srpaulo radius_msg_get_hdr(msg)->authenticator, 16) == 0) { 712189251Ssam RADIUS_DEBUG("Duplicate message from %s", from_addr); 713189251Ssam data->counters.dup_access_requests++; 714189251Ssam client->counters.dup_access_requests++; 715189251Ssam 716189251Ssam if (sess->last_reply) { 717214734Srpaulo struct wpabuf *buf; 718214734Srpaulo buf = radius_msg_get_buf(sess->last_reply); 719214734Srpaulo res = sendto(data->auth_sock, wpabuf_head(buf), 720214734Srpaulo wpabuf_len(buf), 0, 721189251Ssam (struct sockaddr *) from, fromlen); 722189251Ssam if (res < 0) { 723189251Ssam perror("sendto[RADIUS SRV]"); 724189251Ssam } 725189251Ssam return 0; 726189251Ssam } 727189251Ssam 728189251Ssam RADIUS_DEBUG("No previous reply available for duplicate " 729189251Ssam "message"); 730189251Ssam return -1; 731189251Ssam } 732189251Ssam 733189251Ssam eap = radius_msg_get_eap(msg, &eap_len); 734189251Ssam if (eap == NULL) { 735189251Ssam RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", 736189251Ssam from_addr); 737189251Ssam data->counters.packets_dropped++; 738189251Ssam client->counters.packets_dropped++; 739189251Ssam return -1; 740189251Ssam } 741189251Ssam 742189251Ssam RADIUS_DUMP("Received EAP data", eap, eap_len); 743189251Ssam 744189251Ssam /* FIX: if Code is Request, Success, or Failure, send Access-Reject; 745189251Ssam * RFC3579 Sect. 2.6.2. 746189251Ssam * Include EAP-Response/Nak with no preferred method if 747189251Ssam * code == request. 748189251Ssam * If code is not 1-4, discard the packet silently. 749189251Ssam * Or is this already done by the EAP state machine? */ 750189251Ssam 751189251Ssam wpabuf_free(sess->eap_if->eapRespData); 752189251Ssam sess->eap_if->eapRespData = wpabuf_alloc_ext_data(eap, eap_len); 753189251Ssam if (sess->eap_if->eapRespData == NULL) 754189251Ssam os_free(eap); 755189251Ssam eap = NULL; 756189251Ssam sess->eap_if->eapResp = TRUE; 757189251Ssam eap_server_sm_step(sess->eap); 758189251Ssam 759189251Ssam if ((sess->eap_if->eapReq || sess->eap_if->eapSuccess || 760189251Ssam sess->eap_if->eapFail) && sess->eap_if->eapReqData) { 761189251Ssam RADIUS_DUMP("EAP data from the state machine", 762189251Ssam wpabuf_head(sess->eap_if->eapReqData), 763189251Ssam wpabuf_len(sess->eap_if->eapReqData)); 764189251Ssam } else if (sess->eap_if->eapFail) { 765189251Ssam RADIUS_DEBUG("No EAP data from the state machine, but eapFail " 766189251Ssam "set"); 767189251Ssam } else if (eap_sm_method_pending(sess->eap)) { 768214734Srpaulo radius_msg_free(sess->last_msg); 769189251Ssam sess->last_msg = msg; 770189251Ssam sess->last_from_port = from_port; 771189251Ssam os_free(sess->last_from_addr); 772189251Ssam sess->last_from_addr = os_strdup(from_addr); 773189251Ssam sess->last_fromlen = fromlen; 774189251Ssam os_memcpy(&sess->last_from, from, fromlen); 775189251Ssam return -2; 776189251Ssam } else { 777189251Ssam RADIUS_DEBUG("No EAP data from the state machine - ignore this" 778189251Ssam " Access-Request silently (assuming it was a " 779189251Ssam "duplicate)"); 780189251Ssam data->counters.packets_dropped++; 781189251Ssam client->counters.packets_dropped++; 782189251Ssam return -1; 783189251Ssam } 784189251Ssam 785209158Srpaulo if (sess->eap_if->eapSuccess || sess->eap_if->eapFail) 786209158Srpaulo is_complete = 1; 787209158Srpaulo 788189251Ssam reply = radius_server_encapsulate_eap(data, client, sess, msg); 789189251Ssam 790189251Ssam if (reply) { 791214734Srpaulo struct wpabuf *buf; 792214734Srpaulo struct radius_hdr *hdr; 793214734Srpaulo 794189251Ssam RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port); 795189251Ssam if (wpa_debug_level <= MSG_MSGDUMP) { 796189251Ssam radius_msg_dump(reply); 797189251Ssam } 798189251Ssam 799214734Srpaulo switch (radius_msg_get_hdr(reply)->code) { 800189251Ssam case RADIUS_CODE_ACCESS_ACCEPT: 801189251Ssam data->counters.access_accepts++; 802189251Ssam client->counters.access_accepts++; 803189251Ssam break; 804189251Ssam case RADIUS_CODE_ACCESS_REJECT: 805189251Ssam data->counters.access_rejects++; 806189251Ssam client->counters.access_rejects++; 807189251Ssam break; 808189251Ssam case RADIUS_CODE_ACCESS_CHALLENGE: 809189251Ssam data->counters.access_challenges++; 810189251Ssam client->counters.access_challenges++; 811189251Ssam break; 812189251Ssam } 813214734Srpaulo buf = radius_msg_get_buf(reply); 814214734Srpaulo res = sendto(data->auth_sock, wpabuf_head(buf), 815214734Srpaulo wpabuf_len(buf), 0, 816189251Ssam (struct sockaddr *) from, fromlen); 817189251Ssam if (res < 0) { 818189251Ssam perror("sendto[RADIUS SRV]"); 819189251Ssam } 820214734Srpaulo radius_msg_free(sess->last_reply); 821189251Ssam sess->last_reply = reply; 822189251Ssam sess->last_from_port = from_port; 823214734Srpaulo hdr = radius_msg_get_hdr(msg); 824214734Srpaulo sess->last_identifier = hdr->identifier; 825214734Srpaulo os_memcpy(sess->last_authenticator, hdr->authenticator, 16); 826189251Ssam } else { 827189251Ssam data->counters.packets_dropped++; 828189251Ssam client->counters.packets_dropped++; 829189251Ssam } 830189251Ssam 831209158Srpaulo if (is_complete) { 832189251Ssam RADIUS_DEBUG("Removing completed session 0x%x after timeout", 833189251Ssam sess->sess_id); 834189251Ssam eloop_cancel_timeout(radius_server_session_remove_timeout, 835189251Ssam data, sess); 836189251Ssam eloop_register_timeout(10, 0, 837189251Ssam radius_server_session_remove_timeout, 838189251Ssam data, sess); 839189251Ssam } 840189251Ssam 841189251Ssam return 0; 842189251Ssam} 843189251Ssam 844189251Ssam 845189251Ssamstatic void radius_server_receive_auth(int sock, void *eloop_ctx, 846189251Ssam void *sock_ctx) 847189251Ssam{ 848189251Ssam struct radius_server_data *data = eloop_ctx; 849189251Ssam u8 *buf = NULL; 850209158Srpaulo union { 851209158Srpaulo struct sockaddr_storage ss; 852209158Srpaulo struct sockaddr_in sin; 853209158Srpaulo#ifdef CONFIG_IPV6 854209158Srpaulo struct sockaddr_in6 sin6; 855209158Srpaulo#endif /* CONFIG_IPV6 */ 856209158Srpaulo } from; 857189251Ssam socklen_t fromlen; 858189251Ssam int len; 859189251Ssam struct radius_client *client = NULL; 860189251Ssam struct radius_msg *msg = NULL; 861189251Ssam char abuf[50]; 862189251Ssam int from_port = 0; 863189251Ssam 864189251Ssam buf = os_malloc(RADIUS_MAX_MSG_LEN); 865189251Ssam if (buf == NULL) { 866189251Ssam goto fail; 867189251Ssam } 868189251Ssam 869189251Ssam fromlen = sizeof(from); 870189251Ssam len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0, 871209158Srpaulo (struct sockaddr *) &from.ss, &fromlen); 872189251Ssam if (len < 0) { 873189251Ssam perror("recvfrom[radius_server]"); 874189251Ssam goto fail; 875189251Ssam } 876189251Ssam 877189251Ssam#ifdef CONFIG_IPV6 878189251Ssam if (data->ipv6) { 879209158Srpaulo if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf, 880209158Srpaulo sizeof(abuf)) == NULL) 881189251Ssam abuf[0] = '\0'; 882209158Srpaulo from_port = ntohs(from.sin6.sin6_port); 883189251Ssam RADIUS_DEBUG("Received %d bytes from %s:%d", 884189251Ssam len, abuf, from_port); 885189251Ssam 886189251Ssam client = radius_server_get_client(data, 887189251Ssam (struct in_addr *) 888209158Srpaulo &from.sin6.sin6_addr, 1); 889189251Ssam } 890189251Ssam#endif /* CONFIG_IPV6 */ 891189251Ssam 892189251Ssam if (!data->ipv6) { 893209158Srpaulo os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf)); 894209158Srpaulo from_port = ntohs(from.sin.sin_port); 895189251Ssam RADIUS_DEBUG("Received %d bytes from %s:%d", 896189251Ssam len, abuf, from_port); 897189251Ssam 898209158Srpaulo client = radius_server_get_client(data, &from.sin.sin_addr, 0); 899189251Ssam } 900189251Ssam 901189251Ssam RADIUS_DUMP("Received data", buf, len); 902189251Ssam 903189251Ssam if (client == NULL) { 904189251Ssam RADIUS_DEBUG("Unknown client %s - packet ignored", abuf); 905189251Ssam data->counters.invalid_requests++; 906189251Ssam goto fail; 907189251Ssam } 908189251Ssam 909189251Ssam msg = radius_msg_parse(buf, len); 910189251Ssam if (msg == NULL) { 911189251Ssam RADIUS_DEBUG("Parsing incoming RADIUS frame failed"); 912189251Ssam data->counters.malformed_access_requests++; 913189251Ssam client->counters.malformed_access_requests++; 914189251Ssam goto fail; 915189251Ssam } 916189251Ssam 917189251Ssam os_free(buf); 918189251Ssam buf = NULL; 919189251Ssam 920189251Ssam if (wpa_debug_level <= MSG_MSGDUMP) { 921189251Ssam radius_msg_dump(msg); 922189251Ssam } 923189251Ssam 924214734Srpaulo if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCESS_REQUEST) { 925214734Srpaulo RADIUS_DEBUG("Unexpected RADIUS code %d", 926214734Srpaulo radius_msg_get_hdr(msg)->code); 927189251Ssam data->counters.unknown_types++; 928189251Ssam client->counters.unknown_types++; 929189251Ssam goto fail; 930189251Ssam } 931189251Ssam 932189251Ssam data->counters.access_requests++; 933189251Ssam client->counters.access_requests++; 934189251Ssam 935189251Ssam if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret, 936189251Ssam client->shared_secret_len, NULL)) { 937189251Ssam RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf); 938189251Ssam data->counters.bad_authenticators++; 939189251Ssam client->counters.bad_authenticators++; 940189251Ssam goto fail; 941189251Ssam } 942189251Ssam 943189251Ssam if (radius_server_request(data, msg, (struct sockaddr *) &from, 944189251Ssam fromlen, client, abuf, from_port, NULL) == 945189251Ssam -2) 946189251Ssam return; /* msg was stored with the session */ 947189251Ssam 948189251Ssamfail: 949214734Srpaulo radius_msg_free(msg); 950189251Ssam os_free(buf); 951189251Ssam} 952189251Ssam 953189251Ssam 954209158Srpaulostatic int radius_server_disable_pmtu_discovery(int s) 955209158Srpaulo{ 956209158Srpaulo int r = -1; 957209158Srpaulo#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) 958209158Srpaulo /* Turn off Path MTU discovery on IPv4/UDP sockets. */ 959209158Srpaulo int action = IP_PMTUDISC_DONT; 960209158Srpaulo r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, 961209158Srpaulo sizeof(action)); 962209158Srpaulo if (r == -1) 963209158Srpaulo wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: " 964209158Srpaulo "%s", strerror(errno)); 965209158Srpaulo#endif 966209158Srpaulo return r; 967209158Srpaulo} 968209158Srpaulo 969209158Srpaulo 970189251Ssamstatic int radius_server_open_socket(int port) 971189251Ssam{ 972189251Ssam int s; 973189251Ssam struct sockaddr_in addr; 974189251Ssam 975189251Ssam s = socket(PF_INET, SOCK_DGRAM, 0); 976189251Ssam if (s < 0) { 977189251Ssam perror("socket"); 978189251Ssam return -1; 979189251Ssam } 980189251Ssam 981209158Srpaulo radius_server_disable_pmtu_discovery(s); 982209158Srpaulo 983189251Ssam os_memset(&addr, 0, sizeof(addr)); 984189251Ssam addr.sin_family = AF_INET; 985189251Ssam addr.sin_port = htons(port); 986189251Ssam if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 987189251Ssam perror("bind"); 988189251Ssam close(s); 989189251Ssam return -1; 990189251Ssam } 991189251Ssam 992189251Ssam return s; 993189251Ssam} 994189251Ssam 995189251Ssam 996189251Ssam#ifdef CONFIG_IPV6 997189251Ssamstatic int radius_server_open_socket6(int port) 998189251Ssam{ 999189251Ssam int s; 1000189251Ssam struct sockaddr_in6 addr; 1001189251Ssam 1002189251Ssam s = socket(PF_INET6, SOCK_DGRAM, 0); 1003189251Ssam if (s < 0) { 1004189251Ssam perror("socket[IPv6]"); 1005189251Ssam return -1; 1006189251Ssam } 1007189251Ssam 1008189251Ssam os_memset(&addr, 0, sizeof(addr)); 1009189251Ssam addr.sin6_family = AF_INET6; 1010189251Ssam os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any)); 1011189251Ssam addr.sin6_port = htons(port); 1012189251Ssam if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 1013189251Ssam perror("bind"); 1014189251Ssam close(s); 1015189251Ssam return -1; 1016189251Ssam } 1017189251Ssam 1018189251Ssam return s; 1019189251Ssam} 1020189251Ssam#endif /* CONFIG_IPV6 */ 1021189251Ssam 1022189251Ssam 1023189251Ssamstatic void radius_server_free_sessions(struct radius_server_data *data, 1024189251Ssam struct radius_session *sessions) 1025189251Ssam{ 1026189251Ssam struct radius_session *session, *prev; 1027189251Ssam 1028189251Ssam session = sessions; 1029189251Ssam while (session) { 1030189251Ssam prev = session; 1031189251Ssam session = session->next; 1032189251Ssam radius_server_session_free(data, prev); 1033189251Ssam } 1034189251Ssam} 1035189251Ssam 1036189251Ssam 1037189251Ssamstatic void radius_server_free_clients(struct radius_server_data *data, 1038189251Ssam struct radius_client *clients) 1039189251Ssam{ 1040189251Ssam struct radius_client *client, *prev; 1041189251Ssam 1042189251Ssam client = clients; 1043189251Ssam while (client) { 1044189251Ssam prev = client; 1045189251Ssam client = client->next; 1046189251Ssam 1047189251Ssam radius_server_free_sessions(data, prev->sessions); 1048189251Ssam os_free(prev->shared_secret); 1049189251Ssam os_free(prev); 1050189251Ssam } 1051189251Ssam} 1052189251Ssam 1053189251Ssam 1054189251Ssamstatic struct radius_client * 1055189251Ssamradius_server_read_clients(const char *client_file, int ipv6) 1056189251Ssam{ 1057189251Ssam FILE *f; 1058189251Ssam const int buf_size = 1024; 1059189251Ssam char *buf, *pos; 1060189251Ssam struct radius_client *clients, *tail, *entry; 1061189251Ssam int line = 0, mask, failed = 0, i; 1062189251Ssam struct in_addr addr; 1063189251Ssam#ifdef CONFIG_IPV6 1064189251Ssam struct in6_addr addr6; 1065189251Ssam#endif /* CONFIG_IPV6 */ 1066189251Ssam unsigned int val; 1067189251Ssam 1068189251Ssam f = fopen(client_file, "r"); 1069189251Ssam if (f == NULL) { 1070189251Ssam RADIUS_ERROR("Could not open client file '%s'", client_file); 1071189251Ssam return NULL; 1072189251Ssam } 1073189251Ssam 1074189251Ssam buf = os_malloc(buf_size); 1075189251Ssam if (buf == NULL) { 1076189251Ssam fclose(f); 1077189251Ssam return NULL; 1078189251Ssam } 1079189251Ssam 1080189251Ssam clients = tail = NULL; 1081189251Ssam while (fgets(buf, buf_size, f)) { 1082189251Ssam /* Configuration file format: 1083189251Ssam * 192.168.1.0/24 secret 1084189251Ssam * 192.168.1.2 secret 1085189251Ssam * fe80::211:22ff:fe33:4455/64 secretipv6 1086189251Ssam */ 1087189251Ssam line++; 1088189251Ssam buf[buf_size - 1] = '\0'; 1089189251Ssam pos = buf; 1090189251Ssam while (*pos != '\0' && *pos != '\n') 1091189251Ssam pos++; 1092189251Ssam if (*pos == '\n') 1093189251Ssam *pos = '\0'; 1094189251Ssam if (*buf == '\0' || *buf == '#') 1095189251Ssam continue; 1096189251Ssam 1097189251Ssam pos = buf; 1098189251Ssam while ((*pos >= '0' && *pos <= '9') || *pos == '.' || 1099189251Ssam (*pos >= 'a' && *pos <= 'f') || *pos == ':' || 1100189251Ssam (*pos >= 'A' && *pos <= 'F')) { 1101189251Ssam pos++; 1102189251Ssam } 1103189251Ssam 1104189251Ssam if (*pos == '\0') { 1105189251Ssam failed = 1; 1106189251Ssam break; 1107189251Ssam } 1108189251Ssam 1109189251Ssam if (*pos == '/') { 1110189251Ssam char *end; 1111189251Ssam *pos++ = '\0'; 1112189251Ssam mask = strtol(pos, &end, 10); 1113189251Ssam if ((pos == end) || 1114189251Ssam (mask < 0 || mask > (ipv6 ? 128 : 32))) { 1115189251Ssam failed = 1; 1116189251Ssam break; 1117189251Ssam } 1118189251Ssam pos = end; 1119189251Ssam } else { 1120189251Ssam mask = ipv6 ? 128 : 32; 1121189251Ssam *pos++ = '\0'; 1122189251Ssam } 1123189251Ssam 1124189251Ssam if (!ipv6 && inet_aton(buf, &addr) == 0) { 1125189251Ssam failed = 1; 1126189251Ssam break; 1127189251Ssam } 1128189251Ssam#ifdef CONFIG_IPV6 1129189251Ssam if (ipv6 && inet_pton(AF_INET6, buf, &addr6) <= 0) { 1130189251Ssam if (inet_pton(AF_INET, buf, &addr) <= 0) { 1131189251Ssam failed = 1; 1132189251Ssam break; 1133189251Ssam } 1134189251Ssam /* Convert IPv4 address to IPv6 */ 1135189251Ssam if (mask <= 32) 1136189251Ssam mask += (128 - 32); 1137189251Ssam os_memset(addr6.s6_addr, 0, 10); 1138189251Ssam addr6.s6_addr[10] = 0xff; 1139189251Ssam addr6.s6_addr[11] = 0xff; 1140189251Ssam os_memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr, 1141189251Ssam 4); 1142189251Ssam } 1143189251Ssam#endif /* CONFIG_IPV6 */ 1144189251Ssam 1145189251Ssam while (*pos == ' ' || *pos == '\t') { 1146189251Ssam pos++; 1147189251Ssam } 1148189251Ssam 1149189251Ssam if (*pos == '\0') { 1150189251Ssam failed = 1; 1151189251Ssam break; 1152189251Ssam } 1153189251Ssam 1154189251Ssam entry = os_zalloc(sizeof(*entry)); 1155189251Ssam if (entry == NULL) { 1156189251Ssam failed = 1; 1157189251Ssam break; 1158189251Ssam } 1159189251Ssam entry->shared_secret = os_strdup(pos); 1160189251Ssam if (entry->shared_secret == NULL) { 1161189251Ssam failed = 1; 1162189251Ssam os_free(entry); 1163189251Ssam break; 1164189251Ssam } 1165189251Ssam entry->shared_secret_len = os_strlen(entry->shared_secret); 1166189251Ssam entry->addr.s_addr = addr.s_addr; 1167189251Ssam if (!ipv6) { 1168189251Ssam val = 0; 1169189251Ssam for (i = 0; i < mask; i++) 1170189251Ssam val |= 1 << (31 - i); 1171189251Ssam entry->mask.s_addr = htonl(val); 1172189251Ssam } 1173189251Ssam#ifdef CONFIG_IPV6 1174189251Ssam if (ipv6) { 1175189251Ssam int offset = mask / 8; 1176189251Ssam 1177189251Ssam os_memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16); 1178189251Ssam os_memset(entry->mask6.s6_addr, 0xff, offset); 1179189251Ssam val = 0; 1180189251Ssam for (i = 0; i < (mask % 8); i++) 1181189251Ssam val |= 1 << (7 - i); 1182189251Ssam if (offset < 16) 1183189251Ssam entry->mask6.s6_addr[offset] = val; 1184189251Ssam } 1185189251Ssam#endif /* CONFIG_IPV6 */ 1186189251Ssam 1187189251Ssam if (tail == NULL) { 1188189251Ssam clients = tail = entry; 1189189251Ssam } else { 1190189251Ssam tail->next = entry; 1191189251Ssam tail = entry; 1192189251Ssam } 1193189251Ssam } 1194189251Ssam 1195189251Ssam if (failed) { 1196189251Ssam RADIUS_ERROR("Invalid line %d in '%s'", line, client_file); 1197189251Ssam radius_server_free_clients(NULL, clients); 1198189251Ssam clients = NULL; 1199189251Ssam } 1200189251Ssam 1201189251Ssam os_free(buf); 1202189251Ssam fclose(f); 1203189251Ssam 1204189251Ssam return clients; 1205189251Ssam} 1206189251Ssam 1207189251Ssam 1208214734Srpaulo/** 1209214734Srpaulo * radius_server_init - Initialize RADIUS server 1210214734Srpaulo * @conf: Configuration for the RADIUS server 1211214734Srpaulo * Returns: Pointer to private RADIUS server context or %NULL on failure 1212214734Srpaulo * 1213214734Srpaulo * This initializes a RADIUS server instance and returns a context pointer that 1214214734Srpaulo * will be used in other calls to the RADIUS server module. The server can be 1215214734Srpaulo * deinitialize by calling radius_server_deinit(). 1216214734Srpaulo */ 1217189251Ssamstruct radius_server_data * 1218189251Ssamradius_server_init(struct radius_server_conf *conf) 1219189251Ssam{ 1220189251Ssam struct radius_server_data *data; 1221189251Ssam 1222189251Ssam#ifndef CONFIG_IPV6 1223189251Ssam if (conf->ipv6) { 1224189251Ssam fprintf(stderr, "RADIUS server compiled without IPv6 " 1225189251Ssam "support.\n"); 1226189251Ssam return NULL; 1227189251Ssam } 1228189251Ssam#endif /* CONFIG_IPV6 */ 1229189251Ssam 1230189251Ssam data = os_zalloc(sizeof(*data)); 1231189251Ssam if (data == NULL) 1232189251Ssam return NULL; 1233189251Ssam 1234189251Ssam os_get_time(&data->start_time); 1235189251Ssam data->conf_ctx = conf->conf_ctx; 1236189251Ssam data->eap_sim_db_priv = conf->eap_sim_db_priv; 1237189251Ssam data->ssl_ctx = conf->ssl_ctx; 1238214734Srpaulo data->msg_ctx = conf->msg_ctx; 1239189251Ssam data->ipv6 = conf->ipv6; 1240189251Ssam if (conf->pac_opaque_encr_key) { 1241189251Ssam data->pac_opaque_encr_key = os_malloc(16); 1242189251Ssam os_memcpy(data->pac_opaque_encr_key, conf->pac_opaque_encr_key, 1243189251Ssam 16); 1244189251Ssam } 1245189251Ssam if (conf->eap_fast_a_id) { 1246189251Ssam data->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len); 1247189251Ssam if (data->eap_fast_a_id) { 1248189251Ssam os_memcpy(data->eap_fast_a_id, conf->eap_fast_a_id, 1249189251Ssam conf->eap_fast_a_id_len); 1250189251Ssam data->eap_fast_a_id_len = conf->eap_fast_a_id_len; 1251189251Ssam } 1252189251Ssam } 1253189251Ssam if (conf->eap_fast_a_id_info) 1254189251Ssam data->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info); 1255189251Ssam data->eap_fast_prov = conf->eap_fast_prov; 1256189251Ssam data->pac_key_lifetime = conf->pac_key_lifetime; 1257189251Ssam data->pac_key_refresh_time = conf->pac_key_refresh_time; 1258189251Ssam data->get_eap_user = conf->get_eap_user; 1259189251Ssam data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; 1260189251Ssam data->tnc = conf->tnc; 1261189251Ssam data->wps = conf->wps; 1262189251Ssam if (conf->eap_req_id_text) { 1263189251Ssam data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len); 1264189251Ssam if (data->eap_req_id_text) { 1265189251Ssam os_memcpy(data->eap_req_id_text, conf->eap_req_id_text, 1266189251Ssam conf->eap_req_id_text_len); 1267189251Ssam data->eap_req_id_text_len = conf->eap_req_id_text_len; 1268189251Ssam } 1269189251Ssam } 1270189251Ssam 1271189251Ssam data->clients = radius_server_read_clients(conf->client_file, 1272189251Ssam conf->ipv6); 1273189251Ssam if (data->clients == NULL) { 1274189251Ssam printf("No RADIUS clients configured.\n"); 1275189251Ssam radius_server_deinit(data); 1276189251Ssam return NULL; 1277189251Ssam } 1278189251Ssam 1279189251Ssam#ifdef CONFIG_IPV6 1280189251Ssam if (conf->ipv6) 1281189251Ssam data->auth_sock = radius_server_open_socket6(conf->auth_port); 1282189251Ssam else 1283189251Ssam#endif /* CONFIG_IPV6 */ 1284189251Ssam data->auth_sock = radius_server_open_socket(conf->auth_port); 1285189251Ssam if (data->auth_sock < 0) { 1286189251Ssam printf("Failed to open UDP socket for RADIUS authentication " 1287189251Ssam "server\n"); 1288189251Ssam radius_server_deinit(data); 1289189251Ssam return NULL; 1290189251Ssam } 1291189251Ssam if (eloop_register_read_sock(data->auth_sock, 1292189251Ssam radius_server_receive_auth, 1293189251Ssam data, NULL)) { 1294189251Ssam radius_server_deinit(data); 1295189251Ssam return NULL; 1296189251Ssam } 1297189251Ssam 1298189251Ssam return data; 1299189251Ssam} 1300189251Ssam 1301189251Ssam 1302214734Srpaulo/** 1303214734Srpaulo * radius_server_deinit - Deinitialize RADIUS server 1304214734Srpaulo * @data: RADIUS server context from radius_server_init() 1305214734Srpaulo */ 1306189251Ssamvoid radius_server_deinit(struct radius_server_data *data) 1307189251Ssam{ 1308189251Ssam if (data == NULL) 1309189251Ssam return; 1310189251Ssam 1311189251Ssam if (data->auth_sock >= 0) { 1312189251Ssam eloop_unregister_read_sock(data->auth_sock); 1313189251Ssam close(data->auth_sock); 1314189251Ssam } 1315189251Ssam 1316189251Ssam radius_server_free_clients(data, data->clients); 1317189251Ssam 1318189251Ssam os_free(data->pac_opaque_encr_key); 1319189251Ssam os_free(data->eap_fast_a_id); 1320189251Ssam os_free(data->eap_fast_a_id_info); 1321189251Ssam os_free(data->eap_req_id_text); 1322189251Ssam os_free(data); 1323189251Ssam} 1324189251Ssam 1325189251Ssam 1326214734Srpaulo/** 1327214734Srpaulo * radius_server_get_mib - Get RADIUS server MIB information 1328214734Srpaulo * @data: RADIUS server context from radius_server_init() 1329214734Srpaulo * @buf: Buffer for returning the MIB data in text format 1330214734Srpaulo * @buflen: buf length in octets 1331214734Srpaulo * Returns: Number of octets written into buf 1332214734Srpaulo */ 1333189251Ssamint radius_server_get_mib(struct radius_server_data *data, char *buf, 1334189251Ssam size_t buflen) 1335189251Ssam{ 1336189251Ssam int ret, uptime; 1337189251Ssam unsigned int idx; 1338189251Ssam char *end, *pos; 1339189251Ssam struct os_time now; 1340189251Ssam struct radius_client *cli; 1341189251Ssam 1342189251Ssam /* RFC 2619 - RADIUS Authentication Server MIB */ 1343189251Ssam 1344189251Ssam if (data == NULL || buflen == 0) 1345189251Ssam return 0; 1346189251Ssam 1347189251Ssam pos = buf; 1348189251Ssam end = buf + buflen; 1349189251Ssam 1350189251Ssam os_get_time(&now); 1351189251Ssam uptime = (now.sec - data->start_time.sec) * 100 + 1352189251Ssam ((now.usec - data->start_time.usec) / 10000) % 100; 1353189251Ssam ret = os_snprintf(pos, end - pos, 1354189251Ssam "RADIUS-AUTH-SERVER-MIB\n" 1355189251Ssam "radiusAuthServIdent=hostapd\n" 1356189251Ssam "radiusAuthServUpTime=%d\n" 1357189251Ssam "radiusAuthServResetTime=0\n" 1358189251Ssam "radiusAuthServConfigReset=4\n", 1359189251Ssam uptime); 1360189251Ssam if (ret < 0 || ret >= end - pos) { 1361189251Ssam *pos = '\0'; 1362189251Ssam return pos - buf; 1363189251Ssam } 1364189251Ssam pos += ret; 1365189251Ssam 1366189251Ssam ret = os_snprintf(pos, end - pos, 1367189251Ssam "radiusAuthServTotalAccessRequests=%u\n" 1368189251Ssam "radiusAuthServTotalInvalidRequests=%u\n" 1369189251Ssam "radiusAuthServTotalDupAccessRequests=%u\n" 1370189251Ssam "radiusAuthServTotalAccessAccepts=%u\n" 1371189251Ssam "radiusAuthServTotalAccessRejects=%u\n" 1372189251Ssam "radiusAuthServTotalAccessChallenges=%u\n" 1373189251Ssam "radiusAuthServTotalMalformedAccessRequests=%u\n" 1374189251Ssam "radiusAuthServTotalBadAuthenticators=%u\n" 1375189251Ssam "radiusAuthServTotalPacketsDropped=%u\n" 1376189251Ssam "radiusAuthServTotalUnknownTypes=%u\n", 1377189251Ssam data->counters.access_requests, 1378189251Ssam data->counters.invalid_requests, 1379189251Ssam data->counters.dup_access_requests, 1380189251Ssam data->counters.access_accepts, 1381189251Ssam data->counters.access_rejects, 1382189251Ssam data->counters.access_challenges, 1383189251Ssam data->counters.malformed_access_requests, 1384189251Ssam data->counters.bad_authenticators, 1385189251Ssam data->counters.packets_dropped, 1386189251Ssam data->counters.unknown_types); 1387189251Ssam if (ret < 0 || ret >= end - pos) { 1388189251Ssam *pos = '\0'; 1389189251Ssam return pos - buf; 1390189251Ssam } 1391189251Ssam pos += ret; 1392189251Ssam 1393189251Ssam for (cli = data->clients, idx = 0; cli; cli = cli->next, idx++) { 1394189251Ssam char abuf[50], mbuf[50]; 1395189251Ssam#ifdef CONFIG_IPV6 1396189251Ssam if (data->ipv6) { 1397189251Ssam if (inet_ntop(AF_INET6, &cli->addr6, abuf, 1398189251Ssam sizeof(abuf)) == NULL) 1399189251Ssam abuf[0] = '\0'; 1400189251Ssam if (inet_ntop(AF_INET6, &cli->mask6, abuf, 1401189251Ssam sizeof(mbuf)) == NULL) 1402189251Ssam mbuf[0] = '\0'; 1403189251Ssam } 1404189251Ssam#endif /* CONFIG_IPV6 */ 1405189251Ssam if (!data->ipv6) { 1406189251Ssam os_strlcpy(abuf, inet_ntoa(cli->addr), sizeof(abuf)); 1407189251Ssam os_strlcpy(mbuf, inet_ntoa(cli->mask), sizeof(mbuf)); 1408189251Ssam } 1409189251Ssam 1410189251Ssam ret = os_snprintf(pos, end - pos, 1411189251Ssam "radiusAuthClientIndex=%u\n" 1412189251Ssam "radiusAuthClientAddress=%s/%s\n" 1413189251Ssam "radiusAuthServAccessRequests=%u\n" 1414189251Ssam "radiusAuthServDupAccessRequests=%u\n" 1415189251Ssam "radiusAuthServAccessAccepts=%u\n" 1416189251Ssam "radiusAuthServAccessRejects=%u\n" 1417189251Ssam "radiusAuthServAccessChallenges=%u\n" 1418189251Ssam "radiusAuthServMalformedAccessRequests=%u\n" 1419189251Ssam "radiusAuthServBadAuthenticators=%u\n" 1420189251Ssam "radiusAuthServPacketsDropped=%u\n" 1421189251Ssam "radiusAuthServUnknownTypes=%u\n", 1422189251Ssam idx, 1423189251Ssam abuf, mbuf, 1424189251Ssam cli->counters.access_requests, 1425189251Ssam cli->counters.dup_access_requests, 1426189251Ssam cli->counters.access_accepts, 1427189251Ssam cli->counters.access_rejects, 1428189251Ssam cli->counters.access_challenges, 1429189251Ssam cli->counters.malformed_access_requests, 1430189251Ssam cli->counters.bad_authenticators, 1431189251Ssam cli->counters.packets_dropped, 1432189251Ssam cli->counters.unknown_types); 1433189251Ssam if (ret < 0 || ret >= end - pos) { 1434189251Ssam *pos = '\0'; 1435189251Ssam return pos - buf; 1436189251Ssam } 1437189251Ssam pos += ret; 1438189251Ssam } 1439189251Ssam 1440189251Ssam return pos - buf; 1441189251Ssam} 1442189251Ssam 1443189251Ssam 1444189251Ssamstatic int radius_server_get_eap_user(void *ctx, const u8 *identity, 1445189251Ssam size_t identity_len, int phase2, 1446189251Ssam struct eap_user *user) 1447189251Ssam{ 1448189251Ssam struct radius_session *sess = ctx; 1449189251Ssam struct radius_server_data *data = sess->server; 1450189251Ssam 1451189251Ssam return data->get_eap_user(data->conf_ctx, identity, identity_len, 1452189251Ssam phase2, user); 1453189251Ssam} 1454189251Ssam 1455189251Ssam 1456189251Ssamstatic const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len) 1457189251Ssam{ 1458189251Ssam struct radius_session *sess = ctx; 1459189251Ssam struct radius_server_data *data = sess->server; 1460189251Ssam *len = data->eap_req_id_text_len; 1461189251Ssam return data->eap_req_id_text; 1462189251Ssam} 1463189251Ssam 1464189251Ssam 1465189251Ssamstatic struct eapol_callbacks radius_server_eapol_cb = 1466189251Ssam{ 1467189251Ssam .get_eap_user = radius_server_get_eap_user, 1468189251Ssam .get_eap_req_id_text = radius_server_get_eap_req_id_text, 1469189251Ssam}; 1470189251Ssam 1471189251Ssam 1472214734Srpaulo/** 1473214734Srpaulo * radius_server_eap_pending_cb - Pending EAP data notification 1474214734Srpaulo * @data: RADIUS server context from radius_server_init() 1475214734Srpaulo * @ctx: Pending EAP context pointer 1476214734Srpaulo * 1477214734Srpaulo * This function is used to notify EAP server module that a pending operation 1478214734Srpaulo * has been completed and processing of the EAP session can proceed. 1479214734Srpaulo */ 1480189251Ssamvoid radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) 1481189251Ssam{ 1482189251Ssam struct radius_client *cli; 1483189251Ssam struct radius_session *s, *sess = NULL; 1484189251Ssam struct radius_msg *msg; 1485189251Ssam 1486189251Ssam if (data == NULL) 1487189251Ssam return; 1488189251Ssam 1489189251Ssam for (cli = data->clients; cli; cli = cli->next) { 1490189251Ssam for (s = cli->sessions; s; s = s->next) { 1491189251Ssam if (s->eap == ctx && s->last_msg) { 1492189251Ssam sess = s; 1493189251Ssam break; 1494189251Ssam } 1495189251Ssam if (sess) 1496189251Ssam break; 1497189251Ssam } 1498189251Ssam if (sess) 1499189251Ssam break; 1500189251Ssam } 1501189251Ssam 1502189251Ssam if (sess == NULL) { 1503189251Ssam RADIUS_DEBUG("No session matched callback ctx"); 1504189251Ssam return; 1505189251Ssam } 1506189251Ssam 1507189251Ssam msg = sess->last_msg; 1508189251Ssam sess->last_msg = NULL; 1509189251Ssam eap_sm_pending_cb(sess->eap); 1510189251Ssam if (radius_server_request(data, msg, 1511189251Ssam (struct sockaddr *) &sess->last_from, 1512189251Ssam sess->last_fromlen, cli, 1513189251Ssam sess->last_from_addr, 1514189251Ssam sess->last_from_port, sess) == -2) 1515189251Ssam return; /* msg was stored with the session */ 1516189251Ssam 1517189251Ssam radius_msg_free(msg); 1518189251Ssam} 1519