Add support for SPNEGO/Negotiate authentication
This commit adds support for single-sign-on SPNEGO/Negotiate authentication to QAuthenticator, using SSPI on Windows and GSSAPI on other platforms (if KRB5 GSSAPI is available). [ChangeLog][QtNetwork][QAuthenticator] Add support for SPNEGO/Negotiate Task-number: QTBUG-4117 Change-Id: Ie246b887db3fd6201b7ed30b023feca292cd6530 Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
parent
36c2ceca95
commit
93b7b0ec76
@ -398,11 +398,12 @@ void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthentica
|
|||||||
{
|
{
|
||||||
Q_ASSERT(auth);
|
Q_ASSERT(auth);
|
||||||
|
|
||||||
// NTLM is a multi phase authentication. Copying credentials between authenticators would mess things up.
|
// NTLM and Negotiate do multi-phase authentication.
|
||||||
|
// Copying credentialsbetween authenticators would mess things up.
|
||||||
if (fromChannel >= 0) {
|
if (fromChannel >= 0) {
|
||||||
if (!isProxy && channels[fromChannel].authMethod == QAuthenticatorPrivate::Ntlm)
|
const QHttpNetworkConnectionChannel &channel = channels[fromChannel];
|
||||||
return;
|
const QAuthenticatorPrivate::Method method = isProxy ? channel.proxyAuthMethod : channel.authMethod;
|
||||||
if (isProxy && channels[fromChannel].proxyAuthMethod == QAuthenticatorPrivate::Ntlm)
|
if (method == QAuthenticatorPrivate::Ntlm || method == QAuthenticatorPrivate::Negotiate)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -592,7 +593,7 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket,
|
|||||||
if ((channels[i].authMethod != QAuthenticatorPrivate::Ntlm && request.headerField("Authorization").isEmpty()) || channels[i].lastStatus == 401) {
|
if ((channels[i].authMethod != QAuthenticatorPrivate::Ntlm && request.headerField("Authorization").isEmpty()) || channels[i].lastStatus == 401) {
|
||||||
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator);
|
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator);
|
||||||
if (priv && priv->method != QAuthenticatorPrivate::None) {
|
if (priv && priv->method != QAuthenticatorPrivate::None) {
|
||||||
QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false));
|
QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), request.url().host());
|
||||||
request.setHeaderField("Authorization", response);
|
request.setHeaderField("Authorization", response);
|
||||||
channels[i].authenticationCredentialsSent = true;
|
channels[i].authenticationCredentialsSent = true;
|
||||||
}
|
}
|
||||||
@ -604,7 +605,7 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket,
|
|||||||
if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) {
|
if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) {
|
||||||
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator);
|
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator);
|
||||||
if (priv && priv->method != QAuthenticatorPrivate::None) {
|
if (priv && priv->method != QAuthenticatorPrivate::None) {
|
||||||
QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false));
|
QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), networkProxy.hostName());
|
||||||
request.setHeaderField("Proxy-Authorization", response);
|
request.setHeaderField("Proxy-Authorization", response);
|
||||||
channels[i].proxyCredentialsSent = true;
|
channels[i].proxyCredentialsSent = true;
|
||||||
}
|
}
|
||||||
|
@ -444,6 +444,9 @@ QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(boo
|
|||||||
} else if (method < QAuthenticatorPrivate::DigestMd5
|
} else if (method < QAuthenticatorPrivate::DigestMd5
|
||||||
&& line.startsWith("digest")) {
|
&& line.startsWith("digest")) {
|
||||||
method = QAuthenticatorPrivate::DigestMd5;
|
method = QAuthenticatorPrivate::DigestMd5;
|
||||||
|
} else if (method < QAuthenticatorPrivate::Negotiate
|
||||||
|
&& line.startsWith("negotiate")) {
|
||||||
|
method = QAuthenticatorPrivate::Negotiate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return method;
|
return method;
|
||||||
|
@ -199,6 +199,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"use": "openssl"
|
"use": "openssl"
|
||||||
|
},
|
||||||
|
"gssapi": {
|
||||||
|
"label": "KRB5 GSSAPI support",
|
||||||
|
"type": "compile",
|
||||||
|
"test": {
|
||||||
|
"include": [ "gssapi/gssapi.h" ],
|
||||||
|
"main": ["gss_ctx_id_t ctx;"],
|
||||||
|
"qmake": "LIBS += -lgssapi_krb5"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -374,6 +383,20 @@
|
|||||||
"purpose": "Provides API for DNS lookups.",
|
"purpose": "Provides API for DNS lookups.",
|
||||||
"section": "Networking",
|
"section": "Networking",
|
||||||
"output": [ "publicFeature" ]
|
"output": [ "publicFeature" ]
|
||||||
|
},
|
||||||
|
"gssapi": {
|
||||||
|
"label": "GSSAPI",
|
||||||
|
"purpose": "Enable SPNEGO authentication through GSSAPI",
|
||||||
|
"section": "Networking",
|
||||||
|
"condition": "!config.win32 && tests.gssapi",
|
||||||
|
"output": [ "publicFeature", "feature" ]
|
||||||
|
},
|
||||||
|
"sspi": {
|
||||||
|
"label": "SSPI",
|
||||||
|
"purpose": "Enable NTLM/SPNEGO authentication through SSPI",
|
||||||
|
"section": "Networking",
|
||||||
|
"condition": "config.win32 && !config.winrt",
|
||||||
|
"output": [ "publicFeature", "feature" ]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -433,7 +456,8 @@ For example:
|
|||||||
"dtls",
|
"dtls",
|
||||||
"ocsp",
|
"ocsp",
|
||||||
"sctp",
|
"sctp",
|
||||||
"system-proxies"
|
"system-proxies",
|
||||||
|
"gssapi"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -68,6 +68,8 @@ mac {
|
|||||||
!uikit: LIBS_PRIVATE += -framework CoreServices -framework SystemConfiguration
|
!uikit: LIBS_PRIVATE += -framework CoreServices -framework SystemConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qtConfig(gssapi): LIBS_PRIVATE += -lgssapi_krb5
|
||||||
|
|
||||||
uikit:HEADERS += kernel/qnetworkinterface_uikit_p.h
|
uikit:HEADERS += kernel/qnetworkinterface_uikit_p.h
|
||||||
osx:SOURCES += kernel/qnetworkproxy_mac.cpp
|
osx:SOURCES += kernel/qnetworkproxy_mac.cpp
|
||||||
else:win32:!winrt: SOURCES += kernel/qnetworkproxy_win.cpp
|
else:win32:!winrt: SOURCES += kernel/qnetworkproxy_win.cpp
|
||||||
|
@ -54,20 +54,29 @@
|
|||||||
#include <qmutex.h>
|
#include <qmutex.h>
|
||||||
#include <private/qmutexpool_p.h>
|
#include <private/qmutexpool_p.h>
|
||||||
#include <rpc.h>
|
#include <rpc.h>
|
||||||
#ifndef Q_OS_WINRT
|
#endif
|
||||||
|
|
||||||
|
#if QT_CONFIG(sspi) // SSPI
|
||||||
#define SECURITY_WIN32 1
|
#define SECURITY_WIN32 1
|
||||||
#include <security.h>
|
#include <security.h>
|
||||||
#endif
|
#elif QT_CONFIG(gssapi) // GSSAPI
|
||||||
|
#include <gssapi/gssapi.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
static QByteArray qNtlmPhase1();
|
static QByteArray qNtlmPhase1();
|
||||||
static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
|
static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
|
||||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
#if QT_CONFIG(sspi) // SSPI
|
||||||
static QByteArray qNtlmPhase1_SSPI(QAuthenticatorPrivate *ctx);
|
static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
|
||||||
static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
|
const QString& host);
|
||||||
#endif
|
static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
|
||||||
|
const QString& host, const QByteArray& challenge = QByteArray());
|
||||||
|
#elif QT_CONFIG(gssapi) // GSSAPI
|
||||||
|
static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, const QString& host);
|
||||||
|
static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx,
|
||||||
|
const QByteArray& challenge = QByteArray());
|
||||||
|
#endif // gssapi
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\class QAuthenticator
|
\class QAuthenticator
|
||||||
@ -90,6 +99,7 @@ static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray&
|
|||||||
\li Basic
|
\li Basic
|
||||||
\li NTLM version 2
|
\li NTLM version 2
|
||||||
\li Digest-MD5
|
\li Digest-MD5
|
||||||
|
\li SPNEGO/Negotiate
|
||||||
\endlist
|
\endlist
|
||||||
|
|
||||||
\target qauthenticator-options
|
\target qauthenticator-options
|
||||||
@ -133,6 +143,10 @@ static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray&
|
|||||||
|
|
||||||
The Digest-MD5 authentication mechanism supports no outgoing options.
|
The Digest-MD5 authentication mechanism supports no outgoing options.
|
||||||
|
|
||||||
|
\section2 SPNEGO/Negotiate
|
||||||
|
|
||||||
|
This authentication mechanism currently supports no incoming or outgoing options.
|
||||||
|
|
||||||
\sa QSslSocket
|
\sa QSslSocket
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -187,7 +201,7 @@ QAuthenticator &QAuthenticator::operator=(const QAuthenticator &other)
|
|||||||
d->options = other.d->options;
|
d->options = other.d->options;
|
||||||
} else if (d->phase == QAuthenticatorPrivate::Start) {
|
} else if (d->phase == QAuthenticatorPrivate::Start) {
|
||||||
delete d;
|
delete d;
|
||||||
d = 0;
|
d = nullptr;
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -339,21 +353,25 @@ bool QAuthenticator::isNull() const
|
|||||||
return !d;
|
return !d;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
#if QT_CONFIG(sspi) // SSPI
|
||||||
class QNtlmWindowsHandles
|
class QSSPIWindowsHandles
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CredHandle credHandle;
|
CredHandle credHandle;
|
||||||
CtxtHandle ctxHandle;
|
CtxtHandle ctxHandle;
|
||||||
};
|
};
|
||||||
#endif
|
#elif QT_CONFIG(gssapi) // GSSAPI
|
||||||
|
class QGssApiHandles
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
gss_ctx_id_t gssCtx = nullptr;
|
||||||
|
gss_name_t targetName;
|
||||||
|
};
|
||||||
|
#endif // gssapi
|
||||||
|
|
||||||
|
|
||||||
QAuthenticatorPrivate::QAuthenticatorPrivate()
|
QAuthenticatorPrivate::QAuthenticatorPrivate()
|
||||||
: method(None)
|
: method(None)
|
||||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
|
||||||
, ntlmWindowsHandles(0)
|
|
||||||
#endif
|
|
||||||
, hasFailed(false)
|
, hasFailed(false)
|
||||||
, phase(Start)
|
, phase(Start)
|
||||||
, nonceCount(0)
|
, nonceCount(0)
|
||||||
@ -363,13 +381,7 @@ QAuthenticatorPrivate::QAuthenticatorPrivate()
|
|||||||
nonceCount = 0;
|
nonceCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
QAuthenticatorPrivate::~QAuthenticatorPrivate()
|
QAuthenticatorPrivate::~QAuthenticatorPrivate() = default;
|
||||||
{
|
|
||||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
|
||||||
if (ntlmWindowsHandles)
|
|
||||||
delete ntlmWindowsHandles;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void QAuthenticatorPrivate::updateCredentials()
|
void QAuthenticatorPrivate::updateCredentials()
|
||||||
{
|
{
|
||||||
@ -424,6 +436,9 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
|
|||||||
} else if (method < DigestMd5 && str.startsWith("digest")) {
|
} else if (method < DigestMd5 && str.startsWith("digest")) {
|
||||||
method = DigestMd5;
|
method = DigestMd5;
|
||||||
headerVal = current.second.mid(7);
|
headerVal = current.second.mid(7);
|
||||||
|
} else if (method < Negotiate && str.startsWith("negotiate")) {
|
||||||
|
method = Negotiate;
|
||||||
|
headerVal = current.second.mid(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,6 +454,7 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
|
|||||||
phase = Done;
|
phase = Done;
|
||||||
break;
|
break;
|
||||||
case Ntlm:
|
case Ntlm:
|
||||||
|
case Negotiate:
|
||||||
// work is done in calculateResponse()
|
// work is done in calculateResponse()
|
||||||
break;
|
break;
|
||||||
case DigestMd5: {
|
case DigestMd5: {
|
||||||
@ -456,33 +472,36 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path)
|
QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path, const QString& host)
|
||||||
{
|
{
|
||||||
|
#if !QT_CONFIG(sspi) && !QT_CONFIG(gssapi)
|
||||||
|
Q_UNUSED(host);
|
||||||
|
#endif
|
||||||
QByteArray response;
|
QByteArray response;
|
||||||
const char *methodString = 0;
|
const char* methodString = nullptr;
|
||||||
switch(method) {
|
switch(method) {
|
||||||
case QAuthenticatorPrivate::None:
|
case QAuthenticatorPrivate::None:
|
||||||
methodString = "";
|
methodString = "";
|
||||||
phase = Done;
|
phase = Done;
|
||||||
break;
|
break;
|
||||||
case QAuthenticatorPrivate::Basic:
|
case QAuthenticatorPrivate::Basic:
|
||||||
methodString = "Basic ";
|
methodString = "Basic";
|
||||||
response = user.toLatin1() + ':' + password.toLatin1();
|
response = user.toLatin1() + ':' + password.toLatin1();
|
||||||
response = response.toBase64();
|
response = response.toBase64();
|
||||||
phase = Done;
|
phase = Done;
|
||||||
break;
|
break;
|
||||||
case QAuthenticatorPrivate::DigestMd5:
|
case QAuthenticatorPrivate::DigestMd5:
|
||||||
methodString = "Digest ";
|
methodString = "Digest";
|
||||||
response = digestMd5Response(challenge, requestMethod, path);
|
response = digestMd5Response(challenge, requestMethod, path);
|
||||||
phase = Done;
|
phase = Done;
|
||||||
break;
|
break;
|
||||||
case QAuthenticatorPrivate::Ntlm:
|
case QAuthenticatorPrivate::Ntlm:
|
||||||
methodString = "NTLM ";
|
methodString = "NTLM";
|
||||||
if (challenge.isEmpty()) {
|
if (challenge.isEmpty()) {
|
||||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
#if QT_CONFIG(sspi) // SSPI
|
||||||
QByteArray phase1Token;
|
QByteArray phase1Token;
|
||||||
if (user.isEmpty()) // Only pull from system if no user was specified in authenticator
|
if (user.isEmpty()) // Only pull from system if no user was specified in authenticator
|
||||||
phase1Token = qNtlmPhase1_SSPI(this);
|
phase1Token = qSspiStartup(this, method, host);
|
||||||
if (!phase1Token.isEmpty()) {
|
if (!phase1Token.isEmpty()) {
|
||||||
response = phase1Token.toBase64();
|
response = phase1Token.toBase64();
|
||||||
phase = Phase2;
|
phase = Phase2;
|
||||||
@ -496,10 +515,10 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet
|
|||||||
phase = Phase2;
|
phase = Phase2;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
#if QT_CONFIG(sspi) // SSPI
|
||||||
QByteArray phase3Token;
|
QByteArray phase3Token;
|
||||||
if (ntlmWindowsHandles)
|
if (sspiWindowsHandles)
|
||||||
phase3Token = qNtlmPhase3_SSPI(this, QByteArray::fromBase64(challenge));
|
phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge));
|
||||||
if (!phase3Token.isEmpty()) {
|
if (!phase3Token.isEmpty()) {
|
||||||
response = phase3Token.toBase64();
|
response = phase3Token.toBase64();
|
||||||
phase = Done;
|
phase = Done;
|
||||||
@ -511,9 +530,40 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case QAuthenticatorPrivate::Negotiate:
|
||||||
|
methodString = "Negotiate";
|
||||||
|
if (challenge.isEmpty()) {
|
||||||
|
QByteArray phase1Token;
|
||||||
|
#if QT_CONFIG(sspi) // SSPI
|
||||||
|
phase1Token = qSspiStartup(this, method, host);
|
||||||
|
#elif QT_CONFIG(gssapi) // GSSAPI
|
||||||
|
phase1Token = qGssapiStartup(this, host);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!phase1Token.isEmpty()) {
|
||||||
|
response = phase1Token.toBase64();
|
||||||
|
phase = Phase2;
|
||||||
|
} else {
|
||||||
|
phase = Done;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QByteArray phase3Token;
|
||||||
|
#if QT_CONFIG(sspi) // SSPI
|
||||||
|
phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge));
|
||||||
|
#elif QT_CONFIG(gssapi) // GSSAPI
|
||||||
|
phase3Token = qGssapiContinue(this, QByteArray::fromBase64(challenge));
|
||||||
|
#endif
|
||||||
|
if (!phase3Token.isEmpty()) {
|
||||||
|
response = phase3Token.toBase64();
|
||||||
|
phase = Done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return QByteArray(methodString) + response;
|
|
||||||
|
return QByteArray::fromRawData(methodString, qstrlen(methodString)) + ' ' + response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -699,9 +749,10 @@ QByteArray QAuthenticatorPrivate::digestMd5Response(const QByteArray &challenge,
|
|||||||
return credentials;
|
return credentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------- Digest Md5 code ----------------------------------------
|
// ---------------------------- End of Digest Md5 code ---------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------- NTLM code ----------------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NTLM message flags.
|
* NTLM message flags.
|
||||||
@ -1419,156 +1470,237 @@ static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phas
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
// ---------------------------- End of NTLM code ---------------------------------------
|
||||||
|
|
||||||
|
#if QT_CONFIG(sspi) // SSPI
|
||||||
|
// ---------------------------- SSPI code ----------------------------------------------
|
||||||
// See http://davenport.sourceforge.net/ntlm.html
|
// See http://davenport.sourceforge.net/ntlm.html
|
||||||
// and libcurl http_ntlm.c
|
// and libcurl http_ntlm.c
|
||||||
|
|
||||||
// Handle of secur32.dll
|
// Handle of secur32.dll
|
||||||
static HMODULE securityDLLHandle = NULL;
|
static HMODULE securityDLLHandle = nullptr;
|
||||||
// Pointer to SSPI dispatch table
|
// Pointer to SSPI dispatch table
|
||||||
static PSecurityFunctionTable pSecurityFunctionTable = NULL;
|
static PSecurityFunctionTable pSecurityFunctionTable = nullptr;
|
||||||
|
|
||||||
|
static bool q_SSPI_library_load()
|
||||||
static bool q_NTLM_SSPI_library_load()
|
|
||||||
{
|
{
|
||||||
static QBasicMutex mutex;
|
static QBasicMutex mutex;
|
||||||
QMutexLocker l(&mutex);
|
QMutexLocker l(&mutex);
|
||||||
|
|
||||||
// Initialize security interface
|
// Initialize security interface
|
||||||
if (pSecurityFunctionTable == NULL) {
|
if (pSecurityFunctionTable == nullptr) {
|
||||||
securityDLLHandle = LoadLibrary(L"secur32.dll");
|
securityDLLHandle = LoadLibrary(L"secur32.dll");
|
||||||
if (securityDLLHandle != NULL) {
|
if (securityDLLHandle != nullptr) {
|
||||||
INIT_SECURITY_INTERFACE pInitSecurityInterface =
|
INIT_SECURITY_INTERFACE pInitSecurityInterface =
|
||||||
reinterpret_cast<INIT_SECURITY_INTERFACE>(
|
reinterpret_cast<INIT_SECURITY_INTERFACE>(
|
||||||
reinterpret_cast<QFunctionPointer>(GetProcAddress(securityDLLHandle, "InitSecurityInterfaceW")));
|
reinterpret_cast<QFunctionPointer>(GetProcAddress(securityDLLHandle, "InitSecurityInterfaceW")));
|
||||||
if (pInitSecurityInterface != NULL)
|
if (pInitSecurityInterface != nullptr)
|
||||||
pSecurityFunctionTable = pInitSecurityInterface();
|
pSecurityFunctionTable = pInitSecurityInterface();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pSecurityFunctionTable == NULL)
|
if (pSecurityFunctionTable == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 1:
|
static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
|
||||||
static QByteArray qNtlmPhase1_SSPI(QAuthenticatorPrivate *ctx)
|
const QString& host)
|
||||||
{
|
{
|
||||||
QByteArray result;
|
if (!q_SSPI_library_load())
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
if (!q_NTLM_SSPI_library_load())
|
TimeStamp expiry; // For Windows 9x compatibility of SSPI calls
|
||||||
return result;
|
|
||||||
|
|
||||||
// 1. The client obtains a representation of the credential set
|
if (!ctx->sspiWindowsHandles)
|
||||||
// for the user via the SSPI AcquireCredentialsHandle function.
|
ctx->sspiWindowsHandles.reset(new QSSPIWindowsHandles);
|
||||||
if (!ctx->ntlmWindowsHandles)
|
memset(&ctx->sspiWindowsHandles->credHandle, 0, sizeof(CredHandle));
|
||||||
ctx->ntlmWindowsHandles = new QNtlmWindowsHandles;
|
|
||||||
memset(&ctx->ntlmWindowsHandles->credHandle, 0, sizeof(CredHandle));
|
// Acquire our credentials handle
|
||||||
TimeStamp tsDummy;
|
|
||||||
SECURITY_STATUS secStatus = pSecurityFunctionTable->AcquireCredentialsHandle(
|
SECURITY_STATUS secStatus = pSecurityFunctionTable->AcquireCredentialsHandle(
|
||||||
NULL, (SEC_WCHAR*)L"NTLM", SECPKG_CRED_OUTBOUND, NULL, NULL,
|
nullptr,
|
||||||
NULL, NULL, &ctx->ntlmWindowsHandles->credHandle, &tsDummy);
|
(SEC_WCHAR*)(method == QAuthenticatorPrivate::Negotiate ? L"Negotiate" : L"NTLM"),
|
||||||
|
SECPKG_CRED_OUTBOUND, nullptr, nullptr, nullptr, nullptr,
|
||||||
|
&ctx->sspiWindowsHandles->credHandle, &expiry
|
||||||
|
);
|
||||||
if (secStatus != SEC_E_OK) {
|
if (secStatus != SEC_E_OK) {
|
||||||
delete ctx->ntlmWindowsHandles;
|
ctx->sspiWindowsHandles.reset(nullptr);
|
||||||
ctx->ntlmWindowsHandles = 0;
|
return QByteArray();
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. The client calls the SSPI InitializeSecurityContext function
|
return qSspiContinue(ctx, method, host);
|
||||||
// to obtain an authentication request token (in our case, a Type 1 message).
|
|
||||||
// The client sends this token to the server.
|
|
||||||
SecBufferDesc desc;
|
|
||||||
SecBuffer buf;
|
|
||||||
desc.ulVersion = SECBUFFER_VERSION;
|
|
||||||
desc.cBuffers = 1;
|
|
||||||
desc.pBuffers = &buf;
|
|
||||||
buf.cbBuffer = 0;
|
|
||||||
buf.BufferType = SECBUFFER_TOKEN;
|
|
||||||
buf.pvBuffer = NULL;
|
|
||||||
ULONG attrs;
|
|
||||||
|
|
||||||
secStatus = pSecurityFunctionTable->InitializeSecurityContext(&ctx->ntlmWindowsHandles->credHandle, NULL,
|
|
||||||
const_cast<SEC_WCHAR*>(L"") /* host */,
|
|
||||||
ISC_REQ_ALLOCATE_MEMORY,
|
|
||||||
0, SECURITY_NETWORK_DREP,
|
|
||||||
NULL, 0,
|
|
||||||
&ctx->ntlmWindowsHandles->ctxHandle, &desc,
|
|
||||||
&attrs, &tsDummy);
|
|
||||||
if (secStatus == SEC_I_COMPLETE_AND_CONTINUE ||
|
|
||||||
secStatus == SEC_I_CONTINUE_NEEDED) {
|
|
||||||
pSecurityFunctionTable->CompleteAuthToken(&ctx->ntlmWindowsHandles->ctxHandle, &desc);
|
|
||||||
} else if (secStatus != SEC_E_OK) {
|
|
||||||
if ((const char*)buf.pvBuffer)
|
|
||||||
pSecurityFunctionTable->FreeContextBuffer(buf.pvBuffer);
|
|
||||||
pSecurityFunctionTable->FreeCredentialsHandle(&ctx->ntlmWindowsHandles->credHandle);
|
|
||||||
delete ctx->ntlmWindowsHandles;
|
|
||||||
ctx->ntlmWindowsHandles = 0;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = QByteArray((const char*)buf.pvBuffer, buf.cbBuffer);
|
|
||||||
pSecurityFunctionTable->FreeContextBuffer(buf.pvBuffer);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 2:
|
static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
|
||||||
// 3. The server receives the token from the client, and uses it as input to the
|
const QString &host, const QByteArray &challenge)
|
||||||
// AcceptSecurityContext SSPI function. This creates a local security context on
|
|
||||||
// the server to represent the client, and yields an authentication response token
|
|
||||||
// (the Type 2 message), which is sent to the client.
|
|
||||||
|
|
||||||
// Phase 3:
|
|
||||||
static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray& phase2data)
|
|
||||||
{
|
{
|
||||||
// 4. The client receives the response token from the server and calls
|
|
||||||
// InitializeSecurityContext again, passing the server's token as input.
|
|
||||||
// This provides us with another authentication request token (the Type 3 message).
|
|
||||||
// The return value indicates that the security context was successfully initialized;
|
|
||||||
// the token is sent to the server.
|
|
||||||
|
|
||||||
QByteArray result;
|
QByteArray result;
|
||||||
|
SecBuffer challengeBuf;
|
||||||
|
SecBuffer responseBuf;
|
||||||
|
SecBufferDesc challengeDesc;
|
||||||
|
SecBufferDesc responseDesc;
|
||||||
|
unsigned long attrs;
|
||||||
|
TimeStamp expiry; // For Windows 9x compatibility of SSPI calls
|
||||||
|
|
||||||
if (pSecurityFunctionTable == NULL)
|
if (!challenge.isEmpty())
|
||||||
return result;
|
{
|
||||||
|
// Setup the challenge "input" security buffer
|
||||||
SecBuffer type_2, type_3;
|
challengeDesc.ulVersion = SECBUFFER_VERSION;
|
||||||
SecBufferDesc type_2_desc, type_3_desc;
|
challengeDesc.cBuffers = 1;
|
||||||
ULONG attrs;
|
challengeDesc.pBuffers = &challengeBuf;
|
||||||
TimeStamp tsDummy; // For Windows 9x compatibility of SPPI calls
|
challengeBuf.BufferType = SECBUFFER_TOKEN;
|
||||||
|
challengeBuf.pvBuffer = (PVOID)(challenge.data());
|
||||||
type_2_desc.ulVersion = type_3_desc.ulVersion = SECBUFFER_VERSION;
|
challengeBuf.cbBuffer = challenge.length();
|
||||||
type_2_desc.cBuffers = type_3_desc.cBuffers = 1;
|
|
||||||
type_2_desc.pBuffers = &type_2;
|
|
||||||
type_3_desc.pBuffers = &type_3;
|
|
||||||
|
|
||||||
type_2.BufferType = SECBUFFER_TOKEN;
|
|
||||||
type_2.pvBuffer = (PVOID)phase2data.data();
|
|
||||||
type_2.cbBuffer = phase2data.length();
|
|
||||||
type_3.BufferType = SECBUFFER_TOKEN;
|
|
||||||
type_3.pvBuffer = 0;
|
|
||||||
type_3.cbBuffer = 0;
|
|
||||||
|
|
||||||
SECURITY_STATUS secStatus = pSecurityFunctionTable->InitializeSecurityContext(&ctx->ntlmWindowsHandles->credHandle,
|
|
||||||
&ctx->ntlmWindowsHandles->ctxHandle,
|
|
||||||
const_cast<SEC_WCHAR*>(L"") /* host */,
|
|
||||||
ISC_REQ_ALLOCATE_MEMORY,
|
|
||||||
0, SECURITY_NETWORK_DREP, &type_2_desc,
|
|
||||||
0, &ctx->ntlmWindowsHandles->ctxHandle, &type_3_desc,
|
|
||||||
&attrs, &tsDummy);
|
|
||||||
|
|
||||||
if (secStatus == SEC_E_OK && ((const char*)type_3.pvBuffer)) {
|
|
||||||
result = QByteArray((const char*)type_3.pvBuffer, type_3.cbBuffer);
|
|
||||||
pSecurityFunctionTable->FreeContextBuffer(type_3.pvBuffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pSecurityFunctionTable->FreeCredentialsHandle(&ctx->ntlmWindowsHandles->credHandle);
|
// Setup the response "output" security buffer
|
||||||
pSecurityFunctionTable->DeleteSecurityContext(&ctx->ntlmWindowsHandles->ctxHandle);
|
responseDesc.ulVersion = SECBUFFER_VERSION;
|
||||||
delete ctx->ntlmWindowsHandles;
|
responseDesc.cBuffers = 1;
|
||||||
ctx->ntlmWindowsHandles = 0;
|
responseDesc.pBuffers = &responseBuf;
|
||||||
|
responseBuf.BufferType = SECBUFFER_TOKEN;
|
||||||
|
responseBuf.pvBuffer = nullptr;
|
||||||
|
responseBuf.cbBuffer = 0;
|
||||||
|
|
||||||
|
// Calculate target (SPN for Negotiate, empty for NTLM)
|
||||||
|
std::wstring targetNameW = (method == QAuthenticatorPrivate::Negotiate
|
||||||
|
? QLatin1String("HTTP/") + host : QString()).toStdWString();
|
||||||
|
|
||||||
|
// Generate our challenge-response message
|
||||||
|
SECURITY_STATUS secStatus = pSecurityFunctionTable->InitializeSecurityContext(
|
||||||
|
&ctx->sspiWindowsHandles->credHandle,
|
||||||
|
!challenge.isEmpty() ? &ctx->sspiWindowsHandles->ctxHandle : nullptr,
|
||||||
|
const_cast<wchar_t*>(targetNameW.data()),
|
||||||
|
ISC_REQ_ALLOCATE_MEMORY,
|
||||||
|
0, SECURITY_NATIVE_DREP,
|
||||||
|
!challenge.isEmpty() ? &challengeDesc : nullptr,
|
||||||
|
0, &ctx->sspiWindowsHandles->ctxHandle,
|
||||||
|
&responseDesc, &attrs,
|
||||||
|
&expiry
|
||||||
|
);
|
||||||
|
|
||||||
|
if (secStatus == SEC_I_COMPLETE_NEEDED || secStatus == SEC_I_COMPLETE_AND_CONTINUE) {
|
||||||
|
secStatus = pSecurityFunctionTable->CompleteAuthToken(&ctx->sspiWindowsHandles->ctxHandle,
|
||||||
|
&responseDesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secStatus != SEC_I_COMPLETE_AND_CONTINUE && secStatus != SEC_I_CONTINUE_NEEDED) {
|
||||||
|
pSecurityFunctionTable->FreeCredentialsHandle(&ctx->sspiWindowsHandles->credHandle);
|
||||||
|
pSecurityFunctionTable->DeleteSecurityContext(&ctx->sspiWindowsHandles->ctxHandle);
|
||||||
|
ctx->sspiWindowsHandles.reset(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = QByteArray((const char*)responseBuf.pvBuffer, responseBuf.cbBuffer);
|
||||||
|
pSecurityFunctionTable->FreeContextBuffer(responseBuf.pvBuffer);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#endif // Q_OS_WIN && !Q_OS_WINRT
|
|
||||||
|
// ---------------------------- End of SSPI code ---------------------------------------
|
||||||
|
|
||||||
|
#elif QT_CONFIG(gssapi) // GSSAPI
|
||||||
|
|
||||||
|
// ---------------------------- GSSAPI code ----------------------------------------------
|
||||||
|
// See postgres src/interfaces/libpq/fe-auth.c
|
||||||
|
|
||||||
|
// Fetch all errors of a specific type
|
||||||
|
static void q_GSSAPI_error_int(const char *message, OM_uint32 stat, int type)
|
||||||
|
{
|
||||||
|
OM_uint32 minStat, msgCtx = 0;
|
||||||
|
gss_buffer_desc msg;
|
||||||
|
|
||||||
|
do {
|
||||||
|
gss_display_status(&minStat, stat, type, GSS_C_NO_OID, &msgCtx, &msg);
|
||||||
|
qDebug() << message << ": " << reinterpret_cast<const char*>(msg.value);
|
||||||
|
gss_release_buffer(&minStat, &msg);
|
||||||
|
} while (msgCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GSSAPI errors contain two parts; extract both
|
||||||
|
static void q_GSSAPI_error(const char *message, OM_uint32 majStat, OM_uint32 minStat)
|
||||||
|
{
|
||||||
|
// Fetch major error codes
|
||||||
|
q_GSSAPI_error_int(message, majStat, GSS_C_GSS_CODE);
|
||||||
|
|
||||||
|
// Add the minor codes as well
|
||||||
|
q_GSSAPI_error_int(message, minStat, GSS_C_MECH_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send initial GSS authentication token
|
||||||
|
static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, const QString &host)
|
||||||
|
{
|
||||||
|
OM_uint32 majStat, minStat;
|
||||||
|
|
||||||
|
if (!ctx->gssApiHandles)
|
||||||
|
ctx->gssApiHandles.reset(new QGssApiHandles);
|
||||||
|
|
||||||
|
// Convert target name to internal form
|
||||||
|
QByteArray serviceName = QStringLiteral("HTTPS@%1").arg(host).toLocal8Bit();
|
||||||
|
gss_buffer_desc nameDesc = {static_cast<std::size_t>(serviceName.size()), serviceName.data()};
|
||||||
|
|
||||||
|
majStat = gss_import_name(&minStat, &nameDesc,
|
||||||
|
GSS_C_NT_HOSTBASED_SERVICE, &ctx->gssApiHandles->targetName);
|
||||||
|
|
||||||
|
if (majStat != GSS_S_COMPLETE) {
|
||||||
|
q_GSSAPI_error("gss_import_name error", majStat, minStat);
|
||||||
|
ctx->gssApiHandles.reset(nullptr);
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call qGssapiContinue with GSS_C_NO_CONTEXT to get initial packet
|
||||||
|
ctx->gssApiHandles->gssCtx = GSS_C_NO_CONTEXT;
|
||||||
|
return qGssapiContinue(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue GSS authentication with next token as needed
|
||||||
|
static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, const QByteArray& challenge)
|
||||||
|
{
|
||||||
|
OM_uint32 majStat, minStat, ignored;
|
||||||
|
QByteArray result;
|
||||||
|
gss_buffer_desc inBuf = {0, nullptr}; // GSS input token
|
||||||
|
gss_buffer_desc outBuf; // GSS output token
|
||||||
|
|
||||||
|
if (!challenge.isEmpty()) {
|
||||||
|
inBuf.value = const_cast<char*>(challenge.data());
|
||||||
|
inBuf.length = challenge.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
majStat = gss_init_sec_context(&minStat,
|
||||||
|
GSS_C_NO_CREDENTIAL,
|
||||||
|
&ctx->gssApiHandles->gssCtx,
|
||||||
|
ctx->gssApiHandles->targetName,
|
||||||
|
GSS_C_NO_OID,
|
||||||
|
GSS_C_MUTUAL_FLAG,
|
||||||
|
0,
|
||||||
|
GSS_C_NO_CHANNEL_BINDINGS,
|
||||||
|
challenge.isEmpty() ? GSS_C_NO_BUFFER : &inBuf,
|
||||||
|
nullptr,
|
||||||
|
&outBuf,
|
||||||
|
nullptr,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
if (outBuf.length != 0)
|
||||||
|
result = QByteArray(reinterpret_cast<const char*>(outBuf.value), outBuf.length);
|
||||||
|
gss_release_buffer(&ignored, &outBuf);
|
||||||
|
|
||||||
|
if (majStat != GSS_S_COMPLETE && majStat != GSS_S_CONTINUE_NEEDED) {
|
||||||
|
q_GSSAPI_error("gss_init_sec_context error", majStat, minStat);
|
||||||
|
gss_release_name(&ignored, &ctx->gssApiHandles->targetName);
|
||||||
|
if (ctx->gssApiHandles->gssCtx)
|
||||||
|
gss_delete_sec_context(&ignored, &ctx->gssApiHandles->gssCtx, GSS_C_NO_BUFFER);
|
||||||
|
ctx->gssApiHandles.reset(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (majStat == GSS_S_COMPLETE) {
|
||||||
|
gss_release_name(&ignored, &ctx->gssApiHandles->targetName);
|
||||||
|
ctx->gssApiHandles.reset(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------- End of GSSAPI code ----------------------------------------------
|
||||||
|
|
||||||
|
#endif // gssapi
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
#include <QtNetwork/private/qtnetworkglobal_p.h>
|
#include <QtNetwork/private/qtnetworkglobal_p.h>
|
||||||
#include <qhash.h>
|
#include <qhash.h>
|
||||||
#include <qbytearray.h>
|
#include <qbytearray.h>
|
||||||
|
#include <qscopedpointer.h>
|
||||||
#include <qstring.h>
|
#include <qstring.h>
|
||||||
#include <qauthenticator.h>
|
#include <qauthenticator.h>
|
||||||
#include <qvariant.h>
|
#include <qvariant.h>
|
||||||
@ -61,14 +62,16 @@
|
|||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
class QHttpResponseHeader;
|
class QHttpResponseHeader;
|
||||||
#ifdef Q_OS_WIN
|
#if QT_CONFIG(sspi) // SSPI
|
||||||
class QNtlmWindowsHandles;
|
class QSSPIWindowsHandles;
|
||||||
|
#elif QT_CONFIG(gssapi) // GSSAPI
|
||||||
|
class QGssApiHandles;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class Q_AUTOTEST_EXPORT QAuthenticatorPrivate
|
class Q_AUTOTEST_EXPORT QAuthenticatorPrivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum Method { None, Basic, Ntlm, DigestMd5 };
|
enum Method { None, Basic, Ntlm, DigestMd5, Negotiate };
|
||||||
QAuthenticatorPrivate();
|
QAuthenticatorPrivate();
|
||||||
~QAuthenticatorPrivate();
|
~QAuthenticatorPrivate();
|
||||||
|
|
||||||
@ -79,8 +82,10 @@ public:
|
|||||||
Method method;
|
Method method;
|
||||||
QString realm;
|
QString realm;
|
||||||
QByteArray challenge;
|
QByteArray challenge;
|
||||||
#ifdef Q_OS_WIN
|
#if QT_CONFIG(sspi) // SSPI
|
||||||
QNtlmWindowsHandles *ntlmWindowsHandles;
|
QScopedPointer<QSSPIWindowsHandles> sspiWindowsHandles;
|
||||||
|
#elif QT_CONFIG(gssapi) // GSSAPI
|
||||||
|
QScopedPointer<QGssApiHandles> gssApiHandles;
|
||||||
#endif
|
#endif
|
||||||
bool hasFailed; //credentials have been tried but rejected by server.
|
bool hasFailed; //credentials have been tried but rejected by server.
|
||||||
|
|
||||||
@ -100,7 +105,7 @@ public:
|
|||||||
QString workstation;
|
QString workstation;
|
||||||
QString userDomain;
|
QString userDomain;
|
||||||
|
|
||||||
QByteArray calculateResponse(const QByteArray &method, const QByteArray &path);
|
QByteArray calculateResponse(const QByteArray &method, const QByteArray &path, const QString& host);
|
||||||
|
|
||||||
inline static QAuthenticatorPrivate *getPrivate(QAuthenticator &auth) { return auth.d; }
|
inline static QAuthenticatorPrivate *getPrivate(QAuthenticator &auth) { return auth.d; }
|
||||||
inline static const QAuthenticatorPrivate *getPrivate(const QAuthenticator &auth) { return auth.d; }
|
inline static const QAuthenticatorPrivate *getPrivate(const QAuthenticator &auth) { return auth.d; }
|
||||||
|
@ -524,7 +524,7 @@ void QHttpSocketEngine::slotSocketConnected()
|
|||||||
//qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1);
|
//qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1);
|
||||||
if (priv && priv->method != QAuthenticatorPrivate::None) {
|
if (priv && priv->method != QAuthenticatorPrivate::None) {
|
||||||
d->credentialsSent = true;
|
d->credentialsSent = true;
|
||||||
data += "Proxy-Authorization: " + priv->calculateResponse(method, path);
|
data += "Proxy-Authorization: " + priv->calculateResponse(method, path, d->proxy.hostName());
|
||||||
data += "\r\n";
|
data += "\r\n";
|
||||||
}
|
}
|
||||||
data += "\r\n";
|
data += "\r\n";
|
||||||
|
@ -93,7 +93,7 @@ void tst_QAuthenticator::basicAuth()
|
|||||||
|
|
||||||
QCOMPARE(priv->phase, QAuthenticatorPrivate::Start);
|
QCOMPARE(priv->phase, QAuthenticatorPrivate::Start);
|
||||||
|
|
||||||
QCOMPARE(priv->calculateResponse("GET", "/").constData(), QByteArray("Basic " + expectedReply).constData());
|
QCOMPARE(priv->calculateResponse("GET", "/", "").constData(), QByteArray("Basic " + expectedReply).constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QAuthenticator::ntlmAuth_data()
|
void tst_QAuthenticator::ntlmAuth_data()
|
||||||
@ -133,9 +133,9 @@ void tst_QAuthenticator::ntlmAuth()
|
|||||||
headers << qMakePair<QByteArray, QByteArray>("WWW-Authenticate", "NTLM");
|
headers << qMakePair<QByteArray, QByteArray>("WWW-Authenticate", "NTLM");
|
||||||
priv->parseHttpResponse(headers, /*isProxy = */ false);
|
priv->parseHttpResponse(headers, /*isProxy = */ false);
|
||||||
if (sso)
|
if (sso)
|
||||||
QVERIFY(priv->calculateResponse("GET", "/").startsWith("NTLM "));
|
QVERIFY(priv->calculateResponse("GET", "/", "").startsWith("NTLM "));
|
||||||
else
|
else
|
||||||
QCOMPARE(priv->calculateResponse("GET", "/").constData(), "NTLM TlRMTVNTUAABAAAABYIIAAAAAAAAAAAAAAAAAAAAAAA=");
|
QCOMPARE(priv->calculateResponse("GET", "/", "").constData(), "NTLM TlRMTVNTUAABAAAABYIIAAAAAAAAAAAAAAAAAAAAAAA=");
|
||||||
|
|
||||||
// NTLM phase 2: challenge
|
// NTLM phase 2: challenge
|
||||||
headers.clear();
|
headers.clear();
|
||||||
@ -146,7 +146,7 @@ void tst_QAuthenticator::ntlmAuth()
|
|||||||
QEXPECT_FAIL("with-realm-sso", "NTLM authentication code doesn't extract the realm", Continue);
|
QEXPECT_FAIL("with-realm-sso", "NTLM authentication code doesn't extract the realm", Continue);
|
||||||
QCOMPARE(auth.realm(), realm);
|
QCOMPARE(auth.realm(), realm);
|
||||||
|
|
||||||
QVERIFY(priv->calculateResponse("GET", "/").startsWith("NTLM "));
|
QVERIFY(priv->calculateResponse("GET", "/", "").startsWith("NTLM "));
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QAuthenticator::equalityOperators()
|
void tst_QAuthenticator::equalityOperators()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user