QAuthenticator: Fix crash when using NTLM / Negotiate

With NTLM/Negotiate we delete the context used to generate replies once
we get SEC_E_OK. Due to some faulty logic in the http backend we could
end up trying to generate another response. Qt would then pass
references to some offsets of nullptr into the API calls causing it to
crash. Add some sanity checks before the "sspi continue" calls to make
sure this won't happen, and update the condition in the http
backend to check that we have not already sent our credentials.

As a drive-by: correct the initialization of the handles to use
SecInvalidateHandle instead of memset to 0.

Pick-to: 6.4 6.3 6.2 5.15
Fixes: QTBUG-102359
Change-Id: I884ff8fc70609fe8746b99a1d56eeafcda9d2620
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Mårten Nordheim 2022-07-28 12:04:11 +02:00
parent 90ad4d10ef
commit c684b8e939
2 changed files with 22 additions and 7 deletions

View File

@ -570,9 +570,15 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket,
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*authenticator);
// Send "Authorization" header, but not if it's NTLM and the socket is already authenticated.
if (priv && priv->method != QAuthenticatorPrivate::None) {
if ((priv->method != QAuthenticatorPrivate::Ntlm
&& request.headerField("Authorization").isEmpty())
|| channel.lastStatus == 401) {
const bool ntlmNego = priv->method == QAuthenticatorPrivate::Ntlm
|| priv->method == QAuthenticatorPrivate::Negotiate;
const bool authNeeded = channel.lastStatus == 401;
const bool ntlmNegoOk = ntlmNego && authNeeded
&& (priv->phase != QAuthenticatorPrivate::Done
|| !channel.authenticationCredentialsSent);
const bool otherOk =
!ntlmNego && (authNeeded || request.headerField("Authorization").isEmpty());
if (ntlmNegoOk || otherOk) {
QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false),
request.url().host());
request.setHeaderField("Authorization", response);
@ -585,7 +591,13 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket,
priv = QAuthenticatorPrivate::getPrivate(*authenticator);
// Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated.
if (priv && priv->method != QAuthenticatorPrivate::None) {
if (priv->method != QAuthenticatorPrivate::Ntlm || channel.lastStatus == 407) {
const bool ntlmNego = priv->method == QAuthenticatorPrivate::Ntlm
|| priv->method == QAuthenticatorPrivate::Negotiate;
const bool proxyAuthNeeded = channel.lastStatus == 407;
const bool ntlmNegoOk = ntlmNego && proxyAuthNeeded
&& (priv->phase != QAuthenticatorPrivate::Done || !channel.proxyCredentialsSent);
const bool otherOk = !ntlmNego;
if (ntlmNegoOk || otherOk) {
QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false),
networkProxy.hostName());
request.setHeaderField("Proxy-Authorization", response);

View File

@ -626,9 +626,11 @@ QByteArray QAuthenticatorPrivate::calculateResponse(QByteArrayView requestMethod
} else {
QByteArray phase3Token;
#if QT_CONFIG(sspi) // SSPI
phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge));
if (sspiWindowsHandles)
phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge));
#elif QT_CONFIG(gssapi) // GSSAPI
phase3Token = qGssapiContinue(this, QByteArray::fromBase64(challenge));
if (gssApiHandles)
phase3Token = qGssapiContinue(this, QByteArray::fromBase64(challenge));
#endif
if (!phase3Token.isEmpty()) {
response = phase3Token.toBase64();
@ -1583,7 +1585,8 @@ static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate
if (!ctx->sspiWindowsHandles)
ctx->sspiWindowsHandles.reset(new QSSPIWindowsHandles);
memset(&ctx->sspiWindowsHandles->credHandle, 0, sizeof(CredHandle));
SecInvalidateHandle(&ctx->sspiWindowsHandles->credHandle);
SecInvalidateHandle(&ctx->sspiWindowsHandles->ctxHandle);
SEC_WINNT_AUTH_IDENTITY auth;
auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;