diff options
Diffstat (limited to 'src/plugins/tls')
30 files changed, 1374 insertions, 755 deletions
diff --git a/src/plugins/tls/CMakeLists.txt b/src/plugins/tls/CMakeLists.txt index a17cda9594..91b9267123 100644 --- a/src/plugins/tls/CMakeLists.txt +++ b/src/plugins/tls/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + if(QT_FEATURE_securetransport) add_subdirectory(securetransport) endif() diff --git a/src/plugins/tls/certonly/CMakeLists.txt b/src/plugins/tls/certonly/CMakeLists.txt index 14b769cbba..495f408144 100644 --- a/src/plugins/tls/certonly/CMakeLists.txt +++ b/src/plugins/tls/certonly/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_internal_add_plugin(QTlsBackendCertOnlyPlugin OUTPUT_NAME qcertonlybackend CLASS_NAME QTlsBackendCertOnly diff --git a/src/plugins/tls/openssl/CMakeLists.txt b/src/plugins/tls/openssl/CMakeLists.txt index a7fb54f08c..0e0a7a1552 100644 --- a/src/plugins/tls/openssl/CMakeLists.txt +++ b/src/plugins/tls/openssl/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_internal_add_plugin(QTlsBackendOpenSSLPlugin OUTPUT_NAME qopensslbackend CLASS_NAME QTlsBackendOpenSSL @@ -21,6 +24,10 @@ qt_internal_add_plugin(QTlsBackendOpenSSLPlugin OPENSSL_API_COMPAT=0x10100000L ) +if (WIN32) # Windows header issues + set_target_properties(QTlsBackendOpenSSLPlugin PROPERTIES UNITY_BUILD OFF) +endif() + qt_internal_extend_target(QTlsBackendOpenSSLPlugin CONDITION QT_FEATURE_dtls SOURCES qdtls_openssl.cpp qdtls_openssl_p.h @@ -48,12 +55,9 @@ qt_internal_extend_target(QTlsBackendOpenSSLPlugin CONDITION WIN32 crypt32 ) -qt_internal_extend_target(QTlsBackendOpenSSLPlugin CONDITION QT_FEATURE_openssl_linked - LIBRARIES - WrapOpenSSL::WrapOpenSSL -) - -qt_internal_extend_target(QTlsBackendOpenSSLPlugin CONDITION NOT QT_FEATURE_openssl_linked - LIBRARIES - WrapOpenSSLHeaders::WrapOpenSSLHeaders -) +if(QT_FEATURE_openssl_linked) + target_link_libraries(QTlsBackendOpenSSLPlugin PRIVATE WrapOpenSSL::WrapOpenSSL) +else() + qt_internal_add_target_include_dirs(QTlsBackendOpenSSLPlugin + WrapOpenSSLHeaders::WrapOpenSSLHeaders) +endif() diff --git a/src/plugins/tls/openssl/qdtls_openssl.cpp b/src/plugins/tls/openssl/qdtls_openssl.cpp index fe8cbf23e0..fc07a29ec8 100644 --- a/src/plugins/tls/openssl/qdtls_openssl.cpp +++ b/src/plugins/tls/openssl/qdtls_openssl.cpp @@ -1,11 +1,7 @@ // Copyright (C) 2018 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -#ifndef NOMINMAX -#define NOMINMAX -#endif // NOMINMAX - -#include <QtNetwork/private/qnativesocketengine_p.h> +#include <QtNetwork/private/qnativesocketengine_p_p.h> #include "qsslsocket_openssl_symbols_p.h" #include "qdtls_openssl_p.h" @@ -182,7 +178,7 @@ extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst, QMessageAuthenticationCode hmac(dtls->hashAlgorithm, dtls->secret); hmac.addData(peerData); - const QByteArray cookie = hmac.result(); + const QByteArrayView cookie = hmac.resultView(); Q_ASSERT(cookie.size() >= 0); // DTLS1_COOKIE_LENGTH is erroneously 256 bytes long, must be 255 - RFC 6347, 4.2.1. *cookieLength = qMin(DTLS1_COOKIE_LENGTH - 1, cookie.size()); @@ -205,7 +201,7 @@ extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie, return 0; return newCookieLength == cookieLength - && !std::memcmp(cookie, newCookie, cookieLength); + && !q_CRYPTO_memcmp(cookie, newCookie, size_t(cookieLength)); } extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx) @@ -1252,12 +1248,12 @@ unsigned QDtlsPrivateOpenSSL::pskClientCallback(const char *hint, char *identity return 0; // Copy data back into OpenSSL - const int identityLength = qMin(pskAuthenticator.identity().length(), + const int identityLength = qMin(pskAuthenticator.identity().size(), pskAuthenticator.maximumIdentityLength()); std::memcpy(identity, pskAuthenticator.identity().constData(), identityLength); identity[identityLength] = 0; - const int pskLength = qMin(pskAuthenticator.preSharedKey().length(), + const int pskLength = qMin(pskAuthenticator.preSharedKey().size(), pskAuthenticator.maximumPreSharedKeyLength()); std::memcpy(psk, pskAuthenticator.preSharedKey().constData(), pskLength); @@ -1283,7 +1279,7 @@ unsigned QDtlsPrivateOpenSSL::pskServerCallback(const char *identity, unsigned c return 0; // Copy data back into OpenSSL - const int pskLength = qMin(pskAuthenticator.preSharedKey().length(), + const int pskLength = qMin(pskAuthenticator.preSharedKey().size(), pskAuthenticator.maximumPreSharedKeyLength()); std::memcpy(psk, pskAuthenticator.preSharedKey().constData(), pskLength); @@ -1328,7 +1324,7 @@ bool QDtlsPrivateOpenSSL::verifyPeer() // Translate errors from the error list into QSslErrors using CertClass = QTlsPrivate::X509CertificateOpenSSL; errors.reserve(errors.size() + opensslErrors.size()); - for (const auto &error : qAsConst(opensslErrors)) { + for (const auto &error : std::as_const(opensslErrors)) { const auto value = peerCertificateChain.value(error.depth); errors << CertClass::openSSLErrorToQSslError(error.code, value); } diff --git a/src/plugins/tls/openssl/qsslcontext_openssl.cpp b/src/plugins/tls/openssl/qsslcontext_openssl.cpp index 9e59477634..75c192bd01 100644 --- a/src/plugins/tls/openssl/qsslcontext_openssl.cpp +++ b/src/plugins/tls/openssl/qsslcontext_openssl.cpp @@ -63,9 +63,9 @@ static inline QString msgErrorSettingEllipticCurves(const QString &why) return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why); } -long QSslContext::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions) +qssloptions QSslContext::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions) { - long options; + qssloptions options; switch (protocol) { QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED @@ -218,7 +218,7 @@ SSL* QSslContext::createSsl() QList<QByteArray> protocols = sslConfiguration.d.constData()->nextAllowedProtocols; if (!protocols.isEmpty()) { m_supportedNPNVersions.clear(); - for (int a = 0; a < protocols.count(); ++a) { + for (int a = 0; a < protocols.size(); ++a) { if (protocols.at(a).size() > 255) { qCWarning(lcTlsBackend) << "TLS NPN extension" << protocols.at(a) << "is too long and will be ignored."; @@ -230,7 +230,7 @@ SSL* QSslContext::createSsl() } if (m_supportedNPNVersions.size()) { m_npnContext.data = reinterpret_cast<unsigned char *>(m_supportedNPNVersions.data()); - m_npnContext.len = m_supportedNPNVersions.length(); + m_npnContext.len = m_supportedNPNVersions.size(); m_npnContext.status = QSslConfiguration::NextProtocolNegotiationNone; // Callback's type has a parameter 'const unsigned char ** out' // since it was introduced in 1.0.2. Internally, OpenSSL's own code @@ -476,7 +476,7 @@ QT_WARNING_POP } // Enable bug workarounds. - const long options = setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions); + const qssloptions options = setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions); q_SSL_CTX_set_options(sslContext->ctx, options); // Tell OpenSSL to release memory early @@ -632,7 +632,7 @@ QT_WARNING_POP // If we have any intermediate certificates then we need to add them to our chain bool first = true; - for (const QSslCertificate &cert : qAsConst(configuration.d->localCertificateChain)) { + for (const QSslCertificate &cert : std::as_const(configuration.d->localCertificateChain)) { if (first) { first = false; continue; @@ -697,12 +697,14 @@ QT_WARNING_POP return; } - if (!dhparams.isEmpty()) { + if (dhparams.isEmpty()) { + q_SSL_CTX_set_dh_auto(sslContext->ctx, 1); + } else { #ifndef OPENSSL_NO_DEPRECATED_3_0 const QByteArray ¶ms = dhparams.d->derData; const char *ptr = params.constData(); DH *dh = q_d2i_DHparams(nullptr, reinterpret_cast<const unsigned char **>(&ptr), - params.length()); + params.size()); if (dh == nullptr) qFatal("q_d2i_DHparams failed to convert QSslDiffieHellmanParameters to DER form"); q_SSL_CTX_set_tmp_dh(sslContext->ctx, dh); diff --git a/src/plugins/tls/openssl/qsslcontext_openssl_p.h b/src/plugins/tls/openssl/qsslcontext_openssl_p.h index 55af2af292..3bd39baf0c 100644 --- a/src/plugins/tls/openssl/qsslcontext_openssl_p.h +++ b/src/plugins/tls/openssl/qsslcontext_openssl_p.h @@ -37,7 +37,8 @@ public: bool allowRootCertOnDemandLoading); static std::shared_ptr<QSslContext> sharedFromPrivateConfiguration(QSslSocket::SslMode mode, QSslConfigurationPrivate *privConfiguration, bool allowRootCertOnDemandLoading); - static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions); + + static qssloptions setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions); QSslError::SslError error() const; QString errorString() const; diff --git a/src/plugins/tls/openssl/qssldiffiehellmanparameters_openssl.cpp b/src/plugins/tls/openssl/qssldiffiehellmanparameters_openssl.cpp index 81cbc6a12d..16e31e605f 100644 --- a/src/plugins/tls/openssl/qssldiffiehellmanparameters_openssl.cpp +++ b/src/plugins/tls/openssl/qssldiffiehellmanparameters_openssl.cpp @@ -137,8 +137,9 @@ int QTlsBackendOpenSSL::dhParametersFromPem(const QByteArray &pem, QByteArray *d if (isSafeDH(dh)) { char *buf = nullptr; const int len = q_i2d_DHparams(dh, reinterpret_cast<unsigned char **>(&buf)); + const auto freeBuf = qScopeGuard([&] { q_OPENSSL_free(buf); }); if (len > 0) - *data = QByteArray(buf, len); + data->assign({buf, len}); else return DHParams::InvalidInputDataError; } else { diff --git a/src/plugins/tls/openssl/qsslsocket_openssl_symbols.cpp b/src/plugins/tls/openssl/qsslsocket_openssl_symbols.cpp index c3d05f134c..4aa9ca6fb1 100644 --- a/src/plugins/tls/openssl/qsslsocket_openssl_symbols.cpp +++ b/src/plugins/tls/openssl/qsslsocket_openssl_symbols.cpp @@ -28,7 +28,6 @@ #elif QT_CONFIG(library) # include <QtCore/qlibrary.h> #endif -#include <QtCore/qmutex.h> #include <QtCore/qdatetime.h> #if defined(Q_OS_UNIX) #include <QtCore/qdir.h> @@ -123,7 +122,7 @@ DEFINEFUNC2(void, OPENSSL_sk_push, OPENSSL_STACK *a, a, void *b, b, return, DUMM DEFINEFUNC(void, OPENSSL_sk_free, OPENSSL_STACK *a, a, return, DUMMYARG) DEFINEFUNC2(void *, OPENSSL_sk_value, OPENSSL_STACK *a, a, int b, b, return nullptr, return) DEFINEFUNC(int, SSL_session_reused, SSL *a, a, return 0, return) -DEFINEFUNC2(unsigned long, SSL_CTX_set_options, SSL_CTX *ctx, ctx, unsigned long op, op, return 0, return) +DEFINEFUNC2(qssloptions, SSL_CTX_set_options, SSL_CTX *ctx, ctx, qssloptions op, op, return 0, return) using info_callback = void (*) (const SSL *ssl, int type, int val); DEFINEFUNC2(void, SSL_set_info_callback, SSL *ssl, ssl, info_callback cb, cb, return, return) DEFINEFUNC(const char *, SSL_alert_type_string, int value, value, return nullptr, return) @@ -156,10 +155,10 @@ DEFINEFUNC3(int, X509_STORE_set_ex_data, X509_STORE *a, a, int idx, idx, void *d DEFINEFUNC2(void *, X509_STORE_get_ex_data, X509_STORE *r, r, int idx, idx, return nullptr, return) DEFINEFUNC(STACK_OF(X509) *, X509_STORE_CTX_get0_chain, X509_STORE_CTX *a, a, return nullptr, return) DEFINEFUNC3(void, CRYPTO_free, void *str, str, const char *file, file, int line, line, return, DUMMYARG) +DEFINEFUNC3(int, CRYPTO_memcmp, const void * in_a, in_a, const void * in_b, in_b, size_t len, len, return 1, return); DEFINEFUNC(long, OpenSSL_version_num, void, DUMMYARG, return 0, return) DEFINEFUNC(const char *, OpenSSL_version, int a, a, return nullptr, return) DEFINEFUNC(unsigned long, SSL_SESSION_get_ticket_lifetime_hint, const SSL_SESSION *session, session, return 0, return) -DEFINEFUNC4(void, DH_get0_pqg, const DH *dh, dh, const BIGNUM **p, p, const BIGNUM **q, q, const BIGNUM **g, g, return, DUMMYARG) #if QT_CONFIG(dtls) DEFINEFUNC2(int, DTLSv1_listen, SSL *s, s, BIO_ADDR *c, c, return -1, return) @@ -263,7 +262,6 @@ DEFINEFUNC4(int, OBJ_obj2txt, char *a, a, int b, b, ASN1_OBJECT *c, c, int d, d, DEFINEFUNC(int, OBJ_obj2nid, const ASN1_OBJECT *a, a, return NID_undef, return) DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PrivateKey, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return) -DEFINEFUNC4(DH *, PEM_read_bio_DHparams, BIO *a, a, DH **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return) DEFINEFUNC7(int, PEM_write_bio_PrivateKey, BIO *a, a, EVP_PKEY *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return) DEFINEFUNC7(int, PEM_write_bio_PrivateKey_traditional, BIO *a, a, EVP_PKEY *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return) DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PUBKEY, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return) @@ -429,13 +427,21 @@ DEFINEFUNC2(void *, BIO_get_ex_data, BIO *b, b, int idx, idx, return nullptr, re DEFINEFUNC3(int, BIO_set_ex_data, BIO *b, b, int idx, idx, void *data, data, return -1, return) DEFINEFUNC3(void *, CRYPTO_malloc, size_t num, num, const char *file, file, int line, line, return nullptr, return) + +#ifndef OPENSSL_NO_DEPRECATED_3_0 DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return nullptr, return) DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG) +DEFINEFUNC2(int, DH_check, DH *dh, dh, int *codes, codes, return 0, return) +DEFINEFUNC4(void, DH_get0_pqg, const DH *dh, dh, const BIGNUM **p, p, const BIGNUM **q, q, const BIGNUM **g, g, return, DUMMYARG) + DEFINEFUNC3(DH *, d2i_DHparams, DH**a, a, const unsigned char **pp, pp, long length, length, return nullptr, return) DEFINEFUNC2(int, i2d_DHparams, DH *a, a, unsigned char **p, p, return -1, return) -DEFINEFUNC2(int, DH_check, DH *dh, dh, int *codes, codes, return 0, return) + +DEFINEFUNC4(DH *, PEM_read_bio_DHparams, BIO *a, a, DH **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return) +#endif DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return nullptr, return) + #ifndef OPENSSL_NO_EC DEFINEFUNC2(size_t, EC_get_builtin_curves, EC_builtin_curve * r, r, size_t nitems, nitems, return 0, return) DEFINEFUNC(int, EC_curve_nist2nid, const char *name, name, return 0, return) @@ -551,7 +557,7 @@ struct LibGreaterThan { const auto lhsparts = lhs.split(u'.'); const auto rhsparts = rhs.split(u'.'); - Q_ASSERT(lhsparts.count() > 1 && rhsparts.count() > 1); + Q_ASSERT(lhsparts.size() > 1 && rhsparts.size() > 1); // note: checking rhs < lhs, the same as lhs > rhs return std::lexicographical_compare(rhsparts.begin() + 1, rhsparts.end(), @@ -622,7 +628,7 @@ static QStringList findAllLibs(QLatin1StringView filter) QStringList entryList = dir.entryList(filters, QDir::Files); std::sort(entryList.begin(), entryList.end(), LibGreaterThan()); - for (const QString &entry : qAsConst(entryList)) + for (const QString &entry : std::as_const(entryList)) found << path + u'/' + entry; } @@ -640,6 +646,12 @@ static QStringList findAllLibCrypto() } # endif +#if (OPENSSL_VERSION_NUMBER >> 28) < 3 +#define QT_OPENSSL_VERSION "1_1" +#elif OPENSSL_VERSION_MAJOR == 3 // Starting with 3.0 this define is available +#define QT_OPENSSL_VERSION "3" +#endif // > 3 intentionally left undefined + #ifdef Q_OS_WIN struct LoadedOpenSsl { @@ -671,12 +683,6 @@ static LoadedOpenSsl loadOpenSsl() // MSVC and GCC. For 3.0 the version suffix changed again, to just '3'. // For non-x86 builds, an architecture suffix is also appended. -#if (OPENSSL_VERSION_NUMBER >> 28) < 3 -#define QT_OPENSSL_VERSION "1_1" -#elif OPENSSL_VERSION_MAJOR == 3 // Starting with 3.0 this define is available -#define QT_OPENSSL_VERSION "3" -#endif // > 3 intentionally left undefined - #if defined(Q_PROCESSOR_X86_64) #define QT_SSL_SUFFIX "-x64" #elif defined(Q_PROCESSOR_ARM_64) @@ -693,7 +699,7 @@ static LoadedOpenSsl loadOpenSsl() #undef QT_SSL_SUFFIX return result; } -#else +#else // !Q_OS_WIN: struct LoadedOpenSsl { std::unique_ptr<QLibrary> ssl, crypto; @@ -743,10 +749,22 @@ static LoadedOpenSsl loadOpenSsl() #ifdef Q_OS_OPENBSD libcrypto->setLoadHints(QLibrary::ExportExternalSymbolsHint); #endif -#if defined(SHLIB_VERSION_NUMBER) && !defined(Q_OS_QNX) // on QNX, the libs are always libssl.so and libcrypto.so + +#if !defined(Q_OS_QNX) // on QNX, the libs are always libssl.so and libcrypto.so + +#if defined(OPENSSL_SHLIB_VERSION) + // OpenSSL v.3 does not have SLIB_VERSION_NUMBER but has OPENSSL_SHLIB_VERSION. + // The comment about OPENSSL_SHLIB_VERSION in opensslv.h is a bit troublesome: + // "This is defined in free form." + auto shlibVersion = QString("%1"_L1).arg(OPENSSL_SHLIB_VERSION); + libssl->setFileNameAndVersion("ssl"_L1, shlibVersion); + libcrypto->setFileNameAndVersion("crypto"_L1, shlibVersion); +#elif defined(SHLIB_VERSION_NUMBER) // first attempt: the canonical name is libssl.so.<SHLIB_VERSION_NUMBER> libssl->setFileNameAndVersion("ssl"_L1, SHLIB_VERSION_NUMBER ""_L1); libcrypto->setFileNameAndVersion("crypto"_L1, SHLIB_VERSION_NUMBER ""_L1); +#endif // OPENSSL_SHLIB_VERSION + if (libcrypto->load() && libssl->load()) { // libssl.so.<SHLIB_VERSION_NUMBER> and libcrypto.so.<SHLIB_VERSION_NUMBER> found return result; @@ -754,7 +772,7 @@ static LoadedOpenSsl loadOpenSsl() libssl->unload(); libcrypto->unload(); } -#endif +#endif // !defined(Q_OS_QNX) #ifndef Q_OS_DARWIN // second attempt: find the development files libssl.so and libcrypto.so @@ -772,7 +790,7 @@ static LoadedOpenSsl loadOpenSsl() return suffix; }; - static QString suffix = QString::fromLatin1(openSSLSuffix("_1_1")); + static QString suffix = QString::fromLatin1(openSSLSuffix("_" QT_OPENSSL_VERSION)); libssl->setFileNameAndVersion("ssl"_L1 + suffix, -1); libcrypto->setFileNameAndVersion("crypto"_L1 + suffix, -1); @@ -832,412 +850,405 @@ static LoadedOpenSsl loadOpenSsl() } #endif -Q_CONSTINIT static QBasicMutex symbolResolveMutex; -Q_CONSTINIT static QBasicAtomicInt symbolsResolved = Q_BASIC_ATOMIC_INITIALIZER(false); -Q_CONSTINIT static bool triedToResolveSymbols = false; - bool q_resolveOpenSslSymbols() { - if (symbolsResolved.loadAcquire()) - return true; - QMutexLocker locker(&symbolResolveMutex); - if (symbolsResolved.loadRelaxed()) - return true; - if (triedToResolveSymbols) - return false; - triedToResolveSymbols = true; - - LoadedOpenSsl libs = loadOpenSsl(); - if (!libs.ssl || !libs.crypto) { - qCWarning(lcTlsBackend, "Failed to load libssl/libcrypto."); - return false; - } + static bool symbolsResolved = []() { + LoadedOpenSsl libs = loadOpenSsl(); + if (!libs.ssl || !libs.crypto) { + qCWarning(lcTlsBackend, "Failed to load libssl/libcrypto."); + return false; + } - RESOLVEFUNC(OPENSSL_init_ssl) - RESOLVEFUNC(OPENSSL_init_crypto) - RESOLVEFUNC(ASN1_STRING_get0_data) - RESOLVEFUNC(EVP_CIPHER_CTX_reset) - RESOLVEFUNC(AUTHORITY_INFO_ACCESS_free) - RESOLVEFUNC(EVP_PKEY_up_ref) - RESOLVEFUNC(EVP_PKEY_CTX_new) - RESOLVEFUNC(EVP_PKEY_param_check) - RESOLVEFUNC(EVP_PKEY_CTX_free) - RESOLVEFUNC(OPENSSL_sk_new_null) - RESOLVEFUNC(OPENSSL_sk_push) - RESOLVEFUNC(OPENSSL_sk_free) - RESOLVEFUNC(OPENSSL_sk_num) - RESOLVEFUNC(OPENSSL_sk_pop_free) - RESOLVEFUNC(OPENSSL_sk_value) - RESOLVEFUNC(DH_get0_pqg) - RESOLVEFUNC(SSL_CTX_set_options) - RESOLVEFUNC(SSL_set_info_callback) - RESOLVEFUNC(SSL_alert_type_string) - RESOLVEFUNC(SSL_alert_desc_string_long) - RESOLVEFUNC(SSL_CTX_get_security_level) - RESOLVEFUNC(SSL_CTX_set_security_level) + RESOLVEFUNC(OPENSSL_init_ssl) + RESOLVEFUNC(OPENSSL_init_crypto) + RESOLVEFUNC(ASN1_STRING_get0_data) + RESOLVEFUNC(EVP_CIPHER_CTX_reset) + RESOLVEFUNC(AUTHORITY_INFO_ACCESS_free) + RESOLVEFUNC(EVP_PKEY_up_ref) + RESOLVEFUNC(EVP_PKEY_CTX_new) + RESOLVEFUNC(EVP_PKEY_param_check) + RESOLVEFUNC(EVP_PKEY_CTX_free) + RESOLVEFUNC(OPENSSL_sk_new_null) + RESOLVEFUNC(OPENSSL_sk_push) + RESOLVEFUNC(OPENSSL_sk_free) + RESOLVEFUNC(OPENSSL_sk_num) + RESOLVEFUNC(OPENSSL_sk_pop_free) + RESOLVEFUNC(OPENSSL_sk_value) + RESOLVEFUNC(SSL_CTX_set_options) + RESOLVEFUNC(SSL_set_info_callback) + RESOLVEFUNC(SSL_alert_type_string) + RESOLVEFUNC(SSL_alert_desc_string_long) + RESOLVEFUNC(SSL_CTX_get_security_level) + RESOLVEFUNC(SSL_CTX_set_security_level) #ifdef TLS1_3_VERSION - RESOLVEFUNC(SSL_CTX_set_ciphersuites) - RESOLVEFUNC(SSL_set_psk_use_session_callback) - RESOLVEFUNC(SSL_CTX_sess_set_new_cb) - RESOLVEFUNC(SSL_SESSION_is_resumable) + RESOLVEFUNC(SSL_CTX_set_ciphersuites) + RESOLVEFUNC(SSL_set_psk_use_session_callback) + RESOLVEFUNC(SSL_CTX_sess_set_new_cb) + RESOLVEFUNC(SSL_SESSION_is_resumable) #endif // TLS 1.3 or OpenSSL > 1.1.1 - RESOLVEFUNC(SSL_get_client_random) - RESOLVEFUNC(SSL_SESSION_get_master_key) - RESOLVEFUNC(SSL_session_reused) - RESOLVEFUNC(SSL_get_session) - RESOLVEFUNC(SSL_set_options) - RESOLVEFUNC(CRYPTO_get_ex_new_index) - RESOLVEFUNC(TLS_method) - RESOLVEFUNC(TLS_client_method) - RESOLVEFUNC(TLS_server_method) - RESOLVEFUNC(X509_up_ref) - RESOLVEFUNC(X509_STORE_CTX_get0_chain) - RESOLVEFUNC(X509_getm_notBefore) - RESOLVEFUNC(X509_getm_notAfter) - RESOLVEFUNC(ASN1_item_free) - RESOLVEFUNC(X509V3_conf_free) - RESOLVEFUNC(X509_get_version) - RESOLVEFUNC(X509_get_pubkey) - RESOLVEFUNC(X509_STORE_set_verify_cb) - RESOLVEFUNC(X509_STORE_set_ex_data) - RESOLVEFUNC(X509_STORE_get_ex_data) - RESOLVEFUNC(CRYPTO_free) - RESOLVEFUNC(OpenSSL_version_num) - RESOLVEFUNC(OpenSSL_version) - - if (!_q_OpenSSL_version || !_q_OpenSSL_version_num) { - // Apparently, we were built with OpenSSL 1.1 enabled but are now using - // a wrong library. - qCWarning(lcTlsBackend, "Incompatible version of OpenSSL"); - return false; - } + RESOLVEFUNC(SSL_get_client_random) + RESOLVEFUNC(SSL_SESSION_get_master_key) + RESOLVEFUNC(SSL_session_reused) + RESOLVEFUNC(SSL_get_session) + RESOLVEFUNC(SSL_set_options) + RESOLVEFUNC(CRYPTO_get_ex_new_index) + RESOLVEFUNC(TLS_method) + RESOLVEFUNC(TLS_client_method) + RESOLVEFUNC(TLS_server_method) + RESOLVEFUNC(X509_up_ref) + RESOLVEFUNC(X509_STORE_CTX_get0_chain) + RESOLVEFUNC(X509_getm_notBefore) + RESOLVEFUNC(X509_getm_notAfter) + RESOLVEFUNC(ASN1_item_free) + RESOLVEFUNC(X509V3_conf_free) + RESOLVEFUNC(X509_get_version) + RESOLVEFUNC(X509_get_pubkey) + RESOLVEFUNC(X509_STORE_set_verify_cb) + RESOLVEFUNC(X509_STORE_set_ex_data) + RESOLVEFUNC(X509_STORE_get_ex_data) + RESOLVEFUNC(CRYPTO_free) + RESOLVEFUNC(CRYPTO_memcmp) + RESOLVEFUNC(OpenSSL_version_num) + RESOLVEFUNC(OpenSSL_version) + + if (!_q_OpenSSL_version || !_q_OpenSSL_version_num) { + // Apparently, we were built with OpenSSL 1.1 enabled but are now using + // a wrong library. + qCWarning(lcTlsBackend, "Incompatible version of OpenSSL"); + return false; + } #if OPENSSL_VERSION_NUMBER >= 0x30000000 - if (q_OpenSSL_version_num() < 0x30000000) { - qCWarning(lcTlsBackend, "Incompatible version of OpenSSL (built with OpenSSL >= 3.x, runtime version is < 3.x)"); - return false; - } + if (q_OpenSSL_version_num() < 0x30000000) { + qCWarning(lcTlsBackend, "Incompatible version of OpenSSL (built with OpenSSL >= 3.x, runtime version is < 3.x)"); + return false; + } #else - if (q_OpenSSL_version_num() >= 0x30000000) { - qCWarning(lcTlsBackend, "Incompatible version of OpenSSL (built with OpenSSL 1.x, runtime version is >= 3.x)"); - return false; - } + if (q_OpenSSL_version_num() >= 0x30000000) { + qCWarning(lcTlsBackend, "Incompatible version of OpenSSL (built with OpenSSL 1.x, runtime version is >= 3.x)"); + return false; + } #endif // OPENSSL_VERSION_NUMBER - RESOLVEFUNC(SSL_SESSION_get_ticket_lifetime_hint) + RESOLVEFUNC(SSL_SESSION_get_ticket_lifetime_hint) #if QT_CONFIG(dtls) - RESOLVEFUNC(DTLSv1_listen) - RESOLVEFUNC(BIO_ADDR_new) - RESOLVEFUNC(BIO_ADDR_free) - RESOLVEFUNC(BIO_meth_new) - RESOLVEFUNC(BIO_meth_free) - RESOLVEFUNC(BIO_meth_set_write) - RESOLVEFUNC(BIO_meth_set_read) - RESOLVEFUNC(BIO_meth_set_puts) - RESOLVEFUNC(BIO_meth_set_ctrl) - RESOLVEFUNC(BIO_meth_set_create) - RESOLVEFUNC(BIO_meth_set_destroy) + RESOLVEFUNC(DTLSv1_listen) + RESOLVEFUNC(BIO_ADDR_new) + RESOLVEFUNC(BIO_ADDR_free) + RESOLVEFUNC(BIO_meth_new) + RESOLVEFUNC(BIO_meth_free) + RESOLVEFUNC(BIO_meth_set_write) + RESOLVEFUNC(BIO_meth_set_read) + RESOLVEFUNC(BIO_meth_set_puts) + RESOLVEFUNC(BIO_meth_set_ctrl) + RESOLVEFUNC(BIO_meth_set_create) + RESOLVEFUNC(BIO_meth_set_destroy) #endif // dtls #if QT_CONFIG(ocsp) - RESOLVEFUNC(OCSP_SINGLERESP_get0_id) - RESOLVEFUNC(d2i_OCSP_RESPONSE) - RESOLVEFUNC(OCSP_RESPONSE_free) - RESOLVEFUNC(OCSP_response_status) - RESOLVEFUNC(OCSP_response_get1_basic) - RESOLVEFUNC(OCSP_BASICRESP_free) - RESOLVEFUNC(OCSP_basic_verify) - RESOLVEFUNC(OCSP_resp_count) - RESOLVEFUNC(OCSP_resp_get0) - RESOLVEFUNC(OCSP_single_get0_status) - RESOLVEFUNC(OCSP_check_validity) - RESOLVEFUNC(OCSP_cert_to_id) - RESOLVEFUNC(OCSP_id_get0_info) - RESOLVEFUNC(OCSP_resp_get0_certs) - RESOLVEFUNC(OCSP_basic_sign) - RESOLVEFUNC(OCSP_response_create) - RESOLVEFUNC(i2d_OCSP_RESPONSE) - RESOLVEFUNC(OCSP_basic_add1_status) - RESOLVEFUNC(OCSP_BASICRESP_new) - RESOLVEFUNC(OCSP_CERTID_free) - RESOLVEFUNC(OCSP_cert_to_id) - RESOLVEFUNC(OCSP_id_cmp) + RESOLVEFUNC(OCSP_SINGLERESP_get0_id) + RESOLVEFUNC(d2i_OCSP_RESPONSE) + RESOLVEFUNC(OCSP_RESPONSE_free) + RESOLVEFUNC(OCSP_response_status) + RESOLVEFUNC(OCSP_response_get1_basic) + RESOLVEFUNC(OCSP_BASICRESP_free) + RESOLVEFUNC(OCSP_basic_verify) + RESOLVEFUNC(OCSP_resp_count) + RESOLVEFUNC(OCSP_resp_get0) + RESOLVEFUNC(OCSP_single_get0_status) + RESOLVEFUNC(OCSP_check_validity) + RESOLVEFUNC(OCSP_cert_to_id) + RESOLVEFUNC(OCSP_id_get0_info) + RESOLVEFUNC(OCSP_resp_get0_certs) + RESOLVEFUNC(OCSP_basic_sign) + RESOLVEFUNC(OCSP_response_create) + RESOLVEFUNC(i2d_OCSP_RESPONSE) + RESOLVEFUNC(OCSP_basic_add1_status) + RESOLVEFUNC(OCSP_BASICRESP_new) + RESOLVEFUNC(OCSP_CERTID_free) + RESOLVEFUNC(OCSP_cert_to_id) + RESOLVEFUNC(OCSP_id_cmp) #endif // ocsp - RESOLVEFUNC(BIO_set_data) - RESOLVEFUNC(BIO_get_data) - RESOLVEFUNC(BIO_set_init) - RESOLVEFUNC(BIO_get_shutdown) - RESOLVEFUNC(BIO_set_shutdown) - RESOLVEFUNC(ASN1_INTEGER_get) - RESOLVEFUNC(ASN1_INTEGER_cmp) - RESOLVEFUNC(ASN1_STRING_length) - RESOLVEFUNC(ASN1_STRING_to_UTF8) - RESOLVEFUNC(ASN1_TIME_to_tm) - RESOLVEFUNC(BIO_ctrl) - RESOLVEFUNC(BIO_free) - RESOLVEFUNC(BIO_new) - RESOLVEFUNC(BIO_new_mem_buf) - RESOLVEFUNC(BIO_read) - RESOLVEFUNC(BIO_s_mem) - RESOLVEFUNC(BIO_write) - RESOLVEFUNC(BIO_set_flags) - RESOLVEFUNC(BIO_clear_flags) - RESOLVEFUNC(BIO_set_ex_data) - RESOLVEFUNC(BIO_get_ex_data) - RESOLVEFUNC(BN_num_bits) - RESOLVEFUNC(BN_is_word) - RESOLVEFUNC(BN_mod_word) - RESOLVEFUNC(ERR_error_string) - RESOLVEFUNC(ERR_error_string_n) - RESOLVEFUNC(ERR_get_error) - RESOLVEFUNC(EVP_CIPHER_CTX_new) - RESOLVEFUNC(EVP_CIPHER_CTX_free) - RESOLVEFUNC(EVP_CIPHER_CTX_ctrl) - RESOLVEFUNC(EVP_CIPHER_CTX_set_key_length) - RESOLVEFUNC(EVP_CipherInit) - RESOLVEFUNC(EVP_CipherInit_ex) - RESOLVEFUNC(EVP_CipherUpdate) - RESOLVEFUNC(EVP_CipherFinal) - RESOLVEFUNC(EVP_get_digestbyname) + RESOLVEFUNC(BIO_set_data) + RESOLVEFUNC(BIO_get_data) + RESOLVEFUNC(BIO_set_init) + RESOLVEFUNC(BIO_get_shutdown) + RESOLVEFUNC(BIO_set_shutdown) + RESOLVEFUNC(ASN1_INTEGER_get) + RESOLVEFUNC(ASN1_INTEGER_cmp) + RESOLVEFUNC(ASN1_STRING_length) + RESOLVEFUNC(ASN1_STRING_to_UTF8) + RESOLVEFUNC(ASN1_TIME_to_tm) + RESOLVEFUNC(BIO_ctrl) + RESOLVEFUNC(BIO_free) + RESOLVEFUNC(BIO_new) + RESOLVEFUNC(BIO_new_mem_buf) + RESOLVEFUNC(BIO_read) + RESOLVEFUNC(BIO_s_mem) + RESOLVEFUNC(BIO_write) + RESOLVEFUNC(BIO_set_flags) + RESOLVEFUNC(BIO_clear_flags) + RESOLVEFUNC(BIO_set_ex_data) + RESOLVEFUNC(BIO_get_ex_data) + RESOLVEFUNC(BN_num_bits) + RESOLVEFUNC(BN_is_word) + RESOLVEFUNC(BN_mod_word) + RESOLVEFUNC(ERR_error_string) + RESOLVEFUNC(ERR_error_string_n) + RESOLVEFUNC(ERR_get_error) + RESOLVEFUNC(EVP_CIPHER_CTX_new) + RESOLVEFUNC(EVP_CIPHER_CTX_free) + RESOLVEFUNC(EVP_CIPHER_CTX_ctrl) + RESOLVEFUNC(EVP_CIPHER_CTX_set_key_length) + RESOLVEFUNC(EVP_CipherInit) + RESOLVEFUNC(EVP_CipherInit_ex) + RESOLVEFUNC(EVP_CipherUpdate) + RESOLVEFUNC(EVP_CipherFinal) + RESOLVEFUNC(EVP_get_digestbyname) #ifndef OPENSSL_NO_DES - RESOLVEFUNC(EVP_des_cbc) - RESOLVEFUNC(EVP_des_ede3_cbc) + RESOLVEFUNC(EVP_des_cbc) + RESOLVEFUNC(EVP_des_ede3_cbc) #endif #ifndef OPENSSL_NO_RC2 - RESOLVEFUNC(EVP_rc2_cbc) + RESOLVEFUNC(EVP_rc2_cbc) #endif #ifndef OPENSSL_NO_AES - RESOLVEFUNC(EVP_aes_128_cbc) - RESOLVEFUNC(EVP_aes_192_cbc) - RESOLVEFUNC(EVP_aes_256_cbc) + RESOLVEFUNC(EVP_aes_128_cbc) + RESOLVEFUNC(EVP_aes_192_cbc) + RESOLVEFUNC(EVP_aes_256_cbc) #endif - RESOLVEFUNC(EVP_sha1) - RESOLVEFUNC(EVP_PKEY_free) - RESOLVEFUNC(EVP_PKEY_new) - RESOLVEFUNC(EVP_PKEY_type) - RESOLVEFUNC(OBJ_nid2sn) - RESOLVEFUNC(OBJ_nid2ln) - RESOLVEFUNC(OBJ_sn2nid) - RESOLVEFUNC(OBJ_ln2nid) - RESOLVEFUNC(i2t_ASN1_OBJECT) - RESOLVEFUNC(OBJ_obj2txt) - RESOLVEFUNC(OBJ_obj2nid) - RESOLVEFUNC(PEM_read_bio_PrivateKey) - RESOLVEFUNC(PEM_read_bio_DHparams) - RESOLVEFUNC(PEM_write_bio_PrivateKey) - RESOLVEFUNC(PEM_write_bio_PrivateKey_traditional) - RESOLVEFUNC(PEM_read_bio_PUBKEY) - RESOLVEFUNC(PEM_write_bio_PUBKEY) - RESOLVEFUNC(RAND_seed) - RESOLVEFUNC(RAND_status) - RESOLVEFUNC(RAND_bytes) - RESOLVEFUNC(SSL_CIPHER_description) - RESOLVEFUNC(SSL_CIPHER_get_bits) - RESOLVEFUNC(SSL_get_rbio) - RESOLVEFUNC(SSL_CTX_check_private_key) - RESOLVEFUNC(SSL_CTX_ctrl) - RESOLVEFUNC(SSL_CTX_free) - RESOLVEFUNC(SSL_CTX_new) - RESOLVEFUNC(SSL_CTX_set_cipher_list) - RESOLVEFUNC(SSL_CTX_callback_ctrl) - RESOLVEFUNC(SSL_CTX_set_default_verify_paths) - RESOLVEFUNC(SSL_CTX_set_verify) - RESOLVEFUNC(SSL_CTX_set_verify_depth) - RESOLVEFUNC(SSL_CTX_use_certificate) - RESOLVEFUNC(SSL_CTX_use_certificate_file) - RESOLVEFUNC(SSL_CTX_use_PrivateKey) - RESOLVEFUNC(SSL_CTX_use_PrivateKey_file) - RESOLVEFUNC(SSL_CTX_get_cert_store); - RESOLVEFUNC(SSL_CONF_CTX_new); - RESOLVEFUNC(SSL_CONF_CTX_free); - RESOLVEFUNC(SSL_CONF_CTX_set_ssl_ctx); - RESOLVEFUNC(SSL_CONF_CTX_set_flags); - RESOLVEFUNC(SSL_CONF_CTX_finish); - RESOLVEFUNC(SSL_CONF_cmd); - RESOLVEFUNC(SSL_accept) - RESOLVEFUNC(SSL_clear) - RESOLVEFUNC(SSL_connect) - RESOLVEFUNC(SSL_free) - RESOLVEFUNC(SSL_get_ciphers) - RESOLVEFUNC(SSL_get_current_cipher) - RESOLVEFUNC(SSL_version) - RESOLVEFUNC(SSL_get_error) - RESOLVEFUNC(SSL_get_peer_cert_chain) + RESOLVEFUNC(EVP_sha1) + RESOLVEFUNC(EVP_PKEY_free) + RESOLVEFUNC(EVP_PKEY_new) + RESOLVEFUNC(EVP_PKEY_type) + RESOLVEFUNC(OBJ_nid2sn) + RESOLVEFUNC(OBJ_nid2ln) + RESOLVEFUNC(OBJ_sn2nid) + RESOLVEFUNC(OBJ_ln2nid) + RESOLVEFUNC(i2t_ASN1_OBJECT) + RESOLVEFUNC(OBJ_obj2txt) + RESOLVEFUNC(OBJ_obj2nid) + RESOLVEFUNC(PEM_read_bio_PrivateKey) + RESOLVEFUNC(PEM_write_bio_PrivateKey) + RESOLVEFUNC(PEM_write_bio_PrivateKey_traditional) + RESOLVEFUNC(PEM_read_bio_PUBKEY) + RESOLVEFUNC(PEM_write_bio_PUBKEY) + RESOLVEFUNC(RAND_seed) + RESOLVEFUNC(RAND_status) + RESOLVEFUNC(RAND_bytes) + RESOLVEFUNC(SSL_CIPHER_description) + RESOLVEFUNC(SSL_CIPHER_get_bits) + RESOLVEFUNC(SSL_get_rbio) + RESOLVEFUNC(SSL_CTX_check_private_key) + RESOLVEFUNC(SSL_CTX_ctrl) + RESOLVEFUNC(SSL_CTX_free) + RESOLVEFUNC(SSL_CTX_new) + RESOLVEFUNC(SSL_CTX_set_cipher_list) + RESOLVEFUNC(SSL_CTX_callback_ctrl) + RESOLVEFUNC(SSL_CTX_set_default_verify_paths) + RESOLVEFUNC(SSL_CTX_set_verify) + RESOLVEFUNC(SSL_CTX_set_verify_depth) + RESOLVEFUNC(SSL_CTX_use_certificate) + RESOLVEFUNC(SSL_CTX_use_certificate_file) + RESOLVEFUNC(SSL_CTX_use_PrivateKey) + RESOLVEFUNC(SSL_CTX_use_PrivateKey_file) + RESOLVEFUNC(SSL_CTX_get_cert_store); + RESOLVEFUNC(SSL_CONF_CTX_new); + RESOLVEFUNC(SSL_CONF_CTX_free); + RESOLVEFUNC(SSL_CONF_CTX_set_ssl_ctx); + RESOLVEFUNC(SSL_CONF_CTX_set_flags); + RESOLVEFUNC(SSL_CONF_CTX_finish); + RESOLVEFUNC(SSL_CONF_cmd); + RESOLVEFUNC(SSL_accept) + RESOLVEFUNC(SSL_clear) + RESOLVEFUNC(SSL_connect) + RESOLVEFUNC(SSL_free) + RESOLVEFUNC(SSL_get_ciphers) + RESOLVEFUNC(SSL_get_current_cipher) + RESOLVEFUNC(SSL_version) + RESOLVEFUNC(SSL_get_error) + RESOLVEFUNC(SSL_get_peer_cert_chain) #if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 - RESOLVEFUNC(SSL_get1_peer_certificate) - RESOLVEFUNC(EVP_PKEY_get_bits) - RESOLVEFUNC(EVP_PKEY_get_base_id) + RESOLVEFUNC(SSL_get1_peer_certificate) + RESOLVEFUNC(EVP_PKEY_get_bits) + RESOLVEFUNC(EVP_PKEY_get_base_id) #else - RESOLVEFUNC(SSL_get_peer_certificate) - RESOLVEFUNC(EVP_PKEY_base_id) + RESOLVEFUNC(SSL_get_peer_certificate) + RESOLVEFUNC(EVP_PKEY_base_id) #endif // OPENSSL_VERSION_MAJOR >= 3 #ifndef OPENSSL_NO_DEPRECATED_3_0 - RESOLVEFUNC(EVP_PKEY_assign) - RESOLVEFUNC(EVP_PKEY_cmp) + RESOLVEFUNC(DH_new) + RESOLVEFUNC(DH_free) + RESOLVEFUNC(DH_check) + RESOLVEFUNC(DH_get0_pqg) - RESOLVEFUNC(EVP_PKEY_set1_RSA) - RESOLVEFUNC(EVP_PKEY_set1_DSA) - RESOLVEFUNC(EVP_PKEY_set1_DH) + RESOLVEFUNC(d2i_DHparams) + RESOLVEFUNC(i2d_DHparams) - RESOLVEFUNC(EVP_PKEY_get1_DSA) - RESOLVEFUNC(EVP_PKEY_get1_RSA) - RESOLVEFUNC(EVP_PKEY_get1_DH) + RESOLVEFUNC(PEM_read_bio_DHparams) - RESOLVEFUNC(PEM_read_bio_DSA_PUBKEY) - RESOLVEFUNC(PEM_read_bio_RSA_PUBKEY) - RESOLVEFUNC(PEM_read_bio_DSAPrivateKey) - RESOLVEFUNC(PEM_read_bio_RSAPrivateKey) + RESOLVEFUNC(EVP_PKEY_assign) + RESOLVEFUNC(EVP_PKEY_cmp) - RESOLVEFUNC(PEM_write_bio_DSA_PUBKEY) - RESOLVEFUNC(PEM_write_bio_RSA_PUBKEY) - RESOLVEFUNC(PEM_write_bio_DSAPrivateKey) - RESOLVEFUNC(PEM_write_bio_RSAPrivateKey) - RESOLVEFUNC(SSL_CTX_use_RSAPrivateKey) + RESOLVEFUNC(EVP_PKEY_set1_RSA) + RESOLVEFUNC(EVP_PKEY_set1_DSA) + RESOLVEFUNC(EVP_PKEY_set1_DH) - RESOLVEFUNC(DSA_new) - RESOLVEFUNC(DSA_free) + RESOLVEFUNC(EVP_PKEY_get1_DSA) + RESOLVEFUNC(EVP_PKEY_get1_RSA) + RESOLVEFUNC(EVP_PKEY_get1_DH) - RESOLVEFUNC(RSA_new) - RESOLVEFUNC(RSA_free) + RESOLVEFUNC(PEM_read_bio_DSA_PUBKEY) + RESOLVEFUNC(PEM_read_bio_RSA_PUBKEY) + RESOLVEFUNC(PEM_read_bio_DSAPrivateKey) + RESOLVEFUNC(PEM_read_bio_RSAPrivateKey) - RESOLVEFUNC(DH_bits) - RESOLVEFUNC(DSA_bits) - RESOLVEFUNC(RSA_bits) + RESOLVEFUNC(PEM_write_bio_DSA_PUBKEY) + RESOLVEFUNC(PEM_write_bio_RSA_PUBKEY) + RESOLVEFUNC(PEM_write_bio_DSAPrivateKey) + RESOLVEFUNC(PEM_write_bio_RSAPrivateKey) + RESOLVEFUNC(SSL_CTX_use_RSAPrivateKey) + + RESOLVEFUNC(DSA_new) + RESOLVEFUNC(DSA_free) + + RESOLVEFUNC(RSA_new) + RESOLVEFUNC(RSA_free) + + RESOLVEFUNC(DH_bits) + RESOLVEFUNC(DSA_bits) + RESOLVEFUNC(RSA_bits) #ifndef OPENSSL_NO_EC - RESOLVEFUNC(EVP_PKEY_set1_EC_KEY) - RESOLVEFUNC(EVP_PKEY_get1_EC_KEY) - RESOLVEFUNC(PEM_read_bio_EC_PUBKEY) - RESOLVEFUNC(PEM_read_bio_ECPrivateKey) - RESOLVEFUNC(PEM_write_bio_EC_PUBKEY) - RESOLVEFUNC(PEM_write_bio_ECPrivateKey) - RESOLVEFUNC(EC_KEY_get0_group) - RESOLVEFUNC(EC_GROUP_get_degree) - RESOLVEFUNC(EC_KEY_dup) - RESOLVEFUNC(EC_KEY_new_by_curve_name) - RESOLVEFUNC(EC_KEY_free) + RESOLVEFUNC(EVP_PKEY_set1_EC_KEY) + RESOLVEFUNC(EVP_PKEY_get1_EC_KEY) + RESOLVEFUNC(PEM_read_bio_EC_PUBKEY) + RESOLVEFUNC(PEM_read_bio_ECPrivateKey) + RESOLVEFUNC(PEM_write_bio_EC_PUBKEY) + RESOLVEFUNC(PEM_write_bio_ECPrivateKey) + RESOLVEFUNC(EC_KEY_get0_group) + RESOLVEFUNC(EC_GROUP_get_degree) + RESOLVEFUNC(EC_KEY_dup) + RESOLVEFUNC(EC_KEY_new_by_curve_name) + RESOLVEFUNC(EC_KEY_free) #endif // OPENSSL_NO_EC #endif // OPENSSL_NO_DEPRECATED_3_0 - RESOLVEFUNC(SSL_get_verify_result) - RESOLVEFUNC(SSL_new) - RESOLVEFUNC(SSL_get_SSL_CTX) - RESOLVEFUNC(SSL_ctrl) - RESOLVEFUNC(SSL_read) - RESOLVEFUNC(SSL_set_accept_state) - RESOLVEFUNC(SSL_set_bio) - RESOLVEFUNC(SSL_set_connect_state) - RESOLVEFUNC(SSL_shutdown) - RESOLVEFUNC(SSL_in_init) - RESOLVEFUNC(SSL_get_shutdown) - RESOLVEFUNC(SSL_set_session) - RESOLVEFUNC(SSL_SESSION_free) - RESOLVEFUNC(SSL_get1_session) - RESOLVEFUNC(SSL_get_session) - RESOLVEFUNC(SSL_set_ex_data) - RESOLVEFUNC(SSL_get_ex_data) - RESOLVEFUNC(SSL_get_ex_data_X509_STORE_CTX_idx) + RESOLVEFUNC(SSL_get_verify_result) + RESOLVEFUNC(SSL_new) + RESOLVEFUNC(SSL_get_SSL_CTX) + RESOLVEFUNC(SSL_ctrl) + RESOLVEFUNC(SSL_read) + RESOLVEFUNC(SSL_set_accept_state) + RESOLVEFUNC(SSL_set_bio) + RESOLVEFUNC(SSL_set_connect_state) + RESOLVEFUNC(SSL_shutdown) + RESOLVEFUNC(SSL_in_init) + RESOLVEFUNC(SSL_get_shutdown) + RESOLVEFUNC(SSL_set_session) + RESOLVEFUNC(SSL_SESSION_free) + RESOLVEFUNC(SSL_get1_session) + RESOLVEFUNC(SSL_get_session) + RESOLVEFUNC(SSL_set_ex_data) + RESOLVEFUNC(SSL_get_ex_data) + RESOLVEFUNC(SSL_get_ex_data_X509_STORE_CTX_idx) #ifndef OPENSSL_NO_PSK - RESOLVEFUNC(SSL_set_psk_client_callback) - RESOLVEFUNC(SSL_set_psk_server_callback) - RESOLVEFUNC(SSL_CTX_use_psk_identity_hint) + RESOLVEFUNC(SSL_set_psk_client_callback) + RESOLVEFUNC(SSL_set_psk_server_callback) + RESOLVEFUNC(SSL_CTX_use_psk_identity_hint) #endif // !OPENSSL_NO_PSK - RESOLVEFUNC(SSL_write) - RESOLVEFUNC(X509_NAME_entry_count) - RESOLVEFUNC(X509_NAME_get_entry) - RESOLVEFUNC(X509_NAME_ENTRY_get_data) - RESOLVEFUNC(X509_NAME_ENTRY_get_object) - RESOLVEFUNC(X509_PUBKEY_get) - RESOLVEFUNC(X509_STORE_free) - RESOLVEFUNC(X509_STORE_new) - RESOLVEFUNC(X509_STORE_add_cert) - RESOLVEFUNC(X509_STORE_CTX_free) - RESOLVEFUNC(X509_STORE_CTX_init) - RESOLVEFUNC(X509_STORE_CTX_new) - RESOLVEFUNC(X509_STORE_CTX_set_purpose) - RESOLVEFUNC(X509_STORE_CTX_get_error) - RESOLVEFUNC(X509_STORE_CTX_get_error_depth) - RESOLVEFUNC(X509_STORE_CTX_get_current_cert) - RESOLVEFUNC(X509_STORE_CTX_get0_store) - RESOLVEFUNC(X509_cmp) - RESOLVEFUNC(X509_STORE_CTX_get_ex_data) - RESOLVEFUNC(X509_dup) - RESOLVEFUNC(X509_print) - RESOLVEFUNC(X509_digest) - RESOLVEFUNC(X509_EXTENSION_get_object) - RESOLVEFUNC(X509_free) - RESOLVEFUNC(X509_gmtime_adj) - RESOLVEFUNC(ASN1_TIME_free) - RESOLVEFUNC(X509_get_ext) - RESOLVEFUNC(X509_get_ext_count) - RESOLVEFUNC(X509_get_ext_d2i) - RESOLVEFUNC(X509V3_EXT_get) - RESOLVEFUNC(X509V3_EXT_d2i) - RESOLVEFUNC(X509_EXTENSION_get_critical) - RESOLVEFUNC(X509_EXTENSION_get_data) - RESOLVEFUNC(BASIC_CONSTRAINTS_free) - RESOLVEFUNC(AUTHORITY_KEYID_free) - RESOLVEFUNC(GENERAL_NAME_free) - RESOLVEFUNC(ASN1_STRING_print) - RESOLVEFUNC(X509_check_issued) - RESOLVEFUNC(X509_get_issuer_name) - RESOLVEFUNC(X509_get_subject_name) - RESOLVEFUNC(X509_get_serialNumber) - RESOLVEFUNC(X509_verify_cert) - RESOLVEFUNC(d2i_X509) - RESOLVEFUNC(i2d_X509) + RESOLVEFUNC(SSL_write) + RESOLVEFUNC(X509_NAME_entry_count) + RESOLVEFUNC(X509_NAME_get_entry) + RESOLVEFUNC(X509_NAME_ENTRY_get_data) + RESOLVEFUNC(X509_NAME_ENTRY_get_object) + RESOLVEFUNC(X509_PUBKEY_get) + RESOLVEFUNC(X509_STORE_free) + RESOLVEFUNC(X509_STORE_new) + RESOLVEFUNC(X509_STORE_add_cert) + RESOLVEFUNC(X509_STORE_CTX_free) + RESOLVEFUNC(X509_STORE_CTX_init) + RESOLVEFUNC(X509_STORE_CTX_new) + RESOLVEFUNC(X509_STORE_CTX_set_purpose) + RESOLVEFUNC(X509_STORE_CTX_get_error) + RESOLVEFUNC(X509_STORE_CTX_get_error_depth) + RESOLVEFUNC(X509_STORE_CTX_get_current_cert) + RESOLVEFUNC(X509_STORE_CTX_get0_store) + RESOLVEFUNC(X509_cmp) + RESOLVEFUNC(X509_STORE_CTX_get_ex_data) + RESOLVEFUNC(X509_dup) + RESOLVEFUNC(X509_print) + RESOLVEFUNC(X509_digest) + RESOLVEFUNC(X509_EXTENSION_get_object) + RESOLVEFUNC(X509_free) + RESOLVEFUNC(X509_gmtime_adj) + RESOLVEFUNC(ASN1_TIME_free) + RESOLVEFUNC(X509_get_ext) + RESOLVEFUNC(X509_get_ext_count) + RESOLVEFUNC(X509_get_ext_d2i) + RESOLVEFUNC(X509V3_EXT_get) + RESOLVEFUNC(X509V3_EXT_d2i) + RESOLVEFUNC(X509_EXTENSION_get_critical) + RESOLVEFUNC(X509_EXTENSION_get_data) + RESOLVEFUNC(BASIC_CONSTRAINTS_free) + RESOLVEFUNC(AUTHORITY_KEYID_free) + RESOLVEFUNC(GENERAL_NAME_free) + RESOLVEFUNC(ASN1_STRING_print) + RESOLVEFUNC(X509_check_issued) + RESOLVEFUNC(X509_get_issuer_name) + RESOLVEFUNC(X509_get_subject_name) + RESOLVEFUNC(X509_get_serialNumber) + RESOLVEFUNC(X509_verify_cert) + RESOLVEFUNC(d2i_X509) + RESOLVEFUNC(i2d_X509) #if OPENSSL_VERSION_MAJOR < 3 - RESOLVEFUNC(SSL_CTX_load_verify_locations) + RESOLVEFUNC(SSL_CTX_load_verify_locations) #else - RESOLVEFUNC(SSL_CTX_load_verify_dir) + RESOLVEFUNC(SSL_CTX_load_verify_dir) #endif // OPENSSL_VERSION_MAJOR - RESOLVEFUNC(i2d_SSL_SESSION) - RESOLVEFUNC(d2i_SSL_SESSION) + RESOLVEFUNC(i2d_SSL_SESSION) + RESOLVEFUNC(d2i_SSL_SESSION) #ifndef OPENSSL_NO_NEXTPROTONEG - RESOLVEFUNC(SSL_select_next_proto) - RESOLVEFUNC(SSL_CTX_set_next_proto_select_cb) - RESOLVEFUNC(SSL_get0_next_proto_negotiated) - RESOLVEFUNC(SSL_set_alpn_protos) - RESOLVEFUNC(SSL_CTX_set_alpn_select_cb) - RESOLVEFUNC(SSL_get0_alpn_selected) + RESOLVEFUNC(SSL_select_next_proto) + RESOLVEFUNC(SSL_CTX_set_next_proto_select_cb) + RESOLVEFUNC(SSL_get0_next_proto_negotiated) + RESOLVEFUNC(SSL_set_alpn_protos) + RESOLVEFUNC(SSL_CTX_set_alpn_select_cb) + RESOLVEFUNC(SSL_get0_alpn_selected) #endif // !OPENSSL_NO_NEXTPROTONEG #if QT_CONFIG(dtls) - RESOLVEFUNC(SSL_CTX_set_cookie_generate_cb) - RESOLVEFUNC(SSL_CTX_set_cookie_verify_cb) - RESOLVEFUNC(DTLS_server_method) - RESOLVEFUNC(DTLS_client_method) + RESOLVEFUNC(SSL_CTX_set_cookie_generate_cb) + RESOLVEFUNC(SSL_CTX_set_cookie_verify_cb) + RESOLVEFUNC(DTLS_server_method) + RESOLVEFUNC(DTLS_client_method) #endif // dtls - RESOLVEFUNC(CRYPTO_malloc) - RESOLVEFUNC(DH_new) - RESOLVEFUNC(DH_free) - RESOLVEFUNC(d2i_DHparams) - RESOLVEFUNC(i2d_DHparams) - RESOLVEFUNC(DH_check) - RESOLVEFUNC(BN_bin2bn) + RESOLVEFUNC(CRYPTO_malloc) + RESOLVEFUNC(BN_bin2bn) #ifndef OPENSSL_NO_EC - RESOLVEFUNC(EC_get_builtin_curves) + RESOLVEFUNC(EC_get_builtin_curves) #endif // OPENSSL_NO_EC - RESOLVEFUNC(PKCS12_parse) - RESOLVEFUNC(d2i_PKCS12_bio) - RESOLVEFUNC(PKCS12_free) + RESOLVEFUNC(PKCS12_parse) + RESOLVEFUNC(d2i_PKCS12_bio) + RESOLVEFUNC(PKCS12_free) + return true; + }(); - symbolsResolved.storeRelease(true); - return true; + return symbolsResolved; } #endif // QT_CONFIG(library) diff --git a/src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h b/src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h index 97999d4c39..a93c110b3f 100644 --- a/src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h +++ b/src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h @@ -185,7 +185,11 @@ QT_BEGIN_NAMESPACE // **************** Static declarations ****************** #endif // !defined QT_LINKED_OPENSSL - +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 +typedef uint64_t qssloptions; +#else +typedef unsigned long qssloptions; +#endif // TODO: the following lines previously were a part of 1.1 - specific header. // To reduce the amount of the change, I'm directly copying and pasting the // content of the header here. Later, can be better sorted/split into groups, @@ -209,7 +213,7 @@ void q_OPENSSL_sk_push(OPENSSL_STACK *st, void *data); void q_OPENSSL_sk_free(OPENSSL_STACK *a); void * q_OPENSSL_sk_value(OPENSSL_STACK *a, int b); int q_SSL_session_reused(SSL *a); -unsigned long q_SSL_CTX_set_options(SSL_CTX *ctx, unsigned long op); +qssloptions q_SSL_CTX_set_options(SSL_CTX *ctx, qssloptions op); int q_OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings); size_t q_SSL_get_client_random(SSL *a, unsigned char *out, size_t outlen); size_t q_SSL_SESSION_get_master_key(const SSL_SESSION *session, unsigned char *out, size_t outlen); @@ -229,7 +233,6 @@ void q_X509_STORE_set_verify_cb(X509_STORE *ctx, X509_STORE_CTX_verify_cb verify int q_X509_STORE_set_ex_data(X509_STORE *ctx, int idx, void *data); void *q_X509_STORE_get_ex_data(X509_STORE *r, int idx); STACK_OF(X509) *q_X509_STORE_CTX_get0_chain(X509_STORE_CTX *ctx); -void q_DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); # define q_SSL_load_error_strings() q_OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS \ | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL) @@ -387,7 +390,6 @@ int q_OBJ_obj2nid(const ASN1_OBJECT *a); #define q_EVP_get_digestbynid(a) q_EVP_get_digestbyname(q_OBJ_nid2sn(a)) EVP_PKEY *q_PEM_read_bio_PrivateKey(BIO *a, EVP_PKEY **b, pem_password_cb *c, void *d); -DH *q_PEM_read_bio_DHparams(BIO *a, DH **b, pem_password_cb *c, void *d); int q_PEM_write_bio_PrivateKey(BIO *a, EVP_PKEY *b, const EVP_CIPHER *c, unsigned char *d, int e, pem_password_cb *f, void *g); int q_PEM_write_bio_PrivateKey_traditional(BIO *a, EVP_PKEY *b, const EVP_CIPHER *c, unsigned char *d, @@ -500,14 +502,21 @@ X509 *q_X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx); X509_STORE *q_X509_STORE_CTX_get0_store(X509_STORE_CTX *ctx); // Diffie-Hellman support +#ifndef OPENSSL_NO_DEPRECATED_3_0 DH *q_DH_new(); void q_DH_free(DH *dh); +int q_DH_check(DH *dh, int *codes); +void q_DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); + DH *q_d2i_DHparams(DH **a, const unsigned char **pp, long length); int q_i2d_DHparams(DH *a, unsigned char **p); -int q_DH_check(DH *dh, int *codes); + +DH *q_PEM_read_bio_DHparams(BIO *a, DH **b, pem_password_cb *c, void *d); +#endif // OPENSSL_NO_DEPRECATED_3_0 BIGNUM *q_BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); #define q_SSL_CTX_set_tmp_dh(ctx, dh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_DH, 0, (char *)dh) +#define q_SSL_CTX_set_dh_auto(ctx, onoff) q_SSL_CTX_ctrl(ctx,SSL_CTRL_SET_DH_AUTO,onoff,NULL) #ifndef OPENSSL_NO_EC // EC Diffie-Hellman support @@ -658,6 +667,7 @@ void *q_CRYPTO_malloc(size_t num, const char *file, int line); #define q_OPENSSL_malloc(num) q_CRYPTO_malloc(num, "", 0) void q_CRYPTO_free(void *str, const char *file, int line); # define q_OPENSSL_free(addr) q_CRYPTO_free(addr, "", 0) +int q_CRYPTO_memcmp(const void * in_a, const void * in_b, size_t len); void q_SSL_set_info_callback(SSL *ssl, void (*cb) (const SSL *ssl, int type, int val)); const char *q_SSL_alert_type_string(int value); diff --git a/src/plugins/tls/openssl/qtls_openssl.cpp b/src/plugins/tls/openssl/qtls_openssl.cpp index 27abf1bc8d..57d09a649b 100644 --- a/src/plugins/tls/openssl/qtls_openssl.cpp +++ b/src/plugins/tls/openssl/qtls_openssl.cpp @@ -92,7 +92,7 @@ QSslCertificate findCertificateToFetch(const QList<QSslError> &tlsErrors, bool c if (checkAIA) { const auto extensions = certToFetch.extensions(); for (const auto &ext : extensions) { - if (ext.oid() == QStringLiteral("1.3.6.1.5.5.7.1.1")) // See RFC 4325 + if (ext.oid() == u"1.3.6.1.5.5.7.1.1") // See RFC 4325 return certToFetch; } //The only reason we check this extensions is because an application set trusted @@ -490,7 +490,7 @@ void TlsCryptographOpenSSL::init(QSslSocket *qObj, QSslSocketPrivate *dObj) handshakeInterrupted = false; fetchAuthorityInformation = false; - caToFetch = QSslCertificate{}; + caToFetch.reset(); } void TlsCryptographOpenSSL::checkSettingSslContext(std::shared_ptr<QSslContext> tlsContext) @@ -577,7 +577,7 @@ bool TlsCryptographOpenSSL::startHandshake() auto configuration = q->sslConfiguration(); if (!errorsReportedFromCallback) { const auto &peerCertificateChain = configuration.peerCertificateChain(); - for (const auto ¤tError : qAsConst(lastErrors)) { + for (const auto ¤tError : std::as_const(lastErrors)) { emit q->peerVerifyError(QTlsPrivate::X509CertificateOpenSSL::openSSLErrorToQSslError(currentError.code, peerCertificateChain.value(currentError.depth))); if (q->state() != QAbstractSocket::ConnectedState) @@ -697,7 +697,7 @@ bool TlsCryptographOpenSSL::startHandshake() // Translate errors from the error list into QSslErrors. errors.reserve(errors.size() + errorList.size()); - for (const auto &error : qAsConst(errorList)) + for (const auto &error : std::as_const(errorList)) errors << X509CertificateOpenSSL::openSSLErrorToQSslError(error.code, peerCertificateChain.value(error.depth)); if (!errors.isEmpty()) { @@ -749,7 +749,7 @@ void TlsCryptographOpenSSL::enableHandshakeContinuation() void TlsCryptographOpenSSL::cancelCAFetch() { fetchAuthorityInformation = false; - caToFetch = QSslCertificate{}; + caToFetch.reset(); } void TlsCryptographOpenSSL::continueHandshake() @@ -1726,11 +1726,11 @@ unsigned TlsCryptographOpenSSL::pskClientTlsCallback(const char *hint, char *ide return 0; // Copy data back into OpenSSL - const int identityLength = qMin(authenticator.identity().length(), authenticator.maximumIdentityLength()); + const int identityLength = qMin(authenticator.identity().size(), authenticator.maximumIdentityLength()); std::memcpy(identity, authenticator.identity().constData(), identityLength); identity[identityLength] = 0; - const int pskLength = qMin(authenticator.preSharedKey().length(), authenticator.maximumPreSharedKeyLength()); + const int pskLength = qMin(authenticator.preSharedKey().size(), authenticator.maximumPreSharedKeyLength()); std::memcpy(psk, authenticator.preSharedKey().constData(), pskLength); return pskLength; } @@ -1752,7 +1752,7 @@ unsigned TlsCryptographOpenSSL::pskServerTlsCallback(const char *identity, unsig return 0; // Copy data back into OpenSSL - const int pskLength = qMin(authenticator.preSharedKey().length(), authenticator.maximumPreSharedKeyLength()); + const int pskLength = qMin(authenticator.preSharedKey().size(), authenticator.maximumPreSharedKeyLength()); std::memcpy(psk, authenticator.preSharedKey().constData(), pskLength); return pskLength; } @@ -1803,7 +1803,7 @@ void TlsCryptographOpenSSL::caRootLoaded(QSslCertificate cert, QSslCertificate t Q_ASSERT(q); //Done, fetched already: - caToFetch = QSslCertificate{}; + caToFetch.reset(); if (fetchAuthorityInformation) { if (!q->sslConfiguration().caCertificates().contains(trustedRoot)) diff --git a/src/plugins/tls/openssl/qtls_openssl_p.h b/src/plugins/tls/openssl/qtls_openssl_p.h index 31fede2ace..65d21a395b 100644 --- a/src/plugins/tls/openssl/qtls_openssl_p.h +++ b/src/plugins/tls/openssl/qtls_openssl_p.h @@ -120,7 +120,7 @@ private: bool handshakeInterrupted = false; bool fetchAuthorityInformation = false; - QSslCertificate caToFetch; + std::optional<QSslCertificate> caToFetch; bool inSetAndEmitError = false; bool pendingFatalAlert = false; diff --git a/src/plugins/tls/openssl/qtlsbackend_openssl.cpp b/src/plugins/tls/openssl/qtlsbackend_openssl.cpp index ea31086fad..d73515724b 100644 --- a/src/plugins/tls/openssl/qtlsbackend_openssl.cpp +++ b/src/plugins/tls/openssl/qtlsbackend_openssl.cpp @@ -17,7 +17,7 @@ #include <QtNetwork/qssl.h> #include <QtCore/qdir.h> -#include <QtCore/qdiriterator.h> +#include <QtCore/qdirlisting.h> #include <QtCore/qlist.h> #include <QtCore/qmutex.h> #include <QtCore/qscopeguard.h> @@ -31,9 +31,13 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; -Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.ossl"); +#if defined(Q_OS_WIN) || defined(Q_OS_MACOS) +constexpr auto DefaultWarningLevel = QtCriticalMsg; +#else +constexpr auto DefaultWarningLevel = QtDebugMsg; +#endif -Q_GLOBAL_STATIC(QRecursiveMutex, qt_opensslInitMutex) +Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.ossl", DefaultWarningLevel); static void q_loadCiphersForConnection(SSL *connection, QList<QSslCipher> &ciphers, QList<QSslCipher> &defaultCiphers) @@ -59,8 +63,6 @@ static void q_loadCiphersForConnection(SSL *connection, QList<QSslCipher> &ciphe } } -bool QTlsBackendOpenSSL::s_libraryLoaded = false; -bool QTlsBackendOpenSSL::s_loadedCiphersAndCerts = false; int QTlsBackendOpenSSL::s_indexForSSLExtraData = -1; QString QTlsBackendOpenSSL::getErrorsFromOpenSsl() @@ -92,12 +94,10 @@ void QTlsBackendOpenSSL::clearErrorQueue() bool QTlsBackendOpenSSL::ensureLibraryLoaded() { - if (!q_resolveOpenSslSymbols()) - return false; - - const QMutexLocker locker(qt_opensslInitMutex()); + static bool libraryLoaded = []() { + if (!q_resolveOpenSslSymbols()) + return false; - if (!s_libraryLoaded) { // Initialize OpenSSL. if (q_OPENSSL_init_ssl(0, nullptr) != 1) return false; @@ -119,10 +119,10 @@ bool QTlsBackendOpenSSL::ensureLibraryLoaded() return false; } - s_libraryLoaded = true; - } + return true; + }(); - return true; + return libraryLoaded; } QString QTlsBackendOpenSSL::backendName() const @@ -175,11 +175,24 @@ void QTlsBackendOpenSSL::ensureInitialized() const void QTlsBackendOpenSSL::ensureCiphersAndCertsLoaded() const { - const QMutexLocker locker(qt_opensslInitMutex()); + Q_CONSTINIT static bool initializationStarted = false; + Q_CONSTINIT static QAtomicInt initialized = Q_BASIC_ATOMIC_INITIALIZER(0); + Q_CONSTINIT static QRecursiveMutex initMutex; - if (s_loadedCiphersAndCerts) + if (initialized.loadAcquire()) return; - s_loadedCiphersAndCerts = true; + + const QMutexLocker locker(&initMutex); + + if (initializationStarted || initialized.loadAcquire()) + return; + + // Indicate that the initialization has already started in the current + // thread in case of recursive calls. The atomic variable cannot be used + // for this because it is checked without holding the init mutex. + initializationStarted = true; + + auto guard = qScopeGuard([] { initialized.storeRelease(1); }); resetDefaultCiphers(); resetDefaultEllipticCurves(); @@ -191,11 +204,11 @@ void QTlsBackendOpenSSL::ensureCiphersAndCertsLoaded() const #elif defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) // check whether we can enable on-demand root-cert loading (i.e. check whether the sym links are there) const QList<QByteArray> dirs = QSslSocketPrivate::unixRootCertDirectories(); - QStringList symLinkFilter; - symLinkFilter << "[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9]"_L1; + const QStringList symLinkFilter{ + u"[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9]"_s}; for (const auto &dir : dirs) { - QDirIterator iterator(QLatin1StringView(dir), symLinkFilter, QDir::Files); - if (iterator.hasNext()) { + QDirListing dirList(QString::fromLatin1(dir), symLinkFilter, QDir::Files); + if (dirList.cbegin() != dirList.cend()) { // Not empty QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true); break; } @@ -350,7 +363,9 @@ QList<QSslCertificate> systemCaCertificates() QList<QSslCertificate> systemCerts; #if defined(Q_OS_WIN) HCERTSTORE hSystemStore; - hSystemStore = CertOpenSystemStoreW(0, L"ROOT"); + hSystemStore = + CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, + CERT_STORE_READONLY_FLAG | CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT"); if (hSystemStore) { PCCERT_CONTEXT pc = nullptr; while (1) { @@ -379,13 +394,12 @@ QList<QSslCertificate> systemCaCertificates() currentDir.setNameFilters(QStringList{QStringLiteral("*.pem"), QStringLiteral("*.crt")}); for (const auto &directory : directories) { currentDir.setPath(QLatin1StringView(directory)); - QDirIterator it(currentDir); - while (it.hasNext()) { + for (const auto &dirEntry : QDirListing(currentDir)) { // use canonical path here to not load the same certificate twice if symlinked - certFiles.insert(it.nextFileInfo().canonicalFilePath()); + certFiles.insert(dirEntry.canonicalFilePath()); } } - for (const QString& file : qAsConst(certFiles)) + for (const QString& file : std::as_const(certFiles)) systemCerts.append(QSslCertificate::fromPath(file, QSsl::Pem)); } #endif // platform diff --git a/src/plugins/tls/openssl/qtlsbackend_openssl_p.h b/src/plugins/tls/openssl/qtlsbackend_openssl_p.h index 1c5e68e0c6..b9f1f95df0 100644 --- a/src/plugins/tls/openssl/qtlsbackend_openssl_p.h +++ b/src/plugins/tls/openssl/qtlsbackend_openssl_p.h @@ -41,16 +41,13 @@ public: static void logAndClearErrorQueue(); static void clearErrorQueue(); - static bool ensureLibraryLoaded(); // Index used in SSL_get_ex_data to get the matching TlsCryptographerOpenSSL: - static bool s_libraryLoaded; - static bool s_loadedCiphersAndCerts; static int s_indexForSSLExtraData; static QString msgErrorsDuringHandshake(); static QSslCipher qt_OpenSSL_cipher_to_QSslCipher(const SSL_CIPHER *cipher); private: - + static bool ensureLibraryLoaded(); QString backendName() const override; bool isValid() const override; long tlsLibraryVersionNumber() const override; diff --git a/src/plugins/tls/openssl/qtlskey_openssl.cpp b/src/plugins/tls/openssl/qtlskey_openssl.cpp index b17e1d9d74..294fc2ffcd 100644 --- a/src/plugins/tls/openssl/qtlskey_openssl.cpp +++ b/src/plugins/tls/openssl/qtlskey_openssl.cpp @@ -101,19 +101,19 @@ QByteArray TlsKeyOpenSSL::derFromPem(const QByteArray &pem, QMap<QByteArray, QBy QByteArray der(pem); int headerIndex = der.indexOf(header); - int footerIndex = der.indexOf(footer, headerIndex + header.length()); + int footerIndex = der.indexOf(footer, headerIndex + header.size()); if (type() != QSsl::PublicKey) { if (headerIndex == -1 || footerIndex == -1) { header = pkcs8Header(true); footer = pkcs8Footer(true); headerIndex = der.indexOf(header); - footerIndex = der.indexOf(footer, headerIndex + header.length()); + footerIndex = der.indexOf(footer, headerIndex + header.size()); } if (headerIndex == -1 || footerIndex == -1) { header = pkcs8Header(false); footer = pkcs8Footer(false); headerIndex = der.indexOf(header); - footerIndex = der.indexOf(footer, headerIndex + header.length()); + footerIndex = der.indexOf(footer, headerIndex + header.size()); } } if (headerIndex == -1 || footerIndex == -1) @@ -124,7 +124,7 @@ QByteArray TlsKeyOpenSSL::derFromPem(const QByteArray &pem, QMap<QByteArray, QBy if (der.contains("Proc-Type:")) { // taken from QHttpNetworkReplyPrivate::parseHeader int i = 0; - while (i < der.length()) { + while (i < der.size()) { int j = der.indexOf(':', i); // field-name if (j == -1) break; @@ -143,7 +143,7 @@ QByteArray TlsKeyOpenSSL::derFromPem(const QByteArray &pem, QMap<QByteArray, QBy int length = i -(hasCR ? 1: 0) - j; value += der.mid(j, length).trimmed(); j = ++i; - } while (i < der.length() && (der.at(i) == ' ' || der.at(i) == '\t')); + } while (i < der.size() && (der.at(i) == ' ' || der.at(i) == '\t')); if (i == -1) break; // something is wrong @@ -222,7 +222,7 @@ Qt::HANDLE TlsKeyOpenSSL::handle() const #else qCWarning(lcTlsBackend, "This version of OpenSSL disabled direct manipulation with RSA/DSA/DH/EC_KEY structures, consider using QSsl::Opaque instead."); - return Qt::HANDLE(nullptr); + return Qt::HANDLE(genericKey); #endif } diff --git a/src/plugins/tls/openssl/qwindowscarootfetcher.cpp b/src/plugins/tls/openssl/qwindowscarootfetcher.cpp index 82ad3abfd0..a18aae0b71 100644 --- a/src/plugins/tls/openssl/qwindowscarootfetcher.cpp +++ b/src/plugins/tls/openssl/qwindowscarootfetcher.cpp @@ -245,3 +245,5 @@ QHCertStorePointer QWindowsCaRootFetcher::createAdditionalStore() const } QT_END_NAMESPACE + +#include "moc_qwindowscarootfetcher_p.cpp" diff --git a/src/plugins/tls/openssl/qx509_openssl.cpp b/src/plugins/tls/openssl/qx509_openssl.cpp index 29f98755bd..0cd3749f88 100644 --- a/src/plugins/tls/openssl/qx509_openssl.cpp +++ b/src/plugins/tls/openssl/qx509_openssl.cpp @@ -12,12 +12,13 @@ #include <QtNetwork/qsslsocket.h> #include <QtNetwork/qhostaddress.h> -#include <QtCore/qvarlengtharray.h> -#include <QtCore/qscopeguard.h> -#include <QtCore/qdatetime.h> -#include <QtCore/qiodevice.h> #include <QtCore/qendian.h> +#include <QtCore/qdatetime.h> #include <QtCore/qhash.h> +#include <QtCore/qiodevice.h> +#include <QtCore/qscopeguard.h> +#include <QtCore/qtimezone.h> +#include <QtCore/qvarlengtharray.h> QT_BEGIN_NAMESPACE @@ -77,7 +78,7 @@ QDateTime dateTimeFromASN1(const ASN1_TIME *aTime) if (q_ASN1_TIME_to_tm(aTime, &lTime)) { QDate resDate(lTime.tm_year + 1900, lTime.tm_mon + 1, lTime.tm_mday); QTime resTime(lTime.tm_hour, lTime.tm_min, lTime.tm_sec); - result = QDateTime(resDate, resTime, Qt::UTC); + result = QDateTime(resDate, resTime, QTimeZone::UTC); } return result; @@ -616,7 +617,7 @@ QList<QSslError> X509CertificateOpenSSL::verify(const QList<QSslCertificate> &ca const QString &hostName) { // This was previously QSslSocketPrivate::verify(). - if (certificateChain.count() <= 0) + if (certificateChain.size() <= 0) return {QSslError(QSslError::UnspecifiedError)}; QList<QSslError> errors; @@ -658,7 +659,7 @@ QList<QSslError> X509CertificateOpenSSL::verify(const QList<QSslCertificate> &ca // Build the chain of intermediate certificates STACK_OF(X509) *intermediates = nullptr; - if (certificateChain.length() > 1) { + if (certificateChain.size() > 1) { intermediates = (STACK_OF(X509) *) q_OPENSSL_sk_new_null(); if (!intermediates) { @@ -710,7 +711,7 @@ QList<QSslError> X509CertificateOpenSSL::verify(const QList<QSslCertificate> &ca // Translate errors from the error list into QSslErrors. errors.reserve(errors.size() + lastErrors.size()); - for (const auto &error : qAsConst(lastErrors)) + for (const auto &error : std::as_const(lastErrors)) errors << openSSLErrorToQSslError(error.code, certificateChain.value(error.depth)); return errors; diff --git a/src/plugins/tls/schannel/CMakeLists.txt b/src/plugins/tls/schannel/CMakeLists.txt index f03964069a..a7f7fcd99f 100644 --- a/src/plugins/tls/schannel/CMakeLists.txt +++ b/src/plugins/tls/schannel/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_internal_add_plugin(QSchannelBackendPlugin OUTPUT_NAME qschannelbackend CLASS_NAME QSchannelBackend @@ -26,4 +29,6 @@ qt_internal_add_plugin(QSchannelBackendPlugin secur32 bcrypt ncrypt + DEFINES + QT_NO_CAST_FROM_ASCII ) diff --git a/src/plugins/tls/schannel/qtls_schannel.cpp b/src/plugins/tls/schannel/qtls_schannel.cpp index 58e74357d8..a244a90ebc 100644 --- a/src/plugins/tls/schannel/qtls_schannel.cpp +++ b/src/plugins/tls/schannel/qtls_schannel.cpp @@ -26,17 +26,11 @@ #include <security.h> #include <schnlsp.h> -#if NTDDI_VERSION >= NTDDI_WINBLUE && !defined(Q_CC_MINGW) +#if NTDDI_VERSION >= NTDDI_WINBLUE && defined(SECBUFFER_APPLICATION_PROTOCOLS) // ALPN = Application Layer Protocol Negotiation #define SUPPORTS_ALPN 1 #endif -// Redstone 5/1809 has all the API available, but TLS 1.3 is not enabled until a later version of -// Win 10, checked at runtime in supportsTls13() -#if defined(NTDDI_WIN10_RS5) && NTDDI_VERSION >= NTDDI_WIN10_RS5 -#define SUPPORTS_TLS13 1 -#endif - // Not defined in MinGW #ifndef SECBUFFER_ALERT #define SECBUFFER_ALERT 17 @@ -93,6 +87,12 @@ #ifndef SP_PROT_TLS1_3 #define SP_PROT_TLS1_3 (SP_PROT_TLS1_3_CLIENT | SP_PROT_TLS1_3_SERVER) #endif +#ifndef BCRYPT_ECDH_ALGORITHM +#define BCRYPT_ECDH_ALGORITHM L"ECDH" +#endif +#ifndef BCRYPT_ECDSA_ALGORITHM +#define BCRYPT_ECDSA_ALGORITHM L"ECDSA" +#endif /* @future!: @@ -114,10 +114,6 @@ - Check if SEC_I_INCOMPLETE_CREDENTIALS is still returned for both "missing certificate" and "missing PSK" when calling InitializeSecurityContext in "performHandshake". - Medium priority: - - Setting cipher-suites (or ALG_ID) - - People have survived without it in WinRT - Low priority: - Possibly make RAII wrappers for SecBuffer (which I commonly create QScopeGuards for) @@ -133,40 +129,341 @@ Q_LOGGING_CATEGORY(lcTlsBackendSchannel, "qt.tlsbackend.schannel"); QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase); +namespace { +bool supportsTls13(); +} + namespace QTlsPrivate { -QList<QSslCipher> defaultCiphers() +QList<QSslCipher> defaultCiphers(); + +struct SchannelCipherInfo { + const char *openSslCipherSuite; + const char *schannelCipherSuite; + const char *keyExchangeMethod; + const char *authenticationMethod; + const char *encryptionMethod; + int encryptionBits; + const char *hashMethod; + QList<QSsl::SslProtocol> protocols; +}; + +// The list of supported ciphers according to +// https://learn.microsoft.com/en-us/windows/win32/secauthn/tls-cipher-suites-in-windows-server-2022 +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED +std::array<SchannelCipherInfo, 44> schannelCipherInfo = {{ + {"TLS_AES_256_GCM_SHA384", "TLS_AES_256_GCM_SHA384", "", "", "AES", 256, "SHA384", {QSsl::TlsV1_3}}, + {"TLS_AES_128_GCM_SHA256", "TLS_AES_128_GCM_SHA256", "", "", "AES", 128, "SHA256", {QSsl::TlsV1_3}}, + {"ECDHE-ECDSA-AES256-GCM-SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "ECDH", "ECDSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}}, + {"ECDHE-ECDSA-AES128-GCM-SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "ECDH", "ECDSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}}, + {"ECDHE-RSA-AES256-GCM-SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "ECDH", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}}, + {"ECDHE-RSA-AES128-GCM-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "ECDH", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}}, + {"DHE-RSA-AES256-GCM-SHA384", "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "DH", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}}, + {"DHE-RSA-AES128-GCM-SHA256", "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "DH", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}}, + {"ECDHE-ECDSA-AES256-SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "ECDH", "ECDSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}}, + {"ECDHE-ECDSA-AES128-SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "ECDH", "ECDSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}}, + {"ECDHE-RSA-AES256-SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "ECDH", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}}, + {"ECDHE-RSA-AES128-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "ECDH", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}}, + {"ECDHE-ECDSA-AES256-SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "ECDH", "ECDSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"ECDHE-ECDSA-AES128-SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "ECDH", "ECDSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"ECDHE-RSA-AES256-SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "ECDH", "RSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"ECDHE-RSA-AES128-SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "ECDH", "RSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"AES256-GCM-SHA384", "TLS_RSA_WITH_AES_256_GCM_SHA384", "RSA", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}}, + {"AES128-GCM-SHA256", "TLS_RSA_WITH_AES_128_GCM_SHA256", "RSA", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}}, + {"AES256-SHA256", "TLS_RSA_WITH_AES_256_CBC_SHA256", "RSA", "RSA", "AES", 256, "SHA256", {QSsl::TlsV1_2}}, + {"AES128-SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA256", "RSA", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}}, + {"AES256-SHA", "TLS_RSA_WITH_AES_256_CBC_SHA", "RSA", "RSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"AES128-SHA", "TLS_RSA_WITH_AES_128_CBC_SHA", "RSA", "RSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"DES-CBC3-SHA", "TLS_RSA_WITH_3DES_EDE_CBC_SHA", "RSA", "RSA", "3DES", 168, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"NULL-SHA256", "TLS_RSA_WITH_NULL_SHA256", "RSA", "RSA", "", 0, "SHA256", {QSsl::TlsV1_2}}, + {"NULL-SHA", "TLS_RSA_WITH_NULL_SHA", "RSA", "RSA", "", 0, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + + // the following cipher suites are not enabled by default in schannel provider + {"TLS_CHACHA20_POLY1305_SHA256", "TLS_CHACHA20_POLY1305_SHA256", "", "", "CHACHA20_POLY1305", 0, "", {QSsl::TlsV1_3}}, + {"DHE-RSA-AES256-SHA", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "DH", "RSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"DHE-RSA-AES128-SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "DH", "RSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"DHE-DSS-AES256-SHA256", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", "DH", "DSA", "AES", 256, "SHA256", {QSsl::TlsV1_2}}, + {"DHE-DSS-AES128-SHA256", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "DH", "DSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}}, + {"DHE-DSS-AES256-SHA", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", "DH", "DSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"DHE-DSS-AES128-SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "DH", "DSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"EDH-DSS-DES-CBC3-SHA", "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", "DH", "DSA", "3DES", 168, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"RC4-SHA", "TLS_RSA_WITH_RC4_128_SHA", "RSA", "RSA", "RC4", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"RC4-MD5", "TLS_RSA_WITH_RC4_128_MD5", "RSA", "RSA", "RC4", 128, "MD5", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"DES-CBC-SHA", "TLS_RSA_WITH_DES_CBC_SHA", "RSA", "RSA", "DES", 56, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"EDH-DSS-DES-CBC-SHA", "TLS_DHE_DSS_WITH_DES_CBC_SHA", "DH", "DSA", "DES", 56, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + {"NULL-MD5", "TLS_RSA_WITH_NULL_MD5", "RSA", "RSA", "", 0, "MD5", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}}, + + // PSK cipher suites + {"PSK-AES256-GCM-SHA384", "TLS_PSK_WITH_AES_256_GCM_SHA384", "PSK", "", "AES", 256, "SHA384", {QSsl::TlsV1_2}}, + {"PSK-AES128-GCM-SHA256", "TLS_PSK_WITH_AES_128_GCM_SHA256", "PSK", "", "AES", 128, "SHA256", {QSsl::TlsV1_2}}, + {"PSK-AES256-CBC-SHA384", "TLS_PSK_WITH_AES_256_CBC_SHA384", "PSK", "", "AES", 256, "SHA384", {QSsl::TlsV1_2}}, + {"PSK-AES128-CBC-SHA256", "TLS_PSK_WITH_AES_128_CBC_SHA256", "PSK", "", "AES", 128, "SHA256", {QSsl::TlsV1_2}}, + {"PSK-NULL-SHA384", "TLS_PSK_WITH_NULL_SHA384", "PSK", "", "", 0, "SHA384", {QSsl::TlsV1_2}}, + {"PSK-NULL-SHA256", "TLS_PSK_WITH_NULL_SHA256", "PSK", "", "", 0, "SHA256", {QSsl::TlsV1_2}}, +}}; +QT_WARNING_POP + +const SchannelCipherInfo *cipherInfoByOpenSslName(const QString &name) +{ + for (const auto &cipherInfo : schannelCipherInfo) { + if (name == QLatin1StringView(cipherInfo.openSslCipherSuite)) + return &cipherInfo; + } + + return nullptr; +} + +UNICODE_STRING cbcChainingMode = { + sizeof(BCRYPT_CHAIN_MODE_CBC) - 2, + sizeof(BCRYPT_CHAIN_MODE_CBC), + const_cast<PWSTR>(BCRYPT_CHAIN_MODE_CBC) +}; + +UNICODE_STRING gcmChainingMode = { + sizeof(BCRYPT_CHAIN_MODE_GCM) - 2, + sizeof(BCRYPT_CHAIN_MODE_GCM), + const_cast<PWSTR>(BCRYPT_CHAIN_MODE_GCM) +}; + +/** + Determines which algorithms are not used by the requested ciphers to build + up a black list that can be passed to SCH_CREDENTIALS. + */ +QList<CRYPTO_SETTINGS> cryptoSettingsForCiphers(const QList<QSslCipher> &ciphers) +{ + static const QList<QSslCipher> defaultCipherList = defaultCiphers(); + + if (defaultCipherList == ciphers) { + // the ciphers have not been restricted for this session, so no black listing needed + return {}; + } + + QList<const SchannelCipherInfo*> cipherInfo; + + for (const auto &cipher : ciphers) { + if (cipher.isNull()) + continue; + + const auto *info = cipherInfoByOpenSslName(cipher.name()); + if (!cipherInfo.contains(info)) + cipherInfo.append(info); + } + + QList<CRYPTO_SETTINGS> cryptoSettings; + + const auto assignUnicodeString = [](UNICODE_STRING &unicodeString, const wchar_t *characters) { + unicodeString.Length = static_cast<USHORT>(wcslen(characters) * sizeof(WCHAR)); + unicodeString.MaximumLength = unicodeString.Length + sizeof(UNICODE_NULL); + unicodeString.Buffer = const_cast<wchar_t*>(characters); + }; + + // black list of key exchange algorithms + const auto allKeyExchangeAlgorithms = {BCRYPT_RSA_ALGORITHM, + BCRYPT_ECDH_ALGORITHM, + BCRYPT_DH_ALGORITHM}; + + for (const auto &algorithm : allKeyExchangeAlgorithms) { + const auto method = QStringView(algorithm); + + const auto usesMethod = [method](const SchannelCipherInfo *info) { + return QLatin1StringView(info->keyExchangeMethod) == method; + }; + + const bool exclude = std::none_of(cipherInfo.cbegin(), cipherInfo.cend(), usesMethod); + + if (exclude) { + CRYPTO_SETTINGS settings = {}; + settings.eAlgorithmUsage = TlsParametersCngAlgUsageKeyExchange; + assignUnicodeString(settings.strCngAlgId, algorithm); + cryptoSettings.append(settings); + } + } + + // black list of authentication algorithms + const auto allAuthenticationAlgorithms = {BCRYPT_RSA_ALGORITHM, + BCRYPT_DSA_ALGORITHM, + BCRYPT_ECDSA_ALGORITHM, + BCRYPT_DH_ALGORITHM}; + + for (const auto &algorithm : allAuthenticationAlgorithms) { + const auto method = QStringView(algorithm); + + const auto usesMethod = [method](const SchannelCipherInfo *info) { + return QLatin1StringView(info->authenticationMethod) == method; + }; + + const bool exclude = std::none_of(cipherInfo.begin(), cipherInfo.end(), usesMethod); + + if (exclude) { + CRYPTO_SETTINGS settings = {}; + settings.eAlgorithmUsage = TlsParametersCngAlgUsageSignature; + assignUnicodeString(settings.strCngAlgId, algorithm); + cryptoSettings.append(settings); + } + } + + + // black list of encryption algorithms + const auto allEncryptionAlgorithms = {BCRYPT_AES_ALGORITHM, + BCRYPT_RC4_ALGORITHM, + BCRYPT_DES_ALGORITHM, + BCRYPT_3DES_ALGORITHM}; + + for (const auto &algorithm : allEncryptionAlgorithms) { + const auto method = QStringView(algorithm); + + if (method == QLatin1StringView("AES")) { + bool uses128Bit = false; + bool uses256Bit = false; + bool usesGcm = false; + bool usesCbc = false; + for (const auto *info : cipherInfo) { + if (QLatin1StringView(info->encryptionMethod) == method) { + uses128Bit = uses128Bit || (info->encryptionBits == 128); + uses256Bit = uses256Bit || (info->encryptionBits == 256); + usesGcm = usesGcm || + QLatin1StringView(info->schannelCipherSuite).contains("_GCM_"_L1); + usesCbc = usesCbc || + QLatin1StringView(info->schannelCipherSuite).contains("_CBC_"_L1); + } + } + + CRYPTO_SETTINGS settings = {}; + settings.eAlgorithmUsage = TlsParametersCngAlgUsageCipher; + assignUnicodeString(settings.strCngAlgId, algorithm); + + if (usesGcm && !usesCbc) { + settings.cChainingModes = 1; + settings.rgstrChainingModes = &cbcChainingMode; + } else if (!usesGcm && usesCbc) { + settings.cChainingModes = 1; + settings.rgstrChainingModes = &gcmChainingMode; + } + + if (!uses128Bit && uses256Bit) { + settings.dwMinBitLength = 256; + cryptoSettings.append(settings); + } else if (uses128Bit && !uses256Bit) { + settings.dwMaxBitLength = 128; + cryptoSettings.append(settings); + } else if (!uses128Bit && !uses256Bit) { + cryptoSettings.append(settings); + } + } else { + const auto usesMethod = [method](const SchannelCipherInfo *info) { + return QLatin1StringView(info->encryptionMethod) == method; + }; + + const bool exclude = std::none_of(cipherInfo.begin(), cipherInfo.end(), usesMethod); + + if (exclude) { + CRYPTO_SETTINGS settings = {}; + settings.eAlgorithmUsage = TlsParametersCngAlgUsageCipher; + assignUnicodeString(settings.strCngAlgId, algorithm); + cryptoSettings.append(settings); + } + } + } + + // black list of hash algorithms + const auto allHashAlgorithms = {BCRYPT_MD5_ALGORITHM, + BCRYPT_SHA1_ALGORITHM, + BCRYPT_SHA256_ALGORITHM, + BCRYPT_SHA384_ALGORITHM}; + + for (const auto &algorithm : allHashAlgorithms) { + const auto method = QStringView(algorithm); + + const auto usesMethod = [method](const SchannelCipherInfo *info) { + return QLatin1StringView(info->hashMethod) == method; + }; + + const bool exclude = std::none_of(cipherInfo.begin(), cipherInfo.end(), usesMethod); + + if (exclude) { + CRYPTO_SETTINGS settings = {}; + settings.eAlgorithmUsage = TlsParametersCngAlgUsageDigest; + assignUnicodeString(settings.strCngAlgId, algorithm); + cryptoSettings.append(settings); + } + } + + return cryptoSettings; +} + +QList<QSslCipher> ciphersByName(QStringView schannelSuiteName) { - // Previously the code was in QSslSocketBackendPrivate. QList<QSslCipher> ciphers; - // @temp (I hope), stolen from qsslsocket_winrt.cpp - const QString protocolStrings[] = { QStringLiteral("TLSv1"), QStringLiteral("TLSv1.1"), - QStringLiteral("TLSv1.2"), QStringLiteral("TLSv1.3") }; + + for (const auto &cipher : schannelCipherInfo) { + if (QLatin1StringView(cipher.schannelCipherSuite) == schannelSuiteName) { + for (const auto &protocol : cipher.protocols) { QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED - const QSsl::SslProtocol protocols[] = { QSsl::TlsV1_0, QSsl::TlsV1_1, - QSsl::TlsV1_2, QSsl::TlsV1_3 }; + const QString protocolName = ( + protocol == QSsl::TlsV1_0 ? QStringLiteral("TLSv1.0") : + protocol == QSsl::TlsV1_1 ? QStringLiteral("TLSv1.1") : + protocol == QSsl::TlsV1_2 ? QStringLiteral("TLSv1.2") : + protocol == QSsl::TlsV1_3 ? QStringLiteral("TLSv1.3") : + QString()); QT_WARNING_POP - const int size = ARRAYSIZE(protocols); - static_assert(size == ARRAYSIZE(protocolStrings)); - ciphers.reserve(size); - for (int i = 0; i < size; ++i) { - const QSslCipher cipher = QTlsBackend::createCipher(QStringLiteral("Schannel"), - protocols[i], protocolStrings[i]); - ciphers.append(cipher); + ciphers.append(QTlsBackend::createCiphersuite(QLatin1StringView(cipher.openSslCipherSuite), + QLatin1StringView(cipher.keyExchangeMethod), + QLatin1StringView(cipher.encryptionMethod), + QLatin1StringView(cipher.authenticationMethod), + cipher.encryptionBits, + protocol, protocolName)); + } + } } return ciphers; - } -} // namespace QTlsPrivate +QList<QSslCipher> defaultCiphers() +{ + ULONG contextFunctionsCount = {}; + PCRYPT_CONTEXT_FUNCTIONS contextFunctions = {}; -namespace { -bool supportsTls13(); + const auto status = BCryptEnumContextFunctions(CRYPT_LOCAL, L"SSL", NCRYPT_SCHANNEL_INTERFACE, + &contextFunctionsCount, &contextFunctions); + if (!NT_SUCCESS(status)) { + qCWarning(lcTlsBackendSchannel, "Failed to enumerate ciphers"); + return {}; + } + + const bool supportsV13 = supportsTls13(); + + QList<QSslCipher> ciphers; + + for (ULONG index = 0; index < contextFunctions->cFunctions; ++index) { + const auto suiteName = QStringView(contextFunctions->rgpszFunctions[index]); + + const QList<QSslCipher> allCiphers = ciphersByName(suiteName); + + for (const auto &cipher : allCiphers) { + if (!supportsV13 && (cipher.protocol() == QSsl::TlsV1_3)) + continue; + + ciphers.append(cipher); + } + } + + BCryptFreeBuffer(contextFunctions); + + return ciphers; +} + +bool containsTls13Cipher(const QList<QSslCipher> &ciphers) +{ + return std::any_of(ciphers.cbegin(), ciphers.cend(), + [](const QSslCipher &cipher) { return cipher.protocol() == QSsl::TlsV1_3; }); } +} // namespace QTlsPrivate + bool QSchannelBackend::s_loadedCiphersAndCerts = false; Q_GLOBAL_STATIC(QRecursiveMutex, qt_schannel_mutex) @@ -299,7 +596,11 @@ QList<QSslCertificate> QSchannelBackend::systemCaCertificatesImplementation() // Similar to non-Darwin version found in qtlsbackend_openssl.cpp, // QTlsPrivate::systemCaCertificates function. QList<QSslCertificate> systemCerts; - auto hSystemStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT")); + + auto hSystemStore = QHCertStorePointer( + CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, + CERT_STORE_READONLY_FLAG | CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT")); + if (hSystemStore) { PCCERT_CONTEXT pc = nullptr; while ((pc = CertFindCertificateInStore(hSystemStore.get(), X509_ASN_ENCODING, 0, @@ -320,6 +621,11 @@ QTlsPrivate::X509DerReaderPtr QSchannelBackend::X509DerReader() const return QTlsPrivate::X509CertificateGeneric::certificatesFromDer; } +QTlsPrivate::X509Pkcs12ReaderPtr QSchannelBackend::X509Pkcs12Reader() const +{ + return QTlsPrivate::X509CertificateSchannel::importPkcs12; +} + namespace { SecBuffer createSecBuffer(void *ptr, unsigned long length, unsigned long bufferType) @@ -384,7 +690,6 @@ QString schannelErrorToString(qint32 status) bool supportsTls13() { -#ifdef SUPPORTS_TLS13 static bool supported = []() { const auto current = QOperatingSystemVersion::current(); // 20221 just happens to be the preview version I run on my laptop where I tested TLS 1.3. @@ -392,10 +697,8 @@ bool supportsTls13() QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 20221); return current >= minimum; }(); + return supported; -#else - return false; -#endif } DWORD toSchannelProtocol(QSsl::SslProtocol protocol) @@ -460,17 +763,15 @@ QT_WARNING_POP return protocols; } -#ifdef SUPPORTS_TLS13 // In the new API that descended down upon us we are not asked which protocols we want // but rather which protocols we don't want. So now we have this function to disable // anything that is not enabled. -DWORD toSchannelProtocolNegated(QSsl::SslProtocol protocol) +DWORD negatedSchannelProtocols(DWORD wantedProtocols) { DWORD protocols = SP_PROT_ALL; // all protocols - protocols &= ~toSchannelProtocol(protocol); // minus the one(s) we want + protocols &= ~wantedProtocols; // minus the one(s) we want return protocols; } -#endif /*! \internal @@ -508,8 +809,7 @@ bool netscapeWrongCertType(const QList<QSslCertificateExtension> &extensions, bo const auto netscapeIt = std::find_if( extensions.cbegin(), extensions.cend(), [](const QSslCertificateExtension &extension) { - const auto netscapeCertType = QStringLiteral("2.16.840.1.113730.1.1"); - return extension.oid() == netscapeCertType; + return extension.oid() == u"2.16.840.1.113730.1.1"; }); if (netscapeIt != extensions.cend()) { const QByteArray netscapeCertTypeByte = netscapeIt->value().toByteArray(); @@ -680,6 +980,10 @@ qint64 checkIncompleteData(const SecBuffer &secBuffer) return 0; } +DWORD defaultCredsFlag() +{ + return qEnvironmentVariableIsSet("QT_SCH_DEFAULT_CREDS") ? 0 : SCH_CRED_NO_DEFAULT_CREDS; +} } // anonymous namespace @@ -719,6 +1023,10 @@ bool TlsCryptographSchannel::sendToken(void *token, unsigned long tokenLength, b Q_ASSERT(d); auto *plainSocket = d->plainTcpSocket(); Q_ASSERT(plainSocket); + if (plainSocket->state() == QAbstractSocket::UnconnectedState || !plainSocket->isValid() + || !plainSocket->isOpen()) { + return false; + } const qint64 written = plainSocket->write(static_cast<const char *>(token), tokenLength); if (written != qint64(tokenLength)) { @@ -781,7 +1089,7 @@ bool TlsCryptographSchannel::acquireCredentialsHandle() Q_ASSERT(schannelState == SchannelState::InitializeHandshake); const bool isClient = d->tlsMode() == QSslSocket::SslClientMode; - const DWORD protocols = toSchannelProtocol(configuration.protocol()); + DWORD protocols = toSchannelProtocol(configuration.protocol()); if (protocols == DWORD(-1)) { setErrorAndEmit(d, QAbstractSocket::SslInvalidUserDataError, QSslSocket::tr("Invalid protocol chosen")); @@ -835,80 +1143,50 @@ bool TlsCryptographSchannel::acquireCredentialsHandle() certsCount = 1; Q_ASSERT(localCertContext); } - void *credentials = nullptr; -#ifdef SUPPORTS_TLS13 + + const QList<QSslCipher> ciphers = configuration.ciphers(); + if (!ciphers.isEmpty() && !containsTls13Cipher(ciphers)) + protocols &= ~SP_PROT_TLS1_3; + + QList<CRYPTO_SETTINGS> cryptoSettings; + if (!ciphers.isEmpty()) + cryptoSettings = cryptoSettingsForCiphers(ciphers); + TLS_PARAMETERS tlsParameters = { 0, nullptr, - toSchannelProtocolNegated(configuration.protocol()), // what protocols to disable + negatedSchannelProtocols(protocols), // what protocols to disable + static_cast<DWORD>(cryptoSettings.size()), + (cryptoSettings.isEmpty() ? nullptr : cryptoSettings.data()), + 0 + }; + + SCH_CREDENTIALS credentials = { + SCH_CREDENTIALS_VERSION, 0, + certsCount, + &localCertContext, nullptr, - 0 + 0, + nullptr, + 0, + SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT | defaultCredsFlag(), + 1, + &tlsParameters }; - if (supportsTls13()) { - SCH_CREDENTIALS *cred = new SCH_CREDENTIALS{ - SCH_CREDENTIALS_VERSION, - 0, - certsCount, - &localCertContext, - nullptr, - 0, - nullptr, - 0, - SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT - | SCH_CRED_NO_DEFAULT_CREDS, - 1, - &tlsParameters - }; - credentials = cred; - } else -#endif // SUPPORTS_TLS13 - { - SCHANNEL_CRED *cred = new SCHANNEL_CRED{ - SCHANNEL_CRED_VERSION, // dwVersion - certsCount, // cCreds - &localCertContext, // paCred (certificate(s) containing a private key for authentication) - nullptr, // hRootStore - - 0, // cMappers (reserved) - nullptr, // aphMappers (reserved) - - 0, // cSupportedAlgs - nullptr, // palgSupportedAlgs (nullptr = system default) - - protocols, // grbitEnabledProtocols - 0, // dwMinimumCipherStrength (0 = system default) - 0, // dwMaximumCipherStrength (0 = system default) - 0, // dwSessionLifespan (0 = schannel default, 10 hours) - SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT - | SCH_CRED_NO_DEFAULT_CREDS, // dwFlags - 0 // dwCredFormat (must be 0) - }; - credentials = cred; - } - Q_ASSERT(credentials != nullptr); TimeStamp expiration{}; auto status = AcquireCredentialsHandle(nullptr, // pszPrincipal (unused) const_cast<wchar_t *>(UNISP_NAME), // pszPackage isClient ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND, // fCredentialUse nullptr, // pvLogonID (unused) - credentials, // pAuthData + &credentials, // pAuthData nullptr, // pGetKeyFn (unused) nullptr, // pvGetKeyArgument (unused) &credentialHandle, // phCredential &expiration // ptsExpir ); -#ifdef SUPPORTS_TLS13 - if (supportsTls13()) { - delete static_cast<SCH_CREDENTIALS *>(credentials); - } else -#endif // SUPPORTS_TLS13 - { - delete static_cast<SCHANNEL_CRED *>(credentials); - } - if (status != SEC_E_OK) { setErrorAndEmit(d, QAbstractSocket::SslInternalError, schannelErrorToString(status)); return false; @@ -1113,7 +1391,8 @@ bool TlsCryptographSchannel::performHandshake() auto *plainSocket = d->plainTcpSocket(); Q_ASSERT(plainSocket); - if (plainSocket->state() == QAbstractSocket::UnconnectedState) { + if (plainSocket->state() == QAbstractSocket::UnconnectedState || !plainSocket->isValid() + || !plainSocket->isOpen()) { setErrorAndEmit(d, QAbstractSocket::RemoteHostClosedError, QSslSocket::tr("The TLS/SSL connection has been closed")); return false; @@ -1282,6 +1561,11 @@ bool TlsCryptographSchannel::verifyHandshake() // Get session cipher info status = QueryContextAttributes(&contextHandle, + SECPKG_ATTR_CIPHER_INFO, + &cipherInfo); + CHECK_STATUS(status); + + status = QueryContextAttributes(&contextHandle, SECPKG_ATTR_CONNECTION_INFO, &connectionInfo); CHECK_STATUS(status); @@ -1434,6 +1718,7 @@ void TlsCryptographSchannel::reset() deallocateContext(); freeCredentialsHandle(); // in case we already had one (@future: session resumption requires re-use) + cipherInfo = {}; connectionInfo = {}; streamSizes = {}; @@ -1483,8 +1768,10 @@ void TlsCryptographSchannel::transmit() return; // This function should not have been called // Can happen if called through QSslSocket::abort->QSslSocket::close->QSslSocket::flush->here - if (plainSocket->state() == QAbstractSocket::SocketState::UnconnectedState) + if (plainSocket->state() == QAbstractSocket::UnconnectedState || !plainSocket->isValid() + || !plainSocket->isOpen()) { return; + } if (schannelState != SchannelState::Done) { continueHandshake(); @@ -1556,132 +1843,131 @@ void TlsCryptographSchannel::transmit() } } - if (q->isEncrypted()) { // Decrypt data from remote - int totalRead = 0; - bool hadIncompleteData = false; - const auto readBufferMaxSize = d->maxReadBufferSize(); - while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) { - if (missingData > plainSocket->bytesAvailable() - && (!readBufferMaxSize || readBufferMaxSize >= missingData)) { + int totalRead = 0; + bool hadIncompleteData = false; + const auto readBufferMaxSize = d->maxReadBufferSize(); + while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) { + if (missingData > plainSocket->bytesAvailable() + && (!readBufferMaxSize || readBufferMaxSize >= missingData)) { #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackendSchannel, "We're still missing %lld bytes, will check later.", - missingData); + qCDebug(lcTlsBackendSchannel, "We're still missing %lld bytes, will check later.", + missingData); #endif - break; - } + break; + } - missingData = 0; - const qint64 bytesRead = readToBuffer(intermediateBuffer, plainSocket); + missingData = 0; + const qint64 bytesRead = readToBuffer(intermediateBuffer, plainSocket); #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackendSchannel, "Read %lld encrypted bytes from the socket", bytesRead); + qCDebug(lcTlsBackendSchannel, "Read %lld encrypted bytes from the socket", bytesRead); #endif - if (intermediateBuffer.length() == 0 || (hadIncompleteData && bytesRead == 0)) { + if (intermediateBuffer.length() == 0 || (hadIncompleteData && bytesRead == 0)) { #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackendSchannel, - (hadIncompleteData ? "No new data received, leaving loop!" - : "Nothing to decrypt, leaving loop!")); + qCDebug(lcTlsBackendSchannel, + hadIncompleteData ? "No new data received, leaving loop!" + : "Nothing to decrypt, leaving loop!"); #endif - break; - } - hadIncompleteData = false; + break; + } + hadIncompleteData = false; #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackendSchannel, "Total amount of bytes to decrypt: %d", - intermediateBuffer.length()); + qCDebug(lcTlsBackendSchannel, "Total amount of bytes to decrypt: %d", + intermediateBuffer.length()); #endif - SecBuffer dataBuffer[4]{ - createSecBuffer(intermediateBuffer, SECBUFFER_DATA), - createSecBuffer(nullptr, 0, SECBUFFER_EMPTY), - createSecBuffer(nullptr, 0, SECBUFFER_EMPTY), - createSecBuffer(nullptr, 0, SECBUFFER_EMPTY) - }; - SecBufferDesc message{ - SECBUFFER_VERSION, - ARRAYSIZE(dataBuffer), - dataBuffer - }; - auto status = DecryptMessage(&contextHandle, &message, 0, nullptr); - if (status == SEC_E_OK || status == SEC_I_RENEGOTIATE || status == SEC_I_CONTEXT_EXPIRED) { - // There can still be 0 output even if it succeeds, this is fine - if (dataBuffer[1].cbBuffer > 0) { - // It is always decrypted in-place. - // But [0] is the STREAM_HEADER, [1] is the DATA and [2] is the STREAM_TRAILER. - // The pointers in all of those still point into 'intermediateBuffer'. - buffer.append(static_cast<char *>(dataBuffer[1].pvBuffer), - dataBuffer[1].cbBuffer); - totalRead += dataBuffer[1].cbBuffer; + SecBuffer dataBuffer[4]{ + createSecBuffer(intermediateBuffer, SECBUFFER_DATA), + createSecBuffer(nullptr, 0, SECBUFFER_EMPTY), + createSecBuffer(nullptr, 0, SECBUFFER_EMPTY), + createSecBuffer(nullptr, 0, SECBUFFER_EMPTY) + }; + SecBufferDesc message{ + SECBUFFER_VERSION, + ARRAYSIZE(dataBuffer), + dataBuffer + }; + auto status = DecryptMessage(&contextHandle, &message, 0, nullptr); + if (status == SEC_E_OK || status == SEC_I_RENEGOTIATE || status == SEC_I_CONTEXT_EXPIRED) { + // There can still be 0 output even if it succeeds, this is fine + if (dataBuffer[1].cbBuffer > 0) { + // It is always decrypted in-place. + // But [0] is the STREAM_HEADER, [1] is the DATA and [2] is the STREAM_TRAILER. + // The pointers in all of those still point into 'intermediateBuffer'. + buffer.append(static_cast<char *>(dataBuffer[1].pvBuffer), + dataBuffer[1].cbBuffer); + totalRead += dataBuffer[1].cbBuffer; #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackendSchannel, "Decrypted %lu bytes. New read buffer size: %d", - dataBuffer[1].cbBuffer, buffer.size()); + qCDebug(lcTlsBackendSchannel, "Decrypted %lu bytes. New read buffer size: %d", + dataBuffer[1].cbBuffer, buffer.size()); #endif - } - if (dataBuffer[3].BufferType == SECBUFFER_EXTRA) { - // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel - // dataBuffer[3].cbBuffer indicates the amount of bytes _NOT_ processed, - // the rest need to be stored. - retainExtraData(intermediateBuffer, dataBuffer[3]); - } else { - intermediateBuffer.resize(0); - } } + if (dataBuffer[3].BufferType == SECBUFFER_EXTRA) { + // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel + // dataBuffer[3].cbBuffer indicates the amount of bytes _NOT_ processed, + // the rest need to be stored. + retainExtraData(intermediateBuffer, dataBuffer[3]); + } else { + intermediateBuffer.resize(0); + } + } - if (status == SEC_E_INCOMPLETE_MESSAGE) { - missingData = checkIncompleteData(dataBuffer[0]); + if (status == SEC_E_INCOMPLETE_MESSAGE) { + missingData = checkIncompleteData(dataBuffer[0]); #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackendSchannel, - "We didn't have enough data to decrypt anything, will try again!"); + qCDebug(lcTlsBackendSchannel, "We didn't have enough data to decrypt anything, will try again!"); #endif - // We try again, but if we don't get any more data then we leave - hadIncompleteData = true; - } else if (status == SEC_E_INVALID_HANDLE) { - // I don't think this should happen, if it does we're done... - qCWarning(lcTlsBackendSchannel, "The internal SSPI handle is invalid!"); - Q_UNREACHABLE(); - } else if (status == SEC_E_INVALID_TOKEN) { - qCWarning(lcTlsBackendSchannel, "Got SEC_E_INVALID_TOKEN!"); - Q_UNREACHABLE(); // Happened once due to a bug, but shouldn't generally happen(?) - } else if (status == SEC_E_MESSAGE_ALTERED) { - // The message has been altered, disconnect now. - shutdown = true; // skips sending the shutdown alert - disconnectFromHost(); - setErrorAndEmit(d, QAbstractSocket::SslInternalError, - schannelErrorToString(status)); - break; - } else if (status == SEC_E_OUT_OF_SEQUENCE) { - // @todo: I don't know if this one is actually "fatal".. - // This path might never be hit as it seems this is for connection-oriented connections, - // while SEC_E_MESSAGE_ALTERED is for stream-oriented ones (what we use). - shutdown = true; // skips sending the shutdown alert - disconnectFromHost(); - setErrorAndEmit(d, QAbstractSocket::SslInternalError, - schannelErrorToString(status)); - break; - } else if (status == SEC_I_CONTEXT_EXPIRED) { - // 'remote' has initiated a shutdown - disconnectFromHost(); - setErrorAndEmit(d, QAbstractSocket::RemoteHostClosedError, - schannelErrorToString(status)); - break; - } else if (status == SEC_I_RENEGOTIATE) { - // 'remote' wants to renegotiate + // We try again, but if we don't get any more data then we leave + hadIncompleteData = true; + } else if (status == SEC_E_INVALID_HANDLE) { + // I don't think this should happen, if it does we're done... + qCWarning(lcTlsBackendSchannel, "The internal SSPI handle is invalid!"); + Q_UNREACHABLE(); + } else if (status == SEC_E_INVALID_TOKEN) { + // Supposedly we have an invalid token, it's under-documented what + // this means, so to be safe we disconnect. + shutdown = true; + disconnectFromHost(); + setErrorAndEmit(d, QAbstractSocket::SslInternalError, schannelErrorToString(status)); + break; + } else if (status == SEC_E_MESSAGE_ALTERED) { + // The message has been altered, disconnect now. + shutdown = true; // skips sending the shutdown alert + disconnectFromHost(); + setErrorAndEmit(d, QAbstractSocket::SslInternalError, + schannelErrorToString(status)); + break; + } else if (status == SEC_E_OUT_OF_SEQUENCE) { + // @todo: I don't know if this one is actually "fatal".. + // This path might never be hit as it seems this is for connection-oriented connections, + // while SEC_E_MESSAGE_ALTERED is for stream-oriented ones (what we use). + shutdown = true; // skips sending the shutdown alert + disconnectFromHost(); + setErrorAndEmit(d, QAbstractSocket::SslInternalError, + schannelErrorToString(status)); + break; + } else if (status == SEC_I_CONTEXT_EXPIRED) { + // 'remote' has initiated a shutdown + disconnectFromHost(); + break; + } else if (status == SEC_I_RENEGOTIATE) { + // 'remote' wants to renegotiate #ifdef QSSLSOCKET_DEBUG - qCDebug(lcTlsBackendSchannel, "The peer wants to renegotiate."); + qCDebug(lcTlsBackendSchannel, "The peer wants to renegotiate."); #endif - schannelState = SchannelState::Renegotiate; - renegotiating = true; + schannelState = SchannelState::Renegotiate; + renegotiating = true; - // We need to call 'continueHandshake' or else there's no guarantee it ever gets called - continueHandshake(); - break; - } + // We need to call 'continueHandshake' or else there's no guarantee it ever gets called + continueHandshake(); + break; } + } - if (totalRead) { - if (bool *readyReadEmittedPointer = d->readyReadPointer()) - *readyReadEmittedPointer = true; - emit q->readyRead(); - emit q->channelReadyRead(0); - } + if (totalRead) { + if (bool *readyReadEmittedPointer = d->readyReadPointer()) + *readyReadEmittedPointer = true; + emit q->readyRead(); + emit q->channelReadyRead(0); } } @@ -1776,30 +2062,36 @@ void TlsCryptographSchannel::disconnectFromHost() if (SecIsValidHandle(&contextHandle)) { if (!shutdown) { shutdown = true; - if (plainSocket->state() != QAbstractSocket::UnconnectedState) { - if (q->isEncrypted()) { - // Read as much as possible because this is likely our last chance - qint64 tempMax = d->maxReadBufferSize(); - d->setMaxReadBufferSize(0); - transmit(); - d->setMaxReadBufferSize(tempMax); - sendShutdown(); - } + if (plainSocket->state() != QAbstractSocket::UnconnectedState && q->isEncrypted()) { + sendShutdown(); + transmit(); } } } - if (plainSocket->state() != QAbstractSocket::UnconnectedState) - plainSocket->disconnectFromHost(); + plainSocket->disconnectFromHost(); } void TlsCryptographSchannel::disconnected() { Q_ASSERT(d); + auto *plainSocket = d->plainTcpSocket(); + Q_ASSERT(plainSocket); + d->setEncrypted(false); shutdown = true; - d->setEncrypted(false); - deallocateContext(); - freeCredentialsHandle(); + if (plainSocket->bytesAvailable() > 0 || hasUndecryptedData()) { + // Read as much as possible because this is likely our last chance + qint64 tempMax = d->maxReadBufferSize(); + d->setMaxReadBufferSize(0); // Unlimited + transmit(); + d->setMaxReadBufferSize(tempMax); + // Since there were bytes still available we don't want to deallocate + // our context yet. It will happen later, when the socket is re-used or + // destroyed. + } else { + deallocateContext(); + freeCredentialsHandle(); + } } QSslCipher TlsCryptographSchannel::sessionCipher() const @@ -1807,8 +2099,17 @@ QSslCipher TlsCryptographSchannel::sessionCipher() const Q_ASSERT(q); if (!q->isEncrypted()) - return QSslCipher(); - return QSslCipher(QStringLiteral("Schannel"), sessionProtocol()); + return {}; + + const auto sessionProtocol = toQtSslProtocol(connectionInfo.dwProtocol); + + const auto ciphers = ciphersByName(QStringView(cipherInfo.szCipherSuite)); + for (const auto& cipher : ciphers) { + if (cipher.protocol() == sessionProtocol) + return cipher; + } + + return {}; } QSsl::SslProtocol TlsCryptographSchannel::sessionProtocol() const @@ -1992,7 +2293,10 @@ bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext) // the Ca list, not just included during verification. // That being said, it's not trivial to add the root certificates (if and only if they // came from the system root store). And I don't see this mentioned in our documentation. - auto rootStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT")); + auto rootStore = QHCertStorePointer( + CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, + CERT_STORE_READONLY_FLAG | CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT")); + if (!rootStore) { #ifdef QSSLSOCKET_DEBUG qCWarning(lcTlsBackendSchannel, "Failed to open the system root CA certificate store!"); @@ -2106,10 +2410,40 @@ bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext) verifyDepth = DWORD(q->peerVerifyDepth()); const auto &caCertificates = q->sslConfiguration().caCertificates(); + + if (!rootCertOnDemandLoadingAllowed() + && !(chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN) + && (q->peerVerifyMode() == QSslSocket::VerifyPeer + || (isClient && q->peerVerifyMode() == QSslSocket::AutoVerifyPeer))) { + // When verifying a peer Windows "helpfully" builds a chain that + // may include roots from the system store. But we don't want that if + // the user has set their own CA certificates. + // Since Windows claims this is not a partial chain the root is included + // and we have to check that it is one of our configured CAs. + CERT_CHAIN_ELEMENT *element = chain->rgpElement[chain->cElement - 1]; + QSslCertificate certificate = getCertificateFromChainElement(element); + if (!caCertificates.contains(certificate)) { + auto error = QSslError(QSslError::CertificateUntrusted, certificate); + sslErrors += error; + emit q->peerVerifyError(error); + if (q->state() != QAbstractSocket::ConnectedState) + return false; + } + } + QList<QSslCertificate> peerCertificateChain; for (DWORD i = 0; i < verifyDepth; i++) { CERT_CHAIN_ELEMENT *element = chain->rgpElement[i]; QSslCertificate certificate = getCertificateFromChainElement(element); + if (certificate.isNull()) { + const auto &previousCert = !peerCertificateChain.isEmpty() ? peerCertificateChain.last() + : QSslCertificate(); + auto error = QSslError(QSslError::SslError::UnableToGetIssuerCertificate, previousCert); + sslErrors += error; + emit q->peerVerifyError(error); + if (previousCert.isNull() || q->state() != QAbstractSocket::ConnectedState) + return false; + } const QList<QSslCertificateExtension> extensions = certificate.extensions(); #ifdef QSSLSOCKET_DEBUG @@ -2257,7 +2591,7 @@ bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext) } if (!peerCertificateChain.isEmpty()) - QTlsBackend::storePeerCertificate(d, peerCertificateChain.first()); + QTlsBackend::storePeerCertificate(d, peerCertificateChain.constFirst()); const auto &configuration = q->sslConfiguration(); // Probably, updated by QTlsBackend::storePeerCertificate etc. // @Note: Somewhat copied from qsslsocket_mac.cpp diff --git a/src/plugins/tls/schannel/qtls_schannel_p.h b/src/plugins/tls/schannel/qtls_schannel_p.h index f6bfdbc7cf..fab8777249 100644 --- a/src/plugins/tls/schannel/qtls_schannel_p.h +++ b/src/plugins/tls/schannel/qtls_schannel_p.h @@ -93,6 +93,7 @@ private: QSslSocket *q = nullptr; QSslSocketPrivate *d = nullptr; + SecPkgContext_CipherInfo cipherInfo = {}; SecPkgContext_ConnectionInfo connectionInfo = {}; SecPkgContext_StreamSizes streamSizes = {}; diff --git a/src/plugins/tls/schannel/qtlsbackend_schannel_p.h b/src/plugins/tls/schannel/qtlsbackend_schannel_p.h index a70f8922a6..7c2d675e79 100644 --- a/src/plugins/tls/schannel/qtlsbackend_schannel_p.h +++ b/src/plugins/tls/schannel/qtlsbackend_schannel_p.h @@ -57,6 +57,7 @@ private: QTlsPrivate::X509PemReaderPtr X509PemReader() const override; QTlsPrivate::X509DerReaderPtr X509DerReader() const override; + QTlsPrivate::X509Pkcs12ReaderPtr X509Pkcs12Reader() const override; static bool s_loadedCiphersAndCerts; }; diff --git a/src/plugins/tls/schannel/qx509_schannel.cpp b/src/plugins/tls/schannel/qx509_schannel.cpp index 5a5e625268..d9d82dce29 100644 --- a/src/plugins/tls/schannel/qx509_schannel.cpp +++ b/src/plugins/tls/schannel/qx509_schannel.cpp @@ -1,9 +1,11 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#include "qtlsbackend_schannel_p.h" #include "qtlskey_schannel_p.h" #include "qx509_schannel_p.h" +#include <QtCore/private/qsystemerror_p.h> #include <QtNetwork/private/qsslcertificate_p.h> #include <memory> @@ -39,13 +41,173 @@ QSslCertificate X509CertificateSchannel::QSslCertificate_from_CERT_CONTEXT(const QByteArray derData = QByteArray((const char *)certificateContext->pbCertEncoded, certificateContext->cbCertEncoded); QSslCertificate certificate(derData, QSsl::Der); - - auto *certBackend = QTlsBackend::backend<X509CertificateSchannel>(certificate); - Q_ASSERT(certBackend); - certBackend->certificateContext = CertDuplicateCertificateContext(certificateContext); + if (!certificate.isNull()) { + auto *certBackend = QTlsBackend::backend<X509CertificateSchannel>(certificate); + Q_ASSERT(certBackend); + certBackend->certificateContext = CertDuplicateCertificateContext(certificateContext); + } return certificate; } +bool X509CertificateSchannel::importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert, + QList<QSslCertificate> *caCertificates, + const QByteArray &passPhrase) +{ + // These are required + Q_ASSERT(device); + Q_ASSERT(key); + Q_ASSERT(cert); + + QByteArray pkcs12data = device->readAll(); + if (pkcs12data.size() == 0) + return false; + + CRYPT_DATA_BLOB dataBlob; + dataBlob.cbData = pkcs12data.size(); + dataBlob.pbData = reinterpret_cast<BYTE*>(pkcs12data.data()); + + const auto password = QString::fromUtf8(passPhrase); + + const DWORD flags = (CRYPT_EXPORTABLE | PKCS12_NO_PERSIST_KEY | PKCS12_PREFER_CNG_KSP); + + auto certStore = QHCertStorePointer(PFXImportCertStore(&dataBlob, + reinterpret_cast<LPCWSTR>(password.utf16()), + flags)); + + if (!certStore) { + qCWarning(lcTlsBackendSchannel, "Failed to import PFX data: %s", + qPrintable(QSystemError::windowsString())); + return false; + } + + // first extract the certificate with the private key + const auto certContext = QPCCertContextPointer(CertFindCertificateInStore(certStore.get(), + X509_ASN_ENCODING | + PKCS_7_ASN_ENCODING, + 0, + CERT_FIND_HAS_PRIVATE_KEY, + nullptr, nullptr)); + + if (!certContext) { + qCWarning(lcTlsBackendSchannel, "Failed to find certificate in PFX store: %s", + qPrintable(QSystemError::windowsString())); + return false; + } + + *cert = QSslCertificate_from_CERT_CONTEXT(certContext.get()); + + // retrieve the private key for the certificate + NCRYPT_KEY_HANDLE keyHandle = {}; + DWORD keyHandleSize = sizeof(keyHandle); + if (!CertGetCertificateContextProperty(certContext.get(), CERT_NCRYPT_KEY_HANDLE_PROP_ID, + &keyHandle, &keyHandleSize)) { + qCWarning(lcTlsBackendSchannel, "Failed to find private key handle in certificate context: %s", + qPrintable(QSystemError::windowsString())); + return false; + } + + SECURITY_STATUS securityStatus = ERROR_SUCCESS; + + // we need the 'NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG' to make NCryptExportKey succeed + DWORD policy = (NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG); + DWORD policySize = sizeof(policy); + + securityStatus = NCryptSetProperty(keyHandle, NCRYPT_EXPORT_POLICY_PROPERTY, + reinterpret_cast<BYTE*>(&policy), policySize, 0); + if (securityStatus != ERROR_SUCCESS) { + qCWarning(lcTlsBackendSchannel, "Failed to update export policy of private key: 0x%x", + static_cast<unsigned int>(securityStatus)); + return false; + } + + DWORD blobSize = {}; + securityStatus = NCryptExportKey(keyHandle, {}, BCRYPT_RSAFULLPRIVATE_BLOB, + nullptr, nullptr, 0, &blobSize, 0); + if (securityStatus != ERROR_SUCCESS) { + qCWarning(lcTlsBackendSchannel, "Failed to retrieve private key size: 0x%x", + static_cast<unsigned int>(securityStatus)); + return false; + } + + std::vector<BYTE> blob(blobSize); + securityStatus = NCryptExportKey(keyHandle, {}, BCRYPT_RSAFULLPRIVATE_BLOB, + nullptr, blob.data(), blobSize, &blobSize, 0); + if (securityStatus != ERROR_SUCCESS) { + qCWarning(lcTlsBackendSchannel, "Failed to retrieve private key from certificate: 0x%x", + static_cast<unsigned int>(securityStatus)); + return false; + } + + DWORD privateKeySize = {}; + + if (!CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, CNG_RSA_PRIVATE_KEY_BLOB, + blob.data(), nullptr, &privateKeySize)) { + qCWarning(lcTlsBackendSchannel, "Failed to encode private key to key info: %s", + qPrintable(QSystemError::windowsString())); + return false; + } + + std::vector<BYTE> privateKeyData(privateKeySize); + + CRYPT_PRIVATE_KEY_INFO privateKeyInfo = {}; + privateKeyInfo.Algorithm.pszObjId = const_cast<PSTR>(szOID_RSA_RSA); + privateKeyInfo.PrivateKey.cbData = privateKeySize; + privateKeyInfo.PrivateKey.pbData = privateKeyData.data(); + + if (!CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + CNG_RSA_PRIVATE_KEY_BLOB, blob.data(), + privateKeyInfo.PrivateKey.pbData, &privateKeyInfo.PrivateKey.cbData)) { + qCWarning(lcTlsBackendSchannel, "Failed to encode private key to key info: %s", + qPrintable(QSystemError::windowsString())); + return false; + } + + + DWORD derSize = {}; + + if (!CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO, + &privateKeyInfo, nullptr, &derSize)) { + qCWarning(lcTlsBackendSchannel, "Failed to encode key info to DER format: %s", + qPrintable(QSystemError::windowsString())); + + return false; + } + + QByteArray derData(derSize, Qt::Uninitialized); + + if (!CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO, + &privateKeyInfo, reinterpret_cast<BYTE*>(derData.data()), &derSize)) { + qCWarning(lcTlsBackendSchannel, "Failed to encode key info to DER format: %s", + qPrintable(QSystemError::windowsString())); + + return false; + } + + *key = QSslKey(derData, QSsl::Rsa, QSsl::Der, QSsl::PrivateKey); + if (key->isNull()) { + qCWarning(lcTlsBackendSchannel, "Failed to parse private key from DER format"); + return false; + } + + // fetch all the remaining certificates as CA certificates + if (caCertificates) { + PCCERT_CONTEXT caCertContext = nullptr; + while ((caCertContext = CertFindCertificateInStore(certStore.get(), + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + 0, CERT_FIND_ANY, nullptr, caCertContext))) { + if (CertCompareCertificate(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + certContext->pCertInfo, caCertContext->pCertInfo)) + continue; // ignore the certificate with private key + + auto caCertificate = QSslCertificate_from_CERT_CONTEXT(caCertContext); + + caCertificates->append(caCertificate); + } + } + + return true; +} + } // namespace QTlsPrivate QT_END_NAMESPACE diff --git a/src/plugins/tls/schannel/qx509_schannel_p.h b/src/plugins/tls/schannel/qx509_schannel_p.h index 17b91983f2..4625c9584c 100644 --- a/src/plugins/tls/schannel/qx509_schannel_p.h +++ b/src/plugins/tls/schannel/qx509_schannel_p.h @@ -36,6 +36,10 @@ public: Qt::HANDLE handle() const override; static QSslCertificate QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext); + + static bool importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert, + QList<QSslCertificate> *caCertificates, + const QByteArray &passPhrase); private: const CERT_CONTEXT *certificateContext = nullptr; diff --git a/src/plugins/tls/securetransport/CMakeLists.txt b/src/plugins/tls/securetransport/CMakeLists.txt index 0355049157..bb560229e8 100644 --- a/src/plugins/tls/securetransport/CMakeLists.txt +++ b/src/plugins/tls/securetransport/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_internal_add_plugin(QSecureTransportBackendPlugin OUTPUT_NAME qsecuretransportbackend CLASS_NAME QSecureTransportBackend diff --git a/src/plugins/tls/securetransport/qtls_st.cpp b/src/plugins/tls/securetransport/qtls_st.cpp index d2e5132752..48b7f3364f 100644 --- a/src/plugins/tls/securetransport/qtls_st.cpp +++ b/src/plugins/tls/securetransport/qtls_st.cpp @@ -315,42 +315,38 @@ void TlsCryptographSecureTransport::continueHandshake() qCDebug(lcSecureTransport) << d->plainTcpSocket() << "connection encrypted"; #endif -#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13_4, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0) // Unlike OpenSSL, Secure Transport does not allow to negotiate protocols via // a callback during handshake. We can only set our list of preferred protocols // (and send it during handshake) and then receive what our peer has sent to us. // And here we can finally try to find a match (if any). const auto &configuration = q->sslConfiguration(); - if (__builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) { - const auto &requestedProtocols = configuration.allowedNextProtocols(); - if (const int requestedCount = requestedProtocols.size()) { - QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNone); - QTlsBackend::setNegotiatedProtocol(d, {}); - - QCFType<CFArrayRef> cfArray; - const OSStatus result = SSLCopyALPNProtocols(context, &cfArray); - if (result == errSecSuccess && cfArray && CFArrayGetCount(cfArray)) { - const int size = CFArrayGetCount(cfArray); - QList<QString> peerProtocols(size); - for (int i = 0; i < size; ++i) - peerProtocols[i] = QString::fromCFString((CFStringRef)CFArrayGetValueAtIndex(cfArray, i)); - - for (int i = 0; i < requestedCount; ++i) { - const auto requestedName = QString::fromLatin1(requestedProtocols[i]); - for (int j = 0; j < size; ++j) { - if (requestedName == peerProtocols[j]) { - QTlsBackend::setNegotiatedProtocol(d, requestedName.toLatin1()); - QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNegotiated); - break; - } - } - if (configuration.nextProtocolNegotiationStatus() == QSslConfiguration::NextProtocolNegotiationNegotiated) + const auto &requestedProtocols = configuration.allowedNextProtocols(); + if (const int requestedCount = requestedProtocols.size()) { + QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNone); + QTlsBackend::setNegotiatedProtocol(d, {}); + + QCFType<CFArrayRef> cfArray; + const OSStatus result = SSLCopyALPNProtocols(context, &cfArray); + if (result == errSecSuccess && cfArray && CFArrayGetCount(cfArray)) { + const int size = CFArrayGetCount(cfArray); + QList<QString> peerProtocols(size); + for (int i = 0; i < size; ++i) + peerProtocols[i] = QString::fromCFString((CFStringRef)CFArrayGetValueAtIndex(cfArray, i)); + + for (int i = 0; i < requestedCount; ++i) { + const auto requestedName = QString::fromLatin1(requestedProtocols[i]); + for (int j = 0; j < size; ++j) { + if (requestedName == peerProtocols[j]) { + QTlsBackend::setNegotiatedProtocol(d, requestedName.toLatin1()); + QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNegotiated); break; + } } + if (configuration.nextProtocolNegotiationStatus() == QSslConfiguration::NextProtocolNegotiationNegotiated) + break; } } } -#endif // QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE if (!renegotiating) emit q->encrypted(); @@ -377,6 +373,7 @@ void TlsCryptographSecureTransport::disconnectFromHost() if (context) { if (!shutdown) { SSLClose(context); + context.reset(nullptr); shutdown = true; } } @@ -694,35 +691,31 @@ bool TlsCryptographSecureTransport::initSslContext() return false; } -#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13_4, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0) - if (__builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) { - const auto protocolNames = configuration.allowedNextProtocols(); - QCFType<CFMutableArrayRef> cfNames(CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks)); - if (cfNames) { - for (const QByteArray &name : protocolNames) { - if (name.size() > 255) { - qCWarning(lcSecureTransport) << "TLS ALPN extension" << name - << "is too long and will be ignored."; - continue; - } else if (name.isEmpty()) { - continue; - } - QCFString cfName(QString::fromLatin1(name).toCFString()); - CFArrayAppendValue(cfNames, cfName); + const auto protocolNames = configuration.allowedNextProtocols(); + QCFType<CFMutableArrayRef> cfNames(CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks)); + if (cfNames) { + for (const QByteArray &name : protocolNames) { + if (name.size() > 255) { + qCWarning(lcSecureTransport) << "TLS ALPN extension" << name + << "is too long and will be ignored."; + continue; + } else if (name.isEmpty()) { + continue; } + QCFString cfName(QString::fromLatin1(name).toCFString()); + CFArrayAppendValue(cfNames, cfName); + } - if (CFArrayGetCount(cfNames)) { - // Up to the application layer to check that negotiation - // failed, and handle this non-TLS error, we do not handle - // the result of this call as an error: - if (SSLSetALPNProtocols(context, cfNames) != errSecSuccess) - qCWarning(lcSecureTransport) << "SSLSetALPNProtocols failed - too long protocol names?"; - } - } else { - qCWarning(lcSecureTransport) << "failed to allocate ALPN names array"; + if (CFArrayGetCount(cfNames)) { + // Up to the application layer to check that negotiation + // failed, and handle this non-TLS error, we do not handle + // the result of this call as an error: + if (SSLSetALPNProtocols(context, cfNames) != errSecSuccess) + qCWarning(lcSecureTransport) << "SSLSetALPNProtocols failed - too long protocol names?"; } + } else { + qCWarning(lcSecureTransport) << "failed to allocate ALPN names array"; } -#endif // QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE if (mode == QSslSocket::SslClientMode) { // enable Server Name Indication (SNI) @@ -1083,7 +1076,7 @@ bool TlsCryptographSecureTransport::verifyPeerTrust() QTlsBackend::storePeerCertificate(d, peerCertificateChain.at(0)); // Check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer): - for (const QSslCertificate &cert : qAsConst(peerCertificateChain)) { + for (const QSslCertificate &cert : std::as_const(peerCertificateChain)) { if (QSslCertificatePrivate::isBlacklisted(cert) && !canIgnoreVerify) { const QSslError error(QSslError::CertificateBlacklisted, cert); errors << error; @@ -1134,8 +1127,6 @@ bool TlsCryptographSecureTransport::verifyPeerTrust() QCFType<CFDataRef> certData = cert.toDer().toCFData(); if (QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, certData)) CFArrayAppendValue(certArray, secRef); - else - qCWarning(lcSecureTransport, "Failed to create SecCertificate from QSslCertificate"); } SecTrustSetAnchorCertificates(trust, certArray); diff --git a/src/plugins/tls/securetransport/qtls_st_p.h b/src/plugins/tls/securetransport/qtls_st_p.h index 16f7104e67..2903ef4815 100644 --- a/src/plugins/tls/securetransport/qtls_st_p.h +++ b/src/plugins/tls/securetransport/qtls_st_p.h @@ -27,6 +27,11 @@ #include <QtNetwork/qabstractsocket.h> #include <QtNetwork/private/qsslsocket_p.h> +#warning SecureTransport was deprecated in macOS 10.15 and iOS 13, \ +and is no longer supported. We should be using Network.framework instead. \ +See QTBUG-85231 for more information. +QT_WARNING_DISABLE_DEPRECATED + #include <Security/Security.h> #include <Security/SecureTransport.h> diff --git a/src/plugins/tls/securetransport/qtlsbackend_st.cpp b/src/plugins/tls/securetransport/qtlsbackend_st.cpp index 4c385b5af6..54e45d1720 100644 --- a/src/plugins/tls/securetransport/qtlsbackend_st.cpp +++ b/src/plugins/tls/securetransport/qtlsbackend_st.cpp @@ -357,3 +357,5 @@ QTlsPrivate::TlsCryptograph *QSecureTransportBackend::createTlsCryptograph() con QT_END_NAMESPACE + +#include "moc_qtlsbackend_st_p.cpp" diff --git a/src/plugins/tls/shared/qasn1element.cpp b/src/plugins/tls/shared/qasn1element.cpp index cbf038f569..97be46866d 100644 --- a/src/plugins/tls/shared/qasn1element.cpp +++ b/src/plugins/tls/shared/qasn1element.cpp @@ -6,13 +6,17 @@ #include <QtCore/qdatastream.h> #include <QtCore/qdatetime.h> +#include <QtCore/qtimezone.h> #include <QtCore/qlist.h> #include <QDebug> +#include <private/qtools_p.h> #include <limits> QT_BEGIN_NAMESPACE +using namespace QtMiscUtils; + typedef QMap<QByteArray, QByteArray> OidNameMap; static OidNameMap createOidMap() { @@ -213,11 +217,6 @@ QDateTime QAsn1Element::toDateTime() const // QDateTime::fromString is lenient and accepts +- signs in front // of the year; but ASN.1 doesn't allow them. - const auto isAsciiDigit = [](char c) - { - return c >= '0' && c <= '9'; - }; - if (!isAsciiDigit(mValue[0])) return result; @@ -226,31 +225,28 @@ QDateTime QAsn1Element::toDateTime() const return result; if (mType == UtcTimeType && mValue.size() == 13) { - result = QDateTime::fromString(QString::fromLatin1(mValue), - QStringLiteral("yyMMddHHmmsst")); - if (!result.isValid()) - return result; - - Q_ASSERT(result.timeSpec() == Qt::UTC); - - QDate date = result.date(); - // RFC 2459: // Where YY is greater than or equal to 50, the year shall be // interpreted as 19YY; and // // Where YY is less than 50, the year shall be interpreted as 20YY. // - // QDateTime interprets the 'yy' format as 19yy, so we may need to adjust - // the year (bring it in the [1950, 2049] range). - if (date.year() < 1950) - result.setDate(date.addYears(100)); + // so use 1950 as base year. + constexpr int rfc2459CenturyStart = 1950; + const QLatin1StringView inputView(mValue); + QDate date = QDate::fromString(inputView.first(6), u"yyMMdd", rfc2459CenturyStart); + if (!date.isValid()) + return result; - Q_ASSERT(result.date().year() >= 1950); - Q_ASSERT(result.date().year() <= 2049); + Q_ASSERT(date.year() >= rfc2459CenturyStart); + Q_ASSERT(date.year() < 100 + rfc2459CenturyStart); + + QTime time = QTime::fromString(inputView.sliced(6, 6), u"HHmmss"); + if (!time.isValid()) + return result; + result = QDateTime(date, time, QTimeZone::UTC); } else if (mType == GeneralizedTimeType && mValue.size() == 15) { - result = QDateTime::fromString(QString::fromLatin1(mValue), - QStringLiteral("yyyyMMddHHmmsst")); + result = QDateTime::fromString(QString::fromLatin1(mValue), u"yyyyMMddHHmmsst"); } return result; diff --git a/src/plugins/tls/shared/qsslsocket_mac_shared.cpp b/src/plugins/tls/shared/qsslsocket_mac_shared.cpp index f40d2fb770..1257240ee2 100644 --- a/src/plugins/tls/shared/qsslsocket_mac_shared.cpp +++ b/src/plugins/tls/shared/qsslsocket_mac_shared.cpp @@ -6,6 +6,7 @@ #include <QtNetwork/qsslcertificate.h> +#include <QtCore/qloggingcategory.h> #include <QtCore/qglobal.h> #include <QtCore/qdebug.h> @@ -21,6 +22,8 @@ QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcX509, "qt.mac.shared.x509"); + #ifdef Q_OS_MACOS namespace { @@ -74,6 +77,52 @@ bool isCaCertificateTrusted(SecCertificateRef cfCert, int domain) return false; } +bool canDERBeParsed(CFDataRef derData, const QSslCertificate &qtCert) +{ + // We are observing certificates, that while accepted when we copy them + // from the keychain(s), later give us 'Failed to create SslCertificate + // from QSslCertificate'. It's interesting to know at what step the failure + // occurred. Let's check it and skip it below if it's not valid. + + auto checkDer = [](CFDataRef derData, const char *source) + { + Q_ASSERT(source); + Q_ASSERT(derData); + + const auto cfLength = CFDataGetLength(derData); + if (cfLength <= 0) { + qCWarning(lcX509) << source << "returned faulty DER data with invalid length."; + return false; + } + + QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, derData); + if (!secRef) { + qCWarning(lcX509) << source << "returned faulty DER data which cannot be parsed back."; + return false; + } + return true; + }; + + if (!checkDer(derData, "SecCertificateCopyData")) { + qCDebug(lcX509) << "Faulty QSslCertificate is:" << qtCert;// Just in case we managed to parse something. + return false; + } + + // Generic parser failed? + if (qtCert.isNull()) { + qCWarning(lcX509, "QSslCertificate failed to parse DER"); + return false; + } + + const QCFType<CFDataRef> qtDerData = qtCert.toDer().toCFData(); + if (!checkDer(qtDerData, "QSslCertificate")) { + qCWarning(lcX509) << "Faulty QSslCertificate is:" << qtCert; + return false; + } + + return true; +} + } // unnamed namespace #endif // Q_OS_MACOS @@ -94,8 +143,19 @@ QList<QSslCertificate> systemCaCertificates() SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i); QCFType<CFDataRef> derData = SecCertificateCopyData(cfCert); if (isCaCertificateTrusted(cfCert, dom)) { - if (derData) - systemCerts << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der); + if (derData) { + const auto newCert = QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der); + if (!canDERBeParsed(derData, newCert)) { + // Last attempt to get some information about the certificate: + CFShow(cfCert); + continue; + } + systemCerts << newCert; + } else { + // "Returns NULL if the data passed in the certificate parameter + // is not a valid certificate object." + qCWarning(lcX509, "SecCertificateCopyData returned invalid DER data (nullptr)."); + } } } } diff --git a/src/plugins/tls/shared/qwincrypt_p.h b/src/plugins/tls/shared/qwincrypt_p.h index 1b1f0f16c0..48ca4247fa 100644 --- a/src/plugins/tls/shared/qwincrypt_p.h +++ b/src/plugins/tls/shared/qwincrypt_p.h @@ -40,6 +40,16 @@ struct QHCertStoreDeleter { // A simple RAII type used by Schannel code and Window CA fetcher class: using QHCertStorePointer = std::unique_ptr<void, QHCertStoreDeleter>; +struct QPCCertContextDeleter { + void operator()(PCCERT_CONTEXT context) const + { + CertFreeCertificateContext(context); + } +}; + +// A simple RAII type used by Schannel code +using QPCCertContextPointer = std::unique_ptr<const CERT_CONTEXT, QPCCertContextDeleter>; + QT_END_NAMESPACE #endif // QWINCRYPT_P_H diff --git a/src/plugins/tls/shared/qx509_generic.cpp b/src/plugins/tls/shared/qx509_generic.cpp index cfe2786680..5006db1a72 100644 --- a/src/plugins/tls/shared/qx509_generic.cpp +++ b/src/plugins/tls/shared/qx509_generic.cpp @@ -118,7 +118,7 @@ QList<QSslCertificate> X509CertificateGeneric::certificatesFromPem(const QByteAr QByteArray decoded = QByteArray::fromBase64( QByteArray::fromRawData(pem.data() + startPos, endPos - startPos)); - certificates << certificatesFromDer(decoded, 1);; + certificates << certificatesFromDer(decoded, 1); } return certificates; @@ -188,7 +188,7 @@ bool X509CertificateGeneric::parse(const QByteArray &data) if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) return false; - QByteArray issuerDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length()); + QByteArray issuerDer = data.mid(dataStream.device()->pos() - elem.value().size(), elem.value().size()); issuerInfoEntries = elem.toInfo(); // validity period @@ -215,7 +215,7 @@ bool X509CertificateGeneric::parse(const QByteArray &data) if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) return false; - QByteArray subjectDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length()); + QByteArray subjectDer = data.mid(dataStream.device()->pos() - elem.value().size(), elem.value().size()); subjectInfoEntries = elem.toInfo(); subjectMatchesIssuer = issuerDer == subjectDer; @@ -285,7 +285,7 @@ bool X509CertificateGeneric::parse(const QByteArray &data) case QAsn1Element::IpAddressType: { QHostAddress ipAddress; QByteArray ipAddrValue = nameElem.value(); - switch (ipAddrValue.length()) { + switch (ipAddrValue.size()) { case 4: // IPv4 ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast<quint32 *>(ipAddrValue.data()))); break; |