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 <jalopezsilva@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Jorge Lopez Silva 2020-03-04 18:40:05 +00:00 committed by Junio C Hamano
parent d0654dc308
commit 88238e02d5
2 changed files with 79 additions and 5 deletions

View File

@ -29,6 +29,23 @@ http.proxyAuthMethod::
* `ntlm` - NTLM authentication (compare the --ntlm option of `curl(1)`) * `ntlm` - NTLM authentication (compare the --ntlm option of `curl(1)`)
-- --
http.proxySSLCert::
The pathname of a file that stores a client certificate to use to authenticate
with an HTTPS proxy.
http.proxySSLKey::
The pathname of a file that stores a private key to use to authenticate with
an HTTPS proxy.
http.proxySSLCertPasswordProtected::
Enable Git's password prompt for the proxy SSL certificate. Otherwise OpenSSL
will prompt the user, possibly many times, if the certificate or private key
is encrypted.
http.proxySSLCAInfo::
Pathname to the file containing the certificate bundle that should be used to
verify the proxy with when using an HTTPS proxy.
http.emptyAuth:: http.emptyAuth::
Attempt authentication without seeking a username or password. This Attempt authentication without seeking a username or password. This
can be used to attempt GSS-Negotiate authentication without specifying can be used to attempt GSS-Negotiate authentication without specifying

67
http.c
View File

@ -86,6 +86,13 @@ static long curl_low_speed_time = -1;
static int curl_ftp_no_epsv; static int curl_ftp_no_epsv;
static const char *curl_http_proxy; static const char *curl_http_proxy;
static const char *http_proxy_authmethod; static const char *http_proxy_authmethod;
static const char *http_proxy_ssl_cert;
static const char *http_proxy_ssl_key;
static const char *http_proxy_ssl_ca_info;
static struct credential proxy_cert_auth = CREDENTIAL_INIT;
static int proxy_ssl_cert_password_required;
static struct { static struct {
const char *name; const char *name;
long curlauth_param; long curlauth_param;
@ -365,6 +372,20 @@ static int http_options(const char *var, const char *value, void *cb)
if (!strcmp("http.proxyauthmethod", var)) if (!strcmp("http.proxyauthmethod", var))
return git_config_string(&http_proxy_authmethod, var, value); return git_config_string(&http_proxy_authmethod, var, value);
if (!strcmp("http.proxysslcert", var))
return git_config_string(&http_proxy_ssl_cert, var, value);
if (!strcmp("http.proxysslkey", var))
return git_config_string(&http_proxy_ssl_key, var, value);
if (!strcmp("http.proxysslcainfo", var))
return git_config_string(&http_proxy_ssl_ca_info, var, value);
if (!strcmp("http.proxysslcertpasswordprotected", var)) {
proxy_ssl_cert_password_required = git_config_bool(var, value);
return 0;
}
if (!strcmp("http.cookiefile", var)) if (!strcmp("http.cookiefile", var))
return git_config_pathname(&curl_cookie_file, var, value); return git_config_pathname(&curl_cookie_file, var, value);
if (!strcmp("http.savecookies", var)) { if (!strcmp("http.savecookies", var)) {
@ -565,6 +586,21 @@ static int has_cert_password(void)
return 1; return 1;
} }
#if LIBCURL_VERSION_NUM >= 0x073400
static int has_proxy_cert_password(void)
{
if (http_proxy_ssl_cert == NULL || proxy_ssl_cert_password_required != 1)
return 0;
if (!proxy_cert_auth.password) {
proxy_cert_auth.protocol = xstrdup("cert");
proxy_cert_auth.username = xstrdup("");
proxy_cert_auth.path = xstrdup(http_proxy_ssl_cert);
credential_fill(&proxy_cert_auth);
}
return 1;
}
#endif
#if LIBCURL_VERSION_NUM >= 0x071900 #if LIBCURL_VERSION_NUM >= 0x071900
static void set_curl_keepalive(CURL *c) static void set_curl_keepalive(CURL *c)
{ {
@ -924,8 +960,14 @@ static CURL *get_curl_handle(void)
#if LIBCURL_VERSION_NUM >= 0x073400 #if LIBCURL_VERSION_NUM >= 0x073400
curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, NULL); curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, NULL);
#endif #endif
} else if (ssl_cainfo != NULL) } else if (ssl_cainfo != NULL || http_proxy_ssl_ca_info != NULL) {
curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo); if (ssl_cainfo != NULL)
curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
#if LIBCURL_VERSION_NUM >= 0x073400
if (http_proxy_ssl_ca_info != NULL)
curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, http_proxy_ssl_ca_info);
#endif
}
if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) { if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT, curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
@ -1018,9 +1060,18 @@ static CURL *get_curl_handle(void)
CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
#endif #endif
#if LIBCURL_VERSION_NUM >= 0x073400 #if LIBCURL_VERSION_NUM >= 0x073400
else if (starts_with(curl_http_proxy, "https")) else if (starts_with(curl_http_proxy, "https")) {
curl_easy_setopt(result, curl_easy_setopt(result, CURLOPT_PROXYTYPE, CURLPROXY_HTTPS);
CURLOPT_PROXYTYPE, CURLPROXY_HTTPS);
if (http_proxy_ssl_cert)
curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT, http_proxy_ssl_cert);
if (http_proxy_ssl_key)
curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY, http_proxy_ssl_key);
if (has_proxy_cert_password())
curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD, proxy_cert_auth.password);
}
#endif #endif
if (strstr(curl_http_proxy, "://")) if (strstr(curl_http_proxy, "://"))
credential_from_url(&proxy_auth, curl_http_proxy); credential_from_url(&proxy_auth, curl_http_proxy);
@ -1230,6 +1281,12 @@ void http_cleanup(void)
} }
ssl_cert_password_required = 0; ssl_cert_password_required = 0;
if (proxy_cert_auth.password != NULL) {
memset(proxy_cert_auth.password, 0, strlen(proxy_cert_auth.password));
FREE_AND_NULL(proxy_cert_auth.password);
}
proxy_ssl_cert_password_required = 0;
FREE_AND_NULL(cached_accept_language); FREE_AND_NULL(cached_accept_language);
} }