Blob Blame History Raw
diff -rupN --no-dereference qtbase-everywhere-src-5.12.5/src/network/access/qhttpnetworkconnection.cpp qtbase-everywhere-src-5.12.5-new/src/network/access/qhttpnetworkconnection.cpp
--- qtbase-everywhere-src-5.12.5/src/network/access/qhttpnetworkconnection.cpp	2019-09-03 20:52:35.000000000 +0200
+++ qtbase-everywhere-src-5.12.5-new/src/network/access/qhttpnetworkconnection.cpp	2019-09-25 09:47:35.046550343 +0200
@@ -398,11 +398,12 @@ void QHttpNetworkConnectionPrivate::copy
 {
     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 (!isProxy && channels[fromChannel].authMethod == QAuthenticatorPrivate::Ntlm)
-            return;
-        if (isProxy && channels[fromChannel].proxyAuthMethod == QAuthenticatorPrivate::Ntlm)
+        const QHttpNetworkConnectionChannel &channel = channels[fromChannel];
+        const QAuthenticatorPrivate::Method method = isProxy ? channel.proxyAuthMethod : channel.authMethod;
+        if (method == QAuthenticatorPrivate::Ntlm || method == QAuthenticatorPrivate::Negotiate)
             return;
     }
 
@@ -592,7 +593,7 @@ void QHttpNetworkConnectionPrivate::crea
         if ((channels[i].authMethod != QAuthenticatorPrivate::Ntlm && request.headerField("Authorization").isEmpty()) || channels[i].lastStatus == 401) {
             QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator);
             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);
                 channels[i].authenticationCredentialsSent = true;
             }
@@ -604,7 +605,7 @@ void QHttpNetworkConnectionPrivate::crea
         if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) {
             QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator);
             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);
                 channels[i].proxyCredentialsSent = true;
             }
diff -rupN --no-dereference qtbase-everywhere-src-5.12.5/src/network/access/qhttpnetworkreply.cpp qtbase-everywhere-src-5.12.5-new/src/network/access/qhttpnetworkreply.cpp
--- qtbase-everywhere-src-5.12.5/src/network/access/qhttpnetworkreply.cpp	2019-09-03 20:52:35.000000000 +0200
+++ qtbase-everywhere-src-5.12.5-new/src/network/access/qhttpnetworkreply.cpp	2019-09-25 09:47:35.046550343 +0200
@@ -444,6 +444,9 @@ QAuthenticatorPrivate::Method QHttpNetwo
         } else if (method < QAuthenticatorPrivate::DigestMd5
             && line.startsWith("digest")) {
             method = QAuthenticatorPrivate::DigestMd5;
+        } else if (method < QAuthenticatorPrivate::Negotiate
+            && line.startsWith("negotiate")) {
+            method = QAuthenticatorPrivate::Negotiate;
         }
     }
     return method;
diff -rupN --no-dereference qtbase-everywhere-src-5.12.5/src/network/configure.json qtbase-everywhere-src-5.12.5-new/src/network/configure.json
--- qtbase-everywhere-src-5.12.5/src/network/configure.json	2019-09-03 20:52:35.000000000 +0200
+++ qtbase-everywhere-src-5.12.5-new/src/network/configure.json	2019-09-25 09:47:35.047550340 +0200
@@ -184,6 +184,15 @@
                 ]
             },
             "use": "openssl"
+        },
+        "gssapi": {
+            "label": "KRB5 GSSAPI support",
+            "type": "compile",
+            "test": {
+                "include": [ "gssapi/gssapi.h" ],
+                "main": ["gss_ctx_id_t ctx;"],
+                "qmake": "LIBS += -lgssapi_krb5"
+            }
         }
     },
 
@@ -343,6 +352,20 @@
             "purpose": "Provides API for DNS lookups.",
             "section": "Networking",
             "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" ]
         }
     },
 
@@ -396,7 +419,8 @@ For example:
                 "opensslv11",
                 "dtls",
                 "sctp",
-                "system-proxies"
+                "system-proxies",
+                "gssapi"
             ]
         }
     ]
diff -rupN --no-dereference qtbase-everywhere-src-5.12.5/src/network/kernel/kernel.pri qtbase-everywhere-src-5.12.5-new/src/network/kernel/kernel.pri
--- qtbase-everywhere-src-5.12.5/src/network/kernel/kernel.pri	2019-09-03 20:52:35.000000000 +0200
+++ qtbase-everywhere-src-5.12.5-new/src/network/kernel/kernel.pri	2019-09-25 09:47:35.047550340 +0200
@@ -71,6 +71,8 @@ mac {
     !uikit: LIBS_PRIVATE += -framework CoreServices -framework SystemConfiguration
 }
 
