Merge "Convert QSslSocket(Backend)Private into plugin"

This commit is contained in:
Timur Pocheptsov 2021-03-04 19:20:18 +01:00 committed by Qt CI Bot
commit fc52acf621
45 changed files with 3374 additions and 2656 deletions

View File

@ -43,10 +43,9 @@ qt_internal_add_module(Network
socket/qudpsocket.cpp socket/qudpsocket.h
ssl/qasn1element.cpp ssl/qasn1element_p.h
ssl/qpassworddigestor.cpp ssl/qpassworddigestor.h
ssl/qssl.cpp ssl/qssl.h ssl/qssl_p.h ssl/qtls_utils_p.h
ssl/qssl.cpp ssl/qssl.h ssl/qssl_p.h
ssl/qsslcertificate.cpp ssl/qsslcertificate.h ssl/qsslcertificate_p.h
ssl/qsslcertificateextension.cpp ssl/qsslcertificateextension.h ssl/qsslcertificateextension_p.h
ssl/qtls_utils_p.h
ssl/qtlsbackend.cpp ssl/qtlsbackend_p.h
ssl/qtlsbackend_cert.cpp ssl/qtlsbackend_cert_p.h
ssl/qx509_base.cpp ssl/qx509_base_p.h
@ -331,7 +330,8 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_ssl
qt_internal_extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_ssl
SOURCES
ssl/qsslsocket_qt.cpp
ssl/qsslsocket_schannel.cpp ssl/qsslsocket_schannel_p.h
ssl/qwincrypt_p.h
ssl/qtls_schannel.cpp ssl/qtls_schannel_p.h
ssl/qtlsbackend_schannel_p.h
ssl/qtlskey_generic.cpp ssl/qtlskey_generic_p.h
ssl/qtlskey_schannel.cpp ssl/qtlskey_schannel_p.h
@ -345,7 +345,7 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_s
qt_internal_extend_target(Network CONDITION QT_FEATURE_securetransport AND QT_FEATURE_ssl
SOURCES
ssl/qsslsocket_mac.cpp ssl/qsslsocket_mac_p.h
ssl/qtls_st.cpp ssl/qtls_st_p.h
ssl/qsslsocket_mac_shared.cpp
ssl/qsslsocket_qt.cpp
ssl/qtlskey_generic.cpp ssl/qtlskey_generic_p.h
@ -364,8 +364,9 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_openssl AND QT_FEATURE_ss
SOURCES
ssl/qsslcontext_openssl.cpp ssl/qsslcontext_openssl_p.h
ssl/qssldiffiehellmanparameters_openssl.cpp
ssl/qsslsocket_openssl.cpp ssl/qsslsocket_openssl_p.h
ssl/qopenssl.cpp ssl/qopenssl_p.h
ssl/qsslsocket_openssl_symbols.cpp ssl/qsslsocket_openssl_symbols_p.h
ssl/qtls_openssl.cpp ssl/qtls_openssl_p.h
ssl/qtlskey_openssl.cpp ssl/qtlskey_openssl_p.h
ssl/qtlsbackend_openssl.cpp ssl/qtlsbackend_openssl_p.h
ssl/qx509_openssl.cpp ssl/qx509_openssl_p.h

View File

@ -55,4 +55,6 @@
#include <QtCore/private/qglobal_p.h>
#include <QtNetwork/private/qtnetwork-config_p.h>
#define Q_NETWORK_PRIVATE_EXPORT Q_NETWORK_EXPORT
#endif // QTNETWORKGLOBAL_P_H

View File

@ -44,7 +44,6 @@
#include "qsslpresharedkeyauthenticator_p.h"
#include "qsslsocket_openssl_symbols_p.h"
#include "qsslsocket_openssl_p.h"
#include "qsslcertificate_p.h"
#include "qdtls_openssl_p.h"
#include "qx509_openssl_p.h"
@ -198,7 +197,7 @@ extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst,
return 0;
}
void *generic = q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData);
void *generic = q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData);
if (!generic) {
qCWarning(lcSsl, "SSL_get_ex_data returned nullptr, cannot generate cookie");
return 0;
@ -252,7 +251,7 @@ extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx)
return 0;
}
void *generic = q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData);
void *generic = q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData);
if (!generic) {
qCWarning(lcSsl, "SSL_get_ex_data returned nullptr, handshake failure");
return 0;
@ -273,7 +272,7 @@ extern "C" unsigned q_PSK_client_callback(SSL *ssl, const char *hint, char *iden
unsigned max_psk_len)
{
auto *dtls = static_cast<dtlsopenssl::DtlsState *>(q_SSL_get_ex_data(ssl,
QSslSocketBackendPrivate::s_indexForSSLExtraData));
QTlsBackendOpenSSL::s_indexForSSLExtraData));
if (!dtls)
return 0;
@ -285,7 +284,7 @@ extern "C" unsigned q_PSK_server_callback(SSL *ssl, const char *identity, unsign
unsigned max_psk_len)
{
auto *dtls = static_cast<dtlsopenssl::DtlsState *>(q_SSL_get_ex_data(ssl,
QSslSocketBackendPrivate::s_indexForSSLExtraData));
QTlsBackendOpenSSL::s_indexForSSLExtraData));
if (!dtls)
return 0;
@ -693,7 +692,7 @@ bool DtlsState::initCtxAndConnection(QDtlsBasePrivate *dtlsBase)
}
const int set = q_SSL_set_ex_data(newConnection.data(),
QSslSocketBackendPrivate::s_indexForSSLExtraData,
QTlsBackendOpenSSL::s_indexForSSLExtraData,
this);
if (set != 1 && configurationCopy->peerVerifyMode != QSslSocket::VerifyNone) {
@ -815,7 +814,7 @@ bool QDtlsClientVerifierOpenSSL::verifyClient(QUdpSocket *socket, const QByteArr
const int ret = q_DTLSv1_listen(dtls.tlsConnection.data(), peer.data());
if (ret < 0) {
// Since 1.1 - it's a fatal error (not so in 1.0.2 for non-blocking socket)
setDtlsError(QDtlsError::TlsFatalError, QSslSocketBackendPrivate::getErrorsFromOpenSsl());
setDtlsError(QDtlsError::TlsFatalError, QTlsBackendOpenSSL::getErrorsFromOpenSsl());
return false;
}
@ -1031,7 +1030,7 @@ bool QDtlsPrivateOpenSSL::continueHandshake(QUdpSocket *socket, const QByteArray
default:
storePeerCertificates();
setDtlsError(QDtlsError::TlsFatalError,
QSslSocketBackendPrivate::msgErrorsDuringHandshake());
QTlsBackendOpenSSL::msgErrorsDuringHandshake());
dtls.reset();
handshakeState = QDtls::HandshakeNotStarted;
return false;
@ -1192,7 +1191,7 @@ qint64 QDtlsPrivateOpenSSL::writeDatagramEncrypted(QUdpSocket *socket,
// DTLSTODO: we don't know yet what to do. Tests needed - probably,
// some errors can be just ignored (it's UDP, not TCP after all).
// Unlike QSslSocket we do not abort though.
QString description(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
QString description(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
if (socket->error() != QAbstractSocket::UnknownSocketError && description.isEmpty()) {
setDtlsError(QDtlsError::UnderlyingSocketError, socket->errorString());
} else {
@ -1258,7 +1257,7 @@ QByteArray QDtlsPrivateOpenSSL::decryptDatagram(QUdpSocket *socket, const QByteA
default:
setDtlsError(QDtlsError::TlsNonFatalError,
QDtls::tr("Error while reading: %1")
.arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl()));
.arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
return dgram;
}
}
@ -1276,15 +1275,10 @@ unsigned QDtlsPrivateOpenSSL::pskClientCallback(const char *hint, char *identity
if (hint) {
identityHint.clear();
identityHint.append(hint);
// From the original code in QSslSocket:
// "it's NULL terminated, but do not include the NULL" == this fromRawData(ptr/size).
authenticator.d->identityHint = QByteArray::fromRawData(identityHint.constData(),
int(std::strlen(hint)));
}
authenticator.d->maximumIdentityLength = int(max_identity_len) - 1; // needs to be NULL terminated
authenticator.d->maximumPreSharedKeyLength = int(max_psk_len);
QTlsBackend::setupClientPskAuth(&authenticator, hint ? identityHint.constData() : nullptr,
hint ? std::strlen(hint) : 0, max_identity_len, max_psk_len);
pskAuthenticator.swap(authenticator);
}
@ -1314,11 +1308,8 @@ unsigned QDtlsPrivateOpenSSL::pskServerCallback(const char *identity, unsigned c
{
QSslPreSharedKeyAuthenticator authenticator;
// Fill in some read-only fields (for the user)
authenticator.d->identityHint = dtlsConfiguration.preSharedKeyIdentityHint;
authenticator.d->identity = identity;
authenticator.d->maximumIdentityLength = 0; // user cannot set an identity
authenticator.d->maximumPreSharedKeyLength = int(max_psk_len);
QTlsBackend::setupServerPskAuth(&authenticator, identity, dtlsConfiguration.preSharedKeyIdentityHint,
max_psk_len);
pskAuthenticator.swap(authenticator);
}
@ -1367,7 +1358,7 @@ bool QDtlsPrivateOpenSSL::verifyPeer()
name = dtls.udpSocket->peerName();
}
if (!QSslSocketPrivate::isMatchingHostname(dtlsConfiguration.peerCertificate, name))
if (!QTlsPrivate::TlsCryptograph::isMatchingHostname(dtlsConfiguration.peerCertificate, name))
errors << QSslError(QSslError::HostNameMismatch, dtlsConfiguration.peerCertificate);
}
@ -1418,9 +1409,10 @@ void QDtlsPrivateOpenSSL::fetchNegotiatedParameters()
{
Q_ASSERT(dtls.tlsConnection.data());
const SSL_CIPHER *cipher = q_SSL_get_current_cipher(dtls.tlsConnection.data());
sessionCipher = cipher ? QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher)
: QSslCipher();
if (const SSL_CIPHER *cipher = q_SSL_get_current_cipher(dtls.tlsConnection.data()))
sessionCipher = QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(cipher);
else
sessionCipher = {};
// Note: cipher's protocol version will be reported as either TLS 1.0 or
// TLS 1.2, that's how it's set by OpenSSL (and that's what they are?).

View File

@ -47,11 +47,12 @@
#include <openssl/ossl_typ.h>
#include "qtlsbackend_openssl_p.h"
#include "qtls_openssl_p.h"
#include "qdtls_base_p.h"
#include "qdtls_p.h"
#include <private/qsslcontext_openssl_p.h>
#include <private/qsslsocket_openssl_p.h>
#include <private/qopenssl_p.h>
#include <QtNetwork/qsslpresharedkeyauthenticator.h>
#include <QtNetwork/qhostaddress.h>
@ -185,7 +186,6 @@ private:
QByteArray decryptDatagram(QUdpSocket *socket, const QByteArray &tlsdgram) override;
public:
unsigned pskClientCallback(const char *hint, char *identity, unsigned max_identity_len,
unsigned char *psk, unsigned max_psk_len);
unsigned pskServerCallback(const char *identity, unsigned char *psk,

View File

@ -72,6 +72,10 @@ enum class QOcspRevocationReason
RemoveFromCRL
};
namespace QTlsPrivate {
class TlsCryptographOpenSSL;
}
class QOcspResponse;
Q_NETWORK_EXPORT size_t qHash(const QOcspResponse &response, size_t seed = 0) noexcept;
@ -99,7 +103,7 @@ public:
private:
bool isEqual(const QOcspResponse &other) const;
friend class QSslSocketBackendPrivate;
friend class QTlsPrivate::TlsCryptographOpenSSL;
friend bool operator==(const QOcspResponse &lhs, const QOcspResponse &rhs)
{ return lhs.isEqual(rhs); }
friend bool operator!=(const QOcspResponse &lhs, const QOcspResponse &rhs)

View File

@ -0,0 +1,71 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2014 Governikus GmbH & Co. KG
** 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$
**
****************************************************************************/
/****************************************************************************
**
** In addition, as a special exception, the copyright holders listed above give
** permission to link the code of its release of Qt with the OpenSSL project's
** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
** same license as the original version), and distribute the linked executables.
**
** You must comply with the GNU General Public License version 2 in all
** respects for all of the code used other than the "OpenSSL" code. If you
** modify this file, you may extend this exception to your version of the file,
** but you are not obligated to do so. If you do not wish to do so, delete
** this exception statement from your version of this file.
**
****************************************************************************/
#include "qtlsbackend_openssl_p.h"
#include "qopenssl_p.h"
QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC(QTlsBackendOpenSSL, backendOpenSsl)
void QSslSocketPrivate::registerAdHocFactory()
{
// TLSTODO: this is a temporary solution, waiting for
// backends to move to ... plugins.
if (!backendOpenSsl())
qCWarning(lcSsl, "Failed to create backend factory");
}
QT_END_NAMESPACE

View File

@ -67,10 +67,9 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qsslsocket_p.h"
#include <QtCore/qlist.h>
#include <QtCore/qstring.h>
#include "qsslsocket_p.h"
#include "qsslcipher.h"
#ifdef Q_OS_WIN
#include <qt_windows.h>
@ -82,6 +81,8 @@
#endif
#endif // Q_OS_WIN
// This file is included in several *.cpp files and provides different
// openssl declarations where they are needed.
#include <openssl/asn1.h>
#include <openssl/bio.h>
#include <openssl/bn.h>
@ -100,92 +101,17 @@
#include <openssl/rsa.h>
#include <openssl/crypto.h>
#include <openssl/tls1.h>
#if QT_CONFIG(opensslv11)
#include <openssl/dh.h>
#endif
QT_BEGIN_NAMESPACE
struct QSslErrorEntry {
int code;
int depth;
int code = 0;
int depth = 0;
};
Q_DECLARE_TYPEINFO(QSslErrorEntry, Q_PRIMITIVE_TYPE);
class QSslSocketBackendPrivate : public QSslSocketPrivate
{
Q_DECLARE_PUBLIC(QSslSocket)
public:
QSslSocketBackendPrivate();
virtual ~QSslSocketBackendPrivate();
// SSL context
bool initSslContext();
void destroySslContext();
SSL *ssl;
BIO *readBio;
BIO *writeBio;
SSL_SESSION *session;
QList<QSslErrorEntry> errorList;
static int s_indexForSSLExtraData; // index used in SSL_get_ex_data to get the matching QSslSocketBackendPrivate
enum ExDataOffset {
errorOffsetInExData = 1,
socketOffsetInExData = 2
};
bool inSetAndEmitError = false;
// Platform specific functions
void startClientEncryption() override;
void startServerEncryption() override;
void transmit() override;
bool startHandshake();
void disconnectFromHost() override;
void disconnected() override;
QSslCipher sessionCipher() const override;
QSsl::SslProtocol sessionProtocol() const override;
void continueHandshake() override;
bool checkSslErrors();
void storePeerCertificates();
int handleNewSessionTicket(SSL *context);
unsigned int tlsPskClientCallback(const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len);
unsigned int tlsPskServerCallback(const char *identity, unsigned char *psk, unsigned int max_psk_len);
#ifdef Q_OS_WIN
void fetchCaRootForCert(const QSslCertificate &cert);
void _q_caRootLoaded(QSslCertificate,QSslCertificate) override;
#endif
#if QT_CONFIG(ocsp)
bool checkOcspStatus();
#endif
void alertMessageSent(int encoded);
void alertMessageReceived(int encoded);
int emitErrorFromCallback(X509_STORE_CTX *ctx);
void trySendFatalAlert();
bool pendingFatalAlert = false;
bool errorsReportedFromCallback = false;
// This decription will go to setErrorAndEmit(SslHandshakeError, ocspErrorDescription)
QString ocspErrorDescription;
// These will go to sslErrors()
QList<QSslError> ocspErrors;
QByteArray ocspResponseDer;
Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions);
static QSslCipher QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher);
static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName);
static QList<QSslError> verify(const QList<QSslCertificate> &cas, const QList<QSslCertificate> &certificateChain,
const QString &hostName);
static QString getErrorsFromOpenSsl();
static void logAndClearErrorQueue();
static QString msgErrorsDuringHandshake();
};
QT_END_NAMESPACE
#endif

View File

@ -153,8 +153,6 @@ public:
private:
QExplicitlySharedDataPointer<QSslCertificatePrivate> d;
friend class QSslCertificatePrivate;
friend class QSslSocketBackendPrivate;
friend class QTlsBackend;
friend Q_NETWORK_EXPORT size_t qHash(const QSslCertificate &key, size_t seed) noexcept;

View File

@ -71,10 +71,8 @@ public:
~QSslCertificatePrivate();
QList<QSslCertificateExtension> extensions() const;
static bool isBlacklisted(const QSslCertificate &certificate);
static QByteArray subjectInfoToString(QSslCertificate::SubjectInfo info);
friend class QSslSocketBackendPrivate;
Q_NETWORK_PRIVATE_EXPORT static bool isBlacklisted(const QSslCertificate &certificate);
Q_NETWORK_PRIVATE_EXPORT static QByteArray subjectInfoToString(QSslCertificate::SubjectInfo info);
QAtomicInt ref;
std::unique_ptr<QTlsPrivate::X509Certificate> backend;

View File

@ -85,7 +85,7 @@ public:
private:
// ### Qt 7: make implicitly shared
std::unique_ptr<QSslCipherPrivate> d;
friend class QSslSocketBackendPrivate;
friend class QTlsBackend;
};
Q_DECLARE_SHARED(QSslCipher)

View File

@ -201,7 +201,6 @@ public:
private:
friend class QSslSocket;
friend class QSslConfigurationPrivate;
friend class QSslSocketBackendPrivate;
friend class QSslContext;
friend class QDtlsBasePrivate;
friend class dtlsopenssl::DtlsState;

View File

