Skip to content

Commit fe5ba89

Browse files
committed
Merge branch 'bc/http-proactive-auth'
The http transport can now be told to send request with authentication material without first getting a 401 response. * bc/http-proactive-auth: http: allow authenticating proactively
2 parents 12d49fd + 610cbc1 commit fe5ba89

File tree

3 files changed

+192
-6
lines changed

3 files changed

+192
-6
lines changed

Documentation/config/http.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,26 @@ http.emptyAuth::
5656
a username in the URL, as libcurl normally requires a username for
5757
authentication.
5858

59+
http.proactiveAuth::
60+
Attempt authentication without first making an unauthenticated attempt and
61+
receiving a 401 response. This can be used to ensure that all requests are
62+
authenticated. If `http.emptyAuth` is set to true, this value has no effect.
63+
+
64+
If the credential helper used specifies an authentication scheme (i.e., via the
65+
`authtype` field), that value will be used; if a username and password is
66+
provided without a scheme, then Basic authentication is used. The value of the
67+
option determines the scheme requested from the helper. Possible values are:
68+
+
69+
--
70+
* `basic` - Request Basic authentication from the helper.
71+
* `auto` - Allow the helper to pick an appropriate scheme.
72+
* `none` - Disable proactive authentication.
73+
--
74+
+
75+
Note that TLS should always be used with this configuration, since otherwise it
76+
is easy to accidentally expose plaintext credentials if Basic authentication
77+
is selected.
78+
5979
http.delegation::
6080
Control GSSAPI credential delegation. The delegation is disabled
6181
by default in libcurl since version 7.21.7. Set parameter to tell

http.c

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,19 @@ static struct {
108108
};
109109
#endif
110110

111+
enum proactive_auth {
112+
PROACTIVE_AUTH_NONE = 0,
113+
PROACTIVE_AUTH_IF_CREDENTIALS,
114+
PROACTIVE_AUTH_AUTO,
115+
PROACTIVE_AUTH_BASIC,
116+
};
117+
111118
static struct credential proxy_auth = CREDENTIAL_INIT;
112119
static const char *curl_proxyuserpwd;
113120
static char *curl_cookie_file;
114121
static int curl_save_cookies;
115122
struct credential http_auth = CREDENTIAL_INIT;
116-
static int http_proactive_auth;
123+
static enum proactive_auth http_proactive_auth;
117124
static char *user_agent;
118125
static int curl_empty_auth = -1;
119126

@@ -148,6 +155,12 @@ static int http_schannel_check_revoke = 1;
148155
*/
149156
static int http_schannel_use_ssl_cainfo;
150157

158+
static int always_auth_proactively(void)
159+
{
160+
return http_proactive_auth != PROACTIVE_AUTH_NONE &&
161+
http_proactive_auth != PROACTIVE_AUTH_IF_CREDENTIALS;
162+
}
163+
151164
size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
152165
{
153166
size_t size = eltsize * nmemb;
@@ -539,6 +552,20 @@ static int http_options(const char *var, const char *value,
539552
return 0;
540553
}
541554

555+
if (!strcmp("http.proactiveauth", var)) {
556+
if (!value)
557+
return config_error_nonbool(var);
558+
if (!strcmp(value, "auto"))
559+
http_proactive_auth = PROACTIVE_AUTH_AUTO;
560+
else if (!strcmp(value, "basic"))
561+
http_proactive_auth = PROACTIVE_AUTH_BASIC;
562+
else if (!strcmp(value, "none"))
563+
http_proactive_auth = PROACTIVE_AUTH_NONE;
564+
else
565+
warning(_("Unknown value for http.proactiveauth"));
566+
return 0;
567+
}
568+
542569
/* Fall back on the default ones */
543570
return git_default_config(var, value, ctx, data);
544571
}
@@ -580,14 +607,29 @@ static void init_curl_http_auth(CURL *result)
580607
{
581608
if ((!http_auth.username || !*http_auth.username) &&
582609
(!http_auth.credential || !*http_auth.credential)) {
583-
if (curl_empty_auth_enabled())
610+
int empty_auth = curl_empty_auth_enabled();
611+
if ((empty_auth != -1 && !always_auth_proactively()) || empty_auth == 1) {
584612
curl_easy_setopt(result, CURLOPT_USERPWD, ":");
585-
return;
613+
return;
614+
} else if (!always_auth_proactively()) {
615+
return;
616+
} else if (http_proactive_auth == PROACTIVE_AUTH_BASIC) {
617+
strvec_push(&http_auth.wwwauth_headers, "Basic");
618+
}
586619
}
587620

588621
credential_fill(&http_auth, 1);
589622

590623
if (http_auth.password) {
624+
if (always_auth_proactively()) {
625+
/*
626+
* We got a credential without an authtype and we don't
627+
* know what's available. Since our only two options at
628+
* the moment are auto (which defaults to basic) and
629+
* basic, use basic for now.
630+
*/
631+
curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
632+
}
591633
curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
592634
curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
593635
}
@@ -1050,7 +1092,7 @@ static CURL *get_curl_handle(void)
10501092
#endif
10511093
}
10521094

