summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/network/ssl/qsslsocket_mac.cpp64
1 files changed, 63 insertions, 1 deletions
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp
index e29ebbe028..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,7 +161,8 @@ EphemeralSecKeychain::~EphemeralSecKeychain()
}
#endif // Q_OS_MACOS
-}
+
+} // unnamed namespace
static SSLContextRef qt_createSecureTransportContext(QSslSocket::SslMode mode)
{
@@ -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);