Files
git/builtin/upload-pack.c
brian m. carlson 0ffb5a6bf1 Allow cloning from repositories owned by another user
Historically, Git has allowed users to clone from an untrusted
repository, and we have documented that this is safe to do so:

    `upload-pack` tries to avoid any dangerous configuration options or
    hooks from the repository it's serving, making it safe to clone an
    untrusted directory and run commands on the resulting clone.

However, this was broken by f4aa8c8bb1 ("fetch/clone: detect dubious
ownership of local repositories", 2024-04-10) in an attempt to make
things more secure.  That change resulted in a variety of problems when
cloning locally and over SSH, but it did not change the stated security
boundary.  Because the security boundary has not changed, it is safe to
adjust part of the code that patch introduced.

To do that and restore the previous functionality, adjust enter_repo to
take two flags instead of one.

The two bits are

 - ENTER_REPO_STRICT: callers that require exact paths (as opposed
   to allowing known suffixes like ".git", ".git/.git" to be
   omitted) can set this bit.  Corresponds to the "strict" parameter
   that the flags word replaces.

 - ENTER_REPO_ANY_OWNER_OK: callers that are willing to run without
   ownership check can set this bit.

The former is --strict-paths option of "git daemon".  The latter is
set only by upload-pack, which honors the claimed security boundary.

Note that local clones across ownership boundaries require --no-local so
that upload-pack is used.  Document this fact in the manual page and
provide an example.

This patch was based on one written by Junio C Hamano.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-11-15 11:05:06 +09:00

83 lines
2.2 KiB
C

#include "builtin.h"
#include "exec-cmd.h"
#include "gettext.h"
#include "pkt-line.h"
#include "parse-options.h"
#include "path.h"
#include "protocol.h"
#include "replace-object.h"
#include "upload-pack.h"
#include "serve.h"
static const char * const upload_pack_usage[] = {
N_("git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
" [--advertise-refs] <directory>"),
NULL
};
int cmd_upload_pack(int argc, const char **argv, const char *prefix)
{
const char *dir;
int strict = 0;
int advertise_refs = 0;
int stateless_rpc = 0;
int timeout = 0;
struct option options[] = {
OPT_BOOL(0, "stateless-rpc", &stateless_rpc,
N_("quit after a single request/response exchange")),
OPT_HIDDEN_BOOL(0, "http-backend-info-refs", &advertise_refs,
N_("serve up the info/refs for git-http-backend")),
OPT_ALIAS(0, "advertise-refs", "http-backend-info-refs"),
OPT_BOOL(0, "strict", &strict,
N_("do not try <directory>/.git/ if <directory> is no Git directory")),
OPT_INTEGER(0, "timeout", &timeout,
N_("interrupt transfer after <n> seconds of inactivity")),
OPT_END()
};
unsigned enter_repo_flags = ENTER_REPO_ANY_OWNER_OK;
packet_trace_identity("upload-pack");
disable_replace_refs();
/* TODO: This should use NO_LAZY_FETCH_ENVIRONMENT */
xsetenv("GIT_NO_LAZY_FETCH", "1", 0);
argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
if (argc != 1)
usage_with_options(upload_pack_usage, options);
setup_path();
dir = argv[0];
if (strict)
enter_repo_flags |= ENTER_REPO_STRICT;
if (!enter_repo(dir, enter_repo_flags))
die("'%s' does not appear to be a git repository", dir);
switch (determine_protocol_version_server()) {
case protocol_v2:
if (advertise_refs)
protocol_v2_advertise_capabilities();
else
protocol_v2_serve_loop(stateless_rpc);
break;
case protocol_v1:
/*
* v1 is just the original protocol with a version string,
* so just fall through after writing the version string.
*/
if (advertise_refs || !stateless_rpc)
packet_write_fmt(1, "version 1\n");
/* fallthrough */
case protocol_v0:
upload_pack(advertise_refs, stateless_rpc, timeout);
break;
case protocol_unknown_version:
BUG("unknown protocol version");
}
return 0;
}