method_defrag: Add support for version negotiation

Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
Simon Rozman 2020-01-20 14:06:53 +01:00
parent 4af1bdc935
commit b908ff3aa9
5 changed files with 61 additions and 32 deletions

Binary file not shown.

View File

@ -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 class method_defrag : public method_tunnel
{ {
@ -51,7 +51,7 @@ namespace eap
#pragma warning(disable: 4480) #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-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) /// \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-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 [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 /// Constructs a method
/// ///
/// \param[in] mod Module to use for global services /// \param[in] mod Module to use for global services
/// \param[in] inner Inner method /// \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 /// \name Session management
/// @{ /// @{
@ -110,10 +111,22 @@ namespace eap
/// @} /// @}
public:
unsigned char m_version; ///< Negotiated protocol version
protected: protected:
sanitizing_blob m_data_req; ///< Data in request sanitizing_blob m_data_req; ///< Data in request
sanitizing_blob m_data_res; ///< Data in response sanitizing_blob m_data_res; ///< Data in response
bool m_send_res; ///< Are we sending a 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?
}; };

View File

@ -1,21 +1,21 @@
/* /*
Copyright 2015-2020 Amebis 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 under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (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 WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with GÉANTLink. If not, see <http://www.gnu.org/licenses/>. along with GÉANTLink. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "StdAfx.h" #include "StdAfx.h"
@ -30,7 +30,9 @@ using namespace winstd;
// eap::method_defrag // 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), m_send_res(false),
method_tunnel(mod, inner) method_tunnel(mod, inner)
{ {
@ -49,6 +51,8 @@ void eap::method_defrag::begin_session(
// Inner method can generate packets of up to 4GB. // Inner method can generate packets of up to 4GB.
assert(m_inner); assert(m_inner);
m_inner->begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, MAXDWORD); 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. assert(dwReceivedPacketSize >= 1); // Request packet should contain flags at least.
auto data_packet = reinterpret_cast<const unsigned char*>(pReceivedPacket); auto data_packet = reinterpret_cast<const unsigned char*>(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<unsigned char>(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. // Get packet content pointers for more readable code later on.
auto auto
data_content = data_packet + (data_packet[0] & flags_req_length_incl ? 5 : 1), 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(); packet.clear();
if (size_data + 1 > size_max) { if (size_data + 1 > size_max) {
// Write one fragment. // 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); unsigned int length = htonl((unsigned int)size_data);
packet.insert(packet.end(), reinterpret_cast<const unsigned char*>(&length), reinterpret_cast<const unsigned char*>(&length + 1)); packet.insert(packet.end(), reinterpret_cast<const unsigned char*>(&length), reinterpret_cast<const unsigned char*>(&length + 1));
auto data_begin = m_data_res.begin() + 0, data_end = data_begin + (size_max - 5); 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; m_send_res = true;
} else { } else {
// Write single/last fragment. // 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()); packet.insert(packet.end(), m_data_res.begin(), m_data_res.end());
m_data_res.clear(); m_data_res.clear();
m_send_res = false; m_send_res = false;

View File

@ -1,21 +1,21 @@
/* /*
Copyright 2015-2020 Amebis 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 under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (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 WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with GÉANTLink. If not, see <http://www.gnu.org/licenses/>. along with GÉANTLink. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "StdAfx.h" #include "StdAfx.h"
@ -121,7 +121,7 @@ void eap::peer_ttls::get_identity(
} else { } else {
// Per-machine authentication, cannot use UI. // Per-machine authentication, cannot use UI.
throw win_runtime_error(ERROR_NO_SUCH_USER, __FUNCTION__ " Credentials for per-machine authentication not available."); throw win_runtime_error(ERROR_NO_SUCH_USER, __FUNCTION__ " Credentials for per-machine authentication not available.");
} }
} }
// Build our identity. ;) // Build our identity. ;)
@ -138,7 +138,7 @@ void eap::peer_ttls::get_identity(
void eap::peer_ttls::get_method_properties( void eap::peer_ttls::get_method_properties(
_In_ DWORD dwVersion, _In_ DWORD dwVersion,
_In_ DWORD dwFlags, _In_ DWORD dwFlags,
_In_ HANDLE hUserImpersonationToken, _In_ HANDLE hUserImpersonationToken,
_In_count_(dwConnectionDataSize) const BYTE *pConnectionData, _In_count_(dwConnectionDataSize) const BYTE *pConnectionData,
_In_ DWORD dwConnectionDataSize, _In_ DWORD dwConnectionDataSize,
@ -278,7 +278,7 @@ EAP_SESSION_HANDLE eap::peer_ttls::begin_session(
#endif #endif
s->m_method.reset( s->m_method.reset(
new method_eap (*this, eap_type_t::ttls, 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<credentials_ttls*>(s->m_cred.m_cred.get()), meth_inner.release())))); new method_ttls (*this, *cfg_method, *dynamic_cast<credentials_ttls*>(s->m_cred.m_cred.get()), meth_inner.release()))));
// Initialize method. // 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) { if (WaitForSingleObject(obj->m_abort, 5000) == WAIT_OBJECT_0) {
// Aborted. // Aborted.
return 1; return 1;
} }
// Prepare a list of certificates forming certificate chain. // Prepare a list of certificates forming certificate chain.
list<cert_context> context_data; list<cert_context> context_data;
@ -605,7 +605,7 @@ DWORD WINAPI eap::peer_ttls::crl_checker::verify(_In_ crl_checker *obj)
DWORD flags = 0; DWORD flags = 0;
c = CertGetIssuerCertificateFromStore(obj->m_cert->hCertStore, context_data.back(), NULL, &flags); c = CertGetIssuerCertificateFromStore(obj->m_cert->hCertStore, context_data.back(), NULL, &flags);
if (!c) break; if (!c) break;
} }
// Create an array of pointers to CERT_CONTEXT required by CertVerifyRevocation(). // Create an array of pointers to CERT_CONTEXT required by CertVerifyRevocation().
vector<PCERT_CONTEXT> context; vector<PCERT_CONTEXT> 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, if (!CertVerifyRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, CERT_CONTEXT_REVOCATION_TYPE,
(DWORD)(c_end - c), reinterpret_cast<PVOID*>(&*c), (DWORD)(c_end - c), reinterpret_cast<PVOID*>(&*c),
CERT_VERIFY_REV_CHAIN_FLAG, NULL, &status_rev)) CERT_VERIFY_REV_CHAIN_FLAG, NULL, &status_rev))
{ {
PCCERT_CONTEXT cert = *(c + status_rev.dwIndex); PCCERT_CONTEXT cert = *(c + status_rev.dwIndex);
wstring subj; wstring subj;
if (!CertGetNameStringW(cert, CERT_NAME_DNS_TYPE, CERT_NAME_STR_ENABLE_PUNYCODE_FLAG, NULL, 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. // 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); 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: case CRYPT_E_REVOKED:
// One of the certificates in the chain was 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: case CRL_REASON_CERTIFICATE_HOLD:
// The revocation was of administrative nature. No need to black-list. // 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); 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: { default: {
// One of the certificates in the chain was revoked as compromised. Black-list it. // 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. // Resume checking the rest of the chain.
c += (size_t)status_rev.dwIndex + 1; c += (size_t)status_rev.dwIndex + 1;
break; break;
case ERROR_SUCCESS: case ERROR_SUCCESS:
// Odd. CertVerifyRevocation() should return TRUE then. Nevertheless, we take this as a "yes". // Odd. CertVerifyRevocation() should return TRUE then. Nevertheless, we take this as a "yes".
c = c_end; c = c_end;
break; break;
default: default:
// Checking one of the certificates in the chain for revocation failed. Resume checking the rest. // 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); 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; c += (size_t)status_rev.dwIndex + 1;
} }
} else { } else {
// Revocation check finished. // Revocation check finished.
break; break;

@ -1 +1 @@
Subproject commit e776e5a4737ecd725292c7581c8a9f5e26d6516a Subproject commit 973890b11b3ec8125b9756318dd694f8093608c2