libarchive: do not extract unsafe symlinks unless $EXTRACT_UNSAFE_SYMLINKS=1
[oweals/busybox.git] / archival / unzip.c
index d5bca08d4c1756b78e04c67b3846374211b6a798..60416606397ed72fbb645db4f9dc16394b201f97 100644 (file)
 //config:      bool "unzip (24 kb)"
 //config:      default y
 //config:      help
-//config:        unzip will list or extract files from a ZIP archive,
-//config:        commonly found on DOS/WIN systems. The default behavior
-//config:        (with no options) is to extract the archive into the
-//config:        current directory.
+//config:      unzip will list or extract files from a ZIP archive,
+//config:      commonly found on DOS/WIN systems. The default behavior
+//config:      (with no options) is to extract the archive into the
+//config:      current directory.
 //config:
 //config:config FEATURE_UNZIP_CDF
 //config:      bool "Read and use Central Directory data"
 //config:      default y
 //config:      depends on UNZIP
 //config:      help
-//config:        If you know that you only need to deal with simple
-//config:        ZIP files without deleted/updated files, SFX archives etc,
-//config:        you can reduce code size by unselecting this option.
-//config:        To support less trivial ZIPs, say Y.
+//config:      If you know that you only need to deal with simple
+//config:      ZIP files without deleted/updated files, SFX archives etc,
+//config:      you can reduce code size by unselecting this option.
+//config:      To support less trivial ZIPs, say Y.
 //config:
 //config:config FEATURE_UNZIP_BZIP2
 //config:      bool "Support compression method 12 (bzip2)"
@@ -114,6 +114,7 @@ typedef union {
 
 #define FIX_ENDIANNESS_ZIP(zip) \
 do { if (BB_BIG_ENDIAN) { \
+       (zip).fmt.method        = SWAP_LE16((zip).fmt.method      ); \
        (zip).fmt.crc32         = SWAP_LE32((zip).fmt.crc32       ); \
        (zip).fmt.cmpsize       = SWAP_LE32((zip).fmt.cmpsize     ); \
        (zip).fmt.ucmpsize      = SWAP_LE32((zip).fmt.ucmpsize    ); \
@@ -318,6 +319,12 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf)
 };
 #endif
 
+static void die_if_bad_fnamesize(unsigned sz)
+{
+       if (sz > 0xfff) /* more than 4k?! no funny business please */
+               bb_error_msg_and_die("bad archive");
+}
+
 static void unzip_skip(off_t skip)
 {
        if (skip != 0)
@@ -340,8 +347,7 @@ static void unzip_extract_symlink(zip_header_t *zip, const char *dst_fn)
 {
        char *target;
 
-       if (zip->fmt.ucmpsize > 0xfff) /* no funny business please */
-               bb_error_msg_and_die("bad archive");
+       die_if_bad_fnamesize(zip->fmt.ucmpsize);
 
        if (zip->fmt.method == 0) {
                /* Method 0 - stored (not compressed) */
@@ -362,9 +368,15 @@ static void unzip_extract_symlink(zip_header_t *zip, const char *dst_fn)
                target[xstate.mem_output_size] = '\0';
 #endif
        }
+       if (!unsafe_symlink_target(target)) {
 //TODO: libbb candidate
-       if (symlink(target, dst_fn))
-               bb_perror_msg_and_die("can't create symlink '%s'", dst_fn);
+               if (symlink(target, dst_fn)) {
+                       /* shared message */
+                       bb_perror_msg_and_die("can't create %slink '%s' to '%s'",
+                               "sym", dst_fn, target
+                       );
+               }
+       }
        free(target);
 }
 #endif
@@ -784,6 +796,7 @@ int unzip_main(int argc, char **argv)
 
                /* Read filename */
                free(dst_fn);
+               die_if_bad_fnamesize(zip.fmt.filename_len);
                dst_fn = xzalloc(zip.fmt.filename_len + 1);
                xread(zip_fd, dst_fn, zip.fmt.filename_len);
                /* Skip extra header bytes */