diff options
Diffstat (limited to 'src/network/ssl/qsslsocket_mac.cpp')
-rw-r--r-- | src/network/ssl/qsslsocket_mac.cpp | 70 |
1 files changed, 66 insertions, 4 deletions
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp index 8d2efe74be..5c2bd55198 100644 --- a/src/network/ssl/qsslsocket_mac.cpp +++ b/src/network/ssl/qsslsocket_mac.cpp @@ -48,6 +48,7 @@ #include <QtCore/qmessageauthenticationcode.h> #include <QtCore/qcryptographichash.h> +#include <QtCore/qsystemdetection.h> #include <QtCore/qdatastream.h> #include <QtCore/qsysinfo.h> #include <QtCore/qvector.h> @@ -160,14 +161,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; @@ -356,7 +358,7 @@ void QSslSocketPrivate::resetDefaultEllipticCurves() } QSslSocketBackendPrivate::QSslSocketBackendPrivate() - : context(Q_NULLPTR) + : context(nullptr) { } @@ -372,6 +374,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; @@ -838,6 +877,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); @@ -885,7 +947,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); |