Merge branch 'jk/transfer-limit-redirection' into maint-2.3
authorJunio C Hamano <gitster@pobox.com>
Mon, 28 Sep 2015 21:46:05 +0000 (14:46 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 28 Sep 2015 21:46:05 +0000 (14:46 -0700)
Documentation/git.txt
http.c
t/lib-httpd/apache.conf
t/t5812-proto-disable-http.sh
transport.c
transport.h
index b6a12b32ee5b7b01fc49ddda9f0ac280eaf0cac1..41a09cac7af072e99b096a7c81d65af43c91c5dd 100644 (file)
@@ -1071,11 +1071,6 @@ GIT_ICASE_PATHSPECS::
 
          - 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.
 
 
 Discussion[[Discussion]]
diff --git a/http.c b/http.c
index 67986200655f88f5545e3df3669c2f4bbe688247..00e3fc80e8161080549db26afd68e341989af453 100644 (file)
--- a/http.c
+++ b/http.c
@@ -8,6 +8,7 @@
 #include "credential.h"
 #include "version.h"
 #include "pkt-line.h"
+#include "transport.h"
 
 int active_requests;
 int http_is_verbose;
@@ -303,6 +304,7 @@ static void set_curl_keepalive(CURL *c)
 static CURL *get_curl_handle(void)
 {
        CURL *result = curl_easy_init();
+       long allowed_protocols = 0;
 
        if (!result)
                die("curl_easy_init failed");
@@ -350,11 +352,27 @@ static CURL *get_curl_handle(void)
        }
 
        curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
+       curl_easy_setopt(result, CURLOPT_MAXREDIRS, 20);
 #if LIBCURL_VERSION_NUM >= 0x071301
        curl_easy_setopt(result, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
 #elif LIBCURL_VERSION_NUM >= 0x071101
        curl_easy_setopt(result, CURLOPT_POST301, 1);
 #endif
+#if LIBCURL_VERSION_NUM >= 0x071304
+       if (is_transport_allowed("http"))
+               allowed_protocols |= CURLPROTO_HTTP;
+       if (is_transport_allowed("https"))
+               allowed_protocols |= CURLPROTO_HTTPS;
+       if (is_transport_allowed("ftp"))
+               allowed_protocols |= CURLPROTO_FTP;
+       if (is_transport_allowed("ftps"))
+               allowed_protocols |= CURLPROTO_FTPS;
+       curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS, allowed_protocols);
+#else
+       if (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 0b81a0047b8d9cd60266500907e95ddc5b87742c..7d15e6d44c83f6b37297ae01a2998825e313b9b2 100644 (file)
@@ -119,6 +119,10 @@ RewriteRule ^/smart-redir-perm/(.*)$ /smart/$1 [R=301]
 RewriteRule ^/smart-redir-temp/(.*)$ /smart/$1 [R=302]
 RewriteRule ^/smart-redir-auth/(.*)$ /auth/smart/$1 [R=301]
 RewriteRule ^/smart-redir-limited/(.*)/info/refs$ /smart/$1/info/refs [R=301]
+RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302]
+
+RewriteRule ^/loop-redir/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-(.*) /$1 [R=302]
+RewriteRule ^/loop-redir/(.*)$ /loop-redir/x-$1 [R=302]
 
 <IfDefine SSL>
 LoadModule ssl_module modules/mod_ssl.so
index dd5001cbac8a0a66a3235e9e6c83a62bc1ab595f..0d105d54174e061b20707de808b284314f730667 100755 (executable)
@@ -16,5 +16,18 @@ test_expect_success 'create git-accessible repo' '
 
 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"
+       }
+'
+
+test_expect_success 'curl limits redirects' '
+       test_must_fail git clone "$HTTPD_URL/loop-redir/smart/repo.git"
+'
+
 stop_httpd
 test_done
index 94fe8658f2bfa7775e667f7c28264c3dbe7f63b7..647d2c2afaadb4f39dd74b182db969eaf0623aa0 100644 (file)
@@ -909,18 +909,40 @@ static int external_specification_len(const char *url)
        return strchr(url, ':') - url;
 }
 
-void transport_check_allowed(const char *type)
+static const struct string_list *protocol_whitelist(void)
 {
-       struct string_list allowed = STRING_LIST_INIT_DUP;
-       const char *v = getenv("GIT_ALLOW_PROTOCOL");
+       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);
+                       string_list_sort(&allowed);
+                       enabled = 1;
+               } else {
+                       enabled = 0;
+               }
+       }
 
-       if (!v)
-               return;
+       return enabled ? &allowed : NULL;
+}
+
+int is_transport_allowed(const char *type)
+{
+       const struct string_list *allowed = protocol_whitelist();
+       return !allowed || string_list_has_string(allowed, type);
+}
 
-       string_list_split(&allowed, v, ':', -1);
-       if (!unsorted_string_list_has_string(&allowed, type))
+void transport_check_allowed(const char *type)
+{
+       if (!is_transport_allowed(type))
                die("transport '%s' not allowed", type);
-       string_list_clear(&allowed, 0);
+}
+
+int transport_restrict_protocols(void)
+{
+       return !!protocol_whitelist();
 }
 
 struct transport *transport_get(struct remote *remote, const char *url)
index f7df6ec1d2a1f17e9932e531110cc3fe4bdbb81c..ed84da2aa4f39d8974164ca0ef7f0c10642d4824 100644 (file)
@@ -132,13 +132,24 @@ struct transport {
 /* Returns a transport suitable for the url */
 struct transport *transport_get(struct remote *, const char *);
 
+/*
+ * Check whether a transport is allowed by the environment. Type should
+ * generally be the URL scheme, as described in Documentation/git.txt
+ */
+int is_transport_allowed(const char *type);
+
 /*
  * Check whether a transport is allowed by the environment,
- * and die otherwise. type should generally be the URL scheme,
- * as described in Documentation/git.txt
+ * and die otherwise.
  */
 void transport_check_allowed(const char *type);
 
+/*
+ * Returns true if the user has attempted to turn on protocol
+ * restrictions at all.
+ */
+int transport_restrict_protocols(void);
+
 /* Transport options which apply to git:// and scp-style URLs */
 
 /* The program to use on the remote side to send a pack */