From b908ff3aa9e421c973e7f56ea9ede380540441c4 Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Mon, 20 Jan 2020 14:06:53 +0100 Subject: [PATCH] method_defrag: Add support for version negotiation Signed-off-by: Simon Rozman --- lib/Events/res/EventsETW.man | Bin 123700 -> 124666 bytes lib/TTLS/include/Method.h | 25 +++++++++++++++++++------ lib/TTLS/src/Method.cpp | 32 ++++++++++++++++++++++++-------- lib/TTLS/src/Module.cpp | 34 +++++++++++++++++----------------- lib/WinStd | 2 +- 5 files changed, 61 insertions(+), 32 deletions(-) diff --git a/lib/Events/res/EventsETW.man b/lib/Events/res/EventsETW.man index 71a452de7b712b306706aa34e7a3e7a958cbd9f0..b358eeeba7d8e8cb34cf0db48bc0dff7573a6cc1 100644 GIT binary patch delta 297 zcmdmTjQ!VH_6?r@%Dw5jDgL zU?PxAWk>22f=xLoQf#DpcHPa^n%n=>oSH VwWhyeW)zyd;62xNk-dxxY5*%oPJ93W delta 65 zcmex$mVL`H_64x)1^|(u8fO3i diff --git a/lib/TTLS/include/Method.h b/lib/TTLS/include/Method.h index ed7dfe6..12737ad 100644 --- a/lib/TTLS/include/Method.h +++ b/lib/TTLS/include/Method.h @@ -42,7 +42,7 @@ namespace eap /// @{ /// - /// EAP-(T)TLS class defragging method tunnel + /// EAP-(T)TLS/PEAP class defragging method tunnel /// class method_defrag : public method_tunnel { @@ -51,7 +51,7 @@ namespace eap #pragma warning(disable: 4480) /// - /// EAP-(T)TLS request packet flags + /// EAP-(T)TLS/PEAP request packet flags /// /// \sa [The EAP-TLS Authentication Protocol (Chapter: 3.1 EAP-TLS Request Packet)](https://tools.ietf.org/html/rfc5216#section-3.1) /// \sa [The EAP-TTLS Authentication Protocol Version 0 (Chapter: 9.1. Packet Format)](https://tools.ietf.org/html/rfc5281#section-9.1) @@ -64,7 +64,7 @@ namespace eap }; /// - /// EAP-(T)TLS response packet flags + /// EAP-(T)TLS/PEAP response packet flags /// /// \sa [The EAP-TLS Authentication Protocol (Chapter: 3.2 EAP-TLS Response Packet)](https://tools.ietf.org/html/rfc5216#section-3.2) /// \sa [The EAP-TTLS Authentication Protocol Version 0 (Chapter: 9.1. Packet Format)](https://tools.ietf.org/html/rfc5281#section-9.1) @@ -81,10 +81,11 @@ namespace eap /// /// Constructs a method /// - /// \param[in] mod Module to use for global services - /// \param[in] inner Inner method + /// \param[in] mod Module to use for global services + /// \param[in] version_max Maximum protocol version supported by peer + /// \param[in] inner Inner method /// - method_defrag(_In_ module &mod, _In_ method *inner); + method_defrag(_In_ module &mod, _In_ unsigned char version_max, _In_ method *inner); /// \name Session management /// @{ @@ -110,10 +111,22 @@ namespace eap /// @} + public: + unsigned char m_version; ///< Negotiated protocol version + protected: sanitizing_blob m_data_req; ///< Data in request sanitizing_blob m_data_res; ///< Data in response bool m_send_res; ///< Are we sending a response? + + /// + /// Communication phase + /// + enum class phase_t { + unknown = -1, ///< Unknown phase + init = 0, ///< Binding exchange + established, ///< Connection established + } m_phase; ///< What phase is our communication at? }; diff --git a/lib/TTLS/src/Method.cpp b/lib/TTLS/src/Method.cpp index 8d11cf9..5ef6d67 100644 --- a/lib/TTLS/src/Method.cpp +++ b/lib/TTLS/src/Method.cpp @@ -1,21 +1,21 @@ /* Copyright 2015-2020 Amebis - Copyright 2016 GÉANT + Copyright 2016 GÉANT - This file is part of GÉANTLink. + This file is part of GÉANTLink. - GÉANTLink is free software: you can redistribute it and/or modify it + GÉANTLink is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - GÉANTLink is distributed in the hope that it will be useful, but + GÉANTLink is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with GÉANTLink. If not, see . + along with GÉANTLink. If not, see . */ #include "StdAfx.h" @@ -30,7 +30,9 @@ using namespace winstd; // eap::method_defrag ////////////////////////////////////////////////////////////////////// -eap::method_defrag::method_defrag(_In_ module &mod, _In_ method *inner) : +eap::method_defrag::method_defrag(_In_ module &mod, _In_ unsigned char version_max, _In_ method *inner) : + m_version(version_max), + m_phase(phase_t::unknown), m_send_res(false), method_tunnel(mod, inner) { @@ -49,6 +51,8 @@ void eap::method_defrag::begin_session( // Inner method can generate packets of up to 4GB. assert(m_inner); m_inner->begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, MAXDWORD); + + m_phase = phase_t::init; } @@ -59,6 +63,18 @@ EapPeerMethodResponseAction eap::method_defrag::process_request_packet( assert(dwReceivedPacketSize >= 1); // Request packet should contain flags at least. auto data_packet = reinterpret_cast(pReceivedPacket); + // To prevent version downgrade attacks, negotiate protocol version on binding exchange only. Then stick to it! + unsigned char data_version = data_packet[0] & flags_req_ver_mask; + if (m_phase == phase_t::init) { + m_version = min(data_version, m_version); + m_module.log_event(&EAPMETHOD_DEFRAG_VERSION, + event_data(m_version), + event_data(data_version), + event_data::blank); + m_phase = phase_t::established; + } else if (data_version != m_version) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Protocol version mismatch."); + // Get packet content pointers for more readable code later on. auto data_content = data_packet + (data_packet[0] & flags_req_length_incl ? 5 : 1), @@ -124,7 +140,7 @@ void eap::method_defrag::get_response_packet( packet.clear(); if (size_data + 1 > size_max) { // Write one fragment. - packet.push_back(flags_res_length_incl | flags_res_more_frag); + packet.push_back(flags_res_length_incl | flags_res_more_frag | m_version); unsigned int length = htonl((unsigned int)size_data); packet.insert(packet.end(), reinterpret_cast(&length), reinterpret_cast(&length + 1)); auto data_begin = m_data_res.begin() + 0, data_end = data_begin + (size_max - 5); @@ -133,7 +149,7 @@ void eap::method_defrag::get_response_packet( m_send_res = true; } else { // Write single/last fragment. - packet.push_back(0); + packet.push_back(m_version); packet.insert(packet.end(), m_data_res.begin(), m_data_res.end()); m_data_res.clear(); m_send_res = false; diff --git a/lib/TTLS/src/Module.cpp b/lib/TTLS/src/Module.cpp index 726f70b..3c9b27e 100644 --- a/lib/TTLS/src/Module.cpp +++ b/lib/TTLS/src/Module.cpp @@ -1,21 +1,21 @@ /* Copyright 2015-2020 Amebis - Copyright 2016 GÉANT + Copyright 2016 GÉANT - This file is part of GÉANTLink. + This file is part of GÉANTLink. - GÉANTLink is free software: you can redistribute it and/or modify it + GÉANTLink is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - GÉANTLink is distributed in the hope that it will be useful, but + GÉANTLink is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with GÉANTLink. If not, see . + along with GÉANTLink. If not, see . */ #include "StdAfx.h" @@ -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, @@ -278,7 +278,7 @@ EAP_SESSION_HANDLE eap::peer_ttls::begin_session( #endif s->m_method.reset( new method_eap (*this, eap_type_t::ttls, - new method_defrag(*this, + 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())))); // Initialize method. @@ -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; diff --git a/lib/WinStd b/lib/WinStd index e776e5a..973890b 160000 --- a/lib/WinStd +++ b/lib/WinStd @@ -1 +1 @@ -Subproject commit e776e5a4737ecd725292c7581c8a9f5e26d6516a +Subproject commit 973890b11b3ec8125b9756318dd694f8093608c2