📄 eap.c
字号:
* 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 */static void eap_peer_sm_step_idle(struct eap_sm *sm){ /* * 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);}static int eap_peer_req_is_duplicate(struct eap_sm *sm){ int duplicate; duplicate = (sm->reqId == sm->lastId) && sm->rxReq; if (sm->workaround && duplicate && os_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; } return duplicate;}static void eap_peer_sm_step_received(struct eap_sm *sm){ int duplicate = eap_peer_req_is_duplicate(sm); /* * 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);}static void eap_peer_sm_step_local(struct eap_sm *sm){ 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: eap_peer_sm_step_idle(sm); break; case EAP_RECEIVED: eap_peer_sm_step_received(sm); 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; }}SM_STEP(EAP){ /* Global transitions */ 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 { /* Local transitions */ eap_peer_sm_step_local(sm); }}static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, EapType method){ if (!eap_allowed_method(sm, vendor, method)) { wpa_printf(MSG_DEBUG, "EAP: configuration does not allow: " "vendor %u method %u", vendor, method); return FALSE; } if (eap_peer_get_eap_method(vendor, method)) return TRUE; wpa_printf(MSG_DEBUG, "EAP: not included in build: " "vendor %u method %u", vendor, method); return FALSE;}static struct wpabuf * eap_sm_build_expanded_nak( struct eap_sm *sm, int id, const struct eap_method *methods, size_t count){ struct wpabuf *resp; int found = 0; const struct eap_method *m; wpa_printf(MSG_DEBUG, "EAP: Building expanded EAP-Nak"); /* RFC 3748 - 5.3.2: Expanded Nak */ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED, 8 + 8 * (count + 1), EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; wpabuf_put_be24(resp, EAP_VENDOR_IETF); wpabuf_put_be32(resp, EAP_TYPE_NAK); for (m = methods; m; m = m->next) { if (sm->reqVendor == m->vendor && sm->reqVendorMethod == m->method) continue; /* do not allow the current method again */ if (eap_allowed_method(sm, m->vendor, m->method)) { wpa_printf(MSG_DEBUG, "EAP: allowed type: " "vendor=%u method=%u", m->vendor, m->method); wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); wpabuf_put_be24(resp, m->vendor); wpabuf_put_be32(resp, m->method); found++; } } if (!found) { wpa_printf(MSG_DEBUG, "EAP: no more allowed methods"); wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); wpabuf_put_be24(resp, EAP_VENDOR_IETF); wpabuf_put_be32(resp, EAP_TYPE_NONE); } eap_update_len(resp); return resp;}static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id){ struct wpabuf *resp; u8 *start; int found = 0, expanded_found = 0; size_t count; const struct eap_method *methods, *m; wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %u " "vendor=%u method=%u not allowed)", sm->reqMethod, sm->reqVendor, sm->reqVendorMethod); methods = eap_peer_get_methods(&count); if (methods == NULL) return NULL; if (sm->reqMethod == EAP_TYPE_EXPANDED) return eap_sm_build_expanded_nak(sm, id, methods, count); /* RFC 3748 - 5.3.1: Legacy Nak */ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, sizeof(struct eap_hdr) + 1 + count + 1, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; start = wpabuf_put(resp, 0); for (m = methods; m; m = m->next) { if (m->vendor == EAP_VENDOR_IETF && m->method == sm->reqMethod) continue; /* do not allow the current method again */ if (eap_allowed_method(sm, m->vendor, m->method)) { if (m->vendor != EAP_VENDOR_IETF) { if (expanded_found) continue; expanded_found = 1; wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); } else wpabuf_put_u8(resp, m->method); found++; } } if (!found) wpabuf_put_u8(resp, EAP_TYPE_NONE); wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", start, found); eap_update_len(resp); return resp;}static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req){ const struct eap_hdr *hdr = wpabuf_head(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 eap_peer_config *conf){ int aka = 0; char imsi[100]; size_t imsi_len; struct eap_method_type *m = conf->eap_methods; int i; 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); for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF || m[i].method != EAP_TYPE_NONE); i++) { if (m[i].vendor == EAP_VENDOR_IETF && m[i].method == EAP_TYPE_AKA) { aka = 1; break; } } os_free(conf->identity); conf->identity = os_malloc(1 + imsi_len); if (conf->identity == NULL) { wpa_printf(MSG_WARNING, "Failed to allocate buffer for " "IMSI-based identity"); return -1; } conf->identity[0] = aka ? '0' : '1'; os_memcpy(conf->identity + 1, imsi, imsi_len); conf->identity_len = 1 + imsi_len; return 0;}#endif /* PCSC_FUNCS */static int eap_sm_set_scard_pin(struct eap_sm *sm, struct eap_peer_config *conf){#ifdef PCSC_FUNCS if (scard_set_pin(sm->scard_ctx, conf->pin)) { /* * Make sure the same PIN is not tried again in order to avoid * blocking SIM. */ os_free(conf->pin); conf->pin = NULL; wpa_printf(MSG_WARNING, "PIN validation failed"); eap_sm_request_pin(sm); return -1; } return 0;#else /* PCSC_FUNCS */ return -1;#endif /* PCSC_FUNCS */}static int eap_sm_get_scard_identity(struct eap_sm *sm, struct eap_peer_config *conf){#ifdef PCSC_FUNCS if (eap_sm_set_scard_pin(sm, conf)) return -1; return eap_sm_imsi_identity(sm, conf);#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_peer_sm_init() * @id: EAP identifier for the packet * @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. */struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted){ struct eap_peer_config *config = eap_get_config(sm); struct wpabuf *resp; 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); } else { identity = config->identity; identity_len = config->identity_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity", identity, identity_len); } if (identity == NULL) { wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity " "configuration was not available"); if (config->pcsc) { if (eap_sm_get_scard_identity(sm, config) < 0) return NULL; identity = config->identity; identity_len = config->identity_len; wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " "IMSI", identity, identity_len); } else { eap_sm_request_identity(sm); return NULL; } } else if (config->pcsc) { if (eap_sm_set_scard_pin(sm, config) < 0) return NULL; } resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; wpabuf_put_data(resp, identity, identity_len); return resp;}static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req){ const u8 *pos; char *msg; size_t i, msg_len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, req, &msg_len); if (pos == NULL) return; wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data", pos, msg_len); msg = os_malloc(msg_len + 1); if (msg == NULL) return; for (i = 0; i < msg_len; i++) msg[i] = isprint(pos[i]) ? (char) pos[i] : '_';
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -