95 changes: 95 additions & 0 deletions libarchive/test/test_write_disk_secure744.c
@@ -0,0 +1,95 @@
/*-
* Copyright (c) 2003-2007,2016 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");

#define UMASK 022

/*
* Github Issue #744 describes a bug in the sandboxing code that
* causes very long pathnames to not get checked for symlinks.
*/

DEFINE_TEST(test_write_disk_secure744)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
skipping("archive_write_disk security checks not supported on Windows");
#else
struct archive *a;
struct archive_entry *ae;
size_t buff_size = 8192;
char *buff = malloc(buff_size);
char *p = buff;
int n = 0;
int t;

assert(buff != NULL);

/* Start with a known umask. */
assertUmask(UMASK);

/* Create an archive_write_disk object. */
assert((a = archive_write_disk_new()) != NULL);
archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS);

while (p + 500 < buff + buff_size) {
memset(p, 'x', 100);
p += 100;
p[0] = '\0';

buff[0] = ((n / 1000) % 10) + '0';
buff[1] = ((n / 100) % 10)+ '0';
buff[2] = ((n / 10) % 10)+ '0';
buff[3] = ((n / 1) % 10)+ '0';
buff[4] = '_';
++n;

/* Create a symlink pointing to the testworkdir */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, buff);
archive_entry_set_mode(ae, S_IFREG | 0777);
archive_entry_copy_symlink(ae, testworkdir);
assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
archive_entry_free(ae);

*p++ = '/';
sprintf(p, "target%d", n);

/* Try to create a file through the symlink, should fail. */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, buff);
archive_entry_set_mode(ae, S_IFDIR | 0777);

t = archive_write_header(a, ae);
archive_entry_free(ae);
failure("Attempt to create target%d via %d-character symlink should have failed", n, (int)strlen(buff));
if(!assertEqualInt(ARCHIVE_FAILED, t)) {
break;
}
}
archive_free(a);
free(buff);
#endif
}
79 changes: 79 additions & 0 deletions libarchive/test/test_write_disk_secure745.c
@@ -0,0 +1,79 @@
/*-
* Copyright (c) 2003-2007,2016 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");

#define UMASK 022

/*
* Github Issue #745 describes a bug in the sandboxing code that
* allows one to use a symlink to edit the permissions on a file or
* directory outside of the sandbox.
*/

DEFINE_TEST(test_write_disk_secure745)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
skipping("archive_write_disk security checks not supported on Windows");
#else
struct archive *a;
struct archive_entry *ae;

/* Start with a known umask. */
assertUmask(UMASK);

/* Create an archive_write_disk object. */
assert((a = archive_write_disk_new()) != NULL);
archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS);

/* The target dir: The one we're going to try to change permission on */
assertMakeDir("target", 0700);

/* The sandbox dir we're going to run inside of. */
assertMakeDir("sandbox", 0700);
assertChdir("sandbox");

/* Create a symlink pointing to the target directory */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "sym");
archive_entry_set_mode(ae, AE_IFLNK | 0777);
archive_entry_copy_symlink(ae, "../target");
assert(0 == archive_write_header(a, ae));
archive_entry_free(ae);

/* Try to alter the target dir through the symlink; this should fail. */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "sym");
archive_entry_set_mode(ae, S_IFDIR | 0777);
assert(0 == archive_write_header(a, ae));
archive_entry_free(ae);

/* Permission of target dir should not have changed. */
assertFileMode("../target", 0700);

assert(0 == archive_write_close(a));
archive_write_free(a);
#endif
}
129 changes: 129 additions & 0 deletions libarchive/test/test_write_disk_secure746.c
@@ -0,0 +1,129 @@
/*-
* Copyright (c) 2003-2007,2016 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");

#define UMASK 022

/*
* Github Issue #746 describes a problem in which hardlink targets are
* not adequately checked and can be used to modify entries outside of
* the sandbox.
*/

/*
* Verify that ARCHIVE_EXTRACT_SECURE_NODOTDOT disallows '..' in hardlink
* targets.
*/
DEFINE_TEST(test_write_disk_secure746a)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
skipping("archive_write_disk security checks not supported on Windows");
#else
struct archive *a;
struct archive_entry *ae;

/* Start with a known umask. */
assertUmask(UMASK);

/* The target directory we're going to try to affect. */
assertMakeDir("target", 0700);
assertMakeFile("target/foo", 0700, "unmodified");

/* The sandbox dir we're going to work within. */
assertMakeDir("sandbox", 0700);
assertChdir("sandbox");

/* Create an archive_write_disk object. */
assert((a = archive_write_disk_new()) != NULL);
archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_NODOTDOT);

/* Attempt to hardlink to the target directory. */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "bar");
archive_entry_set_mode(ae, AE_IFREG | 0777);
archive_entry_set_size(ae, 8);
archive_entry_copy_hardlink(ae, "../target/foo");
assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
assertEqualInt(ARCHIVE_FATAL, archive_write_data(a, "modified", 8));
archive_entry_free(ae);

/* Verify that target file contents are unchanged. */
assertTextFileContents("unmodified", "../target/foo");
#endif
}

/*
* Verify that ARCHIVE_EXTRACT_SECURE_NOSYMLINK disallows symlinks in hardlink
* targets.
*/
DEFINE_TEST(test_write_disk_secure746b)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
skipping("archive_write_disk security checks not supported on Windows");
#else
struct archive *a;
struct archive_entry *ae;

/* Start with a known umask. */
assertUmask(UMASK);

/* The target directory we're going to try to affect. */
assertMakeDir("target", 0700);
assertMakeFile("target/foo", 0700, "unmodified");

/* The sandbox dir we're going to work within. */
assertMakeDir("sandbox", 0700);
assertChdir("sandbox");

/* Create an archive_write_disk object. */
assert((a = archive_write_disk_new()) != NULL);
archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS);

/* Create a symlink to the target directory. */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "symlink");
archive_entry_set_mode(ae, AE_IFLNK | 0777);
archive_entry_copy_symlink(ae, "../target");
assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
archive_entry_free(ae);

/* Attempt to hardlink to the target directory via the symlink. */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "bar");
archive_entry_set_mode(ae, AE_IFREG | 0777);
archive_entry_set_size(ae, 8);
archive_entry_copy_hardlink(ae, "symlink/foo");
assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae));
assertEqualIntA(a, ARCHIVE_FATAL, archive_write_data(a, "modified", 8));
archive_entry_free(ae);

/* Verify that target file contents are unchanged. */
assertTextFileContents("unmodified", "../target/foo");

assertEqualIntA(a, ARCHIVE_FATAL, archive_write_close(a));
archive_write_free(a);
#endif
}
12 changes: 9 additions & 3 deletions libarchive/test/test_write_format_gnutar_filenames.c
Expand Up @@ -42,6 +42,7 @@ DEFINE_TEST(test_write_format_gnutar_filenames)
struct archive_entry *ae, *template;
struct archive *a;
size_t used;
int i;

buff = malloc(buffsize); /* million bytes of work area */
assert(buff != NULL);
Expand All @@ -55,7 +56,7 @@ DEFINE_TEST(test_write_format_gnutar_filenames)
archive_entry_set_mode(template, S_IFREG | 0755);
archive_entry_set_size(template, 8);

