Merge branch 'jk/transfer-limit-protocol' into maint-2.3
This commit is contained in:
commit
df37727a65
@ -1045,6 +1045,38 @@ GIT_ICASE_PATHSPECS::
|
||||
an operation has touched every ref (e.g., because you are
|
||||
cloning a repository to make a backup).
|
||||
|
||||
`GIT_ALLOW_PROTOCOL`::
|
||||
If set, provide a colon-separated list of protocols which are
|
||||
allowed to be used with fetch/push/clone. This is useful to
|
||||
restrict recursive submodule initialization from an untrusted
|
||||
repository. Any protocol not mentioned will be disallowed (i.e.,
|
||||
this is a whitelist, not a blacklist). If the variable is not
|
||||
set at all, all protocols are enabled. The protocol names
|
||||
currently used by git are:
|
||||
|
||||
- `file`: any local file-based path (including `file://` URLs,
|
||||
or local paths)
|
||||
|
||||
- `git`: the anonymous git protocol over a direct TCP
|
||||
connection (or proxy, if configured)
|
||||
|
||||
- `ssh`: git over ssh (including `host:path` syntax,
|
||||
`git+ssh://`, etc).
|
||||
|
||||
- `rsync`: git over rsync
|
||||
|
||||
- `http`: git over http, both "smart http" and "dumb http".
|
||||
Note that this does _not_ include `https`; if you want both,
|
||||
you should specify both as `http:https`.
|
||||
|
||||
- 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]]
|
||||
------------------------
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "url.h"
|
||||
#include "string-list.h"
|
||||
#include "sha1-array.h"
|
||||
#include "transport.h"
|
||||
|
||||
static char *server_capabilities;
|
||||
static const char *parse_feature_value(const char *, const char *, int *);
|
||||
@ -694,6 +695,8 @@ struct child_process *git_connect(int fd[2], const char *url,
|
||||
else
|
||||
target_host = xstrdup(hostandport);
|
||||
|
||||
transport_check_allowed("git");
|
||||
|
||||
/* These underlying connection commands die() if they
|
||||
* cannot connect.
|
||||
*/
|
||||
@ -727,6 +730,7 @@ struct child_process *git_connect(int fd[2], const char *url,
|
||||
int putty;
|
||||
char *ssh_host = hostandport;
|
||||
const char *port = NULL;
|
||||
transport_check_allowed("ssh");
|
||||
get_host_and_port(&ssh_host, &port);
|
||||
|
||||
if (!port)
|
||||
@ -768,6 +772,7 @@ struct child_process *git_connect(int fd[2], const char *url,
|
||||
/* remove repo-local variables from the environment */
|
||||
conn->env = local_repo_env;
|
||||
conn->use_shell = 1;
|
||||
transport_check_allowed("file");
|
||||
}
|
||||
argv_array_push(&conn->args, cmd.buf);
|
||||
|
||||
|
@ -22,6 +22,15 @@ require_work_tree
|
||||
wt_prefix=$(git rev-parse --show-prefix)
|
||||
cd_to_toplevel
|
||||
|
||||
# Restrict ourselves to a vanilla subset of protocols; the URLs
|
||||
# we get are under control of a remote repository, and we do not
|
||||
# want them kicking off arbitrary git-remote-* programs.
|
||||
#
|
||||
# If the user has already specified a set of allowed protocols,
|
||||
# we assume they know what they're doing and use that instead.
|
||||
: ${GIT_ALLOW_PROTOCOL=file:git:http:https:ssh}
|
||||
export GIT_ALLOW_PROTOCOL
|
||||
|
||||
command=
|
||||
branch=
|
||||
force=
|
||||
|
96
t/lib-proto-disable.sh
Normal file
96
t/lib-proto-disable.sh
Normal file
@ -0,0 +1,96 @@
|
||||
# Test routines for checking protocol disabling.
|
||||
|
||||
# test cloning a particular protocol
|
||||
# $1 - description of the protocol
|
||||
# $2 - machine-readable name of the protocol
|
||||
# $3 - the URL to try cloning
|
||||
test_proto () {
|
||||
desc=$1
|
||||
proto=$2
|
||||
url=$3
|
||||
|
||||
test_expect_success "clone $1 (enabled)" '
|
||||
rm -rf tmp.git &&
|
||||
(
|
||||
GIT_ALLOW_PROTOCOL=$proto &&
|
||||
export GIT_ALLOW_PROTOCOL &&
|
||||
git clone --bare "$url" tmp.git
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success "fetch $1 (enabled)" '
|
||||
(
|
||||
cd tmp.git &&
|
||||
GIT_ALLOW_PROTOCOL=$proto &&
|
||||
export GIT_ALLOW_PROTOCOL &&
|
||||
git fetch
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success "push $1 (enabled)" '
|
||||
(
|
||||
cd tmp.git &&
|
||||
GIT_ALLOW_PROTOCOL=$proto &&
|
||||
export GIT_ALLOW_PROTOCOL &&
|
||||
git push origin HEAD:pushed
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success "push $1 (disabled)" '
|
||||
(
|
||||
cd tmp.git &&
|
||||
GIT_ALLOW_PROTOCOL=none &&
|
||||
export GIT_ALLOW_PROTOCOL &&
|
||||
test_must_fail git push origin HEAD:pushed
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success "fetch $1 (disabled)" '
|
||||
(
|
||||
cd tmp.git &&
|
||||
GIT_ALLOW_PROTOCOL=none &&
|
||||
export GIT_ALLOW_PROTOCOL &&
|
||||
test_must_fail git fetch
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success "clone $1 (disabled)" '
|
||||
rm -rf tmp.git &&
|
||||
(
|
||||
GIT_ALLOW_PROTOCOL=none &&
|
||||
export GIT_ALLOW_PROTOCOL &&
|
||||
test_must_fail git clone --bare "$url" tmp.git
|
||||
)
|
||||
'
|
||||
}
|
||||
|
||||
# set up an ssh wrapper that will access $host/$repo in the
|
||||
# trash directory, and enable it for subsequent tests.
|
||||
setup_ssh_wrapper () {
|
||||
test_expect_success 'setup ssh wrapper' '
|
||||
write_script ssh-wrapper <<-\EOF &&
|
||||
echo >&2 "ssh: $*"
|
||||
host=$1; shift
|
||||
cd "$TRASH_DIRECTORY/$host" &&
|
||||
eval "$*"
|
||||
EOF
|
||||
GIT_SSH="$PWD/ssh-wrapper" &&
|
||||
export GIT_SSH &&
|
||||
export TRASH_DIRECTORY
|
||||
'
|
||||
}
|
||||
|
||||
# set up a wrapper that can be used with remote-ext to
|
||||
# access repositories in the "remote" directory of trash-dir,
|
||||
# like "ext::fake-remote %S repo.git"
|
||||
setup_ext_wrapper () {
|
||||
test_expect_success 'setup ext wrapper' '
|
||||
write_script fake-remote <<-\EOF &&
|
||||
echo >&2 "fake-remote: $*"
|
||||
cd "$TRASH_DIRECTORY/remote" &&
|
||||
eval "$*"
|
||||
EOF
|
||||
PATH=$TRASH_DIRECTORY:$PATH &&
|
||||
export TRASH_DIRECTORY
|
||||
'
|
||||
}
|
14
t/t5810-proto-disable-local.sh
Executable file
14
t/t5810-proto-disable-local.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='test disabling of local paths in clone/fetch'
|
||||
. ./test-lib.sh
|
||||
. "$TEST_DIRECTORY/lib-proto-disable.sh"
|
||||
|
||||
test_expect_success 'setup repository to clone' '
|
||||
test_commit one
|
||||
'
|
||||
|
||||
test_proto "file://" file "file://$PWD"
|
||||
test_proto "path" file .
|
||||
|
||||
test_done
|
20
t/t5811-proto-disable-git.sh
Executable file
20
t/t5811-proto-disable-git.sh
Executable file
@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='test disabling of git-over-tcp in clone/fetch'
|
||||
. ./test-lib.sh
|
||||
. "$TEST_DIRECTORY/lib-proto-disable.sh"
|
||||
. "$TEST_DIRECTORY/lib-git-daemon.sh"
|
||||
start_git_daemon
|
||||
|
||||
test_expect_success 'create git-accessible repo' '
|
||||
bare="$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" &&
|
||||
test_commit one &&
|
||||
git --bare init "$bare" &&
|
||||
git push "$bare" HEAD &&
|
||||
>"$bare/git-daemon-export-ok" &&
|
||||
git -C "$bare" config daemon.receivepack true
|
||||
'
|
||||
|
||||
test_proto "git://" git "$GIT_DAEMON_URL/repo.git"
|
||||
|
||||
test_done
|
20
t/t5812-proto-disable-http.sh
Executable file
20
t/t5812-proto-disable-http.sh
Executable file
@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='test disabling of git-over-http in clone/fetch'
|
||||
. ./test-lib.sh
|
||||
. "$TEST_DIRECTORY/lib-proto-disable.sh"
|
||||
. "$TEST_DIRECTORY/lib-httpd.sh"
|
||||
start_httpd
|
||||
|
||||
test_expect_success 'create git-accessible repo' '
|
||||
bare="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
|
||||
test_commit one &&
|
||||
git --bare init "$bare" &&
|
||||
git push "$bare" HEAD &&
|
||||
git -C "$bare" config http.receivepack true
|
||||
'
|
||||
|
||||
test_proto "smart http" http "$HTTPD_URL/smart/repo.git"
|
||||
|
||||
stop_httpd
|
||||
test_done
|
20
t/t5813-proto-disable-ssh.sh
Executable file
20
t/t5813-proto-disable-ssh.sh
Executable file
@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='test disabling of git-over-ssh in clone/fetch'
|
||||
. ./test-lib.sh
|
||||
. "$TEST_DIRECTORY/lib-proto-disable.sh"
|
||||
|
||||
setup_ssh_wrapper
|
||||
|
||||
test_expect_success 'setup repository to clone' '
|
||||
test_commit one &&
|
||||
mkdir remote &&
|
||||
git init --bare remote/repo.git &&
|
||||
git push remote/repo.git HEAD
|
||||
'
|
||||
|
||||
test_proto "host:path" ssh "remote:repo.git"
|
||||
test_proto "ssh://" ssh "ssh://remote/$PWD/remote/repo.git"
|
||||
test_proto "git+ssh://" ssh "git+ssh://remote/$PWD/remote/repo.git"
|
||||
|
||||
test_done
|
18
t/t5814-proto-disable-ext.sh
Executable file
18
t/t5814-proto-disable-ext.sh
Executable file
@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='test disabling of remote-helper paths in clone/fetch'
|
||||
. ./test-lib.sh
|
||||
. "$TEST_DIRECTORY/lib-proto-disable.sh"
|
||||
|
||||
setup_ext_wrapper
|
||||
|
||||
test_expect_success 'setup repository to clone' '
|
||||
test_commit one &&
|
||||
mkdir remote &&
|
||||
git init --bare remote/repo.git &&
|
||||
git push remote/repo.git HEAD
|
||||
'
|
||||
|
||||
test_proto "remote-helper" ext "ext::fake-remote %S repo.git"
|
||||
|
||||
test_done
|
43
t/t5815-submodule-protos.sh
Executable file
43
t/t5815-submodule-protos.sh
Executable file
@ -0,0 +1,43 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='test protocol whitelisting with submodules'
|
||||
. ./test-lib.sh
|
||||
. "$TEST_DIRECTORY"/lib-proto-disable.sh
|
||||
|
||||
setup_ext_wrapper
|
||||
setup_ssh_wrapper
|
||||
|
||||
test_expect_success 'setup repository with submodules' '
|
||||
mkdir remote &&
|
||||
git init remote/repo.git &&
|
||||
(cd remote/repo.git && test_commit one) &&
|
||||
# submodule-add should probably trust what we feed it on the cmdline,
|
||||
# but its implementation is overly conservative.
|
||||
GIT_ALLOW_PROTOCOL=ssh git submodule add remote:repo.git ssh-module &&
|
||||
GIT_ALLOW_PROTOCOL=ext git submodule add "ext::fake-remote %S repo.git" ext-module &&
|
||||
git commit -m "add submodules"
|
||||
'
|
||||
|
||||
test_expect_success 'clone with recurse-submodules fails' '
|
||||
test_must_fail git clone --recurse-submodules . dst
|
||||
'
|
||||
|
||||
test_expect_success 'setup individual updates' '
|
||||
rm -rf dst &&
|
||||
git clone . dst &&
|
||||
git -C dst submodule init
|
||||
'
|
||||
|
||||
test_expect_success 'update of ssh allowed' '
|
||||
git -C dst submodule update ssh-module
|
||||
'
|
||||
|
||||
test_expect_success 'update of ext not allowed' '
|
||||
test_must_fail git -C dst submodule update ext-module
|
||||
'
|
||||
|
||||
test_expect_success 'user can override whitelist' '
|
||||
GIT_ALLOW_PROTOCOL=ext git -C dst submodule update ext-module
|
||||
'
|
||||
|
||||
test_done
|
@ -1038,6 +1038,8 @@ int transport_helper_init(struct transport *transport, const char *name)
|
||||
struct helper_data *data = xcalloc(1, sizeof(*data));
|
||||
data->name = name;
|
||||
|
||||
transport_check_allowed(name);
|
||||
|
||||
if (getenv("GIT_TRANSPORT_HELPER_DEBUG"))
|
||||
debug = 1;
|
||||
|
||||
|
21
transport.c
21
transport.c
@ -909,6 +909,20 @@ static int external_specification_len(const char *url)
|
||||
return strchr(url, ':') - url;
|
||||
}
|
||||
|
||||
void transport_check_allowed(const char *type)
|
||||
{
|
||||
struct string_list allowed = STRING_LIST_INIT_DUP;
|
||||
const char *v = getenv("GIT_ALLOW_PROTOCOL");
|
||||
|
||||
if (!v)
|
||||
return;
|
||||
|
||||
string_list_split(&allowed, v, ':', -1);
|
||||
if (!unsorted_string_list_has_string(&allowed, type))
|
||||
die("transport '%s' not allowed", type);
|
||||
string_list_clear(&allowed, 0);
|
||||
}
|
||||
|
||||
struct transport *transport_get(struct remote *remote, const char *url)
|
||||
{
|
||||
const char *helper;
|
||||
@ -940,12 +954,14 @@ struct transport *transport_get(struct remote *remote, const char *url)
|
||||
if (helper) {
|
||||
transport_helper_init(ret, helper);
|
||||
} else if (starts_with(url, "rsync:")) {
|
||||
transport_check_allowed("rsync");
|
||||
ret->get_refs_list = get_refs_via_rsync;
|
||||
ret->fetch = fetch_objs_via_rsync;
|
||||
ret->push = rsync_transport_push;
|
||||
ret->smart_options = NULL;
|
||||
} else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) {
|
||||
struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
|
||||
transport_check_allowed("file");
|
||||
ret->data = data;
|
||||
ret->get_refs_list = get_refs_from_bundle;
|
||||
ret->fetch = fetch_refs_from_bundle;
|
||||
@ -957,7 +973,10 @@ struct transport *transport_get(struct remote *remote, const char *url)
|
||||
|| starts_with(url, "ssh://")
|
||||
|| starts_with(url, "git+ssh://")
|
||||
|| starts_with(url, "ssh+git://")) {
|
||||
/* These are builtin smart transports. */
|
||||
/*
|
||||
* These are builtin smart transports; "allowed" transports
|
||||
* will be checked individually in git_connect.
|
||||
*/
|
||||
struct git_transport_data *data = xcalloc(1, sizeof(*data));
|
||||
ret->data = data;
|
||||
ret->set_option = NULL;
|
||||
|
@ -132,6 +132,13 @@ 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,
|
||||
* and die otherwise. type should generally be the URL scheme,
|
||||
* as described in Documentation/git.txt
|
||||
*/
|
||||
void transport_check_allowed(const char *type);
|
||||
|
||||
/* Transport options which apply to git:// and scp-style URLs */
|
||||
|
||||
/* The program to use on the remote side to send a pack */
|
||||
|
Loading…
Reference in New Issue
Block a user