+qtConfig(gssapi): LIBS_PRIVATE += -lgssapi_krb5
+
 uikit:HEADERS += kernel/qnetworkinterface_uikit_p.h
 osx:SOURCES += kernel/qnetworkproxy_mac.cpp
 else:win32:!winrt: SOURCES += kernel/qnetworkproxy_win.cpp
diff -rupN --no-dereference qtbase-everywhere-src-5.12.5/src/network/kernel/qauthenticator.cpp qtbase-everywhere-src-5.12.5-new/src/network/kernel/qauthenticator.cpp
--- qtbase-everywhere-src-5.12.5/src/network/kernel/qauthenticator.cpp	2019-09-03 20:52:35.000000000 +0200
+++ qtbase-everywhere-src-5.12.5-new/src/network/kernel/qauthenticator.cpp	2019-09-25 09:47:35.047550340 +0200
@@ -54,20 +54,29 @@
 #include <qmutex.h>
 #include <private/qmutexpool_p.h>
 #include <rpc.h>
-#ifndef Q_OS_WINRT
+#endif
+
+#if QT_CONFIG(sspi) // SSPI
 #define SECURITY_WIN32 1
 #include <security.h>
-#endif
+#elif QT_CONFIG(gssapi) // GSSAPI
+#include <gssapi/gssapi.h>
 #endif
 
 QT_BEGIN_NAMESPACE
 
 static QByteArray qNtlmPhase1();
 static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
-static QByteArray qNtlmPhase1_SSPI(QAuthenticatorPrivate *ctx);
-static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
-#endif
+#if QT_CONFIG(sspi) // SSPI
+static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
+                               const QString& host);
+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
@@ -90,6 +99,7 @@ static QByteArray qNtlmPhase3_SSPI(QAuth
     \li Basic
     \li NTLM version 2
     \li Digest-MD5
+    \li SPNEGO/Negotiate
   \endlist
 
   \target qauthenticator-options
@@ -133,6 +143,10 @@ static QByteArray qNtlmPhase3_SSPI(QAuth
 
   The Digest-MD5 authentication mechanism supports no outgoing options.
 
+  \section2 SPNEGO/Negotiate
+
+  This authentication mechanism currently supports no incoming or outgoing options.
+
   \sa QSslSocket
 */
 
@@ -187,7 +201,7 @@ QAuthenticator &QAuthenticator::operator
         d->options = other.d->options;
     } else if (d->phase == QAuthenticatorPrivate::Start) {
         delete d;
-        d = 0;
+        d = nullptr;
     }
     return *this;
 }
@@ -339,21 +353,25 @@ bool QAuthenticator::isNull() const
     return !d;
 }
 
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
-class QNtlmWindowsHandles
+#if QT_CONFIG(sspi) // SSPI
+class QSSPIWindowsHandles
 {
 public:
     CredHandle credHandle;
     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()
     : method(None)
-    #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
-    , ntlmWindowsHandles(0)
-    #endif
     , hasFailed(false)
     , phase(Start)
     , nonceCount(0)
@@ -363,14 +381,6 @@ QAuthenticatorPrivate::QAuthenticatorPri
     nonceCount = 0;
 }
 
-QAuthenticatorPrivate::~QAuthenticatorPrivate()
-{
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
-    if (ntlmWindowsHandles)
-        delete ntlmWindowsHandles;
-#endif
-}
-
 void QAuthenticatorPrivate::updateCredentials()
 {
     int separatorPosn = 0;
@@ -424,6 +434,9 @@ void QAuthenticatorPrivate::parseHttpRes
         } else if (method < DigestMd5 && str.startsWith("digest")) {
             method = DigestMd5;
             headerVal = current.second.mid(7);
+        } else if (method < Negotiate && str.startsWith("negotiate")) {
+            method = Negotiate;
+            headerVal = current.second.mid(10);
         }
     }
 
@@ -439,6 +452,7 @@ void QAuthenticatorPrivate::parseHttpRes
             phase = Done;
         break;
     case Ntlm:
+    case Negotiate:
         // work is done in calculateResponse()
         break;
     case DigestMd5: {
@@ -456,10 +470,10 @@ void QAuthenticatorPrivate::parseHttpRes
     }
 }
 
-QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path)
+QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path, const QString& host)
 {
     QByteArray response;
-    const char *methodString = 0;
+    const char* methodString = nullptr;
     switch(method) {
     case QAuthenticatorPrivate::None:
         methodString = "";
@@ -470,7 +484,7 @@ QByteArray QAuthenticatorPrivate::calcul
         phase = Done;
         break;
     case QAuthenticatorPrivate::Basic:
-        methodString = "Basic ";
+        methodString = "Basic";
         response = user.toLatin1() + ':' + password.toLatin1();
         response = response.toBase64();
         phase = Done;
@@ -487,17 +501,17 @@ QByteArray QAuthenticatorPrivate::calcul
     case QAuthenticatorPrivate::CramMd5:
         break;
     case QAuthenticatorPrivate::DigestMd5:
-        methodString = "Digest ";
+        methodString = "Digest";
         response = digestMd5Response(challenge, requestMethod, path);
         phase = Done;
         break;
     case QAuthenticatorPrivate::Ntlm:
-        methodString = "NTLM ";
+        methodString = "NTLM";
         if (challenge.isEmpty()) {
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
+#if QT_CONFIG(sspi) // SSPI
             QByteArray phase1Token;
             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()) {
                 response = phase1Token.toBase64();
                 phase = Phase2;
@@ -511,10 +525,10 @@ QByteArray QAuthenticatorPrivate::calcul
                     phase = Phase2;
             }
         } else {
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
+#if QT_CONFIG(sspi) // SSPI
             QByteArray phase3Token;
-            if (ntlmWindowsHandles)
-                phase3Token = qNtlmPhase3_SSPI(this, QByteArray::fromBase64(challenge));
+            if (sspiWindowsHandles)
+                phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge));
             if (!phase3Token.isEmpty()) {
                 response = phase3Token.toBase64();
                 phase = Done;
@@ -527,8 +541,39 @@ QByteArray QAuthenticatorPrivate::calcul
         }
 
         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;
     }
-    return QByteArray(methodString) + response;
+
+    return QByteArray::fromRawData(methodString, qstrlen(methodString)) + ' ' + response;
 }
 
 
@@ -714,9 +759,10 @@ QByteArray QAuthenticatorPrivate::digest
     return credentials;
 }
 
