Skip to content

Commit f4aa8c8

Browse files
committed
fetch/clone: detect dubious ownership of local repositories
When cloning from somebody else's repositories, it is possible that, say, the `upload-pack` command is overridden in the repository that is about to be cloned, which would then be run in the user's context who started the clone. To remind the user that this is a potentially unsafe operation, let's extend the ownership checks we have already established for regular gitdir discovery to extend also to local repositories that are about to be cloned. This protection extends also to file:// URLs. The fixes in this commit address CVE-2024-32004. Note: This commit does not touch the `fetch`/`clone` code directly, but instead the function used implicitly by both: `enter_repo()`. This function is also used by `git receive-pack` (i.e. pushes), by `git upload-archive`, by `git daemon` and by `git http-backend`. In setups that want to serve repositories owned by different users than the account running the service, this will require `safe.*` settings to be configured accordingly. Also note: there are tiny time windows where a time-of-check-time-of-use ("TOCTOU") race is possible. The real solution to those would be to work with `fstat()` and `openat()`. However, the latter function is not available on Windows (and would have to be emulated with rather expensive low-level `NtCreateFile()` calls), and the changes would be quite extensive, for my taste too extensive for the little gain given that embargoed releases need to pay extra attention to avoid introducing inadvertent bugs. Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 5c5a4a1 commit f4aa8c8

File tree

4 files changed

+38
-3
lines changed

4 files changed

+38
-3
lines changed

cache.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,18 @@ void set_git_work_tree(const char *tree);
606606

607607
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
608608

609+
/*
610+
* Check if a repository is safe and die if it is not, by verifying the
611+
* ownership of the worktree (if any), the git directory, and the gitfile (if
612+
* any).
613+
*
614+
* Exemptions for known-safe repositories can be added via `safe.directory`
615+
* config settings; for non-bare repositories, their worktree needs to be
616+
* added, for bare ones their git directory.
617+
*/
618+
void die_upon_dubious_ownership(const char *gitfile, const char *worktree,
619+
const char *gitdir);
620+
609621
void setup_work_tree(void);
610622
/*
611623
* Find the commondir and gitdir of the repository that contains the current

path.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,7 @@ const char *enter_repo(const char *path, int strict)
840840
if (!suffix[i])
841841
return NULL;
842842
gitfile = read_gitfile(used_path.buf);
843+
die_upon_dubious_ownership(gitfile, NULL, used_path.buf);
843844
if (gitfile) {
844845
strbuf_reset(&used_path);
845846
strbuf_addstr(&used_path, gitfile);
@@ -850,6 +851,7 @@ const char *enter_repo(const char *path, int strict)
850851
}
851852
else {
852853
const char *gitfile = read_gitfile(path);
854+
die_upon_dubious_ownership(gitfile, NULL, path);
853855
if (gitfile)
854856
path = gitfile;
855857
if (chdir(path))

setup.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,6 +1165,27 @@ static int ensure_valid_ownership(const char *gitfile,
11651165
return data.is_safe;
11661166
}
11671167

1168+
void die_upon_dubious_ownership(const char *gitfile, const char *worktree,
1169+
const char *gitdir)
1170+
{
1171+
struct strbuf report = STRBUF_INIT, quoted = STRBUF_INIT;
1172+
const char *path;
1173+
1174+
if (ensure_valid_ownership(gitfile, worktree, gitdir, &report))
1175+
return;
1176+
1177+
strbuf_complete(&report, '\n');
1178+
path = gitfile ? gitfile : gitdir;
1179+
sq_quote_buf_pretty(&quoted, path);
1180+
1181+
die(_("detected dubious ownership in repository at '%s'\n"
1182+
"%s"
1183+
"To add an exception for this directory, call:\n"
1184+
"\n"
1185+
"\tgit config --global --add safe.directory %s"),
1186+
path, report.buf, quoted.buf);
1187+
}
1188+
11681189
static int allowed_bare_repo_cb(const char *key, const char *value, void *d)
11691190
{
11701191
enum allowed_bare_repo *allowed_bare_repo = d;

t/t0411-clone-from-partial.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ test_expect_success 'create evil repo' '
2323
>evil/.git/shallow
2424
'
2525

26-
test_expect_failure 'local clone must not fetch from promisor remote and execute script' '
26+
test_expect_success 'local clone must not fetch from promisor remote and execute script' '
2727
rm -f script-executed &&
2828
test_must_fail git clone \
2929
--upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
@@ -32,7 +32,7 @@ test_expect_failure 'local clone must not fetch from promisor remote and execute
3232
test_path_is_missing script-executed
3333
'
3434

35-
test_expect_failure 'clone from file://... must not fetch from promisor remote and execute script' '
35+
test_expect_success 'clone from file://... must not fetch from promisor remote and execute script' '
3636
rm -f script-executed &&
3737
test_must_fail git clone \
3838
--upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
@@ -41,7 +41,7 @@ test_expect_failure 'clone from file://... must not fetch from promisor remote a
4141
test_path_is_missing script-executed
4242
'
4343

44-
test_expect_failure 'fetch from file://... must not fetch from promisor remote and execute script' '
44+
test_expect_success 'fetch from file://... must not fetch from promisor remote and execute script' '
4545
rm -f script-executed &&
4646
test_must_fail git fetch \
4747
--upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \

0 commit comments

Comments
 (0)