do not allow symlinks pointing into the .got directory; noticed by semarie
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
diff --git a/lib/worktree.c b/lib/worktree.c
index be2544b..879e8cb 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -954,6 +954,7 @@ install_symlink(struct got_worktree *worktree, const char *ondisk_path,
char target_path[PATH_MAX];
size_t len, target_len = 0;
char *resolved_path = NULL, *abspath = NULL;
+ char *path_got = NULL;
const uint8_t *buf = got_object_blob_get_read_buf(blob);
size_t hdrlen = got_object_blob_get_hdrlen(blob);
@@ -1025,6 +1026,23 @@ install_symlink(struct got_worktree *worktree, const char *ondisk_path,
goto done;
}
+ /* Do not allow symlinks pointing into the .got directory. */
+ if (asprintf(&path_got, "%s/%s", worktree->root_path,
+ GOT_WORKTREE_GOT_DIR) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+ if (got_path_is_child(resolved_path ? resolved_path : (abspath ?
+ abspath : target_path), path_got, strlen(path_got))) {
+ /* install as a regular file */
+ got_object_blob_rewind(blob);
+ err = install_blob(worktree, ondisk_path, path,
+ GOT_DEFAULT_FILE_MODE, st_mode, blob,
+ restoring_missing_file, reverting_versioned_file,
+ repo, progress_cb, progress_arg);
+ goto done;
+ }
+
if (symlink(target_path, ondisk_path) == -1) {
if (errno == EEXIST) {
struct stat sb;
@@ -1108,6 +1126,7 @@ install_symlink(struct got_worktree *worktree, const char *ondisk_path,
done:
free(resolved_path);
free(abspath);
+ free(path_got);
return err;
}
diff --git a/regress/cmdline/checkout.sh b/regress/cmdline/checkout.sh
index 6cde66b..a7cf834 100755
--- a/regress/cmdline/checkout.sh
+++ b/regress/cmdline/checkout.sh
@@ -511,6 +511,7 @@ function test_checkout_symlink {
(cd $testroot/repo && ln -s /etc/passwd passwd.link)
(cd $testroot/repo && ln -s ../beta epsilon/beta.link)
(cd $testroot/repo && ln -s nonexistent nonexistent.link)
+ (cd $testroot/repo && ln -s .got/foo dotgotfoo.link)
(cd $testroot/repo && git add .)
git_commit $testroot/repo -m "add symlinks"
@@ -587,6 +588,23 @@ function test_checkout_symlink {
ret="$?"
if [ "$ret" != "0" ]; then
diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ fi
+
+ if [ -h $testroot/wt/dotgotfoo.link ]; then
+ echo -n "dotgotfoo.link symlink points into .got dir: " >&2
+ readlink $testroot/wt/dotgotfoo.link >&2
+ test_done "$testroot" "1"
+ return 1
+ fi
+
+ echo -n ".got/foo" > $testroot/content.expected
+ cp $testroot/wt/dotgotfoo.link $testroot/content
+
+ cmp -s $testroot/content.expected $testroot/content
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/content.expected $testroot/content
fi
test_done "$testroot" "$ret"
}