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>
This commit is contained in:
parent
946facb1ae
commit
7cf8e5ada9
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -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 = {});
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
void QSslSocketPrivate::registerAdHocFactory()
|
||||
{
|
||||
return {QStringLiteral("openssl")};
|
||||
}
|
||||
|
||||
QString QSslSocketPrivate::activeBackend()
|
||||
{
|
||||
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
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
void QSslSocketPrivate::registerAdHocFactory()
|
||||
{
|
||||
return {QStringLiteral("schannel")};
|
||||
}
|
||||
|
||||
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();
|
||||
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
|
||||
|
299
src/network/ssl/qtlsbackend.cpp
Normal file
299
src/network/ssl/qtlsbackend.cpp
Normal file
@ -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
|
177
src/network/ssl/qtlsbackend_p.h
Normal file
177
src/network/ssl/qtlsbackend_p.h
Normal file
@ -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
|
@ -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";
|
||||
|
Loading…
Reference in New Issue
Block a user