From a0efb6742d0abfeb06ac64828d47bcc2d8b45c1d Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Sat, 6 Aug 2016 16:27:15 +0200 Subject: [PATCH] EAP-TTLS work continues... --- lib/EAPBase/include/EAP.h | 29 +++ lib/EAPBase/include/Method.h | 27 +++ lib/EAPBase/include/Module.h | 2 +- lib/EAPBase/src/Method.cpp | 25 +++ lib/Events/res/EventsETW.man | Bin 26106 -> 26098 bytes lib/TLS/include/Method.h | 117 ++++++++++-- lib/TLS/src/Method.cpp | 344 +++++++++++++++++++++++++++-------- lib/TLS/src/StdAfx.h | 3 + lib/TTLS/include/Method.h | 24 ++- lib/TTLS/src/Method.cpp | 37 +++- lib/TTLS/src/Module.cpp | 38 ++-- lib/WinStd | 2 +- 12 files changed, 532 insertions(+), 116 deletions(-) diff --git a/lib/EAPBase/include/EAP.h b/lib/EAPBase/include/EAP.h index 4489090..9fca171 100644 --- a/lib/EAPBase/include/EAP.h +++ b/lib/EAPBase/include/EAP.h @@ -27,6 +27,7 @@ #if !defined(RC_INVOKED) && !defined(MIDL_PASS) +#include #include #include @@ -48,6 +49,11 @@ namespace eap /// Input BLOB cursor /// struct cursor_in; + + /// + /// Sanitizing dynamically allocated BLOB + /// + typedef std::vector > sanitizing_blob; } /// @@ -329,6 +335,17 @@ inline size_t pksizeof(const winstd::eap_type_t &val); /// inline void operator>>(_Inout_ eap::cursor_in &cursor, _Out_ winstd::eap_type_t &val); +#ifndef htonll +/// +/// Convert host converts an unsigned __int64 from host to TCP/IP network byte order. +/// +/// \param[in] val A 64-bit unsigned number in host byte order. +/// +/// \returns The value in TCP/IP's network byte order. +/// +inline unsigned __int64 htonll(unsigned __int64 val); +#endif + #pragma once @@ -684,4 +701,16 @@ inline void operator>>(_Inout_ eap::cursor_in &cursor, _Out_ winstd::eap_type_t val = (winstd::eap_type_t)t; } + +#ifndef htonll + +inline unsigned __int64 htonll(unsigned __int64 val) +{ + return + (unsigned __int64)htonl((u_long)((val >> 32) & 0xffffffff)) | + (unsigned __int64)htonl((u_long)((val ) & 0xffffffff)) << 32; +} + +#endif + #endif diff --git a/lib/EAPBase/include/Method.h b/lib/EAPBase/include/Method.h index f55be04..670f18b 100644 --- a/lib/EAPBase/include/Method.h +++ b/lib/EAPBase/include/Method.h @@ -90,6 +90,33 @@ namespace eap /// \name Packet processing /// @{ + /// + /// Starts an EAP authentication session on the peer EAPHost using the EAP method. + /// + /// \sa [EapPeerBeginSession function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363600.aspx) + /// + /// \returns + /// - \c true if succeeded + /// - \c false otherwise. See \p ppEapError for details. + /// + virtual bool begin_session( + _In_ DWORD dwFlags, + _In_ const EapAttributes *pAttributeArray, + _In_ HANDLE hTokenImpersonateUser, + _In_ DWORD dwMaxSendPacketSize, + _Out_ EAP_ERROR **ppEapError); + + /// + /// Ends an EAP authentication session for the EAP method. + /// + /// \sa [EapPeerEndSession function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363604.aspx) + /// + /// \returns + /// - \c true if succeeded + /// - \c false otherwise. See \p ppEapError for details. + /// + virtual bool end_session(_Out_ EAP_ERROR **ppEapError); + /// /// Processes a packet received by EAPHost from a supplicant. /// diff --git a/lib/EAPBase/include/Module.h b/lib/EAPBase/include/Module.h index c2a5d38..8639752 100644 --- a/lib/EAPBase/include/Module.h +++ b/lib/EAPBase/include/Module.h @@ -835,7 +835,7 @@ namespace eap /// virtual bool begin_session( _In_ DWORD dwFlags, - _In_ const EapAttributes *pAttributeArray, + _In_ const EapAttributes *pAttributeArray, _In_ HANDLE hTokenImpersonateUser, _In_count_(dwConnectionDataSize) const BYTE *pConnectionData, _In_ DWORD dwConnectionDataSize, diff --git a/lib/EAPBase/src/Method.cpp b/lib/EAPBase/src/Method.cpp index 45f93ae..d0d7b27 100644 --- a/lib/EAPBase/src/Method.cpp +++ b/lib/EAPBase/src/Method.cpp @@ -74,3 +74,28 @@ eap::method& eap::method::operator=(_Inout_ method &&other) return *this; } + + +bool eap::method::begin_session( + _In_ DWORD dwFlags, + _In_ const EapAttributes *pAttributeArray, + _In_ HANDLE hTokenImpersonateUser, + _In_ DWORD dwMaxSendPacketSize, + _Out_ EAP_ERROR **ppEapError) +{ + UNREFERENCED_PARAMETER(dwFlags); + UNREFERENCED_PARAMETER(pAttributeArray); + UNREFERENCED_PARAMETER(hTokenImpersonateUser); + UNREFERENCED_PARAMETER(dwMaxSendPacketSize); + UNREFERENCED_PARAMETER(ppEapError); + + return true; +} + + +bool eap::method::end_session(_Out_ EAP_ERROR **ppEapError) +{ + UNREFERENCED_PARAMETER(ppEapError); + + return true; +} diff --git a/lib/Events/res/EventsETW.man b/lib/Events/res/EventsETW.man index fd748079e0841b7b6055e5f6b5d4ed12d66963ea..bab1faafec4738d83b0663b954459f0231d9e49a 100644 GIT binary patch delta 17 Zcmex$n(@ #include +#include #include namespace eap { - enum tls_flags_t { - tls_flags_length_incl = 0x80, ///< Length included - tls_flags_more_frag = 0x40, ///< More fragments - tls_flags_start = 0x20, ///< Start +#pragma pack(push) +#pragma pack(1) + struct tls_random_t { + unsigned long time; + unsigned char data[28]; + }; +#pragma pack(pop) + + enum tls_req_flags_t { + tls_req_flags_length_incl = 0x80, ///< Length included + tls_req_flags_more_frag = 0x40, ///< More fragments + tls_req_flags_start = 0x20, ///< Start + }; + + enum tls_res_flags_t { + tls_res_flags_length_incl = 0x80, ///< Length included + tls_res_flags_more_frag = 0x40, ///< More fragments }; @@ -111,6 +131,22 @@ namespace eap /// \name Packet processing /// @{ + /// + /// Starts an EAP authentication session on the peer EAPHost using the EAP method. + /// + /// \sa [EapPeerBeginSession function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363600.aspx) + /// + /// \returns + /// - \c true if succeeded + /// - \c false otherwise. See \p ppEapError for details. + /// + virtual bool begin_session( + _In_ DWORD dwFlags, + _In_ const EapAttributes *pAttributeArray, + _In_ HANDLE hTokenImpersonateUser, + _In_ DWORD dwMaxSendPacketSize, + _Out_ EAP_ERROR **ppEapError); + /// /// Processes a packet received by EAPHost from a supplicant. /// @@ -142,25 +178,66 @@ namespace eap /// @} + protected: + /// + /// Makes a TLS client hello message + /// + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.4.1.2. Client Hello](https://tools.ietf.org/html/rfc5246#section-7.4.1.2) + /// + /// \returns Client Hello message + /// + sanitizing_blob make_client_hello() const; + + /// + /// Makes a TLS handshake + /// + /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.4. Handshake Protocol](https://tools.ietf.org/html/rfc5246#section-7.4) + /// + /// \param[in] msg Handshake data contents + /// \param[in] encrypt Should make an encrypted handshake message? + /// + /// \returns TLS handshake message + /// + sanitizing_blob make_handshake(_In_ const sanitizing_blob &msg, _In_ bool encrypt); + + /// + /// Encrypt block of data + /// + /// \param[in] msg TLS message to encrypt + /// + /// \returns Encrypted message + /// + std::vector encrypt_message(_In_ const sanitizing_blob &msg); + public: enum phase_t { - phase_handshake_start = 0, - } m_phase; ///< Session phase + phase_client_hello = 0, + phase_server_hello = 1, + } m_phase; ///< Session phase struct { - EapCode m_code; ///< Packet code - BYTE m_id; ///< Packet ID - BYTE m_flags; ///< Packet flags - std::vector m_data; ///< Packet data + EapCode m_code; ///< Packet code + BYTE m_id; ///< Packet ID + BYTE m_flags; ///< Packet flags + std::vector m_data; ///< Packet data } - m_packet_req, ///< Request packet - m_packet_res; ///< Response packet + m_packet_req, ///< Request packet + m_packet_res; ///< Response packet - winstd::crypt_prov m_cp; ///< Cryptography provider + winstd::crypt_prov m_cp; ///< Cryptography provider + winstd::crypt_key m_key_hmac; ///< Symmetric key for HMAC calculation - tls_random_t m_random_client; ///< Client random - tls_random_t m_random_server; ///< Server random + winstd::crypt_key m_key_write; ///< Key for encrypting messages - std::vector > m_session_id; ///< TLS session ID + tls_random_t m_random_client; ///< Client random + tls_random_t m_random_server; ///< Server random + + sanitizing_blob m_session_id; ///< TLS session ID + + winstd::crypt_hash m_hash_handshake_msgs_md5; ///< Running MD5 hash of handshake messages sent + winstd::crypt_hash m_hash_handshake_msgs_sha1; ///< Running SHA-1 hash of handshake messages sent + + protected: + unsigned __int64 m_seq_num; ///< Sequence number for encryption }; } diff --git a/lib/TLS/src/Method.cpp b/lib/TLS/src/Method.cpp index 237029a..50fde02 100644 --- a/lib/TLS/src/Method.cpp +++ b/lib/TLS/src/Method.cpp @@ -29,7 +29,8 @@ using namespace winstd; ////////////////////////////////////////////////////////////////////// eap::method_tls::method_tls(_In_ module &module, _In_ config_method_tls &cfg, _In_ credentials_tls &cred) : - m_phase(phase_handshake_start), + m_phase(phase_client_hello), + m_seq_num(0), method(module, cfg, cred) { m_packet_req.m_code = (EapCode)0; @@ -40,14 +41,17 @@ eap::method_tls::method_tls(_In_ module &module, _In_ config_method_tls &cfg, _I m_packet_res.m_id = 0; m_packet_res.m_flags = 0; - memset(m_random_client, 0, sizeof(tls_random_t)); - memset(m_random_server, 0, sizeof(tls_random_t)); + memset(&m_random_client, 0, sizeof(tls_random_t)); + memset(&m_random_server, 0, sizeof(tls_random_t)); } eap::method_tls::method_tls(_In_ const method_tls &other) : m_phase(other.m_phase), m_session_id(other.m_session_id), + m_hash_handshake_msgs_md5(other.m_hash_handshake_msgs_md5), + m_hash_handshake_msgs_sha1(other.m_hash_handshake_msgs_sha1), + m_seq_num(other.m_seq_num), method(other) { m_packet_req.m_code = other.m_packet_req.m_code ; @@ -60,14 +64,17 @@ eap::method_tls::method_tls(_In_ const method_tls &other) : m_packet_res.m_flags = other.m_packet_res.m_flags; m_packet_res.m_data = other.m_packet_res.m_data ; - memcpy(m_random_client, other.m_random_client, sizeof(tls_random_t)); - memcpy(m_random_server, other.m_random_server, sizeof(tls_random_t)); + memcpy(&m_random_client, &other.m_random_client, sizeof(tls_random_t)); + memcpy(&m_random_server, &other.m_random_server, sizeof(tls_random_t)); } eap::method_tls::method_tls(_Inout_ method_tls &&other) : m_phase(std::move(other.m_phase)), m_session_id(std::move(other.m_session_id)), + m_hash_handshake_msgs_md5(std::move(other.m_hash_handshake_msgs_md5)), + m_hash_handshake_msgs_sha1(std::move(other.m_hash_handshake_msgs_sha1)), + m_seq_num(std::move(other.m_seq_num)), method(std::move(other)) { m_packet_req.m_code = std::move(other.m_packet_req.m_code ); @@ -80,39 +87,44 @@ eap::method_tls::method_tls(_Inout_ method_tls &&other) : m_packet_res.m_flags = std::move(other.m_packet_res.m_flags); m_packet_res.m_data = std::move(other.m_packet_res.m_data ); - memcpy(m_random_client, other.m_random_client, sizeof(tls_random_t)); - memcpy(m_random_server, other.m_random_server, sizeof(tls_random_t)); + memcpy(&m_random_client, &other.m_random_client, sizeof(tls_random_t)); + memcpy(&m_random_server, &other.m_random_server, sizeof(tls_random_t)); } eap::method_tls::~method_tls() { - SecureZeroMemory(m_random_client, sizeof(tls_random_t)); - SecureZeroMemory(m_random_server, sizeof(tls_random_t)); + SecureZeroMemory(&m_random_client, sizeof(tls_random_t)); + SecureZeroMemory(&m_random_server, sizeof(tls_random_t)); } eap::method_tls& eap::method_tls::operator=(_In_ const method_tls &other) { if (this != std::addressof(other)) { - (method&)*this = other; + (method&)*this = other; - m_phase = other.m_phase; + m_phase = other.m_phase; - m_packet_req.m_code = other.m_packet_req.m_code ; - m_packet_req.m_id = other.m_packet_req.m_id ; - m_packet_req.m_flags = other.m_packet_req.m_flags; - m_packet_req.m_data = other.m_packet_req.m_data ; + m_packet_req.m_code = other.m_packet_req.m_code ; + m_packet_req.m_id = other.m_packet_req.m_id ; + m_packet_req.m_flags = other.m_packet_req.m_flags; + m_packet_req.m_data = other.m_packet_req.m_data ; - m_packet_res.m_code = other.m_packet_res.m_code ; - m_packet_res.m_id = other.m_packet_res.m_id ; - m_packet_res.m_flags = other.m_packet_res.m_flags; - m_packet_res.m_data = other.m_packet_res.m_data ; + m_packet_res.m_code = other.m_packet_res.m_code ; + m_packet_res.m_id = other.m_packet_res.m_id ; + m_packet_res.m_flags = other.m_packet_res.m_flags; + m_packet_res.m_data = other.m_packet_res.m_data ; - m_session_id = other.m_session_id; + memcpy(&m_random_client, &other.m_random_client, sizeof(tls_random_t)); + memcpy(&m_random_server, &other.m_random_server, sizeof(tls_random_t)); - memcpy(m_random_client, other.m_random_client, sizeof(tls_random_t)); - memcpy(m_random_server, other.m_random_server, sizeof(tls_random_t)); + m_session_id = other.m_session_id; + + m_hash_handshake_msgs_md5 = other.m_hash_handshake_msgs_md5; + m_hash_handshake_msgs_sha1 = other.m_hash_handshake_msgs_sha1; + + m_seq_num = other.m_seq_num; } return *this; @@ -122,30 +134,78 @@ eap::method_tls& eap::method_tls::operator=(_In_ const method_tls &other) eap::method_tls& eap::method_tls::operator=(_Inout_ method_tls &&other) { if (this != std::addressof(other)) { - (method&)*this = std::move(other); + (method&)*this = std::move(other); - m_phase = std::move(other.m_phase); + m_phase = std::move(other.m_phase); - m_packet_req.m_code = std::move(other.m_packet_req.m_code ); - m_packet_req.m_id = std::move(other.m_packet_req.m_id ); - m_packet_req.m_flags = std::move(other.m_packet_req.m_flags); - m_packet_req.m_data = std::move(other.m_packet_req.m_data ); + m_packet_req.m_code = std::move(other.m_packet_req.m_code ); + m_packet_req.m_id = std::move(other.m_packet_req.m_id ); + m_packet_req.m_flags = std::move(other.m_packet_req.m_flags); + m_packet_req.m_data = std::move(other.m_packet_req.m_data ); - m_packet_res.m_code = std::move(other.m_packet_res.m_code ); - m_packet_res.m_id = std::move(other.m_packet_res.m_id ); - m_packet_res.m_flags = std::move(other.m_packet_res.m_flags); - m_packet_res.m_data = std::move(other.m_packet_res.m_data ); + m_packet_res.m_code = std::move(other.m_packet_res.m_code ); + m_packet_res.m_id = std::move(other.m_packet_res.m_id ); + m_packet_res.m_flags = std::move(other.m_packet_res.m_flags); + m_packet_res.m_data = std::move(other.m_packet_res.m_data ); - m_session_id = std::move(other.m_session_id); + memcpy(&m_random_client, &other.m_random_client, sizeof(tls_random_t)); + memcpy(&m_random_server, &other.m_random_server, sizeof(tls_random_t)); - memcpy(m_random_client, other.m_random_client, sizeof(tls_random_t)); - memcpy(m_random_server, other.m_random_server, sizeof(tls_random_t)); + m_session_id = std::move(other.m_session_id); + + m_hash_handshake_msgs_md5 = std::move(other.m_hash_handshake_msgs_md5); + m_hash_handshake_msgs_sha1 = std::move(other.m_hash_handshake_msgs_sha1); + + m_seq_num = std::move(other.m_seq_num); } return *this; } +bool eap::method_tls::begin_session( + _In_ DWORD dwFlags, + _In_ const EapAttributes *pAttributeArray, + _In_ HANDLE hTokenImpersonateUser, + _In_ DWORD dwMaxSendPacketSize, + _Out_ EAP_ERROR **ppEapError) +{ + if (!eap::method::begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, dwMaxSendPacketSize, ppEapError)) + return false; + + // Create cryptographics provider. + if (!m_cp.create(NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0)) { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error creating cryptographics provider.")); + return false; + } + + // Generate client randomness. + m_random_client.time = (unsigned long)time(NULL); + if (!CryptGenRandom(m_cp, sizeof(m_random_client.data), m_random_client.data)) { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error creating client randomness.")); + return false; + } + + if (!m_hash_handshake_msgs_md5.create(m_cp, CALG_MD5, NULL, 0)) { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error creating MD5 hashing object.")); + return false; + } + + if (!m_hash_handshake_msgs_sha1.create(m_cp, CALG_SHA1, NULL, 0)) { + *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error creating SHA-1 hashing object.")); + return false; + } + + // HMAC symmetric key generation sample. To be used later... + //crypt_hash hash_key; + //hash_key.create(m_cp, CALG_SHA1, 0, 0); + //CryptHashData(hash_key, Data1, sizeof(Data1), 0); + //m_key_hmac.derive(m_cp, CALG_RC4, hash_key, 0); + + return true; +} + + bool eap::method_tls::process_request_packet( _In_bytecount_(dwReceivedPacketSize) const EapPacket *pReceivedPacket, _In_ DWORD dwReceivedPacketSize, @@ -156,10 +216,6 @@ bool eap::method_tls::process_request_packet( assert(pEapOutput); assert(ppEapError); - // Initialize output. - pEapOutput->fAllowNotifications = TRUE; - pEapOutput->action = EapPeerMethodResponseActionDiscard; - // Is this a valid EAP-TLS packet? if (dwReceivedPacketSize < 6) { *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, _T(__FUNCTION__) _T(" Packet is too small. EAP-%s packets should be at least 6B.")); @@ -169,8 +225,8 @@ bool eap::method_tls::process_request_packet( return false; }*/ - if (pReceivedPacket->Data[1] & tls_flags_more_frag) { - if (pReceivedPacket->Data[1] & tls_flags_length_incl) { + if (pReceivedPacket->Data[1] & tls_req_flags_more_frag) { + if (pReceivedPacket->Data[1] & tls_req_flags_length_incl) { // First fragment received. if (dwReceivedPacketSize < 10) { *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, _T(__FUNCTION__) _T(" Packet is too small. EAP-TLS first fragmented packet should be at least 10B.")); @@ -181,7 +237,7 @@ bool eap::method_tls::process_request_packet( m_packet_req.m_code = (EapCode)pReceivedPacket->Code; m_packet_req.m_id = pReceivedPacket->Id; m_packet_req.m_flags = pReceivedPacket->Data[1]; - m_packet_req.m_data.reserve(*(DWORD*)(pReceivedPacket->Data + 2)); + m_packet_req.m_data.reserve(*(unsigned long*)(pReceivedPacket->Data + 2)); m_packet_req.m_data.assign(pReceivedPacket->Data + 6, pReceivedPacket->Data + dwReceivedPacketSize - 4); } else { // Mid fragment received. Append data. @@ -193,53 +249,63 @@ bool eap::method_tls::process_request_packet( m_packet_res.m_id = m_packet_req.m_id; m_packet_res.m_flags = 0; m_packet_res.m_data.clear(); + pEapOutput->fAllowNotifications = FALSE; pEapOutput->action = EapPeerMethodResponseActionSend; return true; } else if (!m_packet_req.m_data.empty()) { // Last fragment received. Append data. m_packet_req.m_data.insert(m_packet_req.m_data.end(), - pReceivedPacket->Data + (!(pReceivedPacket->Data[1] & tls_flags_length_incl) ? 2 : 6), // Should not include "Length" field (by RFC 5281: https://tools.ietf.org/html/rfc5281#section-9.2.2). Tolerate. + pReceivedPacket->Data + (!(pReceivedPacket->Data[1] & tls_req_flags_length_incl) ? 2 : 6), // Should not include "Length" field (by RFC 5281: https://tools.ietf.org/html/rfc5281#section-9.2.2). Tolerate. + pReceivedPacket->Data + dwReceivedPacketSize - 4); + } else { + // This is a complete non-fragmented packet. + m_packet_req.m_code = (EapCode)pReceivedPacket->Code; + m_packet_req.m_id = pReceivedPacket->Id; + m_packet_req.m_flags = pReceivedPacket->Data[1]; + m_packet_req.m_data.assign( + pReceivedPacket->Data + (!(pReceivedPacket->Data[1] & tls_req_flags_length_incl) ? 2 : 6), pReceivedPacket->Data + dwReceivedPacketSize - 4); } - if ( m_packet_req.m_code == EapCodeRequest && - m_packet_req.m_id == m_packet_res.m_id && - m_packet_req.m_data.empty() && - !(m_packet_req.m_flags & (tls_flags_length_incl | tls_flags_more_frag | tls_flags_start)) && - (m_packet_res.m_flags & tls_flags_more_frag )) + if ( m_packet_req.m_code == EapCodeRequest && + m_packet_req.m_id == m_packet_res.m_id && + m_packet_req.m_data.empty() && + !(m_packet_req.m_flags & (tls_req_flags_length_incl | tls_req_flags_more_frag | tls_req_flags_start)) && + (m_packet_res.m_flags & tls_res_flags_more_frag )) { // This is an ACK of our fragmented packet response. Send the next fragment. m_packet_res.m_id++; + pEapOutput->fAllowNotifications = FALSE; pEapOutput->action = EapPeerMethodResponseActionSend; return true; } switch (m_phase) { - case phase_handshake_start: { + case phase_client_hello: { // Is this an EAP-TLS Start packet? if (m_packet_req.m_code != EapCodeRequest) { *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, wstring_printf(_T(__FUNCTION__) _T(" Packet is not a request (expected: %x, received: %x)."), EapCodeRequest, m_packet_req.m_code).c_str()); return false; - } else if (!(m_packet_req.m_flags & tls_flags_start)) { - *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, wstring_printf(_T(__FUNCTION__) _T(" Packet is not EAP-TLS Start (expected: %x, received: %x)."), tls_flags_start, m_packet_req.m_flags).c_str()); + } else if (!(m_packet_req.m_flags & tls_req_flags_start)) { + *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, wstring_printf(_T(__FUNCTION__) _T(" Packet is not EAP-TLS Start (expected: %x, received: %x)."), tls_req_flags_start, m_packet_req.m_flags).c_str()); return false; } - //// Determine minimum EAP-TLS version supported by server and us. - //version_t ver_remote = (version_t)(m_packet_req.m_flags & tls_flags_ver_mask); - //m_version = std::min(ver_remote, version_0); - //m_module.log_event(&EAPMETHOD_HANDSHAKE_START1, event_data((DWORD)pReceivedPacket->Data[0]), event_data((unsigned char)m_version), event_data((unsigned char)ver_remote), event_data::blank); - // Build response packet. m_packet_res.m_code = EapCodeResponse; m_packet_res.m_id = m_packet_req.m_id; m_packet_res.m_flags = 0; + sanitizing_blob hello(make_client_hello()); + sanitizing_blob handshake(make_handshake(hello, false)); + m_packet_res.m_data.assign(handshake.begin(), handshake.end()); + pEapOutput->fAllowNotifications = FALSE; + pEapOutput->action = EapPeerMethodResponseActionSend; + // Save the client_hello message. + CryptHashData(m_hash_handshake_msgs_md5 , hello.data(), (DWORD)hello.size(), 0); + CryptHashData(m_hash_handshake_msgs_sha1, hello.data(), (DWORD)hello.size(), 0); - //if (!m_packet_res.create(EapCodeResponse, pReceivedPacket->Id, eap_type_tls, (BYTE)m_version)) { - // *ppEapError = m_module.make_error(GetLastError(), _T(__FUNCTION__) _T(" Error creating packet.")); - // return false; - //} + m_phase = phase_server_hello; break; } @@ -261,22 +327,22 @@ bool eap::method_tls::get_response_packet( assert(pSendPacket); UNREFERENCED_PARAMETER(ppEapError); - DWORD - size_data = (DWORD)m_packet_res.m_data.size(), + unsigned long + size_data = (unsigned long)m_packet_res.m_data.size(), size_packet = size_data + 6; - WORD size_packet_limit = (WORD)std::min(*pdwSendPacketSize, (WORD)-1); - BYTE *data_dst; + unsigned short size_packet_limit = (unsigned short)std::min(*pdwSendPacketSize, USHRT_MAX); + unsigned char *data_dst; - if (!(m_packet_res.m_flags & tls_flags_more_frag)) { + if (!(m_packet_res.m_flags & tls_res_flags_more_frag)) { // Not fragmented. if (size_packet <= size_packet_limit) { // No need to fragment the packet. - m_packet_res.m_flags &= ~tls_flags_length_incl; // No need to explicitly include the Length field either. + m_packet_res.m_flags &= ~tls_res_flags_length_incl; // No need to explicitly include the Length field either. data_dst = pSendPacket->Data + 2; } else { // But it should be fragmented. - m_packet_res.m_flags |= tls_flags_length_incl | tls_flags_more_frag; - *(DWORD*)(pSendPacket->Data + 2) = (DWORD)size_packet; + m_packet_res.m_flags |= tls_res_flags_length_incl | tls_res_flags_more_frag; + *(unsigned long*)(pSendPacket->Data + 2) = (unsigned long)size_packet; data_dst = pSendPacket->Data + 6; size_data = size_packet_limit - 10; size_packet = size_packet_limit; @@ -285,10 +351,10 @@ bool eap::method_tls::get_response_packet( // Continuing the fragmented packet... if (size_packet <= size_packet_limit) { // This is the last fragment. - m_packet_res.m_flags &= ~(tls_flags_length_incl | tls_flags_more_frag); + m_packet_res.m_flags &= ~(tls_res_flags_length_incl | tls_res_flags_more_frag); } else { // This is a mid fragment. - m_packet_res.m_flags &= ~tls_flags_length_incl; + m_packet_res.m_flags &= ~tls_res_flags_length_incl; size_data = size_packet_limit - 6; size_packet = size_packet_limit; } @@ -297,7 +363,7 @@ bool eap::method_tls::get_response_packet( pSendPacket->Code = (BYTE)m_packet_res.m_code; pSendPacket->Id = m_packet_res.m_id; - *(WORD*)pSendPacket->Length = htons((WORD)size_packet); + *(unsigned short*)pSendPacket->Length = htons((unsigned short)size_packet); pSendPacket->Data[0] = (BYTE)eap_type_tls; pSendPacket->Data[1] = m_packet_res.m_flags; memcpy(data_dst, m_packet_res.m_data.data(), size_data); @@ -305,3 +371,139 @@ bool eap::method_tls::get_response_packet( *pdwSendPacketSize = size_packet; return true; } + + +eap::sanitizing_blob eap::method_tls::make_client_hello() const +{ + unsigned long size_data; + sanitizing_blob msg; + msg.reserve( + 4 + // SSL header + (size_data = + 2 + // SSL version + sizeof(tls_random_t) + // Client random + 1 + // Session ID size + (unsigned long)m_session_id.size()) + // Session ID + 2 + // Length of cypher suite list + 2 + // Cyper suite list + 1 + // Length of compression suite + 1); // Compression suite + + // SSL header + unsigned long ssl_header = htonl(0x01000000 | size_data); // client_hello (0x01) + msg.insert(msg.end(), (unsigned char*)&ssl_header, (unsigned char*)(&ssl_header + 1)); + + // SSL version + msg.push_back(0x03); // SSL major version + msg.push_back(0x01); // SSL minor version + + // Client random + unsigned long time = htonl(m_random_client.time); + msg.insert(msg.end(), (unsigned char*)&time, (unsigned char*)(&time + 1)); + msg.insert(msg.end(), m_random_client.data, m_random_client.data + _countof(m_random_client.data)); // TODO: Check if byte order should be changed! + + // Session ID + msg.push_back((unsigned char)m_session_id.size()); + msg.insert(msg.end(), m_session_id.begin(), m_session_id.end()); + + // Cypher suite list + msg.push_back(0x00); // Length of cypher suite is two (in network-byte-order). + msg.push_back(0x02); // --^ + msg.push_back(0x00); // TLS_RSA_WITH_3DES_EDE_CBC_SHA (0x00 0x0a) + msg.push_back(0x0a); // --^ + + // Compression + msg.push_back(0x01); // Length of compression section + msg.push_back(0x00); // No compression (0) + + return msg; +} + + +eap::sanitizing_blob eap::method_tls::make_handshake(_In_ const sanitizing_blob &msg, _In_ bool encrypt) +{ + const unsigned char *msg_ptr; + unsigned short size_msg; + vector msg_enc; + + if (encrypt) { + // Create an unencrypted handshake first. + msg_enc = encrypt_message(make_handshake(msg, false)); + size_msg = (unsigned short)msg_enc.size(); + msg_ptr = msg_enc.data(); + } else { + size_msg = (unsigned short)msg.size(); + msg_ptr = msg.data(); + } + + // Create a handshake. + sanitizing_blob msg_h; + msg_h.reserve( + 1 + // SSL record type + 2 + // SSL version + 2 + // Message size + size_msg); // Message + + // SSL record type + msg_h.push_back(0x16); // handshake (0x16) + + // SSL version + msg_h.push_back(0x03); // SSL major version + msg_h.push_back(0x01); // SSL minor version + + // Message + unsigned short size_msg_n = htons(size_msg); + msg_h.insert(msg_h.end(), (unsigned char*)&size_msg_n, (unsigned char*)(&size_msg_n + 1)); + msg_h.insert(msg_h.end(), msg_ptr, msg_ptr + size_msg); + + return msg_h; +} + + +std::vector eap::method_tls::encrypt_message(_In_ const sanitizing_blob &msg) +{ + // Create a HMAC hash. + crypt_hash hash_hmac; + hash_hmac.create(m_cp, CALG_HMAC, m_key_hmac, 0); + static const HMAC_INFO s_hmac_info = { CALG_SHA1 }; + CryptSetHashParam(hash_hmac, HP_HMAC_INFO, (const BYTE*)&s_hmac_info, 0); + + // Hash sequence number. + unsigned __int64 seq_num = htonll(m_seq_num); + CryptHashData(hash_hmac, (const BYTE*)&seq_num, sizeof(seq_num), 0); + + // Hash data. + CryptHashData(hash_hmac, msg.data(), (DWORD)msg.size(), 0); + + // Calculate hash. + vector hmac; + CryptGetHashParam(hash_hmac, HP_HASHVAL, hmac, 0); + + unsigned long size = + (unsigned long)msg.size() - 5 + // TLS message without SSL header (SSL record type, SSL version, Message size) + 20 + // SHA-1 + 1; // Padding length + unsigned char padding = (8 - size) % 8; + size += padding; + + // Copy data. + sanitizing_blob enc; + enc.reserve(size); + enc.assign(msg.begin() + 5, msg.end()); + + // Append HMAC hash (in reversed byte order). + std::reverse(hmac.begin(), hmac.end()); + enc.insert(enc.end(), hmac.begin(), hmac.end()); + + // Append padding. + enc.insert(enc.end(), padding + 1, padding); + DWORD size2 = size; + CryptEncrypt(m_key_write, NULL, FALSE, 0, enc.data(), &size2, size); + + // Increment sequence number. + m_seq_num++; + + // Move to regular vector, now the data is encrypted. + std::vector enc2(std::move((std::vector&)enc)); + return enc2; +} diff --git a/lib/TLS/src/StdAfx.h b/lib/TLS/src/StdAfx.h index fb99dd8..97c9438 100644 --- a/lib/TLS/src/StdAfx.h +++ b/lib/TLS/src/StdAfx.h @@ -30,3 +30,6 @@ #include #include +#include + +#include diff --git a/lib/TTLS/include/Method.h b/lib/TTLS/include/Method.h index ace0995..2382e80 100644 --- a/lib/TTLS/include/Method.h +++ b/lib/TTLS/include/Method.h @@ -45,10 +45,10 @@ namespace eap namespace eap { enum ttls_flags_t { - ttls_flags_length_incl = tls_flags_length_incl, ///< Length included - ttls_flags_more_frag = tls_flags_more_frag, ///< More fragments - ttls_flags_start = tls_flags_start, ///< Start - ttls_flags_ver_mask = 0x07, ///< Version mask + ttls_flags_length_incl = tls_req_flags_length_incl, ///< Length included + ttls_flags_more_frag = tls_req_flags_more_frag, ///< More fragments + ttls_flags_start = tls_req_flags_start, ///< Start + ttls_flags_ver_mask = 0x07, ///< Version mask }; @@ -98,6 +98,22 @@ namespace eap /// \name Packet processing /// @{ + /// + /// Starts an EAP authentication session on the peer EAPHost using the EAP method. + /// + /// \sa [EapPeerBeginSession function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363600.aspx) + /// + /// \returns + /// - \c true if succeeded + /// - \c false otherwise. See \p ppEapError for details. + /// + virtual bool begin_session( + _In_ DWORD dwFlags, + _In_ const EapAttributes *pAttributeArray, + _In_ HANDLE hTokenImpersonateUser, + _In_ DWORD dwMaxSendPacketSize, + _Out_ EAP_ERROR **ppEapError); + /// /// Processes a packet received by EAPHost from a supplicant. /// diff --git a/lib/TTLS/src/Method.cpp b/lib/TTLS/src/Method.cpp index f5aaacf..a68125b 100644 --- a/lib/TTLS/src/Method.cpp +++ b/lib/TTLS/src/Method.cpp @@ -71,16 +71,26 @@ eap::method_ttls& eap::method_ttls::operator=(_Inout_ method_ttls &&other) } +bool eap::method_ttls::begin_session( + _In_ DWORD dwFlags, + _In_ const EapAttributes *pAttributeArray, + _In_ HANDLE hTokenImpersonateUser, + _In_ DWORD dwMaxSendPacketSize, + _Out_ EAP_ERROR **ppEapError) +{ + if (!m_outer.begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, dwMaxSendPacketSize, ppEapError)) + return false; + + return true; +} + + bool eap::method_ttls::process_request_packet( _In_bytecount_(dwReceivedPacketSize) const EapPacket *pReceivedPacket, _In_ DWORD dwReceivedPacketSize, _Out_ EapPeerMethodOutput *pEapOutput, _Out_ EAP_ERROR **ppEapError) { - // Initialize output. - pEapOutput->fAllowNotifications = TRUE; - pEapOutput->action = EapPeerMethodResponseActionDiscard; - // Is this a valid EAP-TTLS packet? if (dwReceivedPacketSize < 6) { *ppEapError = m_module.make_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, _T(__FUNCTION__) _T(" Packet is too small. EAP-%s packets should be at least 6B.")); @@ -90,6 +100,15 @@ bool eap::method_ttls::process_request_packet( return false; } + if (pReceivedPacket->Code == EapCodeRequest && (pReceivedPacket->Data[1] & ttls_flags_start)) { + // This is a start EAP-TTLS packet. + + // Determine minimum EAP-TTLS version supported by server and us. + version_t ver_remote = (version_t)(pReceivedPacket->Data[1] & ttls_flags_ver_mask); + m_version = std::min(ver_remote, version_0); + m_module.log_event(&EAPMETHOD_HANDSHAKE_START1, event_data((DWORD)eap_type_ttls), event_data((unsigned char)m_version), event_data((unsigned char)ver_remote), event_data::blank); + } + return m_outer.process_request_packet(pReceivedPacket, dwReceivedPacketSize, pEapOutput, ppEapError); } @@ -99,5 +118,13 @@ bool eap::method_ttls::get_response_packet( _Inout_ DWORD *pdwSendPacketSize, _Out_ EAP_ERROR **ppEapError) { - return m_outer.get_response_packet(pSendPacket, pdwSendPacketSize, ppEapError); + if (!m_outer.get_response_packet(pSendPacket, pdwSendPacketSize, ppEapError)) + return false; + + // Change packet type to EAP-TTLS, and add EAP-TTLS version. + pSendPacket->Data[0] = (BYTE)eap_type_ttls; + pSendPacket->Data[1] &= ~ttls_flags_ver_mask; + pSendPacket->Data[1] |= m_version; + + return true; } diff --git a/lib/TTLS/src/Module.cpp b/lib/TTLS/src/Module.cpp index a730539..9b15de4 100644 --- a/lib/TTLS/src/Module.cpp +++ b/lib/TTLS/src/Module.cpp @@ -90,17 +90,17 @@ bool eap::peer_ttls::get_identity( return false; } - // Unpack cached credentials. - credentials_ttls cred_in(*this); - if (dwUserDataSize && !unpack(cred_in, pUserData, dwUserDataSize, ppEapError)) - return false; - // Get method configuration. const config_provider &cfg_prov(cfg.m_providers.front()); const config_method_ttls *cfg_method = dynamic_cast(cfg_prov.m_methods.front().get()); assert(cfg_method); const config_method_pap *cfg_inner_pap = dynamic_cast(cfg_method->m_inner.get()); + // Unpack cached credentials. + credentials_ttls cred_in(*this); + if (dwUserDataSize && !unpack(cred_in, pUserData, dwUserDataSize, ppEapError)) + return false; + credentials_ttls cred_out(*this); // Determine credential storage target(s). Also used as user-friendly method name for logging. @@ -323,11 +323,6 @@ bool eap::peer_ttls::begin_session( _Out_ EAP_SESSION_HANDLE *phSession, _Out_ EAP_ERROR **ppEapError) { - UNREFERENCED_PARAMETER(dwFlags); - UNREFERENCED_PARAMETER(pAttributeArray); - UNREFERENCED_PARAMETER(hTokenImpersonateUser); - UNREFERENCED_PARAMETER(dwMaxSendPacketSize); - *phSession = NULL; // Allocate new session. @@ -337,10 +332,25 @@ bool eap::peer_ttls::begin_session( return false; } - // Begin the session. - if (!unpack(s->m_cfg, pConnectionData, dwConnectionDataSize, ppEapError) || - !unpack(s->m_cred, pUserData, dwUserDataSize, ppEapError)/* || - !s->begin(dwFlags, pAttributeArray, hTokenImpersonateUser, dwMaxSendPacketSize, ppEapError)*/) + // Unpack configuration. + config_provider_list cfg(*this); + if (!unpack(cfg, pConnectionData, dwConnectionDataSize, ppEapError)) + return false; + else if (cfg.m_providers.empty() || cfg.m_providers.front().m_methods.empty()) { + *ppEapError = make_error(ERROR_INVALID_PARAMETER, _T(__FUNCTION__) _T(" Configuration has no providers and/or methods.")); + return false; + } + + // Copy method configuration. + const config_provider &cfg_prov(cfg.m_providers.front()); + s->m_cfg = *dynamic_cast(cfg_prov.m_methods.front().get()); + + // Unpack credentials. + if (!unpack(s->m_cred, pUserData, dwUserDataSize, ppEapError)) + return false; + + // Initialize method. + if (!s->m_method.begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, dwMaxSendPacketSize, ppEapError)) return false; *phSession = s.release(); diff --git a/lib/WinStd b/lib/WinStd index d82a31e..e547372 160000 --- a/lib/WinStd +++ b/lib/WinStd @@ -1 +1 @@ -Subproject commit d82a31e543b86505380719f7dca478e20a66d294 +Subproject commit e547372197fd87b04008990b2d248aa9902d7f69