Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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(SECBUFFER_APPLICATION_PROTOCOLS)
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.
129QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key,
130 const QString &passPhrase);
131
132namespace {
133bool supportsTls13();
134}
135
136namespace QTlsPrivate {
137
138QList<QSslCipher> defaultCiphers();
139
143 const char *keyExchangeMethod;
145 const char *encryptionMethod;
147 const char *hashMethod;
148 QList<QSsl::SslProtocol> protocols;
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
233QList<CRYPTO_SETTINGS> cryptoSettingsForCiphers(const QList<QSslCipher> &ciphers)
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
242 QList<const SchannelCipherInfo*> cipherInfo;
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
395QList<QSslCipher> ciphersByName(QStringView schannelSuiteName)
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
425QList<QSslCipher> defaultCiphers()
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
459bool containsTls13Cipher(const QList<QSslCipher> &ciphers)
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
490
492{
493 return "Secure Channel (NTDDI: 0x%1)"_L1
494 .arg(QString::number(NTDDI_VERSION, 16).toUpper());
495}
496
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
526
527QList<QSsl::SslProtocol> QSchannelBackend::supportedProtocols() const
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
551QList<QSsl::SupportedFeature> QSchannelBackend::supportedFeatures() const
552{
553 QList<QSsl::SupportedFeature> features;
554
555#ifdef SUPPORTS_ALPN
558#endif
559
560 return features;
561}
562
563QList<QSsl::ImplementedClass> QSchannelBackend::implementedClasses() const
564{
565 QList<QSsl::ImplementedClass> classes;
566
570
571 return classes;
572}
573
578
583
584QList<QSslCertificate> QSchannelBackend::systemCaCertificates() const
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
600 auto hSystemStore = QHCertStorePointer(
601 CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0,
602 CERT_STORE_READONLY_FLAG | CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT"));
603
604 if (hSystemStore) {
605 PCCERT_CONTEXT pc = nullptr;
606 while ((pc = CertFindCertificateInStore(hSystemStore.get(), X509_ASN_ENCODING, 0,
607 CERT_FIND_ANY, nullptr, pc))) {
609 }
610 }
611 return systemCerts;
612}
613
618
623
628
629namespace {
630
631SecBuffer createSecBuffer(void *ptr, unsigned long length, unsigned long bufferType)
632{
633 return SecBuffer{ length, bufferType, ptr };
634}
635
636SecBuffer createSecBuffer(QByteArray &buffer, unsigned long bufferType)
637{
638 return createSecBuffer(buffer.data(), static_cast<unsigned long>(buffer.length()), bufferType);
639}
640
641QString schannelErrorToString(qint32 status)
642{
643 switch (status) {
644 case SEC_E_INSUFFICIENT_MEMORY:
645 return QSslSocket::tr("Insufficient memory");
646 case SEC_E_INTERNAL_ERROR:
647 return QSslSocket::tr("Internal error");
648 case SEC_E_INVALID_HANDLE:
649 return QSslSocket::tr("An internal handle was invalid");
650 case SEC_E_INVALID_TOKEN:
651 return QSslSocket::tr("An internal token was invalid");
652 case SEC_E_LOGON_DENIED:
653 // According to the link below we get this error when Schannel receives TLS1_ALERT_ACCESS_DENIED
654 // https://docs.microsoft.com/en-us/windows/desktop/secauthn/schannel-error-codes-for-tls-and-ssl-alerts
655 return QSslSocket::tr("Access denied");
656 case SEC_E_NO_AUTHENTICATING_AUTHORITY:
657 return QSslSocket::tr("No authority could be contacted for authorization");
658 case SEC_E_NO_CREDENTIALS:
659 return QSslSocket::tr("No credentials");
660 case SEC_E_TARGET_UNKNOWN:
661 return QSslSocket::tr("The target is unknown or unreachable");
662 case SEC_E_UNSUPPORTED_FUNCTION:
663 return QSslSocket::tr("An unsupported function was requested");
664 case SEC_E_WRONG_PRINCIPAL:
665 // SNI error
666 return QSslSocket::tr("The hostname provided does not match the one received from the peer");
668 return QSslSocket::tr("No common protocol exists between the client and the server");
669 case SEC_E_ILLEGAL_MESSAGE:
670 return QSslSocket::tr("Unexpected or badly-formatted message received");
671 case SEC_E_ENCRYPT_FAILURE:
672 return QSslSocket::tr("The data could not be encrypted");
673 case SEC_E_ALGORITHM_MISMATCH:
674 return QSslSocket::tr("No cipher suites in common");
675 case SEC_E_UNKNOWN_CREDENTIALS:
676 // This can mean "invalid argument" in some cases...
677 return QSslSocket::tr("The credentials were not recognized / Invalid argument");
678 case SEC_E_MESSAGE_ALTERED:
679 // According to the Internet it also triggers for messages that are out of order.
680 // https://microsoft.public.platformsdk.security.narkive.com/4JAvlMvD/help-please-schannel-security-contexts-and-decryptmessage
681 return QSslSocket::tr("The message was tampered with, damaged or out of sequence.");
682 case SEC_E_OUT_OF_SEQUENCE:
683 return QSslSocket::tr("A message was received out of sequence.");
684 case SEC_E_CONTEXT_EXPIRED:
685 return QSslSocket::tr("The TLS/SSL connection has been closed");
686 default:
687 return QSslSocket::tr("Unknown error occurred: %1").arg(status);
688 }
689}
690
691bool supportsTls13()
692{
693 static bool supported = []() {
694 const auto current = QOperatingSystemVersion::current();
695 // 20221 just happens to be the preview version I run on my laptop where I tested TLS 1.3.
696 const auto minimum =
698 return current >= minimum;
699 }();
700
701 return supported;
702}
703
704DWORD toSchannelProtocol(QSsl::SslProtocol protocol)
705{
706 DWORD protocols = SP_PROT_NONE;
707 switch (protocol) {
709 return DWORD(-1);
712 case QSsl::DtlsV1_0:
713 case QSsl::DtlsV1_0OrLater:
715 case QSsl::DtlsV1_2:
717 return DWORD(-1); // Not supported at the moment (@future)
720 if (supportsTls13())
721 protocols |= SP_PROT_TLS1_3;
722 break;
725 case QSsl::TlsV1_0:
726 protocols = SP_PROT_TLS1_0;
727 break;
728 case QSsl::TlsV1_1:
729 protocols = SP_PROT_TLS1_1;
730 break;
732 case QSsl::TlsV1_2:
733 protocols = SP_PROT_TLS1_2;
734 break;
735 case QSsl::TlsV1_3:
736 if (supportsTls13())
737 protocols = SP_PROT_TLS1_3;
738 else
739 protocols = DWORD(-1);
740 break;
743 case QSsl::TlsV1_0OrLater:
744 // For the "OrLater" protocols we fall through from one to the next, adding all of them
745 // in ascending order
746 protocols = SP_PROT_TLS1_0;
748 case QSsl::TlsV1_1OrLater:
749 protocols |= SP_PROT_TLS1_1;
752 case QSsl::SecureProtocols: // TLS v1.2 and later is currently considered secure
754 protocols |= SP_PROT_TLS1_2;
757 if (supportsTls13())
758 protocols |= SP_PROT_TLS1_3;
759 else if (protocol == QSsl::TlsV1_3OrLater)
760 protocols = DWORD(-1); // if TlsV1_3OrLater was specifically chosen we should fail
761 break;
762 }
763 return protocols;
764}
765
766// In the new API that descended down upon us we are not asked which protocols we want
767// but rather which protocols we don't want. So now we have this function to disable
768// anything that is not enabled.
769DWORD negatedSchannelProtocols(DWORD wantedProtocols)
770{
771 DWORD protocols = SP_PROT_ALL; // all protocols
772 protocols &= ~wantedProtocols; // minus the one(s) we want
773 return protocols;
774}
775
783QSsl::SslProtocol toQtSslProtocol(DWORD protocol)
784{
785#define MAP_PROTOCOL(sp_protocol, q_protocol) \
786 if (protocol & sp_protocol) { \
787 Q_ASSERT(!(protocol & ~sp_protocol)); \
788 return q_protocol; \
789 }
790
793 MAP_PROTOCOL(SP_PROT_TLS1_0, QSsl::TlsV1_0)
794 MAP_PROTOCOL(SP_PROT_TLS1_1, QSsl::TlsV1_1)
798#undef MAP_PROTOCOL
799 Q_UNREACHABLE();
801}
802
807bool netscapeWrongCertType(const QList<QSslCertificateExtension> &extensions, bool isClient)
808{
809 const auto netscapeIt = std::find_if(
810 extensions.cbegin(), extensions.cend(),
812 return extension.oid() == u"2.16.840.1.113730.1.1";
813 });
814 if (netscapeIt != extensions.cend()) {
815 const QByteArray netscapeCertTypeByte = netscapeIt->value().toByteArray();
816 int netscapeCertType = 0;
817 QDataStream dataStream(netscapeCertTypeByte);
818 dataStream >> netscapeCertType;
819 if (dataStream.status() != QDataStream::Status::Ok)
820 return true;
821 const int expectedPeerCertType = isClient ? NETSCAPE_SSL_SERVER_AUTH_CERT_TYPE
822 : NETSCAPE_SSL_CLIENT_AUTH_CERT_TYPE;
823 if ((netscapeCertType & expectedPeerCertType) == 0)
824 return true;
825 }
826 return false;
827}
828
836bool isCertificateAuthority(const QList<QSslCertificateExtension> &extensions)
837{
838 auto it = std::find_if(extensions.cbegin(), extensions.cend(),
840 return extension.name() == "basicConstraints"_L1;
841 });
842 if (it != extensions.cend()) {
843 QVariantMap basicConstraints = it->value().toMap();
844 return basicConstraints.value("ca"_L1, false).toBool();
845 }
846 return false;
847}
848
854bool matchesContextRequirements(DWORD attributes, DWORD requirements,
856 bool isClient)
857{
858#ifdef QSSLSOCKET_DEBUG
859#define DEBUG_WARN(message) qCWarning(lcTlsBackendSchannel, message)
860#else
861#define DEBUG_WARN(message)
862#endif
863
864#define CHECK_ATTRIBUTE(attributeName) \
865 do { \
866 const DWORD req##attributeName = isClient ? ISC_REQ_##attributeName : ASC_REQ_##attributeName; \
867 const DWORD ret##attributeName = isClient ? ISC_RET_##attributeName : ASC_RET_##attributeName; \
868 if (!(requirements & req##attributeName) != !(attributes & ret##attributeName)) { \
869 DEBUG_WARN("Missing attribute \"" #attributeName "\""); \
870 return false; \
871 } \
872 } while (false)
873
874 CHECK_ATTRIBUTE(CONFIDENTIALITY);
875 CHECK_ATTRIBUTE(REPLAY_DETECT);
876 CHECK_ATTRIBUTE(SEQUENCE_DETECT);
877 CHECK_ATTRIBUTE(STREAM);
879 CHECK_ATTRIBUTE(MUTUAL_AUTH);
880
881 // This one is manual because there is no server / ASC_ version
882 if (isClient) {
883 const auto reqManualCredValidation = ISC_REQ_MANUAL_CRED_VALIDATION;
884 const auto retManualCredValidation = ISC_RET_MANUAL_CRED_VALIDATION;
885 if (!(requirements & reqManualCredValidation) != !(attributes & retManualCredValidation)) {
886 DEBUG_WARN("Missing attribute \"MANUAL_CRED_VALIDATION\"");
887 return false;
888 }
889 }
890
891 return true;
892#undef CHECK_ATTRIBUTE
893#undef DEBUG_WARN
894}
895
896template<typename Required, typename Actual>
897Required const_reinterpret_cast(Actual *p)
898{
899 return Required(p);
900}
901
902#ifdef SUPPORTS_ALPN
903QByteArray createAlpnString(const QByteArrayList &nextAllowedProtocols)
904{
905 QByteArray alpnString;
906 if (!nextAllowedProtocols.isEmpty()) {
907 const QByteArray names = [&nextAllowedProtocols]() {
908 QByteArray protocolString;
909 for (QByteArray proto : nextAllowedProtocols) {
910 if (proto.size() > 255) {
911 qCWarning(lcTlsBackendSchannel)
912 << "TLS ALPN extension" << proto << "is too long and will be ignored.";
913 continue;
914 } else if (proto.isEmpty()) {
915 continue;
916 }
917 protocolString += char(proto.length()) + proto;
918 }
919 return protocolString;
920 }();
921 if (names.isEmpty())
922 return alpnString;
923
924 const quint16 namesSize = names.size();
925 const quint32 alpnId = SecApplicationProtocolNegotiationExt_ALPN;
926 const quint32 totalSize = sizeof(alpnId) + sizeof(namesSize) + namesSize;
927 alpnString = QByteArray::fromRawData(reinterpret_cast<const char *>(&totalSize), sizeof(totalSize))
928 + QByteArray::fromRawData(reinterpret_cast<const char *>(&alpnId), sizeof(alpnId))
929 + QByteArray::fromRawData(reinterpret_cast<const char *>(&namesSize), sizeof(namesSize))
930 + names;
931 }
932 return alpnString;
933}
934#endif // SUPPORTS_ALPN
935
936qint64 readToBuffer(QByteArray &buffer, QTcpSocket *plainSocket)
937{
938 Q_ASSERT(plainSocket);
939 static const qint64 shrinkCutoff = 1024 * 12;
940 static const qint64 defaultRead = 1024 * 16;
941 qint64 bytesRead = 0;
942
943 const auto toRead = std::min(defaultRead, plainSocket->bytesAvailable());
944 if (toRead > 0) {
945 const auto bufferSize = buffer.size();
946 buffer.reserve(bufferSize + toRead); // avoid growth strategy kicking in
947 buffer.resize(bufferSize + toRead);
948 bytesRead = plainSocket->read(buffer.data() + bufferSize, toRead);
949 buffer.resize(bufferSize + bytesRead);
950 // In case of excessive memory usage we shrink:
951 if (buffer.size() < shrinkCutoff && buffer.capacity() > defaultRead)
952 buffer.shrink_to_fit();
953 }
954
955 return bytesRead;
956}
957
958void retainExtraData(QByteArray &buffer, const SecBuffer &secBuffer)
959{
960 Q_ASSERT(secBuffer.BufferType == SECBUFFER_EXTRA);
961 if (int(secBuffer.cbBuffer) >= buffer.size())
962 return;
963
964#ifdef QSSLSOCKET_DEBUG
965 qCDebug(lcTlsBackendSchannel, "We got SECBUFFER_EXTRA, will retain %lu bytes",
966 secBuffer.cbBuffer);
967#endif
968 std::move(buffer.end() - secBuffer.cbBuffer, buffer.end(), buffer.begin());
969 buffer.resize(secBuffer.cbBuffer);
970}
971
972qint64 checkIncompleteData(const SecBuffer &secBuffer)
973{
974 if (secBuffer.BufferType == SECBUFFER_MISSING) {
975#ifdef QSSLSOCKET_DEBUG
976 qCDebug(lcTlsBackendSchannel, "Need %lu more bytes.", secBuffer.cbBuffer);
977#endif
978 return secBuffer.cbBuffer;
979}
980 return 0;
981}
982
983DWORD defaultCredsFlag()
984{
985 return qEnvironmentVariableIsSet("QT_SCH_DEFAULT_CREDS") ? 0 : SCH_CRED_NO_DEFAULT_CREDS;
986}
987} // anonymous namespace
988
989
990namespace QTlsPrivate {
991
993{
994 SecInvalidateHandle(&credentialHandle);
995 SecInvalidateHandle(&contextHandle);
997}
998
1000{
1001 closeCertificateStores();
1002 deallocateContext();
1003 freeCredentialsHandle();
1004 CertFreeCertificateContext(localCertContext);
1005}
1006
1008{
1009 Q_ASSERT(qObj);
1010 Q_ASSERT(dObj);
1011
1012 q = qObj;
1013 d = dObj;
1014
1015 reset();
1016}
1017
1018bool TlsCryptographSchannel::sendToken(void *token, unsigned long tokenLength, bool emitError)
1019{
1020 if (tokenLength == 0)
1021 return true;
1022
1023 Q_ASSERT(d);
1024 auto *plainSocket = d->plainTcpSocket();
1025 Q_ASSERT(plainSocket);
1026 if (plainSocket->state() == QAbstractSocket::UnconnectedState || !plainSocket->isValid()
1027 || !plainSocket->isOpen()) {
1028 return false;
1029 }
1030
1031 const qint64 written = plainSocket->write(static_cast<const char *>(token), tokenLength);
1032 if (written != qint64(tokenLength)) {
1033 // Failed to write/buffer everything or an error occurred
1034 if (emitError)
1035 setErrorAndEmit(d, plainSocket->error(), plainSocket->errorString());
1036 return false;
1037 }
1038 return true;
1039}
1040
1041QString TlsCryptographSchannel::targetName() const
1042{
1043 // Used for SNI extension
1044 Q_ASSERT(q);
1045 Q_ASSERT(d);
1046
1047 const auto verificationPeerName = d->verificationName();
1048 return verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName;
1049}
1050
1051ULONG TlsCryptographSchannel::getContextRequirements()
1052{
1053 Q_ASSERT(d);
1054 Q_ASSERT(q);
1055
1056 const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
1057 ULONG req = 0;
1058
1059 req |= ISC_REQ_ALLOCATE_MEMORY; // Allocate memory for buffers automatically
1060 req |= ISC_REQ_CONFIDENTIALITY; // Encrypt messages
1061 req |= ISC_REQ_REPLAY_DETECT; // Detect replayed messages
1062 req |= ISC_REQ_SEQUENCE_DETECT; // Detect out of sequence messages
1063 req |= ISC_REQ_STREAM; // Support a stream-oriented connection
1064
1065 if (isClient) {
1066 req |= ISC_REQ_MANUAL_CRED_VALIDATION; // Manually validate certificate
1067 } else {
1068 switch (q->peerVerifyMode()) {
1070 // There doesn't seem to be a way to ask for an optional client cert :-(
1073 break;
1075 req |= ISC_REQ_MUTUAL_AUTH;
1076 break;
1077 }
1078 }
1079
1080 return req;
1081}
1082
1083bool TlsCryptographSchannel::acquireCredentialsHandle()
1084{
1085 Q_ASSERT(d);
1086 Q_ASSERT(q);
1087 const auto &configuration = q->sslConfiguration();
1088
1089 Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
1090
1091 const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
1092 DWORD protocols = toSchannelProtocol(configuration.protocol());
1093 if (protocols == DWORD(-1)) {
1095 QSslSocket::tr("Invalid protocol chosen"));
1096 return false;
1097 }
1098
1099 const CERT_CHAIN_CONTEXT *chainContext = nullptr;
1100 auto freeCertChain = qScopeGuard([&chainContext]() {
1101 if (chainContext)
1102 CertFreeCertificateChain(chainContext);
1103 });
1104
1105 DWORD certsCount = 0;
1106 // Set up our certificate stores before trying to use one...
1107 initializeCertificateStores();
1108
1109 // Check if user has specified a certificate chain but it could not be loaded.
1110 // This happens if there was something wrong with the certificate chain or there was no private
1111 // key.
1112 if (!configuration.localCertificateChain().isEmpty() && !localCertificateStore)
1113 return true; // 'true' because "tst_QSslSocket::setEmptyKey" expects us to not disconnect
1114
1115 if (localCertificateStore != nullptr) {
1116 CERT_CHAIN_FIND_BY_ISSUER_PARA findParam;
1117 ZeroMemory(&findParam, sizeof(findParam));
1118 findParam.cbSize = sizeof(findParam);
1119 findParam.pszUsageIdentifier = isClient ? szOID_PKIX_KP_CLIENT_AUTH : szOID_PKIX_KP_SERVER_AUTH;
1120
1121 // There should only be one chain in our store, so.. we grab that one.
1122 chainContext = CertFindChainInStore(localCertificateStore.get(),
1123 X509_ASN_ENCODING,
1124 0,
1125 CERT_CHAIN_FIND_BY_ISSUER,
1126 &findParam,
1127 nullptr);
1128 if (!chainContext) {
1129 const QString message = isClient
1130 ? QSslSocket::tr("The certificate provided cannot be used for a client.")
1131 : QSslSocket::tr("The certificate provided cannot be used for a server.");
1133 return false;
1134 }
1135 Q_ASSERT(chainContext->cChain == 1);
1136 Q_ASSERT(chainContext->rgpChain[0]);
1137 Q_ASSERT(chainContext->rgpChain[0]->cbSize >= 1);
1138 Q_ASSERT(chainContext->rgpChain[0]->rgpElement[0]);
1139 Q_ASSERT(!localCertContext);
1140 localCertContext = CertDuplicateCertificateContext(chainContext->rgpChain[0]
1141 ->rgpElement[0]
1142 ->pCertContext);
1143 certsCount = 1;
1144 Q_ASSERT(localCertContext);
1145 }
1146
1147 const QList<QSslCipher> ciphers = configuration.ciphers();
1148 if (!ciphers.isEmpty() && !containsTls13Cipher(ciphers))
1149 protocols &= ~SP_PROT_TLS1_3;
1150
1151 QList<CRYPTO_SETTINGS> cryptoSettings;
1152 if (!ciphers.isEmpty())
1153 cryptoSettings = cryptoSettingsForCiphers(ciphers);
1154
1155 TLS_PARAMETERS tlsParameters = {
1156 0,
1157 nullptr,
1158 negatedSchannelProtocols(protocols), // what protocols to disable
1159 static_cast<DWORD>(cryptoSettings.size()),
1160 (cryptoSettings.isEmpty() ? nullptr : cryptoSettings.data()),
1161 0
1162 };
1163
1164 SCH_CREDENTIALS credentials = {
1165 SCH_CREDENTIALS_VERSION,
1166 0,
1167 certsCount,
1168 &localCertContext,
1169 nullptr,
1170 0,
1171 nullptr,
1172 0,
1173 SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT | defaultCredsFlag(),
1174 1,
1175 &tlsParameters
1176 };
1177
1178 TimeStamp expiration{};
1179 auto status = AcquireCredentialsHandle(nullptr, // pszPrincipal (unused)
1180 const_cast<wchar_t *>(UNISP_NAME), // pszPackage
1181 isClient ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND, // fCredentialUse
1182 nullptr, // pvLogonID (unused)
1183 &credentials, // pAuthData
1184 nullptr, // pGetKeyFn (unused)
1185 nullptr, // pvGetKeyArgument (unused)
1186 &credentialHandle, // phCredential
1187 &expiration // ptsExpir
1188 );
1189
1190 if (status != SEC_E_OK) {
1191 setErrorAndEmit(d, QAbstractSocket::SslInternalError, schannelErrorToString(status));
1192 return false;
1193 }
1194 return true;
1195}
1196
1197void TlsCryptographSchannel::deallocateContext()
1198{
1199 if (SecIsValidHandle(&contextHandle)) {
1200 DeleteSecurityContext(&contextHandle);
1201 SecInvalidateHandle(&contextHandle);
1202 }
1203}
1204
1205void TlsCryptographSchannel::freeCredentialsHandle()
1206{
1207 if (SecIsValidHandle(&credentialHandle)) {
1208 FreeCredentialsHandle(&credentialHandle);
1209 SecInvalidateHandle(&credentialHandle);
1210 }
1211}
1212
1213void TlsCryptographSchannel::closeCertificateStores()
1214{
1215 localCertificateStore.reset();
1216 peerCertificateStore.reset();
1217 caCertificateStore.reset();
1218}
1219
1220bool TlsCryptographSchannel::createContext()
1221{
1222 Q_ASSERT(q);
1223 Q_ASSERT(d);
1224
1225 Q_ASSERT(SecIsValidHandle(&credentialHandle));
1226 Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
1228 ULONG contextReq = getContextRequirements();
1229
1230 SecBuffer outBuffers[3];
1231 outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
1232 outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
1233 outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
1234 auto freeBuffers = qScopeGuard([&outBuffers]() {
1235 for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
1236 if (outBuffers[i].pvBuffer)
1237 FreeContextBuffer(outBuffers[i].pvBuffer);
1238 }
1239 });
1240 SecBufferDesc outputBufferDesc{
1241 SECBUFFER_VERSION,
1242 ARRAYSIZE(outBuffers),
1243 outBuffers
1244 };
1245
1246 TimeStamp expiry;
1247
1248 SecBufferDesc alpnBufferDesc;
1249 bool useAlpn = false;
1250#ifdef SUPPORTS_ALPN
1251 QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNone);
1252 QByteArray alpnString = createAlpnString(q->sslConfiguration().allowedNextProtocols());
1253 useAlpn = !alpnString.isEmpty();
1254 SecBuffer alpnBuffers[1];
1255 alpnBuffers[0] = createSecBuffer(alpnString, SECBUFFER_APPLICATION_PROTOCOLS);
1256 alpnBufferDesc = {
1257 SECBUFFER_VERSION,
1258 ARRAYSIZE(alpnBuffers),
1259 alpnBuffers
1260 };
1261#endif
1262
1263 auto status = InitializeSecurityContext(&credentialHandle, // phCredential
1264 nullptr, // phContext
1265 const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
1266 contextReq, // fContextReq
1267 0, // Reserved1
1268 0, // TargetDataRep (unused)
1269 useAlpn ? &alpnBufferDesc : nullptr, // pInput
1270 0, // Reserved2
1271 &contextHandle, // phNewContext
1272 &outputBufferDesc, // pOutput
1273 &contextAttributes, // pfContextAttr
1274 &expiry // ptsExpiry
1275 );
1276
1277 // This is the first call to InitializeSecurityContext, so theoretically "CONTINUE_NEEDED"
1278 // should be the only non-error return-code here.
1279 if (status != SEC_I_CONTINUE_NEEDED) {
1280 setErrorAndEmit(d, QAbstractSocket::SslInternalError,
1281 QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status)));
1282 return false;
1283 }
1284
1285 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
1286 return false;
1287 schannelState = SchannelState::PerformHandshake;
1288 return true;
1289}
1290
1291bool TlsCryptographSchannel::acceptContext()
1292{
1293 Q_ASSERT(d);
1294 Q_ASSERT(q);
1295
1296 auto *plainSocket = d->plainTcpSocket();
1297 Q_ASSERT(plainSocket);
1298
1299 Q_ASSERT(SecIsValidHandle(&credentialHandle));
1300 Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
1302 ULONG contextReq = getContextRequirements();
1303
1304 if (missingData > plainSocket->bytesAvailable())
1305 return true;
1306
1307 missingData = 0;
1308 readToBuffer(intermediateBuffer, plainSocket);
1309 if (intermediateBuffer.isEmpty())
1310 return true; // definitely need more data..
1311
1312 SecBuffer inBuffers[2];
1313 inBuffers[0] = createSecBuffer(intermediateBuffer, SECBUFFER_TOKEN);
1314
1315#ifdef SUPPORTS_ALPN
1316 QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNone);
1317 // The string must be alive when we call AcceptSecurityContext
1318 QByteArray alpnString = createAlpnString(q->sslConfiguration().allowedNextProtocols());
1319 if (!alpnString.isEmpty()) {
1320 inBuffers[1] = createSecBuffer(alpnString, SECBUFFER_APPLICATION_PROTOCOLS);
1321 } else
1322#endif
1323 {
1324 inBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
1325 }
1326
1327 SecBufferDesc inputBufferDesc{
1328 SECBUFFER_VERSION,
1329 ARRAYSIZE(inBuffers),
1330 inBuffers
1331 };
1332
1333 SecBuffer outBuffers[3];
1334 outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
1335 outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
1336 outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
1337 auto freeBuffers = qScopeGuard([&outBuffers]() {
1338 for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
1339 if (outBuffers[i].pvBuffer)
1340 FreeContextBuffer(outBuffers[i].pvBuffer);
1341 }
1342 });
1343 SecBufferDesc outputBufferDesc{
1344 SECBUFFER_VERSION,
1345 ARRAYSIZE(outBuffers),
1346 outBuffers
1347 };
1348
1349 TimeStamp expiry;
1350 auto status = AcceptSecurityContext(
1351 &credentialHandle, // phCredential
1352 nullptr, // phContext
1353 &inputBufferDesc, // pInput
1354 contextReq, // fContextReq
1355 0, // TargetDataRep (unused)
1356 &contextHandle, // phNewContext
1357 &outputBufferDesc, // pOutput
1358 &contextAttributes, // pfContextAttr
1359 &expiry // ptsTimeStamp
1360 );
1361
1362 if (status == SEC_E_INCOMPLETE_MESSAGE) {
1363 // Need more data
1364 missingData = checkIncompleteData(outBuffers[0]);
1365 return true;
1366 }
1367
1368 if (inBuffers[1].BufferType == SECBUFFER_EXTRA) {
1369 // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
1370 // inBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need to
1371 // be stored.
1372 retainExtraData(intermediateBuffer, inBuffers[1]);
1373 } else { /* No 'extra' data, message not incomplete */
1374 intermediateBuffer.resize(0);
1375 }
1376
1377 if (status != SEC_I_CONTINUE_NEEDED) {
1379 QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status)));
1380 return false;
1381 }
1382 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
1383 return false;
1384 schannelState = SchannelState::PerformHandshake;
1385 return true;
1386}
1387
1388bool TlsCryptographSchannel::performHandshake()
1389{
1390 Q_ASSERT(d);
1391 auto *plainSocket = d->plainTcpSocket();
1392 Q_ASSERT(plainSocket);
1393
1394 if (plainSocket->state() == QAbstractSocket::UnconnectedState || !plainSocket->isValid()
1395 || !plainSocket->isOpen()) {
1396 setErrorAndEmit(d, QAbstractSocket::RemoteHostClosedError,
1397 QSslSocket::tr("The TLS/SSL connection has been closed"));
1398 return false;
1399 }
1400 Q_ASSERT(SecIsValidHandle(&credentialHandle));
1401 Q_ASSERT(SecIsValidHandle(&contextHandle));
1402 Q_ASSERT(schannelState == SchannelState::PerformHandshake);
1403
1404#ifdef QSSLSOCKET_DEBUG
1405 qCDebug(lcTlsBackendSchannel, "Bytes available from socket: %lld",
1406 plainSocket->bytesAvailable());
1407 qCDebug(lcTlsBackendSchannel, "intermediateBuffer size: %d", intermediateBuffer.size());
1408#endif
1409
1410 if (missingData > plainSocket->bytesAvailable())
1411 return true;
1412
1413 missingData = 0;
1414 readToBuffer(intermediateBuffer, plainSocket);
1415 if (intermediateBuffer.isEmpty())
1416 return true; // no data, will fail
1417
1418 SecBuffer outBuffers[3] = {};
1419 const auto freeOutBuffers = [&outBuffers]() {
1420 for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
1421 if (outBuffers[i].pvBuffer)
1422 FreeContextBuffer(outBuffers[i].pvBuffer);
1423 }
1424 };
1425 const auto outBuffersGuard = qScopeGuard(freeOutBuffers);
1426 // For this call to InitializeSecurityContext we may need to call it twice.
1427 // In some cases us not having a certificate isn't actually an error, but just a request.
1428 // With Schannel, to ignore this warning, we need to call InitializeSecurityContext again
1429 // when we get SEC_I_INCOMPLETE_CREDENTIALS! As far as I can tell it's not documented anywhere.
1430 // https://stackoverflow.com/a/47479968/2493610
1431 SECURITY_STATUS status;
1432 short attempts = 2;
1433 do {
1434 SecBuffer inputBuffers[2];
1435 inputBuffers[0] = createSecBuffer(intermediateBuffer, SECBUFFER_TOKEN);
1436 inputBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
1437 SecBufferDesc inputBufferDesc{
1438 SECBUFFER_VERSION,
1439 ARRAYSIZE(inputBuffers),
1440 inputBuffers
1441 };
1442
1443 freeOutBuffers(); // free buffers from any previous attempt
1444 outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
1445 outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
1446 outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
1447 SecBufferDesc outputBufferDesc{
1448 SECBUFFER_VERSION,
1449 ARRAYSIZE(outBuffers),
1450 outBuffers
1451 };
1452
1453 ULONG contextReq = getContextRequirements();
1454 TimeStamp expiry;
1455 status = InitializeSecurityContext(
1456 &credentialHandle, // phCredential
1457 &contextHandle, // phContext
1458 const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
1459 contextReq, // fContextReq
1460 0, // Reserved1
1461 0, // TargetDataRep (unused)
1462 &inputBufferDesc, // pInput
1463 0, // Reserved2
1464 nullptr, // phNewContext (we already have one)
1465 &outputBufferDesc, // pOutput
1466 &contextAttributes, // pfContextAttr
1467 &expiry // ptsExpiry
1468 );
1469
1470 if (inputBuffers[1].BufferType == SECBUFFER_EXTRA) {
1471 // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
1472 // inputBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need
1473 // to be stored.
1474 retainExtraData(intermediateBuffer, inputBuffers[1]);
1475 } else if (status != SEC_E_INCOMPLETE_MESSAGE) {
1476 // Clear the buffer if we weren't asked for more data
1477 intermediateBuffer.resize(0);
1478 }
1479
1480 --attempts;
1481 } while (status == SEC_I_INCOMPLETE_CREDENTIALS && attempts > 0);
1482
1483 switch (status) {
1484 case SEC_E_OK:
1485 // Need to transmit a final token in the handshake if 'cbBuffer' is non-zero.
1486 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
1487 return false;
1488 schannelState = SchannelState::VerifyHandshake;
1489 return true;
1490 case SEC_I_CONTINUE_NEEDED:
1491 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
1492 return false;
1493 // Must call InitializeSecurityContext again later (done through continueHandshake)
1494 return true;
1495 case SEC_I_INCOMPLETE_CREDENTIALS:
1496 // Schannel takes care of picking certificate to send (other than the one we can specify),
1497 // so if we get here then that means we don't have a certificate the server accepts.
1499 QSslSocket::tr("Server did not accept any certificate we could present."));
1500 return false;
1501 case SEC_I_CONTEXT_EXPIRED:
1502 // "The message sender has finished using the connection and has initiated a shutdown."
1503 if (outBuffers[0].BufferType == SECBUFFER_TOKEN) {
1504 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
1505 return false;
1506 }
1507 if (!shutdown) { // we did not initiate this
1508 setErrorAndEmit(d, QAbstractSocket::RemoteHostClosedError,
1509 QSslSocket::tr("The TLS/SSL connection has been closed"));
1510 }
1511 return true;
1512 case SEC_E_INCOMPLETE_MESSAGE:
1513 // Simply incomplete, wait for more data
1514 missingData = checkIncompleteData(outBuffers[0]);
1515 return true;
1516 case SEC_E_ALGORITHM_MISMATCH:
1518 QSslSocket::tr("Algorithm mismatch"));
1519 shutdown = true; // skip sending the "Shutdown" alert
1520 return false;
1521 }
1522
1523 // Note: We can get here if the connection is using TLS 1.2 and the server certificate uses
1524 // MD5, which is not allowed in Schannel. This causes an "invalid token" error during handshake.
1525 // (If you came here investigating an error: md5 is insecure, update your certificate)
1527 QSslSocket::tr("Handshake failed: %1").arg(schannelErrorToString(status)));
1528 return false;
1529}
1530
1531bool TlsCryptographSchannel::verifyHandshake()
1532{
1533 Q_ASSERT(d);
1534 Q_ASSERT(q);
1535 const auto &configuration = q->sslConfiguration();
1536
1537 sslErrors.clear();
1538
1539 const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
1540#define CHECK_STATUS(status) \
1541 if (status != SEC_E_OK) { \
1542 setErrorAndEmit(d, QAbstractSocket::SslInternalError, \
1543 QSslSocket::tr("Failed to query the TLS context: %1") \
1544 .arg(schannelErrorToString(status))); \
1545 return false; \
1546 }
1547
1548 // Everything is set up, now make sure there's nothing wrong and query some attributes...
1549 if (!matchesContextRequirements(contextAttributes, getContextRequirements(),
1550 configuration.peerVerifyMode(), isClient)) {
1552 QSslSocket::tr("Did not get the required attributes for the connection."));
1553 return false;
1554 }
1555
1556 // Get stream sizes (to know the max size of a message and the size of the header and trailer)
1557 auto status = QueryContextAttributes(&contextHandle,
1558 SECPKG_ATTR_STREAM_SIZES,
1559 &streamSizes);
1560 CHECK_STATUS(status);
1561
1562 // Get session cipher info
1563 status = QueryContextAttributes(&contextHandle,
1564 SECPKG_ATTR_CIPHER_INFO,
1565 &cipherInfo);
1566 CHECK_STATUS(status);
1567
1568 status = QueryContextAttributes(&contextHandle,
1569 SECPKG_ATTR_CONNECTION_INFO,
1570 &connectionInfo);
1571 CHECK_STATUS(status);
1572
1573#ifdef SUPPORTS_ALPN
1574 const auto allowedProtos = configuration.allowedNextProtocols();
1575 if (!allowedProtos.isEmpty()) {
1576 SecPkgContext_ApplicationProtocol alpn;
1577 status = QueryContextAttributes(&contextHandle,
1579 &alpn);
1580 CHECK_STATUS(status);
1581 if (alpn.ProtoNegoStatus == SecApplicationProtocolNegotiationStatus_Success) {
1582 QByteArray negotiatedProto = QByteArray((const char *)alpn.ProtocolId,
1583 alpn.ProtocolIdSize);
1584 if (!allowedProtos.contains(negotiatedProto)) {
1586 QSslSocket::tr("Unwanted protocol was negotiated"));
1587 return false;
1588 }
1589 QTlsBackend::setNegotiatedProtocol(d, negotiatedProto);
1590 QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNegotiated);
1591 } else {
1592 QTlsBackend::setNegotiatedProtocol(d, {});
1593 QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationUnsupported);
1594 }
1595 }
1596#endif // supports ALPN
1597
1598#undef CHECK_STATUS
1599
1600 // Verify certificate
1601 CERT_CONTEXT *certificateContext = nullptr;
1602 auto freeCertificate = qScopeGuard([&certificateContext]() {
1603 if (certificateContext)
1604 CertFreeCertificateContext(certificateContext);
1605 });
1606 status = QueryContextAttributes(&contextHandle,
1607 SECPKG_ATTR_REMOTE_CERT_CONTEXT,
1608 &certificateContext);
1609
1610 // QueryPeer can (currently) not work in Schannel since Schannel itself doesn't have a way to
1611 // ask for a certificate and then still be OK if it's not received.
1612 // To work around this we don't request a certificate at all for QueryPeer.
1613 // For servers AutoVerifyPeer is supposed to be treated the same as QueryPeer.
1614 // This means that servers using Schannel will only request client certificate for "VerifyPeer".
1615 if ((!isClient && configuration.peerVerifyMode() == QSslSocket::PeerVerifyMode::VerifyPeer)
1616 || (isClient && configuration.peerVerifyMode() != QSslSocket::PeerVerifyMode::VerifyNone
1617 && configuration.peerVerifyMode() != QSslSocket::PeerVerifyMode::QueryPeer)) {
1618 if (status != SEC_E_OK) {
1619#ifdef QSSLSOCKET_DEBUG
1620 qCDebug(lcTlsBackendSchannel) << "Couldn't retrieve peer certificate, status:"
1621 << schannelErrorToString(status);
1622#endif
1624 sslErrors += error;
1625 emit q->peerVerifyError(error);
1626 if (q->state() != QAbstractSocket::ConnectedState)
1627 return false;
1628 }
1629 }
1630
1631 // verifyCertContext returns false if the user disconnected while it was checking errors.
1632 if (certificateContext && !verifyCertContext(certificateContext))
1633 return false;
1634
1635 if (!checkSslErrors() || q->state() != QAbstractSocket::ConnectedState) {
1636#ifdef QSSLSOCKET_DEBUG
1637 qCDebug(lcTlsBackendSchannel) << __func__ << "was unsuccessful. Paused:" << d->isPaused();
1638#endif
1639 // If we're paused then checkSslErrors returned false, but it's not an error
1640 return d->isPaused() && q->state() == QAbstractSocket::ConnectedState;
1641 }
1642
1643 schannelState = SchannelState::Done;
1644 return true;
1645}
1646
1647bool TlsCryptographSchannel::renegotiate()
1648{
1649 Q_ASSERT(d);
1650
1651 SecBuffer outBuffers[3];
1652 outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
1653 outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
1654 outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
1655 auto freeBuffers = qScopeGuard([&outBuffers]() {
1656 for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
1657 if (outBuffers[i].pvBuffer)
1658 FreeContextBuffer(outBuffers[i].pvBuffer);
1659 }
1660 });
1661 SecBufferDesc outputBufferDesc{
1662 SECBUFFER_VERSION,
1663 ARRAYSIZE(outBuffers),
1664 outBuffers
1665 };
1666
1667 ULONG contextReq = getContextRequirements();
1668 TimeStamp expiry;
1669 SECURITY_STATUS status;
1670 if (d->tlsMode() == QSslSocket::SslClientMode) {
1671 status = InitializeSecurityContext(&credentialHandle, // phCredential
1672 &contextHandle, // phContext
1673 const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
1674 contextReq, // fContextReq
1675 0, // Reserved1
1676 0, // TargetDataRep (unused)
1677 nullptr, // pInput (nullptr for renegotiate)
1678 0, // Reserved2
1679 nullptr, // phNewContext (we already have one)
1680 &outputBufferDesc, // pOutput
1681 &contextAttributes, // pfContextAttr
1682 &expiry // ptsExpiry
1683 );
1684 } else {
1685 status = AcceptSecurityContext(
1686 &credentialHandle, // phCredential
1687 &contextHandle, // phContext
1688 nullptr, // pInput
1689 contextReq, // fContextReq
1690 0, // TargetDataRep (unused)
1691 nullptr, // phNewContext
1692 &outputBufferDesc, // pOutput
1693 &contextAttributes, // pfContextAttr,
1694 &expiry // ptsTimeStamp
1695 );
1696 }
1697 if (status == SEC_I_CONTINUE_NEEDED) {
1698 schannelState = SchannelState::PerformHandshake;
1699 return sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer);
1700 } else if (status == SEC_E_OK) {
1701 schannelState = SchannelState::PerformHandshake;
1702 return true;
1703 }
1705 QSslSocket::tr("Renegotiation was unsuccessful: %1").arg(schannelErrorToString(status)));
1706 return false;
1707}
1708
1713void TlsCryptographSchannel::reset()
1714{
1715 Q_ASSERT(d);
1716
1717 closeCertificateStores(); // certificate stores could've changed
1718 deallocateContext();
1719 freeCredentialsHandle(); // in case we already had one (@future: session resumption requires re-use)
1720
1721 cipherInfo = {};
1722 connectionInfo = {};
1723 streamSizes = {};
1724
1725 CertFreeCertificateContext(localCertContext);
1726 localCertContext = nullptr;
1727
1728 contextAttributes = 0;
1729 intermediateBuffer.clear();
1730 schannelState = SchannelState::InitializeHandshake;
1731
1732
1733 d->setEncrypted(false);
1734 shutdown = false;
1735 renegotiating = false;
1736
1737 missingData = 0;
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
1754 if (q->isEncrypted())
1755 return; // let's not mess up the connection...
1756 reset();
1758}
1759
1761{
1762 Q_ASSERT(q);
1763 Q_ASSERT(d);
1764 auto *plainSocket = d->plainTcpSocket();
1765 Q_ASSERT(plainSocket);
1766
1768 return; // This function should not have been called
1769
1770 // Can happen if called through QSslSocket::abort->QSslSocket::close->QSslSocket::flush->here
1771 if (plainSocket->state() == QAbstractSocket::UnconnectedState || !plainSocket->isValid()
1772 || !plainSocket->isOpen()) {
1773 return;
1774 }
1775
1776 if (schannelState != SchannelState::Done) {
1778 return;
1779 }
1780
1781 auto &writeBuffer = d->tlsWriteBuffer();
1782 auto &buffer = d->tlsBuffer();
1783 if (q->isEncrypted()) { // encrypt data in writeBuffer and write it to plainSocket
1784 qint64 totalBytesWritten = 0;
1785 qint64 writeBufferSize;
1786 while ((writeBufferSize = writeBuffer.size()) > 0) {
1787 const int headerSize = int(streamSizes.cbHeader);
1788 const int trailerSize = int(streamSizes.cbTrailer);
1789 // Try to read 'cbMaximumMessage' bytes from buffer before encrypting.
1790 const int size = int(std::min(writeBufferSize, qint64(streamSizes.cbMaximumMessage)));
1791 QByteArray fullMessage(headerSize + trailerSize + size, Qt::Uninitialized);
1792 {
1793 // Use peek() here instead of read() so we don't lose data if encryption fails.
1794 qint64 copied = writeBuffer.peek(fullMessage.data() + headerSize, size);
1795 Q_ASSERT(copied == size);
1796 }
1797
1798 SecBuffer inputBuffers[4]{
1799 createSecBuffer(fullMessage.data(), headerSize, SECBUFFER_STREAM_HEADER),
1800 createSecBuffer(fullMessage.data() + headerSize, size, SECBUFFER_DATA),
1801 createSecBuffer(fullMessage.data() + headerSize + size, trailerSize, SECBUFFER_STREAM_TRAILER),
1802 createSecBuffer(nullptr, 0, SECBUFFER_EMPTY)
1803 };
1804 SecBufferDesc message{
1805 SECBUFFER_VERSION,
1806 ARRAYSIZE(inputBuffers),
1807 inputBuffers
1808 };
1809 auto status = EncryptMessage(&contextHandle, 0, &message, 0);
1810 if (status != SEC_E_OK) {
1811 setErrorAndEmit(d, QAbstractSocket::SslInternalError,
1812 QSslSocket::tr("Schannel failed to encrypt data: %1")
1813 .arg(schannelErrorToString(status)));
1814 return;
1815 }
1816 // Data was encrypted successfully, so we free() what we peek()ed earlier
1817 writeBuffer.free(size);
1818
1819 // The trailer's size is not final, so resize fullMessage to not send trailing junk
1820 fullMessage.resize(inputBuffers[0].cbBuffer + inputBuffers[1].cbBuffer + inputBuffers[2].cbBuffer);
1821 const qint64 bytesWritten = plainSocket->write(fullMessage);
1822#ifdef QSSLSOCKET_DEBUG
1823 qCDebug(lcTlsBackendSchannel, "Wrote %lld of total %d bytes", bytesWritten,
1824 fullMessage.length());
1825#endif
1826 if (bytesWritten >= 0) {
1827 totalBytesWritten += bytesWritten;
1828 } else {
1829 setErrorAndEmit(d, plainSocket->error(), plainSocket->errorString());
1830 return;
1831 }
1832 }
1833
1834 if (totalBytesWritten > 0) {
1835 // Don't emit bytesWritten() recursively.
1836 bool &emittedBytesWritten = d->tlsEmittedBytesWritten();
1837 if (!emittedBytesWritten) {
1838 emittedBytesWritten = true;
1839 emit q->bytesWritten(totalBytesWritten);
1840 emittedBytesWritten = false;
1841 }
1842 emit q->channelBytesWritten(0, totalBytesWritten);
1843 }
1844 }
1845
1846 int totalRead = 0;
1847 bool hadIncompleteData = false;
1848 const auto readBufferMaxSize = d->maxReadBufferSize();
1849 while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) {
1850 if (missingData > plainSocket->bytesAvailable()
1851 && (!readBufferMaxSize || readBufferMaxSize >= missingData)) {
1852#ifdef QSSLSOCKET_DEBUG
1853 qCDebug(lcTlsBackendSchannel, "We're still missing %lld bytes, will check later.",
1854 missingData);
1855#endif
1856 break;
1857 }
1858
1859 missingData = 0;
1860 const qint64 bytesRead = readToBuffer(intermediateBuffer, plainSocket);
1861#ifdef QSSLSOCKET_DEBUG
1862 qCDebug(lcTlsBackendSchannel, "Read %lld encrypted bytes from the socket", bytesRead);
1863#endif
1864 if (intermediateBuffer.length() == 0 || (hadIncompleteData && bytesRead == 0)) {
1865#ifdef QSSLSOCKET_DEBUG
1866 qCDebug(lcTlsBackendSchannel,
1867 hadIncompleteData ? "No new data received, leaving loop!"
1868 : "Nothing to decrypt, leaving loop!");
1869#endif
1870 break;
1871 }
1872 hadIncompleteData = false;
1873#ifdef QSSLSOCKET_DEBUG
1874 qCDebug(lcTlsBackendSchannel, "Total amount of bytes to decrypt: %d",
1875 intermediateBuffer.length());
1876#endif
1877
1878 SecBuffer dataBuffer[4]{
1879 createSecBuffer(intermediateBuffer, SECBUFFER_DATA),
1880 createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
1881 createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
1882 createSecBuffer(nullptr, 0, SECBUFFER_EMPTY)
1883 };
1884 SecBufferDesc message{
1885 SECBUFFER_VERSION,
1886 ARRAYSIZE(dataBuffer),
1887 dataBuffer
1888 };
1889 auto status = DecryptMessage(&contextHandle, &message, 0, nullptr);
1890 if (status == SEC_E_OK || status == SEC_I_RENEGOTIATE || status == SEC_I_CONTEXT_EXPIRED) {
1891 // There can still be 0 output even if it succeeds, this is fine
1892 if (dataBuffer[1].cbBuffer > 0) {
1893 // It is always decrypted in-place.
1894 // But [0] is the STREAM_HEADER, [1] is the DATA and [2] is the STREAM_TRAILER.
1895 // The pointers in all of those still point into 'intermediateBuffer'.
1896 buffer.append(static_cast<char *>(dataBuffer[1].pvBuffer),
1897 dataBuffer[1].cbBuffer);
1898 totalRead += dataBuffer[1].cbBuffer;
1899#ifdef QSSLSOCKET_DEBUG
1900 qCDebug(lcTlsBackendSchannel, "Decrypted %lu bytes. New read buffer size: %d",
1901 dataBuffer[1].cbBuffer, buffer.size());
1902#endif
1903 }
1904 if (dataBuffer[3].BufferType == SECBUFFER_EXTRA) {
1905 // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
1906 // dataBuffer[3].cbBuffer indicates the amount of bytes _NOT_ processed,
1907 // the rest need to be stored.
1908 retainExtraData(intermediateBuffer, dataBuffer[3]);
1909 } else {
1910 intermediateBuffer.resize(0);
1911 }
1912 }
1913
1914 if (status == SEC_E_INCOMPLETE_MESSAGE) {
1915 missingData = checkIncompleteData(dataBuffer[0]);
1916#ifdef QSSLSOCKET_DEBUG
1917 qCDebug(lcTlsBackendSchannel, "We didn't have enough data to decrypt anything, will try again!");
1918#endif
1919 // We try again, but if we don't get any more data then we leave
1920 hadIncompleteData = true;
1921 } else if (status == SEC_E_INVALID_HANDLE) {
1922 // I don't think this should happen, if it does we're done...
1923 qCWarning(lcTlsBackendSchannel, "The internal SSPI handle is invalid!");
1924 Q_UNREACHABLE();
1925 } else if (status == SEC_E_INVALID_TOKEN) {
1926 // Supposedly we have an invalid token, it's under-documented what
1927 // this means, so to be safe we disconnect.
1928 shutdown = true;
1930 setErrorAndEmit(d, QAbstractSocket::SslInternalError, schannelErrorToString(status));
1931 break;
1932 } else if (status == SEC_E_MESSAGE_ALTERED) {
1933 // The message has been altered, disconnect now.
1934 shutdown = true; // skips sending the shutdown alert
1936 setErrorAndEmit(d, QAbstractSocket::SslInternalError,
1937 schannelErrorToString(status));
1938 break;
1939 } else if (status == SEC_E_OUT_OF_SEQUENCE) {
1940 // @todo: I don't know if this one is actually "fatal"..
1941 // This path might never be hit as it seems this is for connection-oriented connections,
1942 // while SEC_E_MESSAGE_ALTERED is for stream-oriented ones (what we use).
1943 shutdown = true; // skips sending the shutdown alert
1945 setErrorAndEmit(d, QAbstractSocket::SslInternalError,
1946 schannelErrorToString(status));
1947 break;
1948 } else if (status == SEC_I_CONTEXT_EXPIRED) {
1949 // 'remote' has initiated a shutdown
1951 break;
1952 } else if (status == SEC_I_RENEGOTIATE) {
1953 // 'remote' wants to renegotiate
1954#ifdef QSSLSOCKET_DEBUG
1955 qCDebug(lcTlsBackendSchannel, "The peer wants to renegotiate.");
1956#endif
1957 schannelState = SchannelState::Renegotiate;
1958 renegotiating = true;
1959
1960 // We need to call 'continueHandshake' or else there's no guarantee it ever gets called
1962 break;
1963 }
1964 }
1965
1966 if (totalRead) {
1967 if (bool *readyReadEmittedPointer = d->readyReadPointer())
1968 *readyReadEmittedPointer = true;
1969 emit q->readyRead();
1970 emit q->channelReadyRead(0);
1971 }
1972}
1973
1974void TlsCryptographSchannel::sendShutdown()
1975{
1976 Q_ASSERT(d);
1977
1978 const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
1979 DWORD shutdownToken = SCHANNEL_SHUTDOWN;
1980 SecBuffer buffer = createSecBuffer(&shutdownToken, sizeof(DWORD), SECBUFFER_TOKEN);
1981 SecBufferDesc token{
1982 SECBUFFER_VERSION,
1983 1,
1984 &buffer
1985 };
1986 auto status = ApplyControlToken(&contextHandle, &token);
1987
1988 if (status != SEC_E_OK) {
1989#ifdef QSSLSOCKET_DEBUG
1990 qCDebug(lcTlsBackendSchannel)
1991 << "Failed to apply shutdown control token:" << schannelErrorToString(status);
1992#endif
1993 return;
1994 }
1995
1996 SecBuffer outBuffers[3];
1997 outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
1998 outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
1999 outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
2000 auto freeBuffers = qScopeGuard([&outBuffers]() {
2001 for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
2002 if (outBuffers[i].pvBuffer)
2003 FreeContextBuffer(outBuffers[i].pvBuffer);
2004 }
2005 });
2006 SecBufferDesc outputBufferDesc{
2007 SECBUFFER_VERSION,
2008 ARRAYSIZE(outBuffers),
2009 outBuffers
2010 };
2011
2012 ULONG contextReq = getContextRequirements();
2013 TimeStamp expiry;
2014 if (isClient) {
2015 status = InitializeSecurityContext(&credentialHandle, // phCredential
2016 &contextHandle, // phContext
2017 const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
2018 contextReq, // fContextReq
2019 0, // Reserved1
2020 0, // TargetDataRep (unused)
2021 nullptr, // pInput
2022 0, // Reserved2
2023 nullptr, // phNewContext (we already have one)
2024 &outputBufferDesc, // pOutput
2025 &contextAttributes, // pfContextAttr
2026 &expiry // ptsExpiry
2027 );
2028 } else {
2029 status = AcceptSecurityContext(
2030 &credentialHandle, // phCredential
2031 &contextHandle, // phContext
2032 nullptr, // pInput
2033 contextReq, // fContextReq
2034 0, // TargetDataRep (unused)
2035 nullptr, // phNewContext
2036 &outputBufferDesc, // pOutput
2037 &contextAttributes, // pfContextAttr,
2038 &expiry // ptsTimeStamp
2039 );
2040 }
2041 if (status == SEC_E_OK || status == SEC_I_CONTEXT_EXPIRED) {
2042 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, false)) {
2043 // We failed to send the shutdown message, but it's not that important since we're
2044 // shutting down anyway.
2045 return;
2046 }
2047 } else {
2048#ifdef QSSLSOCKET_DEBUG
2049 qCDebug(lcTlsBackendSchannel)
2050 << "Failed to initialize shutdown:" << schannelErrorToString(status);
2051#endif
2052 }
2053}
2054
2056{
2057 Q_ASSERT(q);
2058 Q_ASSERT(d);
2059 auto *plainSocket = d->plainTcpSocket();
2060 Q_ASSERT(plainSocket);
2061
2062 if (SecIsValidHandle(&contextHandle)) {
2063 if (!shutdown) {
2064 shutdown = true;
2065 if (plainSocket->state() != QAbstractSocket::UnconnectedState && q->isEncrypted()) {
2066 sendShutdown();
2067 transmit();
2068 }
2069 }
2070 }
2071 plainSocket->disconnectFromHost();
2072}
2073
2075{
2076 Q_ASSERT(d);
2077 auto *plainSocket = d->plainTcpSocket();
2078 Q_ASSERT(plainSocket);
2079 d->setEncrypted(false);
2080
2081 shutdown = true;
2082 if (plainSocket->bytesAvailable() > 0 || hasUndecryptedData()) {
2083 // Read as much as possible because this is likely our last chance
2084 qint64 tempMax = d->maxReadBufferSize();
2085 d->setMaxReadBufferSize(0); // Unlimited
2086 transmit();
2087 d->setMaxReadBufferSize(tempMax);
2088 // Since there were bytes still available we don't want to deallocate
2089 // our context yet. It will happen later, when the socket is re-used or
2090 // destroyed.
2091 } else {
2092 deallocateContext();
2093 freeCredentialsHandle();
2094 }
2095}
2096
2098{
2099 Q_ASSERT(q);
2100
2101 if (!q->isEncrypted())
2102 return {};
2103
2104 const auto sessionProtocol = toQtSslProtocol(connectionInfo.dwProtocol);
2105
2106 const auto ciphers = ciphersByName(QStringView(cipherInfo.szCipherSuite));
2107 for (const auto& cipher : ciphers) {
2108 if (cipher.protocol() == sessionProtocol)
2109 return cipher;
2110 }
2111
2112 return {};
2113}
2114
2116{
2117 if (!q->isEncrypted())
2119 return toQtSslProtocol(connectionInfo.dwProtocol);
2120}
2121
2123{
2124 Q_ASSERT(q);
2125 Q_ASSERT(d);
2126 auto *plainSocket = d->plainTcpSocket();
2127 Q_ASSERT(plainSocket);
2128
2129 const bool isServer = d->tlsMode() == QSslSocket::SslServerMode;
2130 switch (schannelState) {
2131 case SchannelState::InitializeHandshake:
2132 if (!SecIsValidHandle(&credentialHandle) && !acquireCredentialsHandle()) {
2134 return;
2135 }
2136 if (!SecIsValidHandle(&credentialHandle)) // Needed to support tst_QSslSocket::setEmptyKey
2137 return;
2138 if (!SecIsValidHandle(&contextHandle) && !(isServer ? acceptContext() : createContext())) {
2140 return;
2141 }
2142 if (schannelState != SchannelState::PerformHandshake)
2143 break;
2144 Q_FALLTHROUGH();
2145 case SchannelState::PerformHandshake:
2146 if (!performHandshake()) {
2148 return;
2149 }
2150 if (schannelState != SchannelState::VerifyHandshake)
2151 break;
2152 Q_FALLTHROUGH();
2153 case SchannelState::VerifyHandshake:
2154 // if we're in shutdown or renegotiating then we might not need to verify
2155 // (since we already did)
2156 if (!verifyHandshake()) {
2157 shutdown = true; // Skip sending shutdown alert
2158 q->abort(); // We don't want to send buffered data
2160 return;
2161 }
2162 if (schannelState != SchannelState::Done)
2163 break;
2164 Q_FALLTHROUGH();
2165 case SchannelState::Done:
2166 // connectionEncrypted is already true if we come here from a renegotiation
2167 if (!q->isEncrypted()) {
2168 d->setEncrypted(true); // all is done
2169 emit q->encrypted();
2170 }
2171 renegotiating = false;
2172 if (d->isPendingClose()) {
2173 d->setPendingClose(false);
2175 } else {
2176 transmit();
2177 }
2178 break;
2179 case SchannelState::Renegotiate:
2180 if (!renegotiate()) {
2182 return;
2183 } else if (intermediateBuffer.size() || plainSocket->bytesAvailable()) {
2185 }
2186 break;
2187 }
2188}
2189
2191{
2192 return sslErrors;
2193}
2194
2195/*
2196 Copied from qsslsocket_mac.cpp, which was copied from qsslsocket_openssl.cpp
2197*/
2198bool TlsCryptographSchannel::checkSslErrors()
2199{
2200 if (sslErrors.isEmpty())
2201 return true;
2202
2203 Q_ASSERT(q);
2204 Q_ASSERT(d);
2205 const auto &configuration = q->sslConfiguration();
2206 auto *plainSocket = d->plainTcpSocket();
2207
2208 emit q->sslErrors(sslErrors);
2209
2210 const bool doVerifyPeer = configuration.peerVerifyMode() == QSslSocket::VerifyPeer
2211 || (configuration.peerVerifyMode() == QSslSocket::AutoVerifyPeer
2213 const bool doEmitSslError = !d->verifyErrorsHaveBeenIgnored();
2214 // check whether we need to emit an SSL handshake error
2215 if (doVerifyPeer && doEmitSslError) {
2216 if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) {
2218 d->setPaused(true);
2219 } else {
2221 sslErrors.constFirst().errorString());
2222 plainSocket->disconnectFromHost();
2223 }
2224 return false;
2225 }
2226
2227 return true;
2228}
2229
2230void TlsCryptographSchannel::initializeCertificateStores()
2231{
2233 Q_ASSERT(d);
2234 Q_ASSERT(q);
2235 const auto &configuration = q->sslConfiguration();
2236
2237 auto createStoreFromCertificateChain = [](const QList<QSslCertificate> certChain, const QSslKey &privateKey) {
2238 const wchar_t *passphrase = L"";
2239 // Need to embed the private key in the certificate
2240 QByteArray pkcs12 = _q_makePkcs12(certChain,
2241 privateKey,
2242 QString::fromWCharArray(passphrase, 0));
2243 CRYPT_DATA_BLOB pfxBlob;
2244 pfxBlob.cbData = DWORD(pkcs12.length());
2245 pfxBlob.pbData = reinterpret_cast<unsigned char *>(pkcs12.data());
2246 return QHCertStorePointer(PFXImportCertStore(&pfxBlob, passphrase, 0));
2247 };
2248
2249 if (!configuration.localCertificateChain().isEmpty()) {
2250 if (configuration.privateKey().isNull()) {
2252 QSslSocket::tr("Cannot provide a certificate with no key"));
2253 return;
2254 }
2255 if (localCertificateStore == nullptr) {
2256 localCertificateStore = createStoreFromCertificateChain(configuration.localCertificateChain(),
2257 configuration.privateKey());
2258 if (localCertificateStore == nullptr)
2259 qCWarning(lcTlsBackendSchannel, "Failed to load certificate chain!");
2260 }
2261 }
2262
2263 if (!configuration.caCertificates().isEmpty() && !caCertificateStore) {
2264 caCertificateStore = createStoreFromCertificateChain(configuration.caCertificates(),
2265 {}); // No private key for the CA certs
2266 }
2267}
2268
2269bool TlsCryptographSchannel::verifyCertContext(CERT_CONTEXT *certContext)
2270{
2271 Q_ASSERT(certContext);
2272 Q_ASSERT(q);
2273 Q_ASSERT(d);
2274
2275 const bool isClient = d->tlsMode() == QSslSocket::SslClientMode;
2276
2277 // Create a collection of stores so we can pass in multiple stores as additional locations to
2278 // search for the certificate chain
2279 auto tempCertCollection = QHCertStorePointer(CertOpenStore(CERT_STORE_PROV_COLLECTION,
2280 X509_ASN_ENCODING,
2281 0,
2282 CERT_STORE_CREATE_NEW_FLAG,
2283 nullptr));
2284 if (!tempCertCollection) {
2285#ifdef QSSLSOCKET_DEBUG
2286 qCWarning(lcTlsBackendSchannel, "Failed to create certificate store collection!");
2287#endif
2288 return false;
2289 }
2290
2291 if (rootCertOnDemandLoadingAllowed()) {
2292 // @future(maybe): following the OpenSSL backend these certificates should be added into
2293 // the Ca list, not just included during verification.
2294 // That being said, it's not trivial to add the root certificates (if and only if they
2295 // came from the system root store). And I don't see this mentioned in our documentation.
2296 auto rootStore = QHCertStorePointer(
2297 CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0,
2298 CERT_STORE_READONLY_FLAG | CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT"));
2299
2300 if (!rootStore) {
2301#ifdef QSSLSOCKET_DEBUG
2302 qCWarning(lcTlsBackendSchannel, "Failed to open the system root CA certificate store!");
2303#endif
2304 return false;
2305 } else if (!CertAddStoreToCollection(tempCertCollection.get(), rootStore.get(), 0, 1)) {
2306#ifdef QSSLSOCKET_DEBUG
2307 qCWarning(lcTlsBackendSchannel,
2308 "Failed to add the system root CA certificate store to the certificate store "
2309 "collection!");
2310#endif
2311 return false;
2312 }
2313 }
2314 if (caCertificateStore) {
2315 if (!CertAddStoreToCollection(tempCertCollection.get(), caCertificateStore.get(), 0, 1)) {
2316#ifdef QSSLSOCKET_DEBUG
2317 qCWarning(lcTlsBackendSchannel,
2318 "Failed to add the user's CA certificate store to the certificate store "
2319 "collection!");
2320#endif
2321 return false;
2322 }
2323 }
2324
2325 if (!CertAddStoreToCollection(tempCertCollection.get(), certContext->hCertStore, 0, 0)) {
2326#ifdef QSSLSOCKET_DEBUG
2327 qCWarning(lcTlsBackendSchannel,
2328 "Failed to add certificate's origin store to the certificate store collection!");
2329#endif
2330 return false;
2331 }
2332
2333 CERT_CHAIN_PARA parameters;
2334 ZeroMemory(&parameters, sizeof(parameters));
2335 parameters.cbSize = sizeof(CERT_CHAIN_PARA);
2336 parameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
2337 parameters.RequestedUsage.Usage.cUsageIdentifier = 1;
2338 LPSTR oid = LPSTR(isClient ? szOID_PKIX_KP_SERVER_AUTH
2339 : szOID_PKIX_KP_CLIENT_AUTH);
2340 parameters.RequestedUsage.Usage.rgpszUsageIdentifier = &oid;
2341
2342 QTlsBackend::clearPeerCertificates(d);
2343 const CERT_CHAIN_CONTEXT *chainContext = nullptr;
2344 auto freeCertChain = qScopeGuard([&chainContext]() {
2345 if (chainContext)
2346 CertFreeCertificateChain(chainContext);
2347 });
2348 BOOL status = CertGetCertificateChain(nullptr, // hChainEngine, default
2349 certContext, // pCertContext
2350 nullptr, // pTime, 'now'
2351 tempCertCollection.get(), // hAdditionalStore, additional cert store
2352 &parameters, // pChainPara
2353 CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, // dwFlags
2354 nullptr, // reserved
2355 &chainContext // ppChainContext
2356 );
2357 if (status == FALSE || !chainContext || chainContext->cChain == 0) {
2359 sslErrors += error;
2360 emit q->peerVerifyError(error);
2361 return q->state() == QAbstractSocket::ConnectedState;
2362 }
2363
2364 // Helper-function to get a QSslCertificate given a CERT_CHAIN_ELEMENT
2365 static auto getCertificateFromChainElement = [](CERT_CHAIN_ELEMENT *element) {
2366 if (!element)
2367 return QSslCertificate();
2368
2369 const CERT_CONTEXT *certContext = element->pCertContext;
2371 };
2372
2373 // Pick a chain to use as the certificate chain, if multiple are available:
2374 // According to https://docs.microsoft.com/en-gb/windows/desktop/api/wincrypt/ns-wincrypt-_cert_chain_context
2375 // this seems to be the best way to get a trusted chain.
2376 CERT_SIMPLE_CHAIN *chain = chainContext->rgpChain[chainContext->cChain - 1];
2377
2378 if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN) {
2380 getCertificateFromChainElement(chain->rgpElement[chain->cElement - 1]));
2381 sslErrors += error;
2382 emit q->peerVerifyError(error);
2383 if (q->state() != QAbstractSocket::ConnectedState)
2384 return false;
2385 }
2386 if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_INVALID_BASIC_CONSTRAINTS) {
2387 // @Note: This is actually one of two errors:
2388 // "either the certificate cannot be used to issue other certificates, or the chain path length has been exceeded."
2389 // But here we are checking the chain's status, so we assume the "issuing" error cannot occur here.
2391 sslErrors += error;
2392 emit q->peerVerifyError(error);
2393 if (q->state() != QAbstractSocket::ConnectedState)
2394 return false;
2395 }
2396 static const DWORD leftoverCertChainErrorMask = CERT_TRUST_IS_CYCLIC | CERT_TRUST_INVALID_EXTENSION
2397 | CERT_TRUST_INVALID_POLICY_CONSTRAINTS | CERT_TRUST_INVALID_NAME_CONSTRAINTS
2398 | CERT_TRUST_CTL_IS_NOT_TIME_VALID | CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID
2399 | CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE;
2400 if (chain->TrustStatus.dwErrorStatus & leftoverCertChainErrorMask) {
2402 sslErrors += error;
2403 emit q->peerVerifyError(error);
2404 if (q->state() != QAbstractSocket::ConnectedState)
2405 return false;
2406 }
2407
2408 DWORD verifyDepth = chain->cElement;
2409 if (q->peerVerifyDepth() > 0 && DWORD(q->peerVerifyDepth()) < verifyDepth)
2410 verifyDepth = DWORD(q->peerVerifyDepth());
2411
2412 const auto &caCertificates = q->sslConfiguration().caCertificates();
2413
2414 if (!rootCertOnDemandLoadingAllowed()
2415 && !(chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN)
2416 && (q->peerVerifyMode() == QSslSocket::VerifyPeer
2417 || (isClient && q->peerVerifyMode() == QSslSocket::AutoVerifyPeer))) {
2418 // When verifying a peer Windows "helpfully" builds a chain that
2419 // may include roots from the system store. But we don't want that if
2420 // the user has set their own CA certificates.
2421 // Since Windows claims this is not a partial chain the root is included
2422 // and we have to check that it is one of our configured CAs.
2423 CERT_CHAIN_ELEMENT *element = chain->rgpElement[chain->cElement - 1];
2424 QSslCertificate certificate = getCertificateFromChainElement(element);
2425 if (!caCertificates.contains(certificate)) {
2426 auto error = QSslError(QSslError::CertificateUntrusted, certificate);
2427 sslErrors += error;
2428 emit q->peerVerifyError(error);
2429 if (q->state() != QAbstractSocket::ConnectedState)
2430 return false;
2431 }
2432 }
2433
2434 QList<QSslCertificate> peerCertificateChain;
2435 for (DWORD i = 0; i < verifyDepth; i++) {
2436 CERT_CHAIN_ELEMENT *element = chain->rgpElement[i];
2437 QSslCertificate certificate = getCertificateFromChainElement(element);
2438 if (certificate.isNull()) {
2439 const auto &previousCert = !peerCertificateChain.isEmpty() ? peerCertificateChain.last()
2440 : QSslCertificate();
2442 sslErrors += error;
2443 emit q->peerVerifyError(error);
2444 if (previousCert.isNull() || q->state() != QAbstractSocket::ConnectedState)
2445 return false;
2446 }
2447 const QList<QSslCertificateExtension> extensions = certificate.extensions();
2448
2449#ifdef QSSLSOCKET_DEBUG
2450 qCDebug(lcTlsBackendSchannel) << "issuer:" << certificate.issuerDisplayName()
2451 << "\nsubject:" << certificate.subjectDisplayName()
2452 << "\nQSslCertificate info:" << certificate
2453 << "\nextended error info:" << element->pwszExtendedErrorInfo
2454 << "\nerror status:" << element->TrustStatus.dwErrorStatus;
2455#endif
2456
2457 peerCertificateChain.append(certificate);
2458 QTlsBackend::storePeerCertificateChain(d, peerCertificateChain);
2459
2460 if (certificate.isBlacklisted()) {
2461 const auto error = QSslError(QSslError::CertificateBlacklisted, certificate);
2462 sslErrors += error;
2463 emit q->peerVerifyError(error);
2464 if (q->state() != QAbstractSocket::ConnectedState)
2465 return false;
2466 }
2467
2468 LONG result = CertVerifyTimeValidity(nullptr /*== now */, element->pCertContext->pCertInfo);
2469 if (result != 0) {
2472 certificate);
2473 sslErrors += error;
2474 emit q->peerVerifyError(error);
2475 if (q->state() != QAbstractSocket::ConnectedState)
2476 return false;
2477 }
2478
2480 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID) {
2481 // handled right above
2482 Q_ASSERT(!sslErrors.isEmpty());
2483 }
2484 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED) {
2485 auto error = QSslError(QSslError::CertificateRevoked, certificate);
2486 sslErrors += error;
2487 emit q->peerVerifyError(error);
2488 if (q->state() != QAbstractSocket::ConnectedState)
2489 return false;
2490 }
2491 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID) {
2493 sslErrors += error;
2494 emit q->peerVerifyError(error);
2495 if (q->state() != QAbstractSocket::ConnectedState)
2496 return false;
2497 }
2498
2499 // While netscape shouldn't be relevant now it defined an extension which is
2500 // still in use. Schannel does not check this automatically, so we do it here.
2501 // It is used to differentiate between client and server certificates.
2502 if (netscapeWrongCertType(extensions, isClient))
2503 element->TrustStatus.dwErrorStatus |= CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
2504
2505 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_VALID_FOR_USAGE) {
2506 auto error = QSslError(QSslError::InvalidPurpose, certificate);
2507 sslErrors += error;
2508 emit q->peerVerifyError(error);
2509 if (q->state() != QAbstractSocket::ConnectedState)
2510 return false;
2511 }
2512 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_UNTRUSTED_ROOT) {
2513 // Override this error if we have the certificate inside our trusted CAs list.
2514 const bool isTrustedRoot = caCertificates.contains(certificate);
2515 if (!isTrustedRoot) {
2516 auto error = QSslError(QSslError::CertificateUntrusted, certificate);
2517 sslErrors += error;
2518 emit q->peerVerifyError(error);
2519 if (q->state() != QAbstractSocket::ConnectedState)
2520 return false;
2521 }
2522 }
2523 static const DWORD certRevocationCheckUnavailableError = CERT_TRUST_IS_OFFLINE_REVOCATION
2524 | CERT_TRUST_REVOCATION_STATUS_UNKNOWN;
2525 if (element->TrustStatus.dwErrorStatus & certRevocationCheckUnavailableError) {
2526 // @future(maybe): Do something with this
2527 }
2528
2529 // Dumping ground of errors that don't fit our specific errors
2530 static const DWORD leftoverCertErrorMask = CERT_TRUST_IS_CYCLIC
2531 | CERT_TRUST_INVALID_EXTENSION | CERT_TRUST_INVALID_NAME_CONSTRAINTS
2532 | CERT_TRUST_INVALID_POLICY_CONSTRAINTS
2533 | CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT
2534 | CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT
2535 | CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT
2536 | CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT
2537 | CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT;
2538 if (element->TrustStatus.dwErrorStatus & leftoverCertErrorMask) {
2539 auto error = QSslError(QSslError::UnspecifiedError, certificate);
2540 sslErrors += error;
2541 emit q->peerVerifyError(error);
2542 if (q->state() != QAbstractSocket::ConnectedState)
2543 return false;
2544 }
2545 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_INVALID_BASIC_CONSTRAINTS) {
2546 auto it = std::find_if(extensions.cbegin(), extensions.cend(),
2548 return extension.name() == "basicConstraints"_L1;
2549 });
2550 if (it != extensions.cend()) {
2551 // @Note: This is actually one of two errors:
2552 // "either the certificate cannot be used to issue other certificates,
2553 // or the chain path length has been exceeded."
2554 QVariantMap basicConstraints = it->value().toMap();
2556 if (i > 0 && !basicConstraints.value("ca"_L1, false).toBool())
2558 else
2560 sslErrors += error;
2561 emit q->peerVerifyError(error);
2562 if (q->state() != QAbstractSocket::ConnectedState)
2563 return false;
2564 }
2565 }
2566 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_EXPLICIT_DISTRUST) {
2568 sslErrors += error;
2569 emit q->peerVerifyError(error);
2570 if (q->state() != QAbstractSocket::ConnectedState)
2571 return false;
2572 }
2573
2574 if (element->TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED) {
2575 // If it's self-signed *and* a CA then we can assume it's a root CA certificate
2576 // and we can ignore the "self-signed" note:
2577 // We check the basicConstraints certificate extension when possible, but this didn't
2578 // exist for version 1, so we can only guess in that case
2579 const bool isRootCertificateAuthority = isCertificateAuthority(extensions)
2580 || certificate.version() == "1";
2581
2582 // Root certificate tends to be signed by themselves, so ignore self-signed status.
2583 if (!isRootCertificateAuthority) {
2585 sslErrors += error;
2586 emit q->peerVerifyError(error);
2587 if (q->state() != QAbstractSocket::ConnectedState)
2588 return false;
2589 }
2590 }
2591 }
2592
2593 if (!peerCertificateChain.isEmpty())
2594 QTlsBackend::storePeerCertificate(d, peerCertificateChain.constFirst());
2595
2596 const auto &configuration = q->sslConfiguration(); // Probably, updated by QTlsBackend::storePeerCertificate etc.
2597 // @Note: Somewhat copied from qsslsocket_mac.cpp
2598 const bool doVerifyPeer = q->peerVerifyMode() == QSslSocket::VerifyPeer
2599 || (q->peerVerifyMode() == QSslSocket::AutoVerifyPeer
2601 // Check the peer certificate itself. First try the subject's common name
2602 // (CN) as a wildcard, then try all alternate subject name DNS entries the
2603 // same way.
2604 if (!configuration.peerCertificate().isNull()) {
2605 // but only if we're a client connecting to a server
2606 // if we're the server, don't check CN
2607 if (d->tlsMode() == QSslSocket::SslClientMode) {
2608 const auto verificationPeerName = d->verificationName();
2609 const QString peerName(verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName);
2610 if (!isMatchingHostname(configuration.peerCertificate(), peerName)) {
2611 // No matches in common names or alternate names.
2612 const QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate());
2613 sslErrors += error;
2614 emit q->peerVerifyError(error);
2615 if (q->state() != QAbstractSocket::ConnectedState)
2616 return false;
2617 }
2618 }
2619 } else if (doVerifyPeer) {
2620 // No peer certificate presented. Report as error if the socket
2621 // expected one.
2623 sslErrors += error;
2624 emit q->peerVerifyError(error);
2625 if (q->state() != QAbstractSocket::ConnectedState)
2626 return false;
2627 }
2628
2629 return true;
2630}
2631
2632bool TlsCryptographSchannel::rootCertOnDemandLoadingAllowed()
2633{
2634 Q_ASSERT(d);
2636}
2637
2638} // namespace QTlsPrivate
2639
qint64 bytesAvailable() const override
Returns the number of incoming bytes that are waiting to be read.
virtual void disconnectFromHost()
Attempts to close the socket.
bool isValid() const
Returns true if the socket is valid and ready for use; otherwise returns false.
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
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
qsizetype length() const noexcept
Same as size().
Definition qbytearray.h:499
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
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:409
\inmodule QtCore\reentrant
Definition qdatastream.h:46
bool isOpen() const
Returns true if the device is open; otherwise returns false.
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
bool isEmpty() const noexcept
Definition qlist.h:401
const T & constFirst() const noexcept
Definition qlist.h:647
void clear()
Definition qlist.h:434
static Q_CORE_EXPORT QOperatingSystemVersionBase current()
\inmodule QtCore
Definition qmutex.h:309
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 isNull() const
Returns true if this is a null certificate (i.e., a certificate with no contents); otherwise returns ...
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:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1309
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:8084
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...
void extension()
[6]
Definition dialogs.cpp:230
QSet< QString >::iterator it
Token token
Definition keywords.cpp:444
QVector3D minimum(const QVector3D &v1, const QVector3D &v2) Q_DECL_NOTHROW
Definition qssgutils_p.h:54
SslProtocol
Describes the protocol of the cipher.
Definition qssl.h:50
@ DtlsV1_2
Definition qssl.h:63
@ TlsV1_2OrLater
Definition qssl.h:59
@ TlsV1_3
Definition qssl.h:66
@ DtlsV1_2OrLater
Definition qssl.h:64
@ TlsV1_3OrLater
Definition qssl.h:67
@ SecureProtocols
Definition qssl.h:55
@ TlsV1_2
Definition qssl.h:53
@ AnyProtocol
Definition qssl.h:54
@ UnknownProtocol
Definition qssl.h:69
Combined button and popup list for selecting options.
Namespace containing onternal types that TLS backends implement.
QList< QSslCipher > defaultCiphers()
QList< QSslCertificate >(*)(const QByteArray &pem, int count) X509PemReaderPtr
UNICODE_STRING cbcChainingMode
QList< CRYPTO_SETTINGS > cryptoSettingsForCiphers(const QList< QSslCipher > &ciphers)
bool(*)(QIODevice *device, QSslKey *key, QSslCertificate *cert, QList< QSslCertificate > *caCertificates, const QByteArray &passPhrase) X509Pkcs12ReaderPtr
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
X509PemReaderPtr X509DerReaderPtr
constexpr Initialization Uninitialized
#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
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
#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:50
unsigned short quint16
Definition qtypes.h:48
int qint32
Definition qtypes.h:49
long long qint64
Definition qtypes.h:60
std::unique_ptr< void, QHCertStoreDeleter > QHCertStorePointer
Definition qwincrypt_p.h:41
QList< QChar > characters
QSettings settings("MySoft", "Star Runner")
[0]
QObject::connect nullptr
QHostInfo info
[0]
const auto certs
[1]
QList< QSsl::SslProtocol > protocols