diff options
Diffstat (limited to 'src/network/ssl/qsslsocket.cpp')
-rw-r--r-- | src/network/ssl/qsslsocket.cpp | 769 |
1 files changed, 619 insertions, 150 deletions
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 66bcb79c4c..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 @@ -899,13 +863,20 @@ void QSslSocket::close() #endif Q_D(QSslSocket); - // We don't want any CA roots fetched anymore. - d->caToFetch = QSslCertificate{}; + // 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 (encryptedBytesToWrite() || !d->writeBuffer.isEmpty()) + 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: @@ -939,29 +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 - // 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 (d->plainSocket) - d->plainSocket->abort(); - close(); -} - -/*! \since 4.4 Returns the socket's SSL configuration state. The default SSL @@ -1226,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 {}; } /*! @@ -1501,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 {}; } /*! @@ -1518,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; } /*! @@ -1534,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; } /*! @@ -1560,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); } /*! @@ -1722,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(); } /*! @@ -1779,7 +1896,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) { @@ -1815,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); @@ -1836,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) { @@ -1847,6 +1965,8 @@ qint64 QSslSocket::writeData(const char *data, qint64 len) return len; } +bool QSslSocketPrivate::s_loadRootCertsOnDemand = false; + /*! \internal */ @@ -1855,7 +1975,6 @@ QSslSocketPrivate::QSslSocketPrivate() , mode(QSslSocket::UnencryptedMode) , autoStartHandshake(false) , connectionEncrypted(false) - , shutdown(false) , ignoreAllSslErrors(false) , readyReadEmittedPointer(nullptr) , allowRootCertOnDemandLoading(true) @@ -1864,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"; + } } /*! @@ -1876,27 +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; - caToFetch = QSslCertificate{}; + + if (backend.get()) { + Q_ASSERT(q_ptr); + backend->init(static_cast<QSslSocket *>(q_ptr), this); + } } /*! @@ -1904,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: @@ -1919,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; } @@ -1967,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(); @@ -1977,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); @@ -2103,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; @@ -2224,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 @@ -2342,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(); @@ -2454,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(); @@ -2468,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; @@ -2492,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) { @@ -2507,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); @@ -2527,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); @@ -2541,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); } /*! @@ -2573,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; @@ -2581,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) @@ -2648,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 .) @@ -2666,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 @@ -2679,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; } @@ -2694,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" |