📄 eapol_sm.c
字号:
/* * Host AP (software wireless LAN access point) user space daemon for * Host AP kernel driver / 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 <stdlib.h>#include <stdio.h>#include <unistd.h>#include <netinet/in.h>#include <string.h>#include <sys/socket.h>#include "hostapd.h"#include "ieee802_1x.h"#include "eapol_sm.h"#include "eloop.h"#include "wpa.h"#include "sta_info.h"#include "eap.h"static 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)/* Definitions for clarifying state machine implementation */#define SM_STATE(machine, state) \static void sm_ ## machine ## _ ## state ## _Enter(struct eapol_state_machine \*sm)#define SM_ENTRY(machine, _state, _data) \sm->_data.state = machine ## _ ## _state; \if (sm->hapd->conf->debug >= HOSTAPD_DEBUG_MINIMAL) \ printf("IEEE 802.1X: " MACSTR " " #machine " entering state " #_state \ "\n", MAC2STR(sm->addr));#define SM_ENTER(machine, state) sm_ ## machine ## _ ## state ## _Enter(sm)#define SM_STEP(machine) \static void sm_ ## machine ## _Step(struct eapol_state_machine *sm)#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm)/* 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->quietWhile > 0) state->quietWhile--; if (state->reAuthWhen > 0) state->reAuthWhen--; if (state->hapd->conf->debug >= HOSTAPD_DEBUG_MSGDUMPS) printf("IEEE 802.1X: " MACSTR " Port Timers TICK " "(timers: %d %d %d)\n", MAC2STR(state->addr), state->aWhile, state->quietWhile, state->reAuthWhen); eapol_sm_step(state); eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state);}/* Authenticator PAE state machine */SM_STATE(AUTH_PAE, INITIALIZE){ SM_ENTRY(AUTH_PAE, INITIALIZE, auth_pae); sm->auth_pae.portMode = Auto; sm->currentId = 255;}SM_STATE(AUTH_PAE, DISCONNECTED){ int from_initialize = sm->auth_pae.state == AUTH_PAE_INITIALIZE; if (sm->auth_pae.eapolLogoff) { if (sm->auth_pae.state == AUTH_PAE_CONNECTING) sm->auth_pae.authEapLogoffsWhileConnecting++; else if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATED) sm->auth_pae.authAuthEapLogoffWhileAuthenticated++; } SM_ENTRY(AUTH_PAE, DISCONNECTED, auth_pae); sm->authPortStatus = Unauthorized; setPortUnauthorized(); sm->auth_pae.reAuthCount = 0; sm->auth_pae.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->auth_pae.authAuthReauthsWhileAuthenticated++; if (sm->auth_pae.eapolStart) sm->auth_pae.authAuthEapStartsWhileAuthenticated++; if (sm->auth_pae.eapolLogoff) sm->auth_pae.authAuthEapLogoffWhileAuthenticated++; } SM_ENTRY(AUTH_PAE, RESTART, auth_pae); sm->auth_pae.eapRestart = TRUE; ieee802_1x_request_identity(sm->hapd, sm->sta);}SM_STATE(AUTH_PAE, CONNECTING){ if (sm->auth_pae.state != AUTH_PAE_CONNECTING) sm->auth_pae.authEntersConnecting++; SM_ENTRY(AUTH_PAE, CONNECTING, auth_pae); sm->reAuthenticate = FALSE; sm->auth_pae.reAuthCount++;}SM_STATE(AUTH_PAE, HELD){ if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING && sm->authFail) sm->auth_pae.authAuthFailWhileAuthenticating++; SM_ENTRY(AUTH_PAE, HELD, auth_pae); sm->authPortStatus = Unauthorized; setPortUnauthorized(); sm->quietWhile = sm->auth_pae.quietPeriod; sm->auth_pae.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->auth_pae.authAuthSuccessesWhileAuthenticating++; SM_ENTRY(AUTH_PAE, AUTHENTICATED, auth_pae); sm->authPortStatus = Authorized; setPortAuthorized(); sm->auth_pae.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->auth_pae.authEntersAuthenticating++; sm->rx_identity = FALSE; } SM_ENTRY(AUTH_PAE, AUTHENTICATING, auth_pae); sm->auth_pae.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->auth_pae.authAuthTimeoutsWhileAuthenticating++; if (sm->auth_pae.eapolStart) sm->auth_pae.authAuthEapStartsWhileAuthenticating++; if (sm->auth_pae.eapolLogoff) sm->auth_pae.authAuthEapLogoffWhileAuthenticating++; } SM_ENTRY(AUTH_PAE, ABORTING, auth_pae); sm->authAbort = TRUE; sm->keyRun = FALSE; sm->keyDone = FALSE;}SM_STATE(AUTH_PAE, FORCE_AUTH){ SM_ENTRY(AUTH_PAE, FORCE_AUTH, auth_pae); sm->authPortStatus = Authorized; setPortAuthorized(); sm->auth_pae.portMode = ForceAuthorized; sm->auth_pae.eapolStart = FALSE; txCannedSuccess();}SM_STATE(AUTH_PAE, FORCE_UNAUTH){ SM_ENTRY(AUTH_PAE, FORCE_UNAUTH, auth_pae); sm->authPortStatus = Unauthorized; setPortUnauthorized(); sm->auth_pae.portMode = ForceUnauthorized; sm->auth_pae.eapolStart = FALSE; txCannedFail();}SM_STEP(AUTH_PAE){ if ((sm->portControl == Auto && sm->auth_pae.portMode != sm->portControl) || sm->initialize || !sm->portEnabled) SM_ENTER(AUTH_PAE, INITIALIZE); else if (sm->portControl == ForceAuthorized && sm->auth_pae.portMode != sm->portControl && !(sm->initialize || !sm->portEnabled)) SM_ENTER(AUTH_PAE, FORCE_AUTH); else if (sm->portControl == ForceUnauthorized && sm->auth_pae.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->auth_pae.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->auth_pae.eapolLogoff || sm->auth_pae.reAuthCount > sm->auth_pae.reAuthMax) SM_ENTER(AUTH_PAE, DISCONNECTED); else if ((sm->be_auth.eapReq && sm->auth_pae.reAuthCount <= sm->auth_pae.reAuthMax) || sm->eapSuccess || sm->eapFail) SM_ENTER(AUTH_PAE, AUTHENTICATING); break; case AUTH_PAE_AUTHENTICATED: if (sm->auth_pae.eapolStart || sm->reAuthenticate) SM_ENTER(AUTH_PAE, RESTART); else if (sm->auth_pae.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->auth_pae.eapolStart || sm->auth_pae.eapolLogoff || sm->authTimeout) SM_ENTER(AUTH_PAE, ABORTING); break; case AUTH_PAE_ABORTING: if (sm->auth_pae.eapolLogoff && !sm->authAbort) SM_ENTER(AUTH_PAE, DISCONNECTED); else if (!sm->auth_pae.eapolLogoff && !sm->authAbort) SM_ENTER(AUTH_PAE, RESTART); break; case AUTH_PAE_FORCE_AUTH: if (sm->auth_pae.eapolStart) SM_ENTER(AUTH_PAE, FORCE_AUTH); break; case AUTH_PAE_FORCE_UNAUTH: if (sm->auth_pae.eapolStart) SM_ENTER(AUTH_PAE, FORCE_UNAUTH); break; } }}/* Backend Authentication state machine */SM_STATE(BE_AUTH, INITIALIZE){ SM_ENTRY(BE_AUTH, INITIALIZE, be_auth); abortAuth(); sm->be_auth.eapNoReq = FALSE; sm->authAbort = FALSE;}SM_STATE(BE_AUTH, REQUEST){ SM_ENTRY(BE_AUTH, REQUEST, be_auth); txReq(); sm->be_auth.eapReq = FALSE; sm->be_auth.backendOtherRequestsToSupplicant++;}SM_STATE(BE_AUTH, RESPONSE){ SM_ENTRY(BE_AUTH, RESPONSE, be_auth); sm->authTimeout = FALSE; sm->eapolEap = FALSE; sm->be_auth.eapNoReq = FALSE; sm->aWhile = sm->be_auth.serverTimeout; sm->be_auth.eapResp = TRUE; sendRespToServer(); sm->be_auth.backendResponses++;}SM_STATE(BE_AUTH, SUCCESS){ SM_ENTRY(BE_AUTH, SUCCESS, be_auth); txReq(); sm->authSuccess = TRUE; sm->keyRun = TRUE;}SM_STATE(BE_AUTH, FAIL){ SM_ENTRY(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(BE_AUTH, TIMEOUT, be_auth); sm->authTimeout = TRUE;}SM_STATE(BE_AUTH, IDLE){ SM_ENTRY(BE_AUTH, IDLE, be_auth); sm->authStart = FALSE;}SM_STATE(BE_AUTH, IGNORE){ SM_ENTRY(BE_AUTH, IGNORE, be_auth); sm->be_auth.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->be_auth.eapReq) SM_ENTER(BE_AUTH, REQUEST); else if (sm->eapTimeout) SM_ENTER(BE_AUTH, TIMEOUT); break; case BE_AUTH_RESPONSE: if (sm->be_auth.eapNoReq) SM_ENTER(BE_AUTH, IGNORE); if (sm->be_auth.eapReq) { sm->be_auth.backendAccessChallenges++; SM_ENTER(BE_AUTH, REQUEST); } else if (sm->aWhile == 0) SM_ENTER(BE_AUTH, TIMEOUT); else if (sm->eapFail) { sm->be_auth.backendAuthFails++; SM_ENTER(BE_AUTH, FAIL); } else if (sm->eapSuccess) { sm->be_auth.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->be_auth.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->be_auth.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(REAUTH_TIMER, INITIALIZE, reauth_timer); sm->reAuthWhen = sm->reauth_timer.reAuthPeriod;}SM_STATE(REAUTH_TIMER, REAUTHENTICATE){ SM_ENTRY(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); sm->reAuthenticate = TRUE; wpa_sm_event(sm->hapd, sm->sta, WPA_REAUTH_EAPOL);}SM_STEP(REAUTH_TIMER){ if (sm->portControl != Auto || sm->initialize || sm->authPortStatus == Unauthorized || !sm->reauth_timer.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(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx);}SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT){ SM_ENTRY(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 && !sm->sta->wpa) 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(KEY_RX, NO_KEY_RECEIVE, key_rx);}SM_STATE(KEY_RX, KEY_RECEIVE){ SM_ENTRY(KEY_RX, KEY_RECEIVE, key_rx); processKey(); sm->key_rx.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->key_rx.rxKey)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -