From 92cfbb14edd31bbdadcf5b3102d3fe538a85a75d Mon Sep 17 00:00:00 2001 From: Pierre-Luc Boily Date: Tue, 28 Feb 2023 12:54:51 -0500 Subject: [PATCH 1/3] It is now possible to validate certificate with ip addresses --- ixwebsocket/IXSocketOpenSSL.cpp | 70 ++- ixwebsocket/IXSocketOpenSSL.cpp.bak | 919 ++++++++++++++++++++++++++++ 2 files changed, 983 insertions(+), 6 deletions(-) create mode 100644 ixwebsocket/IXSocketOpenSSL.cpp.bak diff --git a/ixwebsocket/IXSocketOpenSSL.cpp b/ixwebsocket/IXSocketOpenSSL.cpp index 12c95676..1f5c981b 100644 --- a/ixwebsocket/IXSocketOpenSSL.cpp +++ b/ixwebsocket/IXSocketOpenSSL.cpp @@ -16,17 +16,23 @@ #include #ifdef _WIN32 #include +//To avoid the problem with X509_NAME collision between macro in wincrypt.h and x509v3.h +//It has to be undef before x509v3.h and after wincrypt.h +#undef X509_NAME #else #include #endif -#if OPENSSL_VERSION_NUMBER < 0x10100000L + #include -#endif + #define socketerrno errno #ifdef _WIN32 // For manipulating the certificate store #include +// To avoid the problem with X509_NAME collision between macro in wincrypt.h and x509v3.h +// It has to be undef before x509v3.h and after wincrypt.h +#undef X509_NAME #endif #ifdef _WIN32 @@ -77,6 +83,43 @@ namespace return true; } + + bool isValidIpAddress(const std::string& s) + { + std::vector v; + int n = s.size(); + int start = 0, end = 0; + while (end < n) + { + while (end < n && s[end] != '.') + { + ++end; + } + if (start == end) { + return false; + } + int val = 0; + for (int i = start; i < end; ++i) + { + if (s[i] < '0' || s[i] > '9') + { + return false; + } + val = val * 10 + (s[i] - '0'); + } + if (val < 0 || val > 255) { + return false; + } + v.push_back(val); + start = ++end; + } + if (v.size() != 4) + { + return false; + } + return true; + } + } // namespace #endif @@ -763,10 +806,25 @@ namespace ix // (The docs say that this should work from 1.0.2, and is the default from // 1.1.0, but it does not. To be on the safe side, the manual test // below is enabled for all versions prior to 1.1.0.) - if (!_tlsOptions.disable_hostname_validation) - { - X509_VERIFY_PARAM* param = SSL_get0_param(_ssl_connection); - X509_VERIFY_PARAM_set1_host(param, host.c_str(), host.size()); + if (isValidIpAddress(host)) + { + // We are connecting to an IP address. let OpenSSL validate the + // IP address in SAN + X509_VERIFY_PARAM *param = SSL_get0_param(_ssl_connection); + X509_VERIFY_PARAM_set1_host(param, NULL, 0); + X509_VERIFY_PARAM_set1_ip_asc(param, host.c_str()); + } + else if (!_tlsOptions.disable_hostname_validation) + { + SSL_set1_host(_ssl_connection, host.c_str()); + // Both CN-ID and partial wildcards are deprecated + // Optionally, reject all wildcards via: + // X509_CHECK_FLAG_NO_WILDCARDS + // See X509_check_host(3). + // + SSL_set_hostflags(_ssl_connection, + X509_CHECK_FLAG_NEVER_CHECK_SUBJECT | + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); } #endif handshakeSuccessful = openSSLClientHandshake(host, errMsg, isCancellationRequested); diff --git a/ixwebsocket/IXSocketOpenSSL.cpp.bak b/ixwebsocket/IXSocketOpenSSL.cpp.bak new file mode 100644 index 00000000..b73be23b --- /dev/null +++ b/ixwebsocket/IXSocketOpenSSL.cpp.bak @@ -0,0 +1,919 @@ +/* + * IXSocketOpenSSL.cpp + * Author: Benjamin Sergeant, Matt DeBoer, Max Weisel + * Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved. + * + * Adapted from Satori SDK OpenSSL code. + */ +#ifdef IXWEBSOCKET_USE_OPEN_SSL + +#include "IXSocketOpenSSL.h" + +#include "IXSocketConnect.h" +#include "IXUniquePtr.h" +#include +#include +#include +#ifdef _WIN32 +#include +//To avoid the problem with X509_NAME collision between macro in wincrypt.h and x509v3.h +//It has to be undef before x509v3.h and after wincrypt.h +#undef X509_NAME +#else +#include +#endif + +#include + +#define socketerrno errno + +#ifdef _WIN32 +// For manipulating the certificate store +#include +// To avoid the problem with X509_NAME collision between macro in wincrypt.h and x509v3.h +// It has to be undef before x509v3.h and after wincrypt.h +#undef X509_NAME +#endif + +#ifdef _WIN32 +namespace +{ + bool loadWindowsSystemCertificates(SSL_CTX* ssl, std::string& errorMsg) + { + DWORD flags = CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG | + CERT_SYSTEM_STORE_CURRENT_USER; + HCERTSTORE systemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, flags, L"Root"); + + if (!systemStore) + { + errorMsg = "CertOpenStore failed with "; + errorMsg += std::to_string(GetLastError()); + return false; + } + + PCCERT_CONTEXT certificateIterator = NULL; + X509_STORE* opensslStore = SSL_CTX_get_cert_store(ssl); + + int certificateCount = 0; + while (certificateIterator = CertEnumCertificatesInStore(systemStore, certificateIterator)) + { + X509* x509 = d2i_X509(NULL, + (const unsigned char**) &certificateIterator->pbCertEncoded, + certificateIterator->cbCertEncoded); + + if (x509) + { + if (X509_STORE_add_cert(opensslStore, x509) == 1) + { + ++certificateCount; + } + + X509_free(x509); + } + } + + CertFreeCertificateContext(certificateIterator); + CertCloseStore(systemStore, 0); + + if (certificateCount == 0) + { + errorMsg = "No certificates found"; + return false; + } + + return true; + } + + bool isValidIpAddress(const std::string& s) + { + std::vector v; + int n = s.size(); + int start = 0, end = 0; + while (end < n) + { + while (end < n && s[end] != '.') + { + ++end; + } + if (start == end) { + return false; + } + int val = 0; + for (int i = start; i < end; ++i) + { + if (s[i] < '0' || s[i] > '9') + { + return false; + } + val = val * 10 + (s[i] - '0'); + } + if (val < 0 || val > 255) { + return false; + } + v.push_back(val); + start = ++end; + } + if (v.size() != 4) + { + return false; + } + return true; + } + +} // namespace +#endif + +namespace ix +{ + const std::string kDefaultCiphers = + "ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-SHA " + "ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES256-SHA384 " + "ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES128-SHA " + "ECDHE-RSA-AES256-SHA ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-SHA384 " + "DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-SHA " + "DHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES256-SHA256 AES128-SHA"; + + std::atomic SocketOpenSSL::_openSSLInitializationSuccessful(false); + std::once_flag SocketOpenSSL::_openSSLInitFlag; + std::vector> openSSLMutexes; + + SocketOpenSSL::SocketOpenSSL(const SocketTLSOptions& tlsOptions, int fd) + : Socket(fd) + , _ssl_connection(nullptr) + , _ssl_context(nullptr) + , _tlsOptions(tlsOptions) + { + std::call_once(_openSSLInitFlag, &SocketOpenSSL::openSSLInitialize, this); + } + + SocketOpenSSL::~SocketOpenSSL() + { + SocketOpenSSL::close(); + } + + void SocketOpenSSL::openSSLInitialize() + { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, nullptr)) return; +#else + (void) OPENSSL_config(nullptr); + + if (CRYPTO_get_locking_callback() == nullptr) + { + openSSLMutexes.clear(); + for (int i = 0; i < CRYPTO_num_locks(); ++i) + { + openSSLMutexes.push_back(ix::make_unique()); + } + CRYPTO_set_locking_callback(SocketOpenSSL::openSSLLockingCallback); + } +#endif + + (void) OpenSSL_add_ssl_algorithms(); + (void) SSL_load_error_strings(); + + _openSSLInitializationSuccessful = true; + } + + void SocketOpenSSL::openSSLLockingCallback(int mode, + int type, + const char* /*file*/, + int /*line*/) + { + if (mode & CRYPTO_LOCK) + { + openSSLMutexes[type]->lock(); + } + else + { + openSSLMutexes[type]->unlock(); + } + } + + std::string SocketOpenSSL::getSSLError(int ret) + { + unsigned long e; + + int err = SSL_get_error(_ssl_connection, ret); + + if (err == SSL_ERROR_WANT_CONNECT || err == SSL_ERROR_WANT_ACCEPT) + { + return "OpenSSL failed - connection failure"; + } + else if (err == SSL_ERROR_WANT_X509_LOOKUP) + { + return "OpenSSL failed - x509 error"; + } + else if (err == SSL_ERROR_SYSCALL) + { + e = ERR_get_error(); + if (e > 0) + { + std::string errMsg("OpenSSL failed - "); + errMsg += ERR_error_string(e, nullptr); + return errMsg; + } + else if (e == 0 && ret == 0) + { + return "OpenSSL failed - received early EOF"; + } + else + { + return "OpenSSL failed - underlying BIO reported an I/O error"; + } + } + else if (err == SSL_ERROR_SSL) + { + e = ERR_get_error(); + std::string errMsg("OpenSSL failed - "); + errMsg += ERR_error_string(e, nullptr); + return errMsg; + } + else if (err == SSL_ERROR_NONE) + { + return "OpenSSL failed - err none"; + } + else if (err == SSL_ERROR_ZERO_RETURN) + { + return "OpenSSL failed - err zero return"; + } + else + { + return "OpenSSL failed - unknown error"; + } + } + + SSL_CTX* SocketOpenSSL::openSSLCreateContext(std::string& errMsg) + { + const SSL_METHOD* method = SSLv23_client_method(); + if (method == nullptr) + { + errMsg = "SSLv23_client_method failure"; + return nullptr; + } + _ssl_method = method; + + SSL_CTX* ctx = SSL_CTX_new(_ssl_method); + if (ctx) + { + SSL_CTX_set_mode(ctx, + SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + int options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_CIPHER_SERVER_PREFERENCE; + +#ifdef SSL_OP_NO_TLSv1_3 + // (partially?) work around hang in openssl 1.1.1b, by disabling TLS V1.3 + // https://github.com/openssl/openssl/issues/7967 + options |= SSL_OP_NO_TLSv1_3; +#endif + SSL_CTX_set_options(ctx, options); + } + return ctx; + } + + bool SocketOpenSSL::openSSLAddCARootsFromString(const std::string roots) + { + // Create certificate store + X509_STORE* certificate_store = SSL_CTX_get_cert_store(_ssl_context); + if (certificate_store == nullptr) return false; + + // Configure to allow intermediate certs + X509_STORE_set_flags(certificate_store, + X509_V_FLAG_TRUSTED_FIRST | X509_V_FLAG_PARTIAL_CHAIN); + + // Create a new buffer and populate it with the roots + BIO* buffer = BIO_new_mem_buf((void*) roots.c_str(), static_cast(roots.length())); + if (buffer == nullptr) return false; + + // Read each root in the buffer and add to the certificate store + bool success = true; + size_t number_of_roots = 0; + + while (true) + { + // Read the next root in the buffer + X509* root = PEM_read_bio_X509_AUX(buffer, nullptr, nullptr, (void*) ""); + if (root == nullptr) + { + // No more certs left in the buffer, we're done. + ERR_clear_error(); + break; + } + + // Try adding the root to the certificate store + ERR_clear_error(); + if (!X509_STORE_add_cert(certificate_store, root)) + { + // Failed to add. If the error is unrelated to the x509 lib or the cert already + // exists, we're safe to continue. + unsigned long error = ERR_get_error(); + if (ERR_GET_LIB(error) != ERR_LIB_X509 || + ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE) + { + // Failed. Clean up and bail. + success = false; + X509_free(root); + break; + } + } + + // Clean up and loop + X509_free(root); + number_of_roots++; + } + + // Clean up buffer + BIO_free(buffer); + + // Make sure we loaded at least one certificate. + if (number_of_roots == 0) success = false; + + return success; + } + + /** + * Check whether a hostname matches a pattern + */ + bool SocketOpenSSL::checkHost(const std::string& host, const char* pattern) + { +#ifdef _WIN32 + return PathMatchSpecA(host.c_str(), pattern); +#else + return fnmatch(pattern, host.c_str(), 0) != FNM_NOMATCH; +#endif + } + + bool SocketOpenSSL::openSSLCheckServerCert(SSL* ssl, +#if OPENSSL_VERSION_NUMBER < 0x10100000L + const std::string& hostname, +#else + const std::string& /* hostname */, +#endif + std::string& errMsg) + { + X509* server_cert = SSL_get_peer_certificate(ssl); + if (server_cert == nullptr) + { + errMsg = "OpenSSL failed - peer didn't present a X509 certificate."; + return false; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + // Check server name + bool hostname_verifies_ok = false; + STACK_OF(GENERAL_NAME)* san_names = (STACK_OF(GENERAL_NAME)*) X509_get_ext_d2i( + (X509*) server_cert, NID_subject_alt_name, NULL, NULL); + if (san_names) + { + for (int i = 0; i < sk_GENERAL_NAME_num(san_names); i++) + { + const GENERAL_NAME* sk_name = sk_GENERAL_NAME_value(san_names, i); + if (sk_name->type == GEN_DNS) + { + char* name = (char*) ASN1_STRING_data(sk_name->d.dNSName); + if ((size_t) ASN1_STRING_length(sk_name->d.dNSName) == strlen(name) && + checkHost(hostname, name)) + { + hostname_verifies_ok = true; + break; + } + } + } + } + sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); + + if (!hostname_verifies_ok) + { + int cn_pos = X509_NAME_get_index_by_NID( + X509_get_subject_name((X509*) server_cert), NID_commonName, -1); + if (cn_pos >= 0) + { + X509_NAME_ENTRY* cn_entry = + X509_NAME_get_entry(X509_get_subject_name((X509*) server_cert), cn_pos); + + if (cn_entry != nullptr) + { + ASN1_STRING* cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry); + char* cn = (char*) ASN1_STRING_data(cn_asn1); + + if ((size_t) ASN1_STRING_length(cn_asn1) == strlen(cn) && + checkHost(hostname, cn)) + { + hostname_verifies_ok = true; + } + } + } + } + + if (!hostname_verifies_ok) + { + errMsg = "OpenSSL failed - certificate was issued for a different domain."; + return false; + } +#endif + + X509_free(server_cert); + return true; + } + + bool SocketOpenSSL::openSSLClientHandshake(const std::string& host, + std::string& errMsg, + const CancellationRequest& isCancellationRequested) + { + while (true) + { + if (_ssl_connection == nullptr || _ssl_context == nullptr) + { + return false; + } + + if (isCancellationRequested()) + { + errMsg = "Cancellation requested"; + return false; + } + + ERR_clear_error(); + int connect_result = SSL_connect(_ssl_connection); + if (connect_result == 1) + { + if (_tlsOptions.disable_hostname_validation) + { + return true; + } + + return openSSLCheckServerCert(_ssl_connection, host, errMsg); + } + int reason = SSL_get_error(_ssl_connection, connect_result); + + bool rc = false; + if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE) + { + rc = true; + } + else + { + errMsg = getSSLError(connect_result); + rc = false; + } + + if (!rc) + { + return false; + } + } + } + + bool SocketOpenSSL::openSSLServerHandshake(std::string& errMsg) + { + while (true) + { + if (_ssl_connection == nullptr || _ssl_context == nullptr) + { + return false; + } + + ERR_clear_error(); + int accept_result = SSL_accept(_ssl_connection); + if (accept_result == 1) + { + return true; + } + int reason = SSL_get_error(_ssl_connection, accept_result); + + bool rc = false; + if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE) + { + rc = true; + } + else + { + errMsg = getSSLError(accept_result); + rc = false; + } + + if (!rc) + { + return false; + } + } + } + + bool SocketOpenSSL::handleTLSOptions(std::string& errMsg) + { + ERR_clear_error(); + if (_tlsOptions.hasCertAndKey()) + { + if (SSL_CTX_use_certificate_chain_file(_ssl_context, _tlsOptions.certFile.c_str()) != 1) + { + auto sslErr = ERR_get_error(); + errMsg = "OpenSSL failed - SSL_CTX_use_certificate_chain_file(\"" + + _tlsOptions.certFile + "\") failed: "; + errMsg += ERR_error_string(sslErr, nullptr); + } + else if (SSL_CTX_use_PrivateKey_file( + _ssl_context, _tlsOptions.keyFile.c_str(), SSL_FILETYPE_PEM) != 1) + { + auto sslErr = ERR_get_error(); + errMsg = "OpenSSL failed - SSL_CTX_use_PrivateKey_file(\"" + _tlsOptions.keyFile + + "\") failed: "; + errMsg += ERR_error_string(sslErr, nullptr); + } + else if (!SSL_CTX_check_private_key(_ssl_context)) + { + auto sslErr = ERR_get_error(); + errMsg = "OpenSSL failed - cert/key mismatch(\"" + _tlsOptions.certFile + ", " + + _tlsOptions.keyFile + "\")"; + errMsg += ERR_error_string(sslErr, nullptr); + } + } + + ERR_clear_error(); + if (!_tlsOptions.isPeerVerifyDisabled()) + { + if (_tlsOptions.isUsingSystemDefaults()) + { +#ifdef _WIN32 + if (!loadWindowsSystemCertificates(_ssl_context, errMsg)) + { + return false; + } +#else + if (SSL_CTX_set_default_verify_paths(_ssl_context) == 0) + { + auto sslErr = ERR_get_error(); + errMsg = "OpenSSL failed - SSL_CTX_default_verify_paths loading failed: "; + errMsg += ERR_error_string(sslErr, nullptr); + return false; + } +#endif + } + else + { + if (_tlsOptions.isUsingInMemoryCAs()) + { + // Load from memory + openSSLAddCARootsFromString(_tlsOptions.caFile); + } + else + { + if (SSL_CTX_load_verify_locations( + _ssl_context, _tlsOptions.caFile.c_str(), NULL) != 1) + { + auto sslErr = ERR_get_error(); + errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" + + _tlsOptions.caFile + "\") failed: "; + errMsg += ERR_error_string(sslErr, nullptr); + return false; + } + } + } + + SSL_CTX_set_verify(_ssl_context, + SSL_VERIFY_PEER, + [](int preverify, X509_STORE_CTX*) -> int { return preverify; }); + SSL_CTX_set_verify_depth(_ssl_context, 4); + } + else + { + SSL_CTX_set_verify(_ssl_context, SSL_VERIFY_NONE, nullptr); + } + + if (_tlsOptions.isUsingDefaultCiphers()) + { + if (SSL_CTX_set_cipher_list(_ssl_context, kDefaultCiphers.c_str()) != 1) + { + auto sslErr = ERR_get_error(); + errMsg = "OpenSSL failed - SSL_CTX_set_cipher_list(\"" + kDefaultCiphers + + "\") failed: "; + errMsg += ERR_error_string(sslErr, nullptr); + return false; + } + } + else if (SSL_CTX_set_cipher_list(_ssl_context, _tlsOptions.ciphers.c_str()) != 1) + { + auto sslErr = ERR_get_error(); + errMsg = "OpenSSL failed - SSL_CTX_set_cipher_list(\"" + _tlsOptions.ciphers + + "\") failed: "; + errMsg += ERR_error_string(sslErr, nullptr); + return false; + } + + return true; + } + + bool SocketOpenSSL::accept(std::string& errMsg) + { + bool handshakeSuccessful = false; + { + std::lock_guard lock(_mutex); + + if (!_openSSLInitializationSuccessful) + { + errMsg = "OPENSSL_init_ssl failure"; + return false; + } + + if (_sockfd == -1) + { + return false; + } + + { + const SSL_METHOD* method = SSLv23_server_method(); + if (method == nullptr) + { + errMsg = "SSLv23_server_method failure"; + _ssl_context = nullptr; + } + else + { + _ssl_method = method; + + _ssl_context = SSL_CTX_new(_ssl_method); + if (_ssl_context) + { + SSL_CTX_set_mode(_ssl_context, SSL_MODE_ENABLE_PARTIAL_WRITE); + SSL_CTX_set_mode(_ssl_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + SSL_CTX_set_options(_ssl_context, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + } + } + } + + if (_ssl_context == nullptr) + { + return false; + } + + ERR_clear_error(); + if (_tlsOptions.hasCertAndKey()) + { + if (SSL_CTX_use_certificate_chain_file(_ssl_context, + _tlsOptions.certFile.c_str()) != 1) + { + auto sslErr = ERR_get_error(); + errMsg = "OpenSSL failed - SSL_CTX_use_certificate_chain_file(\"" + + _tlsOptions.certFile + "\") failed: "; + errMsg += ERR_error_string(sslErr, nullptr); + } + else if (SSL_CTX_use_PrivateKey_file( + _ssl_context, _tlsOptions.keyFile.c_str(), SSL_FILETYPE_PEM) != 1) + { + auto sslErr = ERR_get_error(); + errMsg = "OpenSSL failed - SSL_CTX_use_PrivateKey_file(\"" + + _tlsOptions.keyFile + "\") failed: "; + errMsg += ERR_error_string(sslErr, nullptr); + } + } + + + ERR_clear_error(); + if (!_tlsOptions.isPeerVerifyDisabled()) + { + if (_tlsOptions.isUsingSystemDefaults()) + { + if (SSL_CTX_set_default_verify_paths(_ssl_context) == 0) + { + auto sslErr = ERR_get_error(); + errMsg = "OpenSSL failed - SSL_CTX_default_verify_paths loading failed: "; + errMsg += ERR_error_string(sslErr, nullptr); + } + } + else + { + if (_tlsOptions.isUsingInMemoryCAs()) + { + // Load from memory + openSSLAddCARootsFromString(_tlsOptions.caFile); + } + else + { + const char* root_ca_file = _tlsOptions.caFile.c_str(); + STACK_OF(X509_NAME) * rootCAs; + rootCAs = SSL_load_client_CA_file(root_ca_file); + if (rootCAs == NULL) + { + auto sslErr = ERR_get_error(); + errMsg = "OpenSSL failed - SSL_load_client_CA_file('" + + _tlsOptions.caFile + "') failed: "; + errMsg += ERR_error_string(sslErr, nullptr); + } + else + { + SSL_CTX_set_client_CA_list(_ssl_context, rootCAs); + if (SSL_CTX_load_verify_locations( + _ssl_context, root_ca_file, nullptr) != 1) + { + auto sslErr = ERR_get_error(); + errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" + + _tlsOptions.caFile + "\") failed: "; + errMsg += ERR_error_string(sslErr, nullptr); + } + } + } + } + + SSL_CTX_set_verify( + _ssl_context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); + SSL_CTX_set_verify_depth(_ssl_context, 4); + } + else + { + SSL_CTX_set_verify(_ssl_context, SSL_VERIFY_NONE, nullptr); + } + if (_tlsOptions.isUsingDefaultCiphers()) + { + if (SSL_CTX_set_cipher_list(_ssl_context, kDefaultCiphers.c_str()) != 1) + { + return false; + } + } + else if (SSL_CTX_set_cipher_list(_ssl_context, _tlsOptions.ciphers.c_str()) != 1) + { + return false; + } + + _ssl_connection = SSL_new(_ssl_context); + if (_ssl_connection == nullptr) + { + errMsg = "OpenSSL failed to connect"; + SSL_CTX_free(_ssl_context); + _ssl_context = nullptr; + return false; + } + + SSL_set_ecdh_auto(_ssl_connection, 1); + + SSL_set_fd(_ssl_connection, _sockfd); + + handshakeSuccessful = openSSLServerHandshake(errMsg); + } + + if (!handshakeSuccessful) + { + close(); + return false; + } + + return true; + } + + bool SocketOpenSSL::connect(const std::string& host, + int port, + std::string& errMsg, + const CancellationRequest& isCancellationRequested) + { + bool handshakeSuccessful = false; + { + std::lock_guard lock(_mutex); + + if (!_openSSLInitializationSuccessful) + { + errMsg = "OPENSSL_init_ssl failure"; + return false; + } + + _sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested); + if (_sockfd == -1) return false; + + _ssl_context = openSSLCreateContext(errMsg); + if (_ssl_context == nullptr) + { + return false; + } + + if (!handleTLSOptions(errMsg)) + { + return false; + } + + _ssl_connection = SSL_new(_ssl_context); + if (_ssl_connection == nullptr) + { + errMsg = "OpenSSL failed to connect"; + SSL_CTX_free(_ssl_context); + _ssl_context = nullptr; + return false; + } + SSL_set_fd(_ssl_connection, _sockfd); + + // SNI support + SSL_set_tlsext_host_name(_ssl_connection, host.c_str()); + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + // Support for server name verification + // (The docs say that this should work from 1.0.2, and is the default from + // 1.1.0, but it does not. To be on the safe side, the manual test + // below is enabled for all versions prior to 1.1.0.) + if (isValidIpAddress(host)) + { + // We are connecting to an IP address. let OpenSSL validate the + // IP address in SAN + X509_VERIFY_PARAM *param = SSL_get0_param(_ssl_connection); + X509_VERIFY_PARAM_set1_host(param, NULL, 0); + X509_VERIFY_PARAM_set1_ip_asc(param, host.c_str()); + } + else + { + SSL_set1_host(_ssl_connection, host.c_str()); + // Both CN-ID and partial wildcards are deprecated + // Optionally, reject all wildcards via: + // X509_CHECK_FLAG_NO_WILDCARDS + // See X509_check_host(3). + // + SSL_set_hostflags(_ssl_connection, + X509_CHECK_FLAG_NEVER_CHECK_SUBJECT | + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); + } +#endif + handshakeSuccessful = openSSLClientHandshake(host, errMsg, isCancellationRequested); + } + + if (!handshakeSuccessful) + { + close(); + return false; + } + + return true; + } + + void SocketOpenSSL::close() + { + std::lock_guard lock(_mutex); + + if (_ssl_connection != nullptr) + { + SSL_free(_ssl_connection); + _ssl_connection = nullptr; + } + if (_ssl_context != nullptr) + { + SSL_CTX_free(_ssl_context); + _ssl_context = nullptr; + } + + Socket::close(); + } + + ssize_t SocketOpenSSL::send(char* buf, size_t nbyte) + { + std::lock_guard lock(_mutex); + + if (_ssl_connection == nullptr || _ssl_context == nullptr) + { + return 0; + } + + ERR_clear_error(); + ssize_t write_result = SSL_write(_ssl_connection, buf, (int) nbyte); + int reason = SSL_get_error(_ssl_connection, (int) write_result); + + if (reason == SSL_ERROR_NONE) + { + return write_result; + } + else if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE) + { + errno = EWOULDBLOCK; + return -1; + } + else + { + return -1; + } + } + + ssize_t SocketOpenSSL::recv(void* buf, size_t nbyte) + { + while (true) + { + std::lock_guard lock(_mutex); + + if (_ssl_connection == nullptr || _ssl_context == nullptr) + { + return 0; + } + + ERR_clear_error(); + ssize_t read_result = SSL_read(_ssl_connection, buf, (int) nbyte); + + if (read_result > 0) + { + return read_result; + } + + int reason = SSL_get_error(_ssl_connection, (int) read_result); + + if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE) + { + errno = EWOULDBLOCK; + } + return -1; + } + } + +} // namespace ix + +#endif // IXWEBSOCKET_USE_OPEN_SSL From 7528b0a9d7eca6ac0342a6d87054ae839114d7af Mon Sep 17 00:00:00 2001 From: Pierre-Luc Boily Date: Tue, 28 Feb 2023 12:56:36 -0500 Subject: [PATCH 2/3] remove file added previously by error --- ixwebsocket/IXSocketOpenSSL.cpp.bak | 919 ---------------------------- 1 file changed, 919 deletions(-) delete mode 100644 ixwebsocket/IXSocketOpenSSL.cpp.bak diff --git a/ixwebsocket/IXSocketOpenSSL.cpp.bak b/ixwebsocket/IXSocketOpenSSL.cpp.bak deleted file mode 100644 index b73be23b..00000000 --- a/ixwebsocket/IXSocketOpenSSL.cpp.bak +++ /dev/null @@ -1,919 +0,0 @@ -/* - * IXSocketOpenSSL.cpp - * Author: Benjamin Sergeant, Matt DeBoer, Max Weisel - * Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved. - * - * Adapted from Satori SDK OpenSSL code. - */ -#ifdef IXWEBSOCKET_USE_OPEN_SSL - -#include "IXSocketOpenSSL.h" - -#include "IXSocketConnect.h" -#include "IXUniquePtr.h" -#include -#include -#include -#ifdef _WIN32 -#include -//To avoid the problem with X509_NAME collision between macro in wincrypt.h and x509v3.h -//It has to be undef before x509v3.h and after wincrypt.h -#undef X509_NAME -#else -#include -#endif - -#include - -#define socketerrno errno - -#ifdef _WIN32 -// For manipulating the certificate store -#include -// To avoid the problem with X509_NAME collision between macro in wincrypt.h and x509v3.h -// It has to be undef before x509v3.h and after wincrypt.h -#undef X509_NAME -#endif - -#ifdef _WIN32 -namespace -{ - bool loadWindowsSystemCertificates(SSL_CTX* ssl, std::string& errorMsg) - { - DWORD flags = CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG | - CERT_SYSTEM_STORE_CURRENT_USER; - HCERTSTORE systemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, flags, L"Root"); - - if (!systemStore) - { - errorMsg = "CertOpenStore failed with "; - errorMsg += std::to_string(GetLastError()); - return false; - } - - PCCERT_CONTEXT certificateIterator = NULL; - X509_STORE* opensslStore = SSL_CTX_get_cert_store(ssl); - - int certificateCount = 0; - while (certificateIterator = CertEnumCertificatesInStore(systemStore, certificateIterator)) - { - X509* x509 = d2i_X509(NULL, - (const unsigned char**) &certificateIterator->pbCertEncoded, - certificateIterator->cbCertEncoded); - - if (x509) - { - if (X509_STORE_add_cert(opensslStore, x509) == 1) - { - ++certificateCount; - } - - X509_free(x509); - } - } - - CertFreeCertificateContext(certificateIterator); - CertCloseStore(systemStore, 0); - - if (certificateCount == 0) - { - errorMsg = "No certificates found"; - return false; - } - - return true; - } - - bool isValidIpAddress(const std::string& s) - { - std::vector v; - int n = s.size(); - int start = 0, end = 0; - while (end < n) - { - while (end < n && s[end] != '.') - { - ++end; - } - if (start == end) { - return false; - } - int val = 0; - for (int i = start; i < end; ++i) - { - if (s[i] < '0' || s[i] > '9') - { - return false; - } - val = val * 10 + (s[i] - '0'); - } - if (val < 0 || val > 255) { - return false; - } - v.push_back(val); - start = ++end; - } - if (v.size() != 4) - { - return false; - } - return true; - } - -} // namespace -#endif - -namespace ix -{ - const std::string kDefaultCiphers = - "ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-SHA " - "ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES256-SHA384 " - "ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES128-SHA " - "ECDHE-RSA-AES256-SHA ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-SHA384 " - "DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-SHA " - "DHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES256-SHA256 AES128-SHA"; - - std::atomic SocketOpenSSL::_openSSLInitializationSuccessful(false); - std::once_flag SocketOpenSSL::_openSSLInitFlag; - std::vector> openSSLMutexes; - - SocketOpenSSL::SocketOpenSSL(const SocketTLSOptions& tlsOptions, int fd) - : Socket(fd) - , _ssl_connection(nullptr) - , _ssl_context(nullptr) - , _tlsOptions(tlsOptions) - { - std::call_once(_openSSLInitFlag, &SocketOpenSSL::openSSLInitialize, this); - } - - SocketOpenSSL::~SocketOpenSSL() - { - SocketOpenSSL::close(); - } - - void SocketOpenSSL::openSSLInitialize() - { -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, nullptr)) return; -#else - (void) OPENSSL_config(nullptr); - - if (CRYPTO_get_locking_callback() == nullptr) - { - openSSLMutexes.clear(); - for (int i = 0; i < CRYPTO_num_locks(); ++i) - { - openSSLMutexes.push_back(ix::make_unique()); - } - CRYPTO_set_locking_callback(SocketOpenSSL::openSSLLockingCallback); - } -#endif - - (void) OpenSSL_add_ssl_algorithms(); - (void) SSL_load_error_strings(); - - _openSSLInitializationSuccessful = true; - } - - void SocketOpenSSL::openSSLLockingCallback(int mode, - int type, - const char* /*file*/, - int /*line*/) - { - if (mode & CRYPTO_LOCK) - { - openSSLMutexes[type]->lock(); - } - else - { - openSSLMutexes[type]->unlock(); - } - } - - std::string SocketOpenSSL::getSSLError(int ret) - { - unsigned long e; - - int err = SSL_get_error(_ssl_connection, ret); - - if (err == SSL_ERROR_WANT_CONNECT || err == SSL_ERROR_WANT_ACCEPT) - { - return "OpenSSL failed - connection failure"; - } - else if (err == SSL_ERROR_WANT_X509_LOOKUP) - { - return "OpenSSL failed - x509 error"; - } - else if (err == SSL_ERROR_SYSCALL) - { - e = ERR_get_error(); - if (e > 0) - { - std::string errMsg("OpenSSL failed - "); - errMsg += ERR_error_string(e, nullptr); - return errMsg; - } - else if (e == 0 && ret == 0) - { - return "OpenSSL failed - received early EOF"; - } - else - { - return "OpenSSL failed - underlying BIO reported an I/O error"; - } - } - else if (err == SSL_ERROR_SSL) - { - e = ERR_get_error(); - std::string errMsg("OpenSSL failed - "); - errMsg += ERR_error_string(e, nullptr); - return errMsg; - } - else if (err == SSL_ERROR_NONE) - { - return "OpenSSL failed - err none"; - } - else if (err == SSL_ERROR_ZERO_RETURN) - { - return "OpenSSL failed - err zero return"; - } - else - { - return "OpenSSL failed - unknown error"; - } - } - - SSL_CTX* SocketOpenSSL::openSSLCreateContext(std::string& errMsg) - { - const SSL_METHOD* method = SSLv23_client_method(); - if (method == nullptr) - { - errMsg = "SSLv23_client_method failure"; - return nullptr; - } - _ssl_method = method; - - SSL_CTX* ctx = SSL_CTX_new(_ssl_method); - if (ctx) - { - SSL_CTX_set_mode(ctx, - SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - - int options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_CIPHER_SERVER_PREFERENCE; - -#ifdef SSL_OP_NO_TLSv1_3 - // (partially?) work around hang in openssl 1.1.1b, by disabling TLS V1.3 - // https://github.com/openssl/openssl/issues/7967 - options |= SSL_OP_NO_TLSv1_3; -#endif - SSL_CTX_set_options(ctx, options); - } - return ctx; - } - - bool SocketOpenSSL::openSSLAddCARootsFromString(const std::string roots) - { - // Create certificate store - X509_STORE* certificate_store = SSL_CTX_get_cert_store(_ssl_context); - if (certificate_store == nullptr) return false; - - // Configure to allow intermediate certs - X509_STORE_set_flags(certificate_store, - X509_V_FLAG_TRUSTED_FIRST | X509_V_FLAG_PARTIAL_CHAIN); - - // Create a new buffer and populate it with the roots - BIO* buffer = BIO_new_mem_buf((void*) roots.c_str(), static_cast(roots.length())); - if (buffer == nullptr) return false; - - // Read each root in the buffer and add to the certificate store - bool success = true; - size_t number_of_roots = 0; - - while (true) - { - // Read the next root in the buffer - X509* root = PEM_read_bio_X509_AUX(buffer, nullptr, nullptr, (void*) ""); - if (root == nullptr) - { - // No more certs left in the buffer, we're done. - ERR_clear_error(); - break; - } - - // Try adding the root to the certificate store - ERR_clear_error(); - if (!X509_STORE_add_cert(certificate_store, root)) - { - // Failed to add. If the error is unrelated to the x509 lib or the cert already - // exists, we're safe to continue. - unsigned long error = ERR_get_error(); - if (ERR_GET_LIB(error) != ERR_LIB_X509 || - ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE) - { - // Failed. Clean up and bail. - success = false; - X509_free(root); - break; - } - } - - // Clean up and loop - X509_free(root); - number_of_roots++; - } - - // Clean up buffer - BIO_free(buffer); - - // Make sure we loaded at least one certificate. - if (number_of_roots == 0) success = false; - - return success; - } - - /** - * Check whether a hostname matches a pattern - */ - bool SocketOpenSSL::checkHost(const std::string& host, const char* pattern) - { -#ifdef _WIN32 - return PathMatchSpecA(host.c_str(), pattern); -#else - return fnmatch(pattern, host.c_str(), 0) != FNM_NOMATCH; -#endif - } - - bool SocketOpenSSL::openSSLCheckServerCert(SSL* ssl, -#if OPENSSL_VERSION_NUMBER < 0x10100000L - const std::string& hostname, -#else - const std::string& /* hostname */, -#endif - std::string& errMsg) - { - X509* server_cert = SSL_get_peer_certificate(ssl); - if (server_cert == nullptr) - { - errMsg = "OpenSSL failed - peer didn't present a X509 certificate."; - return false; - } - -#if OPENSSL_VERSION_NUMBER < 0x10100000L - // Check server name - bool hostname_verifies_ok = false; - STACK_OF(GENERAL_NAME)* san_names = (STACK_OF(GENERAL_NAME)*) X509_get_ext_d2i( - (X509*) server_cert, NID_subject_alt_name, NULL, NULL); - if (san_names) - { - for (int i = 0; i < sk_GENERAL_NAME_num(san_names); i++) - { - const GENERAL_NAME* sk_name = sk_GENERAL_NAME_value(san_names, i); - if (sk_name->type == GEN_DNS) - { - char* name = (char*) ASN1_STRING_data(sk_name->d.dNSName); - if ((size_t) ASN1_STRING_length(sk_name->d.dNSName) == strlen(name) && - checkHost(hostname, name)) - { - hostname_verifies_ok = true; - break; - } - } - } - } - sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); - - if (!hostname_verifies_ok) - { - int cn_pos = X509_NAME_get_index_by_NID( - X509_get_subject_name((X509*) server_cert), NID_commonName, -1); - if (cn_pos >= 0) - { - X509_NAME_ENTRY* cn_entry = - X509_NAME_get_entry(X509_get_subject_name((X509*) server_cert), cn_pos); - - if (cn_entry != nullptr) - { - ASN1_STRING* cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry); - char* cn = (char*) ASN1_STRING_data(cn_asn1); - - if ((size_t) ASN1_STRING_length(cn_asn1) == strlen(cn) && - checkHost(hostname, cn)) - { - hostname_verifies_ok = true; - } - } - } - } - - if (!hostname_verifies_ok) - { - errMsg = "OpenSSL failed - certificate was issued for a different domain."; - return false; - } -#endif - - X509_free(server_cert); - return true; - } - - bool SocketOpenSSL::openSSLClientHandshake(const std::string& host, - std::string& errMsg, - const CancellationRequest& isCancellationRequested) - { - while (true) - { - if (_ssl_connection == nullptr || _ssl_context == nullptr) - { - return false; - } - - if (isCancellationRequested()) - { - errMsg = "Cancellation requested"; - return false; - } - - ERR_clear_error(); - int connect_result = SSL_connect(_ssl_connection); - if (connect_result == 1) - { - if (_tlsOptions.disable_hostname_validation) - { - return true; - } - - return openSSLCheckServerCert(_ssl_connection, host, errMsg); - } - int reason = SSL_get_error(_ssl_connection, connect_result); - - bool rc = false; - if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE) - { - rc = true; - } - else - { - errMsg = getSSLError(connect_result); - rc = false; - } - - if (!rc) - { - return false; - } - } - } - - bool SocketOpenSSL::openSSLServerHandshake(std::string& errMsg) - { - while (true) - { - if (_ssl_connection == nullptr || _ssl_context == nullptr) - { - return false; - } - - ERR_clear_error(); - int accept_result = SSL_accept(_ssl_connection); - if (accept_result == 1) - { - return true; - } - int reason = SSL_get_error(_ssl_connection, accept_result); - - bool rc = false; - if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE) - { - rc = true; - } - else - { - errMsg = getSSLError(accept_result); - rc = false; - } - - if (!rc) - { - return false; - } - } - } - - bool SocketOpenSSL::handleTLSOptions(std::string& errMsg) - { - ERR_clear_error(); - if (_tlsOptions.hasCertAndKey()) - { - if (SSL_CTX_use_certificate_chain_file(_ssl_context, _tlsOptions.certFile.c_str()) != 1) - { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_use_certificate_chain_file(\"" + - _tlsOptions.certFile + "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - } - else if (SSL_CTX_use_PrivateKey_file( - _ssl_context, _tlsOptions.keyFile.c_str(), SSL_FILETYPE_PEM) != 1) - { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_use_PrivateKey_file(\"" + _tlsOptions.keyFile + - "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - } - else if (!SSL_CTX_check_private_key(_ssl_context)) - { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - cert/key mismatch(\"" + _tlsOptions.certFile + ", " + - _tlsOptions.keyFile + "\")"; - errMsg += ERR_error_string(sslErr, nullptr); - } - } - - ERR_clear_error(); - if (!_tlsOptions.isPeerVerifyDisabled()) - { - if (_tlsOptions.isUsingSystemDefaults()) - { -#ifdef _WIN32 - if (!loadWindowsSystemCertificates(_ssl_context, errMsg)) - { - return false; - } -#else - if (SSL_CTX_set_default_verify_paths(_ssl_context) == 0) - { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_default_verify_paths loading failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - return false; - } -#endif - } - else - { - if (_tlsOptions.isUsingInMemoryCAs()) - { - // Load from memory - openSSLAddCARootsFromString(_tlsOptions.caFile); - } - else - { - if (SSL_CTX_load_verify_locations( - _ssl_context, _tlsOptions.caFile.c_str(), NULL) != 1) - { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" + - _tlsOptions.caFile + "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - return false; - } - } - } - - SSL_CTX_set_verify(_ssl_context, - SSL_VERIFY_PEER, - [](int preverify, X509_STORE_CTX*) -> int { return preverify; }); - SSL_CTX_set_verify_depth(_ssl_context, 4); - } - else - { - SSL_CTX_set_verify(_ssl_context, SSL_VERIFY_NONE, nullptr); - } - - if (_tlsOptions.isUsingDefaultCiphers()) - { - if (SSL_CTX_set_cipher_list(_ssl_context, kDefaultCiphers.c_str()) != 1) - { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_set_cipher_list(\"" + kDefaultCiphers + - "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - return false; - } - } - else if (SSL_CTX_set_cipher_list(_ssl_context, _tlsOptions.ciphers.c_str()) != 1) - { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_set_cipher_list(\"" + _tlsOptions.ciphers + - "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - return false; - } - - return true; - } - - bool SocketOpenSSL::accept(std::string& errMsg) - { - bool handshakeSuccessful = false; - { - std::lock_guard lock(_mutex); - - if (!_openSSLInitializationSuccessful) - { - errMsg = "OPENSSL_init_ssl failure"; - return false; - } - - if (_sockfd == -1) - { - return false; - } - - { - const SSL_METHOD* method = SSLv23_server_method(); - if (method == nullptr) - { - errMsg = "SSLv23_server_method failure"; - _ssl_context = nullptr; - } - else - { - _ssl_method = method; - - _ssl_context = SSL_CTX_new(_ssl_method); - if (_ssl_context) - { - SSL_CTX_set_mode(_ssl_context, SSL_MODE_ENABLE_PARTIAL_WRITE); - SSL_CTX_set_mode(_ssl_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - SSL_CTX_set_options(_ssl_context, - SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); - } - } - } - - if (_ssl_context == nullptr) - { - return false; - } - - ERR_clear_error(); - if (_tlsOptions.hasCertAndKey()) - { - if (SSL_CTX_use_certificate_chain_file(_ssl_context, - _tlsOptions.certFile.c_str()) != 1) - { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_use_certificate_chain_file(\"" + - _tlsOptions.certFile + "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - } - else if (SSL_CTX_use_PrivateKey_file( - _ssl_context, _tlsOptions.keyFile.c_str(), SSL_FILETYPE_PEM) != 1) - { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_use_PrivateKey_file(\"" + - _tlsOptions.keyFile + "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - } - } - - - ERR_clear_error(); - if (!_tlsOptions.isPeerVerifyDisabled()) - { - if (_tlsOptions.isUsingSystemDefaults()) - { - if (SSL_CTX_set_default_verify_paths(_ssl_context) == 0) - { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_default_verify_paths loading failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - } - } - else - { - if (_tlsOptions.isUsingInMemoryCAs()) - { - // Load from memory - openSSLAddCARootsFromString(_tlsOptions.caFile); - } - else - { - const char* root_ca_file = _tlsOptions.caFile.c_str(); - STACK_OF(X509_NAME) * rootCAs; - rootCAs = SSL_load_client_CA_file(root_ca_file); - if (rootCAs == NULL) - { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_load_client_CA_file('" + - _tlsOptions.caFile + "') failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - } - else - { - SSL_CTX_set_client_CA_list(_ssl_context, rootCAs); - if (SSL_CTX_load_verify_locations( - _ssl_context, root_ca_file, nullptr) != 1) - { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" + - _tlsOptions.caFile + "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - } - } - } - } - - SSL_CTX_set_verify( - _ssl_context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); - SSL_CTX_set_verify_depth(_ssl_context, 4); - } - else - { - SSL_CTX_set_verify(_ssl_context, SSL_VERIFY_NONE, nullptr); - } - if (_tlsOptions.isUsingDefaultCiphers()) - { - if (SSL_CTX_set_cipher_list(_ssl_context, kDefaultCiphers.c_str()) != 1) - { - return false; - } - } - else if (SSL_CTX_set_cipher_list(_ssl_context, _tlsOptions.ciphers.c_str()) != 1) - { - return false; - } - - _ssl_connection = SSL_new(_ssl_context); - if (_ssl_connection == nullptr) - { - errMsg = "OpenSSL failed to connect"; - SSL_CTX_free(_ssl_context); - _ssl_context = nullptr; - return false; - } - - SSL_set_ecdh_auto(_ssl_connection, 1); - - SSL_set_fd(_ssl_connection, _sockfd); - - handshakeSuccessful = openSSLServerHandshake(errMsg); - } - - if (!handshakeSuccessful) - { - close(); - return false; - } - - return true; - } - - bool SocketOpenSSL::connect(const std::string& host, - int port, - std::string& errMsg, - const CancellationRequest& isCancellationRequested) - { - bool handshakeSuccessful = false; - { - std::lock_guard lock(_mutex); - - if (!_openSSLInitializationSuccessful) - { - errMsg = "OPENSSL_init_ssl failure"; - return false; - } - - _sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested); - if (_sockfd == -1) return false; - - _ssl_context = openSSLCreateContext(errMsg); - if (_ssl_context == nullptr) - { - return false; - } - - if (!handleTLSOptions(errMsg)) - { - return false; - } - - _ssl_connection = SSL_new(_ssl_context); - if (_ssl_connection == nullptr) - { - errMsg = "OpenSSL failed to connect"; - SSL_CTX_free(_ssl_context); - _ssl_context = nullptr; - return false; - } - SSL_set_fd(_ssl_connection, _sockfd); - - // SNI support - SSL_set_tlsext_host_name(_ssl_connection, host.c_str()); - -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - // Support for server name verification - // (The docs say that this should work from 1.0.2, and is the default from - // 1.1.0, but it does not. To be on the safe side, the manual test - // below is enabled for all versions prior to 1.1.0.) - if (isValidIpAddress(host)) - { - // We are connecting to an IP address. let OpenSSL validate the - // IP address in SAN - X509_VERIFY_PARAM *param = SSL_get0_param(_ssl_connection); - X509_VERIFY_PARAM_set1_host(param, NULL, 0); - X509_VERIFY_PARAM_set1_ip_asc(param, host.c_str()); - } - else - { - SSL_set1_host(_ssl_connection, host.c_str()); - // Both CN-ID and partial wildcards are deprecated - // Optionally, reject all wildcards via: - // X509_CHECK_FLAG_NO_WILDCARDS - // See X509_check_host(3). - // - SSL_set_hostflags(_ssl_connection, - X509_CHECK_FLAG_NEVER_CHECK_SUBJECT | - X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); - } -#endif - handshakeSuccessful = openSSLClientHandshake(host, errMsg, isCancellationRequested); - } - - if (!handshakeSuccessful) - { - close(); - return false; - } - - return true; - } - - void SocketOpenSSL::close() - { - std::lock_guard lock(_mutex); - - if (_ssl_connection != nullptr) - { - SSL_free(_ssl_connection); - _ssl_connection = nullptr; - } - if (_ssl_context != nullptr) - { - SSL_CTX_free(_ssl_context); - _ssl_context = nullptr; - } - - Socket::close(); - } - - ssize_t SocketOpenSSL::send(char* buf, size_t nbyte) - { - std::lock_guard lock(_mutex); - - if (_ssl_connection == nullptr || _ssl_context == nullptr) - { - return 0; - } - - ERR_clear_error(); - ssize_t write_result = SSL_write(_ssl_connection, buf, (int) nbyte); - int reason = SSL_get_error(_ssl_connection, (int) write_result); - - if (reason == SSL_ERROR_NONE) - { - return write_result; - } - else if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE) - { - errno = EWOULDBLOCK; - return -1; - } - else - { - return -1; - } - } - - ssize_t SocketOpenSSL::recv(void* buf, size_t nbyte) - { - while (true) - { - std::lock_guard lock(_mutex); - - if (_ssl_connection == nullptr || _ssl_context == nullptr) - { - return 0; - } - - ERR_clear_error(); - ssize_t read_result = SSL_read(_ssl_connection, buf, (int) nbyte); - - if (read_result > 0) - { - return read_result; - } - - int reason = SSL_get_error(_ssl_connection, (int) read_result); - - if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE) - { - errno = EWOULDBLOCK; - } - return -1; - } - } - -} // namespace ix - -#endif // IXWEBSOCKET_USE_OPEN_SSL From 2e2a255a9de54b1ffb68611b68f9c8ed9e34d69e Mon Sep 17 00:00:00 2001 From: Pierre-Luc Boily Date: Mon, 6 Mar 2023 10:41:34 -0500 Subject: [PATCH 3/3] add test for the new function isValidIpAddress --- test/IXSocketTest.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/IXSocketTest.cpp b/test/IXSocketTest.cpp index ca423706..f7390c8b 100644 --- a/test/IXSocketTest.cpp +++ b/test/IXSocketTest.cpp @@ -95,4 +95,19 @@ TEST_CASE("socket", "[socket]") testSocket(host, port, request, socket, expectedStatus, timeoutSecs); } #endif + +TEST_CASE("isValidIpAddress") { + // Valid IPv4 addresses + CHECK(isValidIpAddress("127.0.0.1")); + CHECK(isValidIpAddress("192.168.0.1")); + CHECK(isValidIpAddress("10.0.0.1")); + CHECK(isValidIpAddress("172.16.0.1")); + + // Invalid IPv4 addresses + CHECK(!isValidIpAddress("256.0.0.1")); + CHECK(!isValidIpAddress("1.2.3.4.5")); + CHECK(!isValidIpAddress("192.168.0.")); + CHECK(!isValidIpAddress("192.168.0.-1")); + CHECK(!isValidIpAddress("192.168.0.256")); +} }