@matheusbsbs / matheus-bagliano / commits / 88238e02d5
Fork 0

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>

Jorge Lopez Silva committed Mar 4, 2020 at 18:40 UTC 88238e02d545dedd832cec9d9ed4ca877c44da19
2 files changed +79 -5
Documentation/config/http.txt
+17
@@ -29,6 +29,23 @@ http.proxyAuthMethod::
29 * `ntlm` - NTLM authentication (compare the --ntlm option of `curl(1)`)
30 --
31
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 +
49 http.emptyAuth::
50 Attempt authentication without seeking a username or password. This
51 can be used to attempt GSS-Negotiate authentication without specifying
http.c
+62 -5
@@ -86,6 +86,13 @@ static long curl_low_speed_time = -1;
86 static int curl_ftp_no_epsv;
87 static const char *curl_http_proxy;
88 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 +
96 static struct {
97 const char *name;
98 long curlauth_param;
@@ -365,6 +372,20 @@ static int http_options(const char *var, const char *value, void *cb)
372 if (!strcmp("http.proxyauthmethod", var))
373 return git_config_string(&http_proxy_authmethod, var, value);
374
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 +
389 if (!strcmp("http.cookiefile", var))
390 return git_config_pathname(&curl_cookie_file, var, value);
391 if (!strcmp("http.savecookies", var)) {
@@ -565,6 +586,21 @@ static int has_cert_password(void)
586 return 1;
587 }
588
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 +
604 #if LIBCURL_VERSION_NUM >= 0x071900
605 static void set_curl_keepalive(CURL *c)
606 {
@@ -924,8 +960,14 @@ static CURL *get_curl_handle(void)
960 #if LIBCURL_VERSION_NUM >= 0x073400
961 curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, NULL);
962 #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 + }
971
972 if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
973 curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
@@ -1018,9 +1060,18 @@ static CURL *get_curl_handle(void)
1060 CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
1061 #endif
1062 #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 + }
1075 #endif
1076 if (strstr(curl_http_proxy, "://"))
1077 credential_from_url(&proxy_auth, curl_http_proxy);
@@ -1230,6 +1281,12 @@ void http_cleanup(void)
1281 }
1282 ssl_cert_password_required = 0;
1283
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 +
1290 FREE_AND_NULL(cached_accept_language);
1291 }
1292