* [PATCH 0/3] http: fix emptyAuth=auto for Negotiate/SPNEGO
@ 2026-04-16 9:20 Matthew John Cheetham via GitGitGadget
2026-04-16 9:20 ` [PATCH 1/3] http: extract http_reauth_prepare() from retry paths Matthew John Cheetham via GitGitGadget
` (3 more replies)
0 siblings, 4 replies; 13+ messages in thread
From: Matthew John Cheetham via GitGitGadget @ 2026-04-16 9:20 UTC (permalink / raw)
To: git; +Cc: gitster, johannes.schindelin, Matthew John Cheetham
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 4dbe66464b (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 40a18fc77c (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 40a18fc77c. Users who want to avoid it can set
http.emptyAuth=false.
Note: this patch series was taken early into Git for Windows for the
2.54.0-rc2 release.
https://github.com/git-for-windows/git/commit/8e94b65c003783d7d7b09d9fccdf06a1363e347c
Matthew John Cheetham (3):
http: extract http_reauth_prepare() from retry paths
http: attempt Negotiate auth in http.emptyAuth=auto mode
t5563: add tests for http.emptyAuth with Negotiate
http.c | 32 +++++++++++++++-
http.h | 6 +++
remote-curl.c | 4 +-
t/t5563-simple-http-auth.sh | 74 +++++++++++++++++++++++++++++++++++++
4 files changed, 112 insertions(+), 4 deletions(-)
base-commit: 2b39a27d40682c09ac1c031f099ee602061597cd
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2087%2Fmjcheetham%2Fspnego-fix-upstream-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2087/mjcheetham/spnego-fix-upstream-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2087
--
gitgitgadget
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH 1/3] http: extract http_reauth_prepare() from retry paths 2026-04-16 9:20 [PATCH 0/3] http: fix emptyAuth=auto for Negotiate/SPNEGO Matthew John Cheetham via GitGitGadget @ 2026-04-16 9:20 ` Matthew John Cheetham via GitGitGadget 2026-04-16 16:21 ` Junio C Hamano 2026-04-16 9:20 ` [PATCH 2/3] http: attempt Negotiate auth in http.emptyAuth=auto mode Matthew John Cheetham via GitGitGadget ` (2 subsequent siblings) 3 siblings, 1 reply; 13+ messages in thread From: Matthew John Cheetham via GitGitGadget @ 2026-04-16 9:20 UTC (permalink / raw) To: git Cc: gitster, johannes.schindelin, Matthew John Cheetham, Matthew John Cheetham From: Matthew John Cheetham <mjcheetham@outlook.com> 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> --- http.c | 7 ++++++- http.h | 6 ++++++ remote-curl.c | 4 ++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/http.c b/http.c index d8d016891b..f208e0ad82 100644 --- a/http.c +++ b/http.c @@ -665,6 +665,11 @@ static void init_curl_http_auth(CURL *result) } } +void http_reauth_prepare(int all_capabilities) +{ + credential_fill(the_repository, &http_auth, all_capabilities); +} + /* *var must be free-able */ static void var_override(char **var, char *value) { @@ -2398,7 +2403,7 @@ static int http_request_recoverable(const char *url, sleep(retry_delay); } } else if (ret == HTTP_REAUTH) { - credential_fill(the_repository, &http_auth, 1); + http_reauth_prepare(1); } ret = http_request(url, result, target, options); diff --git a/http.h b/http.h index f9ee888c3e..729c51904d 100644 --- a/http.h +++ b/http.h @@ -76,6 +76,12 @@ extern int http_is_verbose; extern ssize_t http_post_buffer; extern struct credential http_auth; +/** + * Prepare for an HTTP re-authentication retry. This fills credentials + * via credential_fill() so the next request can include them. + */ +void http_reauth_prepare(int all_capabilities); + extern char curl_errorstr[CURL_ERROR_SIZE]; enum http_follow_config { diff --git a/remote-curl.c b/remote-curl.c index aba60d5712..affdb880f7 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -946,7 +946,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece do { err = probe_rpc(rpc, &results); if (err == HTTP_REAUTH) - credential_fill(the_repository, &http_auth, 0); + http_reauth_prepare(0); } while (err == HTTP_REAUTH); if (err != HTTP_OK) return -1; @@ -1068,7 +1068,7 @@ retry: rpc->any_written = 0; err = run_slot(slot, NULL); if (err == HTTP_REAUTH && !large_request) { - credential_fill(the_repository, &http_auth, 0); + http_reauth_prepare(0); curl_slist_free_all(headers); goto retry; } -- gitgitgadget ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 1/3] http: extract http_reauth_prepare() from retry paths 2026-04-16 9:20 ` [PATCH 1/3] http: extract http_reauth_prepare() from retry paths Matthew John Cheetham via GitGitGadget @ 2026-04-16 16:21 ` Junio C Hamano 0 siblings, 0 replies; 13+ messages in thread From: Junio C Hamano @ 2026-04-16 16:21 UTC (permalink / raw) To: Matthew John Cheetham via GitGitGadget Cc: git, johannes.schindelin, Matthew John Cheetham "Matthew John Cheetham via GitGitGadget" <gitgitgadget@gmail.com> writes: > From: Matthew John Cheetham <mjcheetham@outlook.com> > > 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> > --- > http.c | 7 ++++++- > http.h | 6 ++++++ > remote-curl.c | 4 ++-- > 3 files changed, 14 insertions(+), 3 deletions(-) Neat. > > diff --git a/http.c b/http.c > index d8d016891b..f208e0ad82 100644 > --- a/http.c > +++ b/http.c > @@ -665,6 +665,11 @@ static void init_curl_http_auth(CURL *result) > } > } > > +void http_reauth_prepare(int all_capabilities) > +{ > + credential_fill(the_repository, &http_auth, all_capabilities); > +} > + > /* *var must be free-able */ > static void var_override(char **var, char *value) > { > @@ -2398,7 +2403,7 @@ static int http_request_recoverable(const char *url, > sleep(retry_delay); > } > } else if (ret == HTTP_REAUTH) { > - credential_fill(the_repository, &http_auth, 1); > + http_reauth_prepare(1); > } > > ret = http_request(url, result, target, options); > diff --git a/http.h b/http.h > index f9ee888c3e..729c51904d 100644 > --- a/http.h > +++ b/http.h > @@ -76,6 +76,12 @@ extern int http_is_verbose; > extern ssize_t http_post_buffer; > extern struct credential http_auth; > > +/** > + * Prepare for an HTTP re-authentication retry. This fills credentials > + * via credential_fill() so the next request can include them. > + */ > +void http_reauth_prepare(int all_capabilities); > + > extern char curl_errorstr[CURL_ERROR_SIZE]; > > enum http_follow_config { > diff --git a/remote-curl.c b/remote-curl.c > index aba60d5712..affdb880f7 100644 > --- a/remote-curl.c > +++ b/remote-curl.c > @@ -946,7 +946,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece > do { > err = probe_rpc(rpc, &results); > if (err == HTTP_REAUTH) > - credential_fill(the_repository, &http_auth, 0); > + http_reauth_prepare(0); > } while (err == HTTP_REAUTH); > if (err != HTTP_OK) > return -1; > @@ -1068,7 +1068,7 @@ retry: > rpc->any_written = 0; > err = run_slot(slot, NULL); > if (err == HTTP_REAUTH && !large_request) { > - credential_fill(the_repository, &http_auth, 0); > + http_reauth_prepare(0); > curl_slist_free_all(headers); > goto retry; > } ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 2/3] http: attempt Negotiate auth in http.emptyAuth=auto mode 2026-04-16 9:20 [PATCH 0/3] http: fix emptyAuth=auto for Negotiate/SPNEGO Matthew John Cheetham via GitGitGadget 2026-04-16 9:20 ` [PATCH 1/3] http: extract http_reauth_prepare() from retry paths Matthew John Cheetham via GitGitGadget @ 2026-04-16 9:20 ` Matthew John Cheetham via GitGitGadget 2026-04-16 16:40 ` Junio C Hamano 2026-04-16 9:20 ` [PATCH 3/3] t5563: add tests for http.emptyAuth with Negotiate Matthew John Cheetham via GitGitGadget 2026-04-30 10:54 ` [PATCH v2 0/4] http: fix emptyAuth=auto for Negotiate/SPNEGO Matthew John Cheetham via GitGitGadget 3 siblings, 1 reply; 13+ messages in thread From: Matthew John Cheetham via GitGitGadget @ 2026-04-16 9:20 UTC (permalink / raw) To: git Cc: gitster, johannes.schindelin, Matthew John Cheetham, Matthew John Cheetham From: 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 4dbe66464b (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 40a18fc77c (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> --- http.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/http.c b/http.c index f208e0ad82..1c7ea32ef2 100644 --- a/http.c +++ b/http.c @@ -138,6 +138,7 @@ static unsigned long empty_auth_useless = CURLAUTH_BASIC | CURLAUTH_DIGEST_IE | CURLAUTH_DIGEST; +static int empty_auth_try_negotiate; static struct curl_slist *pragma_header; static struct string_list extra_http_headers = STRING_LIST_INIT_DUP; @@ -667,6 +668,17 @@ static void init_curl_http_auth(CURL *result) void http_reauth_prepare(int all_capabilities) { + /* + * If we deferred stripping Negotiate to give empty auth a + * chance (auto mode), skip credential_fill on this retry so + * that init_curl_http_auth() sends empty credentials and + * libcurl can attempt Negotiate with the system ticket cache. + */ + if (empty_auth_try_negotiate && + !http_auth.password && !http_auth.credential && + (http_auth_methods & CURLAUTH_GSSNEGOTIATE)) + return; + credential_fill(the_repository, &http_auth, all_capabilities); } @@ -1895,7 +1907,18 @@ static int handle_curl_result(struct slot_results *results) http_proactive_auth = PROACTIVE_AUTH_NONE; return HTTP_NOAUTH; } else { - http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE; + if (curl_empty_auth == -1 && + !empty_auth_try_negotiate && + (results->auth_avail & CURLAUTH_GSSNEGOTIATE)) { + /* + * In auto mode, give Negotiate a chance via + * empty auth before stripping it. If it fails, + * we will strip it on the next 401. + */ + empty_auth_try_negotiate = 1; + } else { + http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE; + } if (results->auth_avail) { http_auth_methods &= results->auth_avail; http_auth_methods_restricted = 1; -- gitgitgadget ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 2/3] http: attempt Negotiate auth in http.emptyAuth=auto mode 2026-04-16 9:20 ` [PATCH 2/3] http: attempt Negotiate auth in http.emptyAuth=auto mode Matthew John Cheetham via GitGitGadget @ 2026-04-16 16:40 ` Junio C Hamano 2026-04-28 14:38 ` Matthew John Cheetham 0 siblings, 1 reply; 13+ messages in thread From: Junio C Hamano @ 2026-04-16 16:40 UTC (permalink / raw) To: Matthew John Cheetham via GitGitGadget Cc: git, johannes.schindelin, Matthew John Cheetham "Matthew John Cheetham via GitGitGadget" <gitgitgadget@gmail.com> writes: > From: 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 4dbe66464b > (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 40a18fc77c (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). Well explained. > 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. OK, succeeding after two attempts is much better than failing after only one attempt. > 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. And that is why the previous step shines. Very neat. > The true and false modes are unchanged: true sends empty credentials > on the very first request (before any 401), and false never sends > them. OK. This is a tangent, but "git config --help" on "http.emptyAuth" is horrible. It does not say what the allowed values are, so I had to first write "There are million other things in the system that this patch does not modify, so what's the point of singling out these two settings and saying that this patch does not change them?", before realizing that 'auto' the patch (and the explanation of the "empty-autho auto-detction" above) is about the third possiblity of the same variable and take it back. > Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com> > --- > http.c | 25 ++++++++++++++++++++++++- > 1 file changed, 24 insertions(+), 1 deletion(-) > > diff --git a/http.c b/http.c > index f208e0ad82..1c7ea32ef2 100644 > --- a/http.c > +++ b/http.c > @@ -138,6 +138,7 @@ static unsigned long empty_auth_useless = > CURLAUTH_BASIC > | CURLAUTH_DIGEST_IE > | CURLAUTH_DIGEST; > +static int empty_auth_try_negotiate; > > static struct curl_slist *pragma_header; > static struct string_list extra_http_headers = STRING_LIST_INIT_DUP; I guess the existing code already assumes that we connect to a single destination, run a single "session", and then die, so it is in line with the existing design to have a file-scope global keep track of our "state". In the longer run we may want to move these things to a struct so that we can run multiple sessions without having to kill ourselves and restart, but that is totally outside the topic of these patches to fix the negotiate auth. > @@ -667,6 +668,17 @@ static void init_curl_http_auth(CURL *result) > > void http_reauth_prepare(int all_capabilities) > { > + /* > + * If we deferred stripping Negotiate to give empty auth a > + * chance (auto mode), skip credential_fill on this retry so > + * that init_curl_http_auth() sends empty credentials and > + * libcurl can attempt Negotiate with the system ticket cache. > + */ > + if (empty_auth_try_negotiate && > + !http_auth.password && !http_auth.credential && > + (http_auth_methods & CURLAUTH_GSSNEGOTIATE)) > + return; > + > credential_fill(the_repository, &http_auth, all_capabilities); > } > > @@ -1895,7 +1907,18 @@ static int handle_curl_result(struct slot_results *results) > http_proactive_auth = PROACTIVE_AUTH_NONE; > return HTTP_NOAUTH; > } else { > - http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE; > + if (curl_empty_auth == -1 && > + !empty_auth_try_negotiate && > + (results->auth_avail & CURLAUTH_GSSNEGOTIATE)) { > + /* > + * In auto mode, give Negotiate a chance via > + * empty auth before stripping it. If it fails, > + * we will strip it on the next 401. > + */ > + empty_auth_try_negotiate = 1; > + } else { > + http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE; > + } > if (results->auth_avail) { > http_auth_methods &= results->auth_avail; > http_auth_methods_restricted = 1; ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/3] http: attempt Negotiate auth in http.emptyAuth=auto mode 2026-04-16 16:40 ` Junio C Hamano @ 2026-04-28 14:38 ` Matthew John Cheetham [not found] ` <xmqqse8dz4pi.fsf@gitster.g> 0 siblings, 1 reply; 13+ messages in thread From: Matthew John Cheetham @ 2026-04-28 14:38 UTC (permalink / raw) To: Junio C Hamano, Matthew John Cheetham via GitGitGadget Cc: git, johannes.schindelin On 2026-04-16 17:40, Junio C Hamano wrote: >> The true and false modes are unchanged: true sends empty credentials >> on the very first request (before any 401), and false never sends >> them. > > OK. This is a tangent, but "git config --help" on "http.emptyAuth" > is horrible. It does not say what the allowed values are, so I had > to first write "There are million other things in the system that > this patch does not modify, so what's the point of singling out > these two settings and saying that this patch does not change > them?", before realizing that 'auto' the patch (and the explanation > of the "empty-autho auto-detction" above) is about the third > possiblity of the same variable and take it back. Agreed - the existing description is pretty opaque about what values it actually takes. Should I add another patch to this series to spell out the three values explicitly? How about something like this: http.emptyAuth:: Attempt authentication without seeking a username or password. This can be used to attempt GSS-Negotiate authentication without specifying a username in the URL, as libcurl normally requires a username for authentication. Possible values are: + -- * `auto` (default) - Send empty credentials only if the server's 401 response advertises an authentication mechanism that requires them (such as GSS-Negotiate); otherwise fall back to prompting via the credential helper. * `true` - Always send empty credentials on the very first request, before receiving any 401 response from the server. * `false` - Never send empty credentials. Mechanisms that require empty credentials, such as GSS-Negotiate, will not work. -- Does that read better? Thanks, Matthew ^ permalink raw reply [flat|nested] 13+ messages in thread
[parent not found: <xmqqse8dz4pi.fsf@gitster.g>]
* Re: [PATCH 2/3] http: attempt Negotiate auth in http.emptyAuth=auto mode [not found] ` <xmqqse8dz4pi.fsf@gitster.g> @ 2026-04-30 10:53 ` Matthew John Cheetham 0 siblings, 0 replies; 13+ messages in thread From: Matthew John Cheetham @ 2026-04-30 10:53 UTC (permalink / raw) To: Junio C Hamano; +Cc: Git Mailing List [re-cc:ing the accidentially dropped mailing list] On 2026-04-30 01:12, Junio C Hamano wrote: > Matthew John Cheetham <mjcheetham@outlook.com> writes: > >> Agreed - the existing description is pretty opaque about what values it >> actually takes. Should I add another patch to this series to spell out >> the three values explicitly? How about something like this: >> >> http.emptyAuth:: >> Attempt authentication without seeking a username or >> password. This can be used to attempt GSS-Negotiate >> authentication without specifying a username in the URL, >> as libcurl normally requires a username for >> authentication. Possible values are: >> + >> -- >> * `auto` (default) - Send empty credentials only if the server's >> 401 response advertises an authentication mechanism that >> requires them (such as GSS-Negotiate); otherwise fall back to >> prompting via the credential helper. >> * `true` - Always send empty credentials on the very first >> request, before receiving any 401 response from the server. >> * `false` - Never send empty credentials. Mechanisms that >> require empty credentials, such as GSS-Negotiate, will not >> work. >> -- >> >> Does that read better? > > Surely. Thanks. Submitted as v2 Thanks, Matthew ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 3/3] t5563: add tests for http.emptyAuth with Negotiate 2026-04-16 9:20 [PATCH 0/3] http: fix emptyAuth=auto for Negotiate/SPNEGO Matthew John Cheetham via GitGitGadget 2026-04-16 9:20 ` [PATCH 1/3] http: extract http_reauth_prepare() from retry paths Matthew John Cheetham via GitGitGadget 2026-04-16 9:20 ` [PATCH 2/3] http: attempt Negotiate auth in http.emptyAuth=auto mode Matthew John Cheetham via GitGitGadget @ 2026-04-16 9:20 ` Matthew John Cheetham via GitGitGadget 2026-04-30 10:54 ` [PATCH v2 0/4] http: fix emptyAuth=auto for Negotiate/SPNEGO Matthew John Cheetham via GitGitGadget 3 siblings, 0 replies; 13+ messages in thread From: Matthew John Cheetham via GitGitGadget @ 2026-04-16 9:20 UTC (permalink / raw) To: git Cc: gitster, johannes.schindelin, Matthew John Cheetham, Matthew John Cheetham From: 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> --- t/t5563-simple-http-auth.sh | 74 +++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh index 0063581615..a7d475dd68 100755 --- a/t/t5563-simple-http-auth.sh +++ b/t/t5563-simple-http-auth.sh @@ -719,4 +719,78 @@ test_expect_success 'access using three-legged auth' ' EOF ' +test_lazy_prereq SPNEGO 'curl --version | grep -qi "SPNEGO\|GSS-API\|Kerberos\|negotiate"' + +test_expect_success SPNEGO 'http.emptyAuth=auto attempts Negotiate before credential_fill' ' + test_when_finished "per_test_cleanup" && + + set_credential_reply get <<-EOF && + username=alice + password=secret-passwd + EOF + + # Basic base64(alice:secret-passwd) + cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF && + id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA== + EOF + + cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF && + id=1 status=200 + id=default response=WWW-Authenticate: Negotiate + id=default response=WWW-Authenticate: Basic realm="example.com" + EOF + + test_config_global credential.helper test-helper && + GIT_TRACE_CURL="$TRASH_DIRECTORY/trace-auto" \ + git -c http.emptyAuth=auto \ + ls-remote "$HTTPD_URL/custom_auth/repo.git" && + + # In auto mode with a Negotiate+Basic server, there should be + # three 401 responses: (1) initial no-auth request, (2) empty-auth + # retry where Negotiate fails (no Kerberos ticket), (3) libcurl + # internal Negotiate retry. The fourth attempt uses Basic + # credentials from credential_fill and succeeds. + grep "HTTP/[0-9.]* 401" "$TRASH_DIRECTORY/trace-auto" >actual_401s && + test_line_count = 3 actual_401s && + + expect_credential_query get <<-EOF + capability[]=authtype + capability[]=state + protocol=http + host=$HTTPD_DEST + wwwauth[]=Negotiate + wwwauth[]=Basic realm="example.com" + EOF +' + +test_expect_success SPNEGO 'http.emptyAuth=false skips Negotiate' ' + test_when_finished "per_test_cleanup" && + + set_credential_reply get <<-EOF && + username=alice + password=secret-passwd + EOF + + # Basic base64(alice:secret-passwd) + cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF && + id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA== + EOF + + cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF && + id=1 status=200 + id=default response=WWW-Authenticate: Negotiate + id=default response=WWW-Authenticate: Basic realm="example.com" + EOF + + test_config_global credential.helper test-helper && + GIT_TRACE_CURL="$TRASH_DIRECTORY/trace-false" \ + git -c http.emptyAuth=false \ + ls-remote "$HTTPD_URL/custom_auth/repo.git" && + + # With emptyAuth=false, Negotiate is stripped immediately and + # credential_fill is called right away. Only one 401 response. + grep "HTTP/[0-9.]* 401" "$TRASH_DIRECTORY/trace-false" >actual_401s && + test_line_count = 1 actual_401s +' + test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 0/4] http: fix emptyAuth=auto for Negotiate/SPNEGO 2026-04-16 9:20 [PATCH 0/3] http: fix emptyAuth=auto for Negotiate/SPNEGO Matthew John Cheetham via GitGitGadget ` (2 preceding siblings ...) 2026-04-16 9:20 ` [PATCH 3/3] t5563: add tests for http.emptyAuth with Negotiate Matthew John Cheetham via GitGitGadget @ 2026-04-30 10:54 ` Matthew John Cheetham via GitGitGadget 2026-04-30 10:54 ` [PATCH v2 1/4] http: extract http_reauth_prepare() from retry paths Matthew John Cheetham via GitGitGadget ` (3 more replies) 3 siblings, 4 replies; 13+ messages in thread From: Matthew John Cheetham via GitGitGadget @ 2026-04-30 10:54 UTC (permalink / raw) To: git Cc: gitster, johannes.schindelin, Matthew John Cheetham, Matthew John Cheetham 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 4dbe66464b (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 40a18fc77c (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. Patch 4: Update http.emptyAuth documentation to clarify possible values (true, false, and auto). 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 40a18fc77c. Users who want to avoid it can set http.emptyAuth=false. Note: this patch series was taken early into Git for Windows for the 2.54.0-rc2 release. https://github.com/git-for-windows/git/commit/8e94b65c003783d7d7b09d9fccdf06a1363e347c ---------------------------------------------------------------------------- Update in v2: * Add patch 4 to clarify the available options for http.emptyAuth in the config documentation. Matthew John Cheetham (4): http: extract http_reauth_prepare() from retry paths http: attempt Negotiate auth in http.emptyAuth=auto mode t5563: add tests for http.emptyAuth with Negotiate doc: clarify http.emptyAuth values Documentation/config/http.adoc | 13 +++++- http.c | 32 ++++++++++++++- http.h | 6 +++ remote-curl.c | 4 +- t/t5563-simple-http-auth.sh | 74 ++++++++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 5 deletions(-) base-commit: 2b39a27d40682c09ac1c031f099ee602061597cd Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2087%2Fmjcheetham%2Fspnego-fix-upstream-v2 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2087/mjcheetham/spnego-fix-upstream-v2 Pull-Request: https://github.com/gitgitgadget/git/pull/2087 Range-diff vs v1: 1: 49488cc7d4 = 1: 49488cc7d4 http: extract http_reauth_prepare() from retry paths 2: f175294459 = 2: f175294459 http: attempt Negotiate auth in http.emptyAuth=auto mode 3: 650acab79e = 3: 650acab79e t5563: add tests for http.emptyAuth with Negotiate -: ---------- > 4: e0f236767f doc: clarify http.emptyAuth values -- gitgitgadget ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 1/4] http: extract http_reauth_prepare() from retry paths 2026-04-30 10:54 ` [PATCH v2 0/4] http: fix emptyAuth=auto for Negotiate/SPNEGO Matthew John Cheetham via GitGitGadget @ 2026-04-30 10:54 ` Matthew John Cheetham via GitGitGadget 2026-04-30 10:54 ` [PATCH v2 2/4] http: attempt Negotiate auth in http.emptyAuth=auto mode Matthew John Cheetham via GitGitGadget ` (2 subsequent siblings) 3 siblings, 0 replies; 13+ messages in thread From: Matthew John Cheetham via GitGitGadget @ 2026-04-30 10:54 UTC (permalink / raw) To: git Cc: gitster, johannes.schindelin, Matthew John Cheetham, Matthew John Cheetham, Matthew John Cheetham From: Matthew John Cheetham <mjcheetham@outlook.com> 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> --- http.c | 7 ++++++- http.h | 6 ++++++ remote-curl.c | 4 ++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/http.c b/http.c index d8d016891b..f208e0ad82 100644 --- a/http.c +++ b/http.c @@ -665,6 +665,11 @@ static void init_curl_http_auth(CURL *result) } } +void http_reauth_prepare(int all_capabilities) +{ + credential_fill(the_repository, &http_auth, all_capabilities); +} + /* *var must be free-able */ static void var_override(char **var, char *value) { @@ -2398,7 +2403,7 @@ static int http_request_recoverable(const char *url, sleep(retry_delay); } } else if (ret == HTTP_REAUTH) { - credential_fill(the_repository, &http_auth, 1); + http_reauth_prepare(1); } ret = http_request(url, result, target, options); diff --git a/http.h b/http.h index f9ee888c3e..729c51904d 100644 --- a/http.h +++ b/http.h @@ -76,6 +76,12 @@ extern int http_is_verbose; extern ssize_t http_post_buffer; extern struct credential http_auth; +/** + * Prepare for an HTTP re-authentication retry. This fills credentials + * via credential_fill() so the next request can include them. + */ +void http_reauth_prepare(int all_capabilities); + extern char curl_errorstr[CURL_ERROR_SIZE]; enum http_follow_config { diff --git a/remote-curl.c b/remote-curl.c index aba60d5712..affdb880f7 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -946,7 +946,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece do { err = probe_rpc(rpc, &results); if (err == HTTP_REAUTH) - credential_fill(the_repository, &http_auth, 0); + http_reauth_prepare(0); } while (err == HTTP_REAUTH); if (err != HTTP_OK) return -1; @@ -1068,7 +1068,7 @@ retry: rpc->any_written = 0; err = run_slot(slot, NULL); if (err == HTTP_REAUTH && !large_request) { - credential_fill(the_repository, &http_auth, 0); + http_reauth_prepare(0); curl_slist_free_all(headers); goto retry; } -- gitgitgadget ^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 2/4] http: attempt Negotiate auth in http.emptyAuth=auto mode 2026-04-30 10:54 ` [PATCH v2 0/4] http: fix emptyAuth=auto for Negotiate/SPNEGO Matthew John Cheetham via GitGitGadget 2026-04-30 10:54 ` [PATCH v2 1/4] http: extract http_reauth_prepare() from retry paths Matthew John Cheetham via GitGitGadget @ 2026-04-30 10:54 ` Matthew John Cheetham via GitGitGadget 2026-04-30 10:54 ` [PATCH v2 3/4] t5563: add tests for http.emptyAuth with Negotiate Matthew John Cheetham via GitGitGadget 2026-04-30 10:54 ` [PATCH v2 4/4] doc: clarify http.emptyAuth values Matthew John Cheetham via GitGitGadget 3 siblings, 0 replies; 13+ messages in thread From: Matthew John Cheetham via GitGitGadget @ 2026-04-30 10:54 UTC (permalink / raw) To: git Cc: gitster, johannes.schindelin, Matthew John Cheetham, Matthew John Cheetham, Matthew John Cheetham From: 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 4dbe66464b (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 40a18fc77c (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> --- http.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/http.c b/http.c index f208e0ad82..1c7ea32ef2 100644 --- a/http.c +++ b/http.c @@ -138,6 +138,7 @@ static unsigned long empty_auth_useless = CURLAUTH_BASIC | CURLAUTH_DIGEST_IE | CURLAUTH_DIGEST; +static int empty_auth_try_negotiate; static struct curl_slist *pragma_header; static struct string_list extra_http_headers = STRING_LIST_INIT_DUP; @@ -667,6 +668,17 @@ static void init_curl_http_auth(CURL *result) void http_reauth_prepare(int all_capabilities) { + /* + * If we deferred stripping Negotiate to give empty auth a + * chance (auto mode), skip credential_fill on this retry so + * that init_curl_http_auth() sends empty credentials and + * libcurl can attempt Negotiate with the system ticket cache. + */ + if (empty_auth_try_negotiate && + !http_auth.password && !http_auth.credential && + (http_auth_methods & CURLAUTH_GSSNEGOTIATE)) + return; + credential_fill(the_repository, &http_auth, all_capabilities); } @@ -1895,7 +1907,18 @@ static int handle_curl_result(struct slot_results *results) http_proactive_auth = PROACTIVE_AUTH_NONE; return HTTP_NOAUTH; } else { - http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE; + if (curl_empty_auth == -1 && + !empty_auth_try_negotiate && + (results->auth_avail & CURLAUTH_GSSNEGOTIATE)) { + /* + * In auto mode, give Negotiate a chance via + * empty auth before stripping it. If it fails, + * we will strip it on the next 401. + */ + empty_auth_try_negotiate = 1; + } else { + http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE; + } if (results->auth_avail) { http_auth_methods &= results->auth_avail; http_auth_methods_restricted = 1; -- gitgitgadget ^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 3/4] t5563: add tests for http.emptyAuth with Negotiate 2026-04-30 10:54 ` [PATCH v2 0/4] http: fix emptyAuth=auto for Negotiate/SPNEGO Matthew John Cheetham via GitGitGadget 2026-04-30 10:54 ` [PATCH v2 1/4] http: extract http_reauth_prepare() from retry paths Matthew John Cheetham via GitGitGadget 2026-04-30 10:54 ` [PATCH v2 2/4] http: attempt Negotiate auth in http.emptyAuth=auto mode Matthew John Cheetham via GitGitGadget @ 2026-04-30 10:54 ` Matthew John Cheetham via GitGitGadget 2026-04-30 10:54 ` [PATCH v2 4/4] doc: clarify http.emptyAuth values Matthew John Cheetham via GitGitGadget 3 siblings, 0 replies; 13+ messages in thread From: Matthew John Cheetham via GitGitGadget @ 2026-04-30 10:54 UTC (permalink / raw) To: git Cc: gitster, johannes.schindelin, Matthew John Cheetham, Matthew John Cheetham, Matthew John Cheetham From: 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> --- t/t5563-simple-http-auth.sh | 74 +++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh index 0063581615..a7d475dd68 100755 --- a/t/t5563-simple-http-auth.sh +++ b/t/t5563-simple-http-auth.sh @@ -719,4 +719,78 @@ test_expect_success 'access using three-legged auth' ' EOF ' +test_lazy_prereq SPNEGO 'curl --version | grep -qi "SPNEGO\|GSS-API\|Kerberos\|negotiate"' + +test_expect_success SPNEGO 'http.emptyAuth=auto attempts Negotiate before credential_fill' ' + test_when_finished "per_test_cleanup" && + + set_credential_reply get <<-EOF && + username=alice + password=secret-passwd + EOF + + # Basic base64(alice:secret-passwd) + cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF && + id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA== + EOF + + cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF && + id=1 status=200 + id=default response=WWW-Authenticate: Negotiate + id=default response=WWW-Authenticate: Basic realm="example.com" + EOF + + test_config_global credential.helper test-helper && + GIT_TRACE_CURL="$TRASH_DIRECTORY/trace-auto" \ + git -c http.emptyAuth=auto \ + ls-remote "$HTTPD_URL/custom_auth/repo.git" && + + # In auto mode with a Negotiate+Basic server, there should be + # three 401 responses: (1) initial no-auth request, (2) empty-auth + # retry where Negotiate fails (no Kerberos ticket), (3) libcurl + # internal Negotiate retry. The fourth attempt uses Basic + # credentials from credential_fill and succeeds. + grep "HTTP/[0-9.]* 401" "$TRASH_DIRECTORY/trace-auto" >actual_401s && + test_line_count = 3 actual_401s && + + expect_credential_query get <<-EOF + capability[]=authtype + capability[]=state + protocol=http + host=$HTTPD_DEST + wwwauth[]=Negotiate + wwwauth[]=Basic realm="example.com" + EOF +' + +test_expect_success SPNEGO 'http.emptyAuth=false skips Negotiate' ' + test_when_finished "per_test_cleanup" && + + set_credential_reply get <<-EOF && + username=alice + password=secret-passwd + EOF + + # Basic base64(alice:secret-passwd) + cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF && + id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA== + EOF + + cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF && + id=1 status=200 + id=default response=WWW-Authenticate: Negotiate + id=default response=WWW-Authenticate: Basic realm="example.com" + EOF + + test_config_global credential.helper test-helper && + GIT_TRACE_CURL="$TRASH_DIRECTORY/trace-false" \ + git -c http.emptyAuth=false \ + ls-remote "$HTTPD_URL/custom_auth/repo.git" && + + # With emptyAuth=false, Negotiate is stripped immediately and + # credential_fill is called right away. Only one 401 response. + grep "HTTP/[0-9.]* 401" "$TRASH_DIRECTORY/trace-false" >actual_401s && + test_line_count = 1 actual_401s +' + test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 4/4] doc: clarify http.emptyAuth values 2026-04-30 10:54 ` [PATCH v2 0/4] http: fix emptyAuth=auto for Negotiate/SPNEGO Matthew John Cheetham via GitGitGadget ` (2 preceding siblings ...) 2026-04-30 10:54 ` [PATCH v2 3/4] t5563: add tests for http.emptyAuth with Negotiate Matthew John Cheetham via GitGitGadget @ 2026-04-30 10:54 ` Matthew John Cheetham via GitGitGadget 3 siblings, 0 replies; 13+ messages in thread From: Matthew John Cheetham via GitGitGadget @ 2026-04-30 10:54 UTC (permalink / raw) To: git Cc: gitster, johannes.schindelin, Matthew John Cheetham, Matthew John Cheetham, Matthew John Cheetham From: Matthew John Cheetham <mjcheetham@outlook.com> The existing description of http.emptyAuth explains the purpose of the setting but never says what values it accepts. Readers have to infer from context (or read the source) that it takes 'true', 'false', or 'auto', and what each one means. Document the three accepted values explicitly: * 'auto' (the default) only sends empty credentials when the server's 401 response advertises a mechanism that requires them, such as GSS-Negotiate. This matches the long-standing auto-detection behaviour added in 40a18fc77c (http: add an "auto" mode for http.emptyauth, 2017-02-25). * 'true' unconditionally sends empty credentials on the very first request, before any 401 response, for callers that know they want this behaviour up front. * 'false' disables the feature entirely; mechanisms that depend on empty credentials, such as GSS-Negotiate, will not work in this mode. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com> --- Documentation/config/http.adoc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Documentation/config/http.adoc b/Documentation/config/http.adoc index 849c89f36c..792a71b413 100644 --- a/Documentation/config/http.adoc +++ b/Documentation/config/http.adoc @@ -59,7 +59,18 @@ http.emptyAuth:: Attempt authentication without seeking a username or password. This can be used to attempt GSS-Negotiate authentication without specifying a username in the URL, as libcurl normally requires a username for - authentication. + authentication. Possible values are: ++ +-- +* `auto` (default) - Send empty credentials only if the server's 401 response + advertises an authentication mechanism that requires them (such as + GSS-Negotiate); otherwise fall back to prompting via the credential helper. +* `true` - Always send empty credentials on the very first request, before + receiving any 401 response from the server. +* `false` - Never send empty credentials. Mechanisms that require + empty credentials or an explicit username, such as GSS-Negotiate, will not + work. +-- http.proactiveAuth:: Attempt authentication without first making an unauthenticated attempt and -- gitgitgadget ^ permalink raw reply related [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-04-30 10:54 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-16 9:20 [PATCH 0/3] http: fix emptyAuth=auto for Negotiate/SPNEGO Matthew John Cheetham via GitGitGadget
2026-04-16 9:20 ` [PATCH 1/3] http: extract http_reauth_prepare() from retry paths Matthew John Cheetham via GitGitGadget
2026-04-16 16:21 ` Junio C Hamano
2026-04-16 9:20 ` [PATCH 2/3] http: attempt Negotiate auth in http.emptyAuth=auto mode Matthew John Cheetham via GitGitGadget
2026-04-16 16:40 ` Junio C Hamano
2026-04-28 14:38 ` Matthew John Cheetham
[not found] ` <xmqqse8dz4pi.fsf@gitster.g>
2026-04-30 10:53 ` Matthew John Cheetham
2026-04-16 9:20 ` [PATCH 3/3] t5563: add tests for http.emptyAuth with Negotiate Matthew John Cheetham via GitGitGadget
2026-04-30 10:54 ` [PATCH v2 0/4] http: fix emptyAuth=auto for Negotiate/SPNEGO Matthew John Cheetham via GitGitGadget
2026-04-30 10:54 ` [PATCH v2 1/4] http: extract http_reauth_prepare() from retry paths Matthew John Cheetham via GitGitGadget
2026-04-30 10:54 ` [PATCH v2 2/4] http: attempt Negotiate auth in http.emptyAuth=auto mode Matthew John Cheetham via GitGitGadget
2026-04-30 10:54 ` [PATCH v2 3/4] t5563: add tests for http.emptyAuth with Negotiate Matthew John Cheetham via GitGitGadget
2026-04-30 10:54 ` [PATCH v2 4/4] doc: clarify http.emptyAuth values Matthew John Cheetham via GitGitGadget
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox