summaryrefslogtreecommitdiffstats
path: root/src/plugins/tls/schannel
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/tls/schannel')
-rw-r--r--src/plugins/tls/schannel/CMakeLists.txt16
-rw-r--r--src/plugins/tls/schannel/qtls_schannel.cpp926
-rw-r--r--src/plugins/tls/schannel/qtls_schannel_p.h41
-rw-r--r--src/plugins/tls/schannel/qtlsbackend_schannel_p.h43
-rw-r--r--src/plugins/tls/schannel/qtlskey_schannel.cpp52
-rw-r--r--src/plugins/tls/schannel/qtlskey_schannel_p.h40
-rw-r--r--src/plugins/tls/schannel/qx509_schannel.cpp210
-rw-r--r--src/plugins/tls/schannel/qx509_schannel_p.h44
8 files changed, 830 insertions, 542 deletions
diff --git a/src/plugins/tls/schannel/CMakeLists.txt b/src/plugins/tls/schannel/CMakeLists.txt
index b038f6056e..a7f7fcd99f 100644
--- a/src/plugins/tls/schannel/CMakeLists.txt
+++ b/src/plugins/tls/schannel/CMakeLists.txt
@@ -1,8 +1,11 @@
-qt_internal_add_plugin(QSchannelBackend
- OUTPUT_NAME schannelbackend
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_plugin(QSchannelBackendPlugin
+ OUTPUT_NAME qschannelbackend
CLASS_NAME QSchannelBackend
- TYPE tls
- DEFAULT_IF WINDOWS
+ PLUGIN_TYPE tls
+ DEFAULT_IF WIN32
SOURCES
../shared/qtlskey_base_p.h
../shared/qtlskey_base.cpp
@@ -21,10 +24,11 @@ qt_internal_add_plugin(QSchannelBackend
qtlskey_schannel.cpp qtlskey_schannel_p.h
qx509_schannel.cpp qx509_schannel_p.h
LIBRARIES
+ Qt::NetworkPrivate
crypt32
secur32
bcrypt
ncrypt
- PUBLIC_LIBRARIES
- Qt::NetworkPrivate
+ DEFINES
+ QT_NO_CAST_FROM_ASCII
)
diff --git a/src/plugins/tls/schannel/qtls_schannel.cpp b/src/plugins/tls/schannel/qtls_schannel.cpp
index 83e9cb1c6c..a244a90ebc 100644
--- a/src/plugins/tls/schannel/qtls_schannel.cpp
+++ b/src/plugins/tls/schannel/qtls_schannel.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// #define QSSLSOCKET_DEBUG
@@ -62,17 +26,11 @@
#include <security.h>
#include <schnlsp.h>
-#if NTDDI_VERSION >= NTDDI_WINBLUE && !defined(Q_CC_MINGW)
+#if NTDDI_VERSION >= NTDDI_WINBLUE && defined(SECBUFFER_APPLICATION_PROTOCOLS)
// ALPN = Application Layer Protocol Negotiation
#define SUPPORTS_ALPN 1
#endif
-// Redstone 5/1809 has all the API available, but TLS 1.3 is not enabled until a later version of
-// Win 10, checked at runtime in supportsTls13()
-#if defined(NTDDI_WIN10_RS5) && NTDDI_VERSION >= NTDDI_WIN10_RS5
-#define SUPPORTS_TLS13 1
-#endif
-
// Not defined in MinGW
#ifndef SECBUFFER_ALERT
#define SECBUFFER_ALERT 17
@@ -129,6 +87,12 @@
#ifndef SP_PROT_TLS1_3
#define SP_PROT_TLS1_3 (SP_PROT_TLS1_3_CLIENT | SP_PROT_TLS1_3_SERVER)
#endif
+#ifndef BCRYPT_ECDH_ALGORITHM
+#define BCRYPT_ECDH_ALGORITHM L"ECDH"
+#endif
+#ifndef BCRYPT_ECDSA_ALGORITHM
+#define BCRYPT_ECDSA_ALGORITHM L"ECDSA"
+#endif
/*
@future!:
@@ -150,10 +114,6 @@
- Check if SEC_I_INCOMPLETE_CREDENTIALS is still returned for both "missing certificate" and
"missing PSK" when calling InitializeSecurityContext in "performHandshake".
- Medium priority:
- - Setting cipher-suites (or ALG_ID)
- - People have survived without it in WinRT
-
Low priority:
- Possibly make RAII wrappers for SecBuffer (which I commonly create QScopeGuards for)
@@ -161,43 +121,349 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.schannel");
+using namespace Qt::StringLiterals;
+
+Q_LOGGING_CATEGORY(lcTlsBackendSchannel, "qt.tlsbackend.schannel");
// Defined in qsslsocket_qt.cpp.
QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key,
const QString &passPhrase);
+namespace {
+bool supportsTls13();
+}
+
namespace QTlsPrivate {
+QList<QSslCipher> defaultCiphers();
+
+struct SchannelCipherInfo {
+ const char *openSslCipherSuite;
+ const char *schannelCipherSuite;
+ const char *keyExchangeMethod;
+ const char *authenticationMethod;
+ const char *encryptionMethod;
+ int encryptionBits;
+ const char *hashMethod;
+ QList<QSsl::SslProtocol> protocols;
+};
+
+// The list of supported ciphers according to
+// https://learn.microsoft.com/en-us/windows/win32/secauthn/tls-cipher-suites-in-windows-server-2022
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+std::array<SchannelCipherInfo, 44> schannelCipherInfo = {{
+ {"TLS_AES_256_GCM_SHA384", "TLS_AES_256_GCM_SHA384", "", "", "AES", 256, "SHA384", {QSsl::TlsV1_3}},
+ {"TLS_AES_128_GCM_SHA256", "TLS_AES_128_GCM_SHA256", "", "", "AES", 128, "SHA256", {QSsl::TlsV1_3}},
+ {"ECDHE-ECDSA-AES256-GCM-SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "ECDH", "ECDSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
+ {"ECDHE-ECDSA-AES128-GCM-SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "ECDH", "ECDSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
+ {"ECDHE-RSA-AES256-GCM-SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "ECDH", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
+ {"ECDHE-RSA-AES128-GCM-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "ECDH", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
+ {"DHE-RSA-AES256-GCM-SHA384", "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "DH", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
+ {"DHE-RSA-AES128-GCM-SHA256", "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "DH", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
+ {"ECDHE-ECDSA-AES256-SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "ECDH", "ECDSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
+ {"ECDHE-ECDSA-AES128-SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "ECDH", "ECDSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
+ {"ECDHE-RSA-AES256-SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "ECDH", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
+ {"ECDHE-RSA-AES128-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "ECDH", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
+ {"ECDHE-ECDSA-AES256-SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "ECDH", "ECDSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"ECDHE-ECDSA-AES128-SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "ECDH", "ECDSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"ECDHE-RSA-AES256-SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "ECDH", "RSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"ECDHE-RSA-AES128-SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "ECDH", "RSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"AES256-GCM-SHA384", "TLS_RSA_WITH_AES_256_GCM_SHA384", "RSA", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
+ {"AES128-GCM-SHA256", "TLS_RSA_WITH_AES_128_GCM_SHA256", "RSA", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
+ {"AES256-SHA256", "TLS_RSA_WITH_AES_256_CBC_SHA256", "RSA", "RSA", "AES", 256, "SHA256", {QSsl::TlsV1_2}},
+ {"AES128-SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA256", "RSA", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
+ {"AES256-SHA", "TLS_RSA_WITH_AES_256_CBC_SHA", "RSA", "RSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"AES128-SHA", "TLS_RSA_WITH_AES_128_CBC_SHA", "RSA", "RSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"DES-CBC3-SHA", "TLS_RSA_WITH_3DES_EDE_CBC_SHA", "RSA", "RSA", "3DES", 168, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"NULL-SHA256", "TLS_RSA_WITH_NULL_SHA256", "RSA", "RSA", "", 0, "SHA256", {QSsl::TlsV1_2}},
+ {"NULL-SHA", "TLS_RSA_WITH_NULL_SHA", "RSA", "RSA", "", 0, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+
+ // the following cipher suites are not enabled by default in schannel provider
+ {"TLS_CHACHA20_POLY1305_SHA256", "TLS_CHACHA20_POLY1305_SHA256", "", "", "CHACHA20_POLY1305", 0, "", {QSsl::TlsV1_3}},
+ {"DHE-RSA-AES256-SHA", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "DH", "RSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"DHE-RSA-AES128-SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "DH", "RSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"DHE-DSS-AES256-SHA256", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", "DH", "DSA", "AES", 256, "SHA256", {QSsl::TlsV1_2}},
+ {"DHE-DSS-AES128-SHA256", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "DH", "DSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
+ {"DHE-DSS-AES256-SHA", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", "DH", "DSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"DHE-DSS-AES128-SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "DH", "DSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"EDH-DSS-DES-CBC3-SHA", "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", "DH", "DSA", "3DES", 168, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"RC4-SHA", "TLS_RSA_WITH_RC4_128_SHA", "RSA", "RSA", "RC4", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"RC4-MD5", "TLS_RSA_WITH_RC4_128_MD5", "RSA", "RSA", "RC4", 128, "MD5", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"DES-CBC-SHA", "TLS_RSA_WITH_DES_CBC_SHA", "RSA", "RSA", "DES", 56, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"EDH-DSS-DES-CBC-SHA", "TLS_DHE_DSS_WITH_DES_CBC_SHA", "DH", "DSA", "DES", 56, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+ {"NULL-MD5", "TLS_RSA_WITH_NULL_MD5", "RSA", "RSA", "", 0, "MD5", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
+
+ // PSK cipher suites
+ {"PSK-AES256-GCM-SHA384", "TLS_PSK_WITH_AES_256_GCM_SHA384", "PSK", "", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
+ {"PSK-AES128-GCM-SHA256", "TLS_PSK_WITH_AES_128_GCM_SHA256", "PSK", "", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
+ {"PSK-AES256-CBC-SHA384", "TLS_PSK_WITH_AES_256_CBC_SHA384", "PSK", "", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
+ {"PSK-AES128-CBC-SHA256", "TLS_PSK_WITH_AES_128_CBC_SHA256", "PSK", "", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
+ {"PSK-NULL-SHA384", "TLS_PSK_WITH_NULL_SHA384", "PSK", "", "", 0, "SHA384", {QSsl::TlsV1_2}},
+ {"PSK-NULL-SHA256", "TLS_PSK_WITH_NULL_SHA256", "PSK", "", "", 0, "SHA256", {QSsl::TlsV1_2}},
+}};
+QT_WARNING_POP
+
+const SchannelCipherInfo *cipherInfoByOpenSslName(const QString &name)
+{
+ for (const auto &cipherInfo : schannelCipherInfo) {
+ if (name == QLatin1StringView(cipherInfo.openSslCipherSuite))
+ return &cipherInfo;
+ }
+
+ return nullptr;
+}
+
+UNICODE_STRING cbcChainingMode = {
+ sizeof(BCRYPT_CHAIN_MODE_CBC) - 2,
+ sizeof(BCRYPT_CHAIN_MODE_CBC),
+ const_cast<PWSTR>(BCRYPT_CHAIN_MODE_CBC)
+};
+
+UNICODE_STRING gcmChainingMode = {
+ sizeof(BCRYPT_CHAIN_MODE_GCM) - 2,
+ sizeof(BCRYPT_CHAIN_MODE_GCM),
+ const_cast<PWSTR>(BCRYPT_CHAIN_MODE_GCM)
+};
+
+/**
+ Determines which algorithms are not used by the requested ciphers to build
+ up a black list that can be passed to SCH_CREDENTIALS.
+ */
+QList<CRYPTO_SETTINGS> cryptoSettingsForCiphers(const QList<QSslCipher> &ciphers)
+{
+ static const QList<QSslCipher> defaultCipherList = defaultCiphers();
+
+ if (defaultCipherList == ciphers) {
+ // the ciphers have not been restricted for this session, so no black listing needed
+ return {};
+ }
+
+ QList<const SchannelCipherInfo*> cipherInfo;
+
+ for (const auto &cipher : ciphers) {
+ if (cipher.isNull())
+ continue;
+
+ const auto *info = cipherInfoByOpenSslName(cipher.name());
+ if (!cipherInfo.contains(info))
+ cipherInfo.append(info);
+ }
+
+ QList<CRYPTO_SETTINGS> cryptoSettings;
+
+ const auto assignUnicodeString = [](UNICODE_STRING &unicodeString, const wchar_t *characters) {
+ unicodeString.Length = static_cast<USHORT>(wcslen(characters) * sizeof(WCHAR));
+ unicodeString.MaximumLength = unicodeString.Length + sizeof(UNICODE_NULL);
+ unicodeString.Buffer = const_cast<wchar_t*>(characters);
+ };
+
+ // black list of key exchange algorithms
+ const auto allKeyExchangeAlgorithms = {BCRYPT_RSA_ALGORITHM,
+ BCRYPT_ECDH_ALGORITHM,
+ BCRYPT_DH_ALGORITHM};
+
+ for (const auto &algorithm : allKeyExchangeAlgorithms) {
+ const auto method = QStringView(algorithm);
+
+ const auto usesMethod = [method](const SchannelCipherInfo *info) {
+ return QLatin1StringView(info->keyExchangeMethod) == method;
+ };
+
+ const bool exclude = std::none_of(cipherInfo.cbegin(), cipherInfo.cend(), usesMethod);
+
+ if (exclude) {
+ CRYPTO_SETTINGS settings = {};
+ settings.eAlgorithmUsage = TlsParametersCngAlgUsageKeyExchange;
+ assignUnicodeString(settings.strCngAlgId, algorithm);
+ cryptoSettings.append(settings);
+ }
+ }
+
+ // black list of authentication algorithms
+ const auto allAuthenticationAlgorithms = {BCRYPT_RSA_ALGORITHM,
+ BCRYPT_DSA_ALGORITHM,
+ BCRYPT_ECDSA_ALGORITHM,
+ BCRYPT_DH_ALGORITHM};
+
+ for (const auto &algorithm : allAuthenticationAlgorithms) {
+ const auto method = QStringView(algorithm);
+
+ const auto usesMethod = [method](const SchannelCipherInfo *info) {
+ return QLatin1StringView(info->authenticationMethod) == method;
+ };
+
+ const bool exclude = std::none_of(cipherInfo.begin(), cipherInfo.end(), usesMethod);
+
+ if (exclude) {
+ CRYPTO_SETTINGS settings = {};
+ settings.eAlgorithmUsage = TlsParametersCngAlgUsageSignature;
+ assignUnicodeString(settings.strCngAlgId, algorithm);
+ cryptoSettings.append(settings);
+ }
+ }
+
+
+ // black list of encryption algorithms
+ const auto allEncryptionAlgorithms = {BCRYPT_AES_ALGORITHM,
+ BCRYPT_RC4_ALGORITHM,
+ BCRYPT_DES_ALGORITHM,
+ BCRYPT_3DES_ALGORITHM};
+
+ for (const auto &algorithm : allEncryptionAlgorithms) {
+ const auto method = QStringView(algorithm);
+
+ if (method == QLatin1StringView("AES")) {
+ bool uses128Bit = false;
+ bool uses256Bit = false;
+ bool usesGcm = false;
+ bool usesCbc = false;
+ for (const auto *info : cipherInfo) {
+ if (QLatin1StringView(info->encryptionMethod) == method) {
+ uses128Bit = uses128Bit || (info->encryptionBits == 128);
+ uses256Bit = uses256Bit || (info->encryptionBits == 256);
+ usesGcm = usesGcm ||
+ QLatin1StringView(info->schannelCipherSuite).contains("_GCM_"_L1);
+ usesCbc = usesCbc ||
+ QLatin1StringView(info->schannelCipherSuite).contains("_CBC_"_L1);
+ }
+ }
+
+ CRYPTO_SETTINGS settings = {};
+ settings.eAlgorithmUsage = TlsParametersCngAlgUsageCipher;
+ assignUnicodeString(settings.strCngAlgId, algorithm);
+
+ if (usesGcm && !usesCbc) {
+ settings.cChainingModes = 1;
+ settings.rgstrChainingModes = &cbcChainingMode;
+ } else if (!usesGcm && usesCbc) {
+ settings.cChainingModes = 1;
+ settings.rgstrChainingModes = &gcmChainingMode;
+ }
+
+ if (!uses128Bit && uses256Bit) {
+ settings.dwMinBitLength = 256;
+ cryptoSettings.append(settings);
+ } else if (uses128Bit && !uses256Bit) {
+ settings.dwMaxBitLength = 128;
+ cryptoSettings.append(settings);
+ } else if (!uses128Bit && !uses256Bit) {
+ cryptoSettings.append(settings);
+ }
+ } else {
+ const auto usesMethod = [method](const SchannelCipherInfo *info) {
+ return QLatin1StringView(info->encryptionMethod) == method;
+ };
+
+ const bool exclude = std::none_of(cipherInfo.begin(), cipherInfo.end(), usesMethod);
+
+ if (exclude) {
+ CRYPTO_SETTINGS settings = {};
+ settings.eAlgorithmUsage = TlsParametersCngAlgUsageCipher;
+ assignUnicodeString(settings.strCngAlgId, algorithm);
+ cryptoSettings.append(settings);
+ }
+ }
+ }
+
+ // black list of hash algorithms
+ const auto allHashAlgorithms = {BCRYPT_MD5_ALGORITHM,
+ BCRYPT_SHA1_ALGORITHM,
+ BCRYPT_SHA256_ALGORITHM,
+ BCRYPT_SHA384_ALGORITHM};
+
+ for (const auto &algorithm : allHashAlgorithms) {
+ const auto method = QStringView(algorithm);
+
+ const auto usesMethod = [method](const SchannelCipherInfo *info) {
+ return QLatin1StringView(info->hashMethod) == method;
+ };
+
+ const bool exclude = std::none_of(cipherInfo.begin(), cipherInfo.end(), usesMethod);
+
+ if (exclude) {
+ CRYPTO_SETTINGS settings = {};
+ settings.eAlgorithmUsage = TlsParametersCngAlgUsageDigest;
+ assignUnicodeString(settings.strCngAlgId, algorithm);
+ cryptoSettings.append(settings);
+ }
+ }
+
+ return cryptoSettings;
+}
+
+QList<QSslCipher> ciphersByName(QStringView schannelSuiteName)
+{
+ QList<QSslCipher> ciphers;
+
+ for (const auto &cipher : schannelCipherInfo) {
+ if (QLatin1StringView(cipher.schannelCipherSuite) == schannelSuiteName) {
+ for (const auto &protocol : cipher.protocols) {
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ const QString protocolName = (
+ protocol == QSsl::TlsV1_0 ? QStringLiteral("TLSv1.0") :
+ protocol == QSsl::TlsV1_1 ? QStringLiteral("TLSv1.1") :
+ protocol == QSsl::TlsV1_2 ? QStringLiteral("TLSv1.2") :
+ protocol == QSsl::TlsV1_3 ? QStringLiteral("TLSv1.3") :
+ QString());
+QT_WARNING_POP
+
+ ciphers.append(QTlsBackend::createCiphersuite(QLatin1StringView(cipher.openSslCipherSuite),
+ QLatin1StringView(cipher.keyExchangeMethod),
+ QLatin1StringView(cipher.encryptionMethod),
+ QLatin1StringView(cipher.authenticationMethod),
+ cipher.encryptionBits,
+ protocol, protocolName));
+ }
+ }
+ }
+
+ return ciphers;
+}
+
QList<QSslCipher> defaultCiphers()
{
- // Previously the code was in QSslSocketBackendPrivate.
+ ULONG contextFunctionsCount = {};
+ PCRYPT_CONTEXT_FUNCTIONS contextFunctions = {};
+
+ const auto status = BCryptEnumContextFunctions(CRYPT_LOCAL, L"SSL", NCRYPT_SCHANNEL_INTERFACE,
+ &contextFunctionsCount, &contextFunctions);
+ if (!NT_SUCCESS(status)) {
+ qCWarning(lcTlsBackendSchannel, "Failed to enumerate ciphers");
+ return {};
+ }
+
+ const bool supportsV13 = supportsTls13();
+
QList<QSslCipher> ciphers;
- // @temp (I hope), stolen from qsslsocket_winrt.cpp
- const QString protocolStrings[] = { QStringLiteral("TLSv1"), QStringLiteral("TLSv1.1"),
- QStringLiteral("TLSv1.2"), QStringLiteral("TLSv1.3") };
- const QSsl::SslProtocol protocols[] = { QSsl::TlsV1_0, QSsl::TlsV1_1,
- QSsl::TlsV1_2, QSsl::TlsV1_3 };
- const int size = ARRAYSIZE(protocols);
- static_assert(size == ARRAYSIZE(protocolStrings));
- ciphers.reserve(size);
- for (int i = 0; i < size; ++i) {
- const QSslCipher cipher = QTlsBackend::createCipher(QStringLiteral("Schannel"),
- protocols[i], protocolStrings[i]);
- ciphers.append(cipher);
+ for (ULONG index = 0; index < contextFunctions->cFunctions; ++index) {
+ const auto suiteName = QStringView(contextFunctions->rgpszFunctions[index]);
+
+ const QList<QSslCipher> allCiphers = ciphersByName(suiteName);
+
+ for (const auto &cipher : allCiphers) {
+ if (!supportsV13 && (cipher.protocol() == QSsl::TlsV1_3))
+ continue;
+
+ ciphers.append(cipher);
+ }
}
+ BCryptFreeBuffer(contextFunctions);
+
return ciphers;
+}
+bool containsTls13Cipher(const QList<QSslCipher> &ciphers)
+{
+ return std::any_of(ciphers.cbegin(), ciphers.cend(),
+ [](const QSslCipher &cipher) { return cipher.protocol() == QSsl::TlsV1_3; });
}
} // namespace QTlsPrivate
-namespace {
-bool supportsTls13();
-}
-
bool QSchannelBackend::s_loadedCiphersAndCerts = false;
Q_GLOBAL_STATIC(QRecursiveMutex, qt_schannel_mutex)
@@ -210,7 +476,7 @@ long QSchannelBackend::tlsLibraryVersionNumber() const
QString QSchannelBackend::tlsLibraryVersionString() const
{
const auto os = QOperatingSystemVersion::current();
- return QString::fromLatin1("Secure Channel, %1 %2.%3.%4")
+ return "Secure Channel, %1 %2.%3.%4"_L1
.arg(os.name(),
QString::number(os.majorVersion()),
QString::number(os.minorVersion()),
@@ -219,16 +485,13 @@ QString QSchannelBackend::tlsLibraryVersionString() const
long QSchannelBackend::tlsLibraryBuildVersionNumber() const
{
- return tlsLibraryVersionNumber();
+ return NTDDI_VERSION;
}
QString QSchannelBackend::tlsLibraryBuildVersionString() const
{
- const auto os = QOperatingSystemVersion::current();
- return QString::fromLatin1("%1.%2.%3")
- .arg(QString::number(os.majorVersion()),
- QString::number(os.minorVersion()),
- QString::number(os.microVersion()));
+ return "Secure Channel (NTDDI: 0x%1)"_L1
+ .arg(QString::number(NTDDI_VERSION, 16).toUpper());
}
void QSchannelBackend::ensureInitialized() const
@@ -267,10 +530,13 @@ QList<QSsl::SslProtocol> QSchannelBackend::supportedProtocols() const
protocols << QSsl::AnyProtocol;
protocols << QSsl::SecureProtocols;
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
protocols << QSsl::TlsV1_0;
protocols << QSsl::TlsV1_0OrLater;
protocols << QSsl::TlsV1_1;
protocols << QSsl::TlsV1_1OrLater;
+QT_WARNING_POP
protocols << QSsl::TlsV1_2;
protocols << QSsl::TlsV1_2OrLater;
@@ -286,8 +552,10 @@ QList<QSsl::SupportedFeature> QSchannelBackend::supportedFeatures() const
{
QList<QSsl::SupportedFeature> features;
+#ifdef SUPPORTS_ALPN
features << QSsl::SupportedFeature::ClientSideAlpn;
features << QSsl::SupportedFeature::ServerSideAlpn;
+#endif
return features;
}
@@ -328,7 +596,11 @@ QList<QSslCertificate> QSchannelBackend::systemCaCertificatesImplementation()
// Similar to non-Darwin version found in qtlsbackend_openssl.cpp,
// QTlsPrivate::systemCaCertificates function.
QList<QSslCertificate> systemCerts;
- auto hSystemStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT"));
+
+ auto hSystemStore = QHCertStorePointer(
+ CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0,
+ CERT_STORE_READONLY_FLAG | CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT"));
+
if (hSystemStore) {
PCCERT_CONTEXT pc = nullptr;
while ((pc = CertFindCertificateInStore(hSystemStore.get(), X509_ASN_ENCODING, 0,
@@ -349,6 +621,11 @@ QTlsPrivate::X509DerReaderPtr QSchannelBackend::X509DerReader() const
return QTlsPrivate::X509CertificateGeneric::certificatesFromDer;
}
+QTlsPrivate::X509Pkcs12ReaderPtr QSchannelBackend::X509Pkcs12Reader() const
+{
+ return QTlsPrivate::X509CertificateSchannel::importPkcs12;
+}
+
namespace {
SecBuffer createSecBuffer(void *ptr, unsigned long length, unsigned long bufferType)
@@ -413,7 +690,6 @@ QString schannelErrorToString(qint32 status)
bool supportsTls13()
{
-#ifdef SUPPORTS_TLS13
static bool supported = []() {
const auto current = QOperatingSystemVersion::current();
// 20221 just happens to be the preview version I run on my laptop where I tested TLS 1.3.
@@ -421,10 +697,8 @@ bool supportsTls13()
QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 20221);
return current >= minimum;
}();
+
return supported;
-#else
- return false;
-#endif
}
DWORD toSchannelProtocol(QSsl::SslProtocol protocol)
@@ -433,9 +707,12 @@ DWORD toSchannelProtocol(QSsl::SslProtocol protocol)
switch (protocol) {
case QSsl::UnknownProtocol:
return DWORD(-1);
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
case QSsl::DtlsV1_0:
- case QSsl::DtlsV1_2:
case QSsl::DtlsV1_0OrLater:
+QT_WARNING_POP
+ case QSsl::DtlsV1_2:
case QSsl::DtlsV1_2OrLater:
return DWORD(-1); // Not supported at the moment (@future)
case QSsl::AnyProtocol:
@@ -443,12 +720,15 @@ DWORD toSchannelProtocol(QSsl::SslProtocol protocol)
if (supportsTls13())
protocols |= SP_PROT_TLS1_3;
break;
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
case QSsl::TlsV1_0:
protocols = SP_PROT_TLS1_0;
break;
case QSsl::TlsV1_1:
protocols = SP_PROT_TLS1_1;
break;
+QT_WARNING_POP
case QSsl::TlsV1_2:
protocols = SP_PROT_TLS1_2;
break;
@@ -458,7 +738,8 @@ DWORD toSchannelProtocol(QSsl::SslProtocol protocol)
else
protocols = DWORD(-1);
break;
- case QSsl::SecureProtocols: // TLS v1.0 and later is currently considered secure
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
case QSsl::TlsV1_0OrLater:
// For the "OrLater" protocols we fall through from one to the next, adding all of them
// in ascending order
@@ -467,6 +748,8 @@ DWORD toSchannelProtocol(QSsl::SslProtocol protocol)
case QSsl::TlsV1_1OrLater:
protocols |= SP_PROT_TLS1_1;
Q_FALLTHROUGH();
+QT_WARNING_POP
+ case QSsl::SecureProtocols: // TLS v1.2 and later is currently considered secure
case QSsl::TlsV1_2OrLater:
protocols |= SP_PROT_TLS1_2;
Q_FALLTHROUGH();
@@ -480,17 +763,15 @@ DWORD toSchannelProtocol(QSsl::SslProtocol protocol)
return protocols;
}
-#ifdef SUPPORTS_TLS13
// In the new API that descended down upon us we are not asked which protocols we want
// but rather which protocols we don't want. So now we have this function to disable
// anything that is not enabled.
-DWORD toSchannelProtocolNegated(QSsl::SslProtocol protocol)
+DWORD negatedSchannelProtocols(DWORD wantedProtocols)
{
DWORD protocols = SP_PROT_ALL; // all protocols
- protocols &= ~toSchannelProtocol(protocol); // minus the one(s) we want
+ protocols &= ~wantedProtocols; // minus the one(s) we want
return protocols;
}
-#endif
/*!
\internal
@@ -507,8 +788,11 @@ QSsl::SslProtocol toQtSslProtocol(DWORD protocol)
return q_protocol; \
}
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
MAP_PROTOCOL(SP_PROT_TLS1_0, QSsl::TlsV1_0)
MAP_PROTOCOL(SP_PROT_TLS1_1, QSsl::TlsV1_1)
+QT_WARNING_POP
MAP_PROTOCOL(SP_PROT_TLS1_2, QSsl::TlsV1_2)
MAP_PROTOCOL(SP_PROT_TLS1_3, QSsl::TlsV1_3)
#undef MAP_PROTOCOL
@@ -525,8 +809,7 @@ bool netscapeWrongCertType(const QList<QSslCertificateExtension> &extensions, bo
const auto netscapeIt = std::find_if(
extensions.cbegin(), extensions.cend(),
[](const QSslCertificateExtension &extension) {
- const auto netscapeCertType = QStringLiteral("2.16.840.1.113730.1.1");
- return extension.oid() == netscapeCertType;
+ return extension.oid() == u"2.16.840.1.113730.1.1";
});
if (netscapeIt != extensions.cend()) {
const QByteArray netscapeCertTypeByte = netscapeIt->value().toByteArray();
@@ -554,11 +837,11 @@ bool isCertificateAuthority(const QList<QSslCertificateExtension> &extensions)
{
auto it = std::find_if(extensions.cbegin(), extensions.cend(),
[](const QSslCertificateExtension &extension) {
- return extension.name() == QLatin1String("basicConstraints");
+ return extension.name() == "basicConstraints"_L1;
});
if (it != extensions.cend()) {
QVariantMap basicConstraints = it->value().toMap();
- return basicConstraints.value(QLatin1String("ca"), false).toBool();
+ return basicConstraints.value("ca"_L1, false).toBool();
}
return false;
}
@@ -573,7 +856,7 @@ bool matchesContextRequirements(DWORD attributes, DWORD requirements,
bool isClient)
{
#ifdef QSSLSOCKET_DEBUG
-#define DEBUG_WARN(message) qCWarning(lcTlsBackend, message)
+#define DEBUG_WARN(message) qCWarning(lcTlsBackendSchannel, message)
#else
#define DEBUG_WARN(message)
#endif
@@ -617,21 +900,16 @@ Required const_reinterpret_cast(Actual *p)
}
#ifdef SUPPORTS_ALPN
-bool supportsAlpn()
-{
- return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8_1;
-}
-
QByteArray createAlpnString(const QByteArrayList &nextAllowedProtocols)
{
QByteArray alpnString;
- if (!nextAllowedProtocols.isEmpty() && supportsAlpn()) {
+ if (!nextAllowedProtocols.isEmpty()) {
const QByteArray names = [&nextAllowedProtocols]() {
QByteArray protocolString;
for (QByteArray proto : nextAllowedProtocols) {
if (proto.size() > 255) {
- qCWarning(lcTlsBackend) << "TLS ALPN extension" << proto
- << "is too long and will be ignored.";
+ qCWarning(lcTlsBackendSchannel)
+ << "TLS ALPN extension" << proto << "is too long and will be ignored.";
continue;
} else if (proto.isEmpty()) {
continue;
@@ -684,7 +962,8 @@ void retainExtraData(QByteArray &buffer, const SecBuffer &secBuffer)
return;
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend, "We got SECBUFFER_EXTRA, will retain %lu bytes", secBuffer.cbBuffer);
+ qCDebug(lcTlsBackendSchannel, "We got SECBUFFER_EXTRA, will retain %lu bytes",
+ secBuffer.cbBuffer);
#endif
std::move(buffer.end() - secBuffer.cbBuffer, buffer.end(), buffer.begin());
buffer.resize(secBuffer.cbBuffer);
@@ -694,13 +973,17 @@ qint64 checkIncompleteData(const SecBuffer &secBuffer)
{
if (secBuffer.BufferType == SECBUFFER_MISSING) {
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend, "Need %lu more bytes.", secBuffer.cbBuffer);
+ qCDebug(lcTlsBackendSchannel, "Need %lu more bytes.", secBuffer.cbBuffer);
#endif
return secBuffer.cbBuffer;
}
return 0;
}
+DWORD defaultCredsFlag()
+{
+ return qEnvironmentVariableIsSet("QT_SCH_DEFAULT_CREDS") ? 0 : SCH_CRED_NO_DEFAULT_CREDS;
+}
} // anonymous namespace
@@ -740,6 +1023,10 @@ bool TlsCryptographSchannel::sendToken(void *token, unsigned long tokenLength, b
Q_ASSERT(d);
auto *plainSocket = d->plainTcpSocket();
Q_ASSERT(plainSocket);
+ if (plainSocket->state() == QAbstractSocket::UnconnectedState || !plainSocket->isValid()
+ || !plainSocket->isOpen()) {
+ return false;
+ }
const qint64 written = plainSocket->write(static_cast<const char *>(token), tokenLength);
if (written != qint64(tokenLength)) {
@@ -802,7 +1089,7 @@ bool TlsCryptographSchannel::acquireCredentialsHandle()
Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
- const DWORD protocols = toSchannelProtocol(configuration.protocol());
+ DWORD protocols = toSchannelProtocol(configuration.protocol());
if (protocols == DWORD(-1)) {
setErrorAndEmit(d, QAbstractSocket::SslInvalidUserDataError,
QSslSocket::tr("Invalid protocol chosen"));
@@ -856,80 +1143,50 @@ bool TlsCryptographSchannel::acquireCredentialsHandle()
certsCount = 1;
Q_ASSERT(localCertContext);
}
- void *credentials = nullptr;
-#ifdef SUPPORTS_TLS13
+
+ const QList<QSslCipher> ciphers = configuration.ciphers();
+ if (!ciphers.isEmpty() && !containsTls13Cipher(ciphers))
+ protocols &= ~SP_PROT_TLS1_3;
+
+ QList<CRYPTO_SETTINGS> cryptoSettings;
+ if (!ciphers.isEmpty())
+ cryptoSettings = cryptoSettingsForCiphers(ciphers);
+
TLS_PARAMETERS tlsParameters = {
0,
nullptr,
- toSchannelProtocolNegated(configuration.protocol()), // what protocols to disable
+ negatedSchannelProtocols(protocols), // what protocols to disable
+ static_cast<DWORD>(cryptoSettings.size()),
+ (cryptoSettings.isEmpty() ? nullptr : cryptoSettings.data()),
+ 0
+ };
+
+ SCH_CREDENTIALS credentials = {
+ SCH_CREDENTIALS_VERSION,
0,
+ certsCount,
+ &localCertContext,
nullptr,
- 0
+ 0,
+ nullptr,
+ 0,
+ SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT | defaultCredsFlag(),
+ 1,
+ &tlsParameters
};
- if (supportsTls13()) {
- SCH_CREDENTIALS *cred = new SCH_CREDENTIALS{
- SCH_CREDENTIALS_VERSION,
- 0,
- certsCount,
- &localCertContext,
- nullptr,
- 0,
- nullptr,
- 0,
- SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT
- | SCH_CRED_NO_DEFAULT_CREDS,
- 1,
- &tlsParameters
- };
- credentials = cred;
- } else
-#endif // SUPPORTS_TLS13
- {
- SCHANNEL_CRED *cred = new SCHANNEL_CRED{
- SCHANNEL_CRED_VERSION, // dwVersion
- certsCount, // cCreds
- &localCertContext, // paCred (certificate(s) containing a private key for authentication)
- nullptr, // hRootStore
-
- 0, // cMappers (reserved)
- nullptr, // aphMappers (reserved)
-
- 0, // cSupportedAlgs
- nullptr, // palgSupportedAlgs (nullptr = system default)
-
- protocols, // grbitEnabledProtocols
- 0, // dwMinimumCipherStrength (0 = system default)
- 0, // dwMaximumCipherStrength (0 = system default)
- 0, // dwSessionLifespan (0 = schannel default, 10 hours)
- SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT
- | SCH_CRED_NO_DEFAULT_CREDS, // dwFlags
- 0 // dwCredFormat (must be 0)
- };
- credentials = cred;
- }
- Q_ASSERT(credentials != nullptr);
TimeStamp expiration{};
auto status = AcquireCredentialsHandle(nullptr, // pszPrincipal (unused)
const_cast<wchar_t *>(UNISP_NAME), // pszPackage
isClient ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND, // fCredentialUse
nullptr, // pvLogonID (unused)
- credentials, // pAuthData
+ &credentials, // pAuthData
nullptr, // pGetKeyFn (unused)
nullptr, // pvGetKeyArgument (unused)
&credentialHandle, // phCredential
&expiration // ptsExpir
);
-#ifdef SUPPORTS_TLS13
- if (supportsTls13()) {
- delete static_cast<SCH_CREDENTIALS *>(credentials);
- } else
-#endif // SUPPORTS_TLS13
- {
- delete static_cast<SCHANNEL_CRED *>(credentials);
- }
-
if (status != SEC_E_OK) {
setErrorAndEmit(d, QAbstractSocket::SslInternalError, schannelErrorToString(status));
return false;
@@ -1134,7 +1391,8 @@ bool TlsCryptographSchannel::performHandshake()
auto *plainSocket = d->plainTcpSocket();
Q_ASSERT(plainSocket);
- if (plainSocket->state() == QAbstractSocket::UnconnectedState) {
+ if (plainSocket->state() == QAbstractSocket::UnconnectedState || !plainSocket->isValid()
+ || !plainSocket->isOpen()) {
setErrorAndEmit(d, QAbstractSocket::RemoteHostClosedError,
QSslSocket::tr("The TLS/SSL connection has been closed"));
return false;
@@ -1144,8 +1402,9 @@ bool TlsCryptographSchannel::performHandshake()
Q_ASSERT(schannelState == SchannelState::PerformHandshake);
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend, "Bytes available from socket: %lld", plainSocket->bytesAvailable());
- qCDebug(lcTlsBackend, "intermediateBuffer size: %d", intermediateBuffer.size());
+ qCDebug(lcTlsBackendSchannel, "Bytes available from socket: %lld",
+ plainSocket->bytesAvailable());
+ qCDebug(lcTlsBackendSchannel, "intermediateBuffer size: %d", intermediateBuffer.size());
#endif
if (missingData > plainSocket->bytesAvailable())
@@ -1302,13 +1561,18 @@ bool TlsCryptographSchannel::verifyHandshake()
// Get session cipher info
status = QueryContextAttributes(&contextHandle,
+ SECPKG_ATTR_CIPHER_INFO,
+ &cipherInfo);
+ CHECK_STATUS(status);
+
+ status = QueryContextAttributes(&contextHandle,
SECPKG_ATTR_CONNECTION_INFO,
&connectionInfo);
CHECK_STATUS(status);
#ifdef SUPPORTS_ALPN
const auto allowedProtos = configuration.allowedNextProtocols();
- if (!allowedProtos.isEmpty() && supportsAlpn()) {
+ if (!allowedProtos.isEmpty()) {
SecPkgContext_ApplicationProtocol alpn;
status = QueryContextAttributes(&contextHandle,
SECPKG_ATTR_APPLICATION_PROTOCOL,
@@ -1353,8 +1617,8 @@ bool TlsCryptographSchannel::verifyHandshake()
&& configuration.peerVerifyMode() != QSslSocket::PeerVerifyMode::QueryPeer)) {
if (status != SEC_E_OK) {
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend) << "Couldn't retrieve peer certificate, status:"
- << schannelErrorToString(status);
+ qCDebug(lcTlsBackendSchannel) << "Couldn't retrieve peer certificate, status:"
+ << schannelErrorToString(status);
#endif
const QSslError error{ QSslError::NoPeerCertificate };
sslErrors += error;
@@ -1370,7 +1634,7 @@ bool TlsCryptographSchannel::verifyHandshake()
if (!checkSslErrors() || q->state() != QAbstractSocket::ConnectedState) {
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend) << __func__ << "was unsuccessful. Paused:" << paused;
+ qCDebug(lcTlsBackendSchannel) << __func__ << "was unsuccessful. Paused:" << d->isPaused();
#endif
// If we're paused then checkSslErrors returned false, but it's not an error
return d->isPaused() && q->state() == QAbstractSocket::ConnectedState;
@@ -1454,6 +1718,7 @@ void TlsCryptographSchannel::reset()
deallocateContext();
freeCredentialsHandle(); // in case we already had one (@future: session resumption requires re-use)
+ cipherInfo = {};
connectionInfo = {};
streamSizes = {};
@@ -1503,8 +1768,10 @@ void TlsCryptographSchannel::transmit()
return; // This function should not have been called
// Can happen if called through QSslSocket::abort->QSslSocket::close->QSslSocket::flush->here
- if (plainSocket->state() == QAbstractSocket::SocketState::UnconnectedState)
+ if (plainSocket->state() == QAbstractSocket::UnconnectedState || !plainSocket->isValid()
+ || !plainSocket->isOpen()) {
return;
+ }
if (schannelState != SchannelState::Done) {
continueHandshake();
@@ -1553,7 +1820,8 @@ void TlsCryptographSchannel::transmit()
fullMessage.resize(inputBuffers[0].cbBuffer + inputBuffers[1].cbBuffer + inputBuffers[2].cbBuffer);
const qint64 bytesWritten = plainSocket->write(fullMessage);
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend, "Wrote %lld of total %d bytes", bytesWritten, fullMessage.length());
+ qCDebug(lcTlsBackendSchannel, "Wrote %lld of total %d bytes", bytesWritten,
+ fullMessage.length());
#endif
if (bytesWritten >= 0) {
totalBytesWritten += bytesWritten;
@@ -1575,128 +1843,131 @@ void TlsCryptographSchannel::transmit()
}
}
- if (q->isEncrypted()) { // Decrypt data from remote
- int totalRead = 0;
- bool hadIncompleteData = false;
- const auto readBufferMaxSize = d->maxReadBufferSize();
- while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) {
- if (missingData > plainSocket->bytesAvailable()
- && (!readBufferMaxSize || readBufferMaxSize >= missingData)) {
+ int totalRead = 0;
+ bool hadIncompleteData = false;
+ const auto readBufferMaxSize = d->maxReadBufferSize();
+ while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) {
+ if (missingData > plainSocket->bytesAvailable()
+ && (!readBufferMaxSize || readBufferMaxSize >= missingData)) {
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend, "We're still missing %lld bytes, will check later.", missingData);
+ qCDebug(lcTlsBackendSchannel, "We're still missing %lld bytes, will check later.",
+ missingData);
#endif
- break;
- }
+ break;
+ }
- missingData = 0;
- const qint64 bytesRead = readToBuffer(intermediateBuffer, plainSocket);
+ missingData = 0;
+ const qint64 bytesRead = readToBuffer(intermediateBuffer, plainSocket);
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend, "Read %lld encrypted bytes from the socket", bytesRead);
+ qCDebug(lcTlsBackendSchannel, "Read %lld encrypted bytes from the socket", bytesRead);
#endif
- if (intermediateBuffer.length() == 0 || (hadIncompleteData && bytesRead == 0)) {
+ if (intermediateBuffer.length() == 0 || (hadIncompleteData && bytesRead == 0)) {
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend, (hadIncompleteData ? "No new data received, leaving loop!"
- : "Nothing to decrypt, leaving loop!"));
+ qCDebug(lcTlsBackendSchannel,
+ hadIncompleteData ? "No new data received, leaving loop!"
+ : "Nothing to decrypt, leaving loop!");
#endif
- break;
- }
- hadIncompleteData = false;
+ break;
+ }
+ hadIncompleteData = false;
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend, "Total amount of bytes to decrypt: %d", intermediateBuffer.length());
+ qCDebug(lcTlsBackendSchannel, "Total amount of bytes to decrypt: %d",
+ intermediateBuffer.length());
#endif
- SecBuffer dataBuffer[4]{
- createSecBuffer(intermediateBuffer, SECBUFFER_DATA),
- createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
- createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
- createSecBuffer(nullptr, 0, SECBUFFER_EMPTY)
- };
- SecBufferDesc message{
- SECBUFFER_VERSION,
- ARRAYSIZE(dataBuffer),
- dataBuffer
- };
- auto status = DecryptMessage(&contextHandle, &message, 0, nullptr);
- if (status == SEC_E_OK || status == SEC_I_RENEGOTIATE || status == SEC_I_CONTEXT_EXPIRED) {
- // There can still be 0 output even if it succeeds, this is fine
- if (dataBuffer[1].cbBuffer > 0) {
- // It is always decrypted in-place.
- // But [0] is the STREAM_HEADER, [1] is the DATA and [2] is the STREAM_TRAILER.
- // The pointers in all of those still point into 'intermediateBuffer'.
- buffer.append(static_cast<char *>(dataBuffer[1].pvBuffer),
- dataBuffer[1].cbBuffer);
- totalRead += dataBuffer[1].cbBuffer;
+ SecBuffer dataBuffer[4]{
+ createSecBuffer(intermediateBuffer, SECBUFFER_DATA),
+ createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
+ createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
+ createSecBuffer(nullptr, 0, SECBUFFER_EMPTY)
+ };
+ SecBufferDesc message{
+ SECBUFFER_VERSION,
+ ARRAYSIZE(dataBuffer),
+ dataBuffer
+ };
+ auto status = DecryptMessage(&contextHandle, &message, 0, nullptr);
+ if (status == SEC_E_OK || status == SEC_I_RENEGOTIATE || status == SEC_I_CONTEXT_EXPIRED) {
+ // There can still be 0 output even if it succeeds, this is fine
+ if (dataBuffer[1].cbBuffer > 0) {
+ // It is always decrypted in-place.
+ // But [0] is the STREAM_HEADER, [1] is the DATA and [2] is the STREAM_TRAILER.
+ // The pointers in all of those still point into 'intermediateBuffer'.
+ buffer.append(static_cast<char *>(dataBuffer[1].pvBuffer),
+ dataBuffer[1].cbBuffer);
+ totalRead += dataBuffer[1].cbBuffer;
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend, "Decrypted %lu bytes. New read buffer size: %d",
- dataBuffer[1].cbBuffer, buffer.size());
+ qCDebug(lcTlsBackendSchannel, "Decrypted %lu bytes. New read buffer size: %d",
+ dataBuffer[1].cbBuffer, buffer.size());
#endif
- }
- if (dataBuffer[3].BufferType == SECBUFFER_EXTRA) {
- // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
- // dataBuffer[3].cbBuffer indicates the amount of bytes _NOT_ processed,
- // the rest need to be stored.
- retainExtraData(intermediateBuffer, dataBuffer[3]);
- } else {
- intermediateBuffer.resize(0);
- }
}
+ if (dataBuffer[3].BufferType == SECBUFFER_EXTRA) {
+ // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
+ // dataBuffer[3].cbBuffer indicates the amount of bytes _NOT_ processed,
+ // the rest need to be stored.
+ retainExtraData(intermediateBuffer, dataBuffer[3]);
+ } else {
+ intermediateBuffer.resize(0);
+ }
+ }
- if (status == SEC_E_INCOMPLETE_MESSAGE) {
- missingData = checkIncompleteData(dataBuffer[0]);
+ if (status == SEC_E_INCOMPLETE_MESSAGE) {
+ missingData = checkIncompleteData(dataBuffer[0]);
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend, "We didn't have enough data to decrypt anything, will try again!");
+ qCDebug(lcTlsBackendSchannel, "We didn't have enough data to decrypt anything, will try again!");
#endif
- // We try again, but if we don't get any more data then we leave
- hadIncompleteData = true;
- } else if (status == SEC_E_INVALID_HANDLE) {
- // I don't think this should happen, if it does we're done...
- qCWarning(lcTlsBackend, "The internal SSPI handle is invalid!");
- Q_UNREACHABLE();
- } else if (status == SEC_E_INVALID_TOKEN) {
- qCWarning(lcTlsBackend, "Got SEC_E_INVALID_TOKEN!");
- Q_UNREACHABLE(); // Happened once due to a bug, but shouldn't generally happen(?)
- } else if (status == SEC_E_MESSAGE_ALTERED) {
- // The message has been altered, disconnect now.
- shutdown = true; // skips sending the shutdown alert
- disconnectFromHost();
- setErrorAndEmit(d, QAbstractSocket::SslInternalError,
- schannelErrorToString(status));
- break;
- } else if (status == SEC_E_OUT_OF_SEQUENCE) {
- // @todo: I don't know if this one is actually "fatal"..
- // This path might never be hit as it seems this is for connection-oriented connections,
- // while SEC_E_MESSAGE_ALTERED is for stream-oriented ones (what we use).
- shutdown = true; // skips sending the shutdown alert
- disconnectFromHost();
- setErrorAndEmit(d, QAbstractSocket::SslInternalError,
- schannelErrorToString(status));
- break;
- } else if (status == SEC_I_CONTEXT_EXPIRED) {
- // 'remote' has initiated a shutdown
- disconnectFromHost();
- setErrorAndEmit(d, QAbstractSocket::RemoteHostClosedError,
- schannelErrorToString(status));
- break;
- } else if (status == SEC_I_RENEGOTIATE) {
- // 'remote' wants to renegotiate
+ // We try again, but if we don't get any more data then we leave
+ hadIncompleteData = true;
+ } else if (status == SEC_E_INVALID_HANDLE) {
+ // I don't think this should happen, if it does we're done...
+ qCWarning(lcTlsBackendSchannel, "The internal SSPI handle is invalid!");
+ Q_UNREACHABLE();
+ } else if (status == SEC_E_INVALID_TOKEN) {
+ // Supposedly we have an invalid token, it's under-documented what
+ // this means, so to be safe we disconnect.
+ shutdown = true;
+ disconnectFromHost();
+ setErrorAndEmit(d, QAbstractSocket::SslInternalError, schannelErrorToString(status));
+ break;
+ } else if (status == SEC_E_MESSAGE_ALTERED) {
+ // The message has been altered, disconnect now.
+ shutdown = true; // skips sending the shutdown alert
+ disconnectFromHost();
+ setErrorAndEmit(d, QAbstractSocket::SslInternalError,
+ schannelErrorToString(status));
+ break;
+ } else if (status == SEC_E_OUT_OF_SEQUENCE) {
+ // @todo: I don't know if this one is actually "fatal"..
+ // This path might never be hit as it seems this is for connection-oriented connections,
+ // while SEC_E_MESSAGE_ALTERED is for stream-oriented ones (what we use).
+ shutdown = true; // skips sending the shutdown alert
+ disconnectFromHost();
+ setErrorAndEmit(d, QAbstractSocket::SslInternalError,
+ schannelErrorToString(status));
+ break;
+ } else if (status == SEC_I_CONTEXT_EXPIRED) {
+ // 'remote' has initiated a shutdown
+ disconnectFromHost();
+ break;
+ } else if (status == SEC_I_RENEGOTIATE) {
+ // 'remote' wants to renegotiate
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend, "The peer wants to renegotiate.");
+ qCDebug(lcTlsBackendSchannel, "The peer wants to renegotiate.");
#endif
- schannelState = SchannelState::Renegotiate;
- renegotiating = true;
+ schannelState = SchannelState::Renegotiate;
+ renegotiating = true;
- // We need to call 'continueHandshake' or else there's no guarantee it ever gets called
- continueHandshake();
- break;
- }
+ // We need to call 'continueHandshake' or else there's no guarantee it ever gets called
+ continueHandshake();
+ break;
}
+ }
- if (totalRead) {
- if (bool *readyReadEmittedPointer = d->readyReadPointer())
- *readyReadEmittedPointer = true;
- emit q->readyRead();
- emit q->channelReadyRead(0);
- }
+ if (totalRead) {
+ if (bool *readyReadEmittedPointer = d->readyReadPointer())
+ *readyReadEmittedPointer = true;
+ emit q->readyRead();
+ emit q->channelReadyRead(0);
}
}
@@ -1716,7 +1987,8 @@ void TlsCryptographSchannel::sendShutdown()
if (status != SEC_E_OK) {
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend) << "Failed to apply shutdown control token:" << schannelErrorToString(status);
+ qCDebug(lcTlsBackendSchannel)
+ << "Failed to apply shutdown control token:" << schannelErrorToString(status);
#endif
return;
}
@@ -1774,7 +2046,8 @@ void TlsCryptographSchannel::sendShutdown()
}
} else {
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend) << "Failed to initialize shutdown:" << schannelErrorToString(status);
+ qCDebug(lcTlsBackendSchannel)
+ << "Failed to initialize shutdown:" << schannelErrorToString(status);
#endif
}
}
@@ -1789,30 +2062,36 @@ void TlsCryptographSchannel::disconnectFromHost()
if (SecIsValidHandle(&contextHandle)) {
if (!shutdown) {
shutdown = true;
- if (plainSocket->state() != QAbstractSocket::UnconnectedState) {
- if (q->isEncrypted()) {
- // Read as much as possible because this is likely our last chance
- qint64 tempMax = d->maxReadBufferSize();
- d->setMaxReadBufferSize(0);
- transmit();
- d->setMaxReadBufferSize(tempMax);
- sendShutdown();
- }
+ if (plainSocket->state() != QAbstractSocket::UnconnectedState && q->isEncrypted()) {
+ sendShutdown();
+ transmit();
}
}
}
- if (plainSocket->state() != QAbstractSocket::UnconnectedState)
- plainSocket->disconnectFromHost();
+ plainSocket->disconnectFromHost();
}
void TlsCryptographSchannel::disconnected()
{
Q_ASSERT(d);
+ auto *plainSocket = d->plainTcpSocket();
+ Q_ASSERT(plainSocket);
+ d->setEncrypted(false);
shutdown = true;
- d->setEncrypted(false);
- deallocateContext();
- freeCredentialsHandle();
+ if (plainSocket->bytesAvailable() > 0 || hasUndecryptedData()) {
+ // Read as much as possible because this is likely our last chance
+ qint64 tempMax = d->maxReadBufferSize();
+ d->setMaxReadBufferSize(0); // Unlimited
+ transmit();
+ d->setMaxReadBufferSize(tempMax);
+ // Since there were bytes still available we don't want to deallocate
+ // our context yet. It will happen later, when the socket is re-used or
+ // destroyed.
+ } else {
+ deallocateContext();
+ freeCredentialsHandle();
+ }
}
QSslCipher TlsCryptographSchannel::sessionCipher() const
@@ -1820,8 +2099,17 @@ QSslCipher TlsCryptographSchannel::sessionCipher() const
Q_ASSERT(q);
if (!q->isEncrypted())
- return QSslCipher();
- return QSslCipher(QStringLiteral("Schannel"), sessionProtocol());
+ return {};
+
+ const auto sessionProtocol = toQtSslProtocol(connectionInfo.dwProtocol);
+
+ const auto ciphers = ciphersByName(QStringView(cipherInfo.szCipherSuite));
+ for (const auto& cipher : ciphers) {
+ if (cipher.protocol() == sessionProtocol)
+ return cipher;
+ }
+
+ return {};
}
QSsl::SslProtocol TlsCryptographSchannel::sessionProtocol() const
@@ -1968,7 +2256,7 @@ void TlsCryptographSchannel::initializeCertificateStores()
localCertificateStore = createStoreFromCertificateChain(configuration.localCertificateChain(),
configuration.privateKey());
if (localCertificateStore == nullptr)
- qCWarning(lcTlsBackend, "Failed to load certificate chain!");
+ qCWarning(lcTlsBackendSchannel, "Failed to load certificate chain!");
}
}
@@ -1995,7 +2283,7 @@ bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext)
nullptr));
if (!tempCertCollection) {
#ifdef QSSLSOCKET_DEBUG
- qCWarning(lcTlsBackend, "Failed to create certificate store collection!");
+ qCWarning(lcTlsBackendSchannel, "Failed to create certificate store collection!");
#endif
return false;
}
@@ -2005,15 +2293,20 @@ bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext)
// the Ca list, not just included during verification.
// That being said, it's not trivial to add the root certificates (if and only if they
// came from the system root store). And I don't see this mentioned in our documentation.
- auto rootStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT"));
+ auto rootStore = QHCertStorePointer(
+ CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0,
+ CERT_STORE_READONLY_FLAG | CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT"));
+
if (!rootStore) {
#ifdef QSSLSOCKET_DEBUG
- qCWarning(lcTlsBackend, "Failed to open the system root CA certificate store!");
+ qCWarning(lcTlsBackendSchannel, "Failed to open the system root CA certificate store!");
#endif
return false;
} else if (!CertAddStoreToCollection(tempCertCollection.get(), rootStore.get(), 0, 1)) {
#ifdef QSSLSOCKET_DEBUG
- qCWarning(lcTlsBackend, "Failed to add the system root CA certificate store to the certificate store collection!");
+ qCWarning(lcTlsBackendSchannel,
+ "Failed to add the system root CA certificate store to the certificate store "
+ "collection!");
#endif
return false;
}
@@ -2021,7 +2314,9 @@ bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext)
if (caCertificateStore) {
if (!CertAddStoreToCollection(tempCertCollection.get(), caCertificateStore.get(), 0, 1)) {
#ifdef QSSLSOCKET_DEBUG
- qCWarning(lcTlsBackend, "Failed to add the user's CA certificate store to the certificate store collection!");
+ qCWarning(lcTlsBackendSchannel,
+ "Failed to add the user's CA certificate store to the certificate store "
+ "collection!");
#endif
return false;
}
@@ -2029,7 +2324,8 @@ bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext)
if (!CertAddStoreToCollection(tempCertCollection.get(), certContext->hCertStore, 0, 0)) {
#ifdef QSSLSOCKET_DEBUG
- qCWarning(lcTlsBackend, "Failed to add certificate's origin store to the certificate store collection!");
+ qCWarning(lcTlsBackendSchannel,
+ "Failed to add certificate's origin store to the certificate store collection!");
#endif
return false;
}
@@ -2114,18 +2410,48 @@ bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext)
verifyDepth = DWORD(q->peerVerifyDepth());
const auto &caCertificates = q->sslConfiguration().caCertificates();
+
+ if (!rootCertOnDemandLoadingAllowed()
+ && !(chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN)
+ && (q->peerVerifyMode() == QSslSocket::VerifyPeer
+ || (isClient && q->peerVerifyMode() == QSslSocket::AutoVerifyPeer))) {
+ // When verifying a peer Windows "helpfully" builds a chain that
+ // may include roots from the system store. But we don't want that if
+ // the user has set their own CA certificates.
+ // Since Windows claims this is not a partial chain the root is included
+ // and we have to check that it is one of our configured CAs.
+ CERT_CHAIN_ELEMENT *element = chain->rgpElement[chain->cElement - 1];
+ QSslCertificate certificate = getCertificateFromChainElement(element);
+ if (!caCertificates.contains(certificate)) {
+ auto error = QSslError(QSslError::CertificateUntrusted, certificate);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+
QList<QSslCertificate> peerCertificateChain;
for (DWORD i = 0; i < verifyDepth; i++) {
CERT_CHAIN_ELEMENT *element = chain->rgpElement[i];
QSslCertificate certificate = getCertificateFromChainElement(element);
+ if (certificate.isNull()) {
+ const auto &previousCert = !peerCertificateChain.isEmpty() ? peerCertificateChain.last()
+ : QSslCertificate();
+ auto error = QSslError(QSslError::SslError::UnableToGetIssuerCertificate, previousCert);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (previousCert.isNull() || q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
const QList<QSslCertificateExtension> extensions = certificate.extensions();
#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcTlsBackend) << "issuer:" << certificate.issuerDisplayName()
- << "\nsubject:" << certificate.subjectDisplayName()
- << "\nQSslCertificate info:" << certificate
- << "\nextended error info:" << element->pwszExtendedErrorInfo
- << "\nerror status:" << element->TrustStatus.dwErrorStatus;
+ qCDebug(lcTlsBackendSchannel) << "issuer:" << certificate.issuerDisplayName()
+ << "\nsubject:" << certificate.subjectDisplayName()
+ << "\nQSslCertificate info:" << certificate
+ << "\nextended error info:" << element->pwszExtendedErrorInfo
+ << "\nerror status:" << element->TrustStatus.dwErrorStatus;
#endif
peerCertificateChain.append(certificate);
@@ -2219,7 +2545,7 @@ bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext)
if (element->TrustStatus.dwErrorStatus & CERT_TRUST_INVALID_BASIC_CONSTRAINTS) {
auto it = std::find_if(extensions.cbegin(), extensions.cend(),
[](const QSslCertificateExtension &extension) {
- return extension.name() == QLatin1String("basicConstraints");
+ return extension.name() == "basicConstraints"_L1;
});
if (it != extensions.cend()) {
// @Note: This is actually one of two errors:
@@ -2227,7 +2553,7 @@ bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext)
// or the chain path length has been exceeded."
QVariantMap basicConstraints = it->value().toMap();
QSslError error;
- if (i > 0 && !basicConstraints.value(QLatin1String("ca"), false).toBool())
+ if (i > 0 && !basicConstraints.value("ca"_L1, false).toBool())
error = QSslError(QSslError::InvalidPurpose, certificate);
else
error = QSslError(QSslError::PathLengthExceeded, certificate);
@@ -2265,7 +2591,7 @@ bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext)
}
if (!peerCertificateChain.isEmpty())
- QTlsBackend::storePeerCertificate(d, peerCertificateChain.first());
+ QTlsBackend::storePeerCertificate(d, peerCertificateChain.constFirst());
const auto &configuration = q->sslConfiguration(); // Probably, updated by QTlsBackend::storePeerCertificate etc.
// @Note: Somewhat copied from qsslsocket_mac.cpp
diff --git a/src/plugins/tls/schannel/qtls_schannel_p.h b/src/plugins/tls/schannel/qtls_schannel_p.h
index ca4f8f09cf..fab8777249 100644
--- a/src/plugins/tls/schannel/qtls_schannel_p.h
+++ b/src/plugins/tls/schannel/qtls_schannel_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 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$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTLS_SCHANNEL_P_H
#define QTLS_SCHANNEL_P_H
@@ -129,6 +93,7 @@ private:
QSslSocket *q = nullptr;
QSslSocketPrivate *d = nullptr;
+ SecPkgContext_CipherInfo cipherInfo = {};
SecPkgContext_ConnectionInfo connectionInfo = {};
SecPkgContext_StreamSizes streamSizes = {};
diff --git a/src/plugins/tls/schannel/qtlsbackend_schannel_p.h b/src/plugins/tls/schannel/qtlsbackend_schannel_p.h
index e53cf17f13..7c2d675e79 100644
--- a/src/plugins/tls/schannel/qtlsbackend_schannel_p.h
+++ b/src/plugins/tls/schannel/qtlsbackend_schannel_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTLSBACKEND_ST_P_H
#define QTLSBACKEND_ST_P_H
@@ -93,10 +57,13 @@ private:
QTlsPrivate::X509PemReaderPtr X509PemReader() const override;
QTlsPrivate::X509DerReaderPtr X509DerReader() const override;
+ QTlsPrivate::X509Pkcs12ReaderPtr X509Pkcs12Reader() const override;
static bool s_loadedCiphersAndCerts;
};
+Q_DECLARE_LOGGING_CATEGORY(lcTlsBackendSchannel)
+
QT_END_NAMESPACE
#endif // QTLSBACKEND_ST_P_H
diff --git a/src/plugins/tls/schannel/qtlskey_schannel.cpp b/src/plugins/tls/schannel/qtlskey_schannel.cpp
index e407da2ed3..eb0a2371ab 100644
--- a/src/plugins/tls/schannel/qtlskey_schannel.cpp
+++ b/src/plugins/tls/schannel/qtlskey_schannel.cpp
@@ -1,44 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtNetwork/private/qssl_p.h>
+#include "qtlsbackend_schannel_p.h"
#include "qtlskey_schannel_p.h"
#include "../shared/qwincrypt_p.h"
@@ -50,6 +15,7 @@
#include <QtCore/qscopeguard.h>
#include <QtCore/qbytearray.h>
+#include <QtCore/qvarlengtharray.h>
QT_BEGIN_NAMESPACE
@@ -81,7 +47,7 @@ BCRYPT_ALG_HANDLE getHandle(QSslKeyPrivate::Cipher cipher)
0 // dwFlags
);
if (status < 0) {
- qCWarning(lcTlsBackend, "Failed to open algorithm handle (%ld)!", status);
+ qCWarning(lcTlsBackendSchannel, "Failed to open algorithm handle (%ld)!", status);
return nullptr;
}
@@ -102,7 +68,7 @@ BCRYPT_KEY_HANDLE generateSymmetricKey(BCRYPT_ALG_HANDLE handle,
0 // dwFlags
);
if (status < 0) {
- qCWarning(lcTlsBackend, "Failed to generate symmetric key (%ld)!", status);
+ qCWarning(lcTlsBackendSchannel, "Failed to generate symmetric key (%ld)!", status);
return nullptr;
}
@@ -115,7 +81,8 @@ BCRYPT_KEY_HANDLE generateSymmetricKey(BCRYPT_ALG_HANDLE handle,
);
if (status < 0) {
BCryptDestroyKey(keyHandle);
- qCWarning(lcTlsBackend, "Failed to change the symmetric key's chaining mode (%ld)!", status);
+ qCWarning(lcTlsBackendSchannel, "Failed to change the symmetric key's chaining mode (%ld)!",
+ status);
return nullptr;
}
return keyHandle;
@@ -160,7 +127,8 @@ QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const
BCRYPT_BLOCK_PADDING // dwFlags
);
if (status < 0) {
- qCWarning(lcTlsBackend, "%s failed (%ld)!", encrypt ? "Encrypt" : "Decrypt", status);
+ qCWarning(lcTlsBackendSchannel, "%s failed (%ld)!", encrypt ? "Encrypt" : "Decrypt",
+ status);
return {};
}
}
diff --git a/src/plugins/tls/schannel/qtlskey_schannel_p.h b/src/plugins/tls/schannel/qtlskey_schannel_p.h
index 53c3b447ce..14d146dfd7 100644
--- a/src/plugins/tls/schannel/qtlskey_schannel_p.h
+++ b/src/plugins/tls/schannel/qtlskey_schannel_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTLSKEY_SCHANNEL_P_H
#define QTLSKEY_SCHANNEL_P_H
diff --git a/src/plugins/tls/schannel/qx509_schannel.cpp b/src/plugins/tls/schannel/qx509_schannel.cpp
index 01a21c69f5..d9d82dce29 100644
--- a/src/plugins/tls/schannel/qx509_schannel.cpp
+++ b/src/plugins/tls/schannel/qx509_schannel.cpp
@@ -1,45 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "qtlsbackend_schannel_p.h"
#include "qtlskey_schannel_p.h"
#include "qx509_schannel_p.h"
+#include <QtCore/private/qsystemerror_p.h>
#include <QtNetwork/private/qsslcertificate_p.h>
#include <memory>
@@ -75,13 +41,173 @@ QSslCertificate X509CertificateSchannel::QSslCertificate_from_CERT_CONTEXT(const
QByteArray derData = QByteArray((const char *)certificateContext->pbCertEncoded,
certificateContext->cbCertEncoded);
QSslCertificate certificate(derData, QSsl::Der);
-
- auto *certBackend = QTlsBackend::backend<X509CertificateSchannel>(certificate);
- Q_ASSERT(certBackend);
- certBackend->certificateContext = CertDuplicateCertificateContext(certificateContext);
+ if (!certificate.isNull()) {
+ auto *certBackend = QTlsBackend::backend<X509CertificateSchannel>(certificate);
+ Q_ASSERT(certBackend);
+ certBackend->certificateContext = CertDuplicateCertificateContext(certificateContext);
+ }
return certificate;
}
+bool X509CertificateSchannel::importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert,
+ QList<QSslCertificate> *caCertificates,
+ const QByteArray &passPhrase)
+{
+ // These are required
+ Q_ASSERT(device);
+ Q_ASSERT(key);
+ Q_ASSERT(cert);
+
+ QByteArray pkcs12data = device->readAll();
+ if (pkcs12data.size() == 0)
+ return false;
+
+ CRYPT_DATA_BLOB dataBlob;
+ dataBlob.cbData = pkcs12data.size();
+ dataBlob.pbData = reinterpret_cast<BYTE*>(pkcs12data.data());
+
+ const auto password = QString::fromUtf8(passPhrase);
+
+ const DWORD flags = (CRYPT_EXPORTABLE | PKCS12_NO_PERSIST_KEY | PKCS12_PREFER_CNG_KSP);
+
+ auto certStore = QHCertStorePointer(PFXImportCertStore(&dataBlob,
+ reinterpret_cast<LPCWSTR>(password.utf16()),
+ flags));
+
+ if (!certStore) {
+ qCWarning(lcTlsBackendSchannel, "Failed to import PFX data: %s",
+ qPrintable(QSystemError::windowsString()));
+ return false;
+ }
+
+ // first extract the certificate with the private key
+ const auto certContext = QPCCertContextPointer(CertFindCertificateInStore(certStore.get(),
+ X509_ASN_ENCODING |
+ PKCS_7_ASN_ENCODING,
+ 0,
+ CERT_FIND_HAS_PRIVATE_KEY,
+ nullptr, nullptr));
+
+ if (!certContext) {
+ qCWarning(lcTlsBackendSchannel, "Failed to find certificate in PFX store: %s",
+ qPrintable(QSystemError::windowsString()));
+ return false;
+ }
+
+ *cert = QSslCertificate_from_CERT_CONTEXT(certContext.get());
+
+ // retrieve the private key for the certificate
+ NCRYPT_KEY_HANDLE keyHandle = {};
+ DWORD keyHandleSize = sizeof(keyHandle);
+ if (!CertGetCertificateContextProperty(certContext.get(), CERT_NCRYPT_KEY_HANDLE_PROP_ID,
+ &keyHandle, &keyHandleSize)) {
+ qCWarning(lcTlsBackendSchannel, "Failed to find private key handle in certificate context: %s",
+ qPrintable(QSystemError::windowsString()));
+ return false;
+ }
+
+ SECURITY_STATUS securityStatus = ERROR_SUCCESS;
+
+ // we need the 'NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG' to make NCryptExportKey succeed
+ DWORD policy = (NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG);
+ DWORD policySize = sizeof(policy);
+
+ securityStatus = NCryptSetProperty(keyHandle, NCRYPT_EXPORT_POLICY_PROPERTY,
+ reinterpret_cast<BYTE*>(&policy), policySize, 0);
+ if (securityStatus != ERROR_SUCCESS) {
+ qCWarning(lcTlsBackendSchannel, "Failed to update export policy of private key: 0x%x",
+ static_cast<unsigned int>(securityStatus));
+ return false;
+ }
+
+ DWORD blobSize = {};
+ securityStatus = NCryptExportKey(keyHandle, {}, BCRYPT_RSAFULLPRIVATE_BLOB,
+ nullptr, nullptr, 0, &blobSize, 0);
+ if (securityStatus != ERROR_SUCCESS) {
+ qCWarning(lcTlsBackendSchannel, "Failed to retrieve private key size: 0x%x",
+ static_cast<unsigned int>(securityStatus));
+ return false;
+ }
+
+ std::vector<BYTE> blob(blobSize);
+ securityStatus = NCryptExportKey(keyHandle, {}, BCRYPT_RSAFULLPRIVATE_BLOB,
+ nullptr, blob.data(), blobSize, &blobSize, 0);
+ if (securityStatus != ERROR_SUCCESS) {
+ qCWarning(lcTlsBackendSchannel, "Failed to retrieve private key from certificate: 0x%x",
+ static_cast<unsigned int>(securityStatus));
+ return false;
+ }
+
+ DWORD privateKeySize = {};
+
+ if (!CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, CNG_RSA_PRIVATE_KEY_BLOB,
+ blob.data(), nullptr, &privateKeySize)) {
+ qCWarning(lcTlsBackendSchannel, "Failed to encode private key to key info: %s",
+ qPrintable(QSystemError::windowsString()));
+ return false;
+ }
+
+ std::vector<BYTE> privateKeyData(privateKeySize);
+
+ CRYPT_PRIVATE_KEY_INFO privateKeyInfo = {};
+ privateKeyInfo.Algorithm.pszObjId = const_cast<PSTR>(szOID_RSA_RSA);
+ privateKeyInfo.PrivateKey.cbData = privateKeySize;
+ privateKeyInfo.PrivateKey.pbData = privateKeyData.data();
+
+ if (!CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ CNG_RSA_PRIVATE_KEY_BLOB, blob.data(),
+ privateKeyInfo.PrivateKey.pbData, &privateKeyInfo.PrivateKey.cbData)) {
+ qCWarning(lcTlsBackendSchannel, "Failed to encode private key to key info: %s",
+ qPrintable(QSystemError::windowsString()));
+ return false;
+ }
+
+
+ DWORD derSize = {};
+
+ if (!CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO,
+ &privateKeyInfo, nullptr, &derSize)) {
+ qCWarning(lcTlsBackendSchannel, "Failed to encode key info to DER format: %s",
+ qPrintable(QSystemError::windowsString()));
+
+ return false;
+ }
+
+ QByteArray derData(derSize, Qt::Uninitialized);
+
+ if (!CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO,
+ &privateKeyInfo, reinterpret_cast<BYTE*>(derData.data()), &derSize)) {
+ qCWarning(lcTlsBackendSchannel, "Failed to encode key info to DER format: %s",
+ qPrintable(QSystemError::windowsString()));
+
+ return false;
+ }
+
+ *key = QSslKey(derData, QSsl::Rsa, QSsl::Der, QSsl::PrivateKey);
+ if (key->isNull()) {
+ qCWarning(lcTlsBackendSchannel, "Failed to parse private key from DER format");
+ return false;
+ }
+
+ // fetch all the remaining certificates as CA certificates
+ if (caCertificates) {
+ PCCERT_CONTEXT caCertContext = nullptr;
+ while ((caCertContext = CertFindCertificateInStore(certStore.get(),
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ 0, CERT_FIND_ANY, nullptr, caCertContext))) {
+ if (CertCompareCertificate(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ certContext->pCertInfo, caCertContext->pCertInfo))
+ continue; // ignore the certificate with private key
+
+ auto caCertificate = QSslCertificate_from_CERT_CONTEXT(caCertContext);
+
+ caCertificates->append(caCertificate);
+ }
+ }
+
+ return true;
+}
+
} // namespace QTlsPrivate
QT_END_NAMESPACE
diff --git a/src/plugins/tls/schannel/qx509_schannel_p.h b/src/plugins/tls/schannel/qx509_schannel_p.h
index bf39229e96..4625c9584c 100644
--- a/src/plugins/tls/schannel/qx509_schannel_p.h
+++ b/src/plugins/tls/schannel/qx509_schannel_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QX509_SCHANNEL_P_H
#define QX509_SCHANNEL_P_H
@@ -72,6 +36,10 @@ public:
Qt::HANDLE handle() const override;
static QSslCertificate QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext);
+
+ static bool importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert,
+ QList<QSslCertificate> *caCertificates,
+ const QByteArray &passPhrase);
private:
const CERT_CONTEXT *certificateContext = nullptr;