for (int i = 0; i < 2000; ++i) {
for (i = 0; i < 2000; ++i) {
filename[i] = 'a';
filename[i + 1] = '\0';
archive_entry_copy_pathname(template, filename);
Expand Down Expand Up @@ -97,6 +98,11 @@ DEFINE_TEST(test_write_format_gnutar_linknames)
struct archive_entry *ae, *template;
struct archive *a;
size_t used;
int i;

#ifdef S_IFLNK
assertEqualInt(S_IFLNK, AE_IFLNK);
#endif

buff = malloc(buffsize); /* million bytes of work area */
assert(buff != NULL);
Expand All @@ -107,10 +113,10 @@ DEFINE_TEST(test_write_format_gnutar_linknames)
archive_entry_set_birthtime(template, 3, 30);
archive_entry_set_ctime(template, 4, 40);
archive_entry_set_mtime(template, 5, 50);
archive_entry_set_mode(template, S_IFLNK | 0755);
archive_entry_set_mode(template, AE_IFLNK | 0755);
archive_entry_copy_pathname(template, "link");

for (int i = 0; i < 2000; ++i) {
for (i = 0; i < 2000; ++i) {
filename[i] = 'a';
filename[i + 1] = '\0';
archive_entry_copy_symlink(template, filename);
Expand Down
203 changes: 188 additions & 15 deletions libarchive/test/test_write_format_iso9660.c
Expand Up @@ -117,8 +117,8 @@ DEFINE_TEST(test_write_format_iso9660)
*/
dirname[0] = '\0';
strcpy(dir, "/dir0");
for (i = 0; i < 10; i++) {
dir[4] = '0' + i;
for (i = 0; i < 13; i++) {
dir[4] = "0123456789ABCDEF"[i];
if (i == 0)
strcat(dirname, dir+1);
else
Expand All @@ -134,6 +134,19 @@ DEFINE_TEST(test_write_format_iso9660)
archive_entry_free(ae);
}

strcat(dirname, "/file");
assert((ae = archive_entry_new()) != NULL);
archive_entry_set_atime(ae, 2, 20);
archive_entry_set_birthtime(ae, 3, 30);
archive_entry_set_ctime(ae, 4, 40);
archive_entry_set_mtime(ae, 5, 50);
archive_entry_copy_pathname(ae, dirname);
archive_entry_set_mode(ae, S_IFREG | 0755);
archive_entry_set_size(ae, 8);
assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
archive_entry_free(ae);
assertEqualIntA(a, 8, archive_write_data(a, "12345678", 9));

/*
* "dir0/dir1/file1" has 8 bytes of data.
*/
Expand Down Expand Up @@ -332,6 +345,45 @@ DEFINE_TEST(test_write_format_iso9660)
assert((S_IFDIR | 0555) == archive_entry_mode(ae));
assertEqualInt(2048, archive_entry_size(ae));

/*
* Read "dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(2, archive_entry_atime(ae));
assertEqualInt(3, archive_entry_birthtime(ae));
assertEqualInt(4, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString("dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA",
archive_entry_pathname(ae));
assert((S_IFDIR | 0555) == archive_entry_mode(ae));
assertEqualInt(2048, archive_entry_size(ae));

/*
* Read "dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA/dirB"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(2, archive_entry_atime(ae));
assertEqualInt(3, archive_entry_birthtime(ae));
assertEqualInt(4, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString("dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA/dirB",
archive_entry_pathname(ae));
assert((S_IFDIR | 0555) == archive_entry_mode(ae));
assertEqualInt(2048, archive_entry_size(ae));

/*
* Read "dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA/dirB/dirC"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(2, archive_entry_atime(ae));
assertEqualInt(3, archive_entry_birthtime(ae));
assertEqualInt(4, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString("dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA/dirB/dirC",
archive_entry_pathname(ae));
assert((S_IFDIR | 0555) == archive_entry_mode(ae));
assertEqualInt(2048, archive_entry_size(ae));

/*
* Read "hardlnk"
*/
Expand Down Expand Up @@ -385,6 +437,21 @@ DEFINE_TEST(test_write_format_iso9660)
assertEqualIntA(a, 8, archive_read_data(a, buff2, 10));
assertEqualMem(buff2, "12345678", 8);

/*
* Read "dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA/dirB/dirC/file"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(2, archive_entry_atime(ae));
assertEqualInt(3, archive_entry_birthtime(ae));
assertEqualInt(4, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString("dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA/dirB/dirC/file", archive_entry_pathname(ae));
assert((AE_IFREG | 0555) == archive_entry_mode(ae));
assertEqualInt(1, archive_entry_nlink(ae));
assertEqualInt(8, archive_entry_size(ae));
assertEqualIntA(a, 8, archive_read_data(a, buff2, 10));
assertEqualMem(buff2, "12345678", 8);

/*
* Read "dir0/dir1/file1"
*/
Expand Down Expand Up @@ -580,29 +647,65 @@ DEFINE_TEST(test_write_format_iso9660)
assertEqualInt(2048, archive_entry_size(ae));

/*
* Read "hardlnk"
* Read "dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(5, archive_entry_atime(ae));
assertEqualInt(5, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString("hardlnk", archive_entry_pathname(ae));
assertEqualString("dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA",
archive_entry_pathname(ae));
assert((S_IFDIR | 0700) == archive_entry_mode(ae));
assertEqualInt(2048, archive_entry_size(ae));

/*
* Read "dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA/dirB"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(5, archive_entry_atime(ae));
assertEqualInt(5, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString("dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA/dirB",
archive_entry_pathname(ae));
assert((S_IFDIR | 0700) == archive_entry_mode(ae));
assertEqualInt(2048, archive_entry_size(ae));

/*
* Read "dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA/dirB/dirC"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(5, archive_entry_atime(ae));
assertEqualInt(5, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString("dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA/dirB/dirC",
archive_entry_pathname(ae));
assert((S_IFDIR | 0700) == archive_entry_mode(ae));
assertEqualInt(2048, archive_entry_size(ae));

/*
* Read "file"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(5, archive_entry_atime(ae));
assertEqualInt(5, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString("file", archive_entry_pathname(ae));
assert((AE_IFREG | 0400) == archive_entry_mode(ae));
assertEqualInt(2, archive_entry_nlink(ae));
assertEqualInt(8, archive_entry_size(ae));
assertEqualIntA(a, 8, archive_read_data(a, buff2, 10));
assertEqualMem(buff2, "12345678", 8);

/*
* Read "file"
* Read "hardlnk"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(5, archive_entry_atime(ae));
assertEqualInt(5, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString("file", archive_entry_pathname(ae));
assertEqualString("hardlnk", archive_entry_hardlink(ae));
assertEqualString("hardlnk", archive_entry_pathname(ae));
assertEqualString("file", archive_entry_hardlink(ae));
assert((AE_IFREG | 0400) == archive_entry_mode(ae));
assertEqualInt(2, archive_entry_nlink(ae));
assertEqualInt(0, archive_entry_size(ae));
assertEqualIntA(a, 0, archive_read_data(a, buff2, 10));

Expand All @@ -624,6 +727,22 @@ DEFINE_TEST(test_write_format_iso9660)
assertEqualIntA(a, 8, archive_read_data(a, buff2, 10));
assertEqualMem(buff2, "12345678", 8);

/*
* Read "dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA/dirB/dirC/file"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(5, archive_entry_atime(ae));
assertEqualInt(5, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString(
"dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dirA/dirB/dirC/file",
archive_entry_pathname(ae));
assert((AE_IFREG | 0400) == archive_entry_mode(ae));
assertEqualInt(1, archive_entry_nlink(ae));
assertEqualInt(8, archive_entry_size(ae));
assertEqualIntA(a, 8, archive_read_data(a, buff2, 10));
assertEqualMem(buff2, "12345678", 8);

/*
* Read "dir0/dir1/file1"
*/
Expand Down Expand Up @@ -745,6 +864,42 @@ DEFINE_TEST(test_write_format_iso9660)
assert((S_IFDIR | 0700) == archive_entry_mode(ae));
assertEqualInt(2048, archive_entry_size(ae));

/*
* Read "rr_moved/dir7/dir8/dir9/dira"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(5, archive_entry_atime(ae));
assertEqualInt(5, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString("RR_MOVED/DIR7/DIR8/DIR9/DIRA",
archive_entry_pathname(ae));
assert((S_IFDIR | 0700) == archive_entry_mode(ae));
assertEqualInt(2048, archive_entry_size(ae));

/*
* Read "rr_moved/dir7/dir8/dir9/dira/dirB"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(5, archive_entry_atime(ae));
assertEqualInt(5, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString("RR_MOVED/DIR7/DIR8/DIR9/DIRA/DIRB",
archive_entry_pathname(ae));
assert((S_IFDIR | 0700) == archive_entry_mode(ae));
assertEqualInt(2048, archive_entry_size(ae));

/*
* Read "rr_moved/dir7/dir8/dir9/dirA/dirB/dirC"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(5, archive_entry_atime(ae));
assertEqualInt(5, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString("RR_MOVED/DIR7/DIR8/DIR9/DIRA/DIRB/DIRC",
archive_entry_pathname(ae));
assert((S_IFDIR | 0700) == archive_entry_mode(ae));
assertEqualInt(2048, archive_entry_size(ae));

/*
* Read "dir0"
*/
Expand Down Expand Up @@ -827,33 +982,35 @@ DEFINE_TEST(test_write_format_iso9660)
assertEqualInt(2048, archive_entry_size(ae));

/*
* Read "file"
* Read "hardlink"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(5, archive_entry_atime(ae));
assertEqualInt(0, archive_entry_birthtime(ae));
assertEqualInt(5, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString("FILE", archive_entry_pathname(ae));
assertEqualString("HARDLNK", archive_entry_pathname(ae));
assertEqualString(NULL, archive_entry_hardlink(ae));
assert((AE_IFREG | 0400) == archive_entry_mode(ae));
assertEqualInt(2, archive_entry_nlink(ae));
assertEqualInt(8, archive_entry_size(ae));
assertEqualIntA(a, 8, archive_read_data(a, buff2, 10));
assertEqualMem(buff2, "12345678", 8);

/*
* Read "hardlink"
* Read "file"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(5, archive_entry_atime(ae));
assertEqualInt(0, archive_entry_birthtime(ae));
assertEqualInt(5, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString("HARDLNK", archive_entry_pathname(ae));
assertEqualString("FILE", archive_entry_hardlink(ae));
assertEqualString("FILE", archive_entry_pathname(ae));
assertEqualString("HARDLNK", archive_entry_hardlink(ae));
assert((AE_IFREG | 0400) == archive_entry_mode(ae));
assertEqualInt(2, archive_entry_nlink(ae));
assertEqualInt(0, archive_entry_size(ae));
assertEqualIntA(a, 0, archive_read_data(a, buff2, 10));


/*
* Read longname
*/
Expand All @@ -870,6 +1027,22 @@ DEFINE_TEST(test_write_format_iso9660)
assertEqualIntA(a, 8, archive_read_data(a, buff2, 10));
assertEqualMem(buff2, "12345678", 8);

/*
* Read "rr_moved/dir7/dir8/dir9/dirA/dirB/dirC/file"
*/
assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
assertEqualInt(5, archive_entry_atime(ae));
assertEqualInt(5, archive_entry_ctime(ae));
assertEqualInt(5, archive_entry_mtime(ae));
assertEqualString(
"RR_MOVED/DIR7/DIR8/DIR9/DIRA/DIRB/DIRC/FILE",
archive_entry_pathname(ae));
assert((AE_IFREG | 0400) == archive_entry_mode(ae));
assertEqualInt(1, archive_entry_nlink(ae));
assertEqualInt(8, archive_entry_size(ae));
assertEqualIntA(a, 8, archive_read_data(a, buff2, 10));
assertEqualMem(buff2, "12345678", 8);

/*
* Read "dir0/dir1/file1"
*/
Expand Down
33 changes: 21 additions & 12 deletions libarchive_fe/passphrase.c
Expand Up @@ -121,16 +121,21 @@ readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)

#else /* _WIN32 && !__CYGWIN__ */

#include <termios.h>
#include <signal.h>
#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif
#include <signal.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

#ifndef _PATH_TTY
#define _PATH_TTY "/dev/tty"
#endif

#ifdef TCSASOFT
# define _T_FLUSH (TCSAFLUSH|TCSASOFT)
#else
Expand All @@ -142,11 +147,18 @@ readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
# define _POSIX_VDISABLE VDISABLE
#endif

static volatile sig_atomic_t *signo;
#define M(a,b) (a > b ? a : b)
#define MAX_SIGNO M(M(M(SIGALRM, SIGHUP), \
M(SIGINT, SIGPIPE)), \
M(M(SIGQUIT, SIGTERM), \
M(M(SIGTSTP, SIGTTIN), SIGTTOU)))

static volatile sig_atomic_t signo[MAX_SIGNO + 1];

static void
handler(int s)
{
assert(s <= MAX_SIGNO);
signo[s] = 1;
}

Expand All @@ -166,12 +178,8 @@ readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
return(NULL);
}

if (signo == NULL) {
signo = calloc(SIGRTMAX, sizeof(sig_atomic_t));
}

restart:
for (i = 0; i < SIGRTMAX; i++)
for (i = 0; i <= MAX_SIGNO; i++)
signo[i] = 0;
nr = -1;
save_errno = 0;
Expand All @@ -198,6 +206,7 @@ readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0; /* don't restart system calls */
sa.sa_handler = handler;
/* Keep this list in sync with MAX_SIGNO! */
(void)sigaction(SIGALRM, &sa, &savealrm);
(void)sigaction(SIGHUP, &sa, &savehup);
(void)sigaction(SIGINT, &sa, &saveint);
Expand Down Expand Up @@ -237,11 +246,11 @@ readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
if (p < end) {
if ((flags & RPP_SEVENBIT))
ch &= 0x7f;
if (isalpha(ch)) {
if (isalpha((unsigned char)ch)) {
if ((flags & RPP_FORCELOWER))
ch = (char)tolower(ch);
ch = (char)tolower((unsigned char)ch);
if ((flags & RPP_FORCEUPPER))
ch = (char)toupper(ch);
ch = (char)toupper((unsigned char)ch);
}
*p++ = ch;
}
Expand Down Expand Up @@ -276,7 +285,7 @@ readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
* If we were interrupted by a signal, resend it to ourselves
* now that we have restored the signal handlers.
*/
for (i = 0; i < SIGRTMAX; i++) {
for (i = 0; i <= MAX_SIGNO; i++) {
if (signo[i]) {
kill(getpid(), i);
switch (i) {
Expand Down
1 change: 1 addition & 0 deletions tar/cmdline.c
Expand Up @@ -68,6 +68,7 @@ static const struct bsdtar_option {
{ "auto-compress", 0, 'a' },
{ "b64encode", 0, OPTION_B64ENCODE },
{ "block-size", 1, 'b' },
{ "blocking-factor", 1, 'b' },
{ "bunzip2", 0, 'j' },
{ "bzip", 0, 'j' },
{ "bzip2", 0, 'j' },
Expand Down
1 change: 1 addition & 0 deletions tar/subst.c
Expand Up @@ -84,6 +84,7 @@ add_substitution(struct bsdtar *bsdtar, const char *rule_text)
if (rule == NULL)
lafe_errc(1, errno, "Out of memory");
rule->next = NULL;
rule->result = NULL;

if (subst->last_rule == NULL)
subst->first_rule = rule;
Expand Down
3 changes: 2 additions & 1 deletion tar/test/CMakeLists.txt
Expand Up @@ -100,7 +100,8 @@ IF(ENABLE_TAR AND ENABLE_TEST)
# Experimental new test handling
ADD_CUSTOM_TARGET(run_bsdtar_test
COMMAND bsdtar_test -p $<TARGET_FILE:bsdtar>
-r ${CMAKE_CURRENT_SOURCE_DIR})
-r ${CMAKE_CURRENT_SOURCE_DIR}
-vv)
ADD_DEPENDENCIES(run_bsdtar_test bsdtar)
ADD_DEPENDENCIES(run_all_tests run_bsdtar_test)

Expand Down
86 changes: 83 additions & 3 deletions tar/test/main.c
Expand Up @@ -130,6 +130,13 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/test/main.c,v 1.6 2008/11/05 06:40:53 kientz
# include <crtdbg.h>
#endif

mode_t umasked(mode_t expected_mode)
{
mode_t mode = umask(0);
umask(mode);
return expected_mode & ~mode;
}

/* Path to working directory for current test */
const char *testworkdir;
#ifdef PROGRAM
Expand Down Expand Up @@ -1157,6 +1164,35 @@ assertion_file_contains_lines_any_order(const char *file, int line,
return (0);
}

/* Verify that a text file does not contains the specified strings */
int
assertion_file_contains_no_invalid_strings(const char *file, int line,
const char *pathname, const char *strings[])
{
char *buff;
int i;

buff = slurpfile(NULL, "%s", pathname);
if (buff == NULL) {
failure_start(file, line, "Can't read file: %s", pathname);
failure_finish(NULL);
return (0);
}

for (i = 0; strings[i] != NULL; ++i) {
if (strstr(buff, strings[i]) != NULL) {
failure_start(file, line, "Invalid string in %s: %s", pathname,
strings[i]);
failure_finish(NULL);
free(buff);
return(0);
}
}

free(buff);
return (0);
}

/* Test that two paths point to the same file. */
/* As a side-effect, asserts that both files exist. */
static int
Expand Down Expand Up @@ -1294,6 +1330,11 @@ assertion_file_time(const char *file, int line,
switch (type) {
case 'a': filet_nsec = st.st_atimespec.tv_nsec; break;
case 'b': filet = st.st_birthtime;
/* FreeBSD filesystems that don't support birthtime
* (e.g., UFS1) always return -1 here. */
if (filet == -1) {
return (1);
}
filet_nsec = st.st_birthtimespec.tv_nsec; break;
case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break;
default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type);
Expand Down Expand Up @@ -1361,6 +1402,33 @@ assertion_file_birthtime_recent(const char *file, int line,
return assertion_file_time(file, line, pathname, 0, 0, 'b', 1);
}

/* Verify mode of 'pathname'. */
int
assertion_file_mode(const char *file, int line, const char *pathname, int expected_mode)
{
int mode;
int r;

assertion_count(file, line);
#if defined(_WIN32) && !defined(__CYGWIN__)
failure_start(file, line, "assertFileMode not yet implemented for Windows");
(void)mode; /* UNUSED */
(void)r; /* UNUSED */
#else
{
struct stat st;
r = lstat(pathname, &st);
mode = (int)(st.st_mode & 0777);
}
if (r == 0 && mode == expected_mode)
return (1);
failure_start(file, line, "File %s has mode %o, expected %o",
pathname, mode, expected_mode);
#endif
failure_finish(NULL);
return (0);
}

/* Verify mtime of 'pathname'. */
int
assertion_file_mtime(const char *file, int line,
Expand Down Expand Up @@ -1400,7 +1468,7 @@ assertion_file_nlinks(const char *file, int line,
assertion_count(file, line);
r = lstat(pathname, &st);
if (r == 0 && (int)st.st_nlink == nlinks)
return (1);
return (1);
failure_start(file, line, "File %s has %d links, expected %d",
pathname, st.st_nlink, nlinks);
failure_finish(NULL);
Expand Down Expand Up @@ -1579,8 +1647,12 @@ assertion_make_dir(const char *file, int line, const char *dirname, int mode)
if (0 == _mkdir(dirname))
return (1);
#else
if (0 == mkdir(dirname, mode))
return (1);
if (0 == mkdir(dirname, mode)) {
if (0 == chmod(dirname, mode)) {
assertion_file_mode(file, line, dirname, mode);
return (1);
}
}
#endif
failure_start(file, line, "Could not create directory %s", dirname);
failure_finish(NULL);
Expand Down Expand Up @@ -1629,6 +1701,12 @@ assertion_make_file(const char *file, int line,
failure_finish(NULL);
return (0);
}
if (0 != chmod(path, mode)) {
failure_start(file, line, "Could not chmod %s", path);
failure_finish(NULL);
close(fd);
return (0);
}
if (contents != NULL) {
ssize_t wsize;

Expand All @@ -1641,10 +1719,12 @@ assertion_make_file(const char *file, int line,
failure_start(file, line,
"Could not write to %s", path);
failure_finish(NULL);
close(fd);
return (0);
}
}
close(fd);
assertion_file_mode(file, line, path, mode);
return (1);
#endif
}
Expand Down
10 changes: 10 additions & 0 deletions tar/test/test.h
Expand Up @@ -174,6 +174,9 @@
/* Assert that file contents match a string. */
#define assertFileContents(data, data_size, pathname) \
assertion_file_contents(__FILE__, __LINE__, data, data_size, pathname)
/* Verify that a file does not contain invalid strings */
#define assertFileContainsNoInvalidStrings(pathname, strings) \
assertion_file_contains_no_invalid_strings(__FILE__, __LINE__, pathname, strings)
#define assertFileMtime(pathname, sec, nsec) \
assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec)
#define assertFileMtimeRecent(pathname) \
Expand All @@ -182,6 +185,8 @@
assertion_file_nlinks(__FILE__, __LINE__, pathname, nlinks)
#define assertFileSize(pathname, size) \
assertion_file_size(__FILE__, __LINE__, pathname, size)
#define assertFileMode(pathname, mode) \
assertion_file_mode(__FILE__, __LINE__, pathname, mode)
#define assertTextFileContents(text, pathname) \
assertion_text_file_contents(__FILE__, __LINE__, text, pathname)
#define assertFileContainsLinesAnyOrder(pathname, lines) \
Expand Down Expand Up @@ -239,8 +244,10 @@ int assertion_file_atime_recent(const char *, int, const char *);
int assertion_file_birthtime(const char *, int, const char *, long, long);
int assertion_file_birthtime_recent(const char *, int, const char *);
int assertion_file_contains_lines_any_order(const char *, int, const char *, const char **);
int assertion_file_contains_no_invalid_strings(const char *, int, const char *, const char **);
int assertion_file_contents(const char *, int, const void *, int, const char *);
int assertion_file_exists(const char *, int, const char *);
int assertion_file_mode(const char *, int, const char *, int);
int assertion_file_mtime(const char *, int, const char *, long, long);
int assertion_file_mtime_recent(const char *, int, const char *);
int assertion_file_nlinks(const char *, int, const char *, int);
Expand Down Expand Up @@ -326,6 +333,9 @@ void copy_reference_file(const char *);
*/
void extract_reference_files(const char **);

/* Subtract umask from mode */
mode_t umasked(mode_t expected_mode);

/* Path to working directory for current test */
extern const char *testworkdir;

Expand Down
6 changes: 5 additions & 1 deletion tar/test/test_missing_file.c
Expand Up @@ -27,11 +27,15 @@ __FBSDID("$FreeBSD$");

DEFINE_TEST(test_missing_file)
{
const char * invalid_stderr[] = { "INTERNAL ERROR", NULL };
assertMakeFile("file1", 0644, "file1");
assertMakeFile("file2", 0644, "file2");
assert(0 == systemf("%s -cf archive.tar file1 file2 2>stderr1", testprog));
assertEmptyFile("stderr1");
assert(0 != systemf("%s -cf archive.tar file1 file2 file3 2>stderr2", testprog));
assertFileContainsNoInvalidStrings("stderr2", invalid_stderr);
assert(0 != systemf("%s -cf archive.tar 2>stderr3", testprog));
assert(0 != systemf("%s -cf archive.tar file3 2>stderr4", testprog));
assertFileContainsNoInvalidStrings("stderr3", invalid_stderr);
assert(0 != systemf("%s -cf archive.tar file3 file4 2>stderr4", testprog));
assertFileContainsNoInvalidStrings("stderr4", invalid_stderr);
}
4 changes: 2 additions & 2 deletions tar/test/test_option_H_upper.c
Expand Up @@ -83,10 +83,10 @@ DEFINE_TEST(test_option_H_upper)
assertChdir("test3");
assertEqualInt(0,
systemf("%s -xf archive.tar >c.out 2>c.err", testprog));
assertIsDir("ld1", 0755);
assertIsDir("ld1", umasked(0755));
assertIsSymlink("d1/linkX", "fileX");
assertIsSymlink("d1/link1", "file1");
assertIsReg("link2", 0644);
assertIsReg("link2", umasked(0644));
assertIsSymlink("linkY", "d1/fileY");
assertChdir("..");
}
12 changes: 6 additions & 6 deletions tar/test/test_option_L_upper.c
Expand Up @@ -69,10 +69,10 @@ DEFINE_TEST(test_option_L_upper)
assertChdir("test2");
assertEqualInt(0,
systemf("%s -xf archive.tar >c.out 2>c.err", testprog));
assertIsDir("ld1", 0755);
assertIsReg("d1/link1", 0644);
assertIsDir("ld1", umasked(0755));
assertIsReg("d1/link1", umasked(0644));
assertIsSymlink("d1/linkX", "fileX");
assertIsReg("link2", 0644);
assertIsReg("link2", umasked(0644));
assertIsSymlink("linkY", "d1/fileY");
assertChdir("..");

Expand All @@ -83,10 +83,10 @@ DEFINE_TEST(test_option_L_upper)
assertChdir("test3");
assertEqualInt(0,
systemf("%s -xf archive.tar >c.out 2>c.err", testprog));
assertIsDir("ld1", 0755);
assertIsReg("d1/link1", 0644);
assertIsDir("ld1", umasked(0755));
assertIsReg("d1/link1", umasked(0644));
assertIsSymlink("d1/linkX", "fileX");
assertIsReg("link2", 0644);
assertIsReg("link2", umasked(0644));
assertIsSymlink("linkY", "d1/fileY");
assertChdir("..");
}
4 changes: 2 additions & 2 deletions tar/test/test_option_U_upper.c
Expand Up @@ -135,7 +135,7 @@ DEFINE_TEST(test_option_U_upper)
assertMakeSymlink("d1/file1", "d1/realfile1");
assertEqualInt(0,
systemf("%s -xf ../archive.tar d1/file1 >test.out 2>test.err", testprog));
assertIsReg("d1/file1", 0644);
assertIsReg("d1/file1", umasked(0644));
assertFileContents("d1/file1", 8, "d1/file1");
assertFileContents("realfile1", 9, "d1/realfile1");
assertEmptyFile("test.out");
Expand All @@ -150,7 +150,7 @@ DEFINE_TEST(test_option_U_upper)
assertMakeSymlink("d1/file1", "d1/realfile1");
assertEqualInt(0,
systemf("%s -xPUf ../archive.tar d1/file1 >test.out 2>test.err", testprog));
assertIsReg("d1/file1", 0644);
assertIsReg("d1/file1", umasked(0644));
assertFileContents("d1/file1", 8, "d1/file1");
assertFileContents("realfile1", 9, "d1/realfile1");
assertEmptyFile("test.out");
Expand Down
2 changes: 1 addition & 1 deletion tar/test/test_option_b.c
Expand Up @@ -33,7 +33,7 @@ DEFINE_TEST(test_option_b)

assertMakeFile("file1", 0644, "file1");
if (systemf("cat file1 > test_cat.out 2> test_cat.err") != 0) {
skipping("Platform doesn't have cat");
skipping("This test requires a `cat` program");
return;
}
testprog_ustar = malloc(strlen(testprog) + sizeof(USTAR_OPT) + 1);
Expand Down
2 changes: 1 addition & 1 deletion tar/test/test_option_n.c
Expand Up @@ -55,7 +55,7 @@ DEFINE_TEST(test_option_n)
systemf("%s -xf archive.tar >x.out 2>x.err", testprog));
assertEmptyFile("x.out");
assertEmptyFile("x.err");
assertIsDir("d1", 0755);
assertIsDir("d1", umasked(0755));
assertFileNotExists("d1/file1");
assertChdir("..");
}
2 changes: 1 addition & 1 deletion tar/test/test_symlink_dir.c
Expand Up @@ -63,7 +63,7 @@ DEFINE_TEST(test_symlink_dir)
/* "dir2" is a symlink to a non-existing "real_dir2" */
assertMakeSymlink("dest1/dir2", "real_dir2");
} else {
skipping("some symlink checks");
skipping("Symlinks are not supported on this platform");
}
/* "dir3" is a symlink to an existing "non_dir3" */
assertMakeFile("dest1/non_dir3", 0755, "abcdef");
Expand Down
2 changes: 1 addition & 1 deletion tar/test/test_version.c
Expand Up @@ -88,7 +88,7 @@ DEFINE_TEST(test_version)
if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
++q;
/* Skip arbitrary third-party version numbers. */
while (s > 0 && (*q == ' ' || *q == '/' || *q == '.' || isalnum(*q))) {
while (s > 0 && (*q == ' ' || *q == '-' || *q == '/' || *q == '.' || isalnum(*q))) {
++q;
--s;
}
Expand Down
2 changes: 1 addition & 1 deletion tar/util.c
Expand Up @@ -182,7 +182,7 @@ safe_fprintf(FILE *f, const char *fmt, ...)
}

/* If our output buffer is full, dump it and keep going. */
if (i > (sizeof(outbuff) - 20)) {
if (i > (sizeof(outbuff) - 128)) {
outbuff[i] = '\0';
fprintf(f, "%s", outbuff);
i = 0;
Expand Down
2 changes: 2 additions & 0 deletions tar/write.c
Expand Up @@ -886,6 +886,8 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
"%s", archive_error_string(disk));
if (r == ARCHIVE_FATAL || r == ARCHIVE_FAILED) {
bsdtar->return_value = 1;
archive_entry_free(entry);
archive_read_close(disk);
return;
} else if (r < ARCHIVE_WARN)
continue;
Expand Down