1303 files changed, 32187 insertions, 57138 deletions
diff --git a/archival/libarchive/get_header_tar.c b/archival/libarchive/get_header_tar.c index 278c8fb..c7e3bc1 100644 --- a/archival/libarchive/get_header_tar.c +++ b/archival/libarchive/get_header_tar.c @@ -17,36 +17,6 @@ typedef uint32_t aliased_uint32_t FIX_ALIASING; typedef off_t aliased_off_t FIX_ALIASING; - -const char* FAST_FUNC strip_unsafe_prefix(const char *str) -{ - const char *cp = str; - while (1) { - char *cp2; - if (*cp == '/') { - cp++; - continue; - } - if (strncmp(cp, "/../"+1, 3) == 0) { - cp += 3; - continue; - } - cp2 = strstr(cp, "/../"); - if (!cp2) - break; - cp = cp2 + 4; - } - if (cp != str) { - static smallint warned = 0; - if (!warned) { - warned = 1; - bb_error_msg("removing leading '%.*s' from member names", - (int)(cp - str), str); - } - } - return cp; -} - /* NB: _DESTROYS_ str[len] character! */ static unsigned long long getOctal(char *str, int len) { @@ -90,13 +60,21 @@ static unsigned long long getOctal(char *str, int len) } #define GET_OCTAL(a) getOctal((a), sizeof(a)) +#define TAR_EXTD (ENABLE_FEATURE_TAR_GNU_EXTENSIONS || ENABLE_FEATURE_TAR_SELINUX) +#if !TAR_EXTD +#define process_pax_hdr(archive_handle, sz, global) \ + process_pax_hdr(archive_handle, sz) +#endif /* "global" is 0 or 1 */ static void process_pax_hdr(archive_handle_t *archive_handle, unsigned sz, int global) { +#if !TAR_EXTD + unsigned blk_sz = (sz + 511) & (~511); + seek_by_read(archive_handle->src_fd, blk_sz); +#else + unsigned blk_sz = (sz + 511) & (~511); char *buf, *p; - unsigned blk_sz; - blk_sz = (sz + 511) & (~511); p = buf = xmalloc(blk_sz + 1); xread(archive_handle->src_fd, buf, blk_sz); archive_handle->offset += blk_sz; @@ -115,7 +93,9 @@ static void process_pax_hdr(archive_handle_t *archive_handle, unsigned sz, int g */ p += len; sz -= len; - if ((int)sz < 0 + if ( + /** (int)sz < 0 - not good enough for huge malicious VALUE of 2^32-1 */ + (int)(sz|len) < 0 /* this works */ || len == 0 || errno != EINVAL || *end != ' ' @@ -132,30 +112,39 @@ static void process_pax_hdr(archive_handle_t *archive_handle, unsigned sz, int g p[-1] = '\0'; value = end + 1; -#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS - if (!global && strncmp(value, "path=", sizeof("path=") - 1) == 0) { - value += sizeof("path=") - 1; - free(archive_handle->tar__longname); - archive_handle->tar__longname = xstrdup(value); - continue; +# if ENABLE_FEATURE_TAR_GNU_EXTENSIONS + if (!global) { + if (is_prefixed_with(value, "path=")) { + value += sizeof("path=") - 1; + free(archive_handle->tar__longname); + archive_handle->tar__longname = xstrdup(value); + continue; + } + if (is_prefixed_with(value, "linkpath=")) { + value += sizeof("linkpath=") - 1; + free(archive_handle->tar__linkname); + archive_handle->tar__linkname = xstrdup(value); + continue; + } } -#endif +# endif -#if ENABLE_FEATURE_TAR_SELINUX +# if ENABLE_FEATURE_TAR_SELINUX /* Scan for SELinux contexts, via "RHT.security.selinux" keyword. * This is what Red Hat's patched version of tar uses. */ -# define SELINUX_CONTEXT_KEYWORD "RHT.security.selinux" - if (strncmp(value, SELINUX_CONTEXT_KEYWORD"=", sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1) == 0) { +# define SELINUX_CONTEXT_KEYWORD "RHT.security.selinux" + if (is_prefixed_with(value, SELINUX_CONTEXT_KEYWORD"=")) { value += sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1; free(archive_handle->tar__sctx[global]); archive_handle->tar__sctx[global] = xstrdup(value); continue; } -#endif +# endif } free(buf); +#endif } char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) @@ -198,7 +187,13 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) * the message and we don't check whether we indeed * saw zero block directly before this. */ if (i == 0) { - bb_error_msg("short read"); + /* GNU tar 1.29 will be silent if tar archive ends abruptly + * (if there are no zero blocks at all, and last read returns zero, + * not short read 0 < len < 512). Complain only if + * the very first read fails. Grrr. + */ + if (archive_handle->offset == 0) + bb_error_msg("short read"); /* this merely signals end of archive, not exit(1): */ return EXIT_FAILURE; } @@ -214,7 +209,11 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) archive_handle->offset += i; /* If there is no filename its an empty header */ - if (tar.name[0] == 0 && tar.prefix[0] == 0) { + if (tar.name[0] == 0 && tar.prefix[0] == 0 + /* Have seen a tar archive with pax 'x' header supplying UTF8 filename, + * with actual file having all name fields NUL-filled. Check this: */ + && !p_longname + ) { if (archive_handle->tar__end) { /* Second consecutive empty header - end of archive. * Read until the end to empty the pipe from gz or bz2 @@ -230,7 +229,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) /* Check header has valid magic, "ustar" is for the proper tar, * five NULs are for the old tar format */ - if (strncmp(tar.magic, "ustar", 5) != 0 + if (!is_prefixed_with(tar.magic, "ustar") && (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY || memcmp(tar.magic, "\0\0\0\0", 5) != 0) ) { @@ -378,7 +377,14 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) case '6': file_header->mode |= S_IFIFO; goto size0; + case 'g': /* pax global header */ + case 'x': { /* pax extended header */ + if ((uoff_t)file_header->size > 0xfffff) /* paranoia */ + goto skip_ext_hdr; + process_pax_hdr(archive_handle, file_header->size, (tar.typeflag == 'g')); + goto again_after_align; #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS +/* See http://www.gnu.org/software/tar/manual/html_node/Extensions.html */ case 'L': /* free: paranoia: tar with several consecutive longnames */ free(p_longname); @@ -398,18 +404,17 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) archive_handle->offset += file_header->size; /* return get_header_tar(archive_handle); */ goto again; - case 'D': /* GNU dump dir */ - case 'M': /* Continuation of multi volume archive */ - case 'N': /* Old GNU for names > 100 characters */ - case 'S': /* Sparse file */ - case 'V': /* Volume header */ +/* + * case 'S': // Sparse file + * Was seen in the wild. Not supported (yet?). + * See https://www.gnu.org/software/tar/manual/html_section/tar_92.html + * for the format. (An "Old GNU Format" was seen, not PAX formats). + */ +// case 'D': /* GNU dump dir */ +// case 'M': /* Continuation of multi volume archive */ +// case 'N': /* Old GNU for names > 100 characters */ +// case 'V': /* Volume header */ #endif - case 'g': /* pax global header */ - case 'x': { /* pax extended header */ - if ((uoff_t)file_header->size > 0xfffff) /* paranoia */ - goto skip_ext_hdr; - process_pax_hdr(archive_handle, file_header->size, (tar.typeflag == 'g')); - goto again_after_align; } skip_ext_hdr: { @@ -440,6 +445,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) /* Everything up to and including last ".." component is stripped */ overlapping_strcpy(file_header->name, strip_unsafe_prefix(file_header->name)); +//TODO: do the same for file_header->link_target? /* Strip trailing '/' in directories */ /* Must be done after mode is set as '/' is used to check if it's a directory */ |