@ -79,7 +79,7 @@
QT_BEGIN_NAMESPACE
class QSslConfigurationPrivate: public QSharedData
class Q_NETWORK_PRIVATE_EXPORT QSslConfigurationPrivate: public QSharedData
{
public:
QSslConfigurationPrivate()
@ -114,11 +114,11 @@ public:
bool allowRootCertOnDemandLoading;
bool peerSessionShared;
Q_AUTOTEST_EXPORT static bool peerSessionWasShared(const QSslConfiguration &configuration);
static bool peerSessionWasShared(const QSslConfiguration &configuration);
QSsl::SslOptions sslOptions;
Q_AUTOTEST_EXPORT static const QSsl::SslOptions defaultSslOptions;
static const QSsl::SslOptions defaultSslOptions;
QList<QSslEllipticCurve> ellipticCurves;

View File

@ -43,12 +43,13 @@
#include <QtNetwork/qsslsocket.h>
#include <QtNetwork/qssldiffiehellmanparameters.h>
#include "private/qopenssl_p.h"
#include "private/qssl_p.h"
#include "private/qsslsocket_p.h"
#include "private/qsslcontext_openssl_p.h"
#include "private/qsslsocket_openssl_p.h"
#include "private/qsslsocket_openssl_symbols_p.h"
#include "private/qssldiffiehellmanparameters_p.h"
#include "private/qtlsbackend_openssl_p.h"
#include <vector>
@ -61,10 +62,17 @@ Q_NETWORK_EXPORT void qt_ForceTlsSecurityLevel()
*forceSecurityLevel() = true;
}
// defined in qsslsocket_openssl.cpp:
extern int q_X509Callback(int ok, X509_STORE_CTX *ctx);
namespace QTlsPrivate
{
// These callback functions are defined in qtls_openssl.cpp.
extern "C" int q_X509Callback(int ok, X509_STORE_CTX *ctx);
extern "C" int q_X509CallbackDirect(int ok, X509_STORE_CTX *ctx);
extern QString getErrorsFromOpenSsl();
#if QT_CONFIG(ocsp)
extern "C" int qt_OCSP_status_server_callback(SSL *ssl, void *);
#endif // ocsp
} // namespace QTlsPrivate
#if QT_CONFIG(dtls)
// defined in qdtls_openssl.cpp:
@ -82,9 +90,6 @@ extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie,
extern "C" int q_ssl_sess_set_new_cb(SSL *context, SSL_SESSION *session);
#endif // TLS1_3_VERSION
// Defined in qsslsocket.cpp
QList<QSslCipher> q_getDefaultDtlsCiphers();
static inline QString msgErrorSettingBackendConfig(const QString &why)
{
return QSslSocket::tr("Error when setting the OpenSSL configuration (%1)").arg(why);
@ -95,6 +100,56 @@ static inline QString msgErrorSettingEllipticCurves(const QString &why)
return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why);
}
long QSslContext::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions)
{
long options;
switch (protocol) {
case QSsl::SecureProtocols:
case QSsl::TlsV1_0OrLater:
options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
break;
case QSsl::TlsV1_1OrLater:
options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
break;
case QSsl::TlsV1_2OrLater:
options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
break;
case QSsl::TlsV1_3OrLater:
options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2;
break;
default:
options = SSL_OP_ALL;
}
// This option is disabled by default, so we need to be able to clear it
if (sslOptions & QSsl::SslOptionDisableEmptyFragments)
options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
else
options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
// This option is disabled by default, so we need to be able to clear it
if (sslOptions & QSsl::SslOptionDisableLegacyRenegotiation)
options &= ~SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
else
options |= SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
#endif
#ifdef SSL_OP_NO_TICKET
if (sslOptions & QSsl::SslOptionDisableSessionTickets)
options |= SSL_OP_NO_TICKET;
#endif
#ifdef SSL_OP_NO_COMPRESSION
if (sslOptions & QSsl::SslOptionDisableCompression)
options |= SSL_OP_NO_COMPRESSION;
#endif
if (!(sslOptions & QSsl::SslOptionDisableServerCipherPreference))
options |= SSL_OP_CIPHER_SERVER_PREFERENCE;
return options;
}
QSslContext::QSslContext()
: ctx(nullptr),
pkey(nullptr),
@ -130,6 +185,12 @@ QSharedPointer<QSslContext> QSslContext::sharedFromConfiguration(QSslSocket::Ssl
return sslContext;
}
QSharedPointer<QSslContext> QSslContext::sharedFromPrivateConfiguration(QSslSocket::SslMode mode, QSslConfigurationPrivate *privConfiguration,
bool allowRootCertOnDemandLoading)
{
return sharedFromConfiguration(mode, privConfiguration, allowRootCertOnDemandLoading);
}
#ifndef OPENSSL_NO_NEXTPROTONEG
static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen,
@ -335,7 +396,7 @@ init_context:
}
sslContext->errorStr = QSslSocket::tr("Error creating SSL context (%1)").arg(
unsupportedProtocol ? QSslSocket::tr("unsupported protocol") : QSslSocketBackendPrivate::getErrorsFromOpenSsl()
unsupportedProtocol ? QSslSocket::tr("unsupported protocol") : QTlsBackendOpenSSL::getErrorsFromOpenSsl()
);
sslContext->errorCode = QSslError::UnspecifiedError;
return;
@ -438,7 +499,7 @@ init_context:
}
// Enable bug workarounds.
long options = QSslSocketBackendPrivate::setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions);
const long options = setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions);
q_SSL_CTX_set_options(sslContext->ctx, options);
// Tell OpenSSL to release memory early
@ -464,13 +525,13 @@ init_context:
// Initialize ciphers
QList<QSslCipher> ciphers = sslContext->sslConfiguration.ciphers();
if (ciphers.isEmpty())
ciphers = isDtls ? q_getDefaultDtlsCiphers() : QSslSocketPrivate::defaultCiphers();
ciphers = isDtls ? QTlsBackend::defaultDtlsCiphers() : QTlsBackend::defaultCiphers();
const QByteArray preTls13Ciphers = filterCiphers(ciphers, false);
if (preTls13Ciphers.size()) {
if (!q_SSL_CTX_set_cipher_list(sslContext->ctx, preTls13Ciphers.data())) {
sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
sslContext->errorCode = QSslError::UnspecifiedError;
return;
}
@ -480,7 +541,7 @@ init_context:
#ifdef TLS1_3_VERSION
if (tls13Ciphers.size()) {
if (!q_SSL_CTX_set_ciphersuites(sslContext->ctx, tls13Ciphers.data())) {
sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
sslContext->errorCode = QSslError::UnspecifiedError;
return;
}
@ -513,7 +574,7 @@ init_context:
}
}
if (QSslSocketPrivate::s_loadRootCertsOnDemand && allowRootCertOnDemandLoading) {
if (QSslSocketPrivate::rootCertOnDemandLoadingSupported() && allowRootCertOnDemandLoading) {
// tell OpenSSL the directories where to look up the root certs on demand
const QList<QByteArray> unixDirs = QSslSocketPrivate::unixRootCertDirectories();
int success = 1;
@ -529,7 +590,7 @@ init_context:
}
#endif // OPENSSL_VERSION_MAJOR
if (success != 1) {
const auto qtErrors = QSslSocketBackendPrivate::getErrorsFromOpenSsl();
const auto qtErrors = QTlsBackendOpenSSL::getErrorsFromOpenSsl();
qCWarning(lcSsl) << "An error encountered while to set root certificates location:"
<< qtErrors;
}
@ -545,7 +606,7 @@ init_context:
// Load certificate
if (!q_SSL_CTX_use_certificate(sslContext->ctx, (X509 *)sslContext->sslConfiguration.localCertificate().handle())) {
sslContext->errorStr = QSslSocket::tr("Error loading local certificate, %1").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
sslContext->errorStr = QSslSocket::tr("Error loading local certificate, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
sslContext->errorCode = QSslError::UnspecifiedError;
return;
}
@ -572,14 +633,14 @@ init_context:
sslContext->pkey = nullptr; // Don't free the private key, it belongs to QSslKey
if (!q_SSL_CTX_use_PrivateKey(sslContext->ctx, pkey)) {
sslContext->errorStr = QSslSocket::tr("Error loading private key, %1").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
sslContext->errorStr = QSslSocket::tr("Error loading private key, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
sslContext->errorCode = QSslError::UnspecifiedError;
return;
}
// Check if the certificate matches the private key.
if (!q_SSL_CTX_check_private_key(sslContext->ctx)) {
sslContext->errorStr = QSslSocket::tr("Private key does not certify public key, %1").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
sslContext->errorStr = QSslSocket::tr("Private key does not certify public key, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
sslContext->errorCode = QSslError::UnspecifiedError;
return;
}
@ -605,10 +666,10 @@ init_context:
#if QT_CONFIG(dtls)
isDtls ? dtlscallbacks::q_X509DtlsCallback :
#endif // dtls
q_X509Callback;
QTlsPrivate::q_X509Callback;
if (!isDtls && configuration.handshakeMustInterruptOnError())
verificationCallback = q_X509CallbackDirect;
verificationCallback = QTlsPrivate::q_X509CallbackDirect;
auto verificationMode = SSL_VERIFY_PEER;
if (!isDtls && sslContext->sslConfiguration.missingCertificateIsFatal())
@ -680,7 +741,7 @@ init_context:
for (const auto &sslCurve : qcurves)
curves.push_back(sslCurve.id);
if (!q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_SET_CURVES, long(curves.size()), &curves[0])) {
sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
sslContext->errorStr = msgErrorSettingEllipticCurves(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
sslContext->errorCode = QSslError::UnspecifiedError;
return;
}
@ -690,10 +751,6 @@ init_context:
applyBackendConfig(sslContext);
}
#if QT_CONFIG(ocsp)
extern "C" int qt_OCSP_status_server_callback(SSL *ssl, void *); // Defined in qsslsocket_openssl.cpp.
#endif // ocsp
// static
void QSslContext::applyBackendConfig(QSslContext *sslContext)
{
const QMap<QByteArray, QVariant> &conf = sslContext->sslConfiguration.backendConfiguration();
@ -706,7 +763,7 @@ void QSslContext::applyBackendConfig(QSslContext *sslContext)
// This is our private, undocumented configuration option, existing only for
// the purpose of testing OCSP status responses. We don't even check this
// callback was set. If no - the test must fail.
q_SSL_CTX_set_tlsext_status_cb(sslContext->ctx, qt_OCSP_status_server_callback);
q_SSL_CTX_set_tlsext_status_cb(sslContext->ctx, QTlsPrivate::qt_OCSP_status_server_callback);
if (conf.size() == 1)
return;
}

View File

@ -73,6 +73,9 @@ public:
bool allowRootCertOnDemandLoading);
static QSharedPointer<QSslContext> sharedFromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration,
bool allowRootCertOnDemandLoading);
static QSharedPointer<QSslContext> sharedFromPrivateConfiguration(QSslSocket::SslMode mode, QSslConfigurationPrivate *privConfiguration,
bool allowRootCertOnDemandLoading);
static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions);
QSslError::SslError error() const;
QString errorString() const;

View File

@ -69,22 +69,22 @@ int q_DH_check(DH *dh, int *status)
EVP_PKEY *key = q_EVP_PKEY_new();
if (!key) {
qCWarning(lcSsl, "EVP_PKEY_new failed");
QSslSocketBackendPrivate::logAndClearErrorQueue();
QTlsBackendOpenSSL::logAndClearErrorQueue();
return 0;
}
const auto keyDeleter = qScopeGuard([key](){
q_EVP_PKEY_free(key);
});
if (!q_EVP_PKEY_set1_DH(key, dh)) {
qCWarning(lcSsl, "EVP_PKEY_set1_DH failed");
QSslSocketBackendPrivate::logAndClearErrorQueue();
qCWarning(lcTlsBackend, "EVP_PKEY_set1_DH failed");
QTlsBackendOpenSSL::logAndClearErrorQueue();
return 0;
}
EVP_PKEY_CTX *keyCtx = q_EVP_PKEY_CTX_new(key, nullptr);
if (!keyCtx) {
qCWarning(lcSsl, "EVP_PKEY_CTX_new failed");
QSslSocketBackendPrivate::logAndClearErrorQueue();
qCWarning(lcTlsBackend, "EVP_PKEY_CTX_new failed");
QTlsBackendOpenSSL::logAndClearErrorQueue();
return 0;
}
const auto ctxDeleter = qScopeGuard([keyCtx]{
@ -92,7 +92,7 @@ int q_DH_check(DH *dh, int *status)
});
const int result = q_EVP_PKEY_param_check(keyCtx);
QSslSocketBackendPrivate::logAndClearErrorQueue();
QTlsBackendOpenSSL::logAndClearErrorQueue();
// Note: unlike DH_check, we cannot obtain the 'status',
// if the 'result' is 0 (actually the result is 1 only
// if this 'status' was 0). We could probably check the

View File

@ -82,7 +82,6 @@ private:
friend class QSslContext;
friend class QSslSocketPrivate;
friend class QSslSocketBackendPrivate;
};
Q_DECLARE_TYPEINFO(QSslEllipticCurve, Q_PRIMITIVE_TYPE);

View File

@ -94,8 +94,6 @@ public:
private:
QExplicitlySharedDataPointer<QSslKeyPrivate> d;
friend class QSslCertificate;
friend class QSslSocketBackendPrivate;
friend class QTlsBackend;
};

View File

@ -50,7 +50,6 @@ QT_REQUIRE_CONFIG(ssl);
QT_BEGIN_NAMESPACE
class QSslPreSharedKeyAuthenticatorPrivate;
class QSslPreSharedKeyAuthenticator
{
public:
@ -76,8 +75,7 @@ public:
private:
Q_NETWORK_EXPORT bool isEqual(const QSslPreSharedKeyAuthenticator &other) const;
friend class QSslSocketBackendPrivate;
friend class QDtlsPrivateOpenSSL;
friend class QTlsBackend;
friend bool operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
{ return lhs.isEqual(rhs); }

View File

@ -386,16 +386,8 @@
#include "qsslcipher.h"
#include "qocspresponse.h"
#include "qtlsbackend_p.h"
#ifndef QT_NO_OPENSSL
#include "qsslsocket_openssl_p.h"
#endif
#ifdef QT_SECURETRANSPORT
#include "qsslsocket_mac_p.h"
#endif
#if QT_CONFIG(schannel)
#include "qsslsocket_schannel_p.h"
#endif
#include "qsslconfiguration_p.h"
#include "qsslsocket_p.h"
#include <QtCore/qdebug.h>
#include <QtCore/qdir.h>
@ -433,7 +425,7 @@ Q_GLOBAL_STATIC(QSslSocketGlobalData, globalData)
set to the one returned by the static method defaultCiphers().
*/
QSslSocket::QSslSocket(QObject *parent)
: QTcpSocket(*new QSslSocketBackendPrivate, parent)
: QTcpSocket(*new QSslSocketPrivate, parent)
{
Q_D(QSslSocket);
#ifdef QSSLSOCKET_DEBUG
@ -903,7 +895,8 @@ void QSslSocket::close()
// On Windows, CertGetCertificateChain is probably still doing its
// job, if the socket is re-used, we want to ignore its reported
// root CA.
d->caToFetch = QSslCertificate{};
if (auto *backend = d->backend.get())
backend->cancelCAFetch();
if (!d->abortCalled && (encryptedBytesToWrite() || !d->writeBuffer.isEmpty()))
flush();
@ -1210,7 +1203,9 @@ QSsl::SslProtocol QSslSocket::sessionProtocol() const
QList<QOcspResponse> QSslSocket::ocspResponses() const
{
Q_D(const QSslSocket);
return d->ocspResponses;
if (const auto *backend = d->backend.get())
return backend->ocsps();
return {};
}
/*!
@ -1485,7 +1480,9 @@ bool QSslSocket::waitForDisconnected(int msecs)
QList<QSslError> QSslSocket::sslHandshakeErrors() const
{
Q_D(const QSslSocket);
return d->sslErrors;
if (const auto *backend = d->backend.get())
return backend->tlsErrors();
return {};
}
/*!
@ -1502,12 +1499,14 @@ bool QSslSocket::supportsSsl()
\since 5.0
Returns the version number of the SSL library in use. Note that
this is the version of the library in use at run-time not compile
time. If no SSL support is available then this will return an
undefined value.
time. If no SSL support is available then this will return -1.
*/
long QSslSocket::sslLibraryVersionNumber()
{
return QSslSocketPrivate::sslLibraryVersionNumber();
if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
return tlsBackend->tlsLibraryVersionNumber();
return -1;
}
/*!
@ -1518,20 +1517,23 @@ long QSslSocket::sslLibraryVersionNumber()
*/
QString QSslSocket::sslLibraryVersionString()
{
return QSslSocketPrivate::sslLibraryVersionString();
if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
return tlsBackend->tlsLibraryVersionString();
return {};
}
/*!
\since 5.4
Returns the version number of the SSL library in use at compile
time. If no SSL support is available then this will return an
undefined value.
time. If no SSL support is available then this will return -1.
\sa sslLibraryVersionNumber()
*/
long QSslSocket::sslLibraryBuildVersionNumber()
{
return QSslSocketPrivate::sslLibraryBuildVersionNumber();
if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
return tlsBackend->tlsLibraryBuildVersionNumber();
return -1;
}
/*!
@ -1544,7 +1546,10 @@ long QSslSocket::sslLibraryBuildVersionNumber()
*/
QString QSslSocket::sslLibraryBuildVersionString()
{
return QSslSocketPrivate::sslLibraryBuildVersionString();
if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
return tlsBackend->tlsLibraryBuildVersionString();
return {};
}
/*!
@ -1857,7 +1862,8 @@ void QSslSocket::ignoreSslErrors(const QList<QSslError> &errors)
void QSslSocket::continueInterruptedHandshake()
{
Q_D(QSslSocket);
d->handshakeInterrupted = false;
if (auto *backend = d->backend.get())
backend->enableHandshakeContinuation();
}
/*!
@ -1914,7 +1920,8 @@ void QSslSocket::disconnectFromHost()
}
// Make sure we don't process any signal from the CA fetcher
// (Windows):
d->caToFetch = QSslCertificate{};
if (auto *backend = d->backend.get())
backend->cancelCAFetch();
// Perhaps emit closing()
if (d->state != ClosingState) {
@ -1982,6 +1989,8 @@ qint64 QSslSocket::writeData(const char *data, qint64 len)
return len;
}
bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
/*!
\internal
*/
@ -1990,7 +1999,6 @@ QSslSocketPrivate::QSslSocketPrivate()
, mode(QSslSocket::UnencryptedMode)
, autoStartHandshake(false)
, connectionEncrypted(false)
, shutdown(false)
, ignoreAllSslErrors(false)
, readyReadEmittedPointer(nullptr)
, allowRootCertOnDemandLoading(true)
@ -1999,6 +2007,17 @@ QSslSocketPrivate::QSslSocketPrivate()
, flushTriggered(false)
{
QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration);
const auto *tlsBackend = tlsBackendInUse();
if (!tlsBackend) {
qCWarning(lcSsl, "No TLS backend is available");
return;
}
backend.reset(tlsBackend->createTlsCryptograph());
if (!backend.get()) {
qCWarning(lcSsl) << "The backend named" << tlsBackend->backendName()
<< "does not support TLS";
}
}
/*!
@ -2008,31 +2027,57 @@ QSslSocketPrivate::~QSslSocketPrivate()
{
}
/*!
\internal
*/
bool QSslSocketPrivate::supportsSsl()
{
if (const auto *tlsBackend = tlsBackendInUse())
return tlsBackend->implementedClasses().contains(QSsl::ImplementedClass::Socket);
return false;
}
/*!
\internal
Declared static in QSslSocketPrivate, makes sure the SSL libraries have
been initialized.
*/
void QSslSocketPrivate::ensureInitialized()
{
if (!supportsSsl())
return;
const auto *tlsBackend = tlsBackendInUse();
Q_ASSERT(tlsBackend);
tlsBackend->ensureInitialized();
}
/*!
\internal
*/
void QSslSocketPrivate::init()
{
// TLSTODO: delete those data members.
mode = QSslSocket::UnencryptedMode;
autoStartHandshake = false;
connectionEncrypted = false;
ignoreAllSslErrors = false;
shutdown = false;
abortCalled = false;
pendingClose = false;
flushTriggered = false;
ocspResponses.clear();
systemOrSslErrorDetected = false;
// we don't want to clear the ignoreErrorsList, so
// that it is possible setting it before connecting
// ignoreErrorsList.clear();
// We don't want to clear the ignoreErrorsList, so
// that it is possible setting it before connecting.
buffer.clear();
writeBuffer.clear();
configuration.peerCertificate.clear();
configuration.peerCertificateChain.clear();
fetchAuthorityInformation = false;
caToFetch = QSslCertificate{};
if (backend.get()) {
Q_ASSERT(q_ptr);
backend->init(static_cast<QSslSocket *>(q_ptr), this);
}
}
/*!
@ -2103,7 +2148,35 @@ void QSslSocketPrivate::setDefaultSupportedCiphers(const QList<QSslCipher> &ciph
/*!
\internal
*/
void q_setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers)
void QSslSocketPrivate::resetDefaultEllipticCurves()
{
const auto *tlsBackend = tlsBackendInUse();
if (!tlsBackend)
return;
auto ids = tlsBackend->ellipticCurvesIds();
if (!ids.size())
return;
QList<QSslEllipticCurve> curves;
curves.reserve(ids.size());
for (int id : ids) {
QSslEllipticCurve curve;
curve.id = id;
curves.append(curve);
}
// Set the list of supported ECs, but not the list
// of *default* ECs. OpenSSL doesn't like forcing an EC for the wrong
// ciphersuite, so don't try it -- leave the empty list to mean
// "the implementation will choose the most suitable one".
setDefaultSupportedEllipticCurves(curves);
}
/*!
\internal
*/
void QSslSocketPrivate::setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers)
{
QMutexLocker locker(&globalData()->mutex);
globalData()->dtlsConfig.detach();
@ -2113,7 +2186,7 @@ void q_setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers)
/*!
\internal
*/
QList<QSslCipher> q_getDefaultDtlsCiphers()
QList<QSslCipher> QSslSocketPrivate::defaultDtlsCiphers()
{
QSslSocketPrivate::ensureInitialized();
QMutexLocker locker(&globalData()->mutex);
@ -2360,6 +2433,11 @@ bool QSslSocketPrivate::isPaused() const
return paused;
}
void QSslSocketPrivate::setPaused(bool p)
{
paused = p;
}
bool QSslSocketPrivate::bind(const QHostAddress &address, quint16 port, QAbstractSocket::BindMode mode)
{
// this function is called from QAbstractSocket::bind
@ -2590,6 +2668,7 @@ void QSslSocketPrivate::_q_resumeImplementation()
if (verifyErrorsHaveBeenIgnored()) {
continueHandshake();
} else {
const auto sslErrors = backend->tlsErrors();
Q_ASSERT(!sslErrors.isEmpty());
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, sslErrors.constFirst().errorString());
plainSocket->disconnectFromHost();
@ -2604,11 +2683,14 @@ void QSslSocketPrivate::_q_resumeImplementation()
*/
bool QSslSocketPrivate::verifyErrorsHaveBeenIgnored()
{
Q_ASSERT(backend.get());
bool doEmitSslError;
if (!ignoreErrorsList.empty()) {
// check whether the errors we got are all in the list of expected errors
// (applies only if the method QSslSocket::ignoreSslErrors(const QList<QSslError> &errors)
// was called)
const auto &sslErrors = backend->tlsErrors();
doEmitSslError = false;
for (int a = 0; a < sslErrors.count(); a++) {
if (!ignoreErrorsList.contains(sslErrors.at(a))) {
@ -2625,6 +2707,91 @@ bool QSslSocketPrivate::verifyErrorsHaveBeenIgnored()
return !doEmitSslError;
}
/*!
\internal
*/
bool QSslSocketPrivate::isAutoStartingHandshake() const
{
return autoStartHandshake;
}
/*!
\internal
*/
bool QSslSocketPrivate::isPendingClose() const
{
return pendingClose;
}
/*!
\internal
*/
void QSslSocketPrivate::setPendingClose(bool pc)
{
pendingClose = pc;
}
/*!
\internal
*/
qint64 QSslSocketPrivate::maxReadBufferSize() const
{
return readBufferMaxSize;
}
/*!
\internal
*/
void QSslSocketPrivate::setMaxReadBufferSize(qint64 maxSize)
{
readBufferMaxSize = maxSize;
}
/*!
\internal
*/
void QSslSocketPrivate::setEncrypted(bool enc)
{
connectionEncrypted = enc;
}
/*!
\internal
*/
QIODevicePrivate::QRingBufferRef &QSslSocketPrivate::tlsWriteBuffer()
{
return writeBuffer;
}
/*!
\internal
*/
QIODevicePrivate::QRingBufferRef &QSslSocketPrivate::tlsBuffer()
{
return buffer;
}
/*!
\internal
*/
bool &QSslSocketPrivate::tlsEmittedBytesWritten()
{
return emittedBytesWritten;
}
/*!
\internal
*/
bool *QSslSocketPrivate::readyReadPointer()
{
return readyReadEmittedPointer;
}
bool QSslSocketPrivate::hasUndecryptedData() const
{
return backend.get() && backend->hasUndecryptedData();
}
/*!
\internal
*/
@ -2643,9 +2810,9 @@ qint64 QSslSocketPrivate::peek(char *data, qint64 maxSize)
if (r2 < 0)
return (r > 0 ? r : r2);
return r + r2;
} else {
return -1;
}
return -1;
} else {
//encrypted mode - the socket engine will read and decrypt data into the QIODevice buffer
return QTcpSocketPrivate::peek(data, maxSize);
@ -2668,8 +2835,8 @@ QByteArray QSslSocketPrivate::peek(qint64 maxSize)
//peek at data in the plain socket
if (plainSocket)
return ret + plainSocket->peek(maxSize - ret.length());
else
return QByteArray();
return QByteArray();
} else {
//encrypted mode - the socket engine will read and decrypt data into the QIODevice buffer
return QTcpSocketPrivate::peek(maxSize);
@ -2708,6 +2875,82 @@ bool QSslSocketPrivate::flush()
return plainSocket && plainSocket->flush();
}
/*!
\internal
*/
void QSslSocketPrivate::startClientEncryption()
{
if (backend.get())
backend->startClientEncryption();
}
/*!
\internal
*/
void QSslSocketPrivate::startServerEncryption()
{
if (backend.get())
backend->startServerEncryption();
}
/*!
\internal
*/
void QSslSocketPrivate::transmit()
{
if (backend.get())
backend->transmit();
}
/*!
\internal
*/
void QSslSocketPrivate::disconnectFromHost()
{
if (backend.get())
backend->disconnectFromHost();
}
/*!
\internal
*/
void QSslSocketPrivate::disconnected()
{
if (backend.get())
backend->disconnected();
}
/*!
\internal
*/
QSslCipher QSslSocketPrivate::sessionCipher() const
{
if (backend.get())
return backend->sessionCipher();
return {};
}
/*!
\internal
*/
QSsl::SslProtocol QSslSocketPrivate::sessionProtocol() const
{
if (backend.get())
return backend->sessionProtocol();
return QSsl::UnknownProtocol;
}
/*!
\internal
*/
void QSslSocketPrivate::continueHandshake()
{
if (backend.get())
backend->continueHandshake();
}
/*!
\internal
*/
@ -2716,6 +2959,14 @@ bool QSslSocketPrivate::rootCertOnDemandLoadingSupported()
return s_loadRootCertsOnDemand;
}
/*!
\internal
*/
void QSslSocketPrivate::setRootCertOnDemandLoadingSupported(bool supported)
{
s_loadRootCertsOnDemand = supported;
}
/*!
\internal
*/
@ -2735,10 +2986,13 @@ QList<QByteArray> QSslSocketPrivate::unixRootCertDirectories()
/*!
\internal
*/
void QSslSocketPrivate::checkSettingSslContext(QSslSocket* socket, QSharedPointer<QSslContext> sslContext)
void QSslSocketPrivate::checkSettingSslContext(QSslSocket* socket, QSharedPointer<QSslContext> tlsContext)
{
if (socket->d_func()->sslContextPointer.isNull())
socket->d_func()->sslContextPointer = sslContext;
if (!socket)
return;
if (auto *backend = socket->d_func()->backend.get())
backend->checkSettingSslContext(tlsContext);
}
/*!
@ -2746,7 +3000,13 @@ void QSslSocketPrivate::checkSettingSslContext(QSslSocket* socket, QSharedPointe
*/
QSharedPointer<QSslContext> QSslSocketPrivate::sslContext(QSslSocket *socket)
{
return (socket) ? socket->d_func()->sslContextPointer : QSharedPointer<QSslContext>();
if (!socket)
return {};
if (const auto *backend = socket->d_func()->backend.get())
return backend->sslContext();
return {};
}
bool QSslSocketPrivate::isMatchingHostname(const QSslCertificate &cert, const QString &peerName)
@ -2852,6 +3112,61 @@ QTlsBackend *QSslSocketPrivate::tlsBackendInUse()
return tlsBackend = QTlsBackend::findBackend(activeBackendName);
}
/*!
\internal
*/
QSslSocket::SslMode QSslSocketPrivate::tlsMode() const
{
return mode;
}
/*!
\internal
*/
QSslConfigurationPrivate &QSslSocketPrivate::privateConfiguration()
{
return configuration;
}
/*!
\internal
*/
bool QSslSocketPrivate::isRootsOnDemandAllowed() const
{
return allowRootCertOnDemandLoading;
}
/*!
\internal
*/
QString QSslSocketPrivate::verificationName() const
{
return verificationPeerName;
}
/*!
\internal
*/
QString QSslSocketPrivate::tlsHostName() const
{
return hostName;
}
QTcpSocket *QSslSocketPrivate::plainTcpSocket() const
{
return plainSocket;
}
/*!
\internal
*/
QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
{
if (const auto *tlsBackend = tlsBackendInUse())
return tlsBackend->systemCaCertificates();
return {};
}
QT_END_NAMESPACE
#include "moc_qsslsocket.cpp"

