summaryrefslogtreecommitdiffstats
path: root/src/network/ssl/qsslsocket_mac.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/ssl/qsslsocket_mac.cpp')
-rw-r--r--src/network/ssl/qsslsocket_mac.cpp70
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);