Skip to content

Commit f175294

Browse files
committed
http: attempt Negotiate auth in http.emptyAuth=auto mode
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>
1 parent 49488cc commit f175294

File tree

1 file changed

+24
-1
lines changed

1 file changed

+24
-1
lines changed

http.c

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ static unsigned long empty_auth_useless =
138138
CURLAUTH_BASIC
139139
| CURLAUTH_DIGEST_IE
140140
| CURLAUTH_DIGEST;
141+
static int empty_auth_try_negotiate;
141142

142143
static struct curl_slist *pragma_header;
143144
static struct string_list extra_http_headers = STRING_LIST_INIT_DUP;
@@ -667,6 +668,17 @@ static void init_curl_http_auth(CURL *result)
667668

668669
void http_reauth_prepare(int all_capabilities)
669670
{
671+
/*
672+
* If we deferred stripping Negotiate to give empty auth a
673+
* chance (auto mode), skip credential_fill on this retry so
674+
* that init_curl_http_auth() sends empty credentials and
675+
* libcurl can attempt Negotiate with the system ticket cache.
676+
*/
677+
if (empty_auth_try_negotiate &&
678+
!http_auth.password && !http_auth.credential &&
679+
(http_auth_methods & CURLAUTH_GSSNEGOTIATE))
680+
return;
681+
670682
credential_fill(the_repository, &http_auth, all_capabilities);
671683
}
672684

@@ -1895,7 +1907,18 @@ static int handle_curl_result(struct slot_results *results)
18951907
http_proactive_auth = PROACTIVE_AUTH_NONE;
18961908
return HTTP_NOAUTH;
18971909
} else {
1898-
http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;
1910+
if (curl_empty_auth == -1 &&
1911+
!empty_auth_try_negotiate &&
1912+
(results->auth_avail & CURLAUTH_GSSNEGOTIATE)) {
1913+
/*
1914+
* In auto mode, give Negotiate a chance via
1915+
* empty auth before stripping it. If it fails,
1916+
* we will strip it on the next 401.
1917+
*/
1918+
empty_auth_try_negotiate = 1;
1919+
} else {
1920+
http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;
1921+
}
18991922
if (results->auth_avail) {
19001923
http_auth_methods &= results->auth_avail;
19011924
http_auth_methods_restricted = 1;

0 commit comments

Comments
 (0)