Schannel: TLS1.3 support
It's not possible to connect to microsoft.com with Schannel TLS 1.3 for some reason (also tested with Internet Explorer), but other sites work fine. Must be something they have to iron out for later. In my experience this needs a preview release of Windows. One of my machines is opted into the dev channel of Windows where they enabled TLS 1.3 by default, and it works well in my tests except for the part above. On my other machine, after enabling TLS 1.3 through the registry, I fail to complete the handshake with any site. So around March/April next year is when this code would activate for most people. MinGW apparently defines NTDDI_VERSION as the one for Windows Server 2003, so it currently doesn't build the new TLS 1.3 code. In Qt (as a project) we could consider setting this higher, but that's out of scope for this patch! Fixes: QTBUG-81294 Change-Id: If329959c3a30ecbfbb8c0d335cc39ccb6d012890 Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
parent
844318f54a
commit
51faa0700d
@ -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)));
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user