1053-
if (http_proactive_auth)
1095+
if (http_proactive_auth != PROACTIVE_AUTH_NONE)
10541096
init_curl_http_auth(result);
10551097

10561098
if (getenv("GIT_SSL_VERSION"))
@@ -1294,7 +1336,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
12941336
if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
12951337
die("curl_global_init failed");
12961338

1297-
http_proactive_auth = proactive_auth;
1339+
if (proactive_auth && http_proactive_auth == PROACTIVE_AUTH_NONE)
1340+
http_proactive_auth = PROACTIVE_AUTH_IF_CREDENTIALS;
12981341

12991342
if (remote && remote->http_proxy)
13001343
curl_http_proxy = xstrdup(remote->http_proxy);
@@ -1790,6 +1833,8 @@ static int handle_curl_result(struct slot_results *results)
17901833
return HTTP_REAUTH;
17911834
}
17921835
credential_reject(&http_auth);
1836+
if (always_auth_proactively())
1837+
http_proactive_auth = PROACTIVE_AUTH_NONE;
17931838
return HTTP_NOAUTH;
17941839
} else {
17951840
http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;
@@ -2186,7 +2231,12 @@ static int http_request_reauth(const char *url,
21862231
struct http_get_options *options)
21872232
{
21882233
int i = 3;
2189-
int ret = http_request(url, result, target, options);
2234+
int ret;
2235+
2236+
if (always_auth_proactively())
2237+
credential_fill(&http_auth, 1);
2238+
2239+
ret = http_request(url, result, target, options);
21902240

21912241
if (ret != HTTP_OK && ret != HTTP_REAUTH)
21922242
return ret;

t/t5563-simple-http-auth.sh

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,122 @@ test_expect_success 'access using basic auth invalid credentials' '
178178
EOF
179179
'
180180

181+
test_expect_success 'access using basic proactive auth' '
182+
test_when_finished "per_test_cleanup" &&
183+
184+
set_credential_reply get <<-EOF &&
185+
username=alice
186+
password=secret-passwd
187+
EOF
188+
189+
# Basic base64(alice:secret-passwd)
190+
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
191+
id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
192+
EOF
193+
194+
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
195+
id=1 status=200
196+
id=default status=403
197+
EOF
198+
199+
test_config_global credential.helper test-helper &&
200+
test_config_global http.proactiveAuth basic &&
201+
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
202+
203+
expect_credential_query get <<-EOF &&
204+
capability[]=authtype
205+
capability[]=state
206+
protocol=http
207+
host=$HTTPD_DEST
208+
wwwauth[]=Basic
209+
EOF
210+
211+
expect_credential_query store <<-EOF
212+
protocol=http
213+
host=$HTTPD_DEST
214+
username=alice
215+
password=secret-passwd
216+
EOF
217+
'
218+
219+
test_expect_success 'access using auto proactive auth with basic default' '
220+
test_when_finished "per_test_cleanup" &&
221+
222+
set_credential_reply get <<-EOF &&
223+
username=alice
224+
password=secret-passwd
225+
EOF
226+
227+
# Basic base64(alice:secret-passwd)
228+
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
229+
id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
230+
EOF
231+
232+
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
233+
id=1 status=200
234+
id=default status=403
235+
EOF
236+
237+
test_config_global credential.helper test-helper &&
238+
test_config_global http.proactiveAuth auto &&
239+
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
240+
241+
expect_credential_query get <<-EOF &&
242+
capability[]=authtype
243+
capability[]=state
244+
protocol=http
245+
host=$HTTPD_DEST
246+
EOF
247+
248+
expect_credential_query store <<-EOF
249+
protocol=http
250+
host=$HTTPD_DEST
251+
username=alice
252+
password=secret-passwd
253+
EOF
254+
'
255+
256+
test_expect_success 'access using auto proactive auth with authtype from credential helper' '
257+
test_when_finished "per_test_cleanup" &&
258+
259+
set_credential_reply get <<-EOF &&
260+
capability[]=authtype
261+
authtype=Bearer
262+
credential=YS1naXQtdG9rZW4=
263+
EOF
264+
265+
# Basic base64(a-git-token)
266+
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
267+
id=1 creds=Bearer YS1naXQtdG9rZW4=
268+
EOF
269+
270+
CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
271+
272+
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
273+
id=1 status=200
274+
id=default status=403
275+
EOF
276+
277+
test_config_global credential.helper test-helper &&
278+
test_config_global http.proactiveAuth auto &&
279+
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
280+
281+
expect_credential_query get <<-EOF &&
282+
capability[]=authtype
283+
capability[]=state
284+
protocol=http
285+
host=$HTTPD_DEST
286+
EOF
287+
288+
expect_credential_query store <<-EOF
289+
capability[]=authtype
290+
authtype=Bearer
291+
credential=YS1naXQtdG9rZW4=
292+
protocol=http
293+
host=$HTTPD_DEST
294+
EOF
295+
'
296+
181297
test_expect_success 'access using basic auth with extra challenges' '
182298
test_when_finished "per_test_cleanup" &&
183299

0 commit comments

Comments
 (0)