// Copyright (C) 2021 The Qt Company Ltd. // Copyright (C) 2014 Jeremy LainĂ© // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qtls_st_p.h" #include "qtlsbackend_st_p.h" #include "qtlskey_st_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_MACOS #include #endif QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; // Defined in qsslsocket_qt.cpp. QByteArray _q_makePkcs12(const QList &certs, const QSslKey &key, const QString &passPhrase); namespace QTlsPrivate { // Defined in qtlsbackend_st.cpp QSslCipher QSslCipher_from_SSLCipherSuite(SSLCipherSuite cipher); 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(lcSecureTransport) << "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 = QLatin1StringView(uuidAsByteArray.data(), uuidAsByteArray.size()).mid(1, uuidAsByteArray.size() - 2); const QString keychainName = QDir::tempPath() + QDir::separator() + uuidAsString + ".keychain"_L1; // 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 cfName = keychainName.toCFString(); std::vector 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(lcSecureTransport) << "Failed to create a unique keychain name from" << "QDir::tempPath()"; return; } std::vector passUtf8(256); if (SecRandomCopyBytes(kSecRandomDefault, passUtf8.size(), &passUtf8[0])) { qCWarning(lcSecureTransport) << "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(lcSecureTransport) << "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(lcSecureTransport) << "SecKeychainSettings: failed to disable lock on sleep"; } #ifdef QSSLSOCKET_DEBUG if (keychain) { qCDebug(lcSecureTransport) << "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 void qt_releaseSecureTransportContext(SSLContextRef context) { if (context) CFRelease(context); } } // unnamed namespace // To be also used by qtlsbackend_st.cpp (thus not in unnamed namespace). 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(lcSecureTransport) << "SSLCreateContext failed"; return 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; } #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 TlsCryptographSecureTransport::ReadCallback(TlsCryptographSecureTransport *socket, char *data, size_t *dataLength) { Q_ASSERT(socket); Q_ASSERT(data); Q_ASSERT(dataLength); Q_ASSERT(socket->d); QTcpSocket *plainSocket = socket->d->plainTcpSocket(); 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(lcSecureTransport) << plainSocket << "read" << bytes; #endif if (bytes < 0) { *dataLength = 0; return errSecIO; } const OSStatus err = (size_t(bytes) < *dataLength) ? OSStatus(errSSLWouldBlock) : OSStatus(errSecSuccess); *dataLength = bytes; return err; } OSStatus TlsCryptographSecureTransport::WriteCallback(TlsCryptographSecureTransport *socket, const char *data, size_t *dataLength) { Q_ASSERT(socket); Q_ASSERT(data); Q_ASSERT(dataLength); Q_ASSERT(socket->d); QTcpSocket *plainSocket = socket->d->plainTcpSocket(); Q_ASSERT(plainSocket); const qint64 bytes = plainSocket->write(data, *dataLength); #ifdef QSSLSOCKET_DEBUG qCDebug(lcSecureTransport) << plainSocket << "write" << bytes; #endif if (bytes < 0) { *dataLength = 0; return errSecIO; } const OSStatus err = (size_t(bytes) < *dataLength) ? OSStatus(errSSLWouldBlock) : OSStatus(errSecSuccess); *dataLength = bytes; return err; } TlsCryptographSecureTransport::TlsCryptographSecureTransport() : context(nullptr) { } TlsCryptographSecureTransport::~TlsCryptographSecureTransport() { destroySslContext(); } void TlsCryptographSecureTransport::init(QSslSocket *qObj, QSslSocketPrivate *dObj) { Q_ASSERT(qObj); Q_ASSERT(dObj); q = qObj; d = dObj; renegotiating = false; shutdown = false; } void TlsCryptographSecureTransport::continueHandshake() { Q_ASSERT(q); Q_ASSERT(d); d->setEncrypted(true); #ifdef QSSLSOCKET_DEBUG qCDebug(lcSecureTransport) << d->plainTcpSocket() << "connection encrypted"; #endif #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). const auto &configuration = q->sslConfiguration(); if (__builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) { const auto &requestedProtocols = configuration.allowedNextProtocols(); if (const int requestedCount = requestedProtocols.size()) { QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNone); QTlsBackend::setNegotiatedProtocol(d, {}); QCFType cfArray; const OSStatus result = SSLCopyALPNProtocols(context, &cfArray); if (result == errSecSuccess && cfArray && CFArrayGetCount(cfArray)) { const int size = CFArrayGetCount(cfArray); QList 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]) { QTlsBackend::setNegotiatedProtocol(d, requestedName.toLatin1()); QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNegotiated); break; } } if (configuration.nextProtocolNegotiationStatus() == QSslConfiguration::NextProtocolNegotiationNegotiated) break; } } } } #endif // QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE if (!renegotiating) emit q->encrypted(); if (d->isAutoStartingHandshake() && d->isPendingClose()) { d->setPendingClose(false); q->disconnectFromHost(); } } void TlsCryptographSecureTransport::disconnected() { Q_ASSERT(d && d->plainTcpSocket()); d->setEncrypted(false); if (d->plainTcpSocket()->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 TlsCryptographSecureTransport::disconnectFromHost() { Q_ASSERT(d && d->plainTcpSocket()); if (context) { if (!shutdown) { SSLClose(context); shutdown = true; } } d->plainTcpSocket()->disconnectFromHost(); } QSslCipher TlsCryptographSecureTransport::sessionCipher() const { SSLCipherSuite cipher = 0; if (context && SSLGetNegotiatedCipher(context, &cipher) == errSecSuccess) return QSslCipher_from_SSLCipherSuite(cipher); return QSslCipher(); } QSsl::SslProtocol TlsCryptographSecureTransport::sessionProtocol() const { if (!context) return QSsl::UnknownProtocol; SSLProtocol protocol = kSSLProtocolUnknown; const OSStatus err = SSLGetNegotiatedProtocolVersion(context, &protocol); if (err != errSecSuccess) { qCWarning(lcSecureTransport) << "SSLGetNegotiatedProtocolVersion failed:" << err; return QSsl::UnknownProtocol; } switch (protocol) { QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED case kTLSProtocol1: return QSsl::TlsV1_0; case kTLSProtocol11: return QSsl::TlsV1_1; QT_WARNING_POP case kTLSProtocol12: return QSsl::TlsV1_2; case kTLSProtocol13: return QSsl::TlsV1_3; default: return QSsl::UnknownProtocol; } } void TlsCryptographSecureTransport::startClientEncryption() { if (!initSslContext()) { Q_ASSERT(d); // Error description/code were set, 'error' emitted // by initSslContext, but OpenSSL socket also sets error, // emits a signal twice, so ... setErrorAndEmit(d, QAbstractSocket::SslInternalError, QStringLiteral("Unable to init SSL Context")); return; } startHandshake(); } void TlsCryptographSecureTransport::startServerEncryption() { if (!initSslContext()) { // Error description/code were set, 'error' emitted // by initSslContext, but OpenSSL socket also sets error // emits a signal twice, so ... setErrorAndEmit(d, QAbstractSocket::SslInternalError, QStringLiteral("Unable to init SSL Context")); return; } startHandshake(); } void TlsCryptographSecureTransport::transmit() { Q_ASSERT(q); Q_ASSERT(d); // 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(); auto &writeBuffer = d->tlsWriteBuffer(); 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(lcSecureTransport) << d->plainTcpSocket() << "SSLWrite returned" << err; #endif if (err != errSecSuccess && err != errSSLWouldBlock) { setErrorAndEmit(d, 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. auto &emittedBytesWritten = d->tlsEmittedBytesWritten(); if (!emittedBytesWritten) { emittedBytesWritten = true; emit q->bytesWritten(totalBytesWritten); emittedBytesWritten = false; } emit q->channelBytesWritten(0, totalBytesWritten); } } auto &buffer = d->tlsBuffer(); const auto readBufferMaxSize = d->maxReadBufferSize(); if (isHandshakeComplete()) { QVarLengthArray 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(lcSecureTransport) << d->plainTcpSocket() << "SSLRead returned" << err; #endif if (err == errSSLClosedGraceful) { shutdown = true; // the other side shut down, make sure we do not send shutdown ourselves setErrorAndEmit(d, QAbstractSocket::RemoteHostClosedError, QSslSocket::tr("The TLS/SSL connection has been closed")); break; } else if (err != errSecSuccess && err != errSSLWouldBlock) { setErrorAndEmit(d, QAbstractSocket::SslInternalError, QStringLiteral("SSLRead failed: %1").arg(err)); break; } if (err == errSSLWouldBlock && renegotiating) { startHandshake(); break; } if (readBytes) { buffer.append(data.constData(), readBytes); if (bool *readyReadEmittedPointer = d->readyReadPointer()) *readyReadEmittedPointer = true; emit q->readyRead(); emit q->channelReadyRead(0); } if (err == errSSLWouldBlock) break; } } } SSLCipherSuite TlsCryptographSecureTransport::SSLCipherSuite_from_QSslCipher(const QSslCipher &ciph) { if (ciph.name() == "AES128-SHA"_L1) return TLS_RSA_WITH_AES_128_CBC_SHA; if (ciph.name() == "DHE-RSA-AES128-SHA"_L1) return TLS_DHE_RSA_WITH_AES_128_CBC_SHA; if (ciph.name() == "AES256-SHA"_L1) return TLS_RSA_WITH_AES_256_CBC_SHA; if (ciph.name() == "DHE-RSA-AES256-SHA"_L1) return TLS_DHE_RSA_WITH_AES_256_CBC_SHA; if (ciph.name() == "ECDH-ECDSA-NULL-SHA"_L1) return TLS_ECDH_ECDSA_WITH_NULL_SHA; if (ciph.name() == "ECDH-ECDSA-RC4-SHA"_L1) return TLS_ECDH_ECDSA_WITH_RC4_128_SHA; if (ciph.name() == "ECDH-ECDSA-DES-CBC3-SHA"_L1) return TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA; if (ciph.name() == "ECDH-ECDSA-AES128-SHA"_L1) return TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA; if (ciph.name() == "ECDH-ECDSA-AES256-SHA"_L1) return TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA; if (ciph.name() == "ECDH-ECDSA-RC4-SHA"_L1) return TLS_ECDHE_ECDSA_WITH_RC4_128_SHA; if (ciph.name() == "ECDH-ECDSA-DES-CBC3-SHA"_L1) return TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA; if (ciph.name() == "ECDH-ECDSA-AES128-SHA"_L1) return TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA; if (ciph.name() == "ECDH-ECDSA-AES256-SHA"_L1) return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA; if (ciph.name() == "ECDH-RSA-NULL-SHA"_L1) return TLS_ECDH_RSA_WITH_NULL_SHA; if (ciph.name() == "ECDH-RSA-RC4-SHA"_L1) return TLS_ECDH_RSA_WITH_RC4_128_SHA; if (ciph.name() == "ECDH-RSA-DES-CBC3-SHA"_L1) return TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA; if (ciph.name() == "ECDH-RSA-AES128-SHA"_L1) return TLS_ECDH_RSA_WITH_AES_128_CBC_SHA; if (ciph.name() == "ECDH-RSA-AES256-SHA"_L1) return TLS_ECDH_RSA_WITH_AES_256_CBC_SHA; if (ciph.name() == "ECDH-RSA-RC4-SHA"_L1) return TLS_ECDHE_RSA_WITH_RC4_128_SHA; if (ciph.name() == "ECDH-RSA-DES-CBC3-SHA"_L1) return TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA; if (ciph.name() == "ECDH-RSA-AES128-SHA"_L1) return TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA; if (ciph.name() == "ECDH-RSA-AES256-SHA"_L1) return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA; if (ciph.name() == "DES-CBC3-SHA"_L1) return TLS_RSA_WITH_3DES_EDE_CBC_SHA; if (ciph.name() == "AES128-SHA256"_L1) return TLS_RSA_WITH_AES_128_CBC_SHA256; if (ciph.name() == "AES256-SHA256"_L1) return TLS_RSA_WITH_AES_256_CBC_SHA256; if (ciph.name() == "DHE-RSA-DES-CBC3-SHA"_L1) return TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; if (ciph.name() == "DHE-RSA-AES128-SHA256"_L1) return TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; if (ciph.name() == "DHE-RSA-AES256-SHA256"_L1) return TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; if (ciph.name() == "AES256-GCM-SHA384"_L1) return TLS_RSA_WITH_AES_256_GCM_SHA384; if (ciph.name() == "ECDHE-ECDSA-AES128-SHA256"_L1) return TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; if (ciph.name() == "ECDHE-ECDSA-AES256-SHA384"_L1) return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384; if (ciph.name() == "ECDH-ECDSA-AES128-SHA256"_L1) return TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256; if (ciph.name() == "ECDH-ECDSA-AES256-SHA384"_L1) return TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384; if (ciph.name() == "ECDHE-RSA-AES128-SHA256"_L1) return TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256; if (ciph.name() == "ECDHE-RSA-AES256-SHA384"_L1) return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384; if (ciph.name() == "ECDHE-RSA-AES256-SHA384"_L1) return TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256; if (ciph.name() == "ECDHE-RSA-AES256-GCM-SHA384"_L1) return TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384; if (ciph.name() == "AES128-GCM-SHA256"_L1) return TLS_AES_128_GCM_SHA256; if (ciph.name() == "AES256-GCM-SHA384"_L1) return TLS_AES_256_GCM_SHA384; if (ciph.name() == "CHACHA20-POLY1305-SHA256"_L1) return TLS_CHACHA20_POLY1305_SHA256; if (ciph.name() == "AES128-CCM-SHA256"_L1) return TLS_AES_128_CCM_SHA256; if (ciph.name() == "AES128-CCM8-SHA256"_L1) return TLS_AES_128_CCM_8_SHA256; if (ciph.name() == "ECDHE-ECDSA-AES128-GCM-SHA256"_L1) return TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256; if (ciph.name() == "ECDHE-ECDSA-AES256-GCM-SHA384"_L1) return TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384; if (ciph.name() == "ECDH-ECDSA-AES128-GCM-SHA256"_L1) return TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256; if (ciph.name() == "ECDH-ECDSA-AES256-GCM-SHA384"_L1) return TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384; if (ciph.name() == "ECDHE-RSA-AES128-GCM-SHA256"_L1) return TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256; if (ciph.name() == "ECDH-RSA-AES128-GCM-SHA256"_L1) return TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256; if (ciph.name() == "ECDH-RSA-AES256-GCM-SHA384"_L1) return TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384; if (ciph.name() == "ECDHE-RSA-CHACHA20-POLY1305-SHA256"_L1) return TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256; if (ciph.name() == "ECDHE-ECDSA-CHACHA20-POLY1305-SHA256"_L1) return TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256; return 0; } bool TlsCryptographSecureTransport::initSslContext() { Q_ASSERT(q); Q_ASSERT(d); Q_ASSERT_X(!context, Q_FUNC_INFO, "invalid socket state, context is not null"); auto *plainSocket = d->plainTcpSocket(); Q_ASSERT(plainSocket); const auto mode = d->tlsMode(); context.reset(qt_createSecureTransportContext(mode)); if (!context) { setErrorAndEmit(d, QAbstractSocket::SslInternalError, QStringLiteral("SSLCreateContext failed")); return false; } const OSStatus err = SSLSetIOFuncs(context, reinterpret_cast(&TlsCryptographSecureTransport::ReadCallback), reinterpret_cast(&TlsCryptographSecureTransport::WriteCallback)); if (err != errSecSuccess) { destroySslContext(); setErrorAndEmit(d, QAbstractSocket::SslInternalError, QStringLiteral("SSLSetIOFuncs failed: %1").arg(err)); return false; } SSLSetConnection(context, this); const auto &configuration = q->sslConfiguration(); if (mode == QSslSocket::SslServerMode && !configuration.localCertificateChain().isEmpty()) { QString errorDescription; QAbstractSocket::SocketError errorCode = QAbstractSocket::UnknownSocketError; if (!setSessionCertificate(errorDescription, errorCode)) { destroySslContext(); setErrorAndEmit(d, errorCode, errorDescription); return false; } } if (!setSessionProtocol()) { destroySslContext(); setErrorAndEmit(d, 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.allowedNextProtocols(); QCFType cfNames(CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks)); if (cfNames) { for (const QByteArray &name : protocolNames) { if (name.size() > 255) { qCWarning(lcSecureTransport) << "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(lcSecureTransport) << "SSLSetALPNProtocols failed - too long protocol names?"; } } else { qCWarning(lcSecureTransport) << "failed to allocate ALPN names array"; } } #endif // QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE if (mode == QSslSocket::SslClientMode) { // enable Server Name Indication (SNI) const auto verificationPeerName = d->verificationName(); QString tlsHostName(verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName); if (tlsHostName.isEmpty()) tlsHostName = d->tlsHostName(); 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(d, 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(d, 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 cfCiphers; for (const QSslCipher &cipher : configuration.ciphers()) { if (auto sslCipher = TlsCryptographSecureTransport::SSLCipherSuite_from_QSslCipher(cipher)) cfCiphers << sslCipher; } if (cfCiphers.size() == 0) { qCWarning(lcSecureTransport) << "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(lcSecureTransport) << "failed to set the ciphers from the configuration"; return false; } } return true; } void TlsCryptographSecureTransport::destroySslContext() { context.reset(nullptr); } bool TlsCryptographSecureTransport::setSessionCertificate(QString &errorDescription, QAbstractSocket::SocketError &errorCode) { Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)"); Q_ASSERT(d); const auto &configuration = q->sslConfiguration(); #ifdef QSSLSOCKET_DEBUG auto *plainSocket = d->plainTcpSocket(); #endif 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 pkcs12 = _q_makePkcs12(configuration.localCertificateChain(), configuration.privateKey(), passPhrase).toCFData(); QCFType 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 options = CFDictionaryCreate(nullptr, keys, values, nKeys, nullptr, nullptr); QCFType items; OSStatus err = SecPKCS12Import(pkcs12, options, &items); if (err != errSecSuccess) { #ifdef QSSLSOCKET_DEBUG qCWarning(lcSecureTransport) << 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(lcSecureTransport) << 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(lcSecureTransport) << plainSocket << "SecPKCS12Import returned no identity"; #endif errorCode = QAbstractSocket::SslInvalidUserDataError; errorDescription = QStringLiteral("SecPKCS12Import returned no identity"); return false; } QCFType 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(lcSecureTransport) << 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 TlsCryptographSecureTransport::setSessionProtocol() { Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)"); Q_ASSERT(q); Q_ASSERT(d); // 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. const auto &configuration = q->sslConfiguration(); auto *plainSocket = d->plainTcpSocket(); switch (configuration.protocol()) { case QSsl::TlsV1_3: case QSsl::TlsV1_3OrLater: qCWarning(lcSecureTransport) << plainSocket << "SecureTransport does not support TLS 1.3"; return false; default:; } OSStatus err = errSecSuccess; QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED if (configuration.protocol() == QSsl::TlsV1_0) { #ifdef QSSLSOCKET_DEBUG qCDebug(lcSecureTransport) << 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(lcSecureTransport) << plainSocket << "requesting : TLSv1.1"; #endif err = SSLSetProtocolVersionMin(context, kTLSProtocol11); if (err == errSecSuccess) err = SSLSetProtocolVersionMax(context, kTLSProtocol11); QT_WARNING_POP } else if (configuration.protocol() == QSsl::TlsV1_2) { #ifdef QSSLSOCKET_DEBUG qCDebug(lcSecureTransport) << 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(lcSecureTransport) << plainSocket << "requesting : any"; #endif err = SSLSetProtocolVersionMin(context, kTLSProtocol1); } else if (configuration.protocol() == QSsl::SecureProtocols) { #ifdef QSSLSOCKET_DEBUG qCDebug(lcSecureTransport) << plainSocket << "requesting : TLSv1.2"; #endif err = SSLSetProtocolVersionMin(context, kTLSProtocol12); QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED } else if (configuration.protocol() == QSsl::TlsV1_0OrLater) { #ifdef QSSLSOCKET_DEBUG qCDebug(lcSecureTransport) << plainSocket << "requesting : TLSv1 - TLSv1.2"; #endif err = SSLSetProtocolVersionMin(context, kTLSProtocol1); } else if (configuration.protocol() == QSsl::TlsV1_1OrLater) { #ifdef QSSLSOCKET_DEBUG qCDebug(lcSecureTransport) << plainSocket << "requesting : TLSv1.1 - TLSv1.2"; #endif err = SSLSetProtocolVersionMin(context, kTLSProtocol11); QT_WARNING_POP } else if (configuration.protocol() == QSsl::TlsV1_2OrLater) { #ifdef QSSLSOCKET_DEBUG qCDebug(lcSecureTransport) << plainSocket << "requesting : TLSv1.2"; #endif err = SSLSetProtocolVersionMin(context, kTLSProtocol12); } else { #ifdef QSSLSOCKET_DEBUG qCDebug(lcSecureTransport) << plainSocket << "no protocol version found in the configuration"; #endif return false; } return err == errSecSuccess; } bool TlsCryptographSecureTransport::canIgnoreTrustVerificationFailure() const { Q_ASSERT(q); Q_ASSERT(d); const auto &configuration = q->sslConfiguration(); const QSslSocket::PeerVerifyMode verifyMode = configuration.peerVerifyMode(); return d->tlsMode() == QSslSocket::SslServerMode && (verifyMode == QSslSocket::QueryPeer || verifyMode == QSslSocket::AutoVerifyPeer || verifyMode == QSslSocket::VerifyNone); } bool TlsCryptographSecureTransport::verifySessionProtocol() const { Q_ASSERT(q); const auto &configuration = q->sslConfiguration(); bool protocolOk = false; if (configuration.protocol() == QSsl::AnyProtocol) protocolOk = true; else if (configuration.protocol() == QSsl::SecureProtocols) protocolOk = (sessionProtocol() >= QSsl::TlsV1_2); QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED else if (configuration.protocol() == QSsl::TlsV1_0OrLater) protocolOk = (sessionProtocol() >= QSsl::TlsV1_0); else if (configuration.protocol() == QSsl::TlsV1_1OrLater) protocolOk = (sessionProtocol() >= QSsl::TlsV1_1); QT_WARNING_POP 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 TlsCryptographSecureTransport::verifyPeerTrust() { Q_ASSERT(q); Q_ASSERT(d); const auto mode = d->tlsMode(); const QSslSocket::PeerVerifyMode verifyMode = q->peerVerifyMode(); const bool canIgnoreVerify = canIgnoreTrustVerificationFailure(); Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)"); auto *plainSocket = d->plainTcpSocket(); Q_ASSERT(plainSocket); QCFType trust; OSStatus err = SSLCopyPeerTrust(context, &trust); // !trust - SSLCopyPeerTrust can return errSecSuccess but null trust. if (err != errSecSuccess || !trust) { if (!canIgnoreVerify) { setErrorAndEmit(d, QAbstractSocket::SslHandshakeFailedError, QStringLiteral("Failed to obtain peer trust: %1").arg(err)); plainSocket->disconnectFromHost(); return false; } else { return true; } } QList 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(d, QAbstractSocket::SslHandshakeFailedError, QStringLiteral("SecTrustEvaluate failed: %1").arg(err)); plainSocket->disconnectFromHost(); return false; } QTlsBackend::clearPeerCertificates(d); QList peerCertificateChain; const CFIndex certCount = SecTrustGetCertificateCount(trust); for (CFIndex i = 0; i < certCount; ++i) { SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i); QCFType derData = SecCertificateCopyData(cert); peerCertificateChain << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der); } QTlsBackend::storePeerCertificateChain(d, peerCertificateChain); if (peerCertificateChain.size()) QTlsBackend::storePeerCertificate(d, peerCertificateChain.at(0)); // Check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer): for (const QSslCertificate &cert : qAsConst(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 && d->tlsMode() == QSslSocket::SslClientMode); // Check the peer certificate itself. First try the subject's common name // (CN) as a wildcard, then try all alternate subject name DNS entries the // same way. const auto &peerCertificate = q->peerCertificate(); if (!peerCertificate.isNull()) { // but only if we're a client connecting to a server // if we're the server, don't check CN const QString verificationPeerName = d->verificationName(); if (mode == QSslSocket::SslClientMode) { const QString peerName(verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName); if (!isMatchingHostname(peerCertificate, peerName) && !canIgnoreVerify) { // No matches in common names or alternate names. const QSslError error(QSslError::HostNameMismatch, 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 certArray = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks); const auto &caCertificates = q->sslConfiguration().caCertificates(); for (const QSslCertificate &cert : caCertificates) { QCFType certData = cert.toDer().toCFData(); if (QCFType secRef = SecCertificateCreateWithData(nullptr, certData)) CFArrayAppendValue(certArray, secRef); else qCWarning(lcSecureTransport, "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, 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 TlsCryptographSecureTransport::checkSslErrors() { if (sslErrors.isEmpty()) return true; Q_ASSERT(q); Q_ASSERT(d); emit q->sslErrors(sslErrors); const auto mode = d->tlsMode(); const auto &configuration = q->sslConfiguration(); const bool doVerifyPeer = configuration.peerVerifyMode() == QSslSocket::VerifyPeer || (configuration.peerVerifyMode() == QSslSocket::AutoVerifyPeer && mode == QSslSocket::SslClientMode); const bool doEmitSslError = !d->verifyErrorsHaveBeenIgnored(); // check whether we need to emit an SSL handshake error if (doVerifyPeer && doEmitSslError) { if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) { QSslSocketPrivate::pauseSocketNotifiers(q); d->setPaused(true); } else { setErrorAndEmit(d, QAbstractSocket::SslHandshakeFailedError, sslErrors.constFirst().errorString()); Q_ASSERT(d->plainTcpSocket()); d->plainTcpSocket()->disconnectFromHost(); } return false; } return true; } bool TlsCryptographSecureTransport::startHandshake() { Q_ASSERT(context); Q_ASSERT(q); Q_ASSERT(d); auto *plainSocket = d->plainTcpSocket(); Q_ASSERT(plainSocket); const auto mode = d->tlsMode(); OSStatus err = SSLHandshake(context); #ifdef QSSLSOCKET_DEBUG qCDebug(lcSecureTransport) << 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(d, 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(d, QAbstractSocket::SslHandshakeFailedError, QStringLiteral("SSLHandshake failed: %1").arg(err)); plainSocket->disconnectFromHost(); return false; } // Connection aborted during handshake phase. if (q->state() != QAbstractSocket::ConnectedState) { qCDebug(lcSecureTransport) << "connection aborted"; renegotiating = false; return false; } // check protocol version ourselves, as Secure Transport does not enforce // the requested min / max versions. if (!verifySessionProtocol()) { setErrorAndEmit(d, QAbstractSocket::SslHandshakeFailedError, QStringLiteral("Protocol version mismatch")); plainSocket->disconnectFromHost(); renegotiating = false; return false; } if (verifyPeerTrust()) { continueHandshake(); renegotiating = false; return true; } else { renegotiating = false; return false; } } bool TlsCryptographSecureTransport::isHandshakeComplete() const { Q_ASSERT(q); return q->isEncrypted() && !renegotiating; } QList TlsCryptographSecureTransport::tlsErrors() const { return sslErrors; } } // namespace QTlsPrivate QT_END_NAMESPACE