From: Jan Osusky Date: Wed, 23 Nov 2016 19:24:38 +0000 (+0100) Subject: Allow tar header fields without null terminator X-Git-Tag: v3.3.0~114^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F825%2Fhead;p=thirdparty%2Flibarchive.git Allow tar header fields without null terminator The "archive_read_format_tar_bid" and related "validate_number_field" were revisited to allow one more non-standard way of coding UID/GID and similar number fields in the tar header. Modified "test_compat_gtar" to verify reading of such value. --- diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c index 11ff03101..0ee511ea1 100644 --- a/libarchive/archive_read_support_format_tar.c +++ b/libarchive/archive_read_support_format_tar.c @@ -300,19 +300,29 @@ validate_number_field(const char* p_field, size_t i_size) unsigned char marker = (unsigned char)p_field[0]; /* octal? */ if ((marker >= '0' && marker <= '7') || marker == ' ') { - /* must be terminated by null or space */ - if (p_field[i_size - 1] != '\0' && p_field[i_size - 1] != ' ') { - return 0; - } - /* rest must be octal digits */ size_t i = 0; - for (i = 1; i < i_size - 1; ++i) { - char c = p_field[i]; - if ((c < '0' || c > '7') && c != ' ') { - return 0; + int octal_found = 0; + for (i = 0; i < i_size; ++i) { + switch (p_field[i]) + { + case ' ': /* skip any leading spaces and trailing space*/ + if (octal_found == 0 || i == i_size - 1) { + continue; + } + break; + case '\0': /* null is allowed only at the end */ + if (i != i_size - 1) { + return 0; + } + break; + /* rest must be octal digits */ + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + ++octal_found; + break; } } - return 1; + return octal_found > 0; } /* base 256 (i.e. binary number) */ else if (marker == 128 || marker == 255 || marker == 0) { @@ -376,31 +386,22 @@ archive_read_format_tar_bid(struct archive_read *a, int best_bid) return (0); bid += 2; /* 6 bits of variation in an 8-bit field leaves 2 bits. */ - /* Sanity check: Look at first byte of mode field. */ - switch (255 & (unsigned)header->mode[0]) { - case 0: case 255: - /* Base-256 value: No further verification possible! */ - break; - case ' ': /* Not recommended, but not illegal, either. */ - break; - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - /* Octal Value. */ - /* TODO: Check format of remainder of this field. */ - break; - default: - /* Not a valid mode; bail out here. */ - return (0); - } - - /* Sanity test uid/gid/mtime/size/rdevmajor/rdevminor fields. */ - if (validate_number_field(header->uid, sizeof(header->uid)) == 0 || - validate_number_field(header->gid, sizeof(header->gid)) == 0 || - validate_number_field(header->mtime, sizeof(header->mtime)) == 0 || - validate_number_field(header->size, sizeof(header->size)) == 0 || - validate_number_field(header->rdevmajor, sizeof(header->rdevmajor)) == 0 || - validate_number_field(header->rdevminor, sizeof(header->rdevminor)) == 0) { - return 0; + /* + * Check format of mode/uid/gid/mtime/size/rdevmajor/rdevminor fields. + * These are usually octal numbers but GNU tar encodes "big" values as + * base256 and leading zeroes are sometimes replaced by spaces. + * Even the null terminator is sometimes omitted. Anyway, must be checked + * to avoid false positives. + */ + if (bid > 0 && + (validate_number_field(header->mode, sizeof(header->mode)) == 0 || + validate_number_field(header->uid, sizeof(header->uid)) == 0 || + validate_number_field(header->gid, sizeof(header->gid)) == 0 || + validate_number_field(header->mtime, sizeof(header->mtime)) == 0 || + validate_number_field(header->size, sizeof(header->size)) == 0 || + validate_number_field(header->rdevmajor, sizeof(header->rdevmajor)) == 0 || + validate_number_field(header->rdevminor, sizeof(header->rdevminor)) == 0)) { + bid = 0; } return (bid); diff --git a/libarchive/test/test_compat_gtar.c b/libarchive/test/test_compat_gtar.c index e31e26771..def24aae5 100644 --- a/libarchive/test/test_compat_gtar.c +++ b/libarchive/test/test_compat_gtar.c @@ -107,7 +107,8 @@ test_compat_gtar_1(void) } /* - * test_compat_gtar_2.tar exercises reading of UID > 2097151. + * test_compat_gtar_2.tar exercises reading of UID = 2097152 as base256 + * and GID = 2097152 as octal without null terminator. */ static void test_compat_gtar_2(void) @@ -132,7 +133,7 @@ test_compat_gtar_2(void) /* Check UID and GID */ assertEqualInt(2097152, archive_entry_uid(ae)); - assertEqualInt(1000, archive_entry_gid(ae)); + assertEqualInt(2097152, archive_entry_gid(ae)); /* Verify the end-of-archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); diff --git a/libarchive/test/test_compat_gtar_2.tar.uu b/libarchive/test/test_compat_gtar_2.tar.uu index ed2fa5edf..7843a2cba 100644 --- a/libarchive/test/test_compat_gtar_2.tar.uu +++ b/libarchive/test/test_compat_gtar_2.tar.uu @@ -1,231 +1,49 @@ -begin 660 test_compat_gtar_2.tar.uu -M9FEL95]W:71H7V)I9U]U:60````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M`````````````#`P,#`V-C``@``````@```P,#`Q-S4P`#`P,#`P,#`P,38W -M`#$S,#$T-C