summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/git.txt5
-rw-r--r--http.c18
-rw-r--r--t/lib-httpd/apache.conf4
-rwxr-xr-xt/t5812-proto-disable-http.sh13
-rw-r--r--transport.c38
-rw-r--r--transport.h15
6 files changed, 78 insertions, 15 deletions
diff --git a/Documentation/git.txt b/Documentation/git.txt
index b6a12b3..41a09ca 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -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 6798620..00e3fc8 100644
--- 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);
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 0b81a00..7d15e6d 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -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
diff --git a/t/t5812-proto-disable-http.sh b/t/t5812-proto-disable-http.sh
index dd5001c..0d105d5 100755
--- a/t/t5812-proto-disable-http.sh
+++ b/t/t5812-proto-disable-http.sh
@@ -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
diff --git a/transport.c b/transport.c
index 94fe865..647d2c2 100644
--- a/transport.c
+++ b/transport.c
@@ -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)
diff --git a/transport.h b/transport.h
index f7df6ec..ed84da2 100644
--- a/transport.h
+++ b/transport.h
@@ -133,12 +133,23 @@ struct transport {
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 */