summaryrefslogtreecommitdiffstats
path: root/src/network/ssl
diff options
context:
space:
mode:
authorMårten Nordheim <marten.nordheim@qt.io>2020-09-30 14:44:11 +0200
committerMårten Nordheim <marten.nordheim@qt.io>2020-10-14 15:58:16 +0200
commit51faa0700d18b1235da791dff226faffeafa9f84 (patch)
tree52233e43567b4805dae8889f54f8fb6e08d455c8 /src/network/ssl
parent844318f54aa3f8509c16941727cdc755cbea7f05 (diff)
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>
Diffstat (limited to 'src/network/ssl')
-rw-r--r--src/network/ssl/qsslsocket_schannel.cpp126
-rw-r--r--src/network/ssl/qsslsocket_schannel_p.h3
2 files changed, 105 insertions, 24 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