summaryrefslogtreecommitdiffstats
path: root/src/plugins/tls/openssl
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/tls/openssl')
-rw-r--r--src/plugins/tls/openssl/CMakeLists.txt63
-rw-r--r--src/plugins/tls/openssl/qdtls_openssl.cpp1417
-rw-r--r--src/plugins/tls/openssl/qdtls_openssl_p.h214
-rw-r--r--src/plugins/tls/openssl/qopenssl_p.h82
-rw-r--r--src/plugins/tls/openssl/qsslcontext_openssl.cpp815
-rw-r--r--src/plugins/tls/openssl/qsslcontext_openssl_p.h96
-rw-r--r--src/plugins/tls/openssl/qssldiffiehellmanparameters_openssl.cpp159
-rw-r--r--src/plugins/tls/openssl/qsslsocket_openssl_android.cpp58
-rw-r--r--src/plugins/tls/openssl/qsslsocket_openssl_symbols.cpp1266
-rw-r--r--src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h765
-rw-r--r--src/plugins/tls/openssl/qtls_openssl.cpp1860
-rw-r--r--src/plugins/tls/openssl/qtls_openssl_p.h140
-rw-r--r--src/plugins/tls/openssl/qtlsbackend_openssl.cpp611
-rw-r--r--src/plugins/tls/openssl/qtlsbackend_openssl_p.h104
-rw-r--r--src/plugins/tls/openssl/qtlskey_openssl.cpp541
-rw-r--r--src/plugins/tls/openssl/qtlskey_openssl_p.h100
-rw-r--r--src/plugins/tls/openssl/qwindowscarootfetcher.cpp249
-rw-r--r--src/plugins/tls/openssl/qwindowscarootfetcher_p.h59
-rw-r--r--src/plugins/tls/openssl/qx509_openssl.cpp947
-rw-r--r--src/plugins/tls/openssl/qx509_openssl_p.h88
20 files changed, 9634 insertions, 0 deletions
diff --git a/src/plugins/tls/openssl/CMakeLists.txt b/src/plugins/tls/openssl/CMakeLists.txt
new file mode 100644
index 0000000000..0e0a7a1552
--- /dev/null
+++ b/src/plugins/tls/openssl/CMakeLists.txt
@@ -0,0 +1,63 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_plugin(QTlsBackendOpenSSLPlugin
+ OUTPUT_NAME qopensslbackend
+ CLASS_NAME QTlsBackendOpenSSL
+ PLUGIN_TYPE tls
+ SOURCES
+ ../shared/qx509_base.cpp ../shared/qx509_base_p.h
+ ../shared/qtlskey_base.cpp ../shared/qtlskey_base_p.h
+ ../shared/qasn1element.cpp ../shared/qasn1element_p.h
+ qtlsbackend_openssl.cpp qtlsbackend_openssl_p.h
+ qx509_openssl.cpp qx509_openssl_p.h
+ qtlskey_openssl.cpp qtlskey_openssl_p.h
+ qtls_openssl.cpp qtls_openssl_p.h
+ qssldiffiehellmanparameters_openssl.cpp
+ qsslcontext_openssl.cpp qsslcontext_openssl_p.h
+ qsslsocket_openssl_symbols.cpp qsslsocket_openssl_symbols_p.h
+ qopenssl_p.h
+ LIBRARIES
+ Qt::NetworkPrivate
+ Qt::CorePrivate
+ DEFINES
+ 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
+ ../shared/qdtls_base.cpp ../shared/qdtls_base_p.h
+)
+
+qt_internal_extend_target(QTlsBackendOpenSSLPlugin CONDITION APPLE
+ SOURCES
+ ../shared/qsslsocket_mac_shared.cpp
+ LIBRARIES
+ ${FWCoreFoundation}
+ ${FWSecurity}
+)
+
+qt_internal_extend_target(QTlsBackendOpenSSLPlugin CONDITION ANDROID
+ SOURCES
+ qsslsocket_openssl_android.cpp
+)
+
+qt_internal_extend_target(QTlsBackendOpenSSLPlugin CONDITION WIN32
+ SOURCES
+ qwindowscarootfetcher.cpp qwindowscarootfetcher_p.h
+ ../shared/qwincrypt_p.h
+ LIBRARIES
+ crypt32
+)
+
+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
new file mode 100644
index 0000000000..fc07a29ec8
--- /dev/null
+++ b/src/plugins/tls/openssl/qdtls_openssl.cpp
@@ -0,0 +1,1417 @@
+// 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
+
+#include <QtNetwork/private/qnativesocketengine_p_p.h>
+
+#include "qsslsocket_openssl_symbols_p.h"
+#include "qdtls_openssl_p.h"
+#include "qx509_openssl_p.h"
+
+#include <QtNetwork/private/qsslpresharedkeyauthenticator_p.h>
+#include <QtNetwork/private/qsslcertificate_p.h>
+#include <QtNetwork/private/qssl_p.h>
+
+#include <QtNetwork/qudpsocket.h>
+
+#include <QtCore/qmessageauthenticationcode.h>
+#include <QtCore/qcryptographichash.h>
+
+#include <QtCore/qdebug.h>
+
+#include <cstring>
+#include <cstddef>
+
+QT_BEGIN_NAMESPACE
+
+#define QT_DTLS_VERBOSE 0
+
+#if QT_DTLS_VERBOSE
+
+#define qDtlsWarning(arg) qWarning(arg)
+#define qDtlsDebug(arg) qDebug(arg)
+
+#else
+
+#define qDtlsWarning(arg)
+#define qDtlsDebug(arg)
+
+#endif // QT_DTLS_VERBOSE
+
+namespace dtlsutil
+{
+
+QByteArray cookie_for_peer(SSL *ssl)
+{
+ Q_ASSERT(ssl);
+
+ // SSL_get_rbio does not increment the reference count
+ BIO *readBIO = q_SSL_get_rbio(ssl);
+ if (!readBIO) {
+ qCWarning(lcTlsBackend, "No BIO (dgram) found in SSL object");
+ return {};
+ }
+
+ auto listener = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(readBIO));
+ if (!listener) {
+ qCWarning(lcTlsBackend, "BIO_get_app_data returned invalid (nullptr) value");
+ return {};
+ }
+
+ const QHostAddress peerAddress(listener->remoteAddress);
+ const quint16 peerPort(listener->remotePort);
+ QByteArray peerData;
+ if (peerAddress.protocol() == QAbstractSocket::IPv6Protocol) {
+ const Q_IPV6ADDR sin6_addr(peerAddress.toIPv6Address());
+ peerData.resize(int(sizeof sin6_addr + sizeof peerPort));
+ char *dst = peerData.data();
+ std::memcpy(dst, &peerPort, sizeof peerPort);
+ dst += sizeof peerPort;
+ std::memcpy(dst, &sin6_addr, sizeof sin6_addr);
+ } else if (peerAddress.protocol() == QAbstractSocket::IPv4Protocol) {
+ const quint32 sin_addr(peerAddress.toIPv4Address());
+ peerData.resize(int(sizeof sin_addr + sizeof peerPort));
+ char *dst = peerData.data();
+ std::memcpy(dst, &peerPort, sizeof peerPort);
+ dst += sizeof peerPort;
+ std::memcpy(dst, &sin_addr, sizeof sin_addr);
+ } else {
+ Q_UNREACHABLE();
+ }
+
+ return peerData;
+}
+
+struct FallbackCookieSecret
+{
+ FallbackCookieSecret()
+ {
+ key.resize(32);
+ const int status = q_RAND_bytes(reinterpret_cast<unsigned char *>(key.data()),
+ key.size());
+ if (status <= 0)
+ key.clear();
+ }
+
+ QByteArray key;
+
+ Q_DISABLE_COPY_MOVE(FallbackCookieSecret)
+};
+
+QByteArray fallbackSecret()
+{
+ static const FallbackCookieSecret generator;
+ return generator.key;
+}
+
+int next_timeoutMs(SSL *tlsConnection)
+{
+ Q_ASSERT(tlsConnection);
+ timeval timeLeft = {};
+ q_DTLSv1_get_timeout(tlsConnection, &timeLeft);
+ return timeLeft.tv_sec * 1000;
+}
+
+
+void delete_connection(SSL *ssl)
+{
+ // The 'deleter' for QSharedPointer<SSL>.
+ if (ssl)
+ q_SSL_free(ssl);
+}
+
+void delete_BIO_ADDR(BIO_ADDR *bio)
+{
+ // A deleter for QSharedPointer<BIO_ADDR>
+ if (bio)
+ q_BIO_ADDR_free(bio);
+}
+
+void delete_bio_method(BIO_METHOD *method)
+{
+ // The 'deleter' for QSharedPointer<BIO_METHOD>.
+ if (method)
+ q_BIO_meth_free(method);
+}
+
+// The path MTU discovery is non-trivial: it's a mix of getsockopt/setsockopt
+// (IP_MTU/IP6_MTU/IP_MTU_DISCOVER) and fallback MTU values. It's not
+// supported on all platforms, worse so - imposes specific requirements on
+// underlying UDP socket etc. So for now, we either try a user-proposed MTU
+// hint or rely on our own fallback value. As a fallback mtu OpenSSL uses 576
+// for IPv4 and 1280 for IPv6 (RFC 791, RFC 2460). To KIS we use 576. This
+// rather small MTU value does not affect the size that can be read/written
+// by QDtls, only a handshake (which is allowed to fragment).
+enum class MtuGuess : long
+{
+ defaultMtu = 576
+};
+
+} // namespace dtlsutil
+
+namespace dtlscallbacks
+{
+
+extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst,
+ unsigned *cookieLength)
+{
+ if (!ssl || !dst || !cookieLength) {
+ qCWarning(lcTlsBackend,
+ "Failed to generate cookie - invalid (nullptr) parameter(s)");
+ return 0;
+ }
+
+ void *generic = q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData);
+ if (!generic) {
+ qCWarning(lcTlsBackend, "SSL_get_ex_data returned nullptr, cannot generate cookie");
+ return 0;
+ }
+
+ *cookieLength = 0;
+
+ auto dtls = static_cast<dtlsopenssl::DtlsState *>(generic);
+ if (!dtls->secret.size())
+ return 0;
+
+ const QByteArray peerData(dtlsutil::cookie_for_peer(ssl));
+ if (!peerData.size())
+ return 0;
+
+ QMessageAuthenticationCode hmac(dtls->hashAlgorithm, dtls->secret);
+ hmac.addData(peerData);
+ 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());
+ std::memcpy(dst, cookie.constData(), *cookieLength);
+
+ return 1;
+}
+
+extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie,
+ unsigned cookieLength)
+{
+ if (!ssl || !cookie || !cookieLength) {
+ qCWarning(lcTlsBackend, "Could not verify cookie, invalid (nullptr or zero) parameters");
+ return 0;
+ }
+
+ unsigned char newCookie[DTLS1_COOKIE_LENGTH] = {};
+ unsigned newCookieLength = 0;
+ if (q_generate_cookie_callback(ssl, newCookie, &newCookieLength) != 1)
+ return 0;
+
+ return newCookieLength == cookieLength
+ && !q_CRYPTO_memcmp(cookie, newCookie, size_t(cookieLength));
+}
+
+extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx)
+{
+ if (!ok) {
+ // Store the error and at which depth the error was detected.
+ SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx()));
+ if (!ssl) {
+ qCWarning(lcTlsBackend, "X509_STORE_CTX_get_ex_data returned nullptr, handshake failure");
+ return 0;
+ }
+
+ void *generic = q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData);
+ if (!generic) {
+ qCWarning(lcTlsBackend, "SSL_get_ex_data returned nullptr, handshake failure");
+ return 0;
+ }
+
+ auto dtls = static_cast<dtlsopenssl::DtlsState *>(generic);
+ dtls->x509Errors.append(QTlsPrivate::X509CertificateOpenSSL::errorEntryFromStoreContext(ctx));
+ }
+
+ // Always return 1 (OK) to allow verification to continue. We handle the
+ // errors gracefully after collecting all errors, after verification has
+ // completed.
+ return 1;
+}
+
+extern "C" unsigned q_PSK_client_callback(SSL *ssl, const char *hint, char *identity,
+ unsigned max_identity_len, unsigned char *psk,
+ unsigned max_psk_len)
+{
+ auto *dtls = static_cast<dtlsopenssl::DtlsState *>(q_SSL_get_ex_data(ssl,
+ QTlsBackendOpenSSL::s_indexForSSLExtraData));
+ if (!dtls)
+ return 0;
+
+ Q_ASSERT(dtls->dtlsPrivate);
+ return dtls->dtlsPrivate->pskClientCallback(hint, identity, max_identity_len, psk, max_psk_len);
+}
+
+extern "C" unsigned q_PSK_server_callback(SSL *ssl, const char *identity, unsigned char *psk,
+ unsigned max_psk_len)
+{
+ auto *dtls = static_cast<dtlsopenssl::DtlsState *>(q_SSL_get_ex_data(ssl,
+ QTlsBackendOpenSSL::s_indexForSSLExtraData));
+ if (!dtls)
+ return 0;
+
+ Q_ASSERT(dtls->dtlsPrivate);
+ return dtls->dtlsPrivate->pskServerCallback(identity, psk, max_psk_len);
+}
+
+} // namespace dtlscallbacks
+
+namespace dtlsbio
+{
+
+extern "C" int q_dgram_read(BIO *bio, char *dst, int bytesToRead)
+{
+ if (!bio || !dst || bytesToRead <= 0) {
+ qCWarning(lcTlsBackend, "invalid input parameter(s)");
+ return 0;
+ }
+
+ q_BIO_clear_retry_flags(bio);
+
+ auto dtls = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(bio));
+ // It's us who set data, if OpenSSL does too, the logic here is wrong
+ // then and we have to use BIO_set_app_data then!
+ Q_ASSERT(dtls);
+ int bytesRead = 0;
+ if (dtls->dgram.size()) {
+ bytesRead = qMin(dtls->dgram.size(), bytesToRead);
+ std::memcpy(dst, dtls->dgram.constData(), bytesRead);
+
+ if (!dtls->peeking)
+ dtls->dgram = dtls->dgram.mid(bytesRead);
+ } else {
+ bytesRead = -1;
+ }
+
+ if (bytesRead <= 0)
+ q_BIO_set_retry_read(bio);
+
+ return bytesRead;
+}
+
+extern "C" int q_dgram_write(BIO *bio, const char *src, int bytesToWrite)
+{
+ if (!bio || !src || bytesToWrite <= 0) {
+ qCWarning(lcTlsBackend, "invalid input parameter(s)");
+ return 0;
+ }
+
+ q_BIO_clear_retry_flags(bio);
+
+ auto dtls = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(bio));
+ Q_ASSERT(dtls);
+ if (dtls->writeSuppressed) {
+ // See the comment in QDtls::startHandshake.
+ return bytesToWrite;
+ }
+
+ QUdpSocket *udpSocket = dtls->udpSocket;
+ Q_ASSERT(udpSocket);
+
+ const QByteArray dgram(QByteArray::fromRawData(src, bytesToWrite));
+ qint64 bytesWritten = -1;
+ if (udpSocket->state() == QAbstractSocket::ConnectedState) {
+ bytesWritten = udpSocket->write(dgram);
+ } else {
+ bytesWritten = udpSocket->writeDatagram(dgram, dtls->remoteAddress,
+ dtls->remotePort);
+ }
+
+ if (bytesWritten <= 0)
+ q_BIO_set_retry_write(bio);
+
+ Q_ASSERT(bytesWritten <= std::numeric_limits<int>::max());
+ return int(bytesWritten);
+}
+
+extern "C" int q_dgram_puts(BIO *bio, const char *src)
+{
+ if (!bio || !src) {
+ qCWarning(lcTlsBackend, "invalid input parameter(s)");
+ return 0;
+ }
+
+ return q_dgram_write(bio, src, int(std::strlen(src)));
+}
+
+extern "C" long q_dgram_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+ // This is our custom BIO_ctrl. bio.h defines a lot of BIO_CTRL_*
+ // and BIO_* constants and BIO_somename macros that expands to BIO_ctrl
+ // call with one of those constants as argument. What exactly BIO_ctrl
+ // does - depends on the 'cmd' and the type of BIO (so BIO_ctrl does
+ // not even have a single well-defined value meaning success or failure).
+ // We handle only the most generic commands - the ones documented for
+ // BIO_ctrl - and also DGRAM specific ones. And even for them - in most
+ // cases we do nothing but report a success or some non-error value.
+ // Documents also state: "Source/sink BIOs return an 0 if they do not
+ // recognize the BIO_ctrl() operation." - these are covered by 'default'
+ // label in the switch-statement below. Debug messages in the switch mean:
+ // 1) we got a command that is unexpected for dgram BIO, or:
+ // 2) we do not call any function that would lead to OpenSSL using this
+ // command.
+
+ if (!bio) {
+ qDebug(lcTlsBackend, "invalid 'bio' parameter (nullptr)");
+ return -1;
+ }
+
+ auto dtls = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(bio));
+ Q_ASSERT(dtls);
+
+ switch (cmd) {
+ // Let's start from the most generic ones, in the order in which they are
+ // documented (as BIO_ctrl):
+ case BIO_CTRL_RESET:
+ // BIO_reset macro.
+ // From documentation:
+ // "BIO_reset() normally returns 1 for success and 0 or -1 for failure.
+ // File BIOs are an exception, they return 0 for success and -1 for
+ // failure."
+ // We have nothing to reset and we are not file BIO.
+ return 1;
+ case BIO_C_FILE_SEEK:
+ case BIO_C_FILE_TELL:
+ qDtlsWarning("Unexpected cmd (BIO_C_FILE_SEEK/BIO_C_FILE_TELL)");
+ // These are for BIO_seek, BIO_tell. We are not a file BIO.
+ // Non-negative return value means success.
+ return 0;
+ case BIO_CTRL_FLUSH:
+ // BIO_flush, nothing to do, we do not buffer any data.
+ // 0 or -1 means error, 1 - success.
+ return 1;
+ case BIO_CTRL_EOF:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_EOF)");
+ // BIO_eof, 1 means EOF read. Makes no sense for us.
+ return 0;
+ case BIO_CTRL_SET_CLOSE:
+ // BIO_set_close with BIO_CLOSE/BIO_NOCLOSE flags. Documented as
+ // always returning 1.
+ // From the documentation:
+ // "Typically BIO_CLOSE is used in a source/sink BIO to indicate that
+ // the underlying I/O stream should be closed when the BIO is freed."
+ //
+ // QUdpSocket we work with is not BIO's business, ignoring.
+ return 1;
+ case BIO_CTRL_GET_CLOSE:
+ // BIO_get_close. No, never, see the comment above.
+ return 0;
+ case BIO_CTRL_PENDING:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_PENDING)");
+ // BIO_pending. Not used by DTLS/OpenSSL (we are not buffering).
+ return 0;
+ case BIO_CTRL_WPENDING:
+ // No, we have nothing buffered.
+ return 0;
+ // The constants below are not documented as a part BIO_ctrl documentation,
+ // but they are also not type-specific.
+ case BIO_CTRL_DUP:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DUP)");
+ // BIO_dup_state, not used by DTLS (and socket-related BIOs in general).
+ // For some very specific BIO type this 'cmd' would copy some state
+ // from 'bio' to (BIO*)'ptr'. 1 means success.
+ return 0;
+ case BIO_CTRL_SET_CALLBACK:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_SET_CALLBACK)");
+ // BIO_set_info_callback. We never call this, OpenSSL does not do this
+ // on its own (normally it's used if client code wants to have some
+ // debug information, for example, dumping handshake state via
+ // BIO_printf from SSL info_callback).
+ return 0;
+ case BIO_CTRL_GET_CALLBACK:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_GET_CALLBACK)");
+ // BIO_get_info_callback. We never call this.
+ if (ptr)
+ *static_cast<bio_info_cb **>(ptr) = nullptr;
+ return 0;
+ case BIO_CTRL_SET:
+ case BIO_CTRL_GET:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_SET/BIO_CTRL_GET)");
+ // Somewhat 'documented' as setting/getting IO type. Not used anywhere
+ // except BIO_buffer_get_num_lines (which contradics 'get IO type').
+ // Ignoring.
+ return 0;
+ // DGRAM-specific operation, we have to return some reasonable value
+ // (so far, I've encountered only peek mode switching, connect).
+ case BIO_CTRL_DGRAM_CONNECT:
+ // BIO_ctrl_dgram_connect. Not needed. Our 'dtls' already knows
+ // the peer's address/port. Report success though.
+ return 1;
+ case BIO_CTRL_DGRAM_SET_CONNECTED:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_CONNECTED)");
+ // BIO_ctrl_dgram_set_connected. We never call it, OpenSSL does
+ // not call it on its own (so normally it's done by client code).
+ // Similar to BIO_CTRL_DGRAM_CONNECT, but it also informs the BIO
+ // that its UDP socket is connected. We never need it though.
+ return -1;
+ case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_RECV_TIMEOUT)");
+ // Essentially setsockopt with SO_RCVTIMEO, not needed, our sockets
+ // are non-blocking.
+ return -1;
+ case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_GET_RECV_TIMEOUT)");
+ // getsockopt with SO_RCVTIMEO, not needed, our sockets are
+ // non-blocking. ptr is timeval *.
+ return -1;
+ case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_SEND_TIMEOUT)");
+ // setsockopt, SO_SNDTIMEO, cannot happen.
+ return -1;
+ case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_GET_SEND_TIMEOUT)");
+ // getsockopt, SO_SNDTIMEO, cannot happen.
+ return -1;
+ case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP:
+ // BIO_dgram_recv_timedout. No, we are non-blocking.
+ return 0;
+ case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP:
+ // BIO_dgram_send_timedout. No, we are non-blocking.
+ return 0;
+ case BIO_CTRL_DGRAM_MTU_DISCOVER:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_MTU_DISCOVER)");
+ // setsockopt, IP_MTU_DISCOVER/IP6_MTU_DISCOVER, to be done
+ // in QUdpSocket instead. OpenSSL never calls it, only client
+ // code.
+ return 1;
+ case BIO_CTRL_DGRAM_QUERY_MTU:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_QUERY_MTU)");
+ // To be done in QUdpSocket instead.
+ return 1;
+ case BIO_CTRL_DGRAM_GET_FALLBACK_MTU:
+ qDtlsWarning("Unexpected command *BIO_CTRL_DGRAM_GET_FALLBACK_MTU)");
+ // Without SSL_OP_NO_QUERY_MTU set on SSL, OpenSSL can request for
+ // fallback MTU after several re-transmissions.
+ // Should never happen in our case.
+ return long(dtlsutil::MtuGuess::defaultMtu);
+ case BIO_CTRL_DGRAM_GET_MTU:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_GET_MTU)");
+ return -1;
+ case BIO_CTRL_DGRAM_SET_MTU:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_MTU)");
+ // Should not happen (we don't call BIO_ctrl with this parameter)
+ // and set MTU on SSL instead.
+ return -1; // num is mtu and it's a return value meaning success.
+ case BIO_CTRL_DGRAM_MTU_EXCEEDED:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_MTU_EXCEEDED)");
+ return 0;
+ case BIO_CTRL_DGRAM_GET_PEER:
+ qDtlsDebug("BIO_CTRL_DGRAM_GET_PEER");
+ // BIO_dgram_get_peer. We do not return a real address (DTLS is not
+ // using this address), but let's pretend a success.
+ switch (dtls->remoteAddress.protocol()) {
+ case QAbstractSocket::IPv6Protocol:
+ return sizeof(sockaddr_in6);
+ case QAbstractSocket::IPv4Protocol:
+ return sizeof(sockaddr_in);
+ default:
+ return -1;
+ }
+ case BIO_CTRL_DGRAM_SET_PEER:
+ // Similar to BIO_CTRL_DGRAM_CONNECTED.
+ return 1;
+ case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
+ // DTLSTODO: I'm not sure yet, how it's used by OpenSSL.
+ return 1;
+ case BIO_CTRL_DGRAM_SET_DONT_FRAG:
+ qDtlsDebug("BIO_CTRL_DGRAM_SET_DONT_FRAG");
+ // To be done in QUdpSocket, it's about IP_DONTFRAG etc.
+ return 1;
+ case BIO_CTRL_DGRAM_GET_MTU_OVERHEAD:
+ // AFAIK it's 28 for IPv4 and 48 for IPv6, but let's pretend it's 0
+ // so that OpenSSL does not start suddenly fragmenting the first
+ // client hello (which will result in DTLSv1_listen rejecting it).
+ return 0;
+ case BIO_CTRL_DGRAM_SET_PEEK_MODE:
+ dtls->peeking = num;
+ return 1;
+ default:;
+#if QT_DTLS_VERBOSE
+ qWarning() << "Unexpected cmd (" << cmd << ")";
+#endif
+ }
+
+ return 0;
+}
+
+extern "C" int q_dgram_create(BIO *bio)
+{
+
+ q_BIO_set_init(bio, 1);
+ // With a custom BIO you'd normally allocate some implementation-specific
+ // data and append it to this new BIO using BIO_set_data. We don't need
+ // it and thus q_dgram_destroy below is a noop.
+ return 1;
+}
+
+extern "C" int q_dgram_destroy(BIO *bio)
+{
+ Q_UNUSED(bio);
+ return 1;
+}
+
+const char * const qdtlsMethodName = "qdtlsbio";
+
+} // namespace dtlsbio
+
+namespace dtlsopenssl
+{
+
+bool DtlsState::init(QDtlsBasePrivate *dtlsBase, QUdpSocket *socket,
+ const QHostAddress &remote, quint16 port,
+ const QByteArray &receivedMessage)
+{
+ Q_ASSERT(dtlsBase);
+ Q_ASSERT(socket);
+
+ if (!tlsContext && !initTls(dtlsBase))
+ return false;
+
+ udpSocket = socket;
+
+ setLinkMtu(dtlsBase);
+
+ dgram = receivedMessage;
+ remoteAddress = remote;
+ remotePort = port;
+
+ // SSL_get_rbio does not increment a reference count.
+ BIO *bio = q_SSL_get_rbio(tlsConnection.data());
+ Q_ASSERT(bio);
+ q_BIO_set_app_data(bio, this);
+
+ return true;
+}
+
+void DtlsState::reset()
+{
+ tlsConnection.reset();
+ tlsContext.reset();
+}
+
+bool DtlsState::initTls(QDtlsBasePrivate *dtlsBase)
+{
+ if (tlsContext)
+ return true;
+
+ if (!QSslSocket::supportsSsl())
+ return false;
+
+ if (!initCtxAndConnection(dtlsBase))
+ return false;
+
+ if (!initBIO(dtlsBase)) {
+ tlsConnection.reset();
+ tlsContext.reset();
+ return false;
+ }
+
+ return true;
+}
+
+static QString msgFunctionFailed(const char *function)
+{
+ //: %1: Some function
+ return QDtls::tr("%1 failed").arg(QLatin1StringView(function));
+}
+
+bool DtlsState::initCtxAndConnection(QDtlsBasePrivate *dtlsBase)
+{
+ Q_ASSERT(dtlsBase);
+ Q_ASSERT(QSslSocket::supportsSsl());
+
+ if (dtlsBase->mode == QSslSocket::UnencryptedMode) {
+ dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
+ QDtls::tr("Invalid SslMode, SslServerMode or SslClientMode expected"));
+ return false;
+ }
+
+ if (!QDtlsBasePrivate::isDtlsProtocol(dtlsBase->dtlsConfiguration.protocol())) {
+ dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
+ QDtls::tr("Invalid protocol version, DTLS protocol expected"));
+ return false;
+ }
+
+ const bool rootsOnDemand = QTlsBackend::rootLoadingOnDemandAllowed(dtlsBase->dtlsConfiguration);
+ TlsContext newContext(QSslContext::sharedFromConfiguration(dtlsBase->mode, dtlsBase->dtlsConfiguration,
+ rootsOnDemand));
+
+ if (newContext->error() != QSslError::NoError) {
+ dtlsBase->setDtlsError(QDtlsError::TlsInitializationError, newContext->errorString());
+ return false;
+ }
+
+ TlsConnection newConnection(newContext->createSsl(), dtlsutil::delete_connection);
+ if (!newConnection.data()) {
+ dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
+ msgFunctionFailed("SSL_new"));
+ return false;
+ }
+
+ const int set = q_SSL_set_ex_data(newConnection.data(),
+ QTlsBackendOpenSSL::s_indexForSSLExtraData,
+ this);
+
+ if (set != 1 && dtlsBase->dtlsConfiguration.peerVerifyMode() != QSslSocket::VerifyNone) {
+ dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
+ msgFunctionFailed("SSL_set_ex_data"));
+ return false;
+ }
+
+ if (dtlsBase->mode == QSslSocket::SslServerMode) {
+ if (dtlsBase->dtlsConfiguration.dtlsCookieVerificationEnabled())
+ q_SSL_set_options(newConnection.data(), SSL_OP_COOKIE_EXCHANGE);
+ q_SSL_set_psk_server_callback(newConnection.data(), dtlscallbacks::q_PSK_server_callback);
+ } else {
+ q_SSL_set_psk_client_callback(newConnection.data(), dtlscallbacks::q_PSK_client_callback);
+ }
+
+ tlsContext.swap(newContext);
+ tlsConnection.swap(newConnection);
+
+ return true;
+}
+
+bool DtlsState::initBIO(QDtlsBasePrivate *dtlsBase)
+{
+ Q_ASSERT(dtlsBase);
+ Q_ASSERT(tlsContext && tlsConnection);
+
+ BioMethod customMethod(q_BIO_meth_new(BIO_TYPE_DGRAM, dtlsbio::qdtlsMethodName),
+ dtlsutil::delete_bio_method);
+ if (!customMethod.data()) {
+ dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
+ msgFunctionFailed("BIO_meth_new"));
+ return false;
+ }
+
+ BIO_METHOD *biom = customMethod.data();
+ q_BIO_meth_set_create(biom, dtlsbio::q_dgram_create);
+ q_BIO_meth_set_destroy(biom, dtlsbio::q_dgram_destroy);
+ q_BIO_meth_set_read(biom, dtlsbio::q_dgram_read);
+ q_BIO_meth_set_write(biom, dtlsbio::q_dgram_write);
+ q_BIO_meth_set_puts(biom, dtlsbio::q_dgram_puts);
+ q_BIO_meth_set_ctrl(biom, dtlsbio::q_dgram_ctrl);
+
+ BIO *bio = q_BIO_new(biom);
+ if (!bio) {
+ dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
+ msgFunctionFailed("BIO_new"));
+ return false;
+ }
+
+ q_SSL_set_bio(tlsConnection.data(), bio, bio);
+
+ bioMethod.swap(customMethod);
+
+ return true;
+}
+
+void DtlsState::setLinkMtu(QDtlsBasePrivate *dtlsBase)
+{
+ Q_ASSERT(dtlsBase);
+ Q_ASSERT(udpSocket);
+ Q_ASSERT(tlsConnection.data());
+
+ long mtu = dtlsBase->mtuHint;
+ if (!mtu) {
+ // If the underlying QUdpSocket was connected, getsockopt with
+ // IP_MTU/IP6_MTU can give us some hint:
+ bool optionFound = false;
+ if (udpSocket->state() == QAbstractSocket::ConnectedState) {
+ const QVariant val(udpSocket->socketOption(QAbstractSocket::PathMtuSocketOption));
+ if (val.isValid() && val.canConvert<int>())
+ mtu = val.toInt(&optionFound);
+ }
+
+ if (!optionFound || mtu <= 0) {
+ // OK, our own initial guess.
+ mtu = long(dtlsutil::MtuGuess::defaultMtu);
+ }
+ }
+
+ // For now, we disable this option.
+ q_SSL_set_options(tlsConnection.data(), SSL_OP_NO_QUERY_MTU);
+
+ q_DTLS_set_link_mtu(tlsConnection.data(), mtu);
+}
+
+} // namespace dtlsopenssl
+
+QDtlsClientVerifierOpenSSL::QDtlsClientVerifierOpenSSL()
+ : QDtlsBasePrivate(QSslSocket::SslServerMode, dtlsutil::fallbackSecret())
+{
+}
+
+bool QDtlsClientVerifierOpenSSL::verifyClient(QUdpSocket *socket, const QByteArray &dgram,
+ const QHostAddress &address, quint16 port)
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(dgram.size());
+ Q_ASSERT(!address.isNull());
+ Q_ASSERT(port);
+
+ clearDtlsError();
+ verifiedClientHello.clear();
+
+ if (!dtls.init(this, socket, address, port, dgram))
+ return false;
+
+ dtls.secret = secret;
+ dtls.hashAlgorithm = hashAlgorithm;
+
+ Q_ASSERT(dtls.tlsConnection.data());
+ QSharedPointer<BIO_ADDR> peer(q_BIO_ADDR_new(), dtlsutil::delete_BIO_ADDR);
+ if (!peer.data()) {
+ setDtlsError(QDtlsError::TlsInitializationError,
+ QDtlsClientVerifier::tr("BIO_ADDR_new failed, ignoring client hello"));
+ return false;
+ }
+
+ const int ret = q_DTLSv1_listen(dtls.tlsConnection.data(), peer.data());
+ if (ret < 0) {
+ // Since 1.1 - it's a fatal error (not so in 1.0.2 for non-blocking socket)
+ setDtlsError(QDtlsError::TlsFatalError, QTlsBackendOpenSSL::getErrorsFromOpenSsl());
+ return false;
+ }
+
+ if (ret > 0) {
+ verifiedClientHello = dgram;
+ return true;
+ }
+
+ return false;
+}
+
+QByteArray QDtlsClientVerifierOpenSSL::verifiedHello() const
+{
+ return verifiedClientHello;
+}
+
+void QDtlsPrivateOpenSSL::TimeoutHandler::start(int hintMs)
+{
+ Q_ASSERT(timerId == -1);
+ timerId = startTimer(hintMs > 0 ? hintMs : timeoutMs, Qt::PreciseTimer);
+}
+
+void QDtlsPrivateOpenSSL::TimeoutHandler::doubleTimeout()
+{
+ if (timeoutMs * 2 < 60000)
+ timeoutMs *= 2;
+ else
+ timeoutMs = 60000;
+}
+
+void QDtlsPrivateOpenSSL::TimeoutHandler::stop()
+{
+ if (timerId != -1) {
+ killTimer(timerId);
+ timerId = -1;
+ }
+}
+
+void QDtlsPrivateOpenSSL::TimeoutHandler::timerEvent(QTimerEvent *event)
+{
+ Q_UNUSED(event);
+ Q_ASSERT(timerId != -1);
+
+ killTimer(timerId);
+ timerId = -1;
+
+ Q_ASSERT(dtlsConnection);
+ dtlsConnection->reportTimeout();
+}
+
+QDtlsPrivateOpenSSL::QDtlsPrivateOpenSSL(QDtls *qObject, QSslSocket::SslMode side)
+ : QDtlsBasePrivate(side, dtlsutil::fallbackSecret()), q(qObject)
+{
+ Q_ASSERT(qObject);
+
+ dtls.dtlsPrivate = this;
+}
+
+QSslSocket::SslMode QDtlsPrivateOpenSSL::cryptographMode() const
+{
+ return mode;
+}
+
+void QDtlsPrivateOpenSSL::setPeer(const QHostAddress &addr, quint16 port, const QString &name)
+{
+ remoteAddress = addr;
+ remotePort = port;
+ peerVfyName = name;
+}
+
+QHostAddress QDtlsPrivateOpenSSL::peerAddress() const
+{
+ return remoteAddress;
+}
+
+quint16 QDtlsPrivateOpenSSL::peerPort() const
+{
+ return remotePort;
+}
+
+void QDtlsPrivateOpenSSL::setPeerVerificationName(const QString &name)
+{
+ peerVfyName = name;
+}
+
+QString QDtlsPrivateOpenSSL::peerVerificationName() const
+{
+ return peerVfyName;
+}
+
+void QDtlsPrivateOpenSSL::setDtlsMtuHint(quint16 mtu)
+{
+ mtuHint = mtu;
+}
+
+quint16 QDtlsPrivateOpenSSL::dtlsMtuHint() const
+{
+ return mtuHint;
+}
+
+QDtls::HandshakeState QDtlsPrivateOpenSSL::state() const
+{
+ return handshakeState;
+}
+
+bool QDtlsPrivateOpenSSL::isConnectionEncrypted() const
+{
+ return connectionEncrypted;
+}
+
+bool QDtlsPrivateOpenSSL::startHandshake(QUdpSocket *socket, const QByteArray &dgram)
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(handshakeState == QDtls::HandshakeNotStarted);
+
+ clearDtlsError();
+ connectionEncrypted = false;
+
+ if (!dtls.init(this, socket, remoteAddress, remotePort, dgram))
+ return false;
+
+ if (mode == QSslSocket::SslServerMode && dtlsConfiguration.dtlsCookieVerificationEnabled()) {
+ dtls.secret = secret;
+ dtls.hashAlgorithm = hashAlgorithm;
+ // Let's prepare the state machine so that message sequence 1 does not
+ // surprise DTLS/OpenSSL (such a message would be disregarded as
+ // 'stale or future' in SSL_accept otherwise):
+ int result = 0;
+ QSharedPointer<BIO_ADDR> peer(q_BIO_ADDR_new(), dtlsutil::delete_BIO_ADDR);
+ if (!peer.data()) {
+ setDtlsError(QDtlsError::TlsInitializationError,
+ QDtls::tr("BIO_ADD_new failed, cannot start handshake"));
+ return false;
+ }
+
+ // If it's an invalid/unexpected ClientHello, we don't want to send
+ // VerifyClientRequest - it's a job of QDtlsClientVerifier - so we
+ // suppress any attempts to write into socket:
+ dtls.writeSuppressed = true;
+ result = q_DTLSv1_listen(dtls.tlsConnection.data(), peer.data());
+ dtls.writeSuppressed = false;
+
+ if (result <= 0) {
+ setDtlsError(QDtlsError::TlsFatalError,
+ QDtls::tr("Cannot start the handshake, verified client hello expected"));
+ dtls.reset();
+ return false;
+ }
+ }
+
+ handshakeState = QDtls::HandshakeInProgress;
+ opensslErrors.clear();
+ tlsErrors.clear();
+
+ return continueHandshake(socket, dgram);
+}
+
+bool QDtlsPrivateOpenSSL::continueHandshake(QUdpSocket *socket, const QByteArray &dgram)
+{
+ Q_ASSERT(socket);
+
+ Q_ASSERT(handshakeState == QDtls::HandshakeInProgress);
+
+ clearDtlsError();
+
+ if (timeoutHandler.data())
+ timeoutHandler->stop();
+
+ if (!dtls.init(this, socket, remoteAddress, remotePort, dgram))
+ return false;
+
+ dtls.x509Errors.clear();
+
+ int result = 0;
+ if (mode == QSslSocket::SslServerMode)
+ result = q_SSL_accept(dtls.tlsConnection.data());
+ else
+ result = q_SSL_connect(dtls.tlsConnection.data());
+
+ // DTLSTODO: Investigate/test if it makes sense - QSslSocket can emit
+ // peerVerifyError at this point (and thus potentially client code
+ // will close the underlying TCP connection immediately), but we are using
+ // QUdpSocket, no connection to close, our verification callback returns 1
+ // (verified OK) and this probably means OpenSSL has already sent a reply
+ // to the server's hello/certificate.
+
+ opensslErrors << dtls.x509Errors;
+
+ if (result <= 0) {
+ const auto code = q_SSL_get_error(dtls.tlsConnection.data(), result);
+ switch (code) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ // DTLSTODO: to be tested - in principle, if it was the first call to
+ // continueHandshake and server for some reason discards the client
+ // hello message (even the verified one) - our 'this' will probably
+ // forever stay in this strange InProgress state? (the client
+ // will dully re-transmit the same hello and we discard it again?)
+ // SSL_get_state can provide more information about state
+ // machine and we can switch to NotStarted (since we have not
+ // replied with our hello ...)
+ if (!timeoutHandler.data()) {
+ timeoutHandler.reset(new TimeoutHandler);
+ timeoutHandler->dtlsConnection = this;
+ } else {
+ // Back to 1s.
+ timeoutHandler->resetTimeout();
+ }
+
+ timeoutHandler->start();
+
+ return true; // The handshake is not yet complete.
+ default:
+ storePeerCertificates();
+ setDtlsError(QDtlsError::TlsFatalError,
+ QTlsBackendOpenSSL::msgErrorsDuringHandshake());
+ dtls.reset();
+ handshakeState = QDtls::HandshakeNotStarted;
+ return false;
+ }
+ }
+
+ storePeerCertificates();
+ fetchNegotiatedParameters();
+
+ const bool doVerifyPeer = dtlsConfiguration.peerVerifyMode() == QSslSocket::VerifyPeer
+ || (dtlsConfiguration.peerVerifyMode() == QSslSocket::AutoVerifyPeer
+ && mode == QSslSocket::SslClientMode);
+
+ if (!doVerifyPeer || verifyPeer() || tlsErrorsWereIgnored()) {
+ connectionEncrypted = true;
+ handshakeState = QDtls::HandshakeComplete;
+ return true;
+ }
+
+ setDtlsError(QDtlsError::PeerVerificationError, QDtls::tr("Peer verification failed"));
+ handshakeState = QDtls::PeerVerificationFailed;
+ return false;
+}
+
+
+bool QDtlsPrivateOpenSSL::handleTimeout(QUdpSocket *socket)
+{
+ Q_ASSERT(socket);
+
+ Q_ASSERT(timeoutHandler.data());
+ Q_ASSERT(dtls.tlsConnection.data());
+
+ clearDtlsError();
+
+ dtls.udpSocket = socket;
+
+ if (q_DTLSv1_handle_timeout(dtls.tlsConnection.data()) > 0) {
+ timeoutHandler->doubleTimeout();
+ timeoutHandler->start();
+ } else {
+ timeoutHandler->start(dtlsutil::next_timeoutMs(dtls.tlsConnection.data()));
+ }
+
+ return true;
+}
+
+bool QDtlsPrivateOpenSSL::resumeHandshake(QUdpSocket *socket)
+{
+ Q_UNUSED(socket);
+ Q_ASSERT(socket);
+ Q_ASSERT(handshakeState == QDtls::PeerVerificationFailed);
+
+ clearDtlsError();
+
+ if (tlsErrorsWereIgnored()) {
+ handshakeState = QDtls::HandshakeComplete;
+ connectionEncrypted = true;
+ tlsErrors.clear();
+ tlsErrorsToIgnore.clear();
+ return true;
+ }
+
+ return false;
+}
+
+void QDtlsPrivateOpenSSL::abortHandshake(QUdpSocket *socket)
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(handshakeState == QDtls::PeerVerificationFailed
+ || handshakeState == QDtls::HandshakeInProgress);
+
+ clearDtlsError();
+
+ if (handshakeState == QDtls::PeerVerificationFailed) {
+ // Yes, while peer verification failed, we were actually encrypted.
+ // Let's play it nice - inform our peer about connection shut down.
+ sendShutdownAlert(socket);
+ } else {
+ resetDtls();
+ }
+}
+
+void QDtlsPrivateOpenSSL::sendShutdownAlert(QUdpSocket *socket)
+{
+ Q_ASSERT(socket);
+
+ clearDtlsError();
+
+ if (connectionEncrypted && !connectionWasShutdown) {
+ dtls.udpSocket = socket;
+ Q_ASSERT(dtls.tlsConnection.data());
+ q_SSL_shutdown(dtls.tlsConnection.data());
+ }
+
+ resetDtls();
+}
+
+QList<QSslError> QDtlsPrivateOpenSSL::peerVerificationErrors() const
+{
+ return tlsErrors;
+}
+
+void QDtlsPrivateOpenSSL::ignoreVerificationErrors(const QList<QSslError> &errorsToIgnore)
+{
+ tlsErrorsToIgnore = errorsToIgnore;
+}
+
+QSslCipher QDtlsPrivateOpenSSL::dtlsSessionCipher() const
+{
+ return sessionCipher;
+}
+
+QSsl::SslProtocol QDtlsPrivateOpenSSL::dtlsSessionProtocol() const
+{
+ return sessionProtocol;
+}
+
+qint64 QDtlsPrivateOpenSSL::writeDatagramEncrypted(QUdpSocket *socket,
+ const QByteArray &dgram)
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(dtls.tlsConnection.data());
+ Q_ASSERT(connectionEncrypted);
+
+ clearDtlsError();
+
+ dtls.udpSocket = socket;
+ const int written = q_SSL_write(dtls.tlsConnection.data(),
+ dgram.constData(), dgram.size());
+ if (written > 0)
+ return written;
+
+ const unsigned long errorCode = q_ERR_get_error();
+ if (!dgram.size() && errorCode == SSL_ERROR_NONE) {
+ // With OpenSSL <= 1.1 this can happen. For example, DTLS client
+ // tries to reconnect (while re-using the same address/port) -
+ // DTLS server drops a message with unexpected epoch but says - no
+ // error. We leave to client code to resolve such problems until
+ // OpenSSL provides something better.
+ return 0;
+ }
+
+ switch (errorCode) {
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ // We do not set any error/description ... a user can probably re-try
+ // sending a datagram.
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ connectionWasShutdown = true;
+ setDtlsError(QDtlsError::TlsFatalError, QDtls::tr("The DTLS connection has been closed"));
+ handshakeState = QDtls::HandshakeNotStarted;
+ dtls.reset();
+ break;
+ case SSL_ERROR_SYSCALL:
+ case SSL_ERROR_SSL:
+ default:
+ // DTLSTODO: we don't know yet what to do. Tests needed - probably,
+ // some errors can be just ignored (it's UDP, not TCP after all).
+ // Unlike QSslSocket we do not abort though.
+ QString description(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
+ if (socket->error() != QAbstractSocket::UnknownSocketError && description.isEmpty()) {
+ setDtlsError(QDtlsError::UnderlyingSocketError, socket->errorString());
+ } else {
+ setDtlsError(QDtlsError::TlsFatalError,
+ QDtls::tr("Error while writing: %1").arg(description));
+ }
+ }
+
+ return -1;
+}
+
+QByteArray QDtlsPrivateOpenSSL::decryptDatagram(QUdpSocket *socket, const QByteArray &tlsdgram)
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(tlsdgram.size());
+
+ Q_ASSERT(dtls.tlsConnection.data());
+ Q_ASSERT(connectionEncrypted);
+
+ dtls.dgram = tlsdgram;
+ dtls.udpSocket = socket;
+
+ clearDtlsError();
+
+ QByteArray dgram;
+ dgram.resize(tlsdgram.size());
+ const int read = q_SSL_read(dtls.tlsConnection.data(), dgram.data(),
+ dgram.size());
+
+ if (read > 0) {
+ dgram.resize(read);
+ return dgram;
+ }
+
+ dgram.clear();
+ unsigned long errorCode = q_ERR_get_error();
+ if (errorCode == SSL_ERROR_NONE) {
+ const int shutdown = q_SSL_get_shutdown(dtls.tlsConnection.data());
+ if (shutdown & SSL_RECEIVED_SHUTDOWN)
+ errorCode = SSL_ERROR_ZERO_RETURN;
+ else
+ return dgram;
+ }
+
+ switch (errorCode) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ return dgram;
+ case SSL_ERROR_ZERO_RETURN:
+ // "The connection was shut down cleanly" ... hmm, whatever,
+ // needs testing (DTLSTODO).
+ connectionWasShutdown = true;
+ setDtlsError(QDtlsError::RemoteClosedConnectionError,
+ QDtls::tr("The DTLS connection has been shutdown"));
+ dtls.reset();
+ connectionEncrypted = false;
+ handshakeState = QDtls::HandshakeNotStarted;
+ return dgram;
+ case SSL_ERROR_SYSCALL: // some IO error
+ case SSL_ERROR_SSL: // error in the SSL library
+ // DTLSTODO: Apparently, some errors can be ignored, for example,
+ // ECONNRESET etc. This all needs a lot of testing!!!
+ default:
+ setDtlsError(QDtlsError::TlsNonFatalError,
+ QDtls::tr("Error while reading: %1")
+ .arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
+ return dgram;
+ }
+}
+
+unsigned QDtlsPrivateOpenSSL::pskClientCallback(const char *hint, char *identity,
+ unsigned max_identity_len,
+ unsigned char *psk,
+ unsigned max_psk_len)
+{
+ // The code below is taken (with some modifications) from qsslsocket_openssl
+ // - alas, we cannot simply re-use it, it's in QSslSocketPrivate.
+ {
+ QSslPreSharedKeyAuthenticator authenticator;
+ // Fill in some read-only fields (for client code)
+ if (hint) {
+ identityHint.clear();
+ identityHint.append(hint);
+ }
+
+ QTlsBackend::setupClientPskAuth(&authenticator, hint ? identityHint.constData() : nullptr,
+ hint ? int(std::strlen(hint)) : 0, max_identity_len, max_psk_len);
+ pskAuthenticator.swap(authenticator);
+ }
+
+ // Let the client provide the remaining bits...
+ emit q->pskRequired(&pskAuthenticator);
+
+ // No PSK set? Return now to make the handshake fail
+ if (pskAuthenticator.preSharedKey().isEmpty())
+ return 0;
+
+ // Copy data back into OpenSSL
+ 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().size(),
+ pskAuthenticator.maximumPreSharedKeyLength());
+ std::memcpy(psk, pskAuthenticator.preSharedKey().constData(), pskLength);
+
+ return pskLength;
+}
+
+unsigned QDtlsPrivateOpenSSL::pskServerCallback(const char *identity, unsigned char *psk,
+ unsigned max_psk_len)
+{
+ {
+ QSslPreSharedKeyAuthenticator authenticator;
+ // Fill in some read-only fields (for the user)
+ QTlsBackend::setupServerPskAuth(&authenticator, identity, dtlsConfiguration.preSharedKeyIdentityHint(),
+ max_psk_len);
+ pskAuthenticator.swap(authenticator);
+ }
+
+ // Let the client provide the remaining bits...
+ emit q->pskRequired(&pskAuthenticator);
+
+ // No PSK set? Return now to make the handshake fail
+ if (pskAuthenticator.preSharedKey().isEmpty())
+ return 0;
+
+ // Copy data back into OpenSSL
+ const int pskLength = qMin(pskAuthenticator.preSharedKey().size(),
+ pskAuthenticator.maximumPreSharedKeyLength());
+
+ std::memcpy(psk, pskAuthenticator.preSharedKey().constData(), pskLength);
+
+ return pskLength;
+}
+
+bool QDtlsPrivateOpenSSL::verifyPeer()
+{
+ QList<QSslError> errors;
+
+ // Check the whole chain for blacklisting (including root, as we check for
+ // subjectInfo and issuer)
+ const auto &peerCertificateChain = dtlsConfiguration.peerCertificateChain();
+ for (const QSslCertificate &cert : peerCertificateChain) {
+ if (QSslCertificatePrivate::isBlacklisted(cert))
+ errors << QSslError(QSslError::CertificateBlacklisted, cert);
+ }
+
+ const auto peerCertificate = dtlsConfiguration.peerCertificate();
+ if (peerCertificate.isNull()) {
+ errors << QSslError(QSslError::NoPeerCertificate);
+ } else if (mode == QSslSocket::SslClientMode) {
+ // Check the peer certificate itself. First try the subject's common name
+ // (CN) as a wildcard, then try all alternate subject name DNS entries the
+ // same way.
+
+ // QSslSocket has a rather twisted logic: if verificationPeerName
+ // is empty, we call QAbstractSocket::peerName(), which returns
+ // either peerName (can be set by setPeerName) or host name
+ // (can be set as a result of connectToHost).
+ QString name = peerVfyName;
+ if (name.isEmpty()) {
+ Q_ASSERT(dtls.udpSocket);
+ name = dtls.udpSocket->peerName();
+ }
+
+ if (!QTlsPrivate::TlsCryptograph::isMatchingHostname(peerCertificate, name))
+ errors << QSslError(QSslError::HostNameMismatch, peerCertificate);
+ }
+
+ // Translate errors from the error list into QSslErrors
+ using CertClass = QTlsPrivate::X509CertificateOpenSSL;
+ errors.reserve(errors.size() + opensslErrors.size());
+ for (const auto &error : std::as_const(opensslErrors)) {
+ const auto value = peerCertificateChain.value(error.depth);
+ errors << CertClass::openSSLErrorToQSslError(error.code, value);
+ }
+
+ tlsErrors = errors;
+ return tlsErrors.isEmpty();
+}
+
+void QDtlsPrivateOpenSSL::storePeerCertificates()
+{
+ Q_ASSERT(dtls.tlsConnection.data());
+ // Store the peer certificate and chain. For clients, the peer certificate
+ // chain includes the peer certificate; for servers, it doesn't. Both the
+ // peer certificate and the chain may be empty if the peer didn't present
+ // any certificate.
+ X509 *x509 = q_SSL_get_peer_certificate(dtls.tlsConnection.data());
+ const auto peerCertificate = QTlsPrivate::X509CertificateOpenSSL::certificateFromX509(x509);
+ QTlsBackend::storePeerCertificate(dtlsConfiguration, peerCertificate);
+ q_X509_free(x509);
+
+ auto peerCertificateChain = dtlsConfiguration.peerCertificateChain();
+ if (peerCertificateChain.isEmpty()) {
+ auto stack = q_SSL_get_peer_cert_chain(dtls.tlsConnection.data());
+ peerCertificateChain = QTlsPrivate::X509CertificateOpenSSL::stackOfX509ToQSslCertificates(stack);
+ if (!peerCertificate.isNull() && mode == QSslSocket::SslServerMode)
+ peerCertificateChain.prepend(peerCertificate);
+ QTlsBackend::storePeerCertificateChain(dtlsConfiguration, peerCertificateChain);
+ }
+}
+
+bool QDtlsPrivateOpenSSL::tlsErrorsWereIgnored() const
+{
+ // check whether the errors we got are all in the list of expected errors
+ // (applies only if the method QDtlsConnection::ignoreTlsErrors(const
+ // QList<QSslError> &errors) was called)
+ for (const QSslError &error : tlsErrors) {
+ if (!tlsErrorsToIgnore.contains(error))
+ return false;
+ }
+
+ return !tlsErrorsToIgnore.empty();
+}
+
+void QDtlsPrivateOpenSSL::fetchNegotiatedParameters()
+{
+ Q_ASSERT(dtls.tlsConnection.data());
+
+ if (const SSL_CIPHER *cipher = q_SSL_get_current_cipher(dtls.tlsConnection.data()))
+ sessionCipher = QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(cipher);
+ else
+ sessionCipher = {};
+
+ // Note: cipher's protocol version will be reported as either TLS 1.0 or
+ // TLS 1.2, that's how it's set by OpenSSL (and that's what they are?).
+
+ switch (q_SSL_version(dtls.tlsConnection.data())) {
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ case DTLS1_VERSION:
+ sessionProtocol = QSsl::DtlsV1_0;
+ break;
+QT_WARNING_POP
+ case DTLS1_2_VERSION:
+ sessionProtocol = QSsl::DtlsV1_2;
+ break;
+ default:
+ qCWarning(lcTlsBackend, "unknown protocol version");
+ sessionProtocol = QSsl::UnknownProtocol;
+ }
+}
+
+void QDtlsPrivateOpenSSL::reportTimeout()
+{
+ emit q->handshakeTimeout();
+}
+
+void QDtlsPrivateOpenSSL::resetDtls()
+{
+ dtls.reset();
+ connectionEncrypted = false;
+ tlsErrors.clear();
+ tlsErrorsToIgnore.clear();
+ QTlsBackend::clearPeerCertificates(dtlsConfiguration);
+ connectionWasShutdown = false;
+ handshakeState = QDtls::HandshakeNotStarted;
+ sessionCipher = {};
+ sessionProtocol = QSsl::UnknownProtocol;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/tls/openssl/qdtls_openssl_p.h b/src/plugins/tls/openssl/qdtls_openssl_p.h
new file mode 100644
index 0000000000..44be86f1ed
--- /dev/null
+++ b/src/plugins/tls/openssl/qdtls_openssl_p.h
@@ -0,0 +1,214 @@
+// 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
+
+#ifndef QDTLS_OPENSSL_P_H
+#define QDTLS_OPENSSL_P_H
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include "qsslcontext_openssl_p.h"
+#include "qtlsbackend_openssl_p.h"
+#include "qtls_openssl_p.h"
+#include "qopenssl_p.h"
+
+#include "../shared/qdtls_base_p.h"
+
+#include <QtNetwork/private/qdtls_p.h>
+
+#include <QtNetwork/qsslpresharedkeyauthenticator.h>
+#include <QtNetwork/qhostaddress.h>
+
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qlist.h>
+
+#include <openssl/ossl_typ.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_REQUIRE_CONFIG(openssl);
+QT_REQUIRE_CONFIG(dtls);
+
+QT_BEGIN_NAMESPACE
+
+class QDtlsPrivateOpenSSL;
+class QDtlsBasePrivate;
+class QUdpSocket;
+
+namespace dtlsopenssl
+{
+
+class DtlsState
+{
+public:
+ // Note, bioMethod _must_ outlive BIOs it was used to create. Thus
+ // the order of declarations here matters.
+ using BioMethod = QSharedPointer<BIO_METHOD>;
+ BioMethod bioMethod;
+
+ using TlsContext = std::shared_ptr<QSslContext>;
+ TlsContext tlsContext;
+
+ using TlsConnection = QSharedPointer<SSL>;
+ TlsConnection tlsConnection;
+
+ QByteArray dgram;
+
+ QHostAddress remoteAddress;
+ quint16 remotePort = 0;
+
+ QList<QSslErrorEntry> x509Errors;
+
+ long peeking = false;
+ QUdpSocket *udpSocket = nullptr;
+ bool writeSuppressed = false;
+
+ bool init(QDtlsBasePrivate *dtlsBase, QUdpSocket *socket,
+ const QHostAddress &remote, quint16 port,
+ const QByteArray &receivedMessage);
+
+ void reset();
+
+ QDtlsPrivateOpenSSL *dtlsPrivate = nullptr;
+ QByteArray secret;
+
+#ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
+ QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha1;
+#else
+ QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha256;
+#endif
+
+private:
+
+ bool initTls(QDtlsBasePrivate *dtlsBase);
+ bool initCtxAndConnection(QDtlsBasePrivate *dtlsBase);
+ bool initBIO(QDtlsBasePrivate *dtlsBase);
+ void setLinkMtu(QDtlsBasePrivate *dtlsBase);
+};
+
+} // namespace dtlsopenssl
+
+// The trick with 'right' ancestor in the tree overriding (only once) some shared
+// virtual functions is intentional. Too bad MSVC warns me about ... exactly the
+// feature of C++ that I want to use.
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_MSVC(4250)
+
+class QDtlsClientVerifierOpenSSL : public QTlsPrivate::DtlsCookieVerifier, public QDtlsBasePrivate
+{
+public:
+ QDtlsClientVerifierOpenSSL();
+
+ bool verifyClient(QUdpSocket *socket, const QByteArray &dgram,
+ const QHostAddress &address, quint16 port) override;
+ QByteArray verifiedHello() const override;
+
+private:
+ dtlsopenssl::DtlsState dtls;
+ QByteArray verifiedClientHello;
+};
+
+class QDtlsPrivateOpenSSL : public QTlsPrivate::DtlsCryptograph, public QDtlsBasePrivate
+{
+public:
+
+ QDtlsPrivateOpenSSL(QDtls *qObject, QSslSocket::SslMode mode);
+
+private:
+
+ QSslSocket::SslMode cryptographMode() const override;
+ void setPeer(const QHostAddress &addr, quint16 port, const QString &name) override;
+ QHostAddress peerAddress() const override;
+ quint16 peerPort() const override;
+ void setPeerVerificationName(const QString &name) override;
+ QString peerVerificationName() const override;
+
+ virtual void setDtlsMtuHint(quint16 mtu) override;
+ virtual quint16 dtlsMtuHint() const override;
+
+ virtual QDtls::HandshakeState state() const override;
+ virtual bool isConnectionEncrypted() const override;
+
+ bool startHandshake(QUdpSocket *socket, const QByteArray &datagram) override;
+ bool continueHandshake(QUdpSocket *socket, const QByteArray &datagram) override;
+ bool resumeHandshake(QUdpSocket *socket) override;
+ void abortHandshake(QUdpSocket *socket) override;
+ bool handleTimeout(QUdpSocket *socket) override;
+ void sendShutdownAlert(QUdpSocket *socket) override;
+
+ QList<QSslError> peerVerificationErrors() const override;
+ void ignoreVerificationErrors(const QList<QSslError> &errorsToIgnore) override;
+
+ QSslCipher dtlsSessionCipher() const override;
+ QSsl::SslProtocol dtlsSessionProtocol() const override;
+
+ qint64 writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &datagram) override;
+ QByteArray decryptDatagram(QUdpSocket *socket, const QByteArray &tlsdgram) override;
+
+public:
+ unsigned pskClientCallback(const char *hint, char *identity, unsigned max_identity_len,
+ unsigned char *psk, unsigned max_psk_len);
+ unsigned pskServerCallback(const char *identity, unsigned char *psk,
+ unsigned max_psk_len);
+
+private:
+
+ bool verifyPeer();
+ void storePeerCertificates();
+ bool tlsErrorsWereIgnored() const;
+ void fetchNegotiatedParameters();
+ void reportTimeout();
+ void resetDtls();
+
+ QList<QSslErrorEntry> opensslErrors;
+ dtlsopenssl::DtlsState dtls;
+
+ // We have to externally handle timeouts since we have non-blocking
+ // sockets and OpenSSL(DTLS) with non-blocking UDP sockets does not
+ // know if a timeout has occurred.
+ struct TimeoutHandler : QObject
+ {
+ TimeoutHandler() = default;
+
+ void start(int hintMs = 0);
+ void doubleTimeout();
+ void resetTimeout() {timeoutMs = 1000;}
+ void stop();
+ void timerEvent(QTimerEvent *event) override;
+
+ int timerId = -1;
+ int timeoutMs = 1000;
+
+ QDtlsPrivateOpenSSL *dtlsConnection = nullptr;
+ };
+
+ QDtls *q = nullptr;
+ QDtls::HandshakeState handshakeState = QDtls::HandshakeNotStarted;
+
+ QList<QSslError> tlsErrors;
+ QList<QSslError> tlsErrorsToIgnore;
+ bool connectionEncrypted = false;
+ // We will initialize it 'lazily', just in case somebody wants to move
+ // QDtls to another thread.
+ QScopedPointer<TimeoutHandler> timeoutHandler;
+ bool connectionWasShutdown = false;
+ QSslPreSharedKeyAuthenticator pskAuthenticator;
+ QByteArray identityHint;
+};
+
+QT_WARNING_POP // C4250
+
+QT_END_NAMESPACE
+
+#endif // QDTLS_OPENSSL_P_H
diff --git a/src/plugins/tls/openssl/qopenssl_p.h b/src/plugins/tls/openssl/qopenssl_p.h
new file mode 100644
index 0000000000..370b974630
--- /dev/null
+++ b/src/plugins/tls/openssl/qopenssl_p.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2017 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
+
+/****************************************************************************
+**
+** In addition, as a special exception, the copyright holders listed above give
+** permission to link the code of its release of Qt with the OpenSSL project's
+** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
+** same license as the original version), and distribute the linked executables.
+**
+** You must comply with the GNU General Public License version 2 in all
+** respects for all of the code used other than the "OpenSSL" code. If you
+** modify this file, you may extend this exception to your version of the file,
+** but you are not obligated to do so. If you do not wish to do so, delete
+** this exception statement from your version of this file.
+**
+****************************************************************************/
+
+#ifndef QSSLSOCKET_OPENSSL_P_H
+#define QSSLSOCKET_OPENSSL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include <QtNetwork/private/qsslsocket_p.h>
+
+#include <QtNetwork/qsslcipher.h>
+
+#ifdef Q_OS_WIN
+#include <qt_windows.h>
+#if defined(OCSP_RESPONSE)
+#undef OCSP_RESPONSE
+#endif
+#if defined(X509_NAME)
+#undef X509_NAME
+#endif
+#endif // Q_OS_WIN
+
+// This file is included in several *.cpp files and provides different
+// openssl declarations where they are needed.
+#include <openssl/asn1.h>
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/pkcs7.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/dsa.h>
+#include <openssl/rsa.h>
+#include <openssl/crypto.h>
+#include <openssl/tls1.h>
+#include <openssl/dh.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QSslErrorEntry {
+ int code = 0;
+ int depth = 0;
+};
+
+Q_DECLARE_TYPEINFO(QSslErrorEntry, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/tls/openssl/qsslcontext_openssl.cpp b/src/plugins/tls/openssl/qsslcontext_openssl.cpp
new file mode 100644
index 0000000000..75c192bd01
--- /dev/null
+++ b/src/plugins/tls/openssl/qsslcontext_openssl.cpp
@@ -0,0 +1,815 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+// Copyright (C) 2014 Governikus GmbH & Co. KG.
+// Copyright (C) 2016 Richard J. Moore <rich@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtNetwork/qsslsocket.h>
+#include <QtNetwork/qssldiffiehellmanparameters.h>
+
+#include "qsslsocket_openssl_symbols_p.h"
+#include "qsslcontext_openssl_p.h"
+#include "qtlsbackend_openssl_p.h"
+#include "qtlskey_openssl_p.h"
+#include "qopenssl_p.h"
+
+#include <QtNetwork/private/qssl_p.h>
+#include <QtNetwork/private/qsslsocket_p.h>
+#include <QtNetwork/private/qtlsbackend_p.h>
+
+#include <QtNetwork/private/qssldiffiehellmanparameters_p.h>
+
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(bool, forceSecurityLevel)
+
+namespace QTlsPrivate
+{
+// These callback functions are defined in qtls_openssl.cpp.
+extern "C" int q_X509Callback(int ok, X509_STORE_CTX *ctx);
+extern "C" int q_X509CallbackDirect(int ok, X509_STORE_CTX *ctx);
+
+#if QT_CONFIG(ocsp)
+extern "C" int qt_OCSP_status_server_callback(SSL *ssl, void *);
+#endif // ocsp
+
+} // namespace QTlsPrivate
+
+#if QT_CONFIG(dtls)
+// defined in qdtls_openssl.cpp:
+namespace dtlscallbacks
+{
+extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx);
+extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst,
+ unsigned *cookieLength);
+extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie,
+ unsigned cookieLength);
+}
+#endif // dtls
+
+#ifdef TLS1_3_VERSION
+extern "C" int q_ssl_sess_set_new_cb(SSL *context, SSL_SESSION *session);
+#endif // TLS1_3_VERSION
+
+static inline QString msgErrorSettingBackendConfig(const QString &why)
+{
+ return QSslSocket::tr("Error when setting the OpenSSL configuration (%1)").arg(why);
+}
+
+static inline QString msgErrorSettingEllipticCurves(const QString &why)
+{
+ return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why);
+}
+
+qssloptions QSslContext::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions)
+{
+ qssloptions options;
+ switch (protocol) {
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ case QSsl::TlsV1_0OrLater:
+ options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+ break;
+ case QSsl::TlsV1_1OrLater:
+ options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
+ break;
+QT_WARNING_POP
+ case QSsl::SecureProtocols:
+ case QSsl::TlsV1_2OrLater:
+ options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
+ break;
+ case QSsl::TlsV1_3OrLater:
+ options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2;
+ break;
+ default:
+ options = SSL_OP_ALL;
+ }
+
+ // This option is disabled by default, so we need to be able to clear it
+ if (sslOptions & QSsl::SslOptionDisableEmptyFragments)
+ options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
+ else
+ options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
+
+#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
+ // This option is disabled by default, so we need to be able to clear it
+ if (sslOptions & QSsl::SslOptionDisableLegacyRenegotiation)
+ options &= ~SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
+ else
+ options |= SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
+#endif
+
+#ifdef SSL_OP_NO_TICKET
+ if (sslOptions & QSsl::SslOptionDisableSessionTickets)
+ options |= SSL_OP_NO_TICKET;
+#endif
+#ifdef SSL_OP_NO_COMPRESSION
+ if (sslOptions & QSsl::SslOptionDisableCompression)
+ options |= SSL_OP_NO_COMPRESSION;
+#endif
+
+ if (!(sslOptions & QSsl::SslOptionDisableServerCipherPreference))
+ options |= SSL_OP_CIPHER_SERVER_PREFERENCE;
+
+ return options;
+}
+
+QSslContext::QSslContext()
+ : ctx(nullptr),
+ pkey(nullptr),
+ session(nullptr),
+ m_sessionTicketLifeTimeHint(-1)
+{
+}
+
+QSslContext::~QSslContext()
+{
+ if (ctx)
+ // This will decrement the reference count by 1 and free the context eventually when possible
+ q_SSL_CTX_free(ctx);
+
+ if (pkey)
+ q_EVP_PKEY_free(pkey);
+
+ if (session)
+ q_SSL_SESSION_free(session);
+}
+
+std::shared_ptr<QSslContext> QSslContext::sharedFromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading)
+{
+ struct AccessToPrivateCtor : QSslContext {};
+ std::shared_ptr<QSslContext> sslContext = std::make_shared<AccessToPrivateCtor>();
+ initSslContext(sslContext.get(), mode, configuration, allowRootCertOnDemandLoading);
+ return sslContext;
+}
+
+std::shared_ptr<QSslContext> QSslContext::sharedFromPrivateConfiguration(QSslSocket::SslMode mode, QSslConfigurationPrivate *privConfiguration,
+ bool allowRootCertOnDemandLoading)
+{
+ return sharedFromConfiguration(mode, privConfiguration, allowRootCertOnDemandLoading);
+}
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+
+static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen, void *arg)
+{
+ QSslContext::NPNContext *ctx = reinterpret_cast<QSslContext::NPNContext *>(arg);
+
+ // comment out to debug:
+// QList<QByteArray> supportedVersions;
+// for (unsigned int i = 0; i < inlen; ) {
+// QByteArray version(reinterpret_cast<const char *>(&in[i+1]), in[i]);
+// supportedVersions << version;
+// i += in[i] + 1;
+// }
+
+ int proto = q_SSL_select_next_proto(out, outlen, in, inlen, ctx->data, ctx->len);
+ switch (proto) {
+ case OPENSSL_NPN_UNSUPPORTED:
+ ctx->status = QSslConfiguration::NextProtocolNegotiationNone;
+ break;
+ case OPENSSL_NPN_NEGOTIATED:
+ ctx->status = QSslConfiguration::NextProtocolNegotiationNegotiated;
+ break;
+ case OPENSSL_NPN_NO_OVERLAP:
+ ctx->status = QSslConfiguration::NextProtocolNegotiationUnsupported;
+ break;
+ default:
+ qCWarning(lcTlsBackend, "OpenSSL sent unknown NPN status");
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+QSslContext::NPNContext QSslContext::npnContext() const
+{
+ return m_npnContext;
+}
+#endif // !OPENSSL_NO_NEXTPROTONEG
+
+
+
+// Needs to be deleted by caller
+SSL* QSslContext::createSsl()
+{
+ SSL* ssl = q_SSL_new(ctx);
+ q_SSL_clear(ssl);
+
+ if (!session && !sessionASN1().isEmpty()
+ && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) {
+ const unsigned char *data = reinterpret_cast<const unsigned char *>(m_sessionASN1.constData());
+ session = q_d2i_SSL_SESSION(nullptr, &data, m_sessionASN1.size());
+ // 'session' has refcount 1 already, set by the function above
+ }
+
+ if (session) {
+ // Try to resume the last session we cached
+ if (!q_SSL_set_session(ssl, session)) {
+ qCWarning(lcTlsBackend, "could not set SSL session");
+ q_SSL_SESSION_free(session);
+ session = nullptr;
+ }
+ }
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+ QList<QByteArray> protocols = sslConfiguration.d.constData()->nextAllowedProtocols;
+ if (!protocols.isEmpty()) {
+ m_supportedNPNVersions.clear();
+ 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.";
+ continue;
+ } else if (protocols.at(a).isEmpty()) {
+ continue;
+ }
+ m_supportedNPNVersions.append(protocols.at(a).size()).append(protocols.at(a));
+ }
+ if (m_supportedNPNVersions.size()) {
+ m_npnContext.data = reinterpret_cast<unsigned char *>(m_supportedNPNVersions.data());
+ 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
+ // (tests/examples) cast it to unsigned char * (since it's 'out').
+ // We just re-use our NPN callback and cast here:
+ typedef int (*alpn_callback_t) (SSL *, const unsigned char **, unsigned char *,
+ const unsigned char *, unsigned int, void *);
+ // With ALPN callback is for a server side only, for a client m_npnContext.status
+ // will stay in NextProtocolNegotiationNone.
+ q_SSL_CTX_set_alpn_select_cb(ctx, alpn_callback_t(next_proto_cb), &m_npnContext);
+ // Client:
+ q_SSL_set_alpn_protos(ssl, m_npnContext.data, m_npnContext.len);
+ // And in case our peer does not support ALPN, but supports NPN:
+ q_SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &m_npnContext);
+ }
+ }
+#endif // !OPENSSL_NO_NEXTPROTONEG
+
+ return ssl;
+}
+
+// We cache exactly one session here
+bool QSslContext::cacheSession(SSL* ssl)
+{
+ // don't cache the same session again
+ if (session && session == q_SSL_get_session(ssl))
+ return true;
+
+ // decrease refcount of currently stored session
+ // (this might happen if there are several concurrent handshakes in flight)
+ if (session)
+ q_SSL_SESSION_free(session);
+
+ // cache the session the caller gave us and increase reference count
+ session = q_SSL_get1_session(ssl);
+
+ if (session && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) {
+ int sessionSize = q_i2d_SSL_SESSION(session, nullptr);
+ if (sessionSize > 0) {
+ m_sessionASN1.resize(sessionSize);
+ unsigned char *data = reinterpret_cast<unsigned char *>(m_sessionASN1.data());
+ if (!q_i2d_SSL_SESSION(session, &data))
+ qCWarning(lcTlsBackend, "could not store persistent version of SSL session");
+ m_sessionTicketLifeTimeHint = q_SSL_SESSION_get_ticket_lifetime_hint(session);
+ }
+ }
+
+ return (session != nullptr);
+}
+
+QByteArray QSslContext::sessionASN1() const
+{
+ return m_sessionASN1;
+}
+
+void QSslContext::setSessionASN1(const QByteArray &session)
+{
+ m_sessionASN1 = session;
+}
+
+int QSslContext::sessionTicketLifeTimeHint() const
+{
+ return m_sessionTicketLifeTimeHint;
+}
+
+void QSslContext::forceAutoTestSecurityLevel()
+{
+ *forceSecurityLevel() = true;
+}
+
+QSslError::SslError QSslContext::error() const
+{
+ return errorCode;
+}
+
+QString QSslContext::errorString() const
+{
+ return errorStr;
+}
+
+void QSslContext::initSslContext(QSslContext *sslContext, QSslSocket::SslMode mode,
+ const QSslConfiguration &configuration,
+ bool allowRootCertOnDemandLoading)
+{
+ sslContext->sslConfiguration = configuration;
+ sslContext->errorCode = QSslError::NoError;
+
+ bool client = (mode == QSslSocket::SslClientMode);
+
+ bool reinitialized = false;
+ bool unsupportedProtocol = false;
+ bool isDtls = false;
+init_context:
+ switch (sslContext->sslConfiguration.protocol()) {
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ case QSsl::DtlsV1_0:
+ case QSsl::DtlsV1_0OrLater:
+QT_WARNING_POP
+ case QSsl::DtlsV1_2:
+ case QSsl::DtlsV1_2OrLater:
+#if QT_CONFIG(dtls)
+ isDtls = true;
+ sslContext->ctx = q_SSL_CTX_new(client ? q_DTLS_client_method() : q_DTLS_server_method());
+#else // dtls
+ sslContext->ctx = nullptr;
+ unsupportedProtocol = true;
+ qCWarning(lcTlsBackend, "DTLS protocol requested, but feature 'dtls' is disabled");
+#endif // dtls
+ break;
+ case QSsl::TlsV1_3:
+ case QSsl::TlsV1_3OrLater:
+#if !defined(TLS1_3_VERSION)
+ qCWarning(lcTlsBackend, "TLS 1.3 is not supported");
+ sslContext->ctx = nullptr;
+ unsupportedProtocol = true;
+ break;
+#endif // TLS1_3_VERSION
+ default:
+ // The ssl options will actually control the supported methods
+ sslContext->ctx = q_SSL_CTX_new(client ? q_TLS_client_method() : q_TLS_server_method());
+ }
+
+ if (!sslContext->ctx) {
+ // After stopping Flash 10 the SSL library loses its ciphers. Try re-adding them
+ // by re-initializing the library.
+ if (!reinitialized) {
+ reinitialized = true;
+ if (q_OPENSSL_init_ssl(0, nullptr) == 1)
+ goto init_context;
+ }
+
+ sslContext->errorStr = QSslSocket::tr("Error creating SSL context (%1)").arg(
+ unsupportedProtocol ? QSslSocket::tr("unsupported protocol") : QTlsBackendOpenSSL::getErrorsFromOpenSsl()
+ );
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+ }
+
+ // A nasty hacked OpenSSL using a level that will make our auto-tests fail:
+ if (q_SSL_CTX_get_security_level(sslContext->ctx) > 1 && *forceSecurityLevel())
+ q_SSL_CTX_set_security_level(sslContext->ctx, 1);
+
+ const long anyVersion =
+#if QT_CONFIG(dtls)
+ isDtls ? DTLS_ANY_VERSION : TLS_ANY_VERSION;
+#else
+ TLS_ANY_VERSION;
+#endif // dtls
+ long minVersion = anyVersion;
+ long maxVersion = anyVersion;
+
+ switch (sslContext->sslConfiguration.protocol()) {
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ case QSsl::TlsV1_0:
+ minVersion = TLS1_VERSION;
+ maxVersion = TLS1_VERSION;
+ break;
+ case QSsl::TlsV1_1:
+ minVersion = TLS1_1_VERSION;
+ maxVersion = TLS1_1_VERSION;
+ break;
+QT_WARNING_POP
+ case QSsl::TlsV1_2:
+ minVersion = TLS1_2_VERSION;
+ maxVersion = TLS1_2_VERSION;
+ break;
+ case QSsl::TlsV1_3:
+#ifdef TLS1_3_VERSION
+ minVersion = TLS1_3_VERSION;
+ maxVersion = TLS1_3_VERSION;
+#else
+ // This protocol is not supported by OpenSSL 1.1 and we handle
+ // it as an error (see the code above).
+ Q_UNREACHABLE();
+#endif // TLS1_3_VERSION
+ break;
+ // Ranges:
+ case QSsl::AnyProtocol:
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ case QSsl::TlsV1_0OrLater:
+ minVersion = TLS1_VERSION;
+ maxVersion = 0;
+ break;
+ case QSsl::TlsV1_1OrLater:
+ minVersion = TLS1_1_VERSION;
+ maxVersion = 0;
+ break;
+QT_WARNING_POP
+ case QSsl::SecureProtocols:
+ case QSsl::TlsV1_2OrLater:
+ minVersion = TLS1_2_VERSION;
+ maxVersion = 0;
+ break;
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ case QSsl::DtlsV1_0:
+ minVersion = DTLS1_VERSION;
+ maxVersion = DTLS1_VERSION;
+ break;
+ case QSsl::DtlsV1_0OrLater:
+ minVersion = DTLS1_VERSION;
+ maxVersion = 0;
+ break;
+QT_WARNING_POP
+ case QSsl::DtlsV1_2:
+ minVersion = DTLS1_2_VERSION;
+ maxVersion = DTLS1_2_VERSION;
+ break;
+ case QSsl::DtlsV1_2OrLater:
+ minVersion = DTLS1_2_VERSION;
+ maxVersion = 0;
+ break;
+ case QSsl::TlsV1_3OrLater:
+#ifdef TLS1_3_VERSION
+ minVersion = TLS1_3_VERSION;
+ maxVersion = 0;
+ break;
+#else
+ // This protocol is not supported by OpenSSL 1.1 and we handle
+ // it as an error (see the code above).
+ Q_UNREACHABLE();
+ break;
+#endif // TLS1_3_VERSION
+ case QSsl::UnknownProtocol:
+ break;
+ }
+
+ if (minVersion != anyVersion
+ && !q_SSL_CTX_set_min_proto_version(sslContext->ctx, minVersion)) {
+ sslContext->errorStr = QSslSocket::tr("Error while setting the minimal protocol version");
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+ }
+
+ if (maxVersion != anyVersion
+ && !q_SSL_CTX_set_max_proto_version(sslContext->ctx, maxVersion)) {
+ sslContext->errorStr = QSslSocket::tr("Error while setting the maximum protocol version");
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+ }
+
+ // Enable bug workarounds.
+ const qssloptions options = setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions);
+ q_SSL_CTX_set_options(sslContext->ctx, options);
+
+ // Tell OpenSSL to release memory early
+ // http://www.openssl.org/docs/ssl/SSL_CTX_set_mode.html
+ q_SSL_CTX_set_mode(sslContext->ctx, SSL_MODE_RELEASE_BUFFERS);
+
+ auto filterCiphers = [](const QList<QSslCipher> &ciphers, bool selectTls13)
+ {
+ QByteArray cipherString;
+
+ for (const QSslCipher &cipher : ciphers) {
+ const bool isTls13Cipher = cipher.protocol() == QSsl::TlsV1_3 || cipher.protocol() == QSsl::TlsV1_3OrLater;
+ if (selectTls13 != isTls13Cipher)
+ continue;
+
+ if (cipherString.size())
+ cipherString.append(':');
+ cipherString.append(cipher.name().toLatin1());
+ }
+ return cipherString;
+ };
+
+ // Initialize ciphers
+ QList<QSslCipher> ciphers = sslContext->sslConfiguration.ciphers();
+ if (ciphers.isEmpty())
+ ciphers = isDtls ? QTlsBackend::defaultDtlsCiphers() : QTlsBackend::defaultCiphers();
+
+ const QByteArray preTls13Ciphers = filterCiphers(ciphers, false);
+
+ if (preTls13Ciphers.size()) {
+ if (!q_SSL_CTX_set_cipher_list(sslContext->ctx, preTls13Ciphers.data())) {
+ sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+ }
+ }
+
+ const QByteArray tls13Ciphers = filterCiphers(ciphers, true);
+#ifdef TLS1_3_VERSION
+ if (tls13Ciphers.size()) {
+ if (!q_SSL_CTX_set_ciphersuites(sslContext->ctx, tls13Ciphers.data())) {
+ sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+ }
+ }
+#endif // TLS1_3_VERSION
+ if (!preTls13Ciphers.size() && !tls13Ciphers.size()) {
+ sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QStringLiteral(""));
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+ }
+
+ const QDateTime now = QDateTime::currentDateTimeUtc();
+
+ // Add all our CAs to this store.
+ const auto caCertificates = sslContext->sslConfiguration.caCertificates();
+ for (const QSslCertificate &caCertificate : caCertificates) {
+ // From https://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html:
+ //
+ // If several CA certificates matching the name, key identifier, and
+ // serial number condition are available, only the first one will be
+ // examined. This may lead to unexpected results if the same CA
+ // certificate is available with different expiration dates. If a
+ // ``certificate expired'' verification error occurs, no other
+ // certificate will be searched. Make sure to not have expired
+ // certificates mixed with valid ones.
+ //
+ // See also: QSslSocketBackendPrivate::verify()
+ if (caCertificate.expiryDate() >= now) {
+ q_X509_STORE_add_cert(q_SSL_CTX_get_cert_store(sslContext->ctx), (X509 *)caCertificate.handle());
+ }
+ }
+
+ if (QSslSocketPrivate::rootCertOnDemandLoadingSupported() && allowRootCertOnDemandLoading) {
+ // tell OpenSSL the directories where to look up the root certs on demand
+ const QList<QByteArray> unixDirs = QSslSocketPrivate::unixRootCertDirectories();
+ int success = 1;
+#if OPENSSL_VERSION_MAJOR < 3
+ for (const QByteArray &unixDir : unixDirs) {
+ if ((success = q_SSL_CTX_load_verify_locations(sslContext->ctx, nullptr, unixDir.constData())) != 1)
+ break;
+ }
+#else
+ for (const QByteArray &unixDir : unixDirs) {
+ if ((success = q_SSL_CTX_load_verify_dir(sslContext->ctx, unixDir.constData())) != 1)
+ break;
+ }
+#endif // OPENSSL_VERSION_MAJOR
+ if (success != 1) {
+ const auto qtErrors = QTlsBackendOpenSSL::getErrorsFromOpenSsl();
+ qCWarning(lcTlsBackend) << "An error encountered while to set root certificates location:"
+ << qtErrors;
+ }
+ }
+
+ if (!sslContext->sslConfiguration.localCertificate().isNull()) {
+ // Require a private key as well.
+ if (sslContext->sslConfiguration.privateKey().isNull()) {
+ sslContext->errorStr = QSslSocket::tr("Cannot provide a certificate with no key");
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+ }
+
+ // Load certificate
+ if (!q_SSL_CTX_use_certificate(sslContext->ctx, (X509 *)sslContext->sslConfiguration.localCertificate().handle())) {
+ sslContext->errorStr = QSslSocket::tr("Error loading local certificate, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+ }
+
+ if (configuration.d->privateKey.algorithm() == QSsl::Opaque) {
+ sslContext->pkey = reinterpret_cast<EVP_PKEY *>(configuration.d->privateKey.handle());
+ } else {
+#ifdef OPENSSL_NO_DEPRECATED_3_0
+ auto qtKey = QTlsBackend::backend<QTlsPrivate::TlsKeyOpenSSL>(configuration.d->privateKey);
+ Q_ASSERT(qtKey);
+ sslContext->pkey = qtKey->genericKey;
+ Q_ASSERT(sslContext->pkey);
+ q_EVP_PKEY_up_ref(sslContext->pkey);
+#else
+ // Load private key
+ sslContext->pkey = q_EVP_PKEY_new();
+ // before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free.
+ // this lead to a memory leak. Now we use the *_set1_* functions which do not
+ // take ownership of the RSA/DSA key instance because the QSslKey already has ownership.
+ if (configuration.d->privateKey.algorithm() == QSsl::Rsa)
+ q_EVP_PKEY_set1_RSA(sslContext->pkey, reinterpret_cast<RSA *>(configuration.d->privateKey.handle()));
+ else if (configuration.d->privateKey.algorithm() == QSsl::Dsa)
+ q_EVP_PKEY_set1_DSA(sslContext->pkey, reinterpret_cast<DSA *>(configuration.d->privateKey.handle()));
+#ifndef OPENSSL_NO_EC
+ else if (configuration.d->privateKey.algorithm() == QSsl::Ec)
+ q_EVP_PKEY_set1_EC_KEY(sslContext->pkey, reinterpret_cast<EC_KEY *>(configuration.d->privateKey.handle()));
+#endif // OPENSSL_NO_EC
+#endif // OPENSSL_NO_DEPRECATED_3_0
+ }
+ auto pkey = sslContext->pkey;
+ if (configuration.d->privateKey.algorithm() == QSsl::Opaque)
+ sslContext->pkey = nullptr; // Don't free the private key, it belongs to QSslKey
+
+ if (!q_SSL_CTX_use_PrivateKey(sslContext->ctx, pkey)) {
+ sslContext->errorStr = QSslSocket::tr("Error loading private key, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+ }
+
+ // Check if the certificate matches the private key.
+ if (!q_SSL_CTX_check_private_key(sslContext->ctx)) {
+ sslContext->errorStr = QSslSocket::tr("Private key does not certify public key, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+ }
+
+ // If we have any intermediate certificates then we need to add them to our chain
+ bool first = true;
+ for (const QSslCertificate &cert : std::as_const(configuration.d->localCertificateChain)) {
+ if (first) {
+ first = false;
+ continue;
+ }
+ q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_EXTRA_CHAIN_CERT, 0,
+ q_X509_dup(reinterpret_cast<X509 *>(cert.handle())));
+ }
+ }
+
+ // Initialize peer verification, different callbacks, TLS/DTLS verification first
+ // (note, all these set_some_callback do not have return value):
+ if (sslContext->sslConfiguration.peerVerifyMode() == QSslSocket::VerifyNone) {
+ q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_NONE, nullptr);
+ } else {
+ auto verificationCallback =
+ #if QT_CONFIG(dtls)
+ isDtls ? dtlscallbacks::q_X509DtlsCallback :
+ #endif // dtls
+ QTlsPrivate::q_X509Callback;
+
+ if (!isDtls && configuration.handshakeMustInterruptOnError())
+ verificationCallback = QTlsPrivate::q_X509CallbackDirect;
+
+ auto verificationMode = SSL_VERIFY_PEER;
+ if (!isDtls && sslContext->sslConfiguration.missingCertificateIsFatal())
+ verificationMode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+
+ q_SSL_CTX_set_verify(sslContext->ctx, verificationMode, verificationCallback);
+ }
+
+#ifdef TLS1_3_VERSION
+ // NewSessionTicket callback:
+ if (mode == QSslSocket::SslClientMode && !isDtls) {
+ q_SSL_CTX_sess_set_new_cb(sslContext->ctx, q_ssl_sess_set_new_cb);
+ q_SSL_CTX_set_session_cache_mode(sslContext->ctx, SSL_SESS_CACHE_CLIENT);
+ }
+
+#endif // TLS1_3_VERSION
+
+#if QT_CONFIG(dtls)
+ // DTLS cookies:
+ if (mode == QSslSocket::SslServerMode && isDtls && configuration.dtlsCookieVerificationEnabled()) {
+ q_SSL_CTX_set_cookie_generate_cb(sslContext->ctx, dtlscallbacks::q_generate_cookie_callback);
+ q_SSL_CTX_set_cookie_verify_cb(sslContext->ctx, dtlscallbacks::q_verify_cookie_callback);
+ }
+#endif // dtls
+
+ // Set verification depth.
+ if (sslContext->sslConfiguration.peerVerifyDepth() != 0)
+ q_SSL_CTX_set_verify_depth(sslContext->ctx, sslContext->sslConfiguration.peerVerifyDepth());
+
+ // set persisted session if the user set it
+ if (!configuration.sessionTicket().isEmpty())
+ sslContext->setSessionASN1(configuration.sessionTicket());
+
+ // Set temp DH params
+ QSslDiffieHellmanParameters dhparams = configuration.diffieHellmanParameters();
+
+ if (!dhparams.isValid()) {
+ sslContext->errorStr = QSslSocket::tr("Diffie-Hellman parameters are not valid");
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+ }
+
+ if (dhparams.isEmpty()) {
+ q_SSL_CTX_set_dh_auto(sslContext->ctx, 1);
+ } else {
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ const QByteArray &params = dhparams.d->derData;
+ const char *ptr = params.constData();
+ DH *dh = q_d2i_DHparams(nullptr, reinterpret_cast<const unsigned char **>(&ptr),
+ 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);
+ q_DH_free(dh);
+#else
+ qCWarning(lcTlsBackend, "Diffie-Hellman parameters are not supported, because OpenSSL v3 was built with deprecated API removed");
+#endif
+ }
+
+#ifndef OPENSSL_NO_PSK
+ if (!client)
+ q_SSL_CTX_use_psk_identity_hint(sslContext->ctx, sslContext->sslConfiguration.preSharedKeyIdentityHint().constData());
+#endif // !OPENSSL_NO_PSK
+
+ const auto qcurves = sslContext->sslConfiguration.ellipticCurves();
+ if (!qcurves.isEmpty()) {
+#ifdef OPENSSL_NO_EC
+ sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocket::tr("OpenSSL version with disabled elliptic curves"));
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+#else
+ // Set the curves to be used.
+ std::vector<int> curves;
+ curves.reserve(qcurves.size());
+ for (const auto &sslCurve : qcurves)
+ curves.push_back(sslCurve.id);
+ if (!q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_SET_CURVES, long(curves.size()), &curves[0])) {
+ sslContext->errorStr = msgErrorSettingEllipticCurves(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+ }
+#endif
+ }
+
+ applyBackendConfig(sslContext);
+}
+
+void QSslContext::applyBackendConfig(QSslContext *sslContext)
+{
+ const QMap<QByteArray, QVariant> &conf = sslContext->sslConfiguration.backendConfiguration();
+ if (conf.isEmpty())
+ return;
+
+#if QT_CONFIG(ocsp)
+ auto ocspResponsePos = conf.find("Qt-OCSP-response");
+ if (ocspResponsePos != conf.end()) {
+ // This is our private, undocumented configuration option, existing only for
+ // the purpose of testing OCSP status responses. We don't even check this
+ // callback was set. If no - the test must fail.
+ q_SSL_CTX_set_tlsext_status_cb(sslContext->ctx, QTlsPrivate::qt_OCSP_status_server_callback);
+ if (conf.size() == 1)
+ return;
+ }
+#endif // ocsp
+
+ QSharedPointer<SSL_CONF_CTX> cctx(q_SSL_CONF_CTX_new(), &q_SSL_CONF_CTX_free);
+ if (cctx) {
+ q_SSL_CONF_CTX_set_ssl_ctx(cctx.data(), sslContext->ctx);
+ q_SSL_CONF_CTX_set_flags(cctx.data(), SSL_CONF_FLAG_FILE);
+
+ for (auto i = conf.constBegin(); i != conf.constEnd(); ++i) {
+ if (i.key() == "Qt-OCSP-response") // This never goes to SSL_CONF_cmd().
+ continue;
+
+ if (!i.value().canConvert(QMetaType(QMetaType::QByteArray))) {
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ sslContext->errorStr = msgErrorSettingBackendConfig(
+ QSslSocket::tr("Expecting QByteArray for %1").arg(
+ QString::fromUtf8(i.key())));
+ return;
+ }
+
+ const QByteArray &value = i.value().toByteArray();
+ const int result = q_SSL_CONF_cmd(cctx.data(), i.key().constData(), value.constData());
+ if (result == 2)
+ continue;
+
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ switch (result) {
+ case 0:
+ sslContext->errorStr = msgErrorSettingBackendConfig(
+ QSslSocket::tr("An error occurred attempting to set %1 to %2").arg(
+ QString::fromUtf8(i.key()), QString::fromUtf8(value)));
+ return;
+ case 1:
+ sslContext->errorStr = msgErrorSettingBackendConfig(
+ QSslSocket::tr("Wrong value for %1 (%2)").arg(
+ QString::fromUtf8(i.key()), QString::fromUtf8(value)));
+ return;
+ default:
+ sslContext->errorStr = msgErrorSettingBackendConfig(
+ QSslSocket::tr("Unrecognized command %1 = %2").arg(
+ QString::fromUtf8(i.key()), QString::fromUtf8(value)));
+ return;
+ }
+ }
+
+ if (q_SSL_CONF_CTX_finish(cctx.data()) == 0) {
+ sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_finish() failed"));
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ }
+ } else {
+ sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_CTX_new() failed"));
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/tls/openssl/qsslcontext_openssl_p.h b/src/plugins/tls/openssl/qsslcontext_openssl_p.h
new file mode 100644
index 0000000000..3bd39baf0c
--- /dev/null
+++ b/src/plugins/tls/openssl/qsslcontext_openssl_p.h
@@ -0,0 +1,96 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+
+#ifndef QSSLCONTEXT_OPENSSL_P_H
+#define QSSLCONTEXT_OPENSSL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+#include <QtCore/qvariant.h>
+#include <QtNetwork/qsslcertificate.h>
+#include <QtNetwork/qsslconfiguration.h>
+#include <openssl/ssl.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_SSL
+
+class QSslContext
+{
+public:
+
+ ~QSslContext();
+
+ static std::shared_ptr<QSslContext> sharedFromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration,
+ bool allowRootCertOnDemandLoading);
+ static std::shared_ptr<QSslContext> sharedFromPrivateConfiguration(QSslSocket::SslMode mode, QSslConfigurationPrivate *privConfiguration,
+ bool allowRootCertOnDemandLoading);
+
+ static qssloptions setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions);
+
+ QSslError::SslError error() const;
+ QString errorString() const;
+
+ SSL* createSsl();
+ bool cacheSession(SSL*); // should be called when handshake completed
+
+ QByteArray sessionASN1() const;
+ void setSessionASN1(const QByteArray &sessionASN1);
+ int sessionTicketLifeTimeHint() const;
+
+ static void forceAutoTestSecurityLevel();
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+ // must be public because we want to use it from an OpenSSL callback
+ struct NPNContext {
+ NPNContext() : data(nullptr),
+ len(0),
+ status(QSslConfiguration::NextProtocolNegotiationNone)
+ { }
+ unsigned char *data;
+ unsigned short len;
+ QSslConfiguration::NextProtocolNegotiationStatus status;
+ };
+ NPNContext npnContext() const;
+#endif // !OPENSSL_NO_NEXTPROTONEG
+
+protected:
+ QSslContext();
+
+private:
+ static void initSslContext(QSslContext* sslContext, QSslSocket::SslMode mode, const QSslConfiguration &configuration,
+ bool allowRootCertOnDemandLoading);
+ static void applyBackendConfig(QSslContext *sslContext);
+
+private:
+ SSL_CTX* ctx;
+ EVP_PKEY *pkey;
+ SSL_SESSION *session;
+ QByteArray m_sessionASN1;
+ int m_sessionTicketLifeTimeHint;
+ QSslError::SslError errorCode;
+ QString errorStr;
+ QSslConfiguration sslConfiguration;
+#ifndef OPENSSL_NO_NEXTPROTONEG
+ QByteArray m_supportedNPNVersions;
+ NPNContext m_npnContext;
+#endif // !OPENSSL_NO_NEXTPROTONEG
+};
+
+#endif // QT_NO_SSL
+
+QT_END_NAMESPACE
+
+#endif // QSSLCONTEXT_OPENSSL_P_H
diff --git a/src/plugins/tls/openssl/qssldiffiehellmanparameters_openssl.cpp b/src/plugins/tls/openssl/qssldiffiehellmanparameters_openssl.cpp
new file mode 100644
index 0000000000..16e31e605f
--- /dev/null
+++ b/src/plugins/tls/openssl/qssldiffiehellmanparameters_openssl.cpp
@@ -0,0 +1,159 @@
+// Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
+// Copyright (C) 2016 Richard J. Moore <rich@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsslsocket_openssl_symbols_p.h"
+#include "qtlsbackend_openssl_p.h"
+
+#include <QtNetwork/private/qsslsocket_p.h>
+
+#include <QtCore/qscopeguard.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qdebug.h>
+
+#include <openssl/bn.h>
+#include <openssl/dh.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+
+namespace {
+
+bool isSafeDH(DH *dh)
+{
+ int status = 0;
+ int bad = 0;
+
+ // TLSTODO: check it's needed or if supportsSsl()
+ // is enough.
+ QSslSocketPrivate::ensureInitialized();
+
+ // From https://wiki.openssl.org/index.php/Diffie-Hellman_parameters:
+ //
+ // The additional call to BN_mod_word(dh->p, 24)
+ // (and unmasking of DH_NOT_SUITABLE_GENERATOR)
+ // is performed to ensure your program accepts
+ // IETF group parameters. OpenSSL checks the prime
+ // is congruent to 11 when g = 2; while the IETF's
+ // primes are congruent to 23 when g = 2.
+ // Without the test, the IETF parameters would
+ // fail validation. For details, see Diffie-Hellman
+ // Parameter Check (when g = 2, must p mod 24 == 11?).
+ // Mark p < 1024 bits as unsafe.
+ if (q_DH_bits(dh) < 1024)
+ return false;
+
+ if (q_DH_check(dh, &status) != 1)
+ return false;
+
+ const BIGNUM *p = nullptr;
+ const BIGNUM *q = nullptr;
+ const BIGNUM *g = nullptr;
+ q_DH_get0_pqg(dh, &p, &q, &g);
+
+ if (q_BN_is_word(const_cast<BIGNUM *>(g), DH_GENERATOR_2)) {
+ const unsigned long residue = q_BN_mod_word(p, 24);
+ if (residue == 11 || residue == 23)
+ status &= ~DH_NOT_SUITABLE_GENERATOR;
+ }
+
+ bad |= DH_CHECK_P_NOT_PRIME;
+ bad |= DH_CHECK_P_NOT_SAFE_PRIME;
+ bad |= DH_NOT_SUITABLE_GENERATOR;
+
+ return !(status & bad);
+}
+
+} // unnamed namespace
+
+#endif
+
+int QTlsBackendOpenSSL::dhParametersFromDer(const QByteArray &der, QByteArray *derData) const
+{
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ Q_ASSERT(derData);
+
+ if (der.isEmpty())
+ return DHParams::InvalidInputDataError;
+
+ const unsigned char *data = reinterpret_cast<const unsigned char *>(der.data());
+ const int len = der.size();
+
+ // TLSTODO: check it's needed (loading ciphers and certs in
+ // addition to the library!)
+ QSslSocketPrivate::ensureInitialized();
+
+ DH *dh = q_d2i_DHparams(nullptr, &data, len);
+ if (dh) {
+ const auto dhRaii = qScopeGuard([dh] {q_DH_free(dh);});
+
+ if (isSafeDH(dh))
+ *derData = der;
+ else
+ return DHParams::UnsafeParametersError;
+ } else {
+ return DHParams::InvalidInputDataError;
+ }
+#else
+ Q_UNUSED(der);
+ Q_UNUSED(derData);
+ qCWarning(lcTlsBackend, "Diffie-Hellman parameters are not supported, because OpenSSL v3 was built with deprecated API removed");
+#endif
+ return DHParams::NoError;
+}
+
+int QTlsBackendOpenSSL::dhParametersFromPem(const QByteArray &pem, QByteArray *data) const
+{
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ Q_ASSERT(data);
+
+ if (pem.isEmpty())
+ return DHParams::InvalidInputDataError;
+
+ // TLSTODO: check it was not a cargo-cult programming in case of
+ // DH ...
+ QSslSocketPrivate::ensureInitialized();
+
+ BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size());
+ if (!bio)
+ return DHParams::InvalidInputDataError;
+
+ const auto bioRaii = qScopeGuard([bio]
+ {
+ q_BIO_free(bio);
+ });
+
+ DH *dh = nullptr;
+ q_PEM_read_bio_DHparams(bio, &dh, nullptr, nullptr);
+
+ if (dh) {
+ const auto dhGuard = qScopeGuard([dh]
+ {
+ q_DH_free(dh);
+ });
+
+ 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->assign({buf, len});
+ else
+ return DHParams::InvalidInputDataError;
+ } else {
+ return DHParams::UnsafeParametersError;
+ }
+ } else {
+ return DHParams::InvalidInputDataError;
+ }
+#else
+ Q_UNUSED(pem);
+ Q_UNUSED(data);
+ qCWarning(lcTlsBackend, "Diffie-Hellman parameters are not supported, because OpenSSL v3 was built with deprecated API removed");
+#endif
+ return DHParams::NoError;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/tls/openssl/qsslsocket_openssl_android.cpp b/src/plugins/tls/openssl/qsslsocket_openssl_android.cpp
new file mode 100644
index 0000000000..6c02215c55
--- /dev/null
+++ b/src/plugins/tls/openssl/qsslsocket_openssl_android.cpp
@@ -0,0 +1,58 @@
+// 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
+
+/****************************************************************************
+**
+** In addition, as a special exception, the copyright holders listed above give
+** permission to link the code of its release of Qt with the OpenSSL project's
+** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
+** same license as the original version), and distribute the linked executables.
+**
+** You must comply with the GNU General Public License version 2 in all
+** respects for all of the code used other than the "OpenSSL" code. If you
+** modify this file, you may extend this exception to your version of the file,
+** but you are not obligated to do so. If you do not wish to do so, delete
+** this exception statement from your version of this file.
+**
+****************************************************************************/
+
+#include <QtCore/QJniEnvironment>
+#include <QtCore/QJniObject>
+#include <QtCore/QList>
+#include <QtCore/QByteArray>
+
+QT_BEGIN_NAMESPACE
+
+namespace QTlsPrivate {
+
+QList<QByteArray> fetchSslCertificateData()
+{
+ QList<QByteArray> certificateData;
+
+ QJniObject certificates = QJniObject::callStaticObjectMethod("org/qtproject/qt/android/QtNative",
+ "getSSLCertificates",
+ "()[[B");
+ if (!certificates.isValid())
+ return certificateData;
+
+ QJniEnvironment env;
+ jobjectArray jcertificates = certificates.object<jobjectArray>();
+ const jint nCertificates = env->GetArrayLength(jcertificates);
+ certificateData.reserve(static_cast<int>(nCertificates));
+
+ for (int i = 0; i < nCertificates; ++i) {
+ jbyteArray jCert = static_cast<jbyteArray>(env->GetObjectArrayElement(jcertificates, i));
+ const uint sz = env->GetArrayLength(jCert);
+ jbyte *buffer = env->GetByteArrayElements(jCert, 0);
+ certificateData.append(QByteArray(reinterpret_cast<char*>(buffer), sz));
+
+ env->ReleaseByteArrayElements(jCert, buffer, JNI_ABORT); // don't copy back the elements
+ env->DeleteLocalRef(jCert);
+ }
+
+ return certificateData;
+}
+
+} // namespace QTlsPrivate
+
+QT_END_NAMESPACE
diff --git a/src/plugins/tls/openssl/qsslsocket_openssl_symbols.cpp b/src/plugins/tls/openssl/qsslsocket_openssl_symbols.cpp
new file mode 100644
index 0000000000..4aa9ca6fb1
--- /dev/null
+++ b/src/plugins/tls/openssl/qsslsocket_openssl_symbols.cpp
@@ -0,0 +1,1266 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+// Copyright (C) 2016 Richard J. Moore <rich@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/****************************************************************************
+**
+** In addition, as a special exception, the copyright holders listed above give
+** permission to link the code of its release of Qt with the OpenSSL project's
+** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
+** same license as the original version), and distribute the linked executables.
+**
+** You must comply with the GNU General Public License version 2 in all
+** respects for all of the code used other than the "OpenSSL" code. If you
+** modify this file, you may extend this exception to your version of the file,
+** but you are not obligated to do so. If you do not wish to do so, delete
+** this exception statement from your version of this file.
+**
+****************************************************************************/
+
+#include "qsslsocket_openssl_symbols_p.h"
+#include "qtlsbackend_openssl_p.h"
+
+#include <QtNetwork/private/qssl_p.h>
+
+#ifdef Q_OS_WIN
+# include <QtCore/private/qsystemlibrary_p.h>
+#elif QT_CONFIG(library)
+# include <QtCore/qlibrary.h>
+#endif
+#include <QtCore/qdatetime.h>
+#if defined(Q_OS_UNIX)
+#include <QtCore/qdir.h>
+#endif
+#include <QtCore/private/qduplicatetracker_p.h>
+#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
+#include <link.h>
+#endif
+#ifdef Q_OS_DARWIN
+#include <QtCore/private/qcore_mac_p.h>
+#endif
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+/*
+ Note to maintainer:
+ -------------------
+
+ We load OpenSSL symbols dynamically. Because symbols are known to
+ disappear, and signatures sometimes change, between releases, we need to
+ be careful about how this is done. To ensure we don't end up dereferencing
+ null function pointers, and continue running even if certain functions are
+ missing, we define helper functions for each of the symbols we load from
+ OpenSSL, all prefixed with "q_" (declared in
+ qsslsocket_openssl_symbols_p.h). So instead of calling SSL_connect
+ directly, we call q_SSL_connect, which is a function that checks if the
+ actual SSL_connect fptr is null, and returns a failure if it is, or calls
+ SSL_connect if it isn't.
+
+ This requires a somewhat tedious process of declaring each function we
+ want to call in OpenSSL thrice: once with the q_, in _p.h, once using the
+ DEFINEFUNC macros below, and once in the function that actually resolves
+ the symbols, below the DEFINEFUNC declarations below.
+
+ There's one DEFINEFUNC macro declared for every number of arguments
+ exposed by OpenSSL (feel free to extend when needed). The easiest thing to
+ do is to find an existing entry that matches the arg count of the function
+ you want to import, and do the same.
+
+ The first macro arg is the function return type. The second is the
+ verbatim name of the function/symbol. Then follows a list of N pairs of
+ argument types with a variable name, and just the variable name (char *a,
+ a, char *b, b, etc). Finally there's two arguments - a suitable return
+ statement for the error case (for an int function, return 0 or return -1
+ is usually right). Then either just "return" or DUMMYARG, the latter being
+ for void functions.
+
+ Note: Take into account that these macros and declarations are processed
+ at compile-time, and the result depends on the OpenSSL headers the
+ compiling host has installed, but the symbols are resolved at run-time,
+ possibly with a different version of OpenSSL.
+*/
+
+#ifndef QT_LINKED_OPENSSL
+
+namespace {
+void qsslSocketUnresolvedSymbolWarning(const char *functionName)
+{
+ qCWarning(lcTlsBackend, "QSslSocket: cannot call unresolved function %s", functionName);
+}
+
+#if QT_CONFIG(library)
+void qsslSocketCannotResolveSymbolWarning(const char *functionName)
+{
+ qCWarning(lcTlsBackend, "QSslSocket: cannot resolve %s", functionName);
+}
+#endif
+
+}
+
+#endif // QT_LINKED_OPENSSL
+
+DEFINEFUNC(const unsigned char *, ASN1_STRING_get0_data, const ASN1_STRING *a, a, return nullptr, return)
+DEFINEFUNC2(int, OPENSSL_init_ssl, uint64_t opts, opts, const OPENSSL_INIT_SETTINGS *settings, settings, return 0, return)
+DEFINEFUNC2(int, OPENSSL_init_crypto, uint64_t opts, opts, const OPENSSL_INIT_SETTINGS *settings, settings, return 0, return)
+DEFINEFUNC(BIO *, BIO_new, const BIO_METHOD *a, a, return nullptr, return)
+DEFINEFUNC(const BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return nullptr, return)
+DEFINEFUNC2(int, BN_is_word, BIGNUM *a, a, BN_ULONG w, w, return 0, return)
+DEFINEFUNC(int, EVP_CIPHER_CTX_reset, EVP_CIPHER_CTX *c, c, return 0, return)
+DEFINEFUNC(int, EVP_PKEY_up_ref, EVP_PKEY *a, a, return 0, return)
+DEFINEFUNC2(EVP_PKEY_CTX *, EVP_PKEY_CTX_new, EVP_PKEY *pkey, pkey, ENGINE *e, e, return nullptr, return)
+DEFINEFUNC(int, EVP_PKEY_param_check, EVP_PKEY_CTX *ctx, ctx, return 0, return)
+DEFINEFUNC(void, EVP_PKEY_CTX_free, EVP_PKEY_CTX *ctx, ctx, return, return)
+DEFINEFUNC(int, OPENSSL_sk_num, OPENSSL_STACK *a, a, return -1, return)
+DEFINEFUNC2(void, OPENSSL_sk_pop_free, OPENSSL_STACK *a, a, void (*b)(void*), b, return, DUMMYARG)
+DEFINEFUNC(OPENSSL_STACK *, OPENSSL_sk_new_null, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC2(void, OPENSSL_sk_push, OPENSSL_STACK *a, a, void *b, b, return, DUMMYARG)
+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(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)
+DEFINEFUNC(const char *, SSL_alert_desc_string_long, int value, value, return nullptr, return)
+DEFINEFUNC(int, SSL_CTX_get_security_level, const SSL_CTX *ctx, ctx, return -1, return)
+DEFINEFUNC2(void, SSL_CTX_set_security_level, SSL_CTX *ctx, ctx, int level, level, return, return)
+#ifdef TLS1_3_VERSION
+DEFINEFUNC2(int, SSL_CTX_set_ciphersuites, SSL_CTX *ctx, ctx, const char *str, str, return 0, return)
+DEFINEFUNC2(void, SSL_set_psk_use_session_callback, SSL *ssl, ssl, q_SSL_psk_use_session_cb_func_t callback, callback, return, DUMMYARG)
+DEFINEFUNC2(void, SSL_CTX_sess_set_new_cb, SSL_CTX *ctx, ctx, NewSessionCallback cb, cb, return, return)
+DEFINEFUNC(int, SSL_SESSION_is_resumable, const SSL_SESSION *s, s, return 0, return)
+#endif
+DEFINEFUNC3(size_t, SSL_get_client_random, SSL *a, a, unsigned char *out, out, size_t outlen, outlen, return 0, return)
+DEFINEFUNC3(size_t, SSL_SESSION_get_master_key, const SSL_SESSION *ses, ses, unsigned char *out, out, size_t outlen, outlen, return 0, return)
+DEFINEFUNC6(int, CRYPTO_get_ex_new_index, int class_index, class_index, long argl, argl, void *argp, argp, CRYPTO_EX_new *new_func, new_func, CRYPTO_EX_dup *dup_func, dup_func, CRYPTO_EX_free *free_func, free_func, return -1, return)
+DEFINEFUNC2(unsigned long, SSL_set_options, SSL *ssl, ssl, unsigned long op, op, return 0, return)
+
+DEFINEFUNC(const SSL_METHOD *, TLS_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const SSL_METHOD *, TLS_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const SSL_METHOD *, TLS_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, X509_up_ref, X509 *a, a, return, DUMMYARG)
+DEFINEFUNC(ASN1_TIME *, X509_getm_notBefore, X509 *a, a, return nullptr, return)
+DEFINEFUNC(ASN1_TIME *, X509_getm_notAfter, X509 *a, a, return nullptr, return)
+DEFINEFUNC2(void, ASN1_item_free, ASN1_VALUE *val, val, const ASN1_ITEM *it, it, return, return)
+DEFINEFUNC(void, X509V3_conf_free, CONF_VALUE *val, val, return, return)
+DEFINEFUNC(long, X509_get_version, X509 *a, a, return -1, return)
+DEFINEFUNC(EVP_PKEY *, X509_get_pubkey, X509 *a, a, return nullptr, return)
+DEFINEFUNC2(void, X509_STORE_set_verify_cb, X509_STORE *a, a, X509_STORE_CTX_verify_cb verify_cb, verify_cb, return, DUMMYARG)
+DEFINEFUNC3(int, X509_STORE_set_ex_data, X509_STORE *a, a, int idx, idx, void *data, data, return 0, return)
+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)
+
+#if QT_CONFIG(dtls)
+DEFINEFUNC2(int, DTLSv1_listen, SSL *s, s, BIO_ADDR *c, c, return -1, return)
+DEFINEFUNC(BIO_ADDR *, BIO_ADDR_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, BIO_ADDR_free, BIO_ADDR *ap, ap, return, DUMMYARG)
+DEFINEFUNC2(BIO_METHOD *, BIO_meth_new, int type, type, const char *name, name, return nullptr, return)
+DEFINEFUNC(void, BIO_meth_free, BIO_METHOD *biom, biom, return, DUMMYARG)
+DEFINEFUNC2(int, BIO_meth_set_write, BIO_METHOD *biom, biom, DgramWriteCallback write, write, return 0, return)
+DEFINEFUNC2(int, BIO_meth_set_read, BIO_METHOD *biom, biom, DgramReadCallback read, read, return 0, return)
+DEFINEFUNC2(int, BIO_meth_set_puts, BIO_METHOD *biom, biom, DgramPutsCallback puts, puts, return 0, return)
+DEFINEFUNC2(int, BIO_meth_set_ctrl, BIO_METHOD *biom, biom, DgramCtrlCallback ctrl, ctrl, return 0, return)
+DEFINEFUNC2(int, BIO_meth_set_create, BIO_METHOD *biom, biom, DgramCreateCallback crt, crt, return 0, return)
+DEFINEFUNC2(int, BIO_meth_set_destroy, BIO_METHOD *biom, biom, DgramDestroyCallback dtr, dtr, return 0, return)
+#endif // dtls
+
+#if QT_CONFIG(ocsp)
+DEFINEFUNC(const OCSP_CERTID *, OCSP_SINGLERESP_get0_id, const OCSP_SINGLERESP *x, x, return nullptr, return)
+DEFINEFUNC3(OCSP_RESPONSE *, d2i_OCSP_RESPONSE, OCSP_RESPONSE **a, a, const unsigned char **in, in, long len, len, return nullptr, return)
+DEFINEFUNC(void, OCSP_RESPONSE_free, OCSP_RESPONSE *rs, rs, return, DUMMYARG)
+DEFINEFUNC(OCSP_BASICRESP *, OCSP_response_get1_basic, OCSP_RESPONSE *resp, resp, return nullptr, return)
+DEFINEFUNC(void, OCSP_BASICRESP_free, OCSP_BASICRESP *bs, bs, return, DUMMYARG)
+DEFINEFUNC(int, OCSP_response_status, OCSP_RESPONSE *resp, resp, return OCSP_RESPONSE_STATUS_INTERNALERROR, return)
+DEFINEFUNC4(int, OCSP_basic_verify, OCSP_BASICRESP *bs, bs, STACK_OF(X509) *certs, certs, X509_STORE *st, st, unsigned long flags, flags, return -1, return)
+DEFINEFUNC(int, OCSP_resp_count, OCSP_BASICRESP *bs, bs, return 0, return)
+DEFINEFUNC2(OCSP_SINGLERESP *, OCSP_resp_get0, OCSP_BASICRESP *bs, bs, int idx, idx, return nullptr, return)
+DEFINEFUNC5(int, OCSP_single_get0_status, OCSP_SINGLERESP *single, single, int *reason, reason, ASN1_GENERALIZEDTIME **revtime, revtime,
+ ASN1_GENERALIZEDTIME **thisupd, thisupd, ASN1_GENERALIZEDTIME **nextupd, nextupd, return -1, return)
+DEFINEFUNC4(int, OCSP_check_validity, ASN1_GENERALIZEDTIME *thisupd, thisupd, ASN1_GENERALIZEDTIME *nextupd, nextupd, long nsec, nsec, long maxsec, maxsec, return 0, return)
+DEFINEFUNC3(OCSP_CERTID *, OCSP_cert_to_id, const EVP_MD *dgst, dgst, X509 *subject, subject, X509 *issuer, issuer, return nullptr, return)
+DEFINEFUNC(void, OCSP_CERTID_free, OCSP_CERTID *cid, cid, return, DUMMYARG)
+DEFINEFUNC5(int, OCSP_id_get0_info, ASN1_OCTET_STRING **piNameHash, piNameHash, ASN1_OBJECT **pmd, pmd,
+ ASN1_OCTET_STRING **piKeyHash, piKeyHash, ASN1_INTEGER **pserial, pserial, OCSP_CERTID *cid, cid,
+ return 0, return)
+DEFINEFUNC2(OCSP_RESPONSE *, OCSP_response_create, int status, status, OCSP_BASICRESP *bs, bs, return nullptr, return)
+DEFINEFUNC(const STACK_OF(X509) *, OCSP_resp_get0_certs, const OCSP_BASICRESP *bs, bs, return nullptr, return)
+DEFINEFUNC2(int, OCSP_id_cmp, OCSP_CERTID *a, a, OCSP_CERTID *b, b, return -1, return)
+DEFINEFUNC7(OCSP_SINGLERESP *, OCSP_basic_add1_status, OCSP_BASICRESP *r, r, OCSP_CERTID *c, c, int s, s,
+ int re, re, ASN1_TIME *rt, rt, ASN1_TIME *t, t, ASN1_TIME *n, n, return nullptr, return)
+DEFINEFUNC(OCSP_BASICRESP *, OCSP_BASICRESP_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC2(int, i2d_OCSP_RESPONSE, OCSP_RESPONSE *r, r, unsigned char **ppout, ppout, return 0, return)
+DEFINEFUNC6(int, OCSP_basic_sign, OCSP_BASICRESP *br, br, X509 *signer, signer, EVP_PKEY *key, key,
+ const EVP_MD *dg, dg, STACK_OF(X509) *cs, cs, unsigned long flags, flags, return 0, return)
+#endif // ocsp
+
+DEFINEFUNC(void, AUTHORITY_INFO_ACCESS_free, AUTHORITY_INFO_ACCESS *p, p, return, return)
+DEFINEFUNC2(void, BIO_set_data, BIO *a, a, void *ptr, ptr, return, DUMMYARG)
+DEFINEFUNC(void *, BIO_get_data, BIO *a, a, return nullptr, return)
+DEFINEFUNC2(void, BIO_set_init, BIO *a, a, int init, init, return, DUMMYARG)
+DEFINEFUNC(int, BIO_get_shutdown, BIO *a, a, return -1, return)
+DEFINEFUNC2(void, BIO_set_shutdown, BIO *a, a, int shut, shut, return, DUMMYARG)
+
+DEFINEFUNC(long, ASN1_INTEGER_get, ASN1_INTEGER *a, a, return 0, return)
+DEFINEFUNC2(int, ASN1_INTEGER_cmp, const ASN1_INTEGER *a, a, const ASN1_INTEGER *b, b, return 1, return)
+DEFINEFUNC(int, ASN1_STRING_length, ASN1_STRING *a, a, return 0, return)
+DEFINEFUNC2(int, ASN1_STRING_to_UTF8, unsigned char **a, a, ASN1_STRING *b, b, return 0, return)
+DEFINEFUNC2(int, ASN1_TIME_to_tm, const ASN1_TIME *s, s, struct tm *tm, tm, return 0, return)
+DEFINEFUNC4(long, BIO_ctrl, BIO *a, a, int b, b, long c, c, void *d, d, return -1, return)
+DEFINEFUNC(int, BIO_free, BIO *a, a, return 0, return)
+DEFINEFUNC2(BIO *, BIO_new_mem_buf, void *a, a, int b, b, return nullptr, return)
+DEFINEFUNC3(int, BIO_read, BIO *a, a, void *b, b, int c, c, return -1, return)
+
+DEFINEFUNC3(int, BIO_write, BIO *a, a, const void *b, b, int c, c, return -1, return)
+DEFINEFUNC(int, BN_num_bits, const BIGNUM *a, a, return 0, return)
+DEFINEFUNC2(BN_ULONG, BN_mod_word, const BIGNUM *a, a, BN_ULONG w, w, return static_cast<BN_ULONG>(-1), return)
+DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, const unsigned char **b, b, long c, c, return nullptr, return)
+DEFINEFUNC2(char *, ERR_error_string, unsigned long a, a, char *b, b, return nullptr, return)
+DEFINEFUNC3(void, ERR_error_string_n, unsigned long e, e, char *b, b, size_t len, len, return, DUMMYARG)
+DEFINEFUNC(unsigned long, ERR_get_error, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(EVP_CIPHER_CTX *, EVP_CIPHER_CTX_new, void, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, EVP_CIPHER_CTX_free, EVP_CIPHER_CTX *a, a, return, DUMMYARG)
+DEFINEFUNC4(int, EVP_CIPHER_CTX_ctrl, EVP_CIPHER_CTX *ctx, ctx, int type, type, int arg, arg, void *ptr, ptr, return 0, return)
+DEFINEFUNC2(int, EVP_CIPHER_CTX_set_key_length, EVP_CIPHER_CTX *ctx, ctx, int keylen, keylen, return 0, return)
+DEFINEFUNC5(int, EVP_CipherInit, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *type, type, const unsigned char *key, key, const unsigned char *iv, iv, int enc, enc, return 0, return)
+DEFINEFUNC6(int, EVP_CipherInit_ex, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *cipher, cipher, ENGINE *impl, impl, const unsigned char *key, key, const unsigned char *iv, iv, int enc, enc, return 0, return)
+DEFINEFUNC5(int, EVP_CipherUpdate, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, const unsigned char *in, in, int inl, inl, return 0, return)
+DEFINEFUNC3(int, EVP_CipherFinal, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, return 0, return)
+DEFINEFUNC(const EVP_MD *, EVP_get_digestbyname, const char *name, name, return nullptr, return)
+#ifndef OPENSSL_NO_DES
+DEFINEFUNC(const EVP_CIPHER *, EVP_des_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
+#endif
+#ifndef OPENSSL_NO_RC2
+DEFINEFUNC(const EVP_CIPHER *, EVP_rc2_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
+#endif
+#ifndef OPENSSL_NO_AES
+DEFINEFUNC(const EVP_CIPHER *, EVP_aes_128_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const EVP_CIPHER *, EVP_aes_192_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const EVP_CIPHER *, EVP_aes_256_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
+#endif
+DEFINEFUNC(const EVP_MD *, EVP_sha1, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, EVP_PKEY_free, EVP_PKEY *a, a, return, DUMMYARG)
+DEFINEFUNC(EVP_PKEY *, EVP_PKEY_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(int, EVP_PKEY_type, int a, a, return NID_undef, return)
+DEFINEFUNC2(int, i2d_X509, X509 *a, a, unsigned char **b, b, return -1, return)
+DEFINEFUNC(const char *, OBJ_nid2sn, int a, a, return nullptr, return)
+DEFINEFUNC(const char *, OBJ_nid2ln, int a, a, return nullptr, return)
+DEFINEFUNC(int, OBJ_sn2nid, const char *s, s, return 0, return)
+DEFINEFUNC(int, OBJ_ln2nid, const char *s, s, return 0, return)
+DEFINEFUNC3(int, i2t_ASN1_OBJECT, char *a, a, int b, b, ASN1_OBJECT *c, c, return -1, return)
+DEFINEFUNC4(int, OBJ_obj2txt, char *a, a, int b, b, ASN1_OBJECT *c, c, int d, d, return -1, return)
+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)
+
+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)
+DEFINEFUNC2(int, PEM_write_bio_PUBKEY, BIO *a, a, EVP_PKEY *b, b, return 0, return)
+DEFINEFUNC2(void, RAND_seed, const void *a, a, int b, b, return, DUMMYARG)
+DEFINEFUNC(int, RAND_status, void, DUMMYARG, return -1, return)
+DEFINEFUNC2(int, RAND_bytes, unsigned char *b, b, int n, n, return 0, return)
+DEFINEFUNC(int, SSL_accept, SSL *a, a, return -1, return)
+DEFINEFUNC(int, SSL_clear, SSL *a, a, return -1, return)
+DEFINEFUNC3(char *, SSL_CIPHER_description, const SSL_CIPHER *a, a, char *b, b, int c, c, return nullptr, return)
+DEFINEFUNC2(int, SSL_CIPHER_get_bits, const SSL_CIPHER *a, a, int *b, b, return 0, return)
+DEFINEFUNC(BIO *, SSL_get_rbio, const SSL *s, s, return nullptr, return)
+DEFINEFUNC(int, SSL_connect, SSL *a, a, return -1, return)
+DEFINEFUNC(int, SSL_CTX_check_private_key, const SSL_CTX *a, a, return -1, return)
+DEFINEFUNC4(long, SSL_CTX_ctrl, SSL_CTX *a, a, int b, b, long c, c, void *d, d, return -1, return)
+DEFINEFUNC(void, SSL_CTX_free, SSL_CTX *a, a, return, DUMMYARG)
+DEFINEFUNC(SSL_CTX *, SSL_CTX_new, const SSL_METHOD *a, a, return nullptr, return)
+DEFINEFUNC2(int, SSL_CTX_set_cipher_list, SSL_CTX *a, a, const char *b, b, return -1, return)
+DEFINEFUNC3(long, SSL_CTX_callback_ctrl, SSL_CTX *ctx, ctx, int dst, dst, GenericCallbackType cb, cb, return 0, return)
+DEFINEFUNC(int, SSL_CTX_set_default_verify_paths, SSL_CTX *a, a, return -1, return)
+DEFINEFUNC3(void, SSL_CTX_set_verify, SSL_CTX *a, a, int b, b, int (*c)(int, X509_STORE_CTX *), c, return, DUMMYARG)
+DEFINEFUNC2(void, SSL_CTX_set_verify_depth, SSL_CTX *a, a, int b, b, return, DUMMYARG)
+DEFINEFUNC2(int, SSL_CTX_use_certificate, SSL_CTX *a, a, X509 *b, b, return -1, return)
+DEFINEFUNC3(int, SSL_CTX_use_certificate_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return)
+DEFINEFUNC2(int, SSL_CTX_use_PrivateKey, SSL_CTX *a, a, EVP_PKEY *b, b, return -1, return)
+DEFINEFUNC3(int, SSL_CTX_use_PrivateKey_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return)
+DEFINEFUNC(X509_STORE *, SSL_CTX_get_cert_store, const SSL_CTX *a, a, return nullptr, return)
+DEFINEFUNC(SSL_CONF_CTX *, SSL_CONF_CTX_new, DUMMYARG, DUMMYARG, return nullptr, return);
+DEFINEFUNC(void, SSL_CONF_CTX_free, SSL_CONF_CTX *a, a, return ,return);
+DEFINEFUNC2(void, SSL_CONF_CTX_set_ssl_ctx, SSL_CONF_CTX *a, a, SSL_CTX *b, b, return, return);
+DEFINEFUNC2(unsigned int, SSL_CONF_CTX_set_flags, SSL_CONF_CTX *a, a, unsigned int b, b, return 0, return);
+DEFINEFUNC(int, SSL_CONF_CTX_finish, SSL_CONF_CTX *a, a, return 0, return);
+DEFINEFUNC3(int, SSL_CONF_cmd, SSL_CONF_CTX *a, a, const char *b, b, const char *c, c, return 0, return);
+DEFINEFUNC(void, SSL_free, SSL *a, a, return, DUMMYARG)
+DEFINEFUNC(STACK_OF(SSL_CIPHER) *, SSL_get_ciphers, const SSL *a, a, return nullptr, return)
+DEFINEFUNC(const SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return nullptr, return)
+DEFINEFUNC(int, SSL_version, const SSL *a, a, return 0, return)
+DEFINEFUNC2(int, SSL_get_error, SSL *a, a, int b, b, return -1, return)
+DEFINEFUNC(STACK_OF(X509) *, SSL_get_peer_cert_chain, SSL *a, a, return nullptr, return)
+
+#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3
+DEFINEFUNC(X509 *, SSL_get1_peer_certificate, SSL *a, a, return nullptr, return)
+DEFINEFUNC(int, EVP_PKEY_get_bits, const EVP_PKEY *pkey, pkey, return -1, return)
+DEFINEFUNC(int, EVP_PKEY_get_base_id, const EVP_PKEY *pkey, pkey, return -1, return)
+#else
+DEFINEFUNC(X509 *, SSL_get_peer_certificate, SSL *a, a, return nullptr, return)
+DEFINEFUNC(int, EVP_PKEY_base_id, EVP_PKEY *a, a, return NID_undef, return)
+#endif // OPENSSL_VERSION_MAJOR >= 3
+
+DEFINEFUNC(long, SSL_get_verify_result, const SSL *a, a, return -1, return)
+DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return nullptr, return)
+DEFINEFUNC(SSL_CTX *, SSL_get_SSL_CTX, SSL *a, a, return nullptr, return)
+DEFINEFUNC4(long, SSL_ctrl, SSL *a, a, int cmd, cmd, long larg, larg, void *parg, parg, return -1, return)
+DEFINEFUNC3(int, SSL_read, SSL *a, a, void *b, b, int c, c, return -1, return)
+DEFINEFUNC3(void, SSL_set_bio, SSL *a, a, BIO *b, b, BIO *c, c, return, DUMMYARG)
+DEFINEFUNC(void, SSL_set_accept_state, SSL *a, a, return, DUMMYARG)
+DEFINEFUNC(void, SSL_set_connect_state, SSL *a, a, return, DUMMYARG)
+DEFINEFUNC(int, SSL_shutdown, SSL *a, a, return -1, return)
+DEFINEFUNC(int, SSL_in_init, const SSL *a, a, return 0, return)
+DEFINEFUNC(int, SSL_get_shutdown, const SSL *ssl, ssl, return 0, return)
+DEFINEFUNC2(int, SSL_set_session, SSL* to, to, SSL_SESSION *session, session, return -1, return)
+DEFINEFUNC(void, SSL_SESSION_free, SSL_SESSION *ses, ses, return, DUMMYARG)
+DEFINEFUNC(SSL_SESSION*, SSL_get1_session, SSL *ssl, ssl, return nullptr, return)
+DEFINEFUNC(SSL_SESSION*, SSL_get_session, const SSL *ssl, ssl, return nullptr, return)
+DEFINEFUNC3(int, SSL_set_ex_data, SSL *ssl, ssl, int idx, idx, void *arg, arg, return 0, return)
+DEFINEFUNC2(void *, SSL_get_ex_data, const SSL *ssl, ssl, int idx, idx, return nullptr, return)
+
+#ifndef OPENSSL_NO_PSK
+DEFINEFUNC2(void, SSL_set_psk_client_callback, SSL* ssl, ssl, q_psk_client_callback_t callback, callback, return, DUMMYARG)
+DEFINEFUNC2(void, SSL_set_psk_server_callback, SSL* ssl, ssl, q_psk_server_callback_t callback, callback, return, DUMMYARG)
+DEFINEFUNC2(int, SSL_CTX_use_psk_identity_hint, SSL_CTX* ctx, ctx, const char *hint, hint, return 0, return)
+#endif // !OPENSSL_NO_PSK
+
+DEFINEFUNC3(int, SSL_write, SSL *a, a, const void *b, b, int c, c, return -1, return)
+DEFINEFUNC2(int, X509_cmp, X509 *a, a, X509 *b, b, return -1, return)
+DEFINEFUNC4(int, X509_digest, const X509 *x509, x509, const EVP_MD *type, type, unsigned char *md, md, unsigned int *len, len, return -1, return)
+DEFINEFUNC(X509 *, X509_dup, X509 *a, a, return nullptr, return)
+DEFINEFUNC2(void, X509_print, BIO *a, a, X509 *b, b, return, DUMMYARG);
+DEFINEFUNC(ASN1_OBJECT *, X509_EXTENSION_get_object, X509_EXTENSION *a, a, return nullptr, return)
+DEFINEFUNC(void, X509_free, X509 *a, a, return, DUMMYARG)
+//Q_AUTOTEST_EXPORT ASN1_TIME *q_X509_gmtime_adj(ASN1_TIME *s, long adj);
+DEFINEFUNC2(ASN1_TIME *, X509_gmtime_adj, ASN1_TIME *s, s, long adj, adj, return nullptr, return)
+DEFINEFUNC(void, ASN1_TIME_free, ASN1_TIME *t, t, return, DUMMYARG)
+DEFINEFUNC2(X509_EXTENSION *, X509_get_ext, X509 *a, a, int b, b, return nullptr, return)
+DEFINEFUNC(int, X509_get_ext_count, X509 *a, a, return 0, return)
+DEFINEFUNC4(void *, X509_get_ext_d2i, X509 *a, a, int b, b, int *c, c, int *d, d, return nullptr, return)
+DEFINEFUNC(const X509V3_EXT_METHOD *, X509V3_EXT_get, X509_EXTENSION *a, a, return nullptr, return)
+DEFINEFUNC(void *, X509V3_EXT_d2i, X509_EXTENSION *a, a, return nullptr, return)
+DEFINEFUNC(int, X509_EXTENSION_get_critical, X509_EXTENSION *a, a, return 0, return)
+DEFINEFUNC(ASN1_OCTET_STRING *, X509_EXTENSION_get_data, X509_EXTENSION *a, a, return nullptr, return)
+DEFINEFUNC(void, BASIC_CONSTRAINTS_free, BASIC_CONSTRAINTS *a, a, return, DUMMYARG)
+DEFINEFUNC(void, AUTHORITY_KEYID_free, AUTHORITY_KEYID *a, a, return, DUMMYARG)
+DEFINEFUNC(void, GENERAL_NAME_free, GENERAL_NAME *a, a, return, DUMMYARG)
+DEFINEFUNC2(int, ASN1_STRING_print, BIO *a, a, const ASN1_STRING *b, b, return 0, return)
+DEFINEFUNC2(int, X509_check_issued, X509 *a, a, X509 *b, b, return -1, return)
+DEFINEFUNC(X509_NAME *, X509_get_issuer_name, X509 *a, a, return nullptr, return)
+DEFINEFUNC(X509_NAME *, X509_get_subject_name, X509 *a, a, return nullptr, return)
+DEFINEFUNC(ASN1_INTEGER *, X509_get_serialNumber, X509 *a, a, return nullptr, return)
+DEFINEFUNC(int, X509_verify_cert, X509_STORE_CTX *a, a, return -1, return)
+DEFINEFUNC(int, X509_NAME_entry_count, X509_NAME *a, a, return 0, return)
+DEFINEFUNC2(X509_NAME_ENTRY *, X509_NAME_get_entry, X509_NAME *a, a, int b, b, return nullptr, return)
+DEFINEFUNC(ASN1_STRING *, X509_NAME_ENTRY_get_data, X509_NAME_ENTRY *a, a, return nullptr, return)
+DEFINEFUNC(ASN1_OBJECT *, X509_NAME_ENTRY_get_object, X509_NAME_ENTRY *a, a, return nullptr, return)
+DEFINEFUNC(EVP_PKEY *, X509_PUBKEY_get, X509_PUBKEY *a, a, return nullptr, return)
+DEFINEFUNC(void, X509_STORE_free, X509_STORE *a, a, return, DUMMYARG)
+DEFINEFUNC(X509_STORE *, X509_STORE_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC2(int, X509_STORE_add_cert, X509_STORE *a, a, X509 *b, b, return 0, return)
+DEFINEFUNC(void, X509_STORE_CTX_free, X509_STORE_CTX *a, a, return, DUMMYARG)
+DEFINEFUNC4(int, X509_STORE_CTX_init, X509_STORE_CTX *a, a, X509_STORE *b, b, X509 *c, c, STACK_OF(X509) *d, d, return -1, return)
+DEFINEFUNC2(int, X509_STORE_CTX_set_purpose, X509_STORE_CTX *a, a, int b, b, return -1, return)
+DEFINEFUNC(int, X509_STORE_CTX_get_error, X509_STORE_CTX *a, a, return -1, return)
+DEFINEFUNC(int, X509_STORE_CTX_get_error_depth, X509_STORE_CTX *a, a, return -1, return)
+DEFINEFUNC(X509 *, X509_STORE_CTX_get_current_cert, X509_STORE_CTX *a, a, return nullptr, return)
+DEFINEFUNC(X509_STORE *, X509_STORE_CTX_get0_store, X509_STORE_CTX *ctx, ctx, return nullptr, return)
+DEFINEFUNC(X509_STORE_CTX *, X509_STORE_CTX_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC2(void *, X509_STORE_CTX_get_ex_data, X509_STORE_CTX *ctx, ctx, int idx, idx, return nullptr, return)
+DEFINEFUNC(int, SSL_get_ex_data_X509_STORE_CTX_idx, DUMMYARG, DUMMYARG, return -1, return)
+
+#if OPENSSL_VERSION_MAJOR < 3
+DEFINEFUNC3(int, SSL_CTX_load_verify_locations, SSL_CTX *ctx, ctx, const char *CAfile, CAfile, const char *CApath, CApath, return 0, return)
+#else
+DEFINEFUNC2(int, SSL_CTX_load_verify_dir, SSL_CTX *ctx, ctx, const char *CApath, CApath, return 0, return)
+#endif // OPENSSL_VERSION_MAJOR
+
+DEFINEFUNC2(int, i2d_SSL_SESSION, SSL_SESSION *in, in, unsigned char **pp, pp, return 0, return)
+DEFINEFUNC3(SSL_SESSION *, d2i_SSL_SESSION, SSL_SESSION **a, a, const unsigned char **pp, pp, long length, length, return nullptr, return)
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+DEFINEFUNC6(int, SSL_select_next_proto, unsigned char **out, out, unsigned char *outlen, outlen,
+ const unsigned char *in, in, unsigned int inlen, inlen,
+ const unsigned char *client, client, unsigned int client_len, client_len,
+ return -1, return)
+DEFINEFUNC3(void, SSL_CTX_set_next_proto_select_cb, SSL_CTX *s, s,
+ int (*cb) (SSL *ssl, unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen, void *arg), cb,
+ void *arg, arg, return, DUMMYARG)
+DEFINEFUNC3(void, SSL_get0_next_proto_negotiated, const SSL *s, s,
+ const unsigned char **data, data, unsigned *len, len, return, DUMMYARG)
+DEFINEFUNC3(int, SSL_set_alpn_protos, SSL *s, s, const unsigned char *protos, protos,
+ unsigned protos_len, protos_len, return -1, return)
+DEFINEFUNC3(void, SSL_CTX_set_alpn_select_cb, SSL_CTX *s, s,
+ int (*cb) (SSL *ssl, const unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen, void *arg), cb,
+ void *arg, arg, return, DUMMYARG)
+DEFINEFUNC3(void, SSL_get0_alpn_selected, const SSL *s, s, const unsigned char **data, data,
+ unsigned *len, len, return, DUMMYARG)
+#endif // !OPENSSL_NO_NEXTPROTONEG
+
+// DTLS:
+#if QT_CONFIG(dtls)
+DEFINEFUNC2(void, SSL_CTX_set_cookie_generate_cb, SSL_CTX *ctx, ctx, CookieGenerateCallback cb, cb, return, DUMMYARG)
+DEFINEFUNC2(void, SSL_CTX_set_cookie_verify_cb, SSL_CTX *ctx, ctx, CookieVerifyCallback cb, cb, return, DUMMYARG)
+DEFINEFUNC(const SSL_METHOD *, DTLS_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const SSL_METHOD *, DTLS_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
+#endif // dtls
+DEFINEFUNC2(void, BIO_set_flags, BIO *b, b, int flags, flags, return, DUMMYARG)
+DEFINEFUNC2(void, BIO_clear_flags, BIO *b, b, int flags, flags, return, DUMMYARG)
+DEFINEFUNC2(void *, BIO_get_ex_data, BIO *b, b, int idx, idx, return nullptr, return)
+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)
+
+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)
+#endif // OPENSSL_NO_EC
+
+DEFINEFUNC5(int, PKCS12_parse, PKCS12 *p12, p12, const char *pass, pass, EVP_PKEY **pkey, pkey, \
+ X509 **cert, cert, STACK_OF(X509) **ca, ca, return 1, return);
+DEFINEFUNC2(PKCS12 *, d2i_PKCS12_bio, BIO *bio, bio, PKCS12 **pkcs12, pkcs12, return nullptr, return);
+DEFINEFUNC(void, PKCS12_free, PKCS12 *pkcs12, pkcs12, return, DUMMYARG)
+
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+
+DEFINEFUNC4(DSA *, PEM_read_bio_DSA_PUBKEY, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+DEFINEFUNC4(RSA *, PEM_read_bio_RSA_PUBKEY, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+DEFINEFUNC4(DSA *, PEM_read_bio_DSAPrivateKey, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+DEFINEFUNC4(RSA *, PEM_read_bio_RSAPrivateKey, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+
+DEFINEFUNC2(int, PEM_write_bio_DSA_PUBKEY, BIO *a, a, DSA *b, b, return 0, return)
+DEFINEFUNC2(int, PEM_write_bio_RSA_PUBKEY, BIO *a, a, RSA *b, b, return 0, return)
+DEFINEFUNC7(int, PEM_write_bio_DSAPrivateKey, BIO *a, a, DSA *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_RSAPrivateKey, BIO *a, a, RSA *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)
+
+DEFINEFUNC2(int, SSL_CTX_use_RSAPrivateKey, SSL_CTX *a, a, RSA *b, b, return -1, return)
+
+DEFINEFUNC(DSA *, DSA_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, DSA_free, DSA *a, a, return, DUMMYARG)
+
+DEFINEFUNC(RSA *, RSA_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, RSA_free, RSA *a, a, return, DUMMYARG)
+
+DEFINEFUNC(int, RSA_bits, RSA *a, a, return 0, return)
+DEFINEFUNC(int, DSA_bits, DSA *a, a, return 0, return)
+DEFINEFUNC(int, DH_bits, DH *dh, dh, return 0, return)
+
+DEFINEFUNC(DSA *, EVP_PKEY_get1_DSA, EVP_PKEY *a, a, return nullptr, return)
+DEFINEFUNC(RSA *, EVP_PKEY_get1_RSA, EVP_PKEY *a, a, return nullptr, return)
+DEFINEFUNC(DH *, EVP_PKEY_get1_DH, EVP_PKEY *a, a, return nullptr, return)
+
+DEFINEFUNC2(int, EVP_PKEY_cmp, const EVP_PKEY *a, a, const EVP_PKEY *b, b, return -1, return)
+DEFINEFUNC3(int, EVP_PKEY_assign, EVP_PKEY *a, a, int b, b, void *r, r, return -1, return)
+
+DEFINEFUNC2(int, EVP_PKEY_set1_RSA, EVP_PKEY *a, a, RSA *b, b, return -1, return)
+DEFINEFUNC2(int, EVP_PKEY_set1_DSA, EVP_PKEY *a, a, DSA *b, b, return -1, return)
+DEFINEFUNC2(int, EVP_PKEY_set1_DH, EVP_PKEY *a, a, DH *b, b, return -1, return)
+
+#ifndef OPENSSL_NO_EC
+
+DEFINEFUNC4(EC_KEY *, PEM_read_bio_EC_PUBKEY, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+DEFINEFUNC4(EC_KEY *, PEM_read_bio_ECPrivateKey, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+
+DEFINEFUNC2(int, PEM_write_bio_EC_PUBKEY, BIO *a, a, EC_KEY *b, b, return 0, return)
+DEFINEFUNC7(int, PEM_write_bio_ECPrivateKey, BIO *a, a, EC_KEY *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)
+
+DEFINEFUNC(const EC_GROUP*, EC_KEY_get0_group, const EC_KEY* k, k, return nullptr, return)
+DEFINEFUNC(int, EC_GROUP_get_degree, const EC_GROUP* g, g, return 0, return)
+
+DEFINEFUNC2(int, EVP_PKEY_set1_EC_KEY, EVP_PKEY *a, a, EC_KEY *b, b, return -1, return)
+DEFINEFUNC(EC_KEY *, EVP_PKEY_get1_EC_KEY, EVP_PKEY *a, a, return nullptr, return)
+
+DEFINEFUNC(EC_KEY *, EC_KEY_dup, const EC_KEY *ec, ec, return nullptr, return)
+DEFINEFUNC(EC_KEY *, EC_KEY_new_by_curve_name, int nid, nid, return nullptr, return)
+DEFINEFUNC(void, EC_KEY_free, EC_KEY *ecdh, ecdh, return, DUMMYARG)
+
+#endif // OPENSSL_NO_EC
+
+
+
+#endif
+
+#define RESOLVEFUNC(func) \
+ if (!(_q_##func = _q_PTR_##func(libs.ssl->resolve(#func))) \
+ && !(_q_##func = _q_PTR_##func(libs.crypto->resolve(#func)))) \
+ qsslSocketCannotResolveSymbolWarning(#func);
+
+#if !defined QT_LINKED_OPENSSL
+
+#if !QT_CONFIG(library)
+bool q_resolveOpenSslSymbols()
+{
+ qCWarning(lcTlsBackend, "QSslSocket: unable to resolve symbols. Qt is configured without the "
+ "'library' feature, which means runtime resolving of libraries won't work.");
+ qCWarning(lcTlsBackend, "Either compile Qt statically or with support for runtime resolving "
+ "of libraries.");
+ return false;
+}
+#else
+
+# ifdef Q_OS_UNIX
+struct NumericallyLess
+{
+ typedef bool result_type;
+ result_type operator()(QStringView lhs, QStringView rhs) const
+ {
+ bool ok = false;
+ int b = 0;
+ int a = lhs.toInt(&ok);
+ if (ok)
+ b = rhs.toInt(&ok);
+ if (ok) {
+ // both toInt succeeded
+ return a < b;
+ } else {
+ // compare as strings;
+ return lhs < rhs;
+ }
+ }
+};
+
+struct LibGreaterThan
+{
+ typedef bool result_type;
+ result_type operator()(QStringView lhs, QStringView rhs) const
+ {
+ const auto lhsparts = lhs.split(u'.');
+ const auto rhsparts = rhs.split(u'.');
+ 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(),
+ lhsparts.begin() + 1, lhsparts.end(),
+ NumericallyLess());
+ }
+};
+
+#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
+static int dlIterateCallback(struct dl_phdr_info *info, size_t size, void *data)
+{
+ if (size < sizeof (info->dlpi_addr) + sizeof (info->dlpi_name))
+ return 1;
+ QDuplicateTracker<QString> *paths = (QDuplicateTracker<QString> *)data;
+ QString path = QString::fromLocal8Bit(info->dlpi_name);
+ if (!path.isEmpty()) {
+ QFileInfo fi(path);
+ path = fi.absolutePath();
+ if (!path.isEmpty())
+ (void)paths->hasSeen(std::move(path));
+ }
+ return 0;
+}
+#endif
+
+static QStringList libraryPathList()
+{
+ QStringList paths;
+# ifdef Q_OS_DARWIN
+ paths = QString::fromLatin1(qgetenv("DYLD_LIBRARY_PATH")).split(u':', Qt::SkipEmptyParts);
+
+ // search in .app/Contents/Frameworks
+ UInt32 packageType;
+ CFBundleGetPackageInfo(CFBundleGetMainBundle(), &packageType, nullptr);
+ if (packageType == FOUR_CHAR_CODE('APPL')) {
+ QUrl bundleUrl = QUrl::fromCFURL(QCFType<CFURLRef>(CFBundleCopyBundleURL(CFBundleGetMainBundle())));
+ QUrl frameworksUrl = QUrl::fromCFURL(QCFType<CFURLRef>(CFBundleCopyPrivateFrameworksURL(CFBundleGetMainBundle())));
+ paths << bundleUrl.resolved(frameworksUrl).path();
+ }
+# else
+ paths = QString::fromLatin1(qgetenv("LD_LIBRARY_PATH")).split(u':', Qt::SkipEmptyParts);
+# endif
+ paths << "/lib"_L1 << "/usr/lib"_L1 << "/usr/local/lib"_L1;
+ paths << "/lib64"_L1 << "/usr/lib64"_L1 << "/usr/local/lib64"_L1;
+ paths << "/lib32"_L1 << "/usr/lib32"_L1 << "/usr/local/lib32"_L1;
+
+#if defined(Q_OS_ANDROID)
+ paths << "/system/lib"_L1;
+#elif defined(Q_OS_LINUX)
+ // discover paths of already loaded libraries
+ QDuplicateTracker<QString> loadedPaths;
+ dl_iterate_phdr(dlIterateCallback, &loadedPaths);
+ std::move(loadedPaths).appendTo(paths);
+#endif
+
+ return paths;
+}
+
+Q_NEVER_INLINE
+static QStringList findAllLibs(QLatin1StringView filter)
+{
+ const QStringList paths = libraryPathList();
+ QStringList found;
+ const QStringList filters((QString(filter)));
+
+ for (const QString &path : paths) {
+ QDir dir(path);
+ QStringList entryList = dir.entryList(filters, QDir::Files);
+
+ std::sort(entryList.begin(), entryList.end(), LibGreaterThan());
+ for (const QString &entry : std::as_const(entryList))
+ found << path + u'/' + entry;
+ }
+
+ return found;
+}
+
+static QStringList findAllLibSsl()
+{
+ return findAllLibs("libssl.*"_L1);
+}
+
+static QStringList findAllLibCrypto()
+{
+ return findAllLibs("libcrypto.*"_L1);
+}
+# 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 {
+ std::unique_ptr<QSystemLibrary> ssl, crypto;
+};
+
+static bool tryToLoadOpenSslWin32Library(QLatin1StringView ssleay32LibName, QLatin1StringView libeay32LibName, LoadedOpenSsl &result)
+{
+ auto ssleay32 = std::make_unique<QSystemLibrary>(ssleay32LibName);
+ if (!ssleay32->load(false)) {
+ return FALSE;
+ }
+
+ auto libeay32 = std::make_unique<QSystemLibrary>(libeay32LibName);
+ if (!libeay32->load(false)) {
+ return FALSE;
+ }
+
+ result.ssl = std::move(ssleay32);
+ result.crypto = std::move(libeay32);
+ return TRUE;
+}
+
+static LoadedOpenSsl loadOpenSsl()
+{
+ LoadedOpenSsl result;
+
+ // With OpenSSL 1.1 the names have changed to libssl-1_1 and libcrypto-1_1 for builds using
+ // 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 defined(Q_PROCESSOR_X86_64)
+#define QT_SSL_SUFFIX "-x64"
+#elif defined(Q_PROCESSOR_ARM_64)
+#define QT_SSL_SUFFIX "-arm64"
+#elif defined(Q_PROCESSOR_ARM_32)
+#define QT_SSL_SUFFIX "-arm"
+#else
+#define QT_SSL_SUFFIX
+#endif
+
+ tryToLoadOpenSslWin32Library("libssl-" QT_OPENSSL_VERSION QT_SSL_SUFFIX ""_L1,
+ "libcrypto-" QT_OPENSSL_VERSION QT_SSL_SUFFIX ""_L1, result);
+
+#undef QT_SSL_SUFFIX
+ return result;
+}
+#else // !Q_OS_WIN:
+
+struct LoadedOpenSsl {
+ std::unique_ptr<QLibrary> ssl, crypto;
+};
+
+static LoadedOpenSsl loadOpenSsl()
+{
+ LoadedOpenSsl result = { std::make_unique<QLibrary>(), std::make_unique<QLibrary>() };
+
+# if defined(Q_OS_UNIX)
+ QLibrary * const libssl = result.ssl.get();
+ QLibrary * const libcrypto = result.crypto.get();
+
+ // Try to find the libssl library on the system.
+ //
+ // Up until Qt 4.3, this only searched for the "ssl" library at version -1, that
+ // is, libssl.so on most Unix systems. However, the .so file isn't present in
+ // user installations because it's considered a development file.
+ //
+ // The right thing to do is to load the library at the major version we know how
+ // to work with: the SHLIB_VERSION_NUMBER version (macro defined in opensslv.h)
+ //
+ // However, OpenSSL is a well-known case of binary-compatibility breakage. To
+ // avoid such problems, many system integrators and Linux distributions change
+ // the soname of the binary, letting the full version number be the soname. So
+ // we'll find libssl.so.0.9.7, libssl.so.0.9.8, etc. in the system. For that
+ // reason, we will search a few common paths (see findAllLibSsl() above) in hopes
+ // we find one that works.
+ //
+ // If that fails, for OpenSSL 1.0 we also try some fallbacks -- look up
+ // libssl.so with a hardcoded soname. The reason is QTBUG-68156: the binary
+ // builds of Qt happen (at the time of this writing) on RHEL machines,
+ // which change SHLIB_VERSION_NUMBER to a non-portable string. When running
+ // those binaries on the target systems, this code won't pick up
+ // libssl.so.MODIFIED_SHLIB_VERSION_NUMBER because it doesn't exist there.
+ // Given that the only 1.0 supported release (at the time of this writing)
+ // is 1.0.2, with soname "1.0.0", give that a try too. Note that we mandate
+ // OpenSSL >= 1.0.0 with a configure-time check, and OpenSSL has kept binary
+ // compatibility between 1.0.0 and 1.0.2.
+ //
+ // It is important, however, to try the canonical name and the unversioned name
+ // without going through the loop. By not specifying a path, we let the system
+ // dlopen(3) function determine it for us. This will include any DT_RUNPATH or
+ // DT_RPATH tags on our library header as well as other system-specific search
+ // paths. See the man page for dlopen(3) on your system for more information.
+
+#ifdef Q_OS_OPENBSD
+ libcrypto->setLoadHints(QLibrary::ExportExternalSymbolsHint);
+#endif
+
+#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;
+ } else {
+ libssl->unload();
+ libcrypto->unload();
+ }
+#endif // !defined(Q_OS_QNX)
+
+#ifndef Q_OS_DARWIN
+ // second attempt: find the development files libssl.so and libcrypto.so
+ //
+ // disabled on macOS/iOS:
+ // macOS's /usr/lib/libssl.dylib, /usr/lib/libcrypto.dylib will be picked up in the third
+ // attempt, _after_ <bundle>/Contents/Frameworks has been searched.
+ // iOS does not ship a system libssl.dylib, libcrypto.dylib in the first place.
+# if defined(Q_OS_ANDROID)
+ // OpenSSL 1.1.x must be suffixed otherwise it will use the system libcrypto.so libssl.so which on API-21 are OpenSSL 1.0 not 1.1
+ auto openSSLSuffix = [](const QByteArray &defaultSuffix = {}) {
+ auto suffix = qgetenv("ANDROID_OPENSSL_SUFFIX");
+ if (suffix.isEmpty())
+ return defaultSuffix;
+ return suffix;
+ };
+
+ static QString suffix = QString::fromLatin1(openSSLSuffix("_" QT_OPENSSL_VERSION));
+
+ libssl->setFileNameAndVersion("ssl"_L1 + suffix, -1);
+ libcrypto->setFileNameAndVersion("crypto"_L1 + suffix, -1);
+# else
+ libssl->setFileNameAndVersion("ssl"_L1, -1);
+ libcrypto->setFileNameAndVersion("crypto"_L1, -1);
+# endif
+ if (libcrypto->load() && libssl->load()) {
+ // libssl.so.0 and libcrypto.so.0 found
+ return result;
+ } else {
+ libssl->unload();
+ libcrypto->unload();
+ }
+#endif
+
+ // third attempt: loop on the most common library paths and find libssl
+ const QStringList sslList = findAllLibSsl();
+ const QStringList cryptoList = findAllLibCrypto();
+
+ for (const QString &crypto : cryptoList) {
+#ifdef Q_OS_DARWIN
+ // Clients should not load the unversioned libcrypto dylib as it does not have a stable ABI
+ if (crypto.endsWith("libcrypto.dylib"))
+ continue;
+#endif
+ libcrypto->setFileNameAndVersion(crypto, -1);
+ if (libcrypto->load()) {
+ QFileInfo fi(crypto);
+ QString version = fi.completeSuffix();
+
+ for (const QString &ssl : sslList) {
+ if (!ssl.endsWith(version))
+ continue;
+
+ libssl->setFileNameAndVersion(ssl, -1);
+
+ if (libssl->load()) {
+ // libssl.so.x and libcrypto.so.x found
+ return result;
+ } else {
+ libssl->unload();
+ }
+ }
+ }
+ libcrypto->unload();
+ }
+
+ // failed to load anything
+ result = {};
+ return result;
+
+# else
+ // not implemented for this platform yet
+ return result;
+# endif
+}
+#endif
+
+bool q_resolveOpenSslSymbols()
+{
+ 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(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)
+#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(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;
+ }
+#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;
+ }
+#endif // OPENSSL_VERSION_NUMBER
+
+ 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)
+#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)
+#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)
+#ifndef OPENSSL_NO_DES
+ RESOLVEFUNC(EVP_des_cbc)
+ RESOLVEFUNC(EVP_des_ede3_cbc)
+#endif
+#ifndef OPENSSL_NO_RC2
+ RESOLVEFUNC(EVP_rc2_cbc)
+#endif
+#ifndef OPENSSL_NO_AES
+ 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_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)
+#else
+ RESOLVEFUNC(SSL_get_peer_certificate)
+ RESOLVEFUNC(EVP_PKEY_base_id)
+#endif // OPENSSL_VERSION_MAJOR >= 3
+
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ RESOLVEFUNC(DH_new)
+ RESOLVEFUNC(DH_free)
+ RESOLVEFUNC(DH_check)
+ RESOLVEFUNC(DH_get0_pqg)
+
+ RESOLVEFUNC(d2i_DHparams)
+ RESOLVEFUNC(i2d_DHparams)
+
+ RESOLVEFUNC(PEM_read_bio_DHparams)
+
+ RESOLVEFUNC(EVP_PKEY_assign)
+ RESOLVEFUNC(EVP_PKEY_cmp)
+
+ RESOLVEFUNC(EVP_PKEY_set1_RSA)
+ RESOLVEFUNC(EVP_PKEY_set1_DSA)
+ RESOLVEFUNC(EVP_PKEY_set1_DH)
+
+ RESOLVEFUNC(EVP_PKEY_get1_DSA)
+ RESOLVEFUNC(EVP_PKEY_get1_RSA)
+ RESOLVEFUNC(EVP_PKEY_get1_DH)
+
+ RESOLVEFUNC(PEM_read_bio_DSA_PUBKEY)
+ RESOLVEFUNC(PEM_read_bio_RSA_PUBKEY)
+ RESOLVEFUNC(PEM_read_bio_DSAPrivateKey)
+ RESOLVEFUNC(PEM_read_bio_RSAPrivateKey)
+
+ 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)
+
+#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)
+
+#ifndef OPENSSL_NO_PSK
+ 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)
+#if OPENSSL_VERSION_MAJOR < 3
+ RESOLVEFUNC(SSL_CTX_load_verify_locations)
+#else
+ RESOLVEFUNC(SSL_CTX_load_verify_dir)
+#endif // OPENSSL_VERSION_MAJOR
+ 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)
+#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)
+#endif // dtls
+
+ RESOLVEFUNC(CRYPTO_malloc)
+ RESOLVEFUNC(BN_bin2bn)
+
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(EC_get_builtin_curves)
+#endif // OPENSSL_NO_EC
+
+ RESOLVEFUNC(PKCS12_parse)
+ RESOLVEFUNC(d2i_PKCS12_bio)
+ RESOLVEFUNC(PKCS12_free)
+ return true;
+ }();
+
+ return symbolsResolved;
+}
+#endif // QT_CONFIG(library)
+
+#else // !defined QT_LINKED_OPENSSL
+
+bool q_resolveOpenSslSymbols()
+{
+#ifdef QT_NO_OPENSSL
+ return false;
+#endif
+ return true;
+}
+#endif // !defined QT_LINKED_OPENSSL
+
+QT_END_NAMESPACE
diff --git a/src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h b/src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h
new file mode 100644
index 0000000000..a93c110b3f
--- /dev/null
+++ b/src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h
@@ -0,0 +1,765 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/****************************************************************************
+**
+** In addition, as a special exception, the copyright holders listed above give
+** permission to link the code of its release of Qt with the OpenSSL project's
+** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
+** same license as the original version), and distribute the linked executables.
+**
+** You must comply with the GNU General Public License version 2 in all
+** respects for all of the code used other than the "OpenSSL" code. If you
+** modify this file, you may extend this exception to your version of the file,
+** but you are not obligated to do so. If you do not wish to do so, delete
+** this exception statement from your version of this file.
+**
+****************************************************************************/
+
+#ifndef QSSLSOCKET_OPENSSL_SYMBOLS_P_H
+#define QSSLSOCKET_OPENSSL_SYMBOLS_P_H
+
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include "qopenssl_p.h"
+
+#include <QtCore/qglobal.h>
+
+#if QT_CONFIG(ocsp)
+#include <QtNetwork/private/qocsp_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#define DUMMYARG
+
+#if !defined QT_LINKED_OPENSSL
+// **************** Shared declarations ******************
+// ret func(arg)
+
+# define DEFINEFUNC(ret, func, arg, a, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg); \
+ static _q_PTR_##func _q_##func = nullptr; \
+ ret q_##func(arg) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func); \
+ err; \
+ } \
+ funcret _q_##func(a); \
+ }
+
+// ret func(arg1, arg2)
+# define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2); \
+ static _q_PTR_##func _q_##func = nullptr; \
+ ret q_##func(arg1, arg2) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func);\
+ err; \
+ } \
+ funcret _q_##func(a, b); \
+ }
+
+// ret func(arg1, arg2, arg3)
+# define DEFINEFUNC3(ret, func, arg1, a, arg2, b, arg3, c, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3); \
+ static _q_PTR_##func _q_##func = nullptr; \
+ ret q_##func(arg1, arg2, arg3) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c); \
+ }
+
+// ret func(arg1, arg2, arg3, arg4)
+# define DEFINEFUNC4(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4); \
+ static _q_PTR_##func _q_##func = nullptr; \
+ ret q_##func(arg1, arg2, arg3, arg4) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c, d); \
+ }
+
+// ret func(arg1, arg2, arg3, arg4, arg5)
+# define DEFINEFUNC5(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5); \
+ static _q_PTR_##func _q_##func = nullptr; \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c, d, e); \
+ }
+
+// ret func(arg1, arg2, arg3, arg4, arg6)
+# define DEFINEFUNC6(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6); \
+ static _q_PTR_##func _q_##func = nullptr; \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c, d, e, f); \
+ }
+
+// ret func(arg1, arg2, arg3, arg4, arg6, arg7)
+# define DEFINEFUNC7(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7); \
+ static _q_PTR_##func _q_##func = nullptr; \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c, d, e, f, g); \
+ }
+
+// ret func(arg1, arg2, arg3, arg4, arg6, arg7, arg8, arg9)
+# define DEFINEFUNC9(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, arg8, h, arg9, i, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); \
+ static _q_PTR_##func _q_##func = nullptr; \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { \
+ if (Q_UNLIKELY(!_q_##func)) { \
+ qsslSocketUnresolvedSymbolWarning(#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c, d, e, f, g, h, i); \
+ }
+// **************** Shared declarations ******************
+
+#else // !defined QT_LINKED_OPENSSL
+
+// **************** Static declarations ******************
+
+// ret func(arg)
+# define DEFINEFUNC(ret, func, arg, a, err, funcret) \
+ ret q_##func(arg) { funcret func(a); }
+
+// ret func(arg1, arg2)
+# define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \
+ ret q_##func(arg1, arg2) { funcret func(a, b); }
+
+// ret func(arg1, arg2, arg3)
+# define DEFINEFUNC3(ret, func, arg1, a, arg2, b, arg3, c, err, funcret) \
+ ret q_##func(arg1, arg2, arg3) { funcret func(a, b, c); }
+
+// ret func(arg1, arg2, arg3, arg4)
+# define DEFINEFUNC4(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, err, funcret) \
+ ret q_##func(arg1, arg2, arg3, arg4) { funcret func(a, b, c, d); }
+
+// ret func(arg1, arg2, arg3, arg4, arg5)
+# define DEFINEFUNC5(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, err, funcret) \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5) { funcret func(a, b, c, d, e); }
+
+// ret func(arg1, arg2, arg3, arg4, arg6)
+# define DEFINEFUNC6(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, err, funcret) \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6) { funcret func(a, b, c, d, e, f); }
+
+// ret func(arg1, arg2, arg3, arg4, arg6, arg7)
+# define DEFINEFUNC7(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, err, funcret) \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { funcret func(a, b, c, d, e, f, g); }
+
+// ret func(arg1, arg2, arg3, arg4, arg6, arg7, arg8, arg9)
+# define DEFINEFUNC9(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, arg8, h, arg9, i, err, funcret) \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { funcret func(a, b, c, d, e, f, g, h, i); }
+
+// **************** 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,
+// depending on the functionality.
+
+const unsigned char * q_ASN1_STRING_get0_data(const ASN1_STRING *x);
+
+BIO *q_BIO_new(const BIO_METHOD *a);
+const BIO_METHOD *q_BIO_s_mem();
+
+void q_AUTHORITY_INFO_ACCESS_free(AUTHORITY_INFO_ACCESS *a);
+int q_EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *c);
+int q_EVP_PKEY_up_ref(EVP_PKEY *a);
+EVP_PKEY_CTX *q_EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e);
+void q_EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx);
+int q_EVP_PKEY_param_check(EVP_PKEY_CTX *ctx);
+int q_OPENSSL_sk_num(OPENSSL_STACK *a);
+void q_OPENSSL_sk_pop_free(OPENSSL_STACK *a, void (*b)(void *));
+OPENSSL_STACK *q_OPENSSL_sk_new_null();
+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);
+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);
+int q_CRYPTO_get_ex_new_index(int class_index, long argl, void *argp, CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
+const SSL_METHOD *q_TLS_method();
+const SSL_METHOD *q_TLS_client_method();
+const SSL_METHOD *q_TLS_server_method();
+ASN1_TIME *q_X509_getm_notBefore(X509 *a);
+ASN1_TIME *q_X509_getm_notAfter(X509 *a);
+void q_ASN1_item_free(ASN1_VALUE *val, const ASN1_ITEM *it);
+void q_X509V3_conf_free(CONF_VALUE *val);
+
+void q_X509_up_ref(X509 *a);
+long q_X509_get_version(X509 *a);
+EVP_PKEY *q_X509_get_pubkey(X509 *a);
+void q_X509_STORE_set_verify_cb(X509_STORE *ctx, X509_STORE_CTX_verify_cb verify_cb);
+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);
+
+# define q_SSL_load_error_strings() q_OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS \
+ | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL)
+
+#define q_SKM_sk_num(st) q_OPENSSL_sk_num((OPENSSL_STACK *)st)
+#define q_SKM_sk_value(type, st,i) (type *)q_OPENSSL_sk_value((OPENSSL_STACK *)st, i)
+
+#define q_OPENSSL_add_all_algorithms_conf() q_OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \
+ | OPENSSL_INIT_ADD_ALL_DIGESTS \
+ | OPENSSL_INIT_LOAD_CONFIG, NULL)
+#define q_OPENSSL_add_all_algorithms_noconf() q_OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \
+ | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL)
+
+int q_OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings);
+
+long q_OpenSSL_version_num();
+const char *q_OpenSSL_version(int type);
+
+unsigned long q_SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *session);
+unsigned long q_SSL_set_options(SSL *s, unsigned long op);
+
+#ifdef TLS1_3_VERSION
+int q_SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str);
+
+// The functions below do not really have to be ifdefed like this, but for now
+// they only used in TLS 1.3 handshake (and probably future versions).
+// Plus, 'is resumalbe' is OpenSSL 1.1.1-only (and again we need it for
+// TLS 1.3-specific session management).
+
+extern "C"
+{
+using NewSessionCallback = int (*)(SSL *, SSL_SESSION *);
+}
+
+void q_SSL_CTX_sess_set_new_cb(SSL_CTX *ctx, NewSessionCallback cb);
+int q_SSL_SESSION_is_resumable(const SSL_SESSION *s);
+
+#define q_SSL_CTX_set_session_cache_mode(ctx,m) \
+ q_SSL_CTX_ctrl(ctx,SSL_CTRL_SET_SESS_CACHE_MODE,m,NULL)
+
+#endif
+
+#if QT_CONFIG(dtls)
+// Functions and types required for DTLS support:
+extern "C"
+{
+
+typedef int (*CookieVerifyCallback)(SSL *, const unsigned char *, unsigned);
+typedef int (*DgramWriteCallback) (BIO *, const char *, int);
+typedef int (*DgramReadCallback) (BIO *, char *, int);
+typedef int (*DgramPutsCallback) (BIO *, const char *);
+typedef long (*DgramCtrlCallback) (BIO *, int, long, void *);
+typedef int (*DgramCreateCallback) (BIO *);
+typedef int (*DgramDestroyCallback) (BIO *);
+
+}
+
+int q_DTLSv1_listen(SSL *s, BIO_ADDR *client);
+BIO_ADDR *q_BIO_ADDR_new();
+void q_BIO_ADDR_free(BIO_ADDR *ap);
+
+// API we need for a custom dgram BIO:
+
+BIO_METHOD *q_BIO_meth_new(int type, const char *name);
+void q_BIO_meth_free(BIO_METHOD *biom);
+int q_BIO_meth_set_write(BIO_METHOD *biom, DgramWriteCallback);
+int q_BIO_meth_set_read(BIO_METHOD *biom, DgramReadCallback);
+int q_BIO_meth_set_puts(BIO_METHOD *biom, DgramPutsCallback);
+int q_BIO_meth_set_ctrl(BIO_METHOD *biom, DgramCtrlCallback);
+int q_BIO_meth_set_create(BIO_METHOD *biom, DgramCreateCallback);
+int q_BIO_meth_set_destroy(BIO_METHOD *biom, DgramDestroyCallback);
+
+#endif // dtls
+
+void q_BIO_set_data(BIO *a, void *ptr);
+void *q_BIO_get_data(BIO *a);
+void q_BIO_set_init(BIO *a, int init);
+int q_BIO_get_shutdown(BIO *a);
+void q_BIO_set_shutdown(BIO *a, int shut);
+
+#if QT_CONFIG(ocsp)
+const OCSP_CERTID *q_OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP *x);
+#endif // ocsp
+
+#define q_SSL_CTX_set_min_proto_version(ctx, version) \
+ q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, nullptr)
+
+#define q_SSL_CTX_set_max_proto_version(ctx, version) \
+ q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, nullptr)
+
+extern "C" {
+typedef int (*q_SSL_psk_use_session_cb_func_t)(SSL *, const EVP_MD *, const unsigned char **, size_t *,
+ SSL_SESSION **);
+}
+void q_SSL_set_psk_use_session_callback(SSL *s, q_SSL_psk_use_session_cb_func_t);
+// Here the content of the 1.1 header ends.
+
+bool q_resolveOpenSslSymbols();
+long q_ASN1_INTEGER_get(ASN1_INTEGER *a);
+int q_ASN1_INTEGER_cmp(const ASN1_INTEGER *x, const ASN1_INTEGER *y);
+int q_ASN1_STRING_length(ASN1_STRING *a);
+int q_ASN1_STRING_to_UTF8(unsigned char **a, ASN1_STRING *b);
+int q_ASN1_TIME_to_tm(const ASN1_TIME *s, struct tm *tm);
+long q_BIO_ctrl(BIO *a, int b, long c, void *d);
+int q_BIO_free(BIO *a);
+BIO *q_BIO_new_mem_buf(void *a, int b);
+int q_BIO_read(BIO *a, void *b, int c);
+int q_BIO_write(BIO *a, const void *b, int c);
+int q_BN_num_bits(const BIGNUM *a);
+int q_BN_is_word(BIGNUM *a, BN_ULONG w);
+BN_ULONG q_BN_mod_word(const BIGNUM *a, BN_ULONG w);
+
+X509 *q_d2i_X509(X509 **a, const unsigned char **b, long c);
+char *q_ERR_error_string(unsigned long a, char *b);
+void q_ERR_error_string_n(unsigned long e, char *buf, size_t len);
+unsigned long q_ERR_get_error();
+EVP_CIPHER_CTX *q_EVP_CIPHER_CTX_new();
+void q_EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *a);
+int q_EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr);
+int q_EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *x, int keylen);
+int q_EVP_CipherInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, const unsigned char *key, const unsigned char *iv, int enc);
+int q_EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc);
+int q_EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl);
+int q_EVP_CipherFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
+const EVP_MD *q_EVP_get_digestbyname(const char *name);
+
+#ifndef OPENSSL_NO_DES
+const EVP_CIPHER *q_EVP_des_cbc();
+const EVP_CIPHER *q_EVP_des_ede3_cbc();
+#endif // OPENSSL_NO_DES
+
+#ifndef OPENSSL_NO_RC2
+const EVP_CIPHER *q_EVP_rc2_cbc();
+#endif // OPENSSL_NO_RC2
+
+#ifndef OPENSSL_NO_AES
+const EVP_CIPHER *q_EVP_aes_128_cbc();
+const EVP_CIPHER *q_EVP_aes_192_cbc();
+const EVP_CIPHER *q_EVP_aes_256_cbc();
+#endif // OPENSSL_NO_AES
+
+const EVP_MD *q_EVP_sha1();
+
+void q_EVP_PKEY_free(EVP_PKEY *a);
+int q_EVP_PKEY_type(int a);
+EVP_PKEY *q_EVP_PKEY_new();
+int q_i2d_X509(X509 *a, unsigned char **b);
+const char *q_OBJ_nid2sn(int a);
+const char *q_OBJ_nid2ln(int a);
+int q_OBJ_sn2nid(const char *s);
+int q_OBJ_ln2nid(const char *s);
+int q_i2t_ASN1_OBJECT(char *buf, int buf_len, ASN1_OBJECT *obj);
+int q_OBJ_obj2txt(char *buf, int buf_len, ASN1_OBJECT *obj, int no_name);
+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);
+
+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,
+ int e, pem_password_cb *f, void *g);
+EVP_PKEY *q_PEM_read_bio_PUBKEY(BIO *a, EVP_PKEY **b, pem_password_cb *c, void *d);
+int q_PEM_write_bio_PUBKEY(BIO *a, EVP_PKEY *b);
+
+void q_RAND_seed(const void *a, int b);
+int q_RAND_status();
+int q_RAND_bytes(unsigned char *b, int n);
+int q_SSL_accept(SSL *a);
+int q_SSL_clear(SSL *a);
+char *q_SSL_CIPHER_description(const SSL_CIPHER *a, char *b, int c);
+int q_SSL_CIPHER_get_bits(const SSL_CIPHER *a, int *b);
+BIO *q_SSL_get_rbio(const SSL *s);
+int q_SSL_connect(SSL *a);
+int q_SSL_CTX_check_private_key(const SSL_CTX *a);
+long q_SSL_CTX_ctrl(SSL_CTX *a, int b, long c, void *d);
+void q_SSL_CTX_free(SSL_CTX *a);
+SSL_CTX *q_SSL_CTX_new(const SSL_METHOD *a);
+int q_SSL_CTX_set_cipher_list(SSL_CTX *a, const char *b);
+int q_SSL_CTX_set_default_verify_paths(SSL_CTX *a);
+void q_SSL_CTX_set_verify(SSL_CTX *a, int b, int (*c)(int, X509_STORE_CTX *));
+void q_SSL_CTX_set_verify_depth(SSL_CTX *a, int b);
+extern "C" {
+typedef void (*GenericCallbackType)();
+}
+long q_SSL_CTX_callback_ctrl(SSL_CTX *, int, GenericCallbackType);
+int q_SSL_CTX_use_certificate(SSL_CTX *a, X509 *b);
+int q_SSL_CTX_use_certificate_file(SSL_CTX *a, const char *b, int c);
+int q_SSL_CTX_use_PrivateKey(SSL_CTX *a, EVP_PKEY *b);
+int q_SSL_CTX_use_PrivateKey_file(SSL_CTX *a, const char *b, int c);
+X509_STORE *q_SSL_CTX_get_cert_store(const SSL_CTX *a);
+SSL_CONF_CTX *q_SSL_CONF_CTX_new();
+void q_SSL_CONF_CTX_free(SSL_CONF_CTX *a);
+void q_SSL_CONF_CTX_set_ssl_ctx(SSL_CONF_CTX *a, SSL_CTX *b);
+unsigned int q_SSL_CONF_CTX_set_flags(SSL_CONF_CTX *a, unsigned int b);
+int q_SSL_CONF_CTX_finish(SSL_CONF_CTX *a);
+int q_SSL_CONF_cmd(SSL_CONF_CTX *a, const char *b, const char *c);
+void q_SSL_free(SSL *a);
+STACK_OF(SSL_CIPHER) *q_SSL_get_ciphers(const SSL *a);
+const SSL_CIPHER *q_SSL_get_current_cipher(SSL *a);
+int q_SSL_version(const SSL *a);
+int q_SSL_get_error(SSL *a, int b);
+STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a);
+long q_SSL_get_verify_result(const SSL *a);
+SSL *q_SSL_new(SSL_CTX *a);
+SSL_CTX *q_SSL_get_SSL_CTX(SSL *a);
+long q_SSL_ctrl(SSL *ssl,int cmd, long larg, void *parg);
+int q_SSL_read(SSL *a, void *b, int c);
+void q_SSL_set_bio(SSL *a, BIO *b, BIO *c);
+void q_SSL_set_accept_state(SSL *a);
+void q_SSL_set_connect_state(SSL *a);
+int q_SSL_shutdown(SSL *a);
+int q_SSL_in_init(const SSL *s);
+int q_SSL_get_shutdown(const SSL *ssl);
+int q_SSL_set_session(SSL *to, SSL_SESSION *session);
+void q_SSL_SESSION_free(SSL_SESSION *ses);
+SSL_SESSION *q_SSL_get1_session(SSL *ssl);
+SSL_SESSION *q_SSL_get_session(const SSL *ssl);
+int q_SSL_set_ex_data(SSL *ssl, int idx, void *arg);
+void *q_SSL_get_ex_data(const SSL *ssl, int idx);
+#ifndef OPENSSL_NO_PSK
+typedef unsigned int (*q_psk_client_callback_t)(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len);
+void q_SSL_set_psk_client_callback(SSL *ssl, q_psk_client_callback_t callback);
+typedef unsigned int (*q_psk_server_callback_t)(SSL *ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len);
+void q_SSL_set_psk_server_callback(SSL *ssl, q_psk_server_callback_t callback);
+int q_SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *hint);
+#endif // !OPENSSL_NO_PSK
+int q_SSL_write(SSL *a, const void *b, int c);
+int q_X509_cmp(X509 *a, X509 *b);
+X509 *q_X509_dup(X509 *a);
+void q_X509_print(BIO *a, X509*b);
+int q_X509_digest(const X509 *x509, const EVP_MD *type, unsigned char *md, unsigned int *len);
+ASN1_OBJECT *q_X509_EXTENSION_get_object(X509_EXTENSION *a);
+void q_X509_free(X509 *a);
+ASN1_TIME *q_X509_gmtime_adj(ASN1_TIME *s, long adj);
+void q_ASN1_TIME_free(ASN1_TIME *t);
+X509_EXTENSION *q_X509_get_ext(X509 *a, int b);
+int q_X509_get_ext_count(X509 *a);
+void *q_X509_get_ext_d2i(X509 *a, int b, int *c, int *d);
+const X509V3_EXT_METHOD *q_X509V3_EXT_get(X509_EXTENSION *a);
+void *q_X509V3_EXT_d2i(X509_EXTENSION *a);
+int q_X509_EXTENSION_get_critical(X509_EXTENSION *a);
+ASN1_OCTET_STRING *q_X509_EXTENSION_get_data(X509_EXTENSION *a);
+void q_BASIC_CONSTRAINTS_free(BASIC_CONSTRAINTS *a);
+void q_AUTHORITY_KEYID_free(AUTHORITY_KEYID *a);
+int q_ASN1_STRING_print(BIO *a, const ASN1_STRING *b);
+int q_X509_check_issued(X509 *a, X509 *b);
+X509_NAME *q_X509_get_issuer_name(X509 *a);
+X509_NAME *q_X509_get_subject_name(X509 *a);
+ASN1_INTEGER *q_X509_get_serialNumber(X509 *a);
+int q_X509_verify_cert(X509_STORE_CTX *ctx);
+int q_X509_NAME_entry_count(X509_NAME *a);
+X509_NAME_ENTRY *q_X509_NAME_get_entry(X509_NAME *a,int b);
+ASN1_STRING *q_X509_NAME_ENTRY_get_data(X509_NAME_ENTRY *a);
+ASN1_OBJECT *q_X509_NAME_ENTRY_get_object(X509_NAME_ENTRY *a);
+EVP_PKEY *q_X509_PUBKEY_get(X509_PUBKEY *a);
+void q_X509_STORE_free(X509_STORE *store);
+X509_STORE *q_X509_STORE_new();
+int q_X509_STORE_add_cert(X509_STORE *ctx, X509 *x);
+void q_X509_STORE_CTX_free(X509_STORE_CTX *storeCtx);
+int q_X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store,
+ X509 *x509, STACK_OF(X509) *chain);
+X509_STORE_CTX *q_X509_STORE_CTX_new();
+int q_X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose);
+int q_X509_STORE_CTX_get_error(X509_STORE_CTX *ctx);
+int q_X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx);
+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);
+
+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
+#define q_SSL_CTX_set_tmp_ecdh(ctx, ecdh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_ECDH, 0, (char *)ecdh)
+
+// EC curves management
+size_t q_EC_get_builtin_curves(EC_builtin_curve *r, size_t nitems);
+int q_EC_curve_nist2nid(const char *name);
+#endif // OPENSSL_NO_EC
+
+#define q_SSL_get_server_tmp_key(ssl, key) q_SSL_ctrl((ssl), SSL_CTRL_GET_SERVER_TMP_KEY, 0, (char *)key)
+
+// PKCS#12 support
+int q_PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca);
+PKCS12 *q_d2i_PKCS12_bio(BIO *bio, PKCS12 **pkcs12);
+void q_PKCS12_free(PKCS12 *pkcs12);
+
+#define q_BIO_get_mem_data(b, pp) (int)q_BIO_ctrl(b,BIO_CTRL_INFO,0,(char *)pp)
+#define q_BIO_pending(b) (int)q_BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL)
+#define q_SSL_CTX_set_mode(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_MODE,(op),NULL)
+#define q_sk_GENERAL_NAME_num(st) q_SKM_sk_num((st))
+#define q_sk_GENERAL_NAME_value(st, i) q_SKM_sk_value(GENERAL_NAME, (st), (i))
+
+void q_GENERAL_NAME_free(GENERAL_NAME *a);
+
+#define q_sk_X509_num(st) q_SKM_sk_num((st))
+#define q_sk_X509_value(st, i) q_SKM_sk_value(X509, (st), (i))
+#define q_sk_SSL_CIPHER_num(st) q_SKM_sk_num((st))
+#define q_sk_SSL_CIPHER_value(st, i) q_SKM_sk_value(SSL_CIPHER, (st), (i))
+#define q_SSL_CTX_add_extra_chain_cert(ctx,x509) \
+ q_SSL_CTX_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)x509)
+#define q_OpenSSL_add_all_algorithms() q_OPENSSL_add_all_algorithms_conf()
+
+#if OPENSSL_VERSION_MAJOR < 3
+int q_SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath);
+#else
+int q_SSL_CTX_load_verify_dir(SSL_CTX *ctx, const char *CApath);
+#endif // OPENSSL_VERSION_MAJOR
+
+int q_i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp);
+SSL_SESSION *q_d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp, long length);
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+int q_SSL_select_next_proto(unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen,
+ const unsigned char *client, unsigned int client_len);
+void q_SSL_CTX_set_next_proto_select_cb(SSL_CTX *s,
+ int (*cb) (SSL *ssl, unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen, void *arg),
+ void *arg);
+void q_SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data,
+ unsigned *len);
+int q_SSL_set_alpn_protos(SSL *ssl, const unsigned char *protos,
+ unsigned protos_len);
+void q_SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx,
+ int (*cb) (SSL *ssl,
+ const unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen,
+ void *arg), void *arg);
+void q_SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data,
+ unsigned *len);
+#endif // !OPENSSL_NO_NEXTPROTONEG
+
+
+#if QT_CONFIG(dtls)
+
+extern "C"
+{
+typedef int (*CookieGenerateCallback)(SSL *, unsigned char *, unsigned *);
+}
+
+void q_SSL_CTX_set_cookie_generate_cb(SSL_CTX *ctx, CookieGenerateCallback cb);
+void q_SSL_CTX_set_cookie_verify_cb(SSL_CTX *ctx, CookieVerifyCallback cb);
+const SSL_METHOD *q_DTLS_server_method();
+const SSL_METHOD *q_DTLS_client_method();
+
+#endif // dtls
+
+void *q_X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx, int idx);
+int q_SSL_get_ex_data_X509_STORE_CTX_idx();
+
+#if QT_CONFIG(dtls)
+#define q_DTLS_set_link_mtu(ssl, mtu) q_SSL_ctrl((ssl), DTLS_CTRL_SET_LINK_MTU, (mtu), nullptr)
+#define q_DTLSv1_get_timeout(ssl, arg) q_SSL_ctrl(ssl, DTLS_CTRL_GET_TIMEOUT, 0, arg)
+#define q_DTLSv1_handle_timeout(ssl) q_SSL_ctrl(ssl, DTLS_CTRL_HANDLE_TIMEOUT, 0, nullptr)
+#endif // dtls
+
+void q_BIO_set_flags(BIO *b, int flags);
+void q_BIO_clear_flags(BIO *b, int flags);
+void *q_BIO_get_ex_data(BIO *b, int idx);
+int q_BIO_set_ex_data(BIO *b, int idx, void *data);
+
+#define q_BIO_set_retry_read(b) q_BIO_set_flags(b, (BIO_FLAGS_READ|BIO_FLAGS_SHOULD_RETRY))
+#define q_BIO_set_retry_write(b) q_BIO_set_flags(b, (BIO_FLAGS_WRITE|BIO_FLAGS_SHOULD_RETRY))
+#define q_BIO_clear_retry_flags(b) q_BIO_clear_flags(b, (BIO_FLAGS_RWS|BIO_FLAGS_SHOULD_RETRY))
+#define q_BIO_set_app_data(s,arg) q_BIO_set_ex_data(s,0,arg)
+#define q_BIO_get_app_data(s) q_BIO_get_ex_data(s,0)
+
+#define q_SSL_set_tlsext_status_type(ssl, type) \
+ q_SSL_ctrl((ssl), SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE, (type), nullptr)
+
+#if QT_CONFIG(ocsp)
+
+OCSP_RESPONSE *q_d2i_OCSP_RESPONSE(OCSP_RESPONSE **a, const unsigned char **in, long len);
+int q_i2d_OCSP_RESPONSE(OCSP_RESPONSE *r, unsigned char **ppout);
+OCSP_RESPONSE *q_OCSP_response_create(int status, OCSP_BASICRESP *bs);
+void q_OCSP_RESPONSE_free(OCSP_RESPONSE *rs);
+int q_OCSP_response_status(OCSP_RESPONSE *resp);
+OCSP_BASICRESP *q_OCSP_response_get1_basic(OCSP_RESPONSE *resp);
+OCSP_SINGLERESP *q_OCSP_basic_add1_status(OCSP_BASICRESP *rsp, OCSP_CERTID *cid,
+ int status, int reason, ASN1_TIME *revtime,
+ ASN1_TIME *thisupd, ASN1_TIME *nextupd);
+int q_OCSP_basic_sign(OCSP_BASICRESP *brsp, X509 *signer, EVP_PKEY *key, const EVP_MD *dgst,
+ STACK_OF(X509) *certs, unsigned long flags);
+OCSP_BASICRESP *q_OCSP_BASICRESP_new();
+void q_OCSP_BASICRESP_free(OCSP_BASICRESP *bs);
+int q_OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, X509_STORE *st, unsigned long flags);
+int q_OCSP_resp_count(OCSP_BASICRESP *bs);
+OCSP_SINGLERESP *q_OCSP_resp_get0(OCSP_BASICRESP *bs, int idx);
+int q_OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason, ASN1_GENERALIZEDTIME **revtime,
+ ASN1_GENERALIZEDTIME **thisupd, ASN1_GENERALIZEDTIME **nextupd);
+int q_OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long nsec, long maxsec);
+int q_OCSP_id_get0_info(ASN1_OCTET_STRING **piNameHash, ASN1_OBJECT **pmd, ASN1_OCTET_STRING **pikeyHash,
+ ASN1_INTEGER **pserial, OCSP_CERTID *cid);
+
+const STACK_OF(X509) *q_OCSP_resp_get0_certs(const OCSP_BASICRESP *bs);
+OCSP_CERTID *q_OCSP_cert_to_id(const EVP_MD *dgst, X509 *subject, X509 *issuer);
+void q_OCSP_CERTID_free(OCSP_CERTID *cid);
+int q_OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b);
+
+#define q_SSL_get_tlsext_status_ocsp_resp(ssl, arg) \
+ q_SSL_ctrl(ssl, SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP, 0, arg)
+
+#define q_SSL_CTX_set_tlsext_status_cb(ssl, cb) \
+ q_SSL_CTX_callback_ctrl(ssl, SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB, GenericCallbackType(cb))
+
+# define q_SSL_set_tlsext_status_ocsp_resp(ssl, arg, arglen) \
+ q_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP, arglen, arg)
+
+#endif // ocsp
+
+
+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);
+const char *q_SSL_alert_desc_string_long(int value);
+
+int q_SSL_CTX_get_security_level(const SSL_CTX *ctx);
+void q_SSL_CTX_set_security_level(SSL_CTX *ctx, int level);
+
+// Here we have the ones that make difference between OpenSSL pre/post v3:
+#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3
+X509 *q_SSL_get1_peer_certificate(SSL *a);
+#define q_SSL_get_peer_certificate q_SSL_get1_peer_certificate
+int q_EVP_PKEY_get_bits(const EVP_PKEY *pkey);
+int q_EVP_PKEY_get_base_id(const EVP_PKEY *pkey);
+#define q_EVP_PKEY_base_id q_EVP_PKEY_get_base_id
+#else
+X509 *q_SSL_get_peer_certificate(SSL *a);
+int q_EVP_PKEY_base_id(EVP_PKEY *a);
+#endif // OPENSSL_VERSION_MAJOR >= 3
+
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+
+DSA *q_DSA_new();
+void q_DSA_free(DSA *a);
+
+RSA *q_RSA_new();
+void q_RSA_free(RSA *a);
+
+#ifndef OPENSSL_NO_EC
+
+EC_KEY *q_EC_KEY_dup(const EC_KEY *src);
+EC_KEY *q_EC_KEY_new_by_curve_name(int nid);
+void q_EC_KEY_free(EC_KEY *ecdh);
+
+#endif // OPENSSL_NO_EC
+
+int q_SSL_CTX_use_RSAPrivateKey(SSL_CTX *a, RSA *b);
+
+DSA *q_PEM_read_bio_DSA_PUBKEY(BIO *a, DSA **b, pem_password_cb *c, void *d);
+RSA *q_PEM_read_bio_RSA_PUBKEY(BIO *a, RSA **b, pem_password_cb *c, void *d);
+
+DSA *q_PEM_read_bio_DSAPrivateKey(BIO *a, DSA **b, pem_password_cb *c, void *d);
+RSA *q_PEM_read_bio_RSAPrivateKey(BIO *a, RSA **b, pem_password_cb *c, void *d);
+
+int q_PEM_write_bio_DSA_PUBKEY(BIO *a, DSA *b);
+int q_PEM_write_bio_RSA_PUBKEY(BIO *a, RSA *b);
+
+int q_PEM_write_bio_DSAPrivateKey(BIO *a, DSA *b, const EVP_CIPHER *c, unsigned char *d,
+ int e, pem_password_cb *f, void *g);
+int q_PEM_write_bio_RSAPrivateKey(BIO *a, RSA *b, const EVP_CIPHER *c, unsigned char *d,
+ int e, pem_password_cb *f, void *g);
+
+RSA *q_EVP_PKEY_get1_RSA(EVP_PKEY *a);
+DSA *q_EVP_PKEY_get1_DSA(EVP_PKEY *a);
+DH *q_EVP_PKEY_get1_DH(EVP_PKEY *a);
+
+int q_EVP_PKEY_set1_RSA(EVP_PKEY *a, RSA *b);
+int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b);
+int q_EVP_PKEY_set1_DH(EVP_PKEY *a, DH *b);
+
+int q_DH_bits(DH *dh);
+int q_RSA_bits(RSA *a);
+int q_DSA_bits(DSA *a);
+
+int q_EVP_PKEY_assign(EVP_PKEY *a, int b, void *r);
+int q_EVP_PKEY_cmp(const EVP_PKEY *a, const EVP_PKEY *b);
+
+#ifndef OPENSSL_NO_EC
+
+EC_KEY *q_PEM_read_bio_EC_PUBKEY(BIO *a, EC_KEY **b, pem_password_cb *c, void *d);
+EC_KEY *q_PEM_read_bio_ECPrivateKey(BIO *a, EC_KEY **b, pem_password_cb *c, void *d);
+
+int q_PEM_write_bio_ECPrivateKey(BIO *a, EC_KEY *b, const EVP_CIPHER *c, unsigned char *d,
+ int e, pem_password_cb *f, void *g);
+int q_PEM_write_bio_EC_PUBKEY(BIO *a, EC_KEY *b);
+
+EC_KEY *q_EVP_PKEY_get1_EC_KEY(EVP_PKEY *a);
+int q_EVP_PKEY_set1_EC_KEY(EVP_PKEY *a, EC_KEY *b);
+
+const EC_GROUP* q_EC_KEY_get0_group(const EC_KEY* k);
+int q_EC_GROUP_get_degree(const EC_GROUP* g);
+
+#define q_EVP_PKEY_assign_RSA(pkey,rsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_RSA,\
+ (char *)(rsa))
+#define q_EVP_PKEY_assign_DSA(pkey,dsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_DSA,\
+ (char *)(dsa))
+
+
+#endif // OPENSSL_NO_EC
+
+#endif // OPENSSL_NO_DEPRECATED_3_0
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/tls/openssl/qtls_openssl.cpp b/src/plugins/tls/openssl/qtls_openssl.cpp
new file mode 100644
index 0000000000..57d09a649b
--- /dev/null
+++ b/src/plugins/tls/openssl/qtls_openssl.cpp
@@ -0,0 +1,1860 @@
+// 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 "qsslsocket_openssl_symbols_p.h"
+#include "qx509_openssl_p.h"
+#include "qtls_openssl_p.h"
+
+#ifdef Q_OS_WIN
+#include "qwindowscarootfetcher_p.h"
+#endif
+
+#include <QtNetwork/private/qsslpresharedkeyauthenticator_p.h>
+#include <QtNetwork/private/qsslcertificate_p.h>
+#include <QtNetwork/private/qocspresponse_p.h>
+#include <QtNetwork/private/qsslsocket_p.h>
+
+#include <QtNetwork/qsslpresharedkeyauthenticator.h>
+
+#include <QtCore/qscopedvaluerollback.h>
+#include <QtCore/qscopeguard.h>
+
+#include <algorithm>
+#include <cstring>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace {
+
+QSsl::AlertLevel tlsAlertLevel(int value)
+{
+ using QSsl::AlertLevel;
+
+ if (const char *typeString = q_SSL_alert_type_string(value)) {
+ // Documented to return 'W' for warning, 'F' for fatal,
+ // 'U' for unknown.
+ switch (typeString[0]) {
+ case 'W':
+ return AlertLevel::Warning;
+ case 'F':
+ return AlertLevel::Fatal;
+ default:;
+ }
+ }
+
+ return AlertLevel::Unknown;
+}
+
+QString tlsAlertDescription(int value)
+{
+ QString description = QLatin1StringView(q_SSL_alert_desc_string_long(value));
+ if (!description.size())
+ description = "no description provided"_L1;
+ return description;
+}
+
+QSsl::AlertType tlsAlertType(int value)
+{
+ // In case for some reason openssl gives us a value,
+ // which is not in our enum actually, we leave it to
+ // an application to handle (supposedly they have
+ // if or switch-statements).
+ return QSsl::AlertType(value & 0xff);
+}
+
+#ifdef Q_OS_WIN
+
+QSslCertificate findCertificateToFetch(const QList<QSslError> &tlsErrors, bool checkAIA)
+{
+ QSslCertificate certToFetch;
+
+ for (const auto &tlsError : tlsErrors) {
+ switch (tlsError.error()) {
+ case QSslError::UnableToGetLocalIssuerCertificate: // site presented intermediate cert, but root is unknown
+ case QSslError::SelfSignedCertificateInChain: // site presented a complete chain, but root is unknown
+ certToFetch = tlsError.certificate();
+ break;
+ case QSslError::SelfSignedCertificate:
+ case QSslError::CertificateBlacklisted:
+ //With these errors, we know it will be untrusted so save time by not asking windows
+ return QSslCertificate{};
+ default:
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcTlsBackend) << tlsError.errorString();
+#endif
+ //TODO - this part is strange.
+ break;
+ }
+ }
+
+ if (checkAIA) {
+ const auto extensions = certToFetch.extensions();
+ for (const auto &ext : extensions) {
+ 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
+ //CA certificates explicitly, thus technically disabling CA fetch. So, if it's
+ //the case and an intermediate certificate is missing, and no extensions is
+ //present on the leaf certificate - we fail the handshake immediately.
+ return QSslCertificate{};
+ }
+
+ return certToFetch;
+}
+
+#endif // Q_OS_WIN
+
+} // unnamed namespace
+
+namespace QTlsPrivate {
+
+extern "C" {
+
+int q_X509Callback(int ok, X509_STORE_CTX *ctx)
+{
+ if (!ok) {
+ // Store the error and at which depth the error was detected.
+
+ using ErrorListPtr = QList<QSslErrorEntry> *;
+ ErrorListPtr errors = nullptr;
+
+ // Error list is attached to either 'SSL' or 'X509_STORE'.
+ if (X509_STORE *store = q_X509_STORE_CTX_get0_store(ctx)) // We try store first:
+ errors = ErrorListPtr(q_X509_STORE_get_ex_data(store, 0));
+
+ if (!errors) {
+ // Not found on store? Try SSL and its external data then. According to the OpenSSL's
+ // documentation:
+ //
+ // "Whenever a X509_STORE_CTX object is created for the verification of the
+ // peer's certificate during a handshake, a pointer to the SSL object is
+ // stored into the X509_STORE_CTX object to identify the connection affected.
+ // To retrieve this pointer the X509_STORE_CTX_get_ex_data() function can be
+ // used with the correct index."
+ const auto offset = QTlsBackendOpenSSL::s_indexForSSLExtraData
+ + TlsCryptographOpenSSL::errorOffsetInExData;
+ if (SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(
+ ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx()))) {
+
+ // We may be in a renegotiation, check if we are inside a call to SSL_read:
+ const auto tlsOffset = QTlsBackendOpenSSL::s_indexForSSLExtraData
+ + TlsCryptographOpenSSL::socketOffsetInExData;
+ auto tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, tlsOffset));
+ Q_ASSERT(tls);
+ if (tls->isInSslRead()) {
+ // We are in a renegotiation, make a note of this for later.
+ // We'll check that the certificate is the same as the one we got during
+ // the initial handshake
+ tls->setRenegotiated(true);
+ return 1;
+ }
+
+ errors = ErrorListPtr(q_SSL_get_ex_data(ssl, offset));
+ }
+ }
+
+ if (!errors) {
+ qCWarning(lcTlsBackend, "Neither X509_STORE, nor SSL contains error list, handshake failure");
+ return 0;
+ }
+
+ errors->append(X509CertificateOpenSSL::errorEntryFromStoreContext(ctx));
+ }
+ // Always return OK to allow verification to continue. We handle the
+ // errors gracefully after collecting all errors, after verification has
+ // completed.
+ return 1;
+}
+
+int q_X509CallbackDirect(int ok, X509_STORE_CTX *ctx)
+{
+ // Passed to SSL_CTX_set_verify()
+ // https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_verify.html
+ // Returns 0 to abort verification, 1 to continue.
+
+ // This is a new, experimental verification callback, reporting
+ // errors immediately and returning 0 or 1 depending on an application
+ // either ignoring or not ignoring verification errors as they come.
+ if (!ctx) {
+ qCWarning(lcTlsBackend, "Invalid store context (nullptr)");
+ return 0;
+ }
+
+ if (!ok) {
+ // "Whenever a X509_STORE_CTX object is created for the verification of the
+ // peer's certificate during a handshake, a pointer to the SSL object is
+ // stored into the X509_STORE_CTX object to identify the connection affected.
+ // To retrieve this pointer the X509_STORE_CTX_get_ex_data() function can be
+ // used with the correct index."
+ SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx()));
+ if (!ssl) {
+ qCWarning(lcTlsBackend, "No external data (SSL) found in X509 store object");
+ return 0;
+ }
+
+ const auto offset = QTlsBackendOpenSSL::s_indexForSSLExtraData
+ + TlsCryptographOpenSSL::socketOffsetInExData;
+ auto crypto = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, offset));
+ if (!crypto) {
+ qCWarning(lcTlsBackend, "No external data (TlsCryptographOpenSSL) found in SSL object");
+ return 0;
+ }
+
+ return crypto->emitErrorFromCallback(ctx);
+ }
+ return 1;
+}
+
+#ifndef OPENSSL_NO_PSK
+static unsigned q_ssl_psk_client_callback(SSL *ssl, const char *hint, char *identity, unsigned max_identity_len,
+ unsigned char *psk, unsigned max_psk_len)
+{
+ auto *tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData));
+ return tls->pskClientTlsCallback(hint, identity, max_identity_len, psk, max_psk_len);
+}
+
+static unsigned int q_ssl_psk_server_callback(SSL *ssl, const char *identity, unsigned char *psk,
+ unsigned int max_psk_len)
+{
+ auto *tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData));
+ Q_ASSERT(tls);
+ return tls->pskServerTlsCallback(identity, psk, max_psk_len);
+}
+
+#ifdef TLS1_3_VERSION
+static unsigned q_ssl_psk_restore_client(SSL *ssl, const char *hint, char *identity, unsigned max_identity_len,
+ unsigned char *psk, unsigned max_psk_len)
+{
+ Q_UNUSED(hint);
+ Q_UNUSED(identity);
+ Q_UNUSED(max_identity_len);
+ Q_UNUSED(psk);
+ Q_UNUSED(max_psk_len);
+
+#ifdef QT_DEBUG
+ auto tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData));
+ Q_ASSERT(tls);
+ Q_ASSERT(tls->d);
+ Q_ASSERT(tls->d->tlsMode() == QSslSocket::SslClientMode);
+#endif
+ unsigned retVal = 0;
+
+ // Let developers opt-in to having the normal PSK callback get called for TLS 1.3
+ // PSK (which works differently in a few ways, and is called at the start of every connection).
+ // When they do opt-in we just call the old callback from here.
+ if (qEnvironmentVariableIsSet("QT_USE_TLS_1_3_PSK"))
+ retVal = q_ssl_psk_client_callback(ssl, hint, identity, max_identity_len, psk, max_psk_len);
+
+ q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback);
+
+ return retVal;
+}
+
+static int q_ssl_psk_use_session_callback(SSL *ssl, const EVP_MD *md, const unsigned char **id,
+ size_t *idlen, SSL_SESSION **sess)
+{
+ Q_UNUSED(md);
+ Q_UNUSED(id);
+ Q_UNUSED(idlen);
+ Q_UNUSED(sess);
+
+#ifdef QT_DEBUG
+ auto *tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData));
+ Q_ASSERT(tls);
+ Q_ASSERT(tls->d);
+ Q_ASSERT(tls->d->tlsMode() == QSslSocket::SslClientMode);
+#endif
+
+ // Temporarily rebind the psk because it will be called next. The function will restore it.
+ q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_restore_client);
+
+ return 1; // need to return 1 or else "the connection setup fails."
+}
+
+int q_ssl_sess_set_new_cb(SSL *ssl, SSL_SESSION *session)
+{
+ if (!ssl) {
+ qCWarning(lcTlsBackend, "Invalid SSL (nullptr)");
+ return 0;
+ }
+ if (!session) {
+ qCWarning(lcTlsBackend, "Invalid SSL_SESSION (nullptr)");
+ return 0;
+ }
+
+ auto *tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData));
+ Q_ASSERT(tls);
+ return tls->handleNewSessionTicket(ssl);
+}
+#endif // TLS1_3_VERSION
+
+#endif // !OPENSSL_NO_PSK
+
+#if QT_CONFIG(ocsp)
+
+int qt_OCSP_status_server_callback(SSL *ssl, void *ocspRequest)
+{
+ Q_UNUSED(ocspRequest);
+ if (!ssl)
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ auto crypto = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData));
+ if (!crypto)
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ Q_ASSERT(crypto->d);
+ Q_ASSERT(crypto->d->tlsMode() == QSslSocket::SslServerMode);
+ const QByteArray &response = crypto->ocspResponseDer;
+ Q_ASSERT(response.size());
+
+ unsigned char *derCopy = static_cast<unsigned char *>(q_OPENSSL_malloc(size_t(response.size())));
+ if (!derCopy)
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ std::copy(response.data(), response.data() + response.size(), derCopy);
+ // We don't check the return value: internally OpenSSL simply assigns the
+ // pointer (it assumes it now owns this memory btw!) and the length.
+ q_SSL_set_tlsext_status_ocsp_resp(ssl, derCopy, response.size());
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+#endif // ocsp
+
+void qt_AlertInfoCallback(const SSL *connection, int from, int value)
+{
+ // Passed to SSL_set_info_callback()
+ // https://www.openssl.org/docs/man1.1.1/man3/SSL_set_info_callback.html
+
+ if (!connection) {
+#ifdef QSSLSOCKET_DEBUG
+ qCWarning(lcTlsBackend, "Invalid 'connection' parameter (nullptr)");
+#endif // QSSLSOCKET_DEBUG
+ return;
+ }
+
+ const auto offset = QTlsBackendOpenSSL::s_indexForSSLExtraData
+ + TlsCryptographOpenSSL::socketOffsetInExData;
+ auto crypto = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(connection, offset));
+ if (!crypto) {
+ // SSL_set_ex_data can fail:
+#ifdef QSSLSOCKET_DEBUG
+ qCWarning(lcTlsBackend, "No external data (socket backend) found for parameter 'connection'");
+#endif // QSSLSOCKET_DEBUG
+ return;
+ }
+
+ if (!(from & SSL_CB_ALERT)) {
+ // We only want to know about alerts (at least for now).
+ return;
+ }
+
+ if (from & SSL_CB_WRITE)
+ crypto->alertMessageSent(value);
+ else
+ crypto->alertMessageReceived(value);
+}
+
+} // extern "C"
+
+#if QT_CONFIG(ocsp)
+namespace {
+
+QSslError::SslError qt_OCSP_response_status_to_SslError(long code)
+{
+ switch (code) {
+ case OCSP_RESPONSE_STATUS_MALFORMEDREQUEST:
+ return QSslError::OcspMalformedRequest;
+ case OCSP_RESPONSE_STATUS_INTERNALERROR:
+ return QSslError::OcspInternalError;
+ case OCSP_RESPONSE_STATUS_TRYLATER:
+ return QSslError::OcspTryLater;
+ case OCSP_RESPONSE_STATUS_SIGREQUIRED:
+ return QSslError::OcspSigRequred;
+ case OCSP_RESPONSE_STATUS_UNAUTHORIZED:
+ return QSslError::OcspUnauthorized;
+ case OCSP_RESPONSE_STATUS_SUCCESSFUL:
+ default:
+ return {};
+ }
+ Q_UNREACHABLE();
+}
+
+QOcspRevocationReason qt_OCSP_revocation_reason(int reason)
+{
+ switch (reason) {
+ case OCSP_REVOKED_STATUS_NOSTATUS:
+ return QOcspRevocationReason::None;
+ case OCSP_REVOKED_STATUS_UNSPECIFIED:
+ return QOcspRevocationReason::Unspecified;
+ case OCSP_REVOKED_STATUS_KEYCOMPROMISE:
+ return QOcspRevocationReason::KeyCompromise;
+ case OCSP_REVOKED_STATUS_CACOMPROMISE:
+ return QOcspRevocationReason::CACompromise;
+ case OCSP_REVOKED_STATUS_AFFILIATIONCHANGED:
+ return QOcspRevocationReason::AffiliationChanged;
+ case OCSP_REVOKED_STATUS_SUPERSEDED:
+ return QOcspRevocationReason::Superseded;
+ case OCSP_REVOKED_STATUS_CESSATIONOFOPERATION:
+ return QOcspRevocationReason::CessationOfOperation;
+ case OCSP_REVOKED_STATUS_CERTIFICATEHOLD:
+ return QOcspRevocationReason::CertificateHold;
+ case OCSP_REVOKED_STATUS_REMOVEFROMCRL:
+ return QOcspRevocationReason::RemoveFromCRL;
+ default:
+ return QOcspRevocationReason::None;
+ }
+
+ Q_UNREACHABLE();
+}
+
+bool qt_OCSP_certificate_match(OCSP_SINGLERESP *singleResponse, X509 *peerCert, X509 *issuer)
+{
+ // OCSP_basic_verify does verify that the responder is legit, the response is
+ // correctly signed, CertID is correct. But it does not know which certificate
+ // we were presented with by our peer, so it does not check if it's a response
+ // for our peer's certificate.
+ Q_ASSERT(singleResponse && peerCert && issuer);
+
+ const OCSP_CERTID *certId = q_OCSP_SINGLERESP_get0_id(singleResponse); // Does not increment refcount.
+ if (!certId) {
+ qCWarning(lcTlsBackend, "A SingleResponse without CertID");
+ return false;
+ }
+
+ ASN1_OBJECT *md = nullptr;
+ ASN1_INTEGER *reportedSerialNumber = nullptr;
+ const int result = q_OCSP_id_get0_info(nullptr, &md, nullptr, &reportedSerialNumber, const_cast<OCSP_CERTID *>(certId));
+ if (result != 1 || !md || !reportedSerialNumber) {
+ qCWarning(lcTlsBackend, "Failed to extract a hash and serial number from CertID structure");
+ return false;
+ }
+
+ if (!q_X509_get_serialNumber(peerCert)) {
+ // Is this possible at all? But we have to check this,
+ // ASN1_INTEGER_cmp (called from OCSP_id_cmp) dereferences
+ // without any checks at all.
+ qCWarning(lcTlsBackend, "No serial number in peer's ceritificate");
+ return false;
+ }
+
+ const int nid = q_OBJ_obj2nid(md);
+ if (nid == NID_undef) {
+ qCWarning(lcTlsBackend, "Unknown hash algorithm in CertID");
+ return false;
+ }
+
+ const EVP_MD *digest = q_EVP_get_digestbynid(nid); // Does not increment refcount.
+ if (!digest) {
+ qCWarning(lcTlsBackend) << "No digest for nid" << nid;
+ return false;
+ }
+
+ OCSP_CERTID *recreatedId = q_OCSP_cert_to_id(digest, peerCert, issuer);
+ if (!recreatedId) {
+ qCWarning(lcTlsBackend, "Failed to re-create CertID");
+ return false;
+ }
+ const QSharedPointer<OCSP_CERTID> guard(recreatedId, q_OCSP_CERTID_free);
+
+ if (q_OCSP_id_cmp(const_cast<OCSP_CERTID *>(certId), recreatedId)) {
+ qCDebug(lcTlsBackend, "Certificate ID mismatch");
+ return false;
+ }
+ // Bingo!
+ return true;
+}
+
+} // unnamed namespace
+#endif // ocsp
+
+TlsCryptographOpenSSL::~TlsCryptographOpenSSL()
+{
+ destroySslContext();
+}
+
+void TlsCryptographOpenSSL::init(QSslSocket *qObj, QSslSocketPrivate *dObj)
+{
+ Q_ASSERT(qObj);
+ Q_ASSERT(dObj);
+ q = qObj;
+ d = dObj;
+
+ ocspResponses.clear();
+ ocspResponseDer.clear();
+
+ systemOrSslErrorDetected = false;
+ handshakeInterrupted = false;
+
+ fetchAuthorityInformation = false;
+ caToFetch.reset();
+}
+
+void TlsCryptographOpenSSL::checkSettingSslContext(std::shared_ptr<QSslContext> tlsContext)
+{
+ if (!sslContextPointer)
+ sslContextPointer = std::move(tlsContext);
+}
+
+std::shared_ptr<QSslContext> TlsCryptographOpenSSL::sslContext() const
+{
+ return sslContextPointer;
+}
+
+QList<QSslError> TlsCryptographOpenSSL::tlsErrors() const
+{
+ return sslErrors;
+}
+
+void TlsCryptographOpenSSL::startClientEncryption()
+{
+ if (!initSslContext()) {
+ Q_ASSERT(d);
+ setErrorAndEmit(d, QAbstractSocket::SslInternalError,
+ QSslSocket::tr("Unable to init SSL Context: %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
+ return;
+ }
+
+ // Start connecting. This will place outgoing data in the BIO, so we
+ // follow up with calling transmit().
+ startHandshake();
+ transmit();
+}
+
+void TlsCryptographOpenSSL::startServerEncryption()
+{
+ if (!initSslContext()) {
+ Q_ASSERT(d);
+ setErrorAndEmit(d, QAbstractSocket::SslInternalError,
+ QSslSocket::tr("Unable to init SSL Context: %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
+ return;
+ }
+
+ // Start connecting. This will place outgoing data in the BIO, so we
+ // follow up with calling transmit().
+ startHandshake();
+ transmit();
+}
+
+bool TlsCryptographOpenSSL::startHandshake()
+{
+ // Check if the connection has been established. Get all errors from the
+ // verification stage.
+ Q_ASSERT(q);
+ Q_ASSERT(d);
+
+ using ScopedBool = QScopedValueRollback<bool>;
+
+ if (inSetAndEmitError)
+ return false;
+
+ const auto mode = d->tlsMode();
+
+ pendingFatalAlert = false;
+ errorsReportedFromCallback = false;
+ QList<QSslErrorEntry> lastErrors;
+ q_SSL_set_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData + errorOffsetInExData, &lastErrors);
+
+ // SSL_set_ex_data can fail, but see the callback's code - we handle this there.
+ q_SSL_set_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData + socketOffsetInExData, this);
+ q_SSL_set_info_callback(ssl, qt_AlertInfoCallback);
+
+ int result = (mode == QSslSocket::SslClientMode) ? q_SSL_connect(ssl) : q_SSL_accept(ssl);
+ q_SSL_set_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData + errorOffsetInExData, nullptr);
+ // Note, unlike errors as external data on SSL object, we do not unset
+ // a callback/ex-data if alert notifications are enabled: an alert can
+ // arrive after the handshake, for example, this happens when the server
+ // does not find a ClientCert or does not like it.
+
+ if (!lastErrors.isEmpty() || errorsReportedFromCallback)
+ storePeerCertificates();
+
+ // storePeerCertificate() if called above - would update the
+ // configuration with peer's certificates.
+ auto configuration = q->sslConfiguration();
+ if (!errorsReportedFromCallback) {
+ const auto &peerCertificateChain = configuration.peerCertificateChain();
+ for (const auto &currentError : std::as_const(lastErrors)) {
+ emit q->peerVerifyError(QTlsPrivate::X509CertificateOpenSSL::openSSLErrorToQSslError(currentError.code,
+ peerCertificateChain.value(currentError.depth)));
+ if (q->state() != QAbstractSocket::ConnectedState)
+ break;
+ }
+ }
+
+ errorList << lastErrors;
+
+ // Connection aborted during handshake phase.
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+
+ // Check if we're encrypted or not.
+ if (result <= 0) {
+ switch (q_SSL_get_error(ssl, result)) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ // The handshake is not yet complete.
+ break;
+ default:
+ QString errorString = QTlsBackendOpenSSL::msgErrorsDuringHandshake();
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::startHandshake: error!" << errorString;
+#endif
+ {
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(d, QAbstractSocket::SslHandshakeFailedError, errorString);
+ if (pendingFatalAlert) {
+ trySendFatalAlert();
+ pendingFatalAlert = false;
+ }
+ }
+ q->abort();
+ }
+ return false;
+ }
+
+ // store peer certificate chain
+ storePeerCertificates();
+
+ // Start translating errors.
+ QList<QSslError> errors;
+
+ // Note, the storePeerCerificates() probably updated the configuration at this point.
+ configuration = q->sslConfiguration();
+ // Check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer)
+ const auto &peerCertificateChain = configuration.peerCertificateChain();
+ for (const QSslCertificate &cert : peerCertificateChain) {
+ if (QSslCertificatePrivate::isBlacklisted(cert)) {
+ QSslError error(QSslError::CertificateBlacklisted, cert);
+ errors << error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+
+ const bool doVerifyPeer = configuration.peerVerifyMode() == QSslSocket::VerifyPeer
+ || (configuration.peerVerifyMode() == QSslSocket::AutoVerifyPeer
+ && mode == QSslSocket::SslClientMode);
+
+#if QT_CONFIG(ocsp)
+ // For now it's always QSslSocket::SslClientMode - initSslContext() will bail out early,
+ // if it's enabled in QSslSocket::SslServerMode. This can change.
+ if (!configuration.peerCertificate().isNull() && configuration.ocspStaplingEnabled() && doVerifyPeer) {
+ if (!checkOcspStatus()) {
+ if (ocspErrors.isEmpty()) {
+ {
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(d, QAbstractSocket::SslHandshakeFailedError, ocspErrorDescription);
+ }
+ q->abort();
+ return false;
+ }
+
+ for (const QSslError &error : ocspErrors) {
+ errors << error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+ }
+#endif // ocsp
+
+ // Check the peer certificate itself. First try the subject's common name
+ // (CN) as a wildcard, then try all alternate subject name DNS entries the
+ // same way.
+ if (!configuration.peerCertificate().isNull()) {
+ // but only if we're a client connecting to a server
+ // if we're the server, don't check CN
+ const auto verificationPeerName = d->verificationName();
+ if (mode == QSslSocket::SslClientMode) {
+ QString peerName = (verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName);
+
+ if (!isMatchingHostname(configuration.peerCertificate(), peerName)) {
+ // No matches in common names or alternate names.
+ QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate());
+ errors << error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+ } else {
+ // No peer certificate presented. Report as error if the socket
+ // expected one.
+ if (doVerifyPeer) {
+ QSslError error(QSslError::NoPeerCertificate);
+ errors << error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+
+ // Translate errors from the error list into QSslErrors.
+ errors.reserve(errors.size() + errorList.size());
+ for (const auto &error : std::as_const(errorList))
+ errors << X509CertificateOpenSSL::openSSLErrorToQSslError(error.code, peerCertificateChain.value(error.depth));
+
+ if (!errors.isEmpty()) {
+ sslErrors = errors;
+#ifdef Q_OS_WIN
+ const bool fetchEnabled = QSslSocketPrivate::rootCertOnDemandLoadingSupported()
+ && d->isRootsOnDemandAllowed();
+ // !fetchEnabled is a special case scenario, when we potentially have a missing
+ // intermediate certificate and a recoverable chain, but on demand cert loading
+ // was disabled by setCaCertificates call. For this scenario we check if "Authority
+ // Information Access" is present - wincrypt can deal with such certificates.
+ QSslCertificate certToFetch;
+ if (doVerifyPeer && !d->verifyErrorsHaveBeenIgnored())
+ certToFetch = findCertificateToFetch(sslErrors, !fetchEnabled);
+
+ //Skip this if not using system CAs, or if the SSL errors are configured in advance to be ignorable
+ if (!certToFetch.isNull()) {
+ fetchAuthorityInformation = !fetchEnabled;
+ //Windows desktop versions starting from vista ship with minimal set of roots and download on demand
+ //from the windows update server CA roots that are trusted by MS. It also can fetch a missing intermediate
+ //in case "Authority Information Access" extension is present.
+ //
+ //However, this is only transparent if using WinINET - we have to trigger it
+ //ourselves.
+ fetchCaRootForCert(certToFetch);
+ return false;
+ }
+#endif // Q_OS_WIN
+ if (!checkSslErrors())
+ return false;
+ // A slot, attached to sslErrors signal can call
+ // abort/close/disconnetFromHost/etc; no need to
+ // continue handshake then.
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ } else {
+ sslErrors.clear();
+ }
+
+ continueHandshake();
+ return true;
+}
+
+void TlsCryptographOpenSSL::enableHandshakeContinuation()
+{
+ handshakeInterrupted = false;
+}
+
+void TlsCryptographOpenSSL::cancelCAFetch()
+{
+ fetchAuthorityInformation = false;
+ caToFetch.reset();
+}
+
+void TlsCryptographOpenSSL::continueHandshake()
+{
+ Q_ASSERT(q);
+ Q_ASSERT(d);
+
+ auto *plainSocket = d->plainTcpSocket();
+ Q_ASSERT(plainSocket);
+
+ const auto mode = d->tlsMode();
+
+ // if we have a max read buffer size, reset the plain socket's to match
+ if (const auto maxSize = d->maxReadBufferSize())
+ plainSocket->setReadBufferSize(maxSize);
+
+ if (q_SSL_session_reused(ssl))
+ QTlsBackend::setPeerSessionShared(d, true);
+
+#ifdef QT_DECRYPT_SSL_TRAFFIC
+ if (q_SSL_get_session(ssl)) {
+ size_t master_key_len = q_SSL_SESSION_get_master_key(q_SSL_get_session(ssl), nullptr, 0);
+ size_t client_random_len = q_SSL_get_client_random(ssl, nullptr, 0);
+ QByteArray masterKey(int(master_key_len), Qt::Uninitialized); // Will not overflow
+ QByteArray clientRandom(int(client_random_len), Qt::Uninitialized); // Will not overflow
+
+ q_SSL_SESSION_get_master_key(q_SSL_get_session(ssl),
+ reinterpret_cast<unsigned char*>(masterKey.data()),
+ masterKey.size());
+ q_SSL_get_client_random(ssl, reinterpret_cast<unsigned char *>(clientRandom.data()),
+ clientRandom.size());
+
+ QByteArray debugLineClientRandom("CLIENT_RANDOM ");
+ debugLineClientRandom.append(clientRandom.toHex().toUpper());
+ debugLineClientRandom.append(" ");
+ debugLineClientRandom.append(masterKey.toHex().toUpper());
+ debugLineClientRandom.append("\n");
+
+ QString sslKeyFile = QDir::tempPath() + "/qt-ssl-keys"_L1;
+ QFile file(sslKeyFile);
+ if (!file.open(QIODevice::Append))
+ qCWarning(lcTlsBackend) << "could not open file" << sslKeyFile << "for appending";
+ if (!file.write(debugLineClientRandom))
+ qCWarning(lcTlsBackend) << "could not write to file" << sslKeyFile;
+ file.close();
+ } else {
+ qCWarning(lcTlsBackend, "could not decrypt SSL traffic");
+ }
+#endif // QT_DECRYPT_SSL_TRAFFIC
+
+ const auto &configuration = q->sslConfiguration();
+ // Cache this SSL session inside the QSslContext
+ if (!(configuration.testSslOption(QSsl::SslOptionDisableSessionSharing))) {
+ if (!sslContextPointer->cacheSession(ssl)) {
+ sslContextPointer.reset(); // we could not cache the session
+ } else {
+ // Cache the session for permanent usage as well
+ if (!(configuration.testSslOption(QSsl::SslOptionDisableSessionPersistence))) {
+ if (!sslContextPointer->sessionASN1().isEmpty())
+ QTlsBackend::setSessionAsn1(d, sslContextPointer->sessionASN1());
+ QTlsBackend::setSessionLifetimeHint(d, sslContextPointer->sessionTicketLifeTimeHint());
+ }
+ }
+ }
+
+#if !defined(OPENSSL_NO_NEXTPROTONEG)
+
+ QTlsBackend::setAlpnStatus(d, sslContextPointer->npnContext().status);
+ if (sslContextPointer->npnContext().status == QSslConfiguration::NextProtocolNegotiationUnsupported) {
+ // we could not agree -> be conservative and use HTTP/1.1
+ // T.P.: I have to admit, this is a really strange notion of 'conservative',
+ // given the protocol-neutral nature of ALPN/NPN.
+ QTlsBackend::setNegotiatedProtocol(d, QByteArrayLiteral("http/1.1"));
+ } else {
+ const unsigned char *proto = nullptr;
+ unsigned int proto_len = 0;
+
+ q_SSL_get0_alpn_selected(ssl, &proto, &proto_len);
+ if (proto_len && mode == QSslSocket::SslClientMode) {
+ // Client does not have a callback that sets it ...
+ QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNegotiated);
+ }
+
+ if (!proto_len) { // Test if NPN was more lucky ...
+ q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len);
+ }
+
+ if (proto_len)
+ QTlsBackend::setNegotiatedProtocol(d, QByteArray(reinterpret_cast<const char *>(proto), proto_len));
+ else
+ QTlsBackend::setNegotiatedProtocol(d,{});
+ }
+#endif // !defined(OPENSSL_NO_NEXTPROTONEG)
+
+ if (mode == QSslSocket::SslClientMode) {
+ EVP_PKEY *key;
+ if (q_SSL_get_server_tmp_key(ssl, &key))
+ QTlsBackend::setEphemeralKey(d, QSslKey(key, QSsl::PublicKey));
+ }
+
+ d->setEncrypted(true);
+ emit q->encrypted();
+ if (d->isAutoStartingHandshake() && d->isPendingClose()) {
+ d->setPendingClose(false);
+ q->disconnectFromHost();
+ }
+}
+
+void TlsCryptographOpenSSL::transmit()
+{
+ Q_ASSERT(q);
+ Q_ASSERT(d);
+
+ using ScopedBool = QScopedValueRollback<bool>;
+
+ if (inSetAndEmitError)
+ return;
+
+ // If we don't have any SSL context, don't bother transmitting.
+ if (!ssl)
+ return;
+
+ auto &writeBuffer = d->tlsWriteBuffer();
+ auto &buffer = d->tlsBuffer();
+ auto *plainSocket = d->plainTcpSocket();
+ Q_ASSERT(plainSocket);
+ bool &emittedBytesWritten = d->tlsEmittedBytesWritten();
+
+ bool transmitting;
+ do {
+ transmitting = false;
+
+ // If the connection is secure, we can transfer data from the write
+ // buffer (in plain text) to the write BIO through SSL_write.
+ if (q->isEncrypted() && !writeBuffer.isEmpty()) {
+ qint64 totalBytesWritten = 0;
+ int nextDataBlockSize;
+ while ((nextDataBlockSize = writeBuffer.nextDataBlockSize()) > 0) {
+ int writtenBytes = q_SSL_write(ssl, writeBuffer.readPointer(), nextDataBlockSize);
+ if (writtenBytes <= 0) {
+ int error = q_SSL_get_error(ssl, writtenBytes);
+ //write can result in a want_write_error - not an error - continue transmitting
+ if (error == SSL_ERROR_WANT_WRITE) {
+ transmitting = true;
+ break;
+ } else if (error == SSL_ERROR_WANT_READ) {
+ //write can result in a want_read error, possibly due to renegotiation - not an error - stop transmitting
+ transmitting = false;
+ break;
+ } else {
+ // ### Better error handling.
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(d, QAbstractSocket::SslInternalError,
+ QSslSocket::tr("Unable to write data: %1").arg(
+ QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
+ return;
+ }
+ }
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: encrypted" << writtenBytes << "bytes";
+#endif
+ writeBuffer.free(writtenBytes);
+ totalBytesWritten += writtenBytes;
+
+ if (writtenBytes < nextDataBlockSize) {
+ // break out of the writing loop and try again after we had read
+ transmitting = true;
+ break;
+ }
+ }
+
+ if (totalBytesWritten > 0) {
+ // Don't emit bytesWritten() recursively.
+ if (!emittedBytesWritten) {
+ emittedBytesWritten = true;
+ emit q->bytesWritten(totalBytesWritten);
+ emittedBytesWritten = false;
+ }
+ emit q->channelBytesWritten(0, totalBytesWritten);
+ }
+ }
+
+ // Check if we've got any data to be written to the socket.
+ QVarLengthArray<char, 4096> data;
+ int pendingBytes;
+ while (plainSocket->isValid() && (pendingBytes = q_BIO_pending(writeBio)) > 0
+ && plainSocket->openMode() != QIODevice::NotOpen) {
+ // Read encrypted data from the write BIO into a buffer.
+ data.resize(pendingBytes);
+ int encryptedBytesRead = q_BIO_read(writeBio, data.data(), pendingBytes);
+
+ // Write encrypted data from the buffer to the socket.
+ qint64 actualWritten = plainSocket->write(data.constData(), encryptedBytesRead);
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: wrote" << encryptedBytesRead
+ << "encrypted bytes to the socket" << actualWritten << "actual.";
+#endif
+ if (actualWritten < 0) {
+ //plain socket write fails if it was in the pending close state.
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(d, plainSocket->error(), plainSocket->errorString());
+ return;
+ }
+ transmitting = true;
+ }
+
+ // Check if we've got any data to be read from the socket.
+ if (!q->isEncrypted() || !d->maxReadBufferSize() || buffer.size() < d->maxReadBufferSize())
+ while ((pendingBytes = plainSocket->bytesAvailable()) > 0) {
+ // Read encrypted data from the socket into a buffer.
+ data.resize(pendingBytes);
+ // just peek() here because q_BIO_write could write less data than expected
+ int encryptedBytesRead = plainSocket->peek(data.data(), pendingBytes);
+
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: read" << encryptedBytesRead << "encrypted bytes from the socket";
+#endif
+ // Write encrypted data from the buffer into the read BIO.
+ int writtenToBio = q_BIO_write(readBio, data.constData(), encryptedBytesRead);
+
+ // Throw away the results.
+ if (writtenToBio > 0) {
+ plainSocket->skip(writtenToBio);
+ } else {
+ // ### Better error handling.
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(d, QAbstractSocket::SslInternalError,
+ QSslSocket::tr("Unable to decrypt data: %1")
+ .arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
+ return;
+ }
+
+ transmitting = true;
+ }
+
+ // If the connection isn't secured yet, this is the time to retry the
+ // connect / accept.
+ if (!q->isEncrypted()) {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: testing encryption";
+#endif
+ if (startHandshake()) {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: encryption established";
+#endif
+ d->setEncrypted(true);
+ transmitting = true;
+ } else if (plainSocket->state() != QAbstractSocket::ConnectedState) {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: connection lost";
+#endif
+ break;
+ } else if (d->isPaused()) {
+ // just wait until the user continues
+ return;
+ } else {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: encryption not done yet";
+#endif
+ }
+ }
+
+ // If the request is small and the remote host closes the transmission
+ // after sending, there's a chance that startHandshake() will already
+ // have triggered a shutdown.
+ if (!ssl)
+ continue;
+
+ // We always read everything from the SSL decryption buffers, even if
+ // we have a readBufferMaxSize. There's no point in leaving data there
+ // just so that readBuffer.size() == readBufferMaxSize.
+ int readBytes = 0;
+ const int bytesToRead = 4096;
+ do {
+ if (q->readChannelCount() == 0) {
+ // The read buffer is deallocated, don't try resize or write to it.
+ break;
+ }
+ // Don't use SSL_pending(). It's very unreliable.
+ inSslRead = true;
+ readBytes = q_SSL_read(ssl, buffer.reserve(bytesToRead), bytesToRead);
+ inSslRead = false;
+ if (renegotiated) {
+ renegotiated = false;
+ X509 *x509 = q_SSL_get_peer_certificate(ssl);
+ const auto peerCertificate =
+ QTlsPrivate::X509CertificateOpenSSL::certificateFromX509(x509);
+ // Fail the renegotiate if the certificate has changed, else: continue.
+ if (peerCertificate != q->peerCertificate()) {
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(
+ d, QAbstractSocket::RemoteHostClosedError,
+ QSslSocket::tr(
+ "TLS certificate unexpectedly changed during renegotiation!"));
+ q->abort();
+ return;
+ }
+ }
+ if (readBytes > 0) {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: decrypted" << readBytes << "bytes";
+#endif
+ buffer.chop(bytesToRead - readBytes);
+
+ if (bool *readyReadEmittedPointer = d->readyReadPointer())
+ *readyReadEmittedPointer = true;
+ emit q->readyRead();
+ emit q->channelReadyRead(0);
+ transmitting = true;
+ continue;
+ }
+ buffer.chop(bytesToRead);
+
+ // Error.
+ switch (q_SSL_get_error(ssl, readBytes)) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ // Out of data.
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ // The remote host closed the connection.
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: remote disconnect";
+#endif
+ shutdown = true; // the other side shut down, make sure we do not send shutdown ourselves
+ {
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(d, QAbstractSocket::RemoteHostClosedError,
+ QSslSocket::tr("The TLS/SSL connection has been closed"));
+ }
+ return;
+ case SSL_ERROR_SYSCALL: // some IO error
+ case SSL_ERROR_SSL: // error in the SSL library
+ // we do not know exactly what the error is, nor whether we can recover from it,
+ // so just return to prevent an endless loop in the outer "while" statement
+ systemOrSslErrorDetected = true;
+ {
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(d, QAbstractSocket::SslInternalError,
+ QSslSocket::tr("Error while reading: %1")
+ .arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
+ }
+ return;
+ default:
+ // SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: can only happen with a
+ // BIO_s_connect() or BIO_s_accept(), which we do not call.
+ // SSL_ERROR_WANT_X509_LOOKUP: can only happen with a
+ // SSL_CTX_set_client_cert_cb(), which we do not call.
+ // So this default case should never be triggered.
+ {
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(d, QAbstractSocket::SslInternalError,
+ QSslSocket::tr("Error while reading: %1")
+ .arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
+ }
+ break;
+ }
+ } while (ssl && readBytes > 0);
+ } while (ssl && transmitting);
+}
+
+void TlsCryptographOpenSSL::disconnectFromHost()
+{
+ if (ssl) {
+ if (!shutdown && !q_SSL_in_init(ssl) && !systemOrSslErrorDetected) {
+ if (q_SSL_shutdown(ssl) != 1) {
+ // Some error may be queued, clear it.
+ QTlsBackendOpenSSL::clearErrorQueue();
+ }
+ shutdown = true;
+ transmit();
+ }
+ }
+ Q_ASSERT(d);
+ auto *plainSocket = d->plainTcpSocket();
+ Q_ASSERT(plainSocket);
+ plainSocket->disconnectFromHost();
+}
+
+void TlsCryptographOpenSSL::disconnected()
+{
+ Q_ASSERT(d);
+ auto *plainSocket = d->plainTcpSocket();
+ Q_ASSERT(plainSocket);
+ d->setEncrypted(false);
+
+ if (plainSocket->bytesAvailable() <= 0) {
+ destroySslContext();
+ } else {
+ // Move all bytes into the plain buffer.
+ const qint64 tmpReadBufferMaxSize = d->maxReadBufferSize();
+ // Reset temporarily, so the plain socket buffer is completely drained:
+ d->setMaxReadBufferSize(0);
+ transmit();
+ d->setMaxReadBufferSize(tmpReadBufferMaxSize);
+ }
+ //if there is still buffered data in the plain socket, don't destroy the ssl context yet.
+ //it will be destroyed when the socket is deleted.
+}
+
+QSslCipher TlsCryptographOpenSSL::sessionCipher() const
+{
+ if (!ssl)
+ return {};
+
+ const SSL_CIPHER *sessionCipher = q_SSL_get_current_cipher(ssl);
+ return sessionCipher ? QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(sessionCipher) : QSslCipher{};
+}
+
+QSsl::SslProtocol TlsCryptographOpenSSL::sessionProtocol() const
+{
+ if (!ssl)
+ return QSsl::UnknownProtocol;
+
+ const int ver = q_SSL_version(ssl);
+ switch (ver) {
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ case 0x301:
+ return QSsl::TlsV1_0;
+ case 0x302:
+ return QSsl::TlsV1_1;
+QT_WARNING_POP
+ case 0x303:
+ return QSsl::TlsV1_2;
+ case 0x304:
+ return QSsl::TlsV1_3;
+ }
+
+ return QSsl::UnknownProtocol;
+}
+
+QList<QOcspResponse> TlsCryptographOpenSSL::ocsps() const
+{
+ return ocspResponses;
+}
+
+bool TlsCryptographOpenSSL::checkSslErrors()
+{
+ Q_ASSERT(q);
+ Q_ASSERT(d);
+
+ if (sslErrors.isEmpty())
+ return true;
+
+ emit q->sslErrors(sslErrors);
+
+ const auto vfyMode = q->peerVerifyMode();
+ const auto mode = d->tlsMode();
+
+ bool doVerifyPeer = vfyMode == QSslSocket::VerifyPeer || (vfyMode == QSslSocket::AutoVerifyPeer
+ && mode == QSslSocket::SslClientMode);
+ bool doEmitSslError = !d->verifyErrorsHaveBeenIgnored();
+ // check whether we need to emit an SSL handshake error
+ if (doVerifyPeer && doEmitSslError) {
+ if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) {
+ QSslSocketPrivate::pauseSocketNotifiers(q);
+ d->setPaused(true);
+ } else {
+ setErrorAndEmit(d, QAbstractSocket::SslHandshakeFailedError, sslErrors.constFirst().errorString());
+ auto *plainSocket = d->plainTcpSocket();
+ Q_ASSERT(plainSocket);
+ plainSocket->disconnectFromHost();
+ }
+ return false;
+ }
+ return true;
+}
+
+int TlsCryptographOpenSSL::handleNewSessionTicket(SSL *connection)
+{
+ // If we return 1, this means we own the session, but we don't.
+ // 0 would tell OpenSSL to deref (but they still have it in the
+ // internal cache).
+ Q_ASSERT(connection);
+
+ Q_ASSERT(q);
+ Q_ASSERT(d);
+
+ if (q->sslConfiguration().testSslOption(QSsl::SslOptionDisableSessionPersistence)) {
+ // We silently ignore, do nothing, remove from cache.
+ return 0;
+ }
+
+ SSL_SESSION *currentSession = q_SSL_get_session(connection);
+ if (!currentSession) {
+ qCWarning(lcTlsBackend,
+ "New session ticket callback, the session is invalid (nullptr)");
+ return 0;
+ }
+
+ if (q_SSL_version(connection) < 0x304) {
+ // We only rely on this mechanics with TLS >= 1.3
+ return 0;
+ }
+
+#ifdef TLS1_3_VERSION
+ if (!q_SSL_SESSION_is_resumable(currentSession)) {
+ qCDebug(lcTlsBackend, "New session ticket, but the session is non-resumable");
+ return 0;
+ }
+#endif // TLS1_3_VERSION
+
+ const int sessionSize = q_i2d_SSL_SESSION(currentSession, nullptr);
+ if (sessionSize <= 0) {
+ qCWarning(lcTlsBackend, "could not store persistent version of SSL session");
+ return 0;
+ }
+
+ // We have somewhat perverse naming, it's not a ticket, it's a session.
+ QByteArray sessionTicket(sessionSize, 0);
+ auto data = reinterpret_cast<unsigned char *>(sessionTicket.data());
+ if (!q_i2d_SSL_SESSION(currentSession, &data)) {
+ qCWarning(lcTlsBackend, "could not store persistent version of SSL session");
+ return 0;
+ }
+
+ QTlsBackend::setSessionAsn1(d, sessionTicket);
+ QTlsBackend::setSessionLifetimeHint(d, q_SSL_SESSION_get_ticket_lifetime_hint(currentSession));
+
+ emit q->newSessionTicketReceived();
+ return 0;
+}
+
+void TlsCryptographOpenSSL::alertMessageSent(int value)
+{
+ Q_ASSERT(q);
+ Q_ASSERT(d);
+
+ const auto level = tlsAlertLevel(value);
+ if (level == QSsl::AlertLevel::Fatal && !q->isEncrypted()) {
+ // Note, this logic is handshake-time only:
+ pendingFatalAlert = true;
+ }
+
+ emit q->alertSent(level, tlsAlertType(value), tlsAlertDescription(value));
+
+}
+
+void TlsCryptographOpenSSL::alertMessageReceived(int value)
+{
+ Q_ASSERT(q);
+
+ emit q->alertReceived(tlsAlertLevel(value), tlsAlertType(value), tlsAlertDescription(value));
+}
+
+int TlsCryptographOpenSSL::emitErrorFromCallback(X509_STORE_CTX *ctx)
+{
+ // Returns 0 to abort verification, 1 to continue despite error (as
+ // OpenSSL expects from the verification callback).
+ Q_ASSERT(q);
+ Q_ASSERT(ctx);
+
+ using ScopedBool = QScopedValueRollback<bool>;
+ // While we are not setting, we are emitting and in general -
+ // we want to prevent accidental recursive startHandshake()
+ // calls:
+ const ScopedBool bg(inSetAndEmitError, true);
+
+ X509 *x509 = q_X509_STORE_CTX_get_current_cert(ctx);
+ if (!x509) {
+ qCWarning(lcTlsBackend, "Could not obtain the certificate (that failed to verify)");
+ return 0;
+ }
+
+ const QSslCertificate certificate = QTlsPrivate::X509CertificateOpenSSL::certificateFromX509(x509);
+ const auto errorAndDepth = QTlsPrivate::X509CertificateOpenSSL::errorEntryFromStoreContext(ctx);
+ const QSslError tlsError = QTlsPrivate::X509CertificateOpenSSL::openSSLErrorToQSslError(errorAndDepth.code, certificate);
+
+ errorsReportedFromCallback = true;
+ handshakeInterrupted = true;
+ emit q->handshakeInterruptedOnError(tlsError);
+
+ // Conveniently so, we also can access 'lastErrors' external data set
+ // in startHandshake, we store it for the case an application later
+ // wants to check errors (ignored or not):
+ const auto offset = QTlsBackendOpenSSL::s_indexForSSLExtraData
+ + TlsCryptographOpenSSL::errorOffsetInExData;
+ if (auto errorList = static_cast<QList<QSslErrorEntry> *>(q_SSL_get_ex_data(ssl, offset)))
+ errorList->append(errorAndDepth);
+
+ // An application is expected to ignore this error (by calling ignoreSslErrors)
+ // in its directly connected slot:
+ return !handshakeInterrupted;
+}
+
+void TlsCryptographOpenSSL::trySendFatalAlert()
+{
+ Q_ASSERT(pendingFatalAlert);
+ Q_ASSERT(d);
+
+ auto *plainSocket = d->plainTcpSocket();
+
+ pendingFatalAlert = false;
+ QVarLengthArray<char, 4096> data;
+ int pendingBytes = 0;
+ while (plainSocket->isValid() && (pendingBytes = q_BIO_pending(writeBio)) > 0
+ && plainSocket->openMode() != QIODevice::NotOpen) {
+ // Read encrypted data from the write BIO into a buffer.
+ data.resize(pendingBytes);
+ const int bioReadBytes = q_BIO_read(writeBio, data.data(), pendingBytes);
+
+ // Write encrypted data from the buffer to the socket.
+ qint64 actualWritten = plainSocket->write(data.constData(), bioReadBytes);
+ if (actualWritten < 0)
+ return;
+ plainSocket->flush();
+ }
+}
+
+bool TlsCryptographOpenSSL::initSslContext()
+{
+ Q_ASSERT(q);
+ Q_ASSERT(d);
+
+ // If no external context was set (e.g. by QHttpNetworkConnection) we will
+ // create a new one.
+ const auto mode = d->tlsMode();
+ const auto configuration = q->sslConfiguration();
+ if (!sslContextPointer)
+ sslContextPointer = QSslContext::sharedFromConfiguration(mode, configuration, d->isRootsOnDemandAllowed());
+
+ if (sslContextPointer->error() != QSslError::NoError) {
+ setErrorAndEmit(d, QAbstractSocket::SslInvalidUserDataError, sslContextPointer->errorString());
+ sslContextPointer.reset();
+ return false;
+ }
+
+ // Create and initialize SSL session
+ if (!(ssl = sslContextPointer->createSsl())) {
+ setErrorAndEmit(d, QAbstractSocket::SslInternalError,
+ QSslSocket::tr("Error creating SSL session, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
+ return false;
+ }
+
+ if (configuration.protocol() != QSsl::UnknownProtocol && mode == QSslSocket::SslClientMode) {
+ const auto verificationPeerName = d->verificationName();
+ // Set server hostname on TLS extension. RFC4366 section 3.1 requires it in ACE format.
+ QString tlsHostName = verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName;
+ if (tlsHostName.isEmpty())
+ tlsHostName = d->tlsHostName();
+ QByteArray ace = QUrl::toAce(tlsHostName);
+ // only send the SNI header if the URL is valid and not an IP
+ if (!ace.isEmpty()
+ && !QHostAddress().setAddress(tlsHostName)
+ && !(configuration.testSslOption(QSsl::SslOptionDisableServerNameIndication))) {
+ // We don't send the trailing dot from the host header if present see
+ // https://tools.ietf.org/html/rfc6066#section-3
+ if (ace.endsWith('.'))
+ ace.chop(1);
+ if (!q_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, ace.data()))
+ qCWarning(lcTlsBackend, "could not set SSL_CTRL_SET_TLSEXT_HOSTNAME, Server Name Indication disabled");
+ }
+ }
+
+ // Clear the session.
+ errorList.clear();
+
+ // Initialize memory BIOs for encryption and decryption.
+ readBio = q_BIO_new(q_BIO_s_mem());
+ writeBio = q_BIO_new(q_BIO_s_mem());
+ if (!readBio || !writeBio) {
+ setErrorAndEmit(d, QAbstractSocket::SslInternalError,
+ QSslSocket::tr("Error creating SSL session: %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
+ if (readBio)
+ q_BIO_free(readBio);
+ if (writeBio)
+ q_BIO_free(writeBio);
+ return false;
+ }
+
+ // Assign the bios.
+ q_SSL_set_bio(ssl, readBio, writeBio);
+
+ if (mode == QSslSocket::SslClientMode)
+ q_SSL_set_connect_state(ssl);
+ else
+ q_SSL_set_accept_state(ssl);
+
+ q_SSL_set_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData, this);
+
+#ifndef OPENSSL_NO_PSK
+ // Set the client callback for PSK
+ if (mode == QSslSocket::SslClientMode)
+ q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback);
+ else if (mode == QSslSocket::SslServerMode)
+ q_SSL_set_psk_server_callback(ssl, &q_ssl_psk_server_callback);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10101006L
+ // Set the client callback for TLSv1.3 PSK
+ if (mode == QSslSocket::SslClientMode
+ && QSslSocket::sslLibraryBuildVersionNumber() >= 0x10101006L) {
+ q_SSL_set_psk_use_session_callback(ssl, &q_ssl_psk_use_session_callback);
+ }
+#endif // openssl version >= 0x10101006L
+
+#endif // OPENSSL_NO_PSK
+
+#if QT_CONFIG(ocsp)
+ if (configuration.ocspStaplingEnabled()) {
+ if (mode == QSslSocket::SslServerMode) {
+ setErrorAndEmit(d, QAbstractSocket::SslInvalidUserDataError,
+ QSslSocket::tr("Server-side QSslSocket does not support OCSP stapling"));
+ return false;
+ }
+ if (q_SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp) != 1) {
+ setErrorAndEmit(d, QAbstractSocket::SslInternalError,
+ QSslSocket::tr("Failed to enable OCSP stapling"));
+ return false;
+ }
+ }
+
+ ocspResponseDer.clear();
+ const auto backendConfig = configuration.backendConfiguration();
+ auto responsePos = backendConfig.find("Qt-OCSP-response");
+ if (responsePos != backendConfig.end()) {
+ // This is our private, undocumented 'API' we use for the auto-testing of
+ // OCSP-stapling. It must be a der-encoded OCSP response, presumably set
+ // by tst_QOcsp.
+ const QVariant data(responsePos.value());
+ if (data.canConvert<QByteArray>())
+ ocspResponseDer = data.toByteArray();
+ }
+
+ if (ocspResponseDer.size()) {
+ if (mode != QSslSocket::SslServerMode) {
+ setErrorAndEmit(d, QAbstractSocket::SslInvalidUserDataError,
+ QSslSocket::tr("Client-side sockets do not send OCSP responses"));
+ return false;
+ }
+ }
+#endif // ocsp
+
+ return true;
+}
+
+void TlsCryptographOpenSSL::destroySslContext()
+{
+ if (ssl) {
+ if (!q_SSL_in_init(ssl) && !systemOrSslErrorDetected) {
+ // We do not send a shutdown alert here. Just mark the session as
+ // resumable for qhttpnetworkconnection's "optimization", otherwise
+ // OpenSSL won't start a session resumption.
+ if (q_SSL_shutdown(ssl) != 1) {
+ // Some error may be queued, clear it.
+ const auto errors = QTlsBackendOpenSSL::getErrorsFromOpenSsl();
+ Q_UNUSED(errors);
+ }
+ }
+ q_SSL_free(ssl);
+ ssl = nullptr;
+ }
+ sslContextPointer.reset();
+}
+
+void TlsCryptographOpenSSL::storePeerCertificates()
+{
+ Q_ASSERT(d);
+
+ // Store the peer certificate and chain. For clients, the peer certificate
+ // chain includes the peer certificate; for servers, it doesn't. Both the
+ // peer certificate and the chain may be empty if the peer didn't present
+ // any certificate.
+ X509 *x509 = q_SSL_get_peer_certificate(ssl);
+
+ const auto peerCertificate = QTlsPrivate::X509CertificateOpenSSL::certificateFromX509(x509);
+ QTlsBackend::storePeerCertificate(d, peerCertificate);
+ q_X509_free(x509);
+ auto peerCertificateChain = q->peerCertificateChain();
+ if (peerCertificateChain.isEmpty()) {
+ peerCertificateChain = QTlsPrivate::X509CertificateOpenSSL::stackOfX509ToQSslCertificates(q_SSL_get_peer_cert_chain(ssl));
+ if (!peerCertificate.isNull() && d->tlsMode() == QSslSocket::SslServerMode)
+ peerCertificateChain.prepend(peerCertificate);
+ QTlsBackend::storePeerCertificateChain(d, peerCertificateChain);
+ }
+}
+
+#if QT_CONFIG(ocsp)
+
+bool TlsCryptographOpenSSL::checkOcspStatus()
+{
+ Q_ASSERT(ssl);
+ Q_ASSERT(d);
+
+ const auto &configuration = q->sslConfiguration();
+ Q_ASSERT(d->tlsMode() == QSslSocket::SslClientMode); // See initSslContext() for SslServerMode
+ Q_ASSERT(configuration.peerVerifyMode() != QSslSocket::VerifyNone);
+
+ const auto clearErrorQueue = qScopeGuard([] {
+ QTlsBackendOpenSSL::logAndClearErrorQueue();
+ });
+
+ ocspResponses.clear();
+ ocspErrorDescription.clear();
+ ocspErrors.clear();
+
+ const unsigned char *responseData = nullptr;
+ const long responseLength = q_SSL_get_tlsext_status_ocsp_resp(ssl, &responseData);
+ if (responseLength <= 0 || !responseData) {
+ ocspErrors.push_back(QSslError(QSslError::OcspNoResponseFound));
+ return false;
+ }
+
+ OCSP_RESPONSE *response = q_d2i_OCSP_RESPONSE(nullptr, &responseData, responseLength);
+ if (!response) {
+ // Treat this as a fatal SslHandshakeError.
+ ocspErrorDescription = QSslSocket::tr("Failed to decode OCSP response");
+ return false;
+ }
+ const QSharedPointer<OCSP_RESPONSE> responseGuard(response, q_OCSP_RESPONSE_free);
+
+ const int ocspStatus = q_OCSP_response_status(response);
+ if (ocspStatus != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ // It's not a definitive response, it's an error message (not signed by the responder).
+ ocspErrors.push_back(QSslError(qt_OCSP_response_status_to_SslError(ocspStatus)));
+ return false;
+ }
+
+ OCSP_BASICRESP *basicResponse = q_OCSP_response_get1_basic(response);
+ if (!basicResponse) {
+ // SslHandshakeError.
+ ocspErrorDescription = QSslSocket::tr("Failed to extract basic OCSP response");
+ return false;
+ }
+ const QSharedPointer<OCSP_BASICRESP> basicResponseGuard(basicResponse, q_OCSP_BASICRESP_free);
+
+ SSL_CTX *ctx = q_SSL_get_SSL_CTX(ssl); // Does not increment refcount.
+ Q_ASSERT(ctx);
+ X509_STORE *store = q_SSL_CTX_get_cert_store(ctx); // Does not increment refcount.
+ if (!store) {
+ // SslHandshakeError.
+ ocspErrorDescription = QSslSocket::tr("No certificate verification store, cannot verify OCSP response");
+ return false;
+ }
+
+ STACK_OF(X509) *peerChain = q_SSL_get_peer_cert_chain(ssl); // Does not increment refcount.
+ X509 *peerX509 = q_SSL_get_peer_certificate(ssl);
+ Q_ASSERT(peerChain || peerX509);
+ const QSharedPointer<X509> peerX509Guard(peerX509, q_X509_free);
+ // OCSP_basic_verify with 0 as verificationFlags:
+ //
+ // 0) Tries to find the OCSP responder's certificate in either peerChain
+ // or basicResponse->certs. If not found, verification fails.
+ // 1) It checks the signature using the responder's public key.
+ // 2) Then it tries to validate the responder's cert (building a chain
+ // etc.)
+ // 3) It checks CertID in response.
+ // 4) Ensures the responder is authorized to sign the status respond.
+ //
+ // Note, OpenSSL prior to 1.0.2b would only use bs->certs to
+ // verify the responder's chain (see their commit 4ba9a4265bd).
+ // Working this around - is too much fuss for ancient versions we
+ // are dropping quite soon anyway.
+ const unsigned long verificationFlags = 0;
+ const int success = q_OCSP_basic_verify(basicResponse, peerChain, store, verificationFlags);
+ if (success <= 0)
+ ocspErrors.push_back(QSslError(QSslError::OcspResponseCannotBeTrusted));
+
+ if (q_OCSP_resp_count(basicResponse) != 1) {
+ ocspErrors.push_back(QSslError(QSslError::OcspMalformedResponse));
+ return false;
+ }
+
+ OCSP_SINGLERESP *singleResponse = q_OCSP_resp_get0(basicResponse, 0);
+ if (!singleResponse) {
+ ocspErrors.clear();
+ // A fatal problem -> SslHandshakeError.
+ ocspErrorDescription = QSslSocket::tr("Failed to decode a SingleResponse from OCSP status response");
+ return false;
+ }
+
+ // Let's make sure the response is for the correct certificate - we
+ // can re-create this CertID using our peer's certificate and its
+ // issuer's public key.
+ ocspResponses.push_back(QOcspResponse());
+ QOcspResponsePrivate *dResponse = ocspResponses.back().d.data();
+ dResponse->subjectCert = configuration.peerCertificate();
+ bool matchFound = false;
+ if (dResponse->subjectCert.isSelfSigned()) {
+ dResponse->signerCert = configuration.peerCertificate();
+ matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, peerX509);
+ } else {
+ const STACK_OF(X509) *certs = q_SSL_get_peer_cert_chain(ssl);
+ if (!certs) // Oh, what a cataclysm! Last try:
+ certs = q_OCSP_resp_get0_certs(basicResponse);
+ if (certs) {
+ // It could be the first certificate in 'certs' is our peer's
+ // certificate. Since it was not captured by the 'self-signed' branch
+ // above, the CertID will not match and we'll just iterate on to the
+ // next certificate. So we start from 0, not 1.
+ for (int i = 0, e = q_sk_X509_num(certs); i < e; ++i) {
+ X509 *issuer = q_sk_X509_value(certs, i);
+ matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, issuer);
+ if (matchFound) {
+ if (q_X509_check_issued(issuer, peerX509) == X509_V_OK) {
+ dResponse->signerCert = QTlsPrivate::X509CertificateOpenSSL::certificateFromX509(issuer);
+ break;
+ }
+ matchFound = false;
+ }
+ }
+ }
+ }
+
+ if (!matchFound) {
+ dResponse->signerCert.clear();
+ ocspErrors.push_back({QSslError::OcspResponseCertIdUnknown, configuration.peerCertificate()});
+ }
+
+ // Check if the response is valid time-wise:
+ ASN1_GENERALIZEDTIME *revTime = nullptr;
+ ASN1_GENERALIZEDTIME *thisUpdate = nullptr;
+ ASN1_GENERALIZEDTIME *nextUpdate = nullptr;
+ int reason;
+ const int certStatus = q_OCSP_single_get0_status(singleResponse, &reason, &revTime, &thisUpdate, &nextUpdate);
+ if (!thisUpdate) {
+ // This is unexpected, treat as SslHandshakeError, OCSP_check_validity assumes this pointer
+ // to be != nullptr.
+ ocspErrors.clear();
+ ocspResponses.clear();
+ ocspErrorDescription = QSslSocket::tr("Failed to extract 'this update time' from the SingleResponse");
+ return false;
+ }
+
+ // OCSP_check_validity(this, next, nsec, maxsec) does this check:
+ // this <= now <= next. They allow some freedom to account
+ // for delays/time inaccuracy.
+ // this > now + nsec ? -> NOT_YET_VALID
+ // if maxsec >= 0:
+ // now - maxsec > this ? -> TOO_OLD
+ // now - nsec > next ? -> EXPIRED
+ // next < this ? -> NEXT_BEFORE_THIS
+ // OK.
+ if (!q_OCSP_check_validity(thisUpdate, nextUpdate, 60, -1))
+ ocspErrors.push_back({QSslError::OcspResponseExpired, configuration.peerCertificate()});
+
+ // And finally, the status:
+ switch (certStatus) {
+ case V_OCSP_CERTSTATUS_GOOD:
+ // This certificate was not found among the revoked ones.
+ dResponse->certificateStatus = QOcspCertificateStatus::Good;
+ break;
+ case V_OCSP_CERTSTATUS_REVOKED:
+ dResponse->certificateStatus = QOcspCertificateStatus::Revoked;
+ dResponse->revocationReason = qt_OCSP_revocation_reason(reason);
+ ocspErrors.push_back({QSslError::CertificateRevoked, configuration.peerCertificate()});
+ break;
+ case V_OCSP_CERTSTATUS_UNKNOWN:
+ dResponse->certificateStatus = QOcspCertificateStatus::Unknown;
+ ocspErrors.push_back({QSslError::OcspStatusUnknown, configuration.peerCertificate()});
+ }
+
+ return !ocspErrors.size();
+}
+
+#endif // QT_CONFIG(ocsp)
+
+
+unsigned TlsCryptographOpenSSL::pskClientTlsCallback(const char *hint, char *identity,
+ unsigned max_identity_len,
+ unsigned char *psk, unsigned max_psk_len)
+{
+ Q_ASSERT(q);
+
+ QSslPreSharedKeyAuthenticator authenticator;
+ // Fill in some read-only fields (for the user)
+ const int hintLength = hint ? int(std::strlen(hint)) : 0;
+ QTlsBackend::setupClientPskAuth(&authenticator, hint, hintLength, max_identity_len, max_psk_len);
+ // Let the client provide the remaining bits...
+ emit q->preSharedKeyAuthenticationRequired(&authenticator);
+
+ // No PSK set? Return now to make the handshake fail
+ if (authenticator.preSharedKey().isEmpty())
+ return 0;
+
+ // Copy data back into OpenSSL
+ 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().size(), authenticator.maximumPreSharedKeyLength());
+ std::memcpy(psk, authenticator.preSharedKey().constData(), pskLength);
+ return pskLength;
+}
+
+unsigned TlsCryptographOpenSSL::pskServerTlsCallback(const char *identity, unsigned char *psk,
+ unsigned max_psk_len)
+{
+ Q_ASSERT(q);
+
+ QSslPreSharedKeyAuthenticator authenticator;
+
+ // Fill in some read-only fields (for the user)
+ QTlsBackend::setupServerPskAuth(&authenticator, identity, q->sslConfiguration().preSharedKeyIdentityHint(),
+ max_psk_len);
+ emit q->preSharedKeyAuthenticationRequired(&authenticator);
+
+ // No PSK set? Return now to make the handshake fail
+ if (authenticator.preSharedKey().isEmpty())
+ return 0;
+
+ // Copy data back into OpenSSL
+ const int pskLength = qMin(authenticator.preSharedKey().size(), authenticator.maximumPreSharedKeyLength());
+ std::memcpy(psk, authenticator.preSharedKey().constData(), pskLength);
+ return pskLength;
+}
+
+bool TlsCryptographOpenSSL::isInSslRead() const
+{
+ return inSslRead;
+}
+
+void TlsCryptographOpenSSL::setRenegotiated(bool renegotiated)
+{
+ this->renegotiated = renegotiated;
+}
+
+#ifdef Q_OS_WIN
+
+void TlsCryptographOpenSSL::fetchCaRootForCert(const QSslCertificate &cert)
+{
+ Q_ASSERT(d);
+ Q_ASSERT(q);
+
+ //The root certificate is downloaded from windows update, which blocks for 15 seconds in the worst case
+ //so the request is done in a worker thread.
+ QList<QSslCertificate> customRoots;
+ if (fetchAuthorityInformation)
+ customRoots = q->sslConfiguration().caCertificates();
+
+ //Remember we are fetching and what we are fetching:
+ caToFetch = cert;
+
+ QWindowsCaRootFetcher *fetcher = new QWindowsCaRootFetcher(cert, d->tlsMode(), customRoots,
+ q->peerVerifyName());
+ connect(fetcher, &QWindowsCaRootFetcher::finished, this, &TlsCryptographOpenSSL::caRootLoaded,
+ Qt::QueuedConnection);
+ QMetaObject::invokeMethod(fetcher, "start", Qt::QueuedConnection);
+ QSslSocketPrivate::pauseSocketNotifiers(q);
+ d->setPaused(true);
+}
+
+void TlsCryptographOpenSSL::caRootLoaded(QSslCertificate cert, QSslCertificate trustedRoot)
+{
+ if (caToFetch != cert) {
+ //Ooops, something from the previous connection attempt, ignore!
+ return;
+ }
+
+ Q_ASSERT(d);
+ Q_ASSERT(q);
+
+ //Done, fetched already:
+ caToFetch.reset();
+
+ if (fetchAuthorityInformation) {
+ if (!q->sslConfiguration().caCertificates().contains(trustedRoot))
+ trustedRoot = QSslCertificate{};
+ fetchAuthorityInformation = false;
+ }
+
+ if (!trustedRoot.isNull() && !trustedRoot.isBlacklisted()) {
+ if (QSslSocketPrivate::rootCertOnDemandLoadingSupported()) {
+ //Add the new root cert to default cert list for use by future sockets
+ auto defaultConfig = QSslConfiguration::defaultConfiguration();
+ defaultConfig.addCaCertificate(trustedRoot);
+ QSslConfiguration::setDefaultConfiguration(defaultConfig);
+ }
+ //Add the new root cert to this socket for future connections
+ QTlsBackend::addTustedRoot(d, trustedRoot);
+ //Remove the broken chain ssl errors (as chain is verified by windows)
+ for (int i=sslErrors.count() - 1; i >= 0; --i) {
+ if (sslErrors.at(i).certificate() == cert) {
+ switch (sslErrors.at(i).error()) {
+ case QSslError::UnableToGetLocalIssuerCertificate:
+ case QSslError::CertificateUntrusted:
+ case QSslError::UnableToVerifyFirstCertificate:
+ case QSslError::SelfSignedCertificateInChain:
+ // error can be ignored if OS says the chain is trusted
+ sslErrors.removeAt(i);
+ break;
+ default:
+ // error cannot be ignored
+ break;
+ }
+ }
+ }
+ }
+
+ auto *plainSocket = d->plainTcpSocket();
+ Q_ASSERT(plainSocket);
+ // Continue with remaining errors
+ if (plainSocket)
+ plainSocket->resume();
+ d->setPaused(false);
+ if (checkSslErrors() && ssl) {
+ bool willClose = (d->isAutoStartingHandshake() && d->isPendingClose());
+ continueHandshake();
+ if (!willClose)
+ transmit();
+ }
+}
+
+#endif // Q_OS_WIN
+
+} // namespace QTlsPrivate
+
+QT_END_NAMESPACE
diff --git a/src/plugins/tls/openssl/qtls_openssl_p.h b/src/plugins/tls/openssl/qtls_openssl_p.h
new file mode 100644
index 0000000000..65d21a395b
--- /dev/null
+++ b/src/plugins/tls/openssl/qtls_openssl_p.h
@@ -0,0 +1,140 @@
+// 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
+
+#ifndef QTLS_OPENSSL_P_H
+#define QTLS_OPENSSL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include "qtlsbackend_openssl_p.h"
+#include "qsslcontext_openssl_p.h"
+#include "qopenssl_p.h"
+
+#include <QtNetwork/qsslcertificate.h>
+#include <QtNetwork/qocspresponse.h>
+
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QTlsPrivate {
+
+class TlsCryptographOpenSSL : public TlsCryptograph
+{
+public:
+ enum ExDataOffset {
+ errorOffsetInExData = 1,
+ socketOffsetInExData = 2
+ };
+
+ ~TlsCryptographOpenSSL();
+
+ void init(QSslSocket *qObj, QSslSocketPrivate *dObj) override;
+ void checkSettingSslContext(std::shared_ptr<QSslContext> tlsContext) override;
+ std::shared_ptr<QSslContext> sslContext() const override;
+
+ QList<QSslError> tlsErrors() const override;
+
+ void startClientEncryption() override;
+ void startServerEncryption() override;
+ bool startHandshake();
+ void enableHandshakeContinuation() override;
+ void cancelCAFetch() override;
+ void continueHandshake() override;
+ void transmit() override;
+ void disconnectFromHost() override;
+ void disconnected() override;
+ QSslCipher sessionCipher() const override;
+ QSsl::SslProtocol sessionProtocol() const override;
+ QList<QOcspResponse> ocsps() const override;
+
+ bool checkSslErrors();
+ int handleNewSessionTicket(SSL *connection);
+
+ void alertMessageSent(int encoded);
+ void alertMessageReceived(int encoded);
+
+ int emitErrorFromCallback(X509_STORE_CTX *ctx);
+ void trySendFatalAlert();
+
+#if QT_CONFIG(ocsp)
+ bool checkOcspStatus();
+#endif
+
+ QSslSocket *q = nullptr;
+ QSslSocketPrivate *d = nullptr;
+
+ void storePeerCertificates();
+
+ unsigned pskClientTlsCallback(const char *hint, char *identity, unsigned max_identity_len,
+ unsigned char *psk, unsigned max_psk_len);
+ unsigned pskServerTlsCallback(const char *identity, unsigned char *psk,
+ unsigned max_psk_len);
+
+ bool isInSslRead() const;
+ void setRenegotiated(bool renegotiated);
+
+#ifdef Q_OS_WIN
+ void fetchCaRootForCert(const QSslCertificate &cert);
+ void caRootLoaded(QSslCertificate certificate, QSslCertificate trustedRoot);
+#endif
+
+ QByteArray ocspResponseDer;
+private:
+ // TLSTODO: names were preserved, to make comparison
+ // easier (see qsslsocket_openssl.cpp, while it exists).
+ bool initSslContext();
+ void destroySslContext();
+
+ std::shared_ptr<QSslContext> sslContextPointer;
+ SSL *ssl = nullptr; // TLSTODO: RAII.
+
+ QList<QSslErrorEntry> errorList;
+ QList<QSslError> sslErrors;
+
+ BIO *readBio = nullptr;
+ BIO *writeBio = nullptr;
+
+ QList<QOcspResponse> ocspResponses;
+
+ // This description will go to setErrorAndEmit(SslHandshakeError, ocspErrorDescription)
+ QString ocspErrorDescription;
+ // These will go to sslErrors()
+ QList<QSslError> ocspErrors;
+
+ bool systemOrSslErrorDetected = false;
+ bool handshakeInterrupted = false;
+
+ bool fetchAuthorityInformation = false;
+ std::optional<QSslCertificate> caToFetch;
+
+ bool inSetAndEmitError = false;
+ bool pendingFatalAlert = false;
+ bool errorsReportedFromCallback = false;
+
+ bool shutdown = false;
+
+ bool inSslRead = false;
+ bool renegotiated = false;
+};
+
+} // namespace QTlsPrivate
+
+QT_END_NAMESPACE
+
+#endif // QTLS_OPENSSL_P_H
+
diff --git a/src/plugins/tls/openssl/qtlsbackend_openssl.cpp b/src/plugins/tls/openssl/qtlsbackend_openssl.cpp
new file mode 100644
index 0000000000..d73515724b
--- /dev/null
+++ b/src/plugins/tls/openssl/qtlsbackend_openssl.cpp
@@ -0,0 +1,611 @@
+// 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 "qsslsocket_openssl_symbols_p.h"
+#include "qtlsbackend_openssl_p.h"
+#include "qtlskey_openssl_p.h"
+#include "qx509_openssl_p.h"
+#include "qtls_openssl_p.h"
+
+#if QT_CONFIG(dtls)
+#include "qdtls_openssl_p.h"
+#endif // QT_CONFIG(dtls)
+
+#include <QtNetwork/private/qsslcipher_p.h>
+
+#include <QtNetwork/qsslcipher.h>
+#include <QtNetwork/qssl.h>
+
+#include <QtCore/qdir.h>
+#include <QtCore/qdirlisting.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qscopeguard.h>
+#include <QtCore/qset.h>
+
+#include "qopenssl_p.h"
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
+constexpr auto DefaultWarningLevel = QtCriticalMsg;
+#else
+constexpr auto DefaultWarningLevel = QtDebugMsg;
+#endif
+
+Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.ossl", DefaultWarningLevel);
+
+static void q_loadCiphersForConnection(SSL *connection, QList<QSslCipher> &ciphers,
+ QList<QSslCipher> &defaultCiphers)
+{
+ Q_ASSERT(connection);
+
+ STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(connection);
+ for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) {
+ if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) {
+ const auto ciph = QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(cipher);
+ if (!ciph.isNull()) {
+ // Unconditionally exclude ADH and AECDH ciphers since they offer no MITM protection
+ if (!ciph.name().toLower().startsWith("adh"_L1) &&
+ !ciph.name().toLower().startsWith("exp-adh"_L1) &&
+ !ciph.name().toLower().startsWith("aecdh"_L1)) {
+ ciphers << ciph;
+
+ if (ciph.usedBits() >= 128)
+ defaultCiphers << ciph;
+ }
+ }
+ }
+ }
+}
+
+int QTlsBackendOpenSSL::s_indexForSSLExtraData = -1;
+
+QString QTlsBackendOpenSSL::getErrorsFromOpenSsl()
+{
+ QString errorString;
+ char buf[256] = {}; // OpenSSL docs claim both 120 and 256; use the larger.
+ unsigned long errNum;
+ while ((errNum = q_ERR_get_error())) {
+ if (!errorString.isEmpty())
+ errorString.append(", "_L1);
+ q_ERR_error_string_n(errNum, buf, sizeof buf);
+ errorString.append(QLatin1StringView(buf)); // error is ascii according to man ERR_error_string
+ }
+ return errorString;
+}
+
+void QTlsBackendOpenSSL::logAndClearErrorQueue()
+{
+ const auto errors = getErrorsFromOpenSsl();
+ if (errors.size())
+ qCWarning(lcTlsBackend) << "Discarding errors:" << errors;
+}
+
+void QTlsBackendOpenSSL::clearErrorQueue()
+{
+ while (q_ERR_get_error())
+ ;
+}
+
+bool QTlsBackendOpenSSL::ensureLibraryLoaded()
+{
+ static bool libraryLoaded = []() {
+ if (!q_resolveOpenSslSymbols())
+ return false;
+
+ // Initialize OpenSSL.
+ if (q_OPENSSL_init_ssl(0, nullptr) != 1)
+ return false;
+
+ if (q_OpenSSL_version_num() < 0x10101000L) {
+ qCWarning(lcTlsBackend, "QSslSocket: OpenSSL >= 1.1.1 is required; %s was found instead", q_OpenSSL_version(OPENSSL_VERSION));
+ return false;
+ }
+
+ q_SSL_load_error_strings();
+ q_OpenSSL_add_all_algorithms();
+
+ s_indexForSSLExtraData = q_CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, 0L, nullptr, nullptr,
+ nullptr, nullptr);
+
+ // Initialize OpenSSL's random seed.
+ if (!q_RAND_status()) {
+ qWarning("Random number generator not seeded, disabling SSL support");
+ return false;
+ }
+
+ return true;
+ }();
+
+ return libraryLoaded;
+}
+
+QString QTlsBackendOpenSSL::backendName() const
+{
+ return builtinBackendNames[nameIndexOpenSSL];
+}
+
+bool QTlsBackendOpenSSL::isValid() const
+{
+ return ensureLibraryLoaded();
+}
+
+long QTlsBackendOpenSSL::tlsLibraryVersionNumber() const
+{
+ return q_OpenSSL_version_num();
+}
+
+QString QTlsBackendOpenSSL::tlsLibraryVersionString() const
+{
+ const char *versionString = q_OpenSSL_version(OPENSSL_VERSION);
+ if (!versionString)
+ return QString();
+
+ return QString::fromLatin1(versionString);
+}
+
+long QTlsBackendOpenSSL::tlsLibraryBuildVersionNumber() const
+{
+ return OPENSSL_VERSION_NUMBER;
+}
+
+QString QTlsBackendOpenSSL::tlsLibraryBuildVersionString() const
+{
+ // Using QStringLiteral to store the version string as unicode and
+ // avoid false positives from Google searching the playstore for old
+ // SSL versions. See QTBUG-46265
+ return QStringLiteral(OPENSSL_VERSION_TEXT);
+}
+
+void QTlsBackendOpenSSL::ensureInitialized() const
+{
+ // Old qsslsocket_openssl calls supportsSsl() (which means
+ // library found and symbols resolved, this already assured
+ // by the fact we end up in this function (isValid() returned
+ // true for the backend, see its code). The qsslsocket_openssl
+ // proceedes with loading certificate, ciphers and elliptic
+ // curves.
+ ensureCiphersAndCertsLoaded();
+}
+
+void QTlsBackendOpenSSL::ensureCiphersAndCertsLoaded() const
+{
+ Q_CONSTINIT static bool initializationStarted = false;
+ Q_CONSTINIT static QAtomicInt initialized = Q_BASIC_ATOMIC_INITIALIZER(0);
+ Q_CONSTINIT static QRecursiveMutex initMutex;
+
+ if (initialized.loadAcquire())
+ return;
+
+ 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();
+
+#if QT_CONFIG(library)
+ //load symbols needed to receive certificates from system store
+#if defined(Q_OS_QNX)
+ QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true);
+#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();
+ 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) {
+ QDirListing dirList(QString::fromLatin1(dir), symLinkFilter, QDir::Files);
+ if (dirList.cbegin() != dirList.cend()) { // Not empty
+ QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true);
+ break;
+ }
+ }
+#endif
+#endif // QT_CONFIG(library)
+ // if on-demand loading was not enabled, load the certs now
+ if (!QSslSocketPrivate::rootCertOnDemandLoadingSupported())
+ setDefaultCaCertificates(systemCaCertificates());
+#ifdef Q_OS_WIN
+ //Enabled for fetching additional root certs from windows update on windows.
+ //This flag is set false by setDefaultCaCertificates() indicating the app uses
+ //its own cert bundle rather than the system one.
+ //Same logic that disables the unix on demand cert loading.
+ //Unlike unix, we do preload the certificates from the cert store.
+ QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true);
+#endif
+}
+
+void QTlsBackendOpenSSL::resetDefaultCiphers()
+{
+ SSL_CTX *myCtx = q_SSL_CTX_new(q_TLS_client_method());
+ // Note, we assert, not just silently return/bail out early:
+ // this should never happen and problems with OpenSSL's initialization
+ // must be caught before this (see supportsSsl()).
+ Q_ASSERT(myCtx);
+ SSL *mySsl = q_SSL_new(myCtx);
+ Q_ASSERT(mySsl);
+
+ QList<QSslCipher> ciphers;
+ QList<QSslCipher> defaultCiphers;
+
+ q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers);
+
+ q_SSL_CTX_free(myCtx);
+ q_SSL_free(mySsl);
+
+ setDefaultSupportedCiphers(ciphers);
+ setDefaultCiphers(defaultCiphers);
+
+#if QT_CONFIG(dtls)
+ ciphers.clear();
+ defaultCiphers.clear();
+ myCtx = q_SSL_CTX_new(q_DTLS_client_method());
+ if (myCtx) {
+ mySsl = q_SSL_new(myCtx);
+ if (mySsl) {
+ q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers);
+ setDefaultDtlsCiphers(defaultCiphers);
+ q_SSL_free(mySsl);
+ }
+ q_SSL_CTX_free(myCtx);
+ }
+#endif // dtls
+}
+
+QList<QSsl::SslProtocol> QTlsBackendOpenSSL::supportedProtocols() const
+{
+ QList<QSsl::SslProtocol> protocols;
+
+ protocols << QSsl::AnyProtocol;
+ protocols << QSsl::SecureProtocols;
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ protocols << QSsl::TlsV1_0;
+ protocols << QSsl::TlsV1_0OrLater;
+ protocols << QSsl::TlsV1_1;
+ protocols << QSsl::TlsV1_1OrLater;
+QT_WARNING_POP
+ protocols << QSsl::TlsV1_2;
+ protocols << QSsl::TlsV1_2OrLater;
+
+#ifdef TLS1_3_VERSION
+ protocols << QSsl::TlsV1_3;
+ protocols << QSsl::TlsV1_3OrLater;
+#endif // TLS1_3_VERSION
+
+#if QT_CONFIG(dtls)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ protocols << QSsl::DtlsV1_0;
+ protocols << QSsl::DtlsV1_0OrLater;
+QT_WARNING_POP
+ protocols << QSsl::DtlsV1_2;
+ protocols << QSsl::DtlsV1_2OrLater;
+#endif // dtls
+
+ return protocols;
+}
+
+QList<QSsl::SupportedFeature> QTlsBackendOpenSSL::supportedFeatures() const
+{
+ QList<QSsl::SupportedFeature> features;
+
+ features << QSsl::SupportedFeature::CertificateVerification;
+
+#if !defined(OPENSSL_NO_TLSEXT)
+ features << QSsl::SupportedFeature::ClientSideAlpn;
+ features << QSsl::SupportedFeature::ServerSideAlpn;
+#endif // !OPENSSL_NO_TLSEXT
+
+ features << QSsl::SupportedFeature::Ocsp;
+ features << QSsl::SupportedFeature::Psk;
+ features << QSsl::SupportedFeature::SessionTicket;
+ features << QSsl::SupportedFeature::Alerts;
+
+ return features;
+}
+
+QList<QSsl::ImplementedClass> QTlsBackendOpenSSL::implementedClasses() const
+{
+ QList<QSsl::ImplementedClass> classes;
+
+ classes << QSsl::ImplementedClass::Key;
+ classes << QSsl::ImplementedClass::Certificate;
+ classes << QSsl::ImplementedClass::Socket;
+#if QT_CONFIG(dtls)
+ classes << QSsl::ImplementedClass::Dtls;
+ classes << QSsl::ImplementedClass::DtlsCookie;
+#endif
+ classes << QSsl::ImplementedClass::EllipticCurve;
+ classes << QSsl::ImplementedClass::DiffieHellman;
+
+ return classes;
+}
+
+QTlsPrivate::TlsKey *QTlsBackendOpenSSL::createKey() const
+{
+ return new QTlsPrivate::TlsKeyOpenSSL;
+}
+
+QTlsPrivate::X509Certificate *QTlsBackendOpenSSL::createCertificate() const
+{
+ return new QTlsPrivate::X509CertificateOpenSSL;
+}
+
+namespace QTlsPrivate {
+
+#ifdef Q_OS_ANDROID
+QList<QByteArray> fetchSslCertificateData();
+#endif
+
+QList<QSslCertificate> systemCaCertificates();
+
+#ifndef Q_OS_DARWIN
+QList<QSslCertificate> systemCaCertificates()
+{
+#ifdef QSSLSOCKET_DEBUG
+ QElapsedTimer timer;
+ timer.start();
+#endif
+ QList<QSslCertificate> systemCerts;
+#if defined(Q_OS_WIN)
+ HCERTSTORE hSystemStore;
+ 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) {
+ pc = CertFindCertificateInStore(hSystemStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, pc);
+ if (!pc)
+ break;
+ QByteArray der(reinterpret_cast<const char *>(pc->pbCertEncoded),
+ static_cast<int>(pc->cbCertEncoded));
+ QSslCertificate cert(der, QSsl::Der);
+ systemCerts.append(cert);
+ }
+ CertCloseStore(hSystemStore, 0);
+ }
+#elif defined(Q_OS_ANDROID)
+ const QList<QByteArray> certData = fetchSslCertificateData();
+ for (auto certDatum : certData)
+ systemCerts.append(QSslCertificate::fromData(certDatum, QSsl::Der));
+#elif defined(Q_OS_UNIX)
+ {
+ const QList<QByteArray> directories = QSslSocketPrivate::unixRootCertDirectories();
+ QSet<QString> certFiles = {
+ QStringLiteral("/etc/pki/tls/certs/ca-bundle.crt"), // Fedora, Mandriva
+ QStringLiteral("/usr/local/share/certs/ca-root-nss.crt") // FreeBSD's ca_root_nss
+ };
+ QDir currentDir;
+ currentDir.setNameFilters(QStringList{QStringLiteral("*.pem"), QStringLiteral("*.crt")});
+ for (const auto &directory : directories) {
+ currentDir.setPath(QLatin1StringView(directory));
+ for (const auto &dirEntry : QDirListing(currentDir)) {
+ // use canonical path here to not load the same certificate twice if symlinked
+ certFiles.insert(dirEntry.canonicalFilePath());
+ }
+ }
+ for (const QString& file : std::as_const(certFiles))
+ systemCerts.append(QSslCertificate::fromPath(file, QSsl::Pem));
+ }
+#endif // platform
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcTlsBackend) << "systemCaCertificates retrieval time " << timer.elapsed() << "ms";
+ qCDebug(lcTlsBackend) << "imported " << systemCerts.count() << " certificates";
+#endif
+
+ return systemCerts;
+}
+#endif // !Q_OS_DARWIN
+} // namespace QTlsPrivate
+
+QList<QSslCertificate> QTlsBackendOpenSSL::systemCaCertificates() const
+{
+ return QTlsPrivate::systemCaCertificates();
+}
+
+QTlsPrivate::DtlsCookieVerifier *QTlsBackendOpenSSL::createDtlsCookieVerifier() const
+{
+#if QT_CONFIG(dtls)
+ return new QDtlsClientVerifierOpenSSL;
+#else
+ qCWarning(lcTlsBackend, "Feature 'dtls' is disabled, cannot verify DTLS cookies");
+ return nullptr;
+#endif // QT_CONFIG(dtls)
+}
+
+QTlsPrivate::TlsCryptograph *QTlsBackendOpenSSL::createTlsCryptograph() const
+{
+ return new QTlsPrivate::TlsCryptographOpenSSL;
+}
+
+QTlsPrivate::DtlsCryptograph *QTlsBackendOpenSSL::createDtlsCryptograph(QDtls *q, int mode) const
+{
+#if QT_CONFIG(dtls)
+ return new QDtlsPrivateOpenSSL(q, QSslSocket::SslMode(mode));
+#else
+ Q_UNUSED(q);
+ Q_UNUSED(mode);
+ qCWarning(lcTlsBackend, "Feature 'dtls' is disabled, cannot encrypt UDP datagrams");
+ return nullptr;
+#endif // QT_CONFIG(dtls)
+}
+
+QTlsPrivate::X509ChainVerifyPtr QTlsBackendOpenSSL::X509Verifier() const
+{
+ return QTlsPrivate::X509CertificateOpenSSL::verify;
+}
+
+QTlsPrivate::X509PemReaderPtr QTlsBackendOpenSSL::X509PemReader() const
+{
+ return QTlsPrivate::X509CertificateOpenSSL::certificatesFromPem;
+}
+
+QTlsPrivate::X509DerReaderPtr QTlsBackendOpenSSL::X509DerReader() const
+{
+ return QTlsPrivate::X509CertificateOpenSSL::certificatesFromDer;
+}
+
+QTlsPrivate::X509Pkcs12ReaderPtr QTlsBackendOpenSSL::X509Pkcs12Reader() const
+{
+ return QTlsPrivate::X509CertificateOpenSSL::importPkcs12;
+}
+
+QList<int> QTlsBackendOpenSSL::ellipticCurvesIds() const
+{
+ QList<int> ids;
+
+#ifndef OPENSSL_NO_EC
+ const size_t curveCount = q_EC_get_builtin_curves(nullptr, 0);
+ QVarLengthArray<EC_builtin_curve> builtinCurves(static_cast<int>(curveCount));
+
+ if (q_EC_get_builtin_curves(builtinCurves.data(), curveCount) == curveCount) {
+ ids.reserve(curveCount);
+ for (const auto &ec : builtinCurves)
+ ids.push_back(ec.nid);
+ }
+#endif // OPENSSL_NO_EC
+
+ return ids;
+}
+
+ int QTlsBackendOpenSSL::curveIdFromShortName(const QString &name) const
+ {
+ int nid = 0;
+ if (name.isEmpty())
+ return nid;
+
+ ensureInitialized(); // TLSTODO: check if it's needed!
+#ifndef OPENSSL_NO_EC
+ const QByteArray curveNameLatin1 = name.toLatin1();
+ nid = q_OBJ_sn2nid(curveNameLatin1.data());
+
+ if (nid == 0)
+ nid = q_EC_curve_nist2nid(curveNameLatin1.data());
+#endif // !OPENSSL_NO_EC
+
+ return nid;
+ }
+
+ int QTlsBackendOpenSSL::curveIdFromLongName(const QString &name) const
+ {
+ int nid = 0;
+ if (name.isEmpty())
+ return nid;
+
+ ensureInitialized();
+
+#ifndef OPENSSL_NO_EC
+ const QByteArray curveNameLatin1 = name.toLatin1();
+ nid = q_OBJ_ln2nid(curveNameLatin1.data());
+#endif
+
+ return nid;
+ }
+
+ QString QTlsBackendOpenSSL::shortNameForId(int id) const
+ {
+ QString result;
+
+#ifndef OPENSSL_NO_EC
+ if (id != 0)
+ result = QString::fromLatin1(q_OBJ_nid2sn(id));
+#endif
+
+ return result;
+ }
+
+QString QTlsBackendOpenSSL::longNameForId(int id) const
+{
+ QString result;
+
+#ifndef OPENSSL_NO_EC
+ if (id != 0)
+ result = QString::fromLatin1(q_OBJ_nid2ln(id));
+#endif
+
+ return result;
+}
+
+// NIDs of named curves allowed in TLS as per RFCs 4492 and 7027,
+// see also https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
+static const int tlsNamedCurveNIDs[] = {
+ // RFC 4492
+ NID_sect163k1,
+ NID_sect163r1,
+ NID_sect163r2,
+ NID_sect193r1,
+ NID_sect193r2,
+ NID_sect233k1,
+ NID_sect233r1,
+ NID_sect239k1,
+ NID_sect283k1,
+ NID_sect283r1,
+ NID_sect409k1,
+ NID_sect409r1,
+ NID_sect571k1,
+ NID_sect571r1,
+
+ NID_secp160k1,
+ NID_secp160r1,
+ NID_secp160r2,
+ NID_secp192k1,
+ NID_X9_62_prime192v1, // secp192r1
+ NID_secp224k1,
+ NID_secp224r1,
+ NID_secp256k1,
+ NID_X9_62_prime256v1, // secp256r1
+ NID_secp384r1,
+ NID_secp521r1,
+
+ // RFC 7027
+ NID_brainpoolP256r1,
+ NID_brainpoolP384r1,
+ NID_brainpoolP512r1
+};
+
+const size_t tlsNamedCurveNIDCount = sizeof(tlsNamedCurveNIDs) / sizeof(tlsNamedCurveNIDs[0]);
+
+bool QTlsBackendOpenSSL::isTlsNamedCurve(int id) const
+{
+ const int *const tlsNamedCurveNIDsEnd = tlsNamedCurveNIDs + tlsNamedCurveNIDCount;
+ return std::find(tlsNamedCurveNIDs, tlsNamedCurveNIDsEnd, id) != tlsNamedCurveNIDsEnd;
+}
+
+QString QTlsBackendOpenSSL::msgErrorsDuringHandshake()
+{
+ return QSslSocket::tr("Error during SSL handshake: %1").arg(getErrorsFromOpenSsl());
+}
+
+QSslCipher QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(const SSL_CIPHER *cipher)
+{
+ Q_ASSERT(cipher);
+ char buf [256] = {};
+ const QString desc = QString::fromLatin1(q_SSL_CIPHER_description(cipher, buf, sizeof(buf)));
+ int supportedBits = 0;
+ const int bits = q_SSL_CIPHER_get_bits(cipher, &supportedBits);
+ return createCiphersuite(desc, bits, supportedBits);
+}
+
+void QTlsBackendOpenSSL::forceAutotestSecurityLevel()
+{
+ QSslContext::forceAutoTestSecurityLevel();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qtlsbackend_openssl_p.cpp"
diff --git a/src/plugins/tls/openssl/qtlsbackend_openssl_p.h b/src/plugins/tls/openssl/qtlsbackend_openssl_p.h
new file mode 100644
index 0000000000..b9f1f95df0
--- /dev/null
+++ b/src/plugins/tls/openssl/qtlsbackend_openssl_p.h
@@ -0,0 +1,104 @@
+// 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
+
+#ifndef QTLSBACKEND_OPENSSL_P_H
+#define QTLSBACKEND_OPENSSL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include <QtNetwork/qssldiffiehellmanparameters.h>
+#include <QtNetwork/qsslcertificate.h>
+
+#include <QtNetwork/private/qtlsbackend_p.h>
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qlist.h>
+
+#include <openssl/ssl.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTlsBackendOpenSSL final : public QTlsBackend
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QTlsBackend_iid)
+ Q_INTERFACES(QTlsBackend)
+
+public:
+
+ static QString getErrorsFromOpenSsl();
+ static void logAndClearErrorQueue();
+ static void clearErrorQueue();
+
+ // Index used in SSL_get_ex_data to get the matching TlsCryptographerOpenSSL:
+ 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;
+ QString tlsLibraryVersionString() const override;
+ long tlsLibraryBuildVersionNumber() const override;
+ QString tlsLibraryBuildVersionString() const override;
+
+ void ensureInitialized() const override;
+ void ensureCiphersAndCertsLoaded() const;
+ static void resetDefaultCiphers();
+
+ QList<QSsl::SslProtocol> supportedProtocols() const override;
+ QList<QSsl::SupportedFeature> supportedFeatures() const override;
+ QList<QSsl::ImplementedClass> implementedClasses() const override;
+
+ // QSslKey:
+ QTlsPrivate::TlsKey *createKey() const override;
+
+ // QSslCertificate:
+ QTlsPrivate::X509Certificate *createCertificate() const override;
+ QList<QSslCertificate> systemCaCertificates() const override;
+
+ QTlsPrivate::TlsCryptograph *createTlsCryptograph() const override;
+ QTlsPrivate::DtlsCookieVerifier *createDtlsCookieVerifier() const override;
+ QTlsPrivate::DtlsCryptograph *createDtlsCryptograph(QDtls *q, int mode) const override;
+
+ QTlsPrivate::X509ChainVerifyPtr X509Verifier() const override;
+ QTlsPrivate::X509PemReaderPtr X509PemReader() const override;
+ QTlsPrivate::X509DerReaderPtr X509DerReader() const override;
+ QTlsPrivate::X509Pkcs12ReaderPtr X509Pkcs12Reader() const override;
+
+ // Elliptic curves:
+ QList<int> ellipticCurvesIds() const override;
+ int curveIdFromShortName(const QString &name) const override;
+ int curveIdFromLongName(const QString &name) const override;
+ QString shortNameForId(int cid) const override;
+ QString longNameForId(int cid) const override;
+ bool isTlsNamedCurve(int cid) const override;
+
+ // DH parameters:
+ using DHParams = QSslDiffieHellmanParameters;
+ int dhParametersFromDer(const QByteArray &derData, QByteArray *data) const override;
+ int dhParametersFromPem(const QByteArray &pemData, QByteArray *data) const override;
+
+ void forceAutotestSecurityLevel() override;
+};
+
+Q_DECLARE_LOGGING_CATEGORY(lcTlsBackend)
+
+QT_END_NAMESPACE
+
+#endif // QTLSBACKEND_OPENSSL_P_H
+
+
diff --git a/src/plugins/tls/openssl/qtlskey_openssl.cpp b/src/plugins/tls/openssl/qtlskey_openssl.cpp
new file mode 100644
index 0000000000..294fc2ffcd
--- /dev/null
+++ b/src/plugins/tls/openssl/qtlskey_openssl.cpp
@@ -0,0 +1,541 @@
+// 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 "qsslsocket_openssl_symbols_p.h"
+#include "qtlsbackend_openssl_p.h"
+#include "qtlskey_openssl_p.h"
+
+#include <QtNetwork/private/qsslkey_p.h>
+
+#include <QtNetwork/qsslsocket.h>
+
+#include <QtCore/qscopeguard.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QTlsPrivate {
+
+void TlsKeyOpenSSL::decodeDer(QSsl::KeyType type, QSsl::KeyAlgorithm algorithm, const QByteArray &der,
+ const QByteArray &passPhrase, bool deepClear)
+{
+ if (der.isEmpty())
+ return;
+
+ keyType = type;
+ keyAlgorithm = algorithm;
+
+ QMap<QByteArray, QByteArray> headers;
+ const auto pem = pemFromDer(der, headers);
+
+ decodePem(type, algorithm, pem, passPhrase, deepClear);
+}
+
+void TlsKeyOpenSSL::decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem,
+ const QByteArray &passPhrase, bool deepClear)
+{
+ if (pem.isEmpty())
+ return;
+
+ keyType = type;
+ keyAlgorithm = algorithm;
+
+ clear(deepClear);
+
+ BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size());
+ if (!bio)
+ return;
+
+ const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);});
+
+ void *phrase = const_cast<char *>(passPhrase.data());
+
+#ifdef OPENSSL_NO_DEPRECATED_3_0
+ if (type == QSsl::PublicKey)
+ genericKey = q_PEM_read_bio_PUBKEY(bio, nullptr, nullptr, phrase);
+ else
+ genericKey = q_PEM_read_bio_PrivateKey(bio, nullptr, nullptr, phrase);
+ keyIsNull = !genericKey;
+ if (keyIsNull)
+ QTlsBackendOpenSSL::logAndClearErrorQueue();
+#else
+
+ if (algorithm == QSsl::Rsa) {
+ RSA *result = (type == QSsl::PublicKey)
+ ? q_PEM_read_bio_RSA_PUBKEY(bio, &rsa, nullptr, phrase)
+ : q_PEM_read_bio_RSAPrivateKey(bio, &rsa, nullptr, phrase);
+ if (rsa && rsa == result)
+ keyIsNull = false;
+ } else if (algorithm == QSsl::Dsa) {
+ DSA *result = (type == QSsl::PublicKey)
+ ? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, nullptr, phrase)
+ : q_PEM_read_bio_DSAPrivateKey(bio, &dsa, nullptr, phrase);
+ if (dsa && dsa == result)
+ keyIsNull = false;
+ } else if (algorithm == QSsl::Dh) {
+ EVP_PKEY *result = (type == QSsl::PublicKey)
+ ? q_PEM_read_bio_PUBKEY(bio, nullptr, nullptr, phrase)
+ : q_PEM_read_bio_PrivateKey(bio, nullptr, nullptr, phrase);
+ if (result)
+ dh = q_EVP_PKEY_get1_DH(result);
+ if (dh)
+ keyIsNull = false;
+ q_EVP_PKEY_free(result);
+#ifndef OPENSSL_NO_EC
+ } else if (algorithm == QSsl::Ec) {
+ EC_KEY *result = (type == QSsl::PublicKey)
+ ? q_PEM_read_bio_EC_PUBKEY(bio, &ec, nullptr, phrase)
+ : q_PEM_read_bio_ECPrivateKey(bio, &ec, nullptr, phrase);
+ if (ec && ec == result)
+ keyIsNull = false;
+#endif // OPENSSL_NO_EC
+ }
+
+#endif // OPENSSL_NO_DEPRECATED_3_0
+}
+
+QByteArray TlsKeyOpenSSL::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
+{
+ QByteArray header = pemHeader();
+ QByteArray footer = pemFooter();
+
+ QByteArray der(pem);
+
+ int headerIndex = der.indexOf(header);
+ 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.size());
+ }
+ if (headerIndex == -1 || footerIndex == -1) {
+ header = pkcs8Header(false);
+ footer = pkcs8Footer(false);
+ headerIndex = der.indexOf(header);
+ footerIndex = der.indexOf(footer, headerIndex + header.size());
+ }
+ }
+ if (headerIndex == -1 || footerIndex == -1)
+ return QByteArray();
+
+ der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size()));
+
+ if (der.contains("Proc-Type:")) {
+ // taken from QHttpNetworkReplyPrivate::parseHeader
+ int i = 0;
+ while (i < der.size()) {
+ int j = der.indexOf(':', i); // field-name
+ if (j == -1)
+ break;
+ const QByteArray field = der.mid(i, j - i).trimmed();
+ j++;
+ // any number of LWS is allowed before and after the value
+ QByteArray value;
+ do {
+ i = der.indexOf('\n', j);
+ if (i == -1)
+ break;
+ if (!value.isEmpty())
+ value += ' ';
+ // check if we have CRLF or only LF
+ bool hasCR = (i && der[i-1] == '\r');
+ int length = i -(hasCR ? 1: 0) - j;
+ value += der.mid(j, length).trimmed();
+ j = ++i;
+ } while (i < der.size() && (der.at(i) == ' ' || der.at(i) == '\t'));
+ if (i == -1)
+ break; // something is wrong
+
+ headers->insert(field, value);
+ }
+ der = der.mid(i);
+ }
+
+ return QByteArray::fromBase64(der); // ignores newlines
+}
+
+void TlsKeyOpenSSL::clear(bool deep)
+{
+ keyIsNull = true;
+
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ if (algorithm() == QSsl::Rsa && rsa) {
+ if (deep)
+ q_RSA_free(rsa);
+ rsa = nullptr;
+ }
+ if (algorithm() == QSsl::Dsa && dsa) {
+ if (deep)
+ q_DSA_free(dsa);
+ dsa = nullptr;
+ }
+ if (algorithm() == QSsl::Dh && dh) {
+ if (deep)
+ q_DH_free(dh);
+ dh = nullptr;
+ }
+#ifndef OPENSSL_NO_EC
+ if (algorithm() == QSsl::Ec && ec) {
+ if (deep)
+ q_EC_KEY_free(ec);
+ ec = nullptr;
+ }
+#endif
+#endif // OPENSSL_NO_DEPRECATED_3_0
+
+ if (algorithm() == QSsl::Opaque && opaque) {
+ if (deep)
+ q_EVP_PKEY_free(opaque);
+ opaque = nullptr;
+ }
+
+ if (genericKey) {
+ // None of the above cleared it. genericKey is either
+ // initialised by PEM read operation, or from X509, and
+ // we are the owners and not sharing. So we free it.
+ q_EVP_PKEY_free(genericKey);
+ genericKey = nullptr;
+ }
+}
+
+Qt::HANDLE TlsKeyOpenSSL::handle() const
+{
+ if (keyAlgorithm == QSsl::Opaque)
+ return Qt::HANDLE(opaque);
+
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ switch (keyAlgorithm) {
+ case QSsl::Rsa:
+ return Qt::HANDLE(rsa);
+ case QSsl::Dsa:
+ return Qt::HANDLE(dsa);
+ case QSsl::Dh:
+ return Qt::HANDLE(dh);
+#ifndef OPENSSL_NO_EC
+ case QSsl::Ec:
+ return Qt::HANDLE(ec);
+#endif
+ default:
+ return Qt::HANDLE(nullptr);
+ }
+#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(genericKey);
+#endif
+}
+
+int TlsKeyOpenSSL::length() const
+{
+ if (isNull() || algorithm() == QSsl::Opaque)
+ return -1;
+
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ switch (algorithm()) {
+ case QSsl::Rsa:
+ return q_RSA_bits(rsa);
+ case QSsl::Dsa:
+ return q_DSA_bits(dsa);
+ case QSsl::Dh:
+ return q_DH_bits(dh);
+#ifndef OPENSSL_NO_EC
+ case QSsl::Ec:
+ return q_EC_GROUP_get_degree(q_EC_KEY_get0_group(ec));
+#endif
+ default:
+ return -1;
+ }
+#else // OPENSSL_NO_DEPRECATED_3_0
+ Q_ASSERT(genericKey);
+ return q_EVP_PKEY_get_bits(genericKey);
+#endif // OPENSSL_NO_DEPRECATED_3_0
+}
+
+QByteArray TlsKeyOpenSSL::toPem(const QByteArray &passPhrase) const
+{
+ if (!QSslSocket::supportsSsl() || isNull() || algorithm() == QSsl::Opaque)
+ return {};
+
+ const EVP_CIPHER *cipher = nullptr;
+ if (type() == QSsl::PrivateKey && !passPhrase.isEmpty()) {
+#ifndef OPENSSL_NO_DES
+ cipher = q_EVP_des_ede3_cbc();
+#else
+ return {};
+#endif
+ }
+
+ BIO *bio = q_BIO_new(q_BIO_s_mem());
+ if (!bio)
+ return {};
+
+ const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);});
+
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+
+#define write_pubkey(alg, key) q_PEM_write_bio_##alg##_PUBKEY(bio, key)
+#define write_privatekey(alg, key) \
+ q_PEM_write_bio_##alg##PrivateKey(bio, key, cipher, (uchar *)passPhrase.data(), \
+ passPhrase.size(), nullptr, nullptr)
+
+#else
+
+#define write_pubkey(alg, key) q_PEM_write_bio_PUBKEY(bio, genericKey)
+#define write_privatekey(alg, key) \
+ q_PEM_write_bio_PrivateKey_traditional(bio, genericKey, cipher, (uchar *)passPhrase.data(), passPhrase.size(), nullptr, nullptr)
+
+#endif // OPENSSL_NO_DEPRECATED_3_0
+
+ bool fail = false;
+ if (algorithm() == QSsl::Rsa) {
+ if (type() == QSsl::PublicKey) {
+ if (!write_pubkey(RSA, rsa))
+ fail = true;
+ } else if (!write_privatekey(RSA, rsa)) {
+ fail = true;
+ }
+ } else if (algorithm() == QSsl::Dsa) {
+ if (type() == QSsl::PublicKey) {
+ if (!write_pubkey(DSA, dsa))
+ fail = true;
+ } else if (!write_privatekey(DSA, dsa)) {
+ fail = true;
+ }
+ } else if (algorithm() == QSsl::Dh) {
+#ifdef OPENSSL_NO_DEPRECATED_3_0
+ EVP_PKEY *result = genericKey;
+#else
+ EVP_PKEY *result = q_EVP_PKEY_new();
+ const auto guard = qScopeGuard([result]{if (result) q_EVP_PKEY_free(result);});
+ if (!result || !q_EVP_PKEY_set1_DH(result, dh)) {
+ fail = true;
+ } else
+#endif
+ if (type() == QSsl::PublicKey) {
+ if (!q_PEM_write_bio_PUBKEY(bio, result))
+ fail = true;
+ } else if (!q_PEM_write_bio_PrivateKey(bio, result, cipher, (uchar *)passPhrase.data(),
+ passPhrase.size(), nullptr, nullptr)) {
+ fail = true;
+ }
+#ifndef OPENSSL_NO_EC
+ } else if (algorithm() == QSsl::Ec) {
+ if (type() == QSsl::PublicKey) {
+ if (!write_pubkey(EC, ec))
+ fail = true;
+ } else {
+ if (!write_privatekey(EC, ec))
+ fail = true;
+ }
+#endif
+ } else {
+ fail = true;
+ }
+
+ QByteArray pem;
+ if (!fail) {
+ char *data = nullptr;
+ const long size = q_BIO_get_mem_data(bio, &data);
+ if (size > 0 && data)
+ pem = QByteArray(data, size);
+ } else {
+ QTlsBackendOpenSSL::logAndClearErrorQueue();
+ }
+
+ return pem;
+}
+
+void TlsKeyOpenSSL::fromHandle(Qt::HANDLE handle, QSsl::KeyType expectedType)
+{
+ EVP_PKEY *evpKey = reinterpret_cast<EVP_PKEY *>(handle);
+ if (!evpKey || !fromEVP_PKEY(evpKey)) {
+ opaque = evpKey;
+ keyAlgorithm = QSsl::Opaque;
+ } else {
+ q_EVP_PKEY_free(evpKey);
+ }
+
+ keyType = expectedType;
+ keyIsNull = !opaque;
+}
+
+bool TlsKeyOpenSSL::fromEVP_PKEY(EVP_PKEY *pkey)
+{
+ if (!pkey)
+ return false;
+
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+#define get_key(key, alg) key = q_EVP_PKEY_get1_##alg(pkey)
+#else
+#define get_key(key, alg) q_EVP_PKEY_up_ref(pkey); genericKey = pkey;
+#endif
+
+ switch (q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey))) {
+ case EVP_PKEY_RSA:
+ keyIsNull = false;
+ keyAlgorithm = QSsl::Rsa;
+ keyType = QSsl::PrivateKey;
+ get_key(rsa, RSA);
+ return true;
+ case EVP_PKEY_DSA:
+ keyIsNull = false;
+ keyAlgorithm = QSsl::Dsa;
+ keyType = QSsl::PrivateKey;
+ get_key(dsa, DSA);
+ return true;
+ case EVP_PKEY_DH:
+ keyIsNull = false;
+ keyAlgorithm = QSsl::Dh;
+ keyType = QSsl::PrivateKey;
+ get_key(dh, DH);
+ return true;
+#ifndef OPENSSL_NO_EC
+ case EVP_PKEY_EC:
+ keyIsNull = false;
+ keyAlgorithm = QSsl::Ec;
+ keyType = QSsl::PrivateKey;
+ get_key(ec, EC_KEY);
+ return true;
+#endif
+ default:;
+ // Unknown key type. This could be handled as opaque, but then
+ // we'd eventually leak memory since we wouldn't be able to free
+ // the underlying EVP_PKEY structure. For now, we won't support
+ // this.
+ }
+
+ return false;
+}
+
+QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv, bool enc)
+{
+ const EVP_CIPHER *type = nullptr;
+ int i = 0, len = 0;
+
+ switch (cipher) {
+ case Cipher::DesCbc:
+#ifndef OPENSSL_NO_DES
+ type = q_EVP_des_cbc();
+#endif
+ break;
+ case Cipher::DesEde3Cbc:
+#ifndef OPENSSL_NO_DES
+ type = q_EVP_des_ede3_cbc();
+#endif
+ break;
+ case Cipher::Rc2Cbc:
+#ifndef OPENSSL_NO_RC2
+ type = q_EVP_rc2_cbc();
+#endif
+ break;
+ case Cipher::Aes128Cbc:
+ type = q_EVP_aes_128_cbc();
+ break;
+ case Cipher::Aes192Cbc:
+ type = q_EVP_aes_192_cbc();
+ break;
+ case Cipher::Aes256Cbc:
+ type = q_EVP_aes_256_cbc();
+ break;
+ }
+
+ if (type == nullptr)
+ return {};
+
+ QByteArray output;
+ output.resize(data.size() + EVP_MAX_BLOCK_LENGTH);
+
+ EVP_CIPHER_CTX *ctx = q_EVP_CIPHER_CTX_new();
+ q_EVP_CIPHER_CTX_reset(ctx);
+ if (q_EVP_CipherInit(ctx, type, nullptr, nullptr, enc) != 1) {
+ q_EVP_CIPHER_CTX_free(ctx);
+ QTlsBackendOpenSSL::logAndClearErrorQueue();
+ return {};
+ }
+
+ q_EVP_CIPHER_CTX_set_key_length(ctx, key.size());
+ if (cipher == Cipher::Rc2Cbc)
+ q_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, 8 * key.size(), nullptr);
+
+ q_EVP_CipherInit_ex(ctx, nullptr, nullptr,
+ reinterpret_cast<const unsigned char *>(key.constData()),
+ reinterpret_cast<const unsigned char *>(iv.constData()),
+ enc);
+ q_EVP_CipherUpdate(ctx,
+ reinterpret_cast<unsigned char *>(output.data()), &len,
+ reinterpret_cast<const unsigned char *>(data.constData()), data.size());
+ q_EVP_CipherFinal(ctx,
+ reinterpret_cast<unsigned char *>(output.data()) + len, &i);
+ len += i;
+
+ q_EVP_CIPHER_CTX_reset(ctx);
+ q_EVP_CIPHER_CTX_free(ctx);
+
+ return output.left(len);
+}
+
+QByteArray TlsKeyOpenSSL::decrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const
+{
+ return doCrypt(cipher, data, key, iv, false);
+}
+
+QByteArray TlsKeyOpenSSL::encrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const
+{
+ return doCrypt(cipher, data, key, iv, true);
+}
+
+TlsKeyOpenSSL *TlsKeyOpenSSL::publicKeyFromX509(X509 *x)
+{
+ TlsKeyOpenSSL *tlsKey = new TlsKeyOpenSSL;
+ std::unique_ptr<TlsKeyOpenSSL> keyRaii(tlsKey);
+
+ tlsKey->keyType = QSsl::PublicKey;
+
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+
+#define get_pubkey(keyName, alg) tlsKey->keyName = q_EVP_PKEY_get1_##alg(pkey)
+
+#else
+
+#define get_pubkey(a, b) tlsKey->genericKey = pkey
+
+#endif
+
+ EVP_PKEY *pkey = q_X509_get_pubkey(x);
+ Q_ASSERT(pkey);
+ const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey));
+
+ if (keyType == EVP_PKEY_RSA) {
+ get_pubkey(rsa, RSA);
+ tlsKey->keyAlgorithm = QSsl::Rsa;
+ tlsKey->keyIsNull = false;
+ } else if (keyType == EVP_PKEY_DSA) {
+ get_pubkey(dsa, DSA);
+ tlsKey->keyAlgorithm = QSsl::Dsa;
+ tlsKey->keyIsNull = false;
+#ifndef OPENSSL_NO_EC
+ } else if (keyType == EVP_PKEY_EC) {
+ get_pubkey(ec, EC_KEY);
+ tlsKey->keyAlgorithm = QSsl::Ec;
+ tlsKey->keyIsNull = false;
+#endif
+ } else if (keyType == EVP_PKEY_DH) {
+ // DH unsupported (key is null)
+ } else {
+ // error? (key is null)
+ }
+
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ q_EVP_PKEY_free(pkey);
+#endif
+
+ return keyRaii.release();
+}
+
+} // namespace QTlsPrivate
+
+QT_END_NAMESPACE
diff --git a/src/plugins/tls/openssl/qtlskey_openssl_p.h b/src/plugins/tls/openssl/qtlskey_openssl_p.h
new file mode 100644
index 0000000000..4ee16ffc29
--- /dev/null
+++ b/src/plugins/tls/openssl/qtlskey_openssl_p.h
@@ -0,0 +1,100 @@
+// 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
+
+#ifndef QTLSKEY_OPENSSL_H
+#define QTLSKEY_OPENSSL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include "../shared/qtlskey_base_p.h"
+
+#include <QtNetwork/private/qtlsbackend_p.h>
+#include <QtNetwork/private/qsslkey_p.h>
+
+#include <QtNetwork/qssl.h>
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qglobal.h>
+
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
+#include <openssl/dh.h>
+
+#ifdef OPENSSL_NO_DEPRECATED_3_0
+typedef struct evp_pkey_st EVP_PKEY;
+typedef struct dsa_st DSA;
+typedef struct rsa_st RSA;
+typedef struct dh_st DH;
+typedef struct ec_key_st EC_KEY;
+#endif // OPENSSL_NO_DEPRECATED_3_0
+
+QT_BEGIN_NAMESPACE
+
+QT_REQUIRE_CONFIG(ssl);
+
+namespace QTlsPrivate {
+
+class TlsKeyOpenSSL final : public TlsKeyBase
+{
+public:
+ TlsKeyOpenSSL()
+ : opaque(nullptr)
+ {
+ clear(false);
+ }
+ ~TlsKeyOpenSSL()
+ {
+ clear(true);
+ }
+
+ void decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der,
+ const QByteArray &passPhrase, bool deepClear) override;
+ void decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem,
+ const QByteArray &passPhrase, bool deepClear) override;
+
+ QByteArray toPem(const QByteArray &passPhrase) const override;
+ QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const override;
+
+ void fromHandle(Qt::HANDLE opaque, KeyType expectedType) override;
+
+ void clear(bool deep) override;
+ Qt::HANDLE handle() const override;
+ int length() const override;
+
+ QByteArray decrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const override;
+ QByteArray encrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const override;
+
+ static TlsKeyOpenSSL *publicKeyFromX509(X509 *x);
+
+ union {
+ EVP_PKEY *opaque;
+ RSA *rsa;
+ DSA *dsa;
+ DH *dh;
+#ifndef OPENSSL_NO_EC
+ EC_KEY *ec;
+#endif
+ EVP_PKEY *genericKey;
+ };
+
+ bool fromEVP_PKEY(EVP_PKEY *pkey);
+};
+
+} // namespace QTlsPrivate
+
+QT_END_NAMESPACE
+
+#endif // QTLSKEY_OPENSSL_H
diff --git a/src/plugins/tls/openssl/qwindowscarootfetcher.cpp b/src/plugins/tls/openssl/qwindowscarootfetcher.cpp
new file mode 100644
index 0000000000..a18aae0b71
--- /dev/null
+++ b/src/plugins/tls/openssl/qwindowscarootfetcher.cpp
@@ -0,0 +1,249 @@
+// 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
+
+#include "qwindowscarootfetcher_p.h"
+#include "qx509_openssl_p.h"
+#include "qopenssl_p.h"
+
+#include <QtCore/QThread>
+#include <QtGlobal>
+
+#include <QtCore/qscopeguard.h>
+
+#ifdef QSSLSOCKET_DEBUG
+#include <QtNetwork/private/qtlsbackend_p.h> // for debug categories
+#include <QtCore/QElapsedTimer>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsCaRootFetcherThread : public QThread
+{
+public:
+ QWindowsCaRootFetcherThread()
+ {
+ qRegisterMetaType<QSslCertificate>();
+ setObjectName(QStringLiteral("QWindowsCaRootFetcher"));
+ start();
+ }
+ ~QWindowsCaRootFetcherThread()
+ {
+ quit();
+ wait(15500); // worst case, a running request can block for 15 seconds
+ }
+};
+
+Q_GLOBAL_STATIC(QWindowsCaRootFetcherThread, windowsCaRootFetcherThread);
+
+namespace {
+
+const QList<QSslCertificate> buildVerifiedChain(const QList<QSslCertificate> &caCertificates,
+ PCCERT_CHAIN_CONTEXT chainContext,
+ const QString &peerVerifyName)
+{
+ // We ended up here because OpenSSL verification failed to
+ // build a chain, with intermediate certificate missing
+ // but "Authority Information Access" extension present.
+ // Also, apparently the normal CA fetching path was disabled
+ // by setting custom CA certificates. We convert wincrypt's
+ // structures in QSslCertificate and give OpenSSL the second
+ // chance to verify the now (apparently) complete chain.
+ // In addition, wincrypt gives us a benefit of some checks
+ // we don't have in OpenSSL back-end.
+ Q_ASSERT(chainContext);
+
+ if (!chainContext->cChain)
+ return {};
+
+ QList<QSslCertificate> verifiedChain;
+
+ CERT_SIMPLE_CHAIN *chain = chainContext->rgpChain[chainContext->cChain - 1];
+ if (!chain)
+ return {};
+
+ if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN)
+ return {}; // No need to mess with OpenSSL (the chain is still incomplete).
+
+ for (DWORD i = 0; i < chain->cElement; ++i) {
+ CERT_CHAIN_ELEMENT *element = chain->rgpElement[i];
+ QSslCertificate cert(QByteArray(reinterpret_cast<const char*>(element->pCertContext->pbCertEncoded),
+ int(element->pCertContext->cbCertEncoded)), QSsl::Der);
+
+ if (cert.isBlacklisted())
+ return {};
+
+ if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED) // Good to know!
+ return {};
+
+ if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID)
+ return {};
+
+ verifiedChain.append(cert);
+ }
+
+ // We rely on OpenSSL's ability to find other problems.
+ const auto tlsErrors = QTlsPrivate::X509CertificateOpenSSL::verify(caCertificates, verifiedChain, peerVerifyName);
+ if (tlsErrors.size())
+ verifiedChain.clear();
+
+ return verifiedChain;
+}
+
+} // unnamed namespace
+
+QWindowsCaRootFetcher::QWindowsCaRootFetcher(const QSslCertificate &certificate, QSslSocket::SslMode sslMode,
+ const QList<QSslCertificate> &caCertificates, const QString &hostName)
+ : cert(certificate), mode(sslMode), explicitlyTrustedCAs(caCertificates), peerVerifyName(hostName)
+{
+ moveToThread(windowsCaRootFetcherThread());
+}
+
+QWindowsCaRootFetcher::~QWindowsCaRootFetcher()
+{
+}
+
+void QWindowsCaRootFetcher::start()
+{
+ QByteArray der = cert.toDer();
+ PCCERT_CONTEXT wincert = CertCreateCertificateContext(X509_ASN_ENCODING, (const BYTE *)der.constData(), der.length());
+ if (!wincert) {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcTlsBackend, "QWindowsCaRootFetcher failed to convert certificate to windows form");
+#endif
+ emit finished(cert, QSslCertificate());
+ deleteLater();
+ return;
+ }
+
+ CERT_CHAIN_PARA parameters;
+ memset(&parameters, 0, sizeof(parameters));
+ parameters.cbSize = sizeof(parameters);
+ // set key usage constraint
+ parameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
+ parameters.RequestedUsage.Usage.cUsageIdentifier = 1;
+ LPSTR oid = (LPSTR)(mode == QSslSocket::SslClientMode ? szOID_PKIX_KP_SERVER_AUTH : szOID_PKIX_KP_CLIENT_AUTH);
+ parameters.RequestedUsage.Usage.rgpszUsageIdentifier = &oid;
+
+#ifdef QSSLSOCKET_DEBUG
+ QElapsedTimer stopwatch;
+ stopwatch.start();
+#endif
+ PCCERT_CHAIN_CONTEXT chain;
+ auto additionalStore = createAdditionalStore();
+ BOOL result = CertGetCertificateChain(
+ nullptr, //default engine
+ wincert,
+ nullptr, //current date/time
+ additionalStore.get(), //default store (nullptr) or CAs an application trusts
+ &parameters,
+ 0, //default dwFlags
+ nullptr, //reserved
+ &chain);
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "QWindowsCaRootFetcher" << stopwatch.elapsed() << "ms to get chain";
+#endif
+
+ QSslCertificate trustedRoot;
+ if (result) {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "QWindowsCaRootFetcher - examining windows chains";
+ if (chain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR)
+ qCDebug(lcSsl) << " - TRUSTED";
+ else
+ qCDebug(lcSsl) << " - NOT TRUSTED" << chain->TrustStatus.dwErrorStatus;
+ if (chain->TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED)
+ qCDebug(lcSsl) << " - SELF SIGNED";
+ qCDebug(lcSsl) << "QWindowsCaRootFetcher - dumping simple chains";
+ for (unsigned int i = 0; i < chain->cChain; i++) {
+ if (chain->rgpChain[i]->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR)
+ qCDebug(lcSsl) << " - TRUSTED SIMPLE CHAIN" << i;
+ else
+ qCDebug(lcSsl) << " - UNTRUSTED SIMPLE CHAIN" << i << "reason:" << chain->rgpChain[i]->TrustStatus.dwErrorStatus;
+ for (unsigned int j = 0; j < chain->rgpChain[i]->cElement; j++) {
+ QSslCertificate foundCert(QByteArray((const char *)chain->rgpChain[i]->rgpElement[j]->pCertContext->pbCertEncoded
+ , chain->rgpChain[i]->rgpElement[j]->pCertContext->cbCertEncoded), QSsl::Der);
+ qCDebug(lcSsl) << " - " << foundCert;
+ }
+ }
+ qCDebug(lcSsl) << " - and" << chain->cLowerQualityChainContext << "low quality chains"; //expect 0, we haven't asked for them
+#endif
+
+ //based on http://msdn.microsoft.com/en-us/library/windows/desktop/aa377182%28v=vs.85%29.aspx
+ //about the final chain rgpChain[cChain-1] which must begin with a trusted root to be valid
+ if (chain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR
+ && chain->cChain > 0) {
+ const PCERT_SIMPLE_CHAIN finalChain = chain->rgpChain[chain->cChain - 1];
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/aa377544%28v=vs.85%29.aspx
+ // rgpElement[0] is the end certificate chain element. rgpElement[cElement-1] is the self-signed "root" certificate element.
+ if (finalChain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR
+ && finalChain->cElement > 0) {
+ trustedRoot = QSslCertificate(QByteArray((const char *)finalChain->rgpElement[finalChain->cElement - 1]->pCertContext->pbCertEncoded
+ , finalChain->rgpElement[finalChain->cElement - 1]->pCertContext->cbCertEncoded), QSsl::Der);
+ }
+ } else if (explicitlyTrustedCAs.size()) {
+ // Setting custom CA in configuration, and those CAs are not trusted by MS.
+#if QT_CONFIG(openssl)
+ const auto verifiedChain = buildVerifiedChain(explicitlyTrustedCAs, chain, peerVerifyName);
+ if (verifiedChain.size())
+ trustedRoot = verifiedChain.last();
+#else
+ //It's only OpenSSL code-path that can trigger such a fetch.
+ Q_UNREACHABLE();
+#endif
+ }
+ CertFreeCertificateChain(chain);
+ }
+ CertFreeCertificateContext(wincert);
+
+ emit finished(cert, trustedRoot);
+ deleteLater();
+}
+
+QHCertStorePointer QWindowsCaRootFetcher::createAdditionalStore() const
+{
+ QHCertStorePointer customStore;
+ if (explicitlyTrustedCAs.isEmpty())
+ return customStore;
+
+ if (HCERTSTORE rawPtr = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, nullptr)) {
+ customStore.reset(rawPtr);
+
+ unsigned rootsAdded = 0;
+ for (const QSslCertificate &caCert : explicitlyTrustedCAs) {
+ const auto der = caCert.toDer();
+ PCCERT_CONTEXT winCert = CertCreateCertificateContext(X509_ASN_ENCODING,
+ reinterpret_cast<const BYTE *>(der.data()),
+ DWORD(der.length()));
+ if (!winCert) {
+#if defined(QSSLSOCKET_DEBUG)
+ qCWarning(lcSsl) << "CA fetcher, failed to convert QSslCertificate"
+ << "to the native representation";
+#endif // QSSLSOCKET_DEBUG
+ continue;
+ }
+ const auto deleter = qScopeGuard([winCert](){
+ CertFreeCertificateContext(winCert);
+ });
+ if (CertAddCertificateContextToStore(customStore.get(), winCert, CERT_STORE_ADD_ALWAYS, nullptr))
+ ++rootsAdded;
+#if defined(QSSLSOCKET_DEBUG)
+ else //Why assert? With flags we're using and winCert check - should not happen!
+ Q_ASSERT("CertAddCertificateContextToStore() failed");
+#endif // QSSLSOCKET_DEBUG
+ }
+ if (!rootsAdded) //Useless store, no cert was added.
+ customStore.reset();
+#if defined(QSSLSOCKET_DEBUG)
+ } else {
+
+ qCWarning(lcSsl) << "CA fetcher, failed to create a custom"
+ << "store for explicitly trusted CA certificate";
+#endif // QSSLSOCKET_DEBUG
+ }
+
+ return customStore;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwindowscarootfetcher_p.cpp"
diff --git a/src/plugins/tls/openssl/qwindowscarootfetcher_p.h b/src/plugins/tls/openssl/qwindowscarootfetcher_p.h
new file mode 100644
index 0000000000..715fd19945
--- /dev/null
+++ b/src/plugins/tls/openssl/qwindowscarootfetcher_p.h
@@ -0,0 +1,59 @@
+// 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
+
+#ifndef QWINDOWSCAROOTFETCHER_P_H
+#define QWINDOWSCAROOTFETCHER_P_H
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include <QtNetwork/qsslcertificate.h>
+#include <QtNetwork/qsslsocket.h>
+
+#include <QtCore/QtGlobal>
+#include <QtCore/QObject>
+
+#include "../shared/qwincrypt_p.h"
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsCaRootFetcher : public QObject
+{
+ Q_OBJECT
+public:
+ QWindowsCaRootFetcher(const QSslCertificate &certificate, QSslSocket::SslMode sslMode,
+ const QList<QSslCertificate> &caCertificates = {},
+ const QString &hostName = {});
+ ~QWindowsCaRootFetcher();
+public slots:
+ void start();
+signals:
+ void finished(QSslCertificate brokenChain, QSslCertificate caroot);
+private:
+ QHCertStorePointer createAdditionalStore() const;
+
+ QSslCertificate cert;
+ QSslSocket::SslMode mode;
+ // In case the application set CA certificates in the configuration,
+ // in the past we did not load missing certs. But this disables
+ // recoverable case when a certificate has Authority Information Access
+ // extension. So we try to fetch in this scenario also, but in case
+ // explicitly trusted root was not in a system store, we'll do
+ // additional checks, thus we need 'peerVerifyName':
+ QList<QSslCertificate> explicitlyTrustedCAs;
+ QString peerVerifyName;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSCAROOTFETCHER_P_H
diff --git a/src/plugins/tls/openssl/qx509_openssl.cpp b/src/plugins/tls/openssl/qx509_openssl.cpp
new file mode 100644
index 0000000000..0cd3749f88
--- /dev/null
+++ b/src/plugins/tls/openssl/qx509_openssl.cpp
@@ -0,0 +1,947 @@
+// 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 "qsslsocket_openssl_symbols_p.h"
+#include "qtlsbackend_openssl_p.h"
+#include "qtlskey_openssl_p.h"
+#include "qx509_openssl_p.h"
+#include "qtls_openssl_p.h"
+
+#include <QtNetwork/private/qsslcertificate_p.h>
+
+#include <QtNetwork/qsslsocket.h>
+#include <QtNetwork/qhostaddress.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
+
+using namespace Qt::StringLiterals;
+
+namespace QTlsPrivate {
+
+namespace {
+
+QByteArray asn1ObjectId(ASN1_OBJECT *object)
+{
+ if (!object)
+ return {};
+
+ char buf[80] = {}; // The openssl docs a buffer length of 80 should be more than enough
+ q_OBJ_obj2txt(buf, sizeof(buf), object, 1); // the 1 says always use the oid not the long name
+
+ return QByteArray(buf);
+}
+
+QByteArray asn1ObjectName(ASN1_OBJECT *object)
+{
+ if (!object)
+ return {};
+
+ const int nid = q_OBJ_obj2nid(object);
+ if (nid != NID_undef)
+ return QByteArray(q_OBJ_nid2sn(nid));
+
+ return asn1ObjectId(object);
+}
+
+QMultiMap<QByteArray, QString> mapFromX509Name(X509_NAME *name)
+{
+ if (!name)
+ return {};
+
+ QMultiMap<QByteArray, QString> info;
+ for (int i = 0; i < q_X509_NAME_entry_count(name); ++i) {
+ X509_NAME_ENTRY *e = q_X509_NAME_get_entry(name, i);
+
+ QByteArray name = asn1ObjectName(q_X509_NAME_ENTRY_get_object(e));
+ unsigned char *data = nullptr;
+ int size = q_ASN1_STRING_to_UTF8(&data, q_X509_NAME_ENTRY_get_data(e));
+ info.insert(name, QString::fromUtf8((char*)data, size));
+ q_CRYPTO_free(data, nullptr, 0);
+ }
+
+ return info;
+}
+
+QDateTime dateTimeFromASN1(const ASN1_TIME *aTime)
+{
+ QDateTime result;
+ tm lTime;
+
+ 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, QTimeZone::UTC);
+ }
+
+ return result;
+}
+
+
+#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
+#define ENDCERTSTRING "-----END CERTIFICATE-----"
+
+QByteArray x509ToQByteArray(X509 *x509, QSsl::EncodingFormat format)
+{
+ Q_ASSERT(x509);
+
+ // Use i2d_X509 to convert the X509 to an array.
+ const int length = q_i2d_X509(x509, nullptr);
+ if (length <= 0) {
+ QTlsBackendOpenSSL::logAndClearErrorQueue();
+ return {};
+ }
+
+ QByteArray array;
+ array.resize(length);
+
+ char *data = array.data();
+ char **dataP = &data;
+ unsigned char **dataPu = (unsigned char **)dataP;
+ if (q_i2d_X509(x509, dataPu) < 0)
+ return QByteArray();
+
+ if (format == QSsl::Der)
+ return array;
+
+ // Convert to Base64 - wrap at 64 characters.
+ array = array.toBase64();
+ QByteArray tmp;
+ for (int i = 0; i <= array.size() - 64; i += 64) {
+ tmp += QByteArray::fromRawData(array.data() + i, 64);
+ tmp += '\n';
+ }
+ if (int remainder = array.size() % 64) {
+ tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder);
+ tmp += '\n';
+ }
+
+ return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n";
+}
+
+QString x509ToText(X509 *x509)
+{
+ Q_ASSERT(x509);
+
+ QByteArray result;
+ BIO *bio = q_BIO_new(q_BIO_s_mem());
+ if (!bio)
+ return QString();
+ const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);});
+
+ q_X509_print(bio, x509);
+
+ QVarLengthArray<char, 16384> data;
+ int count = q_BIO_read(bio, data.data(), 16384);
+ if ( count > 0 )
+ result = QByteArray( data.data(), count );
+
+ return QString::fromLatin1(result);
+}
+
+QVariant x509UnknownExtensionToValue(X509_EXTENSION *ext)
+{
+ // Get the extension specific method object if available
+ // we cast away the const-ness here because some versions of openssl
+ // don't use const for the parameters in the functions pointers stored
+ // in the object.
+ Q_ASSERT(ext);
+
+ X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(ext));
+ if (!meth) {
+ ASN1_OCTET_STRING *value = q_X509_EXTENSION_get_data(ext);
+ Q_ASSERT(value);
+ QByteArray result( reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(value)),
+ q_ASN1_STRING_length(value));
+ return result;
+ }
+
+ void *ext_internal = q_X509V3_EXT_d2i(ext);
+ if (!ext_internal)
+ return {};
+
+ const auto extCleaner = qScopeGuard([meth, ext_internal]{
+ Q_ASSERT(ext_internal && meth);
+
+ if (meth->it)
+ q_ASN1_item_free(static_cast<ASN1_VALUE *>(ext_internal), ASN1_ITEM_ptr(meth->it));
+ else if (meth->ext_free)
+ meth->ext_free(ext_internal);
+ else
+ qCWarning(lcTlsBackend, "No method to free an unknown extension, a potential memory leak?");
+ });
+
+ // If this extension can be converted
+ if (meth->i2v) {
+ STACK_OF(CONF_VALUE) *val = meth->i2v(meth, ext_internal, nullptr);
+ const auto stackCleaner = qScopeGuard([val]{
+ if (val)
+ q_OPENSSL_sk_pop_free((OPENSSL_STACK *)val, (void(*)(void*))q_X509V3_conf_free);
+ });
+
+ QVariantMap map;
+ QVariantList list;
+ bool isMap = false;
+
+ for (int j = 0; j < q_SKM_sk_num(val); j++) {
+ CONF_VALUE *nval = q_SKM_sk_value(CONF_VALUE, val, j);
+ if (nval->name && nval->value) {
+ isMap = true;
+ map[QString::fromUtf8(nval->name)] = QString::fromUtf8(nval->value);
+ } else if (nval->name) {
+ list << QString::fromUtf8(nval->name);
+ } else if (nval->value) {
+ list << QString::fromUtf8(nval->value);
+ }
+ }
+
+ if (isMap)
+ return map;
+ else
+ return list;
+ } else if (meth->i2s) {
+ const char *hexString = meth->i2s(meth, ext_internal);
+ QVariant result(hexString ? QString::fromUtf8(hexString) : QString{});
+ q_OPENSSL_free((void *)hexString);
+ return result;
+ } else if (meth->i2r) {
+ QByteArray result;
+
+ BIO *bio = q_BIO_new(q_BIO_s_mem());
+ if (!bio)
+ return result;
+
+ meth->i2r(meth, ext_internal, bio, 0);
+
+ char *bio_buffer;
+ long bio_size = q_BIO_get_mem_data(bio, &bio_buffer);
+ result = QByteArray(bio_buffer, bio_size);
+
+ q_BIO_free(bio);
+ return result;
+ }
+
+ return QVariant();
+}
+
+/*
+ * Convert extensions to a variant. The naming of the keys of the map are
+ * taken from RFC 5280, however we decided the capitalisation in the RFC
+ * was too silly for the real world.
+ */
+QVariant x509ExtensionToValue(X509_EXTENSION *ext)
+{
+ ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext);
+ int nid = q_OBJ_obj2nid(obj);
+
+ // We cast away the const-ness here because some versions of openssl
+ // don't use const for the parameters in the functions pointers stored
+ // in the object.
+ X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(ext));
+
+ void *ext_internal = nullptr; // The value, returned by X509V3_EXT_d2i.
+ const auto extCleaner = qScopeGuard([meth, &ext_internal]() {
+ if (!meth || !ext_internal)
+ return;
+
+ if (meth->it)
+ q_ASN1_item_free(static_cast<ASN1_VALUE *>(ext_internal), ASN1_ITEM_ptr(meth->it));
+ else if (meth->ext_free)
+ meth->ext_free(ext_internal);
+ else
+ qWarning(lcTlsBackend, "Cannot free an extension, a potential memory leak?");
+ });
+
+ const char * hexString = nullptr; // The value returned by meth->i2s.
+ const auto hexStringCleaner = qScopeGuard([&hexString](){
+ if (hexString)
+ q_OPENSSL_free((void*)hexString);
+ });
+
+ switch (nid) {
+ case NID_basic_constraints:
+ {
+ BASIC_CONSTRAINTS *basic = reinterpret_cast<BASIC_CONSTRAINTS *>(q_X509V3_EXT_d2i(ext));
+ if (!basic)
+ return {};
+ QVariantMap result;
+ result["ca"_L1] = basic->ca ? true : false;
+ if (basic->pathlen)
+ result["pathLenConstraint"_L1] = (qlonglong)q_ASN1_INTEGER_get(basic->pathlen);
+
+ q_BASIC_CONSTRAINTS_free(basic);
+ return result;
+ }
+ break;
+ case NID_info_access:
+ {
+ AUTHORITY_INFO_ACCESS *info = reinterpret_cast<AUTHORITY_INFO_ACCESS *>(q_X509V3_EXT_d2i(ext));
+ if (!info)
+ return {};
+ QVariantMap result;
+ for (int i=0; i < q_SKM_sk_num(info); i++) {
+ ACCESS_DESCRIPTION *ad = q_SKM_sk_value(ACCESS_DESCRIPTION, info, i);
+
+ GENERAL_NAME *name = ad->location;
+ if (name->type == GEN_URI) {
+ int len = q_ASN1_STRING_length(name->d.uniformResourceIdentifier);
+ if (len < 0 || len >= 8192) {
+ // broken name
+ continue;
+ }
+
+ const char *uriStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(name->d.uniformResourceIdentifier));
+ const QString uri = QString::fromUtf8(uriStr, len);
+
+ result[QString::fromUtf8(asn1ObjectName(ad->method))] = uri;
+ } else {
+ qCWarning(lcTlsBackend) << "Strange location type" << name->type;
+ }
+ }
+
+ q_AUTHORITY_INFO_ACCESS_free(info);
+ return result;
+ }
+ break;
+ case NID_subject_key_identifier:
+ {
+ ext_internal = q_X509V3_EXT_d2i(ext);
+ if (!ext_internal)
+ return {};
+
+ hexString = meth->i2s(meth, ext_internal);
+ return QVariant(QString::fromUtf8(hexString));
+ }
+ break;
+ case NID_authority_key_identifier:
+ {
+ AUTHORITY_KEYID *auth_key = reinterpret_cast<AUTHORITY_KEYID *>(q_X509V3_EXT_d2i(ext));
+ if (!auth_key)
+ return {};
+ QVariantMap result;
+
+ // keyid
+ if (auth_key->keyid) {
+ QByteArray keyid(reinterpret_cast<const char *>(auth_key->keyid->data),
+ auth_key->keyid->length);
+ result["keyid"_L1] = keyid.toHex();
+ }
+
+ // issuer
+ // TODO: GENERAL_NAMES
+
+ // serial
+ if (auth_key->serial)
+ result["serial"_L1] = (qlonglong)q_ASN1_INTEGER_get(auth_key->serial);
+
+ q_AUTHORITY_KEYID_free(auth_key);
+ return result;
+ }
+ break;
+ }
+
+ return {};
+}
+
+} // Unnamed namespace
+
+extern "C" int qt_X509Callback(int ok, X509_STORE_CTX *ctx)
+{
+ if (!ok) {
+ // Store the error and at which depth the error was detected.
+ using ErrorListPtr = QList<QSslErrorEntry> *;
+ ErrorListPtr errors = nullptr;
+
+ // Error list is attached to either 'SSL' or 'X509_STORE'.
+ if (X509_STORE *store = q_X509_STORE_CTX_get0_store(ctx)) // We try store first:
+ errors = ErrorListPtr(q_X509_STORE_get_ex_data(store, 0));
+
+ if (!errors) {
+ // Not found on store? Try SSL and its external data then. According to the OpenSSL's
+ // documentation:
+ //
+ // "Whenever a X509_STORE_CTX object is created for the verification of the
+ // peer's certificate during a handshake, a pointer to the SSL object is
+ // stored into the X509_STORE_CTX object to identify the connection affected.
+ // To retrieve this pointer the X509_STORE_CTX_get_ex_data() function can be
+ // used with the correct index."
+
+ // TLSTODO: verification callback has to change as soon as TlsCryptographer is in place.
+ // This is a temporary solution for now to ease the transition.
+ const auto offset = QTlsBackendOpenSSL::s_indexForSSLExtraData
+ + TlsCryptographOpenSSL::errorOffsetInExData;
+ if (SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx())))
+ errors = ErrorListPtr(q_SSL_get_ex_data(ssl, offset));
+ }
+
+ if (!errors) {
+ qCWarning(lcTlsBackend, "Neither X509_STORE, nor SSL contains error list, verification failed");
+ return 0;
+ }
+
+ errors->append(X509CertificateOpenSSL::errorEntryFromStoreContext(ctx));
+ }
+ // Always return OK to allow verification to continue. We handle the
+ // errors gracefully after collecting all errors, after verification has
+ // completed.
+ return 1;
+}
+
+X509CertificateOpenSSL::X509CertificateOpenSSL() = default;
+
+X509CertificateOpenSSL::~X509CertificateOpenSSL()
+{
+ if (x509)
+ q_X509_free(x509);
+}
+
+bool X509CertificateOpenSSL::isEqual(const X509Certificate &rhs) const
+{
+ //TLSTODO: to make it safe I'll check the backend type later.
+ const auto &other = static_cast<const X509CertificateOpenSSL &>(rhs);
+ if (x509 && other.x509) {
+ const int ret = q_X509_cmp(x509, other.x509);
+ if (ret >= -1 && ret <= 1)
+ return ret == 0;
+ QTlsBackendOpenSSL::logAndClearErrorQueue();
+ }
+
+ return false;
+}
+
+bool X509CertificateOpenSSL::isSelfSigned() const
+{
+ if (!x509)
+ return false;
+
+ return q_X509_check_issued(x509, x509) == X509_V_OK;
+}
+
+QMultiMap<QSsl::AlternativeNameEntryType, QString>
+X509CertificateOpenSSL::subjectAlternativeNames() const
+{
+ QMultiMap<QSsl::AlternativeNameEntryType, QString> result;
+
+ if (!x509)
+ return result;
+
+ auto *altNames = static_cast<STACK_OF(GENERAL_NAME) *>(q_X509_get_ext_d2i(x509, NID_subject_alt_name,
+ nullptr, nullptr));
+ if (!altNames)
+ return result;
+
+ auto altName = [](ASN1_IA5STRING *ia5, int len) {
+ const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(ia5));
+ return QString::fromLatin1(altNameStr, len);
+ };
+
+ for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) {
+ const GENERAL_NAME *genName = q_sk_GENERAL_NAME_value(altNames, i);
+ if (genName->type != GEN_DNS && genName->type != GEN_EMAIL && genName->type != GEN_IPADD)
+ continue;
+
+ const int len = q_ASN1_STRING_length(genName->d.ia5);
+ if (len < 0 || len >= 8192) {
+ // broken name
+ continue;
+ }
+
+ switch (genName->type) {
+ case GEN_DNS:
+ result.insert(QSsl::DnsEntry, altName(genName->d.ia5, len));
+ break;
+ case GEN_EMAIL:
+ result.insert(QSsl::EmailEntry, altName(genName->d.ia5, len));
+ break;
+ case GEN_IPADD: {
+ QHostAddress ipAddress;
+ switch (len) {
+ case 4: // IPv4
+ ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast<quint32 *>(genName->d.iPAddress->data)));
+ break;
+ case 16: // IPv6
+ ipAddress = QHostAddress(reinterpret_cast<quint8 *>(genName->d.iPAddress->data));
+ break;
+ default: // Unknown IP address format
+ break;
+ }
+ if (!ipAddress.isNull())
+ result.insert(QSsl::IpAddressEntry, ipAddress.toString());
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ q_OPENSSL_sk_pop_free((OPENSSL_STACK*)altNames, reinterpret_cast<void(*)(void*)>(q_GENERAL_NAME_free));
+
+ return result;
+}
+
+TlsKey *X509CertificateOpenSSL::publicKey() const
+{
+ if (!x509)
+ return {};
+
+ return TlsKeyOpenSSL::publicKeyFromX509(x509);
+}
+
+QByteArray X509CertificateOpenSSL::toPem() const
+{
+ if (!x509)
+ return {};
+
+ return x509ToQByteArray(x509, QSsl::Pem);
+}
+
+QByteArray X509CertificateOpenSSL::toDer() const
+{
+ if (!x509)
+ return {};
+
+ return x509ToQByteArray(x509, QSsl::Der);
+
+}
+QString X509CertificateOpenSSL::toText() const
+{
+ if (!x509)
+ return {};
+
+ return x509ToText(x509);
+}
+
+Qt::HANDLE X509CertificateOpenSSL::handle() const
+{
+ return Qt::HANDLE(x509);
+}
+
+size_t X509CertificateOpenSSL::hash(size_t seed) const noexcept
+{
+ if (x509) {
+ const EVP_MD *sha1 = q_EVP_sha1();
+ unsigned int len = 0;
+ unsigned char md[EVP_MAX_MD_SIZE];
+ q_X509_digest(x509, sha1, md, &len);
+ return qHashBits(md, len, seed);
+ }
+
+ return seed;
+}
+
+QSslCertificate X509CertificateOpenSSL::certificateFromX509(X509 *x509)
+{
+ QSslCertificate certificate;
+
+ auto *backend = QTlsBackend::backend<X509CertificateOpenSSL>(certificate);
+ if (!backend || !x509)
+ return certificate;
+
+ ASN1_TIME *nbef = q_X509_getm_notBefore(x509);
+ if (nbef)
+ backend->notValidBefore = dateTimeFromASN1(nbef);
+
+ ASN1_TIME *naft = q_X509_getm_notAfter(x509);
+ if (naft)
+ backend->notValidAfter = dateTimeFromASN1(naft);
+
+ backend->null = false;
+ backend->x509 = q_X509_dup(x509);
+
+ backend->issuerInfoEntries = mapFromX509Name(q_X509_get_issuer_name(x509));
+ backend->subjectInfoEntries = mapFromX509Name(q_X509_get_subject_name(x509));
+ backend->versionString = QByteArray::number(qlonglong(q_X509_get_version(x509)) + 1);
+
+ if (ASN1_INTEGER *serialNumber = q_X509_get_serialNumber(x509)) {
+ QByteArray hexString;
+ hexString.reserve(serialNumber->length * 3);
+ for (int a = 0; a < serialNumber->length; ++a) {
+ hexString += QByteArray::number(serialNumber->data[a], 16).rightJustified(2, '0');
+ hexString += ':';
+ }
+ hexString.chop(1);
+ backend->serialNumberString = hexString;
+ }
+
+ backend->parseExtensions();
+
+ return certificate;
+}
+
+QList<QSslCertificate> X509CertificateOpenSSL::stackOfX509ToQSslCertificates(STACK_OF(X509) *x509)
+{
+ if (!x509)
+ return {};
+
+ QList<QSslCertificate> certificates;
+ for (int i = 0; i < q_sk_X509_num(x509); ++i) {
+ if (X509 *entry = q_sk_X509_value(x509, i))
+ certificates << certificateFromX509(entry);
+ }
+
+ return certificates;
+}
+
+QSslErrorEntry X509CertificateOpenSSL::errorEntryFromStoreContext(X509_STORE_CTX *ctx)
+{
+ Q_ASSERT(ctx);
+
+ return {q_X509_STORE_CTX_get_error(ctx), q_X509_STORE_CTX_get_error_depth(ctx)};
+}
+
+QList<QSslError> X509CertificateOpenSSL::verify(const QList<QSslCertificate> &chain,
+ const QString &hostName)
+{
+ // This was previously QSslSocketPrivate::verify().
+ auto roots = QSslConfiguration::defaultConfiguration().caCertificates();
+#ifndef Q_OS_WIN
+ // On Windows, system CA certificates are already set as default ones.
+ // No need to add them again (and again) and also, if the default configuration
+ // has its own set of CAs, this probably should not be amended by the ones
+ // from the 'ROOT' store, since it's not what an application chose to trust.
+ if (QSslSocketPrivate::rootCertOnDemandLoadingSupported())
+ roots.append(QSslSocketPrivate::systemCaCertificates());
+#endif // Q_OS_WIN
+ return verify(roots, chain, hostName);
+}
+
+QList<QSslError> X509CertificateOpenSSL::verify(const QList<QSslCertificate> &caCertificates,
+ const QList<QSslCertificate> &certificateChain,
+ const QString &hostName)
+{
+ // This was previously QSslSocketPrivate::verify().
+ if (certificateChain.size() <= 0)
+ return {QSslError(QSslError::UnspecifiedError)};
+
+ QList<QSslError> errors;
+ X509_STORE *certStore = q_X509_STORE_new();
+ if (!certStore) {
+ qCWarning(lcTlsBackend) << "Unable to create certificate store";
+ errors << QSslError(QSslError::UnspecifiedError);
+ return errors;
+ }
+ const std::unique_ptr<X509_STORE, decltype(&q_X509_STORE_free)> storeGuard(certStore, q_X509_STORE_free);
+
+ const QDateTime now = QDateTime::currentDateTimeUtc();
+ for (const QSslCertificate &caCertificate : caCertificates) {
+ // From https://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html:
+ //
+ // If several CA certificates matching the name, key identifier, and
+ // serial number condition are available, only the first one will be
+ // examined. This may lead to unexpected results if the same CA
+ // certificate is available with different expiration dates. If a
+ // ``certificate expired'' verification error occurs, no other
+ // certificate will be searched. Make sure to not have expired
+ // certificates mixed with valid ones.
+ //
+ // See also: QSslContext::sharedFromConfiguration()
+ if (caCertificate.expiryDate() >= now) {
+ q_X509_STORE_add_cert(certStore, reinterpret_cast<X509 *>(caCertificate.handle()));
+ }
+ }
+
+ QList<QSslErrorEntry> lastErrors;
+ if (!q_X509_STORE_set_ex_data(certStore, 0, &lastErrors)) {
+ qCWarning(lcTlsBackend) << "Unable to attach external data (error list) to a store";
+ errors << QSslError(QSslError::UnspecifiedError);
+ return errors;
+ }
+
+ // Register a custom callback to get all verification errors.
+ q_X509_STORE_set_verify_cb(certStore, qt_X509Callback);
+
+ // Build the chain of intermediate certificates
+ STACK_OF(X509) *intermediates = nullptr;
+ if (certificateChain.size() > 1) {
+ intermediates = (STACK_OF(X509) *) q_OPENSSL_sk_new_null();
+
+ if (!intermediates) {
+ errors << QSslError(QSslError::UnspecifiedError);
+ return errors;
+ }
+
+ bool first = true;
+ for (const QSslCertificate &cert : certificateChain) {
+ if (first) {
+ first = false;
+ continue;
+ }
+
+ q_OPENSSL_sk_push((OPENSSL_STACK *)intermediates, reinterpret_cast<X509 *>(cert.handle()));
+ }
+ }
+
+ X509_STORE_CTX *storeContext = q_X509_STORE_CTX_new();
+ if (!storeContext) {
+ errors << QSslError(QSslError::UnspecifiedError);
+ return errors;
+ }
+ std::unique_ptr<X509_STORE_CTX, decltype(&q_X509_STORE_CTX_free)> ctxGuard(storeContext, q_X509_STORE_CTX_free);
+
+ if (!q_X509_STORE_CTX_init(storeContext, certStore, reinterpret_cast<X509 *>(certificateChain[0].handle()), intermediates)) {
+ errors << QSslError(QSslError::UnspecifiedError);
+ return errors;
+ }
+
+ // Now we can actually perform the verification of the chain we have built.
+ // We ignore the result of this function since we process errors via the
+ // callback.
+ (void) q_X509_verify_cert(storeContext);
+ ctxGuard.reset();
+ q_OPENSSL_sk_free((OPENSSL_STACK *)intermediates);
+
+ // Now process the errors
+
+ if (certificateChain[0].isBlacklisted())
+ errors << QSslError(QSslError::CertificateBlacklisted, certificateChain[0]);
+
+ // Check the certificate name against the hostname if one was specified
+ if (!hostName.isEmpty() && !TlsCryptograph::isMatchingHostname(certificateChain[0], hostName)) {
+ // No matches in common names or alternate names.
+ QSslError error(QSslError::HostNameMismatch, certificateChain[0]);
+ errors << error;
+ }
+
+ // Translate errors from the error list into QSslErrors.
+ errors.reserve(errors.size() + lastErrors.size());
+ for (const auto &error : std::as_const(lastErrors))
+ errors << openSSLErrorToQSslError(error.code, certificateChain.value(error.depth));
+
+ return errors;
+}
+
+QList<QSslCertificate> X509CertificateOpenSSL::certificatesFromPem(const QByteArray &pem, int count)
+{
+ QList<QSslCertificate> certificates;
+
+ int offset = 0;
+ while (count == -1 || certificates.size() < count) {
+ int startPos = pem.indexOf(BEGINCERTSTRING, offset);
+ if (startPos == -1)
+ break;
+ startPos += sizeof(BEGINCERTSTRING) - 1;
+ if (!matchLineFeed(pem, &startPos))
+ break;
+
+ int endPos = pem.indexOf(ENDCERTSTRING, startPos);
+ if (endPos == -1)
+ break;
+
+ offset = endPos + sizeof(ENDCERTSTRING) - 1;
+ if (offset < pem.size() && !matchLineFeed(pem, &offset))
+ break;
+
+ QByteArray decoded = QByteArray::fromBase64(
+ QByteArray::fromRawData(pem.data() + startPos, endPos - startPos));
+ const unsigned char *data = (const unsigned char *)decoded.data();
+
+ if (X509 *x509 = q_d2i_X509(nullptr, &data, decoded.size())) {
+ certificates << certificateFromX509(x509);
+ q_X509_free(x509);
+ }
+ }
+
+ return certificates;
+}
+
+QList<QSslCertificate> X509CertificateOpenSSL::certificatesFromDer(const QByteArray &der, int count)
+{
+ QList<QSslCertificate> certificates;
+
+ const unsigned char *data = (const unsigned char *)der.data();
+ int size = der.size();
+
+ while (size > 0 && (count == -1 || certificates.size() < count)) {
+ if (X509 *x509 = q_d2i_X509(nullptr, &data, size)) {
+ certificates << certificateFromX509(x509);
+ q_X509_free(x509);
+ } else {
+ break;
+ }
+ size -= ((const char *)data - der.data());
+ }
+
+ return certificates;
+}
+
+bool X509CertificateOpenSSL::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);
+
+ // Read the file into a BIO
+ QByteArray pkcs12data = device->readAll();
+ if (pkcs12data.size() == 0)
+ return false;
+
+ BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pkcs12data.constData()), pkcs12data.size());
+ if (!bio) {
+ qCWarning(lcTlsBackend, "BIO_new_mem_buf returned null");
+ return false;
+ }
+ const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);});
+
+ // Create the PKCS#12 object
+ PKCS12 *p12 = q_d2i_PKCS12_bio(bio, nullptr);
+ if (!p12) {
+ qCWarning(lcTlsBackend, "Unable to read PKCS#12 structure, %s",
+ q_ERR_error_string(q_ERR_get_error(), nullptr));
+ return false;
+ }
+ const auto p12Raii = qScopeGuard([p12]{q_PKCS12_free(p12);});
+
+ // Extract the data
+ EVP_PKEY *pkey = nullptr;
+ X509 *x509 = nullptr;
+ STACK_OF(X509) *ca = nullptr;
+
+ if (!q_PKCS12_parse(p12, passPhrase.constData(), &pkey, &x509, &ca)) {
+ qCWarning(lcTlsBackend, "Unable to parse PKCS#12 structure, %s",
+ q_ERR_error_string(q_ERR_get_error(), nullptr));
+ return false;
+ }
+
+ const auto x509Raii = qScopeGuard([x509]{q_X509_free(x509);});
+ const auto keyRaii = qScopeGuard([pkey]{q_EVP_PKEY_free(pkey);});
+ const auto caRaii = qScopeGuard([ca] {
+ q_OPENSSL_sk_pop_free(reinterpret_cast<OPENSSL_STACK *>(ca),
+ reinterpret_cast<void (*)(void *)>(q_X509_free));
+ });
+
+ // Convert to Qt types
+ auto *tlsKey = QTlsBackend::backend<TlsKeyOpenSSL>(*key);
+ if (!tlsKey || !tlsKey->fromEVP_PKEY(pkey)) {
+ qCWarning(lcTlsBackend, "Unable to convert private key");
+ return false;
+ }
+
+ *cert = certificateFromX509(x509);
+
+ if (caCertificates)
+ *caCertificates = stackOfX509ToQSslCertificates(ca);
+
+ return true;
+}
+
+QSslError X509CertificateOpenSSL::openSSLErrorToQSslError(int errorCode, const QSslCertificate &cert)
+{
+ QSslError error;
+ switch (errorCode) {
+ case X509_V_OK:
+ // X509_V_OK is also reported if the peer had no certificate.
+ break;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ error = QSslError(QSslError::UnableToGetIssuerCertificate, cert); break;
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ error = QSslError(QSslError::UnableToDecryptCertificateSignature, cert); break;
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ error = QSslError(QSslError::UnableToDecodeIssuerPublicKey, cert); break;
+ case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+ error = QSslError(QSslError::CertificateSignatureFailed, cert); break;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ error = QSslError(QSslError::CertificateNotYetValid, cert); break;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ error = QSslError(QSslError::CertificateExpired, cert); break;
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ error = QSslError(QSslError::InvalidNotBeforeField, cert); break;
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ error = QSslError(QSslError::InvalidNotAfterField, cert); break;
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ error = QSslError(QSslError::SelfSignedCertificate, cert); break;
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ error = QSslError(QSslError::SelfSignedCertificateInChain, cert); break;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ error = QSslError(QSslError::UnableToGetLocalIssuerCertificate, cert); break;
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ error = QSslError(QSslError::UnableToVerifyFirstCertificate, cert); break;
+ case X509_V_ERR_CERT_REVOKED:
+ error = QSslError(QSslError::CertificateRevoked, cert); break;
+ case X509_V_ERR_INVALID_CA:
+ error = QSslError(QSslError::InvalidCaCertificate, cert); break;
+ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+ error = QSslError(QSslError::PathLengthExceeded, cert); break;
+ case X509_V_ERR_INVALID_PURPOSE:
+ error = QSslError(QSslError::InvalidPurpose, cert); break;
+ case X509_V_ERR_CERT_UNTRUSTED:
+ error = QSslError(QSslError::CertificateUntrusted, cert); break;
+ case X509_V_ERR_CERT_REJECTED:
+ error = QSslError(QSslError::CertificateRejected, cert); break;
+ default:
+ error = QSslError(QSslError::UnspecifiedError, cert); break;
+ }
+ return error;
+}
+
+void X509CertificateOpenSSL::parseExtensions()
+{
+ extensions.clear();
+
+ if (!x509)
+ return;
+
+ int count = q_X509_get_ext_count(x509);
+ if (count <= 0)
+ return;
+
+ extensions.reserve(count);
+
+ for (int i = 0; i < count; i++) {
+ X509_EXTENSION *ext = q_X509_get_ext(x509, i);
+ if (!ext) {
+ qCWarning(lcTlsBackend) << "Invalid (nullptr) extension at index" << i;
+ continue;
+ }
+
+ extensions << convertExtension(ext);
+ }
+
+ // Converting an extension may result in an error(s), clean them up:
+ QTlsBackendOpenSSL::clearErrorQueue();
+}
+
+X509CertificateBase::X509CertificateExtension X509CertificateOpenSSL::convertExtension(X509_EXTENSION *ext)
+{
+ Q_ASSERT(ext);
+
+ X509CertificateExtension result;
+
+ ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext);
+ if (!obj)
+ return result;
+
+ result.oid = QString::fromUtf8(asn1ObjectId(obj));
+ result.name = QString::fromUtf8(asn1ObjectName(obj));
+
+ result.critical = bool(q_X509_EXTENSION_get_critical(ext));
+
+ // Lets see if we have custom support for this one
+ QVariant extensionValue = x509ExtensionToValue(ext);
+ if (extensionValue.isValid()) {
+ result.value = extensionValue;
+ result.supported = true;
+ return result;
+ }
+
+ extensionValue = x509UnknownExtensionToValue(ext);
+ if (extensionValue.isValid())
+ result.value = extensionValue;
+
+ result.supported = false;
+
+ return result;
+}
+
+} // namespace QTlsPrivate
+
+QT_END_NAMESPACE
diff --git a/src/plugins/tls/openssl/qx509_openssl_p.h b/src/plugins/tls/openssl/qx509_openssl_p.h
new file mode 100644
index 0000000000..3b2e06f343
--- /dev/null
+++ b/src/plugins/tls/openssl/qx509_openssl_p.h
@@ -0,0 +1,88 @@
+// 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
+
+#ifndef QX509_OPENSSL_P_H
+#define QX509_OPENSSL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include "../shared/qx509_base_p.h"
+
+#include <QtNetwork/private/qtlsbackend_p.h>
+
+#include <QtCore/qvariant.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qstring.h>
+
+#include "qopenssl_p.h"
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+namespace QTlsPrivate {
+
+class X509CertificateOpenSSL final : public X509CertificateBase
+{
+public:
+ X509CertificateOpenSSL();
+ ~X509CertificateOpenSSL();
+
+ // TLSTODO: in future may become movable/copyable (ref-counted based
+ // OpenSSL's X509 implementation).
+
+ bool isEqual(const X509Certificate &rhs) const override;
+ bool isSelfSigned() const override;
+ QMultiMap<QSsl::AlternativeNameEntryType, QString> subjectAlternativeNames() const override;
+ TlsKey *publicKey() const override;
+
+ QByteArray toPem() const override;
+ QByteArray toDer() const override;
+ QString toText() const override;
+ Qt::HANDLE handle() const override;
+
+ size_t hash(size_t seed) const noexcept override;
+
+ static QSslCertificate certificateFromX509(X509 *x);
+ static QList<QSslCertificate> stackOfX509ToQSslCertificates(STACK_OF(X509) *x509);
+ static QSslErrorEntry errorEntryFromStoreContext(X509_STORE_CTX *ctx);
+
+ static QList<QSslError> verify(const QList<QSslCertificate> &chain, const QString &hostName);
+ static QList<QSslError> verify(const QList<QSslCertificate> &caCertificates,
+ const QList<QSslCertificate> &certificateChain,
+ const QString &hostName);
+
+ static QList<QSslCertificate> certificatesFromPem(const QByteArray &pem, int count);
+ static QList<QSslCertificate> certificatesFromDer(const QByteArray &der, int count);
+ static bool importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert,
+ QList<QSslCertificate> *caCertificates,
+ const QByteArray &passPhrase);
+
+ static QSslError openSSLErrorToQSslError(int errorCode, const QSslCertificate &cert);
+private:
+ void parseExtensions();
+ static X509CertificateExtension convertExtension(X509_EXTENSION *ext);
+
+ X509 *x509 = nullptr;
+
+ Q_DISABLE_COPY_MOVE(X509CertificateOpenSSL)
+};
+
+extern "C" int qt_X509Callback(int ok, X509_STORE_CTX *ctx);
+
+} // namespace QTlsPrivate
+
+QT_END_NAMESPACE
+
+#endif // QX509_OPENSSL_P_H