diff options
Diffstat (limited to 'src/network/ssl')
-rw-r--r-- | src/network/ssl/qsslsocket_schannel.cpp | 126 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_schannel_p.h | 3 |
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 |