View File

@ -201,6 +201,7 @@ protected:
private:
Q_DECLARE_PRIVATE(QSslSocket)
Q_DISABLE_COPY_MOVE(QSslSocket)
Q_PRIVATE_SLOT(d_func(), void _q_connectedSlot())
Q_PRIVATE_SLOT(d_func(), void _q_hostFoundSlot())
Q_PRIVATE_SLOT(d_func(), void _q_disconnectedSlot())
@ -214,10 +215,6 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_flushWriteBuffer())
Q_PRIVATE_SLOT(d_func(), void _q_flushReadBuffer())
Q_PRIVATE_SLOT(d_func(), void _q_resumeImplementation())
#if defined(Q_OS_WIN) && !QT_CONFIG(schannel)
Q_PRIVATE_SLOT(d_func(), void _q_caRootLoaded(QSslCertificate,QSslCertificate))
#endif
friend class QSslSocketBackendPrivate;
};
#endif // QT_NO_SSL

View File

@ -38,30 +38,22 @@
**
****************************************************************************/
//#define QSSLSOCKET_DEBUG
//#define QT_DECRYPT_SSL_TRAFFIC
#include "qsslcertificate.h"
#include "qssl_p.h"
#include "qsslsocket.h"
#include "qsslsocket_p.h"
#include <QtCore/qglobal.h>
#ifndef QT_NO_OPENSSL
# include "qsslsocket_openssl_p.h"
# include "qsslsocket_openssl_symbols_p.h"
#endif
#ifdef Q_OS_MACOS
#include "qsslcertificate_p.h"
#include "qtlsbackend_p.h"
#ifdef Q_OS_DARWIN
# include <private/qcore_mac_p.h>
#endif
#include <private/qcore_mac_p.h>
#include <QtCore/qdebug.h>
#ifdef Q_OS_MACOS
# include <Security/Security.h>
#endif
#include <CoreFoundation/CFArray.h>
#include <Security/Security.h>
#endif // Q_OS_MACOS
QT_BEGIN_NAMESPACE
@ -114,18 +106,17 @@ bool isCaCertificateTrusted(SecCertificateRef cfCert, int domain)
}
}
} else {
qCWarning(lcSsl, "Error receiving trust for a CA certificate");
qCWarning(lcTlsBackend, "Error receiving trust for a CA certificate");
}
return false;
}
} // anon namespace
} // unnamed namespace
#endif // Q_OS_MACOS
QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
namespace QTlsPrivate {
QList<QSslCertificate> systemCaCertificates()
{
ensureInitialized();
QList<QSslCertificate> systemCerts;
// SecTrustSettingsCopyCertificates is not defined on iOS.
#ifdef Q_OS_MACOS
@ -152,5 +143,6 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
#endif
return systemCerts;
}
} // namespace QTlsPrivate
QT_END_NAMESPACE

View File

@ -52,13 +52,15 @@
**
****************************************************************************/
#include "qsslsocket_openssl_p.h"
#include "qsslsocket_p.h"
#include <QtCore/QJniEnvironment>
#include <QtCore/QJniObject>
QT_BEGIN_NAMESPACE
QList<QByteArray> QSslSocketPrivate::fetchSslCertificateData()
namespace QTlsPrivate {
QList<QByteArray> fetchSslCertificateData()
{
QList<QByteArray> certificateData;
@ -86,4 +88,6 @@ QList<QByteArray> QSslSocketPrivate::fetchSslCertificateData()
return certificateData;
}
} // namespace QTlsPrivate
QT_END_NAMESPACE

View File

@ -69,7 +69,7 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qsslsocket_openssl_p.h"
#include "qopenssl_p.h"
#include <QtCore/qglobal.h>
#if QT_CONFIG(ocsp)

View File

@ -55,59 +55,25 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <private/qtcpsocket_p.h>
#include "qsslkey.h"
#include "qsslconfiguration_p.h"
#include "qocspresponse.h"
#ifndef QT_NO_OPENSSL
#include <private/qsslcontext_openssl_p.h>
#else
class QSslContext;
#endif
#include "qsslconfiguration_p.h"
#include "qsslkey.h"
#include "qtlsbackend_p.h"
#include <QtCore/qlist.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qmutex.h>
#include <private/qringbuffer_p.h>
#if defined(Q_OS_MAC)
#include <Security/SecCertificate.h>
#include <CoreFoundation/CFArray.h>
#elif defined(Q_OS_WIN)
#include <QtCore/qt_windows.h>
#include <memory>
#include <wincrypt.h>
#ifndef HCRYPTPROV_LEGACY
#define HCRYPTPROV_LEGACY HCRYPTPROV
#endif // !HCRYPTPROV_LEGACY
#endif // Q_OS_WIN
#include <QtCore/qstringlist.h>
#include <memory>
QT_BEGIN_NAMESPACE
#if defined(Q_OS_MACOS)
typedef CFDataRef (*PtrSecCertificateCopyData)(SecCertificateRef);
typedef OSStatus (*PtrSecTrustSettingsCopyCertificates)(int, CFArrayRef*);
typedef OSStatus (*PtrSecTrustCopyAnchorCertificates)(CFArrayRef*);
#endif
#if defined(Q_OS_WIN)
// Those are needed by both OpenSSL and Schannel back-ends on Windows:
struct QHCertStoreDeleter {
void operator()(HCERTSTORE store)
{
CertCloseStore(store, 0);
}
};
using QHCertStorePointer = std::unique_ptr<void, QHCertStoreDeleter>;
#endif // Q_OS_WIN
class QSslContext;
class QTlsBackend;
class QSslSocketPrivate : public QTcpSocketPrivate
{
Q_DECLARE_PUBLIC(QSslSocket)
@ -122,14 +88,11 @@ public:
QSslSocket::SslMode mode;
bool autoStartHandshake;
bool connectionEncrypted;
bool shutdown;
bool ignoreAllSslErrors;
QList<QSslError> ignoreErrorsList;
bool* readyReadEmittedPointer;
QSslConfigurationPrivate configuration;
QList<QSslError> sslErrors;
QSharedPointer<QSslContext> sslContextPointer;
// if set, this hostname is used for certificate validation instead of the hostname
// that was used for connecting to.
@ -140,16 +103,14 @@ public:
static bool s_loadRootCertsOnDemand;
static bool supportsSsl();
static long sslLibraryVersionNumber();
static QString sslLibraryVersionString();
static long sslLibraryBuildVersionNumber();
static QString sslLibraryBuildVersionString();
static void ensureInitialized();
static QList<QSslCipher> defaultCiphers();
static QList<QSslCipher> defaultDtlsCiphers();
static QList<QSslCipher> supportedCiphers();
static void setDefaultCiphers(const QList<QSslCipher> &ciphers);
static void setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers);
static void setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers);
static void resetDefaultCiphers();
static QList<QSslEllipticCurve> supportedEllipticCurves();
static void setDefaultSupportedEllipticCurves(const QList<QSslEllipticCurve> &curves);
@ -160,19 +121,19 @@ public:
static void setDefaultCaCertificates(const QList<QSslCertificate> &certs);
static void addDefaultCaCertificate(const QSslCertificate &cert);
static void addDefaultCaCertificates(const QList<QSslCertificate> &certs);
Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QSslCertificate &cert,
const QString &peerName);
Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName);
Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname);
// The socket itself, including private slots.
QTcpSocket *plainSocket;
QTcpSocket *plainSocket = nullptr;
void createPlainSocket(QIODevice::OpenMode openMode);
static void pauseSocketNotifiers(QSslSocket*);
static void resumeSocketNotifiers(QSslSocket*);
Q_NETWORK_EXPORT static void pauseSocketNotifiers(QSslSocket*);
Q_NETWORK_EXPORT static void resumeSocketNotifiers(QSslSocket*);
// ### The 2 methods below should be made member methods once the QSslContext class is made public
static void checkSettingSslContext(QSslSocket*, QSharedPointer<QSslContext>);
static QSharedPointer<QSslContext> sslContext(QSslSocket *socket);
bool isPaused() const;
Q_NETWORK_EXPORT bool isPaused() const;
Q_NETWORK_EXPORT void setPaused(bool p);
bool bind(const QHostAddress &address, quint16, QAbstractSocket::BindMode) override;
void _q_connectedSlot();
void _q_hostFoundSlot();
@ -187,62 +148,59 @@ public:
void _q_flushWriteBuffer();
void _q_flushReadBuffer();
void _q_resumeImplementation();
#if defined(Q_OS_WIN) && !QT_CONFIG(schannel)
virtual void _q_caRootLoaded(QSslCertificate,QSslCertificate) = 0;
#endif
static QList<QByteArray> unixRootCertDirectories(); // used also by QSslContext
Q_NETWORK_PRIVATE_EXPORT static QList<QByteArray> unixRootCertDirectories(); // used also by QSslContext
virtual qint64 peek(char *data, qint64 maxSize) override;
virtual QByteArray peek(qint64 maxSize) override;
qint64 peek(char *data, qint64 maxSize) override;
QByteArray peek(qint64 maxSize) override;
bool flush() override;
// Platform specific functions
virtual void startClientEncryption() = 0;
virtual void startServerEncryption() = 0;
virtual void transmit() = 0;
virtual void disconnectFromHost() = 0;
virtual void disconnected() = 0;
virtual QSslCipher sessionCipher() const = 0;
virtual QSsl::SslProtocol sessionProtocol() const = 0;
virtual void continueHandshake() = 0;
void startClientEncryption();
void startServerEncryption();
void transmit();
void disconnectFromHost();
void disconnected();
QSslCipher sessionCipher() const;
QSsl::SslProtocol sessionProtocol() const;
void continueHandshake();
Q_AUTOTEST_EXPORT static bool rootCertOnDemandLoadingSupported();
Q_NETWORK_PRIVATE_EXPORT static bool rootCertOnDemandLoadingSupported();
Q_NETWORK_PRIVATE_EXPORT static void setRootCertOnDemandLoadingSupported(bool supported);
static QTlsBackend *tlsBackendInUse();
static void registerAdHocFactory();
private:
static bool ensureLibraryLoaded();
static void ensureCiphersAndCertsLoaded();
#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
static QList<QByteArray> fetchSslCertificateData();
#endif
static bool s_libraryLoaded;
static bool s_loadedCiphersAndCerts;
// Needed by TlsCryptograph:
Q_NETWORK_PRIVATE_EXPORT QSslSocket::SslMode tlsMode() const;
Q_NETWORK_PRIVATE_EXPORT QSslConfigurationPrivate &privateConfiguration();
Q_NETWORK_PRIVATE_EXPORT bool isRootsOnDemandAllowed() const;
Q_NETWORK_PRIVATE_EXPORT QString verificationName() const;
Q_NETWORK_PRIVATE_EXPORT QString tlsHostName() const;
Q_NETWORK_PRIVATE_EXPORT QTcpSocket *plainTcpSocket() const;
Q_NETWORK_PRIVATE_EXPORT bool verifyErrorsHaveBeenIgnored();
Q_NETWORK_PRIVATE_EXPORT bool isAutoStartingHandshake() const;
Q_NETWORK_PRIVATE_EXPORT bool isPendingClose() const;
Q_NETWORK_PRIVATE_EXPORT void setPendingClose(bool pc);
Q_NETWORK_PRIVATE_EXPORT qint64 maxReadBufferSize() const;
Q_NETWORK_PRIVATE_EXPORT void setMaxReadBufferSize(qint64 maxSize);
Q_NETWORK_PRIVATE_EXPORT void setEncrypted(bool enc);
Q_NETWORK_PRIVATE_EXPORT QRingBufferRef &tlsWriteBuffer();
Q_NETWORK_PRIVATE_EXPORT QRingBufferRef &tlsBuffer();
Q_NETWORK_PRIVATE_EXPORT bool &tlsEmittedBytesWritten();
Q_NETWORK_PRIVATE_EXPORT bool *readyReadPointer();
protected:
bool verifyErrorsHaveBeenIgnored();
// Only implemented/useful in Schannel for now
virtual bool hasUndecryptedData() { return false; };
bool hasUndecryptedData() const;
bool paused;
bool flushTriggered;
bool systemOrSslErrorDetected = false;
QList<QOcspResponse> ocspResponses;
bool handshakeInterrupted = false;
bool fetchAuthorityInformation = false;
QSslCertificate caToFetch;
static inline QMutex backendMutex;
static inline QString activeBackendName;
static inline QTlsBackend *tlsBackend = nullptr;
};
#if QT_CONFIG(securetransport) || QT_CONFIG(schannel)
// Implemented in qsslsocket_qt.cpp
QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase);
#endif
std::unique_ptr<QTlsPrivate::TlsCryptograph> backend;
};
QT_END_NAMESPACE

