Skip to content

Commit

Permalink
Merge branch 'bw/transport-protocol-policy'
Browse files Browse the repository at this point in the history
Finer-grained control of what protocols are allowed for transports
during clone/fetch/push have been enabled via a new configuration
mechanism.

* bw/transport-protocol-policy:
  http: respect protocol.*.allow=user for http-alternates
  transport: add from_user parameter to is_transport_allowed
  http: create function to get curl allowed protocols
  transport: add protocol policy config option
  http: always warn if libcurl version is too old
  lib-proto-disable: variable name fix
  • Loading branch information
gitster committed Dec 27, 2016
2 parents 05f6e1b + abcbdc0 commit 9d540e9
Show file tree
Hide file tree
Showing 12 changed files with 362 additions and 85 deletions.
46 changes: 46 additions & 0 deletions Documentation/config.txt
Expand Up @@ -2318,6 +2318,52 @@ pretty.<name>::
Note that an alias with the same name as a built-in format
will be silently ignored.

protocol.allow::
If set, provide a user defined default policy for all protocols which
don't explicitly have a policy (`protocol.<name>.allow`). By default,
if unset, known-safe protocols (http, https, git, ssh, file) have a
default policy of `always`, known-dangerous protocols (ext) have a
default policy of `never`, and all other protocols have a default
policy of `user`. Supported policies:
+
--

* `always` - protocol is always able to be used.

* `never` - protocol is never able to be used.

* `user` - protocol is only able to be used when `GIT_PROTOCOL_FROM_USER` is
either unset or has a value of 1. This policy should be used when you want a
protocol to be directly usable by the user but don't want it used by commands which
execute clone/fetch/push commands without user input, e.g. recursive
submodule initialization.

--

protocol.<name>.allow::
Set a policy to be used by protocol `<name>` with clone/fetch/push
commands. See `protocol.allow` above for the available policies.
+
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,
`ssh://`, etc).

- `http`: git over http, both "smart http" and "dumb http".
Note that this does _not_ include `https`; if you want to configure
both, you must do so individually.

- any external helpers are named by their protocol (e.g., use
`hg` to allow the `git-remote-hg` helper)
--

pull.ff::
By default, Git does not create an extra merge commit when merging
a commit that is a descendant of the current commit. Instead, the
Expand Down
38 changes: 14 additions & 24 deletions Documentation/git.txt
Expand Up @@ -1161,30 +1161,20 @@ of clones and fetches.
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,
`ssh://`, etc).

- `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)

If set to a colon-separated list of protocols, behave as if
`protocol.allow` is set to `never`, and each of the listed
protocols has `protocol.<name>.allow` set to `always`
(overriding any existing configuration). In other words, any
protocol not mentioned will be disallowed (i.e., this is a
whitelist, not a blacklist). See the description of
`protocol.allow` in linkgit:git-config[1] for more details.

`GIT_PROTOCOL_FROM_USER`::
Set to 0 to prevent protocols used by fetch/push/clone which are
configured to the `user` state. This is useful to restrict recursive
submodule initialization from an untrusted repository or for programs
which feed potentially-untrusted URLS to git commands. See
linkgit:git-config[1] for more details.

Discussion[[Discussion]]
------------------------
Expand Down
12 changes: 4 additions & 8 deletions git-submodule.sh
Expand Up @@ -21,14 +21,10 @@ 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
# Tell the rest of git that any URLs we get don't come
# directly from the user, so it can apply policy as appropriate.
GIT_PROTOCOL_FROM_USER=0
export GIT_PROTOCOL_FROM_USER

command=
branch=
Expand Down
52 changes: 41 additions & 11 deletions http-walker.c
Expand Up @@ -3,6 +3,7 @@
#include "walker.h"
#include "http.h"
#include "list.h"
#include "transport.h"

struct alt_base {
char *base;
Expand Down Expand Up @@ -160,6 +161,32 @@ static void prefetch(struct walker *walker, unsigned char *sha1)
#endif
}

static int is_alternate_allowed(const char *url)
{
const char *protocols[] = {
"http", "https", "ftp", "ftps"
};
int i;

for (i = 0; i < ARRAY_SIZE(protocols); i++) {
const char *end;
if (skip_prefix(url, protocols[i], &end) &&
starts_with(end, "://"))
break;
}

if (i >= ARRAY_SIZE(protocols)) {
warning("ignoring alternate with unknown protocol: %s", url);
return 0;
}
if (!is_transport_allowed(protocols[i], 0)) {
warning("ignoring alternate with restricted protocol: %s", url);
return 0;
}

return 1;
}

static void process_alternates_response(void *callback_data)
{
struct alternates_request *alt_req =
Expand Down Expand Up @@ -274,17 +301,20 @@ static void process_alternates_response(void *callback_data)
struct strbuf target = STRBUF_INIT;
strbuf_add(&target, base, serverlen);
strbuf_add(&target, data + i, posn - i - 7);
warning("adding alternate object store: %s",
target.buf);
newalt = xmalloc(sizeof(*newalt));
newalt->next = NULL;
newalt->base = strbuf_detach(&target, NULL);
newalt->got_indices = 0;
newalt->packs = NULL;

while (tail->next != NULL)
tail = tail->next;
tail->next = newalt;

if (is_alternate_allowed(target.buf)) {
warning("adding alternate object store: %s",
target.buf);
newalt = xmalloc(sizeof(*newalt));
newalt->next = NULL;
newalt->base = strbuf_detach(&target, NULL);
newalt->got_indices = 0;
newalt->packs = NULL;

while (tail->next != NULL)
tail = tail->next;
tail->next = newalt;
}
}
}
i = posn + 1;
Expand Down
35 changes: 21 additions & 14 deletions http.c
Expand Up @@ -636,11 +636,25 @@ void setup_curl_trace(CURL *handle)
curl_easy_setopt(handle, CURLOPT_DEBUGDATA, NULL);
}

static long get_curl_allowed_protocols(int from_user)
{
long allowed_protocols = 0;

if (is_transport_allowed("http", from_user))
allowed_protocols |= CURLPROTO_HTTP;
if (is_transport_allowed("https", from_user))
allowed_protocols |= CURLPROTO_HTTPS;
if (is_transport_allowed("ftp", from_user))
allowed_protocols |= CURLPROTO_FTP;
if (is_transport_allowed("ftps", from_user))
allowed_protocols |= CURLPROTO_FTPS;

return allowed_protocols;
}

static CURL *get_curl_handle(void)
{
CURL *result = curl_easy_init();
long allowed_protocols = 0;

if (!result)
die("curl_easy_init failed");
Expand Down Expand Up @@ -736,20 +750,13 @@ static CURL *get_curl_handle(void)
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);
curl_easy_setopt(result, CURLOPT_PROTOCOLS, allowed_protocols);
curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS,
get_curl_allowed_protocols(0));
curl_easy_setopt(result, CURLOPT_PROTOCOLS,
get_curl_allowed_protocols(-1));
#else
if (transport_restrict_protocols())
warning("protocol restrictions not applied to curl redirects because\n"
"your curl version is too old (>= 7.19.4)");
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, 1L);
Expand Down

0 comments on commit 9d540e9

Please sign in to comment.