From 20703d78c30613b89dd394ac9a435daed0a31ad4 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 2 Aug 2017 22:10:06 +0200 Subject: [PATCH 1/3] http: add support for selecting SSL backends at runtime The Pull Request at https://github.com/curl/curl/pull/1601 adds support for choosing the SSL backend at runtime to cURL, and will hopefully be merged before version 7.56.0 comes out. Git for Windows will ship with those patches backported to 7.54.1 (and come August 9th, 2017, 7.55.0 and later). This patch adds the Git side of that feature: by setting http.sslBackend to "openssl" or "schannel", Git for Windows can now choose the SSL backend at runtime. This comes in handy because Secure Channel ("schannel") is the native Windows solution, accessing the Windows Credential Store, thereby allowing for enterprise-wide management of certificates. For historical reasons, Git for Windows needs to support OpenSSL still, as it has previously been the only supported SSL backend in Git for Windows for almost a decade. Signed-off-by: Johannes Schindelin --- Documentation/config.txt | 5 +++++ http.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index eb66a11975..c569e72859 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2236,6 +2236,11 @@ http.sslCAPath:: with when fetching or pushing over HTTPS. Can be overridden by the `GIT_SSL_CAPATH` environment variable. +http.sslBackend:: + Name of the SSL backend to use (e.g. "openssl" or "schannel"). + This option is ignored if cURL lacks support for choosing the SSL + backend at runtime. + http.pinnedpubkey:: Public key of the https service. It may either be the filename of a PEM or DER encoded public key file or a string starting with diff --git a/http.c b/http.c index 4162860ee3..fedfb2a207 100644 --- a/http.c +++ b/http.c @@ -155,6 +155,8 @@ static struct active_request_slot *active_queue_head; static char *cached_accept_language; +static char *http_ssl_backend; + size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_) { size_t size = eltsize * nmemb; @@ -302,6 +304,12 @@ static int http_options(const char *var, const char *value, void *cb) curl_ssl_try = git_config_bool(var, value); return 0; } + if (!strcmp("http.sslbackend", var)) { + free(http_ssl_backend); + http_ssl_backend = xstrdup_or_null(value); + return 0; + } + if (!strcmp("http.minsessions", var)) { min_curl_sessions = git_config_int(var, value); #ifndef USE_CURL_MULTI @@ -995,6 +1003,33 @@ void http_init(struct remote *remote, const char *url, int proactive_auth) git_config(urlmatch_config_entry, &config); free(normalized_url); +#if LIBCURL_VERSION_NUM >= 0x073800 + if (http_ssl_backend) { + const curl_ssl_backend **backends; + struct strbuf buf = STRBUF_INIT; + int i; + + switch (curl_global_sslset(-1, http_ssl_backend, &backends)) { + case CURLSSLSET_UNKNOWN_BACKEND: + strbuf_addf(&buf, _("Unsupported SSL backend '%s'. " + "Supported SSL backends:"), + http_ssl_backend); + for (i = 0; backends[i]; i++) + strbuf_addf(&buf, "\n\t%s", backends[i]->name); + die("%s", buf.buf); + case CURLSSLSET_NO_BACKENDS: + die(_("Could not set SSL backend to '%s': " + "cURL was built without SSL backends"), + http_ssl_backend); + case CURLSSLSET_TOO_LATE: + die(_("Could not set SSL backend to '%s': already set"), + http_ssl_backend); + case CURLSSLSET_OK: + break; /* Okay! */ + } + } +#endif + if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) die("curl_global_init failed"); From b913a7056cf3aca103c8a1a840db6548d8a5462c Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Tue, 23 Jan 2018 13:20:55 +1100 Subject: [PATCH 2/3] http: add support for disabling SSL revocation checks in cURL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for a new http.schannelCheckRevoke config value. This config value is only used if http.sslBackend is set to "schannel", which forces cURL to use the Windows Certificate Store when validating server certificates associated with a remote server. This config value should only be set to "false" if you are in an environment where revocation checks are blocked by the network, with no alternative options. This is only supported in cURL 7.44 or later. Note: an earlier iteration tried to use the config setting http.schannel.checkRevoke, but the http.* config settings can be limited to specific URLs via http..* (which would mistake `schannel` for a URL). Helped by Agustín Martín Barbero. Signed-off-by: Brendan Forster Signed-off-by: Johannes Schindelin --- Documentation/config.txt | 8 ++++++++ http.c | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index c569e72859..e107f4c1e2 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2241,6 +2241,14 @@ http.sslBackend:: This option is ignored if cURL lacks support for choosing the SSL backend at runtime. +http.schannelCheckRevoke:: + Used to enforce or disable certificate revocation checks in cURL + when http.sslBackend is set to "schannel". Defaults to `true` if + unset. Only necessary to disable this if Git consistently errors + and the message is about checking the revocation status of a + certificate. This option is ignored if cURL lacks support for + setting the relevant SSL option at runtime. + http.pinnedpubkey:: Public key of the https service. It may either be the filename of a PEM or DER encoded public key file or a string starting with diff --git a/http.c b/http.c index fedfb2a207..2ce2cd447b 100644 --- a/http.c +++ b/http.c @@ -157,6 +157,8 @@ static char *cached_accept_language; static char *http_ssl_backend; +static int http_schannel_check_revoke = 1; + size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_) { size_t size = eltsize * nmemb; @@ -310,6 +312,11 @@ static int http_options(const char *var, const char *value, void *cb) return 0; } + if (!strcmp("http.schannelcheckrevoke", var)) { + http_schannel_check_revoke = git_config_bool(var, value); + return 0; + } + if (!strcmp("http.minsessions", var)) { min_curl_sessions = git_config_int(var, value); #ifndef USE_CURL_MULTI @@ -811,6 +818,16 @@ static CURL *get_curl_handle(void) } #endif + if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) && + !http_schannel_check_revoke) { +#if LIBCURL_VERSION_NUM >= 0x072c00 + curl_easy_setopt(result, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE); +#else + warning("CURLSSLOPT_NO_REVOKE not applied to curl SSL options because\n" + "your curl version is too old (>= 7.44.0)"); +#endif + } + if (http_proactive_auth) init_curl_http_auth(result); From cbea1437734f93a87f0d392e2b0cf92a4c8e33e1 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 14 Jun 2018 14:01:58 +0200 Subject: [PATCH 3/3] http: when using Secure Channel, ignore sslCAInfo by default As of cURL v7.60.0, the Secure Channel backend can use the certificate bundle provided via `http.sslCAInfo`, but that would override the Windows Certificate Store. Since this is not desirable by default, let's tell Git to not ask cURL to use that bundle by default when the `schannel` backend was configured via `http.sslBackend`, unless `http.schannelUseSSLCAInfo` overrides this behavior. Signed-off-by: Johannes Schindelin --- Documentation/config.txt | 8 ++++++++ http.c | 19 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index e107f4c1e2..56a1104469 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2249,6 +2249,14 @@ http.schannelCheckRevoke:: certificate. This option is ignored if cURL lacks support for setting the relevant SSL option at runtime. +http.schannelUseSSLCAInfo:: + As of cURL v7.60.0, the Secure Channel backend can use the + certificate bundle provided via `http.sslCAInfo`, but that would + override the Windows Certificate Store. Since this is not desirable + by default, Git will tell cURL not to use that bundle by default + when the `schannel` backend was configured via `http.sslBackend`, + unless `http.schannelUseSSLCAInfo` overrides this behavior. + http.pinnedpubkey:: Public key of the https service. It may either be the filename of a PEM or DER encoded public key file or a string starting with diff --git a/http.c b/http.c index 2ce2cd447b..0ebf8f77a6 100644 --- a/http.c +++ b/http.c @@ -158,6 +158,12 @@ static char *cached_accept_language; static char *http_ssl_backend; static int http_schannel_check_revoke = 1; +/* + * With the backend being set to `schannel`, setting sslCAinfo would override + * the Certificate Store in cURL v7.60.0 and later, which is not what we want + * by default. + */ +static int http_schannel_use_ssl_cainfo; size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_) { @@ -317,6 +323,11 @@ static int http_options(const char *var, const char *value, void *cb) return 0; } + if (!strcmp("http.schannelusesslcainfo", var)) { + http_schannel_use_ssl_cainfo = git_config_bool(var, value); + return 0; + } + if (!strcmp("http.minsessions", var)) { min_curl_sessions = git_config_int(var, value); #ifndef USE_CURL_MULTI @@ -869,7 +880,13 @@ static CURL *get_curl_handle(void) if (ssl_pinnedkey != NULL) curl_easy_setopt(result, CURLOPT_PINNEDPUBLICKEY, ssl_pinnedkey); #endif - if (ssl_cainfo != NULL) + if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) && + !http_schannel_use_ssl_cainfo) { + curl_easy_setopt(result, CURLOPT_CAINFO, NULL); +#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); if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {