summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimur Pocheptsov <timur.pocheptsov@qt.io>2021-01-18 14:29:10 +0100
committerTimur Pocheptsov <timur.pocheptsov@qt.io>2021-01-22 09:09:46 +0100
commit7cf8e5ada9eac00b200141fdc80a2e76c0422411 (patch)
tree1e832df0dbdad5f7fb38aab6fbfa2a93b2ffa081
parent946facb1ae5ed1cf54bb5141bc5ca20ddbb26755 (diff)
QSsl: add a new private API
This is an abstraction for TLS backend and its factory, preparing to transition to plugin-based design. Task-number: QTBUG-65922 Change-Id: Ibe810e77fd1b715a6bea66cd3f44312b015ac274 Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
-rw-r--r--src/network/CMakeLists.txt1
-rw-r--r--src/network/ssl/qsslsocket.cpp88
-rw-r--r--src/network/ssl/qsslsocket.h2
-rw-r--r--src/network/ssl/qsslsocket_mac.cpp150
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp192
-rw-r--r--src/network/ssl/qsslsocket_p.h18
-rw-r--r--src/network/ssl/qsslsocket_schannel.cpp160
-rw-r--r--src/network/ssl/qtlsbackend.cpp299
-rw-r--r--src/network/ssl/qtlsbackend_p.h177
-rw-r--r--tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp140
10 files changed, 903 insertions, 324 deletions
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
index 003e504c92..28d78989f8 100644
--- a/src/network/CMakeLists.txt
+++ b/src/network/CMakeLists.txt
@@ -326,6 +326,7 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_ssl
ssl/qsslkey.h ssl/qsslkey_p.cpp ssl/qsslkey_p.h
ssl/qsslpresharedkeyauthenticator.cpp ssl/qsslpresharedkeyauthenticator.h ssl/qsslpresharedkeyauthenticator_p.h
ssl/qsslsocket.cpp ssl/qsslsocket.h ssl/qsslsocket_p.h
+ ssl/qtlsbackend.cpp ssl/qtlsbackend_p.h
)
qt_internal_extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_ssl
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 644e3771dd..0c231c1600 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -385,6 +385,7 @@
#include "qsslsocket.h"
#include "qsslcipher.h"
#include "qocspresponse.h"
+#include "qtlsbackend_p.h"
#ifndef QT_NO_OPENSSL
#include "qsslsocket_openssl_p.h"
#endif
@@ -1556,43 +1557,68 @@ QString QSslSocket::sslLibraryBuildVersionString()
*/
QList<QString> QSslSocket::availableBackends()
{
- return QSslSocketPrivate::availableBackends();
+ return QTlsBackendFactory::availableBackendNames();
}
/*!
\since 6.1
- Returns the name of the backend that was loaded (implicitly by QSslSocket
- or by an application via loadBackend() call). If no backend was loaded yet,
- this function returns the name of the backend that will be loaded by QSslSocket.
+ Returns the name of the backend that QSslSocket and related classes
+ use. If the active backend was not set explicitly, this function
+ returns the name of a default backend that QSslSocket selects implicitly
+ from the list of available backends.
- \note When selecting a default backend implicitly from the list of available
- backends, QSslSocket prefers native backends, such as SecureTransport on Darwin,
- or Schannel on Windows.
+ \note When selecting a default backend implicitly, QSslSocket prefers
+ native backends, such as SecureTransport on Darwin, or Schannel on Windows.
- \sa loadBackend(), availableBackends()
+ \sa setActiveBackend(), availableBackends()
*/
QString QSslSocket::activeBackend()
{
- return QSslSocketPrivate::activeBackend();
+ const QMutexLocker locker(&QSslSocketPrivate::backendMutex);
+
+ if (!QSslSocketPrivate::activeBackendName.size())
+ QSslSocketPrivate::activeBackendName = QTlsBackendFactory::defaultBackendName();
+
+ return QSslSocketPrivate::activeBackendName;
}
/*!
\since 6.1
- Returns true if a backend with name \a backendName was loaded
- and was made the current active backend. \a backendName must
- be one of names returned by availableBackends().
+ Returns true if a backend with name \a backendName was set as
+ active backend. \a backendName must be one of names returned
+ by availableBackends().
- \note An application can switch from the default backend,
- that will be implicitly loaded by QSslSocket, to a different backend
- only once. It cannot mix several backends simultaneously. A non-default
- backend must be selected prior to any use of QSslSocket or related classes
- (like QSslCertificate or QSslKey).
+ \note An application cannot mix different backends simultaneously.
+ This implies that a non-default backend must be selected prior
+ to any use of QSslSocket or related classes, e.g. QSslCertificate
+ or QSslKey.
\sa activeBackend(), availableBackends()
*/
-bool QSslSocket::loadBackend(const QString &backendName)
+bool QSslSocket::setActiveBackend(const QString &backendName)
{
- return QSslSocketPrivate::loadBackend(backendName);
+ if (!backendName.size()) {
+ qCWarning(lcSsl, "Invalid parameter (backend name cannot be an empty string)");
+ return false;
+ }
+
+ QMutexLocker locker(&QSslSocketPrivate::backendMutex);
+ if (QSslSocketPrivate::tlsBackend.get()) {
+ qCWarning(lcSsl) << "Cannot set backend named" << backendName
+ << "as active, another backend is already in use";
+ locker.unlock();
+ return activeBackend() == backendName;
+ }
+
+ if (!QTlsBackendFactory::availableBackendNames().contains(backendName)) {
+ qCWarning(lcSsl) << "Cannot set unavailable backend named" << backendName
+ << "as active";
+ return false;
+ }
+
+ QSslSocketPrivate::activeBackendName = backendName;
+
+ return true;
}
/*!
@@ -1606,13 +1632,7 @@ bool QSslSocket::loadBackend(const QString &backendName)
*/
QList<QSsl::SslProtocol> QSslSocket::supportedProtocols(const QString &backendName)
{
- if (Q_UNLIKELY(backendName.size() && !availableBackends().contains(backendName))) {
- qCWarning(lcSsl) << "Cannot provide the list of supported protocols for the backend"
- << backendName;
- return {};
- }
-
- return QSslSocketPrivate::supportedProtocols(backendName);
+ return QTlsBackendFactory::supportedProtocols(backendName.size() ? backendName : activeBackend());
}
/*!
@@ -1638,13 +1658,7 @@ bool QSslSocket::isProtocolSupported(QSsl::SslProtocol protocol, const QString &
*/
QList<QSsl::ImplementedClass> QSslSocket::implementedClasses(const QString &backendName)
{
- if (Q_UNLIKELY(backendName.size() && !availableBackends().contains(backendName))) {
- qCWarning(lcSsl) << "Cannot provide information about supported classes for"
- << "backend" << backendName;
- return {};
- }
-
- return QSslSocketPrivate::implementedClasses(backendName);
+ return QTlsBackendFactory::implementedClasses(backendName.size() ? backendName : activeBackend());
}
/*!
@@ -1669,13 +1683,7 @@ bool QSslSocket::isClassImplemented(QSsl::ImplementedClass cl, const QString &ba
*/
QList<QSsl::SupportedFeature> QSslSocket::supportedFeatures(const QString &backendName)
{
- if (Q_UNLIKELY(backendName.size() && !availableBackends().contains(backendName))) {
- qCWarning(lcSsl) << "Cannot provide information about supported features for"
- << "backend" << backendName;
- return {};
- }
-
- return QSslSocketPrivate::supportedFeatures(backendName);
+ return QTlsBackendFactory::supportedFeatures(backendName.size() ? backendName : activeBackend());
}
/*!
diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h
index 9bc90fad32..8841929eec 100644
--- a/src/network/ssl/qsslsocket.h
+++ b/src/network/ssl/qsslsocket.h
@@ -165,7 +165,7 @@ public:
static QList<QString> availableBackends();
static QString activeBackend();
- static bool loadBackend(const QString &backendName);
+ static bool setActiveBackend(const QString &backendName);
static QList<QSsl::SslProtocol> supportedProtocols(const QString &backendName = {});
static bool isProtocolSupported(QSsl::SslProtocol protocol, const QString &backendName = {});
static QList<QSsl::ImplementedClass> implementedClasses(const QString &backendName = {});
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp
index 9f1d8d1e54..abbcf8a6ac 100644
--- a/src/network/ssl/qsslsocket_mac.cpp
+++ b/src/network/ssl/qsslsocket_mac.cpp
@@ -44,6 +44,7 @@
#include "qsslsocket_mac_p.h"
#include "qasn1element_p.h"
#include "qsslcertificate_p.h"
+#include "qtlsbackend_p.h"
#include "qsslcipher_p.h"
#include "qsslkey_p.h"
@@ -75,6 +76,67 @@ QT_BEGIN_NAMESPACE
namespace
{
+
+// These two classes are ad-hoc temporary solution, to be replaced
+// by the real things soon.
+class SecureTransportBackend : public QTlsBackend
+{
+private:
+ QString backendName() const override
+ {
+ return QTlsBackendFactory::builtinBackendNames[QTlsBackendFactory::nameIndexSecureTransport];
+ }
+};
+
+class SecureTransportBackendFactory : public QTlsBackendFactory
+{
+private:
+ QString backendName() const override
+ {
+ return QTlsBackendFactory::builtinBackendNames[QTlsBackendFactory::nameIndexSecureTransport];
+ }
+ QTlsBackend *create() const override
+ {
+ return new SecureTransportBackend;
+ }
+
+ QList<QSsl::SslProtocol> supportedProtocols() const override
+ {
+ QList<QSsl::SslProtocol> protocols;
+
+ protocols << QSsl::AnyProtocol;
+ protocols << QSsl::SecureProtocols;
+ protocols << QSsl::TlsV1_0;
+ protocols << QSsl::TlsV1_0OrLater;
+ protocols << QSsl::TlsV1_1;
+ protocols << QSsl::TlsV1_1OrLater;
+ protocols << QSsl::TlsV1_2;
+ protocols << QSsl::TlsV1_2OrLater;
+
+ return protocols;
+ }
+
+ QList<QSsl::SupportedFeature> supportedFeatures() const override
+ {
+ QList<QSsl::SupportedFeature> features;
+ features << QSsl::SupportedFeature::ClientSideAlpn;
+
+ return features;
+ }
+
+ QList<QSsl::ImplementedClass> implementedClasses() const override
+ {
+ QList<QSsl::ImplementedClass> classes;
+ classes << QSsl::ImplementedClass::Socket;
+ classes << QSsl::ImplementedClass::Certificate;
+ classes << QSsl::ImplementedClass::Key;
+
+ return classes;
+ }
+};
+
+Q_GLOBAL_STATIC(SecureTransportBackendFactory, factory)
+
#ifdef Q_OS_MACOS
/*
@@ -1552,90 +1614,12 @@ bool QSslSocketBackendPrivate::startHandshake()
}
}
-QList<QString> QSslSocketPrivate::availableBackends()
+void QSslSocketPrivate::registerAdHocFactory()
{
- return {QStringLiteral("securetransport")};
-}
-
-QString QSslSocketPrivate::activeBackend()
-{
- return availableBackends().first();
-}
-
-bool QSslSocketPrivate::loadBackend(const QString &backendName)
-{
- if (backendName.size() && !availableBackends().contains(backendName)) {
- qCWarning(lcSsl) << "A TLS backend with name" << backendName << "is not available";
- return false;
- }
-
- static bool loaded = false;
- static QBasicMutex mutex;
- const QMutexLocker locker(&mutex);
- if (loaded) {
- qCWarning(lcSsl) << "You have already loaded the backend named:" << activeBackend();
- if (backendName.size())
- qCWarning(lcSsl) << "Cannot load:" << backendName;
- else
- qCWarning(lcSsl) << "Cannot load the default backend (securetransport)";
- return true;
- }
- // This code to be placed in qsslsocket.cpp and there
- // the actual plugin to be loaded (so the result can be
- // false if we, for example, failed to resolve OpenSSL
- // symbols).
- return loaded = true;
-}
-
-QList<QSsl::SslProtocol> QSslSocketPrivate::supportedProtocols(const QString &backendName)
-{
- QList<QSsl::SslProtocol> protocols;
- if (backendName.size() && backendName != activeBackend()) {
- qCWarning(lcSsl) << "Unexpected backend name" << backendName
- << "no information about protocols supported can be found";
- return protocols;
- }
-
- protocols << QSsl::AnyProtocol;
- protocols << QSsl::SecureProtocols;
- protocols << QSsl::TlsV1_0;
- protocols << QSsl::TlsV1_0OrLater;
- protocols << QSsl::TlsV1_1;
- protocols << QSsl::TlsV1_1OrLater;
- protocols << QSsl::TlsV1_2;
- protocols << QSsl::TlsV1_2OrLater;
-
- return protocols;
-}
-
-QList<QSsl::ImplementedClass> QSslSocketPrivate::implementedClasses(const QString &backendName)
-{
- QList<QSsl::ImplementedClass> classes;
- if (backendName.size() && backendName != activeBackend()) {
- qCWarning(lcSsl) << "Unexpected backend name" << backendName
- << "no information about classes implemented can be found";
- return classes;
- }
-
- classes << QSsl::ImplementedClass::Key;
- classes << QSsl::ImplementedClass::Certificate;
- classes << QSsl::ImplementedClass::Socket;
-
- return classes;
-}
-
-QList<QSsl::SupportedFeature> QSslSocketPrivate::supportedFeatures(const QString &backendName)
-{
- QList<QSsl::SupportedFeature> features;
- if (backendName.size() && backendName != activeBackend()) {
- qCWarning(lcSsl) << "Unexpected backend name" << backendName
- << "no information about classes implemented can be found";
- return features;
- }
-
- features << QSsl::SupportedFeature::ClientSideAlpn;
-
- return features;
+ // TLSTODO: this is a temporary solution, waiting for
+ // backends to move to ... plugins.
+ if (!factory())
+ qCWarning(lcSsl, "Failed to create backend factory");
}
QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index cab18d3147..2f39b68002 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -101,6 +101,89 @@ QT_BEGIN_NAMESPACE
namespace {
+// These two classes are ad-hoc temporary solution, to be replaced
+// by the real things soon.
+class OpenSSLBackend : public QTlsBackend
+{
+private:
+ QString backendName() const override
+ {
+ return QTlsBackendFactory::builtinBackendNames[QTlsBackendFactory::nameIndexOpenSSL];
+ }
+};
+
+class OpenSSLBackendFactory : public QTlsBackendFactory
+{
+private:
+ QString backendName() const override
+ {
+ return QTlsBackendFactory::builtinBackendNames[QTlsBackendFactory::nameIndexOpenSSL];
+ }
+ QTlsBackend *create() const override
+ {
+ return new OpenSSLBackend;
+ }
+
+ QList<QSsl::SslProtocol> supportedProtocols() const override
+ {
+ QList<QSsl::SslProtocol> protocols;
+
+ protocols << QSsl::AnyProtocol;
+ protocols << QSsl::SecureProtocols;
+ protocols << QSsl::TlsV1_0;
+ protocols << QSsl::TlsV1_0OrLater;
+ protocols << QSsl::TlsV1_1;
+ protocols << QSsl::TlsV1_1OrLater;
+ 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)
+ protocols << QSsl::DtlsV1_0;
+ protocols << QSsl::DtlsV1_0OrLater;
+ protocols << QSsl::DtlsV1_2;
+ protocols << QSsl::DtlsV1_2OrLater;
+#endif // dtls
+
+ return protocols;
+ }
+
+ QList<QSsl::SupportedFeature> supportedFeatures() const override
+ {
+ QList<QSsl::SupportedFeature> features;
+
+ features << QSsl::SupportedFeature::CertificateVerification;
+ features << QSsl::SupportedFeature::ClientSideAlpn;
+ features << QSsl::SupportedFeature::ServerSideAlpn;
+ features << QSsl::SupportedFeature::Ocsp;
+ features << QSsl::SupportedFeature::Psk;
+ features << QSsl::SupportedFeature::SessionTicket;
+ features << QSsl::SupportedFeature::Alerts;
+
+ return features;
+ }
+
+ QList<QSsl::ImplementedClass> implementedClasses() const override
+ {
+ QList<QSsl::ImplementedClass> classes;
+
+ classes << QSsl::ImplementedClass::Key;
+ classes << QSsl::ImplementedClass::Certificate;
+ classes << QSsl::ImplementedClass::Socket;
+ classes << QSsl::ImplementedClass::Dtls;
+ classes << QSsl::ImplementedClass::EllipticCurve;
+ classes << QSsl::ImplementedClass::DiffieHellman;
+
+ return classes;
+ }
+};
+
+Q_GLOBAL_STATIC(OpenSSLBackendFactory, factory)
+
QSsl::AlertLevel tlsAlertLevel(int value)
{
using QSsl::AlertLevel;
@@ -2510,111 +2593,12 @@ bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device,
return true;
}
-QList<QString> QSslSocketPrivate::availableBackends()
-{
- return {QStringLiteral("openssl")};
-}
-
-QString QSslSocketPrivate::activeBackend()
+void QSslSocketPrivate::registerAdHocFactory()
{
- return availableBackends().first();
-}
-
-bool QSslSocketPrivate::loadBackend(const QString &backendName)
-{
- if (backendName.size() && backendName != activeBackend()) {
- qCWarning(lcSsl) << "A TLS backend with name" << backendName << "is not available";
- return false;
- }
-
- static bool loaded = false;
- static QBasicMutex mutex;
- const QMutexLocker locker(&mutex);
- if (loaded) {
- qCWarning(lcSsl) << "You have already loaded the backend named:" << activeBackend();
- if (backendName.size())
- qCWarning(lcSsl) << "Cannot load:" << backendName;
- else
- qCWarning(lcSsl) << "Cannot load the default backend (openssl)";
- return true;
- }
- // This code to be placed in qsslsocket.cpp and there
- // the actual plugin to be loaded (so the result can be
- // false if we, for example, failed to resolve OpenSSL
- // symbols).
- return loaded = true;
-}
-
-QList<QSsl::SslProtocol> QSslSocketPrivate::supportedProtocols(const QString &backendName)
-{
- QList<QSsl::SslProtocol> protocols;
- if (backendName.size() && backendName != activeBackend()) {
- qCWarning(lcSsl) << "Unexpected backend name" << backendName
- << "no information about protocols supported can be found";
- return protocols;
- }
-
- protocols << QSsl::AnyProtocol;
- protocols << QSsl::SecureProtocols;
- protocols << QSsl::TlsV1_0;
- protocols << QSsl::TlsV1_0OrLater;
- protocols << QSsl::TlsV1_1;
- protocols << QSsl::TlsV1_1OrLater;
- 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)
- protocols << QSsl::DtlsV1_0;
- protocols << QSsl::DtlsV1_0OrLater;
- protocols << QSsl::DtlsV1_2;
- protocols << QSsl::DtlsV1_2OrLater;
-#endif // dtls
-
- return protocols;
-}
-
-QList<QSsl::ImplementedClass> QSslSocketPrivate::implementedClasses(const QString &backendName)
-{
- QList<QSsl::ImplementedClass> classes;
- if (backendName.size() && backendName != activeBackend()) {
- qCWarning(lcSsl) << "Unexpected backend name" << backendName
- << "no information about classes implemented can be found";
- return classes;
- }
-
- classes << QSsl::ImplementedClass::Key;
- classes << QSsl::ImplementedClass::Certificate;
- classes << QSsl::ImplementedClass::Socket;
- classes << QSsl::ImplementedClass::Dtls;
- classes << QSsl::ImplementedClass::EllipticCurve;
- classes << QSsl::ImplementedClass::DiffieHellman;
-
- return classes;
-}
-
-QList<QSsl::SupportedFeature> QSslSocketPrivate::supportedFeatures(const QString &backendName)
-{
- QList<QSsl::SupportedFeature> features;
- if (backendName.size() && backendName != activeBackend()) {
- qCWarning(lcSsl) << "Unexpected backend name" << backendName
- << "no information about classes implemented can be found";
- return features;
- }
-
- features << QSsl::SupportedFeature::CertificateVerification;
- features << QSsl::SupportedFeature::ClientSideAlpn;
- features << QSsl::SupportedFeature::ServerSideAlpn;
- features << QSsl::SupportedFeature::Ocsp;
- features << QSsl::SupportedFeature::Psk;
- features << QSsl::SupportedFeature::SessionTicket;
- features << QSsl::SupportedFeature::Alerts;
-
- return features;
+ // TLSTODO: this is a temporary solution, waiting for
+ // backends to move to ... plugins.
+ if (!factory())
+ qCWarning(lcSsl, "Failed to create backend factory");
}
QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index a98f833b0c..0a30f02e5f 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -59,6 +59,7 @@
#include "qsslkey.h"
#include "qsslconfiguration_p.h"
#include "qocspresponse.h"
+#include "qtlsbackend_p.h"
#ifndef QT_NO_OPENSSL
#include <private/qsslcontext_openssl_p.h>
#else
@@ -67,6 +68,8 @@ class QSslContext;
#include <QtCore/qlist.h>
#include <QtCore/qstringlist.h>
+#include <QtCore/qmutex.h>
+
#include <private/qringbuffer_p.h>
#if defined(Q_OS_MAC)
@@ -81,6 +84,8 @@ class QSslContext;
#endif // !HCRYPTPROV_LEGACY
#endif // Q_OS_WIN
+#include <memory>
+
QT_BEGIN_NAMESPACE
#if defined(Q_OS_MACOS)
@@ -91,7 +96,7 @@ QT_BEGIN_NAMESPACE
#if defined(Q_OS_WIN)
-// Those are needed by both OpenSSL and SChannel back-ends on Windows:
+// Those are needed by both OpenSSL and Schannel back-ends on Windows:
struct QHCertStoreDeleter {
void operator()(HCERTSTORE store)
{
@@ -204,12 +209,9 @@ public:
Q_AUTOTEST_EXPORT static bool rootCertOnDemandLoadingSupported();
- static QList<QString> availableBackends();
- static QString activeBackend();
static bool loadBackend(const QString &backendName);
- static QList<QSsl::SslProtocol> supportedProtocols(const QString &backendName);
- static QList<QSsl::ImplementedClass> implementedClasses(const QString &backendName);
- static QList<QSsl::SupportedFeature> supportedFeatures(const QString &backendName);
+ static void registerAdHocFactory();
+
private:
static bool ensureLibraryLoaded();
static void ensureCiphersAndCertsLoaded();
@@ -228,6 +230,10 @@ protected:
bool handshakeInterrupted = false;
bool fetchAuthorityInformation = false;
QSslCertificate caToFetch;
+
+ static inline QMutex backendMutex;
+ static inline QString activeBackendName;
+ static inline std::unique_ptr<QTlsBackend> tlsBackend;
};
#if QT_CONFIG(securetransport) || QT_CONFIG(schannel)
diff --git a/src/network/ssl/qsslsocket_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp
index f0e9e9c9d2..7ac032bd52 100644
--- a/src/network/ssl/qsslsocket_schannel.cpp
+++ b/src/network/ssl/qsslsocket_schannel.cpp
@@ -157,6 +157,75 @@
QT_BEGIN_NAMESPACE
namespace {
+
+class SchannelBackend : public QTlsBackend
+{
+private:
+ QString backendName() const override
+ {
+ return QTlsBackendFactory::builtinBackendNames[QTlsBackendFactory::nameIndexSchannel];
+ }
+};
+
+class SchannelBackendBackendFactory : public QTlsBackendFactory
+{
+private:
+ QString backendName() const override
+ {
+ return QTlsBackendFactory::builtinBackendNames[QTlsBackendFactory::nameIndexSchannel];
+ }
+ QTlsBackend *create() const override
+ {
+ return new SchannelBackend;
+ }
+
+ QList<QSsl::SslProtocol> supportedProtocols() const override
+ {
+ QList<QSsl::SslProtocol> protocols;
+
+ protocols << QSsl::AnyProtocol;
+ protocols << QSsl::SecureProtocols;
+ protocols << QSsl::TlsV1_0;
+ protocols << QSsl::TlsV1_0OrLater;
+ protocols << QSsl::TlsV1_1;
+ protocols << QSsl::TlsV1_1OrLater;
+ protocols << QSsl::TlsV1_2;
+ protocols << QSsl::TlsV1_2OrLater;
+
+ bool supportsTls13();
+ if (supportsTls13()) {
+ protocols << QSsl::TlsV1_3;
+ protocols << QSsl::TlsV1_3OrLater;
+ }
+
+ return protocols;
+ }
+
+ QList<QSsl::SupportedFeature> supportedFeatures() const override
+ {
+ QList<QSsl::SupportedFeature> features;
+
+ features << QSsl::SupportedFeature::ClientSideAlpn;
+ features << QSsl::SupportedFeature::ServerSideAlpn;
+
+ return features;
+ }
+
+ QList<QSsl::ImplementedClass> implementedClasses() const override
+ {
+ QList<QSsl::ImplementedClass> classes;
+
+ classes << QSsl::ImplementedClass::Socket;
+ classes << QSsl::ImplementedClass::Certificate;
+ classes << QSsl::ImplementedClass::Key;
+
+ return classes;
+ }
+};
+
+Q_GLOBAL_STATIC(SchannelBackendFactory, factory)
+
+
SecBuffer createSecBuffer(void *ptr, unsigned long length, unsigned long bufferType)
{
return SecBuffer{ length, bufferType, ptr };
@@ -2144,93 +2213,12 @@ bool QSslSocketBackendPrivate::rootCertOnDemandLoadingAllowed()
return allowRootCertOnDemandLoading && s_loadRootCertsOnDemand;
}
-QList<QString> QSslSocketPrivate::availableBackends()
-{
- return {QStringLiteral("schannel")};
-}
-
-QString QSslSocketPrivate::activeBackend()
+void QSslSocketPrivate::registerAdHocFactory()
{
- return availableBackends().first();
-}
-
-bool QSslSocketPrivate::loadBackend(const QString &backendName)
-{
- if (backendName.size() && !availableBackends().contains(backendName)) {
- qCWarning(lcSsl) << "A TLS backend with name" << backendName << "is not available";
- return false;
- }
-
- static bool loaded = false;
- static QBasicMutex mutex;
- const QMutexLocker locker(&mutex);
- if (loaded) {
- qCWarning(lcSsl) << "You have already loaded the backend named:" << activeBackend();
- qCWarning(lcSsl) << "Cannot load:" << backendName;
- return true;
- }
- // This code to be placed in qsslsocket.cpp and there
- // the actual plugin to be loaded (so the result can be
- // false if we, for example, failed to resolve OpenSSL
- // symbols).
- return loaded = true;
-}
-
-QList<QSsl::SslProtocol> QSslSocketPrivate::supportedProtocols(const QString &backendName)
-{
- QList<QSsl::SslProtocol> protocols;
- if (backendName.size() && backendName != activeBackend()) {
- qCWarning(lcSsl) << "Unexpected backend name" << backendName
- << "no information about protocols supported can be found";
- return protocols;
- }
-
- protocols << QSsl::AnyProtocol;
- protocols << QSsl::SecureProtocols;
- protocols << QSsl::TlsV1_0;
- protocols << QSsl::TlsV1_0OrLater;
- protocols << QSsl::TlsV1_1;
- protocols << QSsl::TlsV1_1OrLater;
- protocols << QSsl::TlsV1_2;
- protocols << QSsl::TlsV1_2OrLater;
-
- if (supportsTls13()) {
- protocols << QSsl::TlsV1_3;
- protocols << QSsl::TlsV1_3OrLater;
- }
-
- return protocols;
-}
-
-QList<QSsl::ImplementedClass> QSslSocketPrivate::implementedClasses(const QString &backendName)
-{
- QList<QSsl::ImplementedClass> classes;
- if (backendName.size() && backendName != activeBackend()) {
- qCWarning(lcSsl) << "Unexpected backend name" << backendName
- << "no information about classes implemented can be found";
- return classes;
- }
-
- classes << QSsl::ImplementedClass::Key;
- classes << QSsl::ImplementedClass::Certificate;
- classes << QSsl::ImplementedClass::Socket;
-
- return classes;
-}
-
-QList<QSsl::SupportedFeature> QSslSocketPrivate::supportedFeatures(const QString &backendName)
-{
- QList<QSsl::SupportedFeature> features;
- if (backendName.size() && backendName != activeBackend()) {
- qCWarning(lcSsl) << "Unexpected backend name" << backendName
- << "no information about classes implemented can be found";
- return features;
- }
-
- features << QSsl::SupportedFeature::ClientSideAlpn;
- features << QSsl::SupportedFeature::ServerSideAlpn;
-
- return features;
+ // TLSTODO: this is a temporary solution, waiting for
+ // backends to move to ... plugins.
+ if (!factory())
+ qCWarning(lcSsl, "Failed to create backend factory");
}
QT_END_NAMESPACE
diff --git a/src/network/ssl/qtlsbackend.cpp b/src/network/ssl/qtlsbackend.cpp
new file mode 100644
index 0000000000..4183c1e2f1
--- /dev/null
+++ b/src/network/ssl/qtlsbackend.cpp
@@ -0,0 +1,299 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtlsbackend_p.h"
+#include "qsslsocket_p.h"
+#include "qssl_p.h"
+
+#include <QtCore/private/qfactoryloader_p.h>
+
+#include <QtCore/qmutex.h>
+
+#include <algorithm>
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QTlsBackendFactory_iid, QStringLiteral("/tlsbackends")))
+
+const QString QTlsBackendFactory::builtinBackendNames[] = {
+ QStringLiteral("schannel"),
+ QStringLiteral("securetransport"),
+ QStringLiteral("openssl")
+};
+
+
+QTlsBackend::QTlsBackend() = default;
+QTlsBackend::~QTlsBackend() = default;
+
+const QString dummyName = QStringLiteral("dummyTLS");
+
+QString QTlsBackend::backendName() const
+{
+ return dummyName;
+}
+
+QSsl::TlsKey *QTlsBackend::createKey() const
+{
+ qCWarning(lcSsl, "Dummy TLS backend, cannot generate a key");
+ return nullptr;
+}
+
+QSsl::X509Certificate *QTlsBackend::createCertificate() const
+{
+ qCWarning(lcSsl, "Dummy TLS backend, cannot create a certificate");
+ return nullptr;
+}
+
+QSsl::TlsCryptograph *QTlsBackend::createTlsCryptograph() const
+{
+ qCWarning(lcSsl, "Dummy TLS backend, cannot create TLS session");
+ return nullptr;
+}
+
+QSsl::DtlsCryptograph *QTlsBackend::createDtlsCryptograph() const
+{
+ qCWarning(lcSsl, "Dummy TLS backend, cannot create DTLS session");
+ return nullptr;
+}
+
+QSsl::DtlsCookieVerifier *QTlsBackend::createDtlsCookieVerifier() const
+{
+ qCWarning(lcSsl, "Dummy TLS backend, cannot create DTLS cookie generator/verifier");
+ return nullptr;
+}
+
+QSsl::X509ChainVerifyPtr QTlsBackend::X509Verifier() const
+{
+ qCWarning(lcSsl, "Dummy TLS backend, cannot verify X509 chain");
+ return nullptr;
+}
+
+QSsl::X509PemReaderPtr QTlsBackend::X509PemReader() const
+{
+ qCWarning(lcSsl, "Dummy TLS backend, cannot read PEM format");
+ return nullptr;
+}
+
+QSsl::X509DerReaderPtr QTlsBackend::X509DerReader() const
+{
+ qCWarning(lcSsl, "Dummy TLS backend, don't know how to read DER");
+ return nullptr;
+}
+
+QSsl::X509Pkcs12ReaderPtr QTlsBackend::X509Pkcs12Reader() const
+{
+ qCWarning(lcSsl, "Dummy TLS backend, cannot read PKCS12");
+ return nullptr;
+}
+
+namespace {
+
+class BackEndFactoryCollection
+{
+public:
+ void addFactory(QTlsBackendFactory *newFactory)
+ {
+ Q_ASSERT(newFactory);
+ Q_ASSERT(std::find(backendFactories.begin(), backendFactories.end(), newFactory) == backendFactories.end());
+ const QMutexLocker locker(&collectionMutex);
+ backendFactories.push_back(newFactory);
+ }
+
+ void removeFactory(QTlsBackendFactory *factory)
+ {
+ Q_ASSERT(factory);
+ const QMutexLocker locker(&collectionMutex);
+ const auto it = std::find(backendFactories.begin(), backendFactories.end(), factory);
+ Q_ASSERT(it != backendFactories.end());
+ backendFactories.erase(it);
+ }
+
+ bool tryPopulateCollection()
+ {
+ if (!loader())
+ return false;
+
+ static QBasicMutex mutex;
+ const QMutexLocker locker(&mutex);
+ if (loaded)
+ return true;
+
+#if QT_CONFIG(library)
+ loader->update();
+#endif
+ int index = 0;
+ while (loader->instance(index))
+ ++index;
+
+ // TLSTODO: obviously, this one should go away:
+ QSslSocketPrivate::registerAdHocFactory();
+
+ return loaded = true;
+ }
+
+ QList<QString> backendNames()
+ {
+ QList<QString> names;
+ if (!tryPopulateCollection())
+ return names;
+
+ const QMutexLocker locker(&collectionMutex);
+ if (!backendFactories.size())
+ return names;
+
+ names.reserve(backendFactories.size());
+ for (const auto *factory : backendFactories)
+ names.append(factory->backendName());
+
+ return names;
+ }
+
+ QTlsBackendFactory *factory(const QString &name)
+ {
+ if (!tryPopulateCollection())
+ return nullptr;
+
+ const QMutexLocker locker(&collectionMutex);
+ const auto it = std::find_if(backendFactories.begin(), backendFactories.end(),
+ [&name](const auto *fct) {return fct->backendName() == name;});
+
+ return it == backendFactories.end() ? nullptr : *it;
+ }
+
+private:
+ std::vector<QTlsBackendFactory *> backendFactories;
+ QMutex collectionMutex;
+ bool loaded = false;
+};
+
+Q_GLOBAL_STATIC(BackEndFactoryCollection, factories);
+
+} // unnamed namespace
+
+QTlsBackendFactory::QTlsBackendFactory()
+{
+ if (factories())
+ factories->addFactory(this);
+}
+
+QTlsBackendFactory::~QTlsBackendFactory()
+{
+ if (factories())
+ factories->removeFactory(this);
+}
+
+QString QTlsBackendFactory::backendName() const
+{
+ return dummyName;
+}
+
+QList<QString> QTlsBackendFactory::availableBackendNames()
+{
+ if (!factories())
+ return {};
+
+ return factories->backendNames();
+}
+
+QString QTlsBackendFactory::defaultBackendName()
+{
+ // We prefer native as default:
+ const auto names = availableBackendNames();
+ auto name = builtinBackendNames[nameIndexSchannel];
+ if (names.contains(name))
+ return name;
+ name = builtinBackendNames[nameIndexSecureTransport];
+ if (names.contains(name))
+ return name;
+ name = builtinBackendNames[nameIndexOpenSSL];
+ if (names.contains(name))
+ return name;
+
+ return {};
+}
+
+QTlsBackend *QTlsBackendFactory::create(const QString &backendName)
+{
+ if (!factories())
+ return {};
+
+ if (const auto *fct = factories->factory(backendName))
+ return fct->create();
+
+ qCWarning(lcSsl) << "Cannot create unknown backend named" << backendName;
+ return nullptr;
+}
+
+QList<QSsl::SslProtocol> QTlsBackendFactory::supportedProtocols(const QString &backendName)
+{
+ if (!factories())
+ return {};
+
+ if (const auto *fct = factories->factory(backendName))
+ return fct->supportedProtocols();
+
+ return {};
+}
+
+QList<QSsl::SupportedFeature> QTlsBackendFactory::supportedFeatures(const QString &backendName)
+{
+ if (!factories())
+ return {};
+
+ if (const auto *fct = factories->factory(backendName))
+ return fct->supportedFeatures();
+
+ return {};
+}
+
+QList<QSsl::ImplementedClass> QTlsBackendFactory::implementedClasses(const QString &backendName)
+{
+ if (!factories())
+ return {};
+
+ if (const auto *fct = factories->factory(backendName))
+ return fct->implementedClasses();
+
+ return {};
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qtlsbackend_p.h b/src/network/ssl/qtlsbackend_p.h
new file mode 100644
index 0000000000..9c4f2d3eb8
--- /dev/null
+++ b/src/network/ssl/qtlsbackend_p.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTLSBACKEND_P_H
+#define QTLSBACKEND_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 <private/qtnetworkglobal_p.h>
+
+#include <QtNetwork/qsslcertificate.h>
+#include <QtNetwork/qsslerror.h>
+#include <QtNetwork/qsslkey.h>
+#include <QtNetwork/qssl.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmap.h>
+
+#include <vector>
+#include <memory>
+
+QT_REQUIRE_CONFIG(ssl);
+
+QT_BEGIN_NAMESPACE
+
+class QByteArray;
+class QIODevice;
+
+namespace QSsl {
+
+// Encapsulates key's data or backend-specific
+// data-structure, like RSA/DSA/DH structs in OpenSSL.
+class TlsKey;
+
+// Abstraction above OpenSSL's X509, or our generic
+// 'derData'-based code.
+class X509Certificate;
+
+// X509-related auxiliary functions, previously static
+// member-functions in different classes.
+using X509ChainVerifyPtr = QList<QSslError> (*)(const QList<QSslCertificate> &chain,
+ const QString &hostName);
+using X509PemReaderPtr = QList<QSslCertificate> (*)(const QByteArray &pem, int count);
+using X509DerReaderPtr = X509PemReaderPtr;
+using X509Pkcs12ReaderPtr = bool (*)(QIODevice *device, QSslKey *key, QSslCertificate *cert,
+ QList<QSslCertificate> *caCertificates,
+ const QByteArray &passPhrase);
+
+// TLS over TCP. Handshake, encryption/decryption.
+class TlsCryptograph;
+
+// TLS over UDP. Handshake, encryption/decryption.
+class DtlsCryptograph;
+
+// DTLS cookie: generation and verification.
+class DtlsCookieVerifier;
+
+} // namespace QSsl
+
+// Factory, creating back-end specific implementations of
+// different entities QSslSocket is using.
+// TLSTODO: consider merging with ... it's own factory
+// below, no real benefit in having this split.
+class Q_NETWORK_EXPORT QTlsBackend : public QObject
+{
+ Q_OBJECT
+public:
+ QTlsBackend();
+ ~QTlsBackend() override;
+
+ virtual QString backendName() const;
+
+ // X509 and keys:
+ virtual QSsl::TlsKey *createKey() const;
+ virtual QSsl::X509Certificate *createCertificate() const;
+
+ // TLS and DTLS:
+ virtual QSsl::TlsCryptograph *createTlsCryptograph() const;
+ virtual QSsl::DtlsCryptograph *createDtlsCryptograph() const;
+ virtual QSsl::DtlsCookieVerifier *createDtlsCookieVerifier() const;
+
+ // X509 machinery:
+ virtual QSsl::X509ChainVerifyPtr X509Verifier() const;
+ virtual QSsl::X509PemReaderPtr X509PemReader() const;
+ virtual QSsl::X509DerReaderPtr X509DerReader() const;
+ virtual QSsl::X509Pkcs12ReaderPtr X509Pkcs12Reader() const;
+
+ Q_DISABLE_COPY_MOVE(QTlsBackend)
+};
+
+// Factory for a backend.
+class Q_NETWORK_EXPORT QTlsBackendFactory : public QObject
+{
+ Q_OBJECT
+public:
+ QTlsBackendFactory();
+ ~QTlsBackendFactory() override;
+
+ virtual QString backendName() const = 0;
+ virtual QTlsBackend *create() const = 0;
+ virtual QList<QSsl::SslProtocol> supportedProtocols() const = 0;
+ virtual QList<QSsl::SupportedFeature> supportedFeatures() const = 0;
+ virtual QList<QSsl::ImplementedClass> implementedClasses() const = 0;
+
+ static QList<QString> availableBackendNames();
+ static QString defaultBackendName();
+ static QTlsBackend *create(const QString &backendName);
+
+ static QList<QSsl::SslProtocol> supportedProtocols(const QString &backendName);
+ static QList<QSsl::SupportedFeature> supportedFeatures(const QString &backendName);
+ static QList<QSsl::ImplementedClass> implementedClasses(const QString &backendName);
+
+ // Built-in, this is what Qt provides out of the box (depending on OS):
+ static constexpr const int nameIndexSchannel = 0;
+ static constexpr const int nameIndexSecureTransport = 1;
+ static constexpr const int nameIndexOpenSSL = 2;
+
+ static const QString builtinBackendNames[];
+
+ Q_DISABLE_COPY_MOVE(QTlsBackendFactory)
+};
+
+#define QTlsBackendFactory_iid "org.qt-project.Qt.QTlsBackendFactory"
+Q_DECLARE_INTERFACE(QTlsBackendFactory, QTlsBackendFactory_iid);
+
+
+QT_END_NAMESPACE
+
+#endif // QTLSBACKEND_P_H
diff --git a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
index df35d93a63..bb77668b81 100644
--- a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
+++ b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
@@ -56,6 +56,8 @@
#ifndef QT_NO_SSL
+#include "private/qtlsbackend_p.h"
+
#ifndef QT_NO_OPENSSL
#include "private/qsslsocket_openssl_p.h"
#include "private/qsslsocket_openssl_symbols_p.h"
@@ -163,6 +165,7 @@ public slots:
#ifndef QT_NO_SSL
private slots:
+ void backends();
void constructing();
void configNoOnDemandLoad();
void simpleConnect();
@@ -437,9 +440,13 @@ void tst_QSslSocket::initTestCase()
#endif // QT_NO_SSL
// Since a backend can be loaded only once by an application (this test in our case),
- // we do backend testing here:
+ // we do backend testing here.
+
+ // Before we tried to load anything, the active is the same thing as the default one:
+ QCOMPARE(QSslSocket::activeBackend(), QTlsBackendFactory::defaultBackendName());
+
const QString nonExistingBackend = QStringLiteral("TheQtTLS");
- QCOMPARE(QSslSocket::loadBackend(nonExistingBackend), false);
+ QCOMPARE(QSslSocket::setActiveBackend(nonExistingBackend), false);
QCOMPARE(QSslSocket::supportedProtocols(nonExistingBackend).size(), 0);
QCOMPARE(QSslSocket::supportedFeatures(nonExistingBackend), QList<QSsl::SupportedFeature>());
QCOMPARE(QSslSocket::implementedClasses(nonExistingBackend), QList<QSsl::ImplementedClass>());
@@ -450,9 +457,9 @@ void tst_QSslSocket::initTestCase()
const auto supportedFt = QSsl::SupportedFeature::ClientSideAlpn;
QVERIFY(QSslSocket::availableBackends().contains(backendName));
- QCOMPARE(QSslSocket::loadBackend(backendName), true);
+ QCOMPARE(QSslSocket::setActiveBackend(backendName), true);
QCOMPARE(QSslSocket::activeBackend(), backendName);
- QCOMPARE(QSslSocket::loadBackend(backendName), true); // Already loaded, but not a fail.
+ QCOMPARE(QSslSocket::setActiveBackend(backendName), true); // We can do it again.
QCOMPARE(QSslSocket::activeBackend(), backendName);
const auto protocols = QSslSocket::supportedProtocols();
@@ -557,6 +564,131 @@ void tst_QSslSocket::proxyAuthenticationRequired(const QNetworkProxy &, QAuthent
#ifndef QT_NO_SSL
+struct MockTlsBackend : QTlsBackend
+{
+ MockTlsBackend(const QString &n) : name(n) {}
+ QString backendName() const override
+ {
+ return name;
+ }
+ QString name;
+};
+
+struct MockTlsFactory : QTlsBackendFactory
+{
+ MockTlsFactory(const QString &mockName) : name(mockName)
+ {
+ }
+ QString backendName() const override
+ {
+ return name;
+ }
+
+ QList<QSsl::SupportedFeature> supportedFeatures() const override
+ {
+ return features;
+ }
+ QList<QSsl::SslProtocol> supportedProtocols() const override
+ {
+ return protocols;
+ }
+ QList<QSsl::ImplementedClass> implementedClasses() const override
+ {
+ return classes;
+ }
+ QTlsBackend *create() const override
+ {
+ auto tls = new MockTlsBackend(name);
+ return tls;
+ }
+ QString name;
+ QList<QSsl::ImplementedClass> classes;
+ QList<QSsl::SupportedFeature> features;
+ QList<QSsl::SslProtocol> protocols;
+};
+
+void tst_QSslSocket::backends()
+{
+ QFETCH_GLOBAL(const bool, setProxy);
+ if (setProxy)
+ QSKIP("Proxy is not interesting for backend test");
+
+ // We are here, protected by !QT_NO_SSL. Some backend must be pre-existing.
+ // Let's test the 'real' backend:
+ auto backendNames = QTlsBackendFactory::availableBackendNames();
+ const auto sizeBefore = backendNames.size();
+ QVERIFY(sizeBefore > 0);
+
+ const auto builtinBackend = backendNames.first();
+ const auto builtinProtocols = QSslSocket::supportedProtocols(builtinBackend);
+ QVERIFY(builtinProtocols.contains(QSsl::SecureProtocols));
+ // Socket and ALPN are supported by all our backends:
+ const auto builtinClasses = QSslSocket::implementedClasses(builtinBackend);
+ QVERIFY(builtinClasses.contains(QSsl::ImplementedClass::Socket));
+ const auto builtinFeatures = QSslSocket::supportedFeatures(builtinBackend);
+ QVERIFY(builtinFeatures.contains(QSsl::SupportedFeature::ClientSideAlpn));
+
+ {
+ // Verify that non-dummy backend can be created (and delete it):
+ const std::unique_ptr<QTlsBackend> systemBackend(QTlsBackendFactory::create(backendNames.first()));
+ QVERIFY(systemBackend.get());
+ }
+
+ const auto protocols = QList<QSsl::SslProtocol>{QSsl::SecureProtocols};
+ const auto classes = QList<QSsl::ImplementedClass>{QSsl::ImplementedClass::Socket};
+ const auto features = QList<QSsl::SupportedFeature>{QSsl::SupportedFeature::CertificateVerification};
+
+ const QString nameA = QStringLiteral("backend A");
+ const QString nameB = QStringLiteral("backend B");
+ const QString nonExisting = QStringLiteral("non-existing backend");
+
+ QVERIFY(!backendNames.contains(nameA));
+ QVERIFY(!backendNames.contains(nameB));
+ QVERIFY(!backendNames.contains(nonExisting));
+ {
+ MockTlsFactory factoryA(nameA);
+ backendNames = QTlsBackendFactory::availableBackendNames();
+ QVERIFY(backendNames.contains(nameA));
+ QVERIFY(!backendNames.contains(nameB));
+ QVERIFY(!backendNames.contains(nonExisting));
+
+ QCOMPARE(factoryA.supportedFeatures().size(), 0);
+ QCOMPARE(factoryA.supportedProtocols().size(), 0);
+ QCOMPARE(factoryA.implementedClasses().size(), 0);
+
+ factoryA.protocols = protocols;
+ factoryA.classes = classes;
+ factoryA.features = features;
+
+ // It's an overrider in some re-implemented factory:
+ QCOMPARE(factoryA.supportedProtocols(), protocols);
+ QCOMPARE(factoryA.supportedFeatures(), features);
+ QCOMPARE(factoryA.implementedClasses(), classes);
+
+ // That's a helper function (static member function):
+ QCOMPARE(QTlsBackendFactory::supportedProtocols(nameA), protocols);
+ QCOMPARE(QTlsBackendFactory::supportedFeatures(nameA), features);
+ QCOMPARE(QTlsBackendFactory::implementedClasses(nameA), classes);
+
+ MockTlsFactory factoryB(nameB);
+ QVERIFY(QTlsBackendFactory::availableBackendNames().contains(nameA));
+ QVERIFY(QTlsBackendFactory::availableBackendNames().contains(nameB));
+ QVERIFY(!QTlsBackendFactory::availableBackendNames().contains(nonExisting));
+
+ const std::unique_ptr<QTlsBackend> backendA(QTlsBackendFactory::create(nameA));
+ QVERIFY(backendA.get());
+ QCOMPARE(backendA->backendName(), nameA);
+
+ const std::unique_ptr<QTlsBackend> nullBackend(QTlsBackendFactory::create(nonExisting));
+ QCOMPARE(nullBackend.get(), nullptr);
+ }
+ backendNames = QTlsBackendFactory::availableBackendNames();
+ QCOMPARE(backendNames.size(), sizeBefore);
+ // Check we cleaned up our factories:
+ QVERIFY(!backendNames.contains(nameA));
+ QVERIFY(!backendNames.contains(nameB));
+}
+
void tst_QSslSocket::constructing()
{
const char readNotOpenMessage[] = "QIODevice::read (QSslSocket): device not open";