📄 eap.c
字号:
* Handles requests for Identity method and builds a response. */SM_STATE(EAP, IDENTITY){ const u8 *eapReqData; size_t eapReqDataLen; SM_ENTRY(EAP, IDENTITY); eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen); eap_sm_processIdentity(sm, eapReqData, eapReqDataLen); free(sm->eapRespData); sm->eapRespData = NULL; sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId, &sm->eapRespDataLen, 0);}/* * Handles requests for Notification method and builds a response. */SM_STATE(EAP, NOTIFICATION){ const u8 *eapReqData; size_t eapReqDataLen; SM_ENTRY(EAP, NOTIFICATION); eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen); eap_sm_processNotify(sm, eapReqData, eapReqDataLen); free(sm->eapRespData); sm->eapRespData = NULL; sm->eapRespData = eap_sm_buildNotify(sm, sm->reqId, &sm->eapRespDataLen);}/* * This state retransmits the previous response packet. */SM_STATE(EAP, RETRANSMIT){ SM_ENTRY(EAP, RETRANSMIT); free(sm->eapRespData); if (sm->lastRespData) { sm->eapRespData = malloc(sm->lastRespDataLen); if (sm->eapRespData) { memcpy(sm->eapRespData, sm->lastRespData, sm->lastRespDataLen); sm->eapRespDataLen = sm->lastRespDataLen; } } else sm->eapRespData = NULL;}/* * This state is entered in case of a successful completion of authentication * and state machine waits here until port is disabled or EAP authentication is * restarted. */SM_STATE(EAP, SUCCESS){ SM_ENTRY(EAP, SUCCESS); if (sm->eapKeyData != NULL) sm->eapKeyAvailable = TRUE; eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); /* * RFC 4137 does not clear eapReq here, but this seems to be required * to avoid processing the same request twice when state machine is * initialized. */ eapol_set_bool(sm, EAPOL_eapReq, FALSE); /* * RFC 4137 does not set eapNoResp here, but this seems to be required * to get EAPOL Supplicant backend state machine into SUCCESS state. In * addition, either eapResp or eapNoResp is required to be set after * processing the received EAP frame. */ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS "EAP authentication completed successfully");}/* * This state is entered in case of a failure and state machine waits here * until port is disabled or EAP authentication is restarted. */SM_STATE(EAP, FAILURE){ SM_ENTRY(EAP, FAILURE); eapol_set_bool(sm, EAPOL_eapFail, TRUE); /* * RFC 4137 does not clear eapReq here, but this seems to be required * to avoid processing the same request twice when state machine is * initialized. */ eapol_set_bool(sm, EAPOL_eapReq, FALSE); /* * RFC 4137 does not set eapNoResp here. However, either eapResp or * eapNoResp is required to be set after processing the received EAP * frame. */ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE "EAP authentication failed");}static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId){ /* * At least Microsoft IAS and Meetinghouse Aegis seem to be sending * EAP-Success/Failure with lastId + 1 even though RFC 3748 and * RFC 4137 require that reqId == lastId. In addition, it looks like * Ringmaster v2.1.2.0 would be using lastId + 2 in EAP-Success. * * Accept this kind of Id if EAP workarounds are enabled. These are * unauthenticated plaintext messages, so this should have minimal * security implications (bit easier to fake EAP-Success/Failure). */ if (sm->workaround && (reqId == ((lastId + 1) & 0xff) || reqId == ((lastId + 2) & 0xff))) { wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected " "identifier field in EAP Success: " "reqId=%d lastId=%d (these are supposed to be " "same)", reqId, lastId); return 1; } wpa_printf(MSG_DEBUG, "EAP: EAP-Success Id mismatch - reqId=%d " "lastId=%d", reqId, lastId); return 0;}/* * RFC 4137 - Appendix A.1: EAP Peer State Machine - State transitions */SM_STEP(EAP){ int duplicate; 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->force_disabled) SM_ENTER_GLOBAL(EAP, DISABLED); else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { /* RFC 4137 does not place any limit on number of EAP messages * in an authentication session. However, some error cases have * ended up in a state were EAP messages were sent between the * peer and server in a loop (e.g., TLS ACK frame in both * direction). Since this is quite undesired outcome, limit the * total number of EAP round-trips and abort authentication if * this limit is exceeded. */ if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { wpa_msg(sm->msg_ctx, MSG_INFO, "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: SM_ENTER(EAP, IDLE); break; case EAP_DISABLED: if (eapol_get_bool(sm, EAPOL_portEnabled) && !sm->force_disabled) SM_ENTER(EAP, INITIALIZE); break; case EAP_IDLE: /* * The first three transitions are from RFC 4137. The last two * are local additions to handle special cases with LEAP and * PEAP server not sending EAP-Success in some cases. */ if (eapol_get_bool(sm, EAPOL_eapReq)) SM_ENTER(EAP, RECEIVED); else if ((eapol_get_bool(sm, EAPOL_altAccept) && sm->decision != DECISION_FAIL) || (eapol_get_int(sm, EAPOL_idleWhile) == 0 && sm->decision == DECISION_UNCOND_SUCC)) SM_ENTER(EAP, SUCCESS); else if (eapol_get_bool(sm, EAPOL_altReject) || (eapol_get_int(sm, EAPOL_idleWhile) == 0 && sm->decision != DECISION_UNCOND_SUCC) || (eapol_get_bool(sm, EAPOL_altAccept) && sm->methodState != METHOD_CONT && sm->decision == DECISION_FAIL)) SM_ENTER(EAP, FAILURE); else if (sm->selectedMethod == EAP_TYPE_LEAP && sm->leap_done && sm->decision != DECISION_FAIL && sm->methodState == METHOD_DONE) SM_ENTER(EAP, SUCCESS); else if (sm->selectedMethod == EAP_TYPE_PEAP && sm->peap_done && sm->decision != DECISION_FAIL && sm->methodState == METHOD_DONE) SM_ENTER(EAP, SUCCESS); break; case EAP_RECEIVED: duplicate = (sm->reqId == sm->lastId) && sm->rxReq; if (sm->workaround && duplicate && memcmp(sm->req_md5, sm->last_md5, 16) != 0) { /* * RFC 4137 uses (reqId == lastId) as the only * verification for duplicate EAP requests. However, * this misses cases where the AS is incorrectly using * the same id again; and unfortunately, such * implementations exist. Use MD5 hash as an extra * verification for the packets being duplicate to * workaround these issues. */ wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again," " but EAP packets were not identical"); wpa_printf(MSG_DEBUG, "EAP: workaround - assume this " "is not a duplicate packet"); duplicate = 0; } /* * Two special cases below for LEAP are local additions to work * around odd LEAP behavior (EAP-Success in the middle of * authentication and then swapped roles). Other transitions * are based on RFC 4137. */ if (sm->rxSuccess && sm->decision != DECISION_FAIL && (sm->reqId == sm->lastId || eap_success_workaround(sm, sm->reqId, sm->lastId))) SM_ENTER(EAP, SUCCESS); else if (sm->methodState != METHOD_CONT && ((sm->rxFailure && sm->decision != DECISION_UNCOND_SUCC) || (sm->rxSuccess && sm->decision == DECISION_FAIL && (sm->selectedMethod != EAP_TYPE_LEAP || sm->methodState != METHOD_MAY_CONT))) && (sm->reqId == sm->lastId || eap_success_workaround(sm, sm->reqId, sm->lastId))) SM_ENTER(EAP, FAILURE); else if (sm->rxReq && duplicate) SM_ENTER(EAP, RETRANSMIT); else if (sm->rxReq && !duplicate && sm->reqMethod == EAP_TYPE_NOTIFICATION && sm->allowNotifications) SM_ENTER(EAP, NOTIFICATION); else if (sm->rxReq && !duplicate && sm->selectedMethod == EAP_TYPE_NONE && sm->reqMethod == EAP_TYPE_IDENTITY) SM_ENTER(EAP, IDENTITY); else if (sm->rxReq && !duplicate && sm->selectedMethod == EAP_TYPE_NONE && sm->reqMethod != EAP_TYPE_IDENTITY && sm->reqMethod != EAP_TYPE_NOTIFICATION) SM_ENTER(EAP, GET_METHOD); else if (sm->rxReq && !duplicate && sm->reqMethod == sm->selectedMethod && sm->methodState != METHOD_DONE) SM_ENTER(EAP, METHOD); else if (sm->selectedMethod == EAP_TYPE_LEAP && (sm->rxSuccess || sm->rxResp)) SM_ENTER(EAP, METHOD); else SM_ENTER(EAP, DISCARD); break; case EAP_GET_METHOD: if (sm->selectedMethod == sm->reqMethod) SM_ENTER(EAP, METHOD); else SM_ENTER(EAP, SEND_RESPONSE); break; case EAP_METHOD: if (sm->ignore) SM_ENTER(EAP, DISCARD); else SM_ENTER(EAP, SEND_RESPONSE); break; case EAP_SEND_RESPONSE: SM_ENTER(EAP, IDLE); break; case EAP_DISCARD: SM_ENTER(EAP, IDLE); break; case EAP_IDENTITY: SM_ENTER(EAP, SEND_RESPONSE); break; case EAP_NOTIFICATION: SM_ENTER(EAP, SEND_RESPONSE); break; case EAP_RETRANSMIT: SM_ENTER(EAP, SEND_RESPONSE); break; case EAP_SUCCESS: break; case EAP_FAILURE: break; }}static Boolean eap_sm_allowMethod(struct eap_sm *sm, EapType method){ struct wpa_ssid *config = eap_get_config(sm); int i; if (!wpa_config_allowed_eap_method(config, method)) return FALSE; for (i = 0; i < NUM_EAP_METHODS; i++) { if (eap_methods[i]->method == method) return TRUE; } return FALSE;}static u8 * eap_sm_buildNak(struct eap_sm *sm, int id, size_t *len){ struct wpa_ssid *config = eap_get_config(sm); struct eap_hdr *resp; u8 *pos; int i, found = 0; wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %d not " "allowed)", sm->reqMethod); *len = sizeof(struct eap_hdr) + 1; resp = malloc(*len + NUM_EAP_METHODS); if (resp == NULL) return NULL; resp->code = EAP_CODE_RESPONSE; resp->identifier = id; pos = (u8 *) (resp + 1); *pos++ = EAP_TYPE_NAK; for (i = 0; i < NUM_EAP_METHODS; i++) { if (eap_methods[i]->method != sm->reqMethod && wpa_config_allowed_eap_method(config, eap_methods[i]->method)) { *pos++ = eap_methods[i]->method; (*len)++; found++; } } if (!found) { *pos = EAP_TYPE_NONE; (*len)++; } wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", ((u8 *) (resp + 1)) + 1, found); resp->length = host_to_be16(*len); return (u8 *) resp;}static void eap_sm_processIdentity(struct eap_sm *sm, const u8 *req, size_t len){ const struct eap_hdr *hdr = (const struct eap_hdr *) req; const u8 *pos = (const u8 *) (hdr + 1); pos++; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED "EAP authentication started"); /* * RFC 3748 - 5.1: Identity * Data field may contain a displayable message in UTF-8. If this * includes NUL-character, only the data before that should be * displayed. Some EAP implementasitons may piggy-back additional * options after the NUL. */ /* TODO: could save displayable message so that it can be shown to the * user in case of interaction is required */ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data", pos, be_to_host16(hdr->length) - 5);}#ifdef PCSC_FUNCSstatic int eap_sm_imsi_identity(struct eap_sm *sm, struct wpa_ssid *ssid){ int aka = 0; char imsi[100]; size_t imsi_len; u8 *pos = ssid->eap_methods; imsi_len = sizeof(imsi); if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) { wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM"); return -1; } wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len); while (pos && *pos != EAP_TYPE_NONE) { if (*pos == EAP_TYPE_AKA) { aka = 1; break; } pos++; } free(ssid->identity); ssid->identity = malloc(1 + imsi_len); if (ssid->identity == NULL) { wpa_printf(MSG_WARNING, "Failed to allocate buffer for " "IMSI-based identity"); return -1; } ssid->identity[0] = aka ? '0' : '1'; memcpy(ssid->identity + 1, imsi, imsi_len); ssid->identity_len = 1 + imsi_len; return 0;}#endif /* PCSC_FUNCS */static int eap_sm_get_scard_identity(struct eap_sm *sm, struct wpa_ssid *ssid){#ifdef PCSC_FUNCS if (scard_set_pin(sm->scard_ctx, ssid->pin)) { /* * Make sure the same PIN is not tried again in order to avoid * blocking SIM. */ free(ssid->pin); ssid->pin = NULL; wpa_printf(MSG_WARNING, "PIN validation failed"); eap_sm_request_pin(sm, ssid); return -1; } return eap_sm_imsi_identity(sm, ssid);#else /* PCSC_FUNCS */ return -1;#endif /* PCSC_FUNCS */}/** * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network * @sm: Pointer to EAP state machine allocated with eap_sm_init() * @id: EAP identifier for the packet * @len: Pointer to a variable that will be set to the length of the response * @encrypted: Whether the packet is for encrypted tunnel (EAP phase 2) * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on * failure * * This function allocates and builds an EAP-Identity/Response packet for the * current network. The caller is responsible for freeing the returned data. */u8 * eap_sm_buildIdentity(struct eap_sm *sm, int id, size_t *len, int encrypted){ struct wpa_ssid *config = eap_get_config(sm); struct eap_hdr *resp; u8 *pos; const u8 *identity; size_t identity_len; if (config == NULL) { wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration " "was not available"); return NULL; } if (sm->m && sm->m->get_identity && (identity = sm->m->get_identity(sm, sm->eap_method_priv, &identity_len)) != NULL) { wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth " "identity", identity, identity_len); } else if (!encrypted && config->anonymous_identity) { identity = config->anonymous_identity; identity_len = config->anonymous_identity_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity", identity, identity_len);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -