From 88238e02d545dedd832cec9d9ed4ca877c44da19 Mon Sep 17 00:00:00 2001 From: Jorge Lopez Silva Date: Wed, 4 Mar 2020 18:40:05 +0000 Subject: [PATCH 1/2] 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 Signed-off-by: Junio C Hamano --- Documentation/config/http.txt | 17 +++++++++ http.c | 67 ++++++++++++++++++++++++++++++++--- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/Documentation/config/http.txt b/Documentation/config/http.txt index 5a32f5b0a5a9c3..71babe57c41302 100644 --- a/Documentation/config/http.txt +++ b/Documentation/config/http.txt @@ -29,6 +29,23 @@ http.proxyAuthMethod:: * `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:: Attempt authentication without seeking a username or password. This can be used to attempt GSS-Negotiate authentication without specifying diff --git a/http.c b/http.c index 5f348169c3cdca..649ee6c6db27ad 100644 --- a/http.c +++ b/http.c @@ -86,6 +86,13 @@ static long curl_low_speed_time = -1; static int curl_ftp_no_epsv; static const char *curl_http_proxy; 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 { const char *name; long curlauth_param; @@ -365,6 +372,20 @@ static int http_options(const char *var, const char *value, void *cb) if (!strcmp("http.proxyauthmethod", var)) 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)) return git_config_pathname(&curl_cookie_file, var, value); if (!strcmp("http.savecookies", var)) { @@ -565,6 +586,21 @@ static int has_cert_password(void) 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 static void set_curl_keepalive(CURL *c) { @@ -924,8 +960,14 @@ static CURL *get_curl_handle(void) #if LIBCURL_VERSION_NUM >= 0x073400 curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, NULL); #endif - } else if (ssl_cainfo != NULL) - curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo); + } else if (ssl_cainfo != NULL || http_proxy_ssl_ca_info != NULL) { + 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) { curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT, @@ -1018,9 +1060,18 @@ static CURL *get_curl_handle(void) CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); #endif #if LIBCURL_VERSION_NUM >= 0x073400 - else if (starts_with(curl_http_proxy, "https")) - curl_easy_setopt(result, - CURLOPT_PROXYTYPE, CURLPROXY_HTTPS); + else if (starts_with(curl_http_proxy, "https")) { + curl_easy_setopt(result, 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 if (strstr(curl_http_proxy, "://")) credential_from_url(&proxy_auth, curl_http_proxy); @@ -1230,6 +1281,12 @@ void http_cleanup(void) } 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); } From af026519c9cf82928be111974e89bc122765f13f Mon Sep 17 00:00:00 2001 From: Jorge Lopez Silva Date: Wed, 4 Mar 2020 18:40:06 +0000 Subject: [PATCH 2/2] http: add environment variable support for HTTPS proxies Add 4 environment variables that can be used to configure the proxy cert, proxy ssl key, the proxy cert password protected flag, and the CA info for the proxy. Documentation for the options was also updated. Signed-off-by: Jorge Lopez Silva Signed-off-by: Junio C Hamano --- Documentation/config/http.txt | 12 ++++++++---- http.c | 7 +++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Documentation/config/http.txt b/Documentation/config/http.txt index 71babe57c41302..a7a2725f300cd1 100644 --- a/Documentation/config/http.txt +++ b/Documentation/config/http.txt @@ -31,20 +31,24 @@ http.proxyAuthMethod:: http.proxySSLCert:: The pathname of a file that stores a client certificate to use to authenticate - with an HTTPS proxy. + with an HTTPS proxy. Can be overridden by the `GIT_PROXY_SSL_CERT` environment + variable. http.proxySSLKey:: The pathname of a file that stores a private key to use to authenticate with - an HTTPS proxy. + an HTTPS proxy. Can be overridden by the `GIT_PROXY_SSL_KEY` environment + variable. 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. + is encrypted. Can be overriden by the `GIT_PROXY_SSL_CERT_PASSWORD_PROTECTED` + environment variable. http.proxySSLCAInfo:: Pathname to the file containing the certificate bundle that should be used to - verify the proxy with when using an HTTPS proxy. + verify the proxy with when using an HTTPS proxy. Can be overriden by the + `GIT_PROXY_SSL_CAINFO` environment variable. http.emptyAuth:: Attempt authentication without seeking a username or password. This diff --git a/http.c b/http.c index 649ee6c6db27ad..6e804b938d4e92 100644 --- a/http.c +++ b/http.c @@ -1211,6 +1211,13 @@ void http_init(struct remote *remote, const char *url, int proactive_auth) max_requests = DEFAULT_MAX_REQUESTS; #endif + set_from_env(&http_proxy_ssl_cert, "GIT_PROXY_SSL_CERT"); + set_from_env(&http_proxy_ssl_key, "GIT_PROXY_SSL_KEY"); + set_from_env(&http_proxy_ssl_ca_info, "GIT_PROXY_SSL_CAINFO"); + + if (getenv("GIT_PROXY_SSL_CERT_PASSWORD_PROTECTED")) + proxy_ssl_cert_password_required = 1; + if (getenv("GIT_CURL_FTP_NO_EPSV")) curl_ftp_no_epsv = 1;