-// ---------------------------- Digest Md5 code ----------------------------------------
+// ---------------------------- End of Digest Md5 code ---------------------------------
 
 
+// ---------------------------- NTLM code ----------------------------------------------
 
 /*
  * NTLM message flags.
@@ -1434,156 +1480,237 @@ static QByteArray qNtlmPhase3(QAuthentic
     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
 // and libcurl http_ntlm.c
 
 // Handle of secur32.dll
-static HMODULE securityDLLHandle = NULL;
+static HMODULE securityDLLHandle = nullptr;
 // Pointer to SSPI dispatch table
-static PSecurityFunctionTable pSecurityFunctionTable = NULL;
+static PSecurityFunctionTable pSecurityFunctionTable = nullptr;
 
-
-static bool q_NTLM_SSPI_library_load()
+static bool q_SSPI_library_load()
 {
     static QBasicMutex mutex;
     QMutexLocker l(&mutex);
 
     // Initialize security interface
-    if (pSecurityFunctionTable == NULL) {
+    if (pSecurityFunctionTable == nullptr) {
         securityDLLHandle = LoadLibrary(L"secur32.dll");
-        if (securityDLLHandle != NULL) {
+        if (securityDLLHandle != nullptr) {
             INIT_SECURITY_INTERFACE pInitSecurityInterface =
                 reinterpret_cast<INIT_SECURITY_INTERFACE>(
                     reinterpret_cast<QFunctionPointer>(GetProcAddress(securityDLLHandle, "InitSecurityInterfaceW")));
-            if (pInitSecurityInterface != NULL)
+            if (pInitSecurityInterface != nullptr)
                 pSecurityFunctionTable = pInitSecurityInterface();
         }
     }
 
-    if (pSecurityFunctionTable == NULL)
+    if (pSecurityFunctionTable == nullptr)
         return false;
 
     return true;
 }
 
-// Phase 1:
-static QByteArray qNtlmPhase1_SSPI(QAuthenticatorPrivate *ctx)
+static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
+                               const QString& host)
 {
-    QByteArray result;
+    if (!q_SSPI_library_load())
+        return QByteArray();
+
+    TimeStamp expiry; // For Windows 9x compatibility of SSPI calls
 
-    if (!q_NTLM_SSPI_library_load())
-        return result;
+    if (!ctx->sspiWindowsHandles)
+        ctx->sspiWindowsHandles.reset(new QSSPIWindowsHandles);
+    memset(&ctx->sspiWindowsHandles->credHandle, 0, sizeof(CredHandle));
 
-    // 1. The client obtains a representation of the credential set
-    // for the user via the SSPI AcquireCredentialsHandle function.
-    if (!ctx->ntlmWindowsHandles)
-        ctx->ntlmWindowsHandles = new QNtlmWindowsHandles;
-    memset(&ctx->ntlmWindowsHandles->credHandle, 0, sizeof(CredHandle));
-    TimeStamp tsDummy;
+    // Acquire our credentials handle
     SECURITY_STATUS secStatus = pSecurityFunctionTable->AcquireCredentialsHandle(
-        NULL, (SEC_WCHAR*)L"NTLM", SECPKG_CRED_OUTBOUND, NULL, NULL,
-        NULL, NULL, &ctx->ntlmWindowsHandles->credHandle, &tsDummy);
+                nullptr,
+                (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) {
-        delete ctx->ntlmWindowsHandles;
-        ctx->ntlmWindowsHandles = 0;
-        return result;
-    }
-
-    // 2. The client calls the SSPI InitializeSecurityContext function
-    // 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;
+        ctx->sspiWindowsHandles.reset(nullptr);
+        return QByteArray();
     }
 
-    result = QByteArray((const char*)buf.pvBuffer, buf.cbBuffer);
-    pSecurityFunctionTable->FreeContextBuffer(buf.pvBuffer);
+    return qSspiContinue(ctx, method, host);
+}
+
+static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
+                                  const QString &host, const QByteArray &challenge)
+{
+    QByteArray result;
+    SecBuffer challengeBuf;
+    SecBuffer responseBuf;
+    SecBufferDesc challengeDesc;
+    SecBufferDesc responseDesc;
+    unsigned long attrs;
+    TimeStamp expiry; // For Windows 9x compatibility of SSPI calls
+
+    if (!challenge.isEmpty())
+    {
+        // Setup the challenge "input" security buffer
+        challengeDesc.ulVersion = SECBUFFER_VERSION;
+        challengeDesc.cBuffers  = 1;
+        challengeDesc.pBuffers  = &challengeBuf;
+        challengeBuf.BufferType = SECBUFFER_TOKEN;
+        challengeBuf.pvBuffer   = (PVOID)(challenge.data());
+        challengeBuf.cbBuffer   = challenge.length();
+    }
+
+    // Setup the response "output" security buffer
+    responseDesc.ulVersion = SECBUFFER_VERSION;
+    responseDesc.cBuffers  = 1;
+    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;
 }
 
-// Phase 2:
-// 3. The server receives the token from the client, and uses it as input to the
-// 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.
+// ---------------------------- End of SSPI code ---------------------------------------
 
-    QByteArray result;
+#elif QT_CONFIG(gssapi) // GSSAPI
 
-    if (pSecurityFunctionTable == NULL)
-        return result;
+// ---------------------------- GSSAPI code ----------------------------------------------
+// See postgres src/interfaces/libpq/fe-auth.c
 
-    SecBuffer type_2, type_3;
-    SecBufferDesc type_2_desc, type_3_desc;
-    ULONG attrs;
-    TimeStamp tsDummy; // For Windows 9x compatibility of SPPI calls
-
-    type_2_desc.ulVersion  = type_3_desc.ulVersion  = SECBUFFER_VERSION;
-    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);
-    pSecurityFunctionTable->DeleteSecurityContext(&ctx->ntlmWindowsHandles->ctxHandle);
-    delete ctx->ntlmWindowsHandles;
-    ctx->ntlmWindowsHandles = 0;
+// 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;
 }
-#endif // Q_OS_WIN && !Q_OS_WINRT
+
+// ---------------------------- End of GSSAPI code ----------------------------------------------
+
+#endif // gssapi
 
 QT_END_NAMESPACE
diff -rupN --no-dereference qtbase-everywhere-src-5.12.5/src/network/kernel/qauthenticator_p.h qtbase-everywhere-src-5.12.5-new/src/network/kernel/qauthenticator_p.h
--- qtbase-everywhere-src-5.12.5/src/network/kernel/qauthenticator_p.h	2019-09-03 20:52:35.000000000 +0200
+++ qtbase-everywhere-src-5.12.5-new/src/network/kernel/qauthenticator_p.h	2019-09-25 09:47:35.047550340 +0200
@@ -54,6 +54,7 @@
 #include <QtNetwork/private/qtnetworkglobal_p.h>
 #include <qhash.h>
 #include <qbytearray.h>
+#include <qscopedpointer.h>
 #include <qstring.h>
 #include <qauthenticator.h>
 #include <qvariant.h>
@@ -61,16 +62,17 @@
 QT_BEGIN_NAMESPACE
 
 class QHttpResponseHeader;
-#ifdef Q_OS_WIN
-class QNtlmWindowsHandles;
+#if QT_CONFIG(sspi) // SSPI
+class QSSPIWindowsHandles;
+#elif QT_CONFIG(gssapi) // GSSAPI
+class QGssApiHandles;
 #endif
 
 class Q_AUTOTEST_EXPORT QAuthenticatorPrivate
 {
 public:
-    enum Method { None, Basic, Plain, Login, Ntlm, CramMd5, DigestMd5 };
+    enum Method { None, Basic, Plain, Login, Ntlm, CramMd5, DigestMd5, Negotiate };
     QAuthenticatorPrivate();
-    ~QAuthenticatorPrivate();
 
     QString user;
     QString extractedUser;
@@ -79,8 +81,10 @@ public:
     Method method;
     QString realm;
     QByteArray challenge;
-#ifdef Q_OS_WIN
-    QNtlmWindowsHandles *ntlmWindowsHandles;
+#if QT_CONFIG(sspi) // SSPI
+    QScopedPointer<QSSPIWindowsHandles> sspiWindowsHandles;
+#elif QT_CONFIG(gssapi) // GSSAPI
+    QScopedPointer<QGssApiHandles> gssApiHandles;
 #endif
     bool hasFailed; //credentials have been tried but rejected by server.
 
@@ -100,7 +104,7 @@ public:
     QString workstation;
     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 const QAuthenticatorPrivate *getPrivate(const QAuthenticator &auth) { return auth.d; }
diff -rupN --no-dereference qtbase-everywhere-src-5.12.5/src/network/socket/qhttpsocketengine.cpp qtbase-everywhere-src-5.12.5-new/src/network/socket/qhttpsocketengine.cpp
--- qtbase-everywhere-src-5.12.5/src/network/socket/qhttpsocketengine.cpp	2019-09-03 20:52:35.000000000 +0200
+++ qtbase-everywhere-src-5.12.5-new/src/network/socket/qhttpsocketengine.cpp	2019-09-25 09:47:35.048550338 +0200
@@ -524,7 +524,7 @@ void QHttpSocketEngine::slotSocketConnec
     //qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1);
     if (priv && priv->method != QAuthenticatorPrivate::None) {
         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";
diff -rupN --no-dereference qtbase-everywhere-src-5.12.5/tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp qtbase-everywhere-src-5.12.5-new/tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp
--- qtbase-everywhere-src-5.12.5/tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp	2019-09-03 20:52:35.000000000 +0200
+++ qtbase-everywhere-src-5.12.5-new/tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp	2019-09-25 09:47:35.048550338 +0200
@@ -93,7 +93,7 @@ void tst_QAuthenticator::basicAuth()
 
     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()
@@ -133,9 +133,9 @@ void tst_QAuthenticator::ntlmAuth()
     headers << qMakePair<QByteArray, QByteArray>("WWW-Authenticate", "NTLM");
     priv->parseHttpResponse(headers, /*isProxy = */ false);
     if (sso)
-        QVERIFY(priv->calculateResponse("GET", "/").startsWith("NTLM "));
+        QVERIFY(priv->calculateResponse("GET", "/", "").startsWith("NTLM "));
     else
-        QCOMPARE(priv->calculateResponse("GET", "/").constData(), "NTLM TlRMTVNTUAABAAAABYIIAAAAAAAAAAAAAAAAAAAAAAA=");
+        QCOMPARE(priv->calculateResponse("GET", "/", "").constData(), "NTLM TlRMTVNTUAABAAAABYIIAAAAAAAAAAAAAAAAAAAAAAA=");
 
     // NTLM phase 2: challenge
     headers.clear();
@@ -146,7 +146,7 @@ void tst_QAuthenticator::ntlmAuth()
     QEXPECT_FAIL("with-realm-sso", "NTLM authentication code doesn't extract the realm", Continue);
     QCOMPARE(auth.realm(), realm);
 
-    QVERIFY(priv->calculateResponse("GET", "/").startsWith("NTLM "));
+    QVERIFY(priv->calculateResponse("GET", "/", "").startsWith("NTLM "));
 }
 
 void tst_QAuthenticator::equalityOperators()