summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2014-08-22 17:20:49 +0200
committerTimur Pocheptsov <Timur.Pocheptsov@digia.com>2015-02-02 14:02:56 +0000
commit5382312e5c93c91be7e74e688331db0feeb438e7 (patch)
tree40ba958eaf0afa31b9c1216f6add01fd59338e52 /src/network
parenta7fe3309d6507b03f7b8d3012612851232a9417c (diff)
Add SecureTransport based SSL backend for iOS and OS X
Add support for SSL on iOS/OS X by adding a SecureTransport based backend. [ChangeLog][QtNetwork][QSslSocket] A new SSL backend for iOS and OS X, implemented with Apple's Secure Transport (Security Framework). Change-Id: I7466db471be2a8a2170f9af9d6ad4c7b6425738b Reviewed-by: Richard J. Moore <rich@kde.org>
Diffstat (limited to 'src/network')
-rw-r--r--src/network/ssl/qsslcertificate.cpp4
-rw-r--r--src/network/ssl/qsslkey_mac.cpp91
-rw-r--r--src/network/ssl/qsslsocket.cpp3
-rw-r--r--src/network/ssl/qsslsocket_mac.cpp1443
-rw-r--r--src/network/ssl/qsslsocket_mac_p.h125
-rw-r--r--src/network/ssl/qsslsocket_p.h2
-rw-r--r--src/network/ssl/ssl.pri9
7 files changed, 1677 insertions, 0 deletions
diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp
index 302b2db285..2eef2f20aa 100644
--- a/src/network/ssl/qsslcertificate.cpp
+++ b/src/network/ssl/qsslcertificate.cpp
@@ -105,12 +105,16 @@
\value EmailAddress The email address associated with the certificate
*/
+#include <QtCore/qglobal.h>
#ifndef QT_NO_OPENSSL
#include "qsslsocket_openssl_symbols_p.h"
#endif
#ifdef Q_OS_WINRT
#include "qsslsocket_winrt_p.h"
#endif
+#ifdef QT_SECURETRANSPORT
+#include "qsslsocket_mac_p.h"
+#endif
#include "qssl_p.h"
#include "qsslcertificate.h"
diff --git a/src/network/ssl/qsslkey_mac.cpp b/src/network/ssl/qsslkey_mac.cpp
new file mode 100644
index 0000000000..6e15012dc9
--- /dev/null
+++ b/src/network/ssl/qsslkey_mac.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsslkey.h"
+#include "qsslkey_p.h"
+
+#include <CommonCrypto/CommonCrypto.h>
+
+QT_USE_NAMESPACE
+
+static QByteArray wrapCCCrypt(CCOperation ccOp,
+ QSslKeyPrivate::Cipher cipher,
+ const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv)
+{
+ int blockSize;
+ CCAlgorithm ccAlgorithm;
+ switch (cipher) {
+ case QSslKeyPrivate::DesCbc:
+ blockSize = kCCBlockSizeDES;
+ ccAlgorithm = kCCAlgorithmDES;
+ break;
+ case QSslKeyPrivate::DesEde3Cbc:
+ blockSize = kCCBlockSize3DES;
+ ccAlgorithm = kCCAlgorithm3DES;
+ break;
+ case QSslKeyPrivate::Rc2Cbc:
+ blockSize = kCCBlockSizeRC2;
+ ccAlgorithm = kCCAlgorithmRC2;
+ break;
+ };
+ size_t plainLength = 0;
+ QByteArray plain(data.size() + blockSize, 0);
+ CCCryptorStatus status = CCCrypt(
+ ccOp, ccAlgorithm, kCCOptionPKCS7Padding,
+ key.constData(), key.size(),
+ iv.constData(),
+ data.constData(), data.size(),
+ plain.data(), plain.size(), &plainLength);
+ if (status == kCCSuccess)
+ return plain.left(plainLength);
+ return QByteArray();
+}
+
+QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
+{
+ return wrapCCCrypt(kCCDecrypt, cipher, data, key, iv);
+}
+
+QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
+{
+ return wrapCCCrypt(kCCEncrypt, cipher, data, key, iv);
+}
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 98972f526a..e12ae33ac2 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -312,6 +312,9 @@
#ifdef Q_OS_WINRT
#include "qsslsocket_winrt_p.h"
#endif
+#ifdef QT_SECURETRANSPORT
+#include "qsslsocket_mac_p.h"
+#endif
#include "qsslconfiguration_p.h"
#include <QtCore/qdebug.h>
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp
new file mode 100644
index 0000000000..08f4c1c6a1
--- /dev/null
+++ b/src/network/ssl/qsslsocket_mac.cpp
@@ -0,0 +1,1443 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsslsocket.h"
+
+#include "qsslsocket_mac_p.h"
+#include "qasn1element_p.h"
+#include "qsslcertificate_p.h"
+#include "qsslcipher_p.h"
+#include "qsslkey_p.h"
+
+#include <QtCore/qmessageauthenticationcode.h>
+#include <QtCore/qcryptographichash.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qsysinfo.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qdebug.h>
+
+#include <algorithm>
+#include <cstddef>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC_WITH_ARGS(QMutex, qt_securetransport_mutex, (QMutex::Recursive))
+
+//#define QSSLSOCKET_DEBUG
+
+bool QSslSocketPrivate::s_libraryLoaded = false;
+bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
+bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
+
+
+#ifndef Q_OS_IOS // dhparam is not used on iOS. (see the SSLSetDiffieHellmanParams call below)
+static const uint8_t dhparam[] =
+ "\x30\x82\x01\x08\x02\x82\x01\x01\x00\x97\xea\xd0\x46\xf7\xae\xa7\x76\x80"
+ "\x9c\x74\x56\x98\xd8\x56\x97\x2b\x20\x6c\x77\xe2\x82\xbb\xc8\x84\xbe\xe7"
+ "\x63\xaf\xcc\x30\xd0\x67\x97\x7d\x1b\xab\x59\x30\xa9\x13\x67\x21\xd7\xd4"
+ "\x0e\x46\xcf\xe5\x80\xdf\xc9\xb9\xba\x54\x9b\x46\x2f\x3b\x45\xfc\x2f\xaf"
+ "\xad\xc0\x17\x56\xdd\x52\x42\x57\x45\x70\x14\xe5\xbe\x67\xaa\xde\x69\x75"
+ "\x30\x0d\xf9\xa2\xc4\x63\x4d\x7a\x39\xef\x14\x62\x18\x33\x44\xa1\xf9\xc1"
+ "\x52\xd1\xb6\x72\x21\x98\xf8\xab\x16\x1b\x7b\x37\x65\xe3\xc5\x11\x00\xf6"
+ "\x36\x1f\xd8\x5f\xd8\x9f\x43\xa8\xce\x9d\xbf\x5e\xd6\x2d\xfa\x0a\xc2\x01"
+ "\x54\xc2\xd9\x81\x54\x55\xb5\x26\xf8\x88\x37\xf5\xfe\xe0\xef\x4a\x34\x81"
+ "\xdc\x5a\xb3\x71\x46\x27\xe3\xcd\x24\xf6\x1b\xf1\xe2\x0f\xc2\xa1\x39\x53"
+ "\x5b\xc5\x38\x46\x8e\x67\x4c\xd9\xdd\xe4\x37\x06\x03\x16\xf1\x1d\x7a\xba"
+ "\x2d\xc1\xe4\x03\x1a\x58\xe5\x29\x5a\x29\x06\x69\x61\x7a\xd8\xa9\x05\x9f"
+ "\xc1\xa2\x45\x9c\x17\xad\x52\x69\x33\xdc\x18\x8d\x15\xa6\x5e\xcd\x94\xf4"
+ "\x45\xbb\x9f\xc2\x7b\x85\x00\x61\xb0\x1a\xdc\x3c\x86\xaa\x9f\x5c\x04\xb3"
+ "\x90\x0b\x35\x64\xff\xd9\xe3\xac\xf2\xf2\xeb\x3a\x63\x02\x01\x02";
+#endif
+
+// No ioErr on iOS. (defined in MacErrors.h on OS X)
+#ifdef Q_OS_IOS
+# define ioErr -36
+#endif
+
+static OSStatus _q_SSLRead(QTcpSocket *plainSocket, char *data, size_t *dataLength)
+{
+ Q_ASSERT(plainSocket);
+ Q_ASSERT(data);
+ Q_ASSERT(dataLength);
+
+ const qint64 bytes = plainSocket->read(data, *dataLength);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "read" << bytes;
+#endif
+ if (bytes < 0) {
+ *dataLength = 0;
+ return ioErr;
+ }
+
+ const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : noErr;
+ *dataLength = bytes;
+
+ return err;
+}
+
+static OSStatus _q_SSLWrite(QTcpSocket *plainSocket, const char *data, size_t *dataLength)
+{
+ Q_ASSERT(plainSocket);
+ Q_ASSERT(data);
+ Q_ASSERT(dataLength);
+
+ const qint64 bytes = plainSocket->write(data, *dataLength);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "write" << bytes;
+#endif
+ if (bytes < 0) {
+ *dataLength = 0;
+ return ioErr;
+ }
+
+ const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : noErr;
+ *dataLength = bytes;
+
+ return err;
+}
+
+void QSslSocketPrivate::ensureInitialized()
+{
+ const QMutexLocker locker(qt_securetransport_mutex);
+ if (s_loadedCiphersAndCerts)
+ return;
+
+ // We have to set it before setDefaultSupportedCiphers,
+ // since this function can trigger static (global)'s initialization
+ // and as a result - recursive ensureInitialized call
+ // from QSslCertificatePrivate's ctor.
+ s_loadedCiphersAndCerts = true;
+
+ QCFType<SSLContextRef> context(SSLCreateContext(Q_NULLPTR, kSSLClientSide, kSSLStreamType));
+ if (context) {
+ QList<QSslCipher> ciphers;
+ QList<QSslCipher> defaultCiphers;
+
+ size_t numCiphers = 0;
+ // Fails only if any of parameters is null.
+ SSLGetNumberSupportedCiphers(context, &numCiphers);
+ QVector<SSLCipherSuite> cfCiphers(numCiphers);
+ // Fails only if any of parameter is null or number of ciphers is wrong.
+ SSLGetSupportedCiphers(context, cfCiphers.data(), &numCiphers);
+
+ for (size_t i = 0; i < size_t(cfCiphers.size()); ++i) {
+ const QSslCipher ciph(QSslSocketBackendPrivate::QSslCipher_from_SSLCipherSuite(cfCiphers[i]));
+ if (!ciph.isNull()) {
+ ciphers << ciph;
+ if (ciph.usedBits() >= 128)
+ defaultCiphers << ciph;
+ }
+ }
+
+ setDefaultSupportedCiphers(ciphers);
+ setDefaultCiphers(defaultCiphers);
+
+ if (!s_loadRootCertsOnDemand)
+ setDefaultCaCertificates(systemCaCertificates());
+ } else {
+ qWarning() << Q_FUNC_INFO << "SSLCreateContext failed";
+ s_loadedCiphersAndCerts = false;
+ }
+
+}
+
+long QSslSocketPrivate::sslLibraryVersionNumber()
+{
+ return 0;
+}
+
+QString QSslSocketPrivate::sslLibraryVersionString()
+{
+ return QStringLiteral("Secure Transport, ") + QSysInfo::prettyProductName();
+}
+
+long QSslSocketPrivate::sslLibraryBuildVersionNumber()
+{
+ return 0;
+}
+
+QString QSslSocketPrivate::sslLibraryBuildVersionString()
+{
+ return sslLibraryVersionString();
+}
+
+bool QSslSocketPrivate::supportsSsl()
+{
+ return true;
+}
+
+void QSslSocketPrivate::resetDefaultCiphers()
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QSslSocketPrivate::resetDefaultEllipticCurves()
+{
+ // No public API for this (?).
+ Q_UNIMPLEMENTED();
+}
+
+
+QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
+{
+ QList<QSslCertificate> systemCerts;
+#ifdef Q_OS_OSX
+ // SecTrustSettingsCopyCertificates is not defined on iOS.
+ QCFType<CFArrayRef> cfCerts;
+ OSStatus status = SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainSystem, &cfCerts);
+ if (status == noErr) {
+ const CFIndex size = CFArrayGetCount(cfCerts);
+ for (CFIndex i = 0; i < size; ++i) {
+ SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i);
+ QCFType<CFDataRef> derData = SecCertificateCopyData(cfCert);
+ systemCerts << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
+ }
+ } else {
+ // no detailed error handling here
+ qWarning("could not retrieve system CA certificates");
+ }
+#endif
+ return systemCerts;
+}
+
+QSslSocketBackendPrivate::QSslSocketBackendPrivate()
+ : context(Q_NULLPTR)
+{
+}
+
+QSslSocketBackendPrivate::~QSslSocketBackendPrivate()
+{
+ destroySslContext();
+}
+
+void QSslSocketBackendPrivate::continueHandshake()
+{
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "connection encrypted";
+#endif
+ Q_Q(QSslSocket);
+ connectionEncrypted = true;
+ emit q->encrypted();
+ if (autoStartHandshake && pendingClose) {
+ pendingClose = false;
+ q->disconnectFromHost();
+ }
+}
+
+void QSslSocketBackendPrivate::disconnected()
+{
+ if (plainSocket->bytesAvailable() <= 0)
+ destroySslContext();
+ // If there is still buffered data in the plain socket, don't destroy the ssl context yet.
+ // It will be destroyed when the socket is deleted.
+}
+
+void QSslSocketBackendPrivate::disconnectFromHost()
+{
+ if (context) {
+ if (!shutdown) {
+ SSLClose(context);
+ shutdown = true;
+ }
+ }
+ plainSocket->disconnectFromHost();
+}
+
+QSslCipher QSslSocketBackendPrivate::sessionCipher() const
+{
+ SSLCipherSuite cipher = 0;
+ if (context && SSLGetNegotiatedCipher(context, &cipher) == noErr)
+ return QSslCipher_from_SSLCipherSuite(cipher);
+
+ return QSslCipher();
+}
+
+QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
+{
+ if (!context)
+ return QSsl::UnknownProtocol;
+
+ SSLProtocol protocol = kSSLProtocolUnknown;
+ const OSStatus err = SSLGetNegotiatedProtocolVersion(context, &protocol);
+ if (err != noErr) {
+ qWarning() << Q_FUNC_INFO << "SSLGetNegotiatedProtocolVersion failed:"
+ << int(err);
+ return QSsl::UnknownProtocol;
+ }
+
+ switch (protocol) {
+ case kSSLProtocol2:
+ return QSsl::SslV2;
+ case kSSLProtocol3:
+ return QSsl::SslV3;
+ case kTLSProtocol1:
+ return QSsl::TlsV1_0;
+ case kTLSProtocol11:
+ return QSsl::TlsV1_1;
+ case kTLSProtocol12:
+ return QSsl::TlsV1_2;
+ default:
+ return QSsl::UnknownProtocol;
+ }
+}
+
+void QSslSocketBackendPrivate::startClientEncryption()
+{
+ if (!initSslContext()) {
+ // Error description/code were set, 'error' emitted
+ // by initSslContext, but OpenSSL socket also sets error
+ // emits a signal twice, so ...
+ setError("Unable to init SSL Context", QAbstractSocket::SslInternalError);
+ return;
+ }
+
+ startHandshake();
+}
+
+void QSslSocketBackendPrivate::startServerEncryption()
+{
+ if (!initSslContext()) {
+ // Error description/code were set, 'error' emitted
+ // by initSslContext, but OpenSSL socket also sets error
+ // emits a signal twice, so ...
+ setError("Unable to init SSL Context", QAbstractSocket::SslInternalError);
+ return;
+ }
+
+ startHandshake();
+}
+
+void QSslSocketBackendPrivate::transmit()
+{
+ Q_Q(QSslSocket);
+
+ // If we don't have any SSL context, don't bother transmitting.
+ // Edit: if SSL session closed, don't bother either.
+ if (!context || shutdown)
+ return;
+
+ if (!connectionEncrypted)
+ startHandshake();
+
+ if (connectionEncrypted && !writeBuffer.isEmpty()) {
+ qint64 totalBytesWritten = 0;
+ while (writeBuffer.nextDataBlockSize() > 0) {
+ const size_t nextDataBlockSize = writeBuffer.nextDataBlockSize();
+ size_t writtenBytes = 0;
+ const OSStatus err = SSLWrite(context, writeBuffer.readPointer(), nextDataBlockSize, &writtenBytes);
+ if (err != noErr && err != errSSLWouldBlock) {
+ qWarning() << Q_FUNC_INFO << "SSL write failed with error:" << int(err);
+ setError("SSL write failed", QAbstractSocket::SslInternalError);
+ break;
+ }
+
+ if (writtenBytes) {
+ writeBuffer.free(writtenBytes);
+ totalBytesWritten += writtenBytes;
+ }
+
+ if (writtenBytes < nextDataBlockSize)
+ break;
+ }
+
+ if (totalBytesWritten > 0) {
+ // Don't emit bytesWritten() recursively.
+ if (!emittedBytesWritten) {
+ emittedBytesWritten = true;
+ emit q->bytesWritten(totalBytesWritten);
+ emittedBytesWritten = false;
+ }
+ }
+ }
+
+ if (connectionEncrypted) {
+ QVarLengthArray<char, 4096> data;
+ while (plainSocket->bytesAvailable() > 0) {
+ size_t readBytes = 0;
+ data.resize(4096);
+ if (shutdown) {
+ // SSLRead(context, data.data(), data.size(), &readBytes) fails with errSSLClosedGraceful
+ // if the session was closed (see disconnectFromHost).
+ // SSLClose SSLRead fails and we'll stay in this loop forever.
+ // At the moment we're never here (see the test '!context || shutdown' above) -
+ // we read nothing from the socket as soon as SSL session closed.
+ qCritical() << Q_FUNC_INFO << "read attempt after SSL session closed";
+ size_t nBytes = plainSocket->bytesAvailable();
+ _q_SSLRead(plainSocket, data.data(), &nBytes);
+ } else {
+ const OSStatus err = SSLRead(context, data.data(), data.size(), &readBytes);
+ if (err != noErr && err != errSSLWouldBlock) {
+ qWarning() << Q_FUNC_INFO << "SSLRead failed with:" << int(err);
+ setError("SSL read failed", QAbstractSocket::SslInternalError);
+ break;
+ }
+ }
+
+ if (readBytes) {
+ char *const ptr = buffer.reserve(readBytes);
+ std::copy(data.data(), data.data() + readBytes, ptr);
+ if (readyReadEmittedPointer)
+ *readyReadEmittedPointer = true;
+ emit q->readyRead();
+ }
+ }
+ }
+}
+
+
+QList<QSslError> (QSslSocketBackendPrivate::verify)(QList<QSslCertificate> certificateChain, const QString &hostName)
+{
+ Q_UNIMPLEMENTED();
+ Q_UNUSED(certificateChain)
+ Q_UNUSED(hostName)
+
+ QList<QSslError> errors;
+ errors << QSslError(QSslError::UnspecifiedError);
+
+ return errors;
+}
+
+bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device,
+ QSslKey *key, QSslCertificate *cert,
+ QList<QSslCertificate> *caCertificates,
+ const QByteArray &passPhrase)
+{
+ Q_UNIMPLEMENTED();
+ Q_UNUSED(device)
+ Q_UNUSED(key)
+ Q_UNUSED(cert)
+ Q_UNUSED(caCertificates)
+ Q_UNUSED(passPhrase)
+ return false;
+}
+
+QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSLCipherSuite(SSLCipherSuite cipher)
+{
+ QSslCipher ciph;
+ switch (cipher) {
+ case SSL_RSA_WITH_NULL_MD5:
+ ciph.d->name = QLatin1String("NULL-MD5");
+ ciph.d->protocol = QSsl::SslV3;
+ break;
+ case SSL_RSA_WITH_NULL_SHA:
+ ciph.d->name = QLatin1String("NULL-SHA");
+ ciph.d->protocol = QSsl::SslV3;
+ break;
+ case SSL_RSA_WITH_RC4_128_MD5:
+ ciph.d->name = QLatin1String("RC4-MD5");
+ ciph.d->protocol = QSsl::SslV3;
+ break;
+ case SSL_RSA_WITH_RC4_128_SHA:
+ ciph.d->name = QLatin1String("RC4-SHA");
+ ciph.d->protocol = QSsl::SslV3;
+ break;
+
+ case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ ciph.d->name = QLatin1String("DES-CBC3-SHA");
+ break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA:
+ ciph.d->name = QLatin1String("AES128-SHA");
+ break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA256:
+ ciph.d->name = QLatin1String("AES128-SHA256");
+ break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA:
+ ciph.d->name = QLatin1String("AES256-SHA");
+ break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA256:
+ ciph.d->name = QLatin1String("AES256-SHA256");
+ break;
+
+ case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ ciph.d->name = QLatin1String("DHE-RSA-DES-CBC3-SHA");
+ break;
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ ciph.d->name = QLatin1String("DHE-RSA-AES128-SHA");
+ break;
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ ciph.d->name = QLatin1String("DHE-RSA-AES128-SHA256");
+ break;
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ ciph.d->name = QLatin1String("DHE-RSA-AES256-SHA");
+ break;
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ ciph.d->name = QLatin1String("DHE-RSA-AES256-SHA256");
+ break;
+
+ case TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ ciph.d->name = QLatin1String("ECDH-ECDSA-NULL-SHA");
+ break;
+ case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ ciph.d->name = QLatin1String("ECDH-ECDSA-RC4-SHA");
+ break;
+ case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDH-ECDSA-DES-CBC3-SHA");
+ break;
+ case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDH-ECDSA-AES128-SHA");
+ break;
+ case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ ciph.d->name = QLatin1String("ECDH-ECDSA-AES128-SHA256");
+ break;
+ case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDH-ECDSA-AES256-SHA");
+ break;
+ case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ ciph.d->name = QLatin1String("ECDH-ECDSA-AES256-SHA384");
+ break;
+
+ case TLS_ECDH_RSA_WITH_NULL_SHA:
+ ciph.d->name = QLatin1String("ECDH-RSA-NULL-SHA");
+ break;
+ case TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ ciph.d->name = QLatin1String("ECDH-RSA-AES256-SHA");
+ break;
+ case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDH-RSA-DES-CBC3-SHA");
+ break;
+ case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDH-RSA-AES128-SHA");
+ break;
+ case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ ciph.d->name = QLatin1String("ECDH-RSA-AES128-SHA256");
+ break;
+ case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDH-RSA-AES256-SHA");
+ break;
+ case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ ciph.d->name = QLatin1String("ECDH-RSA-AES256-SHA384");
+ break;
+
+ case TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+ ciph.d->name = QLatin1String("ECDHE-ECDSA-NULL-SHA");
+ break;
+ case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ ciph.d->name = QLatin1String("ECDHE-ECDSA-RC4-SHA");
+ break;
+ case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDHE-ECDSA-DES-CBC3-SHA");
+ break;
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDHE-ECDSA-AES128-SHA");
+ break;
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ ciph.d->name = QLatin1String("ECDHE-ECDSA-AES128-SHA256");
+ break;
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDHE-ECDSA-AES256-SHA");
+ break;
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ ciph.d->name = QLatin1String("ECDHE-ECDSA-AES256-SHA384");
+ break;
+
+ case TLS_ECDHE_RSA_WITH_NULL_SHA:
+ ciph.d->name = QLatin1String("ECDHE-RSA-NULL-SHA");
+ break;
+ case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ ciph.d->name = QLatin1String("ECDHE-RSA-AES256-SHA");
+ break;
+ case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDHE-RSA-DES-CBC3-SHA");
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDHE-RSA-AES128-SHA");
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ ciph.d->name = QLatin1String("ECDHE-RSA-AES128-SHA256");
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDHE-RSA-AES256-SHA");
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ ciph.d->name = QLatin1String("ECDHE-RSA-AES256-SHA384");
+ break;
+ default:
+ return ciph;
+ }
+ ciph.d->isNull = false;
+
+ // protocol
+ if (ciph.d->protocol == QSsl::SslV3) {
+ ciph.d->protocolString = QLatin1String("SSLv3");
+ } else {
+ ciph.d->protocol = QSsl::TlsV1_2;
+ ciph.d->protocolString = QLatin1String("TLSv1.2");
+ }
+
+ const QStringList bits = ciph.d->name.split('-');
+ if (bits.size() >= 2) {
+ if (bits.size() == 2 || bits.size() == 3) {
+ ciph.d->keyExchangeMethod = QLatin1String("RSA");
+ } else if (ciph.d->name.startsWith("DH-") || ciph.d->name.startsWith("DHE-")) {
+ ciph.d->keyExchangeMethod = QLatin1String("DH");
+ } else if (ciph.d->name.startsWith("ECDH-") || ciph.d->name.startsWith("ECDHE-")) {
+ ciph.d->keyExchangeMethod = QLatin1String("ECDH");
+ } else {
+ qWarning() << Q_FUNC_INFO << "Unknown Kx" << ciph.d->name;
+ }
+
+ if (bits.size() == 2 || bits.size() == 3) {
+ ciph.d->authenticationMethod = QLatin1String("RSA");
+ } else if (ciph.d->name.contains("-ECDSA-")) {
+ ciph.d->authenticationMethod = QLatin1String("ECDSA");
+ } else if (ciph.d->name.contains("-RSA-")) {
+ ciph.d->authenticationMethod = QLatin1String("RSA");
+ } else {
+ qWarning() << Q_FUNC_INFO << "Unknown Au" << ciph.d->name;
+ }
+
+ if (ciph.d->name.contains("RC4-")) {
+ ciph.d->encryptionMethod = QLatin1String("RC4(128)");
+ ciph.d->bits = 128;
+ ciph.d->supportedBits = 128;
+ } else if (ciph.d->name.contains("DES-CBC3-")) {
+ ciph.d->encryptionMethod = QLatin1String("3DES(168)");
+ ciph.d->bits = 168;
+ ciph.d->supportedBits = 168;
+ } else if (ciph.d->name.contains("AES128-")) {
+ ciph.d->encryptionMethod = QLatin1String("AES(128)");
+ ciph.d->bits = 128;
+ ciph.d->supportedBits = 128;
+ } else if (ciph.d->name.contains("AES256-")) {
+ ciph.d->encryptionMethod = QLatin1String("AES(256)");
+ ciph.d->bits = 256;
+ ciph.d->supportedBits = 256;
+ } else if (ciph.d->name.contains("NULL-")) {
+ ciph.d->encryptionMethod = QLatin1String("NULL");
+ } else {
+ qWarning() << Q_FUNC_INFO << "Unknown Enc" << ciph.d->name;
+ }
+ }
+ return ciph;
+}
+
+bool QSslSocketBackendPrivate::initSslContext()
+{
+ Q_Q(QSslSocket);
+
+ Q_ASSERT_X(!context, Q_FUNC_INFO, "invalid socket state, context is not null");
+ Q_ASSERT(plainSocket);
+
+ SSLProtocolSide side = kSSLClientSide;
+ if (mode == QSslSocket::SslServerMode)
+ side = kSSLServerSide;
+
+ context = SSLCreateContext(Q_NULLPTR, side, kSSLStreamType);
+ if (!context) {
+ qWarning() << Q_FUNC_INFO << "SSLCreateContext failed";
+ setError("SSLCreateContext failed", QAbstractSocket::SslInternalError);
+ return false;
+ }
+
+ const OSStatus err = SSLSetIOFuncs(context, reinterpret_cast<SSLReadFunc>(&_q_SSLRead),
+ reinterpret_cast<SSLWriteFunc>(&_q_SSLWrite));
+ if (err != noErr) {
+ qWarning() << Q_FUNC_INFO << "SSLSetIOFuncs failed with error " << int(err);
+ destroySslContext();
+ setError("SSLSetIOFuncs failed", QAbstractSocket::SslInternalError);
+ return false;
+ }
+
+ SSLSetConnection(context, plainSocket);
+
+ if (mode == QSslSocket::SslServerMode
+ && !configuration.localCertificateChain.isEmpty()) {
+ QString errorDescription;
+ QAbstractSocket::SocketError errorCode = QAbstractSocket::UnknownSocketError;
+ if (!setSessionCertificate(errorDescription, errorCode)) {
+ destroySslContext();
+ setError(errorDescription, errorCode);
+ return false;
+ }
+ }
+
+ if (!setSessionProtocol()) {
+ qWarning() << Q_FUNC_INFO << "failed to set protocol version";
+ destroySslContext();
+ setError("Failed to set protocol version", QAbstractSocket::SslInternalError);
+ return false;
+ }
+
+ if (mode == QSslSocket::SslClientMode) {
+ // enable Server Name Indication (SNI)
+ QString tlsHostName(verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName);
+ if (tlsHostName.isEmpty())
+ tlsHostName = hostName;
+
+ const QByteArray ace(QUrl::toAce(tlsHostName));
+ SSLSetPeerDomainName(context, ace.data(), ace.size());
+ // tell SecureTransport we handle peer verification ourselves
+ OSStatus err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnServerAuth, true);
+ if (err == noErr)
+ err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnCertRequested, true);
+
+ if (err != noErr) {
+ qWarning() << Q_FUNC_INFO << "SSLSetSessionOption failed:"<<int(err);
+ destroySslContext();
+ setError(QStringLiteral("SSLSetSessionOption failed: %1").arg(err),
+ QSslSocket::SslInternalError);
+ return false;
+ }
+ //
+ } else {
+ if (configuration.peerVerifyMode != QSslSocket::VerifyNone) {
+// OSStatus err = SSLSetClientSideAuthenticate(context, kAlwaysAuthenticate);
+ OSStatus err = SSLSetClientSideAuthenticate(context, kTryAuthenticate);
+ if (err == noErr) {
+ // We'd like to verify peer ourselves, otherwise handshake will
+ // most probably fail before we can do anything.
+ err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnServerAuth, true);
+ }
+
+ if (err != noErr) {
+ qWarning() << Q_FUNC_INFO << "failed to set SSL context option in server mode";
+ destroySslContext();
+ setError(QStringLiteral("failed to set SSL context option in server mode: %1").arg(err),
+ QAbstractSocket::SslInternalError);
+ return false;
+ }
+ }
+#ifndef Q_OS_IOS
+ // No SSLSetDiffieHellmanParams on iOS; calling it is optional according to docs.
+ SSLSetDiffieHellmanParams(context, dhparam, sizeof(dhparam));
+#endif
+ }
+ return true;
+}
+
+void QSslSocketBackendPrivate::destroySslContext()
+{
+ context = Q_NULLPTR;
+}
+
+static QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase);
+
+
+bool QSslSocketBackendPrivate::setSessionCertificate(QString &errorDescription, QAbstractSocket::SocketError &errorCode)
+{
+ Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)");
+
+ QSslCertificate localCertificate;
+ if (!configuration.localCertificateChain.isEmpty())
+ localCertificate = configuration.localCertificateChain[0];
+
+ if (!localCertificate.isNull()) {
+ // Require a private key as well.
+ if (configuration.privateKey.isNull()) {
+ errorCode = QAbstractSocket::SslInvalidUserDataError;
+ errorDescription = QStringLiteral("Cannot provide a certificate with no key");
+ return false;
+ }
+
+ // import certificates and key
+ const QString passPhrase(QString::fromLatin1("foobar"));
+ QCFType<CFDataRef> pkcs12 = _q_makePkcs12(configuration.localCertificateChain,
+ configuration.privateKey, passPhrase).toCFData();
+ QCFType<CFStringRef> password = passPhrase.toCFString();
+ const void *keys[] = { kSecImportExportPassphrase };
+ const void *values[] = { password };
+ QCFType<CFDictionaryRef> options(CFDictionaryCreate(Q_NULLPTR, keys, values, 1,
+ Q_NULLPTR, Q_NULLPTR));
+ CFArrayRef items = Q_NULLPTR;
+ OSStatus err = SecPKCS12Import(pkcs12, options, &items);
+ if (err != noErr) {
+#ifdef QSSLSOCKET_DEBUG
+ qWarning() << Q_FUNC_INFO << plainSocket
+ << QStringLiteral("SecPKCS12Import failed: %1").arg(err);
+#endif
+ errorCode = QAbstractSocket::SslInvalidUserDataError;
+ errorDescription = QStringLiteral("SecPKCS12Import failed: %1").arg(err);
+ return false;
+ }
+
+ if (!CFArrayGetCount(items)) {
+#ifdef QSSLSOCKET_DEBUG
+ qWarning() << Q_FUNC_INFO << plainSocket << "SecPKCS12Import returned no items";
+#endif
+ errorCode = QAbstractSocket::SslInvalidUserDataError;
+ errorDescription = QStringLiteral("SecPKCS12Import returned no items");
+ return false;
+ }
+
+ CFDictionaryRef import = (CFDictionaryRef)CFArrayGetValueAtIndex(items, 0);
+ SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(import, kSecImportItemIdentity);
+ if (!identity) {
+#ifdef QSSLSOCKET_DEBUG
+ qWarning() << Q_FUNC_INFO << plainSocket << "SecPKCS12Import returned no identity";
+#endif
+ errorCode = QAbstractSocket::SslInvalidUserDataError;
+ errorDescription = QStringLiteral("SecPKCS12Import returned no identity");
+ return false;
+ }
+
+ QCFType<CFMutableArrayRef> certs = CFArrayCreateMutable(Q_NULLPTR, 0, &kCFTypeArrayCallBacks);
+ if (!certs) {
+ errorCode = QAbstractSocket::SslInternalError;
+ errorDescription = QStringLiteral("Failed to allocate certificates array");
+ return false;
+ }
+
+ CFArrayAppendValue(certs, identity);
+
+ QCFType<CFArrayRef> chain((CFArrayRef)CFDictionaryGetValue(import, kSecImportItemCertChain));
+ if (chain) {
+ for (CFIndex i = 1, e = CFArrayGetCount(chain); i < e; ++i)
+ CFArrayAppendValue(certs, CFArrayGetValueAtIndex(chain, i));
+ }
+
+ err = SSLSetCertificate(context, certs);
+ if (err != noErr) {
+#ifdef QSSLSOCKET_DEBUG
+ qWarning() << Q_FUNC_INFO << plainSocket
+ << QStringLiteral("Cannot set certificate and key: %1").arg(err);
+#endif
+ errorCode = QAbstractSocket::SslInvalidUserDataError;
+ errorDescription = QStringLiteral("Cannot set certificate and key: %1").arg(err);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool QSslSocketBackendPrivate::setSessionProtocol()
+{
+ Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)");
+
+ OSStatus err = noErr;
+
+ // QSsl::SslV2 == kSSLProtocol2 is disabled in secure transport and
+ // always fails with errSSLIllegalParam:
+ // if (version < MINIMUM_STREAM_VERSION || version > MAXIMUM_STREAM_VERSION)
+ // return errSSLIllegalParam;
+ // where MINIMUM_STREAM_VERSION is SSL_Version_3_0, MAXIMUM_STREAM_VERSION is TLS_Version_1_2.
+ if (configuration.protocol == QSsl::SslV2) {
+ qDebug() << Q_FUNC_INFO << "protocol QSsl::SslV2 is disabled";
+ return false;
+ }
+
+ if (configuration.protocol == QSsl::SslV3) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "requesting : SSLv3";
+#endif
+ err = SSLSetProtocolVersionMin(context, kSSLProtocol3);
+ if (err == noErr)
+ err = SSLSetProtocolVersionMax(context, kSSLProtocol3);
+ } else if (configuration.protocol == QSsl::TlsV1_0) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "requesting : TLSv1.0";
+#endif
+ err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
+ if (err == noErr)
+ err = SSLSetProtocolVersionMax(context, kTLSProtocol1);
+ } else if (configuration.protocol == QSsl::TlsV1_1) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "requesting : TLSv1.1";
+#endif
+ err = SSLSetProtocolVersionMin(context, kTLSProtocol11);
+ if (err == noErr)
+ err = SSLSetProtocolVersionMax(context, kTLSProtocol11);
+ } else if (configuration.protocol == QSsl::TlsV1_2) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "requesting : TLSv1.2";
+#endif
+ err = SSLSetProtocolVersionMin(context, kTLSProtocol12);
+ if (err == noErr)
+ err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
+ } else if (configuration.protocol == QSsl::AnyProtocol) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "requesting : any";
+#endif
+ // kSSLProtocol3, since kSSLProtocol2 is disabled:
+ err = SSLSetProtocolVersionMin(context, kSSLProtocol3);
+ if (err == noErr)
+ err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
+ } else if (configuration.protocol == QSsl::TlsV1SslV3) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "requesting : SSLv3 - TLSv1.2";
+#endif
+ err = SSLSetProtocolVersionMin(context, kSSLProtocol3);
+ if (err == noErr)
+ err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
+ } else if (configuration.protocol == QSsl::SecureProtocols) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "requesting : TLSv1 - TLSv1.2";
+#endif
+ err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
+ if (err == noErr)
+ err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
+ } else {
+ qDebug() << Q_FUNC_INFO << "no protocol version found in the configuration";
+ return false;
+ }
+
+ return err == noErr;
+}
+
+bool QSslSocketBackendPrivate::verifySessionProtocol() const
+{
+ bool protocolOk = false;
+ if (configuration.protocol == QSsl::AnyProtocol)
+ protocolOk = true;
+ else if (configuration.protocol == QSsl::TlsV1SslV3)
+ protocolOk = (sessionProtocol() >= QSsl::SslV3);
+ else if (configuration.protocol == QSsl::SecureProtocols)
+ protocolOk = (sessionProtocol() >= QSsl::TlsV1_0);
+ else
+ protocolOk = (sessionProtocol() == configuration.protocol);
+
+ return protocolOk;
+}
+
+bool QSslSocketBackendPrivate::verifyPeerTrust()
+{
+ Q_Q(QSslSocket);
+
+ const QSslSocket::PeerVerifyMode verifyMode = configuration.peerVerifyMode;
+ const bool canIgnoreVerify = mode == QSslSocket::SslServerMode
+ && (verifyMode == QSslSocket::QueryPeer
+ || verifyMode == QSslSocket::AutoVerifyPeer
+ || verifyMode == QSslSocket::VerifyNone);
+
+ Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)");
+ Q_ASSERT(plainSocket);
+
+ QCFType<SecTrustRef> trust;
+ OSStatus err = SSLCopyPeerTrust(context, &trust);
+ // !trust - SSLCopyPeerTrust can return noErr but null trust.
+ if (err != noErr || !trust) {
+ if (!canIgnoreVerify) {
+ setError(QStringLiteral("Failed to obtain peer trust: %1").arg(err),
+ QAbstractSocket::SslHandshakeFailedError);
+ plainSocket->disconnectFromHost();
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ 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 ...
+ setError("SecTrustEvaluate failed", QAbstractSocket::SslHandshakeFailedError);
+ 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);
+ }
+ }
+
+ if (certCount > 0) {
+ SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, 0);
+ QCFType<CFDataRef> derData = SecCertificateCopyData(cert);
+ configuration.peerCertificate = QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
+ }
+
+ // check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer)
+ foreach (const QSslCertificate &cert, configuration.peerCertificateChain) {
+ if (QSslCertificatePrivate::isBlacklisted(cert) && !canIgnoreVerify) {
+ const QSslError error(QSslError::CertificateBlacklisted, cert);
+ errors << error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+
+ const bool doVerifyPeer = verifyMode == QSslSocket::VerifyPeer
+ || (verifyMode == QSslSocket::AutoVerifyPeer
+ && mode == QSslSocket::SslClientMode);
+ // Check the peer certificate itself. First try the subject's common name
+ // (CN) as a wildcard, then try all alternate subject name DNS entries the
+ // same way.
+ if (!configuration.peerCertificate.isNull()) {
+ // but only if we're a client connecting to a server
+ // if we're the server, don't check CN
+ if (mode == QSslSocket::SslClientMode) {
+ const QString peerName(verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName);
+ if (!isMatchingHostname(configuration.peerCertificate, peerName) && !canIgnoreVerify) {
+ // No matches in common names or alternate names.
+ const QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate);
+ errors << error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+ } else {
+ // No peer certificate presented. Report as error if the socket
+ // expected one.
+ if (doVerifyPeer && !canIgnoreVerify) {
+ const QSslError error(QSslError::NoPeerCertificate);
+ errors << error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+
+ // TODO: right now we have nothing on server side?
+ if (mode == QSslSocket::SslClientMode) {
+ // verify certificate chain
+ QCFType<CFMutableArrayRef> certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ foreach (const QSslCertificate &cert, configuration.caCertificates) {
+ QCFType<CFDataRef> certData = cert.d->derData.toCFData();
+ QCFType<SecCertificateRef> certRef = SecCertificateCreateWithData(NULL, certData);
+ CFArrayAppendValue(certArray, certRef);
+ }
+ SecTrustSetAnchorCertificates(trust, certArray);
+ SecTrustSetAnchorCertificatesOnly(trust, false);
+
+ SecTrustResultType trustResult = kSecTrustResultInvalid;
+ SecTrustEvaluate(trust, &trustResult);
+ switch (trustResult) {
+ case kSecTrustResultUnspecified:
+ case kSecTrustResultProceed:
+ break;
+ default:
+ if (!canIgnoreVerify) {
+ const QSslError error(QSslError::CertificateUntrusted, configuration.peerCertificate);
+ errors << error;
+ emit q->peerVerifyError(error);
+ }
+ }
+ }
+
+ // report errors
+ if (!errors.isEmpty() && !canIgnoreVerify) {
+ sslErrors = errors;
+ if (!checkSslErrors())
+ return false;
+ } else {
+ sslErrors.clear();
+ }
+
+ return true;
+}
+
+/*
+ Copied verbatim from qsslsocket_openssl.cpp
+*/
+bool QSslSocketBackendPrivate::checkSslErrors()
+{
+ Q_Q(QSslSocket);
+ if (sslErrors.isEmpty())
+ return true;
+
+ emit q->sslErrors(sslErrors);
+
+ const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
+ || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
+ && mode == QSslSocket::SslClientMode);
+ const bool doEmitSslError = !verifyErrorsHaveBeenIgnored();
+ // check whether we need to emit an SSL handshake error
+ if (doVerifyPeer && doEmitSslError) {
+ if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) {
+ pauseSocketNotifiers(q);
+ paused = true;
+ } else {
+ setError(sslErrors.first().errorString(),
+ QAbstractSocket::SslHandshakeFailedError);
+ plainSocket->disconnectFromHost();
+ }
+ return false;
+ }
+
+ return true;
+}
+
+bool QSslSocketBackendPrivate::startHandshake()
+{
+ Q_ASSERT(context);
+ Q_Q(QSslSocket);
+
+ OSStatus err = SSLHandshake(context);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "SSLHandhake returned" << err;
+#endif
+
+ if (err == errSSLWouldBlock) {
+ // startHandshake has to be called again ... later.
+ return false;
+ } else if (err == errSSLServerAuthCompleted) {
+ // TODO: in client mode this happens _before_ ClientCertRequested,
+ // this is the point there we should test the server certificate chain,
+ // not sending any client certificate if the server's certificate validation
+ // fails.
+ // if (!verifyPeerTrust())
+ // ....
+ return startHandshake();
+ } else if (err == errSSLClientCertRequested) {
+ // TODO: If we are here, the server's trust must
+ // be evaluated and accepted already, otherwise,
+ // we can not send our certificate.
+ Q_ASSERT(mode == QSslSocket::SslClientMode);
+ QString errorDescription;
+ QAbstractSocket::SocketError errorCode = QAbstractSocket::UnknownSocketError;
+ // setSessionCertificate does not fail if we have no certificate.
+ // Failure means a real error (invalid certificate, no private key, etc).
+ if (!setSessionCertificate(errorDescription, errorCode)) {
+ qWarning() << Q_FUNC_INFO << "Failed to provide a client certificate";
+ setError(errorDescription, errorCode);
+ return false;
+ } else {
+ // We try to resume a handshake, even if have no
+ // local certificates ... (up to server to deal with our failure).
+ return startHandshake();
+ }
+ } else if (err != errSecSuccess) {
+ setError(QStringLiteral("Error during SSL handshake: %1").arg(err),
+ QAbstractSocket::SslHandshakeFailedError);
+ plainSocket->disconnectFromHost();
+ return false;
+ }
+
+ // Connection aborted during handshake phase.
+ if (q->state() != QAbstractSocket::ConnectedState) {
+ qDebug() << Q_FUNC_INFO << "connection aborted";
+ return false;
+ }
+
+ // check protocol version ourselves, as Secure Transport does not enforce
+ // the requested min / max versions.
+ if (!verifySessionProtocol()) {
+ setError("Protocol version mismatch",
+ QAbstractSocket::SslHandshakeFailedError);
+ plainSocket->disconnectFromHost();
+ return false;
+ }
+
+ if (verifyPeerTrust()) {
+ continueHandshake();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void QSslSocketBackendPrivate::setError(const QString &errorString,
+ QAbstractSocket::SocketError errorCode)
+{
+ Q_Q(QSslSocket);
+
+ q->setErrorString(errorString);
+ q->setSocketError(errorCode);
+ emit q->error(errorCode);
+}
+
+/*
+ PKCS12 helpers.
+*/
+
+static QAsn1Element wrap(quint8 type, const QAsn1Element &child)
+{
+ QByteArray value;
+ QDataStream stream(&value, QIODevice::WriteOnly);
+ child.write(stream);
+ return QAsn1Element(type, value);
+}
+
+static QAsn1Element _q_PKCS7_data(const QByteArray &data)
+{
+ QVector<QAsn1Element> items;
+ items << QAsn1Element::fromObjectId("1.2.840.113549.1.7.1");
+ items << wrap(QAsn1Element::Context0Type,
+ QAsn1Element(QAsn1Element::OctetStringType, data));
+ return QAsn1Element::fromVector(items);
+}
+
+/*!
+ PKCS #12 key derivation.
+
+ Some test vectors:
+ http://www.drh-consultancy.demon.co.uk/test.txt
+*/
+static QByteArray _q_PKCS12_keygen(char id, const QByteArray &salt, const QString &passPhrase, int n, int r)
+{
+ const int u = 20;
+ const int v = 64;
+
+ // password formatting
+ QByteArray passUnicode(passPhrase.size() * 2 + 2, '\0');
+ char *p = passUnicode.data();
+ for (int i = 0; i < passPhrase.size(); ++i) {
+ quint16 ch = passPhrase[i].unicode();
+ *(p++) = (ch & 0xff00) >> 8;
+ *(p++) = (ch & 0xff);
+ }
+
+ // prepare I
+ QByteArray D(64, id);
+ QByteArray S, P;
+ const int sSize = v * ((salt.size() + v - 1) / v);
+ S.resize(sSize);
+ for (int i = 0; i < sSize; ++i) {
+ S[i] = salt[i % salt.size()];
+ }
+ const int pSize = v * ((passUnicode.size() + v - 1) / v);
+ P.resize(pSize);
+ for (int i = 0; i < pSize; ++i) {
+ P[i] = passUnicode[i % passUnicode.size()];
+ }
+ QByteArray I = S + P;
+
+ // apply hashing
+ const int c = (n + u - 1) / u;
+ QByteArray A;
+ QByteArray B;
+ B.resize(v);
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ for (int i = 0; i < c; ++i) {
+ // hash r iterations
+ QByteArray Ai = D + I;
+ for (int j = 0; j < r; ++j) {
+ hash.reset();
+ hash.addData(Ai);
+ Ai = hash.result();
+ }
+
+ for (int j = 0; j < v; ++j) {
+ B[j] = Ai[j % u];
+ }
+
+ // modify I as Ij = (Ij + B + 1) modulo 2^v
+ for (int p = 0; p < I.size(); p += v) {
+ quint8 carry = 1;
+ for (int j = v - 1; j >= 0; --j) {
+ quint16 v = quint8(I[p+j]) + quint8(B[j]) + carry;
+ I[p+j] = v & 0xff;
+ carry = (v & 0xff00) >> 8;
+ }
+ }
+ A += Ai;
+ }
+ return A.left(n);
+}
+
+static QByteArray _q_PKCS12_salt()
+{
+ QByteArray salt;
+ salt.resize(8);
+ for (int i = 0; i < salt.size(); ++i) {
+ salt[i] = (qrand() & 0xff);
+ }
+ return salt;
+}
+
+static QByteArray _q_PKCS12_certBag(const QSslCertificate &cert)
+{
+ QVector<QAsn1Element> items;
+ items << QAsn1Element::fromObjectId("1.2.840.113549.1.12.10.1.3");
+
+ // certificate
+ QVector<QAsn1Element> certItems;
+ certItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.22.1");
+ certItems << wrap(QAsn1Element::Context0Type,
+ QAsn1Element(QAsn1Element::OctetStringType, cert.toDer()));
+ items << wrap(QAsn1Element::Context0Type,
+ QAsn1Element::fromVector(certItems));
+
+ // local key id
+ const QByteArray localKeyId = cert.digest(QCryptographicHash::Sha1);
+ QVector<QAsn1Element> idItems;
+ idItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.21");
+ idItems << wrap(QAsn1Element::SetType,
+ QAsn1Element(QAsn1Element::OctetStringType, localKeyId));
+ items << wrap(QAsn1Element::SetType, QAsn1Element::fromVector(idItems));
+
+ // dump
+ QAsn1Element root = wrap(QAsn1Element::SequenceType, QAsn1Element::fromVector(items));
+ QByteArray ba;
+ QDataStream stream(&ba, QIODevice::WriteOnly);
+ root.write(stream);
+ return ba;
+}
+
+static QAsn1Element _q_PKCS12_key(const QSslKey &key)
+{
+ Q_ASSERT(key.algorithm() == QSsl::Rsa || key.algorithm() == QSsl::Dsa);
+
+ QVector<QAsn1Element> keyItems;
+ keyItems << QAsn1Element::fromInteger(0);
+ QVector<QAsn1Element> algoItems;
+ if (key.algorithm() == QSsl::Rsa)
+ algoItems << QAsn1Element::fromObjectId(RSA_ENCRYPTION_OID);
+ else if (key.algorithm() == QSsl::Dsa)
+ algoItems << QAsn1Element::fromObjectId(DSA_ENCRYPTION_OID);
+ algoItems << QAsn1Element(QAsn1Element::NullType);
+ keyItems << QAsn1Element::fromVector(algoItems);
+ keyItems << QAsn1Element(QAsn1Element::OctetStringType, key.toDer());
+ return QAsn1Element::fromVector(keyItems);
+}
+
+static QByteArray _q_PKCS12_shroudedKeyBag(const QSslKey &key, const QString &passPhrase, const QByteArray &localKeyId)
+{
+ const int iterations = 2048;
+ QByteArray salt = _q_PKCS12_salt();
+ QByteArray cKey = _q_PKCS12_keygen(1, salt, passPhrase, 24, iterations);
+ QByteArray cIv = _q_PKCS12_keygen(2, salt, passPhrase, 8, iterations);
+
+ // prepare and encrypt data
+ QByteArray plain;
+ QDataStream plainStream(&plain, QIODevice::WriteOnly);
+ _q_PKCS12_key(key).write(plainStream);
+ QByteArray crypted = QSslKeyPrivate::encrypt(QSslKeyPrivate::DesEde3Cbc,
+ plain, cKey, cIv);
+
+ QVector<QAsn1Element> items;
+ items << QAsn1Element::fromObjectId("1.2.840.113549.1.12.10.1.2");
+
+ // key
+ QVector<QAsn1Element> keyItems;
+ QVector<QAsn1Element> algoItems;
+ algoItems << QAsn1Element::fromObjectId("1.2.840.113549.1.12.1.3");
+ QVector<QAsn1Element> paramItems;
+ paramItems << QAsn1Element(QAsn1Element::OctetStringType, salt);
+ paramItems << QAsn1Element::fromInteger(iterations);
+ algoItems << QAsn1Element::fromVector(paramItems);
+ keyItems << QAsn1Element::fromVector(algoItems);
+ keyItems << QAsn1Element(QAsn1Element::OctetStringType, crypted);
+ items << wrap(QAsn1Element::Context0Type,
+ QAsn1Element::fromVector(keyItems));
+
+ // local key id
+ QVector<QAsn1Element> idItems;
+ idItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.21");
+ idItems << wrap(QAsn1Element::SetType,
+ QAsn1Element(QAsn1Element::OctetStringType, localKeyId));
+ items << wrap(QAsn1Element::SetType,
+ QAsn1Element::fromVector(idItems));
+
+ // dump
+ QAsn1Element root = wrap(QAsn1Element::SequenceType, QAsn1Element::fromVector(items));
+ QByteArray ba;
+ QDataStream stream(&ba, QIODevice::WriteOnly);
+ root.write(stream);
+ return ba;
+}
+
+static QByteArray _q_PKCS12_bag(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase)
+{
+ QVector<QAsn1Element> items;
+
+ // certs
+ for (int i = 0; i < certs.size(); ++i)
+ items << _q_PKCS7_data(_q_PKCS12_certBag(certs[i]));
+
+ // key
+ const QByteArray localKeyId = certs.first().digest(QCryptographicHash::Sha1);
+ items << _q_PKCS7_data(_q_PKCS12_shroudedKeyBag(key, passPhrase, localKeyId));
+
+ // dump
+ QAsn1Element root = QAsn1Element::fromVector(items);
+ QByteArray ba;
+ QDataStream stream(&ba, QIODevice::WriteOnly);
+ root.write(stream);
+ return ba;
+}
+
+static QAsn1Element _q_PKCS12_mac(const QByteArray &data, const QString &passPhrase)
+{
+ const int iterations = 2048;
+
+ // salt generation
+ QByteArray macSalt = _q_PKCS12_salt();
+ QByteArray key = _q_PKCS12_keygen(3, macSalt, passPhrase, 20, iterations);
+
+ // HMAC calculation
+ QMessageAuthenticationCode hmac(QCryptographicHash::Sha1, key);
+ hmac.addData(data);
+
+ QVector<QAsn1Element> algoItems;
+ algoItems << QAsn1Element::fromObjectId("1.3.14.3.2.26");
+ algoItems << QAsn1Element(QAsn1Element::NullType);
+
+ QVector<QAsn1Element> digestItems;
+ digestItems << QAsn1Element::fromVector(algoItems);
+ digestItems << QAsn1Element(QAsn1Element::OctetStringType, hmac.result());
+
+ QVector<QAsn1Element> macItems;
+ macItems << QAsn1Element::fromVector(digestItems);
+ macItems << QAsn1Element(QAsn1Element::OctetStringType, macSalt);
+ macItems << QAsn1Element::fromInteger(iterations);
+ return QAsn1Element::fromVector(macItems);
+}
+
+QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase)
+{
+ QVector<QAsn1Element> items;
+
+ // version
+ items << QAsn1Element::fromInteger(3);
+
+ // auth safe
+ const QByteArray data = _q_PKCS12_bag(certs, key, passPhrase);
+ items << _q_PKCS7_data(data);
+
+ // HMAC
+ items << _q_PKCS12_mac(data, passPhrase);
+
+ // dump
+ QAsn1Element root = QAsn1Element::fromVector(items);
+ QByteArray ba;
+ QDataStream stream(&ba, QIODevice::WriteOnly);
+ root.write(stream);
+ return ba;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_mac_p.h b/src/network/ssl/qsslsocket_mac_p.h
new file mode 100644
index 0000000000..d8b7fbd019
--- /dev/null
+++ b/src/network/ssl/qsslsocket_mac_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSSLSOCKET_MAC_P_H
+#define QSSLSOCKET_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QtNetwork library. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qcore_mac_p.h>
+
+#include <QtCore/qstring.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qlist.h>
+
+#include "qabstractsocket.h"
+#include "qsslsocket_p.h"
+
+#include <Security/Security.h>
+#include <Security/SecureTransport.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSslSocketBackendPrivate : public QSslSocketPrivate
+{
+ Q_DECLARE_PUBLIC(QSslSocket)
+public:
+ QSslSocketBackendPrivate();
+ virtual ~QSslSocketBackendPrivate();
+
+ // Final-overriders (QSslSocketPrivate):
+ void continueHandshake() Q_DECL_OVERRIDE;
+ void disconnected() Q_DECL_OVERRIDE;
+ void disconnectFromHost() Q_DECL_OVERRIDE;
+ QSslCipher sessionCipher() const Q_DECL_OVERRIDE;
+ QSsl::SslProtocol sessionProtocol() const Q_DECL_OVERRIDE;
+ void startClientEncryption() Q_DECL_OVERRIDE;
+ void startServerEncryption() Q_DECL_OVERRIDE;
+ void transmit() Q_DECL_OVERRIDE;
+
+ static QList<QSslError> (verify)(QList<QSslCertificate> certificateChain,
+ const QString &hostName);
+
+ static bool importPkcs12(QIODevice *device,
+ QSslKey *key, QSslCertificate *cert,
+ QList<QSslCertificate> *caCertificates,
+ const QByteArray &passPhrase);
+
+ static QSslCipher QSslCipher_from_SSLCipherSuite(SSLCipherSuite cipher);
+
+private:
+ // SSL context management/properties:
+ bool initSslContext();
+ void destroySslContext();
+ bool setSessionCertificate(QString &errorDescription,
+ QAbstractSocket::SocketError &errorCode);
+ bool setSessionProtocol();
+ // Aux. functions to do a verification during handshake phase:
+ bool verifySessionProtocol() const;
+ bool verifyPeerTrust();
+
+ bool checkSslErrors();
+ bool startHandshake();
+
+ // Aux. function, sets:
+ //1) socket error code,
+ //2) error string (description)
+ //3) emits a signal.
+ void setError(const QString &errorString,
+ QAbstractSocket::SocketError errorCode);
+
+ mutable QCFType<SSLContextRef> context;
+
+ Q_DISABLE_COPY(QSslSocketBackendPrivate);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index 3262b41bbd..f0e4896f2b 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -53,6 +53,8 @@
#include "qsslconfiguration_p.h"
#ifndef QT_NO_OPENSSL
#include <private/qsslcontext_openssl_p.h>
+#else
+class QSslContext;
#endif
#include <QtCore/qstringlist.h>
diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri
index 210606a1a0..29c47cd7c6 100644
--- a/src/network/ssl/ssl.pri
+++ b/src/network/ssl/ssl.pri
@@ -40,6 +40,15 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op
ssl/qsslsocket_winrt.cpp \
ssl/qsslellipticcurve_dummy.cpp
}
+
+ contains(QT_CONFIG, securetransport) {
+ HEADERS += ssl/qsslsocket_mac_p.h
+ SOURCES += ssl/qsslcertificate_qt.cpp \
+ ssl/qsslkey_qt.cpp \
+ ssl/qsslkey_mac.cpp \
+ ssl/qsslsocket_mac.cpp \
+ ssl/qsslellipticcurve_dummy.cpp
+ }
}
contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) {