1189251Ssam/* 2189251Ssam * EAPOL supplicant state machines 3189251Ssam * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> 4189251Ssam * 5189251Ssam * This program is free software; you can redistribute it and/or modify 6189251Ssam * it under the terms of the GNU General Public License version 2 as 7189251Ssam * published by the Free Software Foundation. 8189251Ssam * 9189251Ssam * Alternatively, this software may be distributed under the terms of BSD 10189251Ssam * license. 11189251Ssam * 12189251Ssam * See README and COPYING for more details. 13189251Ssam */ 14189251Ssam 15189251Ssam#include "includes.h" 16189251Ssam 17189251Ssam#include "common.h" 18189251Ssam#include "state_machine.h" 19189251Ssam#include "wpabuf.h" 20214734Srpaulo#include "eloop.h" 21214734Srpaulo#include "crypto/crypto.h" 22214734Srpaulo#include "crypto/md5.h" 23214734Srpaulo#include "common/eapol_common.h" 24214734Srpaulo#include "eap_peer/eap.h" 25214734Srpaulo#include "eapol_supp_sm.h" 26189251Ssam 27189251Ssam#define STATE_MACHINE_DATA struct eapol_sm 28189251Ssam#define STATE_MACHINE_DEBUG_PREFIX "EAPOL" 29189251Ssam 30189251Ssam 31189251Ssam/* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */ 32189251Ssam 33189251Ssam/** 34189251Ssam * struct eapol_sm - Internal data for EAPOL state machines 35189251Ssam */ 36189251Ssamstruct eapol_sm { 37189251Ssam /* Timers */ 38189251Ssam unsigned int authWhile; 39189251Ssam unsigned int heldWhile; 40189251Ssam unsigned int startWhen; 41189251Ssam unsigned int idleWhile; /* for EAP state machine */ 42189251Ssam int timer_tick_enabled; 43189251Ssam 44189251Ssam /* Global variables */ 45189251Ssam Boolean eapFail; 46189251Ssam Boolean eapolEap; 47189251Ssam Boolean eapSuccess; 48189251Ssam Boolean initialize; 49189251Ssam Boolean keyDone; 50189251Ssam Boolean keyRun; 51189251Ssam PortControl portControl; 52189251Ssam Boolean portEnabled; 53189251Ssam PortStatus suppPortStatus; /* dot1xSuppControlledPortStatus */ 54189251Ssam Boolean portValid; 55189251Ssam Boolean suppAbort; 56189251Ssam Boolean suppFail; 57189251Ssam Boolean suppStart; 58189251Ssam Boolean suppSuccess; 59189251Ssam Boolean suppTimeout; 60189251Ssam 61189251Ssam /* Supplicant PAE state machine */ 62189251Ssam enum { 63189251Ssam SUPP_PAE_UNKNOWN = 0, 64189251Ssam SUPP_PAE_DISCONNECTED = 1, 65189251Ssam SUPP_PAE_LOGOFF = 2, 66189251Ssam SUPP_PAE_CONNECTING = 3, 67189251Ssam SUPP_PAE_AUTHENTICATING = 4, 68189251Ssam SUPP_PAE_AUTHENTICATED = 5, 69189251Ssam /* unused(6) */ 70189251Ssam SUPP_PAE_HELD = 7, 71189251Ssam SUPP_PAE_RESTART = 8, 72189251Ssam SUPP_PAE_S_FORCE_AUTH = 9, 73189251Ssam SUPP_PAE_S_FORCE_UNAUTH = 10 74189251Ssam } SUPP_PAE_state; /* dot1xSuppPaeState */ 75189251Ssam /* Variables */ 76189251Ssam Boolean userLogoff; 77189251Ssam Boolean logoffSent; 78189251Ssam unsigned int startCount; 79189251Ssam Boolean eapRestart; 80189251Ssam PortControl sPortMode; 81189251Ssam /* Constants */ 82189251Ssam unsigned int heldPeriod; /* dot1xSuppHeldPeriod */ 83189251Ssam unsigned int startPeriod; /* dot1xSuppStartPeriod */ 84189251Ssam unsigned int maxStart; /* dot1xSuppMaxStart */ 85189251Ssam 86189251Ssam /* Key Receive state machine */ 87189251Ssam enum { 88189251Ssam KEY_RX_UNKNOWN = 0, 89189251Ssam KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE 90189251Ssam } KEY_RX_state; 91189251Ssam /* Variables */ 92189251Ssam Boolean rxKey; 93189251Ssam 94189251Ssam /* Supplicant Backend state machine */ 95189251Ssam enum { 96189251Ssam SUPP_BE_UNKNOWN = 0, 97189251Ssam SUPP_BE_INITIALIZE = 1, 98189251Ssam SUPP_BE_IDLE = 2, 99189251Ssam SUPP_BE_REQUEST = 3, 100189251Ssam SUPP_BE_RECEIVE = 4, 101189251Ssam SUPP_BE_RESPONSE = 5, 102189251Ssam SUPP_BE_FAIL = 6, 103189251Ssam SUPP_BE_TIMEOUT = 7, 104189251Ssam SUPP_BE_SUCCESS = 8 105189251Ssam } SUPP_BE_state; /* dot1xSuppBackendPaeState */ 106189251Ssam /* Variables */ 107189251Ssam Boolean eapNoResp; 108189251Ssam Boolean eapReq; 109189251Ssam Boolean eapResp; 110189251Ssam /* Constants */ 111189251Ssam unsigned int authPeriod; /* dot1xSuppAuthPeriod */ 112189251Ssam 113189251Ssam /* Statistics */ 114189251Ssam unsigned int dot1xSuppEapolFramesRx; 115189251Ssam unsigned int dot1xSuppEapolFramesTx; 116189251Ssam unsigned int dot1xSuppEapolStartFramesTx; 117189251Ssam unsigned int dot1xSuppEapolLogoffFramesTx; 118189251Ssam unsigned int dot1xSuppEapolRespFramesTx; 119189251Ssam unsigned int dot1xSuppEapolReqIdFramesRx; 120189251Ssam unsigned int dot1xSuppEapolReqFramesRx; 121189251Ssam unsigned int dot1xSuppInvalidEapolFramesRx; 122189251Ssam unsigned int dot1xSuppEapLengthErrorFramesRx; 123189251Ssam unsigned int dot1xSuppLastEapolFrameVersion; 124189251Ssam unsigned char dot1xSuppLastEapolFrameSource[6]; 125189251Ssam 126189251Ssam /* Miscellaneous variables (not defined in IEEE 802.1X-2004) */ 127189251Ssam Boolean changed; 128189251Ssam struct eap_sm *eap; 129189251Ssam struct eap_peer_config *config; 130189251Ssam Boolean initial_req; 131189251Ssam u8 *last_rx_key; 132189251Ssam size_t last_rx_key_len; 133189251Ssam struct wpabuf *eapReqData; /* for EAP */ 134189251Ssam Boolean altAccept; /* for EAP */ 135189251Ssam Boolean altReject; /* for EAP */ 136189251Ssam Boolean replay_counter_valid; 137189251Ssam u8 last_replay_counter[16]; 138189251Ssam struct eapol_config conf; 139189251Ssam struct eapol_ctx *ctx; 140189251Ssam enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE } 141189251Ssam cb_status; 142189251Ssam Boolean cached_pmk; 143189251Ssam 144189251Ssam Boolean unicast_key_received, broadcast_key_received; 145189251Ssam}; 146189251Ssam 147189251Ssam 148189251Ssam#define IEEE8021X_REPLAY_COUNTER_LEN 8 149189251Ssam#define IEEE8021X_KEY_SIGN_LEN 16 150189251Ssam#define IEEE8021X_KEY_IV_LEN 16 151189251Ssam 152189251Ssam#define IEEE8021X_KEY_INDEX_FLAG 0x80 153189251Ssam#define IEEE8021X_KEY_INDEX_MASK 0x03 154189251Ssam 155189251Ssam#ifdef _MSC_VER 156189251Ssam#pragma pack(push, 1) 157189251Ssam#endif /* _MSC_VER */ 158189251Ssam 159189251Ssamstruct ieee802_1x_eapol_key { 160189251Ssam u8 type; 161189251Ssam /* Note: key_length is unaligned */ 162189251Ssam u8 key_length[2]; 163189251Ssam /* does not repeat within the life of the keying material used to 164189251Ssam * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ 165189251Ssam u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; 166189251Ssam u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ 167189251Ssam u8 key_index; /* key flag in the most significant bit: 168189251Ssam * 0 = broadcast (default key), 169189251Ssam * 1 = unicast (key mapping key); key index is in the 170189251Ssam * 7 least significant bits */ 171189251Ssam /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as 172189251Ssam * the key */ 173189251Ssam u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; 174189251Ssam 175189251Ssam /* followed by key: if packet body length = 44 + key length, then the 176189251Ssam * key field (of key_length bytes) contains the key in encrypted form; 177189251Ssam * if packet body length = 44, key field is absent and key_length 178189251Ssam * represents the number of least significant octets from 179189251Ssam * MS-MPPE-Send-Key attribute to be used as the keying material; 180189251Ssam * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ 181189251Ssam} STRUCT_PACKED; 182189251Ssam 183189251Ssam#ifdef _MSC_VER 184189251Ssam#pragma pack(pop) 185189251Ssam#endif /* _MSC_VER */ 186189251Ssam 187189251Ssam 188189251Ssamstatic void eapol_sm_txLogoff(struct eapol_sm *sm); 189189251Ssamstatic void eapol_sm_txStart(struct eapol_sm *sm); 190189251Ssamstatic void eapol_sm_processKey(struct eapol_sm *sm); 191189251Ssamstatic void eapol_sm_getSuppRsp(struct eapol_sm *sm); 192189251Ssamstatic void eapol_sm_txSuppRsp(struct eapol_sm *sm); 193189251Ssamstatic void eapol_sm_abortSupp(struct eapol_sm *sm); 194189251Ssamstatic void eapol_sm_abort_cached(struct eapol_sm *sm); 195189251Ssamstatic void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx); 196214734Srpaulostatic void eapol_sm_set_port_authorized(struct eapol_sm *sm); 197214734Srpaulostatic void eapol_sm_set_port_unauthorized(struct eapol_sm *sm); 198189251Ssam 199189251Ssam 200189251Ssam/* Port Timers state machine - implemented as a function that will be called 201189251Ssam * once a second as a registered event loop timeout */ 202189251Ssamstatic void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) 203189251Ssam{ 204189251Ssam struct eapol_sm *sm = timeout_ctx; 205189251Ssam 206189251Ssam if (sm->authWhile > 0) { 207189251Ssam sm->authWhile--; 208189251Ssam if (sm->authWhile == 0) 209189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0"); 210189251Ssam } 211189251Ssam if (sm->heldWhile > 0) { 212189251Ssam sm->heldWhile--; 213189251Ssam if (sm->heldWhile == 0) 214189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0"); 215189251Ssam } 216189251Ssam if (sm->startWhen > 0) { 217189251Ssam sm->startWhen--; 218189251Ssam if (sm->startWhen == 0) 219189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0"); 220189251Ssam } 221189251Ssam if (sm->idleWhile > 0) { 222189251Ssam sm->idleWhile--; 223189251Ssam if (sm->idleWhile == 0) 224189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0"); 225189251Ssam } 226189251Ssam 227189251Ssam if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) { 228189251Ssam eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, 229189251Ssam sm); 230189251Ssam } else { 231189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick"); 232189251Ssam sm->timer_tick_enabled = 0; 233189251Ssam } 234189251Ssam eapol_sm_step(sm); 235189251Ssam} 236189251Ssam 237189251Ssam 238189251Ssamstatic void eapol_enable_timer_tick(struct eapol_sm *sm) 239189251Ssam{ 240189251Ssam if (sm->timer_tick_enabled) 241189251Ssam return; 242189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick"); 243189251Ssam sm->timer_tick_enabled = 1; 244189251Ssam eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); 245189251Ssam eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); 246189251Ssam} 247189251Ssam 248189251Ssam 249189251SsamSM_STATE(SUPP_PAE, LOGOFF) 250189251Ssam{ 251189251Ssam SM_ENTRY(SUPP_PAE, LOGOFF); 252189251Ssam eapol_sm_txLogoff(sm); 253189251Ssam sm->logoffSent = TRUE; 254189251Ssam sm->suppPortStatus = Unauthorized; 255214734Srpaulo eapol_sm_set_port_unauthorized(sm); 256189251Ssam} 257189251Ssam 258189251Ssam 259189251SsamSM_STATE(SUPP_PAE, DISCONNECTED) 260189251Ssam{ 261189251Ssam SM_ENTRY(SUPP_PAE, DISCONNECTED); 262189251Ssam sm->sPortMode = Auto; 263189251Ssam sm->startCount = 0; 264189251Ssam sm->logoffSent = FALSE; 265189251Ssam sm->suppPortStatus = Unauthorized; 266214734Srpaulo eapol_sm_set_port_unauthorized(sm); 267189251Ssam sm->suppAbort = TRUE; 268189251Ssam 269189251Ssam sm->unicast_key_received = FALSE; 270189251Ssam sm->broadcast_key_received = FALSE; 271189251Ssam} 272189251Ssam 273189251Ssam 274189251SsamSM_STATE(SUPP_PAE, CONNECTING) 275189251Ssam{ 276189251Ssam int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING; 277189251Ssam SM_ENTRY(SUPP_PAE, CONNECTING); 278189251Ssam if (send_start) { 279189251Ssam sm->startWhen = sm->startPeriod; 280189251Ssam sm->startCount++; 281189251Ssam } else { 282189251Ssam /* 283189251Ssam * Do not send EAPOL-Start immediately since in most cases, 284189251Ssam * Authenticator is going to start authentication immediately 285189251Ssam * after association and an extra EAPOL-Start is just going to 286189251Ssam * delay authentication. Use a short timeout to send the first 287189251Ssam * EAPOL-Start if Authenticator does not start authentication. 288189251Ssam */ 289209158Srpaulo#ifdef CONFIG_WPS 290209158Srpaulo /* Reduce latency on starting WPS negotiation. */ 291209158Srpaulo sm->startWhen = 1; 292209158Srpaulo#else /* CONFIG_WPS */ 293189251Ssam sm->startWhen = 3; 294209158Srpaulo#endif /* CONFIG_WPS */ 295189251Ssam } 296189251Ssam eapol_enable_timer_tick(sm); 297189251Ssam sm->eapolEap = FALSE; 298189251Ssam if (send_start) 299189251Ssam eapol_sm_txStart(sm); 300189251Ssam} 301189251Ssam 302189251Ssam 303189251SsamSM_STATE(SUPP_PAE, AUTHENTICATING) 304189251Ssam{ 305189251Ssam SM_ENTRY(SUPP_PAE, AUTHENTICATING); 306189251Ssam sm->startCount = 0; 307189251Ssam sm->suppSuccess = FALSE; 308189251Ssam sm->suppFail = FALSE; 309189251Ssam sm->suppTimeout = FALSE; 310189251Ssam sm->keyRun = FALSE; 311189251Ssam sm->keyDone = FALSE; 312189251Ssam sm->suppStart = TRUE; 313189251Ssam} 314189251Ssam 315189251Ssam 316189251SsamSM_STATE(SUPP_PAE, HELD) 317189251Ssam{ 318189251Ssam SM_ENTRY(SUPP_PAE, HELD); 319189251Ssam sm->heldWhile = sm->heldPeriod; 320189251Ssam eapol_enable_timer_tick(sm); 321189251Ssam sm->suppPortStatus = Unauthorized; 322214734Srpaulo eapol_sm_set_port_unauthorized(sm); 323189251Ssam sm->cb_status = EAPOL_CB_FAILURE; 324189251Ssam} 325189251Ssam 326189251Ssam 327189251SsamSM_STATE(SUPP_PAE, AUTHENTICATED) 328189251Ssam{ 329189251Ssam SM_ENTRY(SUPP_PAE, AUTHENTICATED); 330189251Ssam sm->suppPortStatus = Authorized; 331214734Srpaulo eapol_sm_set_port_authorized(sm); 332189251Ssam sm->cb_status = EAPOL_CB_SUCCESS; 333189251Ssam} 334189251Ssam 335189251Ssam 336189251SsamSM_STATE(SUPP_PAE, RESTART) 337189251Ssam{ 338189251Ssam SM_ENTRY(SUPP_PAE, RESTART); 339189251Ssam sm->eapRestart = TRUE; 340189251Ssam} 341189251Ssam 342189251Ssam 343189251SsamSM_STATE(SUPP_PAE, S_FORCE_AUTH) 344189251Ssam{ 345189251Ssam SM_ENTRY(SUPP_PAE, S_FORCE_AUTH); 346189251Ssam sm->suppPortStatus = Authorized; 347214734Srpaulo eapol_sm_set_port_authorized(sm); 348189251Ssam sm->sPortMode = ForceAuthorized; 349189251Ssam} 350189251Ssam 351189251Ssam 352189251SsamSM_STATE(SUPP_PAE, S_FORCE_UNAUTH) 353189251Ssam{ 354189251Ssam SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH); 355189251Ssam sm->suppPortStatus = Unauthorized; 356214734Srpaulo eapol_sm_set_port_unauthorized(sm); 357189251Ssam sm->sPortMode = ForceUnauthorized; 358189251Ssam eapol_sm_txLogoff(sm); 359189251Ssam} 360189251Ssam 361189251Ssam 362189251SsamSM_STEP(SUPP_PAE) 363189251Ssam{ 364189251Ssam if ((sm->userLogoff && !sm->logoffSent) && 365189251Ssam !(sm->initialize || !sm->portEnabled)) 366189251Ssam SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF); 367189251Ssam else if (((sm->portControl == Auto) && 368189251Ssam (sm->sPortMode != sm->portControl)) || 369189251Ssam sm->initialize || !sm->portEnabled) 370189251Ssam SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED); 371189251Ssam else if ((sm->portControl == ForceAuthorized) && 372189251Ssam (sm->sPortMode != sm->portControl) && 373189251Ssam !(sm->initialize || !sm->portEnabled)) 374189251Ssam SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH); 375189251Ssam else if ((sm->portControl == ForceUnauthorized) && 376189251Ssam (sm->sPortMode != sm->portControl) && 377189251Ssam !(sm->initialize || !sm->portEnabled)) 378189251Ssam SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH); 379189251Ssam else switch (sm->SUPP_PAE_state) { 380189251Ssam case SUPP_PAE_UNKNOWN: 381189251Ssam break; 382189251Ssam case SUPP_PAE_LOGOFF: 383189251Ssam if (!sm->userLogoff) 384189251Ssam SM_ENTER(SUPP_PAE, DISCONNECTED); 385189251Ssam break; 386189251Ssam case SUPP_PAE_DISCONNECTED: 387189251Ssam SM_ENTER(SUPP_PAE, CONNECTING); 388189251Ssam break; 389189251Ssam case SUPP_PAE_CONNECTING: 390189251Ssam if (sm->startWhen == 0 && sm->startCount < sm->maxStart) 391189251Ssam SM_ENTER(SUPP_PAE, CONNECTING); 392189251Ssam else if (sm->startWhen == 0 && 393189251Ssam sm->startCount >= sm->maxStart && 394189251Ssam sm->portValid) 395189251Ssam SM_ENTER(SUPP_PAE, AUTHENTICATED); 396189251Ssam else if (sm->eapSuccess || sm->eapFail) 397189251Ssam SM_ENTER(SUPP_PAE, AUTHENTICATING); 398189251Ssam else if (sm->eapolEap) 399189251Ssam SM_ENTER(SUPP_PAE, RESTART); 400189251Ssam else if (sm->startWhen == 0 && 401189251Ssam sm->startCount >= sm->maxStart && 402189251Ssam !sm->portValid) 403189251Ssam SM_ENTER(SUPP_PAE, HELD); 404189251Ssam break; 405189251Ssam case SUPP_PAE_AUTHENTICATING: 406189251Ssam if (sm->eapSuccess && !sm->portValid && 407189251Ssam sm->conf.accept_802_1x_keys && 408189251Ssam sm->conf.required_keys == 0) { 409189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for " 410189251Ssam "plaintext connection; no EAPOL-Key frames " 411189251Ssam "required"); 412189251Ssam sm->portValid = TRUE; 413189251Ssam if (sm->ctx->eapol_done_cb) 414189251Ssam sm->ctx->eapol_done_cb(sm->ctx->ctx); 415189251Ssam } 416189251Ssam if (sm->eapSuccess && sm->portValid) 417189251Ssam SM_ENTER(SUPP_PAE, AUTHENTICATED); 418189251Ssam else if (sm->eapFail || (sm->keyDone && !sm->portValid)) 419189251Ssam SM_ENTER(SUPP_PAE, HELD); 420189251Ssam else if (sm->suppTimeout) 421189251Ssam SM_ENTER(SUPP_PAE, CONNECTING); 422189251Ssam break; 423189251Ssam case SUPP_PAE_HELD: 424189251Ssam if (sm->heldWhile == 0) 425189251Ssam SM_ENTER(SUPP_PAE, CONNECTING); 426189251Ssam else if (sm->eapolEap) 427189251Ssam SM_ENTER(SUPP_PAE, RESTART); 428189251Ssam break; 429189251Ssam case SUPP_PAE_AUTHENTICATED: 430189251Ssam if (sm->eapolEap && sm->portValid) 431189251Ssam SM_ENTER(SUPP_PAE, RESTART); 432189251Ssam else if (!sm->portValid) 433189251Ssam SM_ENTER(SUPP_PAE, DISCONNECTED); 434189251Ssam break; 435189251Ssam case SUPP_PAE_RESTART: 436189251Ssam if (!sm->eapRestart) 437189251Ssam SM_ENTER(SUPP_PAE, AUTHENTICATING); 438189251Ssam break; 439189251Ssam case SUPP_PAE_S_FORCE_AUTH: 440189251Ssam break; 441189251Ssam case SUPP_PAE_S_FORCE_UNAUTH: 442189251Ssam break; 443189251Ssam } 444189251Ssam} 445189251Ssam 446189251Ssam 447189251SsamSM_STATE(KEY_RX, NO_KEY_RECEIVE) 448189251Ssam{ 449189251Ssam SM_ENTRY(KEY_RX, NO_KEY_RECEIVE); 450189251Ssam} 451189251Ssam 452189251Ssam 453189251SsamSM_STATE(KEY_RX, KEY_RECEIVE) 454189251Ssam{ 455189251Ssam SM_ENTRY(KEY_RX, KEY_RECEIVE); 456189251Ssam eapol_sm_processKey(sm); 457189251Ssam sm->rxKey = FALSE; 458189251Ssam} 459189251Ssam 460189251Ssam 461189251SsamSM_STEP(KEY_RX) 462189251Ssam{ 463189251Ssam if (sm->initialize || !sm->portEnabled) 464189251Ssam SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); 465189251Ssam switch (sm->KEY_RX_state) { 466189251Ssam case KEY_RX_UNKNOWN: 467189251Ssam break; 468189251Ssam case KEY_RX_NO_KEY_RECEIVE: 469189251Ssam if (sm->rxKey) 470189251Ssam SM_ENTER(KEY_RX, KEY_RECEIVE); 471189251Ssam break; 472189251Ssam case KEY_RX_KEY_RECEIVE: 473189251Ssam if (sm->rxKey) 474189251Ssam SM_ENTER(KEY_RX, KEY_RECEIVE); 475189251Ssam break; 476189251Ssam } 477189251Ssam} 478189251Ssam 479189251Ssam 480189251SsamSM_STATE(SUPP_BE, REQUEST) 481189251Ssam{ 482189251Ssam SM_ENTRY(SUPP_BE, REQUEST); 483189251Ssam sm->authWhile = 0; 484189251Ssam sm->eapReq = TRUE; 485189251Ssam eapol_sm_getSuppRsp(sm); 486189251Ssam} 487189251Ssam 488189251Ssam 489189251SsamSM_STATE(SUPP_BE, RESPONSE) 490189251Ssam{ 491189251Ssam SM_ENTRY(SUPP_BE, RESPONSE); 492189251Ssam eapol_sm_txSuppRsp(sm); 493189251Ssam sm->eapResp = FALSE; 494189251Ssam} 495189251Ssam 496189251Ssam 497189251SsamSM_STATE(SUPP_BE, SUCCESS) 498189251Ssam{ 499189251Ssam SM_ENTRY(SUPP_BE, SUCCESS); 500189251Ssam sm->keyRun = TRUE; 501189251Ssam sm->suppSuccess = TRUE; 502189251Ssam 503189251Ssam if (eap_key_available(sm->eap)) { 504189251Ssam /* New key received - clear IEEE 802.1X EAPOL-Key replay 505189251Ssam * counter */ 506189251Ssam sm->replay_counter_valid = FALSE; 507189251Ssam } 508189251Ssam} 509189251Ssam 510189251Ssam 511189251SsamSM_STATE(SUPP_BE, FAIL) 512189251Ssam{ 513189251Ssam SM_ENTRY(SUPP_BE, FAIL); 514189251Ssam sm->suppFail = TRUE; 515189251Ssam} 516189251Ssam 517189251Ssam 518189251SsamSM_STATE(SUPP_BE, TIMEOUT) 519189251Ssam{ 520189251Ssam SM_ENTRY(SUPP_BE, TIMEOUT); 521189251Ssam sm->suppTimeout = TRUE; 522189251Ssam} 523189251Ssam 524189251Ssam 525189251SsamSM_STATE(SUPP_BE, IDLE) 526189251Ssam{ 527189251Ssam SM_ENTRY(SUPP_BE, IDLE); 528189251Ssam sm->suppStart = FALSE; 529189251Ssam sm->initial_req = TRUE; 530189251Ssam} 531189251Ssam 532189251Ssam 533189251SsamSM_STATE(SUPP_BE, INITIALIZE) 534189251Ssam{ 535189251Ssam SM_ENTRY(SUPP_BE, INITIALIZE); 536189251Ssam eapol_sm_abortSupp(sm); 537189251Ssam sm->suppAbort = FALSE; 538189251Ssam} 539189251Ssam 540189251Ssam 541189251SsamSM_STATE(SUPP_BE, RECEIVE) 542189251Ssam{ 543189251Ssam SM_ENTRY(SUPP_BE, RECEIVE); 544189251Ssam sm->authWhile = sm->authPeriod; 545189251Ssam eapol_enable_timer_tick(sm); 546189251Ssam sm->eapolEap = FALSE; 547189251Ssam sm->eapNoResp = FALSE; 548189251Ssam sm->initial_req = FALSE; 549189251Ssam} 550189251Ssam 551189251Ssam 552189251SsamSM_STEP(SUPP_BE) 553189251Ssam{ 554189251Ssam if (sm->initialize || sm->suppAbort) 555189251Ssam SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE); 556189251Ssam else switch (sm->SUPP_BE_state) { 557189251Ssam case SUPP_BE_UNKNOWN: 558189251Ssam break; 559189251Ssam case SUPP_BE_REQUEST: 560189251Ssam /* 561189251Ssam * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL 562189251Ssam * and SUCCESS based on eapFail and eapSuccess, respectively. 563189251Ssam * However, IEEE Std 802.1X-2004 is also specifying that 564189251Ssam * eapNoResp should be set in conjuction with eapSuccess and 565189251Ssam * eapFail which would mean that more than one of the 566189251Ssam * transitions here would be activated at the same time. 567189251Ssam * Skipping RESPONSE and/or RECEIVE states in these cases can 568189251Ssam * cause problems and the direct transitions to do not seem 569189251Ssam * correct. Because of this, the conditions for these 570189251Ssam * transitions are verified only after eapNoResp. They are 571189251Ssam * unlikely to be used since eapNoResp should always be set if 572189251Ssam * either of eapSuccess or eapFail is set. 573189251Ssam */ 574189251Ssam if (sm->eapResp && sm->eapNoResp) { 575189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both " 576189251Ssam "eapResp and eapNoResp set?!"); 577189251Ssam } 578189251Ssam if (sm->eapResp) 579189251Ssam SM_ENTER(SUPP_BE, RESPONSE); 580189251Ssam else if (sm->eapNoResp) 581189251Ssam SM_ENTER(SUPP_BE, RECEIVE); 582189251Ssam else if (sm->eapFail) 583189251Ssam SM_ENTER(SUPP_BE, FAIL); 584189251Ssam else if (sm->eapSuccess) 585189251Ssam SM_ENTER(SUPP_BE, SUCCESS); 586189251Ssam break; 587189251Ssam case SUPP_BE_RESPONSE: 588189251Ssam SM_ENTER(SUPP_BE, RECEIVE); 589189251Ssam break; 590189251Ssam case SUPP_BE_SUCCESS: 591189251Ssam SM_ENTER(SUPP_BE, IDLE); 592189251Ssam break; 593189251Ssam case SUPP_BE_FAIL: 594189251Ssam SM_ENTER(SUPP_BE, IDLE); 595189251Ssam break; 596189251Ssam case SUPP_BE_TIMEOUT: 597189251Ssam SM_ENTER(SUPP_BE, IDLE); 598189251Ssam break; 599189251Ssam case SUPP_BE_IDLE: 600189251Ssam if (sm->eapFail && sm->suppStart) 601189251Ssam SM_ENTER(SUPP_BE, FAIL); 602189251Ssam else if (sm->eapolEap && sm->suppStart) 603189251Ssam SM_ENTER(SUPP_BE, REQUEST); 604189251Ssam else if (sm->eapSuccess && sm->suppStart) 605189251Ssam SM_ENTER(SUPP_BE, SUCCESS); 606189251Ssam break; 607189251Ssam case SUPP_BE_INITIALIZE: 608189251Ssam SM_ENTER(SUPP_BE, IDLE); 609189251Ssam break; 610189251Ssam case SUPP_BE_RECEIVE: 611189251Ssam if (sm->eapolEap) 612189251Ssam SM_ENTER(SUPP_BE, REQUEST); 613189251Ssam else if (sm->eapFail) 614189251Ssam SM_ENTER(SUPP_BE, FAIL); 615189251Ssam else if (sm->authWhile == 0) 616189251Ssam SM_ENTER(SUPP_BE, TIMEOUT); 617189251Ssam else if (sm->eapSuccess) 618189251Ssam SM_ENTER(SUPP_BE, SUCCESS); 619189251Ssam break; 620189251Ssam } 621189251Ssam} 622189251Ssam 623189251Ssam 624189251Ssamstatic void eapol_sm_txLogoff(struct eapol_sm *sm) 625189251Ssam{ 626189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: txLogoff"); 627189251Ssam sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, 628189251Ssam IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0); 629189251Ssam sm->dot1xSuppEapolLogoffFramesTx++; 630189251Ssam sm->dot1xSuppEapolFramesTx++; 631189251Ssam} 632189251Ssam 633189251Ssam 634189251Ssamstatic void eapol_sm_txStart(struct eapol_sm *sm) 635189251Ssam{ 636189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: txStart"); 637189251Ssam sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, 638189251Ssam IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0); 639189251Ssam sm->dot1xSuppEapolStartFramesTx++; 640189251Ssam sm->dot1xSuppEapolFramesTx++; 641189251Ssam} 642189251Ssam 643189251Ssam 644189251Ssam#define IEEE8021X_ENCR_KEY_LEN 32 645189251Ssam#define IEEE8021X_SIGN_KEY_LEN 32 646189251Ssam 647189251Ssamstruct eap_key_data { 648189251Ssam u8 encr_key[IEEE8021X_ENCR_KEY_LEN]; 649189251Ssam u8 sign_key[IEEE8021X_SIGN_KEY_LEN]; 650189251Ssam}; 651189251Ssam 652189251Ssam 653189251Ssamstatic void eapol_sm_processKey(struct eapol_sm *sm) 654189251Ssam{ 655189251Ssam struct ieee802_1x_hdr *hdr; 656189251Ssam struct ieee802_1x_eapol_key *key; 657189251Ssam struct eap_key_data keydata; 658189251Ssam u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32]; 659189251Ssam u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN]; 660189251Ssam int key_len, res, sign_key_len, encr_key_len; 661189251Ssam u16 rx_key_length; 662189251Ssam 663189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: processKey"); 664189251Ssam if (sm->last_rx_key == NULL) 665189251Ssam return; 666189251Ssam 667189251Ssam if (!sm->conf.accept_802_1x_keys) { 668189251Ssam wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key" 669189251Ssam " even though this was not accepted - " 670189251Ssam "ignoring this packet"); 671189251Ssam return; 672189251Ssam } 673189251Ssam 674189251Ssam hdr = (struct ieee802_1x_hdr *) sm->last_rx_key; 675189251Ssam key = (struct ieee802_1x_eapol_key *) (hdr + 1); 676189251Ssam if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) { 677189251Ssam wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame"); 678189251Ssam return; 679189251Ssam } 680189251Ssam rx_key_length = WPA_GET_BE16(key->key_length); 681189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d " 682189251Ssam "EAPOL-Key: type=%d key_length=%d key_index=0x%x", 683189251Ssam hdr->version, hdr->type, be_to_host16(hdr->length), 684189251Ssam key->type, rx_key_length, key->key_index); 685189251Ssam 686189251Ssam eapol_sm_notify_lower_layer_success(sm, 1); 687189251Ssam sign_key_len = IEEE8021X_SIGN_KEY_LEN; 688189251Ssam encr_key_len = IEEE8021X_ENCR_KEY_LEN; 689189251Ssam res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata)); 690189251Ssam if (res < 0) { 691189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for " 692189251Ssam "decrypting EAPOL-Key keys"); 693189251Ssam return; 694189251Ssam } 695189251Ssam if (res == 16) { 696189251Ssam /* LEAP derives only 16 bytes of keying material. */ 697189251Ssam res = eapol_sm_get_key(sm, (u8 *) &keydata, 16); 698189251Ssam if (res) { 699189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP " 700189251Ssam "master key for decrypting EAPOL-Key keys"); 701189251Ssam return; 702189251Ssam } 703189251Ssam sign_key_len = 16; 704189251Ssam encr_key_len = 16; 705189251Ssam os_memcpy(keydata.sign_key, keydata.encr_key, 16); 706189251Ssam } else if (res) { 707189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key " 708189251Ssam "data for decrypting EAPOL-Key keys (res=%d)", res); 709189251Ssam return; 710189251Ssam } 711189251Ssam 712189251Ssam /* The key replay_counter must increase when same master key */ 713189251Ssam if (sm->replay_counter_valid && 714189251Ssam os_memcmp(sm->last_replay_counter, key->replay_counter, 715189251Ssam IEEE8021X_REPLAY_COUNTER_LEN) >= 0) { 716189251Ssam wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did " 717189251Ssam "not increase - ignoring key"); 718189251Ssam wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter", 719189251Ssam sm->last_replay_counter, 720189251Ssam IEEE8021X_REPLAY_COUNTER_LEN); 721189251Ssam wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter", 722189251Ssam key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN); 723189251Ssam return; 724189251Ssam } 725189251Ssam 726189251Ssam /* Verify key signature (HMAC-MD5) */ 727189251Ssam os_memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN); 728189251Ssam os_memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN); 729189251Ssam hmac_md5(keydata.sign_key, sign_key_len, 730189251Ssam sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length), 731189251Ssam key->key_signature); 732189251Ssam if (os_memcmp(orig_key_sign, key->key_signature, 733189251Ssam IEEE8021X_KEY_SIGN_LEN) != 0) { 734189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in " 735189251Ssam "EAPOL-Key packet"); 736189251Ssam os_memcpy(key->key_signature, orig_key_sign, 737189251Ssam IEEE8021X_KEY_SIGN_LEN); 738189251Ssam return; 739189251Ssam } 740189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified"); 741189251Ssam 742189251Ssam key_len = be_to_host16(hdr->length) - sizeof(*key); 743189251Ssam if (key_len > 32 || rx_key_length > 32) { 744189251Ssam wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d", 745189251Ssam key_len ? key_len : rx_key_length); 746189251Ssam return; 747189251Ssam } 748189251Ssam if (key_len == rx_key_length) { 749189251Ssam os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN); 750189251Ssam os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key, 751189251Ssam encr_key_len); 752189251Ssam os_memcpy(datakey, key + 1, key_len); 753209158Srpaulo rc4_skip(ekey, IEEE8021X_KEY_IV_LEN + encr_key_len, 0, 754209158Srpaulo datakey, key_len); 755189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key", 756189251Ssam datakey, key_len); 757189251Ssam } else if (key_len == 0) { 758189251Ssam /* 759189251Ssam * IEEE 802.1X-2004 specifies that least significant Key Length 760189251Ssam * octets from MS-MPPE-Send-Key are used as the key if the key 761189251Ssam * data is not present. This seems to be meaning the beginning 762189251Ssam * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in 763189251Ssam * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator. 764189251Ssam * Anyway, taking the beginning of the keying material from EAP 765189251Ssam * seems to interoperate with Authenticators. 766189251Ssam */ 767189251Ssam key_len = rx_key_length; 768189251Ssam os_memcpy(datakey, keydata.encr_key, key_len); 769189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying " 770189251Ssam "material data encryption key", 771189251Ssam datakey, key_len); 772189251Ssam } else { 773189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d " 774189251Ssam "(key_length=%d)", key_len, rx_key_length); 775189251Ssam return; 776189251Ssam } 777189251Ssam 778189251Ssam sm->replay_counter_valid = TRUE; 779189251Ssam os_memcpy(sm->last_replay_counter, key->replay_counter, 780189251Ssam IEEE8021X_REPLAY_COUNTER_LEN); 781189251Ssam 782189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d " 783189251Ssam "len %d", 784189251Ssam key->key_index & IEEE8021X_KEY_INDEX_FLAG ? 785189251Ssam "unicast" : "broadcast", 786189251Ssam key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len); 787189251Ssam 788189251Ssam if (sm->ctx->set_wep_key && 789189251Ssam sm->ctx->set_wep_key(sm->ctx->ctx, 790189251Ssam key->key_index & IEEE8021X_KEY_INDEX_FLAG, 791189251Ssam key->key_index & IEEE8021X_KEY_INDEX_MASK, 792189251Ssam datakey, key_len) < 0) { 793189251Ssam wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the " 794189251Ssam " driver."); 795189251Ssam } else { 796189251Ssam if (key->key_index & IEEE8021X_KEY_INDEX_FLAG) 797189251Ssam sm->unicast_key_received = TRUE; 798189251Ssam else 799189251Ssam sm->broadcast_key_received = TRUE; 800189251Ssam 801189251Ssam if ((sm->unicast_key_received || 802189251Ssam !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) && 803189251Ssam (sm->broadcast_key_received || 804189251Ssam !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST))) 805189251Ssam { 806189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key " 807189251Ssam "frames received"); 808189251Ssam sm->portValid = TRUE; 809189251Ssam if (sm->ctx->eapol_done_cb) 810189251Ssam sm->ctx->eapol_done_cb(sm->ctx->ctx); 811189251Ssam } 812189251Ssam } 813189251Ssam} 814189251Ssam 815189251Ssam 816189251Ssamstatic void eapol_sm_getSuppRsp(struct eapol_sm *sm) 817189251Ssam{ 818189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp"); 819189251Ssam /* EAP layer processing; no special code is needed, since Supplicant 820189251Ssam * Backend state machine is waiting for eapNoResp or eapResp to be set 821189251Ssam * and these are only set in the EAP state machine when the processing 822189251Ssam * has finished. */ 823189251Ssam} 824189251Ssam 825189251Ssam 826189251Ssamstatic void eapol_sm_txSuppRsp(struct eapol_sm *sm) 827189251Ssam{ 828189251Ssam struct wpabuf *resp; 829189251Ssam 830189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp"); 831189251Ssam resp = eap_get_eapRespData(sm->eap); 832189251Ssam if (resp == NULL) { 833189251Ssam wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data " 834189251Ssam "not available"); 835189251Ssam return; 836189251Ssam } 837189251Ssam 838189251Ssam /* Send EAP-Packet from the EAP layer to the Authenticator */ 839189251Ssam sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, 840189251Ssam IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(resp), 841189251Ssam wpabuf_len(resp)); 842189251Ssam 843189251Ssam /* eapRespData is not used anymore, so free it here */ 844189251Ssam wpabuf_free(resp); 845189251Ssam 846189251Ssam if (sm->initial_req) 847189251Ssam sm->dot1xSuppEapolReqIdFramesRx++; 848189251Ssam else 849189251Ssam sm->dot1xSuppEapolReqFramesRx++; 850189251Ssam sm->dot1xSuppEapolRespFramesTx++; 851189251Ssam sm->dot1xSuppEapolFramesTx++; 852189251Ssam} 853189251Ssam 854189251Ssam 855189251Ssamstatic void eapol_sm_abortSupp(struct eapol_sm *sm) 856189251Ssam{ 857189251Ssam /* release system resources that may have been allocated for the 858189251Ssam * authentication session */ 859189251Ssam os_free(sm->last_rx_key); 860189251Ssam sm->last_rx_key = NULL; 861189251Ssam wpabuf_free(sm->eapReqData); 862189251Ssam sm->eapReqData = NULL; 863189251Ssam eap_sm_abort(sm->eap); 864189251Ssam} 865189251Ssam 866189251Ssam 867189251Ssamstatic void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx) 868189251Ssam{ 869189251Ssam eapol_sm_step(timeout_ctx); 870189251Ssam} 871189251Ssam 872189251Ssam 873214734Srpaulostatic void eapol_sm_set_port_authorized(struct eapol_sm *sm) 874214734Srpaulo{ 875214734Srpaulo if (sm->ctx->port_cb) 876214734Srpaulo sm->ctx->port_cb(sm->ctx->ctx, 1); 877214734Srpaulo} 878214734Srpaulo 879214734Srpaulo 880214734Srpaulostatic void eapol_sm_set_port_unauthorized(struct eapol_sm *sm) 881214734Srpaulo{ 882214734Srpaulo if (sm->ctx->port_cb) 883214734Srpaulo sm->ctx->port_cb(sm->ctx->ctx, 0); 884214734Srpaulo} 885214734Srpaulo 886214734Srpaulo 887189251Ssam/** 888189251Ssam * eapol_sm_step - EAPOL state machine step function 889189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 890189251Ssam * 891189251Ssam * This function is called to notify the state machine about changed external 892189251Ssam * variables. It will step through the EAPOL state machines in loop to process 893189251Ssam * all triggered state changes. 894189251Ssam */ 895189251Ssamvoid eapol_sm_step(struct eapol_sm *sm) 896189251Ssam{ 897189251Ssam int i; 898189251Ssam 899189251Ssam /* In theory, it should be ok to run this in loop until !changed. 900189251Ssam * However, it is better to use a limit on number of iterations to 901189251Ssam * allow events (e.g., SIGTERM) to stop the program cleanly if the 902189251Ssam * state machine were to generate a busy loop. */ 903189251Ssam for (i = 0; i < 100; i++) { 904189251Ssam sm->changed = FALSE; 905189251Ssam SM_STEP_RUN(SUPP_PAE); 906189251Ssam SM_STEP_RUN(KEY_RX); 907189251Ssam SM_STEP_RUN(SUPP_BE); 908189251Ssam if (eap_peer_sm_step(sm->eap)) 909189251Ssam sm->changed = TRUE; 910189251Ssam if (!sm->changed) 911189251Ssam break; 912189251Ssam } 913189251Ssam 914189251Ssam if (sm->changed) { 915189251Ssam /* restart EAPOL state machine step from timeout call in order 916189251Ssam * to allow other events to be processed. */ 917189251Ssam eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); 918189251Ssam eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm); 919189251Ssam } 920189251Ssam 921189251Ssam if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) { 922189251Ssam int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0; 923189251Ssam sm->cb_status = EAPOL_CB_IN_PROGRESS; 924189251Ssam sm->ctx->cb(sm, success, sm->ctx->cb_ctx); 925189251Ssam } 926189251Ssam} 927189251Ssam 928189251Ssam 929189251Ssam#ifdef CONFIG_CTRL_IFACE 930189251Ssamstatic const char *eapol_supp_pae_state(int state) 931189251Ssam{ 932189251Ssam switch (state) { 933189251Ssam case SUPP_PAE_LOGOFF: 934189251Ssam return "LOGOFF"; 935189251Ssam case SUPP_PAE_DISCONNECTED: 936189251Ssam return "DISCONNECTED"; 937189251Ssam case SUPP_PAE_CONNECTING: 938189251Ssam return "CONNECTING"; 939189251Ssam case SUPP_PAE_AUTHENTICATING: 940189251Ssam return "AUTHENTICATING"; 941189251Ssam case SUPP_PAE_HELD: 942189251Ssam return "HELD"; 943189251Ssam case SUPP_PAE_AUTHENTICATED: 944189251Ssam return "AUTHENTICATED"; 945189251Ssam case SUPP_PAE_RESTART: 946189251Ssam return "RESTART"; 947189251Ssam default: 948189251Ssam return "UNKNOWN"; 949189251Ssam } 950189251Ssam} 951189251Ssam 952189251Ssam 953189251Ssamstatic const char *eapol_supp_be_state(int state) 954189251Ssam{ 955189251Ssam switch (state) { 956189251Ssam case SUPP_BE_REQUEST: 957189251Ssam return "REQUEST"; 958189251Ssam case SUPP_BE_RESPONSE: 959189251Ssam return "RESPONSE"; 960189251Ssam case SUPP_BE_SUCCESS: 961189251Ssam return "SUCCESS"; 962189251Ssam case SUPP_BE_FAIL: 963189251Ssam return "FAIL"; 964189251Ssam case SUPP_BE_TIMEOUT: 965189251Ssam return "TIMEOUT"; 966189251Ssam case SUPP_BE_IDLE: 967189251Ssam return "IDLE"; 968189251Ssam case SUPP_BE_INITIALIZE: 969189251Ssam return "INITIALIZE"; 970189251Ssam case SUPP_BE_RECEIVE: 971189251Ssam return "RECEIVE"; 972189251Ssam default: 973189251Ssam return "UNKNOWN"; 974189251Ssam } 975189251Ssam} 976189251Ssam 977189251Ssam 978189251Ssamstatic const char * eapol_port_status(PortStatus status) 979189251Ssam{ 980189251Ssam if (status == Authorized) 981189251Ssam return "Authorized"; 982189251Ssam else 983189251Ssam return "Unauthorized"; 984189251Ssam} 985189251Ssam#endif /* CONFIG_CTRL_IFACE */ 986189251Ssam 987189251Ssam 988189251Ssam#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) 989189251Ssamstatic const char * eapol_port_control(PortControl ctrl) 990189251Ssam{ 991189251Ssam switch (ctrl) { 992189251Ssam case Auto: 993189251Ssam return "Auto"; 994189251Ssam case ForceUnauthorized: 995189251Ssam return "ForceUnauthorized"; 996189251Ssam case ForceAuthorized: 997189251Ssam return "ForceAuthorized"; 998189251Ssam default: 999189251Ssam return "Unknown"; 1000189251Ssam } 1001189251Ssam} 1002189251Ssam#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ 1003189251Ssam 1004189251Ssam 1005189251Ssam/** 1006189251Ssam * eapol_sm_configure - Set EAPOL variables 1007189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1008189251Ssam * @heldPeriod: dot1xSuppHeldPeriod 1009189251Ssam * @authPeriod: dot1xSuppAuthPeriod 1010189251Ssam * @startPeriod: dot1xSuppStartPeriod 1011189251Ssam * @maxStart: dot1xSuppMaxStart 1012189251Ssam * 1013189251Ssam * Set configurable EAPOL state machine variables. Each variable can be set to 1014189251Ssam * the given value or ignored if set to -1 (to set only some of the variables). 1015189251Ssam */ 1016189251Ssamvoid eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, 1017189251Ssam int startPeriod, int maxStart) 1018189251Ssam{ 1019189251Ssam if (sm == NULL) 1020189251Ssam return; 1021189251Ssam if (heldPeriod >= 0) 1022189251Ssam sm->heldPeriod = heldPeriod; 1023189251Ssam if (authPeriod >= 0) 1024189251Ssam sm->authPeriod = authPeriod; 1025189251Ssam if (startPeriod >= 0) 1026189251Ssam sm->startPeriod = startPeriod; 1027189251Ssam if (maxStart >= 0) 1028189251Ssam sm->maxStart = maxStart; 1029189251Ssam} 1030189251Ssam 1031189251Ssam 1032189251Ssam#ifdef CONFIG_CTRL_IFACE 1033189251Ssam/** 1034189251Ssam * eapol_sm_get_status - Get EAPOL state machine status 1035189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1036189251Ssam * @buf: Buffer for status information 1037189251Ssam * @buflen: Maximum buffer length 1038189251Ssam * @verbose: Whether to include verbose status information 1039189251Ssam * Returns: Number of bytes written to buf. 1040189251Ssam * 1041189251Ssam * Query EAPOL state machine for status information. This function fills in a 1042189251Ssam * text area with current status information from the EAPOL state machine. If 1043189251Ssam * the buffer (buf) is not large enough, status information will be truncated 1044189251Ssam * to fit the buffer. 1045189251Ssam */ 1046189251Ssamint eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, 1047189251Ssam int verbose) 1048189251Ssam{ 1049189251Ssam int len, ret; 1050189251Ssam if (sm == NULL) 1051189251Ssam return 0; 1052189251Ssam 1053189251Ssam len = os_snprintf(buf, buflen, 1054189251Ssam "Supplicant PAE state=%s\n" 1055189251Ssam "suppPortStatus=%s\n", 1056189251Ssam eapol_supp_pae_state(sm->SUPP_PAE_state), 1057189251Ssam eapol_port_status(sm->suppPortStatus)); 1058189251Ssam if (len < 0 || (size_t) len >= buflen) 1059189251Ssam return 0; 1060189251Ssam 1061189251Ssam if (verbose) { 1062189251Ssam ret = os_snprintf(buf + len, buflen - len, 1063189251Ssam "heldPeriod=%u\n" 1064189251Ssam "authPeriod=%u\n" 1065189251Ssam "startPeriod=%u\n" 1066189251Ssam "maxStart=%u\n" 1067189251Ssam "portControl=%s\n" 1068189251Ssam "Supplicant Backend state=%s\n", 1069189251Ssam sm->heldPeriod, 1070189251Ssam sm->authPeriod, 1071189251Ssam sm->startPeriod, 1072189251Ssam sm->maxStart, 1073189251Ssam eapol_port_control(sm->portControl), 1074189251Ssam eapol_supp_be_state(sm->SUPP_BE_state)); 1075189251Ssam if (ret < 0 || (size_t) ret >= buflen - len) 1076189251Ssam return len; 1077189251Ssam len += ret; 1078189251Ssam } 1079189251Ssam 1080189251Ssam len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose); 1081189251Ssam 1082189251Ssam return len; 1083189251Ssam} 1084189251Ssam 1085189251Ssam 1086189251Ssam/** 1087189251Ssam * eapol_sm_get_mib - Get EAPOL state machine MIBs 1088189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1089189251Ssam * @buf: Buffer for MIB information 1090189251Ssam * @buflen: Maximum buffer length 1091189251Ssam * Returns: Number of bytes written to buf. 1092189251Ssam * 1093189251Ssam * Query EAPOL state machine for MIB information. This function fills in a 1094189251Ssam * text area with current MIB information from the EAPOL state machine. If 1095189251Ssam * the buffer (buf) is not large enough, MIB information will be truncated to 1096189251Ssam * fit the buffer. 1097189251Ssam */ 1098189251Ssamint eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen) 1099189251Ssam{ 1100189251Ssam size_t len; 1101189251Ssam int ret; 1102189251Ssam 1103189251Ssam if (sm == NULL) 1104189251Ssam return 0; 1105189251Ssam ret = os_snprintf(buf, buflen, 1106189251Ssam "dot1xSuppPaeState=%d\n" 1107189251Ssam "dot1xSuppHeldPeriod=%u\n" 1108189251Ssam "dot1xSuppAuthPeriod=%u\n" 1109189251Ssam "dot1xSuppStartPeriod=%u\n" 1110189251Ssam "dot1xSuppMaxStart=%u\n" 1111189251Ssam "dot1xSuppSuppControlledPortStatus=%s\n" 1112189251Ssam "dot1xSuppBackendPaeState=%d\n", 1113189251Ssam sm->SUPP_PAE_state, 1114189251Ssam sm->heldPeriod, 1115189251Ssam sm->authPeriod, 1116189251Ssam sm->startPeriod, 1117189251Ssam sm->maxStart, 1118189251Ssam sm->suppPortStatus == Authorized ? 1119189251Ssam "Authorized" : "Unauthorized", 1120189251Ssam sm->SUPP_BE_state); 1121189251Ssam 1122189251Ssam if (ret < 0 || (size_t) ret >= buflen) 1123189251Ssam return 0; 1124189251Ssam len = ret; 1125189251Ssam 1126189251Ssam ret = os_snprintf(buf + len, buflen - len, 1127189251Ssam "dot1xSuppEapolFramesRx=%u\n" 1128189251Ssam "dot1xSuppEapolFramesTx=%u\n" 1129189251Ssam "dot1xSuppEapolStartFramesTx=%u\n" 1130189251Ssam "dot1xSuppEapolLogoffFramesTx=%u\n" 1131189251Ssam "dot1xSuppEapolRespFramesTx=%u\n" 1132189251Ssam "dot1xSuppEapolReqIdFramesRx=%u\n" 1133189251Ssam "dot1xSuppEapolReqFramesRx=%u\n" 1134189251Ssam "dot1xSuppInvalidEapolFramesRx=%u\n" 1135189251Ssam "dot1xSuppEapLengthErrorFramesRx=%u\n" 1136189251Ssam "dot1xSuppLastEapolFrameVersion=%u\n" 1137189251Ssam "dot1xSuppLastEapolFrameSource=" MACSTR "\n", 1138189251Ssam sm->dot1xSuppEapolFramesRx, 1139189251Ssam sm->dot1xSuppEapolFramesTx, 1140189251Ssam sm->dot1xSuppEapolStartFramesTx, 1141189251Ssam sm->dot1xSuppEapolLogoffFramesTx, 1142189251Ssam sm->dot1xSuppEapolRespFramesTx, 1143189251Ssam sm->dot1xSuppEapolReqIdFramesRx, 1144189251Ssam sm->dot1xSuppEapolReqFramesRx, 1145189251Ssam sm->dot1xSuppInvalidEapolFramesRx, 1146189251Ssam sm->dot1xSuppEapLengthErrorFramesRx, 1147189251Ssam sm->dot1xSuppLastEapolFrameVersion, 1148189251Ssam MAC2STR(sm->dot1xSuppLastEapolFrameSource)); 1149189251Ssam 1150189251Ssam if (ret < 0 || (size_t) ret >= buflen - len) 1151189251Ssam return len; 1152189251Ssam len += ret; 1153189251Ssam 1154189251Ssam return len; 1155189251Ssam} 1156189251Ssam#endif /* CONFIG_CTRL_IFACE */ 1157189251Ssam 1158189251Ssam 1159189251Ssam/** 1160189251Ssam * eapol_sm_rx_eapol - Process received EAPOL frames 1161189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1162189251Ssam * @src: Source MAC address of the EAPOL packet 1163189251Ssam * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) 1164189251Ssam * @len: Length of the EAPOL frame 1165189251Ssam * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine, 1166189251Ssam * -1 failure 1167189251Ssam */ 1168189251Ssamint eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, 1169189251Ssam size_t len) 1170189251Ssam{ 1171189251Ssam const struct ieee802_1x_hdr *hdr; 1172189251Ssam const struct ieee802_1x_eapol_key *key; 1173189251Ssam int data_len; 1174189251Ssam int res = 1; 1175189251Ssam size_t plen; 1176189251Ssam 1177189251Ssam if (sm == NULL) 1178189251Ssam return 0; 1179189251Ssam sm->dot1xSuppEapolFramesRx++; 1180189251Ssam if (len < sizeof(*hdr)) { 1181189251Ssam sm->dot1xSuppInvalidEapolFramesRx++; 1182189251Ssam return 0; 1183189251Ssam } 1184189251Ssam hdr = (const struct ieee802_1x_hdr *) buf; 1185189251Ssam sm->dot1xSuppLastEapolFrameVersion = hdr->version; 1186189251Ssam os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN); 1187189251Ssam if (hdr->version < EAPOL_VERSION) { 1188189251Ssam /* TODO: backwards compatibility */ 1189189251Ssam } 1190189251Ssam plen = be_to_host16(hdr->length); 1191189251Ssam if (plen > len - sizeof(*hdr)) { 1192189251Ssam sm->dot1xSuppEapLengthErrorFramesRx++; 1193189251Ssam return 0; 1194189251Ssam } 1195189251Ssam#ifdef CONFIG_WPS 1196189251Ssam if (sm->conf.workaround && 1197189251Ssam plen < len - sizeof(*hdr) && 1198189251Ssam hdr->type == IEEE802_1X_TYPE_EAP_PACKET && 1199189251Ssam len - sizeof(*hdr) > sizeof(struct eap_hdr)) { 1200189251Ssam const struct eap_hdr *ehdr = 1201189251Ssam (const struct eap_hdr *) (hdr + 1); 1202189251Ssam u16 elen; 1203189251Ssam 1204189251Ssam elen = be_to_host16(ehdr->length); 1205189251Ssam if (elen > plen && elen <= len - sizeof(*hdr)) { 1206189251Ssam /* 1207189251Ssam * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS 1208189251Ssam * packets with too short EAPOL header length field 1209189251Ssam * (14 octets). This is fixed in firmware Ver.1.49. 1210189251Ssam * As a workaround, fix the EAPOL header based on the 1211189251Ssam * correct length in the EAP packet. 1212189251Ssam */ 1213189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL " 1214189251Ssam "payload length based on EAP header: " 1215189251Ssam "%d -> %d", (int) plen, elen); 1216189251Ssam plen = elen; 1217189251Ssam } 1218189251Ssam } 1219189251Ssam#endif /* CONFIG_WPS */ 1220189251Ssam data_len = plen + sizeof(*hdr); 1221189251Ssam 1222189251Ssam switch (hdr->type) { 1223189251Ssam case IEEE802_1X_TYPE_EAP_PACKET: 1224189251Ssam if (sm->cached_pmk) { 1225189251Ssam /* Trying to use PMKSA caching, but Authenticator did 1226189251Ssam * not seem to have a matching entry. Need to restart 1227189251Ssam * EAPOL state machines. 1228189251Ssam */ 1229189251Ssam eapol_sm_abort_cached(sm); 1230189251Ssam } 1231189251Ssam wpabuf_free(sm->eapReqData); 1232189251Ssam sm->eapReqData = wpabuf_alloc_copy(hdr + 1, plen); 1233189251Ssam if (sm->eapReqData) { 1234189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet " 1235189251Ssam "frame"); 1236189251Ssam sm->eapolEap = TRUE; 1237189251Ssam eapol_sm_step(sm); 1238189251Ssam } 1239189251Ssam break; 1240189251Ssam case IEEE802_1X_TYPE_EAPOL_KEY: 1241189251Ssam if (plen < sizeof(*key)) { 1242189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key " 1243189251Ssam "frame received"); 1244189251Ssam break; 1245189251Ssam } 1246189251Ssam key = (const struct ieee802_1x_eapol_key *) (hdr + 1); 1247189251Ssam if (key->type == EAPOL_KEY_TYPE_WPA || 1248189251Ssam key->type == EAPOL_KEY_TYPE_RSN) { 1249189251Ssam /* WPA Supplicant takes care of this frame. */ 1250189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key " 1251189251Ssam "frame in EAPOL state machines"); 1252189251Ssam res = 0; 1253189251Ssam break; 1254189251Ssam } 1255189251Ssam if (key->type != EAPOL_KEY_TYPE_RC4) { 1256189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown " 1257189251Ssam "EAPOL-Key type %d", key->type); 1258189251Ssam break; 1259189251Ssam } 1260189251Ssam os_free(sm->last_rx_key); 1261189251Ssam sm->last_rx_key = os_malloc(data_len); 1262189251Ssam if (sm->last_rx_key) { 1263189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key " 1264189251Ssam "frame"); 1265189251Ssam os_memcpy(sm->last_rx_key, buf, data_len); 1266189251Ssam sm->last_rx_key_len = data_len; 1267189251Ssam sm->rxKey = TRUE; 1268189251Ssam eapol_sm_step(sm); 1269189251Ssam } 1270189251Ssam break; 1271189251Ssam default: 1272189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d", 1273189251Ssam hdr->type); 1274189251Ssam sm->dot1xSuppInvalidEapolFramesRx++; 1275189251Ssam break; 1276189251Ssam } 1277189251Ssam 1278189251Ssam return res; 1279189251Ssam} 1280189251Ssam 1281189251Ssam 1282189251Ssam/** 1283189251Ssam * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet 1284189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1285189251Ssam * 1286189251Ssam * Notify EAPOL state machine about transmitted EAPOL packet from an external 1287189251Ssam * component, e.g., WPA. This will update the statistics. 1288189251Ssam */ 1289189251Ssamvoid eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm) 1290189251Ssam{ 1291189251Ssam if (sm) 1292189251Ssam sm->dot1xSuppEapolFramesTx++; 1293189251Ssam} 1294189251Ssam 1295189251Ssam 1296189251Ssam/** 1297189251Ssam * eapol_sm_notify_portEnabled - Notification about portEnabled change 1298189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1299189251Ssam * @enabled: New portEnabled value 1300189251Ssam * 1301189251Ssam * Notify EAPOL state machine about new portEnabled value. 1302189251Ssam */ 1303189251Ssamvoid eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled) 1304189251Ssam{ 1305189251Ssam if (sm == NULL) 1306189251Ssam return; 1307189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: External notification - " 1308189251Ssam "portEnabled=%d", enabled); 1309189251Ssam sm->portEnabled = enabled; 1310189251Ssam eapol_sm_step(sm); 1311189251Ssam} 1312189251Ssam 1313189251Ssam 1314189251Ssam/** 1315189251Ssam * eapol_sm_notify_portValid - Notification about portValid change 1316189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1317189251Ssam * @valid: New portValid value 1318189251Ssam * 1319189251Ssam * Notify EAPOL state machine about new portValid value. 1320189251Ssam */ 1321189251Ssamvoid eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid) 1322189251Ssam{ 1323189251Ssam if (sm == NULL) 1324189251Ssam return; 1325189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: External notification - " 1326189251Ssam "portValid=%d", valid); 1327189251Ssam sm->portValid = valid; 1328189251Ssam eapol_sm_step(sm); 1329189251Ssam} 1330189251Ssam 1331189251Ssam 1332189251Ssam/** 1333189251Ssam * eapol_sm_notify_eap_success - Notification of external EAP success trigger 1334189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1335189251Ssam * @success: %TRUE = set success, %FALSE = clear success 1336189251Ssam * 1337189251Ssam * Notify the EAPOL state machine that external event has forced EAP state to 1338189251Ssam * success (success = %TRUE). This can be cleared by setting success = %FALSE. 1339189251Ssam * 1340189251Ssam * This function is called to update EAP state when WPA-PSK key handshake has 1341189251Ssam * been completed successfully since WPA-PSK does not use EAP state machine. 1342189251Ssam */ 1343189251Ssamvoid eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success) 1344189251Ssam{ 1345189251Ssam if (sm == NULL) 1346189251Ssam return; 1347189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: External notification - " 1348189251Ssam "EAP success=%d", success); 1349189251Ssam sm->eapSuccess = success; 1350189251Ssam sm->altAccept = success; 1351189251Ssam if (success) 1352189251Ssam eap_notify_success(sm->eap); 1353189251Ssam eapol_sm_step(sm); 1354189251Ssam} 1355189251Ssam 1356189251Ssam 1357189251Ssam/** 1358189251Ssam * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger 1359189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1360189251Ssam * @fail: %TRUE = set failure, %FALSE = clear failure 1361189251Ssam * 1362189251Ssam * Notify EAPOL state machine that external event has forced EAP state to 1363189251Ssam * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE. 1364189251Ssam */ 1365189251Ssamvoid eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail) 1366189251Ssam{ 1367189251Ssam if (sm == NULL) 1368189251Ssam return; 1369189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: External notification - " 1370189251Ssam "EAP fail=%d", fail); 1371189251Ssam sm->eapFail = fail; 1372189251Ssam sm->altReject = fail; 1373189251Ssam eapol_sm_step(sm); 1374189251Ssam} 1375189251Ssam 1376189251Ssam 1377189251Ssam/** 1378189251Ssam * eapol_sm_notify_config - Notification of EAPOL configuration change 1379189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1380189251Ssam * @config: Pointer to current network EAP configuration 1381189251Ssam * @conf: Pointer to EAPOL configuration data 1382189251Ssam * 1383189251Ssam * Notify EAPOL state machine that configuration has changed. config will be 1384189251Ssam * stored as a backpointer to network configuration. This can be %NULL to clear 1385189251Ssam * the stored pointed. conf will be copied to local EAPOL/EAP configuration 1386189251Ssam * data. If conf is %NULL, this part of the configuration change will be 1387189251Ssam * skipped. 1388189251Ssam */ 1389189251Ssamvoid eapol_sm_notify_config(struct eapol_sm *sm, 1390189251Ssam struct eap_peer_config *config, 1391189251Ssam const struct eapol_config *conf) 1392189251Ssam{ 1393189251Ssam if (sm == NULL) 1394189251Ssam return; 1395189251Ssam 1396189251Ssam sm->config = config; 1397189251Ssam 1398189251Ssam if (conf == NULL) 1399189251Ssam return; 1400189251Ssam 1401189251Ssam sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys; 1402189251Ssam sm->conf.required_keys = conf->required_keys; 1403189251Ssam sm->conf.fast_reauth = conf->fast_reauth; 1404189251Ssam sm->conf.workaround = conf->workaround; 1405189251Ssam if (sm->eap) { 1406189251Ssam eap_set_fast_reauth(sm->eap, conf->fast_reauth); 1407189251Ssam eap_set_workaround(sm->eap, conf->workaround); 1408189251Ssam eap_set_force_disabled(sm->eap, conf->eap_disabled); 1409189251Ssam } 1410189251Ssam} 1411189251Ssam 1412189251Ssam 1413189251Ssam/** 1414189251Ssam * eapol_sm_get_key - Get master session key (MSK) from EAP 1415189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1416189251Ssam * @key: Pointer for key buffer 1417189251Ssam * @len: Number of bytes to copy to key 1418189251Ssam * Returns: 0 on success (len of key available), maximum available key len 1419189251Ssam * (>0) if key is available but it is shorter than len, or -1 on failure. 1420189251Ssam * 1421189251Ssam * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key 1422189251Ssam * is available only after a successful authentication. 1423189251Ssam */ 1424189251Ssamint eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) 1425189251Ssam{ 1426189251Ssam const u8 *eap_key; 1427189251Ssam size_t eap_len; 1428189251Ssam 1429189251Ssam if (sm == NULL || !eap_key_available(sm->eap)) { 1430189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available"); 1431189251Ssam return -1; 1432189251Ssam } 1433189251Ssam eap_key = eap_get_eapKeyData(sm->eap, &eap_len); 1434189251Ssam if (eap_key == NULL) { 1435189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData"); 1436189251Ssam return -1; 1437189251Ssam } 1438189251Ssam if (len > eap_len) { 1439189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not " 1440189251Ssam "available (len=%lu)", 1441189251Ssam (unsigned long) len, (unsigned long) eap_len); 1442189251Ssam return eap_len; 1443189251Ssam } 1444189251Ssam os_memcpy(key, eap_key, len); 1445189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: Successfully fetched key (len=%lu)", 1446189251Ssam (unsigned long) len); 1447189251Ssam return 0; 1448189251Ssam} 1449189251Ssam 1450189251Ssam 1451189251Ssam/** 1452189251Ssam * eapol_sm_notify_logoff - Notification of logon/logoff commands 1453189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1454189251Ssam * @logoff: Whether command was logoff 1455189251Ssam * 1456189251Ssam * Notify EAPOL state machines that user requested logon/logoff. 1457189251Ssam */ 1458189251Ssamvoid eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) 1459189251Ssam{ 1460189251Ssam if (sm) { 1461189251Ssam sm->userLogoff = logoff; 1462189251Ssam eapol_sm_step(sm); 1463189251Ssam } 1464189251Ssam} 1465189251Ssam 1466189251Ssam 1467189251Ssam/** 1468189251Ssam * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching 1469189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1470189251Ssam * 1471189251Ssam * Notify EAPOL state machines that PMKSA caching was successful. This is used 1472189251Ssam * to move EAPOL and EAP state machines into authenticated/successful state. 1473189251Ssam */ 1474189251Ssamvoid eapol_sm_notify_cached(struct eapol_sm *sm) 1475189251Ssam{ 1476189251Ssam if (sm == NULL) 1477189251Ssam return; 1478189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL"); 1479189251Ssam sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED; 1480189251Ssam sm->suppPortStatus = Authorized; 1481214734Srpaulo eapol_sm_set_port_authorized(sm); 1482189251Ssam sm->portValid = TRUE; 1483189251Ssam eap_notify_success(sm->eap); 1484189251Ssam eapol_sm_step(sm); 1485189251Ssam} 1486189251Ssam 1487189251Ssam 1488189251Ssam/** 1489189251Ssam * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching 1490189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1491189251Ssam * @attempt: Whether PMKSA caching is tried 1492189251Ssam * 1493189251Ssam * Notify EAPOL state machines whether PMKSA caching is used. 1494189251Ssam */ 1495189251Ssamvoid eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt) 1496189251Ssam{ 1497189251Ssam if (sm == NULL) 1498189251Ssam return; 1499189251Ssam if (attempt) { 1500189251Ssam wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA"); 1501189251Ssam sm->cached_pmk = TRUE; 1502189251Ssam } else { 1503189251Ssam wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA"); 1504189251Ssam sm->cached_pmk = FALSE; 1505189251Ssam } 1506189251Ssam} 1507189251Ssam 1508189251Ssam 1509189251Ssamstatic void eapol_sm_abort_cached(struct eapol_sm *sm) 1510189251Ssam{ 1511189251Ssam wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, " 1512189251Ssam "doing full EAP authentication"); 1513189251Ssam if (sm == NULL) 1514189251Ssam return; 1515189251Ssam sm->cached_pmk = FALSE; 1516189251Ssam sm->SUPP_PAE_state = SUPP_PAE_CONNECTING; 1517189251Ssam sm->suppPortStatus = Unauthorized; 1518214734Srpaulo eapol_sm_set_port_unauthorized(sm); 1519189251Ssam 1520189251Ssam /* Make sure we do not start sending EAPOL-Start frames first, but 1521189251Ssam * instead move to RESTART state to start EAPOL authentication. */ 1522189251Ssam sm->startWhen = 3; 1523189251Ssam eapol_enable_timer_tick(sm); 1524189251Ssam 1525189251Ssam if (sm->ctx->aborted_cached) 1526189251Ssam sm->ctx->aborted_cached(sm->ctx->ctx); 1527189251Ssam} 1528189251Ssam 1529189251Ssam 1530189251Ssam/** 1531189251Ssam * eapol_sm_register_scard_ctx - Notification of smart card context 1532189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1533189251Ssam * @ctx: Context data for smart card operations 1534189251Ssam * 1535189251Ssam * Notify EAPOL state machines of context data for smart card operations. This 1536189251Ssam * context data will be used as a parameter for scard_*() functions. 1537189251Ssam */ 1538189251Ssamvoid eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx) 1539189251Ssam{ 1540189251Ssam if (sm) { 1541189251Ssam sm->ctx->scard_ctx = ctx; 1542189251Ssam eap_register_scard_ctx(sm->eap, ctx); 1543189251Ssam } 1544189251Ssam} 1545189251Ssam 1546189251Ssam 1547189251Ssam/** 1548189251Ssam * eapol_sm_notify_portControl - Notification of portControl changes 1549189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1550189251Ssam * @portControl: New value for portControl variable 1551189251Ssam * 1552189251Ssam * Notify EAPOL state machines that portControl variable has changed. 1553189251Ssam */ 1554189251Ssamvoid eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl) 1555189251Ssam{ 1556189251Ssam if (sm == NULL) 1557189251Ssam return; 1558189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: External notification - " 1559189251Ssam "portControl=%s", eapol_port_control(portControl)); 1560189251Ssam sm->portControl = portControl; 1561189251Ssam eapol_sm_step(sm); 1562189251Ssam} 1563189251Ssam 1564189251Ssam 1565189251Ssam/** 1566189251Ssam * eapol_sm_notify_ctrl_attached - Notification of attached monitor 1567189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1568189251Ssam * 1569189251Ssam * Notify EAPOL state machines that a monitor was attached to the control 1570189251Ssam * interface to trigger re-sending of pending requests for user input. 1571189251Ssam */ 1572189251Ssamvoid eapol_sm_notify_ctrl_attached(struct eapol_sm *sm) 1573189251Ssam{ 1574189251Ssam if (sm == NULL) 1575189251Ssam return; 1576189251Ssam eap_sm_notify_ctrl_attached(sm->eap); 1577189251Ssam} 1578189251Ssam 1579189251Ssam 1580189251Ssam/** 1581189251Ssam * eapol_sm_notify_ctrl_response - Notification of received user input 1582189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1583189251Ssam * 1584189251Ssam * Notify EAPOL state machines that a control response, i.e., user 1585189251Ssam * input, was received in order to trigger retrying of a pending EAP request. 1586189251Ssam */ 1587189251Ssamvoid eapol_sm_notify_ctrl_response(struct eapol_sm *sm) 1588189251Ssam{ 1589189251Ssam if (sm == NULL) 1590189251Ssam return; 1591189251Ssam if (sm->eapReqData && !sm->eapReq) { 1592189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: received control response (user " 1593189251Ssam "input) notification - retrying pending EAP " 1594189251Ssam "Request"); 1595189251Ssam sm->eapolEap = TRUE; 1596189251Ssam sm->eapReq = TRUE; 1597189251Ssam eapol_sm_step(sm); 1598189251Ssam } 1599189251Ssam} 1600189251Ssam 1601189251Ssam 1602189251Ssam/** 1603189251Ssam * eapol_sm_request_reauth - Request reauthentication 1604189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1605189251Ssam * 1606189251Ssam * This function can be used to request EAPOL reauthentication, e.g., when the 1607189251Ssam * current PMKSA entry is nearing expiration. 1608189251Ssam */ 1609189251Ssamvoid eapol_sm_request_reauth(struct eapol_sm *sm) 1610189251Ssam{ 1611189251Ssam if (sm == NULL || sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED) 1612189251Ssam return; 1613189251Ssam eapol_sm_txStart(sm); 1614189251Ssam} 1615189251Ssam 1616189251Ssam 1617189251Ssam/** 1618189251Ssam * eapol_sm_notify_lower_layer_success - Notification of lower layer success 1619189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1620189251Ssam * @in_eapol_sm: Whether the caller is already running inside EAPOL state 1621189251Ssam * machine loop (eapol_sm_step()) 1622189251Ssam * 1623189251Ssam * Notify EAPOL (and EAP) state machines that a lower layer has detected a 1624189251Ssam * successful authentication. This is used to recover from dropped EAP-Success 1625189251Ssam * messages. 1626189251Ssam */ 1627189251Ssamvoid eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm) 1628189251Ssam{ 1629189251Ssam if (sm == NULL) 1630189251Ssam return; 1631189251Ssam eap_notify_lower_layer_success(sm->eap); 1632189251Ssam if (!in_eapol_sm) 1633189251Ssam eapol_sm_step(sm); 1634189251Ssam} 1635189251Ssam 1636189251Ssam 1637189251Ssam/** 1638189251Ssam * eapol_sm_invalidate_cached_session - Mark cached EAP session data invalid 1639189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1640189251Ssam */ 1641189251Ssamvoid eapol_sm_invalidate_cached_session(struct eapol_sm *sm) 1642189251Ssam{ 1643189251Ssam if (sm) 1644189251Ssam eap_invalidate_cached_session(sm->eap); 1645189251Ssam} 1646189251Ssam 1647189251Ssam 1648189251Ssamstatic struct eap_peer_config * eapol_sm_get_config(void *ctx) 1649189251Ssam{ 1650189251Ssam struct eapol_sm *sm = ctx; 1651189251Ssam return sm ? sm->config : NULL; 1652189251Ssam} 1653189251Ssam 1654189251Ssam 1655189251Ssamstatic struct wpabuf * eapol_sm_get_eapReqData(void *ctx) 1656189251Ssam{ 1657189251Ssam struct eapol_sm *sm = ctx; 1658189251Ssam if (sm == NULL || sm->eapReqData == NULL) 1659189251Ssam return NULL; 1660189251Ssam 1661189251Ssam return sm->eapReqData; 1662189251Ssam} 1663189251Ssam 1664189251Ssam 1665189251Ssamstatic Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable) 1666189251Ssam{ 1667189251Ssam struct eapol_sm *sm = ctx; 1668189251Ssam if (sm == NULL) 1669189251Ssam return FALSE; 1670189251Ssam switch (variable) { 1671189251Ssam case EAPOL_eapSuccess: 1672189251Ssam return sm->eapSuccess; 1673189251Ssam case EAPOL_eapRestart: 1674189251Ssam return sm->eapRestart; 1675189251Ssam case EAPOL_eapFail: 1676189251Ssam return sm->eapFail; 1677189251Ssam case EAPOL_eapResp: 1678189251Ssam return sm->eapResp; 1679189251Ssam case EAPOL_eapNoResp: 1680189251Ssam return sm->eapNoResp; 1681189251Ssam case EAPOL_eapReq: 1682189251Ssam return sm->eapReq; 1683189251Ssam case EAPOL_portEnabled: 1684189251Ssam return sm->portEnabled; 1685189251Ssam case EAPOL_altAccept: 1686189251Ssam return sm->altAccept; 1687189251Ssam case EAPOL_altReject: 1688189251Ssam return sm->altReject; 1689189251Ssam } 1690189251Ssam return FALSE; 1691189251Ssam} 1692189251Ssam 1693189251Ssam 1694189251Ssamstatic void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable, 1695189251Ssam Boolean value) 1696189251Ssam{ 1697189251Ssam struct eapol_sm *sm = ctx; 1698189251Ssam if (sm == NULL) 1699189251Ssam return; 1700189251Ssam switch (variable) { 1701189251Ssam case EAPOL_eapSuccess: 1702189251Ssam sm->eapSuccess = value; 1703189251Ssam break; 1704189251Ssam case EAPOL_eapRestart: 1705189251Ssam sm->eapRestart = value; 1706189251Ssam break; 1707189251Ssam case EAPOL_eapFail: 1708189251Ssam sm->eapFail = value; 1709189251Ssam break; 1710189251Ssam case EAPOL_eapResp: 1711189251Ssam sm->eapResp = value; 1712189251Ssam break; 1713189251Ssam case EAPOL_eapNoResp: 1714189251Ssam sm->eapNoResp = value; 1715189251Ssam break; 1716189251Ssam case EAPOL_eapReq: 1717189251Ssam sm->eapReq = value; 1718189251Ssam break; 1719189251Ssam case EAPOL_portEnabled: 1720189251Ssam sm->portEnabled = value; 1721189251Ssam break; 1722189251Ssam case EAPOL_altAccept: 1723189251Ssam sm->altAccept = value; 1724189251Ssam break; 1725189251Ssam case EAPOL_altReject: 1726189251Ssam sm->altReject = value; 1727189251Ssam break; 1728189251Ssam } 1729189251Ssam} 1730189251Ssam 1731189251Ssam 1732189251Ssamstatic unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable) 1733189251Ssam{ 1734189251Ssam struct eapol_sm *sm = ctx; 1735189251Ssam if (sm == NULL) 1736189251Ssam return 0; 1737189251Ssam switch (variable) { 1738189251Ssam case EAPOL_idleWhile: 1739189251Ssam return sm->idleWhile; 1740189251Ssam } 1741189251Ssam return 0; 1742189251Ssam} 1743189251Ssam 1744189251Ssam 1745189251Ssamstatic void eapol_sm_set_int(void *ctx, enum eapol_int_var variable, 1746189251Ssam unsigned int value) 1747189251Ssam{ 1748189251Ssam struct eapol_sm *sm = ctx; 1749189251Ssam if (sm == NULL) 1750189251Ssam return; 1751189251Ssam switch (variable) { 1752189251Ssam case EAPOL_idleWhile: 1753189251Ssam sm->idleWhile = value; 1754189251Ssam eapol_enable_timer_tick(sm); 1755189251Ssam break; 1756189251Ssam } 1757189251Ssam} 1758189251Ssam 1759189251Ssam 1760189251Ssamstatic void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob) 1761189251Ssam{ 1762189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS 1763189251Ssam struct eapol_sm *sm = ctx; 1764189251Ssam if (sm && sm->ctx && sm->ctx->set_config_blob) 1765189251Ssam sm->ctx->set_config_blob(sm->ctx->ctx, blob); 1766189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */ 1767189251Ssam} 1768189251Ssam 1769189251Ssam 1770189251Ssamstatic const struct wpa_config_blob * 1771189251Ssameapol_sm_get_config_blob(void *ctx, const char *name) 1772189251Ssam{ 1773189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS 1774189251Ssam struct eapol_sm *sm = ctx; 1775189251Ssam if (sm && sm->ctx && sm->ctx->get_config_blob) 1776189251Ssam return sm->ctx->get_config_blob(sm->ctx->ctx, name); 1777189251Ssam else 1778189251Ssam return NULL; 1779189251Ssam#else /* CONFIG_NO_CONFIG_BLOBS */ 1780189251Ssam return NULL; 1781189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */ 1782189251Ssam} 1783189251Ssam 1784189251Ssam 1785189251Ssamstatic void eapol_sm_notify_pending(void *ctx) 1786189251Ssam{ 1787189251Ssam struct eapol_sm *sm = ctx; 1788189251Ssam if (sm == NULL) 1789189251Ssam return; 1790189251Ssam if (sm->eapReqData && !sm->eapReq) { 1791189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP " 1792189251Ssam "state machine - retrying pending EAP Request"); 1793189251Ssam sm->eapolEap = TRUE; 1794189251Ssam sm->eapReq = TRUE; 1795189251Ssam eapol_sm_step(sm); 1796189251Ssam } 1797189251Ssam} 1798189251Ssam 1799189251Ssam 1800189251Ssam#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) 1801189251Ssamstatic void eapol_sm_eap_param_needed(void *ctx, const char *field, 1802189251Ssam const char *txt) 1803189251Ssam{ 1804189251Ssam struct eapol_sm *sm = ctx; 1805189251Ssam wpa_printf(MSG_DEBUG, "EAPOL: EAP parameter needed"); 1806189251Ssam if (sm->ctx->eap_param_needed) 1807189251Ssam sm->ctx->eap_param_needed(sm->ctx->ctx, field, txt); 1808189251Ssam} 1809189251Ssam#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ 1810189251Ssam#define eapol_sm_eap_param_needed NULL 1811189251Ssam#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ 1812189251Ssam 1813189251Ssam 1814189251Ssamstatic struct eapol_callbacks eapol_cb = 1815189251Ssam{ 1816189251Ssam eapol_sm_get_config, 1817189251Ssam eapol_sm_get_bool, 1818189251Ssam eapol_sm_set_bool, 1819189251Ssam eapol_sm_get_int, 1820189251Ssam eapol_sm_set_int, 1821189251Ssam eapol_sm_get_eapReqData, 1822189251Ssam eapol_sm_set_config_blob, 1823189251Ssam eapol_sm_get_config_blob, 1824189251Ssam eapol_sm_notify_pending, 1825189251Ssam eapol_sm_eap_param_needed 1826189251Ssam}; 1827189251Ssam 1828189251Ssam 1829189251Ssam/** 1830189251Ssam * eapol_sm_init - Initialize EAPOL state machine 1831189251Ssam * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer 1832189251Ssam * and EAPOL state machine will free it in eapol_sm_deinit() 1833189251Ssam * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure 1834189251Ssam * 1835189251Ssam * Allocate and initialize an EAPOL state machine. 1836189251Ssam */ 1837189251Ssamstruct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) 1838189251Ssam{ 1839189251Ssam struct eapol_sm *sm; 1840189251Ssam struct eap_config conf; 1841189251Ssam sm = os_zalloc(sizeof(*sm)); 1842189251Ssam if (sm == NULL) 1843189251Ssam return NULL; 1844189251Ssam sm->ctx = ctx; 1845189251Ssam 1846189251Ssam sm->portControl = Auto; 1847189251Ssam 1848189251Ssam /* Supplicant PAE state machine */ 1849189251Ssam sm->heldPeriod = 60; 1850189251Ssam sm->startPeriod = 30; 1851189251Ssam sm->maxStart = 3; 1852189251Ssam 1853189251Ssam /* Supplicant Backend state machine */ 1854189251Ssam sm->authPeriod = 30; 1855189251Ssam 1856189251Ssam os_memset(&conf, 0, sizeof(conf)); 1857189251Ssam conf.opensc_engine_path = ctx->opensc_engine_path; 1858189251Ssam conf.pkcs11_engine_path = ctx->pkcs11_engine_path; 1859189251Ssam conf.pkcs11_module_path = ctx->pkcs11_module_path; 1860189251Ssam conf.wps = ctx->wps; 1861189251Ssam 1862189251Ssam sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf); 1863189251Ssam if (sm->eap == NULL) { 1864189251Ssam os_free(sm); 1865189251Ssam return NULL; 1866189251Ssam } 1867189251Ssam 1868189251Ssam /* Initialize EAPOL state machines */ 1869189251Ssam sm->initialize = TRUE; 1870189251Ssam eapol_sm_step(sm); 1871189251Ssam sm->initialize = FALSE; 1872189251Ssam eapol_sm_step(sm); 1873189251Ssam 1874189251Ssam sm->timer_tick_enabled = 1; 1875189251Ssam eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); 1876189251Ssam 1877189251Ssam return sm; 1878189251Ssam} 1879189251Ssam 1880189251Ssam 1881189251Ssam/** 1882189251Ssam * eapol_sm_deinit - Deinitialize EAPOL state machine 1883189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() 1884189251Ssam * 1885189251Ssam * Deinitialize and free EAPOL state machine. 1886189251Ssam */ 1887189251Ssamvoid eapol_sm_deinit(struct eapol_sm *sm) 1888189251Ssam{ 1889189251Ssam if (sm == NULL) 1890189251Ssam return; 1891189251Ssam eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); 1892189251Ssam eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); 1893189251Ssam eap_peer_sm_deinit(sm->eap); 1894189251Ssam os_free(sm->last_rx_key); 1895189251Ssam wpabuf_free(sm->eapReqData); 1896189251Ssam os_free(sm->ctx); 1897189251Ssam os_free(sm); 1898189251Ssam} 1899