eapol_supp_sm.c revision 209158
1193326Sed/* 2193326Sed * EAPOL supplicant state machines 3193326Sed * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> 4193326Sed * 5193326Sed * This program is free software; you can redistribute it and/or modify 6193326Sed * it under the terms of the GNU General Public License version 2 as 7193326Sed * published by the Free Software Foundation. 8193326Sed * 9193326Sed * Alternatively, this software may be distributed under the terms of BSD 10205219Srdivacky * license. 11205219Srdivacky * 12193326Sed * See README and COPYING for more details. 13193326Sed */ 14193326Sed 15193326Sed#include "includes.h" 16193326Sed 17193326Sed#include "common.h" 18249423Sdim#include "eapol_supp_sm.h" 19249423Sdim#include "eap_peer/eap.h" 20198092Srdivacky#include "eloop.h" 21234353Sdim#include "eapol_common.h" 22212904Sdim#include "md5.h" 23234353Sdim#include "rc4.h" 24234353Sdim#include "state_machine.h" 25198092Srdivacky#include "wpabuf.h" 26234353Sdim 27193326Sed#define STATE_MACHINE_DATA struct eapol_sm 28193326Sed#define STATE_MACHINE_DEBUG_PREFIX "EAPOL" 29193326Sed 30193326Sed 31198092Srdivacky/* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */ 32198092Srdivacky 33198092Srdivacky/** 34193326Sed * struct eapol_sm - Internal data for EAPOL state machines 35198092Srdivacky */ 36193326Sedstruct eapol_sm { 37193326Sed /* Timers */ 38198092Srdivacky unsigned int authWhile; 39198092Srdivacky unsigned int heldWhile; 40206084Srdivacky unsigned int startWhen; 41218893Sdim unsigned int idleWhile; /* for EAP state machine */ 42205219Srdivacky int timer_tick_enabled; 43234353Sdim 44239462Sdim /* Global variables */ 45234353Sdim Boolean eapFail; 46198092Srdivacky Boolean eapolEap; 47195099Sed Boolean eapSuccess; 48195099Sed Boolean initialize; 49195099Sed Boolean keyDone; 50198092Srdivacky Boolean keyRun; 51195341Sed PortControl portControl; 52198092Srdivacky Boolean portEnabled; 53195099Sed PortStatus suppPortStatus; /* dot1xSuppControlledPortStatus */ 54195099Sed Boolean portValid; 55195099Sed Boolean suppAbort; 56198092Srdivacky Boolean suppFail; 57198092Srdivacky Boolean suppStart; 58195099Sed Boolean suppSuccess; 59195099Sed Boolean suppTimeout; 60198092Srdivacky 61195099Sed /* Supplicant PAE state machine */ 62195099Sed enum { 63198092Srdivacky SUPP_PAE_UNKNOWN = 0, 64198092Srdivacky SUPP_PAE_DISCONNECTED = 1, 65195341Sed SUPP_PAE_LOGOFF = 2, 66195341Sed SUPP_PAE_CONNECTING = 3, 67195099Sed SUPP_PAE_AUTHENTICATING = 4, 68198092Srdivacky SUPP_PAE_AUTHENTICATED = 5, 69195099Sed /* unused(6) */ 70195099Sed SUPP_PAE_HELD = 7, 71195099Sed SUPP_PAE_RESTART = 8, 72195341Sed SUPP_PAE_S_FORCE_AUTH = 9, 73195341Sed SUPP_PAE_S_FORCE_UNAUTH = 10 74195341Sed } SUPP_PAE_state; /* dot1xSuppPaeState */ 75195341Sed /* Variables */ 76195341Sed Boolean userLogoff; 77198092Srdivacky Boolean logoffSent; 78195341Sed unsigned int startCount; 79195341Sed Boolean eapRestart; 80195341Sed PortControl sPortMode; 81195341Sed /* Constants */ 82195341Sed unsigned int heldPeriod; /* dot1xSuppHeldPeriod */ 83198092Srdivacky unsigned int startPeriod; /* dot1xSuppStartPeriod */ 84195341Sed unsigned int maxStart; /* dot1xSuppMaxStart */ 85195341Sed 86198092Srdivacky /* Key Receive state machine */ 87195099Sed enum { 88195099Sed KEY_RX_UNKNOWN = 0, 89195099Sed KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE 90198092Srdivacky } KEY_RX_state; 91239462Sdim /* Variables */ 92210299Sed Boolean rxKey; 93210299Sed 94210299Sed /* Supplicant Backend state machine */ 95210299Sed enum { 96210299Sed SUPP_BE_UNKNOWN = 0, 97210299Sed SUPP_BE_INITIALIZE = 1, 98210299Sed SUPP_BE_IDLE = 2, 99210299Sed SUPP_BE_REQUEST = 3, 100210299Sed SUPP_BE_RECEIVE = 4, 101210299Sed SUPP_BE_RESPONSE = 5, 102234353Sdim SUPP_BE_FAIL = 6, 103239462Sdim SUPP_BE_TIMEOUT = 7, 104210299Sed SUPP_BE_SUCCESS = 8 105210299Sed } SUPP_BE_state; /* dot1xSuppBackendPaeState */ 106210299Sed /* Variables */ 107210299Sed Boolean eapNoResp; 108210299Sed Boolean eapReq; 109210299Sed Boolean eapResp; 110210299Sed /* Constants */ 111218893Sdim unsigned int authPeriod; /* dot1xSuppAuthPeriod */ 112218893Sdim 113210299Sed /* Statistics */ 114239462Sdim unsigned int dot1xSuppEapolFramesRx; 115210299Sed unsigned int dot1xSuppEapolFramesTx; 116239462Sdim unsigned int dot1xSuppEapolStartFramesTx; 117210299Sed unsigned int dot1xSuppEapolLogoffFramesTx; 118210299Sed unsigned int dot1xSuppEapolRespFramesTx; 119239462Sdim unsigned int dot1xSuppEapolReqIdFramesRx; 120210299Sed unsigned int dot1xSuppEapolReqFramesRx; 121239462Sdim unsigned int dot1xSuppInvalidEapolFramesRx; 122210299Sed unsigned int dot1xSuppEapLengthErrorFramesRx; 123210299Sed unsigned int dot1xSuppLastEapolFrameVersion; 124234353Sdim unsigned char dot1xSuppLastEapolFrameSource[6]; 125210299Sed 126210299Sed /* Miscellaneous variables (not defined in IEEE 802.1X-2004) */ 127210299Sed Boolean changed; 128210299Sed struct eap_sm *eap; 129210299Sed struct eap_peer_config *config; 130210299Sed Boolean initial_req; 131210299Sed u8 *last_rx_key; 132210299Sed size_t last_rx_key_len; 133234353Sdim struct wpabuf *eapReqData; /* for EAP */ 134210299Sed Boolean altAccept; /* for EAP */ 135210299Sed Boolean altReject; /* for EAP */ 136210299Sed Boolean replay_counter_valid; 137210299Sed u8 last_replay_counter[16]; 138210299Sed struct eapol_config conf; 139210299Sed struct eapol_ctx *ctx; 140210299Sed enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE } 141239462Sdim cb_status; 142193326Sed Boolean cached_pmk; 143193326Sed 144193326Sed Boolean unicast_key_received, broadcast_key_received; 145193326Sed}; 146193326Sed 147193326Sed 148193326Sed#define IEEE8021X_REPLAY_COUNTER_LEN 8 149193326Sed#define IEEE8021X_KEY_SIGN_LEN 16 150193326Sed#define IEEE8021X_KEY_IV_LEN 16 151193326Sed 152193326Sed#define IEEE8021X_KEY_INDEX_FLAG 0x80 153193326Sed#define IEEE8021X_KEY_INDEX_MASK 0x03 154193326Sed 155193326Sed#ifdef _MSC_VER 156193326Sed#pragma pack(push, 1) 157193326Sed#endif /* _MSC_VER */ 158193326Sed 159193326Sedstruct ieee802_1x_eapol_key { 160193326Sed u8 type; 161193326Sed /* Note: key_length is unaligned */ 162193326Sed u8 key_length[2]; 163218893Sdim /* does not repeat within the life of the keying material used to 164218893Sdim * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ 165218893Sdim u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; 166234353Sdim u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ 167239462Sdim u8 key_index; /* key flag in the most significant bit: 168193326Sed * 0 = broadcast (default key), 169193326Sed * 1 = unicast (key mapping key); key index is in the 170193326Sed * 7 least significant bits */ 171193326Sed /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as 172193326Sed * the key */ 173193326Sed u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; 174198092Srdivacky 175193326Sed /* followed by key: if packet body length = 44 + key length, then the 176193326Sed * key field (of key_length bytes) contains the key in encrypted form; 177193326Sed * if packet body length = 44, key field is absent and key_length 178193326Sed * represents the number of least significant octets from 179193326Sed * MS-MPPE-Send-Key attribute to be used as the keying material; 180193326Sed * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ 181193326Sed} STRUCT_PACKED; 182218893Sdim 183218893Sdim#ifdef _MSC_VER 184218893Sdim#pragma pack(pop) 185218893Sdim#endif /* _MSC_VER */ 186212904Sdim 187212904Sdim 188212904Sdimstatic void eapol_sm_txLogoff(struct eapol_sm *sm); 189212904Sdimstatic void eapol_sm_txStart(struct eapol_sm *sm); 190198092Srdivackystatic void eapol_sm_processKey(struct eapol_sm *sm); 191193326Sedstatic void eapol_sm_getSuppRsp(struct eapol_sm *sm); 192193326Sedstatic void eapol_sm_txSuppRsp(struct eapol_sm *sm); 193193326Sedstatic void eapol_sm_abortSupp(struct eapol_sm *sm); 194212904Sdimstatic void eapol_sm_abort_cached(struct eapol_sm *sm); 195218893Sdimstatic void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx); 196234353Sdim 197218893Sdim 198193326Sed/* Port Timers state machine - implemented as a function that will be called 199193326Sed * once a second as a registered event loop timeout */ 200193326Sedstatic void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) 201234353Sdim{ 202234353Sdim struct eapol_sm *sm = timeout_ctx; 203234353Sdim 204198092Srdivacky if (sm->authWhile > 0) { 205193326Sed sm->authWhile--; 206193326Sed if (sm->authWhile == 0) 207193326Sed wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0"); 208193326Sed } 209212904Sdim if (sm->heldWhile > 0) { 210203955Srdivacky sm->heldWhile--; 211203955Srdivacky if (sm->heldWhile == 0) 212234353Sdim wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0"); 213218893Sdim } 214218893Sdim if (sm->startWhen > 0) { 215218893Sdim sm->startWhen--; 216218893Sdim if (sm->startWhen == 0) 217218893Sdim wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0"); 218218893Sdim } 219218893Sdim if (sm->idleWhile > 0) { 220218893Sdim sm->idleWhile--; 221218893Sdim if (sm->idleWhile == 0) 222218893Sdim wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0"); 223218893Sdim } 224218893Sdim 225218893Sdim if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) { 226218893Sdim eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, 227218893Sdim sm); 228218893Sdim } else { 229193326Sed wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick"); 230193326Sed sm->timer_tick_enabled = 0; 231193326Sed } 232193326Sed eapol_sm_step(sm); 233193326Sed} 234198092Srdivacky 235193326Sed 236193326Sedstatic void eapol_enable_timer_tick(struct eapol_sm *sm) 237193326Sed{ 238198092Srdivacky if (sm->timer_tick_enabled) 239193326Sed return; 240193326Sed wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick"); 241193326Sed sm->timer_tick_enabled = 1; 242193326Sed eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); 243193326Sed eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); 244193326Sed} 245193326Sed 246193326Sed 247193326SedSM_STATE(SUPP_PAE, LOGOFF) 248193326Sed{ 249193326Sed SM_ENTRY(SUPP_PAE, LOGOFF); 250193326Sed eapol_sm_txLogoff(sm); 251212904Sdim sm->logoffSent = TRUE; 252212904Sdim sm->suppPortStatus = Unauthorized; 253212904Sdim} 254212904Sdim 255193326Sed 256193326SedSM_STATE(SUPP_PAE, DISCONNECTED) 257251662Sdim{ 258251662Sdim SM_ENTRY(SUPP_PAE, DISCONNECTED); 259251662Sdim sm->sPortMode = Auto; 260251662Sdim sm->startCount = 0; 261251662Sdim sm->logoffSent = FALSE; 262251662Sdim sm->suppPortStatus = Unauthorized; 263251662Sdim sm->suppAbort = TRUE; 264251662Sdim 265251662Sdim sm->unicast_key_received = FALSE; 266251662Sdim sm->broadcast_key_received = FALSE; 267193326Sed} 268193326Sed 269193326Sed 270193326SedSM_STATE(SUPP_PAE, CONNECTING) 271193326Sed{ 272203955Srdivacky int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING; 273193326Sed SM_ENTRY(SUPP_PAE, CONNECTING); 274249423Sdim if (send_start) { 275249423Sdim sm->startWhen = sm->startPeriod; 276249423Sdim sm->startCount++; 277249423Sdim } else { 278249423Sdim /* 279249423Sdim * Do not send EAPOL-Start immediately since in most cases, 280249423Sdim * Authenticator is going to start authentication immediately 281249423Sdim * after association and an extra EAPOL-Start is just going to 282249423Sdim * delay authentication. Use a short timeout to send the first 283249423Sdim * EAPOL-Start if Authenticator does not start authentication. 284249423Sdim */ 285203955Srdivacky#ifdef CONFIG_WPS 286203955Srdivacky /* Reduce latency on starting WPS negotiation. */ 287193326Sed sm->startWhen = 1; 288249423Sdim#else /* CONFIG_WPS */ 289203955Srdivacky sm->startWhen = 3; 290193326Sed#endif /* CONFIG_WPS */ 291249423Sdim } 292249423Sdim eapol_enable_timer_tick(sm); 293193326Sed sm->eapolEap = FALSE; 294203955Srdivacky if (send_start) 295203955Srdivacky eapol_sm_txStart(sm); 296193326Sed} 297203955Srdivacky 298203955Srdivacky 299198092SrdivackySM_STATE(SUPP_PAE, AUTHENTICATING) 300203955Srdivacky{ 301203955Srdivacky SM_ENTRY(SUPP_PAE, AUTHENTICATING); 302203955Srdivacky sm->startCount = 0; 303203955Srdivacky sm->suppSuccess = FALSE; 304203955Srdivacky sm->suppFail = FALSE; 305198092Srdivacky sm->suppTimeout = FALSE; 306203955Srdivacky sm->keyRun = FALSE; 307203955Srdivacky sm->keyDone = FALSE; 308203955Srdivacky sm->suppStart = TRUE; 309198092Srdivacky} 310203955Srdivacky 311203955Srdivacky 312203955SrdivackySM_STATE(SUPP_PAE, HELD) 313198092Srdivacky{ 314221345Sdim SM_ENTRY(SUPP_PAE, HELD); 315221345Sdim sm->heldWhile = sm->heldPeriod; 316221345Sdim eapol_enable_timer_tick(sm); 317221345Sdim sm->suppPortStatus = Unauthorized; 318221345Sdim sm->cb_status = EAPOL_CB_FAILURE; 319221345Sdim} 320234353Sdim 321234353Sdim 322221345SdimSM_STATE(SUPP_PAE, AUTHENTICATED) 323221345Sdim{ 324221345Sdim SM_ENTRY(SUPP_PAE, AUTHENTICATED); 325221345Sdim sm->suppPortStatus = Authorized; 326221345Sdim sm->cb_status = EAPOL_CB_SUCCESS; 327221345Sdim} 328221345Sdim 329221345Sdim 330221345SdimSM_STATE(SUPP_PAE, RESTART) 331221345Sdim{ 332221345Sdim SM_ENTRY(SUPP_PAE, RESTART); 333221345Sdim sm->eapRestart = TRUE; 334221345Sdim} 335221345Sdim 336221345Sdim 337221345SdimSM_STATE(SUPP_PAE, S_FORCE_AUTH) 338221345Sdim{ 339221345Sdim SM_ENTRY(SUPP_PAE, S_FORCE_AUTH); 340221345Sdim sm->suppPortStatus = Authorized; 341221345Sdim sm->sPortMode = ForceAuthorized; 342221345Sdim} 343221345Sdim 344221345Sdim 345221345SdimSM_STATE(SUPP_PAE, S_FORCE_UNAUTH) 346223017Sdim{ 347223017Sdim SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH); 348234353Sdim sm->suppPortStatus = Unauthorized; 349234353Sdim sm->sPortMode = ForceUnauthorized; 350234353Sdim eapol_sm_txLogoff(sm); 351234353Sdim} 352239462Sdim 353239462Sdim 354239462SdimSM_STEP(SUPP_PAE) 355249423Sdim{ 356249423Sdim if ((sm->userLogoff && !sm->logoffSent) && 357249423Sdim !(sm->initialize || !sm->portEnabled)) 358249423Sdim SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF); 359249423Sdim else if (((sm->portControl == Auto) && 360249423Sdim (sm->sPortMode != sm->portControl)) || 361249423Sdim sm->initialize || !sm->portEnabled) 362249423Sdim SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED); 363249423Sdim else if ((sm->portControl == ForceAuthorized) && 364249423Sdim (sm->sPortMode != sm->portControl) && 365249423Sdim !(sm->initialize || !sm->portEnabled)) 366249423Sdim SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH); 367249423Sdim else if ((sm->portControl == ForceUnauthorized) && 368249423Sdim (sm->sPortMode != sm->portControl) && 369249423Sdim !(sm->initialize || !sm->portEnabled)) 370249423Sdim SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH); 371249423Sdim else switch (sm->SUPP_PAE_state) { 372249423Sdim case SUPP_PAE_UNKNOWN: 373249423Sdim break; 374249423Sdim case SUPP_PAE_LOGOFF: 375249423Sdim if (!sm->userLogoff) 376249423Sdim SM_ENTER(SUPP_PAE, DISCONNECTED); 377249423Sdim break; 378249423Sdim case SUPP_PAE_DISCONNECTED: 379203955Srdivacky SM_ENTER(SUPP_PAE, CONNECTING); 380249423Sdim break; 381249423Sdim case SUPP_PAE_CONNECTING: 382249423Sdim if (sm->startWhen == 0 && sm->startCount < sm->maxStart) 383198092Srdivacky SM_ENTER(SUPP_PAE, CONNECTING); 384249423Sdim else if (sm->startWhen == 0 && 385249423Sdim sm->startCount >= sm->maxStart && 386249423Sdim sm->portValid) 387249423Sdim SM_ENTER(SUPP_PAE, AUTHENTICATED); 388249423Sdim else if (sm->eapSuccess || sm->eapFail) 389249423Sdim SM_ENTER(SUPP_PAE, AUTHENTICATING); 390249423Sdim else if (sm->eapolEap) 391249423Sdim SM_ENTER(SUPP_PAE, RESTART); 392249423Sdim else if (sm->startWhen == 0 && 393249423Sdim sm->startCount >= sm->maxStart && 394249423Sdim !sm->portValid) 395249423Sdim SM_ENTER(SUPP_PAE, HELD); 396226633Sdim break; 397234353Sdim case SUPP_PAE_AUTHENTICATING: 398234353Sdim if (sm->eapSuccess && !sm->portValid && 399226633Sdim sm->conf.accept_802_1x_keys && 400221345Sdim sm->conf.required_keys == 0) { 401234353Sdim wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for " 402234353Sdim "plaintext connection; no EAPOL-Key frames " 403234353Sdim "required"); 404234353Sdim sm->portValid = TRUE; 405234353Sdim if (sm->ctx->eapol_done_cb) 406234353Sdim sm->ctx->eapol_done_cb(sm->ctx->ctx); 407234353Sdim } 408234353Sdim if (sm->eapSuccess && sm->portValid) 409221345Sdim SM_ENTER(SUPP_PAE, AUTHENTICATED); 410234353Sdim else if (sm->eapFail || (sm->keyDone && !sm->portValid)) 411234353Sdim SM_ENTER(SUPP_PAE, HELD); 412221345Sdim else if (sm->suppTimeout) 413221345Sdim SM_ENTER(SUPP_PAE, CONNECTING); 414203955Srdivacky break; 415203955Srdivacky case SUPP_PAE_HELD: 416203955Srdivacky if (sm->heldWhile == 0) 417210299Sed SM_ENTER(SUPP_PAE, CONNECTING); 418249423Sdim else if (sm->eapolEap) 419223017Sdim SM_ENTER(SUPP_PAE, RESTART); 420223017Sdim break; 421223017Sdim case SUPP_PAE_AUTHENTICATED: 422249423Sdim if (sm->eapolEap && sm->portValid) 423249423Sdim SM_ENTER(SUPP_PAE, RESTART); 424249423Sdim else if (!sm->portValid) 425210299Sed SM_ENTER(SUPP_PAE, DISCONNECTED); 426249423Sdim break; 427249423Sdim case SUPP_PAE_RESTART: 428249423Sdim if (!sm->eapRestart) 429223017Sdim SM_ENTER(SUPP_PAE, AUTHENTICATING); 430249423Sdim break; 431249423Sdim case SUPP_PAE_S_FORCE_AUTH: 432249423Sdim break; 433234353Sdim case SUPP_PAE_S_FORCE_UNAUTH: 434249423Sdim break; 435249423Sdim } 436249423Sdim} 437223017Sdim 438249423Sdim 439249423SdimSM_STATE(KEY_RX, NO_KEY_RECEIVE) 440249423Sdim{ 441234353Sdim SM_ENTRY(KEY_RX, NO_KEY_RECEIVE); 442226633Sdim} 443226633Sdim 444226633Sdim 445226633SdimSM_STATE(KEY_RX, KEY_RECEIVE) 446226633Sdim{ 447226633Sdim SM_ENTRY(KEY_RX, KEY_RECEIVE); 448226633Sdim eapol_sm_processKey(sm); 449226633Sdim sm->rxKey = FALSE; 450234353Sdim} 451234353Sdim 452234353Sdim 453218893SdimSM_STEP(KEY_RX) 454218893Sdim{ 455234353Sdim if (sm->initialize || !sm->portEnabled) 456218893Sdim SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); 457218893Sdim switch (sm->KEY_RX_state) { 458218893Sdim case KEY_RX_UNKNOWN: 459203955Srdivacky break; 460203955Srdivacky case KEY_RX_NO_KEY_RECEIVE: 461218893Sdim if (sm->rxKey) 462193326Sed SM_ENTER(KEY_RX, KEY_RECEIVE); 463203955Srdivacky break; 464218893Sdim case KEY_RX_KEY_RECEIVE: 465198092Srdivacky if (sm->rxKey) 466203955Srdivacky SM_ENTER(KEY_RX, KEY_RECEIVE); 467203955Srdivacky break; 468203955Srdivacky } 469203955Srdivacky} 470249423Sdim 471193326Sed 472203955SrdivackySM_STATE(SUPP_BE, REQUEST) 473203955Srdivacky{ 474203955Srdivacky SM_ENTRY(SUPP_BE, REQUEST); 475203955Srdivacky sm->authWhile = 0; 476203955Srdivacky sm->eapReq = TRUE; 477249423Sdim eapol_sm_getSuppRsp(sm); 478203955Srdivacky} 479203955Srdivacky 480203955Srdivacky 481203955SrdivackySM_STATE(SUPP_BE, RESPONSE) 482205219Srdivacky{ 483205219Srdivacky SM_ENTRY(SUPP_BE, RESPONSE); 484205219Srdivacky eapol_sm_txSuppRsp(sm); 485205219Srdivacky sm->eapResp = FALSE; 486205219Srdivacky} 487234353Sdim 488218893Sdim 489239462SdimSM_STATE(SUPP_BE, SUCCESS) 490239462Sdim{ 491239462Sdim SM_ENTRY(SUPP_BE, SUCCESS); 492218893Sdim sm->keyRun = TRUE; 493218893Sdim sm->suppSuccess = TRUE; 494234353Sdim 495218893Sdim if (eap_key_available(sm->eap)) { 496239462Sdim /* New key received - clear IEEE 802.1X EAPOL-Key replay 497239462Sdim * counter */ 498239462Sdim sm->replay_counter_valid = FALSE; 499218893Sdim } 500239462Sdim} 501239462Sdim 502239462Sdim 503239462SdimSM_STATE(SUPP_BE, FAIL) 504203955Srdivacky{ 505203955Srdivacky SM_ENTRY(SUPP_BE, FAIL); 506234353Sdim sm->suppFail = TRUE; 507234353Sdim} 508234353Sdim 509234353Sdim 510243830SdimSM_STATE(SUPP_BE, TIMEOUT) 511234353Sdim{ 512243830Sdim SM_ENTRY(SUPP_BE, TIMEOUT); 513243830Sdim sm->suppTimeout = TRUE; 514234353Sdim} 515234353Sdim 516234353Sdim 517234353SdimSM_STATE(SUPP_BE, IDLE) 518234353Sdim{ 519234353Sdim SM_ENTRY(SUPP_BE, IDLE); 520234353Sdim sm->suppStart = FALSE; 521234353Sdim sm->initial_req = TRUE; 522234353Sdim} 523234353Sdim 524234353Sdim 525234353SdimSM_STATE(SUPP_BE, INITIALIZE) 526234353Sdim{ 527234353Sdim SM_ENTRY(SUPP_BE, INITIALIZE); 528234353Sdim eapol_sm_abortSupp(sm); 529234353Sdim sm->suppAbort = FALSE; 530234353Sdim} 531234353Sdim 532234353Sdim 533234353SdimSM_STATE(SUPP_BE, RECEIVE) 534234353Sdim{ 535234353Sdim SM_ENTRY(SUPP_BE, RECEIVE); 536234353Sdim sm->authWhile = sm->authPeriod; 537234353Sdim eapol_enable_timer_tick(sm); 538234353Sdim sm->eapolEap = FALSE; 539234353Sdim sm->eapNoResp = FALSE; 540234353Sdim sm->initial_req = FALSE; 541234353Sdim} 542234353Sdim 543234353Sdim 544234353SdimSM_STEP(SUPP_BE) 545234353Sdim{ 546243830Sdim if (sm->initialize || sm->suppAbort) 547243830Sdim SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE); 548243830Sdim else switch (sm->SUPP_BE_state) { 549243830Sdim case SUPP_BE_UNKNOWN: 550234353Sdim break; 551234353Sdim case SUPP_BE_REQUEST: 552203955Srdivacky /* 553203955Srdivacky * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL 554203955Srdivacky * and SUCCESS based on eapFail and eapSuccess, respectively. 555203955Srdivacky * However, IEEE Std 802.1X-2004 is also specifying that 556203955Srdivacky * eapNoResp should be set in conjuction with eapSuccess and 557203955Srdivacky * eapFail which would mean that more than one of the 558203955Srdivacky * transitions here would be activated at the same time. 559203955Srdivacky * Skipping RESPONSE and/or RECEIVE states in these cases can 560203955Srdivacky * cause problems and the direct transitions to do not seem 561234353Sdim * correct. Because of this, the conditions for these 562234353Sdim * transitions are verified only after eapNoResp. They are 563234353Sdim * unlikely to be used since eapNoResp should always be set if 564234353Sdim * either of eapSuccess or eapFail is set. 565234353Sdim */ 566234353Sdim if (sm->eapResp && sm->eapNoResp) { 567234353Sdim wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both " 568198092Srdivacky "eapResp and eapNoResp set?!"); 569193326Sed } 570193326Sed if (sm->eapResp) 571198092Srdivacky SM_ENTER(SUPP_BE, RESPONSE); 572193326Sed else if (sm->eapNoResp) 573193326Sed SM_ENTER(SUPP_BE, RECEIVE); 574193326Sed else if (sm->eapFail) 575193326Sed SM_ENTER(SUPP_BE, FAIL); 576234353Sdim else if (sm->eapSuccess) 577198092Srdivacky SM_ENTER(SUPP_BE, SUCCESS); 578198092Srdivacky break; 579193326Sed case SUPP_BE_RESPONSE: 580206084Srdivacky SM_ENTER(SUPP_BE, RECEIVE); 581218893Sdim break; 582234353Sdim case SUPP_BE_SUCCESS: 583234353Sdim SM_ENTER(SUPP_BE, IDLE); 584249423Sdim break; 585249423Sdim case SUPP_BE_FAIL: 586249423Sdim SM_ENTER(SUPP_BE, IDLE); 587249423Sdim break; 588218893Sdim case SUPP_BE_TIMEOUT: 589218893Sdim SM_ENTER(SUPP_BE, IDLE); 590234353Sdim break; 591218893Sdim case SUPP_BE_IDLE: 592218893Sdim if (sm->eapFail && sm->suppStart) 593218893Sdim SM_ENTER(SUPP_BE, FAIL); 594218893Sdim else if (sm->eapolEap && sm->suppStart) 595218893Sdim SM_ENTER(SUPP_BE, REQUEST); 596218893Sdim else if (sm->eapSuccess && sm->suppStart) 597234353Sdim SM_ENTER(SUPP_BE, SUCCESS); 598234353Sdim break; 599234353Sdim case SUPP_BE_INITIALIZE: 600193326Sed SM_ENTER(SUPP_BE, IDLE); 601193326Sed break; 602221345Sdim case SUPP_BE_RECEIVE: 603221345Sdim if (sm->eapolEap) 604193326Sed SM_ENTER(SUPP_BE, REQUEST); 605193326Sed else if (sm->eapFail) 606193326Sed SM_ENTER(SUPP_BE, FAIL); 607198092Srdivacky else if (sm->authWhile == 0) 608193326Sed SM_ENTER(SUPP_BE, TIMEOUT); 609193326Sed else if (sm->eapSuccess) 610193326Sed SM_ENTER(SUPP_BE, SUCCESS); 611198092Srdivacky break; 612193326Sed } 613193326Sed} 614198092Srdivacky 615198092Srdivacky 616198092Srdivackystatic void eapol_sm_txLogoff(struct eapol_sm *sm) 617198092Srdivacky{ 618198092Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: txLogoff"); 619198092Srdivacky sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, 620198092Srdivacky IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0); 621198092Srdivacky sm->dot1xSuppEapolLogoffFramesTx++; 622198092Srdivacky sm->dot1xSuppEapolFramesTx++; 623198092Srdivacky} 624198092Srdivacky 625198092Srdivacky 626198092Srdivackystatic void eapol_sm_txStart(struct eapol_sm *sm) 627199482Srdivacky{ 628199482Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: txStart"); 629199482Srdivacky sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, 630234353Sdim IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0); 631234353Sdim sm->dot1xSuppEapolStartFramesTx++; 632234353Sdim sm->dot1xSuppEapolFramesTx++; 633210299Sed} 634234353Sdim 635234353Sdim 636210299Sed#define IEEE8021X_ENCR_KEY_LEN 32 637198092Srdivacky#define IEEE8021X_SIGN_KEY_LEN 32 638234353Sdim 639234353Sdimstruct eap_key_data { 640234353Sdim u8 encr_key[IEEE8021X_ENCR_KEY_LEN]; 641234353Sdim u8 sign_key[IEEE8021X_SIGN_KEY_LEN]; 642234353Sdim}; 643234353Sdim 644234353Sdim 645203955Srdivackystatic void eapol_sm_processKey(struct eapol_sm *sm) 646203955Srdivacky{ 647203955Srdivacky struct ieee802_1x_hdr *hdr; 648203955Srdivacky struct ieee802_1x_eapol_key *key; 649203955Srdivacky struct eap_key_data keydata; 650203955Srdivacky u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32]; 651203955Srdivacky u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN]; 652218893Sdim int key_len, res, sign_key_len, encr_key_len; 653221345Sdim u16 rx_key_length; 654221345Sdim 655193326Sed wpa_printf(MSG_DEBUG, "EAPOL: processKey"); 656234353Sdim if (sm->last_rx_key == NULL) 657243830Sdim return; 658243830Sdim 659234353Sdim if (!sm->conf.accept_802_1x_keys) { 660198092Srdivacky wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key" 661198092Srdivacky " even though this was not accepted - " 662203955Srdivacky "ignoring this packet"); 663198092Srdivacky return; 664198092Srdivacky } 665193326Sed 666203955Srdivacky hdr = (struct ieee802_1x_hdr *) sm->last_rx_key; 667193326Sed key = (struct ieee802_1x_eapol_key *) (hdr + 1); 668193326Sed if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) { 669193326Sed wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame"); 670203955Srdivacky return; 671193326Sed } 672218893Sdim rx_key_length = WPA_GET_BE16(key->key_length); 673218893Sdim wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d " 674203955Srdivacky "EAPOL-Key: type=%d key_length=%d key_index=0x%x", 675203955Srdivacky hdr->version, hdr->type, be_to_host16(hdr->length), 676203955Srdivacky key->type, rx_key_length, key->key_index); 677203955Srdivacky 678198092Srdivacky eapol_sm_notify_lower_layer_success(sm, 1); 679198092Srdivacky sign_key_len = IEEE8021X_SIGN_KEY_LEN; 680198092Srdivacky encr_key_len = IEEE8021X_ENCR_KEY_LEN; 681198092Srdivacky res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata)); 682198092Srdivacky if (res < 0) { 683198092Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for " 684198092Srdivacky "decrypting EAPOL-Key keys"); 685198092Srdivacky return; 686198092Srdivacky } 687198092Srdivacky if (res == 16) { 688198092Srdivacky /* LEAP derives only 16 bytes of keying material. */ 689198092Srdivacky res = eapol_sm_get_key(sm, (u8 *) &keydata, 16); 690193326Sed if (res) { 691198092Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP " 692198092Srdivacky "master key for decrypting EAPOL-Key keys"); 693203955Srdivacky return; 694198092Srdivacky } 695218893Sdim sign_key_len = 16; 696218893Sdim encr_key_len = 16; 697203955Srdivacky os_memcpy(keydata.sign_key, keydata.encr_key, 16); 698203955Srdivacky } else if (res) { 699203955Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key " 700203955Srdivacky "data for decrypting EAPOL-Key keys (res=%d)", res); 701198092Srdivacky return; 702198092Srdivacky } 703198092Srdivacky 704198092Srdivacky /* The key replay_counter must increase when same master key */ 705198092Srdivacky if (sm->replay_counter_valid && 706198092Srdivacky os_memcmp(sm->last_replay_counter, key->replay_counter, 707198092Srdivacky IEEE8021X_REPLAY_COUNTER_LEN) >= 0) { 708198092Srdivacky wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did " 709198092Srdivacky "not increase - ignoring key"); 710198092Srdivacky wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter", 711198092Srdivacky sm->last_replay_counter, 712198092Srdivacky IEEE8021X_REPLAY_COUNTER_LEN); 713198092Srdivacky wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter", 714249423Sdim key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN); 715249423Sdim return; 716202379Srdivacky } 717202379Srdivacky 718198092Srdivacky /* Verify key signature (HMAC-MD5) */ 719198092Srdivacky os_memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN); 720198092Srdivacky os_memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN); 721198092Srdivacky hmac_md5(keydata.sign_key, sign_key_len, 722198092Srdivacky sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length), 723198092Srdivacky key->key_signature); 724198092Srdivacky if (os_memcmp(orig_key_sign, key->key_signature, 725198092Srdivacky IEEE8021X_KEY_SIGN_LEN) != 0) { 726198092Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in " 727198092Srdivacky "EAPOL-Key packet"); 728198092Srdivacky os_memcpy(key->key_signature, orig_key_sign, 729198092Srdivacky IEEE8021X_KEY_SIGN_LEN); 730198092Srdivacky return; 731198092Srdivacky } 732198092Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified"); 733198092Srdivacky 734198092Srdivacky key_len = be_to_host16(hdr->length) - sizeof(*key); 735198092Srdivacky if (key_len > 32 || rx_key_length > 32) { 736198092Srdivacky wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d", 737198092Srdivacky key_len ? key_len : rx_key_length); 738198092Srdivacky return; 739198092Srdivacky } 740198092Srdivacky if (key_len == rx_key_length) { 741198092Srdivacky os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN); 742198092Srdivacky os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key, 743205219Srdivacky encr_key_len); 744205219Srdivacky os_memcpy(datakey, key + 1, key_len); 745205219Srdivacky rc4_skip(ekey, IEEE8021X_KEY_IV_LEN + encr_key_len, 0, 746205219Srdivacky datakey, key_len); 747205219Srdivacky wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key", 748205219Srdivacky datakey, key_len); 749205219Srdivacky } else if (key_len == 0) { 750207619Srdivacky /* 751207619Srdivacky * IEEE 802.1X-2004 specifies that least significant Key Length 752207619Srdivacky * octets from MS-MPPE-Send-Key are used as the key if the key 753207619Srdivacky * data is not present. This seems to be meaning the beginning 754207619Srdivacky * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in 755249423Sdim * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator. 756249423Sdim * Anyway, taking the beginning of the keying material from EAP 757249423Sdim * seems to interoperate with Authenticators. 758249423Sdim */ 759249423Sdim key_len = rx_key_length; 760249423Sdim os_memcpy(datakey, keydata.encr_key, key_len); 761249423Sdim wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying " 762249423Sdim "material data encryption key", 763249423Sdim datakey, key_len); 764249423Sdim } else { 765249423Sdim wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d " 766249423Sdim "(key_length=%d)", key_len, rx_key_length); 767249423Sdim return; 768249423Sdim } 769249423Sdim 770249423Sdim sm->replay_counter_valid = TRUE; 771249423Sdim os_memcpy(sm->last_replay_counter, key->replay_counter, 772249423Sdim IEEE8021X_REPLAY_COUNTER_LEN); 773249423Sdim 774249423Sdim wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d " 775249423Sdim "len %d", 776249423Sdim key->key_index & IEEE8021X_KEY_INDEX_FLAG ? 777249423Sdim "unicast" : "broadcast", 778223017Sdim key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len); 779223017Sdim 780210299Sed if (sm->ctx->set_wep_key && 781210299Sed sm->ctx->set_wep_key(sm->ctx->ctx, 782223017Sdim key->key_index & IEEE8021X_KEY_INDEX_FLAG, 783234353Sdim key->key_index & IEEE8021X_KEY_INDEX_MASK, 784249423Sdim datakey, key_len) < 0) { 785223017Sdim wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the " 786223017Sdim " driver."); 787193326Sed } else { 788193326Sed if (key->key_index & IEEE8021X_KEY_INDEX_FLAG) 789193326Sed sm->unicast_key_received = TRUE; 790198092Srdivacky else 791203955Srdivacky sm->broadcast_key_received = TRUE; 792198092Srdivacky 793193326Sed if ((sm->unicast_key_received || 794223017Sdim !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) && 795223017Sdim (sm->broadcast_key_received || 796223017Sdim !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST))) 797223017Sdim { 798223017Sdim wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key " 799223017Sdim "frames received"); 800193326Sed sm->portValid = TRUE; 801193326Sed if (sm->ctx->eapol_done_cb) 802193326Sed sm->ctx->eapol_done_cb(sm->ctx->ctx); 803193326Sed } 804249423Sdim } 805193326Sed} 806193326Sed 807249423Sdim 808249423Sdimstatic void eapol_sm_getSuppRsp(struct eapol_sm *sm) 809249423Sdim{ 810249423Sdim wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp"); 811210299Sed /* EAP layer processing; no special code is needed, since Supplicant 812223017Sdim * Backend state machine is waiting for eapNoResp or eapResp to be set 813249423Sdim * and these are only set in the EAP state machine when the processing 814249423Sdim * has finished. */ 815249423Sdim} 816249423Sdim 817249423Sdim 818249423Sdimstatic void eapol_sm_txSuppRsp(struct eapol_sm *sm) 819249423Sdim{ 820249423Sdim struct wpabuf *resp; 821249423Sdim 822249423Sdim wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp"); 823249423Sdim resp = eap_get_eapRespData(sm->eap); 824249423Sdim if (resp == NULL) { 825249423Sdim wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data " 826249423Sdim "not available"); 827249423Sdim return; 828249423Sdim } 829249423Sdim 830249423Sdim /* Send EAP-Packet from the EAP layer to the Authenticator */ 831249423Sdim sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, 832249423Sdim IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(resp), 833223017Sdim wpabuf_len(resp)); 834223017Sdim 835223017Sdim /* eapRespData is not used anymore, so free it here */ 836223017Sdim wpabuf_free(resp); 837249423Sdim 838249423Sdim if (sm->initial_req) 839223017Sdim sm->dot1xSuppEapolReqIdFramesRx++; 840223017Sdim else 841223017Sdim sm->dot1xSuppEapolReqFramesRx++; 842223017Sdim sm->dot1xSuppEapolRespFramesTx++; 843223017Sdim sm->dot1xSuppEapolFramesTx++; 844249423Sdim} 845223017Sdim 846223017Sdim 847249423Sdimstatic void eapol_sm_abortSupp(struct eapol_sm *sm) 848249423Sdim{ 849249423Sdim /* release system resources that may have been allocated for the 850249423Sdim * authentication session */ 851223017Sdim os_free(sm->last_rx_key); 852223017Sdim sm->last_rx_key = NULL; 853226633Sdim wpabuf_free(sm->eapReqData); 854226633Sdim sm->eapReqData = NULL; 855226633Sdim eap_sm_abort(sm->eap); 856226633Sdim} 857226633Sdim 858226633Sdim 859226633Sdimstatic void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx) 860226633Sdim{ 861226633Sdim eapol_sm_step(timeout_ctx); 862226633Sdim} 863226633Sdim 864226633Sdim 865226633Sdim/** 866226633Sdim * eapol_sm_step - EAPOL state machine step function 867226633Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 868226633Sdim * 869249423Sdim * This function is called to notify the state machine about changed external 870226633Sdim * variables. It will step through the EAPOL state machines in loop to process 871226633Sdim * all triggered state changes. 872226633Sdim */ 873249423Sdimvoid eapol_sm_step(struct eapol_sm *sm) 874249423Sdim{ 875226633Sdim int i; 876226633Sdim 877249423Sdim /* In theory, it should be ok to run this in loop until !changed. 878249423Sdim * However, it is better to use a limit on number of iterations to 879249423Sdim * allow events (e.g., SIGTERM) to stop the program cleanly if the 880249423Sdim * state machine were to generate a busy loop. */ 881249423Sdim for (i = 0; i < 100; i++) { 882249423Sdim sm->changed = FALSE; 883193326Sed SM_STEP_RUN(SUPP_PAE); 884193326Sed SM_STEP_RUN(KEY_RX); 885193326Sed SM_STEP_RUN(SUPP_BE); 886193326Sed if (eap_peer_sm_step(sm->eap)) 887249423Sdim sm->changed = TRUE; 888193326Sed if (!sm->changed) 889193326Sed break; 890249423Sdim } 891249423Sdim 892249423Sdim if (sm->changed) { 893249423Sdim /* restart EAPOL state machine step from timeout call in order 894210299Sed * to allow other events to be processed. */ 895223017Sdim eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); 896249423Sdim eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm); 897249423Sdim } 898249423Sdim 899249423Sdim if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) { 900249423Sdim int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0; 901249423Sdim sm->cb_status = EAPOL_CB_IN_PROGRESS; 902249423Sdim sm->ctx->cb(sm, success, sm->ctx->cb_ctx); 903249423Sdim } 904249423Sdim} 905249423Sdim 906249423Sdim 907249423Sdim#ifdef CONFIG_CTRL_IFACE 908249423Sdimstatic const char *eapol_supp_pae_state(int state) 909249423Sdim{ 910249423Sdim switch (state) { 911249423Sdim case SUPP_PAE_LOGOFF: 912249423Sdim return "LOGOFF"; 913249423Sdim case SUPP_PAE_DISCONNECTED: 914249423Sdim return "DISCONNECTED"; 915249423Sdim case SUPP_PAE_CONNECTING: 916249423Sdim return "CONNECTING"; 917223017Sdim case SUPP_PAE_AUTHENTICATING: 918223017Sdim return "AUTHENTICATING"; 919223017Sdim case SUPP_PAE_HELD: 920249423Sdim return "HELD"; 921223017Sdim case SUPP_PAE_AUTHENTICATED: 922223017Sdim return "AUTHENTICATED"; 923249423Sdim case SUPP_PAE_RESTART: 924249423Sdim return "RESTART"; 925249423Sdim default: 926249423Sdim return "UNKNOWN"; 927223017Sdim } 928223017Sdim} 929226633Sdim 930226633Sdim 931226633Sdimstatic const char *eapol_supp_be_state(int state) 932226633Sdim{ 933226633Sdim switch (state) { 934226633Sdim case SUPP_BE_REQUEST: 935226633Sdim return "REQUEST"; 936226633Sdim case SUPP_BE_RESPONSE: 937226633Sdim return "RESPONSE"; 938226633Sdim case SUPP_BE_SUCCESS: 939226633Sdim return "SUCCESS"; 940226633Sdim case SUPP_BE_FAIL: 941226633Sdim return "FAIL"; 942226633Sdim case SUPP_BE_TIMEOUT: 943226633Sdim return "TIMEOUT"; 944226633Sdim case SUPP_BE_IDLE: 945226633Sdim return "IDLE"; 946249423Sdim case SUPP_BE_INITIALIZE: 947226633Sdim return "INITIALIZE"; 948226633Sdim case SUPP_BE_RECEIVE: 949226633Sdim return "RECEIVE"; 950249423Sdim default: 951249423Sdim return "UNKNOWN"; 952226633Sdim } 953226633Sdim} 954249423Sdim 955249423Sdim 956249423Sdimstatic const char * eapol_port_status(PortStatus status) 957249423Sdim{ 958249423Sdim if (status == Authorized) 959249423Sdim return "Authorized"; 960193326Sed else 961193326Sed return "Unauthorized"; 962193326Sed} 963203955Srdivacky#endif /* CONFIG_CTRL_IFACE */ 964249423Sdim 965203955Srdivacky 966193326Sed#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) 967249423Sdimstatic const char * eapol_port_control(PortControl ctrl) 968249423Sdim{ 969249423Sdim switch (ctrl) { 970249423Sdim case Auto: 971249423Sdim return "Auto"; 972218893Sdim case ForceUnauthorized: 973249423Sdim return "ForceUnauthorized"; 974249423Sdim case ForceAuthorized: 975249423Sdim return "ForceAuthorized"; 976249423Sdim default: 977249423Sdim return "Unknown"; 978249423Sdim } 979234353Sdim} 980234353Sdim#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ 981249423Sdim 982234353Sdim 983234353Sdim/** 984234353Sdim * eapol_sm_configure - Set EAPOL variables 985234353Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 986234353Sdim * @heldPeriod: dot1xSuppHeldPeriod 987234353Sdim * @authPeriod: dot1xSuppAuthPeriod 988234353Sdim * @startPeriod: dot1xSuppStartPeriod 989234353Sdim * @maxStart: dot1xSuppMaxStart 990234353Sdim * 991234353Sdim * Set configurable EAPOL state machine variables. Each variable can be set to 992234353Sdim * the given value or ignored if set to -1 (to set only some of the variables). 993234353Sdim */ 994234353Sdimvoid eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, 995234353Sdim int startPeriod, int maxStart) 996234353Sdim{ 997234353Sdim if (sm == NULL) 998234353Sdim return; 999234353Sdim if (heldPeriod >= 0) 1000234353Sdim sm->heldPeriod = heldPeriod; 1001234353Sdim if (authPeriod >= 0) 1002249423Sdim sm->authPeriod = authPeriod; 1003203955Srdivacky if (startPeriod >= 0) 1004249423Sdim sm->startPeriod = startPeriod; 1005203955Srdivacky if (maxStart >= 0) 1006203955Srdivacky sm->maxStart = maxStart; 1007249423Sdim} 1008203955Srdivacky 1009199990Srdivacky 1010206084Srdivacky#ifdef CONFIG_CTRL_IFACE 1011206084Srdivacky/** 1012206084Srdivacky * eapol_sm_get_status - Get EAPOL state machine status 1013206084Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1014206084Srdivacky * @buf: Buffer for status information 1015198092Srdivacky * @buflen: Maximum buffer length 1016198092Srdivacky * @verbose: Whether to include verbose status information 1017249423Sdim * Returns: Number of bytes written to buf. 1018249423Sdim * 1019199990Srdivacky * Query EAPOL state machine for status information. This function fills in a 1020193326Sed * text area with current status information from the EAPOL state machine. If 1021193326Sed * the buffer (buf) is not large enough, status information will be truncated 1022193326Sed * to fit the buffer. 1023193326Sed */ 1024203955Srdivackyint eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, 1025193326Sed int verbose) 1026239462Sdim{ 1027239462Sdim int len, ret; 1028239462Sdim if (sm == NULL) 1029239462Sdim return 0; 1030249423Sdim 1031249423Sdim len = os_snprintf(buf, buflen, 1032249423Sdim "Supplicant PAE state=%s\n" 1033249423Sdim "suppPortStatus=%s\n", 1034249423Sdim eapol_supp_pae_state(sm->SUPP_PAE_state), 1035249423Sdim eapol_port_status(sm->suppPortStatus)); 1036249423Sdim if (len < 0 || (size_t) len >= buflen) 1037249423Sdim return 0; 1038249423Sdim 1039249423Sdim if (verbose) { 1040249423Sdim ret = os_snprintf(buf + len, buflen - len, 1041249423Sdim "heldPeriod=%u\n" 1042249423Sdim "authPeriod=%u\n" 1043249423Sdim "startPeriod=%u\n" 1044193326Sed "maxStart=%u\n" 1045193326Sed "portControl=%s\n" 1046193326Sed "Supplicant Backend state=%s\n", 1047193326Sed sm->heldPeriod, 1048249423Sdim sm->authPeriod, 1049249423Sdim sm->startPeriod, 1050203955Srdivacky sm->maxStart, 1051193326Sed eapol_port_control(sm->portControl), 1052234353Sdim eapol_supp_be_state(sm->SUPP_BE_state)); 1053234353Sdim if (ret < 0 || (size_t) ret >= buflen - len) 1054234353Sdim return len; 1055234353Sdim len += ret; 1056198092Srdivacky } 1057198092Srdivacky 1058198092Srdivacky len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose); 1059198092Srdivacky 1060203955Srdivacky return len; 1061198092Srdivacky} 1062193326Sed 1063193326Sed 1064203955Srdivacky/** 1065193326Sed * eapol_sm_get_mib - Get EAPOL state machine MIBs 1066193326Sed * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1067193326Sed * @buf: Buffer for MIB information 1068203955Srdivacky * @buflen: Maximum buffer length 1069198092Srdivacky * Returns: Number of bytes written to buf. 1070221345Sdim * 1071221345Sdim * Query EAPOL state machine for MIB information. This function fills in a 1072221345Sdim * text area with current MIB information from the EAPOL state machine. If 1073221345Sdim * the buffer (buf) is not large enough, MIB information will be truncated to 1074223017Sdim * fit the buffer. 1075223017Sdim */ 1076223017Sdimint eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen) 1077234353Sdim{ 1078249423Sdim size_t len; 1079249423Sdim int ret; 1080223017Sdim 1081249423Sdim if (sm == NULL) 1082249423Sdim return 0; 1083223017Sdim ret = os_snprintf(buf, buflen, 1084198092Srdivacky "dot1xSuppPaeState=%d\n" 1085249423Sdim "dot1xSuppHeldPeriod=%u\n" 1086249423Sdim "dot1xSuppAuthPeriod=%u\n" 1087249423Sdim "dot1xSuppStartPeriod=%u\n" 1088249423Sdim "dot1xSuppMaxStart=%u\n" 1089249423Sdim "dot1xSuppSuppControlledPortStatus=%s\n" 1090249423Sdim "dot1xSuppBackendPaeState=%d\n", 1091249423Sdim sm->SUPP_PAE_state, 1092249423Sdim sm->heldPeriod, 1093249423Sdim sm->authPeriod, 1094249423Sdim sm->startPeriod, 1095226633Sdim sm->maxStart, 1096234353Sdim sm->suppPortStatus == Authorized ? 1097249423Sdim "Authorized" : "Unauthorized", 1098234353Sdim sm->SUPP_BE_state); 1099221345Sdim 1100221345Sdim if (ret < 0 || (size_t) ret >= buflen) 1101249423Sdim return 0; 1102249423Sdim len = ret; 1103234353Sdim 1104239462Sdim ret = os_snprintf(buf + len, buflen - len, 1105239462Sdim "dot1xSuppEapolFramesRx=%u\n" 1106234353Sdim "dot1xSuppEapolFramesTx=%u\n" 1107234353Sdim "dot1xSuppEapolStartFramesTx=%u\n" 1108249423Sdim "dot1xSuppEapolLogoffFramesTx=%u\n" 1109234353Sdim "dot1xSuppEapolRespFramesTx=%u\n" 1110234353Sdim "dot1xSuppEapolReqIdFramesRx=%u\n" 1111249423Sdim "dot1xSuppEapolReqFramesRx=%u\n" 1112239462Sdim "dot1xSuppInvalidEapolFramesRx=%u\n" 1113234353Sdim "dot1xSuppEapLengthErrorFramesRx=%u\n" 1114234353Sdim "dot1xSuppLastEapolFrameVersion=%u\n" 1115249423Sdim "dot1xSuppLastEapolFrameSource=" MACSTR "\n", 1116249423Sdim sm->dot1xSuppEapolFramesRx, 1117203955Srdivacky sm->dot1xSuppEapolFramesTx, 1118249423Sdim sm->dot1xSuppEapolStartFramesTx, 1119203955Srdivacky sm->dot1xSuppEapolLogoffFramesTx, 1120198092Srdivacky sm->dot1xSuppEapolRespFramesTx, 1121249423Sdim sm->dot1xSuppEapolReqIdFramesRx, 1122249423Sdim sm->dot1xSuppEapolReqFramesRx, 1123249423Sdim sm->dot1xSuppInvalidEapolFramesRx, 1124249423Sdim sm->dot1xSuppEapLengthErrorFramesRx, 1125249423Sdim sm->dot1xSuppLastEapolFrameVersion, 1126249423Sdim MAC2STR(sm->dot1xSuppLastEapolFrameSource)); 1127249423Sdim 1128249423Sdim if (ret < 0 || (size_t) ret >= buflen - len) 1129249423Sdim return len; 1130221345Sdim len += ret; 1131249423Sdim 1132249423Sdim return len; 1133221345Sdim} 1134221345Sdim#endif /* CONFIG_CTRL_IFACE */ 1135249423Sdim 1136249423Sdim 1137249423Sdim/** 1138249423Sdim * eapol_sm_rx_eapol - Process received EAPOL frames 1139249423Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1140249423Sdim * @src: Source MAC address of the EAPOL packet 1141249423Sdim * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) 1142249423Sdim * @len: Length of the EAPOL frame 1143249423Sdim * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine, 1144249423Sdim * -1 failure 1145203955Srdivacky */ 1146249423Sdimint eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, 1147203955Srdivacky size_t len) 1148198092Srdivacky{ 1149249423Sdim const struct ieee802_1x_hdr *hdr; 1150249423Sdim const struct ieee802_1x_eapol_key *key; 1151249423Sdim int data_len; 1152249423Sdim int res = 1; 1153249423Sdim size_t plen; 1154249423Sdim 1155249423Sdim if (sm == NULL) 1156249423Sdim return 0; 1157249423Sdim sm->dot1xSuppEapolFramesRx++; 1158221345Sdim if (len < sizeof(*hdr)) { 1159249423Sdim sm->dot1xSuppInvalidEapolFramesRx++; 1160249423Sdim return 0; 1161221345Sdim } 1162221345Sdim hdr = (const struct ieee802_1x_hdr *) buf; 1163249423Sdim sm->dot1xSuppLastEapolFrameVersion = hdr->version; 1164249423Sdim os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN); 1165249423Sdim if (hdr->version < EAPOL_VERSION) { 1166249423Sdim /* TODO: backwards compatibility */ 1167249423Sdim } 1168249423Sdim plen = be_to_host16(hdr->length); 1169249423Sdim if (plen > len - sizeof(*hdr)) { 1170198092Srdivacky sm->dot1xSuppEapLengthErrorFramesRx++; 1171249423Sdim return 0; 1172249423Sdim } 1173249423Sdim#ifdef CONFIG_WPS 1174249423Sdim if (sm->conf.workaround && 1175249423Sdim plen < len - sizeof(*hdr) && 1176249423Sdim hdr->type == IEEE802_1X_TYPE_EAP_PACKET && 1177249423Sdim len - sizeof(*hdr) > sizeof(struct eap_hdr)) { 1178249423Sdim const struct eap_hdr *ehdr = 1179249423Sdim (const struct eap_hdr *) (hdr + 1); 1180249423Sdim u16 elen; 1181249423Sdim 1182249423Sdim elen = be_to_host16(ehdr->length); 1183234353Sdim if (elen > plen && elen <= len - sizeof(*hdr)) { 1184234353Sdim /* 1185234353Sdim * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS 1186234353Sdim * packets with too short EAPOL header length field 1187234353Sdim * (14 octets). This is fixed in firmware Ver.1.49. 1188234353Sdim * As a workaround, fix the EAPOL header based on the 1189234353Sdim * correct length in the EAP packet. 1190234353Sdim */ 1191234353Sdim wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL " 1192221345Sdim "payload length based on EAP header: " 1193221345Sdim "%d -> %d", (int) plen, elen); 1194221345Sdim plen = elen; 1195221345Sdim } 1196221345Sdim } 1197223017Sdim#endif /* CONFIG_WPS */ 1198221345Sdim data_len = plen + sizeof(*hdr); 1199221345Sdim 1200223017Sdim switch (hdr->type) { 1201223017Sdim case IEEE802_1X_TYPE_EAP_PACKET: 1202223017Sdim if (sm->cached_pmk) { 1203223017Sdim /* Trying to use PMKSA caching, but Authenticator did 1204223017Sdim * not seem to have a matching entry. Need to restart 1205223017Sdim * EAPOL state machines. 1206223017Sdim */ 1207223017Sdim eapol_sm_abort_cached(sm); 1208223017Sdim } 1209226633Sdim wpabuf_free(sm->eapReqData); 1210226633Sdim sm->eapReqData = wpabuf_alloc_copy(hdr + 1, plen); 1211234353Sdim if (sm->eapReqData) { 1212226633Sdim wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet " 1213234353Sdim "frame"); 1214226633Sdim sm->eapolEap = TRUE; 1215226633Sdim eapol_sm_step(sm); 1216226633Sdim } 1217226633Sdim break; 1218226633Sdim case IEEE802_1X_TYPE_EAPOL_KEY: 1219234353Sdim if (plen < sizeof(*key)) { 1220234353Sdim wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key " 1221226633Sdim "frame received"); 1222234353Sdim break; 1223234353Sdim } 1224226633Sdim key = (const struct ieee802_1x_eapol_key *) (hdr + 1); 1225226633Sdim if (key->type == EAPOL_KEY_TYPE_WPA || 1226234353Sdim key->type == EAPOL_KEY_TYPE_RSN) { 1227234353Sdim /* WPA Supplicant takes care of this frame. */ 1228226633Sdim wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key " 1229226633Sdim "frame in EAPOL state machines"); 1230226633Sdim res = 0; 1231193326Sed break; 1232193326Sed } 1233193326Sed if (key->type != EAPOL_KEY_TYPE_RC4) { 1234193326Sed wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown " 1235193326Sed "EAPOL-Key type %d", key->type); 1236193326Sed break; 1237239462Sdim } 1238193326Sed os_free(sm->last_rx_key); 1239193326Sed sm->last_rx_key = os_malloc(data_len); 1240193326Sed if (sm->last_rx_key) { 1241193326Sed wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key " 1242239462Sdim "frame"); 1243193326Sed os_memcpy(sm->last_rx_key, buf, data_len); 1244193326Sed sm->last_rx_key_len = data_len; 1245193326Sed sm->rxKey = TRUE; 1246193326Sed eapol_sm_step(sm); 1247193326Sed } 1248193326Sed break; 1249193326Sed default: 1250198092Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d", 1251234353Sdim hdr->type); 1252198092Srdivacky sm->dot1xSuppInvalidEapolFramesRx++; 1253198092Srdivacky break; 1254198092Srdivacky } 1255249423Sdim 1256249423Sdim return res; 1257249423Sdim} 1258234353Sdim 1259193326Sed 1260193326Sed/** 1261198092Srdivacky * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet 1262198092Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1263193326Sed * 1264193326Sed * Notify EAPOL state machine about transmitted EAPOL packet from an external 1265193326Sed * component, e.g., WPA. This will update the statistics. 1266193326Sed */ 1267193326Sedvoid eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm) 1268193326Sed{ 1269193326Sed if (sm) 1270193326Sed sm->dot1xSuppEapolFramesTx++; 1271193326Sed} 1272193326Sed 1273193326Sed 1274193326Sed/** 1275193326Sed * eapol_sm_notify_portEnabled - Notification about portEnabled change 1276193326Sed * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1277193326Sed * @enabled: New portEnabled value 1278193326Sed * 1279193326Sed * Notify EAPOL state machine about new portEnabled value. 1280193326Sed */ 1281193326Sedvoid eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled) 1282193326Sed{ 1283198092Srdivacky if (sm == NULL) 1284198092Srdivacky return; 1285198092Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: External notification - " 1286200583Srdivacky "portEnabled=%d", enabled); 1287234353Sdim sm->portEnabled = enabled; 1288198092Srdivacky eapol_sm_step(sm); 1289198092Srdivacky} 1290198092Srdivacky 1291193326Sed 1292210299Sed/** 1293198092Srdivacky * eapol_sm_notify_portValid - Notification about portValid change 1294195099Sed * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1295195099Sed * @valid: New portValid value 1296195099Sed * 1297195099Sed * Notify EAPOL state machine about new portValid value. 1298195099Sed */ 1299198092Srdivackyvoid eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid) 1300195099Sed{ 1301195099Sed if (sm == NULL) 1302198092Srdivacky return; 1303249423Sdim wpa_printf(MSG_DEBUG, "EAPOL: External notification - " 1304249423Sdim "portValid=%d", valid); 1305249423Sdim sm->portValid = valid; 1306249423Sdim eapol_sm_step(sm); 1307198092Srdivacky} 1308198092Srdivacky 1309198092Srdivacky 1310198092Srdivacky/** 1311198092Srdivacky * eapol_sm_notify_eap_success - Notification of external EAP success trigger 1312198092Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1313198092Srdivacky * @success: %TRUE = set success, %FALSE = clear success 1314198092Srdivacky * 1315198092Srdivacky * Notify the EAPOL state machine that external event has forced EAP state to 1316198092Srdivacky * success (success = %TRUE). This can be cleared by setting success = %FALSE. 1317218893Sdim * 1318234353Sdim * This function is called to update EAP state when WPA-PSK key handshake has 1319198092Srdivacky * been completed successfully since WPA-PSK does not use EAP state machine. 1320198092Srdivacky */ 1321198092Srdivackyvoid eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success) 1322198092Srdivacky{ 1323198092Srdivacky if (sm == NULL) 1324198092Srdivacky return; 1325198092Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: External notification - " 1326198092Srdivacky "EAP success=%d", success); 1327198092Srdivacky sm->eapSuccess = success; 1328198092Srdivacky sm->altAccept = success; 1329198092Srdivacky if (success) 1330198092Srdivacky eap_notify_success(sm->eap); 1331198092Srdivacky eapol_sm_step(sm); 1332198092Srdivacky} 1333234353Sdim 1334234353Sdim 1335218893Sdim/** 1336200583Srdivacky * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger 1337204643Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1338204643Srdivacky * @fail: %TRUE = set failure, %FALSE = clear failure 1339204643Srdivacky * 1340204643Srdivacky * Notify EAPOL state machine that external event has forced EAP state to 1341204643Srdivacky * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE. 1342204643Srdivacky */ 1343204643Srdivackyvoid eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail) 1344204643Srdivacky{ 1345204643Srdivacky if (sm == NULL) 1346204643Srdivacky return; 1347204643Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: External notification - " 1348204643Srdivacky "EAP fail=%d", fail); 1349204643Srdivacky sm->eapFail = fail; 1350239462Sdim sm->altReject = fail; 1351204643Srdivacky eapol_sm_step(sm); 1352200583Srdivacky} 1353200583Srdivacky 1354200583Srdivacky 1355200583Srdivacky/** 1356200583Srdivacky * eapol_sm_notify_config - Notification of EAPOL configuration change 1357200583Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1358243830Sdim * @config: Pointer to current network EAP configuration 1359200583Srdivacky * @conf: Pointer to EAPOL configuration data 1360200583Srdivacky * 1361200583Srdivacky * Notify EAPOL state machine that configuration has changed. config will be 1362200583Srdivacky * stored as a backpointer to network configuration. This can be %NULL to clear 1363200583Srdivacky * the stored pointed. conf will be copied to local EAPOL/EAP configuration 1364200583Srdivacky * data. If conf is %NULL, this part of the configuration change will be 1365200583Srdivacky * skipped. 1366200583Srdivacky */ 1367200583Srdivackyvoid eapol_sm_notify_config(struct eapol_sm *sm, 1368200583Srdivacky struct eap_peer_config *config, 1369234353Sdim const struct eapol_config *conf) 1370200583Srdivacky{ 1371200583Srdivacky if (sm == NULL) 1372200583Srdivacky return; 1373200583Srdivacky 1374200583Srdivacky sm->config = config; 1375234353Sdim 1376234353Sdim if (conf == NULL) 1377198092Srdivacky return; 1378198092Srdivacky 1379234353Sdim sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys; 1380198092Srdivacky sm->conf.required_keys = conf->required_keys; 1381198092Srdivacky sm->conf.fast_reauth = conf->fast_reauth; 1382234353Sdim sm->conf.workaround = conf->workaround; 1383198092Srdivacky if (sm->eap) { 1384198092Srdivacky eap_set_fast_reauth(sm->eap, conf->fast_reauth); 1385198092Srdivacky eap_set_workaround(sm->eap, conf->workaround); 1386198092Srdivacky eap_set_force_disabled(sm->eap, conf->eap_disabled); 1387198092Srdivacky } 1388198092Srdivacky} 1389199482Srdivacky 1390198092Srdivacky 1391198092Srdivacky/** 1392234353Sdim * eapol_sm_get_key - Get master session key (MSK) from EAP 1393198092Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1394198092Srdivacky * @key: Pointer for key buffer 1395198092Srdivacky * @len: Number of bytes to copy to key 1396234353Sdim * Returns: 0 on success (len of key available), maximum available key len 1397198092Srdivacky * (>0) if key is available but it is shorter than len, or -1 on failure. 1398234353Sdim * 1399198092Srdivacky * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key 1400198092Srdivacky * is available only after a successful authentication. 1401198092Srdivacky */ 1402198092Srdivackyint eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) 1403198092Srdivacky{ 1404198092Srdivacky const u8 *eap_key; 1405198092Srdivacky size_t eap_len; 1406198092Srdivacky 1407198092Srdivacky if (sm == NULL || !eap_key_available(sm->eap)) { 1408198092Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available"); 1409198092Srdivacky return -1; 1410198092Srdivacky } 1411198092Srdivacky eap_key = eap_get_eapKeyData(sm->eap, &eap_len); 1412198092Srdivacky if (eap_key == NULL) { 1413198092Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData"); 1414199482Srdivacky return -1; 1415234353Sdim } 1416198092Srdivacky if (len > eap_len) { 1417198092Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not " 1418198092Srdivacky "available (len=%lu)", 1419198092Srdivacky (unsigned long) len, (unsigned long) eap_len); 1420198092Srdivacky return eap_len; 1421198092Srdivacky } 1422198092Srdivacky os_memcpy(key, eap_key, len); 1423199482Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: Successfully fetched key (len=%lu)", 1424199482Srdivacky (unsigned long) len); 1425204643Srdivacky return 0; 1426204643Srdivacky} 1427204643Srdivacky 1428204643Srdivacky 1429204643Srdivacky/** 1430204643Srdivacky * eapol_sm_notify_logoff - Notification of logon/logoff commands 1431204643Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1432204643Srdivacky * @logoff: Whether command was logoff 1433204643Srdivacky * 1434204643Srdivacky * Notify EAPOL state machines that user requested logon/logoff. 1435204643Srdivacky */ 1436204643Srdivackyvoid eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) 1437234353Sdim{ 1438198092Srdivacky if (sm) { 1439198092Srdivacky sm->userLogoff = logoff; 1440198092Srdivacky eapol_sm_step(sm); 1441198092Srdivacky } 1442198092Srdivacky} 1443198092Srdivacky 1444199482Srdivacky 1445199482Srdivacky/** 1446198092Srdivacky * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching 1447198092Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1448198092Srdivacky * 1449198092Srdivacky * Notify EAPOL state machines that PMKSA caching was successful. This is used 1450198092Srdivacky * to move EAPOL and EAP state machines into authenticated/successful state. 1451198092Srdivacky */ 1452198092Srdivackyvoid eapol_sm_notify_cached(struct eapol_sm *sm) 1453199482Srdivacky{ 1454199482Srdivacky if (sm == NULL) 1455234353Sdim return; 1456198092Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL"); 1457198092Srdivacky sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED; 1458198092Srdivacky sm->suppPortStatus = Authorized; 1459198092Srdivacky sm->portValid = TRUE; 1460198092Srdivacky eap_notify_success(sm->eap); 1461198092Srdivacky eapol_sm_step(sm); 1462199482Srdivacky} 1463198092Srdivacky 1464198092Srdivacky 1465206084Srdivacky/** 1466206084Srdivacky * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching 1467206084Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1468206084Srdivacky * @attempt: Whether PMKSA caching is tried 1469206084Srdivacky * 1470206084Srdivacky * Notify EAPOL state machines whether PMKSA caching is used. 1471218893Sdim */ 1472218893Sdimvoid eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt) 1473218893Sdim{ 1474193326Sed if (sm == NULL) 1475193326Sed return; 1476193326Sed if (attempt) { 1477193326Sed wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA"); 1478193326Sed sm->cached_pmk = TRUE; 1479202879Srdivacky } else { 1480202879Srdivacky wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA"); 1481202879Srdivacky sm->cached_pmk = FALSE; 1482202879Srdivacky } 1483202879Srdivacky} 1484202879Srdivacky 1485202879Srdivacky 1486202879Srdivackystatic void eapol_sm_abort_cached(struct eapol_sm *sm) 1487202879Srdivacky{ 1488249423Sdim wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, " 1489249423Sdim "doing full EAP authentication"); 1490249423Sdim if (sm == NULL) 1491249423Sdim return; 1492218893Sdim sm->cached_pmk = FALSE; 1493218893Sdim sm->SUPP_PAE_state = SUPP_PAE_CONNECTING; 1494218893Sdim sm->suppPortStatus = Unauthorized; 1495234353Sdim 1496218893Sdim /* Make sure we do not start sending EAPOL-Start frames first, but 1497234353Sdim * instead move to RESTART state to start EAPOL authentication. */ 1498218893Sdim sm->startWhen = 3; 1499218893Sdim eapol_enable_timer_tick(sm); 1500218893Sdim 1501218893Sdim if (sm->ctx->aborted_cached) 1502218893Sdim sm->ctx->aborted_cached(sm->ctx->ctx); 1503234353Sdim} 1504218893Sdim 1505218893Sdim 1506218893Sdim/** 1507218893Sdim * eapol_sm_register_scard_ctx - Notification of smart card context 1508218893Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1509218893Sdim * @ctx: Context data for smart card operations 1510218893Sdim * 1511218893Sdim * Notify EAPOL state machines of context data for smart card operations. This 1512234353Sdim * context data will be used as a parameter for scard_*() functions. 1513234353Sdim */ 1514234353Sdimvoid eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx) 1515234353Sdim{ 1516234353Sdim if (sm) { 1517234353Sdim sm->ctx->scard_ctx = ctx; 1518234353Sdim eap_register_scard_ctx(sm->eap, ctx); 1519234353Sdim } 1520234353Sdim} 1521234353Sdim 1522234353Sdim 1523218893Sdim/** 1524234353Sdim * eapol_sm_notify_portControl - Notification of portControl changes 1525234353Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1526234353Sdim * @portControl: New value for portControl variable 1527234353Sdim * 1528234353Sdim * Notify EAPOL state machines that portControl variable has changed. 1529234353Sdim */ 1530234353Sdimvoid eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl) 1531234353Sdim{ 1532234353Sdim if (sm == NULL) 1533234353Sdim return; 1534234353Sdim wpa_printf(MSG_DEBUG, "EAPOL: External notification - " 1535234353Sdim "portControl=%s", eapol_port_control(portControl)); 1536234353Sdim sm->portControl = portControl; 1537234353Sdim eapol_sm_step(sm); 1538234353Sdim} 1539234353Sdim 1540234353Sdim 1541234353Sdim/** 1542234353Sdim * eapol_sm_notify_ctrl_attached - Notification of attached monitor 1543234353Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1544234353Sdim * 1545249423Sdim * Notify EAPOL state machines that a monitor was attached to the control 1546249423Sdim * interface to trigger re-sending of pending requests for user input. 1547249423Sdim */ 1548234353Sdimvoid eapol_sm_notify_ctrl_attached(struct eapol_sm *sm) 1549234353Sdim{ 1550234353Sdim if (sm == NULL) 1551234353Sdim return; 1552234353Sdim eap_sm_notify_ctrl_attached(sm->eap); 1553234353Sdim} 1554234353Sdim 1555234353Sdim 1556234353Sdim/** 1557234353Sdim * eapol_sm_notify_ctrl_response - Notification of received user input 1558234353Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1559234353Sdim * 1560234353Sdim * Notify EAPOL state machines that a control response, i.e., user 1561234353Sdim * input, was received in order to trigger retrying of a pending EAP request. 1562243830Sdim */ 1563243830Sdimvoid eapol_sm_notify_ctrl_response(struct eapol_sm *sm) 1564243830Sdim{ 1565243830Sdim if (sm == NULL) 1566243830Sdim return; 1567203955Srdivacky if (sm->eapReqData && !sm->eapReq) { 1568203955Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: received control response (user " 1569210299Sed "input) notification - retrying pending EAP " 1570193326Sed "Request"); 1571210299Sed sm->eapolEap = TRUE; 1572212904Sdim sm->eapReq = TRUE; 1573212904Sdim eapol_sm_step(sm); 1574218893Sdim } 1575218893Sdim} 1576193326Sed 1577193326Sed 1578193326Sed/** 1579193326Sed * eapol_sm_request_reauth - Request reauthentication 1580193326Sed * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1581234353Sdim * 1582193326Sed * This function can be used to request EAPOL reauthentication, e.g., when the 1583221345Sdim * current PMKSA entry is nearing expiration. 1584212904Sdim */ 1585212904Sdimvoid eapol_sm_request_reauth(struct eapol_sm *sm) 1586249423Sdim{ 1587226633Sdim if (sm == NULL || sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED) 1588221345Sdim return; 1589249423Sdim eapol_sm_txStart(sm); 1590234353Sdim} 1591234353Sdim 1592234353Sdim 1593193326Sed/** 1594193326Sed * eapol_sm_notify_lower_layer_success - Notification of lower layer success 1595193326Sed * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1596221345Sdim * @in_eapol_sm: Whether the caller is already running inside EAPOL state 1597212904Sdim * machine loop (eapol_sm_step()) 1598212904Sdim * 1599249423Sdim * Notify EAPOL (and EAP) state machines that a lower layer has detected a 1600221345Sdim * successful authentication. This is used to recover from dropped EAP-Success 1601226633Sdim * messages. 1602221345Sdim */ 1603198092Srdivackyvoid eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm) 1604234353Sdim{ 1605249423Sdim if (sm == NULL) 1606249423Sdim return; 1607193326Sed eap_notify_lower_layer_success(sm->eap); 1608193326Sed if (!in_eapol_sm) 1609243830Sdim eapol_sm_step(sm); 1610243830Sdim} 1611239462Sdim 1612198092Srdivacky 1613234353Sdim/** 1614198092Srdivacky * eapol_sm_invalidate_cached_session - Mark cached EAP session data invalid 1615198092Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1616243830Sdim */ 1617243830Sdimvoid eapol_sm_invalidate_cached_session(struct eapol_sm *sm) 1618243830Sdim{ 1619198092Srdivacky if (sm) 1620234353Sdim eap_invalidate_cached_session(sm->eap); 1621198092Srdivacky} 1622193326Sed 1623206084Srdivacky 1624198092Srdivackystatic struct eap_peer_config * eapol_sm_get_config(void *ctx) 1625198092Srdivacky{ 1626198092Srdivacky struct eapol_sm *sm = ctx; 1627198092Srdivacky return sm ? sm->config : NULL; 1628234353Sdim} 1629207619Srdivacky 1630207619Srdivacky 1631207619Srdivackystatic struct wpabuf * eapol_sm_get_eapReqData(void *ctx) 1632223017Sdim{ 1633223017Sdim struct eapol_sm *sm = ctx; 1634223017Sdim if (sm == NULL || sm->eapReqData == NULL) 1635234353Sdim return NULL; 1636198092Srdivacky 1637198092Srdivacky return sm->eapReqData; 1638198092Srdivacky} 1639198092Srdivacky 1640198092Srdivacky 1641198092Srdivackystatic Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable) 1642223017Sdim{ 1643239462Sdim struct eapol_sm *sm = ctx; 1644239462Sdim if (sm == NULL) 1645223017Sdim return FALSE; 1646223017Sdim switch (variable) { 1647223017Sdim case EAPOL_eapSuccess: 1648234353Sdim return sm->eapSuccess; 1649198092Srdivacky case EAPOL_eapRestart: 1650198092Srdivacky return sm->eapRestart; 1651193326Sed case EAPOL_eapFail: 1652234353Sdim return sm->eapFail; 1653198092Srdivacky case EAPOL_eapResp: 1654193326Sed return sm->eapResp; 1655193326Sed case EAPOL_eapNoResp: 1656210299Sed return sm->eapNoResp; 1657198092Srdivacky case EAPOL_eapReq: 1658193326Sed return sm->eapReq; 1659193326Sed case EAPOL_portEnabled: 1660198092Srdivacky return sm->portEnabled; 1661198092Srdivacky case EAPOL_altAccept: 1662193326Sed return sm->altAccept; 1663198092Srdivacky case EAPOL_altReject: 1664193326Sed return sm->altReject; 1665193326Sed } 1666198092Srdivacky return FALSE; 1667193326Sed} 1668193326Sed 1669193326Sed 1670193326Sedstatic void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable, 1671193326Sed Boolean value) 1672193326Sed{ 1673193326Sed struct eapol_sm *sm = ctx; 1674193326Sed if (sm == NULL) 1675193326Sed return; 1676198092Srdivacky switch (variable) { 1677193326Sed case EAPOL_eapSuccess: 1678193326Sed sm->eapSuccess = value; 1679218893Sdim break; 1680218893Sdim case EAPOL_eapRestart: 1681218893Sdim sm->eapRestart = value; 1682218893Sdim break; 1683239462Sdim case EAPOL_eapFail: 1684218893Sdim sm->eapFail = value; 1685218893Sdim break; 1686218893Sdim case EAPOL_eapResp: 1687218893Sdim sm->eapResp = value; 1688218893Sdim break; 1689239462Sdim case EAPOL_eapNoResp: 1690218893Sdim sm->eapNoResp = value; 1691218893Sdim break; 1692218893Sdim case EAPOL_eapReq: 1693234353Sdim sm->eapReq = value; 1694200583Srdivacky break; 1695200583Srdivacky case EAPOL_portEnabled: 1696234353Sdim sm->portEnabled = value; 1697234353Sdim break; 1698234353Sdim case EAPOL_altAccept: 1699234353Sdim sm->altAccept = value; 1700234353Sdim break; 1701234353Sdim case EAPOL_altReject: 1702234353Sdim sm->altReject = value; 1703234353Sdim break; 1704239462Sdim } 1705239462Sdim} 1706239462Sdim 1707239462Sdim 1708239462Sdimstatic unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable) 1709239462Sdim{ 1710239462Sdim struct eapol_sm *sm = ctx; 1711239462Sdim if (sm == NULL) 1712239462Sdim return 0; 1713239462Sdim switch (variable) { 1714239462Sdim case EAPOL_idleWhile: 1715239462Sdim return sm->idleWhile; 1716239462Sdim } 1717239462Sdim return 0; 1718239462Sdim} 1719239462Sdim 1720239462Sdim 1721193326Sedstatic void eapol_sm_set_int(void *ctx, enum eapol_int_var variable, 1722203955Srdivacky unsigned int value) 1723203955Srdivacky{ 1724210299Sed struct eapol_sm *sm = ctx; 1725193326Sed if (sm == NULL) 1726193326Sed return; 1727193326Sed switch (variable) { 1728218893Sdim case EAPOL_idleWhile: 1729193326Sed sm->idleWhile = value; 1730193326Sed eapol_enable_timer_tick(sm); 1731193326Sed break; 1732193326Sed } 1733193326Sed} 1734193326Sed 1735193326Sed 1736193326Sedstatic void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob) 1737193326Sed{ 1738193326Sed#ifndef CONFIG_NO_CONFIG_BLOBS 1739193326Sed struct eapol_sm *sm = ctx; 1740193326Sed if (sm && sm->ctx && sm->ctx->set_config_blob) 1741194613Sed sm->ctx->set_config_blob(sm->ctx->ctx, blob); 1742218893Sdim#endif /* CONFIG_NO_CONFIG_BLOBS */ 1743234353Sdim} 1744234353Sdim 1745234353Sdim 1746234353Sdimstatic const struct wpa_config_blob * 1747218893Sdimeapol_sm_get_config_blob(void *ctx, const char *name) 1748234353Sdim{ 1749218893Sdim#ifndef CONFIG_NO_CONFIG_BLOBS 1750221345Sdim struct eapol_sm *sm = ctx; 1751221345Sdim if (sm && sm->ctx && sm->ctx->get_config_blob) 1752221345Sdim return sm->ctx->get_config_blob(sm->ctx->ctx, name); 1753218893Sdim else 1754234353Sdim return NULL; 1755203955Srdivacky#else /* CONFIG_NO_CONFIG_BLOBS */ 1756203955Srdivacky return NULL; 1757203955Srdivacky#endif /* CONFIG_NO_CONFIG_BLOBS */ 1758234353Sdim} 1759200583Srdivacky 1760200583Srdivacky 1761193326Sedstatic void eapol_sm_notify_pending(void *ctx) 1762198092Srdivacky{ 1763198092Srdivacky struct eapol_sm *sm = ctx; 1764198092Srdivacky if (sm == NULL) 1765234353Sdim return; 1766234353Sdim if (sm->eapReqData && !sm->eapReq) { 1767234353Sdim wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP " 1768234353Sdim "state machine - retrying pending EAP Request"); 1769208600Srdivacky sm->eapolEap = TRUE; 1770208600Srdivacky sm->eapReq = TRUE; 1771208600Srdivacky eapol_sm_step(sm); 1772208600Srdivacky } 1773208600Srdivacky} 1774208600Srdivacky 1775208600Srdivacky 1776218893Sdim#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) 1777208600Srdivackystatic void eapol_sm_eap_param_needed(void *ctx, const char *field, 1778208600Srdivacky const char *txt) 1779208600Srdivacky{ 1780208600Srdivacky struct eapol_sm *sm = ctx; 1781208600Srdivacky wpa_printf(MSG_DEBUG, "EAPOL: EAP parameter needed"); 1782234353Sdim if (sm->ctx->eap_param_needed) 1783208600Srdivacky sm->ctx->eap_param_needed(sm->ctx->ctx, field, txt); 1784218893Sdim} 1785218893Sdim#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ 1786218893Sdim#define eapol_sm_eap_param_needed NULL 1787234353Sdim#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ 1788193326Sed 1789218893Sdim 1790198092Srdivackystatic struct eapol_callbacks eapol_cb = 1791218893Sdim{ 1792218893Sdim eapol_sm_get_config, 1793218893Sdim eapol_sm_get_bool, 1794193326Sed eapol_sm_set_bool, 1795218893Sdim eapol_sm_get_int, 1796198092Srdivacky eapol_sm_set_int, 1797218893Sdim eapol_sm_get_eapReqData, 1798218893Sdim eapol_sm_set_config_blob, 1799218893Sdim eapol_sm_get_config_blob, 1800193326Sed eapol_sm_notify_pending, 1801221345Sdim eapol_sm_eap_param_needed 1802218893Sdim}; 1803218893Sdim 1804218893Sdim 1805218893Sdim/** 1806218893Sdim * eapol_sm_init - Initialize EAPOL state machine 1807221345Sdim * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer 1808221345Sdim * and EAPOL state machine will free it in eapol_sm_deinit() 1809234353Sdim * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure 1810234353Sdim * 1811221345Sdim * Allocate and initialize an EAPOL state machine. 1812234353Sdim */ 1813208600Srdivackystruct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) 1814218893Sdim{ 1815218893Sdim struct eapol_sm *sm; 1816218893Sdim struct eap_config conf; 1817218893Sdim sm = os_zalloc(sizeof(*sm)); 1818234353Sdim if (sm == NULL) 1819193326Sed return NULL; 1820193326Sed sm->ctx = ctx; 1821234353Sdim 1822234353Sdim sm->portControl = Auto; 1823234353Sdim 1824193326Sed /* Supplicant PAE state machine */ 1825193326Sed sm->heldPeriod = 60; 1826193326Sed sm->startPeriod = 30; 1827218893Sdim sm->maxStart = 3; 1828193326Sed 1829234353Sdim /* Supplicant Backend state machine */ 1830218893Sdim sm->authPeriod = 30; 1831218893Sdim 1832218893Sdim os_memset(&conf, 0, sizeof(conf)); 1833218893Sdim#ifdef EAP_TLS_OPENSSL 1834218893Sdim conf.opensc_engine_path = ctx->opensc_engine_path; 1835218893Sdim conf.pkcs11_engine_path = ctx->pkcs11_engine_path; 1836218893Sdim conf.pkcs11_module_path = ctx->pkcs11_module_path; 1837223017Sdim#endif /* EAP_TLS_OPENSSL */ 1838223017Sdim conf.wps = ctx->wps; 1839223017Sdim 1840223017Sdim sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf); 1841251662Sdim if (sm->eap == NULL) { 1842223017Sdim os_free(sm); 1843223017Sdim return NULL; 1844221345Sdim } 1845221345Sdim 1846221345Sdim /* Initialize EAPOL state machines */ 1847234353Sdim sm->initialize = TRUE; 1848221345Sdim eapol_sm_step(sm); 1849221345Sdim sm->initialize = FALSE; 1850218893Sdim eapol_sm_step(sm); 1851234353Sdim 1852234353Sdim sm->timer_tick_enabled = 1; 1853218893Sdim eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); 1854234353Sdim 1855218893Sdim return sm; 1856218893Sdim} 1857218893Sdim 1858218893Sdim 1859218893Sdim/** 1860234353Sdim * eapol_sm_deinit - Deinitialize EAPOL state machine 1861234353Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1862200583Srdivacky * 1863200583Srdivacky * Deinitialize and free EAPOL state machine. 1864200583Srdivacky */ 1865193326Sedvoid eapol_sm_deinit(struct eapol_sm *sm) 1866200583Srdivacky{ 1867200583Srdivacky if (sm == NULL) 1868200583Srdivacky return; 1869207619Srdivacky eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); 1870207619Srdivacky eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); 1871207619Srdivacky eap_peer_sm_deinit(sm->eap); 1872207619Srdivacky os_free(sm->last_rx_key); 1873234353Sdim wpabuf_free(sm->eapReqData); 1874207619Srdivacky os_free(sm->ctx); 1875207619Srdivacky os_free(sm); 1876207619Srdivacky} 1877234353Sdim