Skip to content

Commit ad9bb6d

Browse files
bk2204gitster
authored andcommitted
http: add support for authtype and credential
Now that we have the credential helper code set up to handle arbitrary authentications schemes, let's add support for this in the HTTP code, where we really want to use it. If we're using this new functionality, don't set a username and password, and instead set a header wherever we'd normally do so, including for proxy authentication. Since we can now handle this case, ask the credential helper to enable the appropriate capabilities. Finally, if we're using the authtype value, set "Expect: 100-continue". Any type of authentication that requires multiple rounds (such as NTLM or Kerberos) requires a 100 Continue (if we're larger than http.postBuffer) because otherwise we send the pack data before we're authenticated, the push gets a 401 response, and we can't rewind the stream. We don't know for certain what other custom schemes might require this, the HTTP/1.1 standard has required handling this since 1999, the broken HTTP server for which we disabled this (Google's) is now fixed and has been for some time, and libcurl has a 1-second fallback in case the HTTP server is still broken. In addition, it is not unreasonable to require compliance with a 25-year old standard to use new Git features. For all of these reasons, do so here. Signed-off-by: brian m. carlson <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 5af5cc6 commit ad9bb6d

File tree

4 files changed

+176
-12
lines changed

4 files changed

+176
-12
lines changed

http.c

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -561,18 +561,34 @@ static int curl_empty_auth_enabled(void)
561561
return 0;
562562
}
563563

