1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 // #define QSSLSOCKET_DEBUG
41
42 #include "qssl_p.h"
43 #include "qsslsocket.h"
44 #include "qsslsocket_schannel_p.h"
45 #include "qsslcertificate.h"
46 #include "qsslcertificateextension.h"
47 #include "qsslcertificate_p.h"
48 #include "qsslcipher_p.h"
49
50 #include <QtCore/qscopeguard.h>
51 #include <QtCore/qoperatingsystemversion.h>
52 #include <QtCore/qregularexpression.h>
53 #include <QtCore/qdatastream.h>
54 #include <QtCore/qmutex.h>
55
56 #define SECURITY_WIN32
57 #include <security.h>
58 #include <schnlsp.h>
59
60 #if NTDDI_VERSION >= NTDDI_WINBLUE && !defined(Q_CC_MINGW)
61 // ALPN = Application Layer Protocol Negotiation
62 #define SUPPORTS_ALPN 1
63 #endif
64
65 // Not defined in MinGW
66 #ifndef SECBUFFER_ALERT
67 #define SECBUFFER_ALERT 17
68 #endif
69 #ifndef SECPKG_ATTR_APPLICATION_PROTOCOL
70 #define SECPKG_ATTR_APPLICATION_PROTOCOL 35
71 #endif
72
73 // Another missing MinGW define
74 #ifndef SEC_E_APPLICATION_PROTOCOL_MISMATCH
75 #define SEC_E_APPLICATION_PROTOCOL_MISMATCH _HRESULT_TYPEDEF_(0x80090367L)
76 #endif
77
78 // Also not defined in MinGW.......
79 #ifndef SP_PROT_TLS1_SERVER
80 #define SP_PROT_TLS1_SERVER 0x00000040
81 #endif
82 #ifndef SP_PROT_TLS1_CLIENT
83 #define SP_PROT_TLS1_CLIENT 0x00000080
84 #endif
85 #ifndef SP_PROT_TLS1_0_SERVER
86 #define SP_PROT_TLS1_0_SERVER SP_PROT_TLS1_SERVER
87 #endif
88 #ifndef SP_PROT_TLS1_0_CLIENT
89 #define SP_PROT_TLS1_0_CLIENT SP_PROT_TLS1_CLIENT
90 #endif
91 #ifndef SP_PROT_TLS1_0
92 #define SP_PROT_TLS1_0 (SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_0_SERVER)
93 #endif
94 #ifndef SP_PROT_TLS1_1_SERVER
95 #define SP_PROT_TLS1_1_SERVER 0x00000100
96 #endif
97 #ifndef SP_PROT_TLS1_1_CLIENT
98 #define SP_PROT_TLS1_1_CLIENT 0x00000200
99 #endif
100 #ifndef SP_PROT_TLS1_1
101 #define SP_PROT_TLS1_1 (SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_1_SERVER)
102 #endif
103 #ifndef SP_PROT_TLS1_2_SERVER
104 #define SP_PROT_TLS1_2_SERVER 0x00000400
105 #endif
106 #ifndef SP_PROT_TLS1_2_CLIENT
107 #define SP_PROT_TLS1_2_CLIENT 0x00000800
108 #endif
109 #ifndef SP_PROT_TLS1_2
110 #define SP_PROT_TLS1_2 (SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_2_SERVER)
111 #endif
112 #ifndef SP_PROT_TLS1_3_SERVER
113 #define SP_PROT_TLS1_3_SERVER 0x00001000
114 #endif
115 #ifndef SP_PROT_TLS1_3_CLIENT
116 #define SP_PROT_TLS1_3_CLIENT 0x00002000
117 #endif
118 #ifndef SP_PROT_TLS1_3
119 #define SP_PROT_TLS1_3 (SP_PROT_TLS1_3_CLIENT | SP_PROT_TLS1_3_SERVER)
120 #endif
121
122 /*
123 @future!:
124
125 - Transmitting intermediate certificates
126 - Look for a way to avoid putting intermediate certificates in the certificate store
127 - No documentation on how to send the chain
128 - A stackoverflow question on this from 3 years ago implies schannel only sends intermediate
129 certificates if it's "in the system or user certificate store".
130 - https://stackoverflow.com/q/30156584/2493610
131 - This can be done by users, but we shouldn't add any and all local intermediate
132 certs to the stores automatically.
133 - PSK support
134 - Was added in Windows 10 (it seems), documentation at time of writing is sparse/non-existent.
135 - Specifically about how to supply credentials when they're requested.
136 - Or how to recognize that they're requested in the first place.
137 - Skip certificate verification.
138 - Check if "PSK-only" is still required to do PSK _at all_ (all-around bad solution).
139 - Check if SEC_I_INCOMPLETE_CREDENTIALS is still returned for both "missing certificate" and
140 "missing PSK" when calling InitializeSecurityContext in "performHandshake".
141
142 Medium priority:
143 - Setting cipher-suites (or ALG_ID)
144 - People have survived without it in WinRT
145
146 Low priority:
147 - Possibly make RAII wrappers for SecBuffer (which I commonly create QScopeGuards for)
148
149 */
150
151 QT_BEGIN_NAMESPACE
152
153 namespace {
createSecBuffer(void * ptr,unsigned long length,unsigned long bufferType)154 SecBuffer createSecBuffer(void *ptr, unsigned long length, unsigned long bufferType)
155 {
156 return SecBuffer{ length, bufferType, ptr };
157 }
158
createSecBuffer(QByteArray & buffer,unsigned long bufferType)159 SecBuffer createSecBuffer(QByteArray &buffer, unsigned long bufferType)
160 {
161 return createSecBuffer(buffer.data(), static_cast<unsigned long>(buffer.length()), bufferType);
162 }
163
schannelErrorToString(qint32 status)164 QString schannelErrorToString(qint32 status)
165 {
166 switch (status) {
167 case SEC_E_INSUFFICIENT_MEMORY:
168 return QSslSocket::tr("Insufficient memory");
169 case SEC_E_INTERNAL_ERROR:
170 return QSslSocket::tr("Internal error");
171 case SEC_E_INVALID_HANDLE:
172 return QSslSocket::tr("An internal handle was invalid");
173 case SEC_E_INVALID_TOKEN:
174 return QSslSocket::tr("An internal token was invalid");
175 case SEC_E_LOGON_DENIED:
176 // According to the link below we get this error when Schannel receives TLS1_ALERT_ACCESS_DENIED
177 // https://docs.microsoft.com/en-us/windows/desktop/secauthn/schannel-error-codes-for-tls-and-ssl-alerts
178 return QSslSocket::tr("Access denied");
179 case SEC_E_NO_AUTHENTICATING_AUTHORITY:
180 return QSslSocket::tr("No authority could be contacted for authorization");
181 case SEC_E_NO_CREDENTIALS:
182 return QSslSocket::tr("No credentials");
183 case SEC_E_TARGET_UNKNOWN:
184 return QSslSocket::tr("The target is unknown or unreachable");
185 case SEC_E_UNSUPPORTED_FUNCTION:
186 return QSslSocket::tr("An unsupported function was requested");
187 case SEC_E_WRONG_PRINCIPAL:
188 // SNI error
189 return QSslSocket::tr("The hostname provided does not match the one received from the peer");
190 case SEC_E_APPLICATION_PROTOCOL_MISMATCH:
191 return QSslSocket::tr("No common protocol exists between the client and the server");
192 case SEC_E_ILLEGAL_MESSAGE:
193 return QSslSocket::tr("Unexpected or badly-formatted message received");
194 case SEC_E_ENCRYPT_FAILURE:
195 return QSslSocket::tr("The data could not be encrypted");
196 case SEC_E_ALGORITHM_MISMATCH:
197 return QSslSocket::tr("No cipher suites in common");
198 case SEC_E_UNKNOWN_CREDENTIALS:
199 // This can mean "invalid argument" in some cases...
200 return QSslSocket::tr("The credentials were not recognized / Invalid argument");
201 case SEC_E_MESSAGE_ALTERED:
202 // According to the Internet it also triggers for messages that are out of order.
203 // https://microsoft.public.platformsdk.security.narkive.com/4JAvlMvD/help-please-schannel-security-contexts-and-decryptmessage
204 return QSslSocket::tr("The message was tampered with, damaged or out of sequence.");
205 case SEC_E_OUT_OF_SEQUENCE:
206 return QSslSocket::tr("A message was received out of sequence.");
207 case SEC_E_CONTEXT_EXPIRED:
208 return QSslSocket::tr("The TLS/SSL connection has been closed");
209 default:
210 return QSslSocket::tr("Unknown error occurred: %1").arg(status);
211 }
212 }
213
toSchannelProtocol(QSsl::SslProtocol protocol)214 DWORD toSchannelProtocol(QSsl::SslProtocol protocol)
215 {
216 DWORD protocols = SP_PROT_NONE;
217 switch (protocol) {
218 case QSsl::UnknownProtocol:
219 return DWORD(-1);
220 case QSsl::DtlsV1_0:
221 case QSsl::DtlsV1_2:
222 case QSsl::DtlsV1_0OrLater:
223 case QSsl::DtlsV1_2OrLater:
224 return DWORD(-1); // Not supported at the moment (@future)
225 case QSsl::AnyProtocol:
226 protocols = SP_PROT_TLS1_0 | SP_PROT_TLS1_1 | SP_PROT_TLS1_2;
227 // @future Add TLS 1.3 when supported by Windows!
228 break;
229 case QSsl::SslV2:
230 case QSsl::SslV3:
231 return DWORD(-1); // Not supported
232 case QSsl::TlsV1SslV3:
233 protocols = SP_PROT_TLS1_0;
234 break;
235 case QSsl::TlsV1_0:
236 protocols = SP_PROT_TLS1_0;
237 break;
238 case QSsl::TlsV1_1:
239 protocols = SP_PROT_TLS1_1;
240 break;
241 case QSsl::TlsV1_2:
242 protocols = SP_PROT_TLS1_2;
243 break;
244 case QSsl::TlsV1_3:
245 if ((false)) // @future[0/1] Replace with version check once it's supported in Windows
246 protocols = SP_PROT_TLS1_3;
247 else
248 protocols = DWORD(-1);
249 break;
250 case QSsl::SecureProtocols: // TLS v1.0 and later is currently considered secure
251 case QSsl::TlsV1_0OrLater:
252 // For the "OrLater" protocols we fall through from one to the next, adding all of them
253 // in ascending order
254 protocols = SP_PROT_TLS1_0;
255 Q_FALLTHROUGH();
256 case QSsl::TlsV1_1OrLater:
257 protocols |= SP_PROT_TLS1_1;
258 Q_FALLTHROUGH();
259 case QSsl::TlsV1_2OrLater:
260 protocols |= SP_PROT_TLS1_2;
261 Q_FALLTHROUGH();
262 case QSsl::TlsV1_3OrLater:
263 if ((false)) // @future[1/1] Also replace this with a version check
264 protocols |= SP_PROT_TLS1_3;
265 else if (protocol == QSsl::TlsV1_3OrLater)
266 protocols = DWORD(-1); // if TlsV1_3OrLater was specifically chosen we should fail
267 break;
268 }
269 return protocols;
270 }
271
272 /*!
273 \internal
274 Used when converting the established session's \a protocol back to
275 Qt's own SslProtocol type.
276
277 Only one protocol should be passed in at a time.
278 */
toQtSslProtocol(DWORD protocol)279 QSsl::SslProtocol toQtSslProtocol(DWORD protocol)
280 {
281 #define MAP_PROTOCOL(sp_protocol, q_protocol) \
282 if (protocol & sp_protocol) { \
283 Q_ASSERT(!(protocol & ~sp_protocol)); \
284 return q_protocol; \
285 }
286
287 MAP_PROTOCOL(SP_PROT_TLS1_0, QSsl::TlsV1_0)
288 MAP_PROTOCOL(SP_PROT_TLS1_1, QSsl::TlsV1_1)
289 MAP_PROTOCOL(SP_PROT_TLS1_2, QSsl::TlsV1_2)
290 MAP_PROTOCOL(SP_PROT_TLS1_3, QSsl::TlsV1_3)
291 #undef MAP_PROTOCOL
292 Q_UNREACHABLE();
293 return QSsl::UnknownProtocol;
294 }
295
296 /*!
297 \internal
298 Used by verifyCertContext to check if a client cert is used by a server or vice versa.
299 */
netscapeWrongCertType(const QList<QSslCertificateExtension> & extensions,bool isClient)300 bool netscapeWrongCertType(const QList<QSslCertificateExtension> &extensions, bool isClient)
301 {
302 const auto netscapeIt = std::find_if(
303 extensions.cbegin(), extensions.cend(),
304 [](const QSslCertificateExtension &extension) {
305 const auto netscapeCertType = QStringLiteral("2.16.840.1.113730.1.1");
306 return extension.oid() == netscapeCertType;
307 });
308 if (netscapeIt != extensions.cend()) {
309 const QByteArray netscapeCertTypeByte = netscapeIt->value().toByteArray();
310 int netscapeCertType = 0;
311 QDataStream dataStream(netscapeCertTypeByte);
312 dataStream >> netscapeCertType;
313 if (dataStream.status() != QDataStream::Status::Ok)
314 return true;
315 const int expectedPeerCertType = isClient ? NETSCAPE_SSL_SERVER_AUTH_CERT_TYPE
316 : NETSCAPE_SSL_CLIENT_AUTH_CERT_TYPE;
317 if ((netscapeCertType & expectedPeerCertType) == 0)
318 return true;
319 }
320 return false;
321 }
322
323 /*!
324 \internal
325 Used by verifyCertContext to check the basicConstraints certificate
326 extension to see if the certificate is a certificate authority.
327 Returns false if the certificate does not have the basicConstraints
328 extension or if it is not a certificate authority.
329 */
isCertificateAuthority(const QList<QSslCertificateExtension> & extensions)330 bool isCertificateAuthority(const QList<QSslCertificateExtension> &extensions)
331 {
332 auto it = std::find_if(extensions.cbegin(), extensions.cend(),
333 [](const QSslCertificateExtension &extension) {
334 return extension.name() == QLatin1String("basicConstraints");
335 });
336 if (it != extensions.cend()) {
337 QVariantMap basicConstraints = it->value().toMap();
338 return basicConstraints.value(QLatin1String("ca"), false).toBool();
339 }
340 return false;
341 }
342
343 /*!
344 \internal
345 Returns true if the attributes we requested from the context/handshake have
346 been given.
347 */
matchesContextRequirements(DWORD attributes,DWORD requirements,QSslSocket::PeerVerifyMode verifyMode,bool isClient)348 bool matchesContextRequirements(DWORD attributes, DWORD requirements,
349 QSslSocket::PeerVerifyMode verifyMode,
350 bool isClient)
351 {
352 #ifdef QSSLSOCKET_DEBUG
353 #define DEBUG_WARN(message) qCWarning(lcSsl, message)
354 #else
355 #define DEBUG_WARN(message)
356 #endif
357
358 #define CHECK_ATTRIBUTE(attributeName) \
359 do { \
360 const DWORD req##attributeName = isClient ? ISC_REQ_##attributeName : ASC_REQ_##attributeName; \
361 const DWORD ret##attributeName = isClient ? ISC_RET_##attributeName : ASC_RET_##attributeName; \
362 if (!(requirements & req##attributeName) != !(attributes & ret##attributeName)) { \
363 DEBUG_WARN("Missing attribute \"" #attributeName "\""); \
364 return false; \
365 } \
366 } while (false)
367
368 CHECK_ATTRIBUTE(CONFIDENTIALITY);
369 CHECK_ATTRIBUTE(REPLAY_DETECT);
370 CHECK_ATTRIBUTE(SEQUENCE_DETECT);
371 CHECK_ATTRIBUTE(STREAM);
372 if (verifyMode == QSslSocket::PeerVerifyMode::VerifyPeer)
373 CHECK_ATTRIBUTE(MUTUAL_AUTH);
374
375 // This one is manual because there is no server / ASC_ version
376 if (isClient) {
377 const auto reqManualCredValidation = ISC_REQ_MANUAL_CRED_VALIDATION;
378 const auto retManualCredValidation = ISC_RET_MANUAL_CRED_VALIDATION;
379 if (!(requirements & reqManualCredValidation) != !(attributes & retManualCredValidation)) {
380 DEBUG_WARN("Missing attribute \"MANUAL_CRED_VALIDATION\"");
381 return false;
382 }
383 }
384
385 return true;
386 #undef CHECK_ATTRIBUTE
387 #undef DEBUG_WARN
388 }
389
390 template<typename Required, typename Actual>
391 Required const_reinterpret_cast(Actual *p)
392 {
393 return Required(p);
394 }
395
396 #ifdef SUPPORTS_ALPN
supportsAlpn()397 bool supportsAlpn()
398 {
399 return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8_1;
400 }
401
createAlpnString(const QByteArrayList & nextAllowedProtocols)402 QByteArray createAlpnString(const QByteArrayList &nextAllowedProtocols)
403 {
404 QByteArray alpnString;
405 if (!nextAllowedProtocols.isEmpty() && supportsAlpn()) {
406 const QByteArray names = [&nextAllowedProtocols]() {
407 QByteArray protocolString;
408 for (QByteArray proto : nextAllowedProtocols) {
409 if (proto.size() > 255) {
410 qCWarning(lcSsl) << "TLS ALPN extension" << proto
411 << "is too long and will be ignored.";
412 continue;
413 } else if (proto.isEmpty()) {
414 continue;
415 }
416 protocolString += char(proto.length()) + proto;
417 }
418 return protocolString;
419 }();
420 if (names.isEmpty())
421 return alpnString;
422
423 const quint16 namesSize = names.size();
424 const quint32 alpnId = SecApplicationProtocolNegotiationExt_ALPN;
425 const quint32 totalSize = sizeof(alpnId) + sizeof(namesSize) + namesSize;
426 alpnString = QByteArray::fromRawData(reinterpret_cast<const char *>(&totalSize), sizeof(totalSize))
427 + QByteArray::fromRawData(reinterpret_cast<const char *>(&alpnId), sizeof(alpnId))
428 + QByteArray::fromRawData(reinterpret_cast<const char *>(&namesSize), sizeof(namesSize))
429 + names;
430 }
431 return alpnString;
432 }
433 #endif // SUPPORTS_ALPN
434
readToBuffer(QByteArray & buffer,QTcpSocket * plainSocket)435 qint64 readToBuffer(QByteArray &buffer, QTcpSocket *plainSocket)
436 {
437 Q_ASSERT(plainSocket);
438 static const qint64 shrinkCutoff = 1024 * 12;
439 static const qint64 defaultRead = 1024 * 16;
440 qint64 bytesRead = 0;
441
442 const auto toRead = std::min(defaultRead, plainSocket->bytesAvailable());
443 if (toRead > 0) {
444 const auto bufferSize = buffer.size();
445 buffer.reserve(bufferSize + toRead); // avoid growth strategy kicking in
446 buffer.resize(bufferSize + toRead);
447 bytesRead = plainSocket->read(buffer.data() + bufferSize, toRead);
448 buffer.resize(bufferSize + bytesRead);
449 // In case of excessive memory usage we shrink:
450 if (buffer.size() < shrinkCutoff && buffer.capacity() > defaultRead)
451 buffer.shrink_to_fit();
452 }
453
454 return bytesRead;
455 }
456
retainExtraData(QByteArray & buffer,const SecBuffer & secBuffer)457 void retainExtraData(QByteArray &buffer, const SecBuffer &secBuffer)
458 {
459 Q_ASSERT(secBuffer.BufferType == SECBUFFER_EXTRA);
460 if (int(secBuffer.cbBuffer) >= buffer.size())
461 return;
462
463 #ifdef QSSLSOCKET_DEBUG
464 qCDebug(lcSsl, "We got SECBUFFER_EXTRA, will retain %lu bytes", secBuffer.cbBuffer);
465 #endif
466 std::move(buffer.end() - secBuffer.cbBuffer, buffer.end(), buffer.begin());
467 buffer.resize(secBuffer.cbBuffer);
468 }
469
checkIncompleteData(const SecBuffer & secBuffer)470 qint64 checkIncompleteData(const SecBuffer &secBuffer)
471 {
472 if (secBuffer.BufferType == SECBUFFER_MISSING) {
473 #ifdef QSSLSOCKET_DEBUG
474 qCDebug(lcSsl, "Need %lu more bytes.", secBuffer.cbBuffer);
475 #endif
476 return secBuffer.cbBuffer;
477 }
478 return 0;
479 }
480
481 } // anonymous namespace
482
483 bool QSslSocketPrivate::s_loadRootCertsOnDemand = true;
484 bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
Q_GLOBAL_STATIC(QRecursiveMutex,qt_schannel_mutex)485 Q_GLOBAL_STATIC(QRecursiveMutex, qt_schannel_mutex)
486
487 void QSslSocketPrivate::ensureInitialized()
488 {
489 const QMutexLocker locker(qt_schannel_mutex);
490 if (s_loadedCiphersAndCerts)
491 return;
492 s_loadedCiphersAndCerts = true;
493
494 setDefaultCaCertificates(systemCaCertificates());
495 s_loadRootCertsOnDemand = true; // setDefaultCaCertificates sets it to false, re-enable it.
496
497 resetDefaultCiphers();
498 }
499
resetDefaultCiphers()500 void QSslSocketPrivate::resetDefaultCiphers()
501 {
502 setDefaultSupportedCiphers(QSslSocketBackendPrivate::defaultCiphers());
503 setDefaultCiphers(QSslSocketBackendPrivate::defaultCiphers());
504 }
505
resetDefaultEllipticCurves()506 void QSslSocketPrivate::resetDefaultEllipticCurves()
507 {
508 Q_UNIMPLEMENTED();
509 }
510
supportsSsl()511 bool QSslSocketPrivate::supportsSsl()
512 {
513 return true;
514 }
515
systemCaCertificates()516 QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
517 {
518 // Copied from qsslsocket_openssl.cpp's systemCaCertificates function.
519 QList<QSslCertificate> systemCerts;
520 auto hSystemStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT"));
521 if (hSystemStore) {
522 PCCERT_CONTEXT pc = nullptr;
523 while ((pc = CertFindCertificateInStore(hSystemStore.get(), X509_ASN_ENCODING, 0,
524 CERT_FIND_ANY, nullptr, pc))) {
525 systemCerts.append(QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(pc));
526 }
527 }
528 return systemCerts;
529 }
530
sslLibraryVersionNumber()531 long QSslSocketPrivate::sslLibraryVersionNumber()
532 {
533 const auto os = QOperatingSystemVersion::current();
534 return (os.majorVersion() << 24) | ((os.minorVersion() & 0xFF) << 16) | (os.microVersion() & 0xFFFF);
535 }
536
sslLibraryVersionString()537 QString QSslSocketPrivate::sslLibraryVersionString()
538 {
539 const auto os = QOperatingSystemVersion::current();
540 return QString::fromLatin1("Secure Channel, %1 %2.%3.%4")
541 .arg(os.name(),
542 QString::number(os.majorVersion()),
543 QString::number(os.minorVersion()),
544 QString::number(os.microVersion()));
545 }
546
sslLibraryBuildVersionNumber()547 long QSslSocketPrivate::sslLibraryBuildVersionNumber()
548 {
549 // There is no separate build version
550 return sslLibraryVersionNumber();
551 }
552
sslLibraryBuildVersionString()553 QString QSslSocketPrivate::sslLibraryBuildVersionString()
554 {
555 const auto os = QOperatingSystemVersion::current();
556 return QString::fromLatin1("%1.%2.%3")
557 .arg(QString::number(os.majorVersion()),
558 QString::number(os.minorVersion()),
559 QString::number(os.microVersion()));
560 }
561
QSslSocketBackendPrivate()562 QSslSocketBackendPrivate::QSslSocketBackendPrivate()
563 {
564 SecInvalidateHandle(&credentialHandle);
565 SecInvalidateHandle(&contextHandle);
566 ensureInitialized();
567 }
568
~QSslSocketBackendPrivate()569 QSslSocketBackendPrivate::~QSslSocketBackendPrivate()
570 {
571 closeCertificateStores();
572 deallocateContext();
573 freeCredentialsHandle();
574 CertFreeCertificateContext(localCertContext);
575 }
576
sendToken(void * token,unsigned long tokenLength,bool emitError)577 bool QSslSocketBackendPrivate::sendToken(void *token, unsigned long tokenLength, bool emitError)
578 {
579 if (tokenLength == 0)
580 return true;
581 const qint64 written = plainSocket->write(static_cast<const char *>(token), tokenLength);
582 if (written != qint64(tokenLength)) {
583 // Failed to write/buffer everything or an error occurred
584 if (emitError)
585 setErrorAndEmit(plainSocket->error(), plainSocket->errorString());
586 return false;
587 }
588 return true;
589 }
590
targetName() const591 QString QSslSocketBackendPrivate::targetName() const
592 {
593 // Used for SNI extension
594 return verificationPeerName.isEmpty() ? q_func()->peerName() : verificationPeerName;
595 }
596
getContextRequirements()597 ULONG QSslSocketBackendPrivate::getContextRequirements()
598 {
599 const bool isClient = mode == QSslSocket::SslClientMode;
600 ULONG req = 0;
601
602 req |= ISC_REQ_ALLOCATE_MEMORY; // Allocate memory for buffers automatically
603 req |= ISC_REQ_CONFIDENTIALITY; // Encrypt messages
604 req |= ISC_REQ_REPLAY_DETECT; // Detect replayed messages
605 req |= ISC_REQ_SEQUENCE_DETECT; // Detect out of sequence messages
606 req |= ISC_REQ_STREAM; // Support a stream-oriented connection
607
608 if (isClient) {
609 req |= ISC_REQ_MANUAL_CRED_VALIDATION; // Manually validate certificate
610 } else {
611 switch (configuration.peerVerifyMode) {
612 case QSslSocket::PeerVerifyMode::VerifyNone:
613 // There doesn't seem to be a way to ask for an optional client cert :-(
614 case QSslSocket::PeerVerifyMode::AutoVerifyPeer:
615 case QSslSocket::PeerVerifyMode::QueryPeer:
616 break;
617 case QSslSocket::PeerVerifyMode::VerifyPeer:
618 req |= ISC_REQ_MUTUAL_AUTH;
619 break;
620 }
621 }
622
623 return req;
624 }
625
acquireCredentialsHandle()626 bool QSslSocketBackendPrivate::acquireCredentialsHandle()
627 {
628 Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
629
630 const bool isClient = mode == QSslSocket::SslClientMode;
631 const DWORD protocols = toSchannelProtocol(configuration.protocol);
632 if (protocols == DWORD(-1)) {
633 setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
634 QSslSocket::tr("Invalid protocol chosen"));
635 return false;
636 }
637
638 const CERT_CHAIN_CONTEXT *chainContext = nullptr;
639 auto freeCertChain = qScopeGuard([&chainContext]() {
640 if (chainContext)
641 CertFreeCertificateChain(chainContext);
642 });
643
644 DWORD certsCount = 0;
645 // Set up our certificate stores before trying to use one...
646 initializeCertificateStores();
647
648 // Check if user has specified a certificate chain but it could not be loaded.
649 // This happens if there was something wrong with the certificate chain or there was no private
650 // key.
651 if (!configuration.localCertificateChain.isEmpty() && !localCertificateStore)
652 return true; // 'true' because "tst_QSslSocket::setEmptyKey" expects us to not disconnect
653
654 if (localCertificateStore != nullptr) {
655 CERT_CHAIN_FIND_BY_ISSUER_PARA findParam;
656 ZeroMemory(&findParam, sizeof(findParam));
657 findParam.cbSize = sizeof(findParam);
658 findParam.pszUsageIdentifier = isClient ? szOID_PKIX_KP_CLIENT_AUTH : szOID_PKIX_KP_SERVER_AUTH;
659
660 // There should only be one chain in our store, so.. we grab that one.
661 chainContext = CertFindChainInStore(localCertificateStore.get(),
662 X509_ASN_ENCODING,
663 0,
664 CERT_CHAIN_FIND_BY_ISSUER,
665 &findParam,
666 nullptr);
667 if (!chainContext) {
668 const QString message = isClient
669 ? QSslSocket::tr("The certificate provided cannot be used for a client.")
670 : QSslSocket::tr("The certificate provided cannot be used for a server.");
671 setErrorAndEmit(QAbstractSocket::SocketError::SslInvalidUserDataError, message);
672 return false;
673 }
674 Q_ASSERT(chainContext->cChain == 1);
675 Q_ASSERT(chainContext->rgpChain[0]);
676 Q_ASSERT(chainContext->rgpChain[0]->cbSize >= 1);
677 Q_ASSERT(chainContext->rgpChain[0]->rgpElement[0]);
678 Q_ASSERT(!localCertContext);
679 localCertContext = CertDuplicateCertificateContext(chainContext->rgpChain[0]
680 ->rgpElement[0]
681 ->pCertContext);
682 certsCount = 1;
683 Q_ASSERT(localCertContext);
684 }
685
686 SCHANNEL_CRED cred{
687 SCHANNEL_CRED_VERSION, // dwVersion
688 certsCount, // cCreds
689 &localCertContext, // paCred (certificate(s) containing a private key for authentication)
690 nullptr, // hRootStore
691
692 0, // cMappers (reserved)
693 nullptr, // aphMappers (reserved)
694
695 0, // cSupportedAlgs
696 nullptr, // palgSupportedAlgs (nullptr = system default) @future: QSslCipher-related
697
698 protocols, // grbitEnabledProtocols
699 0, // dwMinimumCipherStrength (0 = system default)
700 0, // dwMaximumCipherStrength (0 = system default)
701 0, // dwSessionLifespan (0 = schannel default, 10 hours)
702 SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT
703 | SCH_CRED_NO_DEFAULT_CREDS, // dwFlags
704 0 // dwCredFormat (must be 0)
705 };
706
707 TimeStamp expiration{};
708 auto status = AcquireCredentialsHandle(nullptr, // pszPrincipal (unused)
709 const_cast<wchar_t *>(UNISP_NAME), // pszPackage
710 isClient ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND, // fCredentialUse
711 nullptr, // pvLogonID (unused)
712 &cred, // pAuthData
713 nullptr, // pGetKeyFn (unused)
714 nullptr, // pvGetKeyArgument (unused)
715 &credentialHandle, // phCredential
716 &expiration // ptsExpir
717 );
718
719 if (status != SEC_E_OK) {
720 setErrorAndEmit(QAbstractSocket::SslInternalError, schannelErrorToString(status));
721 return false;
722 }
723 return true;
724 }
725
deallocateContext()726 void QSslSocketBackendPrivate::deallocateContext()
727 {
728 if (SecIsValidHandle(&contextHandle)) {
729 DeleteSecurityContext(&contextHandle);
730 SecInvalidateHandle(&contextHandle);
731 }
732 }
733
freeCredentialsHandle()734 void QSslSocketBackendPrivate::freeCredentialsHandle()
735 {
736 if (SecIsValidHandle(&credentialHandle)) {
737 FreeCredentialsHandle(&credentialHandle);
738 SecInvalidateHandle(&credentialHandle);
739 }
740 }
741
closeCertificateStores()742 void QSslSocketBackendPrivate::closeCertificateStores()
743 {
744 localCertificateStore.reset();
745 peerCertificateStore.reset();
746 caCertificateStore.reset();
747 }
748
createContext()749 bool QSslSocketBackendPrivate::createContext()
750 {
751 Q_ASSERT(SecIsValidHandle(&credentialHandle));
752 Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
753 Q_ASSERT(mode == QSslSocket::SslClientMode);
754 ULONG contextReq = getContextRequirements();
755
756 SecBuffer outBuffers[3];
757 outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
758 outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
759 outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
760 auto freeBuffers = qScopeGuard([&outBuffers]() {
761 for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
762 if (outBuffers[i].pvBuffer)
763 FreeContextBuffer(outBuffers[i].pvBuffer);
764 }
765 });
766 SecBufferDesc outputBufferDesc{
767 SECBUFFER_VERSION,
768 ARRAYSIZE(outBuffers),
769 outBuffers
770 };
771
772 TimeStamp expiry;
773
774 SecBufferDesc alpnBufferDesc;
775 bool useAlpn = false;
776 #ifdef SUPPORTS_ALPN
777 configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNone;
778 QByteArray alpnString = createAlpnString(configuration.nextAllowedProtocols);
779 useAlpn = !alpnString.isEmpty();
780 SecBuffer alpnBuffers[1];
781 alpnBuffers[0] = createSecBuffer(alpnString, SECBUFFER_APPLICATION_PROTOCOLS);
782 alpnBufferDesc = {
783 SECBUFFER_VERSION,
784 ARRAYSIZE(alpnBuffers),
785 alpnBuffers
786 };
787 #endif
788
789 auto status = InitializeSecurityContext(&credentialHandle, // phCredential
790 nullptr, // phContext
791 const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
792 contextReq, // fContextReq
793 0, // Reserved1
794 0, // TargetDataRep (unused)
795 useAlpn ? &alpnBufferDesc : nullptr, // pInput
796 0, // Reserved2
797 &contextHandle, // phNewContext
798 &outputBufferDesc, // pOutput
799 &contextAttributes, // pfContextAttr
800 &expiry // ptsExpiry
801 );
802
803 // This is the first call to InitializeSecurityContext, so theoretically "CONTINUE_NEEDED"
804 // should be the only non-error return-code here.
805 if (status != SEC_I_CONTINUE_NEEDED) {
806 setErrorAndEmit(QAbstractSocket::SslInternalError,
807 QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status)));
808 return false;
809 }
810
811 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
812 return false;
813 schannelState = SchannelState::PerformHandshake;
814 return true;
815 }
816
acceptContext()817 bool QSslSocketBackendPrivate::acceptContext()
818 {
819 Q_ASSERT(SecIsValidHandle(&credentialHandle));
820 Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
821 Q_ASSERT(mode == QSslSocket::SslServerMode);
822 ULONG contextReq = getContextRequirements();
823
824 if (missingData > plainSocket->bytesAvailable())
825 return true;
826
827 missingData = 0;
828 readToBuffer(intermediateBuffer, plainSocket);
829 if (intermediateBuffer.isEmpty())
830 return true; // definitely need more data..
831
832 SecBuffer inBuffers[2];
833 inBuffers[0] = createSecBuffer(intermediateBuffer, SECBUFFER_TOKEN);
834
835 #ifdef SUPPORTS_ALPN
836 configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNone;
837 // The string must be alive when we call AcceptSecurityContext
838 QByteArray alpnString = createAlpnString(configuration.nextAllowedProtocols);
839 if (!alpnString.isEmpty()) {
840 inBuffers[1] = createSecBuffer(alpnString, SECBUFFER_APPLICATION_PROTOCOLS);
841 } else
842 #endif
843 {
844 inBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
845 }
846
847 SecBufferDesc inputBufferDesc{
848 SECBUFFER_VERSION,
849 ARRAYSIZE(inBuffers),
850 inBuffers
851 };
852
853 SecBuffer outBuffers[3];
854 outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
855 outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
856 outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
857 auto freeBuffers = qScopeGuard([&outBuffers]() {
858 for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
859 if (outBuffers[i].pvBuffer)
860 FreeContextBuffer(outBuffers[i].pvBuffer);
861 }
862 });
863 SecBufferDesc outputBufferDesc{
864 SECBUFFER_VERSION,
865 ARRAYSIZE(outBuffers),
866 outBuffers
867 };
868
869 TimeStamp expiry;
870 auto status = AcceptSecurityContext(
871 &credentialHandle, // phCredential
872 nullptr, // phContext
873 &inputBufferDesc, // pInput
874 contextReq, // fContextReq
875 0, // TargetDataRep (unused)
876 &contextHandle, // phNewContext
877 &outputBufferDesc, // pOutput
878 &contextAttributes, // pfContextAttr
879 &expiry // ptsTimeStamp
880 );
881
882 if (status == SEC_E_INCOMPLETE_MESSAGE) {
883 // Need more data
884 missingData = checkIncompleteData(outBuffers[0]);
885 return true;
886 }
887
888 if (inBuffers[1].BufferType == SECBUFFER_EXTRA) {
889 // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
890 // inBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need to
891 // be stored.
892 retainExtraData(intermediateBuffer, inBuffers[1]);
893 } else { /* No 'extra' data, message not incomplete */
894 intermediateBuffer.resize(0);
895 }
896
897 if (status != SEC_I_CONTINUE_NEEDED) {
898 setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
899 QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status)));
900 return false;
901 }
902 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
903 return false;
904 schannelState = SchannelState::PerformHandshake;
905 return true;
906 }
907
performHandshake()908 bool QSslSocketBackendPrivate::performHandshake()
909 {
910 if (plainSocket->state() == QAbstractSocket::UnconnectedState) {
911 setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
912 QSslSocket::tr("The TLS/SSL connection has been closed"));
913 return false;
914 }
915 Q_ASSERT(SecIsValidHandle(&credentialHandle));
916 Q_ASSERT(SecIsValidHandle(&contextHandle));
917 Q_ASSERT(schannelState == SchannelState::PerformHandshake);
918
919 #ifdef QSSLSOCKET_DEBUG
920 qCDebug(lcSsl, "Bytes available from socket: %lld", plainSocket->bytesAvailable());
921 qCDebug(lcSsl, "intermediateBuffer size: %d", intermediateBuffer.size());
922 #endif
923
924 if (missingData > plainSocket->bytesAvailable())
925 return true;
926
927 missingData = 0;
928 readToBuffer(intermediateBuffer, plainSocket);
929 if (intermediateBuffer.isEmpty())
930 return true; // no data, will fail
931
932 SecBuffer outBuffers[3] = {};
933 const auto freeOutBuffers = [&outBuffers]() {
934 for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
935 if (outBuffers[i].pvBuffer)
936 FreeContextBuffer(outBuffers[i].pvBuffer);
937 }
938 };
939 const auto outBuffersGuard = qScopeGuard(freeOutBuffers);
940 // For this call to InitializeSecurityContext we may need to call it twice.
941 // In some cases us not having a certificate isn't actually an error, but just a request.
942 // With Schannel, to ignore this warning, we need to call InitializeSecurityContext again
943 // when we get SEC_I_INCOMPLETE_CREDENTIALS! As far as I can tell it's not documented anywhere.
944 // https://stackoverflow.com/a/47479968/2493610
945 SECURITY_STATUS status;
946 short attempts = 2;
947 do {
948 SecBuffer inputBuffers[2];
949 inputBuffers[0] = createSecBuffer(intermediateBuffer, SECBUFFER_TOKEN);
950 inputBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
951 SecBufferDesc inputBufferDesc{
952 SECBUFFER_VERSION,
953 ARRAYSIZE(inputBuffers),
954 inputBuffers
955 };
956
957 freeOutBuffers(); // free buffers from any previous attempt
958 outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
959 outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
960 outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
961 SecBufferDesc outputBufferDesc{
962 SECBUFFER_VERSION,
963 ARRAYSIZE(outBuffers),
964 outBuffers
965 };
966
967 ULONG contextReq = getContextRequirements();
968 TimeStamp expiry;
969 status = InitializeSecurityContext(
970 &credentialHandle, // phCredential
971 &contextHandle, // phContext
972 const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
973 contextReq, // fContextReq
974 0, // Reserved1
975 0, // TargetDataRep (unused)
976 &inputBufferDesc, // pInput
977 0, // Reserved2
978 nullptr, // phNewContext (we already have one)
979 &outputBufferDesc, // pOutput
980 &contextAttributes, // pfContextAttr
981 &expiry // ptsExpiry
982 );
983
984 if (inputBuffers[1].BufferType == SECBUFFER_EXTRA) {
985 // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
986 // inputBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need
987 // to be stored.
988 retainExtraData(intermediateBuffer, inputBuffers[1]);
989 } else if (status != SEC_E_INCOMPLETE_MESSAGE) {
990 // Clear the buffer if we weren't asked for more data
991 intermediateBuffer.resize(0);
992 }
993
994 --attempts;
995 } while (status == SEC_I_INCOMPLETE_CREDENTIALS && attempts > 0);
996
997 switch (status) {
998 case SEC_E_OK:
999 // Need to transmit a final token in the handshake if 'cbBuffer' is non-zero.
1000 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
1001 return false;
1002 schannelState = SchannelState::VerifyHandshake;
1003 return true;
1004 case SEC_I_CONTINUE_NEEDED:
1005 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
1006 return false;
1007 // Must call InitializeSecurityContext again later (done through continueHandshake)
1008 return true;
1009 case SEC_I_INCOMPLETE_CREDENTIALS:
1010 // Schannel takes care of picking certificate to send (other than the one we can specify),
1011 // so if we get here then that means we don't have a certificate the server accepts.
1012 setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
1013 QSslSocket::tr("Server did not accept any certificate we could present."));
1014 return false;
1015 case SEC_I_CONTEXT_EXPIRED:
1016 // "The message sender has finished using the connection and has initiated a shutdown."
1017 if (outBuffers[0].BufferType == SECBUFFER_TOKEN) {
1018 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
1019 return false;
1020 }
1021 if (!shutdown) { // we did not initiate this
1022 setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
1023 QSslSocket::tr("The TLS/SSL connection has been closed"));
1024 }
1025 return true;
1026 case SEC_E_INCOMPLETE_MESSAGE:
1027 // Simply incomplete, wait for more data
1028 missingData = checkIncompleteData(outBuffers[0]);
1029 return true;
1030 case SEC_E_ALGORITHM_MISMATCH:
1031 setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
1032 QSslSocket::tr("Algorithm mismatch"));
1033 shutdown = true; // skip sending the "Shutdown" alert
1034 return false;
1035 }
1036
1037 // Note: We can get here if the connection is using TLS 1.2 and the server certificate uses
1038 // MD5, which is not allowed in Schannel. This causes an "invalid token" error during handshake.
1039 // (If you came here investigating an error: md5 is insecure, update your certificate)
1040 setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
1041 QSslSocket::tr("Handshake failed: %1").arg(schannelErrorToString(status)));
1042 return false;
1043 }
1044
verifyHandshake()1045 bool QSslSocketBackendPrivate::verifyHandshake()
1046 {
1047 Q_Q(QSslSocket);
1048 sslErrors.clear();
1049
1050 const bool isClient = mode == QSslSocket::SslClientMode;
1051 #define CHECK_STATUS(status) \
1052 if (status != SEC_E_OK) { \
1053 setErrorAndEmit(QAbstractSocket::SslInternalError, \
1054 QSslSocket::tr("Failed to query the TLS context: %1") \
1055 .arg(schannelErrorToString(status))); \
1056 return false; \
1057 }
1058
1059 // Everything is set up, now make sure there's nothing wrong and query some attributes...
1060 if (!matchesContextRequirements(contextAttributes, getContextRequirements(),
1061 configuration.peerVerifyMode, isClient)) {
1062 setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
1063 QSslSocket::tr("Did not get the required attributes for the connection."));
1064 return false;
1065 }
1066
1067 // Get stream sizes (to know the max size of a message and the size of the header and trailer)
1068 auto status = QueryContextAttributes(&contextHandle,
1069 SECPKG_ATTR_STREAM_SIZES,
1070 &streamSizes);
1071 CHECK_STATUS(status);
1072
1073 // Get session cipher info
1074 status = QueryContextAttributes(&contextHandle,
1075 SECPKG_ATTR_CONNECTION_INFO,
1076 &connectionInfo);
1077 CHECK_STATUS(status);
1078
1079 #ifdef SUPPORTS_ALPN
1080 if (!configuration.nextAllowedProtocols.isEmpty() && supportsAlpn()) {
1081 SecPkgContext_ApplicationProtocol alpn;
1082 status = QueryContextAttributes(&contextHandle,
1083 SECPKG_ATTR_APPLICATION_PROTOCOL,
1084 &alpn);
1085 CHECK_STATUS(status);
1086 if (alpn.ProtoNegoStatus == SecApplicationProtocolNegotiationStatus_Success) {
1087 QByteArray negotiatedProto = QByteArray((const char *)alpn.ProtocolId,
1088 alpn.ProtocolIdSize);
1089 if (!configuration.nextAllowedProtocols.contains(negotiatedProto)) {
1090 setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
1091 QSslSocket::tr("Unwanted protocol was negotiated"));
1092 return false;
1093 }
1094 configuration.nextNegotiatedProtocol = negotiatedProto;
1095 configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNegotiated;
1096 } else {
1097 configuration.nextNegotiatedProtocol = "";
1098 configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationUnsupported;
1099 }
1100 }
1101 #endif // supports ALPN
1102
1103 #undef CHECK_STATUS
1104
1105 // Verify certificate
1106 CERT_CONTEXT *certificateContext = nullptr;
1107 auto freeCertificate = qScopeGuard([&certificateContext]() {
1108 if (certificateContext)
1109 CertFreeCertificateContext(certificateContext);
1110 });
1111 status = QueryContextAttributes(&contextHandle,
1112 SECPKG_ATTR_REMOTE_CERT_CONTEXT,
1113 &certificateContext);
1114
1115 // QueryPeer can (currently) not work in Schannel since Schannel itself doesn't have a way to
1116 // ask for a certificate and then still be OK if it's not received.
1117 // To work around this we don't request a certificate at all for QueryPeer.
1118 // For servers AutoVerifyPeer is supposed to be treated the same as QueryPeer.
1119 // This means that servers using Schannel will only request client certificate for "VerifyPeer".
1120 if ((!isClient && configuration.peerVerifyMode == QSslSocket::PeerVerifyMode::VerifyPeer)
1121 || (isClient && configuration.peerVerifyMode != QSslSocket::PeerVerifyMode::VerifyNone
1122 && configuration.peerVerifyMode != QSslSocket::PeerVerifyMode::QueryPeer)) {
1123 if (status != SEC_E_OK) {
1124 #ifdef QSSLSOCKET_DEBUG
1125 qCDebug(lcSsl) << "Couldn't retrieve peer certificate, status:"
1126 << schannelErrorToString(status);
1127 #endif
1128 const QSslError error{ QSslError::NoPeerCertificate };
1129 sslErrors += error;
1130 emit q->peerVerifyError(error);
1131 if (q->state() != QAbstractSocket::ConnectedState)
1132 return false;
1133 }
1134 }
1135
1136 // verifyCertContext returns false if the user disconnected while it was checking errors.
1137 if (certificateContext && !verifyCertContext(certificateContext))
1138 return false;
1139
1140 if (!checkSslErrors() || state != QAbstractSocket::ConnectedState) {
1141 #ifdef QSSLSOCKET_DEBUG
1142 qCDebug(lcSsl) << __func__ << "was unsuccessful. Paused:" << paused;
1143 #endif
1144 // If we're paused then checkSslErrors returned false, but it's not an error
1145 return paused && state == QAbstractSocket::ConnectedState;
1146 }
1147
1148 schannelState = SchannelState::Done;
1149 return true;
1150 }
1151
renegotiate()1152 bool QSslSocketBackendPrivate::renegotiate()
1153 {
1154 SecBuffer outBuffers[3];
1155 outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
1156 outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
1157 outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
1158 auto freeBuffers = qScopeGuard([&outBuffers]() {
1159 for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
1160 if (outBuffers[i].pvBuffer)
1161 FreeContextBuffer(outBuffers[i].pvBuffer);
1162 }
1163 });
1164 SecBufferDesc outputBufferDesc{
1165 SECBUFFER_VERSION,
1166 ARRAYSIZE(outBuffers),
1167 outBuffers
1168 };
1169
1170 ULONG contextReq = getContextRequirements();
1171 TimeStamp expiry;
1172 SECURITY_STATUS status;
1173 if (mode == QSslSocket::SslClientMode) {
1174 status = InitializeSecurityContext(&credentialHandle, // phCredential
1175 &contextHandle, // phContext
1176 const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
1177 contextReq, // fContextReq
1178 0, // Reserved1
1179 0, // TargetDataRep (unused)
1180 nullptr, // pInput (nullptr for renegotiate)
1181 0, // Reserved2
1182 nullptr, // phNewContext (we already have one)
1183 &outputBufferDesc, // pOutput
1184 &contextAttributes, // pfContextAttr
1185 &expiry // ptsExpiry
1186 );
1187 } else {
1188 status = AcceptSecurityContext(
1189 &credentialHandle, // phCredential
1190 &contextHandle, // phContext
1191 nullptr, // pInput
1192 contextReq, // fContextReq
1193 0, // TargetDataRep (unused)
1194 nullptr, // phNewContext
1195 &outputBufferDesc, // pOutput
1196 &contextAttributes, // pfContextAttr,
1197 &expiry // ptsTimeStamp
1198 );
1199 }
1200 if (status == SEC_I_CONTINUE_NEEDED) {
1201 schannelState = SchannelState::PerformHandshake;
1202 return sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer);
1203 }
1204 setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
1205 QSslSocket::tr("Renegotiation was unsuccessful: %1").arg(schannelErrorToString(status)));
1206 return false;
1207 }
1208
1209 /*!
1210 \internal
1211 reset the state in preparation for reuse of socket
1212 */
reset()1213 void QSslSocketBackendPrivate::reset()
1214 {
1215 closeCertificateStores(); // certificate stores could've changed
1216 deallocateContext();
1217 freeCredentialsHandle(); // in case we already had one (@future: session resumption requires re-use)
1218
1219 connectionInfo = {};
1220 streamSizes = {};
1221
1222 CertFreeCertificateContext(localCertContext);
1223 localCertContext = nullptr;
1224
1225 contextAttributes = 0;
1226 intermediateBuffer.clear();
1227 schannelState = SchannelState::InitializeHandshake;
1228
1229 connectionEncrypted = false;
1230 shutdown = false;
1231 renegotiating = false;
1232
1233 missingData = 0;
1234 }
1235
startClientEncryption()1236 void QSslSocketBackendPrivate::startClientEncryption()
1237 {
1238 if (connectionEncrypted)
1239 return; // let's not mess up the connection...
1240 reset();
1241 continueHandshake();
1242 }
1243
startServerEncryption()1244 void QSslSocketBackendPrivate::startServerEncryption()
1245 {
1246 if (connectionEncrypted)
1247 return; // let's not mess up the connection...
1248 reset();
1249 continueHandshake();
1250 }
1251
transmit()1252 void QSslSocketBackendPrivate::transmit()
1253 {
1254 Q_Q(QSslSocket);
1255
1256 if (mode == QSslSocket::UnencryptedMode)
1257 return; // This function should not have been called
1258
1259 // Can happen if called through QSslSocket::abort->QSslSocket::close->QSslSocket::flush->here
1260 if (plainSocket->state() == QAbstractSocket::SocketState::UnconnectedState)
1261 return;
1262
1263 if (schannelState != SchannelState::Done) {
1264 continueHandshake();
1265 return;
1266 }
1267
1268 if (connectionEncrypted) { // encrypt data in writeBuffer and write it to plainSocket
1269 qint64 totalBytesWritten = 0;
1270 qint64 writeBufferSize;
1271 while ((writeBufferSize = writeBuffer.size()) > 0) {
1272 const int headerSize = int(streamSizes.cbHeader);
1273 const int trailerSize = int(streamSizes.cbTrailer);
1274 // Try to read 'cbMaximumMessage' bytes from buffer before encrypting.
1275 const int size = int(std::min(writeBufferSize, qint64(streamSizes.cbMaximumMessage)));
1276 QByteArray fullMessage(headerSize + trailerSize + size, Qt::Uninitialized);
1277 {
1278 // Use peek() here instead of read() so we don't lose data if encryption fails.
1279 qint64 copied = writeBuffer.peek(fullMessage.data() + headerSize, size);
1280 Q_ASSERT(copied == size);
1281 }
1282
1283 SecBuffer inputBuffers[4]{
1284 createSecBuffer(fullMessage.data(), headerSize, SECBUFFER_STREAM_HEADER),
1285 createSecBuffer(fullMessage.data() + headerSize, size, SECBUFFER_DATA),
1286 createSecBuffer(fullMessage.data() + headerSize + size, trailerSize, SECBUFFER_STREAM_TRAILER),
1287 createSecBuffer(nullptr, 0, SECBUFFER_EMPTY)
1288 };
1289 SecBufferDesc message{
1290 SECBUFFER_VERSION,
1291 ARRAYSIZE(inputBuffers),
1292 inputBuffers
1293 };
1294 auto status = EncryptMessage(&contextHandle, 0, &message, 0);
1295 if (status != SEC_E_OK) {
1296 setErrorAndEmit(QAbstractSocket::SslInternalError,
1297 QSslSocket::tr("Schannel failed to encrypt data: %1")
1298 .arg(schannelErrorToString(status)));
1299 return;
1300 }
1301 // Data was encrypted successfully, so we free() what we peek()ed earlier
1302 writeBuffer.free(size);
1303
1304 // The trailer's size is not final, so resize fullMessage to not send trailing junk
1305 fullMessage.resize(inputBuffers[0].cbBuffer + inputBuffers[1].cbBuffer + inputBuffers[2].cbBuffer);
1306 const qint64 bytesWritten = plainSocket->write(fullMessage);
1307 #ifdef QSSLSOCKET_DEBUG
1308 qCDebug(lcSsl, "Wrote %lld of total %d bytes", bytesWritten, fullMessage.length());
1309 #endif
1310 if (bytesWritten >= 0) {
1311 totalBytesWritten += bytesWritten;
1312 } else {
1313 setErrorAndEmit(plainSocket->error(), plainSocket->errorString());
1314 return;
1315 }
1316 }
1317
1318 if (totalBytesWritten > 0) {
1319 // Don't emit bytesWritten() recursively.
1320 if (!emittedBytesWritten) {
1321 emittedBytesWritten = true;
1322 emit q->bytesWritten(totalBytesWritten);
1323 emittedBytesWritten = false;
1324 }
1325 emit q->channelBytesWritten(0, totalBytesWritten);
1326 }
1327 }
1328
1329 if (connectionEncrypted) { // Decrypt data from remote
1330 int totalRead = 0;
1331 bool hadIncompleteData = false;
1332 while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) {
1333 if (missingData > plainSocket->bytesAvailable()
1334 && (!readBufferMaxSize || readBufferMaxSize >= missingData)) {
1335 #ifdef QSSLSOCKET_DEBUG
1336 qCDebug(lcSsl, "We're still missing %lld bytes, will check later.", missingData);
1337 #endif
1338 break;
1339 }
1340
1341 missingData = 0;
1342 const qint64 bytesRead = readToBuffer(intermediateBuffer, plainSocket);
1343 #ifdef QSSLSOCKET_DEBUG
1344 qCDebug(lcSsl, "Read %lld encrypted bytes from the socket", bytesRead);
1345 #endif
1346 if (intermediateBuffer.length() == 0 || (hadIncompleteData && bytesRead == 0)) {
1347 #ifdef QSSLSOCKET_DEBUG
1348 qCDebug(lcSsl, (hadIncompleteData ? "No new data received, leaving loop!"
1349 : "Nothing to decrypt, leaving loop!"));
1350 #endif
1351 break;
1352 }
1353 hadIncompleteData = false;
1354 #ifdef QSSLSOCKET_DEBUG
1355 qCDebug(lcSsl, "Total amount of bytes to decrypt: %d", intermediateBuffer.length());
1356 #endif
1357
1358 SecBuffer dataBuffer[4]{
1359 createSecBuffer(intermediateBuffer, SECBUFFER_DATA),
1360 createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
1361 createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
1362 createSecBuffer(nullptr, 0, SECBUFFER_EMPTY)
1363 };
1364 SecBufferDesc message{
1365 SECBUFFER_VERSION,
1366 ARRAYSIZE(dataBuffer),
1367 dataBuffer
1368 };
1369 auto status = DecryptMessage(&contextHandle, &message, 0, nullptr);
1370 if (status == SEC_E_OK || status == SEC_I_RENEGOTIATE || status == SEC_I_CONTEXT_EXPIRED) {
1371 // There can still be 0 output even if it succeeds, this is fine
1372 if (dataBuffer[1].cbBuffer > 0) {
1373 // It is always decrypted in-place.
1374 // But [0] is the STREAM_HEADER, [1] is the DATA and [2] is the STREAM_TRAILER.
1375 // The pointers in all of those still point into 'intermediateBuffer'.
1376 buffer.append(static_cast<char *>(dataBuffer[1].pvBuffer),
1377 dataBuffer[1].cbBuffer);
1378 totalRead += dataBuffer[1].cbBuffer;
1379 #ifdef QSSLSOCKET_DEBUG
1380 qCDebug(lcSsl, "Decrypted %lu bytes. New read buffer size: %d",
1381 dataBuffer[1].cbBuffer, buffer.size());
1382 #endif
1383 }
1384 if (dataBuffer[3].BufferType == SECBUFFER_EXTRA) {
1385 // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
1386 // dataBuffer[3].cbBuffer indicates the amount of bytes _NOT_ processed,
1387 // the rest need to be stored.
1388 retainExtraData(intermediateBuffer, dataBuffer[3]);
1389 } else {
1390 intermediateBuffer.resize(0);
1391 }
1392 }
1393
1394 if (status == SEC_E_INCOMPLETE_MESSAGE) {
1395 missingData = checkIncompleteData(dataBuffer[0]);
1396 #ifdef QSSLSOCKET_DEBUG
1397 qCDebug(lcSsl, "We didn't have enough data to decrypt anything, will try again!");
1398 #endif
1399 // We try again, but if we don't get any more data then we leave
1400 hadIncompleteData = true;
1401 } else if (status == SEC_E_INVALID_HANDLE) {
1402 // I don't think this should happen, if it does we're done...
1403 qCWarning(lcSsl, "The internal SSPI handle is invalid!");
1404 Q_UNREACHABLE();
1405 } else if (status == SEC_E_INVALID_TOKEN) {
1406 qCWarning(lcSsl, "Got SEC_E_INVALID_TOKEN!");
1407 Q_UNREACHABLE(); // Happened once due to a bug, but shouldn't generally happen(?)
1408 } else if (status == SEC_E_MESSAGE_ALTERED) {
1409 // The message has been altered, disconnect now.
1410 shutdown = true; // skips sending the shutdown alert
1411 disconnectFromHost();
1412 setErrorAndEmit(QAbstractSocket::SslInternalError,
1413 schannelErrorToString(status));
1414 break;
1415 } else if (status == SEC_E_OUT_OF_SEQUENCE) {
1416 // @todo: I don't know if this one is actually "fatal"..
1417 // This path might never be hit as it seems this is for connection-oriented connections,
1418 // while SEC_E_MESSAGE_ALTERED is for stream-oriented ones (what we use).
1419 shutdown = true; // skips sending the shutdown alert
1420 disconnectFromHost();
1421 setErrorAndEmit(QAbstractSocket::SslInternalError,
1422 schannelErrorToString(status));
1423 break;
1424 } else if (status == SEC_I_CONTEXT_EXPIRED) {
1425 // 'remote' has initiated a shutdown
1426 disconnectFromHost();
1427 setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
1428 schannelErrorToString(status));
1429 break;
1430 } else if (status == SEC_I_RENEGOTIATE) {
1431 // 'remote' wants to renegotiate
1432 #ifdef QSSLSOCKET_DEBUG
1433 qCDebug(lcSsl, "The peer wants to renegotiate.");
1434 #endif
1435 schannelState = SchannelState::Renegotiate;
1436 renegotiating = true;
1437
1438 // We need to call 'continueHandshake' or else there's no guarantee it ever gets called
1439 continueHandshake();
1440 break;
1441 }
1442 }
1443
1444 if (totalRead) {
1445 if (readyReadEmittedPointer)
1446 *readyReadEmittedPointer = true;
1447 emit q->readyRead();
1448 emit q->channelReadyRead(0);
1449 }
1450 }
1451 }
1452
sendShutdown()1453 void QSslSocketBackendPrivate::sendShutdown()
1454 {
1455 const bool isClient = mode == QSslSocket::SslClientMode;
1456 DWORD shutdownToken = SCHANNEL_SHUTDOWN;
1457 SecBuffer buffer = createSecBuffer(&shutdownToken, sizeof(SCHANNEL_SHUTDOWN), SECBUFFER_TOKEN);
1458 SecBufferDesc token{
1459 SECBUFFER_VERSION,
1460 1,
1461 &buffer
1462 };
1463 auto status = ApplyControlToken(&contextHandle, &token);
1464
1465 if (status != SEC_E_OK) {
1466 #ifdef QSSLSOCKET_DEBUG
1467 qCDebug(lcSsl) << "Failed to apply shutdown control token:" << schannelErrorToString(status);
1468 #endif
1469 return;
1470 }
1471
1472 SecBuffer outBuffers[3];
1473 outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
1474 outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
1475 outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
1476 auto freeBuffers = qScopeGuard([&outBuffers]() {
1477 for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
1478 if (outBuffers[i].pvBuffer)
1479 FreeContextBuffer(outBuffers[i].pvBuffer);
1480 }
1481 });
1482 SecBufferDesc outputBufferDesc{
1483 SECBUFFER_VERSION,
1484 ARRAYSIZE(outBuffers),
1485 outBuffers
1486 };
1487
1488 ULONG contextReq = getContextRequirements();
1489 TimeStamp expiry;
1490 if (isClient) {
1491 status = InitializeSecurityContext(&credentialHandle, // phCredential
1492 &contextHandle, // phContext
1493 const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
1494 contextReq, // fContextReq
1495 0, // Reserved1
1496 0, // TargetDataRep (unused)
1497 nullptr, // pInput
1498 0, // Reserved2
1499 nullptr, // phNewContext (we already have one)
1500 &outputBufferDesc, // pOutput
1501 &contextAttributes, // pfContextAttr
1502 &expiry // ptsExpiry
1503 );
1504 } else {
1505 status = AcceptSecurityContext(
1506 &credentialHandle, // phCredential
1507 &contextHandle, // phContext
1508 nullptr, // pInput
1509 contextReq, // fContextReq
1510 0, // TargetDataRep (unused)
1511 nullptr, // phNewContext
1512 &outputBufferDesc, // pOutput
1513 &contextAttributes, // pfContextAttr,
1514 &expiry // ptsTimeStamp
1515 );
1516 }
1517 if (status == SEC_E_OK || status == SEC_I_CONTEXT_EXPIRED) {
1518 if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, false)) {
1519 // We failed to send the shutdown message, but it's not that important since we're
1520 // shutting down anyway.
1521 return;
1522 }
1523 } else {
1524 #ifdef QSSLSOCKET_DEBUG
1525 qCDebug(lcSsl) << "Failed to initialize shutdown:" << schannelErrorToString(status);
1526 #endif
1527 }
1528 }
1529
disconnectFromHost()1530 void QSslSocketBackendPrivate::disconnectFromHost()
1531 {
1532 if (SecIsValidHandle(&contextHandle)) {
1533 if (!shutdown) {
1534 shutdown = true;
1535 if (plainSocket->state() != QAbstractSocket::UnconnectedState) {
1536 if (connectionEncrypted) {
1537 // Read as much as possible because this is likely our last chance
1538 qint64 tempMax = readBufferMaxSize;
1539 readBufferMaxSize = 0;
1540 transmit();
1541 readBufferMaxSize = tempMax;
1542 sendShutdown();
1543 }
1544 }
1545 }
1546 }
1547 if (plainSocket->state() != QAbstractSocket::UnconnectedState)
1548 plainSocket->disconnectFromHost();
1549 }
1550
disconnected()1551 void QSslSocketBackendPrivate::disconnected()
1552 {
1553 shutdown = true;
1554 connectionEncrypted = false;
1555 deallocateContext();
1556 freeCredentialsHandle();
1557 }
1558
sessionCipher() const1559 QSslCipher QSslSocketBackendPrivate::sessionCipher() const
1560 {
1561 if (!connectionEncrypted)
1562 return QSslCipher();
1563 return QSslCipher(QStringLiteral("Schannel"), sessionProtocol());
1564 }
1565
sessionProtocol() const1566 QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
1567 {
1568 if (!connectionEncrypted)
1569 return QSsl::SslProtocol::UnknownProtocol;
1570 return toQtSslProtocol(connectionInfo.dwProtocol);
1571 }
1572
continueHandshake()1573 void QSslSocketBackendPrivate::continueHandshake()
1574 {
1575 Q_Q(QSslSocket);
1576 const bool isServer = mode == QSslSocket::SslServerMode;
1577 switch (schannelState) {
1578 case SchannelState::InitializeHandshake:
1579 if (!SecIsValidHandle(&credentialHandle) && !acquireCredentialsHandle()) {
1580 disconnectFromHost();
1581 return;
1582 }
1583 if (!SecIsValidHandle(&credentialHandle)) // Needed to support tst_QSslSocket::setEmptyKey
1584 return;
1585 if (!SecIsValidHandle(&contextHandle) && !(isServer ? acceptContext() : createContext())) {
1586 disconnectFromHost();
1587 return;
1588 }
1589 if (schannelState != SchannelState::PerformHandshake)
1590 break;
1591 Q_FALLTHROUGH();
1592 case SchannelState::PerformHandshake:
1593 if (!performHandshake()) {
1594 disconnectFromHost();
1595 return;
1596 }
1597 if (schannelState != SchannelState::VerifyHandshake)
1598 break;
1599 Q_FALLTHROUGH();
1600 case SchannelState::VerifyHandshake:
1601 // if we're in shutdown or renegotiating then we might not need to verify
1602 // (since we already did)
1603 if (!verifyHandshake()) {
1604 shutdown = true; // Skip sending shutdown alert
1605 q->abort(); // We don't want to send buffered data
1606 disconnectFromHost();
1607 return;
1608 }
1609 if (schannelState != SchannelState::Done)
1610 break;
1611 Q_FALLTHROUGH();
1612 case SchannelState::Done:
1613 // connectionEncrypted is already true if we come here from a renegotiation
1614 if (!connectionEncrypted) {
1615 connectionEncrypted = true; // all is done
1616 emit q->encrypted();
1617 }
1618 renegotiating = false;
1619 if (pendingClose) {
1620 pendingClose = false;
1621 disconnectFromHost();
1622 } else {
1623 transmit();
1624 }
1625 break;
1626 case SchannelState::Renegotiate:
1627 if (!renegotiate()) {
1628 disconnectFromHost();
1629 return;
1630 }
1631 break;
1632 }
1633 }
1634
defaultCiphers()1635 QList<QSslCipher> QSslSocketBackendPrivate::defaultCiphers()
1636 {
1637 QList<QSslCipher> ciphers;
1638 // @temp (I hope), stolen from qsslsocket_winrt.cpp
1639 const QString protocolStrings[] = { QStringLiteral("TLSv1"), QStringLiteral("TLSv1.1"),
1640 QStringLiteral("TLSv1.2"), QStringLiteral("TLSv1.3") };
1641 const QSsl::SslProtocol protocols[] = { QSsl::TlsV1_0, QSsl::TlsV1_1,
1642 QSsl::TlsV1_2, QSsl::TlsV1_3 };
1643 const int size = ARRAYSIZE(protocols);
1644 Q_STATIC_ASSERT(size == ARRAYSIZE(protocolStrings));
1645 ciphers.reserve(size);
1646 for (int i = 0; i < size; ++i) {
1647 QSslCipher cipher;
1648 cipher.d->isNull = false;
1649 cipher.d->name = QStringLiteral("Schannel");
1650 cipher.d->protocol = protocols[i];
1651 cipher.d->protocolString = protocolStrings[i];
1652 ciphers.append(cipher);
1653 }
1654
1655 return ciphers;
1656 }
1657
verify(const QList<QSslCertificate> & certificateChain,const QString & hostName)1658 QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain,
1659 const QString &hostName)
1660 {
1661 Q_UNUSED(certificateChain);
1662 Q_UNUSED(hostName);
1663
1664 Q_UNIMPLEMENTED();
1665 return {}; // @future implement(?)
1666 }
1667
importPkcs12(QIODevice * device,QSslKey * key,QSslCertificate * cert,QList<QSslCertificate> * caCertificates,const QByteArray & passPhrase)1668 bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert,
1669 QList<QSslCertificate> *caCertificates,
1670 const QByteArray &passPhrase)
1671 {
1672 Q_UNUSED(device);
1673 Q_UNUSED(key);
1674 Q_UNUSED(cert);
1675 Q_UNUSED(caCertificates);
1676 Q_UNUSED(passPhrase);
1677 // @future: can load into its own certificate store (encountered problems extracting key).
1678 Q_UNIMPLEMENTED();
1679 return false;
1680 }
1681
1682 /*
1683 Copied from qsslsocket_mac.cpp, which was copied from qsslsocket_openssl.cpp
1684 */
checkSslErrors()1685 bool QSslSocketBackendPrivate::checkSslErrors()
1686 {
1687 if (sslErrors.isEmpty())
1688 return true;
1689 Q_Q(QSslSocket);
1690
1691 emit q->sslErrors(sslErrors);
1692
1693 const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
1694 || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
1695 && mode == QSslSocket::SslClientMode);
1696 const bool doEmitSslError = !verifyErrorsHaveBeenIgnored();
1697 // check whether we need to emit an SSL handshake error
1698 if (doVerifyPeer && doEmitSslError) {
1699 if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) {
1700 pauseSocketNotifiers(q);
1701 paused = true;
1702 } else {
1703 setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
1704 sslErrors.constFirst().errorString());
1705 plainSocket->disconnectFromHost();
1706 }
1707 return false;
1708 }
1709
1710 return true;
1711 }
1712
initializeCertificateStores()1713 void QSslSocketBackendPrivate::initializeCertificateStores()
1714 {
1715 //// helper function which turns a chain into a certificate store
1716 auto createStoreFromCertificateChain = [](const QList<QSslCertificate> certChain, const QSslKey &privateKey) {
1717 const wchar_t *passphrase = L"";
1718 // Need to embed the private key in the certificate
1719 QByteArray pkcs12 = _q_makePkcs12(certChain,
1720 privateKey,
1721 QString::fromWCharArray(passphrase, 0));
1722 CRYPT_DATA_BLOB pfxBlob;
1723 pfxBlob.cbData = DWORD(pkcs12.length());
1724 pfxBlob.pbData = reinterpret_cast<unsigned char *>(pkcs12.data());
1725 return QHCertStorePointer(PFXImportCertStore(&pfxBlob, passphrase, 0));
1726 };
1727
1728 if (!configuration.localCertificateChain.isEmpty()) {
1729 if (configuration.privateKey.isNull()) {
1730 setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
1731 QSslSocket::tr("Cannot provide a certificate with no key"));
1732 return;
1733 }
1734 if (localCertificateStore == nullptr) {
1735 localCertificateStore = createStoreFromCertificateChain(configuration.localCertificateChain,
1736 configuration.privateKey);
1737 if (localCertificateStore == nullptr)
1738 qCWarning(lcSsl, "Failed to load certificate chain!");
1739 }
1740 }
1741
1742 if (!configuration.caCertificates.isEmpty() && !caCertificateStore) {
1743 caCertificateStore = createStoreFromCertificateChain(configuration.caCertificates,
1744 {}); // No private key for the CA certs
1745 }
1746 }
1747
verifyCertContext(CERT_CONTEXT * certContext)1748 bool QSslSocketBackendPrivate::verifyCertContext(CERT_CONTEXT *certContext)
1749 {
1750 Q_ASSERT(certContext);
1751 Q_Q(QSslSocket);
1752
1753 const bool isClient = mode == QSslSocket::SslClientMode;
1754
1755 // Create a collection of stores so we can pass in multiple stores as additional locations to
1756 // search for the certificate chain
1757 auto tempCertCollection = QHCertStorePointer(CertOpenStore(CERT_STORE_PROV_COLLECTION,
1758 X509_ASN_ENCODING,
1759 0,
1760 CERT_STORE_CREATE_NEW_FLAG,
1761 nullptr));
1762 if (!tempCertCollection) {
1763 #ifdef QSSLSOCKET_DEBUG
1764 qCWarning(lcSsl, "Failed to create certificate store collection!");
1765 #endif
1766 return false;
1767 }
1768
1769 if (rootCertOnDemandLoadingAllowed()) {
1770 // @future(maybe): following the OpenSSL backend these certificates should be added into
1771 // the Ca list, not just included during verification.
1772 // That being said, it's not trivial to add the root certificates (if and only if they
1773 // came from the system root store). And I don't see this mentioned in our documentation.
1774 auto rootStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT"));
1775 if (!rootStore) {
1776 #ifdef QSSLSOCKET_DEBUG
1777 qCWarning(lcSsl, "Failed to open the system root CA certificate store!");
1778 #endif
1779 return false;
1780 } else if (!CertAddStoreToCollection(tempCertCollection.get(), rootStore.get(), 0, 1)) {
1781 #ifdef QSSLSOCKET_DEBUG
1782 qCWarning(lcSsl, "Failed to add the system root CA certificate store to the certificate store collection!");
1783 #endif
1784 return false;
1785 }
1786 }
1787 if (caCertificateStore) {
1788 if (!CertAddStoreToCollection(tempCertCollection.get(), caCertificateStore.get(), 0, 1)) {
1789 #ifdef QSSLSOCKET_DEBUG
1790 qCWarning(lcSsl, "Failed to add the user's CA certificate store to the certificate store collection!");
1791 #endif
1792 return false;
1793 }
1794 }
1795
1796 if (!CertAddStoreToCollection(tempCertCollection.get(), certContext->hCertStore, 0, 0)) {
1797 #ifdef QSSLSOCKET_DEBUG
1798 qCWarning(lcSsl, "Failed to add certificate's origin store to the certificate store collection!");
1799 #endif
1800 return false;
1801 }
1802
1803 CERT_CHAIN_PARA parameters;
1804 ZeroMemory(¶meters, sizeof(parameters));
1805 parameters.cbSize = sizeof(CERT_CHAIN_PARA);
1806 parameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
1807 parameters.RequestedUsage.Usage.cUsageIdentifier = 1;
1808 LPSTR oid = LPSTR(isClient ? szOID_PKIX_KP_SERVER_AUTH
1809 : szOID_PKIX_KP_CLIENT_AUTH);
1810 parameters.RequestedUsage.Usage.rgpszUsageIdentifier = &oid;
1811
1812 configuration.peerCertificate.clear();
1813 configuration.peerCertificateChain.clear();
1814 const CERT_CHAIN_CONTEXT *chainContext = nullptr;
1815 auto freeCertChain = qScopeGuard([&chainContext]() {
1816 if (chainContext)
1817 CertFreeCertificateChain(chainContext);
1818 });
1819 BOOL status = CertGetCertificateChain(nullptr, // hChainEngine, default
1820 certContext, // pCertContext
1821 nullptr, // pTime, 'now'
1822 tempCertCollection.get(), // hAdditionalStore, additional cert store
1823 ¶meters, // pChainPara
1824 CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, // dwFlags
1825 nullptr, // reserved
1826 &chainContext // ppChainContext
1827 );
1828 if (status == FALSE || !chainContext || chainContext->cChain == 0) {
1829 QSslError error(QSslError::UnableToVerifyFirstCertificate);
1830 sslErrors += error;
1831 emit q->peerVerifyError(error);
1832 return q->state() == QAbstractSocket::ConnectedState;
1833 }
1834
1835 // Helper-function to get a QSslCertificate given a CERT_CHAIN_ELEMENT
1836 static auto getCertificateFromChainElement = [](CERT_CHAIN_ELEMENT *element) {
1837 if (!element)
1838 return QSslCertificate();
1839
1840 const CERT_CONTEXT *certContext = element->pCertContext;
1841 return QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(certContext);
1842 };
1843
1844 // Pick a chain to use as the certificate chain, if multiple are available:
1845 // According to https://docs.microsoft.com/en-gb/windows/desktop/api/wincrypt/ns-wincrypt-_cert_chain_context
1846 // this seems to be the best way to get a trusted chain.
1847 CERT_SIMPLE_CHAIN *chain = chainContext->rgpChain[chainContext->cChain - 1];
1848
1849 if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN) {
1850 auto error = QSslError(QSslError::SslError::UnableToGetIssuerCertificate,
1851 getCertificateFromChainElement(chain->rgpElement[chain->cElement - 1]));
1852 sslErrors += error;
1853 emit q->peerVerifyError(error);
1854 if (q->state() != QAbstractSocket::ConnectedState)
1855 return false;
1856 }
1857 if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_INVALID_BASIC_CONSTRAINTS) {
1858 // @Note: This is actually one of two errors:
1859 // "either the certificate cannot be used to issue other certificates, or the chain path length has been exceeded."
1860 // But here we are checking the chain's status, so we assume the "issuing" error cannot occur here.
1861 auto error = QSslError(QSslError::PathLengthExceeded);
1862 sslErrors += error;
1863 emit q->peerVerifyError(error);
1864 if (q->state() != QAbstractSocket::ConnectedState)
1865 return false;
1866 }
1867 static const DWORD leftoverCertChainErrorMask = CERT_TRUST_IS_CYCLIC | CERT_TRUST_INVALID_EXTENSION
1868 | CERT_TRUST_INVALID_POLICY_CONSTRAINTS | CERT_TRUST_INVALID_NAME_CONSTRAINTS
1869 | CERT_TRUST_CTL_IS_NOT_TIME_VALID | CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID
1870 | CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE;
1871 if (chain->TrustStatus.dwErrorStatus & leftoverCertChainErrorMask) {
1872 auto error = QSslError(QSslError::SslError::UnspecifiedError);
1873 sslErrors += error;
1874 emit q->peerVerifyError(error);
1875 if (q->state() != QAbstractSocket::ConnectedState)
1876 return false;
1877 }
1878
1879 DWORD verifyDepth = chain->cElement;
1880 if (configuration.peerVerifyDepth > 0 && DWORD(configuration.peerVerifyDepth) < verifyDepth)
1881 verifyDepth = DWORD(configuration.peerVerifyDepth);
1882
1883 for (DWORD i = 0; i < verifyDepth; i++) {
1884 CERT_CHAIN_ELEMENT *element = chain->rgpElement[i];
1885 QSslCertificate certificate = getCertificateFromChainElement(element);
1886 const QList<QSslCertificateExtension> extensions = certificate.extensions();
1887
1888 #ifdef QSSLSOCKET_DEBUG
1889 qCDebug(lcSsl) << "issuer:" << certificate.issuerDisplayName()
1890 << "\nsubject:" << certificate.subjectDisplayName()
1891 << "\nQSslCertificate info:" << certificate
1892 << "\nextended error info:" << element->pwszExtendedErrorInfo
1893 << "\nerror status:" << element->TrustStatus.dwErrorStatus;
1894 #endif
1895
1896 configuration.peerCertificateChain.append(certificate);
1897
1898 if (certificate.isBlacklisted()) {
1899 const auto error = QSslError(QSslError::CertificateBlacklisted, certificate);
1900 sslErrors += error;
1901 emit q->peerVerifyError(error);
1902 if (q->state() != QAbstractSocket::ConnectedState)
1903 return false;
1904 }
1905
1906 LONG result = CertVerifyTimeValidity(nullptr /*== now */, element->pCertContext->pCertInfo);
1907 if (result != 0) {
1908 auto error = QSslError(result == -1 ? QSslError::CertificateNotYetValid
1909 : QSslError::CertificateExpired,
1910 certificate);
1911 sslErrors += error;
1912 emit q->peerVerifyError(error);
1913 if (q->state() != QAbstractSocket::ConnectedState)
1914 return false;
1915 }
1916
1917 //// Errors
1918 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID) {
1919 // handled right above
1920 Q_ASSERT(!sslErrors.isEmpty());
1921 }
1922 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED) {
1923 auto error = QSslError(QSslError::CertificateRevoked, certificate);
1924 sslErrors += error;
1925 emit q->peerVerifyError(error);
1926 if (q->state() != QAbstractSocket::ConnectedState)
1927 return false;
1928 }
1929 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID) {
1930 auto error = QSslError(QSslError::CertificateSignatureFailed, certificate);
1931 sslErrors += error;
1932 emit q->peerVerifyError(error);
1933 if (q->state() != QAbstractSocket::ConnectedState)
1934 return false;
1935 }
1936
1937 // While netscape shouldn't be relevant now it defined an extension which is
1938 // still in use. Schannel does not check this automatically, so we do it here.
1939 // It is used to differentiate between client and server certificates.
1940 if (netscapeWrongCertType(extensions, isClient))
1941 element->TrustStatus.dwErrorStatus |= CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
1942
1943 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_VALID_FOR_USAGE) {
1944 auto error = QSslError(QSslError::InvalidPurpose, certificate);
1945 sslErrors += error;
1946 emit q->peerVerifyError(error);
1947 if (q->state() != QAbstractSocket::ConnectedState)
1948 return false;
1949 }
1950 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_UNTRUSTED_ROOT) {
1951 // Override this error if we have the certificate inside our trusted CAs list.
1952 const bool isTrustedRoot = configuration.caCertificates.contains(certificate);
1953 if (!isTrustedRoot) {
1954 auto error = QSslError(QSslError::CertificateUntrusted, certificate);
1955 sslErrors += error;
1956 emit q->peerVerifyError(error);
1957 if (q->state() != QAbstractSocket::ConnectedState)
1958 return false;
1959 }
1960 }
1961 static const DWORD certRevocationCheckUnavailableError = CERT_TRUST_IS_OFFLINE_REVOCATION
1962 | CERT_TRUST_REVOCATION_STATUS_UNKNOWN;
1963 if (element->TrustStatus.dwErrorStatus & certRevocationCheckUnavailableError) {
1964 // @future(maybe): Do something with this
1965 }
1966
1967 // Dumping ground of errors that don't fit our specific errors
1968 static const DWORD leftoverCertErrorMask = CERT_TRUST_IS_CYCLIC
1969 | CERT_TRUST_INVALID_EXTENSION | CERT_TRUST_INVALID_NAME_CONSTRAINTS
1970 | CERT_TRUST_INVALID_POLICY_CONSTRAINTS
1971 | CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT
1972 | CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT
1973 | CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT
1974 | CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT
1975 | CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT;
1976 if (element->TrustStatus.dwErrorStatus & leftoverCertErrorMask) {
1977 auto error = QSslError(QSslError::UnspecifiedError, certificate);
1978 sslErrors += error;
1979 emit q->peerVerifyError(error);
1980 if (q->state() != QAbstractSocket::ConnectedState)
1981 return false;
1982 }
1983 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_INVALID_BASIC_CONSTRAINTS) {
1984 auto it = std::find_if(extensions.cbegin(), extensions.cend(),
1985 [](const QSslCertificateExtension &extension) {
1986 return extension.name() == QLatin1String("basicConstraints");
1987 });
1988 if (it != extensions.cend()) {
1989 // @Note: This is actually one of two errors:
1990 // "either the certificate cannot be used to issue other certificates,
1991 // or the chain path length has been exceeded."
1992 QVariantMap basicConstraints = it->value().toMap();
1993 QSslError error;
1994 if (i > 0 && !basicConstraints.value(QLatin1String("ca"), false).toBool())
1995 error = QSslError(QSslError::InvalidPurpose, certificate);
1996 else
1997 error = QSslError(QSslError::PathLengthExceeded, certificate);
1998 sslErrors += error;
1999 emit q->peerVerifyError(error);
2000 if (q->state() != QAbstractSocket::ConnectedState)
2001 return false;
2002 }
2003 }
2004 if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_EXPLICIT_DISTRUST) {
2005 auto error = QSslError(QSslError::CertificateBlacklisted, certificate);
2006 sslErrors += error;
2007 emit q->peerVerifyError(error);
2008 if (q->state() != QAbstractSocket::ConnectedState)
2009 return false;
2010 }
2011
2012 if (element->TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED) {
2013 // If it's self-signed *and* a CA then we can assume it's a root CA certificate
2014 // and we can ignore the "self-signed" note:
2015 // We check the basicConstraints certificate extension when possible, but this didn't
2016 // exist for version 1, so we can only guess in that case
2017 const bool isRootCertificateAuthority = isCertificateAuthority(extensions)
2018 || certificate.version() == "1";
2019
2020 // Root certificate tends to be signed by themselves, so ignore self-signed status.
2021 if (!isRootCertificateAuthority) {
2022 auto error = QSslError(QSslError::SelfSignedCertificate, certificate);
2023 sslErrors += error;
2024 emit q->peerVerifyError(error);
2025 if (q->state() != QAbstractSocket::ConnectedState)
2026 return false;
2027 }
2028 }
2029 }
2030
2031 if (!configuration.peerCertificateChain.isEmpty())
2032 configuration.peerCertificate = configuration.peerCertificateChain.first();
2033
2034 // @Note: Somewhat copied from qsslsocket_mac.cpp
2035 const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
2036 || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
2037 && mode == QSslSocket::SslClientMode);
2038 // Check the peer certificate itself. First try the subject's common name
2039 // (CN) as a wildcard, then try all alternate subject name DNS entries the
2040 // same way.
2041 if (!configuration.peerCertificate.isNull()) {
2042 // but only if we're a client connecting to a server
2043 // if we're the server, don't check CN
2044 if (mode == QSslSocket::SslClientMode) {
2045 const QString peerName(verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName);
2046 if (!isMatchingHostname(configuration.peerCertificate, peerName)) {
2047 // No matches in common names or alternate names.
2048 const QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate);
2049 sslErrors += error;
2050 emit q->peerVerifyError(error);
2051 if (q->state() != QAbstractSocket::ConnectedState)
2052 return false;
2053 }
2054 }
2055 } else if (doVerifyPeer) {
2056 // No peer certificate presented. Report as error if the socket
2057 // expected one.
2058 const QSslError error(QSslError::NoPeerCertificate);
2059 sslErrors += error;
2060 emit q->peerVerifyError(error);
2061 if (q->state() != QAbstractSocket::ConnectedState)
2062 return false;
2063 }
2064
2065 return true;
2066 }
2067
rootCertOnDemandLoadingAllowed()2068 bool QSslSocketBackendPrivate::rootCertOnDemandLoadingAllowed()
2069 {
2070 return allowRootCertOnDemandLoading && s_loadRootCertsOnDemand;
2071 }
2072
2073 QT_END_NAMESPACE
2074