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