| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** 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 The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qssl_p.h" |
| #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 <private/qeventdispatcher_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_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); |
| for (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); |
| } |
| } |
| for (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)) |
| { |
| } |
| |
| QSslSocketBackendPrivate::~QSslSocketBackendPrivate() |
| { |
| g->syncCaCertificates(QSet<QSslCertificate>(), previousCaCertificates); |
| } |
| |
| bool QSslSocketPrivate::supportsSsl() |
| { |
| return true; |
| } |
| |
| void QSslSocketPrivate::ensureInitialized() |
| { |
| if (s_loadedCiphersAndCerts) |
| return; |
| s_loadedCiphersAndCerts = true; |
| resetDefaultCiphers(); |
| } |
| |
| long QSslSocketPrivate::sslLibraryVersionNumber() |
| { |
| // ### Qt 6: Find a proper replacement for the deprecated method below. |
| return QSysInfo::windowsVersion(); |
| } |
| |
| QString QSslSocketPrivate::sslLibraryVersionString() |
| { |
| return QStringLiteral("Windows Runtime, ") + QSysInfo::prettyProductName(); |
| } |
| |
| long QSslSocketPrivate::sslLibraryBuildVersionNumber() |
| { |
| Q_UNIMPLEMENTED(); |
| return 0; |
| } |
| |
| QString QSslSocketPrivate::sslLibraryBuildVersionString() |
| { |
| Q_UNIMPLEMENTED(); |
| return QString::number(sslLibraryBuildVersionNumber()); |
| } |
| |
| void QSslSocketPrivate::resetDefaultCiphers() |
| { |
| setDefaultSupportedCiphers(QSslSocketBackendPrivate::defaultCiphers()); |
| setDefaultCiphers(QSslSocketBackendPrivate::defaultCiphers()); |
| } |
| |
| |
| QList<QSslCipher> QSslSocketBackendPrivate::defaultCiphers() |
| { |
| QList<QSslCipher> ciphers; |
| const QString protocolStrings[] = { QStringLiteral("TLSv1"), |
| QStringLiteral("TLSv1.1"), QStringLiteral("TLSv1.2") }; |
| const QSsl::SslProtocol protocols[] = { QSsl::TlsV1_0, QSsl::TlsV1_1, QSsl::TlsV1_2 }; |
| const int size = static_cast<int>(ARRAYSIZE(protocols)); |
| ciphers.reserve(size); |
| for (int i = 0; i < size; ++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() |
| { |
| return g->systemCaCertificates; |
| } |
| |
| void QSslSocketBackendPrivate::startClientEncryption() |
| { |
| Q_Q(QSslSocket); |
| |
| QSsl::SslProtocol protocol = q->protocol(); |
| switch (q->protocol()) { |
| case QSsl::SslV2: |
| case QSsl::SslV3: |
| setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, |
| QStringLiteral("unsupported protocol")); |
| return; |
| case QSsl::AnyProtocol: |
| case QSsl::TlsV1SslV3: |
| protectionLevel = SocketProtectionLevel_Tls10; |
| break; |
| case QSsl::TlsV1_0: |
| protectionLevel = SocketProtectionLevel_Tls10; |
| break; |
| case QSsl::TlsV1_1: |
| protectionLevel = SocketProtectionLevel_Tls11; |
| break; |
| case QSsl::TlsV1_2: |
| protectionLevel = SocketProtectionLevel_Tls12; |
| break; |
| case QSsl::TlsV1_0OrLater: |
| case QSsl::TlsV1_1OrLater: |
| case QSsl::TlsV1_2OrLater: |
| case QSsl::TlsV1_3: |
| case QSsl::TlsV1_3OrLater: |
| // TlsV1_0OrLater, TlsV1_1OrLater and TlsV1_2OrLater are disabled on WinRT |
| // because there is no good way to map them to the native API. |
| setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, |
| QStringLiteral("unsupported protocol")); |
| return; |
| case QSsl::SecureProtocols: |
| // SocketProtectionLevel_Tls12 actually means "use TLS1.0, 1.1 or 1.2" |
| // https://docs.microsoft.com/en-us/uwp/api/windows.networking.sockets.socketprotectionlevel |
| 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() |
| { |
| Q_UNIMPLEMENTED(); |
| } |
| |
| void QSslSocketBackendPrivate::transmit() |
| { |
| 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; |
| } |
| emit q->channelBytesWritten(0, totalBytesWritten); |
| } |
| } |
| |
| // 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(); |
| emit q->channelReadyRead(0); |
| } |
| |
| if (pendingClose) { |
| pendingClose = false; |
| q->disconnectFromHost(); |
| } |
| } |
| |
| void QSslSocketBackendPrivate::disconnectFromHost() |
| { |
| QMetaObject::invokeMethod(connectionHelper.data(), "disconnectSocketFromHost", Qt::QueuedConnection); |
| } |
| |
| void QSslSocketBackendPrivate::disconnected() |
| { |
| } |
| |
| QSslCipher QSslSocketBackendPrivate::sessionCipher() const |
| { |
| return configuration.sessionCipher; |
| } |
| |
| QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const |
| { |
| return configuration.sessionCipher.protocol(); |
| } |
| |
| void QSslSocketBackendPrivate::continueHandshake() |
| { |
| IStreamSocket *socket = reinterpret_cast<IStreamSocket *>(plainSocket->socketDescriptor()); |
| if (qintptr(socket) == -1) { |
| setErrorAndEmit(QAbstractSocket::SslInternalError, |
| QStringLiteral("At attempt was made to continue the handshake on an invalid socket.")); |
| 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)) { |
| setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, qt_error_string(hr)); |
| 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)) { |
| setErrorAndEmit(QAbstractSocket::SslInternalError, |
| QSslSocket::tr("Error creating SSL session: %1").arg(qt_error_string(hr))); |
| return; |
| } |
| |
| hr = QEventDispatcherWinRT::runOnXamlThread([this, op]() { |
| HRESULT hr = op->put_Completed(Callback<IAsyncActionCompletedHandler>( |
| this, &QSslSocketBackendPrivate::onSslUpgrade).Get()); |
| return hr; |
| }); |
| Q_ASSERT_SUCCEEDED(hr); |
| } |
| |
| HRESULT QSslSocketBackendPrivate::onSslUpgrade(IAsyncAction *action, AsyncStatus) |
| { |
| Q_Q(QSslSocket); |
| |
| if (wasDeleted) { |
| qCWarning(lcSsl, |
| "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 |
| setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, qt_error_string(hr)); |
| 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) { |
| qCWarning(lcSsl, |
| "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(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 |
| for (const QSslCertificate &certificate : qAsConst(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); |
| setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, sslErrors.constFirst().errorString()); |
| |
| // Disconnect if there are any non-ignorable errors |
| for (const QSslError &error : qAsConst(sslErrors)) { |
| if (ignoreErrorsList.contains(error)) |
| continue; |
| q->disconnectFromHost(); |
| return S_OK; |
| } |
| } |
| |
| if (readBufferMaxSize) |
| plainSocket->setReadBufferSize(readBufferMaxSize); |
| |
| connectionEncrypted = true; |
| emit q->encrypted(); |
| |
| // The write buffer may already have data written to it, so we need to call transmit. |
| // This has to be done in 'q's thread, and not in the current thread (the XAML thread). |
| QMetaObject::invokeMethod(q, [this](){ transmit(); }); |
| |
| if (pendingClose) { |
| pendingClose = false; |
| q->disconnectFromHost(); |
| } |
| |
| return S_OK; |
| } |
| |
| QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain, const QString &hostName) |
| { |
| Q_UNIMPLEMENTED(); |
| Q_UNUSED(certificateChain) |
| Q_UNUSED(hostName) |
| QList<QSslError> errors; |
| |
| 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; |
| } |
| |
| QT_END_NAMESPACE |