mirror of
https://github.com/git/git
synced 2024-10-02 14:45:21 +00:00
Merge branch 'ownership-checks-in-local-clones'
This topic addresses two CVEs: - CVE-2024-32020: Local clones may end up hardlinking files into the target repository's object database when source and target repository reside on the same disk. If the source repository is owned by a different user, then those hardlinked files may be rewritten at any point in time by the untrusted user. - CVE-2024-32021: When cloning a local source repository that contains symlinks via the filesystem, Git may create hardlinks to arbitrary user-readable files on the same filesystem as the target repository in the objects/ directory. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
commit
9e65df5eab
|
@ -320,7 +320,20 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
|
|||
int src_len, dest_len;
|
||||
struct dir_iterator *iter;
|
||||
int iter_status;
|
||||
struct strbuf realpath = STRBUF_INIT;
|
||||
|
||||
/*
|
||||
* Refuse copying directories by default which aren't owned by us. The
|
||||
* code that performs either the copying or hardlinking is not prepared
|
||||
* to handle various edge cases where an adversary may for example
|
||||
* racily swap out files for symlinks. This can cause us to
|
||||
* inadvertently use the wrong source file.
|
||||
*
|
||||
* Furthermore, even if we were prepared to handle such races safely,
|
||||
* creating hardlinks across user boundaries is an inherently unsafe
|
||||
* operation as the hardlinked files can be rewritten at will by the
|
||||
* potentially-untrusted user. We thus refuse to do so by default.
|
||||
*/
|
||||
die_upon_dubious_ownership(NULL, NULL, src_repo);
|
||||
|
||||
mkdir_if_missing(dest->buf, 0777);
|
||||
|
||||
|
@ -358,9 +371,27 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
|
|||
if (unlink(dest->buf) && errno != ENOENT)
|
||||
die_errno(_("failed to unlink '%s'"), dest->buf);
|
||||
if (!option_no_hardlinks) {
|
||||
strbuf_realpath(&realpath, src->buf, 1);
|
||||
if (!link(realpath.buf, dest->buf))
|
||||
if (!link(src->buf, dest->buf)) {
|
||||
struct stat st;
|
||||
|
||||
/*
|
||||
* Sanity-check whether the created hardlink
|
||||
* actually links to the expected file now. This
|
||||
* catches time-of-check-time-of-use bugs in
|
||||
* case the source file was meanwhile swapped.
|
||||
*/
|
||||
if (lstat(dest->buf, &st))
|
||||
die(_("hardlink cannot be checked at '%s'"), dest->buf);
|
||||
if (st.st_mode != iter->st.st_mode ||
|
||||
st.st_ino != iter->st.st_ino ||
|
||||
st.st_dev != iter->st.st_dev ||
|
||||
st.st_size != iter->st.st_size ||
|
||||
st.st_uid != iter->st.st_uid ||
|
||||
st.st_gid != iter->st.st_gid)
|
||||
die(_("hardlink different from source at '%s'"), dest->buf);
|
||||
|
||||
continue;
|
||||
}
|
||||
if (option_local > 0)
|
||||
die_errno(_("failed to create link '%s'"), dest->buf);
|
||||
option_no_hardlinks = 1;
|
||||
|
@ -373,8 +404,6 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
|
|||
strbuf_setlen(src, src_len);
|
||||
die(_("failed to iterate over '%s'"), src->buf);
|
||||
}
|
||||
|
||||
strbuf_release(&realpath);
|
||||
}
|
||||
|
||||
static void clone_local(const char *src_repo, const char *dest_repo)
|
||||
|
|
|
@ -80,4 +80,28 @@ test_expect_success 'safe.directory in included file' '
|
|||
git status
|
||||
'
|
||||
|
||||
test_expect_success 'local clone of unowned repo refused in unsafe directory' '
|
||||
test_when_finished "rm -rf source" &&
|
||||
git init source &&
|
||||
(
|
||||
sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
|
||||
test_commit -C source initial
|
||||
) &&
|
||||
test_must_fail git clone --local source target &&
|
||||
test_path_is_missing target
|
||||
'
|
||||
|
||||
test_expect_success 'local clone of unowned repo accepted in safe directory' '
|
||||
test_when_finished "rm -rf source" &&
|
||||
git init source &&
|
||||
(
|
||||
sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
|
||||
test_commit -C source initial
|
||||
) &&
|
||||
test_must_fail git clone --local source target &&
|
||||
git config --global --add safe.directory "$(pwd)/source/.git" &&
|
||||
git clone --local source target &&
|
||||
test_path_is_dir target
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
Loading…
Reference in a new issue