Blame SOURCES/0002-transport-add-a-protocol-whitelist-environment-varia.patch

776a70
From cfa4e13f09d07f679ffacdddfbe0ef44d1de32d9 Mon Sep 17 00:00:00 2001
776a70
From: Petr Stodulka <pstodulk@redhat.com>
776a70
Date: Wed, 28 Oct 2015 15:21:08 +0100
776a70
Subject: [PATCH 2/5] transport: add a protocol-whitelist environment variable
776a70
776a70
If we are cloning an untrusted remote repository into a
776a70
sandbox, we may also want to fetch remote submodules in
776a70
order to get the complete view as intended by the other
776a70
side. However, that opens us up to attacks where a malicious
776a70
user gets us to clone something they would not otherwise
776a70
have access to (this is not necessarily a problem by itself,
776a70
but we may then act on the cloned contents in a way that
776a70
exposes them to the attacker).
776a70
776a70
Ideally such a setup would sandbox git entirely away from
776a70
high-value items, but this is not always practical or easy
776a70
to set up (e.g., OS network controls may block multiple
776a70
protocols, and we would want to enable some but not others).
776a70
776a70
We can help this case by providing a way to restrict
776a70
particular protocols. We use a whitelist in the environment.
776a70
This is more annoying to set up than a blacklist, but
776a70
defaults to safety if the set of protocols git supports
776a70
grows). If no whitelist is specified, we continue to default
776a70
to allowing all protocols (this is an "unsafe" default, but
776a70
since the minority of users will want this sandboxing
776a70
effect, it is the only sensible one).
776a70
776a70
A note on the tests: ideally these would all be in a single
776a70
test file, but the git-daemon and httpd test infrastructure
776a70
is an all-or-nothing proposition rather than a test-by-test
776a70
prerequisite. By putting them all together, we would be
776a70
unable to test the file-local code on machines without
776a70
apache.
776a70
---
776a70
 Documentation/git.txt | 32 ++++++++++++++++++++++++++++++++
776a70
 connect.c             |  4 ++++
776a70
 transport-helper.c    |  2 ++
776a70
 transport.c           | 21 ++++++++++++++++++++-
776a70
 transport.h           |  7 +++++++
776a70
 5 files changed, 65 insertions(+), 1 deletion(-)
776a70
776a70
diff --git a/Documentation/git.txt b/Documentation/git.txt
776a70
index 443d88f..179a0e8 100644
776a70
--- a/Documentation/git.txt
776a70
+++ b/Documentation/git.txt
776a70
@@ -847,6 +847,38 @@ GIT_LITERAL_PATHSPECS::
776a70
 	literal paths to Git (e.g., paths previously given to you by
776a70
 	`git ls-tree`, `--raw` diff output, etc).
776a70
 
776a70
+`GIT_ALLOW_PROTOCOL`::
776a70
+   If set, provide a colon-separated list of protocols which are
776a70
+   allowed to be used with fetch/push/clone. This is useful to
776a70
+   restrict recursive submodule initialization from an untrusted
776a70
+   repository. Any protocol not mentioned will be disallowed (i.e.,
776a70
+   this is a whitelist, not a blacklist). If the variable is not
776a70
+   set at all, all protocols are enabled.  The protocol names
776a70
+   currently used by git are:
776a70
+
776a70
+     - `file`: any local file-based path (including `file://` URLs,
776a70
+       or local paths)
776a70
+
776a70
+     - `git`: the anonymous git protocol over a direct TCP
776a70
+       connection (or proxy, if configured)
776a70
+
776a70
+     - `ssh`: git over ssh (including `host:path` syntax,
776a70
+       `git+ssh://`, etc).
776a70
+
776a70
+     - `rsync`: git over rsync
776a70
+
776a70
+     - `http`: git over http, both "smart http" and "dumb http".
776a70
+       Note that this does _not_ include `https`; if you want both,
776a70
+       you should specify both as `http:https`.
776a70
+
776a70
+     - any external helpers are named by their protocol (e.g., use
776a70
+       `hg` to allow the `git-remote-hg` helper)
776a70
++
776a70
+Note that this controls only git's internal protocol selection.
776a70
+If libcurl is used (e.g., by the `http` transport), it may
776a70
+redirect to other protocols. There is not currently any way to
776a70
+restrict this.
776a70
+
776a70
 
776a70
 Discussion[[Discussion]]
776a70
 ------------------------
776a70
diff --git a/connect.c b/connect.c
776a70
index f57efd0..6d4ea13 100644
776a70
--- a/connect.c
776a70
+++ b/connect.c
776a70
@@ -6,6 +6,7 @@
776a70
 #include "run-command.h"
776a70
 #include "remote.h"
776a70
 #include "url.h"
776a70
+#include "transport.h"
776a70
 
776a70
 static char *server_capabilities;
776a70
 
776a70
@@ -587,6 +588,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
776a70
 		 * cannot connect.
776a70
 		 */
776a70
 		char *target_host = xstrdup(host);
