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.cpp193
1 files changed, 114 insertions, 79 deletions
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp
index aa0e1b0dd1..730a9552b5 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/qoperatingsystemversion.h>
+#include <QtCore/qscopedvaluerollback.h>
#include <QtCore/qcryptographichash.h>
#include <QtCore/qsystemdetection.h>
#include <QtCore/qdatastream.h>
@@ -242,48 +243,70 @@ static const uint8_t dhparam[] =
"\x90\x0b\x35\x64\xff\xd9\xe3\xac\xf2\xf2\xeb\x3a\x63\x02\x01\x02";
#endif
-// No ioErr on iOS/tvOS/watchOS. (defined in MacErrors.h on macOS)
-#if defined(QT_PLATFORM_UIKIT)
-# define ioErr -36
-#endif
-
-static OSStatus _q_SSLRead(QTcpSocket *plainSocket, char *data, size_t *dataLength)
+OSStatus QSslSocketBackendPrivate::ReadCallback(QSslSocketBackendPrivate *socket,
+ char *data, size_t *dataLength)
{
- Q_ASSERT(plainSocket);
+ Q_ASSERT(socket);
Q_ASSERT(data);
Q_ASSERT(dataLength);
+ QTcpSocket *plainSocket = socket->plainSocket;
+ 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, &currentState);
+ 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(lcSsl) << plainSocket << "read" << bytes;
#endif
if (bytes < 0) {
*dataLength = 0;
- return ioErr;
+ return errSecIO;
}
- const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : noErr;
+ const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : errSecSuccess;
*dataLength = bytes;
return err;
}
-static OSStatus _q_SSLWrite(QTcpSocket *plainSocket, const char *data, size_t *dataLength)
+OSStatus QSslSocketBackendPrivate::WriteCallback(QSslSocketBackendPrivate *socket,
+ const char *data, size_t *dataLength)
{
- Q_ASSERT(plainSocket);
+ Q_ASSERT(socket);
Q_ASSERT(data);
Q_ASSERT(dataLength);
+ QTcpSocket *plainSocket = socket->plainSocket;
+ Q_ASSERT(plainSocket);
+
const qint64 bytes = plainSocket->write(data, *dataLength);
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "write" << bytes;
#endif
if (bytes < 0) {
*dataLength = 0;
- return ioErr;
+ return errSecIO;
}
- const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : noErr;
+ const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : errSecSuccess;
*dataLength = bytes;
return err;
@@ -387,12 +410,12 @@ void QSslSocketBackendPrivate::continueHandshake()
Q_Q(QSslSocket);
connectionEncrypted = true;
-#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_NA, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0)
+#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13, __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, *)) {
+ if (__builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) {
const auto &requestedProtocols = configuration.nextAllowedProtocols;
if (const int requestedCount = requestedProtocols.size()) {
configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNone;
@@ -423,7 +446,9 @@ void QSslSocketBackendPrivate::continueHandshake()
}
#endif // QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE
- emit q->encrypted();
+ if (!renegotiating)
+ emit q->encrypted();
+
if (autoStartHandshake && pendingClose) {
pendingClose = false;
q->disconnectFromHost();
@@ -452,7 +477,7 @@ void QSslSocketBackendPrivate::disconnectFromHost()
QSslCipher QSslSocketBackendPrivate::sessionCipher() const
{
SSLCipherSuite cipher = 0;
- if (context && SSLGetNegotiatedCipher(context, &cipher) == noErr)
+ if (context && SSLGetNegotiatedCipher(context, &cipher) == errSecSuccess)
return QSslCipher_from_SSLCipherSuite(cipher);
return QSslCipher();
@@ -465,7 +490,7 @@ QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
SSLProtocol protocol = kSSLProtocolUnknown;
const OSStatus err = SSLGetNegotiatedProtocolVersion(context, &protocol);
- if (err != noErr) {
+ if (err != errSecSuccess) {
qCWarning(lcSsl) << "SSLGetNegotiatedProtocolVersion failed:" << err;
return QSsl::UnknownProtocol;
}
@@ -521,10 +546,10 @@ void QSslSocketBackendPrivate::transmit()
if (!context || shutdown)
return;
- if (!connectionEncrypted)
+ if (!isHandshakeComplete())
startHandshake();
- if (connectionEncrypted && !writeBuffer.isEmpty()) {
+ if (isHandshakeComplete() && !writeBuffer.isEmpty()) {
qint64 totalBytesWritten = 0;
while (writeBuffer.nextDataBlockSize() > 0 && context) {
const size_t nextDataBlockSize = writeBuffer.nextDataBlockSize();
@@ -533,7 +558,7 @@ void QSslSocketBackendPrivate::transmit()
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "SSLWrite returned" << err;
#endif
- if (err != noErr && err != errSSLWouldBlock) {
+ if (err != errSecSuccess && err != errSSLWouldBlock) {
setErrorAndEmit(QAbstractSocket::SslInternalError,
QStringLiteral("SSLWrite failed: %1").arg(err));
break;
@@ -559,7 +584,7 @@ void QSslSocketBackendPrivate::transmit()
}
}
- if (connectionEncrypted) {
+ if (isHandshakeComplete()) {
QVarLengthArray<char, 4096> data;
while (context && (!readBufferMaxSize || buffer.size() < readBufferMaxSize)) {
size_t readBytes = 0;
@@ -573,12 +598,17 @@ void QSslSocketBackendPrivate::transmit()
setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
QSslSocket::tr("The TLS/SSL connection has been closed"));
break;
- } else if (err != noErr && err != errSSLWouldBlock) {
+ } else if (err != errSecSuccess && err != errSSLWouldBlock) {
setErrorAndEmit(QAbstractSocket::SslInternalError,
QStringLiteral("SSLRead failed: %1").arg(err));
break;
}
+ if (err == errSSLWouldBlock && renegotiating) {
+ startHandshake();
+ break;
+ }
+
if (readBytes) {
buffer.append(data.constData(), readBytes);
if (readyReadEmittedPointer)
@@ -861,16 +891,17 @@ bool QSslSocketBackendPrivate::initSslContext()
return false;
}
- const OSStatus err = SSLSetIOFuncs(context, reinterpret_cast<SSLReadFunc>(&_q_SSLRead),
- reinterpret_cast<SSLWriteFunc>(&_q_SSLWrite));
- if (err != noErr) {
+ const OSStatus err = SSLSetIOFuncs(context,
+ reinterpret_cast<SSLReadFunc>(&QSslSocketBackendPrivate::ReadCallback),
+ reinterpret_cast<SSLWriteFunc>(&QSslSocketBackendPrivate::WriteCallback));
+ if (err != errSecSuccess) {
destroySslContext();
setErrorAndEmit(QAbstractSocket::SslInternalError,
QStringLiteral("SSLSetIOFuncs failed: %1").arg(err));
return false;
}
- SSLSetConnection(context, plainSocket);
+ SSLSetConnection(context, this);
if (mode == QSslSocket::SslServerMode
&& !configuration.localCertificateChain.isEmpty()) {
@@ -889,8 +920,8 @@ 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, *)) {
+#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13, __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.nextAllowedProtocols;
QCFType<CFMutableArrayRef> cfNames(CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks));
if (cfNames) {
@@ -922,10 +953,10 @@ bool QSslSocketBackendPrivate::initSslContext()
SSLSetPeerDomainName(context, ace.data(), ace.size());
// tell SecureTransport we handle peer verification ourselves
OSStatus err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnServerAuth, true);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnCertRequested, true);
- if (err != noErr) {
+ if (err != errSecSuccess) {
destroySslContext();
setErrorAndEmit(QSslSocket::SslInternalError,
QStringLiteral("SSLSetSessionOption failed: %1").arg(err));
@@ -936,13 +967,13 @@ bool QSslSocketBackendPrivate::initSslContext()
if (configuration.peerVerifyMode != QSslSocket::VerifyNone) {
// kAlwaysAuthenticate - always fails even if we set break on client auth.
OSStatus err = SSLSetClientSideAuthenticate(context, kTryAuthenticate);
- if (err == noErr) {
+ 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 != noErr) {
+ if (err != errSecSuccess) {
destroySslContext();
setErrorAndEmit(QAbstractSocket::SslInternalError,
QStringLiteral("failed to set SSL context option in server mode: %1").arg(err));
@@ -1005,7 +1036,7 @@ bool QSslSocketBackendPrivate::setSessionCertificate(QString &errorDescription,
nullptr, nullptr);
QCFType<CFArrayRef> items;
OSStatus err = SecPKCS12Import(pkcs12, options, &items);
- if (err != noErr) {
+ if (err != errSecSuccess) {
#ifdef QSSLSOCKET_DEBUG
qCWarning(lcSsl) << plainSocket
<< QStringLiteral("SecPKCS12Import failed: %1").arg(err);
@@ -1051,7 +1082,7 @@ bool QSslSocketBackendPrivate::setSessionCertificate(QString &errorDescription,
}
err = SSLSetCertificate(context, certs);
- if (err != noErr) {
+ if (err != errSecSuccess) {
#ifdef QSSLSOCKET_DEBUG
qCWarning(lcSsl) << plainSocket
<< QStringLiteral("Cannot set certificate and key: %1").arg(err);
@@ -1079,35 +1110,35 @@ bool QSslSocketBackendPrivate::setSessionProtocol()
return false;
}
- OSStatus err = noErr;
+ OSStatus err = errSecSuccess;
if (configuration.protocol == QSsl::SslV3) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : SSLv3";
#endif
err = SSLSetProtocolVersionMin(context, kSSLProtocol3);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kSSLProtocol3);
} else if (configuration.protocol == QSsl::TlsV1_0) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.0";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol1);
} else if (configuration.protocol == QSsl::TlsV1_1) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.1";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol11);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol11);
} else if (configuration.protocol == QSsl::TlsV1_2) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol12);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else if (configuration.protocol == QSsl::AnyProtocol) {
#ifdef QSSLSOCKET_DEBUG
@@ -1115,42 +1146,42 @@ bool QSslSocketBackendPrivate::setSessionProtocol()
#endif
// kSSLProtocol3, since kSSLProtocol2 is disabled:
err = SSLSetProtocolVersionMin(context, kSSLProtocol3);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else if (configuration.protocol == QSsl::TlsV1SslV3) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : SSLv3 - TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kSSLProtocol3);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else if (configuration.protocol == QSsl::SecureProtocols) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1 - TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else if (configuration.protocol == QSsl::TlsV1_0OrLater) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1 - TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else if (configuration.protocol == QSsl::TlsV1_1OrLater) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.1 - TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol11);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else if (configuration.protocol == QSsl::TlsV1_2OrLater) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol12);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else {
#ifdef QSSLSOCKET_DEBUG
@@ -1159,7 +1190,7 @@ bool QSslSocketBackendPrivate::setSessionProtocol()
return false;
}
- return err == noErr;
+ return err == errSecSuccess;
}
bool QSslSocketBackendPrivate::canIgnoreTrustVerificationFailure() const
@@ -1204,8 +1235,8 @@ bool QSslSocketBackendPrivate::verifyPeerTrust()
QCFType<SecTrustRef> trust;
OSStatus err = SSLCopyPeerTrust(context, &trust);
- // !trust - SSLCopyPeerTrust can return noErr but null trust.
- if (err != noErr || !trust) {
+ // !trust - SSLCopyPeerTrust can return errSecSuccess but null trust.
+ if (err != errSecSuccess || !trust) {
if (!canIgnoreVerify) {
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QStringLiteral("Failed to obtain peer trust: %1").arg(err));
@@ -1217,40 +1248,38 @@ bool QSslSocketBackendPrivate::verifyPeerTrust()
}
QList<QSslError> errors;
- // store certificates
- const int certCount = SecTrustGetCertificateCount(trust);
- // TODO: why this test depends on configuration.peerCertificateChain not being empty????
- if (configuration.peerCertificateChain.isEmpty()) {
- // 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 != noErr) {
- // We can not ignore this, it's not even about trust verification
- // probably ...
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
- QStringLiteral("SecTrustEvaluate failed: %1").arg(err));
- plainSocket->disconnectFromHost();
- return false;
- }
- for (int i = 0; i < certCount; ++i) {
- SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i);
- QCFType<CFDataRef> derData = SecCertificateCopyData(cert);
- configuration.peerCertificateChain << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
- }
+ // 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(QAbstractSocket::SslHandshakeFailedError,
+ QStringLiteral("SecTrustEvaluate failed: %1").arg(err));
+ plainSocket->disconnectFromHost();
+ return false;
}
- if (certCount > 0) {
- SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, 0);
+ configuration.peerCertificate.clear();
+ configuration.peerCertificateChain.clear();
+
+ const CFIndex certCount = SecTrustGetCertificateCount(trust);
+ for (CFIndex i = 0; i < certCount; ++i) {
+ SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i);
QCFType<CFDataRef> derData = SecCertificateCopyData(cert);
- configuration.peerCertificate = QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
+ configuration.peerCertificateChain << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
}
- // check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer)
+ if (configuration.peerCertificateChain.size())
+ configuration.peerCertificate = configuration.peerCertificateChain.at(0);
+
+ // Check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer):
for (const QSslCertificate &cert : qAsConst(configuration.peerCertificateChain)) {
if (QSslCertificatePrivate::isBlacklisted(cert) && !canIgnoreVerify) {
const QSslError error(QSslError::CertificateBlacklisted, cert);
@@ -1294,10 +1323,10 @@ bool QSslSocketBackendPrivate::verifyPeerTrust()
}
// verify certificate chain
- QCFType<CFMutableArrayRef> certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ QCFType<CFMutableArrayRef> certArray = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks);
for (const QSslCertificate &cert : qAsConst(configuration.caCertificates)) {
QCFType<CFDataRef> certData = cert.d->derData.toCFData();
- if (QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(NULL, certData))
+ if (QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, certData))
CFArrayAppendValue(certArray, secRef);
else
qCWarning(lcSsl, "Failed to create SecCertificate from QSslCertificate");
@@ -1422,6 +1451,7 @@ bool QSslSocketBackendPrivate::startHandshake()
// Failure means a real error (invalid certificate, no private key, etc).
if (!setSessionCertificate(errorDescription, errorCode)) {
setErrorAndEmit(errorCode, errorDescription);
+ renegotiating = false;
return false;
} else {
// We try to resume a handshake, even if have no
@@ -1436,6 +1466,7 @@ bool QSslSocketBackendPrivate::startHandshake()
return startHandshake();
}
+ renegotiating = false;
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QStringLiteral("SSLHandshake failed: %1").arg(err));
plainSocket->disconnectFromHost();
@@ -1445,6 +1476,7 @@ bool QSslSocketBackendPrivate::startHandshake()
// Connection aborted during handshake phase.
if (q->state() != QAbstractSocket::ConnectedState) {
qCDebug(lcSsl) << "connection aborted";
+ renegotiating = false;
return false;
}
@@ -1453,13 +1485,16 @@ bool QSslSocketBackendPrivate::startHandshake()
if (!verifySessionProtocol()) {
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, QStringLiteral("Protocol version mismatch"));
plainSocket->disconnectFromHost();
+ renegotiating = false;
return false;
}
if (verifyPeerTrust()) {
continueHandshake();
+ renegotiating = false;
return true;
} else {
+ renegotiating = false;
return false;
}
}