diff options
Diffstat (limited to 'src/network/ssl/qsslsocket_mac.cpp')
-rw-r--r-- | src/network/ssl/qsslsocket_mac.cpp | 1554 |
1 files changed, 0 insertions, 1554 deletions
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp deleted file mode 100644 index 4096fb68c6..0000000000 --- a/src/network/ssl/qsslsocket_mac.cpp +++ /dev/null @@ -1,1554 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Jeremy LainĂ© <jeremy.laine@m4x.org> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsslsocket.h" - -#include "qssl_p.h" -#include "qsslsocket_mac_p.h" -#include "qasn1element_p.h" -#include "qsslcertificate_p.h" -#include "qsslcipher_p.h" -#include "qsslkey_p.h" - -#include <QtCore/qmessageauthenticationcode.h> -#include <QtCore/qoperatingsystemversion.h> -#include <QtCore/qscopedvaluerollback.h> -#include <QtCore/qcryptographichash.h> -#include <QtCore/qsystemdetection.h> -#include <QtCore/qdatastream.h> -#include <QtCore/qsysinfo.h> -#include <QtCore/qlist.h> -#include <QtCore/qmutex.h> -#include <QtCore/qdebug.h> -#include <QtCore/quuid.h> -#include <QtCore/qdir.h> - -#include <algorithm> -#include <cstddef> -#include <limits> -#include <vector> - -#include <QtCore/private/qcore_mac_p.h> - -#ifdef Q_OS_MACOS -#include <CoreServices/CoreServices.h> -#endif - -QT_BEGIN_NAMESPACE - -namespace -{ -#ifdef Q_OS_MACOS -/* - -Our own temporarykeychain is needed only on macOS where SecPKCS12Import changes -the default keychain and where we see annoying pop-ups asking about accessing a -private key. - -*/ - -struct EphemeralSecKeychain -{ - EphemeralSecKeychain(); - ~EphemeralSecKeychain(); - - SecKeychainRef keychain = nullptr; - Q_DISABLE_COPY_MOVE(EphemeralSecKeychain) -}; - -EphemeralSecKeychain::EphemeralSecKeychain() -{ - const auto uuid = QUuid::createUuid(); - if (uuid.isNull()) { - qCWarning(lcSsl) << "Failed to create a unique keychain name"; - return; - } - - const QByteArray uuidAsByteArray = uuid.toByteArray(); - Q_ASSERT(uuidAsByteArray.size() > 2); - Q_ASSERT(uuidAsByteArray.startsWith('{')); - Q_ASSERT(uuidAsByteArray.endsWith('}')); - const auto uuidAsString = QLatin1String(uuidAsByteArray.data(), uuidAsByteArray.size()).mid(1, uuidAsByteArray.size() - 2); - - const QString keychainName - = QDir::tempPath() + QDir::separator() + uuidAsString + QLatin1String(".keychain"); - // SecKeychainCreate, pathName parameter: - // - // "A constant character string representing the POSIX path indicating where - // to store the keychain." - // - // Internally they seem to use std::string, but this does not really help. - // Fortunately, CFString has a convenient API. - QCFType<CFStringRef> cfName = keychainName.toCFString(); - std::vector<char> posixPath; - // "Extracts the contents of a string as a NULL-terminated 8-bit string - // appropriate for passing to POSIX APIs." - posixPath.resize(CFStringGetMaximumSizeOfFileSystemRepresentation(cfName)); - const auto ok = CFStringGetFileSystemRepresentation(cfName, &posixPath[0], - CFIndex(posixPath.size())); - if (!ok) { - qCWarning(lcSsl) << "Failed to create a unique keychain name from" - << "QDir::tempPath()"; - return; - } - - std::vector<uint8_t> passUtf8(256); - if (SecRandomCopyBytes(kSecRandomDefault, passUtf8.size(), &passUtf8[0])) { - qCWarning(lcSsl) << "SecRandomCopyBytes: failed to create a key"; - return; - } - - const OSStatus status = SecKeychainCreate(&posixPath[0], passUtf8.size(), - &passUtf8[0], FALSE, nullptr, - &keychain); - if (status != errSecSuccess || !keychain) { - qCWarning(lcSsl) << "SecKeychainCreate: failed to create a custom keychain"; - if (keychain) { - SecKeychainDelete(keychain); - CFRelease(keychain); - keychain = nullptr; - } - } - - if (keychain) { - SecKeychainSettings settings = {}; - settings.version = SEC_KEYCHAIN_SETTINGS_VERS1; - // Strange, huh? But that's what their docs say to do! With lockOnSleep - // == false, set interval to INT_MAX to never lock ... - settings.lockInterval = INT_MAX; - if (SecKeychainSetSettings(keychain, &settings) != errSecSuccess) - qCWarning(lcSsl) << "SecKeychainSettings: failed to disable lock on sleep"; - } - -#ifdef QSSLSOCKET_DEBUG - if (keychain) { - qCDebug(lcSsl) << "Custom keychain with name" << keychainName << "was created" - << "successfully"; - } -#endif -} - -EphemeralSecKeychain::~EphemeralSecKeychain() -{ - if (keychain) { - // clear file off disk - SecKeychainDelete(keychain); - CFRelease(keychain); - } -} - -#endif // Q_OS_MACOS - -} // unnamed namespace - -static SSLContextRef qt_createSecureTransportContext(QSslSocket::SslMode mode) -{ - const bool isServer = mode == QSslSocket::SslServerMode; - const SSLProtocolSide side = isServer ? kSSLServerSide : kSSLClientSide; - // We never use kSSLDatagramType, so it's kSSLStreamType unconditionally. - SSLContextRef context = SSLCreateContext(nullptr, side, kSSLStreamType); - if (!context) - qCWarning(lcSsl) << "SSLCreateContext failed"; - return context; -} - -static void qt_releaseSecureTransportContext(SSLContextRef context) -{ - if (context) - CFRelease(context); -} - -QSecureTransportContext::QSecureTransportContext(SSLContextRef c) - : context(c) -{ -} - -QSecureTransportContext::~QSecureTransportContext() -{ - qt_releaseSecureTransportContext(context); -} - -QSecureTransportContext::operator SSLContextRef()const -{ - return context; -} - -void QSecureTransportContext::reset(SSLContextRef newContext) -{ - qt_releaseSecureTransportContext(context); - context = newContext; -} - -Q_GLOBAL_STATIC(QRecursiveMutex, qt_securetransport_mutex) - -//#define QSSLSOCKET_DEBUG - -bool QSslSocketPrivate::s_libraryLoaded = false; -bool QSslSocketPrivate::s_loadedCiphersAndCerts = false; -bool QSslSocketPrivate::s_loadRootCertsOnDemand = false; - - -#if !defined(QT_PLATFORM_UIKIT) // dhparam is only used on macOS. (see the SSLSetDiffieHellmanParams call below) -static const uint8_t dhparam[] = - "\x30\x82\x01\x08\x02\x82\x01\x01\x00\x97\xea\xd0\x46\xf7\xae\xa7\x76\x80" - "\x9c\x74\x56\x98\xd8\x56\x97\x2b\x20\x6c\x77\xe2\x82\xbb\xc8\x84\xbe\xe7" - "\x63\xaf\xcc\x30\xd0\x67\x97\x7d\x1b\xab\x59\x30\xa9\x13\x67\x21\xd7\xd4" - "\x0e\x46\xcf\xe5\x80\xdf\xc9\xb9\xba\x54\x9b\x46\x2f\x3b\x45\xfc\x2f\xaf" - "\xad\xc0\x17\x56\xdd\x52\x42\x57\x45\x70\x14\xe5\xbe\x67\xaa\xde\x69\x75" - "\x30\x0d\xf9\xa2\xc4\x63\x4d\x7a\x39\xef\x14\x62\x18\x33\x44\xa1\xf9\xc1" - "\x52\xd1\xb6\x72\x21\x98\xf8\xab\x16\x1b\x7b\x37\x65\xe3\xc5\x11\x00\xf6" - "\x36\x1f\xd8\x5f\xd8\x9f\x43\xa8\xce\x9d\xbf\x5e\xd6\x2d\xfa\x0a\xc2\x01" - "\x54\xc2\xd9\x81\x54\x55\xb5\x26\xf8\x88\x37\xf5\xfe\xe0\xef\x4a\x34\x81" - "\xdc\x5a\xb3\x71\x46\x27\xe3\xcd\x24\xf6\x1b\xf1\xe2\x0f\xc2\xa1\x39\x53" - "\x5b\xc5\x38\x46\x8e\x67\x4c\xd9\xdd\xe4\x37\x06\x03\x16\xf1\x1d\x7a\xba" - "\x2d\xc1\xe4\x03\x1a\x58\xe5\x29\x5a\x29\x06\x69\x61\x7a\xd8\xa9\x05\x9f" - "\xc1\xa2\x45\x9c\x17\xad\x52\x69\x33\xdc\x18\x8d\x15\xa6\x5e\xcd\x94\xf4" - "\x45\xbb\x9f\xc2\x7b\x85\x00\x61\xb0\x1a\xdc\x3c\x86\xaa\x9f\x5c\x04\xb3" - "\x90\x0b\x35\x64\xff\xd9\xe3\xac\xf2\xf2\xeb\x3a\x63\x02\x01\x02"; -#endif - -OSStatus QSslSocketBackendPrivate::ReadCallback(QSslSocketBackendPrivate *socket, - char *data, size_t *dataLength) -{ - Q_ASSERT(socket); - Q_ASSERT(data); - Q_ASSERT(dataLength); - - QTcpSocket *plainSocket = socket->plainSocket; - Q_ASSERT(plainSocket); - - if (socket->isHandshakeComplete()) { - // Check if it's a renegotiation attempt, when the handshake is complete, the - // session state is 'kSSLConnected': - SSLSessionState currentState = kSSLConnected; - const OSStatus result = SSLGetSessionState(socket->context, ¤tState); - if (result != noErr) { - *dataLength = 0; - return result; - } - - if (currentState == kSSLHandshake) { - // Renegotiation detected, don't allow read more yet - 'transmit' - // will notice this and will call 'startHandshake': - *dataLength = 0; - socket->renegotiating = true; - return errSSLWouldBlock; - } - } - - const qint64 bytes = plainSocket->read(data, *dataLength); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "read" << bytes; -#endif - if (bytes < 0) { - *dataLength = 0; - return errSecIO; - } - - const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : errSecSuccess; - *dataLength = bytes; - - return err; -} - -OSStatus QSslSocketBackendPrivate::WriteCallback(QSslSocketBackendPrivate *socket, - const char *data, size_t *dataLength) -{ - Q_ASSERT(socket); - Q_ASSERT(data); - Q_ASSERT(dataLength); - - QTcpSocket *plainSocket = socket->plainSocket; - Q_ASSERT(plainSocket); - - const qint64 bytes = plainSocket->write(data, *dataLength); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "write" << bytes; -#endif - if (bytes < 0) { - *dataLength = 0; - return errSecIO; - } - - const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : errSecSuccess; - *dataLength = bytes; - - return err; -} - -void QSslSocketPrivate::ensureInitialized() -{ - 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 QSecureTransportContext context(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(QSslSocketBackendPrivate::QSslCipher_from_SSLCipherSuite(cfCiphers.at(i))); - if (!ciph.isNull()) { - ciphers << ciph; - if (ciph.usedBits() >= 128) - defaultCiphers << ciph; - } - } - - setDefaultSupportedCiphers(ciphers); - setDefaultCiphers(defaultCiphers); - - if (!s_loadRootCertsOnDemand) - setDefaultCaCertificates(systemCaCertificates()); - } else { - s_loadedCiphersAndCerts = false; - } - -} - -long QSslSocketPrivate::sslLibraryVersionNumber() -{ - return 0; -} - -QString QSslSocketPrivate::sslLibraryVersionString() -{ - return QLatin1String("Secure Transport, ") + QSysInfo::prettyProductName(); -} - -long QSslSocketPrivate::sslLibraryBuildVersionNumber() -{ - return 0; -} - -QString QSslSocketPrivate::sslLibraryBuildVersionString() -{ - return sslLibraryVersionString(); -} - -bool QSslSocketPrivate::supportsSsl() -{ - return true; -} - -void QSslSocketPrivate::resetDefaultCiphers() -{ - Q_UNIMPLEMENTED(); -} - -void QSslSocketPrivate::resetDefaultEllipticCurves() -{ - // No public API for this (?). - Q_UNIMPLEMENTED(); -} - -QSslSocketBackendPrivate::QSslSocketBackendPrivate() - : context(nullptr) -{ -} - -QSslSocketBackendPrivate::~QSslSocketBackendPrivate() -{ - destroySslContext(); -} - -void QSslSocketBackendPrivate::continueHandshake() -{ -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "connection encrypted"; -#endif - Q_Q(QSslSocket); - connectionEncrypted = true; - -#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13_4, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0) - // Unlike OpenSSL, Secure Transport does not allow to negotiate protocols via - // a callback during handshake. We can only set our list of preferred protocols - // (and send it during handshake) and then receive what our peer has sent to us. - // And here we can finally try to find a match (if any). - if (__builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) { - const auto &requestedProtocols = configuration.nextAllowedProtocols; - if (const int requestedCount = requestedProtocols.size()) { - configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNone; - configuration.nextNegotiatedProtocol.clear(); - - QCFType<CFArrayRef> cfArray; - const OSStatus result = SSLCopyALPNProtocols(context, &cfArray); - if (result == errSecSuccess && cfArray && CFArrayGetCount(cfArray)) { - const int size = CFArrayGetCount(cfArray); - QList<QString> peerProtocols(size); - for (int i = 0; i < size; ++i) - peerProtocols[i] = QString::fromCFString((CFStringRef)CFArrayGetValueAtIndex(cfArray, i)); - - for (int i = 0; i < requestedCount; ++i) { - const auto requestedName = QString::fromLatin1(requestedProtocols[i]); - for (int j = 0; j < size; ++j) { - if (requestedName == peerProtocols[j]) { - configuration.nextNegotiatedProtocol = requestedName.toLatin1(); - configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNegotiated; - break; - } - } - if (configuration.nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNegotiated) - break; - } - } - } - } -#endif // QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE - - if (!renegotiating) - emit q->encrypted(); - - if (autoStartHandshake && pendingClose) { - pendingClose = false; - q->disconnectFromHost(); - } -} - -void QSslSocketBackendPrivate::disconnected() -{ - if (plainSocket->bytesAvailable() <= 0) - destroySslContext(); - // If there is still buffered data in the plain socket, don't destroy the ssl context yet. - // It will be destroyed when the socket is deleted. -} - -void QSslSocketBackendPrivate::disconnectFromHost() -{ - if (context) { - if (!shutdown) { - SSLClose(context); - shutdown = true; - } - } - plainSocket->disconnectFromHost(); -} - -QSslCipher QSslSocketBackendPrivate::sessionCipher() const -{ - SSLCipherSuite cipher = 0; - if (context && SSLGetNegotiatedCipher(context, &cipher) == errSecSuccess) - return QSslCipher_from_SSLCipherSuite(cipher); - - return QSslCipher(); -} - -QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const -{ - if (!context) - return QSsl::UnknownProtocol; - - SSLProtocol protocol = kSSLProtocolUnknown; - const OSStatus err = SSLGetNegotiatedProtocolVersion(context, &protocol); - if (err != errSecSuccess) { - qCWarning(lcSsl) << "SSLGetNegotiatedProtocolVersion failed:" << err; - return QSsl::UnknownProtocol; - } - - switch (protocol) { - case kTLSProtocol1: - return QSsl::TlsV1_0; - case kTLSProtocol11: - return QSsl::TlsV1_1; - case kTLSProtocol12: - return QSsl::TlsV1_2; - case kTLSProtocol13: - return QSsl::TlsV1_3; - default: - return QSsl::UnknownProtocol; - } -} - -void QSslSocketBackendPrivate::startClientEncryption() -{ - if (!initSslContext()) { - // Error description/code were set, 'error' emitted - // by initSslContext, but OpenSSL socket also sets error - // emits a signal twice, so ... - setErrorAndEmit(QAbstractSocket::SslInternalError, QStringLiteral("Unable to init SSL Context")); - return; - } - - startHandshake(); -} - -void QSslSocketBackendPrivate::startServerEncryption() -{ - if (!initSslContext()) { - // Error description/code were set, 'error' emitted - // by initSslContext, but OpenSSL socket also sets error - // emits a signal twice, so ... - setErrorAndEmit(QAbstractSocket::SslInternalError, QStringLiteral("Unable to init SSL Context")); - return; - } - - startHandshake(); -} - -void QSslSocketBackendPrivate::transmit() -{ - Q_Q(QSslSocket); - - // If we don't have any SSL context, don't bother transmitting. - // Edit: if SSL session closed, don't bother either. - if (!context || shutdown) - return; - - if (!isHandshakeComplete()) - startHandshake(); - - if (isHandshakeComplete() && !writeBuffer.isEmpty()) { - qint64 totalBytesWritten = 0; - while (writeBuffer.nextDataBlockSize() > 0 && context) { - const size_t nextDataBlockSize = writeBuffer.nextDataBlockSize(); - size_t writtenBytes = 0; - const OSStatus err = SSLWrite(context, writeBuffer.readPointer(), nextDataBlockSize, &writtenBytes); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "SSLWrite returned" << err; -#endif - if (err != errSecSuccess && err != errSSLWouldBlock) { - setErrorAndEmit(QAbstractSocket::SslInternalError, - QStringLiteral("SSLWrite failed: %1").arg(err)); - break; - } - - if (writtenBytes) { - writeBuffer.free(writtenBytes); - totalBytesWritten += writtenBytes; - } - - if (writtenBytes < nextDataBlockSize) - break; - } - - if (totalBytesWritten > 0) { - // Don't emit bytesWritten() recursively. - if (!emittedBytesWritten) { - emittedBytesWritten = true; - emit q->bytesWritten(totalBytesWritten); - emittedBytesWritten = false; - } - emit q->channelBytesWritten(0, totalBytesWritten); - } - } - - if (isHandshakeComplete()) { - QVarLengthArray<char, 4096> data; - while (context && (!readBufferMaxSize || buffer.size() < readBufferMaxSize)) { - size_t readBytes = 0; - data.resize(4096); - const OSStatus err = SSLRead(context, data.data(), data.size(), &readBytes); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "SSLRead returned" << err; -#endif - if (err == errSSLClosedGraceful) { - shutdown = true; // the other side shut down, make sure we do not send shutdown ourselves - setErrorAndEmit(QAbstractSocket::RemoteHostClosedError, - QSslSocket::tr("The TLS/SSL connection has been closed")); - break; - } else if (err != errSecSuccess && err != errSSLWouldBlock) { - setErrorAndEmit(QAbstractSocket::SslInternalError, - QStringLiteral("SSLRead failed: %1").arg(err)); - break; - } - - if (err == errSSLWouldBlock && renegotiating) { - startHandshake(); - break; - } - - if (readBytes) { - buffer.append(data.constData(), readBytes); - if (readyReadEmittedPointer) - *readyReadEmittedPointer = true; - emit q->readyRead(); - emit q->channelReadyRead(0); - } - - if (err == errSSLWouldBlock) - break; - } - } -} - - -QList<QSslError> (QSslSocketBackendPrivate::verify)(QList<QSslCertificate> certificateChain, const QString &hostName) -{ - Q_UNIMPLEMENTED(); - Q_UNUSED(certificateChain); - Q_UNUSED(hostName); - - QList<QSslError> errors; - errors << QSslError(QSslError::UnspecifiedError); - - return errors; -} - -bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device, - QSslKey *key, QSslCertificate *cert, - QList<QSslCertificate> *caCertificates, - const QByteArray &passPhrase) -{ - Q_UNIMPLEMENTED(); - Q_UNUSED(device); - Q_UNUSED(key); - Q_UNUSED(cert); - Q_UNUSED(caCertificates); - Q_UNUSED(passPhrase); - return false; -} - -QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSLCipherSuite(SSLCipherSuite cipher) -{ - QSslCipher ciph; - 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: - ciph.d->name = QLatin1String("AES128-SHA"); - break; - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: - ciph.d->name = QLatin1String("DHE-RSA-AES128-SHA"); - break; - case TLS_RSA_WITH_AES_256_CBC_SHA: - ciph.d->name = QLatin1String("AES256-SHA"); - break; - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: - ciph.d->name = QLatin1String("DHE-RSA-AES256-SHA"); - break; - - // ECDSA addenda, RFC 4492 - case TLS_ECDH_ECDSA_WITH_NULL_SHA: - ciph.d->name = QLatin1String("ECDH-ECDSA-NULL-SHA"); - break; - case TLS_ECDH_ECDSA_WITH_RC4_128_SHA: - ciph.d->name = QLatin1String("ECDH-ECDSA-RC4-SHA"); - break; - case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: - ciph.d->name = QLatin1String("ECDH-ECDSA-DES-CBC3-SHA"); - break; - case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: - ciph.d->name = QLatin1String("ECDH-ECDSA-AES128-SHA"); - break; - case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: - ciph.d->name = QLatin1String("ECDH-ECDSA-AES256-SHA"); - break; - case TLS_ECDHE_ECDSA_WITH_NULL_SHA: - ciph.d->name = QLatin1String("ECDHE-ECDSA-NULL-SHA"); - break; - case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: - ciph.d->name = QLatin1String("ECDHE-ECDSA-RC4-SHA"); - break; - case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: - ciph.d->name = QLatin1String("ECDHE-ECDSA-DES-CBC3-SHA"); - break; - case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: - ciph.d->name = QLatin1String("ECDHE-ECDSA-AES128-SHA"); - break; - case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: - ciph.d->name = QLatin1String("ECDHE-ECDSA-AES256-SHA"); - break; - case TLS_ECDH_RSA_WITH_NULL_SHA: - ciph.d->name = QLatin1String("ECDH-RSA-NULL-SHA"); - break; - case TLS_ECDH_RSA_WITH_RC4_128_SHA: - ciph.d->name = QLatin1String("ECDH-RSA-RC4-SHA"); - break; - case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: - ciph.d->name = QLatin1String("ECDH-RSA-DES-CBC3-SHA"); - break; - case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: - ciph.d->name = QLatin1String("ECDH-RSA-AES128-SHA"); - break; - case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: - ciph.d->name = QLatin1String("ECDH-RSA-AES256-SHA"); - break; - case TLS_ECDHE_RSA_WITH_NULL_SHA: - ciph.d->name = QLatin1String("ECDHE-RSA-NULL-SHA"); - break; - case TLS_ECDHE_RSA_WITH_RC4_128_SHA: - ciph.d->name = QLatin1String("ECDHE-RSA-RC4-SHA"); - break; - case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: - ciph.d->name = QLatin1String("ECDHE-RSA-DES-CBC3-SHA"); - break; - case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: - ciph.d->name = QLatin1String("ECDHE-RSA-AES128-SHA"); - break; - case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: - ciph.d->name = QLatin1String("ECDHE-RSA-AES256-SHA"); - break; - - // TLS 1.2 addenda, RFC 5246 - case TLS_RSA_WITH_3DES_EDE_CBC_SHA: - ciph.d->name = QLatin1String("DES-CBC3-SHA"); - break; - case TLS_RSA_WITH_AES_128_CBC_SHA256: - ciph.d->name = QLatin1String("AES128-SHA256"); - break; - case TLS_RSA_WITH_AES_256_CBC_SHA256: - ciph.d->name = QLatin1String("AES256-SHA256"); - break; - case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: - ciph.d->name = QLatin1String("DHE-RSA-DES-CBC3-SHA"); - break; - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: - ciph.d->name = QLatin1String("DHE-RSA-AES128-SHA256"); - break; - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: - ciph.d->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: - ciph.d->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: - ciph.d->name = QLatin1String("ECDHE-ECDSA-AES128-SHA256"); - break; - case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: - ciph.d->name = QLatin1String("ECDHE-ECDSA-AES256-SHA384"); - break; - case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: - ciph.d->name = QLatin1String("ECDH-ECDSA-AES128-SHA256"); - break; - case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: - ciph.d->name = QLatin1String("ECDH-ECDSA-AES256-SHA384"); - break; - case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: - ciph.d->name = QLatin1String("ECDHE-RSA-AES128-SHA256"); - break; - case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: - ciph.d->name = QLatin1String("ECDHE-RSA-AES256-SHA384"); - break; - case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: - ciph.d->name = QLatin1String("ECDH-RSA-AES128-SHA256"); - break; - case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: - ciph.d->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: - ciph.d->name = QLatin1String("ECDHE-RSA-AES256-GCM-SHA384"); - break; - - default: - return ciph; - } - ciph.d->isNull = false; - - // protocol - ciph.d->protocol = QSsl::TlsV1_2; - ciph.d->protocolString = QLatin1String("TLSv1.2"); - - 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; -} -SSLCipherSuite QSslSocketBackendPrivate::SSLCipherSuite_from_QSslCipher(const QSslCipher &ciph) -{ - if (ciph.d->name == QLatin1String("AES128-SHA")) - return TLS_RSA_WITH_AES_128_CBC_SHA; - if (ciph.d->name == QLatin1String("DHE-RSA-AES128-SHA")) - return TLS_DHE_RSA_WITH_AES_128_CBC_SHA; - if (ciph.d->name == QLatin1String("AES256-SHA")) - return TLS_RSA_WITH_AES_256_CBC_SHA; - if (ciph.d->name == QLatin1String("DHE-RSA-AES256-SHA")) - return TLS_DHE_RSA_WITH_AES_256_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-NULL-SHA")) - return TLS_ECDH_ECDSA_WITH_NULL_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-RC4-SHA")) - return TLS_ECDH_ECDSA_WITH_RC4_128_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-DES-CBC3-SHA")) - return TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-AES128-SHA")) - return TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-AES256-SHA")) - return TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-RC4-SHA")) - return TLS_ECDHE_ECDSA_WITH_RC4_128_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-DES-CBC3-SHA")) - return TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-AES128-SHA")) - return TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-AES256-SHA")) - return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-NULL-SHA")) - return TLS_ECDH_RSA_WITH_NULL_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-RC4-SHA")) - return TLS_ECDH_RSA_WITH_RC4_128_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-DES-CBC3-SHA")) - return TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-AES128-SHA")) - return TLS_ECDH_RSA_WITH_AES_128_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-AES256-SHA")) - return TLS_ECDH_RSA_WITH_AES_256_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-RC4-SHA")) - return TLS_ECDHE_RSA_WITH_RC4_128_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-DES-CBC3-SHA")) - return TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-AES128-SHA")) - return TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA; - if (ciph.d->name == QLatin1String("ECDH-RSA-AES256-SHA")) - return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA; - if (ciph.d->name == QLatin1String("DES-CBC3-SHA")) - return TLS_RSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.d->name == QLatin1String("AES128-SHA256")) - return TLS_RSA_WITH_AES_128_CBC_SHA256; - if (ciph.d->name == QLatin1String("AES256-SHA256")) - return TLS_RSA_WITH_AES_256_CBC_SHA256; - if (ciph.d->name == QLatin1String("DHE-RSA-DES-CBC3-SHA")) - return TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; - if (ciph.d->name == QLatin1String("DHE-RSA-AES128-SHA256")) - return TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; - if (ciph.d->name == QLatin1String("DHE-RSA-AES256-SHA256")) - return TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; - if (ciph.d->name == QLatin1String("AES256-GCM-SHA384")) - return TLS_RSA_WITH_AES_256_GCM_SHA384; - if (ciph.d->name == QLatin1String("ECDHE-ECDSA-AES128-SHA256")) - return TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; - if (ciph.d->name == QLatin1String("ECDHE-ECDSA-AES256-SHA384")) - return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-AES128-SHA256")) - return TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256; - if (ciph.d->name == QLatin1String("ECDH-ECDSA-AES256-SHA384")) - return TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384; - if (ciph.d->name == QLatin1String("ECDHE-RSA-AES128-SHA256")) - return TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256; - if (ciph.d->name == QLatin1String("ECDHE-RSA-AES256-SHA384")) - return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384; - if (ciph.d->name == QLatin1String("ECDHE-RSA-AES256-SHA384")) - return TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256; - if (ciph.d->name == QLatin1String("ECDHE-RSA-AES256-GCM-SHA384")) - return TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384; - return 0; -} - -bool QSslSocketBackendPrivate::initSslContext() -{ - Q_Q(QSslSocket); - - Q_ASSERT_X(!context, Q_FUNC_INFO, "invalid socket state, context is not null"); - Q_ASSERT(plainSocket); - - context.reset(qt_createSecureTransportContext(mode)); - if (!context) { - setErrorAndEmit(QAbstractSocket::SslInternalError, QStringLiteral("SSLCreateContext failed")); - return false; - } - - const OSStatus err = SSLSetIOFuncs(context, - reinterpret_cast<SSLReadFunc>(&QSslSocketBackendPrivate::ReadCallback), - reinterpret_cast<SSLWriteFunc>(&QSslSocketBackendPrivate::WriteCallback)); - if (err != errSecSuccess) { - destroySslContext(); - setErrorAndEmit(QAbstractSocket::SslInternalError, - QStringLiteral("SSLSetIOFuncs failed: %1").arg(err)); - return false; - } - - SSLSetConnection(context, this); - - if (mode == QSslSocket::SslServerMode - && !configuration.localCertificateChain.isEmpty()) { - QString errorDescription; - QAbstractSocket::SocketError errorCode = QAbstractSocket::UnknownSocketError; - if (!setSessionCertificate(errorDescription, errorCode)) { - destroySslContext(); - setErrorAndEmit(errorCode, errorDescription); - return false; - } - } - - if (!setSessionProtocol()) { - destroySslContext(); - setErrorAndEmit(QAbstractSocket::SslInternalError, QStringLiteral("Failed to set protocol version")); - return false; - } - -#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13_4, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0) - if (__builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) { - const auto protocolNames = configuration.nextAllowedProtocols; - QCFType<CFMutableArrayRef> cfNames(CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks)); - if (cfNames) { - for (const QByteArray &name : protocolNames) { - if (name.size() > 255) { - qCWarning(lcSsl) << "TLS ALPN extension" << name - << "is too long and will be ignored."; - continue; - } else if (name.isEmpty()) { - continue; - } - QCFString cfName(QString::fromLatin1(name).toCFString()); - CFArrayAppendValue(cfNames, cfName); - } - - if (CFArrayGetCount(cfNames)) { - // Up to the application layer to check that negotiation - // failed, and handle this non-TLS error, we do not handle - // the result of this call as an error: - if (SSLSetALPNProtocols(context, cfNames) != errSecSuccess) - qCWarning(lcSsl) << "SSLSetALPNProtocols failed - too long protocol names?"; - } - } else { - qCWarning(lcSsl) << "failed to allocate ALPN names array"; - } - } -#endif // QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE - - if (mode == QSslSocket::SslClientMode) { - // enable Server Name Indication (SNI) - QString tlsHostName(verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName); - if (tlsHostName.isEmpty()) - tlsHostName = hostName; - - const QByteArray ace(QUrl::toAce(tlsHostName)); - SSLSetPeerDomainName(context, ace.data(), ace.size()); - // tell SecureTransport we handle peer verification ourselves - OSStatus err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnServerAuth, true); - if (err == errSecSuccess) - err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnCertRequested, true); - - if (err != errSecSuccess) { - destroySslContext(); - setErrorAndEmit(QSslSocket::SslInternalError, - QStringLiteral("SSLSetSessionOption failed: %1").arg(err)); - return false; - } - // - } else { - if (configuration.peerVerifyMode != QSslSocket::VerifyNone) { - // kAlwaysAuthenticate - always fails even if we set break on client auth. - OSStatus err = SSLSetClientSideAuthenticate(context, kTryAuthenticate); - if (err == errSecSuccess) { - // We'd like to verify peer ourselves, otherwise handshake will - // most probably fail before we can do anything. - err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnClientAuth, true); - } - - if (err != errSecSuccess) { - destroySslContext(); - setErrorAndEmit(QAbstractSocket::SslInternalError, - QStringLiteral("failed to set SSL context option in server mode: %1").arg(err)); - return false; - } - } -#if !defined(QT_PLATFORM_UIKIT) - // No SSLSetDiffieHellmanParams on iOS; calling it is optional according to docs. - SSLSetDiffieHellmanParams(context, dhparam, sizeof(dhparam)); -#endif - } - if (configuration.ciphers.size() > 0) { - QVector<SSLCipherSuite> cfCiphers; - for (const QSslCipher &cipher : configuration.ciphers) { - if (auto sslCipher = QSslSocketBackendPrivate::SSLCipherSuite_from_QSslCipher(cipher)) - cfCiphers << sslCipher; - } - if (cfCiphers.size() == 0) { - qCWarning(lcSsl) << "failed to add any of the requested ciphers from the configuration"; - return false; - } - OSStatus err = SSLSetEnabledCiphers(context, cfCiphers.data(), cfCiphers.size()); - if (err != errSecSuccess) { - qCWarning(lcSsl) << "failed to set the ciphers from the configuration"; - return false; - } - } - return true; -} - -void QSslSocketBackendPrivate::destroySslContext() -{ - context.reset(nullptr); -} - -bool QSslSocketBackendPrivate::setSessionCertificate(QString &errorDescription, QAbstractSocket::SocketError &errorCode) -{ - Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)"); - - QSslCertificate localCertificate; - if (!configuration.localCertificateChain.isEmpty()) - localCertificate = configuration.localCertificateChain.at(0); - - if (!localCertificate.isNull()) { - // Require a private key as well. - if (configuration.privateKey.isNull()) { - errorCode = QAbstractSocket::SslInvalidUserDataError; - errorDescription = QStringLiteral("Cannot provide a certificate with no key"); - return false; - } - - // import certificates and key - const QString passPhrase(QString::fromLatin1("foobar")); - QCFType<CFDataRef> pkcs12 = _q_makePkcs12(configuration.localCertificateChain, - configuration.privateKey, passPhrase).toCFData(); - QCFType<CFStringRef> password = passPhrase.toCFString(); - const void *keys[2] = { kSecImportExportPassphrase }; - const void *values[2] = { password }; - CFIndex nKeys = 1; -#ifdef Q_OS_MACOS - bool envOk = false; - const int env = qEnvironmentVariableIntValue("QT_SSL_USE_TEMPORARY_KEYCHAIN", &envOk); - if (envOk && env) { - static const EphemeralSecKeychain temporaryKeychain; - if (temporaryKeychain.keychain) { - nKeys = 2; - keys[1] = kSecImportExportKeychain; - values[1] = temporaryKeychain.keychain; - } - } -#endif - QCFType<CFDictionaryRef> options = CFDictionaryCreate(nullptr, keys, values, nKeys, - nullptr, nullptr); - QCFType<CFArrayRef> items; - OSStatus err = SecPKCS12Import(pkcs12, options, &items); - if (err != errSecSuccess) { -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcSsl) << plainSocket - << QStringLiteral("SecPKCS12Import failed: %1").arg(err); -#endif - errorCode = QAbstractSocket::SslInvalidUserDataError; - errorDescription = QStringLiteral("SecPKCS12Import failed: %1").arg(err); - return false; - } - - if (!CFArrayGetCount(items)) { -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcSsl) << plainSocket << "SecPKCS12Import returned no items"; -#endif - errorCode = QAbstractSocket::SslInvalidUserDataError; - errorDescription = QStringLiteral("SecPKCS12Import returned no items"); - return false; - } - - CFDictionaryRef import = (CFDictionaryRef)CFArrayGetValueAtIndex(items, 0); - SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(import, kSecImportItemIdentity); - if (!identity) { -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcSsl) << plainSocket << "SecPKCS12Import returned no identity"; -#endif - errorCode = QAbstractSocket::SslInvalidUserDataError; - errorDescription = QStringLiteral("SecPKCS12Import returned no identity"); - return false; - } - - QCFType<CFMutableArrayRef> certs = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks); - if (!certs) { - errorCode = QAbstractSocket::SslInternalError; - errorDescription = QStringLiteral("Failed to allocate certificates array"); - return false; - } - - CFArrayAppendValue(certs, identity); - - CFArrayRef chain = (CFArrayRef)CFDictionaryGetValue(import, kSecImportItemCertChain); - if (chain) { - for (CFIndex i = 1, e = CFArrayGetCount(chain); i < e; ++i) - CFArrayAppendValue(certs, CFArrayGetValueAtIndex(chain, i)); - } - - err = SSLSetCertificate(context, certs); - if (err != errSecSuccess) { -#ifdef QSSLSOCKET_DEBUG - qCWarning(lcSsl) << plainSocket - << QStringLiteral("Cannot set certificate and key: %1").arg(err); -#endif - errorCode = QAbstractSocket::SslInvalidUserDataError; - errorDescription = QStringLiteral("Cannot set certificate and key: %1").arg(err); - return false; - } - } - - return true; -} - -bool QSslSocketBackendPrivate::setSessionProtocol() -{ - Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)"); - - // SecureTransport has kTLSProtocol13 constant and also, kTLSProtocolMaxSupported. - // Calling SSLSetProtocolVersionMax/Min with any of these two constants results - // in errInvalidParam and a failure to set the protocol version. This means - // no TLS 1.3 on macOS and iOS. - switch (configuration.protocol) { - case QSsl::TlsV1_3: - case QSsl::TlsV1_3OrLater: - qCWarning(lcSsl) << plainSocket << "SecureTransport does not support TLS 1.3"; - return false; - default:; - } - - OSStatus err = errSecSuccess; - - if (configuration.protocol == QSsl::TlsV1_0) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.0"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol1); - if (err == errSecSuccess) - err = SSLSetProtocolVersionMax(context, kTLSProtocol1); - } else if (configuration.protocol == QSsl::TlsV1_1) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.1"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol11); - if (err == errSecSuccess) - err = SSLSetProtocolVersionMax(context, kTLSProtocol11); - } else if (configuration.protocol == QSsl::TlsV1_2) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.2"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol12); - if (err == errSecSuccess) - err = SSLSetProtocolVersionMax(context, kTLSProtocol12); - } else if (configuration.protocol == QSsl::AnyProtocol) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "requesting : any"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol1); - } else if (configuration.protocol == QSsl::SecureProtocols) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "requesting : TLSv1 - TLSv1.2"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol1); - } else if (configuration.protocol == QSsl::TlsV1_0OrLater) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "requesting : TLSv1 - TLSv1.2"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol1); - } else if (configuration.protocol == QSsl::TlsV1_1OrLater) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.1 - TLSv1.2"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol11); - } else if (configuration.protocol == QSsl::TlsV1_2OrLater) { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.2"; - #endif - err = SSLSetProtocolVersionMin(context, kTLSProtocol12); - } else { - #ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "no protocol version found in the configuration"; - #endif - return false; - } - - return err == errSecSuccess; -} - -bool QSslSocketBackendPrivate::canIgnoreTrustVerificationFailure() const -{ - const QSslSocket::PeerVerifyMode verifyMode = configuration.peerVerifyMode; - return mode == QSslSocket::SslServerMode - && (verifyMode == QSslSocket::QueryPeer - || verifyMode == QSslSocket::AutoVerifyPeer - || verifyMode == QSslSocket::VerifyNone); -} - -bool QSslSocketBackendPrivate::verifySessionProtocol() const -{ - bool protocolOk = false; - if (configuration.protocol == QSsl::AnyProtocol) - protocolOk = true; - else if (configuration.protocol == QSsl::SecureProtocols) - protocolOk = (sessionProtocol() >= QSsl::TlsV1_0); - else if (configuration.protocol == QSsl::TlsV1_0OrLater) - protocolOk = (sessionProtocol() >= QSsl::TlsV1_0); - else if (configuration.protocol == QSsl::TlsV1_1OrLater) - protocolOk = (sessionProtocol() >= QSsl::TlsV1_1); - else if (configuration.protocol == QSsl::TlsV1_2OrLater) - protocolOk = (sessionProtocol() >= QSsl::TlsV1_2); - else if (configuration.protocol == QSsl::TlsV1_3OrLater) - protocolOk = (sessionProtocol() >= QSsl::TlsV1_3OrLater); - else - protocolOk = (sessionProtocol() == configuration.protocol); - - return protocolOk; -} - -bool QSslSocketBackendPrivate::verifyPeerTrust() -{ - Q_Q(QSslSocket); - - const QSslSocket::PeerVerifyMode verifyMode = configuration.peerVerifyMode; - const bool canIgnoreVerify = canIgnoreTrustVerificationFailure(); - - Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)"); - Q_ASSERT(plainSocket); - - QCFType<SecTrustRef> trust; - OSStatus err = SSLCopyPeerTrust(context, &trust); - // !trust - SSLCopyPeerTrust can return errSecSuccess but null trust. - if (err != errSecSuccess || !trust) { - if (!canIgnoreVerify) { - setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QStringLiteral("Failed to obtain peer trust: %1").arg(err)); - plainSocket->disconnectFromHost(); - return false; - } else { - return true; - } - } - - QList<QSslError> errors; - - // Store certificates. - // Apple's docs say SetTrustEvaluate must be called before - // SecTrustGetCertificateAtIndex, but this results - // in 'kSecTrustResultRecoverableTrustFailure', so - // here we just ignore 'res' (later we'll use SetAnchor etc. - // and evaluate again). - SecTrustResultType res = kSecTrustResultInvalid; - err = SecTrustEvaluate(trust, &res); - if (err != errSecSuccess) { - // We can not ignore this, it's not even about trust verification - // probably ... - setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QStringLiteral("SecTrustEvaluate failed: %1").arg(err)); - plainSocket->disconnectFromHost(); - return false; - } - - configuration.peerCertificate.clear(); - configuration.peerCertificateChain.clear(); - - const CFIndex certCount = SecTrustGetCertificateCount(trust); - for (CFIndex i = 0; i < certCount; ++i) { - SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i); - QCFType<CFDataRef> derData = SecCertificateCopyData(cert); - configuration.peerCertificateChain << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der); - } - - if (configuration.peerCertificateChain.size()) - configuration.peerCertificate = configuration.peerCertificateChain.at(0); - - // Check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer): - for (const QSslCertificate &cert : qAsConst(configuration.peerCertificateChain)) { - if (QSslCertificatePrivate::isBlacklisted(cert) && !canIgnoreVerify) { - const QSslError error(QSslError::CertificateBlacklisted, cert); - errors << error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - } - - const bool doVerifyPeer = verifyMode == QSslSocket::VerifyPeer - || (verifyMode == QSslSocket::AutoVerifyPeer - && mode == 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) { - const QString peerName(verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName); - if (!isMatchingHostname(configuration.peerCertificate, peerName) && !canIgnoreVerify) { - // No matches in common names or alternate names. - const QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate); - errors << error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - } - } else { - // No peer certificate presented. Report as error if the socket - // expected one. - if (doVerifyPeer && !canIgnoreVerify) { - const QSslError error(QSslError::NoPeerCertificate); - errors << error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; - } - } - - // verify certificate chain - QCFType<CFMutableArrayRef> certArray = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks); - for (const QSslCertificate &cert : qAsConst(configuration.caCertificates)) { - QCFType<CFDataRef> certData = cert.d->derData.toCFData(); - if (QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, certData)) - CFArrayAppendValue(certArray, secRef); - else - qCWarning(lcSsl, "Failed to create SecCertificate from QSslCertificate"); - } - - SecTrustSetAnchorCertificates(trust, certArray); - - // By default SecTrustEvaluate uses both CA certificates provided in - // QSslConfiguration and the ones from the system database. This behavior can - // be unexpected if a user's code tries to limit the trusted CAs to those - // explicitly set in QSslConfiguration. - // Since on macOS we initialize the default QSslConfiguration copying the - // system CA certificates (using SecTrustSettingsCopyCertificates) we can - // call SecTrustSetAnchorCertificatesOnly(trust, true) to force SecTrustEvaluate - // to use anchors only from our QSslConfiguration. - // Unfortunately, SecTrustSettingsCopyCertificates is not available on iOS - // and the default QSslConfiguration always has an empty list of system CA - // certificates. This leaves no way to provide client code with access to the - // actual system CA certificate list (which most use-cases need) other than - // by letting SecTrustEvaluate fall through to the system list; so, in this case - // (even though the client code may have provided its own certs), we retain - // the default behavior. Note, with macOS SDK below 10.12 using 'trust my - // anchors only' may result in some valid chains rejected, apparently the - // ones containing intermediated certificates; so we use this functionality - // on more recent versions only. - - bool anchorsFromConfigurationOnly = false; - -#ifdef Q_OS_MACOS - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSSierra) - anchorsFromConfigurationOnly = true; -#endif // Q_OS_MACOS - - SecTrustSetAnchorCertificatesOnly(trust, anchorsFromConfigurationOnly); - - SecTrustResultType trustResult = kSecTrustResultInvalid; - SecTrustEvaluate(trust, &trustResult); - switch (trustResult) { - case kSecTrustResultUnspecified: - case kSecTrustResultProceed: - break; - default: - if (!canIgnoreVerify) { - const QSslError error(QSslError::CertificateUntrusted, configuration.peerCertificate); - errors << error; - emit q->peerVerifyError(error); - } - } - - // report errors - if (!errors.isEmpty() && !canIgnoreVerify) { - sslErrors = errors; - // checkSslErrors unconditionally emits sslErrors: - // a user's slot can abort/close/disconnect on this - // signal, so we also test the socket's state: - if (!checkSslErrors() || q->state() != QAbstractSocket::ConnectedState) - return false; - } else { - sslErrors.clear(); - } - - return true; -} - -/* - Copied verbatim from qsslsocket_openssl.cpp -*/ -bool QSslSocketBackendPrivate::checkSslErrors() -{ - Q_Q(QSslSocket); - if (sslErrors.isEmpty()) - return true; - - emit q->sslErrors(sslErrors); - - const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer - || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer - && mode == QSslSocket::SslClientMode); - const bool doEmitSslError = !verifyErrorsHaveBeenIgnored(); - // check whether we need to emit an SSL handshake error - if (doVerifyPeer && doEmitSslError) { - if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) { - pauseSocketNotifiers(q); - paused = true; - } else { - setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - sslErrors.constFirst().errorString()); - plainSocket->disconnectFromHost(); - } - return false; - } - - return true; -} - -bool QSslSocketBackendPrivate::startHandshake() -{ - Q_ASSERT(context); - Q_Q(QSslSocket); - - OSStatus err = SSLHandshake(context); -#ifdef QSSLSOCKET_DEBUG - qCDebug(lcSsl) << plainSocket << "SSLHandhake returned" << err; -#endif - - if (err == errSSLWouldBlock) { - // startHandshake has to be called again ... later. - return false; - } else if (err == errSSLServerAuthCompleted) { - // errSSLServerAuthCompleted is a define for errSSLPeerAuthCompleted, - // it works for both server/client modes. - // In future we'll evaluate peer's trust at this point, - // for now we just continue. - // if (!verifyPeerTrust()) - // ... - return startHandshake(); - } else if (err == errSSLClientCertRequested) { - Q_ASSERT(mode == QSslSocket::SslClientMode); - QString errorDescription; - QAbstractSocket::SocketError errorCode = QAbstractSocket::UnknownSocketError; - // setSessionCertificate does not fail if we have no certificate. - // Failure means a real error (invalid certificate, no private key, etc). - if (!setSessionCertificate(errorDescription, errorCode)) { - setErrorAndEmit(errorCode, errorDescription); - renegotiating = false; - return false; - } else { - // We try to resume a handshake, even if have no - // local certificates ... (up to server to deal with our failure). - return startHandshake(); - } - } else if (err != errSecSuccess) { - if (err == errSSLBadCert && canIgnoreTrustVerificationFailure()) { - // We're on the server side and client did not provide any - // certificate. This is the new 'nice' error returned by - // Security Framework after it was recently updated. - return startHandshake(); - } - - renegotiating = false; - setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, - QStringLiteral("SSLHandshake failed: %1").arg(err)); - plainSocket->disconnectFromHost(); - return false; - } - - // Connection aborted during handshake phase. - if (q->state() != QAbstractSocket::ConnectedState) { - qCDebug(lcSsl) << "connection aborted"; - renegotiating = false; - return false; - } - - // check protocol version ourselves, as Secure Transport does not enforce - // the requested min / max versions. - if (!verifySessionProtocol()) { - setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, QStringLiteral("Protocol version mismatch")); - plainSocket->disconnectFromHost(); - renegotiating = false; - return false; - } - - if (verifyPeerTrust()) { - continueHandshake(); - renegotiating = false; - return true; - } else { - renegotiating = false; - return false; - } -} - -QT_END_NAMESPACE |