http: fix emptyAuth=auto for Negotiate/SPNEGO#6170
Merged
dscho merged 3 commits intogit-for-windows:mainfrom Apr 14, 2026
Merged
http: fix emptyAuth=auto for Negotiate/SPNEGO#6170dscho merged 3 commits intogit-for-windows:mainfrom
dscho merged 3 commits intogit-for-windows:mainfrom
Conversation
All three HTTP retry paths (http_request_recoverable, post_rpc, probe_rpc) call credential_fill() directly when handling HTTP_REAUTH. Extract this into a helper function so that a subsequent commit can add pre-fill logic (such as attempting empty-auth before prompting) in one place. No functional change. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
When a server advertises Negotiate (SPNEGO) authentication, the "auto" mode of http.emptyAuth should detect this as an "exotic" method and proactively send empty credentials, allowing libcurl to use the system Kerberos ticket without prompting the user. However, two features interact to prevent this from working: The Negotiate-stripping logic, introduced in 4dbe664 (remote-curl: fall back to Basic auth if Negotiate fails, 2015-01-08), removes CURLAUTH_GSSNEGOTIATE from the allowed methods on the first 401 response. The empty-auth auto-detection, introduced in 40a18fc (http: add an "auto" mode for http.emptyauth, 2017-02-25), then checks the remaining methods for anything "exotic" -- but Negotiate has already been removed, so auto mode never activates for servers whose only non-Basic/Digest method is Negotiate (e.g., Apache with mod_auth_kerb offering Basic + Negotiate). Fix this by delaying the Negotiate stripping in auto mode: on the first 401, keep Negotiate in the allowed methods so that auto mode can detect it and retry with empty credentials. If that attempt fails (no valid Kerberos ticket), strip Negotiate on the second 401 and fall through to credential_fill() as usual. To support this, also teach http_reauth_prepare() to skip credential_fill() when empty auth is about to be attempted, since filling real credentials would bypass the empty-auth mechanism. The true and false modes are unchanged: true sends empty credentials on the very first request (before any 401), and false never sends them. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
Add tests exercising the interaction between http.emptyAuth and servers that advertise Negotiate (SPNEGO) authentication. Verify that auto mode gives Negotiate a chance via empty auth (resulting in two 401 responses before falling through to credential_fill with Basic credentials), and that false mode strips Negotiate immediately (only one 401 response). Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
dscho
approved these changes
Apr 14, 2026
Member
dscho
left a comment
There was a problem hiding this comment.
Very nice! I am comfortable with this after merging git-for-windows/MINGW-packages#193 (disallowing NTLM via SPNEGO), because that way, we won't reintroduce a way for malicious servers to force NTLM unless the user explicitly allows it.
Member
|
/add relnote bug A really old bug which prevented Kerberos authentication from working with the default The workflow run was started |
github-actions bot
pushed a commit
to git-for-windows/build-extra
that referenced
this pull request
Apr 14, 2026
A _really_ old bug which prevented Kerberos authentication from working with the default [`http.emptyAuth`](https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpemptyAuth) ("auto"), [was fixed](git-for-windows/git#6170). Signed-off-by: gitforwindowshelper[bot] <gitforwindowshelper-bot@users.noreply.github.com>
dscho
added a commit
to dscho/git
that referenced
this pull request
Apr 14, 2026
When a server advertises Negotiate (SPNEGO) authentication alongside Basic, the "auto" mode of http.emptyAuth should allow libcurl to attempt Kerberos authentication using the system ticket cache before falling back to credential_fill(). Currently this never happens due to an interaction between two older features. The Negotiate-stripping logic from 4dbe664 (remote-curl: fall back to Basic auth if Negotiate fails, 2015-01-08) removes CURLAUTH_GSSNEGOTIATE on the first 401, before the auto-detection from 40a18fc (http: add an "auto" mode for http.emptyauth, 2017-02-25) gets a chance to see it as an "exotic" method. The result is that auto mode silently degrades to the same behavior as emptyAuth=false for any server whose only non-Basic/Digest method is Negotiate, forcing Kerberos users to manually set http.emptyAuth=true to get seamless ticket-based authentication. This series fixes the interaction by delaying the Negotiate stripping in auto mode by one round-trip, giving empty auth a chance to use the system Kerberos ticket. If there is no valid ticket, Negotiate is stripped on the second 401 and we fall through to credential_fill() as before. The true and false modes are unchanged. Patch 1: Extract a http_reauth_prepare() helper from the three retry paths that call credential_fill() on HTTP_REAUTH. Pure refactor, no behavior change. Patch 2: Delay the GSSNEGOTIATE stripping in auto mode and teach http_reauth_prepare() to skip credential_fill() when empty auth should be attempted first. Patch 3: Add tests verifying that auto mode produces an extra round-trip (empty auth attempt) compared to false mode, using the existing nph-custom-auth.sh CGI infrastructure. There is a trade-off in auto mode: when a server advertises Negotiate but the client has no valid Kerberos ticket, there is one extra round-trip compared to the current behavior. This matches the trade-off already documented in 40a18fc. Users who want to avoid it can set http.emptyAuth=false.
This was referenced Apr 14, 2026
gitforwindowshelper bot
pushed a commit
that referenced
this pull request
Apr 14, 2026
When a server advertises Negotiate (SPNEGO) authentication alongside Basic, the "auto" mode of http.emptyAuth should allow libcurl to attempt Kerberos authentication using the system ticket cache before falling back to credential_fill(). Currently this never happens due to an interaction between two older features. The Negotiate-stripping logic from 4dbe664 (remote-curl: fall back to Basic auth if Negotiate fails, 2015-01-08) removes CURLAUTH_GSSNEGOTIATE on the first 401, before the auto-detection from 40a18fc (http: add an "auto" mode for http.emptyauth, 2017-02-25) gets a chance to see it as an "exotic" method. The result is that auto mode silently degrades to the same behavior as emptyAuth=false for any server whose only non-Basic/Digest method is Negotiate, forcing Kerberos users to manually set http.emptyAuth=true to get seamless ticket-based authentication. This series fixes the interaction by delaying the Negotiate stripping in auto mode by one round-trip, giving empty auth a chance to use the system Kerberos ticket. If there is no valid ticket, Negotiate is stripped on the second 401 and we fall through to credential_fill() as before. The true and false modes are unchanged. Patch 1: Extract a http_reauth_prepare() helper from the three retry paths that call credential_fill() on HTTP_REAUTH. Pure refactor, no behavior change. Patch 2: Delay the GSSNEGOTIATE stripping in auto mode and teach http_reauth_prepare() to skip credential_fill() when empty auth should be attempted first. Patch 3: Add tests verifying that auto mode produces an extra round-trip (empty auth attempt) compared to false mode, using the existing nph-custom-auth.sh CGI infrastructure. There is a trade-off in auto mode: when a server advertises Negotiate but the client has no valid Kerberos ticket, there is one extra round-trip compared to the current behavior. This matches the trade-off already documented in 40a18fc. Users who want to avoid it can set http.emptyAuth=false.
gitforwindowshelper bot
pushed a commit
to git-for-windows/shears-builds
that referenced
this pull request
Apr 14, 2026
When a server advertises Negotiate (SPNEGO) authentication alongside Basic, the "auto" mode of http.emptyAuth should allow libcurl to attempt Kerberos authentication using the system ticket cache before falling back to credential_fill(). Currently this never happens due to an interaction between two older features. The Negotiate-stripping logic from 4dbe664 (remote-curl: fall back to Basic auth if Negotiate fails, 2015-01-08) removes CURLAUTH_GSSNEGOTIATE on the first 401, before the auto-detection from 40a18fc (http: add an "auto" mode for http.emptyauth, 2017-02-25) gets a chance to see it as an "exotic" method. The result is that auto mode silently degrades to the same behavior as emptyAuth=false for any server whose only non-Basic/Digest method is Negotiate, forcing Kerberos users to manually set http.emptyAuth=true to get seamless ticket-based authentication. This series fixes the interaction by delaying the Negotiate stripping in auto mode by one round-trip, giving empty auth a chance to use the system Kerberos ticket. If there is no valid ticket, Negotiate is stripped on the second 401 and we fall through to credential_fill() as before. The true and false modes are unchanged. Patch 1: Extract a http_reauth_prepare() helper from the three retry paths that call credential_fill() on HTTP_REAUTH. Pure refactor, no behavior change. Patch 2: Delay the GSSNEGOTIATE stripping in auto mode and teach http_reauth_prepare() to skip credential_fill() when empty auth should be attempted first. Patch 3: Add tests verifying that auto mode produces an extra round-trip (empty auth attempt) compared to false mode, using the existing nph-custom-auth.sh CGI infrastructure. There is a trade-off in auto mode: when a server advertises Negotiate but the client has no valid Kerberos ticket, there is one extra round-trip compared to the current behavior. This matches the trade-off already documented in 40a18fc. Users who want to avoid it can set http.emptyAuth=false.
gitforwindowshelper bot
pushed a commit
to git-for-windows/shears-builds
that referenced
this pull request
Apr 14, 2026
When a server advertises Negotiate (SPNEGO) authentication alongside Basic, the "auto" mode of http.emptyAuth should allow libcurl to attempt Kerberos authentication using the system ticket cache before falling back to credential_fill(). Currently this never happens due to an interaction between two older features. The Negotiate-stripping logic from 4dbe664 (remote-curl: fall back to Basic auth if Negotiate fails, 2015-01-08) removes CURLAUTH_GSSNEGOTIATE on the first 401, before the auto-detection from 40a18fc (http: add an "auto" mode for http.emptyauth, 2017-02-25) gets a chance to see it as an "exotic" method. The result is that auto mode silently degrades to the same behavior as emptyAuth=false for any server whose only non-Basic/Digest method is Negotiate, forcing Kerberos users to manually set http.emptyAuth=true to get seamless ticket-based authentication. This series fixes the interaction by delaying the Negotiate stripping in auto mode by one round-trip, giving empty auth a chance to use the system Kerberos ticket. If there is no valid ticket, Negotiate is stripped on the second 401 and we fall through to credential_fill() as before. The true and false modes are unchanged. Patch 1: Extract a http_reauth_prepare() helper from the three retry paths that call credential_fill() on HTTP_REAUTH. Pure refactor, no behavior change. Patch 2: Delay the GSSNEGOTIATE stripping in auto mode and teach http_reauth_prepare() to skip credential_fill() when empty auth should be attempted first. Patch 3: Add tests verifying that auto mode produces an extra round-trip (empty auth attempt) compared to false mode, using the existing nph-custom-auth.sh CGI infrastructure. There is a trade-off in auto mode: when a server advertises Negotiate but the client has no valid Kerberos ticket, there is one extra round-trip compared to the current behavior. This matches the trade-off already documented in 40a18fc. Users who want to avoid it can set http.emptyAuth=false.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When a server advertises Negotiate (SPNEGO) authentication alongside
Basic, the "auto" mode of http.emptyAuth should allow libcurl to
attempt Kerberos authentication using the system ticket cache before
falling back to credential_fill(). Currently this never happens due
to an interaction between two older features.
The Negotiate-stripping logic from 4dbe664 (remote-curl: fall back
to Basic auth if Negotiate fails, 2015-01-08) removes
CURLAUTH_GSSNEGOTIATE on the first 401, before the auto-detection
from 40a18fc (http: add an "auto" mode for http.emptyauth,
2017-02-25) gets a chance to see it as an "exotic" method. The result
is that auto mode silently degrades to the same behavior as
emptyAuth=false for any server whose only non-Basic/Digest method is
Negotiate, forcing Kerberos users to manually set http.emptyAuth=true
to get seamless ticket-based authentication.
This series fixes the interaction by delaying the Negotiate stripping
in auto mode by one round-trip, giving empty auth a chance to use the
system Kerberos ticket. If there is no valid ticket, Negotiate is
stripped on the second 401 and we fall through to credential_fill()
as before. The true and false modes are unchanged.
Patch 1: Extract a http_reauth_prepare() helper from the three
retry paths that call credential_fill() on HTTP_REAUTH.
Pure refactor, no behavior change.
Patch 2: Delay the GSSNEGOTIATE stripping in auto mode and teach
http_reauth_prepare() to skip credential_fill() when
empty auth should be attempted first.
Patch 3: Add tests verifying that auto mode produces an extra
round-trip (empty auth attempt) compared to false mode,
using the existing nph-custom-auth.sh CGI infrastructure.
There is a trade-off in auto mode: when a server advertises Negotiate
but the client has no valid Kerberos ticket, there is one extra
round-trip compared to the current behavior. This matches the
trade-off already documented in 40a18fc. Users who want to avoid
it can set http.emptyAuth=false.