564+
struct curl_slist *http_append_auth_header(const struct credential *c,
565+
struct curl_slist *headers)
566+
{
567+
if (c->authtype && c->credential) {
568+
struct strbuf auth = STRBUF_INIT;
569+
strbuf_addf(&auth, "Authorization: %s %s",
570+
c->authtype, c->credential);
571+
headers = curl_slist_append(headers, auth.buf);
572+
strbuf_release(&auth);
573+
}
574+
return headers;
575+
}
576+
564577
static void init_curl_http_auth(CURL *result)
565578
{
566-
if (!http_auth.username || !*http_auth.username) {
579+
if ((!http_auth.username || !*http_auth.username) &&
580+
(!http_auth.credential || !*http_auth.credential)) {
567581
if (curl_empty_auth_enabled())
568582
curl_easy_setopt(result, CURLOPT_USERPWD, ":");
569583
return;
570584
}
571585

572-
credential_fill(&http_auth, 0);
586+
credential_fill(&http_auth, 1);
573587

574-
curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
575-
curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
588+
if (http_auth.password) {
589+
curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
590+
curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
591+
}
576592
}
577593

578594
/* *var must be free-able */
@@ -586,17 +602,22 @@ static void var_override(const char **var, char *value)
586602

587603
static void set_proxyauth_name_password(CURL *result)
588604
{
605+
if (proxy_auth.password) {
589606
curl_easy_setopt(result, CURLOPT_PROXYUSERNAME,
590607
proxy_auth.username);
591608
curl_easy_setopt(result, CURLOPT_PROXYPASSWORD,
592609
proxy_auth.password);
610+
} else if (proxy_auth.authtype && proxy_auth.credential) {
611+
curl_easy_setopt(result, CURLOPT_PROXYHEADER,
612+
http_append_auth_header(&proxy_auth, NULL));
613+
}
593614
}
594615

595616
static void init_curl_proxy_auth(CURL *result)
596617
{
597618
if (proxy_auth.username) {
598-
if (!proxy_auth.password)
599-
credential_fill(&proxy_auth, 0);
619+
if (!proxy_auth.password && !proxy_auth.credential)
620+
credential_fill(&proxy_auth, 1);
600621
set_proxyauth_name_password(result);
601622
}
602623

@@ -1468,7 +1489,7 @@ struct active_request_slot *get_active_slot(void)
14681489

14691490
curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve);
14701491
curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
1471-
if (http_auth.password || curl_empty_auth_enabled())
1492+
if (http_auth.password || http_auth.credential || curl_empty_auth_enabled())
14721493
init_curl_http_auth(slot->curl);
14731494

14741495
return slot;
@@ -1757,7 +1778,8 @@ static int handle_curl_result(struct slot_results *results)
17571778
} else if (missing_target(results))
17581779
return HTTP_MISSING_TARGET;
17591780
else if (results->http_code == 401) {
1760-
if (http_auth.username && http_auth.password) {
1781+
if ((http_auth.username && http_auth.password) ||\
1782+
(http_auth.authtype && http_auth.credential)) {
17611783
credential_reject(&http_auth);
17621784
return HTTP_NOAUTH;
17631785
} else {
@@ -2065,11 +2087,15 @@ static int http_request(const char *url,
20652087
/* Add additional headers here */
20662088
if (options && options->extra_headers) {
20672089
const struct string_list_item *item;
2068-
for_each_string_list_item(item, options->extra_headers) {
2069-
headers = curl_slist_append(headers, item->string);
2090+
if (options && options->extra_headers) {
2091+
for_each_string_list_item(item, options->extra_headers) {
2092+
headers = curl_slist_append(headers, item->string);
2093+
}
20702094
}
20712095
}
20722096

2097+
headers = http_append_auth_header(&http_auth, headers);
2098+
20732099
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
20742100
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
20752101
curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
@@ -2190,7 +2216,7 @@ static int http_request_reauth(const char *url,
21902216
BUG("Unknown http_request target");
21912217
}
21922218

2193-
credential_fill(&http_auth, 0);
2219+
credential_fill(&http_auth, 1);
21942220

21952221
return http_request(url, result, target, options);
21962222
}

http.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ int http_get_file(const char *url, const char *filename,
175175

176176
int http_fetch_ref(const char *base, struct ref *ref);
177177

178+
struct curl_slist *http_append_auth_header(const struct credential *c,
179+
struct curl_slist *headers);
180+
178181
/* Helpers for fetching packs */
179182
int http_get_info_packs(const char *base_url,
180183
struct packed_git **packs_head);

remote-curl.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -931,7 +931,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
931931
if (err != HTTP_OK)
932932
return -1;
933933

934-
if (results.auth_avail & CURLAUTH_GSSNEGOTIATE)
934+
if (results.auth_avail & CURLAUTH_GSSNEGOTIATE || http_auth.authtype)
935935
needs_100_continue = 1;
936936
}
937937

@@ -942,6 +942,8 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
942942
headers = curl_slist_append(headers, needs_100_continue ?
943943
"Expect: 100-continue" : "Expect:");
944944

945+
headers = http_append_auth_header(&http_auth, headers);
946+
945947
/* Add Accept-Language header */
946948
if (rpc->hdr_accept_language)
947949
headers = curl_slist_append(headers, rpc->hdr_accept_language);

t/t5563-simple-http-auth.sh

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ test_expect_success 'access using basic auth' '
7474
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
7575
7676
expect_credential_query get <<-EOF &&
77+
capability[]=authtype
7778
protocol=http
7879
host=$HTTPD_DEST
7980
wwwauth[]=Basic realm="example.com"
@@ -87,6 +88,43 @@ test_expect_success 'access using basic auth' '
8788
EOF
8889
'
8990

91+
test_expect_success 'access using basic auth via authtype' '
92+
test_when_finished "per_test_cleanup" &&
93+
94+
set_credential_reply get <<-EOF &&
95+
capability[]=authtype
96+
authtype=Basic
97+
credential=YWxpY2U6c2VjcmV0LXBhc3N3ZA==
98+
EOF
99+
100+
# Basic base64(alice:secret-passwd)
101+
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
102+
Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
103+
EOF
104+
105+
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
106+
WWW-Authenticate: Basic realm="example.com"
107+
EOF
108+
109+
test_config_global credential.helper test-helper &&
110+
GIT_CURL_VERBOSE=1 git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
111+
112+
expect_credential_query get <<-EOF &&
113+
capability[]=authtype
114+
protocol=http
115+
host=$HTTPD_DEST
116+
wwwauth[]=Basic realm="example.com"
117+
EOF
118+
119+
expect_credential_query store <<-EOF
120+
capability[]=authtype
121+
authtype=Basic
122+
credential=YWxpY2U6c2VjcmV0LXBhc3N3ZA==
123+
protocol=http
124+
host=$HTTPD_DEST
125+
EOF
126+
'
127+
90128
test_expect_success 'access using basic auth invalid credentials' '
91129
test_when_finished "per_test_cleanup" &&
92130
@@ -108,6 +146,7 @@ test_expect_success 'access using basic auth invalid credentials' '
108146
test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
109147
110148
expect_credential_query get <<-EOF &&
149+
capability[]=authtype
111150
protocol=http
112151
host=$HTTPD_DEST
113152
wwwauth[]=Basic realm="example.com"
@@ -145,6 +184,7 @@ test_expect_success 'access using basic auth with extra challenges' '
145184
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
146185
147186
expect_credential_query get <<-EOF &&
187+
capability[]=authtype
148188
protocol=http
149189
host=$HTTPD_DEST
150190
wwwauth[]=FooBar param1="value1" param2="value2"
@@ -183,6 +223,7 @@ test_expect_success 'access using basic auth mixed-case wwwauth header name' '
183223
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
184224
185225
expect_credential_query get <<-EOF &&
226+
capability[]=authtype
186227
protocol=http
187228
host=$HTTPD_DEST
188229
wwwauth[]=foobar param1="value1" param2="value2"
@@ -226,6 +267,7 @@ test_expect_success 'access using basic auth with wwwauth header continuations'
226267
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
227268
228269
expect_credential_query get <<-EOF &&
270+
capability[]=authtype
229271
protocol=http
230272
host=$HTTPD_DEST
231273
wwwauth[]=FooBar param1="value1" param2="value2"
@@ -271,6 +313,7 @@ test_expect_success 'access using basic auth with wwwauth header empty continuat
271313
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
272314
273315
expect_credential_query get <<-EOF &&
316+
capability[]=authtype
274317
protocol=http
275318
host=$HTTPD_DEST
276319
wwwauth[]=FooBar param1="value1" param2="value2"
@@ -312,6 +355,7 @@ test_expect_success 'access using basic auth with wwwauth header mixed line-endi
312355
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
313356
314357
expect_credential_query get <<-EOF &&
358+
capability[]=authtype
315359
protocol=http
316360
host=$HTTPD_DEST
317361
wwwauth[]=FooBar param1="value1" param2="value2"
@@ -326,4 +370,93 @@ test_expect_success 'access using basic auth with wwwauth header mixed line-endi
326370
EOF
327371
'
328372

373+
test_expect_success 'access using bearer auth' '
374+
test_when_finished "per_test_cleanup" &&
375+
376+
set_credential_reply get <<-EOF &&
377+
capability[]=authtype
378+
authtype=Bearer
379+
credential=YS1naXQtdG9rZW4=
380+
EOF
381+
382+
# Basic base64(a-git-token)
383+
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
384+
Bearer YS1naXQtdG9rZW4=
385+
EOF
386+
387+
CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
388+
389+
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
390+
WWW-Authenticate: FooBar param1="value1" param2="value2"
391+
WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
392+
WWW-Authenticate: Basic realm="example.com"
393+
EOF
394+
395+
test_config_global credential.helper test-helper &&
396+
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
397+
398+
expect_credential_query get <<-EOF &&
399+
capability[]=authtype
400+
protocol=http
401+
host=$HTTPD_DEST
402+
wwwauth[]=FooBar param1="value1" param2="value2"
403+
wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
404+
wwwauth[]=Basic realm="example.com"
405+
EOF
406+
407+
expect_credential_query store <<-EOF
408+
capability[]=authtype
409+
authtype=Bearer
410+
credential=YS1naXQtdG9rZW4=
411+
protocol=http
412+
host=$HTTPD_DEST
413+
EOF
414+
'
415+
416+
test_expect_success 'access using bearer auth with invalid credentials' '
417+
test_when_finished "per_test_cleanup" &&
418+
419+
set_credential_reply get <<-EOF &&
420+
capability[]=authtype
421+
authtype=Bearer
422+
credential=incorrect-token
423+
EOF
424+
425+
# Basic base64(a-git-token)
426+
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
427+
Bearer YS1naXQtdG9rZW4=
428+
EOF
429+
430+
CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
431+
432+
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
433+
WWW-Authenticate: FooBar param1="value1" param2="value2"
434+
WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
435+
WWW-Authenticate: Basic realm="example.com"
436+
EOF
437+
438+
test_config_global credential.helper test-helper &&
439+
test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
440+
441+
expect_credential_query get <<-EOF &&
442+
capability[]=authtype
443+
protocol=http
444+
host=$HTTPD_DEST
445+
wwwauth[]=FooBar param1="value1" param2="value2"
446+
wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
447+
wwwauth[]=Basic realm="example.com"
448+
EOF
449+
450+
expect_credential_query erase <<-EOF
451+
capability[]=authtype
452+
authtype=Bearer
453+
credential=incorrect-token
454+
protocol=http
455+
host=$HTTPD_DEST
456+
wwwauth[]=FooBar param1="value1" param2="value2"
457+
wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
458+
wwwauth[]=Basic realm="example.com"
459+
EOF
460+
'
461+
329462
test_done

0 commit comments

Comments
 (0)