diff options
Diffstat (limited to 'src/network/ssl/qsslsocket_mac.cpp')
-rw-r--r-- | src/network/ssl/qsslsocket_mac.cpp | 86 |
1 files changed, 73 insertions, 13 deletions
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp index f0742fe634..046b432252 100644 --- a/src/network/ssl/qsslsocket_mac.cpp +++ b/src/network/ssl/qsslsocket_mac.cpp @@ -49,6 +49,7 @@ #include <QtCore/qmessageauthenticationcode.h> #include <QtCore/qoperatingsystemversion.h> #include <QtCore/qcryptographichash.h> +#include <QtCore/qsystemdetection.h> #include <QtCore/qdatastream.h> #include <QtCore/qsysinfo.h> #include <QtCore/qvector.h> @@ -97,16 +98,14 @@ EphemeralSecKeychain::EphemeralSecKeychain() return; } - QString uuidAsString(uuid.toString()); - Q_ASSERT(uuidAsString.size() > 2); - Q_ASSERT(uuidAsString.startsWith(QLatin1Char('{')) - && uuidAsString.endsWith(QLatin1Char('}'))); - uuidAsString = uuidAsString.mid(1, uuidAsString.size() - 2); + 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); - QString keychainName(QDir::tempPath()); - keychainName.append(QDir::separator()); - keychainName += uuidAsString; - keychainName += QLatin1String(".keychain"); + const QString keychainName + = QDir::tempPath() + QDir::separator() + uuidAsString + QLatin1String(".keychain"); // SecKeychainCreate, pathName parameter: // // "A constant character string representing the POSIX path indicating where @@ -163,14 +162,15 @@ EphemeralSecKeychain::~EphemeralSecKeychain() } #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(Q_NULLPTR, side, kSSLStreamType); + SSLContextRef context = SSLCreateContext(nullptr, side, kSSLStreamType); if (!context) qCWarning(lcSsl) << "SSLCreateContext failed"; return context; @@ -359,7 +359,7 @@ void QSslSocketPrivate::resetDefaultEllipticCurves() } QSslSocketBackendPrivate::QSslSocketBackendPrivate() - : context(Q_NULLPTR) + : context(nullptr) { } @@ -375,6 +375,43 @@ void QSslSocketBackendPrivate::continueHandshake() #endif Q_Q(QSslSocket); connectionEncrypted = true; + +#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_NA, __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(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); + QVector<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 + emit q->encrypted(); if (autoStartHandshake && pendingClose) { pendingClose = false; @@ -841,6 +878,29 @@ bool QSslSocketBackendPrivate::initSslContext() return false; } +#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_NA, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0) + if (__builtin_available(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) { + 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); @@ -888,7 +948,7 @@ bool QSslSocketBackendPrivate::initSslContext() void QSslSocketBackendPrivate::destroySslContext() { - context.reset(Q_NULLPTR); + context.reset(nullptr); } static QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase); |