Git fork

Merge branch 'bc/allow-upload-pack-from-other-people'

Loosen overly strict ownership check introduced in the recent past,
to keep the promise "cloning a suspicious repository is a safe
first step to inspect it".

* bc/allow-upload-pack-from-other-people:
Allow cloning from repositories owned by another user

+49 -11
+9
Documentation/git-clone.txt
··· 63 prevent the unintentional copying of files by dereferencing the symbolic 64 links. 65 + 66 *NOTE*: this operation can race with concurrent modification to the 67 source repository, similar to running `cp -r <src> <dst>` while modifying 68 _<src>_. ··· 382 + 383 ------------ 384 $ git clone --bare -l /home/proj/.git /pub/scm/proj.git 385 ------------ 386 387 CONFIGURATION
··· 63 prevent the unintentional copying of files by dereferencing the symbolic 64 links. 65 + 66 + This option does not work with repositories owned by other users for security 67 + reasons, and `--no-local` must be specified for the clone to succeed. 68 + + 69 *NOTE*: this operation can race with concurrent modification to the 70 source repository, similar to running `cp -r <src> <dst>` while modifying 71 _<src>_. ··· 385 + 386 ------------ 387 $ git clone --bare -l /home/proj/.git /pub/scm/proj.git 388 + ------------ 389 + 390 + * Clone a local repository from a different user: 391 + + 392 + ------------ 393 + $ git clone --no-local /home/otheruser/proj.git /pub/scm/proj.git 394 ------------ 395 396 CONFIGURATION
+4 -1
builtin/upload-pack.c
··· 39 N_("interrupt transfer after <n> seconds of inactivity")), 40 OPT_END() 41 }; 42 43 packet_trace_identity("upload-pack"); 44 disable_replace_refs(); ··· 54 55 dir = argv[0]; 56 57 - if (!enter_repo(dir, strict)) 58 die("'%s' does not appear to be a git repository", dir); 59 60 switch (determine_protocol_version_server()) {
··· 39 N_("interrupt transfer after <n> seconds of inactivity")), 40 OPT_END() 41 }; 42 + unsigned enter_repo_flags = ENTER_REPO_ANY_OWNER_OK; 43 44 packet_trace_identity("upload-pack"); 45 disable_replace_refs(); ··· 55 56 dir = argv[0]; 57 58 + if (strict) 59 + enter_repo_flags |= ENTER_REPO_STRICT; 60 + if (!enter_repo(dir, enter_repo_flags)) 61 die("'%s' does not appear to be a git repository", dir); 62 63 switch (determine_protocol_version_server()) {
+4 -2
daemon.c
··· 152 size_t rlen; 153 const char *path; 154 const char *dir; 155 156 dir = directory; 157 ··· 242 dir = rpath; 243 } 244 245 - path = enter_repo(dir, strict_paths); 246 if (!path && base_path && base_path_relaxed) { 247 /* 248 * if we fail and base_path_relaxed is enabled, try without 249 * prefixing the base path 250 */ 251 dir = directory; 252 - path = enter_repo(dir, strict_paths); 253 } 254 255 if (!path) {
··· 152 size_t rlen; 153 const char *path; 154 const char *dir; 155 + unsigned enter_repo_flags; 156 157 dir = directory; 158 ··· 243 dir = rpath; 244 } 245 246 + enter_repo_flags = strict_paths ? ENTER_REPO_STRICT : 0; 247 + path = enter_repo(dir, enter_repo_flags); 248 if (!path && base_path && base_path_relaxed) { 249 /* 250 * if we fail and base_path_relaxed is enabled, try without 251 * prefixing the base path 252 */ 253 dir = directory; 254 + path = enter_repo(dir, enter_repo_flags); 255 } 256 257 if (!path) {
+6 -4
path.c
··· 684 * links. User relative paths are also returned as they are given, 685 * except DWIM suffixing. 686 */ 687 - const char *enter_repo(const char *path, int strict) 688 { 689 static struct strbuf validated_path = STRBUF_INIT; 690 static struct strbuf used_path = STRBUF_INIT; ··· 692 if (!path) 693 return NULL; 694 695 - if (!strict) { 696 static const char *suffix[] = { 697 "/.git", "", ".git/.git", ".git", NULL, 698 }; ··· 736 if (!suffix[i]) 737 return NULL; 738 gitfile = read_gitfile(used_path.buf); 739 - die_upon_dubious_ownership(gitfile, NULL, used_path.buf); 740 if (gitfile) { 741 strbuf_reset(&used_path); 742 strbuf_addstr(&used_path, gitfile); ··· 747 } 748 else { 749 const char *gitfile = read_gitfile(path); 750 - die_upon_dubious_ownership(gitfile, NULL, path); 751 if (gitfile) 752 path = gitfile; 753 if (chdir(path))
··· 684 * links. User relative paths are also returned as they are given, 685 * except DWIM suffixing. 686 */ 687 + const char *enter_repo(const char *path, unsigned flags) 688 { 689 static struct strbuf validated_path = STRBUF_INIT; 690 static struct strbuf used_path = STRBUF_INIT; ··· 692 if (!path) 693 return NULL; 694 695 + if (!(flags & ENTER_REPO_STRICT)) { 696 static const char *suffix[] = { 697 "/.git", "", ".git/.git", ".git", NULL, 698 }; ··· 736 if (!suffix[i]) 737 return NULL; 738 gitfile = read_gitfile(used_path.buf); 739 + if (!(flags & ENTER_REPO_ANY_OWNER_OK)) 740 + die_upon_dubious_ownership(gitfile, NULL, used_path.buf); 741 if (gitfile) { 742 strbuf_reset(&used_path); 743 strbuf_addstr(&used_path, gitfile); ··· 748 } 749 else { 750 const char *gitfile = read_gitfile(path); 751 + if (!(flags & ENTER_REPO_ANY_OWNER_OK)) 752 + die_upon_dubious_ownership(gitfile, NULL, path); 753 if (gitfile) 754 path = gitfile; 755 if (chdir(path))
+16 -1
path.h
··· 156 int adjust_shared_perm(const char *path); 157 158 char *interpolate_path(const char *path, int real_home); 159 - const char *enter_repo(const char *path, int strict); 160 const char *remove_leading_path(const char *in, const char *prefix); 161 const char *relative_path(const char *in, const char *prefix, struct strbuf *sb); 162 int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
··· 156 int adjust_shared_perm(const char *path); 157 158 char *interpolate_path(const char *path, int real_home); 159 + 160 + /* The bits are as follows: 161 + * 162 + * - ENTER_REPO_STRICT: callers that require exact paths (as opposed 163 + * to allowing known suffixes like ".git", ".git/.git" to be 164 + * omitted) can set this bit. 165 + * 166 + * - ENTER_REPO_ANY_OWNER_OK: callers that are willing to run without 167 + * ownership check can set this bit. 168 + */ 169 + enum { 170 + ENTER_REPO_STRICT = (1<<0), 171 + ENTER_REPO_ANY_OWNER_OK = (1<<1), 172 + }; 173 + 174 + const char *enter_repo(const char *path, unsigned flags); 175 const char *remove_leading_path(const char *in, const char *prefix); 176 const char *relative_path(const char *in, const char *prefix, struct strbuf *sb); 177 int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
-3
t/t0411-clone-from-partial.sh
··· 28 test_must_fail git clone \ 29 --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \ 30 evil clone1 2>err && 31 - test_grep "detected dubious ownership" err && 32 test_grep ! "fake-upload-pack running" err && 33 test_path_is_missing script-executed 34 ' ··· 38 test_must_fail git clone \ 39 --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \ 40 "file://$(pwd)/evil" clone2 2>err && 41 - test_grep "detected dubious ownership" err && 42 test_grep ! "fake-upload-pack running" err && 43 test_path_is_missing script-executed 44 ' ··· 48 test_must_fail git fetch \ 49 --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \ 50 "file://$(pwd)/evil" 2>err && 51 - test_grep "detected dubious ownership" err && 52 test_grep ! "fake-upload-pack running" err && 53 test_path_is_missing script-executed 54 '
··· 28 test_must_fail git clone \ 29 --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \ 30 evil clone1 2>err && 31 test_grep ! "fake-upload-pack running" err && 32 test_path_is_missing script-executed 33 ' ··· 37 test_must_fail git clone \ 38 --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \ 39 "file://$(pwd)/evil" clone2 2>err && 40 test_grep ! "fake-upload-pack running" err && 41 test_path_is_missing script-executed 42 ' ··· 46 test_must_fail git fetch \ 47 --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \ 48 "file://$(pwd)/evil" 2>err && 49 test_grep ! "fake-upload-pack running" err && 50 test_path_is_missing script-executed 51 '
+10
t/t5605-clone-local.sh
··· 153 ! repo_is_hardlinked force-nonlocal 154 ' 155 156 test_expect_success 'cloning locally respects "-u" for fetching refs' ' 157 test_must_fail git clone --bare -u false a should_not_work.git 158 '
··· 153 ! repo_is_hardlinked force-nonlocal 154 ' 155 156 + test_expect_success 'cloning a local path with --no-local from a different user succeeds' ' 157 + git clone --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \ 158 + --no-local a nonlocal-otheruser 2>err && 159 + ! repo_is_hardlinked nonlocal-otheruser && 160 + # Verify that this is a git repository. 161 + git -C nonlocal-otheruser rev-parse --show-toplevel && 162 + ! test_grep "detected dubious ownership" err 163 + 164 + ' 165 + 166 test_expect_success 'cloning locally respects "-u" for fetching refs' ' 167 test_must_fail git clone --bare -u false a should_not_work.git 168 '