Skip to content

Commit 372370f

Browse files
Knut Frankegitster
authored andcommitted
http: use credential API to handle proxy authentication
Currently, the only way to pass proxy credentials to curl is by including them in the proxy URL. Usually, this means they will end up on disk unencrypted, one way or another (by inclusion in ~/.gitconfig, shell profile or history). Since proxy authentication often uses a domain user, credentials can be security sensitive; therefore, a safer way of passing credentials is desirable. If the configured proxy contains a username but not a password, query the credential API for one. Also, make sure we approve/reject proxy credentials properly. For consistency reasons, add parsing of http_proxy/https_proxy/all_proxy environment variables, which would otherwise be evaluated as a fallback by curl. Without this, we would have different semantics for git configuration and environment variables. Helped-by: Junio C Hamano <[email protected]> Helped-by: Eric Sunshine <[email protected]> Helped-by: Elia Pinto <[email protected]> Signed-off-by: Knut Franke <[email protected]> Signed-off-by: Elia Pinto <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent ef97639 commit 372370f

File tree

3 files changed

+85
-3
lines changed

3 files changed

+85
-3
lines changed

Documentation/config.txt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1596,9 +1596,13 @@ help.htmlPath::
15961596

15971597
http.proxy::
15981598
Override the HTTP proxy, normally configured using the 'http_proxy',
1599-
'https_proxy', and 'all_proxy' environment variables (see
1600-
`curl(1)`). This can be overridden on a per-remote basis; see
1601-
remote.<name>.proxy
1599+
'https_proxy', and 'all_proxy' environment variables (see `curl(1)`). In
1600+
addition to the syntax understood by curl, it is possible to specify a
1601+
proxy string with a user name but no password, in which case git will
1602+
attempt to acquire one in the same way it does for other credentials. See
1603+
linkgit:gitcredentials[7] for more information. The syntax thus is
1604+
'[protocol://][user[:password]@]proxyhost[:port]'. This can be overridden
1605+
on a per-remote basis; see remote.<name>.proxy
16021606

16031607
http.proxyAuthMethod::
16041608
Set the method with which to authenticate against the HTTP proxy. This

http.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ static struct {
8080
* here, too
8181
*/
8282
};
83+
static struct credential proxy_auth = CREDENTIAL_INIT;
84+
static const char *curl_proxyuserpwd;
8385
static const char *curl_cookie_file;
8486
static int curl_save_cookies;
8587
struct credential http_auth = CREDENTIAL_INIT;
@@ -177,6 +179,9 @@ static void finish_active_slot(struct active_request_slot *slot)
177179
#else
178180
slot->results->auth_avail = 0;
179181
#endif
182+
183+
curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CONNECTCODE,
184+
&slot->results->http_connectcode);
180185
}
181186

182187
/* Run callback if appropriate */
@@ -334,8 +339,32 @@ static void var_override(const char **var, char *value)
334339
}
335340
}
336341