View File

@ -0,0 +1,169 @@
/****************************************************************************
**
** 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 QTLS_OPENSSL_P_H
#define QTLS_OPENSSL_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qtnetworkglobal_p.h>
#include "qtlsbackend_openssl_p.h"
#include "qsslcontext_openssl_p.h"
#include "qsslcertificate.h"
#include "qocspresponse.h"
#include "qopenssl_p.h"
#include <QtCore/qsharedpointer.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qglobal.h>
#include <QtCore/qlist.h>
QT_BEGIN_NAMESPACE
namespace QTlsPrivate {
class TlsCryptographOpenSSL : public TlsCryptograph
{
public:
enum ExDataOffset {
errorOffsetInExData = 1,
socketOffsetInExData = 2
};
~TlsCryptographOpenSSL();
void init(QSslSocket *qObj, QSslSocketPrivate *dObj) override;
void checkSettingSslContext(QSharedPointer<QSslContext> tlsContext) override;
QSharedPointer<QSslContext> sslContext() const override;
QList<QSslError> tlsErrors() const override;
void startClientEncryption() override;
void startServerEncryption() override;
bool startHandshake();
void enableHandshakeContinuation() override;
void cancelCAFetch() override;
void continueHandshake() override;
void transmit() override;
void disconnectFromHost() override;
void disconnected() override;
QSslCipher sessionCipher() const override;
QSsl::SslProtocol sessionProtocol() const override;
QList<QOcspResponse> ocsps() const override;
bool checkSslErrors();
int handleNewSessionTicket(SSL *connection);
void alertMessageSent(int encoded);
void alertMessageReceived(int encoded);
int emitErrorFromCallback(X509_STORE_CTX *ctx);
void trySendFatalAlert();
#if QT_CONFIG(ocsp)
bool checkOcspStatus();
#endif
QSslSocket *q = nullptr;
QSslSocketPrivate *d = nullptr;
void storePeerCertificates();
unsigned pskClientTlsCallback(const char *hint, char *identity, unsigned max_identity_len,
unsigned char *psk, unsigned max_psk_len);
unsigned pskServerTlsCallback(const char *identity, unsigned char *psk,
unsigned max_psk_len);
#ifdef Q_OS_WIN
void fetchCaRootForCert(const QSslCertificate &cert);
void caRootLoaded(QSslCertificate certificate, QSslCertificate trustedRoot);
#endif
QByteArray ocspResponseDer;
private:
// TLSTODO: names were preserved, to make comparison
// easier (see qsslsocket_openssl.cpp, while it exists).
bool initSslContext();
void destroySslContext();
QSharedPointer<QSslContext> sslContextPointer;
SSL *ssl = nullptr; // TLSTODO: RAII.
QList<QSslErrorEntry> errorList;
QList<QSslError> sslErrors;
BIO *readBio = nullptr;
BIO *writeBio = nullptr;
QList<QOcspResponse> ocspResponses;
// This decription will go to setErrorAndEmit(SslHandshakeError, ocspErrorDescription)
QString ocspErrorDescription;
// These will go to sslErrors()
QList<QSslError> ocspErrors;
bool systemOrSslErrorDetected = false;
bool handshakeInterrupted = false;
bool fetchAuthorityInformation = false;
QSslCertificate caToFetch;
bool inSetAndEmitError = false;
bool pendingFatalAlert = false;
bool errorsReportedFromCallback = false;
bool shutdown = false;
};
} // namespace QTlsPrivate
QT_END_NAMESPACE
#endif // QTLS_OPENSSL_P_H

View File

@ -41,7 +41,7 @@
#include "qssl_p.h"
#include "qsslsocket.h"
#include "qsslsocket_schannel_p.h"
#include "qtls_schannel_p.h"
#include "qsslcertificate.h"
#include "qsslcertificateextension.h"
#include "qsslcertificate_p.h"
@ -161,10 +161,99 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.schannel");
// Defined in qsslsocket_qt.cpp.
QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key,
const QString &passPhrase);
namespace QTlsPrivate {
QList<QSslCipher> defaultCiphers()
{
// Previously the code was in QSslSocketBackendPrivate.
QList<QSslCipher> ciphers;
// @temp (I hope), stolen from qsslsocket_winrt.cpp
const QString protocolStrings[] = { QStringLiteral("TLSv1"), QStringLiteral("TLSv1.1"),
QStringLiteral("TLSv1.2"), QStringLiteral("TLSv1.3") };
const QSsl::SslProtocol protocols[] = { QSsl::TlsV1_0, QSsl::TlsV1_1,
QSsl::TlsV1_2, QSsl::TlsV1_3 };
const int size = ARRAYSIZE(protocols);
static_assert(size == ARRAYSIZE(protocolStrings));
ciphers.reserve(size);
for (int i = 0; i < size; ++i) {
const QSslCipher cipher = QTlsBackend::createCipher(QStringLiteral("Schannel"),
protocols[i], protocolStrings[i]);
ciphers.append(cipher);
}
return ciphers;
}
} // namespace QTlsPrivate
namespace {
bool supportsTls13();
}
bool QSchannelBackend::s_loadedCiphersAndCerts = false;
Q_GLOBAL_STATIC(QRecursiveMutex, qt_schannel_mutex)
long QSchannelBackend::tlsLibraryVersionNumber() const
{
const auto os = QOperatingSystemVersion::current();
return (os.majorVersion() << 24) | ((os.minorVersion() & 0xFF) << 16) | (os.microVersion() & 0xFFFF);
}
QString QSchannelBackend::tlsLibraryVersionString() const
{
const auto os = QOperatingSystemVersion::current();
return QString::fromLatin1("Secure Channel, %1 %2.%3.%4")
.arg(os.name(),
QString::number(os.majorVersion()),
QString::number(os.minorVersion()),
QString::number(os.microVersion()));
}
long QSchannelBackend::tlsLibraryBuildVersionNumber() const
{
return tlsLibraryVersionNumber();
}
QString QSchannelBackend::tlsLibraryBuildVersionString() const
{
const auto os = QOperatingSystemVersion::current();
return QString::fromLatin1("%1.%2.%3")
.arg(QString::number(os.majorVersion()),
QString::number(os.minorVersion()),
QString::number(os.microVersion()));
}
void QSchannelBackend::ensureInitialized() const
{
ensureInitializedImplementation();
}
void QSchannelBackend::ensureInitializedImplementation()
{
const QMutexLocker<QRecursiveMutex> locker(qt_schannel_mutex);
if (s_loadedCiphersAndCerts)
return;
s_loadedCiphersAndCerts = true;
setDefaultCaCertificates(systemCaCertificatesImplementation());
// setDefaultCaCertificates sets it to false, re-enable it:
QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true);
resetDefaultCiphers();
}
void QSchannelBackend::resetDefaultCiphers()
{
setDefaultSupportedCiphers(QTlsPrivate::defaultCiphers());
setDefaultCiphers(QTlsPrivate::defaultCiphers());
}
QString QSchannelBackend::backendName() const
{
return builtinBackendNames[nameIndexSchannel];
@ -222,6 +311,32 @@ QTlsPrivate::X509Certificate *QSchannelBackend::createCertificate() const
return new QTlsPrivate::X509CertificateSchannel;
}
QList<QSslCertificate> QSchannelBackend::systemCaCertificates() const
{
return systemCaCertificatesImplementation();
}
QTlsPrivate::TlsCryptograph *QSchannelBackend::createTlsCryptograph() const
{
return new QTlsPrivate::TlsCryptographSchannel;
}
QList<QSslCertificate> QSchannelBackend::systemCaCertificatesImplementation()
{
// Similar to non-Darwin version found in qtlsbackend_openssl.cpp,
// QTlsPrivate::systemCaCertificates function.
QList<QSslCertificate> systemCerts;
auto hSystemStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT"));
if (hSystemStore) {
PCCERT_CONTEXT pc = nullptr;
while ((pc = CertFindCertificateInStore(hSystemStore.get(), X509_ASN_ENCODING, 0,
CERT_FIND_ANY, nullptr, pc))) {
systemCerts.append(QTlsPrivate::X509CertificateSchannel::QSslCertificate_from_CERT_CONTEXT(pc));
}
}
return systemCerts;
}
QTlsPrivate::X509PemReaderPtr QSchannelBackend::X509PemReader() const
{
return QTlsPrivate::X509CertificateGeneric::certificatesFromPem;
@ -232,7 +347,7 @@ QTlsPrivate::X509DerReaderPtr QSchannelBackend::X509DerReader() const
return QTlsPrivate::X509CertificateGeneric::certificatesFromDer;
}
Q_GLOBAL_STATIC(QSchannelBackend, backend)
Q_GLOBAL_STATIC(QSchannelBackend, backendSchannel)
namespace {
@ -588,93 +703,17 @@ qint64 checkIncompleteData(const SecBuffer &secBuffer)
} // anonymous namespace
bool QSslSocketPrivate::s_loadRootCertsOnDemand = true;
bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
Q_GLOBAL_STATIC(QRecursiveMutex, qt_schannel_mutex)
void QSslSocketPrivate::ensureInitialized()
{
const QMutexLocker<QRecursiveMutex> locker(qt_schannel_mutex);
if (s_loadedCiphersAndCerts)
return;
s_loadedCiphersAndCerts = true;
namespace QTlsPrivate {
setDefaultCaCertificates(systemCaCertificates());
s_loadRootCertsOnDemand = true; // setDefaultCaCertificates sets it to false, re-enable it.
resetDefaultCiphers();
}
void QSslSocketPrivate::resetDefaultCiphers()
{
setDefaultSupportedCiphers(QSslSocketBackendPrivate::defaultCiphers());
setDefaultCiphers(QSslSocketBackendPrivate::defaultCiphers());
}
void QSslSocketPrivate::resetDefaultEllipticCurves()
{
Q_UNIMPLEMENTED();
}
bool QSslSocketPrivate::supportsSsl()
{
return true;
}
QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
{
// Copied from qsslsocket_openssl.cpp's systemCaCertificates function.
QList<QSslCertificate> systemCerts;
auto hSystemStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT"));
if (hSystemStore) {
PCCERT_CONTEXT pc = nullptr;
while ((pc = CertFindCertificateInStore(hSystemStore.get(), X509_ASN_ENCODING, 0,
CERT_FIND_ANY, nullptr, pc))) {
systemCerts.append(QTlsPrivate::X509CertificateSchannel::QSslCertificate_from_CERT_CONTEXT(pc));
}
}
return systemCerts;
}
long QSslSocketPrivate::sslLibraryVersionNumber()
{
const auto os = QOperatingSystemVersion::current();
return (os.majorVersion() << 24) | ((os.minorVersion() & 0xFF) << 16) | (os.microVersion() & 0xFFFF);
}
QString QSslSocketPrivate::sslLibraryVersionString()
{
const auto os = QOperatingSystemVersion::current();
return QString::fromLatin1("Secure Channel, %1 %2.%3.%4")
.arg(os.name(),
QString::number(os.majorVersion()),
QString::number(os.minorVersion()),
QString::number(os.microVersion()));
}
long QSslSocketPrivate::sslLibraryBuildVersionNumber()
{
// There is no separate build version
return sslLibraryVersionNumber();
}
QString QSslSocketPrivate::sslLibraryBuildVersionString()
{
const auto os = QOperatingSystemVersion::current();
return QString::fromLatin1("%1.%2.%3")
.arg(QString::number(os.majorVersion()),
QString::number(os.minorVersion()),
QString::number(os.microVersion()));
}
QSslSocketBackendPrivate::QSslSocketBackendPrivate()
TlsCryptographSchannel::TlsCryptographSchannel()
{
SecInvalidateHandle(&credentialHandle);
SecInvalidateHandle(&contextHandle);
ensureInitialized();
QSchannelBackend::ensureInitializedImplementation();
}
QSslSocketBackendPrivate::~QSslSocketBackendPrivate()
TlsCryptographSchannel::~TlsCryptographSchannel()
{
closeCertificateStores();
deallocateContext();
@ -682,29 +721,51 @@ QSslSocketBackendPrivate::~QSslSocketBackendPrivate()
CertFreeCertificateContext(localCertContext);
}
bool QSslSocketBackendPrivate::sendToken(void *token, unsigned long tokenLength, bool emitError)
void TlsCryptographSchannel::init(QSslSocket *qObj, QSslSocketPrivate *dObj)
{
Q_ASSERT(qObj);
Q_ASSERT(dObj);
q = qObj;
d = dObj;
reset();
}
bool TlsCryptographSchannel::sendToken(void *token, unsigned long tokenLength, bool emitError)
{
if (tokenLength == 0)
return true;
Q_ASSERT(d);
auto *plainSocket = d->plainTcpSocket();
Q_ASSERT(plainSocket);
const qint64 written = plainSocket->write(static_cast<const char *>(token), tokenLength);
if (written != qint64(tokenLength)) {
// Failed to write/buffer everything or an error occurred
if (emitError)
setErrorAndEmit(plainSocket->error(), plainSocket->errorString());
d->setErrorAndEmit(plainSocket->error(), plainSocket->errorString());
return false;
}
return true;
}
QString QSslSocketBackendPrivate::targetName() const
QString TlsCryptographSchannel::targetName() const
{
// Used for SNI extension
return verificationPeerName.isEmpty() ? q_func()->peerName() : verificationPeerName;
Q_ASSERT(q);
Q_ASSERT(d);
const auto verificationPeerName = d->verificationName();
return verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName;
}
ULONG QSslSocketBackendPrivate::getContextRequirements()
ULONG TlsCryptographSchannel::getContextRequirements()
{
const bool isClient = mode == QSslSocket::SslClientMode;
Q_ASSERT(d);
const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
ULONG req = 0;
req |= ISC_REQ_ALLOCATE_MEMORY; // Allocate memory for buffers automatically
@ -716,7 +777,7 @@ ULONG QSslSocketBackendPrivate::getContextRequirements()
if (isClient) {
req |= ISC_REQ_MANUAL_CRED_VALIDATION; // Manually validate certificate
} else {
switch (configuration.peerVerifyMode) {
switch (d->privateConfiguration().peerVerifyMode) {
case QSslSocket::PeerVerifyMode::VerifyNone:
// There doesn't seem to be a way to ask for an optional client cert :-(
case QSslSocket::PeerVerifyMode::AutoVerifyPeer:
@ -731,15 +792,18 @@ ULONG QSslSocketBackendPrivate::getContextRequirements()
return req;
}
bool QSslSocketBackendPrivate::acquireCredentialsHandle()
bool TlsCryptographSchannel::acquireCredentialsHandle()
{
Q_ASSERT(d);
const auto &configuration = d->privateConfiguration();
Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
const bool isClient = mode == QSslSocket::SslClientMode;
const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
const DWORD protocols = toSchannelProtocol(configuration.protocol);
if (protocols == DWORD(-1)) {
setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
QSslSocket::tr("Invalid protocol chosen"));
d->setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
QSslSocket::tr("Invalid protocol chosen"));
return false;
}
@ -776,7 +840,7 @@ bool QSslSocketBackendPrivate::acquireCredentialsHandle()
const QString message = isClient
? QSslSocket::tr("The certificate provided cannot be used for a client.")
: QSslSocket::tr("The certificate provided cannot be used for a server.");
setErrorAndEmit(QAbstractSocket::SocketError::SslInvalidUserDataError, message);
d->setErrorAndEmit(QAbstractSocket::SocketError::SslInvalidUserDataError, message);
return false;
}
Q_ASSERT(chainContext->cChain == 1);
@ -865,13 +929,13 @@ bool QSslSocketBackendPrivate::acquireCredentialsHandle()
}
if (status != SEC_E_OK) {
setErrorAndEmit(QAbstractSocket::SslInternalError, schannelErrorToString(status));
d->setErrorAndEmit(QAbstractSocket::SslInternalError, schannelErrorToString(status));
return false;
}
return true;
}
void QSslSocketBackendPrivate::deallocateContext()
void TlsCryptographSchannel::deallocateContext()
{
if (SecIsValidHandle(&contextHandle)) {
DeleteSecurityContext(&contextHandle);
@ -879,7 +943,7 @@ void QSslSocketBackendPrivate::deallocateContext()
}
}
void QSslSocketBackendPrivate::freeCredentialsHandle()
void TlsCryptographSchannel::freeCredentialsHandle()
{
if (SecIsValidHandle(&credentialHandle)) {
FreeCredentialsHandle(&credentialHandle);
@ -887,18 +951,21 @@ void QSslSocketBackendPrivate::freeCredentialsHandle()
}
}
void QSslSocketBackendPrivate::closeCertificateStores()
void TlsCryptographSchannel::closeCertificateStores()
{
localCertificateStore.reset();
peerCertificateStore.reset();
caCertificateStore.reset();
}
bool QSslSocketBackendPrivate::createContext()
bool TlsCryptographSchannel::createContext()
{
Q_ASSERT(d);
auto &configuration = d->privateConfiguration();
Q_ASSERT(SecIsValidHandle(&credentialHandle));
Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
Q_ASSERT(mode == QSslSocket::SslClientMode);
Q_ASSERT(d->tlsMode() == QSslSocket::SslClientMode);
ULONG contextReq = getContextRequirements();
SecBuffer outBuffers[3];
@ -951,8 +1018,8 @@ bool QSslSocketBackendPrivate::createContext()
// This is the first call to InitializeSecurityContext, so theoretically "CONTINUE_NEEDED"
// should be the only non-error return-code here.
if (status != SEC_I_CONTINUE_NEEDED) {
setErrorAndEmit(QAbstractSocket::SslInternalError,
QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status)));
d->setErrorAndEmit(QAbstractSocket::SslInternalError,
QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status)));
return false;
}
@ -962,11 +1029,15 @@ bool QSslSocketBackendPrivate::createContext()
return true;
}
bool QSslSocketBackendPrivate::acceptContext()
bool TlsCryptographSchannel::acceptContext()
{
Q_ASSERT(d);
auto &configuration = d->privateConfiguration();
auto *plainSocket = d->plainTcpSocket();
Q_ASSERT(SecIsValidHandle(&credentialHandle));
Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
Q_ASSERT(mode == QSslSocket::SslServerMode);
Q_ASSERT(d->tlsMode() == QSslSocket::SslServerMode);
ULONG contextReq = getContextRequirements();
if (missingData > plainSocket->bytesAvailable())
@ -1043,8 +1114,8 @@ bool QSslSocketBackendPrivate::acceptContext()
}
if (status != SEC_I_CONTINUE_NEEDED) {
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status)));
d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status)));
return false;
}
if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
@ -1053,11 +1124,15 @@ bool QSslSocketBackendPrivate::acceptContext()
return true;
}
bool QSslSocketBackendPrivate::performHandshake()
bool TlsCryptographSchannel::performHandshake()
{
Q_ASSERT(d);
auto *plainSocket = d->plainTcpSocket();
Q_ASSERT(plainSocket);
if (plainSocket->state() == QAbstractSocket::UnconnectedState) {
setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
QSslSocket::tr("The TLS/SSL connection has been closed"));
d->setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
QSslSocket::tr("The TLS/SSL connection has been closed"));
return false;
}
Q_ASSERT(SecIsValidHandle(&credentialHandle));
@ -1157,8 +1232,8 @@ bool QSslSocketBackendPrivate::performHandshake()
case SEC_I_INCOMPLETE_CREDENTIALS:
// Schannel takes care of picking certificate to send (other than the one we can specify),
// so if we get here then that means we don't have a certificate the server accepts.
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QSslSocket::tr("Server did not accept any certificate we could present."));
d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QSslSocket::tr("Server did not accept any certificate we could present."));
return false;
case SEC_I_CONTEXT_EXPIRED:
// "The message sender has finished using the connection and has initiated a shutdown."
@ -1167,8 +1242,8 @@ bool QSslSocketBackendPrivate::performHandshake()
return false;
}
if (!shutdown) { // we did not initiate this
setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
QSslSocket::tr("The TLS/SSL connection has been closed"));
d->setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
QSslSocket::tr("The TLS/SSL connection has been closed"));
}
return true;
case SEC_E_INCOMPLETE_MESSAGE:
@ -1176,8 +1251,8 @@ bool QSslSocketBackendPrivate::performHandshake()
missingData = checkIncompleteData(outBuffers[0]);
return true;
case SEC_E_ALGORITHM_MISMATCH:
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QSslSocket::tr("Algorithm mismatch"));
d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QSslSocket::tr("Algorithm mismatch"));
shutdown = true; // skip sending the "Shutdown" alert
return false;
}
@ -1185,20 +1260,23 @@ bool QSslSocketBackendPrivate::performHandshake()
// Note: We can get here if the connection is using TLS 1.2 and the server certificate uses
// MD5, which is not allowed in Schannel. This causes an "invalid token" error during handshake.
// (If you came here investigating an error: md5 is insecure, update your certificate)
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QSslSocket::tr("Handshake failed: %1").arg(schannelErrorToString(status)));
d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QSslSocket::tr("Handshake failed: %1").arg(schannelErrorToString(status)));
return false;
}
bool QSslSocketBackendPrivate::verifyHandshake()
bool TlsCryptographSchannel::verifyHandshake()
{
Q_Q(QSslSocket);
Q_ASSERT(d);
Q_ASSERT(q);
auto &configuration = d->privateConfiguration();
sslErrors.clear();
const bool isClient = mode == QSslSocket::SslClientMode;
const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
#define CHECK_STATUS(status) \
if (status != SEC_E_OK) { \
setErrorAndEmit(QAbstractSocket::SslInternalError, \
d->setErrorAndEmit(QAbstractSocket::SslInternalError, \
QSslSocket::tr("Failed to query the TLS context: %1") \
.arg(schannelErrorToString(status))); \
return false; \
@ -1207,8 +1285,8 @@ bool QSslSocketBackendPrivate::verifyHandshake()
// Everything is set up, now make sure there's nothing wrong and query some attributes...
if (!matchesContextRequirements(contextAttributes, getContextRequirements(),
configuration.peerVerifyMode, isClient)) {
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QSslSocket::tr("Did not get the required attributes for the connection."));
d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QSslSocket::tr("Did not get the required attributes for the connection."));
return false;
}
@ -1235,7 +1313,7 @@ bool QSslSocketBackendPrivate::verifyHandshake()
QByteArray negotiatedProto = QByteArray((const char *)alpn.ProtocolId,
alpn.ProtocolIdSize);
if (!configuration.nextAllowedProtocols.contains(negotiatedProto)) {
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QSslSocket::tr("Unwanted protocol was negotiated"));
return false;
}
@ -1285,20 +1363,22 @@ bool QSslSocketBackendPrivate::verifyHandshake()
if (certificateContext && !verifyCertContext(certificateContext))
return false;
if (!checkSslErrors() || state != QAbstractSocket::ConnectedState) {
if (!checkSslErrors() || q->state() != QAbstractSocket::ConnectedState) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << __func__ << "was unsuccessful. Paused:" << paused;
#endif
// If we're paused then checkSslErrors returned false, but it's not an error
return paused && state == QAbstractSocket::ConnectedState;
return d->isPaused() && q->state() == QAbstractSocket::ConnectedState;
}
schannelState = SchannelState::Done;
return true;
}
bool QSslSocketBackendPrivate::renegotiate()
bool TlsCryptographSchannel::renegotiate()
{
Q_ASSERT(d);
SecBuffer outBuffers[3];
outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
@ -1318,7 +1398,7 @@ bool QSslSocketBackendPrivate::renegotiate()
ULONG contextReq = getContextRequirements();
TimeStamp expiry;
SECURITY_STATUS status;
if (mode == QSslSocket::SslClientMode) {
if (d->tlsMode() == QSslSocket::SslClientMode) {
status = InitializeSecurityContext(&credentialHandle, // phCredential
&contextHandle, // phContext
const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
@ -1352,7 +1432,7 @@ bool QSslSocketBackendPrivate::renegotiate()
schannelState = SchannelState::PerformHandshake;
return true;
}
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QSslSocket::tr("Renegotiation was unsuccessful: %1").arg(schannelErrorToString(status)));
return false;
}
@ -1361,8 +1441,10 @@ bool QSslSocketBackendPrivate::renegotiate()
\internal
reset the state in preparation for reuse of socket
*/
void QSslSocketBackendPrivate::reset()
void TlsCryptographSchannel::reset()
{
Q_ASSERT(d);
closeCertificateStores(); // certificate stores could've changed
deallocateContext();
freeCredentialsHandle(); // in case we already had one (@future: session resumption requires re-use)
@ -1377,34 +1459,42 @@ void QSslSocketBackendPrivate::reset()
intermediateBuffer.clear();
schannelState = SchannelState::InitializeHandshake;
connectionEncrypted = false;
d->setEncrypted(false);
shutdown = false;
renegotiating = false;
missingData = 0;
}
void QSslSocketBackendPrivate::startClientEncryption()
void TlsCryptographSchannel::startClientEncryption()
{
if (connectionEncrypted)
Q_ASSERT(q);
if (q->isEncrypted())
return; // let's not mess up the connection...
reset();
continueHandshake();
}
void QSslSocketBackendPrivate::startServerEncryption()
void TlsCryptographSchannel::startServerEncryption()
{
if (connectionEncrypted)
Q_ASSERT(q);
if (q->isEncrypted())
return; // let's not mess up the connection...
reset();
continueHandshake();
}
void QSslSocketBackendPrivate::transmit()
void TlsCryptographSchannel::transmit()
{
Q_Q(QSslSocket);
Q_ASSERT(q);
Q_ASSERT(d);
auto *plainSocket = d->plainTcpSocket();
Q_ASSERT(plainSocket);
if (mode == QSslSocket::UnencryptedMode)
if (d->tlsMode() == QSslSocket::UnencryptedMode)
return; // This function should not have been called
// Can happen if called through QSslSocket::abort->QSslSocket::close->QSslSocket::flush->here
@ -1416,7 +1506,9 @@ void QSslSocketBackendPrivate::transmit()
return;
}
if (connectionEncrypted) { // encrypt data in writeBuffer and write it to plainSocket
auto &writeBuffer = d->tlsWriteBuffer();
auto &buffer = d->tlsBuffer();
if (q->isEncrypted()) { // encrypt data in writeBuffer and write it to plainSocket
qint64 totalBytesWritten = 0;
qint64 writeBufferSize;
while ((writeBufferSize = writeBuffer.size()) > 0) {
@ -1444,7 +1536,7 @@ void QSslSocketBackendPrivate::transmit()
};
auto status = EncryptMessage(&contextHandle, 0, &message, 0);
if (status != SEC_E_OK) {
setErrorAndEmit(QAbstractSocket::SslInternalError,
d->setErrorAndEmit(QAbstractSocket::SslInternalError,
QSslSocket::tr("Schannel failed to encrypt data: %1")
.arg(schannelErrorToString(status)));
return;
@ -1461,13 +1553,14 @@ void QSslSocketBackendPrivate::transmit()
if (bytesWritten >= 0) {
totalBytesWritten += bytesWritten;
} else {
setErrorAndEmit(plainSocket->error(), plainSocket->errorString());
d->setErrorAndEmit(plainSocket->error(), plainSocket->errorString());
return;
}
}
if (totalBytesWritten > 0) {
// Don't emit bytesWritten() recursively.
bool &emittedBytesWritten = d->tlsEmittedBytesWritten();
if (!emittedBytesWritten) {
emittedBytesWritten = true;
emit q->bytesWritten(totalBytesWritten);
@ -1477,9 +1570,10 @@ void QSslSocketBackendPrivate::transmit()
}
}
if (connectionEncrypted) { // Decrypt data from remote
if (q->isEncrypted()) { // Decrypt data from remote
int totalRead = 0;
bool hadIncompleteData = false;
const auto readBufferMaxSize = d->maxReadBufferSize();
while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) {
if (missingData > plainSocket->bytesAvailable()
&& (!readBufferMaxSize || readBufferMaxSize >= missingData)) {
@ -1560,7 +1654,7 @@ void QSslSocketBackendPrivate::transmit()
// The message has been altered, disconnect now.
shutdown = true; // skips sending the shutdown alert
disconnectFromHost();
setErrorAndEmit(QAbstractSocket::SslInternalError,
d->setErrorAndEmit(QAbstractSocket::SslInternalError,
schannelErrorToString(status));
break;
} else if (status == SEC_E_OUT_OF_SEQUENCE) {
@ -1569,13 +1663,13 @@ void QSslSocketBackendPrivate::transmit()
// while SEC_E_MESSAGE_ALTERED is for stream-oriented ones (what we use).
shutdown = true; // skips sending the shutdown alert
disconnectFromHost();
setErrorAndEmit(QAbstractSocket::SslInternalError,
d->setErrorAndEmit(QAbstractSocket::SslInternalError,
schannelErrorToString(status));
break;
} else if (status == SEC_I_CONTEXT_EXPIRED) {
// 'remote' has initiated a shutdown
disconnectFromHost();
setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
d->setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
schannelErrorToString(status));
break;
} else if (status == SEC_I_RENEGOTIATE) {
@ -1593,7 +1687,7 @@ void QSslSocketBackendPrivate::transmit()
}
if (totalRead) {
if (readyReadEmittedPointer)
if (bool *readyReadEmittedPointer = d->readyReadPointer())
*readyReadEmittedPointer = true;
emit q->readyRead();
emit q->channelReadyRead(0);
@ -1601,9 +1695,11 @@ void QSslSocketBackendPrivate::transmit()
}
}
void QSslSocketBackendPrivate::sendShutdown()
void TlsCryptographSchannel::sendShutdown()
{
const bool isClient = mode == QSslSocket::SslClientMode;
Q_ASSERT(d);
const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
DWORD shutdownToken = SCHANNEL_SHUTDOWN;
SecBuffer buffer = createSecBuffer(&shutdownToken, sizeof(DWORD), SECBUFFER_TOKEN);
SecBufferDesc token{
@ -1678,18 +1774,23 @@ void QSslSocketBackendPrivate::sendShutdown()
}
}
void QSslSocketBackendPrivate::disconnectFromHost()
void TlsCryptographSchannel::disconnectFromHost()
{
Q_ASSERT(q);
Q_ASSERT(d);
auto *plainSocket = d->plainTcpSocket();
Q_ASSERT(plainSocket);
if (SecIsValidHandle(&contextHandle)) {
if (!shutdown) {
shutdown = true;
if (plainSocket->state() != QAbstractSocket::UnconnectedState) {
if (connectionEncrypted) {
if (q->isEncrypted()) {
// Read as much as possible because this is likely our last chance
qint64 tempMax = readBufferMaxSize;
readBufferMaxSize = 0;
qint64 tempMax = d->maxReadBufferSize();
d->setMaxReadBufferSize(0);
transmit();
readBufferMaxSize = tempMax;
d->setMaxReadBufferSize(tempMax);
sendShutdown();
}
}
@ -1699,32 +1800,40 @@ void QSslSocketBackendPrivate::disconnectFromHost()
plainSocket->disconnectFromHost();
}
void QSslSocketBackendPrivate::disconnected()
void TlsCryptographSchannel::disconnected()
{
Q_ASSERT(d);
shutdown = true;
connectionEncrypted = false;
d->setEncrypted(false);
deallocateContext();
freeCredentialsHandle();
}
QSslCipher QSslSocketBackendPrivate::sessionCipher() const
QSslCipher TlsCryptographSchannel::sessionCipher() const
{
if (!connectionEncrypted)
Q_ASSERT(q);
if (!q->isEncrypted())
return QSslCipher();
return QSslCipher(QStringLiteral("Schannel"), sessionProtocol());
}
QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
QSsl::SslProtocol TlsCryptographSchannel::sessionProtocol() const
{
if (!connectionEncrypted)
if (!q->isEncrypted())
return QSsl::SslProtocol::UnknownProtocol;
return toQtSslProtocol(connectionInfo.dwProtocol);
}
void QSslSocketBackendPrivate::continueHandshake()
void TlsCryptographSchannel::continueHandshake()
{
Q_Q(QSslSocket);
const bool isServer = mode == QSslSocket::SslServerMode;
Q_ASSERT(q);
Q_ASSERT(d);
auto *plainSocket = d->plainTcpSocket();
Q_ASSERT(plainSocket);
const bool isServer = d->tlsMode() == QSslSocket::SslServerMode;
switch (schannelState) {
case SchannelState::InitializeHandshake:
if (!SecIsValidHandle(&credentialHandle) && !acquireCredentialsHandle()) {
@ -1762,13 +1871,13 @@ void QSslSocketBackendPrivate::continueHandshake()
Q_FALLTHROUGH();
case SchannelState::Done:
// connectionEncrypted is already true if we come here from a renegotiation
if (!connectionEncrypted) {
connectionEncrypted = true; // all is done
if (!q->isEncrypted()) {
d->setEncrypted(true); // all is done
emit q->encrypted();
}
renegotiating = false;
if (pendingClose) {
pendingClose = false;
if (d->isPendingClose()) {
d->setPendingClose(false);
disconnectFromHost();
} else {
transmit();
@ -1785,75 +1894,37 @@ void QSslSocketBackendPrivate::continueHandshake()
}
}
QList<QSslCipher> QSslSocketBackendPrivate::defaultCiphers()
QList<QSslError> TlsCryptographSchannel::tlsErrors() const
{
QList<QSslCipher> ciphers;
// @temp (I hope), stolen from qsslsocket_winrt.cpp
const QString protocolStrings[] = { QStringLiteral("TLSv1"), QStringLiteral("TLSv1.1"),
QStringLiteral("TLSv1.2"), QStringLiteral("TLSv1.3") };
const QSsl::SslProtocol protocols[] = { QSsl::TlsV1_0, QSsl::TlsV1_1,
QSsl::TlsV1_2, QSsl::TlsV1_3 };
const int size = ARRAYSIZE(protocols);
static_assert(size == ARRAYSIZE(protocolStrings));
ciphers.reserve(size);
for (int i = 0; i < size; ++i) {
QSslCipher cipher;
cipher.d->isNull = false;
cipher.d->name = QStringLiteral("Schannel");
cipher.d->protocol = protocols[i];
cipher.d->protocolString = protocolStrings[i];
ciphers.append(cipher);
}
return ciphers;
}
QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain,
const QString &hostName)
{
Q_UNUSED(certificateChain);
Q_UNUSED(hostName);
Q_UNIMPLEMENTED();
return {}; // @future implement(?)
}
bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert,
QList<QSslCertificate> *caCertificates,
const QByteArray &passPhrase)
{
Q_UNUSED(device);
Q_UNUSED(key);
Q_UNUSED(cert);
Q_UNUSED(caCertificates);
Q_UNUSED(passPhrase);
// @future: can load into its own certificate store (encountered problems extracting key).
Q_UNIMPLEMENTED();
return false;
return sslErrors;
}
/*
Copied from qsslsocket_mac.cpp, which was copied from qsslsocket_openssl.cpp
*/
bool QSslSocketBackendPrivate::checkSslErrors()
bool TlsCryptographSchannel::checkSslErrors()
{
if (sslErrors.isEmpty())
return true;
Q_Q(QSslSocket);
Q_ASSERT(q);
Q_ASSERT(d);
const auto &configuration = d->privateConfiguration();
auto *plainSocket = d->plainTcpSocket();
emit q->sslErrors(sslErrors);
const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
|| (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
&& mode == QSslSocket::SslClientMode);
const bool doEmitSslError = !verifyErrorsHaveBeenIgnored();
&& d->tlsMode() == QSslSocket::SslClientMode);
const bool doEmitSslError = !d->verifyErrorsHaveBeenIgnored();
// check whether we need to emit an SSL handshake error
if (doVerifyPeer && doEmitSslError) {
if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) {
pauseSocketNotifiers(q);
paused = true;
QSslSocketPrivate::pauseSocketNotifiers(q);
d->setPaused(true);
} else {
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
d->setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
sslErrors.constFirst().errorString());
plainSocket->disconnectFromHost();
}
@ -1863,9 +1934,12 @@ bool QSslSocketBackendPrivate::checkSslErrors()
return true;
}
void QSslSocketBackendPrivate::initializeCertificateStores()
void TlsCryptographSchannel::initializeCertificateStores()
{
//// helper function which turns a chain into a certificate store
Q_ASSERT(d);
const auto &configuration = d->privateConfiguration();
auto createStoreFromCertificateChain = [](const QList<QSslCertificate> certChain, const QSslKey &privateKey) {
const wchar_t *passphrase = L"";
// Need to embed the private key in the certificate
@ -1880,7 +1954,7 @@ void QSslSocketBackendPrivate::initializeCertificateStores()
if (!configuration.localCertificateChain.isEmpty()) {
if (configuration.privateKey.isNull()) {
setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
d->setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
QSslSocket::tr("Cannot provide a certificate with no key"));
return;
}
@ -1898,12 +1972,14 @@ void QSslSocketBackendPrivate::initializeCertificateStores()
}
}
bool QSslSocketBackendPrivate::verifyCertContext(CERT_CONTEXT *certContext)
bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext)
{
Q_ASSERT(certContext);
Q_Q(QSslSocket);
Q_ASSERT(q);
Q_ASSERT(d);
auto &configuration = d->privateConfiguration();
const bool isClient = mode == QSslSocket::SslClientMode;
const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
// Create a collection of stores so we can pass in multiple stores as additional locations to
// search for the certificate chain
@ -2187,14 +2263,15 @@ bool QSslSocketBackendPrivate::verifyCertContext(CERT_CONTEXT *certContext)
// @Note: Somewhat copied from qsslsocket_mac.cpp
const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
|| (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
&& mode == QSslSocket::SslClientMode);
&& d->tlsMode() == QSslSocket::SslClientMode);
// Check the peer certificate itself. First try the subject's common name
// (CN) as a wildcard, then try all alternate subject name DNS entries the
// same way.
if (!configuration.peerCertificate.isNull()) {
// but only if we're a client connecting to a server
// if we're the server, don't check CN
if (mode == QSslSocket::SslClientMode) {
if (d->tlsMode() == QSslSocket::SslClientMode) {
const auto verificationPeerName = d->verificationName();
const QString peerName(verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName);
if (!isMatchingHostname(configuration.peerCertificate, peerName)) {
// No matches in common names or alternate names.
@ -2218,17 +2295,20 @@ bool QSslSocketBackendPrivate::verifyCertContext(CERT_CONTEXT *certContext)
return true;
}
bool QSslSocketBackendPrivate::rootCertOnDemandLoadingAllowed()
bool TlsCryptographSchannel::rootCertOnDemandLoadingAllowed()
{
return allowRootCertOnDemandLoading && s_loadRootCertsOnDemand;
Q_ASSERT(d);
return d->isRootsOnDemandAllowed() && QSslSocketPrivate::rootCertOnDemandLoadingSupported();
}
} // namespace QTlsPrivate
void QSslSocketPrivate::registerAdHocFactory()
{
// TLSTODO: this is a temporary solution, waiting for
// backends to move to ... plugins.
if (!backend())
qCWarning(lcSsl, "Failed to create backend factory");
if (!backendSchannel())
qCWarning(lcTlsBackend, "Failed to create backend factory");
}
QT_END_NAMESPACE

View File

@ -37,8 +37,8 @@
**
****************************************************************************/
#ifndef QSSLSOCKET_SCHANNEL_P_H
#define QSSLSOCKET_SCHANNEL_P_H
#ifndef QTLS_SCHANNEL_P_H
#define QTLS_SCHANNEL_P_H
//
// W A R N I N G
@ -51,12 +51,17 @@
// We mean it.
//
QT_REQUIRE_CONFIG(schannel);
#include <QtNetwork/private/qtnetworkglobal_p.h>
QT_REQUIRE_CONFIG(schannel);
#include <QtCore/qt_windows.h>
#include "qtlsbackend_schannel_p.h"
#include "qsslsocket_p.h"
#include "qwincrypt_p.h"
#define SECURITY_WIN32
#define SCHANNEL_USE_BLACKLISTS 1
#include <Winternl.h> // needed for UNICODE defines
@ -67,15 +72,17 @@ QT_REQUIRE_CONFIG(schannel);
QT_BEGIN_NAMESPACE
class QSslSocketBackendPrivate final : public QSslSocketPrivate
{
Q_DISABLE_COPY_MOVE(QSslSocketBackendPrivate)
Q_DECLARE_PUBLIC(QSslSocket)
public:
QSslSocketBackendPrivate();
~QSslSocketBackendPrivate();
namespace QTlsPrivate {
class TlsCryptographSchannel final : public TlsCryptograph
{
Q_DISABLE_COPY_MOVE(TlsCryptographSchannel)
public:
TlsCryptographSchannel();
~TlsCryptographSchannel();
void init(QSslSocket *q, QSslSocketPrivate *d) override;
// Platform specific functions
void startClientEncryption() override;
void startServerEncryption() override;
void transmit() override;
@ -84,12 +91,7 @@ public:
QSslCipher sessionCipher() const override;
QSsl::SslProtocol sessionProtocol() const override;
void continueHandshake() override;
static QList<QSslCipher> defaultCiphers();
static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain,
const QString &hostName);
static bool importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert,
QList<QSslCertificate> *caCertificates, const QByteArray &passPhrase);
QList<QSslError> tlsErrors() const override;
private:
enum class SchannelState {
@ -123,7 +125,10 @@ private:
bool rootCertOnDemandLoadingAllowed();
bool hasUndecryptedData() override { return intermediateBuffer.size() > 0; }
bool hasUndecryptedData() const override { return intermediateBuffer.size() > 0; }
QSslSocket *q = nullptr;
QSslSocketPrivate *d = nullptr;
SecPkgContext_ConnectionInfo connectionInfo = {};
SecPkgContext_StreamSizes streamSizes = {};
@ -143,8 +148,12 @@ private:
qint64 missingData = 0;
bool renegotiating = false;
bool shutdown = false;
QList<QSslError> sslErrors;
};
} // namespace QTlsPrivate
QT_END_NAMESPACE
#endif // QSSLSOCKET_SCHANNEL_P_H
#endif // QTLS_SCHANNEL_P_H

View File

@ -37,8 +37,8 @@
**
****************************************************************************/
#ifndef QSSLSOCKET_MAC_P_H
#define QSSLSOCKET_MAC_P_H
#ifndef QTLS_ST_P_H
#define QTLS_ST_P_H
//
// W A R N I N G
@ -52,6 +52,10 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qtlsbackend_st_p.h"
#include <QtCore/qobject.h>
#include <QtCore/qstring.h>
#include <QtCore/qglobal.h>
#include <QtCore/qlist.h>
@ -64,6 +68,8 @@
QT_BEGIN_NAMESPACE
namespace QTlsPrivate {
class QSecureTransportContext
{
public:
@ -78,14 +84,13 @@ private:
Q_DISABLE_COPY_MOVE(QSecureTransportContext)
};
class QSslSocketBackendPrivate : public QSslSocketPrivate
class TlsCryptographSecureTransport : public TlsCryptograph
{
Q_DECLARE_PUBLIC(QSslSocket)
public:
QSslSocketBackendPrivate();
virtual ~QSslSocketBackendPrivate();
TlsCryptographSecureTransport();
~TlsCryptographSecureTransport() override;
// Final-overriders (QSslSocketPrivate):
void init(QSslSocket *qObj, QSslSocketPrivate *dObj) override;
void continueHandshake() override;
void disconnected() override;
void disconnectFromHost() override;
@ -94,17 +99,9 @@ public:
void startClientEncryption() override;
void startServerEncryption() override;
void transmit() override;
QList<QSslError> tlsErrors() const override;
static QList<QSslError> verify(QList<QSslCertificate> certificateChain,
const QString &hostName);
static bool importPkcs12(QIODevice *device,
QSslKey *key, QSslCertificate *cert,
QList<QSslCertificate> *caCertificates,
const QByteArray &passPhrase);
static QSslCipher QSslCipher_from_SSLCipherSuite(SSLCipherSuite cipher);
static SSLCipherSuite SSLCipherSuite_from_QSslCipher(const QSslCipher &cipher);
SSLCipherSuite SSLCipherSuite_from_QSslCipher(const QSslCipher &ciph);
private:
// SSL context management/properties:
@ -121,18 +118,24 @@ private:
bool checkSslErrors();
bool startHandshake();
bool isHandshakeComplete() const {return connectionEncrypted && !renegotiating;}
bool isHandshakeComplete() const;
// IO callbacks:
static OSStatus ReadCallback(QSslSocketBackendPrivate *socket, char *data, size_t *dataLength);
static OSStatus WriteCallback(QSslSocketBackendPrivate *plainSocket, const char *data, size_t *dataLength);
static OSStatus ReadCallback(TlsCryptographSecureTransport *socket, char *data, size_t *dataLength);
static OSStatus WriteCallback(TlsCryptographSecureTransport *plainSocket, const char *data, size_t *dataLength);
QSecureTransportContext context;
bool renegotiating = false;
QSslSocket *q = nullptr;
QSslSocketPrivate *d = nullptr;
bool shutdown = false;
QList<QSslError> sslErrors;
Q_DISABLE_COPY_MOVE(QSslSocketBackendPrivate)
Q_DISABLE_COPY_MOVE(TlsCryptographSecureTransport)
};
} // namespace QTlsPrivate
QT_END_NAMESPACE
#endif
#endif // QTLS_ST_P_H

View File

@ -40,7 +40,10 @@
#include "qtlsbackend_p.h"
#if QT_CONFIG(ssl)
#include "qsslpresharedkeyauthenticator_p.h"
#include "qsslpresharedkeyauthenticator.h"
#include "qsslsocket_p.h"
#include "qsslcipher_p.h"
#include "qsslkey_p.h"
#include "qsslkey.h"
#else
@ -199,6 +202,50 @@ TlsKey *X509Certificate::publicKey() const
return nullptr;
}
#if QT_CONFIG(ssl)
TlsCryptograph::~TlsCryptograph() = default;
void TlsCryptograph::checkSettingSslContext(QSharedPointer<QSslContext> tlsContext)
{
Q_UNUSED(tlsContext);
}
QSharedPointer<QSslContext> TlsCryptograph::sslContext() const
{
return {};
}
void TlsCryptograph::enableHandshakeContinuation()
{
}
void TlsCryptograph::cancelCAFetch()
{
}
bool TlsCryptograph::hasUndecryptedData() const
{
return false;
}
QList<QOcspResponse> TlsCryptograph::ocsps() const
{
return {};
}
bool TlsCryptograph::isMatchingHostname(const QSslCertificate &cert, const QString &peerName)
{
return QSslSocketPrivate::isMatchingHostname(cert, peerName);
}
bool TlsCryptograph::isMatchingHostname(const QString &cn, const QString &hostname)
{
return QSslSocketPrivate::isMatchingHostname(cn, hostname);
}
#endif // QT_CONFIG(ssl)
#if QT_CONFIG(dtls)
DtlsBase::~DtlsBase() = default;
#endif // QT_CONFIG(dtls)
@ -228,6 +275,30 @@ bool QTlsBackend::isValid() const
return true;
}
long QTlsBackend::tlsLibraryVersionNumber() const
{
return 0;
}
QString QTlsBackend::tlsLibraryVersionString() const
{
return {};
}
long QTlsBackend::tlsLibraryBuildVersionNumber() const
{
return 0;
}
QString QTlsBackend::tlsLibraryBuildVersionString() const
{
return {};
}
void QTlsBackend::ensureInitialized() const
{
}
QString QTlsBackend::backendName() const
{
return QStringLiteral("dummyTLS");
@ -248,6 +319,12 @@ QTlsPrivate::X509Certificate *QTlsBackend::createCertificate() const
return nullptr;
}
QList<QSslCertificate> QTlsBackend::systemCaCertificates() const
{
REPORT_MISSING_SUPPORT("does not provide system CA certificates");
return {};
}
QTlsPrivate::TlsCryptograph *QTlsBackend::createTlsCryptograph() const
{
REPORT_MISSING_SUPPORT("does not support QSslSocket");
@ -441,4 +518,189 @@ void QTlsBackend::resetBackend(QSslKey &key, QTlsPrivate::TlsKey *keyBackend)
#endif // QT_CONFIG(ssl)
}
void QTlsBackend::setupClientPskAuth(QSslPreSharedKeyAuthenticator *auth, const char *hint,
int hintLength, unsigned maxIdentityLen, unsigned maxPskLen)
{
Q_ASSERT(auth);
#if QT_CONFIG(ssl)
if (hint)
auth->d->identityHint = QByteArray::fromRawData(hint, hintLength); // it's NUL terminated, but do not include the NUL
auth->d->maximumIdentityLength = int(maxIdentityLen) - 1; // needs to be NUL terminated
auth->d->maximumPreSharedKeyLength = int(maxPskLen);
#else
Q_UNUSED(auth);
Q_UNUSED(hint);
Q_UNUSED(hintLength);
Q_UNUSED(maxIdentityLen);
Q_UNUSED(maxPskLen);
#endif
}
void QTlsBackend::setupServerPskAuth(QSslPreSharedKeyAuthenticator *auth, const char *identity,
const QByteArray &identityHint, unsigned int maxPskLen)
{
#if QT_CONFIG(ssl)
Q_ASSERT(auth);
auth->d->identityHint = identityHint;
auth->d->identity = identity;
auth->d->maximumIdentityLength = 0; // user cannot set an identity
auth->d->maximumPreSharedKeyLength = int(maxPskLen);
#else
Q_UNUSED(auth);
Q_UNUSED(identity);
Q_UNUSED(identityHint);
Q_UNUSED(maxPskLen);
#endif
}
#if QT_CONFIG(ssl)
QSslCipher QTlsBackend::createCiphersuite(const QString &descriptionOneLine, int bits, int supportedBits)
{
QSslCipher ciph;
const auto descriptionList = QStringView{descriptionOneLine}.split(QLatin1Char(' '), Qt::SkipEmptyParts);
if (descriptionList.size() > 5) {
ciph.d->isNull = false;
ciph.d->name = descriptionList.at(0).toString();
QString protoString = descriptionList.at(1).toString();
ciph.d->protocolString = protoString;
ciph.d->protocol = QSsl::UnknownProtocol;
if (protoString == QLatin1String("TLSv1"))
ciph.d->protocol = QSsl::TlsV1_0;
else if (protoString == QLatin1String("TLSv1.1"))
ciph.d->protocol = QSsl::TlsV1_1;
else if (protoString == QLatin1String("TLSv1.2"))
ciph.d->protocol = QSsl::TlsV1_2;
else if (protoString == QLatin1String("TLSv1.3"))
ciph.d->protocol = QSsl::TlsV1_3;
if (descriptionList.at(2).startsWith(QLatin1String("Kx=")))
ciph.d->keyExchangeMethod = descriptionList.at(2).mid(3).toString();
if (descriptionList.at(3).startsWith(QLatin1String("Au=")))
ciph.d->authenticationMethod = descriptionList.at(3).mid(3).toString();
if (descriptionList.at(4).startsWith(QLatin1String("Enc=")))
ciph.d->encryptionMethod = descriptionList.at(4).mid(4).toString();
ciph.d->exportable = (descriptionList.size() > 6 && descriptionList.at(6) == QLatin1String("export"));
ciph.d->bits = bits;
ciph.d->supportedBits = supportedBits;
}
return ciph;
}
QSslCipher QTlsBackend::createCiphersuite(const QString &suiteName, QSsl::SslProtocol protocol,
const QString &protocolString)
{
QSslCipher ciph;
if (!suiteName.size())
return ciph;
ciph.d->isNull = false;
ciph.d->name = suiteName;
ciph.d->protocol = protocol;
ciph.d->protocolString = protocolString;
const auto bits = QStringView{ciph.d->name}.split(QLatin1Char('-'));
if (bits.size() >= 2) {
if (bits.size() == 2 || bits.size() == 3)
ciph.d->keyExchangeMethod = QLatin1String("RSA");
else if (bits.front() == QLatin1String("DH") || bits.front() == QLatin1String("DHE"))
ciph.d->keyExchangeMethod = QLatin1String("DH");
else if (bits.front() == QLatin1String("ECDH") || bits.front() == QLatin1String("ECDHE"))
ciph.d->keyExchangeMethod = QLatin1String("ECDH");
else
qCWarning(lcSsl) << "Unknown Kx" << ciph.d->name;
if (bits.size() == 2 || bits.size() == 3)
ciph.d->authenticationMethod = QLatin1String("RSA");
else if (ciph.d->name.contains(QLatin1String("-ECDSA-")))
ciph.d->authenticationMethod = QLatin1String("ECDSA");
else if (ciph.d->name.contains(QLatin1String("-RSA-")))
ciph.d->authenticationMethod = QLatin1String("RSA");
else
qCWarning(lcSsl) << "Unknown Au" << ciph.d->name;
if (ciph.d->name.contains(QLatin1String("RC4-"))) {
ciph.d->encryptionMethod = QLatin1String("RC4(128)");
ciph.d->bits = 128;
ciph.d->supportedBits = 128;
} else if (ciph.d->name.contains(QLatin1String("DES-CBC3-"))) {
ciph.d->encryptionMethod = QLatin1String("3DES(168)");
ciph.d->bits = 168;
ciph.d->supportedBits = 168;
} else if (ciph.d->name.contains(QLatin1String("AES128-"))) {
ciph.d->encryptionMethod = QLatin1String("AES(128)");
ciph.d->bits = 128;
ciph.d->supportedBits = 128;
} else if (ciph.d->name.contains(QLatin1String("AES256-GCM"))) {
ciph.d->encryptionMethod = QLatin1String("AESGCM(256)");
ciph.d->bits = 256;
ciph.d->supportedBits = 256;
} else if (ciph.d->name.contains(QLatin1String("AES256-"))) {
ciph.d->encryptionMethod = QLatin1String("AES(256)");
ciph.d->bits = 256;
ciph.d->supportedBits = 256;
} else if (ciph.d->name.contains(QLatin1String("NULL-"))) {
ciph.d->encryptionMethod = QLatin1String("NULL");
} else {
qCWarning(lcSsl) << "Unknown Enc" << ciph.d->name;
}
}
return ciph;
}
QSslCipher QTlsBackend::createCipher(const QString &name, QSsl::SslProtocol protocol,
const QString &protocolString)
{
// Note the name 'createCipher' (not 'ciphersuite'): we don't provide
// information about Kx, Au, bits/supported etc.
QSslCipher cipher;
cipher.d->isNull = false;
cipher.d->name = name;
cipher.d->protocol = protocol;
cipher.d->protocolString = protocolString;
return cipher;
}
QList<QSslCipher> QTlsBackend::defaultCiphers()
{
return QSslSocketPrivate::defaultCiphers();
}
QList<QSslCipher> QTlsBackend::defaultDtlsCiphers()
{
return QSslSocketPrivate::defaultDtlsCiphers();
}
void QTlsBackend::setDefaultCiphers(const QList<QSslCipher> &ciphers)
{
QSslSocketPrivate::setDefaultCiphers(ciphers);
}
void QTlsBackend::setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers)
{
QSslSocketPrivate::setDefaultDtlsCiphers(ciphers);
}
void QTlsBackend::setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers)
{
QSslSocketPrivate::setDefaultSupportedCiphers(ciphers);
}
void QTlsBackend::resetDefaultEllipticCurves()
{
QSslSocketPrivate::resetDefaultEllipticCurves();
}
void QTlsBackend::setDefaultCaCertificates(const QList<QSslCertificate> &certs)
{
QSslSocketPrivate::setDefaultCaCertificates(certs);
}
#endif // QT_CONFIG(ssl)
QT_END_NAMESPACE

View File

@ -40,20 +40,25 @@
#include "qtlsbackend_openssl_p.h"
#include "qtlskey_openssl_p.h"
#include "qx509_openssl_p.h"
#include "qtls_openssl_p.h"
#include "qsslcipher_p.h"
//#include "qsslsocket_p.h"
#include "qsslcipher.h"
#if QT_CONFIG(dtls)
#include "qdtls_openssl_p.h"
#endif // QT_CONFIG(dtls)
// TLSTODO: Later, this code (ensure initialised, etc.)
// must move from the socket to backend.
#include "qsslsocket_p.h"
//
#include "qsslsocket_openssl_symbols_p.h"
#include "qopenssl_p.h"
#include <qssl.h>
#include <qdir.h>
#include <qdiriterator.h>
#include <qlist.h>
#include <qmutex.h>
#include <qscopeguard.h>
#include <algorithm>
@ -61,6 +66,36 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.ossl");
Q_GLOBAL_STATIC(QRecursiveMutex, qt_opensslInitMutex)
static void q_loadCiphersForConnection(SSL *connection, QList<QSslCipher> &ciphers,
QList<QSslCipher> &defaultCiphers)
{
Q_ASSERT(connection);
STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(connection);
for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) {
if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) {
const auto ciph = QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(cipher);
if (!ciph.isNull()) {
// Unconditionally exclude ADH and AECDH ciphers since they offer no MITM protection
if (!ciph.name().toLower().startsWith(QLatin1String("adh")) &&
!ciph.name().toLower().startsWith(QLatin1String("exp-adh")) &&
!ciph.name().toLower().startsWith(QLatin1String("aecdh"))) {
ciphers << ciph;
if (ciph.usedBits() >= 128)
defaultCiphers << ciph;
}
}
}
}
}
bool QTlsBackendOpenSSL::s_libraryLoaded = false;
bool QTlsBackendOpenSSL::s_loadedCiphersAndCerts = false;
int QTlsBackendOpenSSL::s_indexForSSLExtraData = -1;
QString QTlsBackendOpenSSL::getErrorsFromOpenSsl()
{
QString errorString;
@ -88,6 +123,41 @@ void QTlsBackendOpenSSL::clearErrorQueue()
Q_UNUSED(errs);
}
bool QTlsBackendOpenSSL::ensureLibraryLoaded()
{
if (!q_resolveOpenSslSymbols())
return false;
const QMutexLocker locker(qt_opensslInitMutex());
if (!s_libraryLoaded) {
// Initialize OpenSSL.
if (q_OPENSSL_init_ssl(0, nullptr) != 1)
return false;
if (q_OpenSSL_version_num() < 0x10101000L) {
qCWarning(lcTlsBackend, "QSslSocket: OpenSSL >= 1.1.1 is required; %s was found instead", q_OpenSSL_version(OPENSSL_VERSION));
return false;
}
q_SSL_load_error_strings();
q_OpenSSL_add_all_algorithms();
s_indexForSSLExtraData = q_CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, 0L, nullptr, nullptr,
nullptr, nullptr);
// Initialize OpenSSL's random seed.
if (!q_RAND_status()) {
qWarning("Random number generator not seeded, disabling SSL support");
return false;
}
s_libraryLoaded = true;
}
return true;
}
QString QTlsBackendOpenSSL::backendName() const
{
return builtinBackendNames[nameIndexOpenSSL];
@ -95,9 +165,124 @@ QString QTlsBackendOpenSSL::backendName() const
bool QTlsBackendOpenSSL::isValid() const
{
// TLSTODO: backend should do initialization,
// not socket.
return QSslSocket::supportsSsl();
return ensureLibraryLoaded();
}
long QTlsBackendOpenSSL::tlsLibraryVersionNumber() const
{
return q_OpenSSL_version_num();
}
QString QTlsBackendOpenSSL::tlsLibraryVersionString() const
{
const char *versionString = q_OpenSSL_version(OPENSSL_VERSION);
if (!versionString)
return QString();
return QString::fromLatin1(versionString);
}
long QTlsBackendOpenSSL::tlsLibraryBuildVersionNumber() const
{
return OPENSSL_VERSION_NUMBER;
}
QString QTlsBackendOpenSSL::tlsLibraryBuildVersionString() const
{
// Using QStringLiteral to store the version string as unicode and
// avoid false positives from Google searching the playstore for old
// SSL versions. See QTBUG-46265
return QStringLiteral(OPENSSL_VERSION_TEXT);
}
void QTlsBackendOpenSSL::ensureInitialized() const
{
// Old qsslsocket_openssl calls supportsSsl() (which means
// library found and symbols resolved, this already assured
// by the fact we end up in this function (isValid() returned
// true for the backend, see its code). The qsslsocket_openssl
// proceedes with loading certificate, ciphers and elliptic
// curves.
ensureCiphersAndCertsLoaded();
}
void QTlsBackendOpenSSL::ensureCiphersAndCertsLoaded() const
{
const QMutexLocker locker(qt_opensslInitMutex());
if (s_loadedCiphersAndCerts)
return;
s_loadedCiphersAndCerts = true;
resetDefaultCiphers();
resetDefaultEllipticCurves();
#if QT_CONFIG(library)
//load symbols needed to receive certificates from system store
#if defined(Q_OS_QNX)
QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true);
#elif defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
// check whether we can enable on-demand root-cert loading (i.e. check whether the sym links are there)
QList<QByteArray> dirs = QSslSocketPrivate::unixRootCertDirectories();
QStringList symLinkFilter;
symLinkFilter << QLatin1String("[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9]");
for (int a = 0; a < dirs.count(); ++a) {
QDirIterator iterator(QLatin1String(dirs.at(a)), symLinkFilter, QDir::Files);
if (iterator.hasNext()) {
QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true);
break;
}
}
#endif
#endif // QT_CONFIG(library)
// if on-demand loading was not enabled, load the certs now
if (!QSslSocketPrivate::rootCertOnDemandLoadingSupported())
setDefaultCaCertificates(systemCaCertificates());
#ifdef Q_OS_WIN
//Enabled for fetching additional root certs from windows update on windows.
//This flag is set false by setDefaultCaCertificates() indicating the app uses
//its own cert bundle rather than the system one.
//Same logic that disables the unix on demand cert loading.
//Unlike unix, we do preload the certificates from the cert store.
QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true);
#endif
}
void QTlsBackendOpenSSL::resetDefaultCiphers()
{
SSL_CTX *myCtx = q_SSL_CTX_new(q_TLS_client_method());
// Note, we assert, not just silently return/bail out early:
// this should never happen and problems with OpenSSL's initialization
// must be caught before this (see supportsSsl()).
Q_ASSERT(myCtx);
SSL *mySsl = q_SSL_new(myCtx);
Q_ASSERT(mySsl);
QList<QSslCipher> ciphers;
QList<QSslCipher> defaultCiphers;
q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers);
q_SSL_CTX_free(myCtx);
q_SSL_free(mySsl);
setDefaultSupportedCiphers(ciphers);
setDefaultCiphers(defaultCiphers);
#if QT_CONFIG(dtls)
ciphers.clear();
defaultCiphers.clear();
myCtx = q_SSL_CTX_new(q_DTLS_client_method());
if (myCtx) {
mySsl = q_SSL_new(myCtx);
if (mySsl) {
q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers);
setDefaultDtlsCiphers(defaultCiphers);
q_SSL_free(mySsl);
}
q_SSL_CTX_free(myCtx);
}
#endif // dtls
}
QList<QSsl::SslProtocol> QTlsBackendOpenSSL::supportedProtocols() const
@ -167,6 +352,98 @@ QTlsPrivate::X509Certificate *QTlsBackendOpenSSL::createCertificate() const
return new QTlsPrivate::X509CertificateOpenSSL;
}
namespace QTlsPrivate {
// TLSTODO: remove.
#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
QList<QByteArray> fetchSslCertificateData();
#endif
QList<QSslCertificate> systemCaCertificates();
#ifndef Q_OS_DARWIN
QList<QSslCertificate> systemCaCertificates()
{
#ifdef QSSLSOCKET_DEBUG
QElapsedTimer timer;
timer.start();
#endif
QList<QSslCertificate> systemCerts;
#if defined(Q_OS_WIN)
HCERTSTORE hSystemStore;
hSystemStore = CertOpenSystemStoreW(0, L"ROOT");
if (hSystemStore) {
PCCERT_CONTEXT pc = nullptr;
while (1) {
pc = CertFindCertificateInStore(hSystemStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, pc);
if (!pc)
break;
QByteArray der(reinterpret_cast<const char *>(pc->pbCertEncoded),
static_cast<int>(pc->cbCertEncoded));
QSslCertificate cert(der, QSsl::Der);
systemCerts.append(cert);
}
CertCloseStore(hSystemStore, 0);
}
#elif defined(Q_OS_UNIX)
QSet<QString> certFiles;
QDir currentDir;
QStringList nameFilters;
QList<QByteArray> directories;
QSsl::EncodingFormat platformEncodingFormat;
# ifndef Q_OS_ANDROID
directories = QSslSocketPrivate::unixRootCertDirectories();
nameFilters << QLatin1String("*.pem") << QLatin1String("*.crt");
platformEncodingFormat = QSsl::Pem;
# else
// Q_OS_ANDROID
QByteArray ministroPath = qgetenv("MINISTRO_SSL_CERTS_PATH"); // Set by Ministro
directories << ministroPath;
nameFilters << QLatin1String("*.der");
platformEncodingFormat = QSsl::Der;
# ifndef Q_OS_ANDROID_EMBEDDED
if (ministroPath.isEmpty()) {
QList<QByteArray> certificateData = fetchSslCertificateData();
for (int i = 0; i < certificateData.size(); ++i) {
systemCerts.append(QSslCertificate::fromData(certificateData.at(i), QSsl::Der));
}
} else
# endif //Q_OS_ANDROID_EMBEDDED
# endif //Q_OS_ANDROID
{
currentDir.setNameFilters(nameFilters);
for (int a = 0; a < directories.count(); a++) {
currentDir.setPath(QLatin1String(directories.at(a)));
QDirIterator it(currentDir);
while (it.hasNext()) {
it.next();
// use canonical path here to not load the same certificate twice if symlinked
certFiles.insert(it.fileInfo().canonicalFilePath());
}
}
for (const QString& file : qAsConst(certFiles))
systemCerts.append(QSslCertificate::fromPath(file, platformEncodingFormat));
# ifndef Q_OS_ANDROID
systemCerts.append(QSslCertificate::fromPath(QLatin1String("/etc/pki/tls/certs/ca-bundle.crt"), QSsl::Pem)); // Fedora, Mandriva
systemCerts.append(QSslCertificate::fromPath(QLatin1String("/usr/local/share/certs/ca-root-nss.crt"), QSsl::Pem)); // FreeBSD's ca_root_nss
# endif
}
#endif
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcTlsBackend) << "systemCaCertificates retrieval time " << timer.elapsed() << "ms";
qCDebug(lcTlsBackend) << "imported " << systemCerts.count() << " certificates";
#endif
return systemCerts;
}
#endif // !Q_OS_DARWIN
} // namespace QTlsPrivate
QList<QSslCertificate> QTlsBackendOpenSSL::systemCaCertificates() const
{
return QTlsPrivate::systemCaCertificates();
}
QTlsPrivate::DtlsCookieVerifier *QTlsBackendOpenSSL::createDtlsCookieVerifier() const
{
#if QT_CONFIG(dtls)
@ -177,6 +454,11 @@ QTlsPrivate::DtlsCookieVerifier *QTlsBackendOpenSSL::createDtlsCookieVerifier()
#endif // QT_CONFIG(dtls)
}
QTlsPrivate::TlsCryptograph *QTlsBackendOpenSSL::createTlsCryptograph() const
{
return new QTlsPrivate::TlsCryptographOpenSSL;
}
QTlsPrivate::DtlsCryptograph *QTlsBackendOpenSSL::createDtlsCryptograph(QDtls *q, int mode) const
{
#if QT_CONFIG(dtls)
@ -233,10 +515,7 @@ QList<int> QTlsBackendOpenSSL::ellipticCurvesIds() const
if (name.isEmpty())
return nid;
// TLSTODO: check if it's needed! The fact we are here,
// means OpenSSL was loaded, symbols resolved. Is it because
// of ensureCiphers(AndCertificates)Loaded ?
QSslSocketPrivate::ensureInitialized();
ensureInitialized(); // TLSTODO: check if it's needed!
#ifndef OPENSSL_NO_EC
const QByteArray curveNameLatin1 = name.toLatin1();
nid = q_OBJ_sn2nid(curveNameLatin1.data());
@ -254,10 +533,7 @@ QList<int> QTlsBackendOpenSSL::ellipticCurvesIds() const
if (name.isEmpty())
return nid;
// TLSTODO: check if it's needed! The fact we are here,
// means OpenSSL was loaded, symbols resolved. Is it because
// of ensureCiphers(AndCertificates)Loaded ?
QSslSocketPrivate::ensureInitialized();
ensureInitialized();
#ifndef OPENSSL_NO_EC
const QByteArray curveNameLatin1 = name.toLatin1();
@ -336,4 +612,19 @@ bool QTlsBackendOpenSSL::isTlsNamedCurve(int id) const
return std::find(tlsNamedCurveNIDs, tlsNamedCurveNIDsEnd, id) != tlsNamedCurveNIDsEnd;
}
QString QTlsBackendOpenSSL::msgErrorsDuringHandshake()
{
return QSslSocket::tr("Error during SSL handshake: %1").arg(getErrorsFromOpenSsl());
}
QSslCipher QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(const SSL_CIPHER *cipher)
{
Q_ASSERT(cipher);
char buf [256] = {};
const QString desc = QString::fromLatin1(q_SSL_CIPHER_description(cipher, buf, sizeof(buf)));
int supportedBits = 0;
const int bits = q_SSL_CIPHER_get_bits(cipher, &supportedBits);
return createCiphersuite(desc, bits, supportedBits);
}
QT_END_NAMESPACE

