From 5c0299197bd75cc4fd99cb17cc2ee85c5ac1b53d Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Tue, 4 Feb 2020 13:08:39 +0100 Subject: [PATCH] method_defrag: Move upstream to make reusable Signed-off-by: Simon Rozman --- lib/TLS/build/TLS.vcxproj | 2 + lib/TLS/build/TLS.vcxproj.filters | 6 ++ lib/TLS/include/Method.h | 115 ++++++++++++++++++++++ lib/TLS/src/Method.cpp | 156 ++++++++++++++++++++++++++++++ lib/TLS/src/StdAfx.h | 1 + lib/TTLS/include/Method.h | 81 ---------------- lib/TTLS/src/Method.cpp | 132 ------------------------- lib/TTLS/src/StdAfx.h | 2 + 8 files changed, 282 insertions(+), 213 deletions(-) create mode 100644 lib/TLS/include/Method.h create mode 100644 lib/TLS/src/Method.cpp diff --git a/lib/TLS/build/TLS.vcxproj b/lib/TLS/build/TLS.vcxproj index de8f54c..3d23237 100644 --- a/lib/TLS/build/TLS.vcxproj +++ b/lib/TLS/build/TLS.vcxproj @@ -103,10 +103,12 @@ + + Create diff --git a/lib/TLS/build/TLS.vcxproj.filters b/lib/TLS/build/TLS.vcxproj.filters index 259bf91..d5c72c5 100644 --- a/lib/TLS/build/TLS.vcxproj.filters +++ b/lib/TLS/build/TLS.vcxproj.filters @@ -20,6 +20,9 @@ Header Files + + Header Files + @@ -31,5 +34,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/lib/TLS/include/Method.h b/lib/TLS/include/Method.h new file mode 100644 index 0000000..5ae4844 --- /dev/null +++ b/lib/TLS/include/Method.h @@ -0,0 +1,115 @@ +/* + Copyright 2015-2020 Amebis + Copyright 2016 GÉANT + + This file is part of GÉANTLink. + + 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 + 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 . +*/ + +namespace eap +{ + class method_defrag; +} + +#pragma once + +#include "../../EAPBase/include/Method.h" + + +namespace eap +{ + /// \addtogroup EAPBaseMethod + /// @{ + + /// + /// EAP-(T)TLS/PEAP class defragging method tunnel + /// + class method_defrag : public method + { + public: +#pragma warning(push) +#pragma warning(disable: 4480) + + /// + /// EAP-(T)TLS/PEAP request/response 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-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) + /// \sa [Protected EAP Protocol (PEAP) (Chapter: 3.1. PEAP Packet Format)](https://tools.ietf.org/html/draft-josefsson-pppext-eap-tls-eap-05#section-3.1) + /// + enum flags_t : unsigned char { + flags_length_incl = 0x80, ///< Length included + flags_more_frag = 0x40, ///< More fragments + flags_start = 0x20, ///< Start + flags_ver_mask = 0x07, ///< Version mask + }; + +#pragma warning(pop) + + public: + /// + /// Constructs a 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_ unsigned char version_max, _In_ method *inner); + + /// \name Session management + /// @{ + + virtual void begin_session( + _In_ DWORD dwFlags, + _In_ const EapAttributes *pAttributeArray, + _In_ HANDLE hTokenImpersonateUser, + _In_opt_ DWORD dwMaxSendPacketSize = MAXDWORD); + + /// @} + + /// \name Packet processing + /// @{ + + virtual EapPeerMethodResponseAction process_request_packet( + _In_bytecount_(dwReceivedPacketSize) const void *pReceivedPacket, + _In_ DWORD dwReceivedPacketSize); + + virtual void get_response_packet( + _Out_ sanitizing_blob &packet, + _In_opt_ DWORD size_max = MAXDWORD); + + /// @} + + 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/TLS/src/Method.cpp b/lib/TLS/src/Method.cpp new file mode 100644 index 0000000..a3a425e --- /dev/null +++ b/lib/TLS/src/Method.cpp @@ -0,0 +1,156 @@ +/* + Copyright 2015-2020 Amebis + Copyright 2016 GÉANT + + This file is part of GÉANTLink. + + 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 + 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 . +*/ + +#include "StdAfx.h" + +using namespace std; +using namespace winstd; + + +////////////////////////////////////////////////////////////////////// +// eap::method_defrag +////////////////////////////////////////////////////////////////////// + +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(mod, inner) +{ +} + + +void eap::method_defrag::begin_session( + _In_ DWORD dwFlags, + _In_ const EapAttributes *pAttributeArray, + _In_ HANDLE hTokenImpersonateUser, + _In_opt_ DWORD dwMaxSendPacketSize) +{ + // Inner method may generate packets of up to 4GB. + // But, we can not do the fragmentation if we have less space than flags+length. + if (dwMaxSendPacketSize < 5) + throw invalid_argument(string_printf(__FUNCTION__ " Maximum packet size too small (minimum: %u, available: %u).", 5, dwMaxSendPacketSize)); + method::begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, MAXDWORD); + + m_phase = phase_t::init; +} + + +EapPeerMethodResponseAction eap::method_defrag::process_request_packet( + _In_bytecount_(dwReceivedPacketSize) const void *pReceivedPacket, + _In_ DWORD dwReceivedPacketSize) +{ + auto data_packet = reinterpret_cast(pReceivedPacket); + + if (dwReceivedPacketSize < 1) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete packet flags."); + + // To prevent version downgrade attacks, negotiate protocol version on binding exchange only. Then stick to it! + unsigned char data_version = data_packet[0] & flags_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_length_incl ? 5 : 1), + data_content_end = data_packet + dwReceivedPacketSize; + if (data_content > data_content_end) + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete data."); + + // Do the defragmentation. + if (data_packet[0] & flags_more_frag) { + if (m_data_req.empty()) { + // Start a new packet. + if (data_packet[0] & flags_length_incl) { + // Preallocate data according to the Length field. + m_data_req.reserve(ntohl(*reinterpret_cast(data_packet + 1))); + } + } + m_data_req.insert(m_data_req.end(), data_content, data_content_end); + + // Respond with ACK packet (empty packet). + m_data_res.clear(); + m_send_res = true; + return EapPeerMethodResponseActionSend; + } else if (!m_data_req.empty()) { + // Last fragment received. Append data. + m_data_req.insert(m_data_req.end(), data_content, data_content_end); + } else { + // This is a complete non-fragmented packet. + m_data_req.assign(data_content, data_content_end); + } + + if (m_send_res) { + // We are sending a fragmented message. + if (m_data_req.empty() && (data_packet[0] & (flags_length_incl | flags_more_frag | flags_start)) == 0) { + // Received packet is the ACK of our fragmented message packet. Send the next fragment. + return EapPeerMethodResponseActionSend; + } else + throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " ACK expected."); + } + + // Process the data with underlying method. + auto action = method::process_request_packet(m_data_req.data(), (DWORD)m_data_req.size()); + + // Packet was processed. Clear its data since we use the absence of data to detect first of fragmented message packages. + m_data_req.clear(); + return action; +} + + +void eap::method_defrag::get_response_packet( + _Out_ sanitizing_blob &packet, + _In_opt_ DWORD size_max) +{ + assert(size_max >= 5); // We can not do the fragmentation if we have less space than flags+length. + + if (!m_send_res) { + // Get data from underlying method. + method::get_response_packet(m_data_res, MAXDWORD); + } + + size_t size_data = m_data_res.size(); + assert(size_data <= MAXDWORD); // Packets spanning over 4GB are not supported. + + packet.clear(); + if (size_data + 1 > size_max) { + // Write one fragment. + packet.push_back(flags_length_incl | flags_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); + packet.insert(packet.end(), data_begin, data_end); + m_data_res.erase(data_begin, data_end); + m_send_res = true; + } else { + // Write single/last fragment. + 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/TLS/src/StdAfx.h b/lib/TLS/src/StdAfx.h index 78ab7db..810c157 100644 --- a/lib/TLS/src/StdAfx.h +++ b/lib/TLS/src/StdAfx.h @@ -22,6 +22,7 @@ #include "../include/Config.h" #include "../include/Credentials.h" +#include "../include/Method.h" #include "../../EAPBase/include/EAPXML.h" diff --git a/lib/TTLS/include/Method.h b/lib/TTLS/include/Method.h index 1409085..a5db103 100644 --- a/lib/TTLS/include/Method.h +++ b/lib/TTLS/include/Method.h @@ -20,7 +20,6 @@ namespace eap { - class method_defrag; class method_eapmsg; class method_tls_tunnel; } @@ -41,86 +40,6 @@ namespace eap /// \addtogroup EAPBaseMethod /// @{ - /// - /// EAP-(T)TLS/PEAP class defragging method tunnel - /// - class method_defrag : public method - { - public: -#pragma warning(push) -#pragma warning(disable: 4480) - - /// - /// EAP-(T)TLS/PEAP request/response 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-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) - /// \sa [Protected EAP Protocol (PEAP) Version 2 (Chapter: 3.2. PEAPv2 Packet Format)](https://tools.ietf.org/html/draft-josefsson-pppext-eap-tls-eap-10#section-3.2) - /// - enum flags_t : unsigned char { - flags_length_incl = 0x80, ///< Length included - flags_more_frag = 0x40, ///< More fragments - flags_start = 0x20, ///< Start - flags_tls_length_incl = 0x10, ///< TLS Length included - flags_ver_mask = 0x07, ///< Version mask - }; - -#pragma warning(pop) - - public: - /// - /// Constructs a 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_ unsigned char version_max, _In_ method *inner); - - /// \name Session management - /// @{ - - virtual void begin_session( - _In_ DWORD dwFlags, - _In_ const EapAttributes *pAttributeArray, - _In_ HANDLE hTokenImpersonateUser, - _In_opt_ DWORD dwMaxSendPacketSize = MAXDWORD); - - /// @} - - /// \name Packet processing - /// @{ - - virtual EapPeerMethodResponseAction process_request_packet( - _In_bytecount_(dwReceivedPacketSize) const void *pReceivedPacket, - _In_ DWORD dwReceivedPacketSize); - - virtual void get_response_packet( - _Out_ sanitizing_blob &packet, - _In_opt_ DWORD size_max = MAXDWORD); - - /// @} - - 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? - }; - - /// /// Diameter EAP-Message tunnel method /// diff --git a/lib/TTLS/src/Method.cpp b/lib/TTLS/src/Method.cpp index a4bfbd6..ebed10c 100644 --- a/lib/TTLS/src/Method.cpp +++ b/lib/TTLS/src/Method.cpp @@ -26,138 +26,6 @@ using namespace std; using namespace winstd; -////////////////////////////////////////////////////////////////////// -// eap::method_defrag -////////////////////////////////////////////////////////////////////// - -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(mod, inner) -{ -} - - -void eap::method_defrag::begin_session( - _In_ DWORD dwFlags, - _In_ const EapAttributes *pAttributeArray, - _In_ HANDLE hTokenImpersonateUser, - _In_opt_ DWORD dwMaxSendPacketSize) -{ - // Inner method may generate packets of up to 4GB. - // But, we can not do the fragmentation if we have less space than flags+length. - if (dwMaxSendPacketSize < 5) - throw invalid_argument(string_printf(__FUNCTION__ " Maximum packet size too small (minimum: %u, available: %u).", 5, dwMaxSendPacketSize)); - method::begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, MAXDWORD); - - m_phase = phase_t::init; -} - - -EapPeerMethodResponseAction eap::method_defrag::process_request_packet( - _In_bytecount_(dwReceivedPacketSize) const void *pReceivedPacket, - _In_ DWORD dwReceivedPacketSize) -{ - auto data_packet = reinterpret_cast(pReceivedPacket); - - if (dwReceivedPacketSize < 1) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete packet flags."); - - // To prevent version downgrade attacks, negotiate protocol version on binding exchange only. Then stick to it! - unsigned char data_version = data_packet[0] & flags_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_length_incl ? 5 : 1), - data_content_end = data_packet + dwReceivedPacketSize; - if (data_content > data_content_end) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete data."); - - // Do the defragmentation. - if (data_packet[0] & flags_more_frag) { - if (m_data_req.empty()) { - // Start a new packet. - if (data_packet[0] & flags_length_incl) { - // Preallocate data according to the Length field. - m_data_req.reserve(ntohl(*reinterpret_cast(data_packet + 1))); - } - } - m_data_req.insert(m_data_req.end(), data_content, data_content_end); - - // Respond with ACK packet (empty packet). - m_data_res.clear(); - m_send_res = true; - return EapPeerMethodResponseActionSend; - } else if (!m_data_req.empty()) { - // Last fragment received. Append data. - m_data_req.insert(m_data_req.end(), data_content, data_content_end); - } else { - // This is a complete non-fragmented packet. - m_data_req.assign(data_content, data_content_end); - } - - if (m_send_res) { - // We are sending a fragmented message. - if (m_data_req.empty() && (data_packet[0] & (flags_length_incl | flags_more_frag | flags_start)) == 0) { - // Received packet is the ACK of our fragmented message packet. Send the next fragment. - return EapPeerMethodResponseActionSend; - } else - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " ACK expected."); - } - - // Process the data with underlying method. - auto action = method::process_request_packet(m_data_req.data(), (DWORD)m_data_req.size()); - - // Packet was processed. Clear its data since we use the absence of data to detect first of fragmented message packages. - m_data_req.clear(); - return action; -} - - -void eap::method_defrag::get_response_packet( - _Out_ sanitizing_blob &packet, - _In_opt_ DWORD size_max) -{ - assert(size_max >= 5); // We can not do the fragmentation if we have less space than flags+length. - - if (!m_send_res) { - // Get data from underlying method. - method::get_response_packet(m_data_res, MAXDWORD); - } - - size_t size_data = m_data_res.size(); - assert(size_data <= MAXDWORD); // Packets spanning over 4GB are not supported. - - packet.clear(); - if (size_data + 1 > size_max) { - // Write one fragment. - packet.push_back(flags_length_incl | flags_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); - packet.insert(packet.end(), data_begin, data_end); - m_data_res.erase(data_begin, data_end); - m_send_res = true; - } else { - // Write single/last fragment. - 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; - } -} - - ////////////////////////////////////////////////////////////////////// // eap::method_eapmsg ////////////////////////////////////////////////////////////////////// diff --git a/lib/TTLS/src/StdAfx.h b/lib/TTLS/src/StdAfx.h index 7e028eb..076b835 100644 --- a/lib/TTLS/src/StdAfx.h +++ b/lib/TTLS/src/StdAfx.h @@ -27,6 +27,8 @@ #include "../include/Method.h" #include "../include/Module.h" +#include "../../TLS/include/Method.h" + #include "../../PAP/include/Config.h" #include "../../PAP/include/Method.h"