Skip to content

Commit

Permalink
http: use credential API to handle proxy authentication
Browse files Browse the repository at this point in the history
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 <gitster@pobox.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Helped-by: Elia Pinto <gitter.spiros@gmail.com>
Signed-off-by: Knut Franke <k.franke@science-computing.de>
Signed-off-by: Elia Pinto <gitter.spiros@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Knut Franke authored and Junio C Hamano committed Jan 26, 2016
1 parent ef97639 commit 372370f
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 3 deletions.
10 changes: 7 additions & 3 deletions Documentation/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1596,9 +1596,13 @@ help.htmlPath::

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

http.proxyAuthMethod::
Set the method with which to authenticate against the HTTP proxy. This
Expand Down
77 changes: 77 additions & 0 deletions http.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ static struct {
* here, too
*/
};
static struct credential proxy_auth = CREDENTIAL_INIT;
static const char *curl_proxyuserpwd;
static const char *curl_cookie_file;
static int curl_save_cookies;
struct credential http_auth = CREDENTIAL_INIT;
Expand Down Expand Up @@ -177,6 +179,9 @@ static void finish_active_slot(struct active_request_slot *slot)
#else
slot->results->auth_avail = 0;
#endif

curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CONNECTCODE,
&slot->results->http_connectcode);
}

/* Run callback if appropriate */
Expand Down Expand Up @@ -334,8 +339,32 @@ static void var_override(const char **var, char *value)
}
}

static void set_proxyauth_name_password(CURL *result)
{
#if LIBCURL_VERSION_NUM >= 0x071301
curl_easy_setopt(result, CURLOPT_PROXYUSERNAME,
proxy_auth.username);
curl_easy_setopt(result, CURLOPT_PROXYPASSWORD,
proxy_auth.password);
#else
struct strbuf s = STRBUF_INIT;

strbuf_addstr_urlencode(&s, proxy_auth.username, 1);
strbuf_addch(&s, ':');
strbuf_addstr_urlencode(&s, proxy_auth.password, 1);
curl_proxyuserpwd = strbuf_detach(&s, NULL);
curl_easy_setopt(result, CURLOPT_PROXYUSERPWD, curl_proxyuserpwd);
#endif
}

static void init_curl_proxy_auth(CURL *result)
{
if (proxy_auth.username) {
if (!proxy_auth.password)
credential_fill(&proxy_auth);
set_proxyauth_name_password(result);
}

var_override(&http_proxy_authmethod, getenv("GIT_HTTP_PROXY_AUTHMETHOD"));

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

/*
* CURL also examines these variables as a fallback; but we need to query
* them here in order to decide whether to prompt for missing password (cf.
* init_curl_proxy_auth()).
*
* Unlike many other common environment variables, these are historically
* lowercase only. It appears that CURL did not know this and implemented
* only uppercase variants, which was later corrected to take both - with
* the exception of http_proxy, which is lowercase only also in CURL. As
* the lowercase versions are the historical quasi-standard, they take
* precedence here, as in CURL.
*/
if (!curl_http_proxy) {
if (!strcmp(http_auth.protocol, "https")) {
var_override(&curl_http_proxy, getenv("HTTPS_PROXY"));
var_override(&curl_http_proxy, getenv("https_proxy"));
} else {
var_override(&curl_http_proxy, getenv("http_proxy"));
}
if (!curl_http_proxy) {
var_override(&curl_http_proxy, getenv("ALL_PROXY"));
var_override(&curl_http_proxy, getenv("all_proxy"));
}
}

if (curl_http_proxy) {
curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
#if LIBCURL_VERSION_NUM >= 0x071800
Expand All @@ -530,6 +584,16 @@ static CURL *get_curl_handle(void)
curl_easy_setopt(result,
CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
#endif
if (strstr(curl_http_proxy, "://"))
credential_from_url(&proxy_auth, curl_http_proxy);
else {
struct strbuf url = STRBUF_INIT;
strbuf_addf(&url, "http://%s", curl_http_proxy);
credential_from_url(&proxy_auth, url.buf);
strbuf_release(&url);
}

curl_easy_setopt(result, CURLOPT_PROXY, proxy_auth.host);
}
init_curl_proxy_auth(result);

Expand Down Expand Up @@ -673,6 +737,15 @@ void http_cleanup(void)
curl_http_proxy = NULL;
}

if (proxy_auth.password) {
memset(proxy_auth.password, 0, strlen(proxy_auth.password));
free(proxy_auth.password);
proxy_auth.password = NULL;
}

free((void *)curl_proxyuserpwd);
curl_proxyuserpwd = NULL;

free((void *)http_proxy_authmethod);
http_proxy_authmethod = NULL;

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

if (results->curl_result == CURLE_OK) {
credential_approve(&http_auth);
if (proxy_auth.password)
credential_approve(&proxy_auth);
return HTTP_OK;
} else if (missing_target(results))
return HTTP_MISSING_TARGET;
Expand All @@ -1019,6 +1094,8 @@ static int handle_curl_result(struct slot_results *results)
return HTTP_REAUTH;
}
} else {
if (results->http_connectcode == 407)
credential_reject(&proxy_auth);
#if LIBCURL_VERSION_NUM >= 0x070c00
if (!curl_errorstr[0])
strlcpy(curl_errorstr,
Expand Down
1 change: 1 addition & 0 deletions http.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ struct slot_results {
CURLcode curl_result;
long http_code;
long auth_avail;
long http_connectcode;
};

struct active_request_slot {
Expand Down

0 comments on commit 372370f

Please sign in to comment.