summaryrefslogtreecommitdiffstats
path: root/src/network/ssl/qsslsocket.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/ssl/qsslsocket.cpp')
-rw-r--r--src/network/ssl/qsslsocket.cpp785
1 files changed, 624 insertions, 161 deletions
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 9af8f2a9dc..395394d432 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QSSLSOCKET_DEBUG
@@ -54,10 +18,10 @@
QSslSocket establishes a secure, encrypted TCP connection you can
use for transmitting encrypted data. It can operate in both client
- and server mode, and it supports modern SSL protocols, including
- SSL 3 and TLS 1.2. By default, QSslSocket uses only SSL protocols
+ and server mode, and it supports modern TLS protocols, including
+ TLS 1.3. By default, QSslSocket uses only TLS protocols
which are considered to be secure (QSsl::SecureProtocols), but you can
- change the SSL protocol by calling setProtocol() as long as you do
+ change the TLS protocol by calling setProtocol() as long as you do
it before the handshake has started.
SSL encryption operates on top of the existing TCP stream after
@@ -133,8 +97,7 @@
\list
\li The socket's cryptographic cipher suite can be customized before
- the handshake phase with QSslConfiguration::setCiphers()
- and QSslConfiguration::setDefaultCiphers().
+ the handshake phase with QSslConfiguration::setCiphers().
\li The socket's local certificate and private key can be customized
before the handshake phase with setLocalCertificate() and
setPrivateKey().
@@ -188,7 +151,7 @@
behavior is identical to QTcpSocket.
\value SslClientMode The socket is a client-side SSL socket.
- It is either alreayd encrypted, or it is in the SSL handshake
+ It is either already encrypted, or it is in the SSL handshake
phase (see QSslSocket::isEncrypted()).
\value SslServerMode The socket is a server-side SSL socket.
@@ -323,7 +286,7 @@
*/
/*!
- \fn void QSslSocket::alertSent(QAlertLevel level, QAlertType type, const QString &description)
+ \fn void QSslSocket::alertSent(QSsl::AlertLevel level, QSsl::AlertType type, const QString &description)
QSslSocket emits this signal if an alert message was sent to a peer. \a level
describes if it was a warning or a fatal error. \a type gives the code
@@ -334,11 +297,11 @@
purposes, normally it does not require any actions from the application.
\note Not all backends support this functionality.
- \sa alertReceived(), QAlertLevel, QAlertType
+ \sa alertReceived(), QSsl::AlertLevel, QSsl::AlertType
*/
/*!
- \fn void QSslSocket::alertReceived(QAlertLevel level, QAlertType type, const QString &description)
+ \fn void QSslSocket::alertReceived(QSsl::AlertLevel level, QSsl::AlertType type, const QString &description)
QSslSocket emits this signal if an alert message was received from a peer.
\a level tells if the alert was fatal or it was a warning. \a type is the
@@ -350,7 +313,7 @@
backend will handle it and close the connection.
\note Not all backends support this functionality.
- \sa alertSent(), QAlertLevel, QAlertType
+ \sa alertSent(), QSsl::AlertLevel, QSsl::AlertType
*/
/*!
@@ -385,16 +348,9 @@
#include "qsslsocket.h"
#include "qsslcipher.h"
#include "qocspresponse.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 "qtlsbackend_p.h"
#include "qsslconfiguration_p.h"
+#include "qsslsocket_p.h"
#include <QtCore/qdebug.h>
#include <QtCore/qdir.h>
@@ -406,6 +362,14 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+#ifdef Q_OS_VXWORKS
+constexpr auto isVxworks = true;
+#else
+constexpr auto isVxworks = false;
+#endif
+
class QSslSocketGlobalData
{
public:
@@ -432,7 +396,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
@@ -898,10 +862,21 @@ void QSslSocket::close()
qCDebug(lcSsl) << "QSslSocket::close()";
#endif
Q_D(QSslSocket);
- if (encryptedBytesToWrite() || !d->writeBuffer.isEmpty())
+
+ // On Windows, CertGetCertificateChain is probably still doing its
+ // job, if the socket is re-used, we want to ignore its reported
+ // root CA.
+ if (auto *backend = d->backend.get())
+ backend->cancelCAFetch();
+
+ if (!d->abortCalled && (encryptedBytesToWrite() || !d->writeBuffer.isEmpty()))
flush();
- if (d->plainSocket)
- d->plainSocket->close();
+ if (d->plainSocket) {
+ if (d->abortCalled)
+ d->plainSocket->abort();
+ else
+ d->plainSocket->close();
+ }
QTcpSocket::close();
// must be cleared, reading/writing not possible on closed socket:
@@ -921,25 +896,6 @@ bool QSslSocket::atEnd() const
}
/*!
- This function writes as much as possible from the internal write buffer to
- the underlying network socket, without blocking. If any data was written,
- this function returns \c true; otherwise false is returned.
-
- Call this function if you need QSslSocket to start sending buffered data
- immediately. The number of bytes successfully written depends on the
- operating system. In most cases, you do not need to call this function,
- because QAbstractSocket will start sending data automatically once control
- goes back to the event loop. In the absence of an event loop, call
- waitForBytesWritten() instead.
-
- \sa write(), waitForBytesWritten()
-*/
-bool QSslSocket::flush()
-{
- return d_func()->flush();
-}
-
-/*!
\since 4.4
Sets the size of QSslSocket's internal read buffer to be \a size bytes.
@@ -954,24 +910,6 @@ void QSslSocket::setReadBufferSize(qint64 size)
}
/*!
- Aborts the current connection and resets the socket. Unlike
- disconnectFromHost(), this function immediately closes the socket,
- clearing any pending data in the write buffer.
-
- \sa disconnectFromHost(), close()
-*/
-void QSslSocket::abort()
-{
- Q_D(QSslSocket);
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QSslSocket::abort()";
-#endif
- if (d->plainSocket)
- d->plainSocket->abort();
- close();
-}
-
-/*!
\since 4.4
Returns the socket's SSL configuration state. The default SSL
@@ -1236,7 +1174,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 {};
}
/*!
@@ -1511,7 +1451,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 {};
}
/*!
@@ -1528,12 +1470,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;
}
/*!
@@ -1544,20 +1488,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;
}
/*!
@@ -1570,7 +1517,166 @@ long QSslSocket::sslLibraryBuildVersionNumber()
*/
QString QSslSocket::sslLibraryBuildVersionString()
{
- return QSslSocketPrivate::sslLibraryBuildVersionString();
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
+ return tlsBackend->tlsLibraryBuildVersionString();
+
+ return {};
+}
+
+/*!
+ \since 6.1
+ Returns the names of the currently available backends. These names
+ are in lower case, e.g. "openssl", "securetransport", "schannel"
+ (similar to the already existing feature names for TLS backends in Qt).
+
+ \sa activeBackend()
+*/
+QList<QString> QSslSocket::availableBackends()
+{
+ return QTlsBackend::availableBackendNames();
+}
+
+/*!
+ \since 6.1
+ Returns the name of the backend that QSslSocket and related classes
+ use. If the active backend was not set explicitly, this function
+ returns the name of a default backend that QSslSocket selects implicitly
+ from the list of available backends.
+
+ \note When selecting a default backend implicitly, QSslSocket prefers
+ the OpenSSL backend if available. If it's not available, the Schannel backend
+ is implicitly selected on Windows, and Secure Transport on Darwin platforms.
+ Failing these, if a custom TLS backend is found, it is used.
+ If no other backend is found, the "certificate only" backend is selected.
+ For more information about TLS plugins, please see
+ \l {Enabling and Disabling SSL Support when Building Qt from Source}.
+
+ \sa setActiveBackend(), availableBackends()
+*/
+QString QSslSocket::activeBackend()
+{
+ const QMutexLocker locker(&QSslSocketPrivate::backendMutex);
+
+ if (!QSslSocketPrivate::activeBackendName.size())
+ QSslSocketPrivate::activeBackendName = QTlsBackend::defaultBackendName();
+
+ return QSslSocketPrivate::activeBackendName;
+}
+
+/*!
+ \since 6.1
+ Returns true if a backend with name \a backendName was set as
+ active backend. \a backendName must be one of names returned
+ by availableBackends().
+
+ \note An application cannot mix different backends simultaneously.
+ This implies that a non-default backend must be selected prior
+ to any use of QSslSocket or related classes, e.g. QSslCertificate
+ or QSslKey.
+
+ \sa activeBackend(), availableBackends()
+*/
+bool QSslSocket::setActiveBackend(const QString &backendName)
+{
+ if (!backendName.size()) {
+ qCWarning(lcSsl, "Invalid parameter (backend name cannot be an empty string)");
+ return false;
+ }
+
+ QMutexLocker locker(&QSslSocketPrivate::backendMutex);
+ if (QSslSocketPrivate::tlsBackend) {
+ qCWarning(lcSsl) << "Cannot set backend named" << backendName
+ << "as active, another backend is already in use";
+ locker.unlock();
+ return activeBackend() == backendName;
+ }
+
+ if (!QTlsBackend::availableBackendNames().contains(backendName)) {
+ qCWarning(lcSsl) << "Cannot set unavailable backend named" << backendName
+ << "as active";
+ return false;
+ }
+
+ QSslSocketPrivate::activeBackendName = backendName;
+
+ return true;
+}
+
+/*!
+ \since 6.1
+ If a backend with name \a backendName is available, this function returns the
+ list of TLS protocol versions supported by this backend. An empty \a backendName
+ is understood as a query about the currently active backend. Otherwise, this
+ function returns an empty list.
+
+ \sa availableBackends(), activeBackend(), isProtocolSupported()
+*/
+QList<QSsl::SslProtocol> QSslSocket::supportedProtocols(const QString &backendName)
+{
+ return QTlsBackend::supportedProtocols(backendName.size() ? backendName : activeBackend());
+}
+
+/*!
+ \since 6.1
+ Returns true if \a protocol is supported by a backend named \a backendName. An empty
+ \a backendName is understood as a query about the currently active backend.
+
+ \sa supportedProtocols()
+*/
+bool QSslSocket::isProtocolSupported(QSsl::SslProtocol protocol, const QString &backendName)
+{
+ const auto versions = supportedProtocols(backendName);
+ return versions.contains(protocol);
+}
+
+/*!
+ \since 6.1
+ This function returns backend-specific classes implemented by the backend named
+ \a backendName. An empty \a backendName is understood as a query about the
+ currently active backend.
+
+ \sa QSsl::ImplementedClass, activeBackend(), isClassImplemented()
+*/
+QList<QSsl::ImplementedClass> QSslSocket::implementedClasses(const QString &backendName)
+{
+ return QTlsBackend::implementedClasses(backendName.size() ? backendName : activeBackend());
+}
+
+/*!
+ \since 6.1
+ Returns true if a class \a cl is implemented by the backend named \a backendName. An empty
+ \a backendName is understood as a query about the currently active backend.
+
+ \sa implementedClasses()
+*/
+
+bool QSslSocket::isClassImplemented(QSsl::ImplementedClass cl, const QString &backendName)
+{
+ return implementedClasses(backendName).contains(cl);
+}
+
+/*!
+ \since 6.1
+ This function returns features supported by a backend named \a backendName.
+ An empty \a backendName is understood as a query about the currently active backend.
+
+ \sa QSsl::SupportedFeature, activeBackend()
+*/
+QList<QSsl::SupportedFeature> QSslSocket::supportedFeatures(const QString &backendName)
+{
+ return QTlsBackend::supportedFeatures(backendName.size() ? backendName : activeBackend());
+}
+
+/*!
+ \since 6.1
+ Returns true if a feature \a ft is supported by a backend named \a backendName. An empty
+ \a backendName is understood as a query about the currently active backend.
+
+ \sa QSsl::SupportedFeature, supportedFeatures()
+*/
+bool QSslSocket::isFeatureSupported(QSsl::SupportedFeature ft, const QString &backendName)
+{
+ return supportedFeatures(backendName).contains(ft);
}
/*!
@@ -1732,7 +1838,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();
}
/*!
@@ -1787,6 +1894,10 @@ void QSslSocket::disconnectFromHost()
d->pendingClose = true;
return;
}
+ // Make sure we don't process any signal from the CA fetcher
+ // (Windows):
+ if (auto *backend = d->backend.get())
+ backend->cancelCAFetch();
// Perhaps emit closing()
if (d->state != ClosingState) {
@@ -1822,7 +1933,7 @@ qint64 QSslSocket::readData(char *data, qint64 maxlen)
#endif
} else {
// possibly trigger another transmit() to decrypt more data from the socket
- if (d->plainSocket->bytesAvailable())
+ if (d->plainSocket->bytesAvailable() || d->hasUndecryptedData())
QMetaObject::invokeMethod(this, "_q_flushReadBuffer", Qt::QueuedConnection);
else if (d->state != QAbstractSocket::ConnectedState)
return maxlen ? qint64(-1) : qint64(0);
@@ -1843,7 +1954,7 @@ qint64 QSslSocket::writeData(const char *data, qint64 len)
if (d->mode == UnencryptedMode && !d->autoStartHandshake)
return d->plainSocket->write(data, len);
- d->writeBuffer.append(data, len);
+ d->write(data, len);
// make sure we flush to the plain socket's buffer
if (!d->flushTriggered) {
@@ -1854,6 +1965,8 @@ qint64 QSslSocket::writeData(const char *data, qint64 len)
return len;
}
+bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
+
/*!
\internal
*/
@@ -1862,7 +1975,6 @@ QSslSocketPrivate::QSslSocketPrivate()
, mode(QSslSocket::UnencryptedMode)
, autoStartHandshake(false)
, connectionEncrypted(false)
- , shutdown(false)
, ignoreAllSslErrors(false)
, readyReadEmittedPointer(nullptr)
, allowRootCertOnDemandLoading(true)
@@ -1871,6 +1983,21 @@ QSslSocketPrivate::QSslSocketPrivate()
, flushTriggered(false)
{
QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration);
+ // If the global configuration doesn't allow root certificates to be loaded
+ // on demand then we have to disable it for this socket as well.
+ if (!configuration.allowRootCertOnDemandLoading)
+ allowRootCertOnDemandLoading = false;
+
+ 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";
+ }
}
/*!
@@ -1883,26 +2010,54 @@ 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;
+
+ if (backend.get()) {
+ Q_ASSERT(q_ptr);
+ backend->init(static_cast<QSslSocket *>(q_ptr), this);
+ }
}
/*!
@@ -1910,13 +2065,15 @@ void QSslSocketPrivate::init()
*/
bool QSslSocketPrivate::verifyProtocolSupported(const char *where)
{
- QLatin1String protocolName("DTLS");
+ auto protocolName = "DTLS"_L1;
switch (configuration.protocol) {
case QSsl::UnknownProtocol:
// UnknownProtocol, according to our docs, is for cipher whose protocol is unknown.
// Should not be used when configuring QSslSocket.
- protocolName = QLatin1String("UnknownProtocol");
+ protocolName = "UnknownProtocol"_L1;
Q_FALLTHROUGH();
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
case QSsl::DtlsV1_0:
case QSsl::DtlsV1_2:
case QSsl::DtlsV1_0OrLater:
@@ -1925,6 +2082,7 @@ bool QSslSocketPrivate::verifyProtocolSupported(const char *where)
setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
QSslSocket::tr("Attempted to use an unsupported protocol."));
return false;
+QT_WARNING_POP
default:
return true;
}
@@ -1973,7 +2131,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();
@@ -1983,7 +2169,7 @@ void q_setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers)
/*!
\internal
*/
-QList<QSslCipher> q_getDefaultDtlsCiphers()
+QList<QSslCipher> QSslSocketPrivate::defaultDtlsCiphers()
{
QSslSocketPrivate::ensureInitialized();
QMutexLocker locker(&globalData()->mutex);
@@ -2109,6 +2295,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri
ptr->sessionProtocol = global->sessionProtocol;
ptr->ciphers = global->ciphers;
ptr->caCertificates = global->caCertificates;
+ ptr->allowRootCertOnDemandLoading = global->allowRootCertOnDemandLoading;
ptr->protocol = global->protocol;
ptr->peerVerifyMode = global->peerVerifyMode;
ptr->peerVerifyDepth = global->peerVerifyDepth;
@@ -2230,6 +2417,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
@@ -2340,7 +2532,7 @@ void QSslSocketPrivate::_q_stateChangedSlot(QAbstractSocket::SocketState state)
*/
void QSslSocketPrivate::_q_errorSlot(QAbstractSocket::SocketError error)
{
- Q_UNUSED(error)
+ Q_UNUSED(error);
#ifdef QSSLSOCKET_DEBUG
Q_Q(QSslSocket);
qCDebug(lcSsl) << "QSslSocket::_q_errorSlot(" << error << ')';
@@ -2348,7 +2540,7 @@ void QSslSocketPrivate::_q_errorSlot(QAbstractSocket::SocketError error)
qCDebug(lcSsl) << "\terrorString =" << q->errorString();
#endif
// this moves encrypted bytes from plain socket into our buffer
- if (plainSocket->bytesAvailable()) {
+ if (plainSocket->bytesAvailable() && mode != QSslSocket::UnencryptedMode) {
qint64 tmpReadBufferMaxSize = readBufferMaxSize;
readBufferMaxSize = 0; // reset temporarily so the plain sockets completely drained drained
transmit();
@@ -2460,6 +2652,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();
@@ -2474,13 +2667,16 @@ 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++) {
+ for (int a = 0; a < sslErrors.size(); a++) {
if (!ignoreErrorsList.contains(sslErrors.at(a))) {
doEmitSslError = true;
break;
@@ -2498,6 +2694,91 @@ bool QSslSocketPrivate::verifyErrorsHaveBeenIgnored()
/*!
\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
+*/
qint64 QSslSocketPrivate::peek(char *data, qint64 maxSize)
{
if (mode == QSslSocket::UnencryptedMode && !autoStartHandshake) {
@@ -2513,9 +2794,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);
@@ -2533,13 +2814,13 @@ QByteArray QSslSocketPrivate::peek(qint64 maxSize)
QByteArray ret;
ret.reserve(maxSize);
ret.resize(buffer.peek(ret.data(), maxSize, transactionPos));
- if (ret.length() == maxSize)
+ if (ret.size() == maxSize)
return ret;
//peek at data in the plain socket
if (plainSocket)
- return ret + plainSocket->peek(maxSize - ret.length());
- else
- return QByteArray();
+ return ret + plainSocket->peek(maxSize - ret.size());
+
+ return QByteArray();
} else {
//encrypted mode - the socket engine will read and decrypt data into the QIODevice buffer
return QTcpSocketPrivate::peek(maxSize);
@@ -2547,17 +2828,19 @@ QByteArray QSslSocketPrivate::peek(qint64 maxSize)
}
/*!
- \internal
+ \reimp
*/
-qint64 QSslSocketPrivate::skip(qint64 maxSize)
+qint64 QSslSocket::skipData(qint64 maxSize)
{
- if (mode == QSslSocket::UnencryptedMode && !autoStartHandshake)
- return plainSocket->skip(maxSize);
+ Q_D(QSslSocket);
+
+ if (d->mode == QSslSocket::UnencryptedMode && !d->autoStartHandshake)
+ return d->plainSocket->skip(maxSize);
// In encrypted mode, the SSL backend writes decrypted data directly into the
// QIODevice's read buffer. As this buffer is always emptied by the caller,
// we need to wait for more incoming data.
- return (state == QAbstractSocket::ConnectedState) ? Q_INT64_C(0) : Q_INT64_C(-1);
+ return (d->state == QAbstractSocket::ConnectedState) ? Q_INT64_C(0) : Q_INT64_C(-1);
}
/*!
@@ -2579,6 +2862,82 @@ bool QSslSocketPrivate::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
+*/
bool QSslSocketPrivate::rootCertOnDemandLoadingSupported()
{
return s_loadRootCertsOnDemand;
@@ -2587,34 +2946,63 @@ bool QSslSocketPrivate::rootCertOnDemandLoadingSupported()
/*!
\internal
*/
+void QSslSocketPrivate::setRootCertOnDemandLoadingSupported(bool supported)
+{
+ s_loadRootCertsOnDemand = supported;
+}
+
+/*!
+ \internal
+*/
QList<QByteArray> QSslSocketPrivate::unixRootCertDirectories()
{
- return QList<QByteArray>() << "/etc/ssl/certs/" // (K)ubuntu, OpenSUSE, Mandriva ...
- << "/usr/lib/ssl/certs/" // Gentoo, Mandrake
- << "/usr/share/ssl/" // Centos, Redhat, SuSE
- << "/usr/local/ssl/" // Normal OpenSSL Tarball
- << "/var/ssl/certs/" // AIX
- << "/usr/local/ssl/certs/" // Solaris
- << "/etc/openssl/certs/" // BlackBerry
- << "/opt/openssl/certs/" // HP-UX
- << "/etc/ssl/"; // OpenBSD
+ const auto ba = [](const auto &cstr) constexpr {
+ return QByteArray::fromRawData(std::begin(cstr), std::size(cstr) - 1);
+ };
+ static const QByteArray dirs[] = {
+ ba("/etc/ssl/certs/"), // (K)ubuntu, OpenSUSE, Mandriva ...
+ ba("/usr/lib/ssl/certs/"), // Gentoo, Mandrake
+ ba("/usr/share/ssl/"), // Centos, Redhat, SuSE
+ ba("/usr/local/ssl/"), // Normal OpenSSL Tarball
+ ba("/var/ssl/certs/"), // AIX
+ ba("/usr/local/ssl/certs/"), // Solaris
+ ba("/etc/openssl/certs/"), // BlackBerry
+ ba("/opt/openssl/certs/"), // HP-UX
+ ba("/etc/ssl/"), // OpenBSD
+ };
+ QList<QByteArray> result = QList<QByteArray>::fromReadOnlyData(dirs);
+ if constexpr (isVxworks) {
+ static QByteArray vxworksCertsDir = qgetenv("VXWORKS_CERTS_DIR");
+ if (!vxworksCertsDir.isEmpty())
+ result.push_back(vxworksCertsDir);
+ }
+ return result;
}
/*!
\internal
*/
-void QSslSocketPrivate::checkSettingSslContext(QSslSocket* socket, QSharedPointer<QSslContext> sslContext)
+void QSslSocketPrivate::checkSettingSslContext(QSslSocket* socket, std::shared_ptr<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);
}
/*!
\internal
*/
-QSharedPointer<QSslContext> QSslSocketPrivate::sslContext(QSslSocket *socket)
+std::shared_ptr<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)
@@ -2654,17 +3042,17 @@ bool QSslSocketPrivate::isMatchingHostname(const QSslCertificate &cert, const QS
*/
bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hostname)
{
- int wildcard = cn.indexOf(QLatin1Char('*'));
+ qsizetype wildcard = cn.indexOf(u'*');
// Check this is a wildcard cert, if not then just compare the strings
if (wildcard < 0)
- return QLatin1String(QUrl::toAce(cn)) == hostname;
+ return QLatin1StringView(QUrl::toAce(cn)) == hostname;
- int firstCnDot = cn.indexOf(QLatin1Char('.'));
- int secondCnDot = cn.indexOf(QLatin1Char('.'), firstCnDot+1);
+ qsizetype firstCnDot = cn.indexOf(u'.');
+ qsizetype secondCnDot = cn.indexOf(u'.', firstCnDot+1);
// Check at least 3 components
- if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.length()))
+ if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.size()))
return false;
// Check * is last character of 1st component (ie. there's a following .)
@@ -2672,12 +3060,12 @@ bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hos
return false;
// Check only one star
- if (cn.lastIndexOf(QLatin1Char('*')) != wildcard)
+ if (cn.lastIndexOf(u'*') != wildcard)
return false;
// Reject wildcard character embedded within the A-labels or U-labels of an internationalized
// domain name (RFC6125 section 7.2)
- if (cn.startsWith(QLatin1String("xn--"), Qt::CaseInsensitive))
+ if (cn.startsWith("xn--"_L1, Qt::CaseInsensitive))
return false;
// Check characters preceding * (if any) match
@@ -2685,9 +3073,9 @@ bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hos
return false;
// Check characters following first . match
- int hnDot = hostname.indexOf(QLatin1Char('.'));
+ qsizetype hnDot = hostname.indexOf(u'.');
if (QStringView{hostname}.mid(hnDot + 1) != QStringView{cn}.mid(firstCnDot + 1)
- && QStringView{hostname}.mid(hnDot + 1) != QLatin1String(QUrl::toAce(cn.mid(firstCnDot + 1)))) {
+ && QStringView{hostname}.mid(hnDot + 1) != QLatin1StringView(QUrl::toAce(cn.mid(firstCnDot + 1)))) {
return false;
}
@@ -2700,6 +3088,81 @@ bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hos
return true;
}
+/*!
+ \internal
+*/
+QTlsBackend *QSslSocketPrivate::tlsBackendInUse()
+{
+ const QMutexLocker locker(&backendMutex);
+ if (tlsBackend)
+ return tlsBackend;
+
+ if (!activeBackendName.size())
+ activeBackendName = QTlsBackend::defaultBackendName();
+
+ if (!activeBackendName.size()) {
+ qCWarning(lcSsl, "No functional TLS backend was found");
+ return nullptr;
+ }
+
+ tlsBackend = QTlsBackend::findBackend(activeBackendName);
+ if (tlsBackend) {
+ QObject::connect(tlsBackend, &QObject::destroyed, tlsBackend, [] {
+ const QMutexLocker locker(&backendMutex);
+ tlsBackend = nullptr;
+ },
+ Qt::DirectConnection);
+ }
+ return tlsBackend;
+}
+
+/*!
+ \internal
+*/
+QSslSocket::SslMode QSslSocketPrivate::tlsMode() const
+{
+ return mode;
+}
+
+/*!
+ \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"