📄 eap_peap.c
字号:
if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in " "cryptobinding TLV"); return -1; } wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received"); return 0;}/** * eap_tlv_process - Process a received EAP-TLV message and generate a response * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @ret: Return values from EAP request validation and processing * @req: EAP-TLV request to be processed. The caller must have validated that * the buffer is large enough to contain full request (hdr->length bytes) and * that the EAP type is EAP_TYPE_TLV. * @resp: Buffer to return a pointer to the allocated response message. This * field should be initialized to %NULL before the call. The value will be * updated if a response message is generated. The caller is responsible for * freeing the allocated message. * @force_failure: Force negotiation to fail * Returns: 0 on success, -1 on failure */static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data, struct eap_method_ret *ret, const struct wpabuf *req, struct wpabuf **resp, int force_failure){ size_t left, tlv_len; const u8 *pos; const u8 *result_tlv = NULL, *crypto_tlv = NULL; size_t result_tlv_len = 0, crypto_tlv_len = 0; int tlv_type, mandatory; /* Parse TLVs */ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left); if (pos == NULL) return -1; wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left); while (left >= 4) { mandatory = !!(pos[0] & 0x80); tlv_type = WPA_GET_BE16(pos) & 0x3fff; pos += 2; tlv_len = WPA_GET_BE16(pos); pos += 2; left -= 4; if (tlv_len > left) { wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun " "(tlv_len=%lu left=%lu)", (unsigned long) tlv_len, (unsigned long) left); return -1; } switch (tlv_type) { case EAP_TLV_RESULT_TLV: result_tlv = pos; result_tlv_len = tlv_len; break; case EAP_TLV_CRYPTO_BINDING_TLV: crypto_tlv = pos; crypto_tlv_len = tlv_len; break; default: wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type " "%d%s", tlv_type, mandatory ? " (mandatory)" : ""); if (mandatory) { /* NAK TLV and ignore all TLVs in this packet. */ *resp = eap_tlv_build_nak(eap_get_id(req), tlv_type); return *resp == NULL ? -1 : 0; } /* Ignore this TLV, but process other TLVs */ break; } pos += tlv_len; left -= tlv_len; } if (left) { wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in " "Request (left=%lu)", (unsigned long) left); return -1; } /* Process supported TLVs */ if (crypto_tlv && data->crypto_binding != NO_BINDING) { wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV", crypto_tlv, crypto_tlv_len); if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4, crypto_tlv_len + 4) < 0) { if (result_tlv == NULL) return -1; force_failure = 1; } } else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) { wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV"); return -1; } if (result_tlv) { int status, resp_status; wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV", result_tlv, result_tlv_len); if (result_tlv_len < 2) { wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV " "(len=%lu)", (unsigned long) result_tlv_len); return -1; } status = WPA_GET_BE16(result_tlv); if (status == EAP_TLV_RESULT_SUCCESS) { wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success " "- EAP-TLV/Phase2 Completed"); if (force_failure) { wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure" " - force failed Phase 2"); resp_status = EAP_TLV_RESULT_FAILURE; ret->decision = DECISION_FAIL; } else { resp_status = EAP_TLV_RESULT_SUCCESS; ret->decision = DECISION_UNCOND_SUCC; } } else if (status == EAP_TLV_RESULT_FAILURE) { wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure"); resp_status = EAP_TLV_RESULT_FAILURE; ret->decision = DECISION_FAIL; } else { wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result " "Status %d", status); resp_status = EAP_TLV_RESULT_FAILURE; ret->decision = DECISION_FAIL; } ret->methodState = METHOD_DONE; *resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL, eap_get_id(req), resp_status); } return 0;}static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf){ struct wpabuf *e; struct eap_tlv_hdr *tlv; if (buf == NULL) return NULL; /* Encapsulate EAP packet in EAP-Payload TLV */ wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV"); e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf)); if (e == NULL) { wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory " "for TLV encapsulation"); wpabuf_free(buf); return NULL; } tlv = wpabuf_put(e, sizeof(*tlv)); tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_EAP_PAYLOAD_TLV); tlv->length = host_to_be16(wpabuf_len(buf)); wpabuf_put_buf(e, buf); wpabuf_free(buf); return e;}static int eap_peap_phase2_request(struct eap_sm *sm, struct eap_peap_data *data, struct eap_method_ret *ret, struct wpabuf *req, struct wpabuf **resp){ struct eap_hdr *hdr = wpabuf_mhead(req); size_t len = be_to_host16(hdr->length); u8 *pos; struct eap_method_ret iret; struct eap_peer_config *config = eap_get_config(sm); if (len <= sizeof(struct eap_hdr)) { wpa_printf(MSG_INFO, "EAP-PEAP: too short " "Phase 2 request (len=%lu)", (unsigned long) len); return -1; } pos = (u8 *) (hdr + 1); wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos); switch (*pos) { case EAP_TYPE_IDENTITY: *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); break; case EAP_TYPE_TLV: os_memset(&iret, 0, sizeof(iret)); if (eap_tlv_process(sm, data, &iret, req, resp, data->phase2_eap_started && !data->phase2_eap_success)) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return -1; } if (iret.methodState == METHOD_DONE || iret.methodState == METHOD_MAY_CONT) { ret->methodState = iret.methodState; ret->decision = iret.decision; data->phase2_success = 1; } break; case EAP_TYPE_EXPANDED:#ifdef EAP_TNC if (data->soh) { const u8 *epos; size_t eleft; epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, req, &eleft); if (epos) { struct wpabuf *buf; wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH EAP Extensions"); buf = tncc_process_soh_request(epos, eleft); if (buf) { *resp = eap_msg_alloc( EAP_VENDOR_MICROSOFT, 0x21, wpabuf_len(buf), EAP_CODE_RESPONSE, hdr->identifier); if (*resp == NULL) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return -1; } wpabuf_put_buf(*resp, buf); wpabuf_free(buf); break; } } }#endif /* EAP_TNC */ /* fall through */ default: if (data->phase2_type.vendor == EAP_VENDOR_IETF && data->phase2_type.method == EAP_TYPE_NONE) { size_t i; for (i = 0; i < data->num_phase2_types; i++) { if (data->phase2_types[i].vendor != EAP_VENDOR_IETF || data->phase2_types[i].method != *pos) continue; data->phase2_type.vendor = data->phase2_types[i].vendor; data->phase2_type.method = data->phase2_types[i].method; wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected " "Phase 2 EAP vendor %d method %d", data->phase2_type.vendor, data->phase2_type.method); break; } } if (*pos != data->phase2_type.method || *pos == EAP_TYPE_NONE) { if (eap_peer_tls_phase2_nak(data->phase2_types, data->num_phase2_types, hdr, resp)) return -1; return 0; } if (data->phase2_priv == NULL) { data->phase2_method = eap_peer_get_eap_method( data->phase2_type.vendor, data->phase2_type.method); if (data->phase2_method) { sm->init_phase2 = 1; sm->mschapv2_full_key = 1; data->phase2_priv = data->phase2_method->init(sm); sm->init_phase2 = 0; sm->mschapv2_full_key = 0; } } if (data->phase2_priv == NULL || data->phase2_method == NULL) { wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize " "Phase 2 EAP method %d", *pos); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return -1; } data->phase2_eap_started = 1; os_memset(&iret, 0, sizeof(iret)); *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, req); if ((iret.methodState == METHOD_DONE || iret.methodState == METHOD_MAY_CONT) && (iret.decision == DECISION_UNCOND_SUCC || iret.decision == DECISION_COND_SUCC)) { data->phase2_eap_success = 1; data->phase2_success = 1; } break; } if (*resp == NULL && (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || config->pending_req_new_password)) { wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); } return 0;}static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, struct eap_method_ret *ret, const struct eap_hdr *req, const struct wpabuf *in_data, struct wpabuf **out_data){ struct wpabuf *in_decrypted = NULL; int res, skip_change = 0; struct eap_hdr *hdr, *rhdr; struct wpabuf *resp = NULL; size_t len; wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" " Phase 2", (unsigned long) wpabuf_len(in_data)); if (data->pending_phase2_req) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - " "skip decryption and use old data"); /* Clear TLS reassembly state. */ eap_peer_tls_reset_input(&data->ssl); in_decrypted = data->pending_phase2_req; data->pending_phase2_req = NULL; skip_change = 1; goto continue_req; } if (wpabuf_len(in_data) == 0 && sm->workaround && data->phase2_success) { /* * Cisco ACS seems to be using TLS ACK to terminate * EAP-PEAPv0/GTC. Try to reply with TLS ACK. */ wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but " "expected data - acknowledge with TLS ACK since " "Phase 2 has been completed"); ret->decision = DECISION_COND_SUCC; ret->methodState = METHOD_DONE; return 1; } else if (wpabuf_len(in_data) == 0) { /* Received TLS ACK - requesting more fragments */ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP, data->peap_version, req->identifier, NULL, out_data); } res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); if (res) return res;continue_req: wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", in_decrypted); hdr = wpabuf_mhead(in_decrypted); if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST && be_to_host16(hdr->length) == 5 && eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) { /* At least FreeRADIUS seems to send full EAP header with * EAP Request Identity */ skip_change = 1; } if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST && eap_get_type(in_decrypted) == EAP_TYPE_TLV) { skip_change = 1; } if (data->peap_version == 0 && !skip_change) { struct eap_hdr *nhdr; struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) + wpabuf_len(in_decrypted)); if (nmsg == NULL) { wpabuf_free(in_decrypted); return 0; } nhdr = wpabuf_put(nmsg, sizeof(*nhdr)); wpabuf_put_buf(nmsg, in_decrypted); nhdr->code = req->code; nhdr->identifier = req->identifier; nhdr->length = host_to_be16(sizeof(struct eap_hdr) + wpabuf_len(in_decrypted)); wpabuf_free(in_decrypted); in_decrypted = nmsg; } if (data->peap_version >= 2) { struct eap_tlv_hdr *tlv; struct wpabuf *nmsg; if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) { wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 " "EAP TLV"); wpabuf_free(in_decrypted); return 0; } tlv = wpabuf_mhead(in_decrypted); if ((be_to_host16(tlv->tlv_type) & 0x3fff) != EAP_TLV_EAP_PAYLOAD_TLV) { wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV"); wpabuf_free(in_decrypted); return 0; } if (sizeof(*tlv) + be_to_host16(tlv->length) > wpabuf_len(in_decrypted)) { wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV " "length"); wpabuf_free(in_decrypted);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -