diff --git a/lib/EAPBase/include/Method.h b/lib/EAPBase/include/Method.h index fa8c8b7..51f986f 100644 --- a/lib/EAPBase/include/Method.h +++ b/lib/EAPBase/include/Method.h @@ -280,9 +280,10 @@ namespace eap /// /// \param[in] mod Module to use for global services /// \param[in] eap_method EAP method type + /// \param[in] cred User credentials /// \param[in] inner Inner method /// - method_eap(_In_ module &mod, _In_ winstd::eap_type_t eap_method, _In_ method *inner); + method_eap(_In_ module &mod, _In_ winstd::eap_type_t eap_method, _In_ credentials &cred, _In_ method *inner); /// \name Session management /// @{ @@ -310,8 +311,18 @@ namespace eap protected: const winstd::eap_type_t m_eap_method; ///< EAP method type + credentials &m_cred; ///< User credentials unsigned char m_id; ///< Request packet ID - bool m_send_nak; ///< Are we sending Legacy Nak response? + + /// + /// Communication phase + /// + enum class phase_t { + unknown = -1, ///< Unknown phase + identity = 0, ///< Send identity + inner, ///< Send inner method response + nak, ///< Send Legacy Nak response + } m_phase; ///< What phase is our communication at? }; /// @} diff --git a/lib/EAPBase/src/Method.cpp b/lib/EAPBase/src/Method.cpp index 4d5bed4..0d50499 100644 --- a/lib/EAPBase/src/Method.cpp +++ b/lib/EAPBase/src/Method.cpp @@ -200,10 +200,11 @@ EapPeerMethodResponseAction eap::method_tunnel::set_response_attributes(_In_ con // eap::method_eap ////////////////////////////////////////////////////////////////////// -eap::method_eap::method_eap(_In_ module &mod, _In_ winstd::eap_type_t eap_method, _In_ method *inner) : +eap::method_eap::method_eap(_In_ module &mod, _In_ eap_type_t eap_method, _In_ credentials &cred, _In_ method *inner) : m_eap_method(eap_method), + m_cred(cred), m_id(0), - m_send_nak(false), + m_phase(phase_t::unknown), method_tunnel(mod, inner) { } @@ -246,14 +247,18 @@ EapPeerMethodResponseAction eap::method_eap::process_request_packet( // Save request packet ID to make matching response packet in get_response_packet() later. m_id = hdr->Id; - if ((eap_type_t)hdr->Data[0] != m_eap_method) { - // Unsupported EAP method. Respond with Legacy Nak. - m_send_nak = true; + if ((eap_type_t)hdr->Data[0] == eap_type_t::identity) { + // EAP Identity. Respond with identity. + m_phase = phase_t::identity; return EapPeerMethodResponseActionSend; - } else { + } else if ((eap_type_t)hdr->Data[0] == m_eap_method) { // Process the data with underlying method. - m_send_nak = false; + m_phase = phase_t::inner; return method_tunnel::process_request_packet(hdr->Data + 1, size_packet - sizeof(EapPacket)); + } else { + // Unsupported EAP method. Respond with Legacy Nak. + m_phase = phase_t::nak; + return EapPeerMethodResponseActionSend; } } @@ -270,14 +275,27 @@ void eap::method_eap::get_response_packet( hdr.Code = (BYTE)EapCodeResponse; hdr.Id = m_id; - if (!m_send_nak) { + switch (m_phase) { + case phase_t::identity: { + hdr.Data[0] = (BYTE)eap_type_t::identity; + + // Convert identity to UTF-8. + sanitizing_string identity_utf8; + WideCharToMultiByte(CP_UTF8, 0, m_cred.get_identity(), identity_utf8, NULL, NULL); + packet.assign(identity_utf8.cbegin(), identity_utf8.cend()); + break; + } + + case phase_t::inner: hdr.Data[0] = (BYTE)m_eap_method; packet.reserve(size_max); // To avoid reallocation when inserting EAP packet header later. // Get data from underlying method. method_tunnel::get_response_packet(packet, size_max - sizeof(EapPacket)); - } else { + break; + + case phase_t::nak: { // Respond with Legacy Nak suggesting our EAP method to continue. hdr.Data[0] = (BYTE)eap_type_t::nak; @@ -289,6 +307,11 @@ void eap::method_eap::get_response_packet( // Data of Legacy Nak packet is a list of supported EAP types: our method alone. packet.assign(1, (unsigned char)m_eap_method); + break; + } + + default: + throw invalid_argument(string_printf(__FUNCTION__ " Unknown phase (phase %u).", m_phase)); } size_t size_packet = packet.size() + sizeof(EapPacket); diff --git a/lib/TTLS/src/Module.cpp b/lib/TTLS/src/Module.cpp index 3c9b27e..27408f8 100644 --- a/lib/TTLS/src/Module.cpp +++ b/lib/TTLS/src/Module.cpp @@ -121,7 +121,7 @@ void eap::peer_ttls::get_identity( } else { // Per-machine authentication, cannot use UI. throw win_runtime_error(ERROR_NO_SUCH_USER, __FUNCTION__ " Credentials for per-machine authentication not available."); -} + } } // Build our identity. ;) @@ -138,7 +138,7 @@ void eap::peer_ttls::get_identity( void eap::peer_ttls::get_method_properties( _In_ DWORD dwVersion, - _In_ DWORD dwFlags, + _In_ DWORD dwFlags, _In_ HANDLE hUserImpersonationToken, _In_count_(dwConnectionDataSize) const BYTE *pConnectionData, _In_ DWORD dwConnectionDataSize, @@ -259,11 +259,11 @@ EAP_SESSION_HANDLE eap::peer_ttls::begin_session( case eap_type_t::legacy_mschapv2: meth_inner.reset(new method_mschapv2_diameter(*this, dynamic_cast(*cfg_inner), dynamic_cast(*cred_inner))); break; case eap_type_t::mschapv2 : meth_inner.reset( new method_eapmsg (*this, cred_inner->get_identity().c_str(), - new method_eap (*this, eap_type_t::mschapv2, + new method_eap (*this, eap_type_t::mschapv2, *cred_inner, new method_mschapv2(*this, dynamic_cast(*cfg_inner), dynamic_cast(*cred_inner))))); break; case eap_type_t::gtc : meth_inner.reset( new method_eapmsg (*this, cred_inner->get_identity().c_str(), - new method_eap (*this, eap_type_t::gtc, + new method_eap (*this, eap_type_t::gtc, *cred_inner, new method_gtc (*this, dynamic_cast(*cfg_inner), dynamic_cast(*cred_inner))))); break; default: throw invalid_argument(__FUNCTION__ " Unsupported inner authentication method."); } @@ -277,7 +277,7 @@ EAP_SESSION_HANDLE eap::peer_ttls::begin_session( } #endif s->m_method.reset( - new method_eap (*this, eap_type_t::ttls, + new method_eap (*this, eap_type_t::ttls, *s->m_cred.m_cred, new method_defrag(*this, 0, /* Schannel supports retrieving keying material for EAP-TTLSv0 only. */ new method_ttls (*this, *cfg_method, *dynamic_cast(s->m_cred.m_cred.get()), meth_inner.release())))); @@ -596,7 +596,7 @@ DWORD WINAPI eap::peer_ttls::crl_checker::verify(_In_ crl_checker *obj) if (WaitForSingleObject(obj->m_abort, 5000) == WAIT_OBJECT_0) { // Aborted. return 1; -} + } // Prepare a list of certificates forming certificate chain. list context_data; @@ -605,7 +605,7 @@ DWORD WINAPI eap::peer_ttls::crl_checker::verify(_In_ crl_checker *obj) DWORD flags = 0; c = CertGetIssuerCertificateFromStore(obj->m_cert->hCertStore, context_data.back(), NULL, &flags); if (!c) break; -} + } // Create an array of pointers to CERT_CONTEXT required by CertVerifyRevocation(). vector context; @@ -623,7 +623,7 @@ DWORD WINAPI eap::peer_ttls::crl_checker::verify(_In_ crl_checker *obj) if (!CertVerifyRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, CERT_CONTEXT_REVOCATION_TYPE, (DWORD)(c_end - c), reinterpret_cast(&*c), CERT_VERIFY_REV_CHAIN_FLAG, NULL, &status_rev)) -{ + { PCCERT_CONTEXT cert = *(c + status_rev.dwIndex); wstring subj; if (!CertGetNameStringW(cert, CERT_NAME_DNS_TYPE, CERT_NAME_STR_ENABLE_PUNYCODE_FLAG, NULL, subj)) @@ -639,7 +639,7 @@ DWORD WINAPI eap::peer_ttls::crl_checker::verify(_In_ crl_checker *obj) // This really was an error, as it appeared before the root CA cerficate in the chain. obj->m_module.log_event(&EAPMETHOD_TLS_SERVER_CERT_REVOKE_SKIPPED, event_data((unsigned int)eap_type_t::ttls), event_data(subj), event_data::blank); } - break; + break; case CRYPT_E_REVOKED: // One of the certificates in the chain was revoked. @@ -650,7 +650,7 @@ DWORD WINAPI eap::peer_ttls::crl_checker::verify(_In_ crl_checker *obj) case CRL_REASON_CERTIFICATE_HOLD: // The revocation was of administrative nature. No need to black-list. obj->m_module.log_event(&EAPMETHOD_TLS_SERVER_CERT_REVOKED1, event_data((unsigned int)eap_type_t::ttls), event_data(subj), event_data(status_rev.dwReason), event_data::blank); - break; + break; default: { // One of the certificates in the chain was revoked as compromised. Black-list it. @@ -669,18 +669,18 @@ DWORD WINAPI eap::peer_ttls::crl_checker::verify(_In_ crl_checker *obj) // Resume checking the rest of the chain. c += (size_t)status_rev.dwIndex + 1; - break; + break; case ERROR_SUCCESS: // Odd. CertVerifyRevocation() should return TRUE then. Nevertheless, we take this as a "yes". c = c_end; - break; + break; - default: + default: // Checking one of the certificates in the chain for revocation failed. Resume checking the rest. obj->m_module.log_event(&EAPMETHOD_TLS_SERVER_CERT_REVOKE_FAILED, event_data((unsigned int)eap_type_t::ttls), event_data(subj), event_data(status_rev.dwError), event_data::blank); c += (size_t)status_rev.dwIndex + 1; - } + } } else { // Revocation check finished. break;