1189251Ssam/* 2189251Ssam * EAP peer state machines (RFC 4137) 3252726Srpaulo * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam * 8189251Ssam * This file implements the Peer State Machine as defined in RFC 4137. The used 9189251Ssam * states and state transitions match mostly with the RFC. However, there are 10189251Ssam * couple of additional transitions for working around small issues noticed 11189251Ssam * during testing. These exceptions are explained in comments within the 12189251Ssam * functions in this file. The method functions, m.func(), are similar to the 13189251Ssam * ones used in RFC 4137, but some small changes have used here to optimize 14189251Ssam * operations and to add functionality needed for fast re-authentication 15189251Ssam * (session resumption). 16189251Ssam */ 17189251Ssam 18189251Ssam#include "includes.h" 19189251Ssam 20189251Ssam#include "common.h" 21189251Ssam#include "pcsc_funcs.h" 22189251Ssam#include "state_machine.h" 23252726Srpaulo#include "ext_password.h" 24214734Srpaulo#include "crypto/crypto.h" 25214734Srpaulo#include "crypto/tls.h" 26214734Srpaulo#include "common/wpa_ctrl.h" 27189251Ssam#include "eap_common/eap_wsc_common.h" 28214734Srpaulo#include "eap_i.h" 29214734Srpaulo#include "eap_config.h" 30189251Ssam 31189251Ssam#define STATE_MACHINE_DATA struct eap_sm 32189251Ssam#define STATE_MACHINE_DEBUG_PREFIX "EAP" 33189251Ssam 34189251Ssam#define EAP_MAX_AUTH_ROUNDS 50 35252726Srpaulo#define EAP_CLIENT_TIMEOUT_DEFAULT 60 36189251Ssam 37189251Ssam 38189251Ssamstatic Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, 39189251Ssam EapType method); 40189251Ssamstatic struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id); 41189251Ssamstatic void eap_sm_processIdentity(struct eap_sm *sm, 42189251Ssam const struct wpabuf *req); 43189251Ssamstatic void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req); 44189251Ssamstatic struct wpabuf * eap_sm_buildNotify(int id); 45189251Ssamstatic void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req); 46189251Ssam#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) 47189251Ssamstatic const char * eap_sm_method_state_txt(EapMethodState state); 48189251Ssamstatic const char * eap_sm_decision_txt(EapDecision decision); 49189251Ssam#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ 50189251Ssam 51189251Ssam 52189251Ssam 53189251Ssamstatic Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var) 54189251Ssam{ 55189251Ssam return sm->eapol_cb->get_bool(sm->eapol_ctx, var); 56189251Ssam} 57189251Ssam 58189251Ssam 59189251Ssamstatic void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var, 60189251Ssam Boolean value) 61189251Ssam{ 62189251Ssam sm->eapol_cb->set_bool(sm->eapol_ctx, var, value); 63189251Ssam} 64189251Ssam 65189251Ssam 66189251Ssamstatic unsigned int eapol_get_int(struct eap_sm *sm, enum eapol_int_var var) 67189251Ssam{ 68189251Ssam return sm->eapol_cb->get_int(sm->eapol_ctx, var); 69189251Ssam} 70189251Ssam 71189251Ssam 72189251Ssamstatic void eapol_set_int(struct eap_sm *sm, enum eapol_int_var var, 73189251Ssam unsigned int value) 74189251Ssam{ 75189251Ssam sm->eapol_cb->set_int(sm->eapol_ctx, var, value); 76189251Ssam} 77189251Ssam 78189251Ssam 79189251Ssamstatic struct wpabuf * eapol_get_eapReqData(struct eap_sm *sm) 80189251Ssam{ 81189251Ssam return sm->eapol_cb->get_eapReqData(sm->eapol_ctx); 82189251Ssam} 83189251Ssam 84189251Ssam 85252726Srpaulostatic void eap_notify_status(struct eap_sm *sm, const char *status, 86252726Srpaulo const char *parameter) 87252726Srpaulo{ 88252726Srpaulo wpa_printf(MSG_DEBUG, "EAP: Status notification: %s (param=%s)", 89252726Srpaulo status, parameter); 90252726Srpaulo if (sm->eapol_cb->notify_status) 91252726Srpaulo sm->eapol_cb->notify_status(sm->eapol_ctx, status, parameter); 92252726Srpaulo} 93252726Srpaulo 94252726Srpaulo 95189251Ssamstatic void eap_deinit_prev_method(struct eap_sm *sm, const char *txt) 96189251Ssam{ 97252726Srpaulo ext_password_free(sm->ext_pw_buf); 98252726Srpaulo sm->ext_pw_buf = NULL; 99252726Srpaulo 100189251Ssam if (sm->m == NULL || sm->eap_method_priv == NULL) 101189251Ssam return; 102189251Ssam 103189251Ssam wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method " 104189251Ssam "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt); 105189251Ssam sm->m->deinit(sm, sm->eap_method_priv); 106189251Ssam sm->eap_method_priv = NULL; 107189251Ssam sm->m = NULL; 108189251Ssam} 109189251Ssam 110189251Ssam 111189251Ssam/** 112189251Ssam * eap_allowed_method - Check whether EAP method is allowed 113189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 114189251Ssam * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types 115189251Ssam * @method: EAP type 116189251Ssam * Returns: 1 = allowed EAP method, 0 = not allowed 117189251Ssam */ 118189251Ssamint eap_allowed_method(struct eap_sm *sm, int vendor, u32 method) 119189251Ssam{ 120189251Ssam struct eap_peer_config *config = eap_get_config(sm); 121189251Ssam int i; 122189251Ssam struct eap_method_type *m; 123189251Ssam 124189251Ssam if (config == NULL || config->eap_methods == NULL) 125189251Ssam return 1; 126189251Ssam 127189251Ssam m = config->eap_methods; 128189251Ssam for (i = 0; m[i].vendor != EAP_VENDOR_IETF || 129189251Ssam m[i].method != EAP_TYPE_NONE; i++) { 130189251Ssam if (m[i].vendor == vendor && m[i].method == method) 131189251Ssam return 1; 132189251Ssam } 133189251Ssam return 0; 134189251Ssam} 135189251Ssam 136189251Ssam 137189251Ssam/* 138189251Ssam * This state initializes state machine variables when the machine is 139189251Ssam * activated (portEnabled = TRUE). This is also used when re-starting 140189251Ssam * authentication (eapRestart == TRUE). 141189251Ssam */ 142189251SsamSM_STATE(EAP, INITIALIZE) 143189251Ssam{ 144189251Ssam SM_ENTRY(EAP, INITIALIZE); 145189251Ssam if (sm->fast_reauth && sm->m && sm->m->has_reauth_data && 146189251Ssam sm->m->has_reauth_data(sm, sm->eap_method_priv) && 147189251Ssam !sm->prev_failure) { 148189251Ssam wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for " 149189251Ssam "fast reauthentication"); 150189251Ssam sm->m->deinit_for_reauth(sm, sm->eap_method_priv); 151189251Ssam } else { 152189251Ssam eap_deinit_prev_method(sm, "INITIALIZE"); 153189251Ssam } 154189251Ssam sm->selectedMethod = EAP_TYPE_NONE; 155189251Ssam sm->methodState = METHOD_NONE; 156189251Ssam sm->allowNotifications = TRUE; 157189251Ssam sm->decision = DECISION_FAIL; 158252726Srpaulo sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; 159189251Ssam eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); 160189251Ssam eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); 161189251Ssam eapol_set_bool(sm, EAPOL_eapFail, FALSE); 162189251Ssam os_free(sm->eapKeyData); 163189251Ssam sm->eapKeyData = NULL; 164189251Ssam sm->eapKeyAvailable = FALSE; 165189251Ssam eapol_set_bool(sm, EAPOL_eapRestart, FALSE); 166189251Ssam sm->lastId = -1; /* new session - make sure this does not match with 167189251Ssam * the first EAP-Packet */ 168189251Ssam /* 169189251Ssam * RFC 4137 does not reset eapResp and eapNoResp here. However, this 170189251Ssam * seemed to be able to trigger cases where both were set and if EAPOL 171189251Ssam * state machine uses eapNoResp first, it may end up not sending a real 172189251Ssam * reply correctly. This occurred when the workaround in FAIL state set 173189251Ssam * eapNoResp = TRUE.. Maybe that workaround needs to be fixed to do 174189251Ssam * something else(?) 175189251Ssam */ 176189251Ssam eapol_set_bool(sm, EAPOL_eapResp, FALSE); 177189251Ssam eapol_set_bool(sm, EAPOL_eapNoResp, FALSE); 178189251Ssam sm->num_rounds = 0; 179189251Ssam sm->prev_failure = 0; 180189251Ssam} 181189251Ssam 182189251Ssam 183189251Ssam/* 184189251Ssam * This state is reached whenever service from the lower layer is interrupted 185189251Ssam * or unavailable (portEnabled == FALSE). Immediate transition to INITIALIZE 186189251Ssam * occurs when the port becomes enabled. 187189251Ssam */ 188189251SsamSM_STATE(EAP, DISABLED) 189189251Ssam{ 190189251Ssam SM_ENTRY(EAP, DISABLED); 191189251Ssam sm->num_rounds = 0; 192252726Srpaulo /* 193252726Srpaulo * RFC 4137 does not describe clearing of idleWhile here, but doing so 194252726Srpaulo * allows the timer tick to be stopped more quickly when EAP is not in 195252726Srpaulo * use. 196252726Srpaulo */ 197252726Srpaulo eapol_set_int(sm, EAPOL_idleWhile, 0); 198189251Ssam} 199189251Ssam 200189251Ssam 201189251Ssam/* 202189251Ssam * The state machine spends most of its time here, waiting for something to 203189251Ssam * happen. This state is entered unconditionally from INITIALIZE, DISCARD, and 204189251Ssam * SEND_RESPONSE states. 205189251Ssam */ 206189251SsamSM_STATE(EAP, IDLE) 207189251Ssam{ 208189251Ssam SM_ENTRY(EAP, IDLE); 209189251Ssam} 210189251Ssam 211189251Ssam 212189251Ssam/* 213189251Ssam * This state is entered when an EAP packet is received (eapReq == TRUE) to 214189251Ssam * parse the packet header. 215189251Ssam */ 216189251SsamSM_STATE(EAP, RECEIVED) 217189251Ssam{ 218189251Ssam const struct wpabuf *eapReqData; 219189251Ssam 220189251Ssam SM_ENTRY(EAP, RECEIVED); 221189251Ssam eapReqData = eapol_get_eapReqData(sm); 222189251Ssam /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */ 223189251Ssam eap_sm_parseEapReq(sm, eapReqData); 224189251Ssam sm->num_rounds++; 225189251Ssam} 226189251Ssam 227189251Ssam 228189251Ssam/* 229189251Ssam * This state is entered when a request for a new type comes in. Either the 230189251Ssam * correct method is started, or a Nak response is built. 231189251Ssam */ 232189251SsamSM_STATE(EAP, GET_METHOD) 233189251Ssam{ 234189251Ssam int reinit; 235189251Ssam EapType method; 236252726Srpaulo const struct eap_method *eap_method; 237189251Ssam 238189251Ssam SM_ENTRY(EAP, GET_METHOD); 239189251Ssam 240189251Ssam if (sm->reqMethod == EAP_TYPE_EXPANDED) 241189251Ssam method = sm->reqVendorMethod; 242189251Ssam else 243189251Ssam method = sm->reqMethod; 244189251Ssam 245252726Srpaulo eap_method = eap_peer_get_eap_method(sm->reqVendor, method); 246252726Srpaulo 247189251Ssam if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) { 248189251Ssam wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed", 249189251Ssam sm->reqVendor, method); 250214734Srpaulo wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD 251214734Srpaulo "vendor=%u method=%u -> NAK", 252214734Srpaulo sm->reqVendor, method); 253252726Srpaulo eap_notify_status(sm, "refuse proposed method", 254252726Srpaulo eap_method ? eap_method->name : "unknown"); 255189251Ssam goto nak; 256189251Ssam } 257189251Ssam 258214734Srpaulo wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD 259214734Srpaulo "vendor=%u method=%u", sm->reqVendor, method); 260214734Srpaulo 261252726Srpaulo eap_notify_status(sm, "accept proposed method", 262252726Srpaulo eap_method ? eap_method->name : "unknown"); 263189251Ssam /* 264189251Ssam * RFC 4137 does not define specific operation for fast 265189251Ssam * re-authentication (session resumption). The design here is to allow 266189251Ssam * the previously used method data to be maintained for 267189251Ssam * re-authentication if the method support session resumption. 268189251Ssam * Otherwise, the previously used method data is freed and a new method 269189251Ssam * is allocated here. 270189251Ssam */ 271189251Ssam if (sm->fast_reauth && 272189251Ssam sm->m && sm->m->vendor == sm->reqVendor && 273189251Ssam sm->m->method == method && 274189251Ssam sm->m->has_reauth_data && 275189251Ssam sm->m->has_reauth_data(sm, sm->eap_method_priv)) { 276189251Ssam wpa_printf(MSG_DEBUG, "EAP: Using previous method data" 277189251Ssam " for fast re-authentication"); 278189251Ssam reinit = 1; 279189251Ssam } else { 280189251Ssam eap_deinit_prev_method(sm, "GET_METHOD"); 281189251Ssam reinit = 0; 282189251Ssam } 283189251Ssam 284189251Ssam sm->selectedMethod = sm->reqMethod; 285189251Ssam if (sm->m == NULL) 286252726Srpaulo sm->m = eap_method; 287189251Ssam if (!sm->m) { 288189251Ssam wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: " 289189251Ssam "vendor %d method %d", 290189251Ssam sm->reqVendor, method); 291189251Ssam goto nak; 292189251Ssam } 293189251Ssam 294252726Srpaulo sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; 295252726Srpaulo 296189251Ssam wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: " 297189251Ssam "vendor %u method %u (%s)", 298189251Ssam sm->reqVendor, method, sm->m->name); 299189251Ssam if (reinit) 300189251Ssam sm->eap_method_priv = sm->m->init_for_reauth( 301189251Ssam sm, sm->eap_method_priv); 302189251Ssam else 303189251Ssam sm->eap_method_priv = sm->m->init(sm); 304189251Ssam 305189251Ssam if (sm->eap_method_priv == NULL) { 306189251Ssam struct eap_peer_config *config = eap_get_config(sm); 307189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, 308189251Ssam "EAP: Failed to initialize EAP method: vendor %u " 309189251Ssam "method %u (%s)", 310189251Ssam sm->reqVendor, method, sm->m->name); 311189251Ssam sm->m = NULL; 312189251Ssam sm->methodState = METHOD_NONE; 313189251Ssam sm->selectedMethod = EAP_TYPE_NONE; 314189251Ssam if (sm->reqMethod == EAP_TYPE_TLS && config && 315189251Ssam (config->pending_req_pin || 316189251Ssam config->pending_req_passphrase)) { 317189251Ssam /* 318189251Ssam * Return without generating Nak in order to allow 319189251Ssam * entering of PIN code or passphrase to retry the 320189251Ssam * current EAP packet. 321189251Ssam */ 322189251Ssam wpa_printf(MSG_DEBUG, "EAP: Pending PIN/passphrase " 323189251Ssam "request - skip Nak"); 324189251Ssam return; 325189251Ssam } 326189251Ssam 327189251Ssam goto nak; 328189251Ssam } 329189251Ssam 330189251Ssam sm->methodState = METHOD_INIT; 331189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_METHOD 332189251Ssam "EAP vendor %u method %u (%s) selected", 333189251Ssam sm->reqVendor, method, sm->m->name); 334189251Ssam return; 335189251Ssam 336189251Ssamnak: 337189251Ssam wpabuf_free(sm->eapRespData); 338189251Ssam sm->eapRespData = NULL; 339189251Ssam sm->eapRespData = eap_sm_buildNak(sm, sm->reqId); 340189251Ssam} 341189251Ssam 342189251Ssam 343189251Ssam/* 344189251Ssam * The method processing happens here. The request from the authenticator is 345189251Ssam * processed, and an appropriate response packet is built. 346189251Ssam */ 347189251SsamSM_STATE(EAP, METHOD) 348189251Ssam{ 349189251Ssam struct wpabuf *eapReqData; 350189251Ssam struct eap_method_ret ret; 351252726Srpaulo int min_len = 1; 352189251Ssam 353189251Ssam SM_ENTRY(EAP, METHOD); 354189251Ssam if (sm->m == NULL) { 355189251Ssam wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected"); 356189251Ssam return; 357189251Ssam } 358189251Ssam 359189251Ssam eapReqData = eapol_get_eapReqData(sm); 360252726Srpaulo if (sm->m->vendor == EAP_VENDOR_IETF && sm->m->method == EAP_TYPE_LEAP) 361252726Srpaulo min_len = 0; /* LEAP uses EAP-Success without payload */ 362252726Srpaulo if (!eap_hdr_len_valid(eapReqData, min_len)) 363252726Srpaulo return; 364189251Ssam 365189251Ssam /* 366189251Ssam * Get ignore, methodState, decision, allowNotifications, and 367189251Ssam * eapRespData. RFC 4137 uses three separate method procedure (check, 368189251Ssam * process, and buildResp) in this state. These have been combined into 369189251Ssam * a single function call to m->process() in order to optimize EAP 370189251Ssam * method implementation interface a bit. These procedures are only 371189251Ssam * used from within this METHOD state, so there is no need to keep 372189251Ssam * these as separate C functions. 373189251Ssam * 374189251Ssam * The RFC 4137 procedures return values as follows: 375189251Ssam * ignore = m.check(eapReqData) 376189251Ssam * (methodState, decision, allowNotifications) = m.process(eapReqData) 377189251Ssam * eapRespData = m.buildResp(reqId) 378189251Ssam */ 379189251Ssam os_memset(&ret, 0, sizeof(ret)); 380189251Ssam ret.ignore = sm->ignore; 381189251Ssam ret.methodState = sm->methodState; 382189251Ssam ret.decision = sm->decision; 383189251Ssam ret.allowNotifications = sm->allowNotifications; 384189251Ssam wpabuf_free(sm->eapRespData); 385189251Ssam sm->eapRespData = NULL; 386189251Ssam sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret, 387189251Ssam eapReqData); 388189251Ssam wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s " 389189251Ssam "methodState=%s decision=%s", 390189251Ssam ret.ignore ? "TRUE" : "FALSE", 391189251Ssam eap_sm_method_state_txt(ret.methodState), 392189251Ssam eap_sm_decision_txt(ret.decision)); 393189251Ssam 394189251Ssam sm->ignore = ret.ignore; 395189251Ssam if (sm->ignore) 396189251Ssam return; 397189251Ssam sm->methodState = ret.methodState; 398189251Ssam sm->decision = ret.decision; 399189251Ssam sm->allowNotifications = ret.allowNotifications; 400189251Ssam 401189251Ssam if (sm->m->isKeyAvailable && sm->m->getKey && 402189251Ssam sm->m->isKeyAvailable(sm, sm->eap_method_priv)) { 403189251Ssam os_free(sm->eapKeyData); 404189251Ssam sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, 405189251Ssam &sm->eapKeyDataLen); 406189251Ssam } 407189251Ssam} 408189251Ssam 409189251Ssam 410189251Ssam/* 411189251Ssam * This state signals the lower layer that a response packet is ready to be 412189251Ssam * sent. 413189251Ssam */ 414189251SsamSM_STATE(EAP, SEND_RESPONSE) 415189251Ssam{ 416189251Ssam SM_ENTRY(EAP, SEND_RESPONSE); 417189251Ssam wpabuf_free(sm->lastRespData); 418189251Ssam if (sm->eapRespData) { 419189251Ssam if (sm->workaround) 420189251Ssam os_memcpy(sm->last_md5, sm->req_md5, 16); 421189251Ssam sm->lastId = sm->reqId; 422189251Ssam sm->lastRespData = wpabuf_dup(sm->eapRespData); 423189251Ssam eapol_set_bool(sm, EAPOL_eapResp, TRUE); 424189251Ssam } else 425189251Ssam sm->lastRespData = NULL; 426189251Ssam eapol_set_bool(sm, EAPOL_eapReq, FALSE); 427189251Ssam eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); 428189251Ssam} 429189251Ssam 430189251Ssam 431189251Ssam/* 432189251Ssam * This state signals the lower layer that the request was discarded, and no 433189251Ssam * response packet will be sent at this time. 434189251Ssam */ 435189251SsamSM_STATE(EAP, DISCARD) 436189251Ssam{ 437189251Ssam SM_ENTRY(EAP, DISCARD); 438189251Ssam eapol_set_bool(sm, EAPOL_eapReq, FALSE); 439189251Ssam eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); 440189251Ssam} 441189251Ssam 442189251Ssam 443189251Ssam/* 444189251Ssam * Handles requests for Identity method and builds a response. 445189251Ssam */ 446189251SsamSM_STATE(EAP, IDENTITY) 447189251Ssam{ 448189251Ssam const struct wpabuf *eapReqData; 449189251Ssam 450189251Ssam SM_ENTRY(EAP, IDENTITY); 451189251Ssam eapReqData = eapol_get_eapReqData(sm); 452252726Srpaulo if (!eap_hdr_len_valid(eapReqData, 1)) 453252726Srpaulo return; 454189251Ssam eap_sm_processIdentity(sm, eapReqData); 455189251Ssam wpabuf_free(sm->eapRespData); 456189251Ssam sm->eapRespData = NULL; 457189251Ssam sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId, 0); 458189251Ssam} 459189251Ssam 460189251Ssam 461189251Ssam/* 462189251Ssam * Handles requests for Notification method and builds a response. 463189251Ssam */ 464189251SsamSM_STATE(EAP, NOTIFICATION) 465189251Ssam{ 466189251Ssam const struct wpabuf *eapReqData; 467189251Ssam 468189251Ssam SM_ENTRY(EAP, NOTIFICATION); 469189251Ssam eapReqData = eapol_get_eapReqData(sm); 470252726Srpaulo if (!eap_hdr_len_valid(eapReqData, 1)) 471252726Srpaulo return; 472189251Ssam eap_sm_processNotify(sm, eapReqData); 473189251Ssam wpabuf_free(sm->eapRespData); 474189251Ssam sm->eapRespData = NULL; 475189251Ssam sm->eapRespData = eap_sm_buildNotify(sm->reqId); 476189251Ssam} 477189251Ssam 478189251Ssam 479189251Ssam/* 480189251Ssam * This state retransmits the previous response packet. 481189251Ssam */ 482189251SsamSM_STATE(EAP, RETRANSMIT) 483189251Ssam{ 484189251Ssam SM_ENTRY(EAP, RETRANSMIT); 485189251Ssam wpabuf_free(sm->eapRespData); 486189251Ssam if (sm->lastRespData) 487189251Ssam sm->eapRespData = wpabuf_dup(sm->lastRespData); 488189251Ssam else 489189251Ssam sm->eapRespData = NULL; 490189251Ssam} 491189251Ssam 492189251Ssam 493189251Ssam/* 494189251Ssam * This state is entered in case of a successful completion of authentication 495189251Ssam * and state machine waits here until port is disabled or EAP authentication is 496189251Ssam * restarted. 497189251Ssam */ 498189251SsamSM_STATE(EAP, SUCCESS) 499189251Ssam{ 500189251Ssam SM_ENTRY(EAP, SUCCESS); 501189251Ssam if (sm->eapKeyData != NULL) 502189251Ssam sm->eapKeyAvailable = TRUE; 503189251Ssam eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); 504189251Ssam 505189251Ssam /* 506189251Ssam * RFC 4137 does not clear eapReq here, but this seems to be required 507189251Ssam * to avoid processing the same request twice when state machine is 508189251Ssam * initialized. 509189251Ssam */ 510189251Ssam eapol_set_bool(sm, EAPOL_eapReq, FALSE); 511189251Ssam 512189251Ssam /* 513189251Ssam * RFC 4137 does not set eapNoResp here, but this seems to be required 514189251Ssam * to get EAPOL Supplicant backend state machine into SUCCESS state. In 515189251Ssam * addition, either eapResp or eapNoResp is required to be set after 516189251Ssam * processing the received EAP frame. 517189251Ssam */ 518189251Ssam eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); 519189251Ssam 520189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS 521189251Ssam "EAP authentication completed successfully"); 522189251Ssam} 523189251Ssam 524189251Ssam 525189251Ssam/* 526189251Ssam * This state is entered in case of a failure and state machine waits here 527189251Ssam * until port is disabled or EAP authentication is restarted. 528189251Ssam */ 529189251SsamSM_STATE(EAP, FAILURE) 530189251Ssam{ 531189251Ssam SM_ENTRY(EAP, FAILURE); 532189251Ssam eapol_set_bool(sm, EAPOL_eapFail, TRUE); 533189251Ssam 534189251Ssam /* 535189251Ssam * RFC 4137 does not clear eapReq here, but this seems to be required 536189251Ssam * to avoid processing the same request twice when state machine is 537189251Ssam * initialized. 538189251Ssam */ 539189251Ssam eapol_set_bool(sm, EAPOL_eapReq, FALSE); 540189251Ssam 541189251Ssam /* 542189251Ssam * RFC 4137 does not set eapNoResp here. However, either eapResp or 543189251Ssam * eapNoResp is required to be set after processing the received EAP 544189251Ssam * frame. 545189251Ssam */ 546189251Ssam eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); 547189251Ssam 548189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE 549189251Ssam "EAP authentication failed"); 550189251Ssam 551189251Ssam sm->prev_failure = 1; 552189251Ssam} 553189251Ssam 554189251Ssam 555189251Ssamstatic int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId) 556189251Ssam{ 557189251Ssam /* 558189251Ssam * At least Microsoft IAS and Meetinghouse Aegis seem to be sending 559189251Ssam * EAP-Success/Failure with lastId + 1 even though RFC 3748 and 560189251Ssam * RFC 4137 require that reqId == lastId. In addition, it looks like 561189251Ssam * Ringmaster v2.1.2.0 would be using lastId + 2 in EAP-Success. 562189251Ssam * 563189251Ssam * Accept this kind of Id if EAP workarounds are enabled. These are 564189251Ssam * unauthenticated plaintext messages, so this should have minimal 565189251Ssam * security implications (bit easier to fake EAP-Success/Failure). 566189251Ssam */ 567189251Ssam if (sm->workaround && (reqId == ((lastId + 1) & 0xff) || 568189251Ssam reqId == ((lastId + 2) & 0xff))) { 569189251Ssam wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected " 570189251Ssam "identifier field in EAP Success: " 571189251Ssam "reqId=%d lastId=%d (these are supposed to be " 572189251Ssam "same)", reqId, lastId); 573189251Ssam return 1; 574189251Ssam } 575189251Ssam wpa_printf(MSG_DEBUG, "EAP: EAP-Success Id mismatch - reqId=%d " 576189251Ssam "lastId=%d", reqId, lastId); 577189251Ssam return 0; 578189251Ssam} 579189251Ssam 580189251Ssam 581189251Ssam/* 582189251Ssam * RFC 4137 - Appendix A.1: EAP Peer State Machine - State transitions 583189251Ssam */ 584189251Ssam 585189251Ssamstatic void eap_peer_sm_step_idle(struct eap_sm *sm) 586189251Ssam{ 587189251Ssam /* 588189251Ssam * The first three transitions are from RFC 4137. The last two are 589189251Ssam * local additions to handle special cases with LEAP and PEAP server 590189251Ssam * not sending EAP-Success in some cases. 591189251Ssam */ 592189251Ssam if (eapol_get_bool(sm, EAPOL_eapReq)) 593189251Ssam SM_ENTER(EAP, RECEIVED); 594189251Ssam else if ((eapol_get_bool(sm, EAPOL_altAccept) && 595189251Ssam sm->decision != DECISION_FAIL) || 596189251Ssam (eapol_get_int(sm, EAPOL_idleWhile) == 0 && 597189251Ssam sm->decision == DECISION_UNCOND_SUCC)) 598189251Ssam SM_ENTER(EAP, SUCCESS); 599189251Ssam else if (eapol_get_bool(sm, EAPOL_altReject) || 600189251Ssam (eapol_get_int(sm, EAPOL_idleWhile) == 0 && 601189251Ssam sm->decision != DECISION_UNCOND_SUCC) || 602189251Ssam (eapol_get_bool(sm, EAPOL_altAccept) && 603189251Ssam sm->methodState != METHOD_CONT && 604189251Ssam sm->decision == DECISION_FAIL)) 605189251Ssam SM_ENTER(EAP, FAILURE); 606189251Ssam else if (sm->selectedMethod == EAP_TYPE_LEAP && 607189251Ssam sm->leap_done && sm->decision != DECISION_FAIL && 608189251Ssam sm->methodState == METHOD_DONE) 609189251Ssam SM_ENTER(EAP, SUCCESS); 610189251Ssam else if (sm->selectedMethod == EAP_TYPE_PEAP && 611189251Ssam sm->peap_done && sm->decision != DECISION_FAIL && 612189251Ssam sm->methodState == METHOD_DONE) 613189251Ssam SM_ENTER(EAP, SUCCESS); 614189251Ssam} 615189251Ssam 616189251Ssam 617189251Ssamstatic int eap_peer_req_is_duplicate(struct eap_sm *sm) 618189251Ssam{ 619189251Ssam int duplicate; 620189251Ssam 621189251Ssam duplicate = (sm->reqId == sm->lastId) && sm->rxReq; 622189251Ssam if (sm->workaround && duplicate && 623189251Ssam os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) { 624189251Ssam /* 625189251Ssam * RFC 4137 uses (reqId == lastId) as the only verification for 626189251Ssam * duplicate EAP requests. However, this misses cases where the 627189251Ssam * AS is incorrectly using the same id again; and 628189251Ssam * unfortunately, such implementations exist. Use MD5 hash as 629189251Ssam * an extra verification for the packets being duplicate to 630189251Ssam * workaround these issues. 631189251Ssam */ 632189251Ssam wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again, but " 633189251Ssam "EAP packets were not identical"); 634189251Ssam wpa_printf(MSG_DEBUG, "EAP: workaround - assume this is not a " 635189251Ssam "duplicate packet"); 636189251Ssam duplicate = 0; 637189251Ssam } 638189251Ssam 639189251Ssam return duplicate; 640189251Ssam} 641189251Ssam 642189251Ssam 643189251Ssamstatic void eap_peer_sm_step_received(struct eap_sm *sm) 644189251Ssam{ 645189251Ssam int duplicate = eap_peer_req_is_duplicate(sm); 646189251Ssam 647189251Ssam /* 648189251Ssam * Two special cases below for LEAP are local additions to work around 649189251Ssam * odd LEAP behavior (EAP-Success in the middle of authentication and 650189251Ssam * then swapped roles). Other transitions are based on RFC 4137. 651189251Ssam */ 652189251Ssam if (sm->rxSuccess && sm->decision != DECISION_FAIL && 653189251Ssam (sm->reqId == sm->lastId || 654189251Ssam eap_success_workaround(sm, sm->reqId, sm->lastId))) 655189251Ssam SM_ENTER(EAP, SUCCESS); 656189251Ssam else if (sm->methodState != METHOD_CONT && 657189251Ssam ((sm->rxFailure && 658189251Ssam sm->decision != DECISION_UNCOND_SUCC) || 659189251Ssam (sm->rxSuccess && sm->decision == DECISION_FAIL && 660189251Ssam (sm->selectedMethod != EAP_TYPE_LEAP || 661189251Ssam sm->methodState != METHOD_MAY_CONT))) && 662189251Ssam (sm->reqId == sm->lastId || 663189251Ssam eap_success_workaround(sm, sm->reqId, sm->lastId))) 664189251Ssam SM_ENTER(EAP, FAILURE); 665189251Ssam else if (sm->rxReq && duplicate) 666189251Ssam SM_ENTER(EAP, RETRANSMIT); 667189251Ssam else if (sm->rxReq && !duplicate && 668189251Ssam sm->reqMethod == EAP_TYPE_NOTIFICATION && 669189251Ssam sm->allowNotifications) 670189251Ssam SM_ENTER(EAP, NOTIFICATION); 671189251Ssam else if (sm->rxReq && !duplicate && 672189251Ssam sm->selectedMethod == EAP_TYPE_NONE && 673189251Ssam sm->reqMethod == EAP_TYPE_IDENTITY) 674189251Ssam SM_ENTER(EAP, IDENTITY); 675189251Ssam else if (sm->rxReq && !duplicate && 676189251Ssam sm->selectedMethod == EAP_TYPE_NONE && 677189251Ssam sm->reqMethod != EAP_TYPE_IDENTITY && 678189251Ssam sm->reqMethod != EAP_TYPE_NOTIFICATION) 679189251Ssam SM_ENTER(EAP, GET_METHOD); 680189251Ssam else if (sm->rxReq && !duplicate && 681189251Ssam sm->reqMethod == sm->selectedMethod && 682189251Ssam sm->methodState != METHOD_DONE) 683189251Ssam SM_ENTER(EAP, METHOD); 684189251Ssam else if (sm->selectedMethod == EAP_TYPE_LEAP && 685189251Ssam (sm->rxSuccess || sm->rxResp)) 686189251Ssam SM_ENTER(EAP, METHOD); 687189251Ssam else 688189251Ssam SM_ENTER(EAP, DISCARD); 689189251Ssam} 690189251Ssam 691189251Ssam 692189251Ssamstatic void eap_peer_sm_step_local(struct eap_sm *sm) 693189251Ssam{ 694189251Ssam switch (sm->EAP_state) { 695189251Ssam case EAP_INITIALIZE: 696189251Ssam SM_ENTER(EAP, IDLE); 697189251Ssam break; 698189251Ssam case EAP_DISABLED: 699189251Ssam if (eapol_get_bool(sm, EAPOL_portEnabled) && 700189251Ssam !sm->force_disabled) 701189251Ssam SM_ENTER(EAP, INITIALIZE); 702189251Ssam break; 703189251Ssam case EAP_IDLE: 704189251Ssam eap_peer_sm_step_idle(sm); 705189251Ssam break; 706189251Ssam case EAP_RECEIVED: 707189251Ssam eap_peer_sm_step_received(sm); 708189251Ssam break; 709189251Ssam case EAP_GET_METHOD: 710189251Ssam if (sm->selectedMethod == sm->reqMethod) 711189251Ssam SM_ENTER(EAP, METHOD); 712189251Ssam else 713189251Ssam SM_ENTER(EAP, SEND_RESPONSE); 714189251Ssam break; 715189251Ssam case EAP_METHOD: 716189251Ssam if (sm->ignore) 717189251Ssam SM_ENTER(EAP, DISCARD); 718189251Ssam else 719189251Ssam SM_ENTER(EAP, SEND_RESPONSE); 720189251Ssam break; 721189251Ssam case EAP_SEND_RESPONSE: 722189251Ssam SM_ENTER(EAP, IDLE); 723189251Ssam break; 724189251Ssam case EAP_DISCARD: 725189251Ssam SM_ENTER(EAP, IDLE); 726189251Ssam break; 727189251Ssam case EAP_IDENTITY: 728189251Ssam SM_ENTER(EAP, SEND_RESPONSE); 729189251Ssam break; 730189251Ssam case EAP_NOTIFICATION: 731189251Ssam SM_ENTER(EAP, SEND_RESPONSE); 732189251Ssam break; 733189251Ssam case EAP_RETRANSMIT: 734189251Ssam SM_ENTER(EAP, SEND_RESPONSE); 735189251Ssam break; 736189251Ssam case EAP_SUCCESS: 737189251Ssam break; 738189251Ssam case EAP_FAILURE: 739189251Ssam break; 740189251Ssam } 741189251Ssam} 742189251Ssam 743189251Ssam 744189251SsamSM_STEP(EAP) 745189251Ssam{ 746189251Ssam /* Global transitions */ 747189251Ssam if (eapol_get_bool(sm, EAPOL_eapRestart) && 748189251Ssam eapol_get_bool(sm, EAPOL_portEnabled)) 749189251Ssam SM_ENTER_GLOBAL(EAP, INITIALIZE); 750189251Ssam else if (!eapol_get_bool(sm, EAPOL_portEnabled) || sm->force_disabled) 751189251Ssam SM_ENTER_GLOBAL(EAP, DISABLED); 752189251Ssam else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { 753189251Ssam /* RFC 4137 does not place any limit on number of EAP messages 754189251Ssam * in an authentication session. However, some error cases have 755189251Ssam * ended up in a state were EAP messages were sent between the 756189251Ssam * peer and server in a loop (e.g., TLS ACK frame in both 757189251Ssam * direction). Since this is quite undesired outcome, limit the 758189251Ssam * total number of EAP round-trips and abort authentication if 759189251Ssam * this limit is exceeded. 760189251Ssam */ 761189251Ssam if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { 762189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, "EAP: more than %d " 763189251Ssam "authentication rounds - abort", 764189251Ssam EAP_MAX_AUTH_ROUNDS); 765189251Ssam sm->num_rounds++; 766189251Ssam SM_ENTER_GLOBAL(EAP, FAILURE); 767189251Ssam } 768189251Ssam } else { 769189251Ssam /* Local transitions */ 770189251Ssam eap_peer_sm_step_local(sm); 771189251Ssam } 772189251Ssam} 773189251Ssam 774189251Ssam 775189251Ssamstatic Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, 776189251Ssam EapType method) 777189251Ssam{ 778189251Ssam if (!eap_allowed_method(sm, vendor, method)) { 779189251Ssam wpa_printf(MSG_DEBUG, "EAP: configuration does not allow: " 780189251Ssam "vendor %u method %u", vendor, method); 781189251Ssam return FALSE; 782189251Ssam } 783189251Ssam if (eap_peer_get_eap_method(vendor, method)) 784189251Ssam return TRUE; 785189251Ssam wpa_printf(MSG_DEBUG, "EAP: not included in build: " 786189251Ssam "vendor %u method %u", vendor, method); 787189251Ssam return FALSE; 788189251Ssam} 789189251Ssam 790189251Ssam 791189251Ssamstatic struct wpabuf * eap_sm_build_expanded_nak( 792189251Ssam struct eap_sm *sm, int id, const struct eap_method *methods, 793189251Ssam size_t count) 794189251Ssam{ 795189251Ssam struct wpabuf *resp; 796189251Ssam int found = 0; 797189251Ssam const struct eap_method *m; 798189251Ssam 799189251Ssam wpa_printf(MSG_DEBUG, "EAP: Building expanded EAP-Nak"); 800189251Ssam 801189251Ssam /* RFC 3748 - 5.3.2: Expanded Nak */ 802189251Ssam resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED, 803189251Ssam 8 + 8 * (count + 1), EAP_CODE_RESPONSE, id); 804189251Ssam if (resp == NULL) 805189251Ssam return NULL; 806189251Ssam 807189251Ssam wpabuf_put_be24(resp, EAP_VENDOR_IETF); 808189251Ssam wpabuf_put_be32(resp, EAP_TYPE_NAK); 809189251Ssam 810189251Ssam for (m = methods; m; m = m->next) { 811189251Ssam if (sm->reqVendor == m->vendor && 812189251Ssam sm->reqVendorMethod == m->method) 813189251Ssam continue; /* do not allow the current method again */ 814189251Ssam if (eap_allowed_method(sm, m->vendor, m->method)) { 815189251Ssam wpa_printf(MSG_DEBUG, "EAP: allowed type: " 816189251Ssam "vendor=%u method=%u", 817189251Ssam m->vendor, m->method); 818189251Ssam wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); 819189251Ssam wpabuf_put_be24(resp, m->vendor); 820189251Ssam wpabuf_put_be32(resp, m->method); 821189251Ssam 822189251Ssam found++; 823189251Ssam } 824189251Ssam } 825189251Ssam if (!found) { 826189251Ssam wpa_printf(MSG_DEBUG, "EAP: no more allowed methods"); 827189251Ssam wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); 828189251Ssam wpabuf_put_be24(resp, EAP_VENDOR_IETF); 829189251Ssam wpabuf_put_be32(resp, EAP_TYPE_NONE); 830189251Ssam } 831189251Ssam 832189251Ssam eap_update_len(resp); 833189251Ssam 834189251Ssam return resp; 835189251Ssam} 836189251Ssam 837189251Ssam 838189251Ssamstatic struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id) 839189251Ssam{ 840189251Ssam struct wpabuf *resp; 841189251Ssam u8 *start; 842189251Ssam int found = 0, expanded_found = 0; 843189251Ssam size_t count; 844189251Ssam const struct eap_method *methods, *m; 845189251Ssam 846189251Ssam wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %u " 847189251Ssam "vendor=%u method=%u not allowed)", sm->reqMethod, 848189251Ssam sm->reqVendor, sm->reqVendorMethod); 849189251Ssam methods = eap_peer_get_methods(&count); 850189251Ssam if (methods == NULL) 851189251Ssam return NULL; 852189251Ssam if (sm->reqMethod == EAP_TYPE_EXPANDED) 853189251Ssam return eap_sm_build_expanded_nak(sm, id, methods, count); 854189251Ssam 855189251Ssam /* RFC 3748 - 5.3.1: Legacy Nak */ 856189251Ssam resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, 857189251Ssam sizeof(struct eap_hdr) + 1 + count + 1, 858189251Ssam EAP_CODE_RESPONSE, id); 859189251Ssam if (resp == NULL) 860189251Ssam return NULL; 861189251Ssam 862189251Ssam start = wpabuf_put(resp, 0); 863189251Ssam for (m = methods; m; m = m->next) { 864189251Ssam if (m->vendor == EAP_VENDOR_IETF && m->method == sm->reqMethod) 865189251Ssam continue; /* do not allow the current method again */ 866189251Ssam if (eap_allowed_method(sm, m->vendor, m->method)) { 867189251Ssam if (m->vendor != EAP_VENDOR_IETF) { 868189251Ssam if (expanded_found) 869189251Ssam continue; 870189251Ssam expanded_found = 1; 871189251Ssam wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); 872189251Ssam } else 873189251Ssam wpabuf_put_u8(resp, m->method); 874189251Ssam found++; 875189251Ssam } 876189251Ssam } 877189251Ssam if (!found) 878189251Ssam wpabuf_put_u8(resp, EAP_TYPE_NONE); 879189251Ssam wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", start, found); 880189251Ssam 881189251Ssam eap_update_len(resp); 882189251Ssam 883189251Ssam return resp; 884189251Ssam} 885189251Ssam 886189251Ssam 887189251Ssamstatic void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) 888189251Ssam{ 889252726Srpaulo const u8 *pos; 890252726Srpaulo size_t msg_len; 891189251Ssam 892189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED 893189251Ssam "EAP authentication started"); 894189251Ssam 895252726Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req, 896252726Srpaulo &msg_len); 897252726Srpaulo if (pos == NULL) 898252726Srpaulo return; 899252726Srpaulo 900189251Ssam /* 901189251Ssam * RFC 3748 - 5.1: Identity 902189251Ssam * Data field may contain a displayable message in UTF-8. If this 903189251Ssam * includes NUL-character, only the data before that should be 904189251Ssam * displayed. Some EAP implementasitons may piggy-back additional 905189251Ssam * options after the NUL. 906189251Ssam */ 907189251Ssam /* TODO: could save displayable message so that it can be shown to the 908189251Ssam * user in case of interaction is required */ 909189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data", 910252726Srpaulo pos, msg_len); 911189251Ssam} 912189251Ssam 913189251Ssam 914189251Ssam#ifdef PCSC_FUNCS 915252726Srpaulo 916252726Srpaulo/* 917252726Srpaulo * Rules for figuring out MNC length based on IMSI for SIM cards that do not 918252726Srpaulo * include MNC length field. 919252726Srpaulo */ 920252726Srpaulostatic int mnc_len_from_imsi(const char *imsi) 921252726Srpaulo{ 922252726Srpaulo char mcc_str[4]; 923252726Srpaulo unsigned int mcc; 924252726Srpaulo 925252726Srpaulo os_memcpy(mcc_str, imsi, 3); 926252726Srpaulo mcc_str[3] = '\0'; 927252726Srpaulo mcc = atoi(mcc_str); 928252726Srpaulo 929252726Srpaulo if (mcc == 244) 930252726Srpaulo return 2; /* Networks in Finland use 2-digit MNC */ 931252726Srpaulo 932252726Srpaulo return -1; 933252726Srpaulo} 934252726Srpaulo 935252726Srpaulo 936252726Srpaulostatic int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi, 937252726Srpaulo size_t max_len, size_t *imsi_len) 938252726Srpaulo{ 939252726Srpaulo int mnc_len; 940252726Srpaulo char *pos, mnc[4]; 941252726Srpaulo 942252726Srpaulo if (*imsi_len + 36 > max_len) { 943252726Srpaulo wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer"); 944252726Srpaulo return -1; 945252726Srpaulo } 946252726Srpaulo 947252726Srpaulo /* MNC (2 or 3 digits) */ 948252726Srpaulo mnc_len = scard_get_mnc_len(sm->scard_ctx); 949252726Srpaulo if (mnc_len < 0) 950252726Srpaulo mnc_len = mnc_len_from_imsi(imsi); 951252726Srpaulo if (mnc_len < 0) { 952252726Srpaulo wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM " 953252726Srpaulo "assuming 3"); 954252726Srpaulo mnc_len = 3; 955252726Srpaulo } 956252726Srpaulo 957252726Srpaulo if (mnc_len == 2) { 958252726Srpaulo mnc[0] = '0'; 959252726Srpaulo mnc[1] = imsi[3]; 960252726Srpaulo mnc[2] = imsi[4]; 961252726Srpaulo } else if (mnc_len == 3) { 962252726Srpaulo mnc[0] = imsi[3]; 963252726Srpaulo mnc[1] = imsi[4]; 964252726Srpaulo mnc[2] = imsi[5]; 965252726Srpaulo } 966252726Srpaulo mnc[3] = '\0'; 967252726Srpaulo 968252726Srpaulo pos = imsi + *imsi_len; 969252726Srpaulo pos += os_snprintf(pos, imsi + max_len - pos, 970252726Srpaulo "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org", 971252726Srpaulo mnc, imsi[0], imsi[1], imsi[2]); 972252726Srpaulo *imsi_len = pos - imsi; 973252726Srpaulo 974252726Srpaulo return 0; 975252726Srpaulo} 976252726Srpaulo 977252726Srpaulo 978189251Ssamstatic int eap_sm_imsi_identity(struct eap_sm *sm, 979189251Ssam struct eap_peer_config *conf) 980189251Ssam{ 981252726Srpaulo enum { EAP_SM_SIM, EAP_SM_AKA, EAP_SM_AKA_PRIME } method = EAP_SM_SIM; 982189251Ssam char imsi[100]; 983189251Ssam size_t imsi_len; 984189251Ssam struct eap_method_type *m = conf->eap_methods; 985189251Ssam int i; 986189251Ssam 987189251Ssam imsi_len = sizeof(imsi); 988189251Ssam if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) { 989189251Ssam wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM"); 990189251Ssam return -1; 991189251Ssam } 992189251Ssam 993189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len); 994189251Ssam 995252726Srpaulo if (imsi_len < 7) { 996252726Srpaulo wpa_printf(MSG_WARNING, "Too short IMSI for SIM identity"); 997252726Srpaulo return -1; 998252726Srpaulo } 999252726Srpaulo 1000252726Srpaulo if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) { 1001252726Srpaulo wpa_printf(MSG_WARNING, "Could not add realm to SIM identity"); 1002252726Srpaulo return -1; 1003252726Srpaulo } 1004252726Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "IMSI + realm", (u8 *) imsi, imsi_len); 1005252726Srpaulo 1006189251Ssam for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF || 1007189251Ssam m[i].method != EAP_TYPE_NONE); i++) { 1008189251Ssam if (m[i].vendor == EAP_VENDOR_IETF && 1009252726Srpaulo m[i].method == EAP_TYPE_AKA_PRIME) { 1010252726Srpaulo method = EAP_SM_AKA_PRIME; 1011252726Srpaulo break; 1012252726Srpaulo } 1013252726Srpaulo 1014252726Srpaulo if (m[i].vendor == EAP_VENDOR_IETF && 1015189251Ssam m[i].method == EAP_TYPE_AKA) { 1016252726Srpaulo method = EAP_SM_AKA; 1017189251Ssam break; 1018189251Ssam } 1019189251Ssam } 1020189251Ssam 1021189251Ssam os_free(conf->identity); 1022189251Ssam conf->identity = os_malloc(1 + imsi_len); 1023189251Ssam if (conf->identity == NULL) { 1024189251Ssam wpa_printf(MSG_WARNING, "Failed to allocate buffer for " 1025189251Ssam "IMSI-based identity"); 1026189251Ssam return -1; 1027189251Ssam } 1028189251Ssam 1029252726Srpaulo switch (method) { 1030252726Srpaulo case EAP_SM_SIM: 1031252726Srpaulo conf->identity[0] = '1'; 1032252726Srpaulo break; 1033252726Srpaulo case EAP_SM_AKA: 1034252726Srpaulo conf->identity[0] = '0'; 1035252726Srpaulo break; 1036252726Srpaulo case EAP_SM_AKA_PRIME: 1037252726Srpaulo conf->identity[0] = '6'; 1038252726Srpaulo break; 1039252726Srpaulo } 1040189251Ssam os_memcpy(conf->identity + 1, imsi, imsi_len); 1041189251Ssam conf->identity_len = 1 + imsi_len; 1042189251Ssam 1043189251Ssam return 0; 1044189251Ssam} 1045252726Srpaulo 1046189251Ssam#endif /* PCSC_FUNCS */ 1047189251Ssam 1048189251Ssam 1049189251Ssamstatic int eap_sm_set_scard_pin(struct eap_sm *sm, 1050189251Ssam struct eap_peer_config *conf) 1051189251Ssam{ 1052189251Ssam#ifdef PCSC_FUNCS 1053189251Ssam if (scard_set_pin(sm->scard_ctx, conf->pin)) { 1054189251Ssam /* 1055189251Ssam * Make sure the same PIN is not tried again in order to avoid 1056189251Ssam * blocking SIM. 1057189251Ssam */ 1058189251Ssam os_free(conf->pin); 1059189251Ssam conf->pin = NULL; 1060189251Ssam 1061189251Ssam wpa_printf(MSG_WARNING, "PIN validation failed"); 1062189251Ssam eap_sm_request_pin(sm); 1063189251Ssam return -1; 1064189251Ssam } 1065189251Ssam return 0; 1066189251Ssam#else /* PCSC_FUNCS */ 1067189251Ssam return -1; 1068189251Ssam#endif /* PCSC_FUNCS */ 1069189251Ssam} 1070189251Ssam 1071189251Ssamstatic int eap_sm_get_scard_identity(struct eap_sm *sm, 1072189251Ssam struct eap_peer_config *conf) 1073189251Ssam{ 1074189251Ssam#ifdef PCSC_FUNCS 1075189251Ssam if (eap_sm_set_scard_pin(sm, conf)) 1076189251Ssam return -1; 1077189251Ssam 1078189251Ssam return eap_sm_imsi_identity(sm, conf); 1079189251Ssam#else /* PCSC_FUNCS */ 1080189251Ssam return -1; 1081189251Ssam#endif /* PCSC_FUNCS */ 1082189251Ssam} 1083189251Ssam 1084189251Ssam 1085189251Ssam/** 1086189251Ssam * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network 1087189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1088189251Ssam * @id: EAP identifier for the packet 1089189251Ssam * @encrypted: Whether the packet is for encrypted tunnel (EAP phase 2) 1090189251Ssam * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on 1091189251Ssam * failure 1092189251Ssam * 1093189251Ssam * This function allocates and builds an EAP-Identity/Response packet for the 1094189251Ssam * current network. The caller is responsible for freeing the returned data. 1095189251Ssam */ 1096189251Ssamstruct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted) 1097189251Ssam{ 1098189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1099189251Ssam struct wpabuf *resp; 1100189251Ssam const u8 *identity; 1101189251Ssam size_t identity_len; 1102189251Ssam 1103189251Ssam if (config == NULL) { 1104189251Ssam wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration " 1105189251Ssam "was not available"); 1106189251Ssam return NULL; 1107189251Ssam } 1108189251Ssam 1109189251Ssam if (sm->m && sm->m->get_identity && 1110189251Ssam (identity = sm->m->get_identity(sm, sm->eap_method_priv, 1111189251Ssam &identity_len)) != NULL) { 1112189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth " 1113189251Ssam "identity", identity, identity_len); 1114189251Ssam } else if (!encrypted && config->anonymous_identity) { 1115189251Ssam identity = config->anonymous_identity; 1116189251Ssam identity_len = config->anonymous_identity_len; 1117189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity", 1118189251Ssam identity, identity_len); 1119189251Ssam } else { 1120189251Ssam identity = config->identity; 1121189251Ssam identity_len = config->identity_len; 1122189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity", 1123189251Ssam identity, identity_len); 1124189251Ssam } 1125189251Ssam 1126189251Ssam if (identity == NULL) { 1127189251Ssam wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity " 1128189251Ssam "configuration was not available"); 1129189251Ssam if (config->pcsc) { 1130189251Ssam if (eap_sm_get_scard_identity(sm, config) < 0) 1131189251Ssam return NULL; 1132189251Ssam identity = config->identity; 1133189251Ssam identity_len = config->identity_len; 1134189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " 1135189251Ssam "IMSI", identity, identity_len); 1136189251Ssam } else { 1137189251Ssam eap_sm_request_identity(sm); 1138189251Ssam return NULL; 1139189251Ssam } 1140189251Ssam } else if (config->pcsc) { 1141189251Ssam if (eap_sm_set_scard_pin(sm, config) < 0) 1142189251Ssam return NULL; 1143189251Ssam } 1144189251Ssam 1145189251Ssam resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len, 1146189251Ssam EAP_CODE_RESPONSE, id); 1147189251Ssam if (resp == NULL) 1148189251Ssam return NULL; 1149189251Ssam 1150189251Ssam wpabuf_put_data(resp, identity, identity_len); 1151189251Ssam 1152189251Ssam return resp; 1153189251Ssam} 1154189251Ssam 1155189251Ssam 1156189251Ssamstatic void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req) 1157189251Ssam{ 1158189251Ssam const u8 *pos; 1159189251Ssam char *msg; 1160189251Ssam size_t i, msg_len; 1161189251Ssam 1162189251Ssam pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, req, 1163189251Ssam &msg_len); 1164189251Ssam if (pos == NULL) 1165189251Ssam return; 1166189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data", 1167189251Ssam pos, msg_len); 1168189251Ssam 1169189251Ssam msg = os_malloc(msg_len + 1); 1170189251Ssam if (msg == NULL) 1171189251Ssam return; 1172189251Ssam for (i = 0; i < msg_len; i++) 1173189251Ssam msg[i] = isprint(pos[i]) ? (char) pos[i] : '_'; 1174189251Ssam msg[msg_len] = '\0'; 1175189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, "%s%s", 1176189251Ssam WPA_EVENT_EAP_NOTIFICATION, msg); 1177189251Ssam os_free(msg); 1178189251Ssam} 1179189251Ssam 1180189251Ssam 1181189251Ssamstatic struct wpabuf * eap_sm_buildNotify(int id) 1182189251Ssam{ 1183189251Ssam struct wpabuf *resp; 1184189251Ssam 1185189251Ssam wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification"); 1186189251Ssam resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0, 1187189251Ssam EAP_CODE_RESPONSE, id); 1188189251Ssam if (resp == NULL) 1189189251Ssam return NULL; 1190189251Ssam 1191189251Ssam return resp; 1192189251Ssam} 1193189251Ssam 1194189251Ssam 1195189251Ssamstatic void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) 1196189251Ssam{ 1197189251Ssam const struct eap_hdr *hdr; 1198189251Ssam size_t plen; 1199189251Ssam const u8 *pos; 1200189251Ssam 1201189251Ssam sm->rxReq = sm->rxResp = sm->rxSuccess = sm->rxFailure = FALSE; 1202189251Ssam sm->reqId = 0; 1203189251Ssam sm->reqMethod = EAP_TYPE_NONE; 1204189251Ssam sm->reqVendor = EAP_VENDOR_IETF; 1205189251Ssam sm->reqVendorMethod = EAP_TYPE_NONE; 1206189251Ssam 1207189251Ssam if (req == NULL || wpabuf_len(req) < sizeof(*hdr)) 1208189251Ssam return; 1209189251Ssam 1210189251Ssam hdr = wpabuf_head(req); 1211189251Ssam plen = be_to_host16(hdr->length); 1212189251Ssam if (plen > wpabuf_len(req)) { 1213189251Ssam wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet " 1214189251Ssam "(len=%lu plen=%lu)", 1215189251Ssam (unsigned long) wpabuf_len(req), 1216189251Ssam (unsigned long) plen); 1217189251Ssam return; 1218189251Ssam } 1219189251Ssam 1220189251Ssam sm->reqId = hdr->identifier; 1221189251Ssam 1222189251Ssam if (sm->workaround) { 1223189251Ssam const u8 *addr[1]; 1224189251Ssam addr[0] = wpabuf_head(req); 1225189251Ssam md5_vector(1, addr, &plen, sm->req_md5); 1226189251Ssam } 1227189251Ssam 1228189251Ssam switch (hdr->code) { 1229189251Ssam case EAP_CODE_REQUEST: 1230189251Ssam if (plen < sizeof(*hdr) + 1) { 1231189251Ssam wpa_printf(MSG_DEBUG, "EAP: Too short EAP-Request - " 1232189251Ssam "no Type field"); 1233189251Ssam return; 1234189251Ssam } 1235189251Ssam sm->rxReq = TRUE; 1236189251Ssam pos = (const u8 *) (hdr + 1); 1237189251Ssam sm->reqMethod = *pos++; 1238189251Ssam if (sm->reqMethod == EAP_TYPE_EXPANDED) { 1239189251Ssam if (plen < sizeof(*hdr) + 8) { 1240189251Ssam wpa_printf(MSG_DEBUG, "EAP: Ignored truncated " 1241189251Ssam "expanded EAP-Packet (plen=%lu)", 1242189251Ssam (unsigned long) plen); 1243189251Ssam return; 1244189251Ssam } 1245189251Ssam sm->reqVendor = WPA_GET_BE24(pos); 1246189251Ssam pos += 3; 1247189251Ssam sm->reqVendorMethod = WPA_GET_BE32(pos); 1248189251Ssam } 1249189251Ssam wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request id=%d " 1250189251Ssam "method=%u vendor=%u vendorMethod=%u", 1251189251Ssam sm->reqId, sm->reqMethod, sm->reqVendor, 1252189251Ssam sm->reqVendorMethod); 1253189251Ssam break; 1254189251Ssam case EAP_CODE_RESPONSE: 1255189251Ssam if (sm->selectedMethod == EAP_TYPE_LEAP) { 1256189251Ssam /* 1257189251Ssam * LEAP differs from RFC 4137 by using reversed roles 1258189251Ssam * for mutual authentication and because of this, we 1259189251Ssam * need to accept EAP-Response frames if LEAP is used. 1260189251Ssam */ 1261189251Ssam if (plen < sizeof(*hdr) + 1) { 1262189251Ssam wpa_printf(MSG_DEBUG, "EAP: Too short " 1263189251Ssam "EAP-Response - no Type field"); 1264189251Ssam return; 1265189251Ssam } 1266189251Ssam sm->rxResp = TRUE; 1267189251Ssam pos = (const u8 *) (hdr + 1); 1268189251Ssam sm->reqMethod = *pos; 1269189251Ssam wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for " 1270189251Ssam "LEAP method=%d id=%d", 1271189251Ssam sm->reqMethod, sm->reqId); 1272189251Ssam break; 1273189251Ssam } 1274189251Ssam wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response"); 1275189251Ssam break; 1276189251Ssam case EAP_CODE_SUCCESS: 1277189251Ssam wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success"); 1278252726Srpaulo eap_notify_status(sm, "completion", "success"); 1279189251Ssam sm->rxSuccess = TRUE; 1280189251Ssam break; 1281189251Ssam case EAP_CODE_FAILURE: 1282189251Ssam wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure"); 1283252726Srpaulo eap_notify_status(sm, "completion", "failure"); 1284189251Ssam sm->rxFailure = TRUE; 1285189251Ssam break; 1286189251Ssam default: 1287189251Ssam wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown " 1288189251Ssam "code %d", hdr->code); 1289189251Ssam break; 1290189251Ssam } 1291189251Ssam} 1292189251Ssam 1293189251Ssam 1294214734Srpaulostatic void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, 1295214734Srpaulo union tls_event_data *data) 1296214734Srpaulo{ 1297214734Srpaulo struct eap_sm *sm = ctx; 1298214734Srpaulo char *hash_hex = NULL; 1299214734Srpaulo 1300214734Srpaulo switch (ev) { 1301252726Srpaulo case TLS_CERT_CHAIN_SUCCESS: 1302252726Srpaulo eap_notify_status(sm, "remote certificate verification", 1303252726Srpaulo "success"); 1304252726Srpaulo break; 1305214734Srpaulo case TLS_CERT_CHAIN_FAILURE: 1306214734Srpaulo wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR 1307214734Srpaulo "reason=%d depth=%d subject='%s' err='%s'", 1308214734Srpaulo data->cert_fail.reason, 1309214734Srpaulo data->cert_fail.depth, 1310214734Srpaulo data->cert_fail.subject, 1311214734Srpaulo data->cert_fail.reason_txt); 1312252726Srpaulo eap_notify_status(sm, "remote certificate verification", 1313252726Srpaulo data->cert_fail.reason_txt); 1314214734Srpaulo break; 1315214734Srpaulo case TLS_PEER_CERTIFICATE: 1316252726Srpaulo if (!sm->eapol_cb->notify_cert) 1317252726Srpaulo break; 1318252726Srpaulo 1319214734Srpaulo if (data->peer_cert.hash) { 1320214734Srpaulo size_t len = data->peer_cert.hash_len * 2 + 1; 1321214734Srpaulo hash_hex = os_malloc(len); 1322214734Srpaulo if (hash_hex) { 1323214734Srpaulo wpa_snprintf_hex(hash_hex, len, 1324214734Srpaulo data->peer_cert.hash, 1325214734Srpaulo data->peer_cert.hash_len); 1326214734Srpaulo } 1327214734Srpaulo } 1328214734Srpaulo 1329252726Srpaulo sm->eapol_cb->notify_cert(sm->eapol_ctx, 1330252726Srpaulo data->peer_cert.depth, 1331252726Srpaulo data->peer_cert.subject, 1332252726Srpaulo hash_hex, data->peer_cert.cert); 1333214734Srpaulo break; 1334252726Srpaulo case TLS_ALERT: 1335252726Srpaulo if (data->alert.is_local) 1336252726Srpaulo eap_notify_status(sm, "local TLS alert", 1337252726Srpaulo data->alert.description); 1338252726Srpaulo else 1339252726Srpaulo eap_notify_status(sm, "remote TLS alert", 1340252726Srpaulo data->alert.description); 1341252726Srpaulo break; 1342214734Srpaulo } 1343214734Srpaulo 1344214734Srpaulo os_free(hash_hex); 1345214734Srpaulo} 1346214734Srpaulo 1347214734Srpaulo 1348189251Ssam/** 1349189251Ssam * eap_peer_sm_init - Allocate and initialize EAP peer state machine 1350189251Ssam * @eapol_ctx: Context data to be used with eapol_cb calls 1351189251Ssam * @eapol_cb: Pointer to EAPOL callback functions 1352189251Ssam * @msg_ctx: Context data for wpa_msg() calls 1353189251Ssam * @conf: EAP configuration 1354189251Ssam * Returns: Pointer to the allocated EAP state machine or %NULL on failure 1355189251Ssam * 1356189251Ssam * This function allocates and initializes an EAP state machine. In addition, 1357189251Ssam * this initializes TLS library for the new EAP state machine. eapol_cb pointer 1358189251Ssam * will be in use until eap_peer_sm_deinit() is used to deinitialize this EAP 1359189251Ssam * state machine. Consequently, the caller must make sure that this data 1360189251Ssam * structure remains alive while the EAP state machine is active. 1361189251Ssam */ 1362189251Ssamstruct eap_sm * eap_peer_sm_init(void *eapol_ctx, 1363189251Ssam struct eapol_callbacks *eapol_cb, 1364189251Ssam void *msg_ctx, struct eap_config *conf) 1365189251Ssam{ 1366189251Ssam struct eap_sm *sm; 1367189251Ssam struct tls_config tlsconf; 1368189251Ssam 1369189251Ssam sm = os_zalloc(sizeof(*sm)); 1370189251Ssam if (sm == NULL) 1371189251Ssam return NULL; 1372189251Ssam sm->eapol_ctx = eapol_ctx; 1373189251Ssam sm->eapol_cb = eapol_cb; 1374189251Ssam sm->msg_ctx = msg_ctx; 1375252726Srpaulo sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; 1376189251Ssam sm->wps = conf->wps; 1377189251Ssam 1378189251Ssam os_memset(&tlsconf, 0, sizeof(tlsconf)); 1379189251Ssam tlsconf.opensc_engine_path = conf->opensc_engine_path; 1380189251Ssam tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path; 1381189251Ssam tlsconf.pkcs11_module_path = conf->pkcs11_module_path; 1382214734Srpaulo#ifdef CONFIG_FIPS 1383214734Srpaulo tlsconf.fips_mode = 1; 1384214734Srpaulo#endif /* CONFIG_FIPS */ 1385214734Srpaulo tlsconf.event_cb = eap_peer_sm_tls_event; 1386214734Srpaulo tlsconf.cb_ctx = sm; 1387252726Srpaulo tlsconf.cert_in_cb = conf->cert_in_cb; 1388189251Ssam sm->ssl_ctx = tls_init(&tlsconf); 1389189251Ssam if (sm->ssl_ctx == NULL) { 1390189251Ssam wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS " 1391189251Ssam "context."); 1392189251Ssam os_free(sm); 1393189251Ssam return NULL; 1394189251Ssam } 1395189251Ssam 1396252726Srpaulo sm->ssl_ctx2 = tls_init(&tlsconf); 1397252726Srpaulo if (sm->ssl_ctx2 == NULL) { 1398252726Srpaulo wpa_printf(MSG_INFO, "SSL: Failed to initialize TLS " 1399252726Srpaulo "context (2)."); 1400252726Srpaulo /* Run without separate TLS context within TLS tunnel */ 1401252726Srpaulo } 1402252726Srpaulo 1403189251Ssam return sm; 1404189251Ssam} 1405189251Ssam 1406189251Ssam 1407189251Ssam/** 1408189251Ssam * eap_peer_sm_deinit - Deinitialize and free an EAP peer state machine 1409189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1410189251Ssam * 1411189251Ssam * This function deinitializes EAP state machine and frees all allocated 1412189251Ssam * resources. 1413189251Ssam */ 1414189251Ssamvoid eap_peer_sm_deinit(struct eap_sm *sm) 1415189251Ssam{ 1416189251Ssam if (sm == NULL) 1417189251Ssam return; 1418189251Ssam eap_deinit_prev_method(sm, "EAP deinit"); 1419189251Ssam eap_sm_abort(sm); 1420252726Srpaulo if (sm->ssl_ctx2) 1421252726Srpaulo tls_deinit(sm->ssl_ctx2); 1422189251Ssam tls_deinit(sm->ssl_ctx); 1423189251Ssam os_free(sm); 1424189251Ssam} 1425189251Ssam 1426189251Ssam 1427189251Ssam/** 1428189251Ssam * eap_peer_sm_step - Step EAP peer state machine 1429189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1430189251Ssam * Returns: 1 if EAP state was changed or 0 if not 1431189251Ssam * 1432189251Ssam * This function advances EAP state machine to a new state to match with the 1433189251Ssam * current variables. This should be called whenever variables used by the EAP 1434189251Ssam * state machine have changed. 1435189251Ssam */ 1436189251Ssamint eap_peer_sm_step(struct eap_sm *sm) 1437189251Ssam{ 1438189251Ssam int res = 0; 1439189251Ssam do { 1440189251Ssam sm->changed = FALSE; 1441189251Ssam SM_STEP_RUN(EAP); 1442189251Ssam if (sm->changed) 1443189251Ssam res = 1; 1444189251Ssam } while (sm->changed); 1445189251Ssam return res; 1446189251Ssam} 1447189251Ssam 1448189251Ssam 1449189251Ssam/** 1450189251Ssam * eap_sm_abort - Abort EAP authentication 1451189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1452189251Ssam * 1453189251Ssam * Release system resources that have been allocated for the authentication 1454189251Ssam * session without fully deinitializing the EAP state machine. 1455189251Ssam */ 1456189251Ssamvoid eap_sm_abort(struct eap_sm *sm) 1457189251Ssam{ 1458189251Ssam wpabuf_free(sm->lastRespData); 1459189251Ssam sm->lastRespData = NULL; 1460189251Ssam wpabuf_free(sm->eapRespData); 1461189251Ssam sm->eapRespData = NULL; 1462189251Ssam os_free(sm->eapKeyData); 1463189251Ssam sm->eapKeyData = NULL; 1464189251Ssam 1465189251Ssam /* This is not clearly specified in the EAP statemachines draft, but 1466189251Ssam * it seems necessary to make sure that some of the EAPOL variables get 1467189251Ssam * cleared for the next authentication. */ 1468189251Ssam eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); 1469189251Ssam} 1470189251Ssam 1471189251Ssam 1472189251Ssam#ifdef CONFIG_CTRL_IFACE 1473189251Ssamstatic const char * eap_sm_state_txt(int state) 1474189251Ssam{ 1475189251Ssam switch (state) { 1476189251Ssam case EAP_INITIALIZE: 1477189251Ssam return "INITIALIZE"; 1478189251Ssam case EAP_DISABLED: 1479189251Ssam return "DISABLED"; 1480189251Ssam case EAP_IDLE: 1481189251Ssam return "IDLE"; 1482189251Ssam case EAP_RECEIVED: 1483189251Ssam return "RECEIVED"; 1484189251Ssam case EAP_GET_METHOD: 1485189251Ssam return "GET_METHOD"; 1486189251Ssam case EAP_METHOD: 1487189251Ssam return "METHOD"; 1488189251Ssam case EAP_SEND_RESPONSE: 1489189251Ssam return "SEND_RESPONSE"; 1490189251Ssam case EAP_DISCARD: 1491189251Ssam return "DISCARD"; 1492189251Ssam case EAP_IDENTITY: 1493189251Ssam return "IDENTITY"; 1494189251Ssam case EAP_NOTIFICATION: 1495189251Ssam return "NOTIFICATION"; 1496189251Ssam case EAP_RETRANSMIT: 1497189251Ssam return "RETRANSMIT"; 1498189251Ssam case EAP_SUCCESS: 1499189251Ssam return "SUCCESS"; 1500189251Ssam case EAP_FAILURE: 1501189251Ssam return "FAILURE"; 1502189251Ssam default: 1503189251Ssam return "UNKNOWN"; 1504189251Ssam } 1505189251Ssam} 1506189251Ssam#endif /* CONFIG_CTRL_IFACE */ 1507189251Ssam 1508189251Ssam 1509189251Ssam#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) 1510189251Ssamstatic const char * eap_sm_method_state_txt(EapMethodState state) 1511189251Ssam{ 1512189251Ssam switch (state) { 1513189251Ssam case METHOD_NONE: 1514189251Ssam return "NONE"; 1515189251Ssam case METHOD_INIT: 1516189251Ssam return "INIT"; 1517189251Ssam case METHOD_CONT: 1518189251Ssam return "CONT"; 1519189251Ssam case METHOD_MAY_CONT: 1520189251Ssam return "MAY_CONT"; 1521189251Ssam case METHOD_DONE: 1522189251Ssam return "DONE"; 1523189251Ssam default: 1524189251Ssam return "UNKNOWN"; 1525189251Ssam } 1526189251Ssam} 1527189251Ssam 1528189251Ssam 1529189251Ssamstatic const char * eap_sm_decision_txt(EapDecision decision) 1530189251Ssam{ 1531189251Ssam switch (decision) { 1532189251Ssam case DECISION_FAIL: 1533189251Ssam return "FAIL"; 1534189251Ssam case DECISION_COND_SUCC: 1535189251Ssam return "COND_SUCC"; 1536189251Ssam case DECISION_UNCOND_SUCC: 1537189251Ssam return "UNCOND_SUCC"; 1538189251Ssam default: 1539189251Ssam return "UNKNOWN"; 1540189251Ssam } 1541189251Ssam} 1542189251Ssam#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ 1543189251Ssam 1544189251Ssam 1545189251Ssam#ifdef CONFIG_CTRL_IFACE 1546189251Ssam 1547189251Ssam/** 1548189251Ssam * eap_sm_get_status - Get EAP state machine status 1549189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1550189251Ssam * @buf: Buffer for status information 1551189251Ssam * @buflen: Maximum buffer length 1552189251Ssam * @verbose: Whether to include verbose status information 1553189251Ssam * Returns: Number of bytes written to buf. 1554189251Ssam * 1555189251Ssam * Query EAP state machine for status information. This function fills in a 1556189251Ssam * text area with current status information from the EAPOL state machine. If 1557189251Ssam * the buffer (buf) is not large enough, status information will be truncated 1558189251Ssam * to fit the buffer. 1559189251Ssam */ 1560189251Ssamint eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) 1561189251Ssam{ 1562189251Ssam int len, ret; 1563189251Ssam 1564189251Ssam if (sm == NULL) 1565189251Ssam return 0; 1566189251Ssam 1567189251Ssam len = os_snprintf(buf, buflen, 1568189251Ssam "EAP state=%s\n", 1569189251Ssam eap_sm_state_txt(sm->EAP_state)); 1570189251Ssam if (len < 0 || (size_t) len >= buflen) 1571189251Ssam return 0; 1572189251Ssam 1573189251Ssam if (sm->selectedMethod != EAP_TYPE_NONE) { 1574189251Ssam const char *name; 1575189251Ssam if (sm->m) { 1576189251Ssam name = sm->m->name; 1577189251Ssam } else { 1578189251Ssam const struct eap_method *m = 1579189251Ssam eap_peer_get_eap_method(EAP_VENDOR_IETF, 1580189251Ssam sm->selectedMethod); 1581189251Ssam if (m) 1582189251Ssam name = m->name; 1583189251Ssam else 1584189251Ssam name = "?"; 1585189251Ssam } 1586189251Ssam ret = os_snprintf(buf + len, buflen - len, 1587189251Ssam "selectedMethod=%d (EAP-%s)\n", 1588189251Ssam sm->selectedMethod, name); 1589189251Ssam if (ret < 0 || (size_t) ret >= buflen - len) 1590189251Ssam return len; 1591189251Ssam len += ret; 1592189251Ssam 1593189251Ssam if (sm->m && sm->m->get_status) { 1594189251Ssam len += sm->m->get_status(sm, sm->eap_method_priv, 1595189251Ssam buf + len, buflen - len, 1596189251Ssam verbose); 1597189251Ssam } 1598189251Ssam } 1599189251Ssam 1600189251Ssam if (verbose) { 1601189251Ssam ret = os_snprintf(buf + len, buflen - len, 1602189251Ssam "reqMethod=%d\n" 1603189251Ssam "methodState=%s\n" 1604189251Ssam "decision=%s\n" 1605189251Ssam "ClientTimeout=%d\n", 1606189251Ssam sm->reqMethod, 1607189251Ssam eap_sm_method_state_txt(sm->methodState), 1608189251Ssam eap_sm_decision_txt(sm->decision), 1609189251Ssam sm->ClientTimeout); 1610189251Ssam if (ret < 0 || (size_t) ret >= buflen - len) 1611189251Ssam return len; 1612189251Ssam len += ret; 1613189251Ssam } 1614189251Ssam 1615189251Ssam return len; 1616189251Ssam} 1617189251Ssam#endif /* CONFIG_CTRL_IFACE */ 1618189251Ssam 1619189251Ssam 1620189251Ssam#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) 1621252726Srpaulostatic void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, 1622189251Ssam const char *msg, size_t msglen) 1623189251Ssam{ 1624189251Ssam struct eap_peer_config *config; 1625252726Srpaulo char *txt = NULL, *tmp; 1626189251Ssam 1627189251Ssam if (sm == NULL) 1628189251Ssam return; 1629189251Ssam config = eap_get_config(sm); 1630189251Ssam if (config == NULL) 1631189251Ssam return; 1632189251Ssam 1633252726Srpaulo switch (field) { 1634252726Srpaulo case WPA_CTRL_REQ_EAP_IDENTITY: 1635189251Ssam config->pending_req_identity++; 1636189251Ssam break; 1637252726Srpaulo case WPA_CTRL_REQ_EAP_PASSWORD: 1638189251Ssam config->pending_req_password++; 1639189251Ssam break; 1640252726Srpaulo case WPA_CTRL_REQ_EAP_NEW_PASSWORD: 1641189251Ssam config->pending_req_new_password++; 1642189251Ssam break; 1643252726Srpaulo case WPA_CTRL_REQ_EAP_PIN: 1644189251Ssam config->pending_req_pin++; 1645189251Ssam break; 1646252726Srpaulo case WPA_CTRL_REQ_EAP_OTP: 1647189251Ssam if (msg) { 1648189251Ssam tmp = os_malloc(msglen + 3); 1649189251Ssam if (tmp == NULL) 1650189251Ssam return; 1651189251Ssam tmp[0] = '['; 1652189251Ssam os_memcpy(tmp + 1, msg, msglen); 1653189251Ssam tmp[msglen + 1] = ']'; 1654189251Ssam tmp[msglen + 2] = '\0'; 1655189251Ssam txt = tmp; 1656189251Ssam os_free(config->pending_req_otp); 1657189251Ssam config->pending_req_otp = tmp; 1658189251Ssam config->pending_req_otp_len = msglen + 3; 1659189251Ssam } else { 1660189251Ssam if (config->pending_req_otp == NULL) 1661189251Ssam return; 1662189251Ssam txt = config->pending_req_otp; 1663189251Ssam } 1664189251Ssam break; 1665252726Srpaulo case WPA_CTRL_REQ_EAP_PASSPHRASE: 1666189251Ssam config->pending_req_passphrase++; 1667189251Ssam break; 1668189251Ssam default: 1669189251Ssam return; 1670189251Ssam } 1671189251Ssam 1672189251Ssam if (sm->eapol_cb->eap_param_needed) 1673189251Ssam sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt); 1674189251Ssam} 1675189251Ssam#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ 1676189251Ssam#define eap_sm_request(sm, type, msg, msglen) do { } while (0) 1677189251Ssam#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ 1678189251Ssam 1679252726Srpauloconst char * eap_sm_get_method_name(struct eap_sm *sm) 1680252726Srpaulo{ 1681252726Srpaulo if (sm->m == NULL) 1682252726Srpaulo return "UNKNOWN"; 1683252726Srpaulo return sm->m->name; 1684252726Srpaulo} 1685189251Ssam 1686252726Srpaulo 1687189251Ssam/** 1688189251Ssam * eap_sm_request_identity - Request identity from user (ctrl_iface) 1689189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1690189251Ssam * 1691189251Ssam * EAP methods can call this function to request identity information for the 1692189251Ssam * current network. This is normally called when the identity is not included 1693189251Ssam * in the network configuration. The request will be sent to monitor programs 1694189251Ssam * through the control interface. 1695189251Ssam */ 1696189251Ssamvoid eap_sm_request_identity(struct eap_sm *sm) 1697189251Ssam{ 1698252726Srpaulo eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0); 1699189251Ssam} 1700189251Ssam 1701189251Ssam 1702189251Ssam/** 1703189251Ssam * eap_sm_request_password - Request password from user (ctrl_iface) 1704189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1705189251Ssam * 1706189251Ssam * EAP methods can call this function to request password information for the 1707189251Ssam * current network. This is normally called when the password is not included 1708189251Ssam * in the network configuration. The request will be sent to monitor programs 1709189251Ssam * through the control interface. 1710189251Ssam */ 1711189251Ssamvoid eap_sm_request_password(struct eap_sm *sm) 1712189251Ssam{ 1713252726Srpaulo eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0); 1714189251Ssam} 1715189251Ssam 1716189251Ssam 1717189251Ssam/** 1718189251Ssam * eap_sm_request_new_password - Request new password from user (ctrl_iface) 1719189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1720189251Ssam * 1721189251Ssam * EAP methods can call this function to request new password information for 1722189251Ssam * the current network. This is normally called when the EAP method indicates 1723189251Ssam * that the current password has expired and password change is required. The 1724189251Ssam * request will be sent to monitor programs through the control interface. 1725189251Ssam */ 1726189251Ssamvoid eap_sm_request_new_password(struct eap_sm *sm) 1727189251Ssam{ 1728252726Srpaulo eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0); 1729189251Ssam} 1730189251Ssam 1731189251Ssam 1732189251Ssam/** 1733189251Ssam * eap_sm_request_pin - Request SIM or smart card PIN from user (ctrl_iface) 1734189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1735189251Ssam * 1736189251Ssam * EAP methods can call this function to request SIM or smart card PIN 1737189251Ssam * information for the current network. This is normally called when the PIN is 1738189251Ssam * not included in the network configuration. The request will be sent to 1739189251Ssam * monitor programs through the control interface. 1740189251Ssam */ 1741189251Ssamvoid eap_sm_request_pin(struct eap_sm *sm) 1742189251Ssam{ 1743252726Srpaulo eap_sm_request(sm, WPA_CTRL_REQ_EAP_PIN, NULL, 0); 1744189251Ssam} 1745189251Ssam 1746189251Ssam 1747189251Ssam/** 1748189251Ssam * eap_sm_request_otp - Request one time password from user (ctrl_iface) 1749189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1750189251Ssam * @msg: Message to be displayed to the user when asking for OTP 1751189251Ssam * @msg_len: Length of the user displayable message 1752189251Ssam * 1753189251Ssam * EAP methods can call this function to request open time password (OTP) for 1754189251Ssam * the current network. The request will be sent to monitor programs through 1755189251Ssam * the control interface. 1756189251Ssam */ 1757189251Ssamvoid eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len) 1758189251Ssam{ 1759252726Srpaulo eap_sm_request(sm, WPA_CTRL_REQ_EAP_OTP, msg, msg_len); 1760189251Ssam} 1761189251Ssam 1762189251Ssam 1763189251Ssam/** 1764189251Ssam * eap_sm_request_passphrase - Request passphrase from user (ctrl_iface) 1765189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1766189251Ssam * 1767189251Ssam * EAP methods can call this function to request passphrase for a private key 1768189251Ssam * for the current network. This is normally called when the passphrase is not 1769189251Ssam * included in the network configuration. The request will be sent to monitor 1770189251Ssam * programs through the control interface. 1771189251Ssam */ 1772189251Ssamvoid eap_sm_request_passphrase(struct eap_sm *sm) 1773189251Ssam{ 1774252726Srpaulo eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSPHRASE, NULL, 0); 1775189251Ssam} 1776189251Ssam 1777189251Ssam 1778189251Ssam/** 1779189251Ssam * eap_sm_notify_ctrl_attached - Notification of attached monitor 1780189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1781189251Ssam * 1782189251Ssam * Notify EAP state machines that a monitor was attached to the control 1783189251Ssam * interface to trigger re-sending of pending requests for user input. 1784189251Ssam */ 1785189251Ssamvoid eap_sm_notify_ctrl_attached(struct eap_sm *sm) 1786189251Ssam{ 1787189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1788189251Ssam 1789189251Ssam if (config == NULL) 1790189251Ssam return; 1791189251Ssam 1792189251Ssam /* Re-send any pending requests for user data since a new control 1793189251Ssam * interface was added. This handles cases where the EAP authentication 1794189251Ssam * starts immediately after system startup when the user interface is 1795189251Ssam * not yet running. */ 1796189251Ssam if (config->pending_req_identity) 1797189251Ssam eap_sm_request_identity(sm); 1798189251Ssam if (config->pending_req_password) 1799189251Ssam eap_sm_request_password(sm); 1800189251Ssam if (config->pending_req_new_password) 1801189251Ssam eap_sm_request_new_password(sm); 1802189251Ssam if (config->pending_req_otp) 1803189251Ssam eap_sm_request_otp(sm, NULL, 0); 1804189251Ssam if (config->pending_req_pin) 1805189251Ssam eap_sm_request_pin(sm); 1806189251Ssam if (config->pending_req_passphrase) 1807189251Ssam eap_sm_request_passphrase(sm); 1808189251Ssam} 1809189251Ssam 1810189251Ssam 1811189251Ssamstatic int eap_allowed_phase2_type(int vendor, int type) 1812189251Ssam{ 1813189251Ssam if (vendor != EAP_VENDOR_IETF) 1814189251Ssam return 0; 1815189251Ssam return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS && 1816189251Ssam type != EAP_TYPE_FAST; 1817189251Ssam} 1818189251Ssam 1819189251Ssam 1820189251Ssam/** 1821189251Ssam * eap_get_phase2_type - Get EAP type for the given EAP phase 2 method name 1822189251Ssam * @name: EAP method name, e.g., MD5 1823189251Ssam * @vendor: Buffer for returning EAP Vendor-Id 1824189251Ssam * Returns: EAP method type or %EAP_TYPE_NONE if not found 1825189251Ssam * 1826189251Ssam * This function maps EAP type names into EAP type numbers that are allowed for 1827189251Ssam * Phase 2, i.e., for tunneled authentication. Phase 2 is used, e.g., with 1828189251Ssam * EAP-PEAP, EAP-TTLS, and EAP-FAST. 1829189251Ssam */ 1830189251Ssamu32 eap_get_phase2_type(const char *name, int *vendor) 1831189251Ssam{ 1832189251Ssam int v; 1833189251Ssam u8 type = eap_peer_get_type(name, &v); 1834189251Ssam if (eap_allowed_phase2_type(v, type)) { 1835189251Ssam *vendor = v; 1836189251Ssam return type; 1837189251Ssam } 1838189251Ssam *vendor = EAP_VENDOR_IETF; 1839189251Ssam return EAP_TYPE_NONE; 1840189251Ssam} 1841189251Ssam 1842189251Ssam 1843189251Ssam/** 1844189251Ssam * eap_get_phase2_types - Get list of allowed EAP phase 2 types 1845189251Ssam * @config: Pointer to a network configuration 1846189251Ssam * @count: Pointer to a variable to be filled with number of returned EAP types 1847189251Ssam * Returns: Pointer to allocated type list or %NULL on failure 1848189251Ssam * 1849189251Ssam * This function generates an array of allowed EAP phase 2 (tunneled) types for 1850189251Ssam * the given network configuration. 1851189251Ssam */ 1852189251Ssamstruct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config, 1853189251Ssam size_t *count) 1854189251Ssam{ 1855189251Ssam struct eap_method_type *buf; 1856189251Ssam u32 method; 1857189251Ssam int vendor; 1858189251Ssam size_t mcount; 1859189251Ssam const struct eap_method *methods, *m; 1860189251Ssam 1861189251Ssam methods = eap_peer_get_methods(&mcount); 1862189251Ssam if (methods == NULL) 1863189251Ssam return NULL; 1864189251Ssam *count = 0; 1865189251Ssam buf = os_malloc(mcount * sizeof(struct eap_method_type)); 1866189251Ssam if (buf == NULL) 1867189251Ssam return NULL; 1868189251Ssam 1869189251Ssam for (m = methods; m; m = m->next) { 1870189251Ssam vendor = m->vendor; 1871189251Ssam method = m->method; 1872189251Ssam if (eap_allowed_phase2_type(vendor, method)) { 1873189251Ssam if (vendor == EAP_VENDOR_IETF && 1874189251Ssam method == EAP_TYPE_TLS && config && 1875189251Ssam config->private_key2 == NULL) 1876189251Ssam continue; 1877189251Ssam buf[*count].vendor = vendor; 1878189251Ssam buf[*count].method = method; 1879189251Ssam (*count)++; 1880189251Ssam } 1881189251Ssam } 1882189251Ssam 1883189251Ssam return buf; 1884189251Ssam} 1885189251Ssam 1886189251Ssam 1887189251Ssam/** 1888189251Ssam * eap_set_fast_reauth - Update fast_reauth setting 1889189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1890189251Ssam * @enabled: 1 = Fast reauthentication is enabled, 0 = Disabled 1891189251Ssam */ 1892189251Ssamvoid eap_set_fast_reauth(struct eap_sm *sm, int enabled) 1893189251Ssam{ 1894189251Ssam sm->fast_reauth = enabled; 1895189251Ssam} 1896189251Ssam 1897189251Ssam 1898189251Ssam/** 1899189251Ssam * eap_set_workaround - Update EAP workarounds setting 1900189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1901189251Ssam * @workaround: 1 = Enable EAP workarounds, 0 = Disable EAP workarounds 1902189251Ssam */ 1903189251Ssamvoid eap_set_workaround(struct eap_sm *sm, unsigned int workaround) 1904189251Ssam{ 1905189251Ssam sm->workaround = workaround; 1906189251Ssam} 1907189251Ssam 1908189251Ssam 1909189251Ssam/** 1910189251Ssam * eap_get_config - Get current network configuration 1911189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1912189251Ssam * Returns: Pointer to the current network configuration or %NULL if not found 1913189251Ssam * 1914189251Ssam * EAP peer methods should avoid using this function if they can use other 1915189251Ssam * access functions, like eap_get_config_identity() and 1916189251Ssam * eap_get_config_password(), that do not require direct access to 1917189251Ssam * struct eap_peer_config. 1918189251Ssam */ 1919189251Ssamstruct eap_peer_config * eap_get_config(struct eap_sm *sm) 1920189251Ssam{ 1921189251Ssam return sm->eapol_cb->get_config(sm->eapol_ctx); 1922189251Ssam} 1923189251Ssam 1924189251Ssam 1925189251Ssam/** 1926189251Ssam * eap_get_config_identity - Get identity from the network configuration 1927189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1928189251Ssam * @len: Buffer for the length of the identity 1929189251Ssam * Returns: Pointer to the identity or %NULL if not found 1930189251Ssam */ 1931189251Ssamconst u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len) 1932189251Ssam{ 1933189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1934189251Ssam if (config == NULL) 1935189251Ssam return NULL; 1936189251Ssam *len = config->identity_len; 1937189251Ssam return config->identity; 1938189251Ssam} 1939189251Ssam 1940189251Ssam 1941252726Srpaulostatic int eap_get_ext_password(struct eap_sm *sm, 1942252726Srpaulo struct eap_peer_config *config) 1943252726Srpaulo{ 1944252726Srpaulo char *name; 1945252726Srpaulo 1946252726Srpaulo if (config->password == NULL) 1947252726Srpaulo return -1; 1948252726Srpaulo 1949252726Srpaulo name = os_zalloc(config->password_len + 1); 1950252726Srpaulo if (name == NULL) 1951252726Srpaulo return -1; 1952252726Srpaulo os_memcpy(name, config->password, config->password_len); 1953252726Srpaulo 1954252726Srpaulo ext_password_free(sm->ext_pw_buf); 1955252726Srpaulo sm->ext_pw_buf = ext_password_get(sm->ext_pw, name); 1956252726Srpaulo os_free(name); 1957252726Srpaulo 1958252726Srpaulo return sm->ext_pw_buf == NULL ? -1 : 0; 1959252726Srpaulo} 1960252726Srpaulo 1961252726Srpaulo 1962189251Ssam/** 1963189251Ssam * eap_get_config_password - Get password from the network configuration 1964189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1965189251Ssam * @len: Buffer for the length of the password 1966189251Ssam * Returns: Pointer to the password or %NULL if not found 1967189251Ssam */ 1968189251Ssamconst u8 * eap_get_config_password(struct eap_sm *sm, size_t *len) 1969189251Ssam{ 1970189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1971189251Ssam if (config == NULL) 1972189251Ssam return NULL; 1973252726Srpaulo 1974252726Srpaulo if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { 1975252726Srpaulo if (eap_get_ext_password(sm, config) < 0) 1976252726Srpaulo return NULL; 1977252726Srpaulo *len = wpabuf_len(sm->ext_pw_buf); 1978252726Srpaulo return wpabuf_head(sm->ext_pw_buf); 1979252726Srpaulo } 1980252726Srpaulo 1981189251Ssam *len = config->password_len; 1982189251Ssam return config->password; 1983189251Ssam} 1984189251Ssam 1985189251Ssam 1986189251Ssam/** 1987189251Ssam * eap_get_config_password2 - Get password from the network configuration 1988189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 1989189251Ssam * @len: Buffer for the length of the password 1990189251Ssam * @hash: Buffer for returning whether the password is stored as a 1991189251Ssam * NtPasswordHash instead of plaintext password; can be %NULL if this 1992189251Ssam * information is not needed 1993189251Ssam * Returns: Pointer to the password or %NULL if not found 1994189251Ssam */ 1995189251Ssamconst u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash) 1996189251Ssam{ 1997189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1998189251Ssam if (config == NULL) 1999189251Ssam return NULL; 2000252726Srpaulo 2001252726Srpaulo if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { 2002252726Srpaulo if (eap_get_ext_password(sm, config) < 0) 2003252726Srpaulo return NULL; 2004252726Srpaulo *len = wpabuf_len(sm->ext_pw_buf); 2005252726Srpaulo return wpabuf_head(sm->ext_pw_buf); 2006252726Srpaulo } 2007252726Srpaulo 2008189251Ssam *len = config->password_len; 2009189251Ssam if (hash) 2010189251Ssam *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH); 2011189251Ssam return config->password; 2012189251Ssam} 2013189251Ssam 2014189251Ssam 2015189251Ssam/** 2016189251Ssam * eap_get_config_new_password - Get new password from network configuration 2017189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2018189251Ssam * @len: Buffer for the length of the new password 2019189251Ssam * Returns: Pointer to the new password or %NULL if not found 2020189251Ssam */ 2021189251Ssamconst u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len) 2022189251Ssam{ 2023189251Ssam struct eap_peer_config *config = eap_get_config(sm); 2024189251Ssam if (config == NULL) 2025189251Ssam return NULL; 2026189251Ssam *len = config->new_password_len; 2027189251Ssam return config->new_password; 2028189251Ssam} 2029189251Ssam 2030189251Ssam 2031189251Ssam/** 2032189251Ssam * eap_get_config_otp - Get one-time password from the network configuration 2033189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2034189251Ssam * @len: Buffer for the length of the one-time password 2035189251Ssam * Returns: Pointer to the one-time password or %NULL if not found 2036189251Ssam */ 2037189251Ssamconst u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len) 2038189251Ssam{ 2039189251Ssam struct eap_peer_config *config = eap_get_config(sm); 2040189251Ssam if (config == NULL) 2041189251Ssam return NULL; 2042189251Ssam *len = config->otp_len; 2043189251Ssam return config->otp; 2044189251Ssam} 2045189251Ssam 2046189251Ssam 2047189251Ssam/** 2048189251Ssam * eap_clear_config_otp - Clear used one-time password 2049189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2050189251Ssam * 2051189251Ssam * This function clears a used one-time password (OTP) from the current network 2052189251Ssam * configuration. This should be called when the OTP has been used and is not 2053189251Ssam * needed anymore. 2054189251Ssam */ 2055189251Ssamvoid eap_clear_config_otp(struct eap_sm *sm) 2056189251Ssam{ 2057189251Ssam struct eap_peer_config *config = eap_get_config(sm); 2058189251Ssam if (config == NULL) 2059189251Ssam return; 2060189251Ssam os_memset(config->otp, 0, config->otp_len); 2061189251Ssam os_free(config->otp); 2062189251Ssam config->otp = NULL; 2063189251Ssam config->otp_len = 0; 2064189251Ssam} 2065189251Ssam 2066189251Ssam 2067189251Ssam/** 2068189251Ssam * eap_get_config_phase1 - Get phase1 data from the network configuration 2069189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2070189251Ssam * Returns: Pointer to the phase1 data or %NULL if not found 2071189251Ssam */ 2072189251Ssamconst char * eap_get_config_phase1(struct eap_sm *sm) 2073189251Ssam{ 2074189251Ssam struct eap_peer_config *config = eap_get_config(sm); 2075189251Ssam if (config == NULL) 2076189251Ssam return NULL; 2077189251Ssam return config->phase1; 2078189251Ssam} 2079189251Ssam 2080189251Ssam 2081189251Ssam/** 2082189251Ssam * eap_get_config_phase2 - Get phase2 data from the network configuration 2083189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2084189251Ssam * Returns: Pointer to the phase1 data or %NULL if not found 2085189251Ssam */ 2086189251Ssamconst char * eap_get_config_phase2(struct eap_sm *sm) 2087189251Ssam{ 2088189251Ssam struct eap_peer_config *config = eap_get_config(sm); 2089189251Ssam if (config == NULL) 2090189251Ssam return NULL; 2091189251Ssam return config->phase2; 2092189251Ssam} 2093189251Ssam 2094189251Ssam 2095252726Srpauloint eap_get_config_fragment_size(struct eap_sm *sm) 2096252726Srpaulo{ 2097252726Srpaulo struct eap_peer_config *config = eap_get_config(sm); 2098252726Srpaulo if (config == NULL) 2099252726Srpaulo return -1; 2100252726Srpaulo return config->fragment_size; 2101252726Srpaulo} 2102252726Srpaulo 2103252726Srpaulo 2104189251Ssam/** 2105189251Ssam * eap_key_available - Get key availability (eapKeyAvailable variable) 2106189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2107189251Ssam * Returns: 1 if EAP keying material is available, 0 if not 2108189251Ssam */ 2109189251Ssamint eap_key_available(struct eap_sm *sm) 2110189251Ssam{ 2111189251Ssam return sm ? sm->eapKeyAvailable : 0; 2112189251Ssam} 2113189251Ssam 2114189251Ssam 2115189251Ssam/** 2116189251Ssam * eap_notify_success - Notify EAP state machine about external success trigger 2117189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2118189251Ssam * 2119189251Ssam * This function is called when external event, e.g., successful completion of 2120189251Ssam * WPA-PSK key handshake, is indicating that EAP state machine should move to 2121189251Ssam * success state. This is mainly used with security modes that do not use EAP 2122189251Ssam * state machine (e.g., WPA-PSK). 2123189251Ssam */ 2124189251Ssamvoid eap_notify_success(struct eap_sm *sm) 2125189251Ssam{ 2126189251Ssam if (sm) { 2127189251Ssam sm->decision = DECISION_COND_SUCC; 2128189251Ssam sm->EAP_state = EAP_SUCCESS; 2129189251Ssam } 2130189251Ssam} 2131189251Ssam 2132189251Ssam 2133189251Ssam/** 2134189251Ssam * eap_notify_lower_layer_success - Notification of lower layer success 2135189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2136189251Ssam * 2137189251Ssam * Notify EAP state machines that a lower layer has detected a successful 2138189251Ssam * authentication. This is used to recover from dropped EAP-Success messages. 2139189251Ssam */ 2140189251Ssamvoid eap_notify_lower_layer_success(struct eap_sm *sm) 2141189251Ssam{ 2142189251Ssam if (sm == NULL) 2143189251Ssam return; 2144189251Ssam 2145189251Ssam if (eapol_get_bool(sm, EAPOL_eapSuccess) || 2146189251Ssam sm->decision == DECISION_FAIL || 2147189251Ssam (sm->methodState != METHOD_MAY_CONT && 2148189251Ssam sm->methodState != METHOD_DONE)) 2149189251Ssam return; 2150189251Ssam 2151189251Ssam if (sm->eapKeyData != NULL) 2152189251Ssam sm->eapKeyAvailable = TRUE; 2153189251Ssam eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); 2154189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS 2155189251Ssam "EAP authentication completed successfully (based on lower " 2156189251Ssam "layer success)"); 2157189251Ssam} 2158189251Ssam 2159189251Ssam 2160189251Ssam/** 2161189251Ssam * eap_get_eapKeyData - Get master session key (MSK) from EAP state machine 2162189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2163189251Ssam * @len: Pointer to variable that will be set to number of bytes in the key 2164189251Ssam * Returns: Pointer to the EAP keying data or %NULL on failure 2165189251Ssam * 2166189251Ssam * Fetch EAP keying material (MSK, eapKeyData) from the EAP state machine. The 2167189251Ssam * key is available only after a successful authentication. EAP state machine 2168189251Ssam * continues to manage the key data and the caller must not change or free the 2169189251Ssam * returned data. 2170189251Ssam */ 2171189251Ssamconst u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len) 2172189251Ssam{ 2173189251Ssam if (sm == NULL || sm->eapKeyData == NULL) { 2174189251Ssam *len = 0; 2175189251Ssam return NULL; 2176189251Ssam } 2177189251Ssam 2178189251Ssam *len = sm->eapKeyDataLen; 2179189251Ssam return sm->eapKeyData; 2180189251Ssam} 2181189251Ssam 2182189251Ssam 2183189251Ssam/** 2184189251Ssam * eap_get_eapKeyData - Get EAP response data 2185189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2186189251Ssam * Returns: Pointer to the EAP response (eapRespData) or %NULL on failure 2187189251Ssam * 2188189251Ssam * Fetch EAP response (eapRespData) from the EAP state machine. This data is 2189189251Ssam * available when EAP state machine has processed an incoming EAP request. The 2190189251Ssam * EAP state machine does not maintain a reference to the response after this 2191189251Ssam * function is called and the caller is responsible for freeing the data. 2192189251Ssam */ 2193189251Ssamstruct wpabuf * eap_get_eapRespData(struct eap_sm *sm) 2194189251Ssam{ 2195189251Ssam struct wpabuf *resp; 2196189251Ssam 2197189251Ssam if (sm == NULL || sm->eapRespData == NULL) 2198189251Ssam return NULL; 2199189251Ssam 2200189251Ssam resp = sm->eapRespData; 2201189251Ssam sm->eapRespData = NULL; 2202189251Ssam 2203189251Ssam return resp; 2204189251Ssam} 2205189251Ssam 2206189251Ssam 2207189251Ssam/** 2208189251Ssam * eap_sm_register_scard_ctx - Notification of smart card context 2209189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2210189251Ssam * @ctx: Context data for smart card operations 2211189251Ssam * 2212189251Ssam * Notify EAP state machines of context data for smart card operations. This 2213189251Ssam * context data will be used as a parameter for scard_*() functions. 2214189251Ssam */ 2215189251Ssamvoid eap_register_scard_ctx(struct eap_sm *sm, void *ctx) 2216189251Ssam{ 2217189251Ssam if (sm) 2218189251Ssam sm->scard_ctx = ctx; 2219189251Ssam} 2220189251Ssam 2221189251Ssam 2222189251Ssam/** 2223189251Ssam * eap_set_config_blob - Set or add a named configuration blob 2224189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2225189251Ssam * @blob: New value for the blob 2226189251Ssam * 2227189251Ssam * Adds a new configuration blob or replaces the current value of an existing 2228189251Ssam * blob. 2229189251Ssam */ 2230189251Ssamvoid eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob) 2231189251Ssam{ 2232189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS 2233189251Ssam sm->eapol_cb->set_config_blob(sm->eapol_ctx, blob); 2234189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */ 2235189251Ssam} 2236189251Ssam 2237189251Ssam 2238189251Ssam/** 2239189251Ssam * eap_get_config_blob - Get a named configuration blob 2240189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2241189251Ssam * @name: Name of the blob 2242189251Ssam * Returns: Pointer to blob data or %NULL if not found 2243189251Ssam */ 2244189251Ssamconst struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm, 2245189251Ssam const char *name) 2246189251Ssam{ 2247189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS 2248189251Ssam return sm->eapol_cb->get_config_blob(sm->eapol_ctx, name); 2249189251Ssam#else /* CONFIG_NO_CONFIG_BLOBS */ 2250189251Ssam return NULL; 2251189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */ 2252189251Ssam} 2253189251Ssam 2254189251Ssam 2255189251Ssam/** 2256189251Ssam * eap_set_force_disabled - Set force_disabled flag 2257189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2258189251Ssam * @disabled: 1 = EAP disabled, 0 = EAP enabled 2259189251Ssam * 2260189251Ssam * This function is used to force EAP state machine to be disabled when it is 2261189251Ssam * not in use (e.g., with WPA-PSK or plaintext connections). 2262189251Ssam */ 2263189251Ssamvoid eap_set_force_disabled(struct eap_sm *sm, int disabled) 2264189251Ssam{ 2265189251Ssam sm->force_disabled = disabled; 2266189251Ssam} 2267189251Ssam 2268189251Ssam 2269189251Ssam /** 2270189251Ssam * eap_notify_pending - Notify that EAP method is ready to re-process a request 2271189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2272189251Ssam * 2273189251Ssam * An EAP method can perform a pending operation (e.g., to get a response from 2274189251Ssam * an external process). Once the response is available, this function can be 2275189251Ssam * used to request EAPOL state machine to retry delivering the previously 2276189251Ssam * received (and still unanswered) EAP request to EAP state machine. 2277189251Ssam */ 2278189251Ssamvoid eap_notify_pending(struct eap_sm *sm) 2279189251Ssam{ 2280189251Ssam sm->eapol_cb->notify_pending(sm->eapol_ctx); 2281189251Ssam} 2282189251Ssam 2283189251Ssam 2284189251Ssam/** 2285189251Ssam * eap_invalidate_cached_session - Mark cached session data invalid 2286189251Ssam * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2287189251Ssam */ 2288189251Ssamvoid eap_invalidate_cached_session(struct eap_sm *sm) 2289189251Ssam{ 2290189251Ssam if (sm) 2291189251Ssam eap_deinit_prev_method(sm, "invalidate"); 2292189251Ssam} 2293189251Ssam 2294189251Ssam 2295189251Ssamint eap_is_wps_pbc_enrollee(struct eap_peer_config *conf) 2296189251Ssam{ 2297189251Ssam if (conf->identity_len != WSC_ID_ENROLLEE_LEN || 2298189251Ssam os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)) 2299189251Ssam return 0; /* Not a WPS Enrollee */ 2300189251Ssam 2301189251Ssam if (conf->phase1 == NULL || os_strstr(conf->phase1, "pbc=1") == NULL) 2302189251Ssam return 0; /* Not using PBC */ 2303189251Ssam 2304189251Ssam return 1; 2305189251Ssam} 2306189251Ssam 2307189251Ssam 2308189251Ssamint eap_is_wps_pin_enrollee(struct eap_peer_config *conf) 2309189251Ssam{ 2310189251Ssam if (conf->identity_len != WSC_ID_ENROLLEE_LEN || 2311189251Ssam os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)) 2312189251Ssam return 0; /* Not a WPS Enrollee */ 2313189251Ssam 2314189251Ssam if (conf->phase1 == NULL || os_strstr(conf->phase1, "pin=") == NULL) 2315189251Ssam return 0; /* Not using PIN */ 2316189251Ssam 2317189251Ssam return 1; 2318189251Ssam} 2319252726Srpaulo 2320252726Srpaulo 2321252726Srpaulovoid eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext) 2322252726Srpaulo{ 2323252726Srpaulo ext_password_free(sm->ext_pw_buf); 2324252726Srpaulo sm->ext_pw_buf = NULL; 2325252726Srpaulo sm->ext_pw = ext; 2326252726Srpaulo} 2327252726Srpaulo 2328252726Srpaulo 2329252726Srpaulo/** 2330252726Srpaulo * eap_set_anon_id - Set or add anonymous identity 2331252726Srpaulo * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 2332252726Srpaulo * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear 2333252726Srpaulo * @len: Length of anonymous identity in octets 2334252726Srpaulo */ 2335252726Srpaulovoid eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len) 2336252726Srpaulo{ 2337252726Srpaulo if (sm->eapol_cb->set_anon_id) 2338252726Srpaulo sm->eapol_cb->set_anon_id(sm->eapol_ctx, id, len); 2339252726Srpaulo} 2340