776a70
+		transport_check_allowed("git");
776a70
 		if (git_use_proxy(host))
776a70
 			conn = git_proxy_connect(fd, host);
776a70
 		else
776a70
@@ -623,6 +625,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
776a70
 	if (protocol == PROTO_SSH) {
776a70
 		const char *ssh = getenv("GIT_SSH");
776a70
 		int putty = ssh && strcasestr(ssh, "plink");
776a70
+		transport_check_allowed("ssh");
776a70
 		if (!ssh) ssh = "ssh";
776a70
 
776a70
 		*arg++ = ssh;
776a70
@@ -639,6 +642,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
776a70
 		/* remove repo-local variables from the environment */
776a70
 		conn->env = local_repo_env;
776a70
 		conn->use_shell = 1;
776a70
+		transport_check_allowed("file");
776a70
 	}
776a70
 	*arg++ = cmd.buf;
776a70
 	*arg = NULL;
776a70
diff --git a/transport-helper.c b/transport-helper.c
776a70
index 522d791..be8402a 100644
776a70
--- a/transport-helper.c
776a70
+++ b/transport-helper.c
776a70
@@ -932,6 +932,8 @@ int transport_helper_init(struct transport *transport, const char *name)
776a70
 	struct helper_data *data = xcalloc(sizeof(*data), 1);
776a70
 	data->name = name;
776a70
 
776a70
+	transport_check_allowed(name);
776a70
+
776a70
 	if (getenv("GIT_TRANSPORT_HELPER_DEBUG"))
776a70
 		debug = 1;
776a70
 
776a70
diff --git a/transport.c b/transport.c
776a70
index ba5d8af..733717d 100644
776a70
--- a/transport.c
776a70
+++ b/transport.c
776a70
@@ -894,6 +894,20 @@ static int external_specification_len(const char *url)
776a70
 	return strchr(url, ':') - url;
776a70
 }
776a70
 
776a70
+void transport_check_allowed(const char *type)
776a70
+{
776a70
+	struct string_list allowed = STRING_LIST_INIT_DUP;
776a70
+	const char *v = getenv("GIT_ALLOW_PROTOCOL");
776a70
+
776a70
+	if (!v)
776a70
+		return;
776a70
+
776a70
+	string_list_split(&allowed, v, ':', -1);
776a70
+	if (!unsorted_string_list_has_string(&allowed, type))
776a70
+		die("transport '%s' not allowed", type);
776a70
+	string_list_clear(&allowed, 0);
776a70
+}
776a70
+
776a70
 struct transport *transport_get(struct remote *remote, const char *url)
776a70
 {
776a70
 	const char *helper;
776a70
@@ -925,12 +939,14 @@ struct transport *transport_get(struct remote *remote, const char *url)
776a70
 	if (helper) {
776a70
 		transport_helper_init(ret, helper);
776a70
 	} else if (!prefixcmp(url, "rsync:")) {
776a70
+		transport_check_allowed("rsync");
776a70
 		ret->get_refs_list = get_refs_via_rsync;
776a70
 		ret->fetch = fetch_objs_via_rsync;
776a70
 		ret->push = rsync_transport_push;
776a70
 		ret->smart_options = NULL;
776a70
 	} else if (is_local(url) && is_file(url) && is_bundle(url, 1)) {
776a70
 		struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
776a70
+		transport_check_allowed("file");
776a70
 		ret->data = data;
776a70
 		ret->get_refs_list = get_refs_from_bundle;
776a70
 		ret->fetch = fetch_refs_from_bundle;
776a70
@@ -942,7 +958,10 @@ struct transport *transport_get(struct remote *remote, const char *url)
776a70
 		|| !prefixcmp(url, "ssh://")
776a70
 		|| !prefixcmp(url, "git+ssh://")
776a70
 		|| !prefixcmp(url, "ssh+git://")) {
776a70
-		/* These are builtin smart transports. */
776a70
+		/*
776a70
+		 * These are builtin smart transports; "allowed" transports
776a70
+		 * will be checked individually in git_connect.
776a70
+		 */
776a70
 		struct git_transport_data *data = xcalloc(1, sizeof(*data));
776a70
 		ret->data = data;
776a70
 		ret->set_option = NULL;
776a70
diff --git a/transport.h b/transport.h
776a70
index fcb1d25..2beda7d 100644
776a70
--- a/transport.h
776a70
+++ b/transport.h
776a70
@@ -113,6 +113,13 @@ struct transport {
776a70
 /* Returns a transport suitable for the url */
776a70
 struct transport *transport_get(struct remote *, const char *);
776a70
 
776a70
+/*
776a70
+ * Check whether a transport is allowed by the environment,
776a70
+ * and die otherwise. type should generally be the URL scheme,
776a70
+ * as described in Documentation/git.txt
776a70
+ */
776a70
+void transport_check_allowed(const char *type);
776a70
+
776a70
 /* Transport options which apply to git:// and scp-style URLs */
776a70
 
776a70
 /* The program to use on the remote side to send a pack */
776a70
-- 
776a70
2.1.0
776a70