When restoring a directory, allow symlinks to be followed. The full
logic here gets a little complex, but the net effect is that the SECURE_SYMLINKS flag will prevent us from ever following a symlink. Without it, we'll only follow symlinks to dirs. bsdtar specifies SECURE_SYMLINKS by default, suppresses it for -P. I've also beefed up the write_disk_secure test to verify this behavior. PR: bin/126849
This commit is contained in:
parent
3726c6bbc1
commit
11663004bd
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=182838
@ -907,14 +907,26 @@ restore_entry(struct archive_write_disk *a)
|
||||
* We know something is in the way, but we don't know what;
|
||||
* we need to find out before we go any further.
|
||||
*/
|
||||
if (lstat(a->name, &a->st) != 0) {
|
||||
int r = 0;
|
||||
/*
|
||||
* The SECURE_SYMLINK logic has already removed a
|
||||
* symlink to a dir if the client wants that. So
|
||||
* follow the symlink if we're creating a dir.
|
||||
*/
|
||||
if (S_ISDIR(a->mode))
|
||||
r = stat(a->name, &a->st);
|
||||
/*
|
||||
* If it's not a dir (or it's a broken symlink),
|
||||
* then don't follow it.
|
||||
*/
|
||||
if (r != 0 || !S_ISDIR(a->mode))
|
||||
r = lstat(a->name, &a->st);
|
||||
if (r != 0) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Can't stat existing object");
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
/* TODO: if it's a symlink... */
|
||||
|
||||
/*
|
||||
* NO_OVERWRITE_NEWER doesn't apply to directories.
|
||||
*/
|
||||
|
@ -105,6 +105,79 @@ DEFINE_TEST(test_write_disk_secure)
|
||||
archive_entry_free(ae);
|
||||
assert(0 == archive_write_finish_entry(a));
|
||||
|
||||
/*
|
||||
* Without security checks, extracting a dir over a link to a
|
||||
* dir should follow the link.
|
||||
*/
|
||||
/* Create a symlink to a dir. */
|
||||
assert((ae = archive_entry_new()) != NULL);
|
||||
archive_entry_copy_pathname(ae, "link_to_dir3");
|
||||
archive_entry_set_mode(ae, S_IFLNK | 0777);
|
||||
archive_entry_set_symlink(ae, "dir");
|
||||
archive_write_disk_set_options(a, 0);
|
||||
assert(0 == archive_write_header(a, ae));
|
||||
assert(0 == archive_write_finish_entry(a));
|
||||
/* Extract a dir whose name matches the symlink. */
|
||||
assert(archive_entry_clear(ae) != NULL);
|
||||
archive_entry_copy_pathname(ae, "link_to_dir3");
|
||||
archive_entry_set_mode(ae, S_IFDIR | 0777);
|
||||
assert(0 == archive_write_header(a, ae));
|
||||
assert(0 == archive_write_finish_entry(a));
|
||||
/* Verify link was followed. */
|
||||
assertEqualInt(0, lstat("link_to_dir3", &st));
|
||||
assert(S_ISLNK(st.st_mode));
|
||||
archive_entry_free(ae);
|
||||
|
||||
/*
|
||||
* As above, but a broken link, so the link should get replaced.
|
||||
*/
|
||||
/* Create a symlink to a dir. */
|
||||
assert((ae = archive_entry_new()) != NULL);
|
||||
archive_entry_copy_pathname(ae, "link_to_dir4");
|
||||
archive_entry_set_mode(ae, S_IFLNK | 0777);
|
||||
archive_entry_set_symlink(ae, "nonexistent_dir");
|
||||
archive_write_disk_set_options(a, 0);
|
||||
assert(0 == archive_write_header(a, ae));
|
||||
assert(0 == archive_write_finish_entry(a));
|
||||
/* Extract a dir whose name matches the symlink. */
|
||||
assert(archive_entry_clear(ae) != NULL);
|
||||
archive_entry_copy_pathname(ae, "link_to_dir4");
|
||||
archive_entry_set_mode(ae, S_IFDIR | 0777);
|
||||
assert(0 == archive_write_header(a, ae));
|
||||
assert(0 == archive_write_finish_entry(a));
|
||||
/* Verify link was followed. */
|
||||
assertEqualInt(0, lstat("link_to_dir4", &st));
|
||||
assert(S_ISDIR(st.st_mode));
|
||||
archive_entry_free(ae);
|
||||
|
||||
/*
|
||||
* As above, but a link to a non-dir, so the link should get replaced.
|
||||
*/
|
||||
/* Create a regular file and a symlink to it */
|
||||
assert((ae = archive_entry_new()) != NULL);
|
||||
archive_entry_copy_pathname(ae, "non_dir");
|
||||
archive_entry_set_mode(ae, S_IFREG | 0777);
|
||||
archive_write_disk_set_options(a, 0);
|
||||
assert(0 == archive_write_header(a, ae));
|
||||
assert(0 == archive_write_finish_entry(a));
|
||||
/* Create symlink to the file. */
|
||||
archive_entry_copy_pathname(ae, "link_to_dir5");
|
||||
archive_entry_set_mode(ae, S_IFLNK | 0777);
|
||||
archive_entry_set_symlink(ae, "non_dir");
|
||||
archive_write_disk_set_options(a, 0);
|
||||
assert(0 == archive_write_header(a, ae));
|
||||
assert(0 == archive_write_finish_entry(a));
|
||||
/* Extract a dir whose name matches the symlink. */
|
||||
assert(archive_entry_clear(ae) != NULL);
|
||||
archive_entry_copy_pathname(ae, "link_to_dir5");
|
||||
archive_entry_set_mode(ae, S_IFDIR | 0777);
|
||||
assert(0 == archive_write_header(a, ae));
|
||||
assert(0 == archive_write_finish_entry(a));
|
||||
/* Verify link was followed. */
|
||||
assertEqualInt(0, lstat("link_to_dir5", &st));
|
||||
assert(S_ISDIR(st.st_mode));
|
||||
|
||||
|
||||
#if ARCHIVE_VERSION_NUMBER < 2000000
|
||||
archive_write_finish(a);
|
||||
#else
|
||||
|
Loading…
Reference in New Issue
Block a user