summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Knight <andrew.knight@digia.com>2014-08-28 14:05:49 +0300
committerAndrew Knight <andrew.knight@digia.com>2014-08-30 08:21:10 +0200
commit74a7f13ac153fa9c48ee72909ce0b3e4b4e882da (patch)
tree73f7145de286c00cedcb59c974457aba2036793b /src
parent5328ec7e1043ac892b46afd4c315d4b8e3136aed (diff)
winrt: Implement missing SSL socket methods
Move from a stub to a functional SSL socket implementation. This implementation has some limitations. Due to the way the native SSL upgrade works, it is not possible to ignore SSL errors after the handshake has begun. The user must set the ignore flags before connecting to the host. Due to missing implementation in the underlying native socket, the synchronous methods (waitForConnected(), waitForDisconnected()) are not functional either. Users must rely on the asynchronous methods instead. This is not a problem in the general case, as HTTP(S) is not affected. SSL server sockets are not supported by the native API, so it is not possible to bind an SSL server socket. Change-Id: Id0b323f273892580b294aa5a6ff601a8241470df Reviewed-by: Maurice Kalinowski <maurice.kalinowski@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/network/ssl/qsslsocket_winrt.cpp582
-rw-r--r--src/network/ssl/qsslsocket_winrt_p.h48
2 files changed, 562 insertions, 68 deletions
diff --git a/src/network/ssl/qsslsocket_winrt.cpp b/src/network/ssl/qsslsocket_winrt.cpp
index da4cf91f49..c9ddd9ec1b 100644
--- a/src/network/ssl/qsslsocket_winrt.cpp
+++ b/src/network/ssl/qsslsocket_winrt.cpp
@@ -39,39 +39,132 @@
**
****************************************************************************/
-/****************************************************************************
-**
-** In addition, as a special exception, the copyright holders listed above give
-** permission to link the code of its release of Qt with the OpenSSL project's
-** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
-** same license as the original version), and distribute the linked executables.
-**
-** You must comply with the GNU General Public License version 2 in all
-** respects for all of the code used other than the "OpenSSL" code. If you
-** modify this file, you may extend this exception to your version of the file,
-** but you are not obligated to do so. If you do not wish to do so, delete
-** this exception statement from your version of this file.
-**
-****************************************************************************/
-
-//#define QSSLSOCKET_DEBUG
-//#define QT_DECRYPT_SSL_TRAFFIC
-
#include "qsslsocket_winrt_p.h"
#include "qsslsocket.h"
#include "qsslcertificate_p.h"
+#include "qsslcipher_p.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QSysInfo>
+#include <QtCore/qfunctions_winrt.h>
+#include <private/qnativesocketengine_winrt_p.h>
+
+#include <windows.networking.h>
+#include <windows.networking.sockets.h>
+#include <windows.security.cryptography.certificates.h>
+#include <robuffer.h>
+
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Foundation::Collections;
+using namespace ABI::Windows::Networking;
+using namespace ABI::Windows::Networking::Sockets;
+using namespace ABI::Windows::Security::Cryptography::Certificates;
+using namespace ABI::Windows::Storage::Streams;
QT_BEGIN_NAMESPACE
-bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
+// For QSet<QSslError>
+inline uint qHash(const QSslError &error, uint seed)
+ Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(error)))
+{ return (qHash(error.error()) ^ seed); }
+
+// For QSet<QSslCertificate>
+inline uint qHash(const QSslCertificate &certificate, uint seed)
+ Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(certificate)))
+{ return (qHash(certificate.handle()) ^ seed); }
+
+bool QSslSocketPrivate::s_libraryLoaded = true;
+bool QSslSocketPrivate::s_loadRootCertsOnDemand = true;
+bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
+
+struct SslSocketGlobal
+{
+ SslSocketGlobal()
+ {
+ HRESULT hr;
+ hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
+ &hostNameFactory);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ ComPtr<ICertificateStoresStatics> certificateStores;
+ hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Certificates_CertificateStores).Get(),
+ &certificateStores);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ hr = certificateStores->get_TrustedRootCertificationAuthorities(&rootStore);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ ComPtr<IAsyncOperation<IVectorView<Certificate *> *>> op;
+ hr = certificateStores->FindAllAsync(&op);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IVectorView<Certificate *>> certificates;
+ hr = QWinRTFunctions::await(op, certificates.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ quint32 size;
+ hr = certificates->get_Size(&size);
+ Q_ASSERT_SUCCEEDED(hr);
+ for (quint32 i = 0; i < size; ++i) {
+ ComPtr<ICertificate> certificate;
+ hr = certificates->GetAt(i, &certificate);
+ Q_ASSERT_SUCCEEDED(hr);
+ systemCaCertificates.append(QSslCertificatePrivate::QSslCertificate_from_Certificate(certificate.Get()));
+ }
+ }
+
+ void syncCaCertificates(const QSet<QSslCertificate> &add, const QSet<QSslCertificate> &remove)
+ {
+ QMutexLocker locker(&certificateMutex);
+ foreach (const QSslCertificate &certificate, add) {
+ QHash<QSslCertificate, QAtomicInt>::iterator it = additionalCertificates.find(certificate);
+ if (it != additionalCertificates.end()) {
+ it.value().ref(); // Add a reference
+ } else {
+ // install certificate
+ HRESULT hr;
+ hr = rootStore->Add(static_cast<ICertificate *>(certificate.handle()));
+ Q_ASSERT_SUCCEEDED(hr);
+ additionalCertificates.insert(certificate, 1);
+ }
+ }
+ foreach (const QSslCertificate &certificate, remove) {
+ QHash<QSslCertificate, QAtomicInt>::iterator it = additionalCertificates.find(certificate);
+ if (it != additionalCertificates.end() && !it.value().deref()) {
+ // no more references, remove certificate
+ HRESULT hr;
+ hr = rootStore->Delete(static_cast<ICertificate *>(certificate.handle()));
+ Q_ASSERT_SUCCEEDED(hr);
+ additionalCertificates.erase(it);
+ }
+ }
+ }
+
+ ComPtr<IHostNameFactory> hostNameFactory;
+ QList<QSslCertificate> systemCaCertificates;
+
+private:
+ QMutex certificateMutex;
+ ComPtr<ICertificateStore> rootStore;
+ QHash<QSslCertificate, QAtomicInt> additionalCertificates;
+};
+Q_GLOBAL_STATIC(SslSocketGlobal, g)
+
+// Called on the socket's thread to avoid cross-thread deletion
+void QSslSocketConnectionHelper::disconnectSocketFromHost()
+{
+ if (d->plainSocket)
+ d->plainSocket->disconnectFromHost();
+}
QSslSocketBackendPrivate::QSslSocketBackendPrivate()
+ : connectionHelper(new QSslSocketConnectionHelper(this))
{
- ensureInitialized();
}
QSslSocketBackendPrivate::~QSslSocketBackendPrivate()
{
+ g->syncCaCertificates(QSet<QSslCertificate>(), previousCaCertificates);
}
void QSslSocketPrivate::deinitialize()
@@ -84,31 +177,28 @@ bool QSslSocketPrivate::supportsSsl()
return true;
}
-bool QSslSocketPrivate::ensureLibraryLoaded()
-{
- return true;
-}
-
-void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
-{
- Q_UNIMPLEMENTED();
-}
-
void QSslSocketPrivate::ensureInitialized()
{
+ if (s_loadedCiphersAndCerts)
+ return;
+ s_loadedCiphersAndCerts = true;
+ resetDefaultCiphers();
}
long QSslSocketPrivate::sslLibraryVersionNumber()
{
- Q_UNIMPLEMENTED();
- return 0;
+ return QSysInfo::windowsVersion();
}
-
QString QSslSocketPrivate::sslLibraryVersionString()
{
- Q_UNIMPLEMENTED();
- return QString::number(sslLibraryVersionNumber());
+ switch (QSysInfo::windowsVersion()) {
+ case QSysInfo::WV_WINDOWS8_1:
+ return QStringLiteral("Windows Runtime 8.1 SSL library");
+ default:
+ break;
+ }
+ return QStringLiteral("Windows Runtime SSL library");
}
long QSslSocketPrivate::sslLibraryBuildVersionNumber()
@@ -125,20 +215,68 @@ QString QSslSocketPrivate::sslLibraryBuildVersionString()
void QSslSocketPrivate::resetDefaultCiphers()
{
- Q_UNIMPLEMENTED();
+ setDefaultSupportedCiphers(QSslSocketBackendPrivate::defaultCiphers());
+ setDefaultCiphers(QSslSocketBackendPrivate::defaultCiphers());
+}
+
+
+QList<QSslCipher> QSslSocketBackendPrivate::defaultCiphers()
+{
+ QList<QSslCipher> ciphers;
+ const QString protocolStrings[] = { QStringLiteral("SSLv3"), QStringLiteral("TLSv1"),
+ QStringLiteral("TLSv1.1"), QStringLiteral("TLSv1.2") };
+ const QSsl::SslProtocol protocols[] = { QSsl::SslV3, QSsl::TlsV1_0, QSsl::TlsV1_1, QSsl::TlsV1_2 };
+ for (int i = 0; i < ARRAYSIZE(protocols); ++i) {
+ QSslCipher cipher;
+ cipher.d->isNull = false;
+ cipher.d->name = QStringLiteral("WINRT");
+ cipher.d->protocol = protocols[i];
+ cipher.d->protocolString = protocolStrings[i];
+ ciphers.append(cipher);
+ }
+ return ciphers;
}
QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
{
- Q_UNIMPLEMENTED();
- ensureInitialized();
- QList<QSslCertificate> systemCerts;
- return systemCerts;
+ return g->systemCaCertificates;
}
void QSslSocketBackendPrivate::startClientEncryption()
{
- Q_UNIMPLEMENTED();
+ Q_Q(QSslSocket);
+
+ QSsl::SslProtocol protocol = q->protocol();
+ switch (q->protocol()) {
+ case QSsl::AnyProtocol:
+ case QSsl::SslV3:
+ protectionLevel = SocketProtectionLevel_Ssl; // Only use this value if weak cipher support is required
+ break;
+ case QSsl::SecureProtocols:
+ case QSsl::TlsV1SslV3:
+ case QSsl::TlsV1_0:
+ protectionLevel = SocketProtectionLevel_Tls10;
+ break;
+ case QSsl::TlsV1_1:
+ protectionLevel = SocketProtectionLevel_Tls11;
+ break;
+ case QSsl::TlsV1_2:
+ protectionLevel = SocketProtectionLevel_Tls12;
+ break;
+ default:
+ protectionLevel = SocketProtectionLevel_Tls12; // default to highest
+ protocol = QSsl::TlsV1_2;
+ break;
+ }
+
+ // Sync custom certificates
+ const QSet<QSslCertificate> caCertificates = configuration.caCertificates.toSet();
+ const QSet<QSslCertificate> newCertificates = caCertificates - previousCaCertificates;
+ const QSet<QSslCertificate> oldCertificates = previousCaCertificates - caCertificates;
+ g->syncCaCertificates(newCertificates, oldCertificates);
+ previousCaCertificates = caCertificates;
+
+ continueHandshake();
}
void QSslSocketBackendPrivate::startServerEncryption()
@@ -148,33 +286,379 @@ void QSslSocketBackendPrivate::startServerEncryption()
void QSslSocketBackendPrivate::transmit()
{
- Q_UNIMPLEMENTED();
+ Q_Q(QSslSocket);
+
+ if (connectionEncrypted && !writeBuffer.isEmpty()) {
+ qint64 totalBytesWritten = 0;
+ int nextDataBlockSize;
+ while ((nextDataBlockSize = writeBuffer.nextDataBlockSize()) > 0) {
+ int writtenBytes = plainSocket->write(writeBuffer.readPointer(), nextDataBlockSize);
+ writtenBytes = nextDataBlockSize;
+
+ 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;
+ }
+ }
+ }
+
+ // Check if we've got any data to be read from the socket.
+ int pendingBytes;
+ bool bytesRead = false;
+ while ((pendingBytes = plainSocket->bytesAvailable()) > 0) {
+ char *ptr = buffer.reserve(pendingBytes);
+ int readBytes = plainSocket->read(ptr, pendingBytes);
+ buffer.chop(pendingBytes - readBytes);
+ bytesRead = true;
+ }
+
+ if (bytesRead) {
+ if (readyReadEmittedPointer)
+ *readyReadEmittedPointer = true;
+ emit q->readyRead();
+ }
+
+ if (pendingClose) {
+ pendingClose = false;
+ q->disconnectFromHost();
+ }
}
void QSslSocketBackendPrivate::disconnectFromHost()
{
- Q_UNIMPLEMENTED();
+ QMetaObject::invokeMethod(connectionHelper.data(), "disconnectSocketFromHost", Qt::QueuedConnection);
}
void QSslSocketBackendPrivate::disconnected()
{
- Q_UNIMPLEMENTED();
}
QSslCipher QSslSocketBackendPrivate::sessionCipher() const
{
- Q_UNIMPLEMENTED();
- return QSslCipher();
+ return configuration.sessionCipher;
}
QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
{
- Q_UNIMPLEMENTED();
- return QSsl::UnknownProtocol;
+ return configuration.sessionCipher.protocol();
}
+
void QSslSocketBackendPrivate::continueHandshake()
{
- Q_UNIMPLEMENTED();
+ Q_Q(QSslSocket);
+
+ IStreamSocket *socket = reinterpret_cast<IStreamSocket *>(plainSocket->socketDescriptor());
+ if (qintptr(socket) == -1) {
+ q->setErrorString(QStringLiteral("At attempt was made to continue the handshake on an invalid socket."));
+ q->setSocketError(QAbstractSocket::SslInternalError);
+ emit q->error(QAbstractSocket::SslInternalError);
+ return;
+ }
+
+ HRESULT hr;
+ ComPtr<IHostName> hostName;
+ const QString host = verificationPeerName.isEmpty() ? plainSocket->peerName()
+ : verificationPeerName;
+ if (host.isEmpty()) {
+ ComPtr<IStreamSocketInformation> info;
+ hr = socket->get_Information(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = info->get_RemoteAddress(&hostName);
+ } else {
+ HStringReference hostRef(reinterpret_cast<LPCWSTR>(host.utf16()), host.length());
+ hr = g->hostNameFactory->CreateHostName(hostRef.Get(), &hostName);
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+ if (FAILED(hr)) {
+ q->setErrorString(qt_error_string(hr));
+ q->setSocketError(QAbstractSocket::SslInvalidUserDataError);
+ emit q->error(QAbstractSocket::SslInvalidUserDataError);
+ return;
+ }
+
+ ComPtr<IStreamSocketControl> control;
+ hr = socket->get_Control(&control);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ ComPtr<IStreamSocketControl2> control2;
+ hr = control.As(&control2);
+ ComPtr<IVector<ChainValidationResult>> ignoreList;
+ hr = control2->get_IgnorableServerCertificateErrors(&ignoreList);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ QSet<QSslError> ignoreErrors = ignoreErrorsList.toSet();
+ for (int i = ChainValidationResult_Untrusted; i < ChainValidationResult_OtherErrors + 1; ++i) {
+ // Populate the native ignore list - break to add, continue to skip
+ switch (i) {
+ case ChainValidationResult_Revoked:
+ case ChainValidationResult_InvalidSignature:
+ case ChainValidationResult_BasicConstraintsError:
+ case ChainValidationResult_InvalidCertificateAuthorityPolicy:
+ case ChainValidationResult_UnknownCriticalExtension:
+ case ChainValidationResult_OtherErrors:
+ continue; // The above errors can't be ignored in the handshake
+ case ChainValidationResult_Untrusted:
+ if (ignoreAllSslErrors || ignoreErrors.contains(QSslError::CertificateUntrusted))
+ break;
+ continue;
+ case ChainValidationResult_Expired:
+ if (ignoreAllSslErrors || ignoreErrors.contains(QSslError::CertificateExpired))
+ break;
+ continue;
+ case ChainValidationResult_IncompleteChain:
+ if (ignoreAllSslErrors
+ || ignoreErrors.contains(QSslError::InvalidCaCertificate)
+ || ignoreErrors.contains(QSslError::UnableToVerifyFirstCertificate)
+ || ignoreErrors.contains(QSslError::UnableToGetIssuerCertificate)) {
+ break;
+ }
+ continue;
+ case ChainValidationResult_WrongUsage:
+ if (ignoreAllSslErrors || ignoreErrors.contains(QSslError::InvalidPurpose))
+ break;
+ continue;
+ case ChainValidationResult_InvalidName:
+ if (ignoreAllSslErrors
+ || ignoreErrors.contains(QSslError::HostNameMismatch)
+ || ignoreErrors.contains(QSslError::SubjectIssuerMismatch)) {
+ break;
+ }
+ continue;
+ case ChainValidationResult_RevocationInformationMissing:
+ case ChainValidationResult_RevocationFailure:
+ default:
+ if (ignoreAllSslErrors)
+ break;
+ continue;
+ }
+ hr = ignoreList->Append(static_cast<ChainValidationResult>(i));
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+
+ ComPtr<IAsyncAction> op;
+ hr = socket->UpgradeToSslAsync(protectionLevel, hostName.Get(), &op);
+ if (FAILED(hr)) {
+ q->setErrorString(QSslSocket::tr("Error creating SSL session: %1")
+ .arg(qt_error_string(hr)));
+ q->setSocketError(QAbstractSocket::SslInternalError);
+ emit q->error(QAbstractSocket::SslInternalError);
+ return;
+ }
+
+ hr = op->put_Completed(Callback<IAsyncActionCompletedHandler>(
+ this, &QSslSocketBackendPrivate::onSslUpgrade).Get());
+ Q_ASSERT_SUCCEEDED(hr);
+}
+
+HRESULT QSslSocketBackendPrivate::onSslUpgrade(IAsyncAction *action, AsyncStatus)
+{
+ Q_Q(QSslSocket);
+
+ if (wasDeleted) {
+ qWarning("SSL upgrade callback received after the delegate was deleted. "
+ "This may be indicative of an internal bug in the WinRT SSL implementation.");
+ return S_OK;
+ }
+
+ HRESULT hr = action->GetResults();
+ QSet<QSslError> errors;
+ switch (hr) {
+ case SEC_E_INVALID_TOKEN: // Occurs when the server doesn't support the requested protocol
+ q->setErrorString(qt_error_string(hr));
+ q->setSocketError(QAbstractSocket::SslHandshakeFailedError);
+ emit q->error(QAbstractSocket::SslHandshakeFailedError);
+ q->disconnectFromHost();
+ return S_OK;
+ default:
+ if (FAILED(hr))
+ qErrnoWarning(hr, "error"); // Unhandled error; let sslErrors take care of it
+ break;
+ }
+
+ IStreamSocket *socket = reinterpret_cast<IStreamSocket *>(plainSocket->socketDescriptor());
+ if (qintptr(socket) == -1) {
+ qWarning("The underlying TCP socket used by the SSL socket is invalid. "
+ "This may be indicative of an internal bug in the WinRT SSL implementation.");
+ return S_OK;
+ }
+
+ ComPtr<IStreamSocketInformation> info;
+ hr = socket->get_Information(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IStreamSocketInformation2> info2;
+ hr = info.As(&info2);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ // Cipher
+ QSsl::SslProtocol protocol;
+ SocketProtectionLevel protectionLevel;
+ hr = info->get_ProtectionLevel(&protectionLevel);
+ switch (protectionLevel) {
+ default:
+ protocol = QSsl::UnknownProtocol;
+ break;
+ case SocketProtectionLevel_Ssl:
+ protocol = QSsl::SslV3;
+ break;
+ case SocketProtectionLevel_Tls10:
+ protocol = QSsl::TlsV1_0;
+ break;
+ case SocketProtectionLevel_Tls11:
+ protocol = QSsl::TlsV1_1;
+ break;
+ case SocketProtectionLevel_Tls12:
+ protocol = QSsl::TlsV1_2;
+ break;
+ }
+ configuration.sessionCipher = QSslCipher(QStringLiteral("WINRT"), protocol); // The actual cipher name is not accessible
+
+ // Certificate & chain
+ ComPtr<ICertificate> certificate;
+ hr = info2->get_ServerCertificate(&certificate);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ QList<QSslCertificate> peerCertificateChain;
+ if (certificate) {
+ ComPtr<IAsyncOperation<CertificateChain *>> op;
+ hr = certificate->BuildChainAsync(Q_NULLPTR, &op);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<ICertificateChain> certificateChain;
+ hr = QWinRTFunctions::await(op, certificateChain.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+
+ ComPtr<IVectorView<Certificate *>> certificates;
+ hr = certificateChain->GetCertificates(true, &certificates);
+ Q_ASSERT_SUCCEEDED(hr);
+ quint32 certificatesLength;
+ hr = certificates->get_Size(&certificatesLength);
+ Q_ASSERT_SUCCEEDED(hr);
+ for (quint32 i = 0; i < certificatesLength; ++i) {
+ ComPtr<ICertificate> chainCertificate;
+ hr = certificates->GetAt(i, &chainCertificate);
+ Q_ASSERT_SUCCEEDED(hr);
+ peerCertificateChain.append(QSslCertificatePrivate::QSslCertificate_from_Certificate(chainCertificate.Get()));
+ }
+ }
+
+ configuration.peerCertificate = certificate ? QSslCertificatePrivate::QSslCertificate_from_Certificate(certificate.Get())
+ : QSslCertificate();
+ configuration.peerCertificateChain = peerCertificateChain;
+
+ // Errors
+ ComPtr<IVectorView<ChainValidationResult>> chainValidationResults;
+ hr = info2->get_ServerCertificateErrors(&chainValidationResults);
+ Q_ASSERT_SUCCEEDED(hr);
+ quint32 size;
+ hr = chainValidationResults->get_Size(&size);
+ Q_ASSERT_SUCCEEDED(hr);
+ for (quint32 i = 0; i < size; ++i) {
+ ChainValidationResult result;
+ hr = chainValidationResults->GetAt(i, &result);
+ Q_ASSERT_SUCCEEDED(hr);
+ switch (result) {
+ case ChainValidationResult_Success:
+ break;
+ case ChainValidationResult_Untrusted:
+ errors.insert(QSslError::CertificateUntrusted);
+ break;
+ case ChainValidationResult_Revoked:
+ errors.insert(QSslError::CertificateRevoked);
+ break;
+ case ChainValidationResult_Expired:
+ errors.insert(QSslError::CertificateExpired);
+ break;
+ case ChainValidationResult_IncompleteChain:
+ errors.insert(QSslError::UnableToGetIssuerCertificate);
+ break;
+ case ChainValidationResult_InvalidSignature:
+ errors.insert(QSslError::CertificateSignatureFailed);
+ break;
+ case ChainValidationResult_WrongUsage:
+ errors.insert(QSslError::InvalidPurpose);
+ break;
+ case ChainValidationResult_InvalidName:
+ errors.insert(QSslError::HostNameMismatch);
+ break;
+ case ChainValidationResult_InvalidCertificateAuthorityPolicy:
+ errors.insert(QSslError::InvalidCaCertificate);
+ break;
+ default:
+ errors.insert(QSslError::UnspecifiedError);
+ break;
+ }
+ }
+
+ sslErrors = errors.toList();
+
+ // Peer validation
+ if (!configuration.peerCertificate.isNull()) {
+ const QString peerName = verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName;
+ if (!isMatchingHostname(configuration.peerCertificate, peerName)) {
+ // No matches in common names or alternate names.
+ const QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate);
+ const int index = sslErrors.indexOf(QSslError::HostNameMismatch);
+ if (index >= 0) // Replace the existing error
+ sslErrors[index] = error;
+ else
+ sslErrors.append(error);
+ emit q->peerVerifyError(error);
+ }
+
+ // Peer validation required, but no certificate is present
+ } else if (configuration.peerVerifyMode == QSslSocket::VerifyPeer
+ || configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer) {
+ QSslError error(QSslError::NoPeerCertificate);
+ sslErrors.append(error);
+ emit q->peerVerifyError(error);
+ }
+
+ // Peer chain validation
+ foreach (const QSslCertificate &certificate, peerCertificateChain) {
+ if (!QSslCertificatePrivate::isBlacklisted(certificate))
+ continue;
+
+ QSslError error(QSslError::CertificateBlacklisted, certificate);
+ sslErrors.append(error);
+ emit q->peerVerifyError(error);
+ }
+
+ if (!sslErrors.isEmpty()) {
+ emit q->sslErrors(sslErrors);
+ q->setErrorString(sslErrors.first().errorString());
+ q->setSocketError(QAbstractSocket::SslHandshakeFailedError);
+ emit q->error(QAbstractSocket::SslHandshakeFailedError);
+
+ // Disconnect if there are any non-ignorable errors
+ foreach (const QSslError &error, sslErrors) {
+ if (ignoreErrorsList.contains(error))
+ continue;
+ q->disconnectFromHost();
+ return S_OK;
+ }
+ }
+
+ if (readBufferMaxSize)
+ plainSocket->setReadBufferSize(readBufferMaxSize);
+
+ connectionEncrypted = true;
+ emit q->encrypted();
+
+ if (pendingClose) {
+ pendingClose = false;
+ q->disconnectFromHost();
+ }
+
+ return S_OK;
}
QList<QSslError> QSslSocketBackendPrivate::verify(QList<QSslCertificate> certificateChain, const QString &hostName)
diff --git a/src/network/ssl/qsslsocket_winrt_p.h b/src/network/ssl/qsslsocket_winrt_p.h
index 791330a6fd..aa31c85d6e 100644
--- a/src/network/ssl/qsslsocket_winrt_p.h
+++ b/src/network/ssl/qsslsocket_winrt_p.h
@@ -39,30 +39,15 @@
**
****************************************************************************/
-/****************************************************************************
-**
-** In addition, as a special exception, the copyright holders listed above give
-** permission to link the code of its release of Qt with the OpenSSL project's
-** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
-** same license as the original version), and distribute the linked executables.
-**
-** You must comply with the GNU General Public License version 2 in all
-** respects for all of the code used other than the "OpenSSL" code. If you
-** modify this file, you may extend this exception to your version of the file,
-** but you are not obligated to do so. If you do not wish to do so, delete
-** this exception statement from your version of this file.
-**
-****************************************************************************/
-
-#ifndef QSSLSOCKET_OPENSSL_P_H
-#define QSSLSOCKET_OPENSSL_P_H
+#ifndef QSSLSOCKET_WINRT_P_H
+#define QSSLSOCKET_WINRT_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 QLibrary class. This header file may change from
+// of the QtNetwork library. This header file may change from
// version to version without notice, or even be removed.
//
// We mean it.
@@ -70,8 +55,24 @@
#include "qsslsocket_p.h"
+#include <wrl.h>
+#include <windows.networking.sockets.h>
+
QT_BEGIN_NAMESPACE
+class QSslSocketConnectionHelper : public QObject
+{
+ Q_OBJECT
+public:
+ QSslSocketConnectionHelper(QSslSocketBackendPrivate *d)
+ : d(d) { }
+
+ Q_INVOKABLE void disconnectSocketFromHost();
+
+private:
+ QSslSocketBackendPrivate *d;
+};
+
class QSslSocketBackendPrivate : public QSslSocketPrivate
{
Q_DECLARE_PUBLIC(QSslSocket)
@@ -89,13 +90,22 @@ public:
QSsl::SslProtocol sessionProtocol() const Q_DECL_OVERRIDE;
void continueHandshake() Q_DECL_OVERRIDE;
+ static QList<QSslCipher> defaultCiphers();
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);
+
+private:
+ HRESULT onSslUpgrade(ABI::Windows::Foundation::IAsyncAction *,
+ ABI::Windows::Foundation::AsyncStatus);
+
+ QScopedPointer<QSslSocketConnectionHelper> connectionHelper;
+ ABI::Windows::Networking::Sockets::SocketProtectionLevel protectionLevel;
+ QSet<QSslCertificate> previousCaCertificates;
};
QT_END_NAMESPACE
-#endif
+#endif // QSSLSOCKET_WINRT_P_H