342+
static void set_proxyauth_name_password(CURL *result)
343+
{
344+
#if LIBCURL_VERSION_NUM >= 0x071301
345+
curl_easy_setopt(result, CURLOPT_PROXYUSERNAME,
346+
proxy_auth.username);
347+
curl_easy_setopt(result, CURLOPT_PROXYPASSWORD,
348+
proxy_auth.password);
349+
#else
350+
struct strbuf s = STRBUF_INIT;
351+
352+
strbuf_addstr_urlencode(&s, proxy_auth.username, 1);
353+
strbuf_addch(&s, ':');
354+
strbuf_addstr_urlencode(&s, proxy_auth.password, 1);
355+
curl_proxyuserpwd = strbuf_detach(&s, NULL);
356+
curl_easy_setopt(result, CURLOPT_PROXYUSERPWD, curl_proxyuserpwd);
357+
#endif
358+
}
359+
337360
static void init_curl_proxy_auth(CURL *result)
338361
{
362+
if (proxy_auth.username) {
363+
if (!proxy_auth.password)
364+
credential_fill(&proxy_auth);
365+
set_proxyauth_name_password(result);
366+
}
367+
339368
var_override(&http_proxy_authmethod, getenv("GIT_HTTP_PROXY_AUTHMETHOD"));
340369

341370
#if LIBCURL_VERSION_NUM >= 0x070a07 /* CURLOPT_PROXYAUTH and CURLAUTH_ANY */
@@ -517,6 +546,31 @@ static CURL *get_curl_handle(void)
517546
curl_easy_setopt(result, CURLOPT_USE_SSL, CURLUSESSL_TRY);
518547
#endif
519548

549+
/*
550+
* CURL also examines these variables as a fallback; but we need to query
551+
* them here in order to decide whether to prompt for missing password (cf.
552+
* init_curl_proxy_auth()).
553+
*
554+
* Unlike many other common environment variables, these are historically
555+
* lowercase only. It appears that CURL did not know this and implemented
556+
* only uppercase variants, which was later corrected to take both - with
557+
* the exception of http_proxy, which is lowercase only also in CURL. As
558+
* the lowercase versions are the historical quasi-standard, they take
559+
* precedence here, as in CURL.
560+
*/
561+
if (!curl_http_proxy) {
562+
if (!strcmp(http_auth.protocol, "https")) {
563+
var_override(&curl_http_proxy, getenv("HTTPS_PROXY"));
564+
var_override(&curl_http_proxy, getenv("https_proxy"));
565+
} else {
566+
var_override(&curl_http_proxy, getenv("http_proxy"));
567+
}
568+
if (!curl_http_proxy) {
569+
var_override(&curl_http_proxy, getenv("ALL_PROXY"));
570+
var_override(&curl_http_proxy, getenv("all_proxy"));
571+
}
572+
}
573+
520574
if (curl_http_proxy) {
521575
curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
522576
#if LIBCURL_VERSION_NUM >= 0x071800
@@ -530,6 +584,16 @@ static CURL *get_curl_handle(void)
530584
curl_easy_setopt(result,
531585
CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
532586
#endif
587+
if (strstr(curl_http_proxy, "://"))
588+
credential_from_url(&proxy_auth, curl_http_proxy);
589+
else {
590+
struct strbuf url = STRBUF_INIT;
591+
strbuf_addf(&url, "http://%s", curl_http_proxy);
592+
credential_from_url(&proxy_auth, url.buf);
593+
strbuf_release(&url);
594+
}
595+
596+
curl_easy_setopt(result, CURLOPT_PROXY, proxy_auth.host);
533597
}
534598
init_curl_proxy_auth(result);
535599

@@ -673,6 +737,15 @@ void http_cleanup(void)
673737
curl_http_proxy = NULL;
674738
}
675739

740+
if (proxy_auth.password) {
741+
memset(proxy_auth.password, 0, strlen(proxy_auth.password));
742+
free(proxy_auth.password);
743+
proxy_auth.password = NULL;
744+
}
745+
746+
free((void *)curl_proxyuserpwd);
747+
curl_proxyuserpwd = NULL;
748+
676749
free((void *)http_proxy_authmethod);
677750
http_proxy_authmethod = NULL;
678751

@@ -1005,6 +1078,8 @@ static int handle_curl_result(struct slot_results *results)
10051078

10061079
if (results->curl_result == CURLE_OK) {
10071080
credential_approve(&http_auth);
1081+
if (proxy_auth.password)
1082+
credential_approve(&proxy_auth);
10081083
return HTTP_OK;
10091084
} else if (missing_target(results))
10101085
return HTTP_MISSING_TARGET;
@@ -1019,6 +1094,8 @@ static int handle_curl_result(struct slot_results *results)
10191094
return HTTP_REAUTH;
10201095
}
10211096
} else {
1097+
if (results->http_connectcode == 407)
1098+
credential_reject(&proxy_auth);
10221099
#if LIBCURL_VERSION_NUM >= 0x070c00
10231100
if (!curl_errorstr[0])
10241101
strlcpy(curl_errorstr,

http.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ struct slot_results {
5454
CURLcode curl_result;
5555
long http_code;
5656
long auth_avail;
57+
long http_connectcode;
5758
};
5859

5960
struct active_request_slot {

0 commit comments

Comments
 (0)