diff options
-rw-r--r-- | src/network/ssl/qsslsocket_schannel.cpp | 126 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_schannel_p.h | 3 | ||||
-rw-r--r-- | tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp | 52 |
3 files changed, 137 insertions, 44 deletions
diff --git a/src/network/ssl/qsslsocket_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp index f0a9f6a027..0722d380d0 100644 --- a/src/network/ssl/qsslsocket_schannel.cpp +++ b/src/network/ssl/qsslsocket_schannel.cpp @@ -62,6 +62,12 @@ #define SUPPORTS_ALPN 1 #endif +// Redstone 5/1809 has all the API available, but TLS 1.3 is not enabled until a later version of +// Win 10, checked at runtime in supportsTls13() +#if NTDDI_VERSION >= NTDDI_WIN10_RS5 +#define SUPPORTS_TLS13 1 +#endif + // Not defined in MinGW #ifndef SECBUFFER_ALERT #define SECBUFFER_ALERT 17 @@ -211,6 +217,22 @@ QString schannelErrorToString(qint32 status) } } +bool supportsTls13() +{ +#ifdef SUPPORTS_TLS13 + static bool supported = []() { + const auto current = QOperatingSystemVersion::current(); + // 20221 just happens to be the preview version I run on my laptop where I tested TLS 1.3. + const auto minimum = + QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 20221); + return current >= minimum; + }(); + return supported; +#else + return false; +#endif +} + DWORD toSchannelProtocol(QSsl::SslProtocol protocol) { DWORD protocols = SP_PROT_NONE; @@ -224,7 +246,8 @@ DWORD toSchannelProtocol(QSsl::SslProtocol protocol) return DWORD(-1); // Not supported at the moment (@future) case QSsl::AnyProtocol: protocols = SP_PROT_TLS1_0 | SP_PROT_TLS1_1 | SP_PROT_TLS1_2; - // @future Add TLS 1.3 when supported by Windows! + if (supportsTls13()) + protocols |= SP_PROT_TLS1_3; break; case QSsl::TlsV1_0: protocols = SP_PROT_TLS1_0; @@ -236,7 +259,7 @@ DWORD toSchannelProtocol(QSsl::SslProtocol protocol) protocols = SP_PROT_TLS1_2; break; case QSsl::TlsV1_3: - if ((false)) // @future[0/1] Replace with version check once it's supported in Windows + if (supportsTls13()) protocols = SP_PROT_TLS1_3; else protocols = DWORD(-1); @@ -254,7 +277,7 @@ DWORD toSchannelProtocol(QSsl::SslProtocol protocol) protocols |= SP_PROT_TLS1_2; Q_FALLTHROUGH(); case QSsl::TlsV1_3OrLater: - if ((false)) // @future[1/1] Also replace this with a version check + if (supportsTls13()) protocols |= SP_PROT_TLS1_3; else if (protocol == QSsl::TlsV1_3OrLater) protocols = DWORD(-1); // if TlsV1_3OrLater was specifically chosen we should fail @@ -263,6 +286,18 @@ DWORD toSchannelProtocol(QSsl::SslProtocol protocol) return protocols; } +#ifdef SUPPORTS_TLS13 +// In the new API that descended down upon us we are not asked which protocols we want +// but rather which protocols we don't want. So now we have this function to disable +// anything that is not enabled. +DWORD toSchannelProtocolNegated(QSsl::SslProtocol protocol) +{ + DWORD protocols = SP_PROT_ALL; // all protocols + protocols &= ~toSchannelProtocol(protocol); // minus the one(s) we want + return protocols; +} +#endif + /*! \internal Used when converting the established session's \a protocol back to @@ -676,40 +711,80 @@ bool QSslSocketBackendPrivate::acquireCredentialsHandle() certsCount = 1; Q_ASSERT(localCertContext); } - - SCHANNEL_CRED cred{ - SCHANNEL_CRED_VERSION, // dwVersion - certsCount, // cCreds - &localCertContext, // paCred (certificate(s) containing a private key for authentication) - nullptr, // hRootStore - - 0, // cMappers (reserved) - nullptr, // aphMappers (reserved) - - 0, // cSupportedAlgs - nullptr, // palgSupportedAlgs (nullptr = system default) @future: QSslCipher-related - - protocols, // grbitEnabledProtocols - 0, // dwMinimumCipherStrength (0 = system default) - 0, // dwMaximumCipherStrength (0 = system default) - 0, // dwSessionLifespan (0 = schannel default, 10 hours) - SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT - | SCH_CRED_NO_DEFAULT_CREDS, // dwFlags - 0 // dwCredFormat (must be 0) + void *credentials = nullptr; +#ifdef SUPPORTS_TLS13 + TLS_PARAMETERS tlsParameters = { + 0, + nullptr, + toSchannelProtocolNegated(configuration.protocol), // what protocols to disable + 0, + nullptr, + 0 }; + if (supportsTls13()) { + SCH_CREDENTIALS *cred = new SCH_CREDENTIALS{ + SCH_CREDENTIALS_VERSION, + 0, + certsCount, + &localCertContext, + nullptr, + 0, + nullptr, + 0, + SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT + | SCH_CRED_NO_DEFAULT_CREDS, + 1, + &tlsParameters + }; + credentials = cred; + } else +#endif // SUPPORTS_TLS13 + { + SCHANNEL_CRED *cred = new SCHANNEL_CRED{ + SCHANNEL_CRED_VERSION, // dwVersion + certsCount, // cCreds + &localCertContext, // paCred (certificate(s) containing a private key for authentication) + nullptr, // hRootStore + + 0, // cMappers (reserved) + nullptr, // aphMappers (reserved) + + 0, // cSupportedAlgs + nullptr, // palgSupportedAlgs (nullptr = system default) + + protocols, // grbitEnabledProtocols + 0, // dwMinimumCipherStrength (0 = system default) + 0, // dwMaximumCipherStrength (0 = system default) + 0, // dwSessionLifespan (0 = schannel default, 10 hours) + SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT + | SCH_CRED_NO_DEFAULT_CREDS, // dwFlags + 0 // dwCredFormat (must be 0) + }; + credentials = cred; + } + Q_ASSERT(credentials != nullptr); TimeStamp expiration{}; auto status = AcquireCredentialsHandle(nullptr, // pszPrincipal (unused) const_cast<wchar_t *>(UNISP_NAME), // pszPackage isClient ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND, // fCredentialUse nullptr, // pvLogonID (unused) - &cred, // pAuthData + credentials, // pAuthData nullptr, // pGetKeyFn (unused) nullptr, // pvGetKeyArgument (unused) &credentialHandle, // phCredential &expiration // ptsExpir ); +#ifdef SUPPORTS_TLS13 + if (supportsTls13()) { + delete static_cast<SCH_CREDENTIALS *>(credentials); + } else +#endif // SUPPORTS_TLS13 + { + delete static_cast<SCHANNEL_CRED *>(credentials); + } + if (status != SEC_E_OK) { setErrorAndEmit(QAbstractSocket::SslInternalError, schannelErrorToString(status)); return false; @@ -1194,6 +1269,9 @@ bool QSslSocketBackendPrivate::renegotiate() if (status == SEC_I_CONTINUE_NEEDED) { schannelState = SchannelState::PerformHandshake; return sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer); + } else if (status == SEC_E_OK) { + schannelState = SchannelState::PerformHandshake; + return true; } setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, QSslSocket::tr("Renegotiation was unsuccessful: %1").arg(schannelErrorToString(status))); diff --git a/src/network/ssl/qsslsocket_schannel_p.h b/src/network/ssl/qsslsocket_schannel_p.h index fe29dadec0..57c8c75629 100644 --- a/src/network/ssl/qsslsocket_schannel_p.h +++ b/src/network/ssl/qsslsocket_schannel_p.h @@ -58,8 +58,11 @@ QT_REQUIRE_CONFIG(schannel); #include "qsslsocket_p.h" #define SECURITY_WIN32 +#define SCHANNEL_USE_BLACKLISTS 1 +#include <Winternl.h> // needed for UNICODE defines #include <security.h> #include <schnlsp.h> +#undef SCHANNEL_USE_BLACKLISTS #undef SECURITY_WIN32 QT_BEGIN_NAMESPACE diff --git a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp index e6fea87011..6f5edfa1cf 100644 --- a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp @@ -341,6 +341,24 @@ QString httpServerCertChainPath() #endif // QT_TEST_SERVER } +bool supportsTls13() +{ +#ifdef TLS1_3_VERSION + return true; +#elif QT_CONFIG(schannel) + // Copied from qsslsocket_schannel.cpp #supportsTls13() + static bool supported = []() { + const auto current = QOperatingSystemVersion::current(); + const auto minimum = + QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 20221); + return current >= minimum; + }(); + return supported; +#else + return false; +#endif +} + } // unnamed namespace tst_QSslSocket::tst_QSslSocket() @@ -1168,13 +1186,12 @@ void tst_QSslSocket::protocol() socket->abort(); } -#ifdef TLS1_3_VERSION - { + if (supportsTls13()) { // qt-test-server probably doesn't allow TLSV1.3 socket->setProtocol(QSsl::TlsV1_3); QCOMPARE(socket->protocol(), QSsl::TlsV1_3); socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443); - if (setProxy && !socket->waitForEncrypted()) + if (!socket->waitForEncrypted(10'000)) QSKIP("TLS 1.3 is not supported by the test server or the test is flaky - see QTBUG-29941"); QCOMPARE(socket->protocol(), QSsl::TlsV1_3); socket->abort(); @@ -1182,12 +1199,11 @@ void tst_QSslSocket::protocol() socket->connectToHost(QtNetworkSettings::httpServerName(), 443); QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString())); socket->startClientEncryption(); - if (setProxy && !socket->waitForEncrypted()) + if (!socket->waitForEncrypted(10'000)) QSKIP("TLS 1.3 is not supported by the test server or the test is flaky - see QTBUG-29941"); QCOMPARE(socket->sessionProtocol(), QSsl::TlsV1_3); socket->abort(); } -#endif // TLS1_3_VERSION { // qt-test-server allows SSLV3, so it allows AnyProtocol. socket->setProtocol(QSsl::AnyProtocol); @@ -1335,30 +1351,26 @@ void tst_QSslSocket::protocolServerSide_data() QTest::newRow("tls1.0orlater-tls1.0") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_0 << true; QTest::newRow("tls1.0orlater-tls1.1") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_1 << true; QTest::newRow("tls1.0orlater-tls1.2") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_2 << true; -#ifdef TLS1_3_VERSION - QTest::newRow("tls1.0orlater-tls1.3") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_3 << true; -#endif + if (supportsTls13()) + QTest::newRow("tls1.0orlater-tls1.3") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_3 << true; QTest::newRow("tls1.1orlater-tls1.0") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_0 << false; QTest::newRow("tls1.1orlater-tls1.1") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_1 << true; QTest::newRow("tls1.1orlater-tls1.2") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_2 << true; -#ifdef TLS1_3_VERSION - QTest::newRow("tls1.1orlater-tls1.3") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_3 << true; -#endif + if (supportsTls13()) + QTest::newRow("tls1.1orlater-tls1.3") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_3 << true; QTest::newRow("tls1.2orlater-tls1.0") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_0 << false; QTest::newRow("tls1.2orlater-tls1.1") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_1 << false; QTest::newRow("tls1.2orlater-tls1.2") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_2 << true; -#ifdef TLS1_3_VERSION - QTest::newRow("tls1.2orlater-tls1.3") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_3 << true; -#endif -#ifdef TLS1_3_VERSION - QTest::newRow("tls1.3orlater-tls1.0") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_0 << false; - QTest::newRow("tls1.3orlater-tls1.1") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_1 << false; - QTest::newRow("tls1.3orlater-tls1.2") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_2 << false; - QTest::newRow("tls1.3orlater-tls1.3") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_3 << true; -#endif // TLS1_3_VERSION + if (supportsTls13()) { + QTest::newRow("tls1.2orlater-tls1.3") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_3 << true; + QTest::newRow("tls1.3orlater-tls1.0") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_0 << false; + QTest::newRow("tls1.3orlater-tls1.1") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_1 << false; + QTest::newRow("tls1.3orlater-tls1.2") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_2 << false; + QTest::newRow("tls1.3orlater-tls1.3") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_3 << true; + } QTest::newRow("any-tls1.0") << QSsl::AnyProtocol << QSsl::TlsV1_0 << true; QTest::newRow("any-secure") << QSsl::AnyProtocol << QSsl::SecureProtocols << true; |