diff --git a/include/Common.props b/include/Common.props index adffbf5..68b5890 100644 --- a/include/Common.props +++ b/include/Common.props @@ -32,7 +32,7 @@ Level4 - _WIN32_WINNT=0x0600;ISOLATION_AWARE_ENABLED=1;CERT_CHAIN_PARA_HAS_EXTRA_FIELDS;%(PreprocessorDefinitions) + _WIN32_WINNT=0x0600;ISOLATION_AWARE_ENABLED=1;SECURITY_WIN32;CERT_CHAIN_PARA_HAS_EXTRA_FIELDS;%(PreprocessorDefinitions) Use StdAfx.h ProgramDatabase diff --git a/lib/EAPBase/src/Module.cpp b/lib/EAPBase/src/Module.cpp index 3947a5f..06968fd 100644 --- a/lib/EAPBase/src/Module.cpp +++ b/lib/EAPBase/src/Module.cpp @@ -102,6 +102,12 @@ EAP_ERROR* eap::module::make_error(_In_ std::exception &err) const return make_error(HRESULT_CODE(e.number()), what.c_str()); } + { + sec_runtime_error &e(dynamic_cast(err)); + if (&e) + return make_error(HRESULT_CODE(e.number()), what.c_str()); + } + { invalid_argument &e(dynamic_cast(err)); if (&e) diff --git a/lib/EAPBase/src/StdAfx.h b/lib/EAPBase/src/StdAfx.h index c0c1c30..ae21bec 100644 --- a/lib/EAPBase/src/StdAfx.h +++ b/lib/EAPBase/src/StdAfx.h @@ -30,5 +30,6 @@ #include #include +#include #include diff --git a/lib/Events/res/EventsETW.man b/lib/Events/res/EventsETW.man index 955ffc3..495ac59 100644 Binary files a/lib/Events/res/EventsETW.man and b/lib/Events/res/EventsETW.man differ diff --git a/lib/TLS/include/Config.h b/lib/TLS/include/Config.h index 4798702..7271632 100644 --- a/lib/TLS/include/Config.h +++ b/lib/TLS/include/Config.h @@ -167,10 +167,6 @@ namespace eap public: std::list m_trusted_root_ca; ///< Trusted root CAs - std::list m_server_names; ///< Acceptable authenticating server names - - // Following members are used for session resumptions. They are not exported/imported to XML. - sanitizing_blob m_session_id; ///< TLS session ID - tls_master_secret m_master_secret; ///< TLS master secret + std::list m_server_names; ///< Acceptable authenticating server names }; } diff --git a/lib/TLS/include/Method.h b/lib/TLS/include/Method.h index 819d240..589e762 100644 --- a/lib/TLS/include/Method.h +++ b/lib/TLS/include/Method.h @@ -36,6 +36,7 @@ namespace eap #include "../../EAPBase/include/Method.h" #include +#include #include #include @@ -127,19 +128,6 @@ namespace eap std::vector m_data; ///< Packet data }; -#pragma pack(push) -#pragma pack(1) - /// - /// TLS message - /// - struct message_header - { - tls_message_type_t type; ///< Message type (one of `message_type_t` constants) - tls_version version; ///< SSL/TLS version - unsigned char length[2]; ///< Message length (in network byte order) - }; -#pragma pack(pop) - public: /// /// Constructs an EAP method @@ -216,271 +204,30 @@ namespace eap /// @} protected: - /// \name Client handshake message generation - /// @{ + /// + /// Process handshake + /// + void process_handshake(); /// - /// Makes a TLS client hello message + /// Process application data /// - /// \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(); + void process_application_data(); /// - /// Makes a TLS client certificate message + /// Processes an application message /// - /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.4.6. Client Certificate)](https://tools.ietf.org/html/rfc5246#section-7.4.6) + /// \param[in] msg Application message data + /// \param[in] size_msg Application message data size /// - /// \returns Client certificate message - /// - sanitizing_blob make_client_cert() const; - - /// - /// Makes a TLS client key exchange message - /// - /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.4.7. Client Key Exchange Message )](https://tools.ietf.org/html/rfc5246#section-7.4.7) - /// - /// \param[in] pms Pre-master secret - /// - /// \returns Client key exchange message - /// - sanitizing_blob make_client_key_exchange(_In_ const tls_master_secret &pms) const; - - /// - /// Makes a TLS finished message - /// - /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter A.1. Record Layer)](https://tools.ietf.org/html/rfc5246#appendix-A.1) - /// - /// \returns Change cipher spec - /// - eap::sanitizing_blob make_finished() const; - - /// @} - - /// \name Client/Server handshake hashing - /// @{ - - /// - /// Hashes handshake message for "finished" message validation. - /// - /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.4.9. Finished)](https://tools.ietf.org/html/rfc5246#section-7.4.9) - /// - /// \param[in] data Data to hash - /// \param[in] size \p data size in bytes - /// - inline void hash_handshake(_In_count_(size) const void *data, _In_ size_t size) - { - CryptHashData(m_hash_handshake_msgs_md5 , (const BYTE*)data, (DWORD)size, 0); - CryptHashData(m_hash_handshake_msgs_sha1 , (const BYTE*)data, (DWORD)size, 0); - CryptHashData(m_hash_handshake_msgs_sha256, (const BYTE*)data, (DWORD)size, 0); - } - - /// - /// Hashes handshake message for "finished" message validation. - /// - /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.4.9. Finished)](https://tools.ietf.org/html/rfc5246#section-7.4.9) - /// - /// \param[in] data Data to hash - /// \param[in] size \p data size in bytes - /// - template - inline void hash_handshake(_In_ const std::vector<_Ty, _Ax> &data) - { - hash_handshake(data.data(), data.size() * sizeof(_Ty)); - } - - /// @} - - /// - /// Makes a TLS message - /// - /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter A.1. Record Layer)](https://tools.ietf.org/html/rfc5246#appendix-A.1) - /// - /// \param[in] type Message type - /// \param[inout] data Message data contents - /// - /// \returns TLS message message - /// - eap::sanitizing_blob make_message(_In_ tls_message_type_t type, _Inout_ sanitizing_blob &&data); - - /// @} - - /// \name Key derivation - /// @{ - - /// - /// Generates master session key - /// - /// \sa [The EAP-TLS Authentication Protocol (Chapter 2.3. Key Hierarchy)](https://tools.ietf.org/html/rfc5216#section-2.3) - /// - virtual void derive_msk(); - - /// @} - - /// \name Server message processing - /// @{ - - /// - /// Processes messages in a TLS packet - /// - /// \param[in] pck Packet data - /// \param[in] size_pck \p pck size in bytes - /// - void process_packet(_In_bytecount_(size_pck) const void *pck, _In_ size_t size_pck); - - /// - /// Processes a TLS change_cipher_spec message - /// - /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.1. Change Cipher Spec Protocol)](https://tools.ietf.org/html/rfc5246#section-7.1) - /// - /// \param[in] msg TLS change_cipher_spec message data - /// \param[in] msg_size TLS change_cipher_spec message data size - /// - virtual void process_change_cipher_spec(_In_bytecount_(msg_size) const void *msg, _In_ size_t msg_size); - - /// - /// Processes a TLS alert message - /// - /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 7.2. Alert Protocol)](https://tools.ietf.org/html/rfc5246#section-7.2) - /// - /// \param[in] msg TLS alert message data - /// \param[in] msg_size TLS alert message data size - /// - virtual void process_alert(_In_bytecount_(msg_size) const void *msg, _In_ size_t msg_size); - - /// - /// Processes a TLS handshake message - /// - /// \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 TLS handshake message data - /// \param[in] msg_size TLS handshake message data size - /// - virtual void process_handshake(_In_bytecount_(msg_size) const void *msg, _In_ size_t msg_size); - - /// - /// Processes a TLS application_data message - /// - /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 10. Application Data Protocol)](https://tools.ietf.org/html/rfc5246#section-10) - /// - /// \param[in] msg TLS application_data message data - /// \param[in] msg_size TLS application_data message data size - /// - virtual void process_application_data(_In_bytecount_(msg_size) const void *msg, _In_ size_t msg_size); - - ///// - ///// Processes a vendor-specific TLS message - ///// - ///// \note Please see `m_cipher_spec` member if the message data came encrypted. - ///// - ///// \param[in] type TLS message type - ///// \param[in] msg TLS message data - ///// \param[in] msg_size TLS message data size - ///// - //virtual void process_vendor_data(_In_ tls_message_type_t type, _In_bytecount_(msg_size) const void *msg, _In_ size_t msg_size); - - /// @} + virtual void process_application_data(_In_bytecount_(size_msg) const void *msg, _In_ size_t size_msg); +#ifndef SCHANNEL_SRV_CERT_CHECK /// /// Verifies server's certificate if trusted by configuration /// void verify_server_trust() const; - - /// \name Encryption - /// @{ - - /// - /// Encrypt TLS message - /// - /// \param[in] type Message type - /// \param[inout] data TLS message to encrypt - /// - void encrypt_message(_In_ tls_message_type_t type, _Inout_ sanitizing_blob &data); - - /// - /// Decrypt TLS message - /// - /// \param[in] type Original message type for HMAC verification - /// \param[inout] data TLS message to decrypt - /// - void decrypt_message(_In_ tls_message_type_t type, _Inout_ sanitizing_blob &data); - - /// @} - - /// \name Pseudo-random generation - /// @{ - - /// - /// Calculates pseudo-random P_hash data defined in RFC 5246 - /// - /// \sa [The Transport Layer Security (TLS) Protocol Version 1.1 (Chapter 5. HMAC and the Pseudorandom Function)](https://tools.ietf.org/html/rfc4346#section-5) - /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 5. HMAC and the Pseudorandom Function)](https://tools.ietf.org/html/rfc5246#section-5) - /// - /// \param[in] cp Handle of the cryptographics provider - /// \param[in] alg Hashing Algorithm to use (CALG_TLS1PRF = combination of MD5 and SHA-1, CALG_SHA_256...) - /// \param[in] secret Hashing secret key - /// \param[in] seed Random seed - /// \param[in] size_seed \p seed size - /// \param[in] size Number of bytes of pseudo-random data required - /// - /// \returns Generated pseudo-random data (\p size bytes) - /// - static sanitizing_blob prf( - _In_ HCRYPTPROV cp, - _In_ ALG_ID alg, - _In_ const tls_master_secret &secret, - _In_bytecount_(size_seed) const void *seed, - _In_ size_t size_seed, - _In_ size_t size); - - /// - /// Calculates pseudo-random P_hash data defined in RFC 5246 - /// - /// \sa [The Transport Layer Security (TLS) Protocol Version 1.1 (Chapter 5. HMAC and the Pseudorandom Function)](https://tools.ietf.org/html/rfc4346#section-5) - /// \sa [The Transport Layer Security (TLS) Protocol Version 1.2 (Chapter 5. HMAC and the Pseudorandom Function)](https://tools.ietf.org/html/rfc5246#section-5) - /// - /// \param[in] cp Handle of the cryptographics provider - /// \param[in] alg Hashing Algorithm to use (CALG_TLS1PRF = combination of MD5 and SHA-1, CALG_SHA_256...) - /// \param[in] secret Hashing secret key - /// \param[in] seed Random seed - /// \param[in] size Number of bytes of pseudo-random data required - /// - /// \returns Generated pseudo-random data (\p size bytes) - /// - template - inline static sanitizing_blob prf( - _In_ HCRYPTPROV cp, - _In_ ALG_ID alg, - _In_ const tls_master_secret &secret, - _In_ const std::vector<_Ty, _Ax> &seed, - _In_ size_t size) - { - return prf(cp, alg, secret, seed.data(), seed.size() * sizeof(_Ty), size); - } - - /// @} - - /// - /// Creates a key - /// - /// \sa [How to export and import plain text session keys by using CryptoAPI](https://support.microsoft.com/en-us/kb/228786) - /// - /// \param[in] cp Handle of the cryptographics provider - /// \param[in] alg Key algorithm - /// \param[in] key Key that decrypts \p secret - /// \param[in] secret Key data - /// \param[in] size_secret \p secret size - /// - /// \returns Key - /// - HCRYPTKEY create_key( - _In_ HCRYPTPROV cp, - _In_ ALG_ID alg, - _In_ HCRYPTKEY key, - _In_bytecount_(size_secret) const void *secret, - _In_ size_t size_secret); +#endif protected: credentials_tls &m_cred; ///< EAP-TLS user credentials @@ -488,47 +235,20 @@ namespace eap packet m_packet_req; ///< Request packet packet m_packet_res; ///< Response packet - winstd::crypt_prov m_cp; ///< Cryptography provider for general services - winstd::crypt_prov m_cp_enc_client; ///< Cryptography provider for encryption - winstd::crypt_prov m_cp_enc_server; ///< Cryptography provider for encryption - winstd::crypt_key m_key_exp1; ///< Key for importing derived keys - - tls_version m_tls_version; ///< TLS version in use - ALG_ID m_alg_prf; ///< Pseudo-random function algorithm in use - - tls_conn_state m_state_client; ///< Client TLS connection state - tls_conn_state m_state_client_pending; ///< Client TLS connection state (pending) - tls_conn_state m_state_server; ///< Server TLS connection state - tls_conn_state m_state_server_pending; ///< Server TLS connection state (pending) - - tls_master_secret m_master_secret; ///< TLS master secret - tls_random m_random_client; ///< Client random - tls_random m_random_server; ///< Server random - - tls_random m_key_mppe_client; ///< MS-MPPE-Recv-Key - tls_random m_key_mppe_server; ///< MS-MPPE-Send-Key - - sanitizing_blob m_session_id; ///< TLS session ID - - std::list m_server_cert_chain; ///< Server certificate chain - - winstd::crypt_hash m_hash_handshake_msgs_md5; ///< Running MD5 hash of handshake messages - winstd::crypt_hash m_hash_handshake_msgs_sha1; ///< Running SHA-1 hash of handshake messages - winstd::crypt_hash m_hash_handshake_msgs_sha256; ///< Running SHA-256 hash of handshake messages - - bool m_handshake[tls_handshake_type_max]; ///< Handshake flags (map od handshake messages received) + HANDLE m_user_ctx; ///< Handle to user context + winstd::tstring m_sc_target_name; ///< Schannel target name + winstd::sec_credentials m_sc_cred; ///< Schannel client credentials + std::vector m_sc_queue; ///< TLS data queue + winstd::sec_context m_sc_ctx; ///< Schannel context enum { phase_unknown = -1, ///< Unknown phase - phase_client_hello = 0, ///< Send client hello - phase_server_hello, ///< Wait for server hello - phase_change_cipher_spec, ///< Wait for change cipher spec - phase_application_data ///< Exchange application data + phase_handshake_init = 0, ///< Handshake initialize + phase_handshake_cont, ///< Handshake continue + phase_application_data, ///< Exchange application data + phase_shutdown, ///< Connection shut down } m_phase; ///< What phase is our communication at? - unsigned __int64 m_seq_num_client; ///< Sequence number for encrypting - unsigned __int64 m_seq_num_server; ///< Sequence number for decrypting - // The following members are required to avoid memory leakage in get_result() EAP_ATTRIBUTES m_eap_attr_desc; ///< EAP Radius attributes descriptor std::vector m_eap_attr; ///< EAP Radius attributes diff --git a/lib/TLS/src/Config.cpp b/lib/TLS/src/Config.cpp index cead6b3..56d27f1 100644 --- a/lib/TLS/src/Config.cpp +++ b/lib/TLS/src/Config.cpp @@ -75,8 +75,6 @@ eap::config_method_tls::config_method_tls(_In_ module &mod) : config_method_with eap::config_method_tls::config_method_tls(_In_ const config_method_tls &other) : m_trusted_root_ca(other.m_trusted_root_ca), m_server_names(other.m_server_names), - m_session_id(other.m_session_id), - m_master_secret(other.m_master_secret), config_method_with_cred(other) { } @@ -85,8 +83,6 @@ eap::config_method_tls::config_method_tls(_In_ const config_method_tls &other) : eap::config_method_tls::config_method_tls(_Inout_ config_method_tls &&other) : m_trusted_root_ca(std::move(other.m_trusted_root_ca)), m_server_names(std::move(other.m_server_names)), - m_session_id(std::move(other.m_session_id)), - m_master_secret(std::move(other.m_master_secret)), config_method_with_cred(std::move(other)) { } @@ -98,8 +94,6 @@ eap::config_method_tls& eap::config_method_tls::operator=(_In_ const config_meth (config_method_with_cred&)*this = other; m_trusted_root_ca = other.m_trusted_root_ca; m_server_names = other.m_server_names; - m_session_id = other.m_session_id; - m_master_secret = other.m_master_secret; } return *this; @@ -112,8 +106,6 @@ eap::config_method_tls& eap::config_method_tls::operator=(_Inout_ config_method_ (config_method_with_cred&&)*this = std::move(other); m_trusted_root_ca = std::move(other.m_trusted_root_ca); m_server_names = std::move(other.m_server_names); - m_session_id = std::move(other.m_session_id); - m_master_secret = std::move(other.m_master_secret); } return *this; @@ -161,10 +153,8 @@ void eap::config_method_tls::save(_In_ IXMLDOMDocument *pDoc, _In_ IXMLDOMNode * } // - for (list::const_iterator i = m_server_names.begin(), i_end = m_server_names.end(); i != i_end; ++i) { - wstring str; - MultiByteToWideChar(CP_UTF8, 0, i->c_str(), (int)i->length(), str); - if (FAILED(hr = eapxml::put_element_value(pDoc, pXmlElServerSideCredential, bstr(L"ServerName"), bstrNamespace, bstr(str)))) + for (list::const_iterator i = m_server_names.begin(), i_end = m_server_names.end(); i != i_end; ++i) { + if (FAILED(hr = eapxml::put_element_value(pDoc, pXmlElServerSideCredential, bstr(L"ServerName"), bstrNamespace, bstr(*i)))) throw com_runtime_error(hr, __FUNCTION__ " Error creating element."); } } @@ -231,12 +221,7 @@ void eap::config_method_tls::load(_In_ IXMLDOMNode *pConfigRoot) pXmlListServerIDs->get_item(j, &pXmlElServerID); bstr bstrServerID; pXmlElServerID->get_text(&bstrServerID); - - // Server names (FQDNs) are always ASCII. Hopefully. Convert them to UTF-8 anyway for consistent comparison. CP_ANSI varies. - string str; - WideCharToMultiByte(CP_UTF8, 0, bstrServerID, bstrServerID.length(), str, NULL, NULL); - - m_server_names.push_back(str); + m_server_names.push_back(wstring(bstrServerID)); } m_module.log_config((xpathServerSideCredential + L"/ServerName").c_str(), m_server_names); @@ -250,8 +235,6 @@ void eap::config_method_tls::operator<<(_Inout_ cursor_out &cursor) const config_method_with_cred::operator<<(cursor); cursor << m_trusted_root_ca; cursor << m_server_names ; - cursor << m_session_id ; - cursor << m_master_secret ; } @@ -260,9 +243,7 @@ size_t eap::config_method_tls::get_pk_size() const return config_method_with_cred::get_pk_size() + pksizeof(m_trusted_root_ca) + - pksizeof(m_server_names ) + - pksizeof(m_session_id ) + - pksizeof(m_master_secret ); + pksizeof(m_server_names ); } @@ -271,8 +252,6 @@ void eap::config_method_tls::operator>>(_Inout_ cursor_in &cursor) config_method_with_cred::operator>>(cursor); cursor >> m_trusted_root_ca; cursor >> m_server_names ; - cursor >> m_session_id ; - cursor >> m_master_secret ; } diff --git a/lib/TLS/src/Method.cpp b/lib/TLS/src/Method.cpp index 4addcf9..c11fb73 100644 --- a/lib/TLS/src/Method.cpp +++ b/lib/TLS/src/Method.cpp @@ -20,6 +20,8 @@ #include "StdAfx.h" +#pragma comment(lib, "Secur32.lib") + using namespace std; using namespace winstd; @@ -95,53 +97,29 @@ void eap::method_tls::packet::clear() eap::method_tls::method_tls(_In_ module &module, _In_ config_provider_list &cfg, _In_ credentials_tls &cred) : m_cred(cred), + m_user_ctx(NULL), m_phase(phase_unknown), - m_seq_num_client(0), - m_seq_num_server(0), m_blob_cfg(NULL), #ifdef EAP_USE_NATIVE_CREDENTIAL_CACHE m_blob_cred(NULL), #endif method(module, cfg, cred) { - m_tls_version = tls_version_1_2; - memset(m_handshake, 0, sizeof(m_handshake)); } eap::method_tls::method_tls(_Inout_ method_tls &&other) : - m_cred ( other.m_cred ), - m_packet_req (std::move(other.m_packet_req )), - m_packet_res (std::move(other.m_packet_res )), - m_cp (std::move(other.m_cp )), - m_cp_enc_client (std::move(other.m_cp_enc_client )), - m_cp_enc_server (std::move(other.m_cp_enc_server )), - m_key_exp1 (std::move(other.m_key_exp1 )), - m_tls_version (std::move(other.m_tls_version )), - m_alg_prf (std::move(other.m_alg_prf )), - m_state_client (std::move(other.m_state_client )), - m_state_client_pending (std::move(other.m_state_client_pending )), - m_state_server (std::move(other.m_state_server )), - m_state_server_pending (std::move(other.m_state_server_pending )), - m_master_secret (std::move(other.m_master_secret )), - m_random_client (std::move(other.m_random_client )), - m_random_server (std::move(other.m_random_server )), - m_key_mppe_client (std::move(other.m_key_mppe_client )), - m_key_mppe_server (std::move(other.m_key_mppe_server )), - m_session_id (std::move(other.m_session_id )), - m_server_cert_chain (std::move(other.m_server_cert_chain )), - 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_hash_handshake_msgs_sha256(std::move(other.m_hash_handshake_msgs_sha256)), - m_phase (std::move(other.m_phase )), - m_seq_num_client (std::move(other.m_seq_num_client )), - m_seq_num_server (std::move(other.m_seq_num_server )), - method (std::move(other )) + m_cred ( other.m_cred ), + m_packet_req (std::move(other.m_packet_req )), + m_packet_res (std::move(other.m_packet_res )), + m_user_ctx (std::move(other.m_user_ctx )), + m_sc_target_name(std::move(other.m_sc_target_name)), + m_sc_cred (std::move(other.m_sc_cred )), + m_sc_queue (std::move(other.m_sc_queue )), + m_sc_ctx (std::move(other.m_sc_ctx )), + m_phase (std::move(other.m_phase )), + method (std::move(other )) { - memcpy(m_handshake, other.m_handshake, sizeof(m_handshake)); -#ifdef _DEBUG - memset(other.m_handshake, 0, sizeof(m_handshake)); -#endif } @@ -161,37 +139,15 @@ eap::method_tls& eap::method_tls::operator=(_Inout_ method_tls &&other) { if (this != std::addressof(other)) { assert(std::addressof(m_cred) == std::addressof(other.m_cred)); // Move method with same credentials only! - (method&)*this = std::move(other ); - m_packet_req = std::move(other.m_packet_req ); - m_packet_res = std::move(other.m_packet_res ); - m_cp = std::move(other.m_cp ); - m_cp_enc_client = std::move(other.m_cp_enc_client ); - m_cp_enc_server = std::move(other.m_cp_enc_server ); - m_key_exp1 = std::move(other.m_key_exp1 ); - m_tls_version = std::move(other.m_tls_version ); - m_alg_prf = std::move(other.m_alg_prf ); - m_state_client = std::move(other.m_state_client ); - m_state_client_pending = std::move(other.m_state_client_pending ); - m_state_server = std::move(other.m_state_server ); - m_state_server_pending = std::move(other.m_state_server_pending ); - m_master_secret = std::move(other.m_master_secret ); - m_random_client = std::move(other.m_random_client ); - m_random_server = std::move(other.m_random_server ); - m_key_mppe_client = std::move(other.m_key_mppe_client ); - m_key_mppe_server = std::move(other.m_key_mppe_server ); - m_session_id = std::move(other.m_session_id ); - m_server_cert_chain = std::move(other.m_server_cert_chain ); - 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_hash_handshake_msgs_sha256 = std::move(other.m_hash_handshake_msgs_sha256); - m_phase = std::move(other.m_phase ); - m_seq_num_client = std::move(other.m_seq_num_client ); - m_seq_num_server = std::move(other.m_seq_num_server ); - - memcpy(m_handshake, other.m_handshake, sizeof(m_handshake)); -#ifdef _DEBUG - memset(other.m_handshake, 0, sizeof(m_handshake)); -#endif + (method&)*this = std::move(other ); + m_packet_req = std::move(other.m_packet_req ); + m_packet_res = std::move(other.m_packet_res ); + m_user_ctx = std::move(other.m_user_ctx ); + m_sc_target_name = std::move(other.m_sc_target_name); + m_sc_cred = std::move(other.m_sc_cred ); + m_sc_queue = std::move(other.m_sc_queue ); + m_sc_ctx = std::move(other.m_sc_ctx ); + m_phase = std::move(other.m_phase ); } return *this; @@ -206,25 +162,61 @@ void eap::method_tls::begin_session( { method::begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, dwMaxSendPacketSize); - // Create cryptographics provider for support needs (handshake hashing, client random, temporary keys...). - if (!m_cp.create(NULL, NULL, PROV_RSA_AES)) - throw win_runtime_error(__FUNCTION__ " Error creating cryptographics provider."); - - // Microsoft CryptoAPI does not support importing clear text session keys. - // Therefore, we trick it to say the session key is "encrypted" with an exponent-of-one key. - if (!m_key_exp1.create_exp1(m_cp, AT_KEYEXCHANGE)) - throw win_runtime_error(__FUNCTION__ " Error creating exponent-of-one key."); + m_user_ctx = hTokenImpersonateUser; + user_impersonator impersonating(m_user_ctx); + // Get method configuration. if (m_cfg.m_providers.empty() || m_cfg.m_providers.front().m_methods.empty()) throw invalid_argument(__FUNCTION__ " Configuration has no providers and/or methods."); - const config_provider &cfg_prov(m_cfg.m_providers.front()); const config_method_tls *cfg_method = dynamic_cast(cfg_prov.m_methods.front().get()); assert(cfg_method); - // Restore previous session ID and master secret. We might get lucky. - m_session_id = cfg_method->m_session_id; - m_master_secret = cfg_method->m_master_secret; + // Build (expected) server name(s) for Schannel. + m_sc_target_name.clear(); + for (list::const_iterator name = cfg_method->m_server_names.cbegin(), name_end = cfg_method->m_server_names.cend(); name != name_end; ++name) { + if (name != cfg_method->m_server_names.cbegin()) + m_sc_target_name += _T(';'); +#ifdef _UNICODE + m_sc_target_name.insert(m_sc_target_name.end(), name->begin(), name->end()); +#else + string buf; + WideCharToMultiByte(CP_ACP, 0, name->c_str(), -1, buf, NULL, NULL); + m_sc_target_name.insert(m_sc_target_name.end(), buf.begin(), buf.end()); +#endif + } + + // Prepare client credentials for Schannel. + PCCERT_CONTEXT certs[] = { m_cred.m_cert ? m_cred.m_cert : NULL }; + SCHANNEL_CRED cred = { + SCHANNEL_CRED_VERSION, // dwVersion + m_cred.m_cert ? 1 : 0, // cCreds + certs, // paCred + NULL, // hRootStore: Not valid for client credentials + 0, // cMappers + NULL, // aphMappers + 0, // cSupportedAlgs: Use system configured default + NULL, // palgSupportedAlgs: Use system configured default + 0, // grbitEnabledProtocols: Use default + 0, // dwMinimumCipherStrength: Use system configured default + 0, // dwMaximumCipherStrength: Use system configured default + 0, // dwSessionLifespan: Use system configured default = 10hr +#ifdef SCHANNEL_SRV_CERT_CHECK + SCH_CRED_AUTO_CRED_VALIDATION | // dwFlags: Let Schannel verify server certificate +#else + SCH_CRED_MANUAL_CRED_VALIDATION | // dwFlags: Prevent Schannel verify server certificate (we want to use custom root CA store and multiple name checking) +#endif + SCH_CRED_CACHE_ONLY_URL_RETRIEVAL_ON_CREATE | // dwFlags: Do not attempt online revocation check - we do not expect to have network connection yet + SCH_CRED_IGNORE_NO_REVOCATION_CHECK | // dwFlags: Ignore no-revocation-check errors (TODO: Test if this flag is required.) + SCH_CRED_IGNORE_REVOCATION_OFFLINE | // dwFlags: Ignore offline-revocation errors - we do not expect to have network connection yet + SCH_CRED_NO_DEFAULT_CREDS | // dwFlags: If client certificate we provided is not acceptable, do not try to select one on your own + (cfg_method->m_server_names.empty() ? SCH_CRED_NO_SERVERNAME_CHECK : 0) | // dwFlags: When no expected server name is given, do not do the server name check. + 0x00400000 /*SCH_USE_STRONG_CRYPTO*/, // dwFlags: Do not use broken ciphers + 0 // dwCredFormat + }; + SECURITY_STATUS stat = m_sc_cred.acquire(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &cred); + if (FAILED(stat)) + throw sec_runtime_error(stat, __FUNCTION__ " Error acquiring Schannel credentials handle."); } @@ -318,155 +310,27 @@ void eap::method_tls::process_request_packet( if (pReceivedPacket->Code == EapCodeRequest && (m_packet_req.m_flags & flags_req_start)) { // This is the EAP-TLS start message: (re)initialize method. - m_module.log_event(&EAPMETHOD_TLS_HANDSHAKE_START2, event_data((unsigned int)eap_type_tls), event_data::blank); - m_phase = phase_client_hello; - } else { - // Process the packet. - memset(m_handshake, 0, sizeof(m_handshake)); - m_packet_res.m_data.clear(); - process_packet(m_packet_req.m_data.data(), m_packet_req.m_data.size()); - } + m_phase = phase_handshake_init; + m_sc_queue.assign(m_packet_req.m_data.begin(), m_packet_req.m_data.end()); + } else + m_sc_queue.insert(m_sc_queue.end(), m_packet_req.m_data.begin(), m_packet_req.m_data.end()); switch (m_phase) { - case phase_client_hello: { - m_tls_version = tls_version_1_2; - - m_key_mppe_client.clear(); - m_key_mppe_server.clear(); - - m_server_cert_chain.clear(); - - // Create handshake hashing objects. - if (!m_hash_handshake_msgs_md5.create(m_cp, CALG_MD5)) - throw win_runtime_error(__FUNCTION__ " Error creating MD5 hashing object."); - if (!m_hash_handshake_msgs_sha1.create(m_cp, CALG_SHA1)) - throw win_runtime_error(__FUNCTION__ " Error creating SHA-1 hashing object."); - if (!m_hash_handshake_msgs_sha256.create(m_cp, CALG_SHA_256)) - throw win_runtime_error(__FUNCTION__ " Error creating SHA-256 hashing object."); - - m_seq_num_client = 0; - m_seq_num_server = 0; - - // Build client hello packet. - sanitizing_blob msg_client_hello(make_message(tls_message_type_handshake, make_client_hello())); - m_packet_res.m_data.insert(m_packet_res.m_data.end(), msg_client_hello.begin(), msg_client_hello.end()); - - m_phase = phase_server_hello; - break; - } - - case phase_server_hello: { - if (!m_handshake[tls_handshake_type_server_hello]) - throw win_runtime_error(__FUNCTION__ " Server did not hello back. No server random! What cipher to use?"); - - // Create cryptographics provider (based on server selected cipher?). - if (!m_cp_enc_client.create(NULL, NULL, PROV_RSA_AES)) - throw win_runtime_error(__FUNCTION__ " Error creating cryptographics provider."); - - if (m_handshake[tls_handshake_type_certificate]) { - // Do we trust this server? - if (m_server_cert_chain.empty()) - throw win_runtime_error(ERROR_ENCRYPTION_FAILED, __FUNCTION__ " Server sent an empty certificate (chain)."); - verify_server_trust(); - } - - if (m_handshake[tls_handshake_type_certificate_request]) { - // Client certificate requested. - sanitizing_blob msg_client_cert(make_message(tls_message_type_handshake, make_client_cert())); - m_packet_res.m_data.insert(m_packet_res.m_data.end(), msg_client_cert.begin(), msg_client_cert.end()); - } - - if (m_handshake[tls_handshake_type_server_hello_done]) { - if (m_server_cert_chain.empty()) - throw win_runtime_error(ERROR_ENCRYPTION_FAILED, __FUNCTION__ " Can not do a client key exchange without a server public key (missing server certificate)."); - - // Generate pre-master secret. PMS will get sanitized in its destructor when going out-of-scope. - // Always use latest supported version by client (not negotiated one, to detect version rollback attacks). - tls_master_secret pms(m_cp_enc_client, tls_version_1_2); - - // Derive master secret. - static const unsigned char s_label[] = "master secret"; - sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1); - seed.insert(seed.end(), (const unsigned char*)&m_random_client, (const unsigned char*)(&m_random_client + 1)); - seed.insert(seed.end(), (const unsigned char*)&m_random_server, (const unsigned char*)(&m_random_server + 1)); - memcpy(&m_master_secret, prf(m_cp_enc_client, m_alg_prf, pms, seed, sizeof(tls_master_secret)).data(), sizeof(tls_master_secret)); - - // Create client key exchange message, and append to packet. - sanitizing_blob msg_client_key_exchange(make_message(tls_message_type_handshake, make_client_key_exchange(pms))); - m_packet_res.m_data.insert(m_packet_res.m_data.end(), msg_client_key_exchange.begin(), msg_client_key_exchange.end()); - } - - if (m_handshake[tls_handshake_type_certificate_request]) { - // TODO: Create and append client certificate verify message! - } - - // Append change cipher spec to packet. - sanitizing_blob ccs(make_message(tls_message_type_change_cipher_spec, sanitizing_blob(1, 1))); - m_packet_res.m_data.insert(m_packet_res.m_data.end(), ccs.begin(), ccs.end()); - - // Adopt server state as client pending. - // If server already send the change cipher spec, use active server state. Otherwise pending. - m_state_client_pending = m_state_server.m_alg_encrypt ? m_state_server : m_state_server_pending; - - // Derive client side keys. - static const unsigned char s_label[] = "key expansion"; - sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1); - seed.insert(seed.end(), (const unsigned char*)&m_random_server, (const unsigned char*)(&m_random_server + 1)); - seed.insert(seed.end(), (const unsigned char*)&m_random_client, (const unsigned char*)(&m_random_client + 1)); - sanitizing_blob key_block(prf(m_cp_enc_client, m_alg_prf, m_master_secret, seed, - 2*m_state_client_pending.m_size_mac_key + // client_write_MAC_secret & server_write_MAC_secret (SHA1) - 2*m_state_client_pending.m_size_enc_key + // client_write_key & server_write_key - 2*m_state_client_pending.m_size_enc_iv )); // client_write_IV & server_write_IV - const unsigned char *_key_block = key_block.data(); - - // client_write_MAC_secret - m_state_client_pending.m_padding_hmac = hmac_padding(m_cp_enc_client, m_state_client_pending.m_alg_mac, _key_block, m_state_client_pending.m_size_mac_key); - _key_block += m_state_client_pending.m_size_mac_key; - - // server_write_MAC_secret - _key_block += m_state_client_pending.m_size_mac_key; - - // client_write_key - m_state_client_pending.m_key = create_key(m_cp_enc_client, m_state_client_pending.m_alg_encrypt, m_key_exp1, _key_block, m_state_client_pending.m_size_enc_key); - _key_block += m_state_client_pending.m_size_enc_key; - - // server_write_key - _key_block += m_state_client_pending.m_size_enc_key; - - if (m_state_client_pending.m_size_enc_iv && m_tls_version < tls_version_1_1) { - // client_write_IV - if (!CryptSetKeyParam(m_state_client_pending.m_key, KP_IV, _key_block, 0)) - throw win_runtime_error(__FUNCTION__ " Error setting client_write_IV."); - _key_block += m_state_client_pending.m_size_enc_iv; - } - - // Accept client pending state as current client state. - m_state_client = std::move(m_state_client_pending); - - // Create finished message, and append to packet. - sanitizing_blob msg_finished(make_message(tls_message_type_handshake, make_finished())); - m_packet_res.m_data.insert(m_packet_res.m_data.end(), msg_finished.begin(), msg_finished.end()); - - m_phase = m_handshake[tls_handshake_type_finished] ? phase_application_data : phase_change_cipher_spec; - break; - } - - case phase_change_cipher_spec: - // Wait in this phase until server sends change cipher spec and finish. - if (m_state_server.m_alg_encrypt && m_handshake[tls_handshake_type_finished]) - m_phase = phase_application_data; + case phase_handshake_init: + case phase_handshake_cont: + process_handshake(); break; case phase_application_data: - if (m_handshake[tls_handshake_type_hello_request]) - m_phase = phase_client_hello; + process_application_data(); + break; } + pEapOutput->fAllowNotifications = TRUE; + pEapOutput->action = EapPeerMethodResponseActionSend; + // EAP-Request packet was processed. Clear its data since we use the absence of data to detect first of fragmented message packages. m_packet_req.m_data.clear(); - - pEapOutput->fAllowNotifications = FALSE; - pEapOutput->action = EapPeerMethodResponseActionSend; } @@ -538,22 +402,25 @@ void eap::method_tls::get_result( switch (reason) { case EapPeerMethodResultSuccess: { - if (!m_handshake[tls_handshake_type_finished]) - throw invalid_argument(__FUNCTION__ " Premature success."); - m_module.log_event(&EAPMETHOD_TLS_SUCCESS, event_data((unsigned int)eap_type_tls), event_data::blank); // Derive MSK/EMSK for line encryption. - derive_msk(); + SecPkgContext_EapKeyBlock key_block; + SECURITY_STATUS status = QueryContextAttributes(m_sc_ctx, SECPKG_ATTR_EAP_KEY_BLOCK, &key_block); + if (FAILED(status)) + throw sec_runtime_error(status, __FUNCTION__ "Error generating MSK in Schannel."); + const unsigned char *_key_block = key_block.rgbKeys; // Fill array with RADIUS attributes. eap_attr a; m_eap_attr.clear(); m_eap_attr.reserve(3); - a.create_ms_mppe_key(16, (LPCBYTE)&m_key_mppe_client, sizeof(tls_random)); + a.create_ms_mppe_key(16, _key_block, sizeof(tls_random)); m_eap_attr.push_back(std::move(a)); - a.create_ms_mppe_key(17, (LPCBYTE)&m_key_mppe_server, sizeof(tls_random)); + _key_block += sizeof(tls_random); + a.create_ms_mppe_key(17, _key_block, sizeof(tls_random)); m_eap_attr.push_back(std::move(a)); + _key_block += sizeof(tls_random); m_eap_attr.push_back(eap_attr::blank); m_eap_attr_desc.dwNumberOfAttributes = (DWORD)m_eap_attr.size(); @@ -566,20 +433,12 @@ void eap::method_tls::get_result( ppResult->fIsSuccess = TRUE; ppResult->dwFailureReasonCode = ERROR_SUCCESS; - // Update configuration with session resumption data and prepare BLOB. - cfg_method->m_session_id = m_session_id; - cfg_method->m_master_secret = m_master_secret; - break; } case EapPeerMethodResultFailure: m_module.log_event(&EAPMETHOD_TLS_FAILURE, event_data((unsigned int)eap_type_tls), event_data::blank); - // Clear session resumption data. - cfg_method->m_session_id.clear(); - cfg_method->m_master_secret.clear(); - // Mark credentials as failed, so GUI can re-prompt user. cfg_method->m_auth_failed = true; @@ -611,594 +470,219 @@ void eap::method_tls::get_result( } -eap::sanitizing_blob eap::method_tls::make_client_hello() +void eap::method_tls::process_handshake() { - static const unsigned char s_cipher_suite[] = { - 0x00, 0x2f, // TLS_RSA_WITH_AES_128_CBC_SHA (required by TLS 1.2) - 0x00, 0x0a, // TLS_RSA_WITH_3DES_EDE_CBC_SHA (required by EAP-TLS) - }; - static const unsigned char s_compression_suite[] = { - 0x00, // No compression - }; - - size_t size_data; - sanitizing_blob msg; - msg.reserve( - 4 + // SSL header - (size_data = - 2 + // SSL version - sizeof(tls_random) + // Client random - 1 + // Session ID size - m_session_id.size() + // Session ID - 2 + // Length of cypher suite list - sizeof(s_cipher_suite) + // Cipher suite list - 1 + // Length of compression suite - sizeof(s_compression_suite))); // Compression suite - - // SSL header - assert(size_data <= 0xffffff); - unsigned int ssl_header = htonl((tls_handshake_type_client_hello << 24) | (unsigned int)size_data); - msg.insert(msg.end(), (unsigned char*)&ssl_header, (unsigned char*)(&ssl_header + 1)); - - // SSL version - msg.insert(msg.end(), (unsigned char*)&m_tls_version, (unsigned char*)(&m_tls_version + 1)); - - // Generate client random and add it to the message - m_random_client.randomize(m_cp); - msg.insert(msg.end(), (unsigned char*)&m_random_client, (unsigned char*)(&m_random_client + 1)); - - // Session ID - assert(m_session_id.size() <= 32); - msg.push_back((unsigned char)m_session_id.size()); - msg.insert(msg.end(), m_session_id.begin(), m_session_id.end()); - - // Cypher suite list - unsigned short size_cipher_suite2 = htons((unsigned short)sizeof(s_cipher_suite)); - msg.insert(msg.end(), (unsigned char*)&size_cipher_suite2, (unsigned char*)(&size_cipher_suite2 + 1)); - msg.insert(msg.end(), s_cipher_suite, s_cipher_suite + _countof(s_cipher_suite)); - - // Compression - msg.push_back((unsigned char)sizeof(s_compression_suite)); - msg.insert(msg.end(), s_compression_suite, s_compression_suite + _countof(s_compression_suite)); - - return msg; -} - - -eap::sanitizing_blob eap::method_tls::make_client_cert() const -{ - // Select client certificate. - PCCERT_CONTEXT cert = m_cred.m_cert ? m_cred.m_cert : NULL; - - size_t size_data, size_list; - sanitizing_blob msg; - msg.reserve( - 4 + // SSL header - (size_data = - 3 + // Certificate list size - (size_list = - (cert ? 3 + cert->cbCertEncoded : 0)))); // Certificate (optional) - - // SSL header - assert(size_data <= 0xffffff); - unsigned int ssl_header = htonl((tls_handshake_type_certificate << 24) | (unsigned int)size_data); - msg.insert(msg.end(), (unsigned char*)&ssl_header, (unsigned char*)(&ssl_header + 1)); - - // List size - assert(size_list <= 0xffffff); - msg.push_back((unsigned char)((size_list >> 16) & 0xff)); - msg.push_back((unsigned char)((size_list >> 8) & 0xff)); - msg.push_back((unsigned char)((size_list ) & 0xff)); - - if (cert) { - // Cert size - assert(cert->cbCertEncoded <= 0xffffff); - msg.push_back((unsigned char)((cert->cbCertEncoded >> 16) & 0xff)); - msg.push_back((unsigned char)((cert->cbCertEncoded >> 8) & 0xff)); - msg.push_back((unsigned char)((cert->cbCertEncoded ) & 0xff)); - - msg.insert(msg.end(), cert->pbCertEncoded, cert->pbCertEncoded + cert->cbCertEncoded); - } - - return msg; -} - - -eap::sanitizing_blob eap::method_tls::make_client_key_exchange(_In_ const tls_master_secret &pms) const -{ - // Encrypt pre-master key with server public key first. - sanitizing_blob pms_enc((const unsigned char*)&pms, (const unsigned char*)(&pms + 1)); - crypt_key key; - if (!key.import_public(m_cp_enc_client, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(m_server_cert_chain.front()->pCertInfo->SubjectPublicKeyInfo))) - throw win_runtime_error(__FUNCTION__ " Error importing server's public key."); - if (!CryptEncrypt(key, NULL, TRUE, 0, pms_enc)) - throw win_runtime_error(__FUNCTION__ " Error encrypting PMS."); - - size_t size_data, size_pms_enc = pms_enc.size(); - sanitizing_blob msg; - msg.reserve( - 4 + // SSL header - (size_data = - 2 + // Encrypted pre master secret size - size_pms_enc)); // Encrypted pre master secret - - // SSL header - assert(size_data <= 0xffffff); - unsigned int ssl_header = htonl((tls_handshake_type_client_key_exchange << 24) | (unsigned int)size_data); - msg.insert(msg.end(), (unsigned char*)&ssl_header, (unsigned char*)(&ssl_header + 1)); - - // Encrypted pre master secret size - assert(size_pms_enc <= 0xffff); - msg.push_back((unsigned char)((size_pms_enc >> 8) & 0xff)); - msg.push_back((unsigned char)((size_pms_enc ) & 0xff)); - - // Encrypted pre master secret -#ifdef _HOST_LOW_ENDIAN - std::reverse(pms_enc.begin(), pms_enc.end()); -#endif - msg.insert(msg.end(), pms_enc.begin(), pms_enc.end()); - - return msg; -} - - -eap::sanitizing_blob eap::method_tls::make_finished() const -{ - sanitizing_blob msg; - msg.reserve( - 4 + // SSL header - 12); // verify_data is 12B - - // SSL header - unsigned int ssl_header = htonl((unsigned int)(tls_handshake_type_finished << 24) | 12); - msg.insert(msg.end(), (unsigned char*)&ssl_header, (unsigned char*)(&ssl_header + 1)); - - // Create label + hash MD5 + hash SHA-1 seed. - crypt_hash hash; - static const unsigned char s_label[] = "client finished"; - sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1), hash_data; - if (m_tls_version < tls_version_1_2) { - hash = m_hash_handshake_msgs_md5; // duplicate - if (!CryptGetHashParam(hash, HP_HASHVAL, hash_data, 0)) - throw win_runtime_error(__FUNCTION__ " Error finishing MD5 hash calculation."); - seed.insert(seed.end(), hash_data.begin(), hash_data.end()); - hash = m_hash_handshake_msgs_sha1; // duplicate - if (!CryptGetHashParam(hash, HP_HASHVAL, hash_data, 0)) - throw win_runtime_error(__FUNCTION__ " Error finishing SHA-1 hash calculation."); - seed.insert(seed.end(), hash_data.begin(), hash_data.end()); - } else { - hash = m_hash_handshake_msgs_sha256; // duplicate - if (!CryptGetHashParam(hash, HP_HASHVAL, hash_data, 0)) - throw win_runtime_error(__FUNCTION__ " Error finishing SHA-256 hash calculation."); - seed.insert(seed.end(), hash_data.begin(), hash_data.end()); - } - sanitizing_blob verify(prf(m_cp_enc_client, m_alg_prf, m_master_secret, seed, 12)); - msg.insert(msg.end(), verify.begin(), verify.end()); - - return msg; -} - - -eap::sanitizing_blob eap::method_tls::make_message(_In_ tls_message_type_t type, _Inout_ sanitizing_blob &&data) -{ - if (type == tls_message_type_handshake) - hash_handshake(data); - - if (m_state_client.m_alg_encrypt) - encrypt_message(type, data); - - size_t size_data = data.size(); - assert(size_data <= 0xffff); - message_header hdr = { - type, // SSL record type + // Prepare input buffer(s). + SecBuffer buf_in[] = { { - m_tls_version.major, // SSL major version - m_tls_version.minor, // SSL minor version + (unsigned long)m_sc_queue.size(), + SECBUFFER_TOKEN, + m_sc_queue.data() }, - { - // Data length (unencrypted, network byte order) - (unsigned char)((size_data >> 8) & 0xff), - (unsigned char)((size_data ) & 0xff), - } + { 0, SECBUFFER_EMPTY, NULL }, + }; + SecBufferDesc buf_in_desc = { + SECBUFFER_VERSION, + _countof(buf_in), + buf_in }; - sanitizing_blob msg; - msg.reserve(sizeof(message_header) + size_data); - msg.assign((const unsigned char*)&hdr, (const unsigned char*)(&hdr + 1)); - msg.insert(msg.end(), data.begin(), data.end()); - return msg; -} + // Prepare output buffer(s). + SecBuffer buf_out[] = { + { 0, SECBUFFER_TOKEN, NULL }, + { 0, SECBUFFER_ALERT, NULL }, + }; + sec_buffer_desc buf_out_desc(buf_out, _countof(buf_out)); + user_impersonator impersonating(m_user_ctx); + SECURITY_STATUS status; + if (m_phase == phase_handshake_init) { + m_module.log_event(&EAPMETHOD_TLS_HANDSHAKE_START2, event_data((unsigned int)eap_type_tls), event_data::blank); + status = m_sc_ctx.initialize( + m_sc_cred, + !m_sc_target_name.empty() ? m_sc_target_name.c_str() : NULL, + ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_INTEGRITY | ISC_REQ_STREAM | /*ISC_REQ_USE_SUPPLIED_CREDS |*/ ISC_REQ_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY, + 0, + &buf_in_desc, + &buf_out_desc); + } else { + status = m_sc_ctx.process( + m_sc_cred, + !m_sc_target_name.empty() ? m_sc_target_name.c_str() : NULL, + ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_INTEGRITY | ISC_REQ_STREAM | /*ISC_REQ_USE_SUPPLIED_CREDS |*/ ISC_REQ_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY, + 0, + &buf_in_desc, + &buf_out_desc); + } -void eap::method_tls::derive_msk() -{ - static const unsigned char s_label[] = "client EAP encryption"; - sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1); - seed.insert(seed.end(), (const unsigned char*)&m_random_client, (const unsigned char*)(&m_random_client + 1)); - seed.insert(seed.end(), (const unsigned char*)&m_random_server, (const unsigned char*)(&m_random_server + 1)); - sanitizing_blob key_block(prf(m_cp_enc_client, m_alg_prf, m_master_secret, seed, 2*sizeof(tls_random))); - const unsigned char *_key_block = key_block.data(); +#ifndef SCHANNEL_SRV_CERT_CHECK + if (status == SEC_E_OK) + verify_server_trust(); +#endif - // MS-MPPE-Recv-Key - memcpy(&m_key_mppe_client, _key_block, sizeof(tls_random)); - _key_block += sizeof(tls_random); + if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED) { + // Send Schannel's token via EAP. + assert(buf_out[0].BufferType == SECBUFFER_TOKEN); + assert(m_sc_ctx.m_attrib & ISC_RET_ALLOCATED_MEMORY); + m_packet_res.m_data.assign((const unsigned char*)buf_out[0].pvBuffer, (const unsigned char*)buf_out[0].pvBuffer + buf_out[0].cbBuffer); + if (buf_in[1].BufferType == SECBUFFER_EXTRA) { + // Server appended extra data. Process it. + process_application_data(&*(m_sc_queue.end() - buf_in[1].cbBuffer), buf_in[1].cbBuffer); + } + m_sc_queue.clear(); - // MS-MPPE-Send-Key - memcpy(&m_key_mppe_server, _key_block, sizeof(tls_random)); - _key_block += sizeof(tls_random); -} - - -void eap::method_tls::process_packet(_In_bytecount_(size_pck) const void *_pck, _In_ size_t size_pck) -{ - sanitizing_blob data; - - for (const unsigned char *pck = (const unsigned char*)_pck, *pck_end = pck + size_pck; pck < pck_end; ) { - if (pck + 5 > pck_end) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete message header."); - const message_header *hdr = (const message_header*)pck; - const unsigned char - *msg = (const unsigned char*)(hdr + 1), - *msg_end = msg + ntohs(*(unsigned short*)hdr->length); - if (msg_end > pck_end) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete message data."); - - if (hdr->version >= tls_version_1_0) { - // Process TLS message. - switch (hdr->type) { - case tls_message_type_change_cipher_spec: - if (m_state_server.m_alg_encrypt) { - sanitizing_blob msg_dec(msg, msg_end); - decrypt_message(hdr->type, msg_dec); - process_change_cipher_spec(msg_dec.data(), msg_dec.size()); - } else - process_change_cipher_spec(msg, msg_end - msg); - break; - - case tls_message_type_alert: - if (m_state_server.m_alg_encrypt) { - sanitizing_blob msg_dec(msg, msg_end); - decrypt_message(hdr->type, msg_dec); - process_alert(msg_dec.data(), msg_dec.size()); - } else - process_alert(msg, msg_end - msg); - break; - - case tls_message_type_handshake: - if (m_state_server.m_alg_encrypt) { - sanitizing_blob msg_dec(msg, msg_end); - decrypt_message(hdr->type, msg_dec); - process_handshake(msg_dec.data(), msg_dec.size()); - } else - process_handshake(msg, msg_end - msg); - break; - - case tls_message_type_application_data: { - if (!m_state_server.m_alg_encrypt) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Application data should be encrypted."); - - sanitizing_blob msg_dec(msg, msg_end); - decrypt_message(hdr->type, msg_dec); - process_application_data(msg_dec.data(), msg_dec.size()); - break; - } - - //default: - // if (m_state_server.m_alg_encrypt) { - // sanitizing_blob msg_dec(msg, msg_end); - // decrypt_message(hdr->type, msg_dec); - // process_vendor_data(hdr->type, msg_dec.data(), msg_dec.size()); - // } else - // process_vendor_data(hdr->type, msg, msg_end - msg); - } + m_phase = status == SEC_E_OK ? phase_application_data : phase_handshake_cont; + } else if (status == SEC_E_INCOMPLETE_MESSAGE) { + // Schannel neeeds more data. Send ACK packet to server to send more. + } else if (FAILED(status)) { + if (m_sc_ctx.m_attrib & ISC_RET_EXTENDED_ERROR) { + // Send alert via EAP. Not that EAP will transmit it once we throw this is an error... + assert(buf_out[1].BufferType == SECBUFFER_ALERT); + assert(m_sc_ctx.m_attrib & ISC_RET_ALLOCATED_MEMORY); + m_packet_res.m_data.assign((const unsigned char*)buf_out[1].pvBuffer, (const unsigned char*)buf_out[1].pvBuffer + buf_out[1].cbBuffer); } - pck = msg_end; + throw sec_runtime_error(status, __FUNCTION__ " Schannel error."); } } -void eap::method_tls::process_change_cipher_spec(_In_bytecount_(msg_size) const void *_msg, _In_ size_t msg_size) +void eap::method_tls::process_application_data() { - if (msg_size < 1) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete change cipher spec."); - - const unsigned char *msg = (const unsigned char*)_msg; - if (msg[0] != 1) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, string_printf(__FUNCTION__ " Invalid change cipher spec message (expected 1, received %u).", msg[0])); - - m_module.log_event(&EAPMETHOD_TLS_CHANGE_CIPHER_SPEC, event_data((unsigned int)eap_type_tls), event_data::blank); - - if (!m_state_server_pending.m_alg_encrypt) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Change cipher spec received without cipher being negotiated first."); - - // Create cryptographics provider (based on server selected cipher?). - if (!m_cp_enc_server.create(NULL, NULL, PROV_RSA_AES)) - throw win_runtime_error(__FUNCTION__ " Error creating cryptographics provider."); - - static const unsigned char s_label[] = "key expansion"; - sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1); - seed.insert(seed.end(), (const unsigned char*)&m_random_server, (const unsigned char*)(&m_random_server + 1)); - seed.insert(seed.end(), (const unsigned char*)&m_random_client, (const unsigned char*)(&m_random_client + 1)); - sanitizing_blob key_block(prf(m_cp_enc_server, m_alg_prf, m_master_secret, seed, - 2*m_state_server_pending.m_size_mac_key + // client_write_MAC_secret & server_write_MAC_secret (SHA1) - 2*m_state_server_pending.m_size_enc_key + // client_write_key & server_write_key - 2*m_state_server_pending.m_size_enc_iv )); // client_write_IV & server_write_IV - const unsigned char *_key_block = key_block.data(); - - // client_write_MAC_secret - _key_block += m_state_server_pending.m_size_mac_key; - - // server_write_MAC_secret - m_state_server_pending.m_padding_hmac = hmac_padding(m_cp_enc_server, m_state_server_pending.m_alg_mac, _key_block, m_state_server_pending.m_size_mac_key); - _key_block += m_state_server_pending.m_size_mac_key; - - // client_write_key - _key_block += m_state_server_pending.m_size_enc_key; - - // server_write_key - m_state_server_pending.m_key = create_key(m_cp_enc_server, m_state_server_pending.m_alg_encrypt, m_key_exp1, _key_block, m_state_server_pending.m_size_enc_key); - _key_block += m_state_server_pending.m_size_enc_key; - - if (m_state_server_pending.m_size_enc_iv && m_tls_version < tls_version_1_1) { - // client_write_IV - _key_block += m_state_server_pending.m_size_enc_iv; - - // server_write_IV - if (!CryptSetKeyParam(m_state_server_pending.m_key, KP_IV, _key_block, 0)) - throw win_runtime_error(__FUNCTION__ " Error setting server_write_IV."); - _key_block += m_state_server_pending.m_size_enc_iv; + if (m_sc_queue.empty()) { + // An ACK packet received. Nothing to unencrypt. + process_application_data(NULL, 0); + return; } - // Accept server pending state as current server state. - m_state_server = std::move(m_state_server_pending); - m_state_server_pending.m_alg_encrypt = 0; // Explicitly invalidate server pending state. (To mark that server must re-negotiate cipher.) -} + if (!(m_sc_ctx.m_attrib & ISC_RET_CONFIDENTIALITY)) + throw runtime_error(__FUNCTION__ " Connection is not encrypted."); + // Prepare input/output buffer(s). + SecBuffer buf[] = { + { 0, SECBUFFER_TOKEN, NULL }, + { 0, SECBUFFER_ALERT, NULL }, + { + (unsigned long)m_sc_queue.size(), + SECBUFFER_DATA, + m_sc_queue.data() + }, + }; + SecBufferDesc buf_desc = { + SECBUFFER_VERSION, + _countof(buf), + buf + }; -void eap::method_tls::process_alert(_In_bytecount_(msg_size) const void *_msg, _In_ size_t msg_size) -{ - if (msg_size < 2) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete alert."); - - const unsigned char *msg = (const unsigned char*)_msg; - - m_module.log_event(&EAPMETHOD_TLS_ALERT, event_data((unsigned int)eap_type_tls), event_data((unsigned char)msg[0]), event_data((unsigned char)msg[1]), event_data::blank); - - //if (msg[0] == alert_level_fatal) { - // // Clear session ID to avoid reconnection attempts. - // m_session_id.clear(); - //} -} - - -void eap::method_tls::process_handshake(_In_bytecount_(msg_size) const void *_msg, _In_ size_t msg_size) -{ - for (const unsigned char *msg = (const unsigned char*)_msg, *msg_end = msg + msg_size; msg < msg_end; ) { - // Parse record header. - if (msg + sizeof(unsigned int) > msg_end) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete record header."); - unsigned int hdr = ntohl(*(unsigned int*)msg); - const unsigned char - *rec = msg + sizeof(unsigned int), - *rec_end = rec + (hdr & 0xffffff); - if (rec_end > msg_end) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete record data."); - - // Process record. - tls_handshake_type_t type = (tls_handshake_type_t)((hdr >> 24) & 0xff); - switch (type) { - case tls_handshake_type_server_hello: - // TLS version - if (rec + 2 > rec_end) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Server SSL/TLS version missing or incomplete."); - else if (*(tls_version*)rec < tls_version_1_0 || m_tls_version < *(tls_version*)rec) - throw win_runtime_error(ERROR_NOT_SUPPORTED, __FUNCTION__ " Unsupported SSL/TLS version."); - m_tls_version = *(tls_version*)rec; - m_alg_prf = m_tls_version < tls_version_1_2 ? CALG_TLS1PRF : CALG_SHA_256; - rec += 2; - - // Server random - if (rec + sizeof(tls_random) > rec_end) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Server random missing or incomplete."); - memcpy(&m_random_server, rec, sizeof(tls_random)); - rec += sizeof(tls_random); - - // Session ID - if (rec + 1 > rec_end || rec + 1 + rec[0] > rec_end) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Session ID missing or incomplete."); - assert(rec[0] <= 32); // According to RFC 5246 session IDs should not be longer than 32B. - if (m_session_id.size() != rec[0] || memcmp(m_session_id.data(), rec + 1, rec[0]) != 0) { - m_module.log_event(&EAPMETHOD_TLS_SESSION_NEW, event_data((unsigned int)eap_type_tls), event_data::blank); - m_session_id.assign(rec + 1, rec + 1 + rec[0]); - } else - m_module.log_event(&EAPMETHOD_TLS_SESSION_RESUME, event_data((unsigned int)eap_type_tls), event_data::blank); - rec += rec[0] + 1; - - // Cipher - if (rec + 2 > rec_end) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Cipher or incomplete."); - if (rec[0] == 0x00 || rec[1] == 0x2f) { - // TLS_RSA_WITH_AES_128_CBC_SHA - m_state_server_pending.m_alg_encrypt = CALG_AES_128; - m_state_server_pending.m_size_enc_key = 128/8; // AES-128 - m_state_server_pending.m_size_enc_iv = 128/8; // AES-128 - m_state_server_pending.m_size_enc_block = 128/8; // AES-128 - m_state_server_pending.m_alg_mac = CALG_SHA1; - m_state_server_pending.m_size_mac_key = 160/8; // SHA-1 - m_state_server_pending.m_size_mac_hash = 160/8; // SHA-1 - } else if (rec[0] == 0x00 || rec[1] == 0x0a) { - // TLS_RSA_WITH_3DES_EDE_CBC_SHA - m_state_server_pending.m_alg_encrypt = CALG_3DES; - m_state_server_pending.m_size_enc_key = 192/8; // 3DES 192bits - m_state_server_pending.m_size_enc_iv = 64/8; // 3DES 64bits - m_state_server_pending.m_size_enc_block = 64/8; // 3DES 64bits - m_state_server_pending.m_alg_mac = CALG_SHA1; - m_state_server_pending.m_size_mac_key = 160/8; // SHA-1 - m_state_server_pending.m_size_mac_hash = 160/8; // SHA-1 - } else - throw win_runtime_error(ERROR_NOT_SUPPORTED, string_printf(__FUNCTION__ " Other than requested cipher selected (received 0x%02x%02x).", rec[0], rec[1])); - - m_module.log_event(&EAPMETHOD_TLS_SERVER_HELLO1, - event_data((unsigned int)eap_type_tls), - event_data(((unsigned int)m_tls_version.major << 8) | (unsigned int)m_tls_version.minor), - event_data((unsigned int)m_session_id.size()), - event_data(m_session_id.data(), (ULONG)m_session_id.size()), - event_data::blank); - break; - - case tls_handshake_type_certificate: { - // Certificate list size - if (rec + 3 > rec_end) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Certificate list size missing or incomplete."); - const unsigned char - *list = rec + 3, - *list_end = list + ((rec[0] << 16) | (rec[1] << 8) | rec[2]); - if (list_end > rec_end) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Certificate list missing or incomplete."); - - m_server_cert_chain.clear(); - while (list < list_end) { - // Certificate size - if (list + 3 > list_end) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Certificate size missing or incomplete."); - const unsigned char - *cert = list + 3, - *cert_end = cert + ((list[0] << 16) | (list[1] << 8) | list[2]); - if (cert_end > list_end) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Certificate rec missing or incomplete."); - - // Certificate - cert_context c; - if (!c.create(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, cert, (DWORD)(cert_end - cert))) - throw win_runtime_error(__FUNCTION__ " Error reading certificate."); - m_server_cert_chain.push_back(std::move(c)); - - list = cert_end; - } - - wstring cert_name(!m_server_cert_chain.empty() ? get_cert_title(m_server_cert_chain.front()) : L""); - m_module.log_event(&EAPMETHOD_TLS_CERTIFICATE, event_data((unsigned int)eap_type_tls), event_data(cert_name), event_data::blank); - break; - } - - case tls_handshake_type_certificate_request: - m_module.log_event(&EAPMETHOD_TLS_CERTIFICATE_REQUEST, event_data((unsigned int)eap_type_tls), event_data::blank); - break; - - case tls_handshake_type_server_hello_done: - m_module.log_event(&EAPMETHOD_TLS_SERVER_HELLO_DONE, event_data((unsigned int)eap_type_tls), event_data::blank); - break; - - case tls_handshake_type_finished: { - if (!m_state_server.m_alg_encrypt) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Finished message should be encrypted."); - - // According to https://tools.ietf.org/html/rfc5246#section-7.4.9 all verify_data is 12B. - if (rec_end - rec != 12) - throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, string_printf(__FUNCTION__ " Finished record size incorrect (expected 12B, received %uB).", rec_end - rec)); - - // Create label + hash MD5 + hash SHA-1 seed. - crypt_hash hash; - static const unsigned char s_label[] = "server finished"; - sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1), hash_data; - if (m_tls_version < tls_version_1_2) { - hash = m_hash_handshake_msgs_md5; // duplicate - if (!CryptGetHashParam(hash, HP_HASHVAL, hash_data, 0)) - throw win_runtime_error(__FUNCTION__ " Error finishing MD5 hash calculation."); - seed.insert(seed.end(), hash_data.begin(), hash_data.end()); - hash = m_hash_handshake_msgs_sha1; // duplicate - if (!CryptGetHashParam(hash, HP_HASHVAL, hash_data, 0)) - throw win_runtime_error(__FUNCTION__ " Error finishing SHA-1 hash calculation."); - seed.insert(seed.end(), hash_data.begin(), hash_data.end()); - } else { - hash = m_hash_handshake_msgs_sha256; // duplicate - if (!CryptGetHashParam(hash, HP_HASHVAL, hash_data, 0)) - throw win_runtime_error(__FUNCTION__ " Error finishing SHA-256 hash calculation."); - seed.insert(seed.end(), hash_data.begin(), hash_data.end()); - } - - if (memcmp(prf(m_cp_enc_server, m_alg_prf, m_master_secret, seed, 12).data(), rec, 12)) - throw win_runtime_error(ERROR_ENCRYPTION_FAILED, __FUNCTION__ " Integrity check failed."); - - m_module.log_event(&EAPMETHOD_TLS_FINISHED, event_data((unsigned int)eap_type_tls), event_data::blank); - break; - } - - default: - m_module.log_event(&EAPMETHOD_TLS_HANDSHAKE_IGNORE, event_data((unsigned int)eap_type_tls), event_data((unsigned char)type), event_data::blank); + // Decrypt the message. + SECURITY_STATUS status = DecryptMessage(m_sc_ctx, &buf_desc, 0, NULL); + if (status == SEC_E_OK) { + assert(buf[2].BufferType == SECBUFFER_DATA); + process_application_data(buf[2].pvBuffer, buf[2].cbBuffer); + } else if (status == SEC_E_INCOMPLETE_MESSAGE) { + // Schannel neeeds more data. Send ACK packet to server to send more. + } else if (status == SEC_I_CONTEXT_EXPIRED) { + // Server initiated connection shutdown. + m_sc_queue.clear(); + m_phase = phase_shutdown; + } else if (status == SEC_I_RENEGOTIATE) { + // Re-negotiation required. + m_sc_queue.clear(); + m_phase = phase_handshake_init; + process_handshake(); + } else if (FAILED(status)) { + if (m_sc_ctx.m_attrib & ISC_RET_EXTENDED_ERROR) { + // Send alert via EAP. Not that EAP will transmit it once we throw this is an error... + assert(buf[1].BufferType == SECBUFFER_ALERT); + assert(m_sc_ctx.m_attrib & ISC_RET_ALLOCATED_MEMORY); + m_packet_res.m_data.assign((const unsigned char*)buf[1].pvBuffer, (const unsigned char*)buf[1].pvBuffer + buf[1].cbBuffer); } - if (type < tls_handshake_type_max) { - // Set the flag this handshake message was received. - m_handshake[type] = true; - } - - if (type != tls_handshake_type_hello_request) { - // Hash all but hello requests (https://tools.ietf.org/html/rfc5246#section-7.4.1.1). - hash_handshake(msg, rec_end - msg); - } - - msg = rec_end; + throw sec_runtime_error(status, __FUNCTION__ " Schannel error."); } } -void eap::method_tls::process_application_data(_In_bytecount_(msg_size) const void *msg, _In_ size_t msg_size) +void eap::method_tls::process_application_data(_In_bytecount_(size_msg) const void *msg, _In_ size_t size_msg) { UNREFERENCED_PARAMETER(msg); - UNREFERENCED_PARAMETER(msg_size); + UNREFERENCED_PARAMETER(size_msg); // TODO: Parse application data (Diameter AVP) } -//void eap::method_tls::process_vendor_data(_In_ tls_message_type_t type, _In_bytecount_(msg_size) const void *msg, _In_ size_t msg_size) -//{ -// UNREFERENCED_PARAMETER(type); -// UNREFERENCED_PARAMETER(msg); -// UNREFERENCED_PARAMETER(msg_size); -//} - +#ifndef SCHANNEL_SRV_CERT_CHECK void eap::method_tls::verify_server_trust() const { - assert(!m_server_cert_chain.empty()); - const cert_context &cert = m_server_cert_chain.front(); + SECURITY_STATUS status; - string subj; - if (!CertGetNameStringA(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, subj)) - throw win_runtime_error(__FUNCTION__ " Error retrieving server's certificate subject name."); + cert_context cert; + status = QueryContextAttributes(m_sc_ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&cert); + if (FAILED(status)) + throw sec_runtime_error(status, __FUNCTION__ " Error retrieving server certificate from Schannel."); const config_provider &cfg_prov(m_cfg.m_providers.front()); const config_method_tls *cfg_method = dynamic_cast(cfg_prov.m_methods.front().get()); assert(cfg_method); + // Check server name. if (!cfg_method->m_server_names.empty()) { - // Check server name. - for (list::const_iterator s = cfg_method->m_server_names.cbegin(), s_end = cfg_method->m_server_names.cend();; ++s) { - if (s != s_end) { - const char - *a = s->c_str(), - *b = subj.c_str(); - size_t - len_a = s->length(), - len_b = subj.length(); + bool found = false; - if (_stricmp(a, b) == 0 || // Direct match - a[0] == '*' && len_b + 1 >= len_a && _stricmp(a + 1, b + len_b - (len_a - 1)) == 0) // "*..." wildchar match - { - m_module.log_event(&EAPMETHOD_TLS_SERVER_NAME_TRUSTED, event_data(subj), event_data::blank); - break; + // Search subjectAltName2 and subjectAltName. + for (DWORD i = 0; !found && i < cert->pCertInfo->cExtension; i++) { + unique_ptr > san_info; + if (strcmp(cert->pCertInfo->rgExtension[i].pszObjId, szOID_SUBJECT_ALT_NAME2) == 0) { + unsigned char *output = NULL; + DWORD size_output; + if (!CryptDecodeObjectEx( + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + szOID_SUBJECT_ALT_NAME2, + cert->pCertInfo->rgExtension[i].Value.pbData, cert->pCertInfo->rgExtension[i].Value.cbData, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_ENABLE_PUNYCODE_FLAG, + NULL, + &output, &size_output)) + throw win_runtime_error(__FUNCTION__ " Error decoding certificate extension."); + san_info.reset((CERT_ALT_NAME_INFO*)output); + } else if (strcmp(cert->pCertInfo->rgExtension[i].pszObjId, szOID_SUBJECT_ALT_NAME) == 0) { + unsigned char *output = NULL; + DWORD size_output; + if (!CryptDecodeObjectEx( + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + szOID_SUBJECT_ALT_NAME, + cert->pCertInfo->rgExtension[i].Value.pbData, cert->pCertInfo->rgExtension[i].Value.cbData, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_ENABLE_PUNYCODE_FLAG, + NULL, + &output, &size_output)) + throw win_runtime_error(__FUNCTION__ " Error decoding certificate extension."); + san_info.reset((CERT_ALT_NAME_INFO*)output); + } else { + // Skip this extension. + continue; + } + + for (list::const_iterator s = cfg_method->m_server_names.cbegin(), s_end = cfg_method->m_server_names.cend(); !found && s != s_end; ++s) { + for (DWORD i = 0; !found && i < san_info->cAltEntry; i++) { + if (san_info->rgAltEntry[i].dwAltNameChoice == CERT_ALT_NAME_DNS_NAME && + _wcsicmp(s->c_str(), san_info->rgAltEntry[i].pwszDNSName) == 0) + { + m_module.log_event(&EAPMETHOD_TLS_SERVER_NAME_TRUSTED1, event_data(san_info->rgAltEntry[i].pwszDNSName), event_data::blank); + found = true; + break; + } } - } else - throw win_runtime_error(ERROR_INVALID_DOMAINNAME, string_printf(__FUNCTION__ " Server name %s is not on the list of trusted server names.", subj.c_str()).c_str()); + } } + + if (!found) + throw win_runtime_error(ERROR_INVALID_DOMAINNAME, __FUNCTION__ " Server name is not on the list of trusted server names."); } if (cert->pCertInfo->Issuer.cbData == cert->pCertInfo->Subject.cbData && memcmp(cert->pCertInfo->Issuer.pbData, cert->pCertInfo->Subject.pbData, cert->pCertInfo->Issuer.cbData) == 0) - throw com_runtime_error(CRYPT_E_SELF_SIGNED, string_printf(__FUNCTION__ " Server is using a self-signed certificate %s. Cannot trust it.", subj.c_str()).c_str()); + throw com_runtime_error(CRYPT_E_SELF_SIGNED, __FUNCTION__ " Server is using a self-signed certificate. Cannot trust it."); // Create temporary certificate store of our trusted root CAs. cert_store store; @@ -1207,17 +691,20 @@ void eap::method_tls::verify_server_trust() const for (list::const_iterator c = cfg_method->m_trusted_root_ca.cbegin(), c_end = cfg_method->m_trusted_root_ca.cend(); c != c_end; ++c) CertAddCertificateContextToStore(store, *c, CERT_STORE_ADD_REPLACE_EXISTING, NULL); - // Add all certificates from the server's certificate chain, except the first one. - for (list::const_iterator c = m_server_cert_chain.cbegin(), c_end = m_server_cert_chain.cend(); ++c != c_end;) { - const cert_context &_c = *c; - if (_c->pCertInfo->Issuer.cbData == _c->pCertInfo->Subject.cbData && - memcmp(_c->pCertInfo->Issuer.pbData, _c->pCertInfo->Subject.pbData, _c->pCertInfo->Issuer.cbData) == 0) + // Add all intermediate certificates from the server's certificate chain. + for (cert_context c(cert); c;) { + DWORD flags = 0; + c.attach(CertGetIssuerCertificateFromStore(cert->hCertStore, c, NULL, &flags)); + if (!c) break; + + if (c->pCertInfo->Issuer.cbData == c->pCertInfo->Subject.cbData && + memcmp(c->pCertInfo->Issuer.pbData, c->pCertInfo->Subject.pbData, c->pCertInfo->Issuer.cbData) == 0) { // Skip the root CA certificates (self-signed). We define in whom we trust! continue; } - CertAddCertificateContextToStore(store, *c, CERT_STORE_ADD_REPLACE_EXISTING, NULL); + CertAddCertificateContextToStore(store, c, CERT_STORE_ADD_REPLACE_EXISTING, NULL); } // Prepare the certificate chain validation, and check. @@ -1240,7 +727,7 @@ void eap::method_tls::verify_server_trust() const if (!context.create(NULL, cert, NULL, store, &chain_params, 0)) throw win_runtime_error(__FUNCTION__ " Error creating certificate chain context."); - // Check chain validation error flags. Ignore CERT_TRUST_IS_UNTRUSTED_ROOT flag when we check root CA explicitly. + // Check chain validation error flags. Ignore CERT_TRUST_IS_UNTRUSTED_ROOT flag since we check root CA explicitly. if (context->TrustStatus.dwErrorStatus != CERT_TRUST_NO_ERROR && (cfg_method->m_trusted_root_ca.empty() || (context->TrustStatus.dwErrorStatus & ~CERT_TRUST_IS_UNTRUSTED_ROOT) != CERT_TRUST_NO_ERROR)) throw win_runtime_error(context->TrustStatus.dwErrorStatus, "Error validating certificate chain."); @@ -1271,326 +758,4 @@ void eap::method_tls::verify_server_trust() const m_module.log_event(&EAPMETHOD_TLS_SERVER_CERT_TRUSTED, event_data::blank); } - -void eap::method_tls::encrypt_message(_In_ tls_message_type_t type, _Inout_ sanitizing_blob &data) -{ - // Hash sequence number, TLS header, and message. - size_t size_data = data.size(); - hmac_hash hash(m_cp_enc_client, m_state_client.m_alg_mac, m_state_client.m_padding_hmac); - unsigned __int64 seq_num2 = htonll(m_seq_num_client); - unsigned short size_data2 = htons((unsigned short)size_data); - if (!CryptHashData(hash, (const BYTE*)&seq_num2 , sizeof(seq_num2 ), 0) || - !CryptHashData(hash, (const BYTE*)&type , sizeof(type ), 0) || - !CryptHashData(hash, (const BYTE*)&m_tls_version, sizeof(m_tls_version), 0) || - !CryptHashData(hash, (const BYTE*)&size_data2 , sizeof(size_data2 ), 0) || - !CryptHashData(hash, data.data() , (DWORD)size_data , 0)) - throw win_runtime_error(__FUNCTION__ " Error hashing data."); - sanitizing_blob hmac; - hash.calculate(hmac); - - size_t size_data_enc = - size_data + // TLS message - hmac.size(); // HMAC hash - - if (m_state_client.m_size_enc_block) { - // Block cypher - - if (m_tls_version >= tls_version_1_1) { - // TLS 1.1+: Set random IV. - data.insert(data.begin(), m_state_client.m_size_enc_iv, 0); - if (!CryptGenRandom(m_cp_enc_client, (DWORD)m_state_client.m_size_enc_iv, data.data())) - throw win_runtime_error(__FUNCTION__ " Error generating IV."); - size_data_enc += m_state_client.m_size_enc_iv; - } - - // Calculate padding. - size_data_enc += 1; // Padding length - unsigned char size_padding = (unsigned char)((m_state_client.m_size_enc_block - size_data_enc) % m_state_client.m_size_enc_block); - size_data_enc += size_padding; - - // Append HMAC hash and padding. - data.reserve(size_data_enc); - data.insert(data.end(), hmac.begin(), hmac.end()); - data.insert(data.end(), size_padding + 1, size_padding); - } else { - // Stream cipher - - // Append HMAC hash. - data.reserve(size_data_enc); - data.insert(data.end(), hmac.begin(), hmac.end()); - } - - // Encrypt. - assert(size_data_enc < 0xffffffff); - DWORD size_data_enc2 = (DWORD)size_data_enc; - if (!CryptEncrypt(m_state_client.m_key, NULL, FALSE, 0, data.data(), &size_data_enc2, (DWORD)size_data_enc)) - throw win_runtime_error(__FUNCTION__ " Error encrypting message."); - - // Increment sequence number. - m_seq_num_client++; -} - - -void eap::method_tls::decrypt_message(_In_ tls_message_type_t type, _Inout_ sanitizing_blob &data) -{ - // Decrypt. - if (!CryptDecrypt(m_state_server.m_key, NULL, FALSE, 0, data)) - throw win_runtime_error(__FUNCTION__ " Error decrypting message."); - - if (!data.empty()) { - size_t size_data = data.size(); - bool padding_ok = true; - - if (m_state_server.m_size_enc_block) { - // Check padding. Do not throw until HMAC is calculated. - // [Canvel, B., "Password Interception in a SSL/TLS Channel"](http://lasecwww.epfl.ch/memo_ssl.shtml) - unsigned char padding = data.back(); - size_data = (size_t)padding + 1 <= size_data ? size_data - (padding + 1) : 0; - for (size_t i = size_data, i_end = data.size() - 1; i < i_end; i++) - if (data[i] != padding) - padding_ok = false; - - // Remove padding. - data.resize(size_data); - - if (m_tls_version >= tls_version_1_1) { - // TLS 1.1+: Remove random IV. - data.erase(data.begin(), data.begin() + m_state_server.m_size_enc_iv); - size_data -= m_state_server.m_size_enc_iv; - } - } - - size_data -= m_state_server.m_size_mac_hash; - - // Hash sequence number, TLS header (without length), original message length, and message. - hmac_hash hash(m_cp_enc_server, m_state_server.m_alg_mac, m_state_server.m_padding_hmac); - unsigned __int64 seq_num2 = htonll(m_seq_num_server); - unsigned short size_data2 = htons((unsigned short)size_data); - if (!CryptHashData(hash, (const BYTE*)&seq_num2 , sizeof(seq_num2 ), 0) || - !CryptHashData(hash, (const BYTE*)&type , sizeof(type ), 0) || - !CryptHashData(hash, (const BYTE*)&m_tls_version, sizeof(m_tls_version), 0) || - !CryptHashData(hash, (const BYTE*)&size_data2 , sizeof(size_data2 ), 0) || - !CryptHashData(hash, data.data() , (DWORD)size_data , 0)) - throw win_runtime_error(__FUNCTION__ " Error hashing data."); - sanitizing_blob hmac; - hash.calculate(hmac); - - // // Check padding results. - if (!padding_ok) - throw invalid_argument(__FUNCTION__ " Incorrect message padding."); - - // Verify hash. - if (memcmp(&*(data.begin() + size_data), hmac.data(), m_state_server.m_size_mac_hash) != 0) - throw win_runtime_error(ERROR_DECRYPTION_FAILED, __FUNCTION__ " Integrity check failed."); - - // Strip hash and padding. - data.resize(size_data); - - // Increment sequence number. - m_seq_num_server++; - } -} - - -eap::sanitizing_blob eap::method_tls::prf( - _In_ HCRYPTPROV cp, - _In_ ALG_ID alg, - _In_ const tls_master_secret &secret, - _In_bytecount_(size_seed) const void *seed, - _In_ size_t size_seed, - _In_ size_t size) -{ - sanitizing_blob data; - data.reserve(size); - - if (alg == CALG_TLS1PRF) { - // Split secret in two halves. - size_t - size_S1 = (sizeof(tls_master_secret) + 1) / 2, - size_S2 = size_S1; - const void - *S1 = &secret, - *S2 = (const unsigned char*)&secret + (sizeof(tls_master_secret) - size_S2); - - // Precalculate HMAC padding for speed. - hmac_padding - padding1(cp, CALG_MD5 , S1, size_S1), - padding2(cp, CALG_SHA1, S2, size_S2); - - // Prepare A for p_hash. - sanitizing_blob - A1((unsigned char*)seed, (unsigned char*)seed + size_seed), - A2((unsigned char*)seed, (unsigned char*)seed + size_seed); - - sanitizing_blob - hmac1, - hmac2; - data.resize(size); - for (size_t i = 0, off1 = 0, off2 = 0; i < size; ) { - if (off1 >= hmac1.size()) { - // Rehash A. - hmac_hash hash1(cp, CALG_MD5 , padding1); - if (!CryptHashData(hash1, A1.data(), (DWORD)A1.size(), 0)) - throw win_runtime_error(__FUNCTION__ " Error hashing A1."); - hash1.calculate(A1); - - // Hash A and seed. - hmac_hash hash2(cp, CALG_MD5 , padding1); - if (!CryptHashData(hash2, A1.data(), (DWORD)A1.size(), 0) || - !CryptHashData(hash2, (const BYTE*)seed , (DWORD)size_seed, 0)) - throw win_runtime_error(__FUNCTION__ " Error hashing seed,label or data."); - hash2.calculate(hmac1); - off1 = 0; - } - - if (off2 >= hmac2.size()) { - // Rehash A. - hmac_hash hash1(cp, CALG_SHA1 , padding2); - if (!CryptHashData(hash1, A2.data(), (DWORD)A2.size(), 0)) - throw win_runtime_error(__FUNCTION__ " Error hashing A2."); - hash1.calculate(A2); - - // Hash A and seed. - hmac_hash hash2(cp, CALG_SHA1 , padding2); - if (!CryptHashData(hash2, A2.data(), (DWORD)A2.size(), 0) || - !CryptHashData(hash2, (const BYTE*)seed , (DWORD)size_seed, 0)) - throw win_runtime_error(__FUNCTION__ " Error hashing seed,label or data."); - hash2.calculate(hmac2); - off2 = 0; - } - - // XOR combine amount of data we have (and need). - size_t i_end = std::min(i + std::min(hmac1.size() - off1, hmac2.size() - off2), size); - while (i < i_end) - data[i++] = hmac1[off1++] ^ hmac2[off2++]; - } - } else { - // Precalculate HMAC padding for speed. - hmac_padding padding(cp, alg, &secret, sizeof(tls_master_secret)); - - // Prepare A for p_hash. - sanitizing_blob A((unsigned char*)seed, (unsigned char*)seed + size_seed); - - sanitizing_blob hmac; - for (size_t i = 0; i < size; ) { - // Rehash A. - hmac_hash hash1(cp, alg, padding); - if (!CryptHashData(hash1, A.data(), (DWORD)A.size(), 0)) - throw win_runtime_error(__FUNCTION__ " Error hashing A."); - hash1.calculate(A); - - // Hash A and seed. - hmac_hash hash2(cp, alg, padding); - if (!CryptHashData(hash2, A.data(), (DWORD)A.size() , 0) || - !CryptHashData(hash2, (const BYTE*)seed , (DWORD)size_seed, 0)) - throw win_runtime_error(__FUNCTION__ " Error hashing seed,label or data."); - hash2.calculate(hmac); - - size_t n = std::min(hmac.size(), size - i); - data.insert(data.end(), hmac.begin(), hmac.begin() + n); - i += n; - } - } - - return data; -} - - -HCRYPTKEY eap::method_tls::create_key( - _In_ HCRYPTPROV cp, - _In_ ALG_ID alg, - _In_ HCRYPTKEY key, - _In_bytecount_(size_secret) const void *secret, - _In_ size_t size_secret) -{ -#if 1 - UNREFERENCED_PARAMETER(key); - assert(size_secret <= 0xffffffff); - - // Prepare exported key BLOB. - struct key_blob_prefix { - PUBLICKEYSTRUC header; - DWORD size; - } const prefix = { - { - PLAINTEXTKEYBLOB, - CUR_BLOB_VERSION, - 0, - alg, - }, - (DWORD)size_secret, - }; - sanitizing_blob key_blob; - key_blob.reserve(sizeof(key_blob_prefix) + size_secret); - key_blob.assign((const unsigned char*)&prefix, (const unsigned char*)(&prefix + 1)); - key_blob.insert(key_blob.end(), (const unsigned char*)secret, (const unsigned char*)secret + size_secret); - - // Import the key. - winstd::crypt_key key_out; - if (!key_out.import(cp, key_blob.data(), (DWORD)key_blob.size(), NULL, 0)) - throw winstd::win_runtime_error(__FUNCTION__ " Error importing key."); - return key_out.detach(); -#else - // Get private key's algorithm. - ALG_ID alg_key; - if (!CryptGetKeyParam(key, KP_ALGID, alg_key, 0)) - throw win_runtime_error(__FUNCTION__ " Error getting key's algorithm.'"); - - // Get private key's length in bytes. - DWORD size_key = CryptGetKeyParam(key, KP_KEYLEN, size_key, 0) ? size_key/8 : 0; - - // SIMPLEBLOB Format is documented in SDK - // Copy header to buffer -#pragma pack(push) -#pragma pack(1) - struct key_blob_prefix { - PUBLICKEYSTRUC header; - ALG_ID alg; - } const prefix = { - { - SIMPLEBLOB, - CUR_BLOB_VERSION, - 0, - alg, - }, - alg_key, - }; -#pragma pack(pop) - sanitizing_blob key_blob; - key_blob.reserve(sizeof(key_blob_prefix) + size_key); - key_blob.assign((const unsigned char*)&prefix, (const unsigned char*)(&prefix + 1)); - - // Key in EME-PKCS1-v1_5 (RFC 3447). - key_blob.push_back(0); // Initial zero - key_blob.push_back(2); // PKCS #1 block type = 2 - - // PS - size_t size_ps = size_key - size_secret - 3; - assert(size_ps >= 8); -#if 1 - key_blob.insert(key_blob.end(), size_ps, 1); -#else - // Is random PS required at all? We are importing a clear-text session key with the exponent-of-one key. How low on security can we get? - key_blob.insert(key_blob.end(), size_ps, 0); - unsigned char *ps = &*(key_blob.end() - size_ps); - CryptGenRandom(cp, (DWORD)size_ps, ps); - for (size_t i = 0; i < size_ps; i++) - if (ps[i] == 0) ps[i] = 1; #endif - - key_blob.push_back(0); // PS and M zero delimiter - - // M - key_blob.insert(key_blob.end(), (const unsigned char*)secret, (const unsigned char*)secret + size_secret); - -#ifdef _HOST_LOW_ENDIAN - std::reverse(key_blob.end() - size_key, key_blob.end()); -#endif - - // Import the key. - winstd::crypt_key key_out; - if (!key_out.import(cp, key_blob.data(), (DWORD)key_blob.size(), key, 0)) - throw winstd::win_runtime_error(__FUNCTION__ " Error importing key."); - return key_out.detach(); -#endif -} diff --git a/lib/TLS/src/StdAfx.h b/lib/TLS/src/StdAfx.h index ba927a1..70f2064 100644 --- a/lib/TLS/src/StdAfx.h +++ b/lib/TLS/src/StdAfx.h @@ -31,6 +31,7 @@ #include #include +#include #include #include diff --git a/lib/TLS_UI/include/TLS_UI.h b/lib/TLS_UI/include/TLS_UI.h index f1db3fd..1d59ad6 100644 --- a/lib/TLS_UI/include/TLS_UI.h +++ b/lib/TLS_UI/include/TLS_UI.h @@ -119,7 +119,7 @@ public: /// /// Construct the validator with a value to store data /// - wxHostNameValidator(std::string *val = NULL); + wxHostNameValidator(std::wstring *val = NULL); /// /// Copy constructor @@ -149,10 +149,10 @@ public: /// /// Parses FQDN value /// - static bool Parse(const wxString &val_in, size_t i_start, size_t i_end, wxTextCtrl *ctrl, wxWindow *parent, std::string *val_out = NULL); + static bool Parse(const wxString &val_in, size_t i_start, size_t i_end, wxTextCtrl *ctrl, wxWindow *parent, std::wstring *val_out = NULL); protected: - std::string *m_val; ///< Pointer to variable to receive control's parsed value + std::wstring *m_val; ///< Pointer to variable to receive control's parsed value }; @@ -165,7 +165,7 @@ public: /// /// Construct the validator with a value to store data /// - wxFQDNValidator(std::string *val = NULL); + wxFQDNValidator(std::wstring *val = NULL); /// /// Copy constructor @@ -195,10 +195,10 @@ public: /// /// Parses FQDN value /// - static bool Parse(const wxString &val_in, size_t i_start, size_t i_end, wxTextCtrl *ctrl, wxWindow *parent, std::string *val_out = NULL); + static bool Parse(const wxString &val_in, size_t i_start, size_t i_end, wxTextCtrl *ctrl, wxWindow *parent, std::wstring *val_out = NULL); protected: - std::string *m_val; ///< Pointer to variable to receive control's parsed value + std::wstring *m_val; ///< Pointer to variable to receive control's parsed value }; @@ -211,7 +211,7 @@ public: /// /// Construct the validator with a value to store data /// - wxFQDNListValidator(std::list *val = NULL); + wxFQDNListValidator(std::list *val = NULL); /// /// Copy constructor @@ -241,10 +241,10 @@ public: /// /// Parses FQDN list value /// - static bool Parse(const wxString &val_in, size_t i_start, size_t i_end, wxTextCtrl *ctrl, wxWindow *parent, std::list *val_out = NULL); + static bool Parse(const wxString &val_in, size_t i_start, size_t i_end, wxTextCtrl *ctrl, wxWindow *parent, std::list *val_out = NULL); protected: - std::list *m_val; ///< Pointer to variable to receive control's parsed value + std::list *m_val; ///< Pointer to variable to receive control's parsed value }; @@ -311,7 +311,7 @@ protected: eap::config_method_tls &m_cfg; ///< TLS configuration winstd::library m_certmgr; ///< certmgr.dll resource library reference wxIcon m_icon; ///< Panel icon - std::list m_server_names_val; ///< Acceptable authenticating server names + std::list m_server_names_val; ///< Acceptable authenticating server names }; @@ -331,7 +331,6 @@ public: protected: /// \cond internal virtual void OnInitDialog(wxInitDialogEvent& event); - virtual bool TransferDataFromWindow(); /// \endcond protected: diff --git a/lib/TLS_UI/res/wxTLS_UI.cpp b/lib/TLS_UI/res/wxTLS_UI.cpp index 87e2cf3..eac078c 100644 --- a/lib/TLS_UI/res/wxTLS_UI.cpp +++ b/lib/TLS_UI/res/wxTLS_UI.cpp @@ -74,11 +74,11 @@ wxEAPTLSServerTrustConfigPanelBase::wxEAPTLSServerTrustConfigPanelBase( wxWindow sb_server_names->Add( m_server_names_label, 0, wxBOTTOM, 5 ); m_server_names = new wxTextCtrl( sb_server_trust->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - m_server_names->SetToolTip( _("A semicolon delimited list of acceptable server FQDN names; blank to skip name check; \"*\" wildchar allowed") ); + m_server_names->SetToolTip( _("A semicolon delimited list of acceptable server FQDN names; blank to skip name check; Unicode characters allowed") ); sb_server_names->Add( m_server_names, 0, wxEXPAND|wxBOTTOM, 5 ); - m_server_names_note = new wxStaticText( sb_server_trust->GetStaticBox(), wxID_ANY, _("(Example: foo.bar.com;*.domain.org)"), wxDefaultPosition, wxDefaultSize, 0 ); + m_server_names_note = new wxStaticText( sb_server_trust->GetStaticBox(), wxID_ANY, _("(Example: foo.bar.com;server2.bar.com)"), wxDefaultPosition, wxDefaultSize, 0 ); m_server_names_note->Wrap( -1 ); sb_server_names->Add( m_server_names_note, 0, wxALIGN_RIGHT, 5 ); diff --git a/lib/TLS_UI/res/wxTLS_UI.fbp b/lib/TLS_UI/res/wxTLS_UI.fbp index dfeba30..5435dc8 100644 --- a/lib/TLS_UI/res/wxTLS_UI.fbp +++ b/lib/TLS_UI/res/wxTLS_UI.fbp @@ -870,7 +870,7 @@ 0 - A semicolon delimited list of acceptable server FQDN names; blank to skip name check; "*" wildchar allowed + A semicolon delimited list of acceptable server FQDN names; blank to skip name check; Unicode characters allowed wxFILTER_NONE wxDefaultValidator @@ -940,7 +940,7 @@ 0 0 wxID_ANY - (Example: foo.bar.com;*.domain.org) + (Example: foo.bar.com;server2.bar.com) 0 diff --git a/lib/TLS_UI/src/TLS_UI.cpp b/lib/TLS_UI/src/TLS_UI.cpp index f9f63c6..05cbe83 100644 --- a/lib/TLS_UI/src/TLS_UI.cpp +++ b/lib/TLS_UI/src/TLS_UI.cpp @@ -46,7 +46,7 @@ wxCertificateClientData::~wxCertificateClientData() wxIMPLEMENT_DYNAMIC_CLASS(wxHostNameValidator, wxValidator); -wxHostNameValidator::wxHostNameValidator(std::string *val) : +wxHostNameValidator::wxHostNameValidator(std::wstring *val) : m_val(val), wxValidator() { @@ -98,7 +98,7 @@ bool wxHostNameValidator::TransferFromWindow() } -bool wxHostNameValidator::Parse(const wxString &val_in, size_t i_start, size_t i_end, wxTextCtrl *ctrl, wxWindow *parent, std::string *val_out) +bool wxHostNameValidator::Parse(const wxString &val_in, size_t i_start, size_t i_end, wxTextCtrl *ctrl, wxWindow *parent, std::wstring *val_out) { const wxStringCharType *buf = val_in; @@ -108,7 +108,7 @@ bool wxHostNameValidator::Parse(const wxString &val_in, size_t i_start, size_t i // End of host name found. if (val_out) val_out->assign(val_in.c_str() + i_start, i - i_start); return true; - } else if (_tcschr(wxT("abcdefghijklmnopqrstuvwxyz0123456789-*"), buf[i])) { + } else if (buf[i] == _T('-') || buf[i] == _T('_') || _istalnum(buf[i])) { // Valid character found. i++; } else { @@ -129,7 +129,7 @@ bool wxHostNameValidator::Parse(const wxString &val_in, size_t i_start, size_t i wxIMPLEMENT_DYNAMIC_CLASS(wxFQDNValidator, wxValidator); -wxFQDNValidator::wxFQDNValidator(std::string *val) : +wxFQDNValidator::wxFQDNValidator(std::wstring *val) : m_val(val), wxValidator() { @@ -181,7 +181,7 @@ bool wxFQDNValidator::TransferFromWindow() } -bool wxFQDNValidator::Parse(const wxString &val_in, size_t i_start, size_t i_end, wxTextCtrl *ctrl, wxWindow *parent, std::string *val_out) +bool wxFQDNValidator::Parse(const wxString &val_in, size_t i_start, size_t i_end, wxTextCtrl *ctrl, wxWindow *parent, std::wstring *val_out) { const wxStringCharType *buf = val_in; @@ -210,7 +210,7 @@ bool wxFQDNValidator::Parse(const wxString &val_in, size_t i_start, size_t i_end wxIMPLEMENT_DYNAMIC_CLASS(wxFQDNListValidator, wxValidator); -wxFQDNListValidator::wxFQDNListValidator(std::list *val) : +wxFQDNListValidator::wxFQDNListValidator(std::list *val) : m_val(val), wxValidator() { @@ -246,7 +246,7 @@ bool wxFQDNListValidator::TransferToWindow() if (m_val) { wxString str; - for (std::list::const_iterator name = m_val->cbegin(), name_end = m_val->cend(); name != name_end; ++name) { + for (std::list::const_iterator name = m_val->cbegin(), name_end = m_val->cend(); name != name_end; ++name) { if (!str.IsEmpty()) str += wxT("; "); str += *name; } @@ -267,11 +267,11 @@ bool wxFQDNListValidator::TransferFromWindow() } -bool wxFQDNListValidator::Parse(const wxString &val_in, size_t i_start, size_t i_end, wxTextCtrl *ctrl, wxWindow *parent, std::list *val_out) +bool wxFQDNListValidator::Parse(const wxString &val_in, size_t i_start, size_t i_end, wxTextCtrl *ctrl, wxWindow *parent, std::list *val_out) { const wxStringCharType *buf = val_in; - std::string _fqdn, *fqdn = val_out ? &_fqdn : NULL; - std::list _val_out; + std::wstring _fqdn, *fqdn = val_out ? &_fqdn : NULL; + std::list _val_out; size_t i = i_start; for (;;) { @@ -603,21 +603,3 @@ void wxTLSConfigPanel::OnInitDialog(wxInitDialogEvent& event) if (m_credentials) m_credentials->GetEventHandler()->ProcessEvent(event); } - - -bool wxTLSConfigPanel::TransferDataFromWindow() -{ - wxCHECK(wxPanel::TransferDataFromWindow(), false); - - if (!m_prov.m_read_only) { - // This is not a provider-locked configuration. The data will get saved. - - // Reset session ID and master secret to force clean connect next time. - m_cfg.m_session_id.clear(); - m_cfg.m_master_secret.clear(); - } - - return true; -} - - diff --git a/lib/TTLS/include/Method.h b/lib/TTLS/include/Method.h index d1d0204..2a74f47 100644 --- a/lib/TTLS/include/Method.h +++ b/lib/TTLS/include/Method.h @@ -112,14 +112,15 @@ namespace eap /// @} - /// - /// Generates master session key - /// - /// \sa [The EAP-TLS Authentication Protocol (Chapter 2.3. Key Hierarchy)](https://tools.ietf.org/html/rfc5216#section-2.3) - /// - virtual void derive_msk(); - protected: + /// + /// Processes an application message + /// + /// \param[in] msg Application message data + /// \param[in] size_msg Application message data size + /// + virtual void process_application_data(_In_bytecount_(size_msg) const void *msg, _In_ size_t size_msg); + /// /// Makes a PAP client message /// diff --git a/lib/TTLS/src/Method.cpp b/lib/TTLS/src/Method.cpp index 838a461..6698343 100644 --- a/lib/TTLS/src/Method.cpp +++ b/lib/TTLS/src/Method.cpp @@ -71,23 +71,6 @@ void eap::method_ttls::process_request_packet( // Do the TLS. method_tls::process_request_packet(pReceivedPacket, dwReceivedPacketSize, pEapOutput); - - if (m_phase == phase_application_data) { - // Send inner authentication. - if (!m_state_client.m_alg_encrypt) - throw runtime_error(__FUNCTION__ " Refusing to send credentials unencrypted."); - - m_module.log_event(&EAPMETHOD_TTLS_INNER_CRED, event_data((unsigned int)eap_type_ttls), event_data(m_cred.m_inner->get_name()), event_data::blank); - - m_packet_res.m_code = EapCodeResponse; - m_packet_res.m_id = m_packet_req.m_id; - m_packet_res.m_flags = 0; - sanitizing_blob msg_application(make_message(tls_message_type_application_data, make_pap_client())); - m_packet_res.m_data.insert(m_packet_res.m_data.end(), msg_application.begin(), msg_application.end()); - - pEapOutput->fAllowNotifications = FALSE; - pEapOutput->action = EapPeerMethodResponseActionSend; - } } @@ -133,6 +116,13 @@ void eap::method_ttls::get_result( throw win_runtime_error(ERROR_NOT_SUPPORTED, __FUNCTION__ " Not supported."); } + // EAP-TTLS uses different label in PRF for MSK derivation than EAP-TLS. + static const DWORD s_key_id = 0x01; // EAP-TTLSv0 Keying Material + static const SecPkgContext_EapPrfInfo s_prf_info = { 0, sizeof(s_key_id), (PBYTE)&s_key_id }; + SECURITY_STATUS status = SetContextAttributes(m_sc_ctx, SECPKG_ATTR_EAP_PRF_INFO, (void*)&s_prf_info, sizeof(s_prf_info)); + if (FAILED(status)) + throw sec_runtime_error(status, __FUNCTION__ "Error setting EAP-TTLS PRF in Schannel."); + // The TLS was OK. method_tls::get_result(EapPeerMethodResultSuccess, ppResult); @@ -146,37 +136,51 @@ void eap::method_ttls::get_result( } -void eap::method_ttls::derive_msk() +void eap::method_ttls::process_application_data(_In_bytecount_(size_msg) const void *msg, _In_ size_t size_msg) { - // - // TLS versions 1.0 [RFC2246] and 1.1 [RFC4346] define the same PRF - // function, and any EAP-TTLSv0 implementation based on these versions - // of TLS must use the PRF defined therein. It is expected that future - // versions of or extensions to the TLS protocol will permit alternative - // PRF functions to be negotiated. If an alternative PRF function is - // specified for the underlying TLS version or has been negotiated - // during the TLS handshake negotiation, then that alternative PRF - // function must be used in EAP-TTLSv0 computations instead of the TLS - // 1.0/1.1 PRF. - // - // [Extensible Authentication Protocol Tunneled Transport Layer Security Authenticated Protocol Version 0 (EAP-TTLSv0) (Chapter 7.8. Use of TLS PRF)](https://tools.ietf.org/html/rfc5281#section-7.8) - // - // If we use PRF_SHA256() the key exchange fails. Therefore we use PRF of TLS 1.0/1.1. - // - static const unsigned char s_label[] = "ttls keying material"; - sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1); - seed.insert(seed.end(), (const unsigned char*)&m_random_client, (const unsigned char*)(&m_random_client + 1)); - seed.insert(seed.end(), (const unsigned char*)&m_random_server, (const unsigned char*)(&m_random_server + 1)); - sanitizing_blob key_block(prf(m_cp, CALG_TLS1PRF, m_master_secret, seed, 2*sizeof(tls_random))); - const unsigned char *_key_block = key_block.data(); + UNREFERENCED_PARAMETER(msg); + UNREFERENCED_PARAMETER(size_msg); - // MSK: MPPE-Recv-Key - memcpy(&m_key_mppe_client, _key_block, sizeof(tls_random)); - _key_block += sizeof(tls_random); + // Prepare inner authentication. + if (!(m_sc_ctx.m_attrib & ISC_RET_CONFIDENTIALITY)) + throw runtime_error(__FUNCTION__ " Refusing to send credentials unencrypted."); - // MSK: MPPE-Send-Key - memcpy(&m_key_mppe_server, _key_block, sizeof(tls_random)); - _key_block += sizeof(tls_random); + m_module.log_event(&EAPMETHOD_TTLS_INNER_CRED, event_data((unsigned int)eap_type_ttls), event_data(m_cred.m_inner->get_name()), event_data::blank); + + SECURITY_STATUS status; + + // Get maximum message sizes. + SecPkgContext_StreamSizes sizes; + status = QueryContextAttributes(m_sc_ctx, SECPKG_ATTR_STREAM_SIZES, &sizes); + if (FAILED(status)) + throw sec_runtime_error(status, __FUNCTION__ " Error getting Schannel required encryption sizes."); + + // Make PAP message. + sanitizing_blob msg_pap(make_pap_client()); + assert(msg_pap.size() < sizes.cbMaximumMessage); + unsigned long size_data = std::min(sizes.cbMaximumMessage, (unsigned long)msg_pap.size()); // Truncate + + sanitizing_blob data(sizes.cbHeader + size_data + sizes.cbTrailer, 0); + memcpy(data.data() + sizes.cbHeader, msg_pap.data(), size_data); + + // Prepare input/output buffer(s). + SecBuffer buf[] = { + { sizes.cbHeader, SECBUFFER_STREAM_HEADER , data.data() }, + { size_data, SECBUFFER_DATA , data.data() + sizes.cbHeader }, + { sizes.cbTrailer, SECBUFFER_STREAM_TRAILER, data.data() + sizes.cbHeader + size_data }, + { 0, SECBUFFER_EMPTY , NULL }, + }; + SecBufferDesc buf_desc = { + SECBUFFER_VERSION, + _countof(buf), + buf + }; + + // Encrypt the message. + status = EncryptMessage(m_sc_ctx, 0, &buf_desc, 0); + if (FAILED(status)) + throw sec_runtime_error(status, __FUNCTION__ " Error encrypting message."); + m_packet_res.m_data.insert(m_packet_res.m_data.end(), (const unsigned char*)buf[0].pvBuffer, (const unsigned char*)buf[0].pvBuffer + buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer); } diff --git a/lib/TTLS/src/StdAfx.h b/lib/TTLS/src/StdAfx.h index 25fe406..8602e9a 100644 --- a/lib/TTLS/src/StdAfx.h +++ b/lib/TTLS/src/StdAfx.h @@ -30,3 +30,4 @@ #include #include +#include diff --git a/lib/WinStd b/lib/WinStd index 8a0799c..f94b723 160000 --- a/lib/WinStd +++ b/lib/WinStd @@ -1 +1 @@ -Subproject commit 8a0799c2b75ebb783a6b777121e1e9ddc89a5549 +Subproject commit f94b72379ec67fad89a99336048890c4e7ddda34