Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qtls_schannel.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4// #define QSSLSOCKET_DEBUG
5
8#include "qx509_schannel_p.h"
9#include "qtls_schannel_p.h"
10
11#include <QtNetwork/private/qsslcertificate_p.h>
12#include <QtNetwork/private/qsslcipher_p.h>
13#include <QtNetwork/private/qssl_p.h>
14
15#include <QtNetwork/qsslcertificate.h>
16#include <QtNetwork/qsslcertificateextension.h>
17#include <QtNetwork/qsslsocket.h>
18
19#include <QtCore/qscopeguard.h>
20#include <QtCore/qoperatingsystemversion.h>
21#include <QtCore/qregularexpression.h>
22#include <QtCore/qdatastream.h>
23#include <QtCore/qmutex.h>
24
25#define SECURITY_WIN32
26#include <security.h>
27#include <schnlsp.h>
28
29#if NTDDI_VERSION >= NTDDI_WINBLUE && !defined(Q_CC_MINGW)
30// ALPN = Application Layer Protocol Negotiation
31#define SUPPORTS_ALPN 1
32#endif
33
34// Not defined in MinGW
35#ifndef SECBUFFER_ALERT
36#define SECBUFFER_ALERT 17
37#endif
38#ifndef SECPKG_ATTR_APPLICATION_PROTOCOL
39#define SECPKG_ATTR_APPLICATION_PROTOCOL 35
40#endif
41
42// Another missing MinGW define
43#ifndef SEC_E_APPLICATION_PROTOCOL_MISMATCH
44#define SEC_E_APPLICATION_PROTOCOL_MISMATCH _HRESULT_TYPEDEF_(0x80090367L)
45#endif
46
47// Also not defined in MinGW.......
48#ifndef SP_PROT_TLS1_SERVER
49#define SP_PROT_TLS1_SERVER 0x00000040
50#endif
51#ifndef SP_PROT_TLS1_CLIENT
52#define SP_PROT_TLS1_CLIENT 0x00000080
53#endif
54#ifndef SP_PROT_TLS1_0_SERVER
55#define SP_PROT_TLS1_0_SERVER SP_PROT_TLS1_SERVER
56#endif
57#ifndef SP_PROT_TLS1_0_CLIENT
58#define SP_PROT_TLS1_0_CLIENT SP_PROT_TLS1_CLIENT
59#endif
60#ifndef SP_PROT_TLS1_0
61#define SP_PROT_TLS1_0 (SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_0_SERVER)
62#endif
63#ifndef SP_PROT_TLS1_1_SERVER
64#define SP_PROT_TLS1_1_SERVER 0x00000100
65#endif
66#ifndef SP_PROT_TLS1_1_CLIENT
67#define SP_PROT_TLS1_1_CLIENT 0x00000200
68#endif
69#ifndef SP_PROT_TLS1_1
70#define SP_PROT_TLS1_1 (SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_1_SERVER)
71#endif
72#ifndef SP_PROT_TLS1_2_SERVER
73#define SP_PROT_TLS1_2_SERVER 0x00000400
74#endif
75#ifndef SP_PROT_TLS1_2_CLIENT
76#define SP_PROT_TLS1_2_CLIENT 0x00000800
77#endif
78#ifndef SP_PROT_TLS1_2
79#define SP_PROT_TLS1_2 (SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_2_SERVER)
80#endif
81#ifndef SP_PROT_TLS1_3_SERVER
82#define SP_PROT_TLS1_3_SERVER 0x00001000
83#endif
84#ifndef SP_PROT_TLS1_3_CLIENT
85#define SP_PROT_TLS1_3_CLIENT 0x00002000
86#endif
87#ifndef SP_PROT_TLS1_3
88#define SP_PROT_TLS1_3 (SP_PROT_TLS1_3_CLIENT | SP_PROT_TLS1_3_SERVER)
89#endif
90#ifndef BCRYPT_ECDH_ALGORITHM
91#define BCRYPT_ECDH_ALGORITHM L"ECDH"
92#endif
93#ifndef BCRYPT_ECDSA_ALGORITHM
94#define BCRYPT_ECDSA_ALGORITHM L"ECDSA"
95#endif
96
97/*
98 @future!:
99
100 - Transmitting intermediate certificates
101 - Look for a way to avoid putting intermediate certificates in the certificate store
102 - No documentation on how to send the chain
103 - A stackoverflow question on this from 3 years ago implies schannel only sends intermediate
104 certificates if it's "in the system or user certificate store".
105 - https://stackoverflow.com/q/30156584/2493610
106 - This can be done by users, but we shouldn't add any and all local intermediate
107 certs to the stores automatically.
108 - PSK support
109 - Was added in Windows 10 (it seems), documentation at time of writing is sparse/non-existent.
110 - Specifically about how to supply credentials when they're requested.
111 - Or how to recognize that they're requested in the first place.
112 - Skip certificate verification.
113 - Check if "PSK-only" is still required to do PSK _at all_ (all-around bad solution).
114 - Check if SEC_I_INCOMPLETE_CREDENTIALS is still returned for both "missing certificate" and
115 "missing PSK" when calling InitializeSecurityContext in "performHandshake".
116
117 Low priority:
118 - Possibly make RAII wrappers for SecBuffer (which I commonly create QScopeGuards for)
119
120*/
121
123
124using namespace Qt::StringLiterals;
125
126Q_LOGGING_CATEGORY(lcTlsBackendSchannel, "qt.tlsbackend.schannel");
127
128// Defined in qsslsocket_qt.cpp.
130 const QString &passPhrase);
131
132namespace {
133bool supportsTls13();
134}
135
136namespace QTlsPrivate {
137
139
143 const char *keyExchangeMethod;
145 const char *encryptionMethod;
147 const char *hashMethod;
149};
150
151// The list of supported ciphers according to
152// https://learn.microsoft.com/en-us/windows/win32/secauthn/tls-cipher-suites-in-windows-server-2022
155std::array<SchannelCipherInfo, 44> schannelCipherInfo = {{
156 {"TLS_AES_256_GCM_SHA384", "TLS_AES_256_GCM_SHA384", "", "", "AES", 256, "SHA384", {QSsl::TlsV1_3}},
157 {"TLS_AES_128_GCM_SHA256", "TLS_AES_128_GCM_SHA256", "", "", "AES", 128, "SHA256", {QSsl::TlsV1_3}},
158 {"ECDHE-ECDSA-AES256-GCM-SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "ECDH", "ECDSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
159 {"ECDHE-ECDSA-AES128-GCM-SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "ECDH", "ECDSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
160 {"ECDHE-RSA-AES256-GCM-SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "ECDH", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
161 {"ECDHE-RSA-AES128-GCM-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "ECDH", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
162 {"DHE-RSA-AES256-GCM-SHA384", "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "DH", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
163 {"DHE-RSA-AES128-GCM-SHA256", "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "DH", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
164 {"ECDHE-ECDSA-AES256-SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "ECDH", "ECDSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
165 {"ECDHE-ECDSA-AES128-SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "ECDH", "ECDSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
166 {"ECDHE-RSA-AES256-SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "ECDH", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
167 {"ECDHE-RSA-AES128-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "ECDH", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
168 {"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}},
169 {"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}},
170 {"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}},
171 {"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}},
172 {"AES256-GCM-SHA384", "TLS_RSA_WITH_AES_256_GCM_SHA384", "RSA", "RSA", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
173 {"AES128-GCM-SHA256", "TLS_RSA_WITH_AES_128_GCM_SHA256", "RSA", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
174 {"AES256-SHA256", "TLS_RSA_WITH_AES_256_CBC_SHA256", "RSA", "RSA", "AES", 256, "SHA256", {QSsl::TlsV1_2}},
175 {"AES128-SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA256", "RSA", "RSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
176 {"AES256-SHA", "TLS_RSA_WITH_AES_256_CBC_SHA", "RSA", "RSA", "AES", 256, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
177 {"AES128-SHA", "TLS_RSA_WITH_AES_128_CBC_SHA", "RSA", "RSA", "AES", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
178 {"DES-CBC3-SHA", "TLS_RSA_WITH_3DES_EDE_CBC_SHA", "RSA", "RSA", "3DES", 168, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
179 {"NULL-SHA256", "TLS_RSA_WITH_NULL_SHA256", "RSA", "RSA", "", 0, "SHA256", {QSsl::TlsV1_2}},
180 {"NULL-SHA", "TLS_RSA_WITH_NULL_SHA", "RSA", "RSA", "", 0, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
181
182 // the following cipher suites are not enabled by default in schannel provider
183 {"TLS_CHACHA20_POLY1305_SHA256", "TLS_CHACHA20_POLY1305_SHA256", "", "", "CHACHA20_POLY1305", 0, "", {QSsl::TlsV1_3}},
184 {"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}},
185 {"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}},
186 {"DHE-DSS-AES256-SHA256", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", "DH", "DSA", "AES", 256, "SHA256", {QSsl::TlsV1_2}},
187 {"DHE-DSS-AES128-SHA256", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "DH", "DSA", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
188 {"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}},
189 {"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}},
190 {"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}},
191 {"RC4-SHA", "TLS_RSA_WITH_RC4_128_SHA", "RSA", "RSA", "RC4", 128, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
192 {"RC4-MD5", "TLS_RSA_WITH_RC4_128_MD5", "RSA", "RSA", "RC4", 128, "MD5", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
193 {"DES-CBC-SHA", "TLS_RSA_WITH_DES_CBC_SHA", "RSA", "RSA", "DES", 56, "SHA1", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
194 {"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}},
195 {"NULL-MD5", "TLS_RSA_WITH_NULL_MD5", "RSA", "RSA", "", 0, "MD5", {QSsl::TlsV1_2, QSsl::TlsV1_1, QSsl::TlsV1_0}},
196
197 // PSK cipher suites
198 {"PSK-AES256-GCM-SHA384", "TLS_PSK_WITH_AES_256_GCM_SHA384", "PSK", "", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
199 {"PSK-AES128-GCM-SHA256", "TLS_PSK_WITH_AES_128_GCM_SHA256", "PSK", "", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
200 {"PSK-AES256-CBC-SHA384", "TLS_PSK_WITH_AES_256_CBC_SHA384", "PSK", "", "AES", 256, "SHA384", {QSsl::TlsV1_2}},
201 {"PSK-AES128-CBC-SHA256", "TLS_PSK_WITH_AES_128_CBC_SHA256", "PSK", "", "AES", 128, "SHA256", {QSsl::TlsV1_2}},
202 {"PSK-NULL-SHA384", "TLS_PSK_WITH_NULL_SHA384", "PSK", "", "", 0, "SHA384", {QSsl::TlsV1_2}},
203 {"PSK-NULL-SHA256", "TLS_PSK_WITH_NULL_SHA256", "PSK", "", "", 0, "SHA256", {QSsl::TlsV1_2}},
204}};
206
208{
209 for (const auto &cipherInfo : schannelCipherInfo) {
210 if (name == QLatin1StringView(cipherInfo.openSslCipherSuite))
211 return &cipherInfo;
212 }
213
214 return nullptr;
215}
216
217UNICODE_STRING cbcChainingMode = {
218 sizeof(BCRYPT_CHAIN_MODE_CBC) - 2,
219 sizeof(BCRYPT_CHAIN_MODE_CBC),
220 const_cast<PWSTR>(BCRYPT_CHAIN_MODE_CBC)
221};
222
223UNICODE_STRING gcmChainingMode = {
224 sizeof(BCRYPT_CHAIN_MODE_GCM) - 2,
225 sizeof(BCRYPT_CHAIN_MODE_GCM),
226 const_cast<PWSTR>(BCRYPT_CHAIN_MODE_GCM)
227};
228
234{
235 static const QList<QSslCipher> defaultCipherList = defaultCiphers();
236
237 if (defaultCipherList == ciphers) {
238 // the ciphers have not been restricted for this session, so no black listing needed
239 return {};
240 }
241
243
244 for (const auto &cipher : ciphers) {
245 if (cipher.isNull())
246 continue;
247
248 const auto *info = cipherInfoByOpenSslName(cipher.name());
249 if (!cipherInfo.contains(info))
250 cipherInfo.append(info);
251 }
252
253 QList<CRYPTO_SETTINGS> cryptoSettings;
254
255 const auto assignUnicodeString = [](UNICODE_STRING &unicodeString, const wchar_t *characters) {
256 unicodeString.Length = static_cast<USHORT>(wcslen(characters) * sizeof(WCHAR));
257 unicodeString.MaximumLength = unicodeString.Length + sizeof(UNICODE_NULL);
258 unicodeString.Buffer = const_cast<wchar_t*>(characters);
259 };
260
261 // black list of key exchange algorithms
262 const auto allKeyExchangeAlgorithms = {BCRYPT_RSA_ALGORITHM,
264 BCRYPT_DH_ALGORITHM};
265
266 for (const auto &algorithm : allKeyExchangeAlgorithms) {
267 const auto method = QStringView(algorithm);
268
269 const auto usesMethod = [method](const SchannelCipherInfo *info) {
270 return QLatin1StringView(info->keyExchangeMethod) == method;
271 };
272
273 const bool exclude = std::none_of(cipherInfo.cbegin(), cipherInfo.cend(), usesMethod);
274
275 if (exclude) {
276 CRYPTO_SETTINGS settings = {};
277 settings.eAlgorithmUsage = TlsParametersCngAlgUsageKeyExchange;
278 assignUnicodeString(settings.strCngAlgId, algorithm);
279 cryptoSettings.append(settings);
280 }
281 }
282
283 // black list of authentication algorithms
284 const auto allAuthenticationAlgorithms = {BCRYPT_RSA_ALGORITHM,
285 BCRYPT_DSA_ALGORITHM,
287 BCRYPT_DH_ALGORITHM};
288
289 for (const auto &algorithm : allAuthenticationAlgorithms) {
290 const auto method = QStringView(algorithm);
291
292 const auto usesMethod = [method](const SchannelCipherInfo *info) {
293 return QLatin1StringView(info->authenticationMethod) == method;
294 };
295
296 const bool exclude = std::none_of(cipherInfo.begin(), cipherInfo.end(), usesMethod);
297
298 if (exclude) {
299 CRYPTO_SETTINGS settings = {};
300 settings.eAlgorithmUsage = TlsParametersCngAlgUsageSignature;
301 assignUnicodeString(settings.strCngAlgId, algorithm);
302 cryptoSettings.append(settings);
303 }
304 }
305
306
307 // black list of encryption algorithms
308 const auto allEncryptionAlgorithms = {BCRYPT_AES_ALGORITHM,
309 BCRYPT_RC4_ALGORITHM,
310 BCRYPT_DES_ALGORITHM,
311 BCRYPT_3DES_ALGORITHM};
312
313 for (const auto &algorithm : allEncryptionAlgorithms) {
314 const auto method = QStringView(algorithm);
315
316 if (method == QLatin1StringView("AES")) {
317 bool uses128Bit = false;
318 bool uses256Bit = false;
319 bool usesGcm = false;
320 bool usesCbc = false;
321 for (const auto *info : cipherInfo) {
322 if (QLatin1StringView(info->encryptionMethod) == method) {
323 uses128Bit = uses128Bit || (info->encryptionBits == 128);
324 uses256Bit = uses256Bit || (info->encryptionBits == 256);
325 usesGcm = usesGcm ||
326 QLatin1StringView(info->schannelCipherSuite).contains("_GCM_"_L1);
327 usesCbc = usesCbc ||
328 QLatin1StringView(info->schannelCipherSuite).contains("_CBC_"_L1);
329 }
330 }
331
332 CRYPTO_SETTINGS settings = {};
333 settings.eAlgorithmUsage = TlsParametersCngAlgUsageCipher;
334 assignUnicodeString(settings.strCngAlgId, algorithm);
335
336 if (usesGcm && !usesCbc) {
337 settings.cChainingModes = 1;
338 settings.rgstrChainingModes = &cbcChainingMode;
339 } else if (!usesGcm && usesCbc) {
340 settings.cChainingModes = 1;
341 settings.rgstrChainingModes = &gcmChainingMode;
342 }
343
344 if (!uses128Bit && uses256Bit) {
345 settings.dwMinBitLength = 256;
346 cryptoSettings.append(settings);
347 } else if (uses128Bit && !uses256Bit) {
348 settings.dwMaxBitLength = 128;
349 cryptoSettings.append(settings);
350 } else if (!uses128Bit && !uses256Bit) {
351 cryptoSettings.append(settings);
352 }
353 } else {
354 const auto usesMethod = [method](const SchannelCipherInfo *info) {
355 return QLatin1StringView(info->encryptionMethod) == method;
356 };
357
358 const bool exclude = std::none_of(cipherInfo.begin(), cipherInfo.end(), usesMethod);
359
360 if (exclude) {
361 CRYPTO_SETTINGS settings = {};
362 settings.eAlgorithmUsage = TlsParametersCngAlgUsageCipher;
363 assignUnicodeString(settings.strCngAlgId, algorithm);
364 cryptoSettings.append(settings);
365 }
366 }
367 }
368
369 // black list of hash algorithms
370 const auto allHashAlgorithms = {BCRYPT_MD5_ALGORITHM,
371 BCRYPT_SHA1_ALGORITHM,
372 BCRYPT_SHA256_ALGORITHM,
373 BCRYPT_SHA384_ALGORITHM};
374
375 for (const auto &algorithm : allHashAlgorithms) {
376 const auto method = QStringView(algorithm);
377
378 const auto usesMethod = [method](const SchannelCipherInfo *info) {
379 return QLatin1StringView(info->hashMethod) == method;
380 };
381
382 const bool exclude = std::none_of(cipherInfo.begin(), cipherInfo.end(), usesMethod);
383
384 if (exclude) {
385 CRYPTO_SETTINGS settings = {};
386 settings.eAlgorithmUsage = TlsParametersCngAlgUsageDigest;
387 assignUnicodeString(settings.strCngAlgId, algorithm);
388 cryptoSettings.append(settings);
389 }
390 }
391
392 return cryptoSettings;
393}
394
396{
397 QList<QSslCipher> ciphers;
398
399 for (const auto &cipher : schannelCipherInfo) {
400 if (QLatin1StringView(cipher.schannelCipherSuite) == schannelSuiteName) {
401 for (const auto &protocol : cipher.protocols) {
404 const QString protocolName = (
405 protocol == QSsl::TlsV1_0 ? QStringLiteral("TLSv1.0") :
406 protocol == QSsl::TlsV1_1 ? QStringLiteral("TLSv1.1") :
407 protocol == QSsl::TlsV1_2 ? QStringLiteral("TLSv1.2") :
408 protocol == QSsl::TlsV1_3 ? QStringLiteral("TLSv1.3") :
409 QString());
411
412 ciphers.append(QTlsBackend::createCiphersuite(QLatin1StringView(cipher.openSslCipherSuite),
413 QLatin1StringView(cipher.keyExchangeMethod),
414 QLatin1StringView(cipher.encryptionMethod),
415 QLatin1StringView(cipher.authenticationMethod),
416 cipher.encryptionBits,
417 protocol, protocolName));
418 }
419 }
420 }
421
422 return ciphers;
423}
424
426{
427 ULONG contextFunctionsCount = {};
428 PCRYPT_CONTEXT_FUNCTIONS contextFunctions = {};
429
430 const auto status = BCryptEnumContextFunctions(CRYPT_LOCAL, L"SSL", NCRYPT_SCHANNEL_INTERFACE,
431 &contextFunctionsCount, &contextFunctions);
432 if (!NT_SUCCESS(status)) {
433 qCWarning(lcTlsBackendSchannel, "Failed to enumerate ciphers");
434 return {};
435 }
436
437 const bool supportsV13 = supportsTls13();
438
439 QList<QSslCipher> ciphers;
440
441 for (ULONG index = 0; index < contextFunctions->cFunctions; ++index) {
442 const auto suiteName = QStringView(contextFunctions->rgpszFunctions[index]);
443
444 const QList<QSslCipher> allCiphers = ciphersByName(suiteName);
445
446 for (const auto &cipher : allCiphers) {
447 if (!supportsV13 && (cipher.protocol() == QSsl::TlsV1_3))
448 continue;
449
450 ciphers.append(cipher);
451 }
452 }
453
454 BCryptFreeBuffer(contextFunctions);
455
456 return ciphers;
457}
458
460{
461 return std::any_of(ciphers.cbegin(), ciphers.cend(),
462 [](const QSslCipher &cipher) { return cipher.protocol() == QSsl::TlsV1_3; });
463}
464
465} // namespace QTlsPrivate
466
467bool QSchannelBackend::s_loadedCiphersAndCerts = false;
468Q_GLOBAL_STATIC(QRecursiveMutex, qt_schannel_mutex)
469
470long QSchannelBackend::tlsLibraryVersionNumber() const
471{
472 const auto os = QOperatingSystemVersion::current();
473 return (os.majorVersion() << 24) | ((os.minorVersion() & 0xFF) << 16) | (os.microVersion() & 0xFFFF);
474}
475
477{
478 const auto os = QOperatingSystemVersion::current();
479 return "Secure Channel, %1 %2.%3.%4"_L1
480 .arg(os.name(),
481 QString::number(os.majorVersion()),
482 QString::number(os.minorVersion()),
483 QString::number(os.microVersion()));
484}
485
487{
488 return NTDDI_VERSION;
489}
490
492{
493 return "Secure Channel (NTDDI: 0x%1)"_L1
494 .arg(QString::number(NTDDI_VERSION, 16).toUpper());
495}
496
498{
500}
501
503{
504 const QMutexLocker<QRecursiveMutex> locker(qt_schannel_mutex);
505 if (s_loadedCiphersAndCerts)
506 return;
507 s_loadedCiphersAndCerts = true;
508
509 setDefaultCaCertificates(systemCaCertificatesImplementation());
510 // setDefaultCaCertificates sets it to false, re-enable it:
512
513 resetDefaultCiphers();
514}
515
516void QSchannelBackend::resetDefaultCiphers()
517{
518 setDefaultSupportedCiphers(QTlsPrivate::defaultCiphers());
519 setDefaultCiphers(QTlsPrivate::defaultCiphers());
520}
521
523{
525}
526
528{
529 QList<QSsl::SslProtocol> protocols;
530
531 protocols << QSsl::AnyProtocol;
532 protocols << QSsl::SecureProtocols;
535 protocols << QSsl::TlsV1_0;
536 protocols << QSsl::TlsV1_0OrLater;
537 protocols << QSsl::TlsV1_1;
538 protocols << QSsl::TlsV1_1OrLater;
540 protocols << QSsl::TlsV1_2;
541 protocols << QSsl::TlsV1_2OrLater;
542
543 if (supportsTls13()) {
544 protocols << QSsl::TlsV1_3;
545 protocols << QSsl::TlsV1_3OrLater;
546 }
547
548 return protocols;
549}
550
552{
554
555#ifdef SUPPORTS_ALPN
558#endif
559
560 return features;
561}
562
564{
566
570
571 return classes;
572}
573
575{
577}
578
580{
582}
583
585{
586 return systemCaCertificatesImplementation();
587}
588
589QTlsPrivate::TlsCryptograph *QSchannelBackend::createTlsCryptograph() const
590{
592}
593
594QList<QSslCertificate> QSchannelBackend::systemCaCertificatesImplementation()
595{
596 // Similar to non-Darwin version found in qtlsbackend_openssl.cpp,
597 // QTlsPrivate::systemCaCertificates function.
598 QList<QSslCertificate> systemCerts;
599 auto hSystemStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT"));
600 if (hSystemStore) {
601 PCCERT_CONTEXT pc = nullptr;
602 while ((pc = CertFindCertificateInStore(hSystemStore.get(), X509_ASN_ENCODING, 0,
603 CERT_FIND_ANY, nullptr, pc))) {
605 }
606 }
607 return systemCerts;
608}
609
611{
613}
614
616{
618}
619
621{
623}
624
625namespace {
626
627SecBuffer createSecBuffer(void *ptr, unsigned long length, unsigned long bufferType)
628{
629 return SecBuffer{ length, bufferType, ptr };
630}
631
632SecBuffer createSecBuffer(QByteArray &buffer, unsigned long bufferType)
633{
634 return createSecBuffer(buffer.data(), static_cast<unsigned long>(buffer.length()), bufferType);
635}
636
637QString schannelErrorToString(qint32 status)
638{
639 switch (status) {
640 case SEC_E_INSUFFICIENT_MEMORY:
641 return QSslSocket::tr("Insufficient memory");
642 case SEC_E_INTERNAL_ERROR:
643 return QSslSocket::tr("Internal error");
644 case SEC_E_INVALID_HANDLE:
645 return QSslSocket::tr("An internal handle was invalid");
646 case SEC_E_INVALID_TOKEN:
647 return QSslSocket::tr("An internal token was invalid");
648 case SEC_E_LOGON_DENIED:
649 // According to the link below we get this error when Schannel receives TLS1_ALERT_ACCESS_DENIED
650 // https://docs.microsoft.com/en-us/windows/desktop/secauthn/schannel-error-codes-for-tls-and-ssl-alerts
651 return QSslSocket::tr("Access denied");
652 case SEC_E_NO_AUTHENTICATING_AUTHORITY:
653 return QSslSocket::tr("No authority could be contacted for authorization");
654 case SEC_E_NO_CREDENTIALS:
655 return QSslSocket::tr("No credentials");
656 case SEC_E_TARGET_UNKNOWN:
657 return QSslSocket::tr("The target is unknown or unreachable");
658 case SEC_E_UNSUPPORTED_FUNCTION:
659 return QSslSocket::tr("An unsupported function was requested");
660 case SEC_E_WRONG_PRINCIPAL:
661 // SNI error
662 return QSslSocket::tr("The hostname provided does not match the one received from the peer");
664 return QSslSocket::tr("No common protocol exists between the client and the server");
665 case SEC_E_ILLEGAL_MESSAGE:
666 return QSslSocket::tr("Unexpected or badly-formatted message received");
667 case SEC_E_ENCRYPT_FAILURE:
668 return QSslSocket::tr("The data could not be encrypted");
669 case SEC_E_ALGORITHM_MISMATCH:
670 return QSslSocket::tr("No cipher suites in common");
671 case SEC_E_UNKNOWN_CREDENTIALS:
672 // This can mean "invalid argument" in some cases...
673 return QSslSocket::tr("The credentials were not recognized / Invalid argument");
674 case SEC_E_MESSAGE_ALTERED:
675 // According to the Internet it also triggers for messages that are out of order.
676 // https://microsoft.public.platformsdk.security.narkive.com/4JAvlMvD/help-please-schannel-security-contexts-and-decryptmessage
677 return QSslSocket::tr("The message was tampered with, damaged or out of sequence.");
678 case SEC_E_OUT_OF_SEQUENCE:
679 return QSslSocket::tr("A message was received out of sequence.");
680 case SEC_E_CONTEXT_EXPIRED:
681 return QSslSocket::tr("The TLS/SSL connection has been closed");
682 default:
683 return QSslSocket::tr("Unknown error occurred: %1").arg(status);
684 }
685}
686
687bool supportsTls13()
688{
689 static bool supported = []() {
690 const auto current = QOperatingSystemVersion::current();
691 // 20221 just happens to be the preview version I run on my laptop where I tested TLS 1.3.
692 const auto minimum =
694 return current >= minimum;
695 }();
696
697 return supported;
698}
699
700DWORD toSchannelProtocol(QSsl::SslProtocol protocol)
701{
702 DWORD protocols = SP_PROT_NONE;
703 switch (protocol) {
705 return DWORD(-1);
708 case QSsl::DtlsV1_0:
709 case QSsl::DtlsV1_0OrLater:
711 case QSsl::DtlsV1_2:
713 return DWORD(-1); // Not supported at the moment (@future)
716 if (supportsTls13())
717 protocols |= SP_PROT_TLS1_3;
718 break;
721 case QSsl::TlsV1_0:
722 protocols = SP_PROT_TLS1_0;
723 break;
724 case QSsl::TlsV1_1:
725 protocols = SP_PROT_TLS1_1;
726 break;
728 case QSsl::TlsV1_2:
729 protocols = SP_PROT_TLS1_2;
730 break;
731 case QSsl::TlsV1_3:
732 if (supportsTls13())
733 protocols = SP_PROT_TLS1_3;
734 else
735 protocols = DWORD(-1);
736 break;
739 case QSsl::TlsV1_0OrLater:
740 // For the "OrLater" protocols we fall through from one to the next, adding all of them
741 // in ascending order
742 protocols = SP_PROT_TLS1_0;
744 case QSsl::TlsV1_1OrLater:
745 protocols |= SP_PROT_TLS1_1;
748 case QSsl::SecureProtocols: // TLS v1.2 and later is currently considered secure
750 protocols |= SP_PROT_TLS1_2;
753 if (supportsTls13())
754 protocols |= SP_PROT_TLS1_3;
755 else if (protocol == QSsl::TlsV1_3OrLater)
756 protocols = DWORD(-1); // if TlsV1_3OrLater was specifically chosen we should fail
757 break;
758 }
759 return protocols;
760}
761
762// In the new API that descended down upon us we are not asked which protocols we want
763// but rather which protocols we don't want. So now we have this function to disable
764// anything that is not enabled.
765DWORD negatedSchannelProtocols(DWORD wantedProtocols)
766{
767 DWORD protocols = SP_PROT_ALL; // all protocols
768 protocols &= ~wantedProtocols; // minus the one(s) we want
769 return protocols;
770}
771
779QSsl::SslProtocol toQtSslProtocol(DWORD protocol)
780{
781#define MAP_PROTOCOL(sp_protocol, q_protocol) \
782 if (protocol & sp_protocol) { \
783 Q_ASSERT(!(protocol & ~sp_protocol)); \
784 return q_protocol; \
785 }
786
789 MAP_PROTOCOL(SP_PROT_TLS1_0, QSsl::TlsV1_0)
790 MAP_PROTOCOL(SP_PROT_TLS1_1, QSsl::TlsV1_1)
794#undef MAP_PROTOCOL
795 Q_UNREACHABLE();
797}
798
803bool netscapeWrongCertType(const QList<QSslCertificateExtension> &extensions, bool isClient)
804{
805 const auto netscapeIt = std::find_if(
806 extensions.cbegin(), extensions.cend(),
808 const auto netscapeCertType = QStringLiteral("2.16.840.1.113730.1.1");
809 return extension.oid() == netscapeCertType;
810 });
811 if (netscapeIt != extensions.cend()) {
812 const QByteArray netscapeCertTypeByte = netscapeIt->value().toByteArray();
813 int netscapeCertType = 0;
814 QDataStream dataStream(netscapeCertTypeByte);
815 dataStream >> netscapeCertType;
816 if (dataStream.status() != QDataStream::Status::Ok)
817 return true;
818 const int expectedPeerCertType = isClient ? NETSCAPE_SSL_SERVER_AUTH_CERT_TYPE
819 : NETSCAPE_SSL_CLIENT_AUTH_CERT_TYPE;
820 if ((netscapeCertType & expectedPeerCertType) == 0)
821 return true;
822 }
823 return false;
824}
825
833bool isCertificateAuthority(const QList<QSslCertificateExtension> &extensions)
834{
835 auto it = std::find_if(extensions.cbegin(), extensions.cend(),
837 return extension.name() == "basicConstraints"_L1;
838 });
839 if (it != extensions.cend()) {
840 QVariantMap basicConstraints = it->value().toMap();
841 return basicConstraints.value("ca"_L1, false).toBool();
842 }
843 return false;
844}
845
851bool matchesContextRequirements(DWORD attributes, DWORD requirements,
853 bool isClient)
854{
855#ifdef QSSLSOCKET_DEBUG
856#define DEBUG_WARN(message) qCWarning(lcTlsBackendSchannel, message)
857#else
858#define DEBUG_WARN(message)
859#endif
860
861#define CHECK_ATTRIBUTE(attributeName) \
862 do { \
863 const DWORD req##attributeName = isClient ? ISC_REQ_##attributeName : ASC_REQ_##attributeName; \
864 const DWORD ret##attributeName = isClient ? ISC_RET_##attributeName : ASC_RET_##attributeName; \
865 if (!(requirements & req##attributeName) != !(attributes & ret##attributeName)) { \
866 DEBUG_WARN("Missing attribute \"" #attributeName "\""); \
867 return false; \
868 } \
869 } while (false)
870
871 CHECK_ATTRIBUTE(CONFIDENTIALITY);
872 CHECK_ATTRIBUTE(REPLAY_DETECT);
873 CHECK_ATTRIBUTE(SEQUENCE_DETECT);
874 CHECK_ATTRIBUTE(STREAM);
876 CHECK_ATTRIBUTE(MUTUAL_AUTH);
877
878 // This one is manual because there is no server / ASC_ version
879 if (isClient) {
880 const auto reqManualCredValidation = ISC_REQ_MANUAL_CRED_VALIDATION;
881 const auto retManualCredValidation = ISC_RET_MANUAL_CRED_VALIDATION;
882 if (!(requirements & reqManualCredValidation) != !(attributes & retManualCredValidation)) {
883 DEBUG_WARN("Missing attribute \"MANUAL_CRED_VALIDATION\"");
884 return false;
885 }
886 }
887
888 return true;
889#undef CHECK_ATTRIBUTE
890#undef DEBUG_WARN
891}
892
893template<typename Required, typename Actual>
894Required const_reinterpret_cast(Actual *p)
895{
896 return Required(p);
897}
898
899#ifdef SUPPORTS_ALPN
900QByteArray createAlpnString(const QByteArrayList &nextAllowedProtocols)
901{
902 QByteArray alpnString;
903 if (!nextAllowedProtocols.isEmpty()) {
904 const QByteArray names = [&nextAllowedProtocols]() {
905 QByteArray protocolString;
906 for (QByteArray proto : nextAllowedProtocols) {
907 if (proto.size() > 255) {
908 qCWarning(lcTlsBackendSchannel)
909 << "TLS ALPN extension" << proto << "is too long and will be ignored.";
910 continue;
911 } else if (proto.isEmpty()) {
912 continue;
913 }
914 protocolString += char(proto.length()) + proto;
915 }
916 return protocolString;
917 }();
918 if (names.isEmpty())
919 return alpnString;
920
921 const quint16 namesSize = names.size();
922 const quint32 alpnId = SecApplicationProtocolNegotiationExt_ALPN;
923 const quint32 totalSize = sizeof(alpnId) + sizeof(namesSize) + namesSize;
924 alpnString = QByteArray::fromRawData(reinterpret_cast<const char *>(&totalSize), sizeof(totalSize))
925 + QByteArray::fromRawData(reinterpret_cast<const char *>(&alpnId), sizeof(alpnId))
926 + QByteArray::fromRawData(reinterpret_cast<const char *>(&namesSize), sizeof(namesSize))
927 + names;
928 }
929 return alpnString;
930}
931#endif // SUPPORTS_ALPN
932
933qint64 readToBuffer(QByteArray &buffer, QTcpSocket *plainSocket)
934{
935 Q_ASSERT(plainSocket);
936 static const qint64 shrinkCutoff = 1024 * 12;
937 static const qint64 defaultRead = 1024 * 16;
938 qint64 bytesRead = 0;
939
940 const auto toRead = std::min(defaultRead, plainSocket->bytesAvailable());
941 if (toRead > 0) {
942 const auto bufferSize = buffer.size();
943 buffer.reserve(bufferSize + toRead); // avoid growth strategy kicking in
944 buffer.resize(bufferSize + toRead);
945 bytesRead = plainSocket->read(buffer.data() + bufferSize, toRead);
946 buffer.resize(bufferSize + bytesRead);
947 // In case of excessive memory usage we shrink:
948 if (buffer.size() < shrinkCutoff && buffer.capacity() > defaultRead)
949 buffer.shrink_to_fit();
950 }
951
952 return bytesRead;
953}
954
955void retainExtraData(QByteArray &buffer, const SecBuffer &secBuffer)
956{
957 Q_ASSERT(secBuffer.BufferType == SECBUFFER_EXTRA);
958 if (int(secBuffer.cbBuffer) >= buffer.size())
959 return;
960
961#ifdef QSSLSOCKET_DEBUG
962 qCDebug(lcTlsBackendSchannel, "We got SECBUFFER_EXTRA, will retain %lu bytes",
963 secBuffer.cbBuffer);
964#endif
965 std::move(buffer.end() - secBuffer.cbBuffer, buffer.end(), buffer.begin());
966 buffer.resize(secBuffer.cbBuffer);
967}
968
969qint64 checkIncompleteData(const SecBuffer &secBuffer)
970{
971 if (secBuffer.BufferType == SECBUFFER_MISSING) {
972#ifdef QSSLSOCKET_DEBUG
973 qCDebug(lcTlsBackendSchannel, "Need %lu more bytes.", secBuffer.cbBuffer);
974#endif
975 return secBuffer.cbBuffer;
976}
977 return 0;
978}
979
980DWORD defaultCredsFlag()
981{
982 return qEnvironmentVariableIsSet("QT_SCH_DEFAULT_CREDS") ? 0 : SCH_CRED_NO_DEFAULT_CREDS;
983}
984} // anonymous namespace
985
986
987namespace QTlsPrivate {
988
990{
991 SecInvalidateHandle(&credentialHandle);
992 SecInvalidateHandle(&contextHandle);
994}
995
997{
998 closeCertificateStores();
999 deallocateContext();
1000 freeCredentialsHandle();
1001 CertFreeCertificateContext(localCertContext);
1002}
1003
1005{
1006 Q_ASSERT(qObj);
1007 Q_ASSERT(dObj);
1008
1009 q = qObj;
1010 d = dObj;
1011
1012 reset();
1013}
1014
1015bool TlsCryptographSchannel::sendToken(void *token, unsigned long tokenLength, bool emitError)
1016{
1017 if (tokenLength == 0)
1018 return true;
1019
1020 Q_ASSERT(d);
1021 auto *plainSocket = d->plainTcpSocket();
1022 Q_ASSERT(plainSocket);
1023
1024 const qint64 written = plainSocket->write(static_cast<const char *>(token), tokenLength);
1025 if (written != qint64(tokenLength)) {
1026 // Failed to write/buffer everything or an error occurred
1027 if (emitError)
1028 setErrorAndEmit(d, plainSocket->error(), plainSocket->errorString());
1029 return false;
1030 }
1031 return true;
1032}
1033
1034QString TlsCryptographSchannel::targetName() const
1035{
1036 // Used for SNI extension
1037 Q_ASSERT(q);
1038 Q_ASSERT(d);
1039
1040 const auto verificationPeerName = d->verificationName();
1041 return verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName;
1042}
1043
1044ULONG TlsCryptographSchannel::getContextRequirements()
1045{
1046 Q_ASSERT(d);
1047 Q_ASSERT(q);
1048
1049 const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
1050 ULONG req = 0;
1051
1052 req |= ISC_REQ_ALLOCATE_MEMORY; // Allocate memory for buffers automatically
1053 req |= ISC_REQ_CONFIDENTIALITY; // Encrypt messages
1054 req |= ISC_REQ_REPLAY_DETECT; // Detect replayed messages
1055 req |= ISC_REQ_SEQUENCE_DETECT; // Detect out of sequence messages
1056 req |= ISC_REQ_STREAM; // Support a stream-oriented connection
1057
1058 if (isClient) {
1059 req |= ISC_REQ_MANUAL_CRED_VALIDATION; // Manually validate certificate
1060 } else {
1061 switch (q->peerVerifyMode()) {
1063 // There doesn't seem to be a way to ask for an optional client cert :-(
1066 break;
1068 req |= ISC_REQ_MUTUAL_AUTH;
1069 break;
1070 }
1071 }
1072
1073 return req;
1074}
1075
1076bool TlsCryptographSchannel::acquireCredentialsHandle()
1077{
1078 Q_ASSERT(d);
1079 Q_ASSERT(q);
1080 const auto &configuration = q->sslConfiguration();
1081
1082 Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
1083
1084 const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
1085 DWORD protocols = toSchannelProtocol(configuration.protocol());
1086 if (protocols == DWORD(-1)) {
1088 QSslSocket::tr("Invalid protocol chosen"));
1089 return false;
1090 }
1091
1092 const CERT_CHAIN_CONTEXT *chainContext = nullptr;
1093 auto freeCertChain = qScopeGuard([&chainContext]() {
1094 if (chainContext)
1095 CertFreeCertificateChain(chainContext);
1096 });
1097
1098 DWORD certsCount = 0;
1099 // Set up our certificate stores before trying to use one...
1100 initializeCertificateStores();
1101
1102 // Check if user has specified a certificate chain but it could not be loaded.
1103 // This happens if there was something wrong with the certificate chain or there was no private
1104 // key.
1105 if (!configuration.localCertificateChain().isEmpty() && !localCertificateStore)
1106 return true; // 'true' because "tst_QSslSocket::setEmptyKey" expects us to not disconnect
1107
1108 if (localCertificateStore != nullptr) {
1109 CERT_CHAIN_FIND_BY_ISSUER_PARA findParam;
1110 ZeroMemory(&findParam, sizeof(findParam));
1111 findParam.cbSize = sizeof(findParam);
1112 findParam.pszUsageIdentifier = isClient ? szOID_PKIX_KP_CLIENT_AUTH : szOID_PKIX_KP_SERVER_AUTH;
1113
1114 // There should only be one chain in our store, so.. we grab that one.
1115 chainContext = CertFindChainInStore(localCertificateStore.get(),
1116 X509_ASN_ENCODING,
1117 0,
1118 CERT_CHAIN_FIND_BY_ISSUER,
1119 &findParam,
1120 nullptr);
1121 if (!chainContext) {
1122 const QString message = isClient
1123 ? QSslSocket::tr("The certificate provided cannot be used for a client.")
1124 : QSslSocket::tr("The certificate provided cannot be used for a server.");
1126 return false;
1127 }
1128 Q_ASSERT(chainContext->cChain == 1);
1129 Q_ASSERT(chainContext->rgpChain[0]);
1130 Q_ASSERT(chainContext->rgpChain[0]->cbSize >= 1);
1131 Q_ASSERT(chainContext->rgpChain[0]->rgpElement[0]);
1132 Q_ASSERT(!localCertContext);
1133 localCertContext = CertDuplicateCertificateContext(chainContext->rgpChain[0]
1134 ->rgpElement[0]
1135 ->pCertContext);
1136 certsCount = 1;
1137 Q_ASSERT(localCertContext);
1138 }
1139
1140 const QList<QSslCipher> ciphers = configuration.ciphers();
1141 if (!containsTls13Cipher(ciphers))
1142 protocols &= ~SP_PROT_TLS1_3;
1143
1144 QList<CRYPTO_SETTINGS> cryptoSettings = cryptoSettingsForCiphers(ciphers);
1145
1146 TLS_PARAMETERS tlsParameters = {
1147 0,
1148 nullptr,
1149 negatedSchannelProtocols(protocols), // what protocols to disable
1150 static_cast<DWORD>(cryptoSettings.size()),
1151 (cryptoSettings.isEmpty() ? nullptr : cryptoSettings.data()),
1152 0
1153 };
1154
1155 SCH_CREDENTIALS credentials = {
1156 SCH_CREDENTIALS_VERSION,
1157 0,
1158 certsCount,
1159 &localCertContext,
1160 nullptr,
1161 0,
1162 nullptr,
1163 0,
1164 SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT | defaultCredsFlag(),
1165 1,
1166 &tlsParameters
1167 };
1168
1169 TimeStamp expiration{};
1170 auto status = AcquireCredentialsHandle(nullptr, // pszPrincipal (unused)
1171 const_cast<wchar_t *>(UNISP_NAME), // pszPackage
1172 isClient ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND, // fCredentialUse
1173 nullptr, // pvLogonID (unused)
1174 &credentials, // pAuthData
1175 nullptr, // pGetKeyFn (unused)
1176 nullptr, // pvGetKeyArgument (unused)
1177 &credentialHandle, // phCredential
1178 &expiration // ptsExpir
1179 );
1180
1181 if (status != SEC_E_OK) {
1182 setErrorAndEmit(d, QAbstractSocket::SslInternalError, schannelErrorToString(status));
1183 return false;
1184 }
1185 return true;
1186}
1187
1188void TlsCryptographSchannel::deallocateContext()
1189{
1190 if (SecIsValidHandle(&contextHandle)) {
1191 DeleteSecurityContext(&contextHandle);
1192 SecInvalidateHandle(&contextHandle);
1193 }
1194}
1195
1196void TlsCryptographSchannel::freeCredentialsHandle()
1197{
1198 if (SecIsValidHandle(&credentialHandle)) {
1199 FreeCredentialsHandle(&credentialHandle);
1200 SecInvalidateHandle(&credentialHandle);
1201 }
1202}
1203
1204void TlsCryptographSchannel::closeCertificateStores()
1205{
1206 localCertificateStore.reset();
1207 peerCertificateStore.reset();
1208 caCertificateStore.reset();
1209}
1210
1211bool TlsCryptographSchannel::createContext()
1212{
1213 Q_ASSERT(q);
1214 Q_ASSERT(d);
1215
1216 Q_ASSERT(SecIsValidHandle(&credentialHandle));
1217 Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
1219 ULONG contextReq = getContextRequirements();
1220
1221 SecBuffer outBuffers[3];
1222 outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
1223 outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
1224 outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
1225 auto freeBuffers = qScopeGuard([&outBuffers]() {
1226 for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
1227 if (outBuffers[i].pvBuffer)
1228 FreeContextBuffer(outBuffers[i].pvBuffer);
1229 }
1230 });
1231 SecBufferDesc outputBufferDesc{
1232 SECBUFFER_VERSION,
1233 ARRAYSIZE(outBuffers),
1234 outBuffers
1235 };
1236
1237 TimeStamp expiry;
1238
1239 SecBufferDesc alpnBufferDesc;
1240 bool useAlpn = false;
1241#ifdef SUPPORTS_ALPN
1242 QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNone);
1243 QByteArray alpnString = createAlpnString(q->sslConfiguration().allowedNextProtocols());
1244 useAlpn = !alpnString.isEmpty();
1245 SecBuffer alpnBuffers[1];
1246 alpnBuffers[0] = createSecBuffer(alpnString, SECBUFFER_APPLICATION_PROTOCOLS);
1247 alpnBufferDesc = {
1248 SECBUFFER_VERSION,
1249 ARRAYSIZE(alpnBuffers),
1250 alpnBuffers
1251 };
1252#endif
1253
1254 auto status = InitializeSecurityContext(&credentialHandle, // phCredential
1255 nullptr, // phContext
1256 const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
1257 contextReq, // fContextReq
1258 0, // Reserved1
1259 0, // TargetDataRep (unused)
1260 useAlpn ? &alpnBufferDesc : nullptr, // pInput
1261 0, // Reserved2
1262 &contextHandle, // phNewContext
1263 &outputBufferDesc, // pOutput
1264 &contextAttributes, // pfContextAttr
1265 &expiry // ptsExpiry
1266 );
1267
1268 // This is the first call to InitializeSecurityContext, so theoretically "CONTINUE_NEEDED"
1269 // should be the only non-error return-code here.
1270 if (status != SEC_I_CONTINUE_NEEDED) {
1271 setErrorAndEmit(d, QAbstractSocket::SslInternalError,
1272 QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status)));
1273 return false;
1274 }
1275
1276 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
1277 return false;
1278 schannelState = SchannelState::PerformHandshake;
1279 return true;
1280}
1281
1282bool TlsCryptographSchannel::acceptContext()
1283{
1284 Q_ASSERT(d);
1285 Q_ASSERT(q);
1286
1287 auto *plainSocket = d->plainTcpSocket();
1288 Q_ASSERT(plainSocket);
1289
1290 Q_ASSERT(SecIsValidHandle(&credentialHandle));
1291 Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
1293 ULONG contextReq = getContextRequirements();
1294
1295 if (missingData > plainSocket->bytesAvailable())
1296 return true;
1297
1298 missingData = 0;
1299 readToBuffer(intermediateBuffer, plainSocket);
1300 if (intermediateBuffer.isEmpty())
1301 return true; // definitely need more data..
1302
1303 SecBuffer inBuffers[2];
1304 inBuffers[0] = createSecBuffer(intermediateBuffer, SECBUFFER_TOKEN);
1305
1306#ifdef SUPPORTS_ALPN
1307 QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNone);
1308 // The string must be alive when we call AcceptSecurityContext
1309 QByteArray alpnString = createAlpnString(q->sslConfiguration().allowedNextProtocols());
1310 if (!alpnString.isEmpty()) {
1311 inBuffers[1] = createSecBuffer(alpnString, SECBUFFER_APPLICATION_PROTOCOLS);
1312 } else
1313#endif
1314 {
1315 inBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
1316 }
1317
1318 SecBufferDesc inputBufferDesc{
1319 SECBUFFER_VERSION,
1320 ARRAYSIZE(inBuffers),
1321 inBuffers
1322 };
1323
1324 SecBuffer outBuffers[3];
1325 outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
1326 outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
1327 outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
1328 auto freeBuffers = qScopeGuard([&outBuffers]() {
1329 for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
1330 if (outBuffers[i].pvBuffer)
1331 FreeContextBuffer(outBuffers[i].pvBuffer);
1332 }
1333 });
1334 SecBufferDesc outputBufferDesc{
1335 SECBUFFER_VERSION,
1336 ARRAYSIZE(outBuffers),
1337 outBuffers
1338 };
1339
1340 TimeStamp expiry;
1341 auto status = AcceptSecurityContext(
1342 &credentialHandle, // phCredential
1343 nullptr, // phContext
1344 &inputBufferDesc, // pInput
1345 contextReq, // fContextReq
1346 0, // TargetDataRep (unused)
1347 &contextHandle, // phNewContext
1348 &outputBufferDesc, // pOutput
1349 &contextAttributes, // pfContextAttr
1350 &expiry // ptsTimeStamp
1351 );
1352
1353 if (status == SEC_E_INCOMPLETE_MESSAGE) {
1354 // Need more data
1355 missingData = checkIncompleteData(outBuffers[0]);
1356 return true;
1357 }
1358
1359 if (inBuffers[1].BufferType == SECBUFFER_EXTRA) {
1360 // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
1361 // inBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need to
1362 // be stored.
1363 retainExtraData(intermediateBuffer, inBuffers[1]);
1364 } else { /* No 'extra' data, message not incomplete */
1365 intermediateBuffer.resize(0);
1366 }
1367
1368 if (status != SEC_I_CONTINUE_NEEDED) {
1370 QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status)));
1371 return false;
1372 }
1373 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
1374 return false;
1375 schannelState = SchannelState::PerformHandshake;
1376 return true;
1377}
1378
1379bool TlsCryptographSchannel::performHandshake()
1380{
1381 Q_ASSERT(d);
1382 auto *plainSocket = d->plainTcpSocket();
1383 Q_ASSERT(plainSocket);
1384
1385 if (plainSocket->state() == QAbstractSocket::UnconnectedState) {
1386 setErrorAndEmit(d, QAbstractSocket::RemoteHostClosedError,
1387 QSslSocket::tr("The TLS/SSL connection has been closed"));
1388 return false;
1389 }
1390 Q_ASSERT(SecIsValidHandle(&credentialHandle));
1391 Q_ASSERT(SecIsValidHandle(&contextHandle));
1392 Q_ASSERT(schannelState == SchannelState::PerformHandshake);
1393
1394#ifdef QSSLSOCKET_DEBUG
1395 qCDebug(lcTlsBackendSchannel, "Bytes available from socket: %lld",
1396 plainSocket->bytesAvailable());
1397 qCDebug(lcTlsBackendSchannel, "intermediateBuffer size: %d", intermediateBuffer.size());
1398#endif
1399
1400 if (missingData > plainSocket->bytesAvailable())
1401 return true;
1402
1403 missingData = 0;
1404 readToBuffer(intermediateBuffer, plainSocket);
1405 if (intermediateBuffer.isEmpty())
1406 return true; // no data, will fail
1407
1408 SecBuffer outBuffers[3] = {};
1409 const auto freeOutBuffers = [&outBuffers]() {
1410 for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
1411 if (outBuffers[i].pvBuffer)
1412 FreeContextBuffer(outBuffers[i].pvBuffer);
1413 }
1414 };
1415 const auto outBuffersGuard = qScopeGuard(freeOutBuffers);
1416 // For this call to InitializeSecurityContext we may need to call it twice.
1417 // In some cases us not having a certificate isn't actually an error, but just a request.
1418 // With Schannel, to ignore this warning, we need to call InitializeSecurityContext again
1419 // when we get SEC_I_INCOMPLETE_CREDENTIALS! As far as I can tell it's not documented anywhere.
1420 // https://stackoverflow.com/a/47479968/2493610
1421 SECURITY_STATUS status;
1422 short attempts = 2;
1423 do {
1424 SecBuffer inputBuffers[2];
1425 inputBuffers[0] = createSecBuffer(intermediateBuffer, SECBUFFER_TOKEN);
1426 inputBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
1427 SecBufferDesc inputBufferDesc{
1428 SECBUFFER_VERSION,
1429 ARRAYSIZE(inputBuffers),
1430 inputBuffers
1431 };
1432
1433 freeOutBuffers(); // free buffers from any previous attempt
1434 outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
1435 outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
1436 outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
1437 SecBufferDesc outputBufferDesc{
1438 SECBUFFER_VERSION,
1439 ARRAYSIZE(outBuffers),
1440 outBuffers
1441 };
1442
1443 ULONG contextReq = getContextRequirements();
1444 TimeStamp expiry;
1445 status = InitializeSecurityContext(
1446 &credentialHandle, // phCredential
1447 &contextHandle, // phContext
1448 const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
1449 contextReq, // fContextReq
1450 0, // Reserved1
1451 0, // TargetDataRep (unused)
1452 &inputBufferDesc, // pInput
1453 0, // Reserved2
1454 nullptr, // phNewContext (we already have one)
1455 &outputBufferDesc, // pOutput
1456 &contextAttributes, // pfContextAttr
1457 &expiry // ptsExpiry
1458 );
1459
1460 if (inputBuffers[1].BufferType == SECBUFFER_EXTRA) {
1461 // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
1462 // inputBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need
1463 // to be stored.
1464 retainExtraData(intermediateBuffer, inputBuffers[1]);
1465 } else if (status != SEC_E_INCOMPLETE_MESSAGE) {
1466 // Clear the buffer if we weren't asked for more data
1467 intermediateBuffer.resize(0);
1468 }
1469
1470 --attempts;
1471 } while (status == SEC_I_INCOMPLETE_CREDENTIALS && attempts > 0);
1472
1473 switch (status) {
1474 case SEC_E_OK:
1475 // Need to transmit a final token in the handshake if 'cbBuffer' is non-zero.
1476 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
1477 return false;
1478 schannelState = SchannelState::VerifyHandshake;
1479 return true;
1480 case SEC_I_CONTINUE_NEEDED:
1481 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
1482 return false;
1483 // Must call InitializeSecurityContext again later (done through continueHandshake)
1484 return true;
1485 case SEC_I_INCOMPLETE_CREDENTIALS:
1486 // Schannel takes care of picking certificate to send (other than the one we can specify),
1487 // so if we get here then that means we don't have a certificate the server accepts.
1489 QSslSocket::tr("Server did not accept any certificate we could present."));
1490 return false;
1491 case SEC_I_CONTEXT_EXPIRED:
1492 // "The message sender has finished using the connection and has initiated a shutdown."
1493 if (outBuffers[0].BufferType == SECBUFFER_TOKEN) {
1494 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
1495 return false;
1496 }
1497 if (!shutdown) { // we did not initiate this
1498 setErrorAndEmit(d, QAbstractSocket::RemoteHostClosedError,
1499 QSslSocket::tr("The TLS/SSL connection has been closed"));
1500 }
1501 return true;
1502 case SEC_E_INCOMPLETE_MESSAGE:
1503 // Simply incomplete, wait for more data
1504 missingData = checkIncompleteData(outBuffers[0]);
1505 return true;
1506 case SEC_E_ALGORITHM_MISMATCH:
1508 QSslSocket::tr("Algorithm mismatch"));
1509 shutdown = true; // skip sending the "Shutdown" alert
1510 return false;
1511 }
1512
1513 // Note: We can get here if the connection is using TLS 1.2 and the server certificate uses
1514 // MD5, which is not allowed in Schannel. This causes an "invalid token" error during handshake.
1515 // (If you came here investigating an error: md5 is insecure, update your certificate)
1517 QSslSocket::tr("Handshake failed: %1").arg(schannelErrorToString(status)));
1518 return false;
1519}
1520
1521bool TlsCryptographSchannel::verifyHandshake()
1522{
1523 Q_ASSERT(d);
1524 Q_ASSERT(q);
1525 const auto &configuration = q->sslConfiguration();
1526
1527 sslErrors.clear();
1528
1529 const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
1530#define CHECK_STATUS(status) \
1531 if (status != SEC_E_OK) { \
1532 setErrorAndEmit(d, QAbstractSocket::SslInternalError, \
1533 QSslSocket::tr("Failed to query the TLS context: %1") \
1534 .arg(schannelErrorToString(status))); \
1535 return false; \
1536 }
1537
1538 // Everything is set up, now make sure there's nothing wrong and query some attributes...
1539 if (!matchesContextRequirements(contextAttributes, getContextRequirements(),
1540 configuration.peerVerifyMode(), isClient)) {
1542 QSslSocket::tr("Did not get the required attributes for the connection."));
1543 return false;
1544 }
1545
1546 // Get stream sizes (to know the max size of a message and the size of the header and trailer)
1547 auto status = QueryContextAttributes(&contextHandle,
1548 SECPKG_ATTR_STREAM_SIZES,
1549 &streamSizes);
1550 CHECK_STATUS(status);
1551
1552 // Get session cipher info
1553 status = QueryContextAttributes(&contextHandle,
1554 SECPKG_ATTR_CIPHER_INFO,
1555 &cipherInfo);
1556 CHECK_STATUS(status);
1557
1558 status = QueryContextAttributes(&contextHandle,
1559 SECPKG_ATTR_CONNECTION_INFO,
1560 &connectionInfo);
1561 CHECK_STATUS(status);
1562
1563#ifdef SUPPORTS_ALPN
1564 const auto allowedProtos = configuration.allowedNextProtocols();
1565 if (!allowedProtos.isEmpty()) {
1566 SecPkgContext_ApplicationProtocol alpn;
1567 status = QueryContextAttributes(&contextHandle,
1569 &alpn);
1570 CHECK_STATUS(status);
1571 if (alpn.ProtoNegoStatus == SecApplicationProtocolNegotiationStatus_Success) {
1572 QByteArray negotiatedProto = QByteArray((const char *)alpn.ProtocolId,
1573 alpn.ProtocolIdSize);
1574 if (!allowedProtos.contains(negotiatedProto)) {
1576 QSslSocket::tr("Unwanted protocol was negotiated"));
1577 return false;
1578 }
1579 QTlsBackend::setNegotiatedProtocol(d, negotiatedProto);
1580 QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNegotiated);
1581 } else {
1582 QTlsBackend::setNegotiatedProtocol(d, {});
1583 QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationUnsupported);
1584 }
1585 }
1586#endif // supports ALPN
1587
1588#undef CHECK_STATUS
1589
1590 // Verify certificate
1591 CERT_CONTEXT *certificateContext = nullptr;
1592 auto freeCertificate = qScopeGuard([&certificateContext]() {
1593 if (certificateContext)
1594 CertFreeCertificateContext(certificateContext);
1595 });
1596 status = QueryContextAttributes(&contextHandle,
1597 SECPKG_ATTR_REMOTE_CERT_CONTEXT,
1598 &certificateContext);
1599
1600 // QueryPeer can (currently) not work in Schannel since Schannel itself doesn't have a way to
1601 // ask for a certificate and then still be OK if it's not received.
1602 // To work around this we don't request a certificate at all for QueryPeer.
1603 // For servers AutoVerifyPeer is supposed to be treated the same as QueryPeer.
1604 // This means that servers using Schannel will only request client certificate for "VerifyPeer".
1605 if ((!isClient && configuration.peerVerifyMode() == QSslSocket::PeerVerifyMode::VerifyPeer)
1606 || (isClient && configuration.peerVerifyMode() != QSslSocket::PeerVerifyMode::VerifyNone
1607 && configuration.peerVerifyMode() != QSslSocket::PeerVerifyMode::QueryPeer)) {
1608 if (status != SEC_E_OK) {
1609#ifdef QSSLSOCKET_DEBUG
1610 qCDebug(lcTlsBackendSchannel) << "Couldn't retrieve peer certificate, status:"
1611 << schannelErrorToString(status);
1612#endif
1614 sslErrors += error;
1615 emit q->peerVerifyError(error);
1616 if (q->state() != QAbstractSocket::ConnectedState)
1617 return false;
1618 }
1619 }
1620
1621 // verifyCertContext returns false if the user disconnected while it was checking errors.
1622 if (certificateContext && !verifyCertContext(certificateContext))
1623 return false;
1624
1625 if (!checkSslErrors() || q->state() != QAbstractSocket::ConnectedState) {
1626#ifdef QSSLSOCKET_DEBUG
1627 qCDebug(lcTlsBackendSchannel) << __func__ << "was unsuccessful. Paused:" << d->isPaused();
1628#endif
1629 // If we're paused then checkSslErrors returned false, but it's not an error
1630 return d->isPaused() && q->state() == QAbstractSocket::ConnectedState;
1631 }
1632
1633 schannelState = SchannelState::Done;
1634 return true;
1635}
1636
1637bool TlsCryptographSchannel::renegotiate()
1638{
1639 Q_ASSERT(d);
1640
1641 SecBuffer outBuffers[3];
1642 outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
1643 outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
1644 outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
1645 auto freeBuffers = qScopeGuard([&outBuffers]() {
1646 for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
1647 if (outBuffers[i].pvBuffer)
1648 FreeContextBuffer(outBuffers[i].pvBuffer);
1649 }
1650 });
1651 SecBufferDesc outputBufferDesc{
1652 SECBUFFER_VERSION,
1653 ARRAYSIZE(outBuffers),
1654 outBuffers
1655 };
1656
1657 ULONG contextReq = getContextRequirements();
1658 TimeStamp expiry;
1659 SECURITY_STATUS status;
1660 if (d->tlsMode() == QSslSocket::SslClientMode) {
1661 status = InitializeSecurityContext(&credentialHandle, // phCredential
1662 &contextHandle, // phContext
1663 const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
1664 contextReq, // fContextReq
1665 0, // Reserved1
1666 0, // TargetDataRep (unused)
1667 nullptr, // pInput (nullptr for renegotiate)
1668 0, // Reserved2
1669 nullptr, // phNewContext (we already have one)
1670 &outputBufferDesc, // pOutput
1671 &contextAttributes, // pfContextAttr
1672 &expiry // ptsExpiry
1673 );
1674 } else {
1675 status = AcceptSecurityContext(
1676 &credentialHandle, // phCredential
1677 &contextHandle, // phContext
1678 nullptr, // pInput
1679 contextReq, // fContextReq
1680 0, // TargetDataRep (unused)
1681 nullptr, // phNewContext
1682 &outputBufferDesc, // pOutput
1683 &contextAttributes, // pfContextAttr,
1684 &expiry // ptsTimeStamp
1685 );
1686 }
1687 if (status == SEC_I_CONTINUE_NEEDED) {
1688 schannelState = SchannelState::PerformHandshake;
1689 return sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer);
1690 } else if (status == SEC_E_OK) {
1691 schannelState = SchannelState::PerformHandshake;
1692 return true;
1693 }
1695 QSslSocket::tr("Renegotiation was unsuccessful: %1").arg(schannelErrorToString(status)));
1696 return false;
1697}
1698
1703void TlsCryptographSchannel::reset()
1704{
1705 Q_ASSERT(d);
1706
1707 closeCertificateStores(); // certificate stores could've changed
1708 deallocateContext();
1709 freeCredentialsHandle(); // in case we already had one (@future: session resumption requires re-use)
1710
1711 cipherInfo = {};
1712 connectionInfo = {};
1713 streamSizes = {};
1714
1715 CertFreeCertificateContext(localCertContext);
1716 localCertContext = nullptr;
1717
1718 contextAttributes = 0;
1719 intermediateBuffer.clear();
1720 schannelState = SchannelState::InitializeHandshake;
1721
1722
1723 d->setEncrypted(false);
1724 shutdown = false;
1725 renegotiating = false;
1726
1727 missingData = 0;
1728}
1729
1731{
1732 Q_ASSERT(q);
1733
1734 if (q->isEncrypted())
1735 return; // let's not mess up the connection...
1736 reset();
1738}
1739
1741{
1742 Q_ASSERT(q);
1743
1744 if (q->isEncrypted())
1745 return; // let's not mess up the connection...
1746 reset();
1748}
1749
1751{
1752 Q_ASSERT(q);
1753 Q_ASSERT(d);
1754 auto *plainSocket = d->plainTcpSocket();
1755 Q_ASSERT(plainSocket);
1756
1758 return; // This function should not have been called
1759
1760 // Can happen if called through QSslSocket::abort->QSslSocket::close->QSslSocket::flush->here
1762 return;
1763
1764 if (schannelState != SchannelState::Done) {
1766 return;
1767 }
1768
1769 auto &writeBuffer = d->tlsWriteBuffer();
1770 auto &buffer = d->tlsBuffer();
1771 if (q->isEncrypted()) { // encrypt data in writeBuffer and write it to plainSocket
1772 qint64 totalBytesWritten = 0;
1773 qint64 writeBufferSize;
1774 while ((writeBufferSize = writeBuffer.size()) > 0) {
1775 const int headerSize = int(streamSizes.cbHeader);
1776 const int trailerSize = int(streamSizes.cbTrailer);
1777 // Try to read 'cbMaximumMessage' bytes from buffer before encrypting.
1778 const int size = int(std::min(writeBufferSize, qint64(streamSizes.cbMaximumMessage)));
1779 QByteArray fullMessage(headerSize + trailerSize + size, Qt::Uninitialized);
1780 {
1781 // Use peek() here instead of read() so we don't lose data if encryption fails.
1782 qint64 copied = writeBuffer.peek(fullMessage.data() + headerSize, size);
1783 Q_ASSERT(copied == size);
1784 }
1785
1786 SecBuffer inputBuffers[4]{
1787 createSecBuffer(fullMessage.data(), headerSize, SECBUFFER_STREAM_HEADER),
1788 createSecBuffer(fullMessage.data() + headerSize, size, SECBUFFER_DATA),
1789 createSecBuffer(fullMessage.data() + headerSize + size, trailerSize, SECBUFFER_STREAM_TRAILER),
1790 createSecBuffer(nullptr, 0, SECBUFFER_EMPTY)
1791 };
1792 SecBufferDesc message{
1793 SECBUFFER_VERSION,
1794 ARRAYSIZE(inputBuffers),
1795 inputBuffers
1796 };
1797 auto status = EncryptMessage(&contextHandle, 0, &message, 0);
1798 if (status != SEC_E_OK) {
1799 setErrorAndEmit(d, QAbstractSocket::SslInternalError,
1800 QSslSocket::tr("Schannel failed to encrypt data: %1")
1801 .arg(schannelErrorToString(status)));
1802 return;
1803 }
1804 // Data was encrypted successfully, so we free() what we peek()ed earlier
1805 writeBuffer.free(size);
1806
1807 // The trailer's size is not final, so resize fullMessage to not send trailing junk
1808 fullMessage.resize(inputBuffers[0].cbBuffer + inputBuffers[1].cbBuffer + inputBuffers[2].cbBuffer);
1809 const qint64 bytesWritten = plainSocket->write(fullMessage);
1810#ifdef QSSLSOCKET_DEBUG
1811 qCDebug(lcTlsBackendSchannel, "Wrote %lld of total %d bytes", bytesWritten,
1812 fullMessage.length());
1813#endif
1814 if (bytesWritten >= 0) {
1815 totalBytesWritten += bytesWritten;
1816 } else {
1817 setErrorAndEmit(d, plainSocket->error(), plainSocket->errorString());
1818 return;
1819 }
1820 }
1821
1822 if (totalBytesWritten > 0) {
1823 // Don't emit bytesWritten() recursively.
1824 bool &emittedBytesWritten = d->tlsEmittedBytesWritten();
1825 if (!emittedBytesWritten) {
1826 emittedBytesWritten = true;
1827 emit q->bytesWritten(totalBytesWritten);
1828 emittedBytesWritten = false;
1829 }
1830 emit q->channelBytesWritten(0, totalBytesWritten);
1831 }
1832 }
1833
1834 int totalRead = 0;
1835 bool hadIncompleteData = false;
1836 const auto readBufferMaxSize = d->maxReadBufferSize();
1837 while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) {
1838 if (missingData > plainSocket->bytesAvailable()
1839 && (!readBufferMaxSize || readBufferMaxSize >= missingData)) {
1840#ifdef QSSLSOCKET_DEBUG
1841 qCDebug(lcTlsBackendSchannel, "We're still missing %lld bytes, will check later.",
1842 missingData);
1843#endif
1844 break;
1845 }
1846
1847 missingData = 0;
1848 const qint64 bytesRead = readToBuffer(intermediateBuffer, plainSocket);
1849#ifdef QSSLSOCKET_DEBUG
1850 qCDebug(lcTlsBackendSchannel, "Read %lld encrypted bytes from the socket", bytesRead);
1851#endif
1852 if (intermediateBuffer.length() == 0 || (hadIncompleteData && bytesRead == 0)) {
1853#ifdef QSSLSOCKET_DEBUG
1854 qCDebug(lcTlsBackendSchannel,
1855 hadIncompleteData ? "No new data received, leaving loop!"
1856 : "Nothing to decrypt, leaving loop!");
1857#endif
1858 break;
1859 }
1860 hadIncompleteData = false;
1861#ifdef QSSLSOCKET_DEBUG
1862 qCDebug(lcTlsBackendSchannel, "Total amount of bytes to decrypt: %d",
1863 intermediateBuffer.length());
1864#endif
1865
1866 SecBuffer dataBuffer[4]{
1867 createSecBuffer(intermediateBuffer, SECBUFFER_DATA),
1868 createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
1869 createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
1870 createSecBuffer(nullptr, 0, SECBUFFER_EMPTY)
1871 };
1872 SecBufferDesc message{
1873 SECBUFFER_VERSION,
1874 ARRAYSIZE(dataBuffer),
1875 dataBuffer
1876 };
1877 auto status = DecryptMessage(&contextHandle, &message, 0, nullptr);
1878 if (status == SEC_E_OK || status == SEC_I_RENEGOTIATE || status == SEC_I_CONTEXT_EXPIRED) {
1879 // There can still be 0 output even if it succeeds, this is fine
1880 if (dataBuffer[1].cbBuffer > 0) {
1881 // It is always decrypted in-place.
1882 // But [0] is the STREAM_HEADER, [1] is the DATA and [2] is the STREAM_TRAILER.
1883 // The pointers in all of those still point into 'intermediateBuffer'.
1884 buffer.append(static_cast<char *>(dataBuffer[1].pvBuffer),
1885 dataBuffer[1].cbBuffer);
1886 totalRead += dataBuffer[1].cbBuffer;
1887#ifdef QSSLSOCKET_DEBUG
1888 qCDebug(lcTlsBackendSchannel, "Decrypted %lu bytes. New read buffer size: %d",
1889 dataBuffer[1].cbBuffer, buffer.size());
1890#endif
1891 }
1892 if (dataBuffer[3].BufferType == SECBUFFER_EXTRA) {
1893 // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
1894 // dataBuffer[3].cbBuffer indicates the amount of bytes _NOT_ processed,
1895 // the rest need to be stored.
1896 retainExtraData(intermediateBuffer, dataBuffer[3]);
1897 } else {
1898 intermediateBuffer.resize(0);
1899 }
1900 }
1901
1902 if (status == SEC_E_INCOMPLETE_MESSAGE) {
1903 missingData = checkIncompleteData(dataBuffer[0]);
1904#ifdef QSSLSOCKET_DEBUG
1905 qCDebug(lcTlsBackendSchannel, "We didn't have enough data to decrypt anything, will try again!");
1906#endif
1907 // We try again, but if we don't get any more data then we leave
1908 hadIncompleteData = true;
1909 } else if (status == SEC_E_INVALID_HANDLE) {
1910 // I don't think this should happen, if it does we're done...
1911 qCWarning(lcTlsBackendSchannel, "The internal SSPI handle is invalid!");
1912 Q_UNREACHABLE();
1913 } else if (status == SEC_E_INVALID_TOKEN) {
1914 qCWarning(lcTlsBackendSchannel, "Got SEC_E_INVALID_TOKEN!");
1915 Q_UNREACHABLE(); // Happened once due to a bug, but shouldn't generally happen(?)
1916 } else if (status == SEC_E_MESSAGE_ALTERED) {
1917 // The message has been altered, disconnect now.
1918 shutdown = true; // skips sending the shutdown alert
1920 setErrorAndEmit(d, QAbstractSocket::SslInternalError,
1921 schannelErrorToString(status));
1922 break;
1923 } else if (status == SEC_E_OUT_OF_SEQUENCE) {
1924 // @todo: I don't know if this one is actually "fatal"..
1925 // This path might never be hit as it seems this is for connection-oriented connections,
1926 // while SEC_E_MESSAGE_ALTERED is for stream-oriented ones (what we use).
1927 shutdown = true; // skips sending the shutdown alert
1929 setErrorAndEmit(d, QAbstractSocket::SslInternalError,
1930 schannelErrorToString(status));
1931 break;
1932 } else if (status == SEC_I_CONTEXT_EXPIRED) {
1933 // 'remote' has initiated a shutdown
1935 break;
1936 } else if (status == SEC_I_RENEGOTIATE) {
1937 // 'remote' wants to renegotiate
1938#ifdef QSSLSOCKET_DEBUG
1939 qCDebug(lcTlsBackendSchannel, "The peer wants to renegotiate.");
1940#endif
1941 schannelState = SchannelState::Renegotiate;
1942 renegotiating = true;
1943
1944 // We need to call 'continueHandshake' or else there's no guarantee it ever gets called
1946 break;
1947 }
1948 }
1949
1950 if (totalRead) {
1951 if (bool *readyReadEmittedPointer = d->readyReadPointer())
1952 *readyReadEmittedPointer = true;
1953 emit q->readyRead();
1954 emit q->channelReadyRead(0);
1955 }
1956}
1957
1958void TlsCryptographSchannel::sendShutdown()
1959{
1960 Q_ASSERT(d);
1961
1962 const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
1963 DWORD shutdownToken = SCHANNEL_SHUTDOWN;
1964 SecBuffer buffer = createSecBuffer(&shutdownToken, sizeof(DWORD), SECBUFFER_TOKEN);
1965 SecBufferDesc token{
1966 SECBUFFER_VERSION,
1967 1,
1968 &buffer
1969 };
1970 auto status = ApplyControlToken(&contextHandle, &token);
1971
1972 if (status != SEC_E_OK) {
1973#ifdef QSSLSOCKET_DEBUG
1974 qCDebug(lcTlsBackendSchannel)
1975 << "Failed to apply shutdown control token:" << schannelErrorToString(status);
1976#endif
1977 return;
1978 }
1979
1980 SecBuffer outBuffers[3];
1981 outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
1982 outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
1983 outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
1984 auto freeBuffers = qScopeGuard([&outBuffers]() {
1985 for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
1986 if (outBuffers[i].pvBuffer)
1987 FreeContextBuffer(outBuffers[i].pvBuffer);
1988 }
1989 });
1990 SecBufferDesc outputBufferDesc{
1991 SECBUFFER_VERSION,
1992 ARRAYSIZE(outBuffers),
1993 outBuffers
1994 };
1995
1996 ULONG contextReq = getContextRequirements();
1997 TimeStamp expiry;
1998 if (isClient) {
1999 status = InitializeSecurityContext(&credentialHandle, // phCredential
2000 &contextHandle, // phContext
2001 const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
2002 contextReq, // fContextReq
2003 0, // Reserved1
2004 0, // TargetDataRep (unused)
2005 nullptr, // pInput
2006 0, // Reserved2
2007 nullptr, // phNewContext (we already have one)
2008 &outputBufferDesc, // pOutput
2009 &contextAttributes, // pfContextAttr
2010 &expiry // ptsExpiry
2011 );
2012 } else {
2013 status = AcceptSecurityContext(
2014 &credentialHandle, // phCredential
2015 &contextHandle, // phContext
2016 nullptr, // pInput
2017 contextReq, // fContextReq
2018 0, // TargetDataRep (unused)
2019 nullptr, // phNewContext
2020 &outputBufferDesc, // pOutput
2021 &contextAttributes, // pfContextAttr,
2022 &expiry // ptsTimeStamp
2023 );
2024 }
2025 if (status == SEC_E_OK || status == SEC_I_CONTEXT_EXPIRED) {
2026 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, false)) {
2027 // We failed to send the shutdown message, but it's not that important since we're
2028 // shutting down anyway.
2029 return;
2030 }
2031 } else {
2032#ifdef QSSLSOCKET_DEBUG
2033 qCDebug(lcTlsBackendSchannel)
2034 << "Failed to initialize shutdown:" << schannelErrorToString(status);
2035#endif
2036 }
2037}
2038
2040{
2041 Q_ASSERT(q);
2042 Q_ASSERT(d);
2043 auto *plainSocket = d->plainTcpSocket();
2044 Q_ASSERT(plainSocket);
2045
2046 if (SecIsValidHandle(&contextHandle)) {
2047 if (!shutdown) {
2048 shutdown = true;
2049 if (plainSocket->state() != QAbstractSocket::UnconnectedState && q->isEncrypted()) {
2050 sendShutdown();
2051 transmit();
2052 }
2053 }
2054 }
2055 plainSocket->disconnectFromHost();
2056}
2057
2059{
2060 Q_ASSERT(d);
2061 auto *plainSocket = d->plainTcpSocket();
2062 Q_ASSERT(plainSocket);
2063 d->setEncrypted(false);
2064
2065 shutdown = true;
2066 if (plainSocket->bytesAvailable() > 0 || hasUndecryptedData()) {
2067 // Read as much as possible because this is likely our last chance
2068 qint64 tempMax = d->maxReadBufferSize();
2069 d->setMaxReadBufferSize(0); // Unlimited
2070 transmit();
2071 d->setMaxReadBufferSize(tempMax);
2072 // Since there were bytes still available we don't want to deallocate
2073 // our context yet. It will happen later, when the socket is re-used or
2074 // destroyed.
2075 } else {
2076 deallocateContext();
2077 freeCredentialsHandle();
2078 }
2079}
2080
2082{
2083 Q_ASSERT(q);
2084
2085 if (!q->isEncrypted())
2086 return {};
2087
2088 const auto sessionProtocol = toQtSslProtocol(connectionInfo.dwProtocol);
2089
2090 const auto ciphers = ciphersByName(QStringView(cipherInfo.szCipherSuite));
2091 for (const auto& cipher : ciphers) {
2092 if (cipher.protocol() == sessionProtocol)
2093 return cipher;
2094 }
2095
2096 return {};
2097}
2098
2100{
2101 if (!q->isEncrypted())
2103 return toQtSslProtocol(connectionInfo.dwProtocol);
2104}
2105
2107{
2108 Q_ASSERT(q);
2109 Q_ASSERT(d);
2110 auto *plainSocket = d->plainTcpSocket();
2111 Q_ASSERT(plainSocket);
2112
2113 const bool isServer = d->tlsMode() == QSslSocket::SslServerMode;
2114 switch (schannelState) {
2115 case SchannelState::InitializeHandshake:
2116 if (!SecIsValidHandle(&credentialHandle) && !acquireCredentialsHandle()) {
2118 return;
2119 }
2120 if (!SecIsValidHandle(&credentialHandle)) // Needed to support tst_QSslSocket::setEmptyKey
2121 return;
2122 if (!SecIsValidHandle(&contextHandle) && !(isServer ? acceptContext() : createContext())) {
2124 return;
2125 }
2126 if (schannelState != SchannelState::PerformHandshake)
2127 break;
2128 Q_FALLTHROUGH();
2129 case SchannelState::PerformHandshake:
2130 if (!performHandshake()) {
2132 return;
2133 }
2134 if (schannelState != SchannelState::VerifyHandshake)
2135 break;
2136 Q_FALLTHROUGH();
2137 case SchannelState::VerifyHandshake:
2138 // if we're in shutdown or renegotiating then we might not need to verify
2139 // (since we already did)
2140 if (!verifyHandshake()) {
2141 shutdown = true; // Skip sending shutdown alert
2142 q->abort(); // We don't want to send buffered data
2144 return;
2145 }
2146 if (schannelState != SchannelState::Done)
2147 break;
2148 Q_FALLTHROUGH();
2149 case SchannelState::Done:
2150 // connectionEncrypted is already true if we come here from a renegotiation
2151 if (!q->isEncrypted()) {
2152 d->setEncrypted(true); // all is done
2153 emit q->encrypted();
2154 }
2155 renegotiating = false;
2156 if (d->isPendingClose()) {
2157 d->setPendingClose(false);
2159 } else {
2160 transmit();
2161 }
2162 break;
2163 case SchannelState::Renegotiate:
2164 if (!renegotiate()) {
2166 return;
2167 } else if (intermediateBuffer.size() || plainSocket->bytesAvailable()) {
2169 }
2170 break;
2171 }
2172}
2173
2175{
2176 return sslErrors;
2177}
2178
2179/*
2180 Copied from qsslsocket_mac.cpp, which was copied from qsslsocket_openssl.cpp
2181*/
2182bool TlsCryptographSchannel::checkSslErrors()
2183{
2184 if (sslErrors.isEmpty())
2185 return true;
2186
2187 Q_ASSERT(q);
2188 Q_ASSERT(d);
2189 const auto &configuration = q->sslConfiguration();
2190 auto *plainSocket = d->plainTcpSocket();
2191
2192 emit q->sslErrors(sslErrors);
2193
2194 const bool doVerifyPeer = configuration.peerVerifyMode() == QSslSocket::VerifyPeer
2195 || (configuration.peerVerifyMode() == QSslSocket::AutoVerifyPeer
2197 const bool doEmitSslError = !d->verifyErrorsHaveBeenIgnored();
2198 // check whether we need to emit an SSL handshake error
2199 if (doVerifyPeer && doEmitSslError) {
2200 if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) {
2202 d->setPaused(true);
2203 } else {
2205 sslErrors.constFirst().errorString());
2206 plainSocket->disconnectFromHost();
2207 }
2208 return false;
2209 }
2210
2211 return true;
2212}
2213
2214void TlsCryptographSchannel::initializeCertificateStores()
2215{
2217 Q_ASSERT(d);
2218 Q_ASSERT(q);
2219 const auto &configuration = q->sslConfiguration();
2220
2221 auto createStoreFromCertificateChain = [](const QList<QSslCertificate> certChain, const QSslKey &privateKey) {
2222 const wchar_t *passphrase = L"";
2223 // Need to embed the private key in the certificate
2224 QByteArray pkcs12 = _q_makePkcs12(certChain,
2225 privateKey,
2226 QString::fromWCharArray(passphrase, 0));
2227 CRYPT_DATA_BLOB pfxBlob;
2228 pfxBlob.cbData = DWORD(pkcs12.length());
2229 pfxBlob.pbData = reinterpret_cast<unsigned char *>(pkcs12.data());
2230 return QHCertStorePointer(PFXImportCertStore(&pfxBlob, passphrase, 0));
2231 };
2232
2233 if (!configuration.localCertificateChain().isEmpty()) {
2234 if (configuration.privateKey().isNull()) {
2236 QSslSocket::tr("Cannot provide a certificate with no key"));
2237 return;
2238 }
2239 if (localCertificateStore == nullptr) {
2240 localCertificateStore = createStoreFromCertificateChain(configuration.localCertificateChain(),
2241 configuration.privateKey());
2242 if (localCertificateStore == nullptr)
2243 qCWarning(lcTlsBackendSchannel, "Failed to load certificate chain!");
2244 }
2245 }
2246
2247 if (!configuration.caCertificates().isEmpty() && !caCertificateStore) {
2248 caCertificateStore = createStoreFromCertificateChain(configuration.caCertificates(),
2249 {}); // No private key for the CA certs
2250 }
2251}
2252
2253bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext)
2254{
2255 Q_ASSERT(certContext);
2256 Q_ASSERT(q);
2257 Q_ASSERT(d);
2258
2259 const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
2260
2261 // Create a collection of stores so we can pass in multiple stores as additional locations to
2262 // search for the certificate chain
2263 auto tempCertCollection = QHCertStorePointer(CertOpenStore(CERT_STORE_PROV_COLLECTION,
2264 X509_ASN_ENCODING,
2265 0,
2266 CERT_STORE_CREATE_NEW_FLAG,
2267 nullptr));
2268 if (!tempCertCollection) {
2269#ifdef QSSLSOCKET_DEBUG
2270 qCWarning(lcTlsBackendSchannel, "Failed to create certificate store collection!");
2271#endif
2272 return false;
2273 }
2274
2275 if (rootCertOnDemandLoadingAllowed()) {
2276 // @future(maybe): following the OpenSSL backend these certificates should be added into
2277 // the Ca list, not just included during verification.
2278 // That being said, it's not trivial to add the root certificates (if and only if they
2279 // came from the system root store). And I don't see this mentioned in our documentation.
2280 auto rootStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT"));
2281 if (!rootStore) {
2282#ifdef QSSLSOCKET_DEBUG
2283 qCWarning(lcTlsBackendSchannel, "Failed to open the system root CA certificate store!");
2284#endif
2285 return false;
2286 } else if (!CertAddStoreToCollection(tempCertCollection.get(), rootStore.get(), 0, 1)) {
2287#ifdef QSSLSOCKET_DEBUG
2288 qCWarning(lcTlsBackendSchannel,
2289 "Failed to add the system root CA certificate store to the certificate store "
2290 "collection!");
2291#endif
2292 return false;
2293 }
2294 }
2295 if (caCertificateStore) {
2296 if (!CertAddStoreToCollection(tempCertCollection.get(), caCertificateStore.get(), 0, 1)) {
2297#ifdef QSSLSOCKET_DEBUG
2298 qCWarning(lcTlsBackendSchannel,
2299 "Failed to add the user's CA certificate store to the certificate store "
2300 "collection!");
2301#endif
2302 return false;
2303 }
2304 }
2305
2306 if (!CertAddStoreToCollection(tempCertCollection.get(), certContext->hCertStore, 0, 0)) {
2307#ifdef QSSLSOCKET_DEBUG
2308 qCWarning(lcTlsBackendSchannel,
2309 "Failed to add certificate's origin store to the certificate store collection!");
2310#endif
2311 return false;
2312 }
2313
2314 CERT_CHAIN_PARA parameters;
2315 ZeroMemory(&parameters, sizeof(parameters));
2316 parameters.cbSize = sizeof(CERT_CHAIN_PARA);
2317 parameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
2318 parameters.RequestedUsage.Usage.cUsageIdentifier = 1;
2319 LPSTR oid = LPSTR(isClient ? szOID_PKIX_KP_SERVER_AUTH
2320 : szOID_PKIX_KP_CLIENT_AUTH);
2321 parameters.RequestedUsage.Usage.rgpszUsageIdentifier = &oid;
2322
2323 QTlsBackend::clearPeerCertificates(d);
2324 const CERT_CHAIN_CONTEXT *chainContext = nullptr;
2325 auto freeCertChain = qScopeGuard([&chainContext]() {
2326 if (chainContext)
2327 CertFreeCertificateChain(chainContext);
2328 });
2329 BOOL status = CertGetCertificateChain(nullptr, // hChainEngine, default
2330 certContext, // pCertContext
2331 nullptr, // pTime, 'now'
2332 tempCertCollection.get(), // hAdditionalStore, additional cert store
2333 &parameters, // pChainPara
2334 CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, // dwFlags
2335 nullptr, // reserved
2336 &chainContext // ppChainContext
2337 );
2338 if (status == FALSE || !chainContext || chainContext->cChain == 0) {
2340 sslErrors += error;
2341 emit q->peerVerifyError(error);
2342 return q->state() == QAbstractSocket::ConnectedState;
2343 }
2344
2345 // Helper-function to get a QSslCertificate given a CERT_CHAIN_ELEMENT
2346 static auto getCertificateFromChainElement = [](CERT_CHAIN_ELEMENT *element) {
2347 if (!element)
2348 return QSslCertificate();
2349
2350 const CERT_CONTEXT *certContext = element->pCertContext;
2352 };
2353
2354 // Pick a chain to use as the certificate chain, if multiple are available:
2355 // According to https://docs.microsoft.com/en-gb/windows/desktop/api/wincrypt/ns-wincrypt-_cert_chain_context
2356 // this seems to be the best way to get a trusted chain.
2357 CERT_SIMPLE_CHAIN *chain = chainContext->rgpChain[chainContext->cChain - 1];
2358
2359 if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN) {
2361 getCertificateFromChainElement(chain->rgpElement[chain->cElement - 1]));
2362 sslErrors += error;
2363 emit q->peerVerifyError(error);
2364 if (q->state() != QAbstractSocket::ConnectedState)
2365 return false;
2366 }
2367 if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_INVALID_BASIC_CONSTRAINTS) {
2368 // @Note: This is actually one of two errors:
2369 // "either the certificate cannot be used to issue other certificates, or the chain path length has been exceeded."
2370 // But here we are checking the chain's status, so we assume the "issuing" error cannot occur here.
2372 sslErrors += error;
2373 emit q->peerVerifyError(error);
2374 if (q->state() != QAbstractSocket::ConnectedState)
2375 return false;
2376 }
2377 static const DWORD leftoverCertChainErrorMask = CERT_TRUST_IS_CYCLIC | CERT_TRUST_INVALID_EXTENSION
2378 | CERT_TRUST_INVALID_POLICY_CONSTRAINTS | CERT_TRUST_INVALID_NAME_CONSTRAINTS
2379 | CERT_TRUST_CTL_IS_NOT_TIME_VALID | CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID
2380 | CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE;
2381 if (chain->TrustStatus.dwErrorStatus & leftoverCertChainErrorMask) {
2383 sslErrors += error;
2384 emit q->peerVerifyError(error);
2385 if (q->state() != QAbstractSocket::ConnectedState)
2386 return false;
2387 }
2388
2389 DWORD verifyDepth = chain->cElement;
2390 if (q->peerVerifyDepth() > 0 && DWORD(q->peerVerifyDepth()) < verifyDepth)
2391 verifyDepth = DWORD(q->peerVerifyDepth());
2392
2393 const auto &caCertificates = q->sslConfiguration().caCertificates();
2394
2395 if (!rootCertOnDemandLoadingAllowed()
2396 && !(chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN)
2397 && (q->peerVerifyMode() == QSslSocket::VerifyPeer
2398 || (isClient && q->peerVerifyMode() == QSslSocket::AutoVerifyPeer))) {
2399 // When verifying a peer Windows "helpfully" builds a chain that
2400 // may include roots from the system store. But we don't want that if
2401 // the user has set their own CA certificates.
2402 // Since Windows claims this is not a partial chain the root is included
2403 // and we have to check that it is one of our configured CAs.
2404 CERT_CHAIN_ELEMENT *element = chain->rgpElement[chain->cElement - 1];
2405 QSslCertificate certificate = getCertificateFromChainElement(element);
2406 if (!caCertificates.contains(certificate)) {
2407 auto error = QSslError(QSslError::CertificateUntrusted, certificate);
2408 sslErrors += error;
2409 emit q->peerVerifyError(error);
2410 if (q->state() != QAbstractSocket::ConnectedState)
2411 return false;
2412 }
2413 }
2414
2415 QList<QSslCertificate> peerCertificateChain;
2416 for (DWORD i = 0; i < verifyDepth; i++) {
2417 CERT_CHAIN_ELEMENT *element = chain->rgpElement[i];
2418 QSslCertificate certificate = getCertificateFromChainElement(element);
2419 const QList<QSslCertificateExtension> extensions = certificate.extensions();
2420
2421#ifdef QSSLSOCKET_DEBUG
2422 qCDebug(lcTlsBackendSchannel) << "issuer:" << certificate.issuerDisplayName()
2423 << "\nsubject:" << certificate.subjectDisplayName()
2424 << "\nQSslCertificate info:" << certificate
2425 << "\nextended error info:" << element->pwszExtendedErrorInfo
2426 << "\nerror status:" << element->TrustStatus.dwErrorStatus;
2427#endif
2428
2429 peerCertificateChain.append(certificate);
2430 QTlsBackend::storePeerCertificateChain(d, peerCertificateChain);
2431
2432 if (certificate.isBlacklisted()) {
2433 const auto error = QSslError(QSslError::CertificateBlacklisted, certificate);
2434 sslErrors += error;
2435 emit q->peerVerifyError(error);
2436 if (q->state() != QAbstractSocket::ConnectedState)
2437 return false;
2438 }
2439
2440 LONG result = CertVerifyTimeValidity(nullptr /*== now */, element->pCertContext->pCertInfo);
2441 if (result != 0) {
2444 certificate);
2445 sslErrors += error;
2446 emit q->peerVerifyError(error);
2447 if (q->state() != QAbstractSocket::ConnectedState)
2448 return false;
2449 }
2450
2452 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID) {
2453 // handled right above
2454 Q_ASSERT(!sslErrors.isEmpty());
2455 }
2456 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED) {
2457 auto error = QSslError(QSslError::CertificateRevoked, certificate);
2458 sslErrors += error;
2459 emit q->peerVerifyError(error);
2460 if (q->state() != QAbstractSocket::ConnectedState)
2461 return false;
2462 }
2463 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID) {
2465 sslErrors += error;
2466 emit q->peerVerifyError(error);
2467 if (q->state() != QAbstractSocket::ConnectedState)
2468 return false;
2469 }
2470
2471 // While netscape shouldn't be relevant now it defined an extension which is
2472 // still in use. Schannel does not check this automatically, so we do it here.
2473 // It is used to differentiate between client and server certificates.
2474 if (netscapeWrongCertType(extensions, isClient))
2475 element->TrustStatus.dwErrorStatus |= CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
2476
2477 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_VALID_FOR_USAGE) {
2478 auto error = QSslError(QSslError::InvalidPurpose, certificate);
2479 sslErrors += error;
2480 emit q->peerVerifyError(error);
2481 if (q->state() != QAbstractSocket::ConnectedState)
2482 return false;
2483 }
2484 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_UNTRUSTED_ROOT) {
2485 // Override this error if we have the certificate inside our trusted CAs list.
2486 const bool isTrustedRoot = caCertificates.contains(certificate);
2487 if (!isTrustedRoot) {
2488 auto error = QSslError(QSslError::CertificateUntrusted, certificate);
2489 sslErrors += error;
2490 emit q->peerVerifyError(error);
2491 if (q->state() != QAbstractSocket::ConnectedState)
2492 return false;
2493 }
2494 }
2495 static const DWORD certRevocationCheckUnavailableError = CERT_TRUST_IS_OFFLINE_REVOCATION
2496 | CERT_TRUST_REVOCATION_STATUS_UNKNOWN;
2497 if (element->TrustStatus.dwErrorStatus & certRevocationCheckUnavailableError) {
2498 // @future(maybe): Do something with this
2499 }
2500
2501 // Dumping ground of errors that don't fit our specific errors
2502 static const DWORD leftoverCertErrorMask = CERT_TRUST_IS_CYCLIC
2503 | CERT_TRUST_INVALID_EXTENSION | CERT_TRUST_INVALID_NAME_CONSTRAINTS
2504 | CERT_TRUST_INVALID_POLICY_CONSTRAINTS
2505 | CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT
2506 | CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT
2507 | CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT
2508 | CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT
2509 | CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT;
2510 if (element->TrustStatus.dwErrorStatus & leftoverCertErrorMask) {
2511 auto error = QSslError(QSslError::UnspecifiedError, certificate);
2512 sslErrors += error;
2513 emit q->peerVerifyError(error);
2514 if (q->state() != QAbstractSocket::ConnectedState)
2515 return false;
2516 }
2517 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_INVALID_BASIC_CONSTRAINTS) {
2518 auto it = std::find_if(extensions.cbegin(), extensions.cend(),
2520 return extension.name() == "basicConstraints"_L1;
2521 });
2522 if (it != extensions.cend()) {
2523 // @Note: This is actually one of two errors:
2524 // "either the certificate cannot be used to issue other certificates,
2525 // or the chain path length has been exceeded."
2526 QVariantMap basicConstraints = it->value().toMap();
2528 if (i > 0 && !basicConstraints.value("ca"_L1, false).toBool())
2530 else
2532 sslErrors += error;
2533 emit q->peerVerifyError(error);
2534 if (q->state() != QAbstractSocket::ConnectedState)
2535 return false;
2536 }
2537 }
2538 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_EXPLICIT_DISTRUST) {
2540 sslErrors += error;
2541 emit q->peerVerifyError(error);
2542 if (q->state() != QAbstractSocket::ConnectedState)
2543 return false;
2544 }
2545
2546 if (element->TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED) {
2547 // If it's self-signed *and* a CA then we can assume it's a root CA certificate
2548 // and we can ignore the "self-signed" note:
2549 // We check the basicConstraints certificate extension when possible, but this didn't
2550 // exist for version 1, so we can only guess in that case
2551 const bool isRootCertificateAuthority = isCertificateAuthority(extensions)
2552 || certificate.version() == "1";
2553
2554 // Root certificate tends to be signed by themselves, so ignore self-signed status.
2555 if (!isRootCertificateAuthority) {
2557 sslErrors += error;
2558 emit q->peerVerifyError(error);
2559 if (q->state() != QAbstractSocket::ConnectedState)
2560 return false;
2561 }
2562 }
2563 }
2564
2565 if (!peerCertificateChain.isEmpty())
2566 QTlsBackend::storePeerCertificate(d, peerCertificateChain.first());
2567
2568 const auto &configuration = q->sslConfiguration(); // Probably, updated by QTlsBackend::storePeerCertificate etc.
2569 // @Note: Somewhat copied from qsslsocket_mac.cpp
2570 const bool doVerifyPeer = q->peerVerifyMode() == QSslSocket::VerifyPeer
2571 || (q->peerVerifyMode() == QSslSocket::AutoVerifyPeer
2573 // Check the peer certificate itself. First try the subject's common name
2574 // (CN) as a wildcard, then try all alternate subject name DNS entries the
2575 // same way.
2576 if (!configuration.peerCertificate().isNull()) {
2577 // but only if we're a client connecting to a server
2578 // if we're the server, don't check CN
2579 if (d->tlsMode() == QSslSocket::SslClientMode) {
2580 const auto verificationPeerName = d->verificationName();
2581 const QString peerName(verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName);
2582 if (!isMatchingHostname(configuration.peerCertificate(), peerName)) {
2583 // No matches in common names or alternate names.
2584 const QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate());
2585 sslErrors += error;
2586 emit q->peerVerifyError(error);
2587 if (q->state() != QAbstractSocket::ConnectedState)
2588 return false;
2589 }
2590 }
2591 } else if (doVerifyPeer) {
2592 // No peer certificate presented. Report as error if the socket
2593 // expected one.
2595 sslErrors += error;
2596 emit q->peerVerifyError(error);
2597 if (q->state() != QAbstractSocket::ConnectedState)
2598 return false;
2599 }
2600
2601 return true;
2602}
2603
2604bool TlsCryptographSchannel::rootCertOnDemandLoadingAllowed()
2605{
2606 Q_ASSERT(d);
2608}
2609
2610} // namespace QTlsPrivate
2611
qint64 bytesAvailable() const override
Returns the number of incoming bytes that are waiting to be read.
virtual void disconnectFromHost()
Attempts to close the socket.
SocketState state() const
Returns the state of the socket.
SocketError error() const
Returns the type of error that last occurred.
\inmodule QtCore
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
qsizetype length() const noexcept
Same as size().
Definition qbytearray.h:479
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
void clear()
Clears the contents of the byte array and makes it null.
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:394
\inmodule QtCore\reentrant
Definition qdatastream.h:30
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
QString errorString() const
Returns a human-readable description of the last device error that occurred.
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
bool contains(QStringView s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
T & first()
Definition qlist.h:628
iterator end()
Definition qlist.h:609
iterator begin()
Definition qlist.h:608
const T & constFirst() const noexcept
Definition qlist.h:630
pointer data()
Definition qlist.h:414
const_iterator cend() const noexcept
Definition qlist.h:614
void append(parameter_type t)
Definition qlist.h:441
const_iterator cbegin() const noexcept
Definition qlist.h:613
void clear()
Definition qlist.h:417
T value(const Key &key, const T &defaultValue=T()) const
Definition qmap.h:356
\inmodule QtCore
Definition qmutex.h:317
static QOperatingSystemVersion current()
[0]
\inmodule QtCore
Definition qmutex.h:313
QTlsPrivate::X509Certificate * createCertificate() const override
QString backendName() const override
long tlsLibraryBuildVersionNumber() const override
QTlsPrivate::TlsKey * createKey() const override
QList< QSslCertificate > systemCaCertificates() const override
QTlsPrivate::X509Pkcs12ReaderPtr X509Pkcs12Reader() const override
QList< QSsl::SupportedFeature > supportedFeatures() const override
QString tlsLibraryVersionString() const override
void ensureInitialized() const override
QTlsPrivate::X509DerReaderPtr X509DerReader() const override
QList< QSsl::SslProtocol > supportedProtocols() const override
QTlsPrivate::TlsCryptograph * createTlsCryptograph() const override
QTlsPrivate::X509PemReaderPtr X509PemReader() const override
QList< QSsl::ImplementedClass > implementedClasses() const override
QString tlsLibraryBuildVersionString() const override
static void ensureInitializedImplementation()
The QSslCertificateExtension class provides an API for accessing the extensions of an X509 certificat...
The QSslCertificate class provides a convenient API for an X509 certificate.
QString issuerDisplayName() const
QByteArray version() const
Returns the certificate's version string.
QString subjectDisplayName() const
QList< QSslCertificateExtension > extensions() const
Returns a list containing the X509 extensions of this certificate.
bool isBlacklisted() const
Returns true if this certificate is blacklisted; otherwise returns false.
The QSslCipher class represents an SSL cryptographic cipher.
Definition qsslcipher.h:22
The QSslError class provides an SSL error.
Definition qsslerror.h:21
QString errorString() const
Returns a short localized human-readable description of the error.
@ SelfSignedCertificate
Definition qsslerror.h:34
@ UnableToVerifyFirstCertificate
Definition qsslerror.h:37
@ CertificateRevoked
Definition qsslerror.h:38
@ CertificateNotYetValid
Definition qsslerror.h:30
@ CertificateBlacklisted
Definition qsslerror.h:49
@ UnableToGetIssuerCertificate
Definition qsslerror.h:26
@ UnspecifiedError
Definition qsslerror.h:62
@ PathLengthExceeded
Definition qsslerror.h:40
@ HostNameMismatch
Definition qsslerror.h:47
@ CertificateUntrusted
Definition qsslerror.h:42
@ CertificateSignatureFailed
Definition qsslerror.h:29
@ InvalidPurpose
Definition qsslerror.h:41
@ NoPeerCertificate
Definition qsslerror.h:46
The QSslKey class provides an interface for private and public keys.
Definition qsslkey.h:23
bool * readyReadPointer()
bool verifyErrorsHaveBeenIgnored()
void setEncrypted(bool enc)
void setMaxReadBufferSize(qint64 maxSize)
QTcpSocket * plainTcpSocket() const
QString verificationName() const
static void setRootCertOnDemandLoadingSupported(bool supported)
bool isRootsOnDemandAllowed() const
bool isPaused() const
qint64 maxReadBufferSize() const
bool & tlsEmittedBytesWritten()
QRingBufferRef & tlsBuffer()
static bool rootCertOnDemandLoadingSupported()
void setPaused(bool p)
void setPendingClose(bool pc)
static void pauseSocketNotifiers(QSslSocket *)
QRingBufferRef & tlsWriteBuffer()
QSslSocket::SslMode tlsMode() const
bool isPendingClose() const
The QSslSocket class provides an SSL encrypted socket for both clients and servers.
Definition qsslsocket.h:29
@ UnencryptedMode
Definition qsslsocket.h:34
\inmodule QtCore
Definition qstringview.h:76
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1164
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
The QTcpSocket class provides a TCP socket.
Definition qtcpsocket.h:18
static constexpr const int nameIndexSchannel
static const QString builtinBackendNames[]
void init(QSslSocket *q, QSslSocketPrivate *d) override
QSsl::SslProtocol sessionProtocol() const override
bool hasUndecryptedData() const override
QSslCipher sessionCipher() const override
QList< QSslError > tlsErrors() const override
TlsKey is an abstract class, that allows a TLS plugin to provide an underlying implementation for the...
static QList< QSslCertificate > certificatesFromDer(const QByteArray &der, int count)
static QList< QSslCertificate > certificatesFromPem(const QByteArray &pem, int count)
static bool importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert, QList< QSslCertificate > *caCertificates, const QByteArray &passPhrase)
static QSslCertificate QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext)
X509Certificate is an abstract class that allows a TLS backend to provide an implementation of the QS...
bool toBool() const
Returns the variant as a bool if the variant has userType() Bool.
void extension()
[6]
Definition dialogs.cpp:230
QSet< QString >::iterator it
Token token
Definition keywords.cpp:444
SslProtocol
Describes the protocol of the cipher.
Definition qssl.h:43
@ DtlsV1_2
Definition qssl.h:56
@ TlsV1_2OrLater
Definition qssl.h:52
@ TlsV1_3
Definition qssl.h:59
@ DtlsV1_2OrLater
Definition qssl.h:57
@ TlsV1_3OrLater
Definition qssl.h:60
@ SecureProtocols
Definition qssl.h:48
@ TlsV1_2
Definition qssl.h:46
@ AnyProtocol
Definition qssl.h:47
@ UnknownProtocol
Definition qssl.h:62
Combined button and popup list for selecting options.
Namespace containing onternal types that TLS backends implement.
QList< QSslCipher > defaultCiphers()
bool(*)(QIODevice *device, QSslKey *key, QSslCertificate *cert, QList< QSslCertificate > *caCertificates, const QByteArray &passPhrase) X509Pkcs12ReaderPtr
UNICODE_STRING cbcChainingMode
QList< CRYPTO_SETTINGS > cryptoSettingsForCiphers(const QList< QSslCipher > &ciphers)
bool containsTls13Cipher(const QList< QSslCipher > &ciphers)
UNICODE_STRING gcmChainingMode
QT_WARNING_POP const SchannelCipherInfo * cipherInfoByOpenSslName(const QString &name)
QList< QSslCipher > ciphersByName(QStringView schannelSuiteName)
QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED std::array< SchannelCipherInfo, 44 > schannelCipherInfo
constexpr Initialization Uninitialized
QVector3D minimum(const QVector3D &v1, const QVector3D &v2) Q_DECL_NOTHROW
Definition qssgutils_p.h:52
#define Q_FALLTHROUGH()
#define QT_WARNING_POP
#define QT_WARNING_DISABLE_DEPRECATED
#define QT_WARNING_PUSH
DBusConnection const char DBusError * error
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
static const qint64 headerSize
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
static ControlElement< T > * ptr(QWidget *widget)
@ Required
GLenum GLsizei GLuint GLint * bytesWritten
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLenum GLuint GLenum GLsizei length
GLenum GLuint buffer
GLuint GLsizei const GLchar * message
GLuint name
GLboolean reset
GLuint GLuint * names
GLuint writeBuffer
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
SSL_CTX int(*) void arg)
#define QStringLiteral(str)
#define NTDDI_VERSION
Definition qt_windows.h:22
#define tr(X)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
#define SECPKG_ATTR_APPLICATION_PROTOCOL
#define BCRYPT_ECDSA_ALGORITHM
#define SP_PROT_TLS1_2
#define MAP_PROTOCOL(sp_protocol, q_protocol)
#define SP_PROT_TLS1_0
#define SP_PROT_TLS1_3
#define CHECK_STATUS(status)
#define CHECK_ATTRIBUTE(attributeName)
#define DEBUG_WARN(message)
QByteArray _q_makePkcs12(const QList< QSslCertificate > &certs, const QSslKey &key, const QString &passPhrase)
#define BCRYPT_ECDH_ALGORITHM
#define SP_PROT_TLS1_1
#define SEC_E_APPLICATION_PROTOCOL_MISMATCH
#define SECBUFFER_ALERT
#define emit
unsigned int quint32
Definition qtypes.h:45
unsigned short quint16
Definition qtypes.h:43
int qint32
Definition qtypes.h:44
long long qint64
Definition qtypes.h:55
std::unique_ptr< void, QHCertStoreDeleter > QHCertStorePointer
Definition qwincrypt_p.h:41
QList< QChar > characters
QFileInfo info(fileName)
[8]
QSettings settings("MySoft", "Star Runner")
[0]
QObject::connect nullptr
const auto certs
[1]
bool contains(const AT &t) const noexcept
Definition qlist.h:44
QList< QSsl::SslProtocol > protocols