Skip to content

Commit 88238e0

Browse files
jalopezsilvagitster
authored andcommitted
http: add client cert support for HTTPS proxies
Git supports performing connections to HTTPS proxies, but we don't support doing mutual authentication with them (through TLS). Add the necessary options to be able to send a client certificate to the HTTPS proxy. A client certificate can provide an alternative way of authentication instead of using 'ProxyAuthorization' or other more common methods of authentication. Libcurl supports this functionality already, so changes are somewhat minimal. The feature is guarded by the first available libcurl version that supports these options. 4 configuration options are added and documented, cert, key, cert password protected and CA info. The CA info should be used to specify a different CA path to validate the HTTPS proxy cert. Signed-off-by: Jorge Lopez Silva <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent d0654dc commit 88238e0

File tree

2 files changed

+79
-5
lines changed

2 files changed

+79
-5
lines changed

Documentation/config/http.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,23 @@ http.proxyAuthMethod::
2929
* `ntlm` - NTLM authentication (compare the --ntlm option of `curl(1)`)
3030
--
3131

32+
http.proxySSLCert::
33+
The pathname of a file that stores a client certificate to use to authenticate
34+
with an HTTPS proxy.
35+
36+
http.proxySSLKey::
37+
The pathname of a file that stores a private key to use to authenticate with
38+
an HTTPS proxy.
39+
40+
http.proxySSLCertPasswordProtected::
41+
Enable Git's password prompt for the proxy SSL certificate. Otherwise OpenSSL
42+
will prompt the user, possibly many times, if the certificate or private key
43+
is encrypted.
44+
45+
http.proxySSLCAInfo::
46+
Pathname to the file containing the certificate bundle that should be used to
47+
verify the proxy with when using an HTTPS proxy.
48+
3249
http.emptyAuth::
3350
Attempt authentication without seeking a username or password. This
3451
can be used to attempt GSS-Negotiate authentication without specifying

http.c

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,13 @@ static long curl_low_speed_time = -1;
8686
static int curl_ftp_no_epsv;
8787
static const char *curl_http_proxy;
8888
static const char *http_proxy_authmethod;
89+
90+
static const char *http_proxy_ssl_cert;
91+
static const char *http_proxy_ssl_key;
92+
static const char *http_proxy_ssl_ca_info;
93+
static struct credential proxy_cert_auth = CREDENTIAL_INIT;
94+
static int proxy_ssl_cert_password_required;
95+
8996
static struct {
9097
const char *name;
9198
long curlauth_param;
@@ -365,6 +372,20 @@ static int http_options(const char *var, const char *value, void *cb)
365372
if (!strcmp("http.proxyauthmethod", var))
366373
return git_config_string(&http_proxy_authmethod, var, value);
367374

375+
if (!strcmp("http.proxysslcert", var))
376+
return git_config_string(&http_proxy_ssl_cert, var, value);
377+
378+
if (!strcmp("http.proxysslkey", var))
379+
return git_config_string(&http_proxy_ssl_key, var, value);
380+
381+
if (!strcmp("http.proxysslcainfo", var))
382+
return git_config_string(&http_proxy_ssl_ca_info, var, value);
383+
384+
if (!strcmp("http.proxysslcertpasswordprotected", var)) {
385+
proxy_ssl_cert_password_required = git_config_bool(var, value);
386+
return 0;
387+
}
388+
368389
if (!strcmp("http.cookiefile", var))
369390
return git_config_pathname(&curl_cookie_file, var, value);
370391
if (!strcmp("http.savecookies", var)) {
@@ -565,6 +586,21 @@ static int has_cert_password(void)
565586
return 1;
566587
}
567588

589+
#if LIBCURL_VERSION_NUM >= 0x073400
590+
static int has_proxy_cert_password(void)
591+
{
592+
if (http_proxy_ssl_cert == NULL || proxy_ssl_cert_password_required != 1)
593+
return 0;
594+
if (!proxy_cert_auth.password) {
595+
proxy_cert_auth.protocol = xstrdup("cert");
596+
proxy_cert_auth.username = xstrdup("");
597+
proxy_cert_auth.path = xstrdup(http_proxy_ssl_cert);
598+
credential_fill(&proxy_cert_auth);
599+
}
600+
return 1;
601+
}
602+
#endif
603+
568604
#if LIBCURL_VERSION_NUM >= 0x071900
569605
static void set_curl_keepalive(CURL *c)
570606
{
@@ -924,8 +960,14 @@ static CURL *get_curl_handle(void)
924960
#if LIBCURL_VERSION_NUM >= 0x073400
925961
curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, NULL);
926962
#endif
927-
} else if (ssl_cainfo != NULL)
928-
curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
963+
} else if (ssl_cainfo != NULL || http_proxy_ssl_ca_info != NULL) {
964+
if (ssl_cainfo != NULL)
965+
curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
966+
#if LIBCURL_VERSION_NUM >= 0x073400
967+
if (http_proxy_ssl_ca_info != NULL)
968+
curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, http_proxy_ssl_ca_info);
969+
#endif
970+
}
929971

930972
if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
931973
curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
@@ -1018,9 +1060,18 @@ static CURL *get_curl_handle(void)
10181060
CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
10191061
#endif
10201062
#if LIBCURL_VERSION_NUM >= 0x073400
1021-
else if (starts_with(curl_http_proxy, "https"))
1022-
curl_easy_setopt(result,
1023-
CURLOPT_PROXYTYPE, CURLPROXY_HTTPS);
1063+
else if (starts_with(curl_http_proxy, "https")) {
1064+
curl_easy_setopt(result, CURLOPT_PROXYTYPE, CURLPROXY_HTTPS);
1065+
1066+
if (http_proxy_ssl_cert)
1067+
curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT, http_proxy_ssl_cert);
1068+
1069+
if (http_proxy_ssl_key)
1070+
curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY, http_proxy_ssl_key);
1071+
1072+
if (has_proxy_cert_password())
1073+
curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD, proxy_cert_auth.password);
1074+
}
10241075
#endif
10251076
if (strstr(curl_http_proxy, "://"))
10261077
credential_from_url(&proxy_auth, curl_http_proxy);
@@ -1230,6 +1281,12 @@ void http_cleanup(void)
12301281
}
12311282
ssl_cert_password_required = 0;
12321283

1284+
if (proxy_cert_auth.password != NULL) {
1285+
memset(proxy_cert_auth.password, 0, strlen(proxy_cert_auth.password));
1286+
FREE_AND_NULL(proxy_cert_auth.password);
1287+
}
1288+
proxy_ssl_cert_password_required = 0;
1289+
12331290
FREE_AND_NULL(cached_accept_language);
12341291
}
12351292

0 commit comments

Comments
 (0)