View File

@ -54,22 +54,44 @@
#include <private/qtnetworkglobal_p.h>
#include "qssldiffiehellmanparameters.h"
#include "qsslcertificate.h"
#include "qtlsbackend_p.h"
#include <QtCore/qglobal.h>
#include <QtCore/qlist.h>
#include <openssl/ssl.h>
QT_BEGIN_NAMESPACE
class QTlsBackendOpenSSL final : public QTlsBackend
{
public:
static QString getErrorsFromOpenSsl();
static void logAndClearErrorQueue();
static void clearErrorQueue();
static bool ensureLibraryLoaded();
// Index used in SSL_get_ex_data to get the matching TlsCryptographerOpenSSL:
static bool s_libraryLoaded;
static bool s_loadedCiphersAndCerts;
static int s_indexForSSLExtraData;
static QString msgErrorsDuringHandshake();
static QSslCipher qt_OpenSSL_cipher_to_QSslCipher(const SSL_CIPHER *cipher);
private:
QString backendName() const override;
bool isValid() const override;
long tlsLibraryVersionNumber() const override;
QString tlsLibraryVersionString() const override;
long tlsLibraryBuildVersionNumber() const override;
QString tlsLibraryBuildVersionString() const override;
void ensureInitialized() const override;
void ensureCiphersAndCertsLoaded() const;
static void resetDefaultCiphers();
QList<QSsl::SslProtocol> supportedProtocols() const override;
QList<QSsl::SupportedFeature> supportedFeatures() const override;
@ -80,7 +102,9 @@ private:
// QSslCertificate:
QTlsPrivate::X509Certificate *createCertificate() const override;
QList<QSslCertificate> systemCaCertificates() const override;
QTlsPrivate::TlsCryptograph *createTlsCryptograph() const override;
QTlsPrivate::DtlsCookieVerifier *createDtlsCookieVerifier() const override;
QTlsPrivate::DtlsCryptograph *createDtlsCryptograph(QDtls *q, int mode) const override;

View File

@ -62,11 +62,12 @@
#endif
#include <QtNetwork/qsslcertificate.h>
#include <QtNetwork/qsslerror.h>
#include <QtNetwork/qsslcipher.h>
#include <QtNetwork/qsslkey.h>
#include <QtNetwork/qssl.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qsharedpointer.h>
#include <QtCore/qnamespace.h>
#include <QtCore/qobject.h>
#include <QtCore/qglobal.h>
@ -78,11 +79,17 @@
QT_BEGIN_NAMESPACE
class QSslPreSharedKeyAuthenticator;
class QSslSocketPrivate;
class QHostAddress;
class QSslContext;
class QSslSocket;
class QByteArray;
class QSslCipher;
class QUdpSocket;
class QIODevice;
class QSslError;
class QSslKey;
namespace QTlsPrivate {
@ -93,9 +100,8 @@ namespace QTlsPrivate {
// however strange they are, for now preserved to ease the transition
// (this may change in future - for example, 'decodeDer' is not just
// decoding DER, it's initializing a key from DER. Note, QSslKey requires
// a real TLS library because private keys tend to be encrypted. This
// base class does not need a working TLS library.
class TlsKey {
// a real TLS library because private keys tend to be encrypted.
class Q_NETWORK_PRIVATE_EXPORT TlsKey {
public:
virtual ~TlsKey();
@ -137,7 +143,7 @@ public:
// An abstraction hiding OpenSSL's X509 or our generic
// 'derData'-based code.
class X509Certificate
class Q_NETWORK_PRIVATE_EXPORT X509Certificate
{
public:
virtual ~X509Certificate();
@ -191,12 +197,43 @@ using X509Pkcs12ReaderPtr = bool (*)(QIODevice *device, QSslKey *key, QSslCertif
QList<QSslCertificate> *caCertificates,
const QByteArray &passPhrase);
#if QT_CONFIG(ssl)
// TLS over TCP. Handshake, encryption/decryption.
class Q_NETWORK_PRIVATE_EXPORT TlsCryptograph : public QObject
{
public:
virtual ~TlsCryptograph();
virtual void init(QSslSocket *q, QSslSocketPrivate *d) = 0;
virtual void checkSettingSslContext(QSharedPointer<QSslContext> tlsContext);
virtual QSharedPointer<QSslContext> sslContext() const;
virtual QList<QSslError> tlsErrors() const = 0;
virtual void startClientEncryption() = 0;
virtual void startServerEncryption() = 0;
virtual void continueHandshake() = 0;
virtual void enableHandshakeContinuation();
virtual void disconnectFromHost() = 0;
virtual void disconnected() = 0;
virtual void cancelCAFetch();
virtual QSslCipher sessionCipher() const = 0;
virtual QSsl::SslProtocol sessionProtocol() const = 0;
virtual void transmit() = 0;
virtual bool hasUndecryptedData() const;
virtual QList<QOcspResponse> ocsps() const;
static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName);
static bool isMatchingHostname(const QString &cn, const QString &hostname);
};
#else
class TlsCryptograph;
#endif // QT_CONFIG(ssl)
#if QT_CONFIG(dtls)
class DtlsBase
class Q_NETWORK_PRIVATE_EXPORT DtlsBase
{
public:
virtual ~DtlsBase();
@ -217,7 +254,7 @@ public:
};
// DTLS cookie: generation and verification.
class DtlsCookieVerifier : virtual public DtlsBase
class Q_NETWORK_EXPORT DtlsCookieVerifier : virtual public DtlsBase
{
public:
virtual bool verifyClient(QUdpSocket *socket, const QByteArray &dgram,
@ -226,7 +263,7 @@ public:
};
// TLS over UDP. Handshake, encryption/decryption.
class DtlsCryptograph : virtual public DtlsBase
class Q_NETWORK_PRIVATE_EXPORT DtlsCryptograph : virtual public DtlsBase
{
public:
@ -279,6 +316,11 @@ public:
~QTlsBackend() override;
virtual bool isValid() const;
virtual long tlsLibraryVersionNumber() const;
virtual QString tlsLibraryVersionString() const;
virtual long tlsLibraryBuildVersionNumber() const;
virtual QString tlsLibraryBuildVersionString() const;
virtual void ensureInitialized() const;
virtual QString backendName() const = 0;
virtual QList<QSsl::SslProtocol> supportedProtocols() const = 0;
@ -289,6 +331,8 @@ public:
virtual QTlsPrivate::TlsKey *createKey() const;
virtual QTlsPrivate::X509Certificate *createCertificate() const;
virtual QList<QSslCertificate> systemCaCertificates() const;
// TLS and DTLS:
virtual QTlsPrivate::TlsCryptograph *createTlsCryptograph() const;
virtual QTlsPrivate::DtlsCryptograph *createDtlsCryptograph(class QDtls *qObject, int mode) const;
@ -338,6 +382,31 @@ public:
static void resetBackend(QSslKey &key, QTlsPrivate::TlsKey *keyBackend);
static void setupClientPskAuth(QSslPreSharedKeyAuthenticator *auth, const char *hint,
int hintLength, unsigned maxIdentityLen, unsigned maxPskLen);
static void setupServerPskAuth(QSslPreSharedKeyAuthenticator *auth, const char *identity,
const QByteArray &identityHint, unsigned maxPskLen);
#if QT_CONFIG(ssl)
static QSslCipher createCiphersuite(const QString &description, int bits, int supportedBits);
static QSslCipher createCiphersuite(const QString &suiteName, QSsl::SslProtocol protocol,
const QString &protocolString);
static QSslCipher createCipher(const QString &name, QSsl::SslProtocol protocol,
const QString &protocolString);
// Those statics are implemented using QSslSocketPrivate (which is not exported,
// unlike QTlsBackend).
static QList<QSslCipher> defaultCiphers();
static QList<QSslCipher> defaultDtlsCiphers();
static void setDefaultCiphers(const QList<QSslCipher> &ciphers);
static void setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers);
static void setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers);
static void resetDefaultEllipticCurves();
static void setDefaultCaCertificates(const QList<QSslCertificate> &certs);
#endif // QT_CONFIG(ssl)
Q_DISABLE_COPY_MOVE(QTlsBackend)
};

View File

@ -62,7 +62,18 @@ QT_BEGIN_NAMESPACE
class QSchannelBackend : public QTlsBackend
{
public:
static void ensureInitializedImplementation();
private:
long tlsLibraryVersionNumber() const override;
QString tlsLibraryVersionString() const override;
long tlsLibraryBuildVersionNumber() const override;
QString tlsLibraryBuildVersionString() const override;
void ensureInitialized() const override;
static void resetDefaultCiphers();
QString backendName() const override;
QList<QSsl::SslProtocol> supportedProtocols() const override;
QList<QSsl::SupportedFeature> supportedFeatures() const override;
@ -71,8 +82,15 @@ private:
QTlsPrivate::TlsKey *createKey() const override;
QTlsPrivate::X509Certificate *createCertificate() const override;
QTlsPrivate::TlsCryptograph * createTlsCryptograph() const override;
QList<QSslCertificate> systemCaCertificates() const override;
static QList<QSslCertificate> systemCaCertificatesImplementation();
QTlsPrivate::X509PemReaderPtr X509PemReader() const override;
QTlsPrivate::X509DerReaderPtr X509DerReader() const override;
static bool s_loadedCiphersAndCerts;
};
QT_END_NAMESPACE

View File

@ -40,11 +40,234 @@
#include "qtlsbackend_st_p.h"
#include "qtlskey_st_p.h"
#include "qx509_st_p.h"
#include "qtls_st_p.h"
#include <QtCore/qsysinfo.h>
#include <QtCore/qmutex.h>
QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC(QRecursiveMutex, qt_securetransport_mutex)
Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.securetransport");
namespace QTlsPrivate {
QList<QSslCertificate> systemCaCertificates(); // defined in qsslsocket_mac_shared.cpp
SSLContextRef qt_createSecureTransportContext(QSslSocket::SslMode mode);
QSslCipher QSslCipher_from_SSLCipherSuite(SSLCipherSuite cipher)
{
QString name;
switch (cipher) {
// Sorted as in CipherSuite.h (and groupped by their RFC)
// TLS addenda using AES, per RFC 3268
case TLS_RSA_WITH_AES_128_CBC_SHA:
name = QLatin1String("AES128-SHA");
break;
case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
name = QLatin1String("DHE-RSA-AES128-SHA");
break;
case TLS_RSA_WITH_AES_256_CBC_SHA:
name = QLatin1String("AES256-SHA");
break;
case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
name = QLatin1String("DHE-RSA-AES256-SHA");
break;
// ECDSA addenda, RFC 4492
case TLS_ECDH_ECDSA_WITH_NULL_SHA:
name = QLatin1String("ECDH-ECDSA-NULL-SHA");
break;
case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
name = QLatin1String("ECDH-ECDSA-RC4-SHA");
break;
case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
name = QLatin1String("ECDH-ECDSA-DES-CBC3-SHA");
break;
case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
name = QLatin1String("ECDH-ECDSA-AES128-SHA");
break;
case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
name = QLatin1String("ECDH-ECDSA-AES256-SHA");
break;
case TLS_ECDHE_ECDSA_WITH_NULL_SHA:
name = QLatin1String("ECDHE-ECDSA-NULL-SHA");
break;
case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
name = QLatin1String("ECDHE-ECDSA-RC4-SHA");
break;
case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
name = QLatin1String("ECDHE-ECDSA-DES-CBC3-SHA");
break;
case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
name = QLatin1String("ECDHE-ECDSA-AES128-SHA");
break;
case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
name = QLatin1String("ECDHE-ECDSA-AES256-SHA");
break;
case TLS_ECDH_RSA_WITH_NULL_SHA:
name = QLatin1String("ECDH-RSA-NULL-SHA");
break;
case TLS_ECDH_RSA_WITH_RC4_128_SHA:
name = QLatin1String("ECDH-RSA-RC4-SHA");
break;
case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
name = QLatin1String("ECDH-RSA-DES-CBC3-SHA");
break;
case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
name = QLatin1String("ECDH-RSA-AES128-SHA");
break;
case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
name = QLatin1String("ECDH-RSA-AES256-SHA");
break;
case TLS_ECDHE_RSA_WITH_NULL_SHA:
name = QLatin1String("ECDHE-RSA-NULL-SHA");
break;
case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
name = QLatin1String("ECDHE-RSA-RC4-SHA");
break;
case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
name = QLatin1String("ECDHE-RSA-DES-CBC3-SHA");
break;
case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
name = QLatin1String("ECDHE-RSA-AES128-SHA");
break;
case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
name = QLatin1String("ECDHE-RSA-AES256-SHA");
break;
// TLS 1.2 addenda, RFC 5246
case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
name = QLatin1String("DES-CBC3-SHA");
break;
case TLS_RSA_WITH_AES_128_CBC_SHA256:
name = QLatin1String("AES128-SHA256");
break;
case TLS_RSA_WITH_AES_256_CBC_SHA256:
name = QLatin1String("AES256-SHA256");
break;
case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
name = QLatin1String("DHE-RSA-DES-CBC3-SHA");
break;
case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
name = QLatin1String("DHE-RSA-AES128-SHA256");
break;
case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
name = QLatin1String("DHE-RSA-AES256-SHA256");
break;
// Addendum from RFC 4279, TLS PSK
// all missing atm.
// RFC 4785 - Pre-Shared Key (PSK) Ciphersuites with NULL Encryption
// all missing atm.
// Addenda from rfc 5288 AES Galois Counter Mode (CGM) Cipher Suites for TLS
case TLS_RSA_WITH_AES_256_GCM_SHA384:
name = QLatin1String("AES256-GCM-SHA384");
break;
// RFC 5487 - PSK with SHA-256/384 and AES GCM
// all missing atm.
// Addenda from rfc 5289 Elliptic Curve Cipher Suites with HMAC SHA-256/384
case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
name = QLatin1String("ECDHE-ECDSA-AES128-SHA256");
break;
case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
name = QLatin1String("ECDHE-ECDSA-AES256-SHA384");
break;
case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
name = QLatin1String("ECDH-ECDSA-AES128-SHA256");
break;
case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
name = QLatin1String("ECDH-ECDSA-AES256-SHA384");
break;
case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
name = QLatin1String("ECDHE-RSA-AES128-SHA256");
break;
case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
name = QLatin1String("ECDHE-RSA-AES256-SHA384");
break;
case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
name = QLatin1String("ECDH-RSA-AES128-SHA256");
break;
case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
name = QLatin1String("ECDH-RSA-AES256-SHA384");
break;
// Addenda from rfc 5289 Elliptic Curve Cipher Suites
// with SHA-256/384 and AES Galois Counter Mode (GCM)
case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
name = QLatin1String("ECDHE-RSA-AES256-GCM-SHA384");
break;
default:
return {};
}
return QTlsBackend::createCiphersuite(name, QSsl::TlsV1_2, QLatin1String("TLSv1.2"));
}
} // namespace QTlsPrivate
bool QSecureTransportBackend::s_loadedCiphersAndCerts = false;
QString QSecureTransportBackend::tlsLibraryVersionString() const
{
return QLatin1String("Secure Transport, ") + QSysInfo::prettyProductName();
}
QString QSecureTransportBackend::tlsLibraryBuildVersionString() const
{
return tlsLibraryVersionString();
}
void QSecureTransportBackend::ensureInitialized() const
{
const QMutexLocker locker(qt_securetransport_mutex());
if (s_loadedCiphersAndCerts)
return;
// We have to set it before setDefaultSupportedCiphers,
// since this function can trigger static (global)'s initialization
// and as a result - recursive ensureInitialized call
// from QSslCertificatePrivate's ctor.
s_loadedCiphersAndCerts = true;
const QTlsPrivate::QSecureTransportContext context(QTlsPrivate::qt_createSecureTransportContext(QSslSocket::SslClientMode));
if (context) {
QList<QSslCipher> ciphers;
QList<QSslCipher> defaultCiphers;
size_t numCiphers = 0;
// Fails only if any of parameters is null.
SSLGetNumberSupportedCiphers(context, &numCiphers);
QList<SSLCipherSuite> cfCiphers(numCiphers);
// Fails only if any of parameter is null or number of ciphers is wrong.
SSLGetSupportedCiphers(context, cfCiphers.data(), &numCiphers);
for (size_t i = 0; i < size_t(cfCiphers.size()); ++i) {
const QSslCipher ciph(QTlsPrivate::QSslCipher_from_SSLCipherSuite(cfCiphers.at(i)));
if (!ciph.isNull()) {
ciphers << ciph;
if (ciph.usedBits() >= 128)
defaultCiphers << ciph;
}
}
setDefaultSupportedCiphers(ciphers);
setDefaultCiphers(defaultCiphers);
if (!QSslSocketPrivate::rootCertOnDemandLoadingSupported())
setDefaultCaCertificates(systemCaCertificates());
} else {
s_loadedCiphersAndCerts = false;
}
}
QString QSecureTransportBackend::backendName() const
{
return builtinBackendNames[nameIndexSecureTransport];
@ -60,6 +283,11 @@ QTlsPrivate::X509Certificate *QSecureTransportBackend::createCertificate() const
return new QTlsPrivate::X509CertificateSecureTransport;
}
QList<QSslCertificate> QSecureTransportBackend::systemCaCertificates() const
{
return QTlsPrivate::systemCaCertificates();
}
QList<QSsl::SslProtocol> QSecureTransportBackend::supportedProtocols() const
{
QList<QSsl::SslProtocol> protocols;
@ -104,5 +332,10 @@ QTlsPrivate::X509DerReaderPtr QSecureTransportBackend::X509DerReader() const
return QTlsPrivate::X509CertificateGeneric::certificatesFromDer;
}
QTlsPrivate::TlsCryptograph *QSecureTransportBackend::createTlsCryptograph() const
{
return new QTlsPrivate::TlsCryptographSecureTransport;
}
QT_END_NAMESPACE

View File

@ -63,6 +63,11 @@ QT_BEGIN_NAMESPACE
class QSecureTransportBackend : public QTlsBackend
{
private:
QString tlsLibraryVersionString() const override;
virtual QString tlsLibraryBuildVersionString() const override;
virtual void ensureInitialized() const override;
QString backendName() const override;
QList<QSsl::SslProtocol> supportedProtocols() const override;
@ -72,8 +77,14 @@ private:
QTlsPrivate::TlsKey *createKey() const override;
QTlsPrivate::X509Certificate *createCertificate() const override;
QList<QSslCertificate> systemCaCertificates() const override;
QTlsPrivate::X509PemReaderPtr X509PemReader() const override;
QTlsPrivate::X509DerReaderPtr X509DerReader() const override;
QTlsPrivate::TlsCryptograph *createTlsCryptograph() const override;
static bool s_loadedCiphersAndCerts;
};
QT_END_NAMESPACE

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** 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.
@ -37,15 +37,15 @@
**
****************************************************************************/
#ifndef QTLS_UTILS_P_H
#define QTLS_UTILS_P_H
#ifndef QWINCRYPT_P_H
#define QWINCRYPT_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// 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.
@ -53,47 +53,29 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#if QT_CONFIG(openssl)
#include <QtNetwork/private/qsslsocket_openssl_p.h>
#endif
#include <QtNetwork/private/qssl_p.h>
#include <QtCore/qt_windows.h>
#include <QtCore/qglobal.h>
#include <QtCore/qdebug.h>
#include <wincrypt.h>
#ifndef HCRYPTPROV_LEGACY
#define HCRYPTPROV_LEGACY HCRYPTPROV
#endif // !HCRYPTPROV_LEGACY
#include <memory>
QT_BEGIN_NAMESPACE
namespace QTlslUtils
{
template <class NativeTlsType, void (*Deleter)(NativeTlsType *)>
void safe_delete(NativeTlsType *object)
{
if (object)
Deleter(object);
}
template<class NativeTlsType, int ok, int (*Deleter)(NativeTlsType *)>
void safe_delete(NativeTlsType *object)
{
if (object) {
if (Deleter(object) != ok) {
qCWarning(lcSsl, "Failed to free a resource.");
#if QT_CONFIG(openssl) // || wolfssl later
QSslSocketBackendPrivate::logAndClearErrorQueue();
#endif // QT_CONFIG(openssl)
}
struct QHCertStoreDeleter {
void operator()(HCERTSTORE store)
{
CertCloseStore(store, 0);
}
}
};
template<class NativeTlsType>
using Deleter = std::unique_ptr<NativeTlsType, void (*)(NativeTlsType *)>;
} // namespace QTlsUtils
// A simple RAII type used by Schannel code and Window CA fetcher class:
using QHCertStorePointer = std::unique_ptr<void, QHCertStoreDeleter>;
QT_END_NAMESPACE
#endif // QTLS_UTILS_P_H
#endif // QWINCRYPT_P_H

View File

@ -52,7 +52,8 @@
#include "qsslsocket_p.h" // Transitively includes Wincrypt.h
#if QT_CONFIG(openssl)
#include "qsslsocket_openssl_p.h"
#include "qopenssl_p.h"
#include "qx509_openssl_p.h"
#endif
QT_BEGIN_NAMESPACE
@ -77,7 +78,9 @@ Q_GLOBAL_STATIC(QWindowsCaRootFetcherThread, windowsCaRootFetcherThread);
#if QT_CONFIG(openssl)
namespace {
// TLSTODO: we have to ask the currently active TLS backend about verification
// support and get a function pointer. QT_CONFIG(openssl) check is becoming useless
// as soon as we have several plugins.
const QList<QSslCertificate> buildVerifiedChain(const QList<QSslCertificate> &caCertificates,
PCCERT_CHAIN_CONTEXT chainContext,
const QString &peerVerifyName)
@ -123,7 +126,7 @@ const QList<QSslCertificate> buildVerifiedChain(const QList<QSslCertificate> &ca
}
// We rely on OpenSSL's ability to find other problems.
const auto tlsErrors = QSslSocketBackendPrivate::verify(caCertificates, verifiedChain, peerVerifyName);
const auto tlsErrors = QTlsPrivate::X509CertificateOpenSSL::verify(caCertificates, verifiedChain, peerVerifyName);
if (tlsErrors.size())
verifiedChain.clear();
@ -195,7 +198,7 @@ void QWindowsCaRootFetcher::start()
qCDebug(lcSsl) << " - NOT TRUSTED" << chain->TrustStatus.dwErrorStatus;
if (chain->TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED)
qCDebug(lcSsl) << " - SELF SIGNED";
qCDebug(lcSsl) << "QSslSocketBackendPrivate::fetchCaRootForCert - dumping simple chains";
qCDebug(lcSsl) << "QWindowsCaRootFetcher - dumping simple chains";
for (unsigned int i = 0; i < chain->cChain; i++) {
if (chain->rgpChain[i]->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR)
qCDebug(lcSsl) << " - TRUSTED SIMPLE CHAIN" << i;

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** 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.
@ -40,15 +40,15 @@
#ifndef QWINDOWSCAROOTFETCHER_P_H
#define QWINDOWSCAROOTFETCHER_P_H
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtCore/QtGlobal>
#include <QtCore/QObject>
#include "qsslsocket_p.h"
#include "qsslsocket.h"
#include "qsslcertificate.h"
#include "qsslsocket.h"
#include <memory>
#include "qwincrypt_p.h"
//
// W A R N I N G

View File

@ -43,7 +43,8 @@
#include "qx509_openssl_p.h"
#include "qsslsocket_openssl_symbols_p.h"
#include "qtlsbackend_openssl_p.h"
#include "qtls_openssl_p.h"
#include "qsslsocket.h"
#include <QtNetwork/qhostaddress.h>
@ -356,8 +357,8 @@ extern "C" int qt_X509Callback(int ok, X509_STORE_CTX *ctx)
// TLSTODO: verification callback has to change as soon as TlsCryptographer is in place.
// This is a temporary solution for now to ease the transition.
const auto offset = QSslSocketBackendPrivate::s_indexForSSLExtraData
+ QSslSocketBackendPrivate::errorOffsetInExData;
const auto offset = QTlsBackendOpenSSL::s_indexForSSLExtraData
+ TlsCryptographOpenSSL::errorOffsetInExData;
if (SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx())))
errors = ErrorListPtr(q_SSL_get_ex_data(ssl, offset));
}
@ -587,7 +588,7 @@ QList<QSslError> X509CertificateOpenSSL::verify(const QList<QSslCertificate> &ch
// No need to add them again (and again) and also, if the default configuration
// has its own set of CAs, this probably should not be amended by the ones
// from the 'ROOT' store, since it's not what an application chose to trust.
if (QSslSocketPrivate::s_loadRootCertsOnDemand)
if (QSslSocketPrivate::rootCertOnDemandLoadingSupported())
roots.append(QSslSocketPrivate::systemCaCertificates());
#endif // Q_OS_WIN
return verify(roots, chain, hostName);

View File

@ -53,8 +53,7 @@
#include <private/qtnetworkglobal_p.h>
// TLSTODO: only temporary, and only because of QSslErrorEntry!
#include <private/qsslsocket_openssl_p.h>
#include <private/qopenssl_p.h>
#include <private/qtlsbackend_p.h>
#include <private/qx509_base_p.h>

View File

@ -32,7 +32,6 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtNetwork/private/qsslsocket_openssl_symbols_p.h>
#include <QtNetwork/private/qsslsocket_openssl_p.h>
#include <QtNetwork/qsslcertificate.h>
#include <QtNetwork/qtcpserver.h>

View File

@ -59,7 +59,6 @@
#include "private/qtlsbackend_p.h"
#ifndef QT_NO_OPENSSL
#include "private/qsslsocket_openssl_p.h"
#include "private/qsslsocket_openssl_symbols_p.h"
#endif // QT_NO_OPENSSL
@ -244,9 +243,6 @@ private slots:
void writeBigChunk();
void blacklistedCertificates();
void versionAccessors();
#ifndef QT_NO_OPENSSL
void sslOptions();
#endif
void encryptWithoutConnecting();
void resume_data();
void resume();
@ -3001,60 +2997,6 @@ void tst_QSslSocket::versionAccessors()
qDebug() << QString::number(QSslSocket::sslLibraryVersionNumber(), 16);
}
#ifndef QT_NO_OPENSSL
void tst_QSslSocket::sslOptions()
{
if (!QSslSocket::supportsSsl())
return;
#ifdef SSL_OP_NO_COMPRESSION
QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols,
QSslConfigurationPrivate::defaultSslOptions),
long(SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION|SSL_OP_CIPHER_SERVER_PREFERENCE));
#else
QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols,
QSslConfigurationPrivate::defaultSslOptions),
long(SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_CIPHER_SERVER_PREFERENCE));
#endif
QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols,
QSsl::SslOptionDisableEmptyFragments
|QSsl::SslOptionDisableLegacyRenegotiation),
long(SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_CIPHER_SERVER_PREFERENCE));
#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols,
QSsl::SslOptionDisableEmptyFragments),
long((SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION|SSL_OP_CIPHER_SERVER_PREFERENCE)));
#endif
#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols,
QSsl::SslOptionDisableLegacyRenegotiation),
long((SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_CIPHER_SERVER_PREFERENCE) & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS));
#endif
#ifdef SSL_OP_NO_TICKET
QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols,
QSsl::SslOptionDisableEmptyFragments
|QSsl::SslOptionDisableLegacyRenegotiation
|QSsl::SslOptionDisableSessionTickets),
long((SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TICKET|SSL_OP_CIPHER_SERVER_PREFERENCE)));
#endif
#ifdef SSL_OP_NO_TICKET
#ifdef SSL_OP_NO_COMPRESSION
QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols,
QSsl::SslOptionDisableEmptyFragments
|QSsl::SslOptionDisableLegacyRenegotiation
|QSsl::SslOptionDisableSessionTickets
|QSsl::SslOptionDisableCompression),
long((SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TICKET|SSL_OP_NO_COMPRESSION|SSL_OP_CIPHER_SERVER_PREFERENCE)));
#endif
#endif
}
#endif
void tst_QSslSocket::encryptWithoutConnecting()
{
if (!QSslSocket::supportsSsl())

View File

@ -44,9 +44,6 @@
#ifdef QT_BUILD_INTERNAL
#include <QtNetwork/private/qhostinfo_p.h>
#ifndef QT_NO_OPENSSL
#include <QtNetwork/private/qsslsocket_openssl_p.h>
#endif
#endif
Q_DECLARE_METATYPE(QSharedPointer<char>)