📄 eapol_supp_sm.c
字号:
/* * EAPOL supplicant state machines * Copyright (c) 2004-2008, Jouni Malinen <j@w1.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 "common.h"#include "eapol_supp_sm.h"#include "eap_peer/eap.h"#include "eloop.h"#include "eapol_common.h"#include "md5.h"#include "rc4.h"#include "state_machine.h"#include "wpabuf.h"#define STATE_MACHINE_DATA struct eapol_sm#define STATE_MACHINE_DEBUG_PREFIX "EAPOL"/* IEEE 802.1X-2004 - Supplicant - EAPOL state machines *//** * struct eapol_sm - Internal data for EAPOL state machines */struct eapol_sm { /* Timers */ unsigned int authWhile; unsigned int heldWhile; unsigned int startWhen; unsigned int idleWhile; /* for EAP state machine */ int timer_tick_enabled; /* Global variables */ Boolean eapFail; Boolean eapolEap; Boolean eapSuccess; Boolean initialize; Boolean keyDone; Boolean keyRun; PortControl portControl; Boolean portEnabled; PortStatus suppPortStatus; /* dot1xSuppControlledPortStatus */ Boolean portValid; Boolean suppAbort; Boolean suppFail; Boolean suppStart; Boolean suppSuccess; Boolean suppTimeout; /* Supplicant PAE state machine */ enum { SUPP_PAE_UNKNOWN = 0, SUPP_PAE_DISCONNECTED = 1, SUPP_PAE_LOGOFF = 2, SUPP_PAE_CONNECTING = 3, SUPP_PAE_AUTHENTICATING = 4, SUPP_PAE_AUTHENTICATED = 5, /* unused(6) */ SUPP_PAE_HELD = 7, SUPP_PAE_RESTART = 8, SUPP_PAE_S_FORCE_AUTH = 9, SUPP_PAE_S_FORCE_UNAUTH = 10 } SUPP_PAE_state; /* dot1xSuppPaeState */ /* Variables */ Boolean userLogoff; Boolean logoffSent; unsigned int startCount; Boolean eapRestart; PortControl sPortMode; /* Constants */ unsigned int heldPeriod; /* dot1xSuppHeldPeriod */ unsigned int startPeriod; /* dot1xSuppStartPeriod */ unsigned int maxStart; /* dot1xSuppMaxStart */ /* Key Receive state machine */ enum { KEY_RX_UNKNOWN = 0, KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE } KEY_RX_state; /* Variables */ Boolean rxKey; /* Supplicant Backend state machine */ enum { SUPP_BE_UNKNOWN = 0, SUPP_BE_INITIALIZE = 1, SUPP_BE_IDLE = 2, SUPP_BE_REQUEST = 3, SUPP_BE_RECEIVE = 4, SUPP_BE_RESPONSE = 5, SUPP_BE_FAIL = 6, SUPP_BE_TIMEOUT = 7, SUPP_BE_SUCCESS = 8 } SUPP_BE_state; /* dot1xSuppBackendPaeState */ /* Variables */ Boolean eapNoResp; Boolean eapReq; Boolean eapResp; /* Constants */ unsigned int authPeriod; /* dot1xSuppAuthPeriod */ /* Statistics */ unsigned int dot1xSuppEapolFramesRx; unsigned int dot1xSuppEapolFramesTx; unsigned int dot1xSuppEapolStartFramesTx; unsigned int dot1xSuppEapolLogoffFramesTx; unsigned int dot1xSuppEapolRespFramesTx; unsigned int dot1xSuppEapolReqIdFramesRx; unsigned int dot1xSuppEapolReqFramesRx; unsigned int dot1xSuppInvalidEapolFramesRx; unsigned int dot1xSuppEapLengthErrorFramesRx; unsigned int dot1xSuppLastEapolFrameVersion; unsigned char dot1xSuppLastEapolFrameSource[6]; /* Miscellaneous variables (not defined in IEEE 802.1X-2004) */ Boolean changed; struct eap_sm *eap; struct eap_peer_config *config; Boolean initial_req; u8 *last_rx_key; size_t last_rx_key_len; struct wpabuf *eapReqData; /* for EAP */ Boolean altAccept; /* for EAP */ Boolean altReject; /* for EAP */ Boolean replay_counter_valid; u8 last_replay_counter[16]; struct eapol_config conf; struct eapol_ctx *ctx; enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE } cb_status; Boolean cached_pmk; Boolean unicast_key_received, broadcast_key_received;};#define IEEE8021X_REPLAY_COUNTER_LEN 8#define IEEE8021X_KEY_SIGN_LEN 16#define IEEE8021X_KEY_IV_LEN 16#define IEEE8021X_KEY_INDEX_FLAG 0x80#define IEEE8021X_KEY_INDEX_MASK 0x03#ifdef _MSC_VER#pragma pack(push, 1)#endif /* _MSC_VER */struct ieee802_1x_eapol_key { u8 type; /* Note: key_length is unaligned */ u8 key_length[2]; /* does not repeat within the life of the keying material used to * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ u8 key_index; /* key flag in the most significant bit: * 0 = broadcast (default key), * 1 = unicast (key mapping key); key index is in the * 7 least significant bits */ /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as * the key */ u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; /* followed by key: if packet body length = 44 + key length, then the * key field (of key_length bytes) contains the key in encrypted form; * if packet body length = 44, key field is absent and key_length * represents the number of least significant octets from * MS-MPPE-Send-Key attribute to be used as the keying material; * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */} STRUCT_PACKED;#ifdef _MSC_VER#pragma pack(pop)#endif /* _MSC_VER */static void eapol_sm_txLogoff(struct eapol_sm *sm);static void eapol_sm_txStart(struct eapol_sm *sm);static void eapol_sm_processKey(struct eapol_sm *sm);static void eapol_sm_getSuppRsp(struct eapol_sm *sm);static void eapol_sm_txSuppRsp(struct eapol_sm *sm);static void eapol_sm_abortSupp(struct eapol_sm *sm);static void eapol_sm_abort_cached(struct eapol_sm *sm);static void eapol_sm_step_timeout(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_sm *sm = timeout_ctx; if (sm->authWhile > 0) { sm->authWhile--; if (sm->authWhile == 0) wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0"); } if (sm->heldWhile > 0) { sm->heldWhile--; if (sm->heldWhile == 0) wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0"); } if (sm->startWhen > 0) { sm->startWhen--; if (sm->startWhen == 0) wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0"); } if (sm->idleWhile > 0) { sm->idleWhile--; if (sm->idleWhile == 0) wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0"); } if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) { eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, sm); } else { wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick"); sm->timer_tick_enabled = 0; } eapol_sm_step(sm);}static void eapol_enable_timer_tick(struct eapol_sm *sm){ if (sm->timer_tick_enabled) return; wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick"); sm->timer_tick_enabled = 1; eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);}SM_STATE(SUPP_PAE, LOGOFF){ SM_ENTRY(SUPP_PAE, LOGOFF); eapol_sm_txLogoff(sm); sm->logoffSent = TRUE; sm->suppPortStatus = Unauthorized;}SM_STATE(SUPP_PAE, DISCONNECTED){ SM_ENTRY(SUPP_PAE, DISCONNECTED); sm->sPortMode = Auto; sm->startCount = 0; sm->logoffSent = FALSE; sm->suppPortStatus = Unauthorized; sm->suppAbort = TRUE; sm->unicast_key_received = FALSE; sm->broadcast_key_received = FALSE;}SM_STATE(SUPP_PAE, CONNECTING){ int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING; SM_ENTRY(SUPP_PAE, CONNECTING); if (send_start) { sm->startWhen = sm->startPeriod; sm->startCount++; } else { /* * Do not send EAPOL-Start immediately since in most cases, * Authenticator is going to start authentication immediately * after association and an extra EAPOL-Start is just going to * delay authentication. Use a short timeout to send the first * EAPOL-Start if Authenticator does not start authentication. */ sm->startWhen = 3; } eapol_enable_timer_tick(sm); sm->eapolEap = FALSE; if (send_start) eapol_sm_txStart(sm);}SM_STATE(SUPP_PAE, AUTHENTICATING){ SM_ENTRY(SUPP_PAE, AUTHENTICATING); sm->startCount = 0; sm->suppSuccess = FALSE; sm->suppFail = FALSE; sm->suppTimeout = FALSE; sm->keyRun = FALSE; sm->keyDone = FALSE; sm->suppStart = TRUE;}SM_STATE(SUPP_PAE, HELD){ SM_ENTRY(SUPP_PAE, HELD); sm->heldWhile = sm->heldPeriod; eapol_enable_timer_tick(sm); sm->suppPortStatus = Unauthorized; sm->cb_status = EAPOL_CB_FAILURE;}SM_STATE(SUPP_PAE, AUTHENTICATED){ SM_ENTRY(SUPP_PAE, AUTHENTICATED); sm->suppPortStatus = Authorized; sm->cb_status = EAPOL_CB_SUCCESS;}SM_STATE(SUPP_PAE, RESTART){ SM_ENTRY(SUPP_PAE, RESTART); sm->eapRestart = TRUE;}SM_STATE(SUPP_PAE, S_FORCE_AUTH){ SM_ENTRY(SUPP_PAE, S_FORCE_AUTH); sm->suppPortStatus = Authorized; sm->sPortMode = ForceAuthorized;}SM_STATE(SUPP_PAE, S_FORCE_UNAUTH){ SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH); sm->suppPortStatus = Unauthorized; sm->sPortMode = ForceUnauthorized; eapol_sm_txLogoff(sm);}SM_STEP(SUPP_PAE){ if ((sm->userLogoff && !sm->logoffSent) && !(sm->initialize || !sm->portEnabled)) SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF); else if (((sm->portControl == Auto) && (sm->sPortMode != sm->portControl)) || sm->initialize || !sm->portEnabled) SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED); else if ((sm->portControl == ForceAuthorized) && (sm->sPortMode != sm->portControl) && !(sm->initialize || !sm->portEnabled)) SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH); else if ((sm->portControl == ForceUnauthorized) && (sm->sPortMode != sm->portControl) && !(sm->initialize || !sm->portEnabled)) SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH); else switch (sm->SUPP_PAE_state) { case SUPP_PAE_UNKNOWN: break; case SUPP_PAE_LOGOFF: if (!sm->userLogoff) SM_ENTER(SUPP_PAE, DISCONNECTED); break; case SUPP_PAE_DISCONNECTED: SM_ENTER(SUPP_PAE, CONNECTING); break; case SUPP_PAE_CONNECTING: if (sm->startWhen == 0 && sm->startCount < sm->maxStart) SM_ENTER(SUPP_PAE, CONNECTING); else if (sm->startWhen == 0 && sm->startCount >= sm->maxStart && sm->portValid) SM_ENTER(SUPP_PAE, AUTHENTICATED); else if (sm->eapSuccess || sm->eapFail) SM_ENTER(SUPP_PAE, AUTHENTICATING); else if (sm->eapolEap) SM_ENTER(SUPP_PAE, RESTART); else if (sm->startWhen == 0 && sm->startCount >= sm->maxStart && !sm->portValid) SM_ENTER(SUPP_PAE, HELD); break; case SUPP_PAE_AUTHENTICATING: if (sm->eapSuccess && !sm->portValid && sm->conf.accept_802_1x_keys && sm->conf.required_keys == 0) { wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for " "plaintext connection; no EAPOL-Key frames " "required"); sm->portValid = TRUE; if (sm->ctx->eapol_done_cb) sm->ctx->eapol_done_cb(sm->ctx->ctx); } if (sm->eapSuccess && sm->portValid) SM_ENTER(SUPP_PAE, AUTHENTICATED); else if (sm->eapFail || (sm->keyDone && !sm->portValid)) SM_ENTER(SUPP_PAE, HELD); else if (sm->suppTimeout) SM_ENTER(SUPP_PAE, CONNECTING); break; case SUPP_PAE_HELD: if (sm->heldWhile == 0) SM_ENTER(SUPP_PAE, CONNECTING); else if (sm->eapolEap) SM_ENTER(SUPP_PAE, RESTART); break; case SUPP_PAE_AUTHENTICATED: if (sm->eapolEap && sm->portValid) SM_ENTER(SUPP_PAE, RESTART); else if (!sm->portValid) SM_ENTER(SUPP_PAE, DISCONNECTED); break; case SUPP_PAE_RESTART: if (!sm->eapRestart) SM_ENTER(SUPP_PAE, AUTHENTICATING); break; case SUPP_PAE_S_FORCE_AUTH: break; case SUPP_PAE_S_FORCE_UNAUTH: break; }}SM_STATE(KEY_RX, NO_KEY_RECEIVE){ SM_ENTRY(KEY_RX, NO_KEY_RECEIVE);}SM_STATE(KEY_RX, KEY_RECEIVE){ SM_ENTRY(KEY_RX, KEY_RECEIVE); eapol_sm_processKey(sm); sm->rxKey = FALSE;}SM_STEP(KEY_RX){ if (sm->initialize || !sm->portEnabled) SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); switch (sm->KEY_RX_state) { case KEY_RX_UNKNOWN: break; 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -