From f7792d2b6d3d1e5dd47a8f6429b817b793bcfb9b Mon Sep 17 00:00:00 2001 From: Tobias Koenig Date: Fri, 28 Apr 2023 10:42:04 +0200 Subject: Schannel: Add support for proper listing of ciphers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reworked the code to list supported ciphers for Schannel backend to provide the proper cipher names and cipher properties. Note: restricting the used ciphers for a TLS connection is only supported for TLS 1.2 yet! [ChangeLog][QtNetwork][QSslConfiguration] Add support for listing supported ciphers with Schannel backend. Change-Id: Idfc17335ec489315387a3e84b7a76a263be22378 Reviewed-by: MÃ¥rten Nordheim --- src/plugins/tls/schannel/qtls_schannel.cpp | 383 ++++++++++++++++++++++++++--- src/plugins/tls/schannel/qtls_schannel_p.h | 1 + 2 files changed, 355 insertions(+), 29 deletions(-) (limited to 'src/plugins/tls/schannel') diff --git a/src/plugins/tls/schannel/qtls_schannel.cpp b/src/plugins/tls/schannel/qtls_schannel.cpp index ae9ff06eae..9d171b5030 100644 --- a/src/plugins/tls/schannel/qtls_schannel.cpp +++ b/src/plugins/tls/schannel/qtls_schannel.cpp @@ -87,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!: @@ -108,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) @@ -127,39 +129,341 @@ Q_LOGGING_CATEGORY(lcTlsBackendSchannel, "qt.tlsbackend.schannel"); QByteArray _q_makePkcs12(const QList &certs, const QSslKey &key, const QString &passPhrase); +namespace { +bool supportsTls13(); +} + namespace QTlsPrivate { -QList defaultCiphers() +QList defaultCiphers(); + +struct SchannelCipherInfo { + const char *openSslCipherSuite; + const char *schannelCipherSuite; + const char *keyExchangeMethod; + const char *authenticationMethod; + const char *encryptionMethod; + int encryptionBits; + const char *hashMethod; + QList 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 = {{ + {"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(BCRYPT_CHAIN_MODE_CBC) +}; + +UNICODE_STRING gcmChainingMode = { + sizeof(BCRYPT_CHAIN_MODE_GCM) - 2, + sizeof(BCRYPT_CHAIN_MODE_GCM), + const_cast(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 cryptoSettingsForCiphers(const QList &ciphers) +{ + static const QList defaultCipherList = defaultCiphers(); + + if (defaultCipherList == ciphers) { + // the ciphers have not been restricted for this session, so no black listing needed + return {}; + } + + QList cipherInfo; + + for (const auto &cipher : ciphers) { + if (cipher.isNull()) + continue; + + const auto *info = cipherInfoByOpenSslName(cipher.name()); + if (!cipherInfo.contains(info)) + cipherInfo.append(info); + } + + QList cryptoSettings; + + const auto assignUnicodeString = [](UNICODE_STRING &unicodeString, const wchar_t *characters) { + unicodeString.Length = static_cast(wcslen(characters) * sizeof(WCHAR)); + unicodeString.MaximumLength = unicodeString.Length + sizeof(UNICODE_NULL); + unicodeString.Buffer = const_cast(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 ciphersByName(QStringView schannelSuiteName) { - // Previously the code was in QSslSocketBackendPrivate. QList ciphers; - const QString protocolStrings[] = { QStringLiteral("TLSv1"), QStringLiteral("TLSv1.1"), - QStringLiteral("TLSv1.2"), QStringLiteral("TLSv1.3") }; + + for (const auto &cipher : schannelCipherInfo) { + if (QLatin1StringView(cipher.schannelCipherSuite) == schannelSuiteName) { + for (const auto &protocol : cipher.protocols) { QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED - const QSsl::SslProtocol protocols[] = { QSsl::TlsV1_0, QSsl::TlsV1_1, - QSsl::TlsV1_2, QSsl::TlsV1_3 }; + 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 - 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); + ciphers.append(QTlsBackend::createCiphersuite(QLatin1StringView(cipher.openSslCipherSuite), + QLatin1StringView(cipher.keyExchangeMethod), + QLatin1StringView(cipher.encryptionMethod), + QLatin1StringView(cipher.authenticationMethod), + cipher.encryptionBits, + protocol, protocolName)); + } + } } return ciphers; - } -} // namespace QTlsPrivate +QList defaultCiphers() +{ + ULONG contextFunctionsCount = {}; + PCRYPT_CONTEXT_FUNCTIONS contextFunctions = {}; -namespace { -bool supportsTls13(); + 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 ciphers; + + for (ULONG index = 0; index < contextFunctions->cFunctions; ++index) { + const auto suiteName = QStringView(contextFunctions->rgpszFunctions[index]); + + const QList 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 &ciphers) +{ + return std::any_of(ciphers.cbegin(), ciphers.cend(), + [](const QSslCipher &cipher) { return cipher.protocol() == QSsl::TlsV1_3; }); } +} // namespace QTlsPrivate + bool QSchannelBackend::s_loadedCiphersAndCerts = false; Q_GLOBAL_STATIC(QRecursiveMutex, qt_schannel_mutex) @@ -458,10 +762,10 @@ QT_WARNING_POP // 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; } @@ -778,7 +1082,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")); @@ -833,12 +1137,18 @@ bool TlsCryptographSchannel::acquireCredentialsHandle() Q_ASSERT(localCertContext); } + const QList ciphers = configuration.ciphers(); + if (!containsTls13Cipher(ciphers)) + protocols &= ~SP_PROT_TLS1_3; + + QList cryptoSettings = cryptoSettingsForCiphers(ciphers); + TLS_PARAMETERS tlsParameters = { 0, nullptr, - toSchannelProtocolNegated(configuration.protocol()), // what protocols to disable - 0, - nullptr, + negatedSchannelProtocols(protocols), // what protocols to disable + static_cast(cryptoSettings.size()), + (cryptoSettings.isEmpty() ? nullptr : cryptoSettings.data()), 0 }; @@ -1240,6 +1550,11 @@ bool TlsCryptographSchannel::verifyHandshake() CHECK_STATUS(status); // Get session cipher info + status = QueryContextAttributes(&contextHandle, + SECPKG_ATTR_CIPHER_INFO, + &cipherInfo); + CHECK_STATUS(status); + status = QueryContextAttributes(&contextHandle, SECPKG_ATTR_CONNECTION_INFO, &connectionInfo); @@ -1393,6 +1708,7 @@ void TlsCryptographSchannel::reset() deallocateContext(); freeCredentialsHandle(); // in case we already had one (@future: session resumption requires re-use) + cipherInfo = {}; connectionInfo = {}; streamSizes = {}; @@ -1767,8 +2083,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 diff --git a/src/plugins/tls/schannel/qtls_schannel_p.h b/src/plugins/tls/schannel/qtls_schannel_p.h index f6bfdbc7cf..fab8777249 100644 --- a/src/plugins/tls/schannel/qtls_schannel_p.h +++ b/src/plugins/tls/schannel/qtls_schannel_p.h @@ -93,6 +93,7 @@ private: QSslSocket *q = nullptr; QSslSocketPrivate *d = nullptr; + SecPkgContext_CipherInfo cipherInfo = {}; SecPkgContext_ConnectionInfo connectionInfo = {}; SecPkgContext_StreamSizes streamSizes = {}; -- cgit v1.2.3