diff options
-rw-r--r-- | src/network/access/qhttpnetworkconnection.cpp | 9 | ||||
-rw-r--r-- | src/network/kernel/qauthenticator.cpp | 66 | ||||
-rw-r--r-- | src/network/kernel/qauthenticator_p.h | 2 | ||||
-rw-r--r-- | src/network/socket/qhttpsocketengine.cpp | 2 | ||||
-rw-r--r-- | tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp | 6 |
5 files changed, 68 insertions, 17 deletions
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 24be5b1464..c133a09044 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -449,7 +449,14 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket if (auth->isNull()) auth->detach(); QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth); - priv->parseHttpResponse(fields, isProxy); + priv->parseHttpResponse(fields, isProxy, reply->url().host()); + // Update method in case it changed + if (priv->method == QAuthenticatorPrivate::None) + return false; + if (isProxy) + channels[i].proxyAuthMethod = priv->method; + else + channels[i].authMethod = priv->method; if (priv->phase == QAuthenticatorPrivate::Done) { pauseConnection(); diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp index c23e82dd85..3db7161add 100644 --- a/src/network/kernel/qauthenticator.cpp +++ b/src/network/kernel/qauthenticator.cpp @@ -81,6 +81,7 @@ static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method, const QString& host, const QByteArray& challenge = QByteArray()); #elif QT_CONFIG(gssapi) // GSSAPI +static bool qGssapiTestGetCredentials(const QString &host); static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, const QString& host); static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, const QByteArray& challenge = QByteArray()); @@ -422,7 +423,7 @@ void QAuthenticatorPrivate::updateCredentials() } } -void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByteArray> > &values, bool isProxy) +void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByteArray> > &values, bool isProxy, const QString &host) { const char *search = isProxy ? "proxy-authenticate" : "www-authenticate"; @@ -454,6 +455,13 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt headerVal = current.second.mid(7); } else if (method < Negotiate && str.startsWith("negotiate")) { #if QT_CONFIG(sspi) || QT_CONFIG(gssapi) // if it's not supported then we shouldn't try to use it +#if QT_CONFIG(gssapi) + // For GSSAPI there needs to be a KDC set up for the host (afaict). + // So let's only conditionally use it if we can fetch the credentials. + // Sadly it's a bit slow because it requires a DNS lookup. + if (!qGssapiTestGetCredentials(host)) + continue; +#endif method = Negotiate; headerVal = current.second.mid(10); #endif @@ -583,6 +591,7 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet phase = Phase2; } else { phase = Done; + return ""; } } else { QByteArray phase3Token; @@ -595,6 +604,9 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet response = phase3Token.toBase64(); phase = Done; challenge = ""; + } else { + phase = Done; + return ""; } } @@ -1677,26 +1689,36 @@ static void q_GSSAPI_error(const char *message, OM_uint32 majStat, OM_uint32 min q_GSSAPI_error_int(message, minStat, GSS_C_MECH_CODE); } +static gss_name_t qGSsapiGetServiceName(const QString &host) +{ + QByteArray serviceName = "HTTPS@" + host.toLocal8Bit(); + gss_buffer_desc nameDesc = {static_cast<std::size_t>(serviceName.size()), serviceName.data()}; + + gss_name_t importedName; + OM_uint32 minStat; + OM_uint32 majStat = gss_import_name(&minStat, &nameDesc, + GSS_C_NT_HOSTBASED_SERVICE, &importedName); + + if (majStat != GSS_S_COMPLETE) { + q_GSSAPI_error("gss_import_name error", majStat, minStat); + return nullptr; + } + return importedName; +} + // 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); + gss_name_t name = qGSsapiGetServiceName(host); + if (name == nullptr) { ctx->gssApiHandles.reset(nullptr); return QByteArray(); } + ctx->gssApiHandles->targetName = name; // Call qGssapiContinue with GSS_C_NO_CONTEXT to get initial packet ctx->gssApiHandles->gssCtx = GSS_C_NO_CONTEXT; @@ -1750,6 +1772,28 @@ static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, const QByteArray& return result; } +static bool qGssapiTestGetCredentials(const QString &host) +{ + gss_name_t serviceName = qGSsapiGetServiceName(host); + if (!serviceName) + return false; // Something was wrong with the service name, so skip this + OM_uint32 minStat; + gss_cred_id_t cred; + OM_uint32 majStat = gss_acquire_cred(&minStat, serviceName, GSS_C_INDEFINITE, + GSS_C_NO_OID_SET, GSS_C_INITIATE, &cred, nullptr, + nullptr); + + OM_uint32 ignored; + gss_release_name(&ignored, &serviceName); + gss_release_cred(&ignored, &cred); + + if (majStat != GSS_S_COMPLETE) { + q_GSSAPI_error("gss_acquire_cred", majStat, minStat); + return false; + } + return true; +} + // ---------------------------- End of GSSAPI code ---------------------------------------------- #endif // gssapi diff --git a/src/network/kernel/qauthenticator_p.h b/src/network/kernel/qauthenticator_p.h index 99dfdbda2c..1813634ee0 100644 --- a/src/network/kernel/qauthenticator_p.h +++ b/src/network/kernel/qauthenticator_p.h @@ -113,7 +113,7 @@ public: QByteArray digestMd5Response(const QByteArray &challenge, const QByteArray &method, const QByteArray &path); static QHash<QByteArray, QByteArray> parseDigestAuthenticationChallenge(const QByteArray &challenge); - void parseHttpResponse(const QList<QPair<QByteArray, QByteArray> >&, bool isProxy); + void parseHttpResponse(const QList<QPair<QByteArray, QByteArray> >&, bool isProxy, const QString &host); void updateCredentials(); }; diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp index a23e2d3071..5324038dbd 100644 --- a/src/network/socket/qhttpsocketengine.cpp +++ b/src/network/socket/qhttpsocketengine.cpp @@ -607,7 +607,7 @@ void QHttpSocketEngine::slotSocketReadNotification() priv->hasFailed = true; } - priv->parseHttpResponse(d->reply->header(), true); + priv->parseHttpResponse(d->reply->header(), true, d->proxy.hostName()); if (priv->phase == QAuthenticatorPrivate::Invalid) { // problem parsing the reply diff --git a/tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp b/tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp index 94a998a6a6..7e56921606 100644 --- a/tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp +++ b/tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp @@ -83,7 +83,7 @@ void tst_QAuthenticator::basicAuth() QList<QPair<QByteArray, QByteArray> > headers; headers << qMakePair(QByteArray("WWW-Authenticate"), "Basic " + data.toUtf8()); - priv->parseHttpResponse(headers, /*isProxy = */ false); + priv->parseHttpResponse(headers, /*isProxy = */ false, {}); QCOMPARE(auth.realm(), realm); QCOMPARE(auth.option("realm").toString(), realm); @@ -131,7 +131,7 @@ void tst_QAuthenticator::ntlmAuth() // Current implementation uses flags: // NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET headers << qMakePair(QByteArrayLiteral("WWW-Authenticate"), QByteArrayLiteral("NTLM")); - priv->parseHttpResponse(headers, /*isProxy = */ false); + priv->parseHttpResponse(headers, /*isProxy = */ false, {}); if (sso) QVERIFY(priv->calculateResponse("GET", "/", "").startsWith("NTLM ")); else @@ -140,7 +140,7 @@ void tst_QAuthenticator::ntlmAuth() // NTLM phase 2: challenge headers.clear(); headers << qMakePair(QByteArray("WWW-Authenticate"), "NTLM " + data.toUtf8()); - priv->parseHttpResponse(headers, /*isProxy = */ false); + priv->parseHttpResponse(headers, /*isProxy = */ false, {}); QEXPECT_FAIL("with-realm", "NTLM authentication code doesn't extract the realm", Continue); QEXPECT_FAIL("with-realm-sso", "NTLM authentication code doesn't extract the realm", Continue); |