1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
Backport of:
From f4113cac0c88b4f36ee6f3abf3218034440a68e3 Mon Sep 17 00:00:00 2001
From: Blake Burkhart <bburky@bburky.com>
Date: Tue, 22 Sep 2015 18:06:04 -0400
Subject: [PATCH] http: limit redirection to protocol-whitelist
Previously, libcurl would follow redirection to any protocol
it was compiled for support with. This is desirable to allow
redirection from HTTP to HTTPS. However, it would even
successfully allow redirection from HTTP to SFTP, a protocol
that git does not otherwise support at all. Furthermore
git's new protocol-whitelisting could be bypassed by
following a redirect within the remote helper, as it was
only enforced at transport selection time.
This patch limits redirects within libcurl to HTTP, HTTPS,
FTP and FTPS. If there is a protocol-whitelist present, this
list is limited to those also allowed by the whitelist. As
redirection happens from within libcurl, it is impossible
for an HTTP redirect to a protocol implemented within
another remote helper.
When the curl version git was compiled with is too old to
support restrictions on protocol redirection, we warn the
user if GIT_ALLOW_PROTOCOL restrictions were requested. This
is a little inaccurate, as even without that variable in the
environment, we would still restrict SFTP, etc, and we do
not warn in that case. But anything else means we would
literally warn every time git accesses an http remote.
This commit includes a test, but it is not as robust as we
would hope. It redirects an http request to ftp, and checks
that curl complained about the protocol, which means that we
are relying on curl's specific error message to know what
happened. Ideally we would redirect to a working ftp server
and confirm that we can clone without protocol restrictions,
and not with them. But we do not have a portable way of
providing an ftp server, nor any other protocol that curl
supports (https is the closest, but we would have to deal
with certificates).
[jk: added test and version warning]
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
Documentation/git.txt | 5 -----
http.c | 17 +++++++++++++++++
t/lib-httpd/apache.conf | 1 +
t/t5812-proto-disable-http.sh | 9 +++++++++
4 files changed, 27 insertions(+), 5 deletions(-)
Index: git-1.7.9.5/Documentation/git.txt
===================================================================
--- git-1.7.9.5.orig/Documentation/git.txt 2015-12-14 12:36:05.513295946 -0500
+++ git-1.7.9.5/Documentation/git.txt 2015-12-14 12:36:05.509295899 -0500
@@ -633,11 +633,6 @@
- any external helpers are named by their protocol (e.g., use
`hg` to allow the `git-remote-hg` helper)
-+
-Note that this controls only git's internal protocol selection.
-If libcurl is used (e.g., by the `http` transport), it may
-redirect to other protocols. There is not currently any way to
-restrict this.
Environment Variables
Index: git-1.7.9.5/http.c
===================================================================
--- git-1.7.9.5.orig/http.c 2015-12-14 12:36:05.513295946 -0500
+++ git-1.7.9.5/http.c 2015-12-14 12:41:08.756793731 -0500
@@ -4,6 +4,7 @@
#include "run-command.h"
#include "url.h"
#include "credential.h"
+#include "string-list.h"
int active_requests;
int http_is_verbose;
@@ -241,9 +242,40 @@
return 1;
}
+static const struct string_list *http_protocol_whitelist(void)
+{
+ static int enabled = -1;
+ static struct string_list allowed = STRING_LIST_INIT_DUP;
+
+ if (enabled < 0) {
+ const char *v = getenv("GIT_ALLOW_PROTOCOL");
+ if (v) {
+ string_list_split(&allowed, v, ':', -1);
+ sort_string_list(&allowed);
+ enabled = 1;
+ } else {
+ enabled = 0;
+ }
+ }
+
+ return enabled ? &allowed : NULL;
+}
+
+int http_is_transport_allowed(const char *type)
+{
+ const struct string_list *allowed = http_protocol_whitelist();
+ return !allowed || string_list_has_string(allowed, type);
+}
+
+int http_transport_restrict_protocols(void)
+{
+ return !!http_protocol_whitelist();
+}
+
static CURL *get_curl_handle(void)
{
CURL *result = curl_easy_init();
+ long allowed_protocols = 0;
if (!curl_ssl_verify) {
curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
@@ -294,6 +326,21 @@
#elif LIBCURL_VERSION_NUM >= 0x071101
curl_easy_setopt(result, CURLOPT_POST301, 1);
#endif
+#if LIBCURL_VERSION_NUM >= 0x071304
+ if (http_is_transport_allowed("http"))
+ allowed_protocols |= CURLPROTO_HTTP;
+ if (http_is_transport_allowed("https"))
+ allowed_protocols |= CURLPROTO_HTTPS;
+ if (http_is_transport_allowed("ftp"))
+ allowed_protocols |= CURLPROTO_FTP;
+ if (http_is_transport_allowed("ftps"))
+ allowed_protocols |= CURLPROTO_FTPS;
+ curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS, allowed_protocols);
+#else
+ if (http_transport_restrict_protocols())
+ warning("protocol restrictions not applied to curl redirects because\n"
+ "your curl version is too old (>= 7.19.4)");
+#endif
if (getenv("GIT_CURL_VERBOSE"))
curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
Index: git-1.7.9.5/t/lib-httpd/apache.conf
===================================================================
--- git-1.7.9.5.orig/t/lib-httpd/apache.conf 2015-12-14 12:36:05.513295946 -0500
+++ git-1.7.9.5/t/lib-httpd/apache.conf 2015-12-14 12:36:05.509295899 -0500
@@ -71,6 +71,7 @@
RewriteEngine on
RewriteRule ^/smart-redir-perm/(.*)$ /smart/$1 [R=301]
RewriteRule ^/smart-redir-temp/(.*)$ /smart/$1 [R=302]
+RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302]
<IfDefine SSL>
LoadModule ssl_module modules/mod_ssl.so
Index: git-1.7.9.5/t/t5812-proto-disable-http.sh
===================================================================
--- git-1.7.9.5.orig/t/t5812-proto-disable-http.sh 2015-12-14 12:36:05.513295946 -0500
+++ git-1.7.9.5/t/t5812-proto-disable-http.sh 2015-12-14 12:36:05.509295899 -0500
@@ -16,5 +16,14 @@
test_proto "smart http" http "$HTTPD_URL/smart/repo.git"
+test_expect_success 'curl redirects respect whitelist' '
+ test_must_fail env GIT_ALLOW_PROTOCOL=http:https \
+ git clone "$HTTPD_URL/ftp-redir/repo.git" 2>stderr &&
+ {
+ test_i18ngrep "ftp.*disabled" stderr ||
+ test_i18ngrep "your curl version is too old"
+ }
+'
+
stop_httpd
test_done
|