Re: [PATCH 2/2] setup: make bareRepository=explicit work in GIT_DIR of a secondary worktree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Mar 8, 2024 at 1:20 PM Junio C Hamano <gitster@xxxxxxxxx> wrote:
>
> If you have /var/tmp/primary/ as a repository, and if you create a
> secondary worktree of it at /var/tmp/secondary/, the layout would
> look like this:
>
>     $ cd /var/tmp/
>     $ git init primary
>     $ cd primary
>     $ pwd
>     /var/tmp/primary
>     $ git worktree add ../secondary
>     $ cat ../seconary/.git

Nit: typo, should be `secondary` (missing the `d`)


>     gitdir: /var/tmp/primary/.git/worktrees/secondary
>     $ ls /var/tmp/primary/.git/worktrees/secondary
>     commondir  gitdir  HEAD  index  refs
>     $ cat /var/tmp/primary/.git/worktrees/secondary/gitdir
>     /var/tmp/secondary/.git
>
> When the configuration variable 'safe.bareRepository=explicit' is
> set to explicit, the change made by 45bb9162 (setup: allow cwd=.git
> w/ bareRepository=explicit, 2024-01-20) allows you to work in the
> /var/tmp/primary/.git directory (i.e., $GIT_DIR of the primary
> worktree).  The idea is that if it is safe to work in the repository
> in its working tree, it should be equally safe to work in the
> ".git/" directory of that working tree, too.
>
> Now, for the same reason, let's allow command execution from within
> the $GIT_DIR directory of a secondary worktree.  This is useful for
> tools working with secondary worktrees when the 'bareRepository'
> setting is set to 'explicit'.
>
> In the previous commit, we created a helper function to house the
> logic that checks if a directory that looks like a bare repository
> is actually a part of a non-bare repository.  Extend the helper
> function to also check if the apparent bare-repository is a $GIT_DIR
> of a secondary worktree, by checking three things:
>
>  * The path to the $GIT_DIR must be a subdirectory of
>    ".git/worktrees/", which is the primary worktree [*].
>
>  * Such $GIT_DIR must have file "gitdir", that records the path of
>    the ".git" file that is at the root level of the secondary
>    worktree.
>
>  * That ".git" file in turn points back at the $GIT_DIR we are
>    inspecting.
>
> The latter two points are merely for checking sanity.  The security
> lies in the first requirement.
>
> Remember that a tree object with an entry whose pathname component
> is ".git" is forbidden at various levels (fsck, object transfer and
> checkout), so malicious projects cannot cause users to clone and
> checkout a crafted ".git" directory in a shell directory that
> pretends to be a working tree with that ".git" thing at its root
> level.  That is where 45bb9162 (setup: allow cwd=.git w/
> bareRepository=explicit, 2024-01-20) draws its security guarantee
> from.  And the solution for secondary worktrees in this commit draws
> its security guarantee from the same place.
>
> Signed-off-by: Junio C Hamano <gitster@xxxxxxxxx>
> ---
>  setup.c                         | 52 ++++++++++++++++++++++++++++++++-
>  t/t0035-safe-bare-repository.sh |  8 ++++-
>  2 files changed, 58 insertions(+), 2 deletions(-)
>
> diff --git a/setup.c b/setup.c
> index 3081be4970..68860dcd18 100644
> --- a/setup.c
> +++ b/setup.c
> @@ -1231,9 +1231,59 @@ static const char *allowed_bare_repo_to_string(
>         return NULL;
>  }
>
> +static int is_git_dir_of_secondary_worktree(const char *path)
> +{
> +       int result = 0; /* assume not */
> +       struct strbuf gitfile_here = STRBUF_INIT;
> +       struct strbuf gitfile_there = STRBUF_INIT;
> +       const char *gitfile_contents;
> +       int error_code = 0;
> +
> +       /*
> +        * We should be a subdirectory of /.git/worktrees inside
> +        * the $GIT_DIR of the primary worktree.
> +        *
> +        * NEEDSWORK: some folks create secondary worktrees out of a
> +        * bare repository; they don't count ;-), at least not yet.
> +        */
> +       if (!strstr(path, "/.git/worktrees/"))

Do we need to be concerned about path separators being different on
Windows? Or have they already been normalized here?

> +               goto out;
> +
> +       /*
> +        * Does gitdir that points at the ".git" file at the root of
> +        * the secondary worktree roundtrip here?
> +        */

What loss of security do we have if we don't have as stringent of a
check? i.e. if we just did `return !!strstr(path, "/.git/worktrees/)`?
Or maybe we even combine the existing ends_with(.git) check with this
and do something like:

static int is_under_dotgit_dir(const char *path) {
        char *dotgit = strstr(path, "/.git");
        return dotgit && (dotgit[5] == '\0' || dotgit[5] == '/');
}



> +       strbuf_addf(&gitfile_here, "%s/gitdir", path);
> +       if (!file_exists(gitfile_here.buf))
> +               goto out;
> +       if (strbuf_read_file(&gitfile_there, gitfile_here.buf, 0) < 0)
> +               goto out;
> +       strbuf_trim_trailing_newline(&gitfile_there);
> +
> +       gitfile_contents = read_gitfile_gently(gitfile_there.buf, &error_code);
> +       if ((!gitfile_contents) || strcmp(gitfile_contents, path))
> +               goto out;
> +
> +       /* OK, we are happy */
> +       result = 1;
> +
> +out:
> +       strbuf_release(&gitfile_here);
> +       strbuf_release(&gitfile_there);
> +       return result;
> +}
> +
>  static int is_repo_with_working_tree(const char *path)
>  {
> -       return ends_with_path_components(path, ".git");
> +       /* $GIT_DIR immediately below the primary working tree */
> +       if (ends_with_path_components(path, ".git"))
> +               return 1;
> +
> +       /* Are we in $GIT_DIR of a secondary worktree? */
> +       if (is_git_dir_of_secondary_worktree(path))
> +               return 1;
> +
> +       return 0;
>  }
>
>  /*
> diff --git a/t/t0035-safe-bare-repository.sh b/t/t0035-safe-bare-repository.sh
> index 8048856379..62cdfcefc1 100755
> --- a/t/t0035-safe-bare-repository.sh
> +++ b/t/t0035-safe-bare-repository.sh
> @@ -31,7 +31,9 @@ expect_rejected () {
>
>  test_expect_success 'setup bare repo in worktree' '
>         git init outer-repo &&
> -       git init --bare outer-repo/bare-repo
> +       git init --bare outer-repo/bare-repo &&
> +       git -C outer-repo worktree add ../outer-secondary &&
> +       test_path_is_dir outer-secondary
>  '
>
>  test_expect_success 'safe.bareRepository unset' '
> @@ -86,4 +88,8 @@ test_expect_success 'no trace when "bare repository" is a subdir of .git' '
>         expect_accepted_implicit -C outer-repo/.git/objects
>  '
>
> +test_expect_success 'no trace in $GIT_DIR of secondary worktree' '
> +       expect_accepted_implicit -C outer-repo/.git/worktrees/outer-secondary
> +'
> +
>  test_done
> --
> 2.44.0-165-ge09f1254c5
>





[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux