📄 eapol_sm.c
字号:
/* * hostapd / IEEE 802.1X Authenticator - EAPOL state machine * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */#include "includes.h"#include "hostapd.h"#include "ieee802_1x.h"#include "eapol_sm.h"#include "eloop.h"#include "wpa.h"#include "preauth.h"#include "sta_info.h"#include "eap.h"#include "state_machine.h"#define STATE_MACHINE_DATA struct eapol_state_machine#define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X"#define STATE_MACHINE_ADDR sm->addrstatic struct eapol_callbacks eapol_cb;/* EAPOL state machines are described in IEEE Std 802.1X-REV-d11, Chap. 8.2 */#define setPortAuthorized() \ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 1)#define setPortUnauthorized() \ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 0)/* procedures */#define txCannedFail() ieee802_1x_tx_canned_eap(sm->hapd, sm->sta, 0)#define txCannedSuccess() ieee802_1x_tx_canned_eap(sm->hapd, sm->sta, 1)#define txReq() ieee802_1x_tx_req(sm->hapd, sm->sta)#define sendRespToServer() ieee802_1x_send_resp_to_server(sm->hapd, sm->sta)#define abortAuth() ieee802_1x_abort_auth(sm->hapd, sm->sta)#define txKey() ieee802_1x_tx_key(sm->hapd, sm->sta)#define processKey() do { } while (0)static void eapol_sm_step_run(struct eapol_state_machine *sm);static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx);/* Port Timers state machine - implemented as a function that will be called * once a second as a registered event loop timeout */static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx){ struct eapol_state_machine *state = timeout_ctx; if (state->aWhile > 0) { state->aWhile--; if (state->aWhile == 0) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " - aWhile --> 0", MAC2STR(state->addr)); } } if (state->quietWhile > 0) { state->quietWhile--; if (state->quietWhile == 0) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " - quietWhile --> 0", MAC2STR(state->addr)); } } if (state->reAuthWhen > 0) { state->reAuthWhen--; if (state->reAuthWhen == 0) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " - reAuthWhen --> 0", MAC2STR(state->addr)); } } eapol_sm_step_run(state); eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state);}/* Authenticator PAE state machine */SM_STATE(AUTH_PAE, INITIALIZE){ SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae); sm->portMode = Auto; sm->currentId = 255;}SM_STATE(AUTH_PAE, DISCONNECTED){ int from_initialize = sm->auth_pae_state == AUTH_PAE_INITIALIZE; if (sm->eapolLogoff) { if (sm->auth_pae_state == AUTH_PAE_CONNECTING) sm->authEapLogoffsWhileConnecting++; else if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) sm->authAuthEapLogoffWhileAuthenticated++; } SM_ENTRY_MA(AUTH_PAE, DISCONNECTED, auth_pae); sm->authPortStatus = Unauthorized; setPortUnauthorized(); sm->reAuthCount = 0; sm->eapolLogoff = FALSE; if (!from_initialize) { if (sm->flags & EAPOL_SM_PREAUTH) rsn_preauth_finished(sm->hapd, sm->sta, 0); else ieee802_1x_finished(sm->hapd, sm->sta, 0); }}SM_STATE(AUTH_PAE, RESTART){ if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) { if (sm->reAuthenticate) sm->authAuthReauthsWhileAuthenticated++; if (sm->eapolStart) sm->authAuthEapStartsWhileAuthenticated++; if (sm->eapolLogoff) sm->authAuthEapLogoffWhileAuthenticated++; } SM_ENTRY_MA(AUTH_PAE, RESTART, auth_pae); sm->eapRestart = TRUE; ieee802_1x_request_identity(sm->hapd, sm->sta);}SM_STATE(AUTH_PAE, CONNECTING){ if (sm->auth_pae_state != AUTH_PAE_CONNECTING) sm->authEntersConnecting++; SM_ENTRY_MA(AUTH_PAE, CONNECTING, auth_pae); sm->reAuthenticate = FALSE; sm->reAuthCount++;}SM_STATE(AUTH_PAE, HELD){ if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authFail) sm->authAuthFailWhileAuthenticating++; SM_ENTRY_MA(AUTH_PAE, HELD, auth_pae); sm->authPortStatus = Unauthorized; setPortUnauthorized(); sm->quietWhile = sm->quietPeriod; sm->eapolLogoff = FALSE; hostapd_logger(sm->hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "authentication failed"); if (sm->flags & EAPOL_SM_PREAUTH) rsn_preauth_finished(sm->hapd, sm->sta, 0); else ieee802_1x_finished(sm->hapd, sm->sta, 0);}SM_STATE(AUTH_PAE, AUTHENTICATED){ if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess) sm->authAuthSuccessesWhileAuthenticating++; SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae); sm->authPortStatus = Authorized; setPortAuthorized(); sm->reAuthCount = 0; hostapd_logger(sm->hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_INFO, "authenticated"); if (sm->flags & EAPOL_SM_PREAUTH) rsn_preauth_finished(sm->hapd, sm->sta, 1); else ieee802_1x_finished(sm->hapd, sm->sta, 1);}SM_STATE(AUTH_PAE, AUTHENTICATING){ if (sm->auth_pae_state == AUTH_PAE_CONNECTING && sm->rx_identity) { sm->authEntersAuthenticating++; sm->rx_identity = FALSE; } SM_ENTRY_MA(AUTH_PAE, AUTHENTICATING, auth_pae); sm->eapolStart = FALSE; sm->authSuccess = FALSE; sm->authFail = FALSE; sm->authTimeout = FALSE; sm->authStart = TRUE; sm->keyRun = FALSE; sm->keyDone = FALSE;}SM_STATE(AUTH_PAE, ABORTING){ if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING) { if (sm->authTimeout) sm->authAuthTimeoutsWhileAuthenticating++; if (sm->eapolStart) sm->authAuthEapStartsWhileAuthenticating++; if (sm->eapolLogoff) sm->authAuthEapLogoffWhileAuthenticating++; } SM_ENTRY_MA(AUTH_PAE, ABORTING, auth_pae); sm->authAbort = TRUE; sm->keyRun = FALSE; sm->keyDone = FALSE;}SM_STATE(AUTH_PAE, FORCE_AUTH){ SM_ENTRY_MA(AUTH_PAE, FORCE_AUTH, auth_pae); sm->authPortStatus = Authorized; setPortAuthorized(); sm->portMode = ForceAuthorized; sm->eapolStart = FALSE; txCannedSuccess();}SM_STATE(AUTH_PAE, FORCE_UNAUTH){ SM_ENTRY_MA(AUTH_PAE, FORCE_UNAUTH, auth_pae); sm->authPortStatus = Unauthorized; setPortUnauthorized(); sm->portMode = ForceUnauthorized; sm->eapolStart = FALSE; txCannedFail();}SM_STEP(AUTH_PAE){ if ((sm->portControl == Auto && sm->portMode != sm->portControl) || sm->initialize || !sm->portEnabled) SM_ENTER(AUTH_PAE, INITIALIZE); else if (sm->portControl == ForceAuthorized && sm->portMode != sm->portControl && !(sm->initialize || !sm->portEnabled)) SM_ENTER(AUTH_PAE, FORCE_AUTH); else if (sm->portControl == ForceUnauthorized && sm->portMode != sm->portControl && !(sm->initialize || !sm->portEnabled)) SM_ENTER(AUTH_PAE, FORCE_UNAUTH); else { switch (sm->auth_pae_state) { case AUTH_PAE_INITIALIZE: SM_ENTER(AUTH_PAE, DISCONNECTED); break; case AUTH_PAE_DISCONNECTED: SM_ENTER(AUTH_PAE, RESTART); break; case AUTH_PAE_RESTART: if (!sm->eapRestart) SM_ENTER(AUTH_PAE, CONNECTING); break; case AUTH_PAE_HELD: if (sm->quietWhile == 0) SM_ENTER(AUTH_PAE, RESTART); break; case AUTH_PAE_CONNECTING: if (sm->eapolLogoff || sm->reAuthCount > sm->reAuthMax) SM_ENTER(AUTH_PAE, DISCONNECTED); else if ((sm->eapReq && sm->reAuthCount <= sm->reAuthMax) || sm->eapSuccess || sm->eapFail) SM_ENTER(AUTH_PAE, AUTHENTICATING); break; case AUTH_PAE_AUTHENTICATED: if (sm->eapolStart || sm->reAuthenticate) SM_ENTER(AUTH_PAE, RESTART); else if (sm->eapolLogoff || !sm->portValid) SM_ENTER(AUTH_PAE, DISCONNECTED); break; case AUTH_PAE_AUTHENTICATING: if (sm->authSuccess && sm->portValid) SM_ENTER(AUTH_PAE, AUTHENTICATED); else if (sm->authFail || (sm->keyDone && !sm->portValid)) SM_ENTER(AUTH_PAE, HELD); else if (sm->eapolStart || sm->eapolLogoff || sm->authTimeout) SM_ENTER(AUTH_PAE, ABORTING); break; case AUTH_PAE_ABORTING: if (sm->eapolLogoff && !sm->authAbort) SM_ENTER(AUTH_PAE, DISCONNECTED); else if (!sm->eapolLogoff && !sm->authAbort) SM_ENTER(AUTH_PAE, RESTART); break; case AUTH_PAE_FORCE_AUTH: if (sm->eapolStart) SM_ENTER(AUTH_PAE, FORCE_AUTH); break; case AUTH_PAE_FORCE_UNAUTH: if (sm->eapolStart) SM_ENTER(AUTH_PAE, FORCE_UNAUTH); break; } }}/* Backend Authentication state machine */SM_STATE(BE_AUTH, INITIALIZE){ SM_ENTRY_MA(BE_AUTH, INITIALIZE, be_auth); abortAuth(); sm->eapNoReq = FALSE; sm->authAbort = FALSE;}SM_STATE(BE_AUTH, REQUEST){ SM_ENTRY_MA(BE_AUTH, REQUEST, be_auth); txReq(); sm->eapReq = FALSE; sm->backendOtherRequestsToSupplicant++; /* * Clearing eapolEap here is not specified in IEEE Std 802.1X-2004, but * it looks like this would be logical thing to do there since the old * EAP response would not be valid anymore after the new EAP request * was sent out. * * A race condition has been reported, in which hostapd ended up * sending out EAP-Response/Identity as a response to the first * EAP-Request from the main EAP method. This can be avoided by * clearing eapolEap here. */ sm->eapolEap = FALSE;}SM_STATE(BE_AUTH, RESPONSE){ SM_ENTRY_MA(BE_AUTH, RESPONSE, be_auth); sm->authTimeout = FALSE; sm->eapolEap = FALSE; sm->eapNoReq = FALSE; sm->aWhile = sm->serverTimeout; sm->eapResp = TRUE; sendRespToServer(); sm->backendResponses++;}SM_STATE(BE_AUTH, SUCCESS){ SM_ENTRY_MA(BE_AUTH, SUCCESS, be_auth); txReq(); sm->authSuccess = TRUE; sm->keyRun = TRUE;}SM_STATE(BE_AUTH, FAIL){ SM_ENTRY_MA(BE_AUTH, FAIL, be_auth); /* Note: IEEE 802.1X-REV-d11 has unconditional txReq() here. * txCannelFail() is used as a workaround for the case where * authentication server does not include EAP-Message with * Access-Reject. */ if (sm->last_eap_radius == NULL) txCannedFail(); else txReq(); sm->authFail = TRUE;}SM_STATE(BE_AUTH, TIMEOUT){ SM_ENTRY_MA(BE_AUTH, TIMEOUT, be_auth); sm->authTimeout = TRUE;}SM_STATE(BE_AUTH, IDLE){ SM_ENTRY_MA(BE_AUTH, IDLE, be_auth); sm->authStart = FALSE;}SM_STATE(BE_AUTH, IGNORE){ SM_ENTRY_MA(BE_AUTH, IGNORE, be_auth); sm->eapNoReq = FALSE;}SM_STEP(BE_AUTH){ if (sm->portControl != Auto || sm->initialize || sm->authAbort) { SM_ENTER(BE_AUTH, INITIALIZE); return; } switch (sm->be_auth_state) { case BE_AUTH_INITIALIZE: SM_ENTER(BE_AUTH, IDLE); break; case BE_AUTH_REQUEST: if (sm->eapolEap) SM_ENTER(BE_AUTH, RESPONSE); else if (sm->eapReq) SM_ENTER(BE_AUTH, REQUEST); else if (sm->eapTimeout) SM_ENTER(BE_AUTH, TIMEOUT); break; case BE_AUTH_RESPONSE: if (sm->eapNoReq) SM_ENTER(BE_AUTH, IGNORE); if (sm->eapReq) { sm->backendAccessChallenges++; SM_ENTER(BE_AUTH, REQUEST); } else if (sm->aWhile == 0) SM_ENTER(BE_AUTH, TIMEOUT); else if (sm->eapFail) { sm->backendAuthFails++; SM_ENTER(BE_AUTH, FAIL); } else if (sm->eapSuccess) { sm->backendAuthSuccesses++; SM_ENTER(BE_AUTH, SUCCESS); } break; case BE_AUTH_SUCCESS: SM_ENTER(BE_AUTH, IDLE); break; case BE_AUTH_FAIL: SM_ENTER(BE_AUTH, IDLE); break; case BE_AUTH_TIMEOUT: SM_ENTER(BE_AUTH, IDLE); break; case BE_AUTH_IDLE: if (sm->eapFail && sm->authStart) SM_ENTER(BE_AUTH, FAIL); else if (sm->eapReq && sm->authStart) SM_ENTER(BE_AUTH, REQUEST); else if (sm->eapSuccess && sm->authStart) SM_ENTER(BE_AUTH, SUCCESS); break; case BE_AUTH_IGNORE: if (sm->eapolEap) SM_ENTER(BE_AUTH, RESPONSE); else if (sm->eapReq) SM_ENTER(BE_AUTH, REQUEST); else if (sm->eapTimeout) SM_ENTER(BE_AUTH, TIMEOUT); break; }}/* Reauthentication Timer state machine */SM_STATE(REAUTH_TIMER, INITIALIZE){ SM_ENTRY_MA(REAUTH_TIMER, INITIALIZE, reauth_timer); sm->reAuthWhen = sm->reAuthPeriod;}SM_STATE(REAUTH_TIMER, REAUTHENTICATE){ SM_ENTRY_MA(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); sm->reAuthenticate = TRUE; wpa_auth_sm_event(sm->sta->wpa_sm, WPA_REAUTH_EAPOL);}SM_STEP(REAUTH_TIMER){ if (sm->portControl != Auto || sm->initialize || sm->authPortStatus == Unauthorized || !sm->reAuthEnabled) { SM_ENTER(REAUTH_TIMER, INITIALIZE); return; } switch (sm->reauth_timer_state) { case REAUTH_TIMER_INITIALIZE: if (sm->reAuthWhen == 0) SM_ENTER(REAUTH_TIMER, REAUTHENTICATE); break; case REAUTH_TIMER_REAUTHENTICATE: SM_ENTER(REAUTH_TIMER, INITIALIZE); break; }}/* Authenticator Key Transmit state machine */SM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT){ SM_ENTRY_MA(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx);}SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT){ SM_ENTRY_MA(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx); txKey(); sm->keyAvailable = FALSE; sm->keyDone = TRUE;}SM_STEP(AUTH_KEY_TX){ if (sm->initialize || sm->portControl != Auto) { SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT); return; } switch (sm->auth_key_tx_state) { case AUTH_KEY_TX_NO_KEY_TRANSMIT: if (sm->keyTxEnabled && sm->keyAvailable && sm->keyRun && !wpa_auth_sta_wpa_version(sm->sta->wpa_sm)) SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); break; case AUTH_KEY_TX_KEY_TRANSMIT: if (!sm->keyTxEnabled || !sm->keyRun) SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT); else if (sm->keyAvailable) SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); break; }}/* Key Receive state machine */SM_STATE(KEY_RX, NO_KEY_RECEIVE){ SM_ENTRY_MA(KEY_RX, NO_KEY_RECEIVE, key_rx);}SM_STATE(KEY_RX, KEY_RECEIVE){ SM_ENTRY_MA(KEY_RX, KEY_RECEIVE, key_rx); processKey(); sm->rxKey = FALSE;}SM_STEP(KEY_RX){ if (sm->initialize || !sm->portEnabled) { SM_ENTER(KEY_RX, NO_KEY_RECEIVE); return; } switch (sm->key_rx_state) { case KEY_RX_NO_KEY_RECEIVE: if (sm->rxKey) SM_ENTER(KEY_RX, KEY_RECEIVE); break; case KEY_RX_KEY_RECEIVE: if (sm->rxKey) SM_ENTER(KEY_RX, KEY_RECEIVE); break; }}/* Controlled Directions state machine */SM_STATE(CTRL_DIR, FORCE_BOTH){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -