1189251Ssam/* 2189251Ssam * EAP peer state machines (RFC 4137) 3214734Srpaulo * Copyright (c) 2004-2010, 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 * This file implements the Peer State Machine as defined in RFC 4137. The used 15189251Ssam * states and state transitions match mostly with the RFC. However, there are 16189251Ssam * couple of additional transitions for working around small issues noticed 17189251Ssam * during testing. These exceptions are explained in comments within the 18189251Ssam * functions in this file. The method functions, m.func(), are similar to the 19189251Ssam * ones used in RFC 4137, but some small changes have used here to optimize 20189251Ssam * operations and to add functionality needed for fast re-authentication 21189251Ssam * (session resumption). 22189251Ssam */ 23189251Ssam 24189251Ssam#include "includes.h" 25189251Ssam 26189251Ssam#include "common.h" 27189251Ssam#include "pcsc_funcs.h" 28189251Ssam#include "state_machine.h" 29214734Srpaulo#include "crypto/crypto.h" 30214734Srpaulo#include "crypto/tls.h" 31214734Srpaulo#include "common/wpa_ctrl.h" 32189251Ssam#include "eap_common/eap_wsc_common.h" 33214734Srpaulo#include "eap_i.h" 34214734Srpaulo#include "eap_config.h" 35189251Ssam 36189251Ssam#define STATE_MACHINE_DATA struct eap_sm 37189251Ssam#define STATE_MACHINE_DEBUG_PREFIX "EAP" 38189251Ssam 39189251Ssam#define EAP_MAX_AUTH_ROUNDS 50 40189251Ssam 41189251Ssam 42189251Ssamstatic Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, 43189251Ssam EapType method); 44189251Ssamstatic struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id); 45189251Ssamstatic void eap_sm_processIdentity(struct eap_sm *sm, 46189251Ssam const struct wpabuf *req); 47189251Ssamstatic void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req); 48189251Ssamstatic struct wpabuf * eap_sm_buildNotify(int id); 49189251Ssamstatic void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req); 50189251Ssam#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) 51189251Ssamstatic const char * eap_sm_method_state_txt(EapMethodState state); 52189251Ssamstatic const char * eap_sm_decision_txt(EapDecision decision); 53189251Ssam#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ 54189251Ssam 55189251Ssam 56189251Ssam 57189251Ssamstatic Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var) 58189251Ssam{ 59189251Ssam return sm->eapol_cb->get_bool(sm->eapol_ctx, var); 60189251Ssam} 61189251Ssam 62189251Ssam 63189251Ssamstatic void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var, 64189251Ssam Boolean value) 65189251Ssam{ 66189251Ssam sm->eapol_cb->set_bool(sm->eapol_ctx, var, value); 67189251Ssam} 68189251Ssam 69189251Ssam 70189251Ssamstatic unsigned int eapol_get_int(struct eap_sm *sm, enum eapol_int_var var) 71189251Ssam{ 72189251Ssam return sm->eapol_cb->get_int(sm->eapol_ctx, var); 73189251Ssam} 74189251Ssam 75189251Ssam 76189251Ssamstatic void eapol_set_int(struct eap_sm *sm, enum eapol_int_var var, 77189251Ssam unsigned int value) 78189251Ssam{ 79189251Ssam sm->eapol_cb->set_int(sm->eapol_ctx, var, value); 80189251Ssam} 81189251Ssam 82189251Ssam 83189251Ssamstatic struct wpabuf * eapol_get_eapReqData(struct eap_sm *sm) 84189251Ssam{ 85189251Ssam return sm->eapol_cb->get_eapReqData(sm->eapol_ctx); 86189251Ssam} 87189251Ssam 88189251Ssam 89189251Ssamstatic void eap_deinit_prev_method(struct eap_sm *sm, const char *txt) 90189251Ssam{ 91189251Ssam if (sm->m == NULL || sm->eap_method_priv == NULL) 92189251Ssam return; 93189251Ssam 94189251Ssam wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method " 95189251Ssam "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt); 96189251Ssam sm->m->deinit(sm, sm->eap_method_priv); 97189251Ssam sm->eap_method_priv = NULL; 98189251Ssam sm->m = NULL; 99189251Ssam} 100189251Ssam 101189251Ssam 102189251Ssam/** 103189251Ssam * eap_allowed_method - Check whether EAP method is allowed 104189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 105189251Ssam * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types 106189251Ssam * @method: EAP type 107189251Ssam * Returns: 1 = allowed EAP method, 0 = not allowed 108189251Ssam */ 109189251Ssamint eap_allowed_method(struct eap_sm *sm, int vendor, u32 method) 110189251Ssam{ 111189251Ssam struct eap_peer_config *config = eap_get_config(sm); 112189251Ssam int i; 113189251Ssam struct eap_method_type *m; 114189251Ssam 115189251Ssam if (config == NULL || config->eap_methods == NULL) 116189251Ssam return 1; 117189251Ssam 118189251Ssam m = config->eap_methods; 119189251Ssam for (i = 0; m[i].vendor != EAP_VENDOR_IETF || 120189251Ssam m[i].method != EAP_TYPE_NONE; i++) { 121189251Ssam if (m[i].vendor == vendor && m[i].method == method) 122189251Ssam return 1; 123189251Ssam } 124189251Ssam return 0; 125189251Ssam} 126189251Ssam 127189251Ssam 128189251Ssam/* 129189251Ssam * This state initializes state machine variables when the machine is 130189251Ssam * activated (portEnabled = TRUE). This is also used when re-starting 131189251Ssam * authentication (eapRestart == TRUE). 132189251Ssam */ 133189251SsamSM_STATE(EAP, INITIALIZE) 134189251Ssam{ 135189251Ssam SM_ENTRY(EAP, INITIALIZE); 136189251Ssam if (sm->fast_reauth && sm->m && sm->m->has_reauth_data && 137189251Ssam sm->m->has_reauth_data(sm, sm->eap_method_priv) && 138189251Ssam !sm->prev_failure) { 139189251Ssam wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for " 140189251Ssam "fast reauthentication"); 141189251Ssam sm->m->deinit_for_reauth(sm, sm->eap_method_priv); 142189251Ssam } else { 143189251Ssam eap_deinit_prev_method(sm, "INITIALIZE"); 144189251Ssam } 145189251Ssam sm->selectedMethod = EAP_TYPE_NONE; 146189251Ssam sm->methodState = METHOD_NONE; 147189251Ssam sm->allowNotifications = TRUE; 148189251Ssam sm->decision = DECISION_FAIL; 149189251Ssam eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); 150189251Ssam eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); 151189251Ssam eapol_set_bool(sm, EAPOL_eapFail, FALSE); 152189251Ssam os_free(sm->eapKeyData); 153189251Ssam sm->eapKeyData = NULL; 154189251Ssam sm->eapKeyAvailable = FALSE; 155189251Ssam eapol_set_bool(sm, EAPOL_eapRestart, FALSE); 156189251Ssam sm->lastId = -1; /* new session - make sure this does not match with 157189251Ssam * the first EAP-Packet */ 158189251Ssam /* 159189251Ssam * RFC 4137 does not reset eapResp and eapNoResp here. However, this 160189251Ssam * seemed to be able to trigger cases where both were set and if EAPOL 161189251Ssam * state machine uses eapNoResp first, it may end up not sending a real 162189251Ssam * reply correctly. This occurred when the workaround in FAIL state set 163189251Ssam * eapNoResp = TRUE.. Maybe that workaround needs to be fixed to do 164189251Ssam * something else(?) 165189251Ssam */ 166189251Ssam eapol_set_bool(sm, EAPOL_eapResp, FALSE); 167189251Ssam eapol_set_bool(sm, EAPOL_eapNoResp, FALSE); 168189251Ssam sm->num_rounds = 0; 169189251Ssam sm->prev_failure = 0; 170189251Ssam} 171189251Ssam 172189251Ssam 173189251Ssam/* 174189251Ssam * This state is reached whenever service from the lower layer is interrupted 175189251Ssam * or unavailable (portEnabled == FALSE). Immediate transition to INITIALIZE 176189251Ssam * occurs when the port becomes enabled. 177189251Ssam */ 178189251SsamSM_STATE(EAP, DISABLED) 179189251Ssam{ 180189251Ssam SM_ENTRY(EAP, DISABLED); 181189251Ssam sm->num_rounds = 0; 182189251Ssam} 183189251Ssam 184189251Ssam 185189251Ssam/* 186189251Ssam * The state machine spends most of its time here, waiting for something to 187189251Ssam * happen. This state is entered unconditionally from INITIALIZE, DISCARD, and 188189251Ssam * SEND_RESPONSE states. 189189251Ssam */ 190189251SsamSM_STATE(EAP, IDLE) 191189251Ssam{ 192189251Ssam SM_ENTRY(EAP, IDLE); 193189251Ssam} 194189251Ssam 195189251Ssam 196189251Ssam/* 197189251Ssam * This state is entered when an EAP packet is received (eapReq == TRUE) to 198189251Ssam * parse the packet header. 199189251Ssam */ 200189251SsamSM_STATE(EAP, RECEIVED) 201189251Ssam{ 202189251Ssam const struct wpabuf *eapReqData; 203189251Ssam 204189251Ssam SM_ENTRY(EAP, RECEIVED); 205189251Ssam eapReqData = eapol_get_eapReqData(sm); 206189251Ssam /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */ 207189251Ssam eap_sm_parseEapReq(sm, eapReqData); 208189251Ssam sm->num_rounds++; 209189251Ssam} 210189251Ssam 211189251Ssam 212189251Ssam/* 213189251Ssam * This state is entered when a request for a new type comes in. Either the 214189251Ssam * correct method is started, or a Nak response is built. 215189251Ssam */ 216189251SsamSM_STATE(EAP, GET_METHOD) 217189251Ssam{ 218189251Ssam int reinit; 219189251Ssam EapType method; 220189251Ssam 221189251Ssam SM_ENTRY(EAP, GET_METHOD); 222189251Ssam 223189251Ssam if (sm->reqMethod == EAP_TYPE_EXPANDED) 224189251Ssam method = sm->reqVendorMethod; 225189251Ssam else 226189251Ssam method = sm->reqMethod; 227189251Ssam 228189251Ssam if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) { 229189251Ssam wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed", 230189251Ssam sm->reqVendor, method); 231214734Srpaulo wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD 232214734Srpaulo "vendor=%u method=%u -> NAK", 233214734Srpaulo sm->reqVendor, method); 234189251Ssam goto nak; 235189251Ssam } 236189251Ssam 237214734Srpaulo wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD 238214734Srpaulo "vendor=%u method=%u", sm->reqVendor, method); 239214734Srpaulo 240189251Ssam /* 241189251Ssam * RFC 4137 does not define specific operation for fast 242189251Ssam * re-authentication (session resumption). The design here is to allow 243189251Ssam * the previously used method data to be maintained for 244189251Ssam * re-authentication if the method support session resumption. 245189251Ssam * Otherwise, the previously used method data is freed and a new method 246189251Ssam * is allocated here. 247189251Ssam */ 248189251Ssam if (sm->fast_reauth && 249189251Ssam sm->m && sm->m->vendor == sm->reqVendor && 250189251Ssam sm->m->method == method && 251189251Ssam sm->m->has_reauth_data && 252189251Ssam sm->m->has_reauth_data(sm, sm->eap_method_priv)) { 253189251Ssam wpa_printf(MSG_DEBUG, "EAP: Using previous method data" 254189251Ssam " for fast re-authentication"); 255189251Ssam reinit = 1; 256189251Ssam } else { 257189251Ssam eap_deinit_prev_method(sm, "GET_METHOD"); 258189251Ssam reinit = 0; 259189251Ssam } 260189251Ssam 261189251Ssam sm->selectedMethod = sm->reqMethod; 262189251Ssam if (sm->m == NULL) 263189251Ssam sm->m = eap_peer_get_eap_method(sm->reqVendor, method); 264189251Ssam if (!sm->m) { 265189251Ssam wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: " 266189251Ssam "vendor %d method %d", 267189251Ssam sm->reqVendor, method); 268189251Ssam goto nak; 269189251Ssam } 270189251Ssam 271189251Ssam wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: " 272189251Ssam "vendor %u method %u (%s)", 273189251Ssam sm->reqVendor, method, sm->m->name); 274189251Ssam if (reinit) 275189251Ssam sm->eap_method_priv = sm->m->init_for_reauth( 276189251Ssam sm, sm->eap_method_priv); 277189251Ssam else 278189251Ssam sm->eap_method_priv = sm->m->init(sm); 279189251Ssam 280189251Ssam if (sm->eap_method_priv == NULL) { 281189251Ssam struct eap_peer_config *config = eap_get_config(sm); 282189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, 283189251Ssam "EAP: Failed to initialize EAP method: vendor %u " 284189251Ssam "method %u (%s)", 285189251Ssam sm->reqVendor, method, sm->m->name); 286189251Ssam sm->m = NULL; 287189251Ssam sm->methodState = METHOD_NONE; 288189251Ssam sm->selectedMethod = EAP_TYPE_NONE; 289189251Ssam if (sm->reqMethod == EAP_TYPE_TLS && config && 290189251Ssam (config->pending_req_pin || 291189251Ssam config->pending_req_passphrase)) { 292189251Ssam /* 293189251Ssam * Return without generating Nak in order to allow 294189251Ssam * entering of PIN code or passphrase to retry the 295189251Ssam * current EAP packet. 296189251Ssam */ 297189251Ssam wpa_printf(MSG_DEBUG, "EAP: Pending PIN/passphrase " 298189251Ssam "request - skip Nak"); 299189251Ssam return; 300189251Ssam } 301189251Ssam 302189251Ssam goto nak; 303189251Ssam } 304189251Ssam 305189251Ssam sm->methodState = METHOD_INIT; 306189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_METHOD 307189251Ssam "EAP vendor %u method %u (%s) selected", 308189251Ssam sm->reqVendor, method, sm->m->name); 309189251Ssam return; 310189251Ssam 311189251Ssamnak: 312189251Ssam wpabuf_free(sm->eapRespData); 313189251Ssam sm->eapRespData = NULL; 314189251Ssam sm->eapRespData = eap_sm_buildNak(sm, sm->reqId); 315189251Ssam} 316189251Ssam 317189251Ssam 318189251Ssam/* 319189251Ssam * The method processing happens here. The request from the authenticator is 320189251Ssam * processed, and an appropriate response packet is built. 321189251Ssam */ 322189251SsamSM_STATE(EAP, METHOD) 323189251Ssam{ 324189251Ssam struct wpabuf *eapReqData; 325189251Ssam struct eap_method_ret ret; 326189251Ssam 327189251Ssam SM_ENTRY(EAP, METHOD); 328189251Ssam if (sm->m == NULL) { 329189251Ssam wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected"); 330189251Ssam return; 331189251Ssam } 332189251Ssam 333189251Ssam eapReqData = eapol_get_eapReqData(sm); 334189251Ssam 335189251Ssam /* 336189251Ssam * Get ignore, methodState, decision, allowNotifications, and 337189251Ssam * eapRespData. RFC 4137 uses three separate method procedure (check, 338189251Ssam * process, and buildResp) in this state. These have been combined into 339189251Ssam * a single function call to m->process() in order to optimize EAP 340189251Ssam * method implementation interface a bit. These procedures are only 341189251Ssam * used from within this METHOD state, so there is no need to keep 342189251Ssam * these as separate C functions. 343189251Ssam * 344189251Ssam * The RFC 4137 procedures return values as follows: 345189251Ssam * ignore = m.check(eapReqData) 346189251Ssam * (methodState, decision, allowNotifications) = m.process(eapReqData) 347189251Ssam * eapRespData = m.buildResp(reqId) 348189251Ssam */ 349189251Ssam os_memset(&ret, 0, sizeof(ret)); 350189251Ssam ret.ignore = sm->ignore; 351189251Ssam ret.methodState = sm->methodState; 352189251Ssam ret.decision = sm->decision; 353189251Ssam ret.allowNotifications = sm->allowNotifications; 354189251Ssam wpabuf_free(sm->eapRespData); 355189251Ssam sm->eapRespData = NULL; 356189251Ssam sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret, 357189251Ssam eapReqData); 358189251Ssam wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s " 359189251Ssam "methodState=%s decision=%s", 360189251Ssam ret.ignore ? "TRUE" : "FALSE", 361189251Ssam eap_sm_method_state_txt(ret.methodState), 362189251Ssam eap_sm_decision_txt(ret.decision)); 363189251Ssam 364189251Ssam sm->ignore = ret.ignore; 365189251Ssam if (sm->ignore) 366189251Ssam return; 367189251Ssam sm->methodState = ret.methodState; 368189251Ssam sm->decision = ret.decision; 369189251Ssam sm->allowNotifications = ret.allowNotifications; 370189251Ssam 371189251Ssam if (sm->m->isKeyAvailable && sm->m->getKey && 372189251Ssam sm->m->isKeyAvailable(sm, sm->eap_method_priv)) { 373189251Ssam os_free(sm->eapKeyData); 374189251Ssam sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, 375189251Ssam &sm->eapKeyDataLen); 376189251Ssam } 377189251Ssam} 378189251Ssam 379189251Ssam 380189251Ssam/* 381189251Ssam * This state signals the lower layer that a response packet is ready to be 382189251Ssam * sent. 383189251Ssam */ 384189251SsamSM_STATE(EAP, SEND_RESPONSE) 385189251Ssam{ 386189251Ssam SM_ENTRY(EAP, SEND_RESPONSE); 387189251Ssam wpabuf_free(sm->lastRespData); 388189251Ssam if (sm->eapRespData) { 389189251Ssam if (sm->workaround) 390189251Ssam os_memcpy(sm->last_md5, sm->req_md5, 16); 391189251Ssam sm->lastId = sm->reqId; 392189251Ssam sm->lastRespData = wpabuf_dup(sm->eapRespData); 393189251Ssam eapol_set_bool(sm, EAPOL_eapResp, TRUE); 394189251Ssam } else 395189251Ssam sm->lastRespData = NULL; 396189251Ssam eapol_set_bool(sm, EAPOL_eapReq, FALSE); 397189251Ssam eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); 398189251Ssam} 399189251Ssam 400189251Ssam 401189251Ssam/* 402189251Ssam * This state signals the lower layer that the request was discarded, and no 403189251Ssam * response packet will be sent at this time. 404189251Ssam */ 405189251SsamSM_STATE(EAP, DISCARD) 406189251Ssam{ 407189251Ssam SM_ENTRY(EAP, DISCARD); 408189251Ssam eapol_set_bool(sm, EAPOL_eapReq, FALSE); 409189251Ssam eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); 410189251Ssam} 411189251Ssam 412189251Ssam 413189251Ssam/* 414189251Ssam * Handles requests for Identity method and builds a response. 415189251Ssam */ 416189251SsamSM_STATE(EAP, IDENTITY) 417189251Ssam{ 418189251Ssam const struct wpabuf *eapReqData; 419189251Ssam 420189251Ssam SM_ENTRY(EAP, IDENTITY); 421189251Ssam eapReqData = eapol_get_eapReqData(sm); 422189251Ssam eap_sm_processIdentity(sm, eapReqData); 423189251Ssam wpabuf_free(sm->eapRespData); 424189251Ssam sm->eapRespData = NULL; 425189251Ssam sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId, 0); 426189251Ssam} 427189251Ssam 428189251Ssam 429189251Ssam/* 430189251Ssam * Handles requests for Notification method and builds a response. 431189251Ssam */ 432189251SsamSM_STATE(EAP, NOTIFICATION) 433189251Ssam{ 434189251Ssam const struct wpabuf *eapReqData; 435189251Ssam 436189251Ssam SM_ENTRY(EAP, NOTIFICATION); 437189251Ssam eapReqData = eapol_get_eapReqData(sm); 438189251Ssam eap_sm_processNotify(sm, eapReqData); 439189251Ssam wpabuf_free(sm->eapRespData); 440189251Ssam sm->eapRespData = NULL; 441189251Ssam sm->eapRespData = eap_sm_buildNotify(sm->reqId); 442189251Ssam} 443189251Ssam 444189251Ssam 445189251Ssam/* 446189251Ssam * This state retransmits the previous response packet. 447189251Ssam */ 448189251SsamSM_STATE(EAP, RETRANSMIT) 449189251Ssam{ 450189251Ssam SM_ENTRY(EAP, RETRANSMIT); 451189251Ssam wpabuf_free(sm->eapRespData); 452189251Ssam if (sm->lastRespData) 453189251Ssam sm->eapRespData = wpabuf_dup(sm->lastRespData); 454189251Ssam else 455189251Ssam sm->eapRespData = NULL; 456189251Ssam} 457189251Ssam 458189251Ssam 459189251Ssam/* 460189251Ssam * This state is entered in case of a successful completion of authentication 461189251Ssam * and state machine waits here until port is disabled or EAP authentication is 462189251Ssam * restarted. 463189251Ssam */ 464189251SsamSM_STATE(EAP, SUCCESS) 465189251Ssam{ 466189251Ssam SM_ENTRY(EAP, SUCCESS); 467189251Ssam if (sm->eapKeyData != NULL) 468189251Ssam sm->eapKeyAvailable = TRUE; 469189251Ssam eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); 470189251Ssam 471189251Ssam /* 472189251Ssam * RFC 4137 does not clear eapReq here, but this seems to be required 473189251Ssam * to avoid processing the same request twice when state machine is 474189251Ssam * initialized. 475189251Ssam */ 476189251Ssam eapol_set_bool(sm, EAPOL_eapReq, FALSE); 477189251Ssam 478189251Ssam /* 479189251Ssam * RFC 4137 does not set eapNoResp here, but this seems to be required 480189251Ssam * to get EAPOL Supplicant backend state machine into SUCCESS state. In 481189251Ssam * addition, either eapResp or eapNoResp is required to be set after 482189251Ssam * processing the received EAP frame. 483189251Ssam */ 484189251Ssam eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); 485189251Ssam 486189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS 487189251Ssam "EAP authentication completed successfully"); 488189251Ssam} 489189251Ssam 490189251Ssam 491189251Ssam/* 492189251Ssam * This state is entered in case of a failure and state machine waits here 493189251Ssam * until port is disabled or EAP authentication is restarted. 494189251Ssam */ 495189251SsamSM_STATE(EAP, FAILURE) 496189251Ssam{ 497189251Ssam SM_ENTRY(EAP, FAILURE); 498189251Ssam eapol_set_bool(sm, EAPOL_eapFail, TRUE); 499189251Ssam 500189251Ssam /* 501189251Ssam * RFC 4137 does not clear eapReq here, but this seems to be required 502189251Ssam * to avoid processing the same request twice when state machine is 503189251Ssam * initialized. 504189251Ssam */ 505189251Ssam eapol_set_bool(sm, EAPOL_eapReq, FALSE); 506189251Ssam 507189251Ssam /* 508189251Ssam * RFC 4137 does not set eapNoResp here. However, either eapResp or 509189251Ssam * eapNoResp is required to be set after processing the received EAP 510189251Ssam * frame. 511189251Ssam */ 512189251Ssam eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); 513189251Ssam 514189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE 515189251Ssam "EAP authentication failed"); 516189251Ssam 517189251Ssam sm->prev_failure = 1; 518189251Ssam} 519189251Ssam 520189251Ssam 521189251Ssamstatic int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId) 522189251Ssam{ 523189251Ssam /* 524189251Ssam * At least Microsoft IAS and Meetinghouse Aegis seem to be sending 525189251Ssam * EAP-Success/Failure with lastId + 1 even though RFC 3748 and 526189251Ssam * RFC 4137 require that reqId == lastId. In addition, it looks like 527189251Ssam * Ringmaster v2.1.2.0 would be using lastId + 2 in EAP-Success. 528189251Ssam * 529189251Ssam * Accept this kind of Id if EAP workarounds are enabled. These are 530189251Ssam * unauthenticated plaintext messages, so this should have minimal 531189251Ssam * security implications (bit easier to fake EAP-Success/Failure). 532189251Ssam */ 533189251Ssam if (sm->workaround && (reqId == ((lastId + 1) & 0xff) || 534189251Ssam reqId == ((lastId + 2) & 0xff))) { 535189251Ssam wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected " 536189251Ssam "identifier field in EAP Success: " 537189251Ssam "reqId=%d lastId=%d (these are supposed to be " 538189251Ssam "same)", reqId, lastId); 539189251Ssam return 1; 540189251Ssam } 541189251Ssam wpa_printf(MSG_DEBUG, "EAP: EAP-Success Id mismatch - reqId=%d " 542189251Ssam "lastId=%d", reqId, lastId); 543189251Ssam return 0; 544189251Ssam} 545189251Ssam 546189251Ssam 547189251Ssam/* 548189251Ssam * RFC 4137 - Appendix A.1: EAP Peer State Machine - State transitions 549189251Ssam */ 550189251Ssam 551189251Ssamstatic void eap_peer_sm_step_idle(struct eap_sm *sm) 552189251Ssam{ 553189251Ssam /* 554189251Ssam * The first three transitions are from RFC 4137. The last two are 555189251Ssam * local additions to handle special cases with LEAP and PEAP server 556189251Ssam * not sending EAP-Success in some cases. 557189251Ssam */ 558189251Ssam if (eapol_get_bool(sm, EAPOL_eapReq)) 559189251Ssam SM_ENTER(EAP, RECEIVED); 560189251Ssam else if ((eapol_get_bool(sm, EAPOL_altAccept) && 561189251Ssam sm->decision != DECISION_FAIL) || 562189251Ssam (eapol_get_int(sm, EAPOL_idleWhile) == 0 && 563189251Ssam sm->decision == DECISION_UNCOND_SUCC)) 564189251Ssam SM_ENTER(EAP, SUCCESS); 565189251Ssam else if (eapol_get_bool(sm, EAPOL_altReject) || 566189251Ssam (eapol_get_int(sm, EAPOL_idleWhile) == 0 && 567189251Ssam sm->decision != DECISION_UNCOND_SUCC) || 568189251Ssam (eapol_get_bool(sm, EAPOL_altAccept) && 569189251Ssam sm->methodState != METHOD_CONT && 570189251Ssam sm->decision == DECISION_FAIL)) 571189251Ssam SM_ENTER(EAP, FAILURE); 572189251Ssam else if (sm->selectedMethod == EAP_TYPE_LEAP && 573189251Ssam sm->leap_done && sm->decision != DECISION_FAIL && 574189251Ssam sm->methodState == METHOD_DONE) 575189251Ssam SM_ENTER(EAP, SUCCESS); 576189251Ssam else if (sm->selectedMethod == EAP_TYPE_PEAP && 577189251Ssam sm->peap_done && sm->decision != DECISION_FAIL && 578189251Ssam sm->methodState == METHOD_DONE) 579189251Ssam SM_ENTER(EAP, SUCCESS); 580189251Ssam} 581189251Ssam 582189251Ssam 583189251Ssamstatic int eap_peer_req_is_duplicate(struct eap_sm *sm) 584189251Ssam{ 585189251Ssam int duplicate; 586189251Ssam 587189251Ssam duplicate = (sm->reqId == sm->lastId) && sm->rxReq; 588189251Ssam if (sm->workaround && duplicate && 589189251Ssam os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) { 590189251Ssam /* 591189251Ssam * RFC 4137 uses (reqId == lastId) as the only verification for 592189251Ssam * duplicate EAP requests. However, this misses cases where the 593189251Ssam * AS is incorrectly using the same id again; and 594189251Ssam * unfortunately, such implementations exist. Use MD5 hash as 595189251Ssam * an extra verification for the packets being duplicate to 596189251Ssam * workaround these issues. 597189251Ssam */ 598189251Ssam wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again, but " 599189251Ssam "EAP packets were not identical"); 600189251Ssam wpa_printf(MSG_DEBUG, "EAP: workaround - assume this is not a " 601189251Ssam "duplicate packet"); 602189251Ssam duplicate = 0; 603189251Ssam } 604189251Ssam 605189251Ssam return duplicate; 606189251Ssam} 607189251Ssam 608189251Ssam 609189251Ssamstatic void eap_peer_sm_step_received(struct eap_sm *sm) 610189251Ssam{ 611189251Ssam int duplicate = eap_peer_req_is_duplicate(sm); 612189251Ssam 613189251Ssam /* 614189251Ssam * Two special cases below for LEAP are local additions to work around 615189251Ssam * odd LEAP behavior (EAP-Success in the middle of authentication and 616189251Ssam * then swapped roles). Other transitions are based on RFC 4137. 617189251Ssam */ 618189251Ssam if (sm->rxSuccess && sm->decision != DECISION_FAIL && 619189251Ssam (sm->reqId == sm->lastId || 620189251Ssam eap_success_workaround(sm, sm->reqId, sm->lastId))) 621189251Ssam SM_ENTER(EAP, SUCCESS); 622189251Ssam else if (sm->methodState != METHOD_CONT && 623189251Ssam ((sm->rxFailure && 624189251Ssam sm->decision != DECISION_UNCOND_SUCC) || 625189251Ssam (sm->rxSuccess && sm->decision == DECISION_FAIL && 626189251Ssam (sm->selectedMethod != EAP_TYPE_LEAP || 627189251Ssam sm->methodState != METHOD_MAY_CONT))) && 628189251Ssam (sm->reqId == sm->lastId || 629189251Ssam eap_success_workaround(sm, sm->reqId, sm->lastId))) 630189251Ssam SM_ENTER(EAP, FAILURE); 631189251Ssam else if (sm->rxReq && duplicate) 632189251Ssam SM_ENTER(EAP, RETRANSMIT); 633189251Ssam else if (sm->rxReq && !duplicate && 634189251Ssam sm->reqMethod == EAP_TYPE_NOTIFICATION && 635189251Ssam sm->allowNotifications) 636189251Ssam SM_ENTER(EAP, NOTIFICATION); 637189251Ssam else if (sm->rxReq && !duplicate && 638189251Ssam sm->selectedMethod == EAP_TYPE_NONE && 639189251Ssam sm->reqMethod == EAP_TYPE_IDENTITY) 640189251Ssam SM_ENTER(EAP, IDENTITY); 641189251Ssam else if (sm->rxReq && !duplicate && 642189251Ssam sm->selectedMethod == EAP_TYPE_NONE && 643189251Ssam sm->reqMethod != EAP_TYPE_IDENTITY && 644189251Ssam sm->reqMethod != EAP_TYPE_NOTIFICATION) 645189251Ssam SM_ENTER(EAP, GET_METHOD); 646189251Ssam else if (sm->rxReq && !duplicate && 647189251Ssam sm->reqMethod == sm->selectedMethod && 648189251Ssam sm->methodState != METHOD_DONE) 649189251Ssam SM_ENTER(EAP, METHOD); 650189251Ssam else if (sm->selectedMethod == EAP_TYPE_LEAP && 651189251Ssam (sm->rxSuccess || sm->rxResp)) 652189251Ssam SM_ENTER(EAP, METHOD); 653189251Ssam else 654189251Ssam SM_ENTER(EAP, DISCARD); 655189251Ssam} 656189251Ssam 657189251Ssam 658189251Ssamstatic void eap_peer_sm_step_local(struct eap_sm *sm) 659189251Ssam{ 660189251Ssam switch (sm->EAP_state) { 661189251Ssam case EAP_INITIALIZE: 662189251Ssam SM_ENTER(EAP, IDLE); 663189251Ssam break; 664189251Ssam case EAP_DISABLED: 665189251Ssam if (eapol_get_bool(sm, EAPOL_portEnabled) && 666189251Ssam !sm->force_disabled) 667189251Ssam SM_ENTER(EAP, INITIALIZE); 668189251Ssam break; 669189251Ssam case EAP_IDLE: 670189251Ssam eap_peer_sm_step_idle(sm); 671189251Ssam break; 672189251Ssam case EAP_RECEIVED: 673189251Ssam eap_peer_sm_step_received(sm); 674189251Ssam break; 675189251Ssam case EAP_GET_METHOD: 676189251Ssam if (sm->selectedMethod == sm->reqMethod) 677189251Ssam SM_ENTER(EAP, METHOD); 678189251Ssam else 679189251Ssam SM_ENTER(EAP, SEND_RESPONSE); 680189251Ssam break; 681189251Ssam case EAP_METHOD: 682189251Ssam if (sm->ignore) 683189251Ssam SM_ENTER(EAP, DISCARD); 684189251Ssam else 685189251Ssam SM_ENTER(EAP, SEND_RESPONSE); 686189251Ssam break; 687189251Ssam case EAP_SEND_RESPONSE: 688189251Ssam SM_ENTER(EAP, IDLE); 689189251Ssam break; 690189251Ssam case EAP_DISCARD: 691189251Ssam SM_ENTER(EAP, IDLE); 692189251Ssam break; 693189251Ssam case EAP_IDENTITY: 694189251Ssam SM_ENTER(EAP, SEND_RESPONSE); 695189251Ssam break; 696189251Ssam case EAP_NOTIFICATION: 697189251Ssam SM_ENTER(EAP, SEND_RESPONSE); 698189251Ssam break; 699189251Ssam case EAP_RETRANSMIT: 700189251Ssam SM_ENTER(EAP, SEND_RESPONSE); 701189251Ssam break; 702189251Ssam case EAP_SUCCESS: 703189251Ssam break; 704189251Ssam case EAP_FAILURE: 705189251Ssam break; 706189251Ssam } 707189251Ssam} 708189251Ssam 709189251Ssam 710189251SsamSM_STEP(EAP) 711189251Ssam{ 712189251Ssam /* Global transitions */ 713189251Ssam if (eapol_get_bool(sm, EAPOL_eapRestart) && 714189251Ssam eapol_get_bool(sm, EAPOL_portEnabled)) 715189251Ssam SM_ENTER_GLOBAL(EAP, INITIALIZE); 716189251Ssam else if (!eapol_get_bool(sm, EAPOL_portEnabled) || sm->force_disabled) 717189251Ssam SM_ENTER_GLOBAL(EAP, DISABLED); 718189251Ssam else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { 719189251Ssam /* RFC 4137 does not place any limit on number of EAP messages 720189251Ssam * in an authentication session. However, some error cases have 721189251Ssam * ended up in a state were EAP messages were sent between the 722189251Ssam * peer and server in a loop (e.g., TLS ACK frame in both 723189251Ssam * direction). Since this is quite undesired outcome, limit the 724189251Ssam * total number of EAP round-trips and abort authentication if 725189251Ssam * this limit is exceeded. 726189251Ssam */ 727189251Ssam if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { 728189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, "EAP: more than %d " 729189251Ssam "authentication rounds - abort", 730189251Ssam EAP_MAX_AUTH_ROUNDS); 731189251Ssam sm->num_rounds++; 732189251Ssam SM_ENTER_GLOBAL(EAP, FAILURE); 733189251Ssam } 734189251Ssam } else { 735189251Ssam /* Local transitions */ 736189251Ssam eap_peer_sm_step_local(sm); 737189251Ssam } 738189251Ssam} 739189251Ssam 740189251Ssam 741189251Ssamstatic Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, 742189251Ssam EapType method) 743189251Ssam{ 744189251Ssam if (!eap_allowed_method(sm, vendor, method)) { 745189251Ssam wpa_printf(MSG_DEBUG, "EAP: configuration does not allow: " 746189251Ssam "vendor %u method %u", vendor, method); 747189251Ssam return FALSE; 748189251Ssam } 749189251Ssam if (eap_peer_get_eap_method(vendor, method)) 750189251Ssam return TRUE; 751189251Ssam wpa_printf(MSG_DEBUG, "EAP: not included in build: " 752189251Ssam "vendor %u method %u", vendor, method); 753189251Ssam return FALSE; 754189251Ssam} 755189251Ssam 756189251Ssam 757189251Ssamstatic struct wpabuf * eap_sm_build_expanded_nak( 758189251Ssam struct eap_sm *sm, int id, const struct eap_method *methods, 759189251Ssam size_t count) 760189251Ssam{ 761189251Ssam struct wpabuf *resp; 762189251Ssam int found = 0; 763189251Ssam const struct eap_method *m; 764189251Ssam 765189251Ssam wpa_printf(MSG_DEBUG, "EAP: Building expanded EAP-Nak"); 766189251Ssam 767189251Ssam /* RFC 3748 - 5.3.2: Expanded Nak */ 768189251Ssam resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED, 769189251Ssam 8 + 8 * (count + 1), EAP_CODE_RESPONSE, id); 770189251Ssam if (resp == NULL) 771189251Ssam return NULL; 772189251Ssam 773189251Ssam wpabuf_put_be24(resp, EAP_VENDOR_IETF); 774189251Ssam wpabuf_put_be32(resp, EAP_TYPE_NAK); 775189251Ssam 776189251Ssam for (m = methods; m; m = m->next) { 777189251Ssam if (sm->reqVendor == m->vendor && 778189251Ssam sm->reqVendorMethod == m->method) 779189251Ssam continue; /* do not allow the current method again */ 780189251Ssam if (eap_allowed_method(sm, m->vendor, m->method)) { 781189251Ssam wpa_printf(MSG_DEBUG, "EAP: allowed type: " 782189251Ssam "vendor=%u method=%u", 783189251Ssam m->vendor, m->method); 784189251Ssam wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); 785189251Ssam wpabuf_put_be24(resp, m->vendor); 786189251Ssam wpabuf_put_be32(resp, m->method); 787189251Ssam 788189251Ssam found++; 789189251Ssam } 790189251Ssam } 791189251Ssam if (!found) { 792189251Ssam wpa_printf(MSG_DEBUG, "EAP: no more allowed methods"); 793189251Ssam wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); 794189251Ssam wpabuf_put_be24(resp, EAP_VENDOR_IETF); 795189251Ssam wpabuf_put_be32(resp, EAP_TYPE_NONE); 796189251Ssam } 797189251Ssam 798189251Ssam eap_update_len(resp); 799189251Ssam 800189251Ssam return resp; 801189251Ssam} 802189251Ssam 803189251Ssam 804189251Ssamstatic struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id) 805189251Ssam{ 806189251Ssam struct wpabuf *resp; 807189251Ssam u8 *start; 808189251Ssam int found = 0, expanded_found = 0; 809189251Ssam size_t count; 810189251Ssam const struct eap_method *methods, *m; 811189251Ssam 812189251Ssam wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %u " 813189251Ssam "vendor=%u method=%u not allowed)", sm->reqMethod, 814189251Ssam sm->reqVendor, sm->reqVendorMethod); 815189251Ssam methods = eap_peer_get_methods(&count); 816189251Ssam if (methods == NULL) 817189251Ssam return NULL; 818189251Ssam if (sm->reqMethod == EAP_TYPE_EXPANDED) 819189251Ssam return eap_sm_build_expanded_nak(sm, id, methods, count); 820189251Ssam 821189251Ssam /* RFC 3748 - 5.3.1: Legacy Nak */ 822189251Ssam resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, 823189251Ssam sizeof(struct eap_hdr) + 1 + count + 1, 824189251Ssam EAP_CODE_RESPONSE, id); 825189251Ssam if (resp == NULL) 826189251Ssam return NULL; 827189251Ssam 828189251Ssam start = wpabuf_put(resp, 0); 829189251Ssam for (m = methods; m; m = m->next) { 830189251Ssam if (m->vendor == EAP_VENDOR_IETF && m->method == sm->reqMethod) 831189251Ssam continue; /* do not allow the current method again */ 832189251Ssam if (eap_allowed_method(sm, m->vendor, m->method)) { 833189251Ssam if (m->vendor != EAP_VENDOR_IETF) { 834189251Ssam if (expanded_found) 835189251Ssam continue; 836189251Ssam expanded_found = 1; 837189251Ssam wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); 838189251Ssam } else 839189251Ssam wpabuf_put_u8(resp, m->method); 840189251Ssam found++; 841189251Ssam } 842189251Ssam } 843189251Ssam if (!found) 844189251Ssam wpabuf_put_u8(resp, EAP_TYPE_NONE); 845189251Ssam wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", start, found); 846189251Ssam 847189251Ssam eap_update_len(resp); 848189251Ssam 849189251Ssam return resp; 850189251Ssam} 851189251Ssam 852189251Ssam 853189251Ssamstatic void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) 854189251Ssam{ 855189251Ssam const struct eap_hdr *hdr = wpabuf_head(req); 856189251Ssam const u8 *pos = (const u8 *) (hdr + 1); 857189251Ssam pos++; 858189251Ssam 859189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED 860189251Ssam "EAP authentication started"); 861189251Ssam 862189251Ssam /* 863189251Ssam * RFC 3748 - 5.1: Identity 864189251Ssam * Data field may contain a displayable message in UTF-8. If this 865189251Ssam * includes NUL-character, only the data before that should be 866189251Ssam * displayed. Some EAP implementasitons may piggy-back additional 867189251Ssam * options after the NUL. 868189251Ssam */ 869189251Ssam /* TODO: could save displayable message so that it can be shown to the 870189251Ssam * user in case of interaction is required */ 871189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data", 872189251Ssam pos, be_to_host16(hdr->length) - 5); 873189251Ssam} 874189251Ssam 875189251Ssam 876189251Ssam#ifdef PCSC_FUNCS 877189251Ssamstatic int eap_sm_imsi_identity(struct eap_sm *sm, 878189251Ssam struct eap_peer_config *conf) 879189251Ssam{ 880189251Ssam int aka = 0; 881189251Ssam char imsi[100]; 882189251Ssam size_t imsi_len; 883189251Ssam struct eap_method_type *m = conf->eap_methods; 884189251Ssam int i; 885189251Ssam 886189251Ssam imsi_len = sizeof(imsi); 887189251Ssam if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) { 888189251Ssam wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM"); 889189251Ssam return -1; 890189251Ssam } 891189251Ssam 892189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len); 893189251Ssam 894189251Ssam for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF || 895189251Ssam m[i].method != EAP_TYPE_NONE); i++) { 896189251Ssam if (m[i].vendor == EAP_VENDOR_IETF && 897189251Ssam m[i].method == EAP_TYPE_AKA) { 898189251Ssam aka = 1; 899189251Ssam break; 900189251Ssam } 901189251Ssam } 902189251Ssam 903189251Ssam os_free(conf->identity); 904189251Ssam conf->identity = os_malloc(1 + imsi_len); 905189251Ssam if (conf->identity == NULL) { 906189251Ssam wpa_printf(MSG_WARNING, "Failed to allocate buffer for " 907189251Ssam "IMSI-based identity"); 908189251Ssam return -1; 909189251Ssam } 910189251Ssam 911189251Ssam conf->identity[0] = aka ? '0' : '1'; 912189251Ssam os_memcpy(conf->identity + 1, imsi, imsi_len); 913189251Ssam conf->identity_len = 1 + imsi_len; 914189251Ssam 915189251Ssam return 0; 916189251Ssam} 917189251Ssam#endif /* PCSC_FUNCS */ 918189251Ssam 919189251Ssam 920189251Ssamstatic int eap_sm_set_scard_pin(struct eap_sm *sm, 921189251Ssam struct eap_peer_config *conf) 922189251Ssam{ 923189251Ssam#ifdef PCSC_FUNCS 924189251Ssam if (scard_set_pin(sm->scard_ctx, conf->pin)) { 925189251Ssam /* 926189251Ssam * Make sure the same PIN is not tried again in order to avoid 927189251Ssam * blocking SIM. 928189251Ssam */ 929189251Ssam os_free(conf->pin); 930189251Ssam conf->pin = NULL; 931189251Ssam 932189251Ssam wpa_printf(MSG_WARNING, "PIN validation failed"); 933189251Ssam eap_sm_request_pin(sm); 934189251Ssam return -1; 935189251Ssam } 936189251Ssam return 0; 937189251Ssam#else /* PCSC_FUNCS */ 938189251Ssam return -1; 939189251Ssam#endif /* PCSC_FUNCS */ 940189251Ssam} 941189251Ssam 942189251Ssamstatic int eap_sm_get_scard_identity(struct eap_sm *sm, 943189251Ssam struct eap_peer_config *conf) 944189251Ssam{ 945189251Ssam#ifdef PCSC_FUNCS 946189251Ssam if (eap_sm_set_scard_pin(sm, conf)) 947189251Ssam return -1; 948189251Ssam 949189251Ssam return eap_sm_imsi_identity(sm, conf); 950189251Ssam#else /* PCSC_FUNCS */ 951189251Ssam return -1; 952189251Ssam#endif /* PCSC_FUNCS */ 953189251Ssam} 954189251Ssam 955189251Ssam 956189251Ssam/** 957189251Ssam * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network 958189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 959189251Ssam * @id: EAP identifier for the packet 960189251Ssam * @encrypted: Whether the packet is for encrypted tunnel (EAP phase 2) 961189251Ssam * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on 962189251Ssam * failure 963189251Ssam * 964189251Ssam * This function allocates and builds an EAP-Identity/Response packet for the 965189251Ssam * current network. The caller is responsible for freeing the returned data. 966189251Ssam */ 967189251Ssamstruct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted) 968189251Ssam{ 969189251Ssam struct eap_peer_config *config = eap_get_config(sm); 970189251Ssam struct wpabuf *resp; 971189251Ssam const u8 *identity; 972189251Ssam size_t identity_len; 973189251Ssam 974189251Ssam if (config == NULL) { 975189251Ssam wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration " 976189251Ssam "was not available"); 977189251Ssam return NULL; 978189251Ssam } 979189251Ssam 980189251Ssam if (sm->m && sm->m->get_identity && 981189251Ssam (identity = sm->m->get_identity(sm, sm->eap_method_priv, 982189251Ssam &identity_len)) != NULL) { 983189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth " 984189251Ssam "identity", identity, identity_len); 985189251Ssam } else if (!encrypted && config->anonymous_identity) { 986189251Ssam identity = config->anonymous_identity; 987189251Ssam identity_len = config->anonymous_identity_len; 988189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity", 989189251Ssam identity, identity_len); 990189251Ssam } else { 991189251Ssam identity = config->identity; 992189251Ssam identity_len = config->identity_len; 993189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity", 994189251Ssam identity, identity_len); 995189251Ssam } 996189251Ssam 997189251Ssam if (identity == NULL) { 998189251Ssam wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity " 999189251Ssam "configuration was not available"); 1000189251Ssam if (config->pcsc) { 1001189251Ssam if (eap_sm_get_scard_identity(sm, config) < 0) 1002189251Ssam return NULL; 1003189251Ssam identity = config->identity; 1004189251Ssam identity_len = config->identity_len; 1005189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " 1006189251Ssam "IMSI", identity, identity_len); 1007189251Ssam } else { 1008189251Ssam eap_sm_request_identity(sm); 1009189251Ssam return NULL; 1010189251Ssam } 1011189251Ssam } else if (config->pcsc) { 1012189251Ssam if (eap_sm_set_scard_pin(sm, config) < 0) 1013189251Ssam return NULL; 1014189251Ssam } 1015189251Ssam 1016189251Ssam resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len, 1017189251Ssam EAP_CODE_RESPONSE, id); 1018189251Ssam if (resp == NULL) 1019189251Ssam return NULL; 1020189251Ssam 1021189251Ssam wpabuf_put_data(resp, identity, identity_len); 1022189251Ssam 1023189251Ssam return resp; 1024189251Ssam} 1025189251Ssam 1026189251Ssam 1027189251Ssamstatic void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req) 1028189251Ssam{ 1029189251Ssam const u8 *pos; 1030189251Ssam char *msg; 1031189251Ssam size_t i, msg_len; 1032189251Ssam 1033189251Ssam pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, req, 1034189251Ssam &msg_len); 1035189251Ssam if (pos == NULL) 1036189251Ssam return; 1037189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data", 1038189251Ssam pos, msg_len); 1039189251Ssam 1040189251Ssam msg = os_malloc(msg_len + 1); 1041189251Ssam if (msg == NULL) 1042189251Ssam return; 1043189251Ssam for (i = 0; i < msg_len; i++) 1044189251Ssam msg[i] = isprint(pos[i]) ? (char) pos[i] : '_'; 1045189251Ssam msg[msg_len] = '\0'; 1046189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, "%s%s", 1047189251Ssam WPA_EVENT_EAP_NOTIFICATION, msg); 1048189251Ssam os_free(msg); 1049189251Ssam} 1050189251Ssam 1051189251Ssam 1052189251Ssamstatic struct wpabuf * eap_sm_buildNotify(int id) 1053189251Ssam{ 1054189251Ssam struct wpabuf *resp; 1055189251Ssam 1056189251Ssam wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification"); 1057189251Ssam resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0, 1058189251Ssam EAP_CODE_RESPONSE, id); 1059189251Ssam if (resp == NULL) 1060189251Ssam return NULL; 1061189251Ssam 1062189251Ssam return resp; 1063189251Ssam} 1064189251Ssam 1065189251Ssam 1066189251Ssamstatic void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) 1067189251Ssam{ 1068189251Ssam const struct eap_hdr *hdr; 1069189251Ssam size_t plen; 1070189251Ssam const u8 *pos; 1071189251Ssam 1072189251Ssam sm->rxReq = sm->rxResp = sm->rxSuccess = sm->rxFailure = FALSE; 1073189251Ssam sm->reqId = 0; 1074189251Ssam sm->reqMethod = EAP_TYPE_NONE; 1075189251Ssam sm->reqVendor = EAP_VENDOR_IETF; 1076189251Ssam sm->reqVendorMethod = EAP_TYPE_NONE; 1077189251Ssam 1078189251Ssam if (req == NULL || wpabuf_len(req) < sizeof(*hdr)) 1079189251Ssam return; 1080189251Ssam 1081189251Ssam hdr = wpabuf_head(req); 1082189251Ssam plen = be_to_host16(hdr->length); 1083189251Ssam if (plen > wpabuf_len(req)) { 1084189251Ssam wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet " 1085189251Ssam "(len=%lu plen=%lu)", 1086189251Ssam (unsigned long) wpabuf_len(req), 1087189251Ssam (unsigned long) plen); 1088189251Ssam return; 1089189251Ssam } 1090189251Ssam 1091189251Ssam sm->reqId = hdr->identifier; 1092189251Ssam 1093189251Ssam if (sm->workaround) { 1094189251Ssam const u8 *addr[1]; 1095189251Ssam addr[0] = wpabuf_head(req); 1096189251Ssam md5_vector(1, addr, &plen, sm->req_md5); 1097189251Ssam } 1098189251Ssam 1099189251Ssam switch (hdr->code) { 1100189251Ssam case EAP_CODE_REQUEST: 1101189251Ssam if (plen < sizeof(*hdr) + 1) { 1102189251Ssam wpa_printf(MSG_DEBUG, "EAP: Too short EAP-Request - " 1103189251Ssam "no Type field"); 1104189251Ssam return; 1105189251Ssam } 1106189251Ssam sm->rxReq = TRUE; 1107189251Ssam pos = (const u8 *) (hdr + 1); 1108189251Ssam sm->reqMethod = *pos++; 1109189251Ssam if (sm->reqMethod == EAP_TYPE_EXPANDED) { 1110189251Ssam if (plen < sizeof(*hdr) + 8) { 1111189251Ssam wpa_printf(MSG_DEBUG, "EAP: Ignored truncated " 1112189251Ssam "expanded EAP-Packet (plen=%lu)", 1113189251Ssam (unsigned long) plen); 1114189251Ssam return; 1115189251Ssam } 1116189251Ssam sm->reqVendor = WPA_GET_BE24(pos); 1117189251Ssam pos += 3; 1118189251Ssam sm->reqVendorMethod = WPA_GET_BE32(pos); 1119189251Ssam } 1120189251Ssam wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request id=%d " 1121189251Ssam "method=%u vendor=%u vendorMethod=%u", 1122189251Ssam sm->reqId, sm->reqMethod, sm->reqVendor, 1123189251Ssam sm->reqVendorMethod); 1124189251Ssam break; 1125189251Ssam case EAP_CODE_RESPONSE: 1126189251Ssam if (sm->selectedMethod == EAP_TYPE_LEAP) { 1127189251Ssam /* 1128189251Ssam * LEAP differs from RFC 4137 by using reversed roles 1129189251Ssam * for mutual authentication and because of this, we 1130189251Ssam * need to accept EAP-Response frames if LEAP is used. 1131189251Ssam */ 1132189251Ssam if (plen < sizeof(*hdr) + 1) { 1133189251Ssam wpa_printf(MSG_DEBUG, "EAP: Too short " 1134189251Ssam "EAP-Response - no Type field"); 1135189251Ssam return; 1136189251Ssam } 1137189251Ssam sm->rxResp = TRUE; 1138189251Ssam pos = (const u8 *) (hdr + 1); 1139189251Ssam sm->reqMethod = *pos; 1140189251Ssam wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for " 1141189251Ssam "LEAP method=%d id=%d", 1142189251Ssam sm->reqMethod, sm->reqId); 1143189251Ssam break; 1144189251Ssam } 1145189251Ssam wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response"); 1146189251Ssam break; 1147189251Ssam case EAP_CODE_SUCCESS: 1148189251Ssam wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success"); 1149189251Ssam sm->rxSuccess = TRUE; 1150189251Ssam break; 1151189251Ssam case EAP_CODE_FAILURE: 1152189251Ssam wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure"); 1153189251Ssam sm->rxFailure = TRUE; 1154189251Ssam break; 1155189251Ssam default: 1156189251Ssam wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown " 1157189251Ssam "code %d", hdr->code); 1158189251Ssam break; 1159189251Ssam } 1160189251Ssam} 1161189251Ssam 1162189251Ssam 1163214734Srpaulostatic void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, 1164214734Srpaulo union tls_event_data *data) 1165214734Srpaulo{ 1166214734Srpaulo struct eap_sm *sm = ctx; 1167214734Srpaulo char *hash_hex = NULL; 1168214734Srpaulo char *cert_hex = NULL; 1169214734Srpaulo 1170214734Srpaulo switch (ev) { 1171214734Srpaulo case TLS_CERT_CHAIN_FAILURE: 1172214734Srpaulo wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR 1173214734Srpaulo "reason=%d depth=%d subject='%s' err='%s'", 1174214734Srpaulo data->cert_fail.reason, 1175214734Srpaulo data->cert_fail.depth, 1176214734Srpaulo data->cert_fail.subject, 1177214734Srpaulo data->cert_fail.reason_txt); 1178214734Srpaulo break; 1179214734Srpaulo case TLS_PEER_CERTIFICATE: 1180214734Srpaulo if (data->peer_cert.hash) { 1181214734Srpaulo size_t len = data->peer_cert.hash_len * 2 + 1; 1182214734Srpaulo hash_hex = os_malloc(len); 1183214734Srpaulo if (hash_hex) { 1184214734Srpaulo wpa_snprintf_hex(hash_hex, len, 1185214734Srpaulo data->peer_cert.hash, 1186214734Srpaulo data->peer_cert.hash_len); 1187214734Srpaulo } 1188214734Srpaulo } 1189214734Srpaulo wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PEER_CERT 1190214734Srpaulo "depth=%d subject='%s'%s%s", 1191214734Srpaulo data->peer_cert.depth, data->peer_cert.subject, 1192214734Srpaulo hash_hex ? " hash=" : "", hash_hex ? hash_hex : ""); 1193214734Srpaulo 1194214734Srpaulo if (data->peer_cert.cert) { 1195214734Srpaulo size_t len = wpabuf_len(data->peer_cert.cert) * 2 + 1; 1196214734Srpaulo cert_hex = os_malloc(len); 1197214734Srpaulo if (cert_hex == NULL) 1198214734Srpaulo break; 1199214734Srpaulo wpa_snprintf_hex(cert_hex, len, 1200214734Srpaulo wpabuf_head(data->peer_cert.cert), 1201214734Srpaulo wpabuf_len(data->peer_cert.cert)); 1202214734Srpaulo wpa_msg_ctrl(sm->msg_ctx, MSG_INFO, 1203214734Srpaulo WPA_EVENT_EAP_PEER_CERT 1204214734Srpaulo "depth=%d subject='%s' cert=%s", 1205214734Srpaulo data->peer_cert.depth, 1206214734Srpaulo data->peer_cert.subject, 1207214734Srpaulo cert_hex); 1208214734Srpaulo } 1209214734Srpaulo break; 1210214734Srpaulo } 1211214734Srpaulo 1212214734Srpaulo os_free(hash_hex); 1213214734Srpaulo os_free(cert_hex); 1214214734Srpaulo} 1215214734Srpaulo 1216214734Srpaulo 1217189251Ssam/** 1218189251Ssam * eap_peer_sm_init - Allocate and initialize EAP peer state machine 1219189251Ssam * @eapol_ctx: Context data to be used with eapol_cb calls 1220189251Ssam * @eapol_cb: Pointer to EAPOL callback functions 1221189251Ssam * @msg_ctx: Context data for wpa_msg() calls 1222189251Ssam * @conf: EAP configuration 1223189251Ssam * Returns: Pointer to the allocated EAP state machine or %NULL on failure 1224189251Ssam * 1225189251Ssam * This function allocates and initializes an EAP state machine. In addition, 1226189251Ssam * this initializes TLS library for the new EAP state machine. eapol_cb pointer 1227189251Ssam * will be in use until eap_peer_sm_deinit() is used to deinitialize this EAP 1228189251Ssam * state machine. Consequently, the caller must make sure that this data 1229189251Ssam * structure remains alive while the EAP state machine is active. 1230189251Ssam */ 1231189251Ssamstruct eap_sm * eap_peer_sm_init(void *eapol_ctx, 1232189251Ssam struct eapol_callbacks *eapol_cb, 1233189251Ssam void *msg_ctx, struct eap_config *conf) 1234189251Ssam{ 1235189251Ssam struct eap_sm *sm; 1236189251Ssam struct tls_config tlsconf; 1237189251Ssam 1238189251Ssam sm = os_zalloc(sizeof(*sm)); 1239189251Ssam if (sm == NULL) 1240189251Ssam return NULL; 1241189251Ssam sm->eapol_ctx = eapol_ctx; 1242189251Ssam sm->eapol_cb = eapol_cb; 1243189251Ssam sm->msg_ctx = msg_ctx; 1244189251Ssam sm->ClientTimeout = 60; 1245189251Ssam sm->wps = conf->wps; 1246189251Ssam 1247189251Ssam os_memset(&tlsconf, 0, sizeof(tlsconf)); 1248189251Ssam tlsconf.opensc_engine_path = conf->opensc_engine_path; 1249189251Ssam tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path; 1250189251Ssam tlsconf.pkcs11_module_path = conf->pkcs11_module_path; 1251214734Srpaulo#ifdef CONFIG_FIPS 1252214734Srpaulo tlsconf.fips_mode = 1; 1253214734Srpaulo#endif /* CONFIG_FIPS */ 1254214734Srpaulo tlsconf.event_cb = eap_peer_sm_tls_event; 1255214734Srpaulo tlsconf.cb_ctx = sm; 1256189251Ssam sm->ssl_ctx = tls_init(&tlsconf); 1257189251Ssam if (sm->ssl_ctx == NULL) { 1258189251Ssam wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS " 1259189251Ssam "context."); 1260189251Ssam os_free(sm); 1261189251Ssam return NULL; 1262189251Ssam } 1263189251Ssam 1264189251Ssam return sm; 1265189251Ssam} 1266189251Ssam 1267189251Ssam 1268189251Ssam/** 1269189251Ssam * eap_peer_sm_deinit - Deinitialize and free an EAP peer state machine 1270189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1271189251Ssam * 1272189251Ssam * This function deinitializes EAP state machine and frees all allocated 1273189251Ssam * resources. 1274189251Ssam */ 1275189251Ssamvoid eap_peer_sm_deinit(struct eap_sm *sm) 1276189251Ssam{ 1277189251Ssam if (sm == NULL) 1278189251Ssam return; 1279189251Ssam eap_deinit_prev_method(sm, "EAP deinit"); 1280189251Ssam eap_sm_abort(sm); 1281189251Ssam tls_deinit(sm->ssl_ctx); 1282189251Ssam os_free(sm); 1283189251Ssam} 1284189251Ssam 1285189251Ssam 1286189251Ssam/** 1287189251Ssam * eap_peer_sm_step - Step EAP peer state machine 1288189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1289189251Ssam * Returns: 1 if EAP state was changed or 0 if not 1290189251Ssam * 1291189251Ssam * This function advances EAP state machine to a new state to match with the 1292189251Ssam * current variables. This should be called whenever variables used by the EAP 1293189251Ssam * state machine have changed. 1294189251Ssam */ 1295189251Ssamint eap_peer_sm_step(struct eap_sm *sm) 1296189251Ssam{ 1297189251Ssam int res = 0; 1298189251Ssam do { 1299189251Ssam sm->changed = FALSE; 1300189251Ssam SM_STEP_RUN(EAP); 1301189251Ssam if (sm->changed) 1302189251Ssam res = 1; 1303189251Ssam } while (sm->changed); 1304189251Ssam return res; 1305189251Ssam} 1306189251Ssam 1307189251Ssam 1308189251Ssam/** 1309189251Ssam * eap_sm_abort - Abort EAP authentication 1310189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1311189251Ssam * 1312189251Ssam * Release system resources that have been allocated for the authentication 1313189251Ssam * session without fully deinitializing the EAP state machine. 1314189251Ssam */ 1315189251Ssamvoid eap_sm_abort(struct eap_sm *sm) 1316189251Ssam{ 1317189251Ssam wpabuf_free(sm->lastRespData); 1318189251Ssam sm->lastRespData = NULL; 1319189251Ssam wpabuf_free(sm->eapRespData); 1320189251Ssam sm->eapRespData = NULL; 1321189251Ssam os_free(sm->eapKeyData); 1322189251Ssam sm->eapKeyData = NULL; 1323189251Ssam 1324189251Ssam /* This is not clearly specified in the EAP statemachines draft, but 1325189251Ssam * it seems necessary to make sure that some of the EAPOL variables get 1326189251Ssam * cleared for the next authentication. */ 1327189251Ssam eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); 1328189251Ssam} 1329189251Ssam 1330189251Ssam 1331189251Ssam#ifdef CONFIG_CTRL_IFACE 1332189251Ssamstatic const char * eap_sm_state_txt(int state) 1333189251Ssam{ 1334189251Ssam switch (state) { 1335189251Ssam case EAP_INITIALIZE: 1336189251Ssam return "INITIALIZE"; 1337189251Ssam case EAP_DISABLED: 1338189251Ssam return "DISABLED"; 1339189251Ssam case EAP_IDLE: 1340189251Ssam return "IDLE"; 1341189251Ssam case EAP_RECEIVED: 1342189251Ssam return "RECEIVED"; 1343189251Ssam case EAP_GET_METHOD: 1344189251Ssam return "GET_METHOD"; 1345189251Ssam case EAP_METHOD: 1346189251Ssam return "METHOD"; 1347189251Ssam case EAP_SEND_RESPONSE: 1348189251Ssam return "SEND_RESPONSE"; 1349189251Ssam case EAP_DISCARD: 1350189251Ssam return "DISCARD"; 1351189251Ssam case EAP_IDENTITY: 1352189251Ssam return "IDENTITY"; 1353189251Ssam case EAP_NOTIFICATION: 1354189251Ssam return "NOTIFICATION"; 1355189251Ssam case EAP_RETRANSMIT: 1356189251Ssam return "RETRANSMIT"; 1357189251Ssam case EAP_SUCCESS: 1358189251Ssam return "SUCCESS"; 1359189251Ssam case EAP_FAILURE: 1360189251Ssam return "FAILURE"; 1361189251Ssam default: 1362189251Ssam return "UNKNOWN"; 1363189251Ssam } 1364189251Ssam} 1365189251Ssam#endif /* CONFIG_CTRL_IFACE */ 1366189251Ssam 1367189251Ssam 1368189251Ssam#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) 1369189251Ssamstatic const char * eap_sm_method_state_txt(EapMethodState state) 1370189251Ssam{ 1371189251Ssam switch (state) { 1372189251Ssam case METHOD_NONE: 1373189251Ssam return "NONE"; 1374189251Ssam case METHOD_INIT: 1375189251Ssam return "INIT"; 1376189251Ssam case METHOD_CONT: 1377189251Ssam return "CONT"; 1378189251Ssam case METHOD_MAY_CONT: 1379189251Ssam return "MAY_CONT"; 1380189251Ssam case METHOD_DONE: 1381189251Ssam return "DONE"; 1382189251Ssam default: 1383189251Ssam return "UNKNOWN"; 1384189251Ssam } 1385189251Ssam} 1386189251Ssam 1387189251Ssam 1388189251Ssamstatic const char * eap_sm_decision_txt(EapDecision decision) 1389189251Ssam{ 1390189251Ssam switch (decision) { 1391189251Ssam case DECISION_FAIL: 1392189251Ssam return "FAIL"; 1393189251Ssam case DECISION_COND_SUCC: 1394189251Ssam return "COND_SUCC"; 1395189251Ssam case DECISION_UNCOND_SUCC: 1396189251Ssam return "UNCOND_SUCC"; 1397189251Ssam default: 1398189251Ssam return "UNKNOWN"; 1399189251Ssam } 1400189251Ssam} 1401189251Ssam#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ 1402189251Ssam 1403189251Ssam 1404189251Ssam#ifdef CONFIG_CTRL_IFACE 1405189251Ssam 1406189251Ssam/** 1407189251Ssam * eap_sm_get_status - Get EAP state machine status 1408189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1409189251Ssam * @buf: Buffer for status information 1410189251Ssam * @buflen: Maximum buffer length 1411189251Ssam * @verbose: Whether to include verbose status information 1412189251Ssam * Returns: Number of bytes written to buf. 1413189251Ssam * 1414189251Ssam * Query EAP state machine for status information. This function fills in a 1415189251Ssam * text area with current status information from the EAPOL state machine. If 1416189251Ssam * the buffer (buf) is not large enough, status information will be truncated 1417189251Ssam * to fit the buffer. 1418189251Ssam */ 1419189251Ssamint eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) 1420189251Ssam{ 1421189251Ssam int len, ret; 1422189251Ssam 1423189251Ssam if (sm == NULL) 1424189251Ssam return 0; 1425189251Ssam 1426189251Ssam len = os_snprintf(buf, buflen, 1427189251Ssam "EAP state=%s\n", 1428189251Ssam eap_sm_state_txt(sm->EAP_state)); 1429189251Ssam if (len < 0 || (size_t) len >= buflen) 1430189251Ssam return 0; 1431189251Ssam 1432189251Ssam if (sm->selectedMethod != EAP_TYPE_NONE) { 1433189251Ssam const char *name; 1434189251Ssam if (sm->m) { 1435189251Ssam name = sm->m->name; 1436189251Ssam } else { 1437189251Ssam const struct eap_method *m = 1438189251Ssam eap_peer_get_eap_method(EAP_VENDOR_IETF, 1439189251Ssam sm->selectedMethod); 1440189251Ssam if (m) 1441189251Ssam name = m->name; 1442189251Ssam else 1443189251Ssam name = "?"; 1444189251Ssam } 1445189251Ssam ret = os_snprintf(buf + len, buflen - len, 1446189251Ssam "selectedMethod=%d (EAP-%s)\n", 1447189251Ssam sm->selectedMethod, name); 1448189251Ssam if (ret < 0 || (size_t) ret >= buflen - len) 1449189251Ssam return len; 1450189251Ssam len += ret; 1451189251Ssam 1452189251Ssam if (sm->m && sm->m->get_status) { 1453189251Ssam len += sm->m->get_status(sm, sm->eap_method_priv, 1454189251Ssam buf + len, buflen - len, 1455189251Ssam verbose); 1456189251Ssam } 1457189251Ssam } 1458189251Ssam 1459189251Ssam if (verbose) { 1460189251Ssam ret = os_snprintf(buf + len, buflen - len, 1461189251Ssam "reqMethod=%d\n" 1462189251Ssam "methodState=%s\n" 1463189251Ssam "decision=%s\n" 1464189251Ssam "ClientTimeout=%d\n", 1465189251Ssam sm->reqMethod, 1466189251Ssam eap_sm_method_state_txt(sm->methodState), 1467189251Ssam eap_sm_decision_txt(sm->decision), 1468189251Ssam sm->ClientTimeout); 1469189251Ssam if (ret < 0 || (size_t) ret >= buflen - len) 1470189251Ssam return len; 1471189251Ssam len += ret; 1472189251Ssam } 1473189251Ssam 1474189251Ssam return len; 1475189251Ssam} 1476189251Ssam#endif /* CONFIG_CTRL_IFACE */ 1477189251Ssam 1478189251Ssam 1479189251Ssam#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) 1480189251Ssamtypedef enum { 1481189251Ssam TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP, TYPE_PIN, TYPE_NEW_PASSWORD, 1482189251Ssam TYPE_PASSPHRASE 1483189251Ssam} eap_ctrl_req_type; 1484189251Ssam 1485189251Ssamstatic void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, 1486189251Ssam const char *msg, size_t msglen) 1487189251Ssam{ 1488189251Ssam struct eap_peer_config *config; 1489189251Ssam char *field, *txt, *tmp; 1490189251Ssam 1491189251Ssam if (sm == NULL) 1492189251Ssam return; 1493189251Ssam config = eap_get_config(sm); 1494189251Ssam if (config == NULL) 1495189251Ssam return; 1496189251Ssam 1497189251Ssam switch (type) { 1498189251Ssam case TYPE_IDENTITY: 1499189251Ssam field = "IDENTITY"; 1500189251Ssam txt = "Identity"; 1501189251Ssam config->pending_req_identity++; 1502189251Ssam break; 1503189251Ssam case TYPE_PASSWORD: 1504189251Ssam field = "PASSWORD"; 1505189251Ssam txt = "Password"; 1506189251Ssam config->pending_req_password++; 1507189251Ssam break; 1508189251Ssam case TYPE_NEW_PASSWORD: 1509189251Ssam field = "NEW_PASSWORD"; 1510189251Ssam txt = "New Password"; 1511189251Ssam config->pending_req_new_password++; 1512189251Ssam break; 1513189251Ssam case TYPE_PIN: 1514189251Ssam field = "PIN"; 1515189251Ssam txt = "PIN"; 1516189251Ssam config->pending_req_pin++; 1517189251Ssam break; 1518189251Ssam case TYPE_OTP: 1519189251Ssam field = "OTP"; 1520189251Ssam if (msg) { 1521189251Ssam tmp = os_malloc(msglen + 3); 1522189251Ssam if (tmp == NULL) 1523189251Ssam return; 1524189251Ssam tmp[0] = '['; 1525189251Ssam os_memcpy(tmp + 1, msg, msglen); 1526189251Ssam tmp[msglen + 1] = ']'; 1527189251Ssam tmp[msglen + 2] = '\0'; 1528189251Ssam txt = tmp; 1529189251Ssam os_free(config->pending_req_otp); 1530189251Ssam config->pending_req_otp = tmp; 1531189251Ssam config->pending_req_otp_len = msglen + 3; 1532189251Ssam } else { 1533189251Ssam if (config->pending_req_otp == NULL) 1534189251Ssam return; 1535189251Ssam txt = config->pending_req_otp; 1536189251Ssam } 1537189251Ssam break; 1538189251Ssam case TYPE_PASSPHRASE: 1539189251Ssam field = "PASSPHRASE"; 1540189251Ssam txt = "Private key passphrase"; 1541189251Ssam config->pending_req_passphrase++; 1542189251Ssam break; 1543189251Ssam default: 1544189251Ssam return; 1545189251Ssam } 1546189251Ssam 1547189251Ssam if (sm->eapol_cb->eap_param_needed) 1548189251Ssam sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt); 1549189251Ssam} 1550189251Ssam#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ 1551189251Ssam#define eap_sm_request(sm, type, msg, msglen) do { } while (0) 1552189251Ssam#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ 1553189251Ssam 1554189251Ssam 1555189251Ssam/** 1556189251Ssam * eap_sm_request_identity - Request identity from user (ctrl_iface) 1557189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1558189251Ssam * 1559189251Ssam * EAP methods can call this function to request identity information for the 1560189251Ssam * current network. This is normally called when the identity is not included 1561189251Ssam * in the network configuration. The request will be sent to monitor programs 1562189251Ssam * through the control interface. 1563189251Ssam */ 1564189251Ssamvoid eap_sm_request_identity(struct eap_sm *sm) 1565189251Ssam{ 1566189251Ssam eap_sm_request(sm, TYPE_IDENTITY, NULL, 0); 1567189251Ssam} 1568189251Ssam 1569189251Ssam 1570189251Ssam/** 1571189251Ssam * eap_sm_request_password - Request password from user (ctrl_iface) 1572189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1573189251Ssam * 1574189251Ssam * EAP methods can call this function to request password information for the 1575189251Ssam * current network. This is normally called when the password is not included 1576189251Ssam * in the network configuration. The request will be sent to monitor programs 1577189251Ssam * through the control interface. 1578189251Ssam */ 1579189251Ssamvoid eap_sm_request_password(struct eap_sm *sm) 1580189251Ssam{ 1581189251Ssam eap_sm_request(sm, TYPE_PASSWORD, NULL, 0); 1582189251Ssam} 1583189251Ssam 1584189251Ssam 1585189251Ssam/** 1586189251Ssam * eap_sm_request_new_password - Request new password from user (ctrl_iface) 1587189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1588189251Ssam * 1589189251Ssam * EAP methods can call this function to request new password information for 1590189251Ssam * the current network. This is normally called when the EAP method indicates 1591189251Ssam * that the current password has expired and password change is required. The 1592189251Ssam * request will be sent to monitor programs through the control interface. 1593189251Ssam */ 1594189251Ssamvoid eap_sm_request_new_password(struct eap_sm *sm) 1595189251Ssam{ 1596189251Ssam eap_sm_request(sm, TYPE_NEW_PASSWORD, NULL, 0); 1597189251Ssam} 1598189251Ssam 1599189251Ssam 1600189251Ssam/** 1601189251Ssam * eap_sm_request_pin - Request SIM or smart card PIN from user (ctrl_iface) 1602189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1603189251Ssam * 1604189251Ssam * EAP methods can call this function to request SIM or smart card PIN 1605189251Ssam * information for the current network. This is normally called when the PIN is 1606189251Ssam * not included in the network configuration. The request will be sent to 1607189251Ssam * monitor programs through the control interface. 1608189251Ssam */ 1609189251Ssamvoid eap_sm_request_pin(struct eap_sm *sm) 1610189251Ssam{ 1611189251Ssam eap_sm_request(sm, TYPE_PIN, NULL, 0); 1612189251Ssam} 1613189251Ssam 1614189251Ssam 1615189251Ssam/** 1616189251Ssam * eap_sm_request_otp - Request one time password from user (ctrl_iface) 1617189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1618189251Ssam * @msg: Message to be displayed to the user when asking for OTP 1619189251Ssam * @msg_len: Length of the user displayable message 1620189251Ssam * 1621189251Ssam * EAP methods can call this function to request open time password (OTP) for 1622189251Ssam * the current network. The request will be sent to monitor programs through 1623189251Ssam * the control interface. 1624189251Ssam */ 1625189251Ssamvoid eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len) 1626189251Ssam{ 1627189251Ssam eap_sm_request(sm, TYPE_OTP, msg, msg_len); 1628189251Ssam} 1629189251Ssam 1630189251Ssam 1631189251Ssam/** 1632189251Ssam * eap_sm_request_passphrase - Request passphrase from user (ctrl_iface) 1633189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1634189251Ssam * 1635189251Ssam * EAP methods can call this function to request passphrase for a private key 1636189251Ssam * for the current network. This is normally called when the passphrase is not 1637189251Ssam * included in the network configuration. The request will be sent to monitor 1638189251Ssam * programs through the control interface. 1639189251Ssam */ 1640189251Ssamvoid eap_sm_request_passphrase(struct eap_sm *sm) 1641189251Ssam{ 1642189251Ssam eap_sm_request(sm, TYPE_PASSPHRASE, NULL, 0); 1643189251Ssam} 1644189251Ssam 1645189251Ssam 1646189251Ssam/** 1647189251Ssam * eap_sm_notify_ctrl_attached - Notification of attached monitor 1648189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1649189251Ssam * 1650189251Ssam * Notify EAP state machines that a monitor was attached to the control 1651189251Ssam * interface to trigger re-sending of pending requests for user input. 1652189251Ssam */ 1653189251Ssamvoid eap_sm_notify_ctrl_attached(struct eap_sm *sm) 1654189251Ssam{ 1655189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1656189251Ssam 1657189251Ssam if (config == NULL) 1658189251Ssam return; 1659189251Ssam 1660189251Ssam /* Re-send any pending requests for user data since a new control 1661189251Ssam * interface was added. This handles cases where the EAP authentication 1662189251Ssam * starts immediately after system startup when the user interface is 1663189251Ssam * not yet running. */ 1664189251Ssam if (config->pending_req_identity) 1665189251Ssam eap_sm_request_identity(sm); 1666189251Ssam if (config->pending_req_password) 1667189251Ssam eap_sm_request_password(sm); 1668189251Ssam if (config->pending_req_new_password) 1669189251Ssam eap_sm_request_new_password(sm); 1670189251Ssam if (config->pending_req_otp) 1671189251Ssam eap_sm_request_otp(sm, NULL, 0); 1672189251Ssam if (config->pending_req_pin) 1673189251Ssam eap_sm_request_pin(sm); 1674189251Ssam if (config->pending_req_passphrase) 1675189251Ssam eap_sm_request_passphrase(sm); 1676189251Ssam} 1677189251Ssam 1678189251Ssam 1679189251Ssamstatic int eap_allowed_phase2_type(int vendor, int type) 1680189251Ssam{ 1681189251Ssam if (vendor != EAP_VENDOR_IETF) 1682189251Ssam return 0; 1683189251Ssam return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS && 1684189251Ssam type != EAP_TYPE_FAST; 1685189251Ssam} 1686189251Ssam 1687189251Ssam 1688189251Ssam/** 1689189251Ssam * eap_get_phase2_type - Get EAP type for the given EAP phase 2 method name 1690189251Ssam * @name: EAP method name, e.g., MD5 1691189251Ssam * @vendor: Buffer for returning EAP Vendor-Id 1692189251Ssam * Returns: EAP method type or %EAP_TYPE_NONE if not found 1693189251Ssam * 1694189251Ssam * This function maps EAP type names into EAP type numbers that are allowed for 1695189251Ssam * Phase 2, i.e., for tunneled authentication. Phase 2 is used, e.g., with 1696189251Ssam * EAP-PEAP, EAP-TTLS, and EAP-FAST. 1697189251Ssam */ 1698189251Ssamu32 eap_get_phase2_type(const char *name, int *vendor) 1699189251Ssam{ 1700189251Ssam int v; 1701189251Ssam u8 type = eap_peer_get_type(name, &v); 1702189251Ssam if (eap_allowed_phase2_type(v, type)) { 1703189251Ssam *vendor = v; 1704189251Ssam return type; 1705189251Ssam } 1706189251Ssam *vendor = EAP_VENDOR_IETF; 1707189251Ssam return EAP_TYPE_NONE; 1708189251Ssam} 1709189251Ssam 1710189251Ssam 1711189251Ssam/** 1712189251Ssam * eap_get_phase2_types - Get list of allowed EAP phase 2 types 1713189251Ssam * @config: Pointer to a network configuration 1714189251Ssam * @count: Pointer to a variable to be filled with number of returned EAP types 1715189251Ssam * Returns: Pointer to allocated type list or %NULL on failure 1716189251Ssam * 1717189251Ssam * This function generates an array of allowed EAP phase 2 (tunneled) types for 1718189251Ssam * the given network configuration. 1719189251Ssam */ 1720189251Ssamstruct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config, 1721189251Ssam size_t *count) 1722189251Ssam{ 1723189251Ssam struct eap_method_type *buf; 1724189251Ssam u32 method; 1725189251Ssam int vendor; 1726189251Ssam size_t mcount; 1727189251Ssam const struct eap_method *methods, *m; 1728189251Ssam 1729189251Ssam methods = eap_peer_get_methods(&mcount); 1730189251Ssam if (methods == NULL) 1731189251Ssam return NULL; 1732189251Ssam *count = 0; 1733189251Ssam buf = os_malloc(mcount * sizeof(struct eap_method_type)); 1734189251Ssam if (buf == NULL) 1735189251Ssam return NULL; 1736189251Ssam 1737189251Ssam for (m = methods; m; m = m->next) { 1738189251Ssam vendor = m->vendor; 1739189251Ssam method = m->method; 1740189251Ssam if (eap_allowed_phase2_type(vendor, method)) { 1741189251Ssam if (vendor == EAP_VENDOR_IETF && 1742189251Ssam method == EAP_TYPE_TLS && config && 1743189251Ssam config->private_key2 == NULL) 1744189251Ssam continue; 1745189251Ssam buf[*count].vendor = vendor; 1746189251Ssam buf[*count].method = method; 1747189251Ssam (*count)++; 1748189251Ssam } 1749189251Ssam } 1750189251Ssam 1751189251Ssam return buf; 1752189251Ssam} 1753189251Ssam 1754189251Ssam 1755189251Ssam/** 1756189251Ssam * eap_set_fast_reauth - Update fast_reauth setting 1757189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1758189251Ssam * @enabled: 1 = Fast reauthentication is enabled, 0 = Disabled 1759189251Ssam */ 1760189251Ssamvoid eap_set_fast_reauth(struct eap_sm *sm, int enabled) 1761189251Ssam{ 1762189251Ssam sm->fast_reauth = enabled; 1763189251Ssam} 1764189251Ssam 1765189251Ssam 1766189251Ssam/** 1767189251Ssam * eap_set_workaround - Update EAP workarounds setting 1768189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1769189251Ssam * @workaround: 1 = Enable EAP workarounds, 0 = Disable EAP workarounds 1770189251Ssam */ 1771189251Ssamvoid eap_set_workaround(struct eap_sm *sm, unsigned int workaround) 1772189251Ssam{ 1773189251Ssam sm->workaround = workaround; 1774189251Ssam} 1775189251Ssam 1776189251Ssam 1777189251Ssam/** 1778189251Ssam * eap_get_config - Get current network configuration 1779189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1780189251Ssam * Returns: Pointer to the current network configuration or %NULL if not found 1781189251Ssam * 1782189251Ssam * EAP peer methods should avoid using this function if they can use other 1783189251Ssam * access functions, like eap_get_config_identity() and 1784189251Ssam * eap_get_config_password(), that do not require direct access to 1785189251Ssam * struct eap_peer_config. 1786189251Ssam */ 1787189251Ssamstruct eap_peer_config * eap_get_config(struct eap_sm *sm) 1788189251Ssam{ 1789189251Ssam return sm->eapol_cb->get_config(sm->eapol_ctx); 1790189251Ssam} 1791189251Ssam 1792189251Ssam 1793189251Ssam/** 1794189251Ssam * eap_get_config_identity - Get identity from the network configuration 1795189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1796189251Ssam * @len: Buffer for the length of the identity 1797189251Ssam * Returns: Pointer to the identity or %NULL if not found 1798189251Ssam */ 1799189251Ssamconst u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len) 1800189251Ssam{ 1801189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1802189251Ssam if (config == NULL) 1803189251Ssam return NULL; 1804189251Ssam *len = config->identity_len; 1805189251Ssam return config->identity; 1806189251Ssam} 1807189251Ssam 1808189251Ssam 1809189251Ssam/** 1810189251Ssam * eap_get_config_password - Get password from the network configuration 1811189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1812189251Ssam * @len: Buffer for the length of the password 1813189251Ssam * Returns: Pointer to the password or %NULL if not found 1814189251Ssam */ 1815189251Ssamconst u8 * eap_get_config_password(struct eap_sm *sm, size_t *len) 1816189251Ssam{ 1817189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1818189251Ssam if (config == NULL) 1819189251Ssam return NULL; 1820189251Ssam *len = config->password_len; 1821189251Ssam return config->password; 1822189251Ssam} 1823189251Ssam 1824189251Ssam 1825189251Ssam/** 1826189251Ssam * eap_get_config_password2 - Get password from the network configuration 1827189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1828189251Ssam * @len: Buffer for the length of the password 1829189251Ssam * @hash: Buffer for returning whether the password is stored as a 1830189251Ssam * NtPasswordHash instead of plaintext password; can be %NULL if this 1831189251Ssam * information is not needed 1832189251Ssam * Returns: Pointer to the password or %NULL if not found 1833189251Ssam */ 1834189251Ssamconst u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash) 1835189251Ssam{ 1836189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1837189251Ssam if (config == NULL) 1838189251Ssam return NULL; 1839189251Ssam *len = config->password_len; 1840189251Ssam if (hash) 1841189251Ssam *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH); 1842189251Ssam return config->password; 1843189251Ssam} 1844189251Ssam 1845189251Ssam 1846189251Ssam/** 1847189251Ssam * eap_get_config_new_password - Get new password from network configuration 1848189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1849189251Ssam * @len: Buffer for the length of the new password 1850189251Ssam * Returns: Pointer to the new password or %NULL if not found 1851189251Ssam */ 1852189251Ssamconst u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len) 1853189251Ssam{ 1854189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1855189251Ssam if (config == NULL) 1856189251Ssam return NULL; 1857189251Ssam *len = config->new_password_len; 1858189251Ssam return config->new_password; 1859189251Ssam} 1860189251Ssam 1861189251Ssam 1862189251Ssam/** 1863189251Ssam * eap_get_config_otp - Get one-time password from the network configuration 1864189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1865189251Ssam * @len: Buffer for the length of the one-time password 1866189251Ssam * Returns: Pointer to the one-time password or %NULL if not found 1867189251Ssam */ 1868189251Ssamconst u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len) 1869189251Ssam{ 1870189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1871189251Ssam if (config == NULL) 1872189251Ssam return NULL; 1873189251Ssam *len = config->otp_len; 1874189251Ssam return config->otp; 1875189251Ssam} 1876189251Ssam 1877189251Ssam 1878189251Ssam/** 1879189251Ssam * eap_clear_config_otp - Clear used one-time password 1880189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1881189251Ssam * 1882189251Ssam * This function clears a used one-time password (OTP) from the current network 1883189251Ssam * configuration. This should be called when the OTP has been used and is not 1884189251Ssam * needed anymore. 1885189251Ssam */ 1886189251Ssamvoid eap_clear_config_otp(struct eap_sm *sm) 1887189251Ssam{ 1888189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1889189251Ssam if (config == NULL) 1890189251Ssam return; 1891189251Ssam os_memset(config->otp, 0, config->otp_len); 1892189251Ssam os_free(config->otp); 1893189251Ssam config->otp = NULL; 1894189251Ssam config->otp_len = 0; 1895189251Ssam} 1896189251Ssam 1897189251Ssam 1898189251Ssam/** 1899189251Ssam * eap_get_config_phase1 - Get phase1 data from the network configuration 1900189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1901189251Ssam * Returns: Pointer to the phase1 data or %NULL if not found 1902189251Ssam */ 1903189251Ssamconst char * eap_get_config_phase1(struct eap_sm *sm) 1904189251Ssam{ 1905189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1906189251Ssam if (config == NULL) 1907189251Ssam return NULL; 1908189251Ssam return config->phase1; 1909189251Ssam} 1910189251Ssam 1911189251Ssam 1912189251Ssam/** 1913189251Ssam * eap_get_config_phase2 - Get phase2 data from the network configuration 1914189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1915189251Ssam * Returns: Pointer to the phase1 data or %NULL if not found 1916189251Ssam */ 1917189251Ssamconst char * eap_get_config_phase2(struct eap_sm *sm) 1918189251Ssam{ 1919189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1920189251Ssam if (config == NULL) 1921189251Ssam return NULL; 1922189251Ssam return config->phase2; 1923189251Ssam} 1924189251Ssam 1925189251Ssam 1926189251Ssam/** 1927189251Ssam * eap_key_available - Get key availability (eapKeyAvailable variable) 1928189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1929189251Ssam * Returns: 1 if EAP keying material is available, 0 if not 1930189251Ssam */ 1931189251Ssamint eap_key_available(struct eap_sm *sm) 1932189251Ssam{ 1933189251Ssam return sm ? sm->eapKeyAvailable : 0; 1934189251Ssam} 1935189251Ssam 1936189251Ssam 1937189251Ssam/** 1938189251Ssam * eap_notify_success - Notify EAP state machine about external success trigger 1939189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1940189251Ssam * 1941189251Ssam * This function is called when external event, e.g., successful completion of 1942189251Ssam * WPA-PSK key handshake, is indicating that EAP state machine should move to 1943189251Ssam * success state. This is mainly used with security modes that do not use EAP 1944189251Ssam * state machine (e.g., WPA-PSK). 1945189251Ssam */ 1946189251Ssamvoid eap_notify_success(struct eap_sm *sm) 1947189251Ssam{ 1948189251Ssam if (sm) { 1949189251Ssam sm->decision = DECISION_COND_SUCC; 1950189251Ssam sm->EAP_state = EAP_SUCCESS; 1951189251Ssam } 1952189251Ssam} 1953189251Ssam 1954189251Ssam 1955189251Ssam/** 1956189251Ssam * eap_notify_lower_layer_success - Notification of lower layer success 1957189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1958189251Ssam * 1959189251Ssam * Notify EAP state machines that a lower layer has detected a successful 1960189251Ssam * authentication. This is used to recover from dropped EAP-Success messages. 1961189251Ssam */ 1962189251Ssamvoid eap_notify_lower_layer_success(struct eap_sm *sm) 1963189251Ssam{ 1964189251Ssam if (sm == NULL) 1965189251Ssam return; 1966189251Ssam 1967189251Ssam if (eapol_get_bool(sm, EAPOL_eapSuccess) || 1968189251Ssam sm->decision == DECISION_FAIL || 1969189251Ssam (sm->methodState != METHOD_MAY_CONT && 1970189251Ssam sm->methodState != METHOD_DONE)) 1971189251Ssam return; 1972189251Ssam 1973189251Ssam if (sm->eapKeyData != NULL) 1974189251Ssam sm->eapKeyAvailable = TRUE; 1975189251Ssam eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); 1976189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS 1977189251Ssam "EAP authentication completed successfully (based on lower " 1978189251Ssam "layer success)"); 1979189251Ssam} 1980189251Ssam 1981189251Ssam 1982189251Ssam/** 1983189251Ssam * eap_get_eapKeyData - Get master session key (MSK) from EAP state machine 1984189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1985189251Ssam * @len: Pointer to variable that will be set to number of bytes in the key 1986189251Ssam * Returns: Pointer to the EAP keying data or %NULL on failure 1987189251Ssam * 1988189251Ssam * Fetch EAP keying material (MSK, eapKeyData) from the EAP state machine. The 1989189251Ssam * key is available only after a successful authentication. EAP state machine 1990189251Ssam * continues to manage the key data and the caller must not change or free the 1991189251Ssam * returned data. 1992189251Ssam */ 1993189251Ssamconst u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len) 1994189251Ssam{ 1995189251Ssam if (sm == NULL || sm->eapKeyData == NULL) { 1996189251Ssam *len = 0; 1997189251Ssam return NULL; 1998189251Ssam } 1999189251Ssam 2000189251Ssam *len = sm->eapKeyDataLen; 2001189251Ssam return sm->eapKeyData; 2002189251Ssam} 2003189251Ssam 2004189251Ssam 2005189251Ssam/** 2006189251Ssam * eap_get_eapKeyData - Get EAP response data 2007189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2008189251Ssam * Returns: Pointer to the EAP response (eapRespData) or %NULL on failure 2009189251Ssam * 2010189251Ssam * Fetch EAP response (eapRespData) from the EAP state machine. This data is 2011189251Ssam * available when EAP state machine has processed an incoming EAP request. The 2012189251Ssam * EAP state machine does not maintain a reference to the response after this 2013189251Ssam * function is called and the caller is responsible for freeing the data. 2014189251Ssam */ 2015189251Ssamstruct wpabuf * eap_get_eapRespData(struct eap_sm *sm) 2016189251Ssam{ 2017189251Ssam struct wpabuf *resp; 2018189251Ssam 2019189251Ssam if (sm == NULL || sm->eapRespData == NULL) 2020189251Ssam return NULL; 2021189251Ssam 2022189251Ssam resp = sm->eapRespData; 2023189251Ssam sm->eapRespData = NULL; 2024189251Ssam 2025189251Ssam return resp; 2026189251Ssam} 2027189251Ssam 2028189251Ssam 2029189251Ssam/** 2030189251Ssam * eap_sm_register_scard_ctx - Notification of smart card context 2031189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2032189251Ssam * @ctx: Context data for smart card operations 2033189251Ssam * 2034189251Ssam * Notify EAP state machines of context data for smart card operations. This 2035189251Ssam * context data will be used as a parameter for scard_*() functions. 2036189251Ssam */ 2037189251Ssamvoid eap_register_scard_ctx(struct eap_sm *sm, void *ctx) 2038189251Ssam{ 2039189251Ssam if (sm) 2040189251Ssam sm->scard_ctx = ctx; 2041189251Ssam} 2042189251Ssam 2043189251Ssam 2044189251Ssam/** 2045189251Ssam * eap_set_config_blob - Set or add a named configuration blob 2046189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2047189251Ssam * @blob: New value for the blob 2048189251Ssam * 2049189251Ssam * Adds a new configuration blob or replaces the current value of an existing 2050189251Ssam * blob. 2051189251Ssam */ 2052189251Ssamvoid eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob) 2053189251Ssam{ 2054189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS 2055189251Ssam sm->eapol_cb->set_config_blob(sm->eapol_ctx, blob); 2056189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */ 2057189251Ssam} 2058189251Ssam 2059189251Ssam 2060189251Ssam/** 2061189251Ssam * eap_get_config_blob - Get a named configuration blob 2062189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2063189251Ssam * @name: Name of the blob 2064189251Ssam * Returns: Pointer to blob data or %NULL if not found 2065189251Ssam */ 2066189251Ssamconst struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm, 2067189251Ssam const char *name) 2068189251Ssam{ 2069189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS 2070189251Ssam return sm->eapol_cb->get_config_blob(sm->eapol_ctx, name); 2071189251Ssam#else /* CONFIG_NO_CONFIG_BLOBS */ 2072189251Ssam return NULL; 2073189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */ 2074189251Ssam} 2075189251Ssam 2076189251Ssam 2077189251Ssam/** 2078189251Ssam * eap_set_force_disabled - Set force_disabled flag 2079189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2080189251Ssam * @disabled: 1 = EAP disabled, 0 = EAP enabled 2081189251Ssam * 2082189251Ssam * This function is used to force EAP state machine to be disabled when it is 2083189251Ssam * not in use (e.g., with WPA-PSK or plaintext connections). 2084189251Ssam */ 2085189251Ssamvoid eap_set_force_disabled(struct eap_sm *sm, int disabled) 2086189251Ssam{ 2087189251Ssam sm->force_disabled = disabled; 2088189251Ssam} 2089189251Ssam 2090189251Ssam 2091189251Ssam /** 2092189251Ssam * eap_notify_pending - Notify that EAP method is ready to re-process a request 2093189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2094189251Ssam * 2095189251Ssam * An EAP method can perform a pending operation (e.g., to get a response from 2096189251Ssam * an external process). Once the response is available, this function can be 2097189251Ssam * used to request EAPOL state machine to retry delivering the previously 2098189251Ssam * received (and still unanswered) EAP request to EAP state machine. 2099189251Ssam */ 2100189251Ssamvoid eap_notify_pending(struct eap_sm *sm) 2101189251Ssam{ 2102189251Ssam sm->eapol_cb->notify_pending(sm->eapol_ctx); 2103189251Ssam} 2104189251Ssam 2105189251Ssam 2106189251Ssam/** 2107189251Ssam * eap_invalidate_cached_session - Mark cached session data invalid 2108189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2109189251Ssam */ 2110189251Ssamvoid eap_invalidate_cached_session(struct eap_sm *sm) 2111189251Ssam{ 2112189251Ssam if (sm) 2113189251Ssam eap_deinit_prev_method(sm, "invalidate"); 2114189251Ssam} 2115189251Ssam 2116189251Ssam 2117189251Ssamint eap_is_wps_pbc_enrollee(struct eap_peer_config *conf) 2118189251Ssam{ 2119189251Ssam if (conf->identity_len != WSC_ID_ENROLLEE_LEN || 2120189251Ssam os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)) 2121189251Ssam return 0; /* Not a WPS Enrollee */ 2122189251Ssam 2123189251Ssam if (conf->phase1 == NULL || os_strstr(conf->phase1, "pbc=1") == NULL) 2124189251Ssam return 0; /* Not using PBC */ 2125189251Ssam 2126189251Ssam return 1; 2127189251Ssam} 2128189251Ssam 2129189251Ssam 2130189251Ssamint eap_is_wps_pin_enrollee(struct eap_peer_config *conf) 2131189251Ssam{ 2132189251Ssam if (conf->identity_len != WSC_ID_ENROLLEE_LEN || 2133189251Ssam os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)) 2134189251Ssam return 0; /* Not a WPS Enrollee */ 2135189251Ssam 2136189251Ssam if (conf->phase1 == NULL || os_strstr(conf->phase1, "pin=") == NULL) 2137189251Ssam return 0; /* Not using PIN */ 2138189251Ssam 2139189251Ssam return 1; 2140189251Ssam} 2141