Skip to content

Commit

Permalink
Add ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS option
Browse files Browse the repository at this point in the history
This fixes a directory traversal in the cpio tool.
  • Loading branch information
ghedo committed Mar 4, 2015
1 parent fc04ba0 commit 5935715
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 4 deletions.
3 changes: 2 additions & 1 deletion cpio/bsdcpio.1
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ See above for description.
.It Fl Fl insecure
(i and p mode only)
Disable security checks during extraction or copying.
This allows extraction via symbolic links and path names containing
This allows extraction via symbolic links, absolute paths,
and path names containing
.Sq ..
in the name.
.It Fl J , Fl Fl xz
Expand Down
2 changes: 2 additions & 0 deletions cpio/cpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ main(int argc, char *argv[])
cpio->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER;
cpio->extract_flags |= ARCHIVE_EXTRACT_SECURE_SYMLINKS;
cpio->extract_flags |= ARCHIVE_EXTRACT_SECURE_NODOTDOT;
cpio->extract_flags |= ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS;
cpio->extract_flags |= ARCHIVE_EXTRACT_PERM;
cpio->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
cpio->extract_flags |= ARCHIVE_EXTRACT_ACL;
Expand Down Expand Up @@ -256,6 +257,7 @@ main(int argc, char *argv[])
case OPTION_INSECURE:
cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_SYMLINKS;
cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NODOTDOT;
cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS;
break;
case 'L': /* GNU cpio */
cpio->option_follow_links = 1;
Expand Down
2 changes: 2 additions & 0 deletions libarchive/archive.h
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,8 @@ __LA_DECL int archive_read_set_passphrase_callback(struct archive *,
/* Default: Do not use HFS+ compression if it was not compressed. */
/* This has no effect except on Mac OS v10.6 or later. */
#define ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED (0x8000)
/* Default: Do not reject entries with absolute paths */
#define ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS (0x10000)

__LA_DECL int archive_read_extract(struct archive *, struct archive_entry *,
int flags);
Expand Down
3 changes: 3 additions & 0 deletions libarchive/archive_write_disk.3
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ The default is to not refuse such paths.
Note that paths ending in
.Pa ..
always cause an error, regardless of this flag.
.It Cm ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS
Refuse to extract an absolute path.
The default is to not refuse such paths.
.It Cm ARCHIVE_EXTRACT_SPARSE
Scan data for blocks of NUL bytes and try to recreate them with holes.
This results in sparse files, independent of whether the archive format
Expand Down
14 changes: 11 additions & 3 deletions libarchive/archive_write_disk_posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -2509,8 +2509,9 @@ cleanup_pathname_win(struct archive_write_disk *a)
/*
* Canonicalize the pathname. In particular, this strips duplicate
* '/' characters, '.' elements, and trailing '/'. It also raises an
* error for an empty path, a trailing '..' or (if _SECURE_NODOTDOT is
* set) any '..' in the path.
* error for an empty path, a trailing '..', (if _SECURE_NODOTDOT is
* set) any '..' in the path or (if ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS
* is set) if the path is absolute.
*/
static int
cleanup_pathname(struct archive_write_disk *a)
Expand All @@ -2529,8 +2530,15 @@ cleanup_pathname(struct archive_write_disk *a)
cleanup_pathname_win(a);
#endif
/* Skip leading '/'. */
if (*src == '/')
if (*src == '/') {
if (a->flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Path is absolute");
return (ARCHIVE_FAILED);
}

separator = *src++;
}

/* Scan the pathname one element at a time. */
for (;;) {
Expand Down
23 changes: 23 additions & 0 deletions libarchive/test/test_write_disk_secure.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,29 @@ DEFINE_TEST(test_write_disk_secure)
assert(S_ISDIR(st.st_mode));
archive_entry_free(ae);

/*
* Without security checks, we should be able to
* extract an absolute path.
*/
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp");
archive_entry_set_mode(ae, S_IFREG | 0777);
assert(0 == archive_write_header(a, ae));
assert(0 == archive_write_finish_entry(a));
assertFileExists("/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp");
assert(0 == unlink("/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp"));

/* But with security checks enabled, this should fail. */
assert(archive_entry_clear(ae) != NULL);
archive_entry_copy_pathname(ae, "/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp");
archive_entry_set_mode(ae, S_IFREG | 0777);
archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS);
failure("Extracting an absolute path should fail here.");
assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
archive_entry_free(ae);
assert(0 == archive_write_finish_entry(a));
assertFileNotExists("/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp");

assertEqualInt(ARCHIVE_OK, archive_write_free(a));

/* Test the entries on disk. */
Expand Down

0 comments on commit 5935715

Please sign in to comment.