📄 eap.c
字号:
/* * hostapd / EAP Standalone Authenticator state machine (RFC 4137) * Copyright (c) 2004-2006, 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 "sta_info.h"#include "eap_i.h"#include "state_machine.h"#define STATE_MACHINE_DATA struct eap_sm#define STATE_MACHINE_DEBUG_PREFIX "EAP"#define EAP_MAX_AUTH_ROUNDS 50static void eap_user_free(struct eap_user *user);/* EAP state machines are described in RFC 4137 */static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, int eapSRTT, int eapRTTVAR, int methodTimeout);static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len);static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len);static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len);static int eap_sm_nextId(struct eap_sm *sm, int id);static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len);static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor);static int eap_sm_Policy_getDecision(struct eap_sm *sm);static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method);static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var){ return sm->eapol_cb->get_bool(sm->eapol_ctx, var);}static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var, Boolean value){ sm->eapol_cb->set_bool(sm->eapol_ctx, var, value);}static void eapol_set_eapReqData(struct eap_sm *sm, const u8 *eapReqData, size_t eapReqDataLen){ wpa_hexdump(MSG_MSGDUMP, "EAP: eapReqData -> EAPOL", sm->eapReqData, sm->eapReqDataLen); sm->eapol_cb->set_eapReqData(sm->eapol_ctx, eapReqData, eapReqDataLen);}static void eapol_set_eapKeyData(struct eap_sm *sm, const u8 *eapKeyData, size_t eapKeyDataLen){ wpa_hexdump(MSG_MSGDUMP, "EAP: eapKeyData -> EAPOL", sm->eapKeyData, sm->eapKeyDataLen); sm->eapol_cb->set_eapKeyData(sm->eapol_ctx, eapKeyData, eapKeyDataLen);}/** * eap_user_get - Fetch user information from the database * @sm: Pointer to EAP state machine allocated with eap_sm_init() * @identity: Identity (User-Name) of the user * @identity_len: Length of identity in bytes * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user * Returns: 0 on success, or -1 on failure * * This function is used to fetch user information for EAP. The user will be * selected based on the specified identity. sm->user and * sm->user_eap_method_index are updated for the new user when a matching user * is found. sm->user can be used to get user information (e.g., password). */int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, int phase2){ struct eap_user *user; if (sm == NULL || sm->eapol_cb == NULL || sm->eapol_cb->get_eap_user == NULL) return -1; eap_user_free(sm->user); sm->user = NULL; user = wpa_zalloc(sizeof(*user)); if (user == NULL) return -1; if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity, identity_len, phase2, user) != 0) { eap_user_free(user); return -1; } sm->user = user; sm->user_eap_method_index = 0; return 0;}SM_STATE(EAP, DISABLED){ SM_ENTRY(EAP, DISABLED); sm->num_rounds = 0;}SM_STATE(EAP, INITIALIZE){ SM_ENTRY(EAP, INITIALIZE); sm->currentId = -1; eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); eapol_set_bool(sm, EAPOL_eapFail, FALSE); eapol_set_bool(sm, EAPOL_eapTimeout, FALSE); free(sm->eapKeyData); sm->eapKeyData = NULL; sm->eapKeyDataLen = 0; /* eapKeyAvailable = FALSE */ eapol_set_bool(sm, EAPOL_eapRestart, FALSE); /* * This is not defined in RFC 4137, but method state needs to be * reseted here so that it does not remain in success state when * re-authentication starts. */ if (sm->m && sm->eap_method_priv) { sm->m->reset(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; } sm->m = NULL; sm->user_eap_method_index = 0; if (sm->backend_auth) { sm->currentMethod = EAP_TYPE_NONE; /* parse rxResp, respId, respMethod */ eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen); if (sm->rxResp) { sm->currentId = sm->respId; } } sm->num_rounds = 0; sm->method_pending = METHOD_PENDING_NONE;}SM_STATE(EAP, PICK_UP_METHOD){ SM_ENTRY(EAP, PICK_UP_METHOD); if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) { sm->currentMethod = sm->respMethod; if (sm->m && sm->eap_method_priv) { sm->m->reset(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; } sm->m = eap_sm_get_eap_methods(EAP_VENDOR_IETF, sm->currentMethod); if (sm->m && sm->m->initPickUp) { sm->eap_method_priv = sm->m->initPickUp(sm); if (sm->eap_method_priv == NULL) { wpa_printf(MSG_DEBUG, "EAP: Failed to " "initialize EAP method %d", sm->currentMethod); sm->m = NULL; sm->currentMethod = EAP_TYPE_NONE; } } else { sm->m = NULL; sm->currentMethod = EAP_TYPE_NONE; } }}SM_STATE(EAP, IDLE){ SM_ENTRY(EAP, IDLE); sm->retransWhile = eap_sm_calculateTimeout(sm, sm->retransCount, sm->eapSRTT, sm->eapRTTVAR, sm->methodTimeout);}SM_STATE(EAP, RETRANSMIT){ SM_ENTRY(EAP, RETRANSMIT); /* TODO: Is this needed since EAPOL state machines take care of * retransmit? */}SM_STATE(EAP, RECEIVED){ SM_ENTRY(EAP, RECEIVED); /* parse rxResp, respId, respMethod */ eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen); sm->num_rounds++;}SM_STATE(EAP, DISCARD){ SM_ENTRY(EAP, DISCARD); eapol_set_bool(sm, EAPOL_eapResp, FALSE); eapol_set_bool(sm, EAPOL_eapNoReq, TRUE);}SM_STATE(EAP, SEND_REQUEST){ SM_ENTRY(EAP, SEND_REQUEST); sm->retransCount = 0; if (sm->eapReqData) { eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen); free(sm->lastReqData); sm->lastReqData = sm->eapReqData; sm->lastReqDataLen = sm->eapReqDataLen; sm->eapReqData = NULL; sm->eapReqDataLen = 0; eapol_set_bool(sm, EAPOL_eapResp, FALSE); eapol_set_bool(sm, EAPOL_eapReq, TRUE); } else { wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData"); eapol_set_bool(sm, EAPOL_eapResp, FALSE); eapol_set_bool(sm, EAPOL_eapReq, FALSE); eapol_set_bool(sm, EAPOL_eapNoReq, TRUE); }}SM_STATE(EAP, INTEGRITY_CHECK){ SM_ENTRY(EAP, INTEGRITY_CHECK); if (sm->m->check) { sm->ignore = sm->m->check(sm, sm->eap_method_priv, sm->eapRespData, sm->eapRespDataLen); }}SM_STATE(EAP, METHOD_REQUEST){ SM_ENTRY(EAP, METHOD_REQUEST); if (sm->m == NULL) { wpa_printf(MSG_DEBUG, "EAP: method not initialized"); return; } sm->currentId = eap_sm_nextId(sm, sm->currentId); wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d", sm->currentId); sm->lastId = sm->currentId; free(sm->eapReqData); sm->eapReqData = sm->m->buildReq(sm, sm->eap_method_priv, sm->currentId, &sm->eapReqDataLen); if (sm->m->getTimeout) sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv); else sm->methodTimeout = 0;}SM_STATE(EAP, METHOD_RESPONSE){ SM_ENTRY(EAP, METHOD_RESPONSE); sm->m->process(sm, sm->eap_method_priv, sm->eapRespData, sm->eapRespDataLen); if (sm->m->isDone(sm, sm->eap_method_priv)) { eap_sm_Policy_update(sm, NULL, 0); free(sm->eapKeyData); if (sm->m->getKey) { sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, &sm->eapKeyDataLen); } else { sm->eapKeyData = NULL; sm->eapKeyDataLen = 0; } sm->methodState = METHOD_END; } else { sm->methodState = METHOD_CONTINUE; }}SM_STATE(EAP, PROPOSE_METHOD){ int vendor; EapType type; SM_ENTRY(EAP, PROPOSE_METHOD); type = eap_sm_Policy_getNextMethod(sm, &vendor); if (vendor == EAP_VENDOR_IETF) sm->currentMethod = type; else sm->currentMethod = EAP_TYPE_EXPANDED; if (sm->m && sm->eap_method_priv) { sm->m->reset(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; } sm->m = eap_sm_get_eap_methods(vendor, type); if (sm->m) { sm->eap_method_priv = sm->m->init(sm); if (sm->eap_method_priv == NULL) { wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP " "method %d", sm->currentMethod); sm->m = NULL; sm->currentMethod = EAP_TYPE_NONE; } } if (sm->currentMethod == EAP_TYPE_IDENTITY || sm->currentMethod == EAP_TYPE_NOTIFICATION) sm->methodState = METHOD_CONTINUE; else sm->methodState = METHOD_PROPOSED;}SM_STATE(EAP, NAK){ struct eap_hdr *nak; size_t len = 0; u8 *pos, *nak_list = NULL; SM_ENTRY(EAP, NAK); if (sm->eap_method_priv) { sm->m->reset(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; } sm->m = NULL; nak = (struct eap_hdr *) sm->eapRespData; if (nak && sm->eapRespDataLen > sizeof(*nak)) { len = ntohs(nak->length); if (len > sm->eapRespDataLen) len = sm->eapRespDataLen; pos = (u8 *) (nak + 1); len -= sizeof(*nak); if (*pos == EAP_TYPE_NAK) { pos++; len--; nak_list = pos; } } eap_sm_Policy_update(sm, nak_list, len);}SM_STATE(EAP, SELECT_ACTION){ SM_ENTRY(EAP, SELECT_ACTION); sm->decision = eap_sm_Policy_getDecision(sm);}SM_STATE(EAP, TIMEOUT_FAILURE){ SM_ENTRY(EAP, TIMEOUT_FAILURE); eapol_set_bool(sm, EAPOL_eapTimeout, TRUE);}SM_STATE(EAP, FAILURE){ SM_ENTRY(EAP, FAILURE); free(sm->eapReqData); sm->eapReqData = eap_sm_buildFailure(sm, sm->currentId, &sm->eapReqDataLen); if (sm->eapReqData) { eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen); free(sm->eapReqData); sm->eapReqData = NULL; sm->eapReqDataLen = 0; } free(sm->lastReqData); sm->lastReqData = NULL; sm->lastReqDataLen = 0; eapol_set_bool(sm, EAPOL_eapFail, TRUE);}SM_STATE(EAP, SUCCESS){ SM_ENTRY(EAP, SUCCESS); free(sm->eapReqData); sm->eapReqData = eap_sm_buildSuccess(sm, sm->currentId, &sm->eapReqDataLen); if (sm->eapReqData) { eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen); free(sm->eapReqData); sm->eapReqData = NULL; sm->eapReqDataLen = 0; } free(sm->lastReqData); sm->lastReqData = NULL; sm->lastReqDataLen = 0; if (sm->eapKeyData) { eapol_set_eapKeyData(sm, sm->eapKeyData, sm->eapKeyDataLen); } eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);}SM_STEP(EAP){ if (eapol_get_bool(sm, EAPOL_eapRestart) && eapol_get_bool(sm, EAPOL_portEnabled)) SM_ENTER_GLOBAL(EAP, INITIALIZE); else if (!eapol_get_bool(sm, EAPOL_portEnabled)) SM_ENTER_GLOBAL(EAP, DISABLED); else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { wpa_printf(MSG_DEBUG, "EAP: more than %d " "authentication rounds - abort", EAP_MAX_AUTH_ROUNDS); sm->num_rounds++; SM_ENTER_GLOBAL(EAP, FAILURE); } } else switch (sm->EAP_state) { case EAP_INITIALIZE: if (sm->backend_auth) { if (!sm->rxResp) SM_ENTER(EAP, SELECT_ACTION); else if (sm->rxResp && (sm->respMethod == EAP_TYPE_NAK || (sm->respMethod == EAP_TYPE_EXPANDED && sm->respVendor == EAP_VENDOR_IETF && sm->respVendorMethod == EAP_TYPE_NAK))) SM_ENTER(EAP, NAK); else SM_ENTER(EAP, PICK_UP_METHOD); } else { SM_ENTER(EAP, SELECT_ACTION); } break; case EAP_PICK_UP_METHOD: if (sm->currentMethod == EAP_TYPE_NONE) { SM_ENTER(EAP, SELECT_ACTION); } else { SM_ENTER(EAP, METHOD_RESPONSE); } break; case EAP_DISABLED: if (eapol_get_bool(sm, EAPOL_portEnabled)) SM_ENTER(EAP, INITIALIZE); break; case EAP_IDLE: if (sm->retransWhile == 0) SM_ENTER(EAP, RETRANSMIT); else if (eapol_get_bool(sm, EAPOL_eapResp)) SM_ENTER(EAP, RECEIVED); break; case EAP_RETRANSMIT: if (sm->retransCount > sm->MaxRetrans) SM_ENTER(EAP, TIMEOUT_FAILURE); else SM_ENTER(EAP, IDLE); break; case EAP_RECEIVED: if (sm->rxResp && (sm->respId == sm->currentId) && (sm->respMethod == EAP_TYPE_NAK || (sm->respMethod == EAP_TYPE_EXPANDED && sm->respVendor == EAP_VENDOR_IETF && sm->respVendorMethod == EAP_TYPE_NAK)) && (sm->methodState == METHOD_PROPOSED)) SM_ENTER(EAP, NAK); else if (sm->rxResp && (sm->respId == sm->currentId) && ((sm->respMethod == sm->currentMethod) || (sm->respMethod == EAP_TYPE_EXPANDED && sm->respVendor == EAP_VENDOR_IETF && sm->respVendorMethod == sm->currentMethod))) SM_ENTER(EAP, INTEGRITY_CHECK); else { wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: " "rxResp=%d respId=%d currentId=%d " "respMethod=%d currentMethod=%d", sm->rxResp, sm->respId, sm->currentId, sm->respMethod, sm->currentMethod); SM_ENTER(EAP, DISCARD); } break; case EAP_DISCARD: SM_ENTER(EAP, IDLE); break; case EAP_SEND_REQUEST: SM_ENTER(EAP, IDLE); break; case EAP_INTEGRITY_CHECK: if (sm->ignore) SM_ENTER(EAP, DISCARD); else SM_ENTER(EAP, METHOD_RESPONSE); break; case EAP_METHOD_REQUEST: SM_ENTER(EAP, SEND_REQUEST); break; case EAP_METHOD_RESPONSE: /* * Note: Mechanism to allow EAP methods to wait while going * through pending processing is an extension to RFC 4137 * which only defines the transits to SELECT_ACTION and * METHOD_REQUEST from this METHOD_RESPONSE state. */ if (sm->methodState == METHOD_END) SM_ENTER(EAP, SELECT_ACTION); else if (sm->method_pending == METHOD_PENDING_WAIT) { wpa_printf(MSG_DEBUG, "EAP: Method has pending " "processing - wait before proceeding to " "METHOD_REQUEST state"); } else if (sm->method_pending == METHOD_PENDING_CONT) { wpa_printf(MSG_DEBUG, "EAP: Method has completed " "pending processing - reprocess pending " "EAP message"); sm->method_pending = METHOD_PENDING_NONE; SM_ENTER(EAP, METHOD_RESPONSE); } else SM_ENTER(EAP, METHOD_REQUEST); break; case EAP_PROPOSE_METHOD: /* * Note: Mechanism to allow EAP methods to wait while going * through pending processing is an extension to RFC 4137 * which only defines the transit to METHOD_REQUEST from this * PROPOSE_METHOD state. */ if (sm->method_pending == METHOD_PENDING_WAIT) { wpa_printf(MSG_DEBUG, "EAP: Method has pending " "processing - wait before proceeding to " "METHOD_REQUEST state"); if (sm->user_eap_method_index > 0) sm->user_eap_method_index--; } else if (sm->method_pending == METHOD_PENDING_CONT) { wpa_printf(MSG_DEBUG, "EAP: Method has completed " "pending processing - reprocess pending " "EAP message"); sm->method_pending = METHOD_PENDING_NONE; SM_ENTER(EAP, PROPOSE_METHOD); } else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -