1214501Srpaulo/* 2214501Srpaulo * IEEE 802.1X-2004 Authenticator - EAPOL state machine 3214501Srpaulo * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> 4214501Srpaulo * 5214501Srpaulo * This program is free software; you can redistribute it and/or modify 6214501Srpaulo * it under the terms of the GNU General Public License version 2 as 7214501Srpaulo * published by the Free Software Foundation. 8214501Srpaulo * 9214501Srpaulo * Alternatively, this software may be distributed under the terms of BSD 10214501Srpaulo * license. 11214501Srpaulo * 12214501Srpaulo * See README and COPYING for more details. 13214501Srpaulo */ 14214501Srpaulo 15214501Srpaulo#include "includes.h" 16214501Srpaulo 17214501Srpaulo#include "common.h" 18214501Srpaulo#include "eloop.h" 19214501Srpaulo#include "state_machine.h" 20214501Srpaulo#include "common/eapol_common.h" 21214501Srpaulo#include "eap_common/eap_defs.h" 22214501Srpaulo#include "eap_common/eap_common.h" 23214501Srpaulo#include "eap_server/eap.h" 24214501Srpaulo#include "eapol_auth_sm.h" 25214501Srpaulo#include "eapol_auth_sm_i.h" 26214501Srpaulo 27214501Srpaulo#define STATE_MACHINE_DATA struct eapol_state_machine 28214501Srpaulo#define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X" 29214501Srpaulo#define STATE_MACHINE_ADDR sm->addr 30214501Srpaulo 31214501Srpaulostatic struct eapol_callbacks eapol_cb; 32214501Srpaulo 33214501Srpaulo/* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */ 34214501Srpaulo 35214501Srpaulo#define setPortAuthorized() \ 36214501Srpaulosm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 1) 37214501Srpaulo#define setPortUnauthorized() \ 38214501Srpaulosm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 0) 39214501Srpaulo 40214501Srpaulo/* procedures */ 41214501Srpaulo#define txCannedFail() eapol_auth_tx_canned_eap(sm, 0) 42214501Srpaulo#define txCannedSuccess() eapol_auth_tx_canned_eap(sm, 1) 43214501Srpaulo#define txReq() eapol_auth_tx_req(sm) 44214501Srpaulo#define abortAuth() sm->eapol->cb.abort_auth(sm->eapol->conf.ctx, sm->sta) 45214501Srpaulo#define txKey() sm->eapol->cb.tx_key(sm->eapol->conf.ctx, sm->sta) 46214501Srpaulo#define processKey() do { } while (0) 47214501Srpaulo 48214501Srpaulo 49214501Srpaulostatic void eapol_sm_step_run(struct eapol_state_machine *sm); 50214501Srpaulostatic void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); 51214501Srpaulostatic void eapol_auth_initialize(struct eapol_state_machine *sm); 52214501Srpaulo 53214501Srpaulo 54214501Srpaulostatic void eapol_auth_logger(struct eapol_authenticator *eapol, 55214501Srpaulo const u8 *addr, eapol_logger_level level, 56214501Srpaulo const char *txt) 57214501Srpaulo{ 58214501Srpaulo if (eapol->cb.logger == NULL) 59214501Srpaulo return; 60214501Srpaulo eapol->cb.logger(eapol->conf.ctx, addr, level, txt); 61214501Srpaulo} 62214501Srpaulo 63214501Srpaulo 64214501Srpaulostatic void eapol_auth_vlogger(struct eapol_authenticator *eapol, 65214501Srpaulo const u8 *addr, eapol_logger_level level, 66214501Srpaulo const char *fmt, ...) 67214501Srpaulo{ 68214501Srpaulo char *format; 69214501Srpaulo int maxlen; 70214501Srpaulo va_list ap; 71214501Srpaulo 72214501Srpaulo if (eapol->cb.logger == NULL) 73214501Srpaulo return; 74214501Srpaulo 75214501Srpaulo maxlen = os_strlen(fmt) + 100; 76214501Srpaulo format = os_malloc(maxlen); 77214501Srpaulo if (!format) 78214501Srpaulo return; 79214501Srpaulo 80214501Srpaulo va_start(ap, fmt); 81214501Srpaulo vsnprintf(format, maxlen, fmt, ap); 82214501Srpaulo va_end(ap); 83214501Srpaulo 84214501Srpaulo eapol_auth_logger(eapol, addr, level, format); 85214501Srpaulo 86214501Srpaulo os_free(format); 87214501Srpaulo} 88214501Srpaulo 89214501Srpaulo 90214501Srpaulostatic void eapol_auth_tx_canned_eap(struct eapol_state_machine *sm, 91214501Srpaulo int success) 92214501Srpaulo{ 93214501Srpaulo struct eap_hdr eap; 94214501Srpaulo 95214501Srpaulo os_memset(&eap, 0, sizeof(eap)); 96214501Srpaulo 97214501Srpaulo eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; 98214501Srpaulo eap.identifier = ++sm->last_eap_id; 99214501Srpaulo eap.length = host_to_be16(sizeof(eap)); 100214501Srpaulo 101214501Srpaulo eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, 102214501Srpaulo "Sending canned EAP packet %s (identifier %d)", 103214501Srpaulo success ? "SUCCESS" : "FAILURE", eap.identifier); 104214501Srpaulo sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta, 105214501Srpaulo IEEE802_1X_TYPE_EAP_PACKET, 106214501Srpaulo (u8 *) &eap, sizeof(eap)); 107214501Srpaulo sm->dot1xAuthEapolFramesTx++; 108214501Srpaulo} 109214501Srpaulo 110214501Srpaulo 111214501Srpaulostatic void eapol_auth_tx_req(struct eapol_state_machine *sm) 112214501Srpaulo{ 113214501Srpaulo if (sm->eap_if->eapReqData == NULL || 114214501Srpaulo wpabuf_len(sm->eap_if->eapReqData) < sizeof(struct eap_hdr)) { 115214501Srpaulo eapol_auth_logger(sm->eapol, sm->addr, 116214501Srpaulo EAPOL_LOGGER_DEBUG, 117214501Srpaulo "TxReq called, but there is no EAP request " 118214501Srpaulo "from authentication server"); 119214501Srpaulo return; 120214501Srpaulo } 121214501Srpaulo 122214501Srpaulo if (sm->flags & EAPOL_SM_WAIT_START) { 123214501Srpaulo wpa_printf(MSG_DEBUG, "EAPOL: Drop EAPOL TX to " MACSTR 124214501Srpaulo " while waiting for EAPOL-Start", 125214501Srpaulo MAC2STR(sm->addr)); 126214501Srpaulo return; 127214501Srpaulo } 128214501Srpaulo 129214501Srpaulo sm->last_eap_id = eap_get_id(sm->eap_if->eapReqData); 130214501Srpaulo eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, 131214501Srpaulo "Sending EAP Packet (identifier %d)", 132214501Srpaulo sm->last_eap_id); 133214501Srpaulo sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta, 134214501Srpaulo IEEE802_1X_TYPE_EAP_PACKET, 135214501Srpaulo wpabuf_head(sm->eap_if->eapReqData), 136214501Srpaulo wpabuf_len(sm->eap_if->eapReqData)); 137214501Srpaulo sm->dot1xAuthEapolFramesTx++; 138214501Srpaulo if (eap_get_type(sm->eap_if->eapReqData) == EAP_TYPE_IDENTITY) 139214501Srpaulo sm->dot1xAuthEapolReqIdFramesTx++; 140214501Srpaulo else 141214501Srpaulo sm->dot1xAuthEapolReqFramesTx++; 142214501Srpaulo} 143214501Srpaulo 144214501Srpaulo 145214501Srpaulo/** 146214501Srpaulo * eapol_port_timers_tick - Port Timers state machine 147214501Srpaulo * @eloop_ctx: struct eapol_state_machine * 148214501Srpaulo * @timeout_ctx: Not used 149214501Srpaulo * 150214501Srpaulo * This statemachine is implemented as a function that will be called 151214501Srpaulo * once a second as a registered event loop timeout. 152214501Srpaulo */ 153214501Srpaulostatic void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) 154214501Srpaulo{ 155214501Srpaulo struct eapol_state_machine *state = timeout_ctx; 156214501Srpaulo 157214501Srpaulo if (state->aWhile > 0) { 158214501Srpaulo state->aWhile--; 159214501Srpaulo if (state->aWhile == 0) { 160214501Srpaulo wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR 161214501Srpaulo " - aWhile --> 0", 162214501Srpaulo MAC2STR(state->addr)); 163214501Srpaulo } 164214501Srpaulo } 165214501Srpaulo 166214501Srpaulo if (state->quietWhile > 0) { 167214501Srpaulo state->quietWhile--; 168214501Srpaulo if (state->quietWhile == 0) { 169214501Srpaulo wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR 170214501Srpaulo " - quietWhile --> 0", 171214501Srpaulo MAC2STR(state->addr)); 172214501Srpaulo } 173214501Srpaulo } 174214501Srpaulo 175214501Srpaulo if (state->reAuthWhen > 0) { 176214501Srpaulo state->reAuthWhen--; 177214501Srpaulo if (state->reAuthWhen == 0) { 178214501Srpaulo wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR 179214501Srpaulo " - reAuthWhen --> 0", 180214501Srpaulo MAC2STR(state->addr)); 181214501Srpaulo } 182214501Srpaulo } 183214501Srpaulo 184214501Srpaulo if (state->eap_if->retransWhile > 0) { 185214501Srpaulo state->eap_if->retransWhile--; 186214501Srpaulo if (state->eap_if->retransWhile == 0) { 187214501Srpaulo wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR 188214501Srpaulo " - (EAP) retransWhile --> 0", 189214501Srpaulo MAC2STR(state->addr)); 190214501Srpaulo } 191214501Srpaulo } 192214501Srpaulo 193214501Srpaulo eapol_sm_step_run(state); 194214501Srpaulo 195214501Srpaulo eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state); 196214501Srpaulo} 197214501Srpaulo 198214501Srpaulo 199214501Srpaulo 200214501Srpaulo/* Authenticator PAE state machine */ 201214501Srpaulo 202214501SrpauloSM_STATE(AUTH_PAE, INITIALIZE) 203214501Srpaulo{ 204214501Srpaulo SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae); 205214501Srpaulo sm->portMode = Auto; 206214501Srpaulo} 207214501Srpaulo 208214501Srpaulo 209214501SrpauloSM_STATE(AUTH_PAE, DISCONNECTED) 210214501Srpaulo{ 211214501Srpaulo int from_initialize = sm->auth_pae_state == AUTH_PAE_INITIALIZE; 212214501Srpaulo 213214501Srpaulo if (sm->eapolLogoff) { 214214501Srpaulo if (sm->auth_pae_state == AUTH_PAE_CONNECTING) 215214501Srpaulo sm->authEapLogoffsWhileConnecting++; 216214501Srpaulo else if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) 217214501Srpaulo sm->authAuthEapLogoffWhileAuthenticated++; 218214501Srpaulo } 219214501Srpaulo 220214501Srpaulo SM_ENTRY_MA(AUTH_PAE, DISCONNECTED, auth_pae); 221214501Srpaulo 222214501Srpaulo sm->authPortStatus = Unauthorized; 223214501Srpaulo setPortUnauthorized(); 224214501Srpaulo sm->reAuthCount = 0; 225214501Srpaulo sm->eapolLogoff = FALSE; 226214501Srpaulo if (!from_initialize) { 227214501Srpaulo sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, 228214501Srpaulo sm->flags & EAPOL_SM_PREAUTH); 229214501Srpaulo } 230214501Srpaulo} 231214501Srpaulo 232214501Srpaulo 233214501SrpauloSM_STATE(AUTH_PAE, RESTART) 234214501Srpaulo{ 235214501Srpaulo if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) { 236214501Srpaulo if (sm->reAuthenticate) 237214501Srpaulo sm->authAuthReauthsWhileAuthenticated++; 238214501Srpaulo if (sm->eapolStart) 239214501Srpaulo sm->authAuthEapStartsWhileAuthenticated++; 240214501Srpaulo if (sm->eapolLogoff) 241214501Srpaulo sm->authAuthEapLogoffWhileAuthenticated++; 242214501Srpaulo } 243214501Srpaulo 244214501Srpaulo SM_ENTRY_MA(AUTH_PAE, RESTART, auth_pae); 245214501Srpaulo 246214501Srpaulo sm->eap_if->eapRestart = TRUE; 247214501Srpaulo} 248214501Srpaulo 249214501Srpaulo 250214501SrpauloSM_STATE(AUTH_PAE, CONNECTING) 251214501Srpaulo{ 252214501Srpaulo if (sm->auth_pae_state != AUTH_PAE_CONNECTING) 253214501Srpaulo sm->authEntersConnecting++; 254214501Srpaulo 255214501Srpaulo SM_ENTRY_MA(AUTH_PAE, CONNECTING, auth_pae); 256214501Srpaulo 257214501Srpaulo sm->reAuthenticate = FALSE; 258214501Srpaulo sm->reAuthCount++; 259214501Srpaulo} 260214501Srpaulo 261214501Srpaulo 262214501SrpauloSM_STATE(AUTH_PAE, HELD) 263214501Srpaulo{ 264214501Srpaulo if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authFail) 265214501Srpaulo sm->authAuthFailWhileAuthenticating++; 266214501Srpaulo 267214501Srpaulo SM_ENTRY_MA(AUTH_PAE, HELD, auth_pae); 268214501Srpaulo 269214501Srpaulo sm->authPortStatus = Unauthorized; 270214501Srpaulo setPortUnauthorized(); 271214501Srpaulo sm->quietWhile = sm->quietPeriod; 272214501Srpaulo sm->eapolLogoff = FALSE; 273214501Srpaulo 274214501Srpaulo eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_WARNING, 275214501Srpaulo "authentication failed - EAP type: %d (%s)", 276214501Srpaulo sm->eap_type_authsrv, 277214501Srpaulo eap_server_get_name(0, sm->eap_type_authsrv)); 278214501Srpaulo if (sm->eap_type_authsrv != sm->eap_type_supp) { 279214501Srpaulo eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO, 280214501Srpaulo "Supplicant used different EAP type: " 281214501Srpaulo "%d (%s)", sm->eap_type_supp, 282214501Srpaulo eap_server_get_name(0, sm->eap_type_supp)); 283214501Srpaulo } 284214501Srpaulo sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, 285214501Srpaulo sm->flags & EAPOL_SM_PREAUTH); 286214501Srpaulo} 287214501Srpaulo 288214501Srpaulo 289214501SrpauloSM_STATE(AUTH_PAE, AUTHENTICATED) 290214501Srpaulo{ 291214501Srpaulo char *extra = ""; 292214501Srpaulo 293214501Srpaulo if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess) 294214501Srpaulo sm->authAuthSuccessesWhileAuthenticating++; 295214501Srpaulo 296214501Srpaulo SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae); 297214501Srpaulo 298214501Srpaulo sm->authPortStatus = Authorized; 299214501Srpaulo setPortAuthorized(); 300214501Srpaulo sm->reAuthCount = 0; 301214501Srpaulo if (sm->flags & EAPOL_SM_PREAUTH) 302214501Srpaulo extra = " (pre-authentication)"; 303214501Srpaulo else if (sm->flags & EAPOL_SM_FROM_PMKSA_CACHE) 304214501Srpaulo extra = " (PMKSA cache)"; 305214501Srpaulo eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO, 306214501Srpaulo "authenticated - EAP type: %d (%s)%s", 307214501Srpaulo sm->eap_type_authsrv, 308214501Srpaulo eap_server_get_name(0, sm->eap_type_authsrv), 309214501Srpaulo extra); 310214501Srpaulo sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 1, 311214501Srpaulo sm->flags & EAPOL_SM_PREAUTH); 312214501Srpaulo} 313214501Srpaulo 314214501Srpaulo 315214501SrpauloSM_STATE(AUTH_PAE, AUTHENTICATING) 316214501Srpaulo{ 317214501Srpaulo SM_ENTRY_MA(AUTH_PAE, AUTHENTICATING, auth_pae); 318214501Srpaulo 319214501Srpaulo sm->eapolStart = FALSE; 320214501Srpaulo sm->authSuccess = FALSE; 321214501Srpaulo sm->authFail = FALSE; 322214501Srpaulo sm->authTimeout = FALSE; 323214501Srpaulo sm->authStart = TRUE; 324214501Srpaulo sm->keyRun = FALSE; 325214501Srpaulo sm->keyDone = FALSE; 326214501Srpaulo} 327214501Srpaulo 328214501Srpaulo 329214501SrpauloSM_STATE(AUTH_PAE, ABORTING) 330214501Srpaulo{ 331214501Srpaulo if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING) { 332214501Srpaulo if (sm->authTimeout) 333214501Srpaulo sm->authAuthTimeoutsWhileAuthenticating++; 334214501Srpaulo if (sm->eapolStart) 335214501Srpaulo sm->authAuthEapStartsWhileAuthenticating++; 336214501Srpaulo if (sm->eapolLogoff) 337214501Srpaulo sm->authAuthEapLogoffWhileAuthenticating++; 338214501Srpaulo } 339214501Srpaulo 340214501Srpaulo SM_ENTRY_MA(AUTH_PAE, ABORTING, auth_pae); 341214501Srpaulo 342214501Srpaulo sm->authAbort = TRUE; 343214501Srpaulo sm->keyRun = FALSE; 344214501Srpaulo sm->keyDone = FALSE; 345214501Srpaulo} 346214501Srpaulo 347214501Srpaulo 348214501SrpauloSM_STATE(AUTH_PAE, FORCE_AUTH) 349214501Srpaulo{ 350214501Srpaulo SM_ENTRY_MA(AUTH_PAE, FORCE_AUTH, auth_pae); 351214501Srpaulo 352214501Srpaulo sm->authPortStatus = Authorized; 353214501Srpaulo setPortAuthorized(); 354214501Srpaulo sm->portMode = ForceAuthorized; 355214501Srpaulo sm->eapolStart = FALSE; 356214501Srpaulo txCannedSuccess(); 357214501Srpaulo} 358214501Srpaulo 359214501Srpaulo 360214501SrpauloSM_STATE(AUTH_PAE, FORCE_UNAUTH) 361214501Srpaulo{ 362214501Srpaulo SM_ENTRY_MA(AUTH_PAE, FORCE_UNAUTH, auth_pae); 363214501Srpaulo 364214501Srpaulo sm->authPortStatus = Unauthorized; 365214501Srpaulo setPortUnauthorized(); 366214501Srpaulo sm->portMode = ForceUnauthorized; 367214501Srpaulo sm->eapolStart = FALSE; 368214501Srpaulo txCannedFail(); 369214501Srpaulo} 370214501Srpaulo 371214501Srpaulo 372214501SrpauloSM_STEP(AUTH_PAE) 373214501Srpaulo{ 374214501Srpaulo if ((sm->portControl == Auto && sm->portMode != sm->portControl) || 375214501Srpaulo sm->initialize || !sm->eap_if->portEnabled) 376214501Srpaulo SM_ENTER_GLOBAL(AUTH_PAE, INITIALIZE); 377214501Srpaulo else if (sm->portControl == ForceAuthorized && 378214501Srpaulo sm->portMode != sm->portControl && 379214501Srpaulo !(sm->initialize || !sm->eap_if->portEnabled)) 380214501Srpaulo SM_ENTER_GLOBAL(AUTH_PAE, FORCE_AUTH); 381214501Srpaulo else if (sm->portControl == ForceUnauthorized && 382214501Srpaulo sm->portMode != sm->portControl && 383214501Srpaulo !(sm->initialize || !sm->eap_if->portEnabled)) 384214501Srpaulo SM_ENTER_GLOBAL(AUTH_PAE, FORCE_UNAUTH); 385214501Srpaulo else { 386214501Srpaulo switch (sm->auth_pae_state) { 387214501Srpaulo case AUTH_PAE_INITIALIZE: 388214501Srpaulo SM_ENTER(AUTH_PAE, DISCONNECTED); 389214501Srpaulo break; 390214501Srpaulo case AUTH_PAE_DISCONNECTED: 391214501Srpaulo SM_ENTER(AUTH_PAE, RESTART); 392214501Srpaulo break; 393214501Srpaulo case AUTH_PAE_RESTART: 394214501Srpaulo if (!sm->eap_if->eapRestart) 395214501Srpaulo SM_ENTER(AUTH_PAE, CONNECTING); 396214501Srpaulo break; 397214501Srpaulo case AUTH_PAE_HELD: 398214501Srpaulo if (sm->quietWhile == 0) 399214501Srpaulo SM_ENTER(AUTH_PAE, RESTART); 400214501Srpaulo break; 401214501Srpaulo case AUTH_PAE_CONNECTING: 402214501Srpaulo if (sm->eapolLogoff || sm->reAuthCount > sm->reAuthMax) 403214501Srpaulo SM_ENTER(AUTH_PAE, DISCONNECTED); 404214501Srpaulo else if ((sm->eap_if->eapReq && 405214501Srpaulo sm->reAuthCount <= sm->reAuthMax) || 406214501Srpaulo sm->eap_if->eapSuccess || sm->eap_if->eapFail) 407214501Srpaulo SM_ENTER(AUTH_PAE, AUTHENTICATING); 408214501Srpaulo break; 409214501Srpaulo case AUTH_PAE_AUTHENTICATED: 410214501Srpaulo if (sm->eapolStart || sm->reAuthenticate) 411214501Srpaulo SM_ENTER(AUTH_PAE, RESTART); 412214501Srpaulo else if (sm->eapolLogoff || !sm->portValid) 413214501Srpaulo SM_ENTER(AUTH_PAE, DISCONNECTED); 414214501Srpaulo break; 415214501Srpaulo case AUTH_PAE_AUTHENTICATING: 416214501Srpaulo if (sm->authSuccess && sm->portValid) 417214501Srpaulo SM_ENTER(AUTH_PAE, AUTHENTICATED); 418214501Srpaulo else if (sm->authFail || 419214501Srpaulo (sm->keyDone && !sm->portValid)) 420214501Srpaulo SM_ENTER(AUTH_PAE, HELD); 421214501Srpaulo else if (sm->eapolStart || sm->eapolLogoff || 422214501Srpaulo sm->authTimeout) 423214501Srpaulo SM_ENTER(AUTH_PAE, ABORTING); 424214501Srpaulo break; 425214501Srpaulo case AUTH_PAE_ABORTING: 426214501Srpaulo if (sm->eapolLogoff && !sm->authAbort) 427214501Srpaulo SM_ENTER(AUTH_PAE, DISCONNECTED); 428214501Srpaulo else if (!sm->eapolLogoff && !sm->authAbort) 429214501Srpaulo SM_ENTER(AUTH_PAE, RESTART); 430214501Srpaulo break; 431214501Srpaulo case AUTH_PAE_FORCE_AUTH: 432214501Srpaulo if (sm->eapolStart) 433214501Srpaulo SM_ENTER(AUTH_PAE, FORCE_AUTH); 434214501Srpaulo break; 435214501Srpaulo case AUTH_PAE_FORCE_UNAUTH: 436214501Srpaulo if (sm->eapolStart) 437214501Srpaulo SM_ENTER(AUTH_PAE, FORCE_UNAUTH); 438214501Srpaulo break; 439214501Srpaulo } 440214501Srpaulo } 441214501Srpaulo} 442214501Srpaulo 443214501Srpaulo 444214501Srpaulo 445214501Srpaulo/* Backend Authentication state machine */ 446214501Srpaulo 447214501SrpauloSM_STATE(BE_AUTH, INITIALIZE) 448214501Srpaulo{ 449214501Srpaulo SM_ENTRY_MA(BE_AUTH, INITIALIZE, be_auth); 450214501Srpaulo 451214501Srpaulo abortAuth(); 452214501Srpaulo sm->eap_if->eapNoReq = FALSE; 453214501Srpaulo sm->authAbort = FALSE; 454214501Srpaulo} 455214501Srpaulo 456214501Srpaulo 457214501SrpauloSM_STATE(BE_AUTH, REQUEST) 458214501Srpaulo{ 459214501Srpaulo SM_ENTRY_MA(BE_AUTH, REQUEST, be_auth); 460214501Srpaulo 461214501Srpaulo txReq(); 462214501Srpaulo sm->eap_if->eapReq = FALSE; 463214501Srpaulo sm->backendOtherRequestsToSupplicant++; 464214501Srpaulo 465214501Srpaulo /* 466214501Srpaulo * Clearing eapolEap here is not specified in IEEE Std 802.1X-2004, but 467214501Srpaulo * it looks like this would be logical thing to do there since the old 468214501Srpaulo * EAP response would not be valid anymore after the new EAP request 469214501Srpaulo * was sent out. 470214501Srpaulo * 471214501Srpaulo * A race condition has been reported, in which hostapd ended up 472214501Srpaulo * sending out EAP-Response/Identity as a response to the first 473214501Srpaulo * EAP-Request from the main EAP method. This can be avoided by 474214501Srpaulo * clearing eapolEap here. 475214501Srpaulo */ 476214501Srpaulo sm->eapolEap = FALSE; 477214501Srpaulo} 478214501Srpaulo 479214501Srpaulo 480214501SrpauloSM_STATE(BE_AUTH, RESPONSE) 481214501Srpaulo{ 482214501Srpaulo SM_ENTRY_MA(BE_AUTH, RESPONSE, be_auth); 483214501Srpaulo 484214501Srpaulo sm->authTimeout = FALSE; 485214501Srpaulo sm->eapolEap = FALSE; 486214501Srpaulo sm->eap_if->eapNoReq = FALSE; 487214501Srpaulo sm->aWhile = sm->serverTimeout; 488214501Srpaulo sm->eap_if->eapResp = TRUE; 489214501Srpaulo /* sendRespToServer(); */ 490214501Srpaulo sm->backendResponses++; 491214501Srpaulo} 492214501Srpaulo 493214501Srpaulo 494214501SrpauloSM_STATE(BE_AUTH, SUCCESS) 495214501Srpaulo{ 496214501Srpaulo SM_ENTRY_MA(BE_AUTH, SUCCESS, be_auth); 497214501Srpaulo 498214501Srpaulo txReq(); 499214501Srpaulo sm->authSuccess = TRUE; 500214501Srpaulo sm->keyRun = TRUE; 501214501Srpaulo} 502214501Srpaulo 503214501Srpaulo 504214501SrpauloSM_STATE(BE_AUTH, FAIL) 505214501Srpaulo{ 506214501Srpaulo SM_ENTRY_MA(BE_AUTH, FAIL, be_auth); 507214501Srpaulo 508214501Srpaulo txReq(); 509214501Srpaulo sm->authFail = TRUE; 510214501Srpaulo} 511214501Srpaulo 512214501Srpaulo 513214501SrpauloSM_STATE(BE_AUTH, TIMEOUT) 514214501Srpaulo{ 515214501Srpaulo SM_ENTRY_MA(BE_AUTH, TIMEOUT, be_auth); 516214501Srpaulo 517214501Srpaulo sm->authTimeout = TRUE; 518214501Srpaulo} 519214501Srpaulo 520214501Srpaulo 521214501SrpauloSM_STATE(BE_AUTH, IDLE) 522214501Srpaulo{ 523214501Srpaulo SM_ENTRY_MA(BE_AUTH, IDLE, be_auth); 524214501Srpaulo 525214501Srpaulo sm->authStart = FALSE; 526214501Srpaulo} 527214501Srpaulo 528214501Srpaulo 529214501SrpauloSM_STATE(BE_AUTH, IGNORE) 530214501Srpaulo{ 531214501Srpaulo SM_ENTRY_MA(BE_AUTH, IGNORE, be_auth); 532214501Srpaulo 533214501Srpaulo sm->eap_if->eapNoReq = FALSE; 534214501Srpaulo} 535214501Srpaulo 536214501Srpaulo 537214501SrpauloSM_STEP(BE_AUTH) 538214501Srpaulo{ 539214501Srpaulo if (sm->portControl != Auto || sm->initialize || sm->authAbort) { 540214501Srpaulo SM_ENTER_GLOBAL(BE_AUTH, INITIALIZE); 541214501Srpaulo return; 542214501Srpaulo } 543214501Srpaulo 544214501Srpaulo switch (sm->be_auth_state) { 545214501Srpaulo case BE_AUTH_INITIALIZE: 546214501Srpaulo SM_ENTER(BE_AUTH, IDLE); 547214501Srpaulo break; 548214501Srpaulo case BE_AUTH_REQUEST: 549214501Srpaulo if (sm->eapolEap) 550214501Srpaulo SM_ENTER(BE_AUTH, RESPONSE); 551214501Srpaulo else if (sm->eap_if->eapReq) 552214501Srpaulo SM_ENTER(BE_AUTH, REQUEST); 553214501Srpaulo else if (sm->eap_if->eapTimeout) 554214501Srpaulo SM_ENTER(BE_AUTH, TIMEOUT); 555214501Srpaulo break; 556214501Srpaulo case BE_AUTH_RESPONSE: 557214501Srpaulo if (sm->eap_if->eapNoReq) 558214501Srpaulo SM_ENTER(BE_AUTH, IGNORE); 559214501Srpaulo if (sm->eap_if->eapReq) { 560214501Srpaulo sm->backendAccessChallenges++; 561214501Srpaulo SM_ENTER(BE_AUTH, REQUEST); 562214501Srpaulo } else if (sm->aWhile == 0) 563214501Srpaulo SM_ENTER(BE_AUTH, TIMEOUT); 564214501Srpaulo else if (sm->eap_if->eapFail) { 565214501Srpaulo sm->backendAuthFails++; 566214501Srpaulo SM_ENTER(BE_AUTH, FAIL); 567214501Srpaulo } else if (sm->eap_if->eapSuccess) { 568214501Srpaulo sm->backendAuthSuccesses++; 569214501Srpaulo SM_ENTER(BE_AUTH, SUCCESS); 570214501Srpaulo } 571214501Srpaulo break; 572214501Srpaulo case BE_AUTH_SUCCESS: 573214501Srpaulo SM_ENTER(BE_AUTH, IDLE); 574214501Srpaulo break; 575214501Srpaulo case BE_AUTH_FAIL: 576214501Srpaulo SM_ENTER(BE_AUTH, IDLE); 577214501Srpaulo break; 578214501Srpaulo case BE_AUTH_TIMEOUT: 579214501Srpaulo SM_ENTER(BE_AUTH, IDLE); 580214501Srpaulo break; 581214501Srpaulo case BE_AUTH_IDLE: 582214501Srpaulo if (sm->eap_if->eapFail && sm->authStart) 583214501Srpaulo SM_ENTER(BE_AUTH, FAIL); 584214501Srpaulo else if (sm->eap_if->eapReq && sm->authStart) 585214501Srpaulo SM_ENTER(BE_AUTH, REQUEST); 586214501Srpaulo else if (sm->eap_if->eapSuccess && sm->authStart) 587214501Srpaulo SM_ENTER(BE_AUTH, SUCCESS); 588214501Srpaulo break; 589214501Srpaulo case BE_AUTH_IGNORE: 590214501Srpaulo if (sm->eapolEap) 591214501Srpaulo SM_ENTER(BE_AUTH, RESPONSE); 592214501Srpaulo else if (sm->eap_if->eapReq) 593214501Srpaulo SM_ENTER(BE_AUTH, REQUEST); 594214501Srpaulo else if (sm->eap_if->eapTimeout) 595214501Srpaulo SM_ENTER(BE_AUTH, TIMEOUT); 596214501Srpaulo break; 597214501Srpaulo } 598214501Srpaulo} 599214501Srpaulo 600214501Srpaulo 601214501Srpaulo 602214501Srpaulo/* Reauthentication Timer state machine */ 603214501Srpaulo 604214501SrpauloSM_STATE(REAUTH_TIMER, INITIALIZE) 605214501Srpaulo{ 606214501Srpaulo SM_ENTRY_MA(REAUTH_TIMER, INITIALIZE, reauth_timer); 607214501Srpaulo 608214501Srpaulo sm->reAuthWhen = sm->reAuthPeriod; 609214501Srpaulo} 610214501Srpaulo 611214501Srpaulo 612214501SrpauloSM_STATE(REAUTH_TIMER, REAUTHENTICATE) 613214501Srpaulo{ 614214501Srpaulo SM_ENTRY_MA(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); 615214501Srpaulo 616214501Srpaulo sm->reAuthenticate = TRUE; 617214501Srpaulo sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta, 618214501Srpaulo EAPOL_AUTH_REAUTHENTICATE); 619214501Srpaulo} 620214501Srpaulo 621214501Srpaulo 622214501SrpauloSM_STEP(REAUTH_TIMER) 623214501Srpaulo{ 624214501Srpaulo if (sm->portControl != Auto || sm->initialize || 625214501Srpaulo sm->authPortStatus == Unauthorized || !sm->reAuthEnabled) { 626214501Srpaulo SM_ENTER_GLOBAL(REAUTH_TIMER, INITIALIZE); 627214501Srpaulo return; 628214501Srpaulo } 629214501Srpaulo 630214501Srpaulo switch (sm->reauth_timer_state) { 631214501Srpaulo case REAUTH_TIMER_INITIALIZE: 632214501Srpaulo if (sm->reAuthWhen == 0) 633214501Srpaulo SM_ENTER(REAUTH_TIMER, REAUTHENTICATE); 634214501Srpaulo break; 635214501Srpaulo case REAUTH_TIMER_REAUTHENTICATE: 636214501Srpaulo SM_ENTER(REAUTH_TIMER, INITIALIZE); 637214501Srpaulo break; 638214501Srpaulo } 639214501Srpaulo} 640214501Srpaulo 641214501Srpaulo 642214501Srpaulo 643214501Srpaulo/* Authenticator Key Transmit state machine */ 644214501Srpaulo 645214501SrpauloSM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT) 646214501Srpaulo{ 647214501Srpaulo SM_ENTRY_MA(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx); 648214501Srpaulo} 649214501Srpaulo 650214501Srpaulo 651214501SrpauloSM_STATE(AUTH_KEY_TX, KEY_TRANSMIT) 652214501Srpaulo{ 653214501Srpaulo SM_ENTRY_MA(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx); 654214501Srpaulo 655214501Srpaulo txKey(); 656214501Srpaulo sm->eap_if->eapKeyAvailable = FALSE; 657214501Srpaulo sm->keyDone = TRUE; 658214501Srpaulo} 659214501Srpaulo 660214501Srpaulo 661214501SrpauloSM_STEP(AUTH_KEY_TX) 662214501Srpaulo{ 663214501Srpaulo if (sm->initialize || sm->portControl != Auto) { 664214501Srpaulo SM_ENTER_GLOBAL(AUTH_KEY_TX, NO_KEY_TRANSMIT); 665214501Srpaulo return; 666214501Srpaulo } 667214501Srpaulo 668214501Srpaulo switch (sm->auth_key_tx_state) { 669214501Srpaulo case AUTH_KEY_TX_NO_KEY_TRANSMIT: 670214501Srpaulo if (sm->keyTxEnabled && sm->eap_if->eapKeyAvailable && 671214501Srpaulo sm->keyRun && !(sm->flags & EAPOL_SM_USES_WPA)) 672214501Srpaulo SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); 673214501Srpaulo break; 674214501Srpaulo case AUTH_KEY_TX_KEY_TRANSMIT: 675214501Srpaulo if (!sm->keyTxEnabled || !sm->keyRun) 676214501Srpaulo SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT); 677214501Srpaulo else if (sm->eap_if->eapKeyAvailable) 678214501Srpaulo SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); 679214501Srpaulo break; 680214501Srpaulo } 681214501Srpaulo} 682214501Srpaulo 683214501Srpaulo 684214501Srpaulo 685214501Srpaulo/* Key Receive state machine */ 686214501Srpaulo 687214501SrpauloSM_STATE(KEY_RX, NO_KEY_RECEIVE) 688214501Srpaulo{ 689214501Srpaulo SM_ENTRY_MA(KEY_RX, NO_KEY_RECEIVE, key_rx); 690214501Srpaulo} 691214501Srpaulo 692214501Srpaulo 693214501SrpauloSM_STATE(KEY_RX, KEY_RECEIVE) 694214501Srpaulo{ 695214501Srpaulo SM_ENTRY_MA(KEY_RX, KEY_RECEIVE, key_rx); 696214501Srpaulo 697214501Srpaulo processKey(); 698214501Srpaulo sm->rxKey = FALSE; 699214501Srpaulo} 700214501Srpaulo 701214501Srpaulo 702214501SrpauloSM_STEP(KEY_RX) 703214501Srpaulo{ 704214501Srpaulo if (sm->initialize || !sm->eap_if->portEnabled) { 705214501Srpaulo SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); 706214501Srpaulo return; 707214501Srpaulo } 708214501Srpaulo 709214501Srpaulo switch (sm->key_rx_state) { 710214501Srpaulo case KEY_RX_NO_KEY_RECEIVE: 711214501Srpaulo if (sm->rxKey) 712214501Srpaulo SM_ENTER(KEY_RX, KEY_RECEIVE); 713214501Srpaulo break; 714214501Srpaulo case KEY_RX_KEY_RECEIVE: 715214501Srpaulo if (sm->rxKey) 716214501Srpaulo SM_ENTER(KEY_RX, KEY_RECEIVE); 717214501Srpaulo break; 718214501Srpaulo } 719214501Srpaulo} 720214501Srpaulo 721214501Srpaulo 722214501Srpaulo 723214501Srpaulo/* Controlled Directions state machine */ 724214501Srpaulo 725214501SrpauloSM_STATE(CTRL_DIR, FORCE_BOTH) 726214501Srpaulo{ 727214501Srpaulo SM_ENTRY_MA(CTRL_DIR, FORCE_BOTH, ctrl_dir); 728214501Srpaulo sm->operControlledDirections = Both; 729214501Srpaulo} 730214501Srpaulo 731214501Srpaulo 732214501SrpauloSM_STATE(CTRL_DIR, IN_OR_BOTH) 733214501Srpaulo{ 734214501Srpaulo SM_ENTRY_MA(CTRL_DIR, IN_OR_BOTH, ctrl_dir); 735214501Srpaulo sm->operControlledDirections = sm->adminControlledDirections; 736214501Srpaulo} 737214501Srpaulo 738214501Srpaulo 739214501SrpauloSM_STEP(CTRL_DIR) 740214501Srpaulo{ 741214501Srpaulo if (sm->initialize) { 742214501Srpaulo SM_ENTER_GLOBAL(CTRL_DIR, IN_OR_BOTH); 743214501Srpaulo return; 744214501Srpaulo } 745214501Srpaulo 746214501Srpaulo switch (sm->ctrl_dir_state) { 747214501Srpaulo case CTRL_DIR_FORCE_BOTH: 748214501Srpaulo if (sm->eap_if->portEnabled && sm->operEdge) 749214501Srpaulo SM_ENTER(CTRL_DIR, IN_OR_BOTH); 750214501Srpaulo break; 751214501Srpaulo case CTRL_DIR_IN_OR_BOTH: 752214501Srpaulo if (sm->operControlledDirections != 753214501Srpaulo sm->adminControlledDirections) 754214501Srpaulo SM_ENTER(CTRL_DIR, IN_OR_BOTH); 755214501Srpaulo if (!sm->eap_if->portEnabled || !sm->operEdge) 756214501Srpaulo SM_ENTER(CTRL_DIR, FORCE_BOTH); 757214501Srpaulo break; 758214501Srpaulo } 759214501Srpaulo} 760214501Srpaulo 761214501Srpaulo 762214501Srpaulo 763214501Srpaulostruct eapol_state_machine * 764214501Srpauloeapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, 765214501Srpaulo int flags, const struct wpabuf *assoc_wps_ie, void *sta_ctx) 766214501Srpaulo{ 767214501Srpaulo struct eapol_state_machine *sm; 768214501Srpaulo struct eap_config eap_conf; 769214501Srpaulo 770214501Srpaulo if (eapol == NULL) 771214501Srpaulo return NULL; 772214501Srpaulo 773214501Srpaulo sm = os_zalloc(sizeof(*sm)); 774214501Srpaulo if (sm == NULL) { 775214501Srpaulo wpa_printf(MSG_DEBUG, "IEEE 802.1X state machine allocation " 776214501Srpaulo "failed"); 777214501Srpaulo return NULL; 778214501Srpaulo } 779214501Srpaulo sm->radius_identifier = -1; 780214501Srpaulo os_memcpy(sm->addr, addr, ETH_ALEN); 781214501Srpaulo sm->flags = flags; 782214501Srpaulo 783214501Srpaulo sm->eapol = eapol; 784214501Srpaulo sm->sta = sta_ctx; 785214501Srpaulo 786214501Srpaulo /* Set default values for state machine constants */ 787214501Srpaulo sm->auth_pae_state = AUTH_PAE_INITIALIZE; 788214501Srpaulo sm->quietPeriod = AUTH_PAE_DEFAULT_quietPeriod; 789214501Srpaulo sm->reAuthMax = AUTH_PAE_DEFAULT_reAuthMax; 790214501Srpaulo 791214501Srpaulo sm->be_auth_state = BE_AUTH_INITIALIZE; 792214501Srpaulo sm->serverTimeout = BE_AUTH_DEFAULT_serverTimeout; 793214501Srpaulo 794214501Srpaulo sm->reauth_timer_state = REAUTH_TIMER_INITIALIZE; 795214501Srpaulo sm->reAuthPeriod = eapol->conf.eap_reauth_period; 796214501Srpaulo sm->reAuthEnabled = eapol->conf.eap_reauth_period > 0 ? TRUE : FALSE; 797214501Srpaulo 798214501Srpaulo sm->auth_key_tx_state = AUTH_KEY_TX_NO_KEY_TRANSMIT; 799214501Srpaulo 800214501Srpaulo sm->key_rx_state = KEY_RX_NO_KEY_RECEIVE; 801214501Srpaulo 802214501Srpaulo sm->ctrl_dir_state = CTRL_DIR_IN_OR_BOTH; 803214501Srpaulo 804214501Srpaulo sm->portControl = Auto; 805214501Srpaulo 806214501Srpaulo if (!eapol->conf.wpa && 807214501Srpaulo (eapol->default_wep_key || eapol->conf.individual_wep_key_len > 0)) 808214501Srpaulo sm->keyTxEnabled = TRUE; 809214501Srpaulo else 810214501Srpaulo sm->keyTxEnabled = FALSE; 811214501Srpaulo if (eapol->conf.wpa) 812214501Srpaulo sm->portValid = FALSE; 813214501Srpaulo else 814214501Srpaulo sm->portValid = TRUE; 815214501Srpaulo 816214501Srpaulo os_memset(&eap_conf, 0, sizeof(eap_conf)); 817214501Srpaulo eap_conf.eap_server = eapol->conf.eap_server; 818214501Srpaulo eap_conf.ssl_ctx = eapol->conf.ssl_ctx; 819214501Srpaulo eap_conf.msg_ctx = eapol->conf.msg_ctx; 820214501Srpaulo eap_conf.eap_sim_db_priv = eapol->conf.eap_sim_db_priv; 821214501Srpaulo eap_conf.pac_opaque_encr_key = eapol->conf.pac_opaque_encr_key; 822214501Srpaulo eap_conf.eap_fast_a_id = eapol->conf.eap_fast_a_id; 823214501Srpaulo eap_conf.eap_fast_a_id_len = eapol->conf.eap_fast_a_id_len; 824214501Srpaulo eap_conf.eap_fast_a_id_info = eapol->conf.eap_fast_a_id_info; 825214501Srpaulo eap_conf.eap_fast_prov = eapol->conf.eap_fast_prov; 826214501Srpaulo eap_conf.pac_key_lifetime = eapol->conf.pac_key_lifetime; 827214501Srpaulo eap_conf.pac_key_refresh_time = eapol->conf.pac_key_refresh_time; 828214501Srpaulo eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind; 829214501Srpaulo eap_conf.tnc = eapol->conf.tnc; 830214501Srpaulo eap_conf.wps = eapol->conf.wps; 831214501Srpaulo eap_conf.assoc_wps_ie = assoc_wps_ie; 832214501Srpaulo eap_conf.peer_addr = addr; 833214501Srpaulo sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); 834214501Srpaulo if (sm->eap == NULL) { 835214501Srpaulo eapol_auth_free(sm); 836214501Srpaulo return NULL; 837214501Srpaulo } 838214501Srpaulo sm->eap_if = eap_get_interface(sm->eap); 839214501Srpaulo 840214501Srpaulo eapol_auth_initialize(sm); 841214501Srpaulo 842214501Srpaulo return sm; 843214501Srpaulo} 844214501Srpaulo 845214501Srpaulo 846214501Srpaulovoid eapol_auth_free(struct eapol_state_machine *sm) 847214501Srpaulo{ 848214501Srpaulo if (sm == NULL) 849214501Srpaulo return; 850214501Srpaulo 851214501Srpaulo eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); 852214501Srpaulo eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL); 853214501Srpaulo if (sm->eap) 854214501Srpaulo eap_server_sm_deinit(sm->eap); 855214501Srpaulo os_free(sm); 856214501Srpaulo} 857214501Srpaulo 858214501Srpaulo 859214501Srpaulostatic int eapol_sm_sta_entry_alive(struct eapol_authenticator *eapol, 860214501Srpaulo const u8 *addr) 861214501Srpaulo{ 862214501Srpaulo return eapol->cb.sta_entry_alive(eapol->conf.ctx, addr); 863214501Srpaulo} 864214501Srpaulo 865214501Srpaulo 866214501Srpaulostatic void eapol_sm_step_run(struct eapol_state_machine *sm) 867214501Srpaulo{ 868214501Srpaulo struct eapol_authenticator *eapol = sm->eapol; 869214501Srpaulo u8 addr[ETH_ALEN]; 870214501Srpaulo unsigned int prev_auth_pae, prev_be_auth, prev_reauth_timer, 871214501Srpaulo prev_auth_key_tx, prev_key_rx, prev_ctrl_dir; 872214501Srpaulo int max_steps = 100; 873214501Srpaulo 874214501Srpaulo os_memcpy(addr, sm->addr, ETH_ALEN); 875214501Srpaulo 876214501Srpaulo /* 877214501Srpaulo * Allow EAPOL state machines to run as long as there are state 878214501Srpaulo * changes, but exit and return here through event loop if more than 879214501Srpaulo * 100 steps is needed as a precaution against infinite loops inside 880214501Srpaulo * eloop callback. 881214501Srpaulo */ 882214501Srpaulorestart: 883214501Srpaulo prev_auth_pae = sm->auth_pae_state; 884214501Srpaulo prev_be_auth = sm->be_auth_state; 885214501Srpaulo prev_reauth_timer = sm->reauth_timer_state; 886214501Srpaulo prev_auth_key_tx = sm->auth_key_tx_state; 887214501Srpaulo prev_key_rx = sm->key_rx_state; 888214501Srpaulo prev_ctrl_dir = sm->ctrl_dir_state; 889214501Srpaulo 890214501Srpaulo SM_STEP_RUN(AUTH_PAE); 891214501Srpaulo if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) 892214501Srpaulo SM_STEP_RUN(BE_AUTH); 893214501Srpaulo if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) 894214501Srpaulo SM_STEP_RUN(REAUTH_TIMER); 895214501Srpaulo if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) 896214501Srpaulo SM_STEP_RUN(AUTH_KEY_TX); 897214501Srpaulo if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) 898214501Srpaulo SM_STEP_RUN(KEY_RX); 899214501Srpaulo if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) 900214501Srpaulo SM_STEP_RUN(CTRL_DIR); 901214501Srpaulo 902214501Srpaulo if (prev_auth_pae != sm->auth_pae_state || 903214501Srpaulo prev_be_auth != sm->be_auth_state || 904214501Srpaulo prev_reauth_timer != sm->reauth_timer_state || 905214501Srpaulo prev_auth_key_tx != sm->auth_key_tx_state || 906214501Srpaulo prev_key_rx != sm->key_rx_state || 907214501Srpaulo prev_ctrl_dir != sm->ctrl_dir_state) { 908214501Srpaulo if (--max_steps > 0) 909214501Srpaulo goto restart; 910214501Srpaulo /* Re-run from eloop timeout */ 911214501Srpaulo eapol_auth_step(sm); 912214501Srpaulo return; 913214501Srpaulo } 914214501Srpaulo 915214501Srpaulo if (eapol_sm_sta_entry_alive(eapol, addr) && sm->eap) { 916214501Srpaulo if (eap_server_sm_step(sm->eap)) { 917214501Srpaulo if (--max_steps > 0) 918214501Srpaulo goto restart; 919214501Srpaulo /* Re-run from eloop timeout */ 920214501Srpaulo eapol_auth_step(sm); 921214501Srpaulo return; 922214501Srpaulo } 923214501Srpaulo 924214501Srpaulo /* TODO: find a better location for this */ 925214501Srpaulo if (sm->eap_if->aaaEapResp) { 926214501Srpaulo sm->eap_if->aaaEapResp = FALSE; 927214501Srpaulo if (sm->eap_if->aaaEapRespData == NULL) { 928214501Srpaulo wpa_printf(MSG_DEBUG, "EAPOL: aaaEapResp set, " 929214501Srpaulo "but no aaaEapRespData available"); 930214501Srpaulo return; 931214501Srpaulo } 932214501Srpaulo sm->eapol->cb.aaa_send( 933214501Srpaulo sm->eapol->conf.ctx, sm->sta, 934214501Srpaulo wpabuf_head(sm->eap_if->aaaEapRespData), 935214501Srpaulo wpabuf_len(sm->eap_if->aaaEapRespData)); 936214501Srpaulo } 937214501Srpaulo } 938214501Srpaulo 939214501Srpaulo if (eapol_sm_sta_entry_alive(eapol, addr)) 940214501Srpaulo sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta, 941214501Srpaulo EAPOL_AUTH_SM_CHANGE); 942214501Srpaulo} 943214501Srpaulo 944214501Srpaulo 945214501Srpaulostatic void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx) 946214501Srpaulo{ 947214501Srpaulo struct eapol_state_machine *sm = eloop_ctx; 948214501Srpaulo eapol_sm_step_run(sm); 949214501Srpaulo} 950214501Srpaulo 951214501Srpaulo 952214501Srpaulo/** 953214501Srpaulo * eapol_auth_step - Advance EAPOL state machines 954214501Srpaulo * @sm: EAPOL state machine 955214501Srpaulo * 956214501Srpaulo * This function is called to advance EAPOL state machines after any change 957214501Srpaulo * that could affect their state. 958214501Srpaulo */ 959214501Srpaulovoid eapol_auth_step(struct eapol_state_machine *sm) 960214501Srpaulo{ 961214501Srpaulo /* 962214501Srpaulo * Run eapol_sm_step_run from a registered timeout to make sure that 963214501Srpaulo * other possible timeouts/events are processed and to avoid long 964214501Srpaulo * function call chains. 965214501Srpaulo */ 966214501Srpaulo 967214501Srpaulo eloop_register_timeout(0, 0, eapol_sm_step_cb, sm, NULL); 968214501Srpaulo} 969214501Srpaulo 970214501Srpaulo 971214501Srpaulostatic void eapol_auth_initialize(struct eapol_state_machine *sm) 972214501Srpaulo{ 973214501Srpaulo sm->initializing = TRUE; 974214501Srpaulo /* Initialize the state machines by asserting initialize and then 975214501Srpaulo * deasserting it after one step */ 976214501Srpaulo sm->initialize = TRUE; 977214501Srpaulo eapol_sm_step_run(sm); 978214501Srpaulo sm->initialize = FALSE; 979214501Srpaulo eapol_sm_step_run(sm); 980214501Srpaulo sm->initializing = FALSE; 981214501Srpaulo 982214501Srpaulo /* Start one second tick for port timers state machine */ 983214501Srpaulo eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); 984214501Srpaulo eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); 985214501Srpaulo} 986214501Srpaulo 987214501Srpaulo 988214501Srpaulostatic int eapol_sm_get_eap_user(void *ctx, const u8 *identity, 989214501Srpaulo size_t identity_len, int phase2, 990214501Srpaulo struct eap_user *user) 991214501Srpaulo{ 992214501Srpaulo struct eapol_state_machine *sm = ctx; 993214501Srpaulo return sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity, 994214501Srpaulo identity_len, phase2, user); 995214501Srpaulo} 996214501Srpaulo 997214501Srpaulo 998214501Srpaulostatic const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) 999214501Srpaulo{ 1000214501Srpaulo struct eapol_state_machine *sm = ctx; 1001214501Srpaulo *len = sm->eapol->conf.eap_req_id_text_len; 1002214501Srpaulo return sm->eapol->conf.eap_req_id_text; 1003214501Srpaulo} 1004214501Srpaulo 1005214501Srpaulo 1006214501Srpaulostatic struct eapol_callbacks eapol_cb = 1007214501Srpaulo{ 1008214501Srpaulo eapol_sm_get_eap_user, 1009214501Srpaulo eapol_sm_get_eap_req_id_text 1010214501Srpaulo}; 1011214501Srpaulo 1012214501Srpaulo 1013214501Srpauloint eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) 1014214501Srpaulo{ 1015214501Srpaulo if (sm == NULL || ctx != sm->eap) 1016214501Srpaulo return -1; 1017214501Srpaulo 1018214501Srpaulo eap_sm_pending_cb(sm->eap); 1019214501Srpaulo eapol_auth_step(sm); 1020214501Srpaulo 1021214501Srpaulo return 0; 1022214501Srpaulo} 1023214501Srpaulo 1024214501Srpaulo 1025214501Srpaulostatic int eapol_auth_conf_clone(struct eapol_auth_config *dst, 1026214501Srpaulo struct eapol_auth_config *src) 1027214501Srpaulo{ 1028214501Srpaulo dst->ctx = src->ctx; 1029214501Srpaulo dst->eap_reauth_period = src->eap_reauth_period; 1030214501Srpaulo dst->wpa = src->wpa; 1031214501Srpaulo dst->individual_wep_key_len = src->individual_wep_key_len; 1032214501Srpaulo dst->eap_server = src->eap_server; 1033214501Srpaulo dst->ssl_ctx = src->ssl_ctx; 1034214501Srpaulo dst->msg_ctx = src->msg_ctx; 1035214501Srpaulo dst->eap_sim_db_priv = src->eap_sim_db_priv; 1036214501Srpaulo os_free(dst->eap_req_id_text); 1037214501Srpaulo if (src->eap_req_id_text) { 1038214501Srpaulo dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len); 1039214501Srpaulo if (dst->eap_req_id_text == NULL) 1040214501Srpaulo return -1; 1041214501Srpaulo os_memcpy(dst->eap_req_id_text, src->eap_req_id_text, 1042214501Srpaulo src->eap_req_id_text_len); 1043214501Srpaulo dst->eap_req_id_text_len = src->eap_req_id_text_len; 1044214501Srpaulo } else { 1045214501Srpaulo dst->eap_req_id_text = NULL; 1046214501Srpaulo dst->eap_req_id_text_len = 0; 1047214501Srpaulo } 1048214501Srpaulo if (src->pac_opaque_encr_key) { 1049214501Srpaulo dst->pac_opaque_encr_key = os_malloc(16); 1050214501Srpaulo os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key, 1051214501Srpaulo 16); 1052214501Srpaulo } else 1053214501Srpaulo dst->pac_opaque_encr_key = NULL; 1054214501Srpaulo if (src->eap_fast_a_id) { 1055214501Srpaulo dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len); 1056214501Srpaulo if (dst->eap_fast_a_id == NULL) { 1057214501Srpaulo os_free(dst->eap_req_id_text); 1058214501Srpaulo return -1; 1059214501Srpaulo } 1060214501Srpaulo os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id, 1061214501Srpaulo src->eap_fast_a_id_len); 1062214501Srpaulo dst->eap_fast_a_id_len = src->eap_fast_a_id_len; 1063214501Srpaulo } else 1064214501Srpaulo dst->eap_fast_a_id = NULL; 1065214501Srpaulo if (src->eap_fast_a_id_info) { 1066214501Srpaulo dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info); 1067214501Srpaulo if (dst->eap_fast_a_id_info == NULL) { 1068214501Srpaulo os_free(dst->eap_req_id_text); 1069214501Srpaulo os_free(dst->eap_fast_a_id); 1070214501Srpaulo return -1; 1071214501Srpaulo } 1072214501Srpaulo } else 1073214501Srpaulo dst->eap_fast_a_id_info = NULL; 1074214501Srpaulo dst->eap_fast_prov = src->eap_fast_prov; 1075214501Srpaulo dst->pac_key_lifetime = src->pac_key_lifetime; 1076214501Srpaulo dst->pac_key_refresh_time = src->pac_key_refresh_time; 1077214501Srpaulo dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind; 1078214501Srpaulo dst->tnc = src->tnc; 1079214501Srpaulo dst->wps = src->wps; 1080214501Srpaulo return 0; 1081214501Srpaulo} 1082214501Srpaulo 1083214501Srpaulo 1084214501Srpaulostatic void eapol_auth_conf_free(struct eapol_auth_config *conf) 1085214501Srpaulo{ 1086214501Srpaulo os_free(conf->eap_req_id_text); 1087214501Srpaulo conf->eap_req_id_text = NULL; 1088214501Srpaulo os_free(conf->pac_opaque_encr_key); 1089214501Srpaulo conf->pac_opaque_encr_key = NULL; 1090214501Srpaulo os_free(conf->eap_fast_a_id); 1091214501Srpaulo conf->eap_fast_a_id = NULL; 1092214501Srpaulo os_free(conf->eap_fast_a_id_info); 1093214501Srpaulo conf->eap_fast_a_id_info = NULL; 1094214501Srpaulo} 1095214501Srpaulo 1096214501Srpaulo 1097214501Srpaulostruct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, 1098214501Srpaulo struct eapol_auth_cb *cb) 1099214501Srpaulo{ 1100214501Srpaulo struct eapol_authenticator *eapol; 1101214501Srpaulo 1102214501Srpaulo eapol = os_zalloc(sizeof(*eapol)); 1103214501Srpaulo if (eapol == NULL) 1104214501Srpaulo return NULL; 1105214501Srpaulo 1106214501Srpaulo if (eapol_auth_conf_clone(&eapol->conf, conf) < 0) { 1107214501Srpaulo os_free(eapol); 1108214501Srpaulo return NULL; 1109214501Srpaulo } 1110214501Srpaulo 1111214501Srpaulo if (conf->individual_wep_key_len > 0) { 1112214501Srpaulo /* use key0 in individual key and key1 in broadcast key */ 1113214501Srpaulo eapol->default_wep_key_idx = 1; 1114214501Srpaulo } 1115214501Srpaulo 1116214501Srpaulo eapol->cb.eapol_send = cb->eapol_send; 1117214501Srpaulo eapol->cb.aaa_send = cb->aaa_send; 1118214501Srpaulo eapol->cb.finished = cb->finished; 1119214501Srpaulo eapol->cb.get_eap_user = cb->get_eap_user; 1120214501Srpaulo eapol->cb.sta_entry_alive = cb->sta_entry_alive; 1121214501Srpaulo eapol->cb.logger = cb->logger; 1122214501Srpaulo eapol->cb.set_port_authorized = cb->set_port_authorized; 1123214501Srpaulo eapol->cb.abort_auth = cb->abort_auth; 1124214501Srpaulo eapol->cb.tx_key = cb->tx_key; 1125214501Srpaulo eapol->cb.eapol_event = cb->eapol_event; 1126214501Srpaulo 1127214501Srpaulo return eapol; 1128214501Srpaulo} 1129214501Srpaulo 1130214501Srpaulo 1131214501Srpaulovoid eapol_auth_deinit(struct eapol_authenticator *eapol) 1132214501Srpaulo{ 1133214501Srpaulo if (eapol == NULL) 1134214501Srpaulo return; 1135214501Srpaulo 1136214501Srpaulo eapol_auth_conf_free(&eapol->conf); 1137214501Srpaulo os_free(eapol->default_wep_key); 1138214501Srpaulo os_free(eapol); 1139214501Srpaulo} 1140