ash: less hackish implementation of evaltreenr()
[oweals/busybox.git] / archival / tar.c
index fcabb01261be225d65e815042157f787f5f11689..280ded4e14e325811e2cf976a050bac6e7ba4b27 100644 (file)
  *
  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
-/* TODO: security with -C DESTDIR option can be enhanced.
- * Consider tar file created via:
- * $ tar cvf bug.tar anything.txt
- * $ ln -s /tmp symlink
- * $ tar --append -f bug.tar symlink
- * $ rm symlink
- * $ mkdir symlink
- * $ tar --append -f bug.tar symlink/evil.py
- *
- * This will result in an archive which contains:
- * $ tar --list -f bug.tar
- * anything.txt
- * symlink
- * symlink/evil.py
- *
- * Untarring it puts evil.py in '/tmp' even if the -C DESTDIR is given.
- * This doesn't feel right, and IIRC GNU tar doesn't do that.
- */
 
 //config:config TAR
 //config:      bool "tar (40 kb)"
 //config:      default y
 //config:      help
-//config:        tar is an archiving program. It's commonly used with gzip to
-//config:        create compressed archives. It's probably the most widely used
-//config:        UNIX archive program.
+//config:      tar is an archiving program. It's commonly used with gzip to
+//config:      create compressed archives. It's probably the most widely used
+//config:      UNIX archive program.
 //config:
 //config:config FEATURE_TAR_LONG_OPTIONS
 //config:      bool "Enable long options"
 //config:      default y
 //config:      depends on TAR && (FEATURE_SEAMLESS_Z || FEATURE_SEAMLESS_GZ || FEATURE_SEAMLESS_BZ2 || FEATURE_SEAMLESS_LZMA || FEATURE_SEAMLESS_XZ)
 //config:      help
-//config:        With this option tar can automatically detect compressed
-//config:        tarballs. Currently it works only on files (not pipes etc).
+//config:      With this option tar can automatically detect compressed
+//config:      tarballs. Currently it works only on files (not pipes etc).
 //config:
 //config:config FEATURE_TAR_FROM
 //config:      bool "Enable -X (exclude from) and -T (include from) options)"
 //config:      default y
 //config:      depends on TAR
 //config:      help
-//config:        If you enable this option you'll be able to specify
-//config:        a list of files to include or exclude from an archive.
+//config:      If you enable this option you'll be able to specify
+//config:      a list of files to include or exclude from an archive.
 //config:
 //config:config FEATURE_TAR_OLDGNU_COMPATIBILITY
 //config:      bool "Support old tar header format"
 //config:      default y
 //config:      depends on TAR || DPKG
 //config:      help
-//config:        This option is required to unpack archives created in
-//config:        the old GNU format; help to kill this old format by
-//config:        repacking your ancient archives with the new format.
+//config:      This option is required to unpack archives created in
+//config:      the old GNU format; help to kill this old format by
+//config:      repacking your ancient archives with the new format.
 //config:
 //config:config FEATURE_TAR_OLDSUN_COMPATIBILITY
 //config:      bool "Enable untarring of tarballs with checksums produced by buggy Sun tar"
 //config:      default y
 //config:      depends on TAR || DPKG
 //config:      help
-//config:        This option is required to unpack archives created by some old
-//config:        version of Sun's tar (it was calculating checksum using signed
-//config:        arithmetic). It is said to be fixed in newer Sun tar, but "old"
-//config:        tarballs still exist.
+//config:      This option is required to unpack archives created by some old
+//config:      version of Sun's tar (it was calculating checksum using signed
+//config:      arithmetic). It is said to be fixed in newer Sun tar, but "old"
+//config:      tarballs still exist.
 //config:
 //config:config FEATURE_TAR_GNU_EXTENSIONS
 //config:      bool "Support GNU tar extensions (long filenames)"
 //config:      default y
 //config:      depends on TAR && FEATURE_TAR_LONG_OPTIONS
 //config:      help
-//config:        If you enable this option you'll be able to instruct tar to send
-//config:        the contents of each extracted file to the standard input of an
-//config:        external program.
+//config:      If you enable this option you'll be able to instruct tar to send
+//config:      the contents of each extracted file to the standard input of an
+//config:      external program.
 //config:
 //config:config FEATURE_TAR_UNAME_GNAME
 //config:      bool "Enable use of user and group names"
 //config:      default y
 //config:      depends on TAR
 //config:      help
-//config:        Enable use of user and group names in tar. This affects contents
-//config:        listings (-t) and preserving permissions when unpacking (-p).
-//config:        +200 bytes.
+//config:      Enable use of user and group names in tar. This affects contents
+//config:      listings (-t) and preserving permissions when unpacking (-p).
+//config:      +200 bytes.
 //config:
 //config:config FEATURE_TAR_NOPRESERVE_TIME
 //config:      bool "Enable -m (do not preserve time) GNU option"
 //config:      default n
 //config:      depends on TAR && SELINUX
 //config:      help
-//config:        With this option busybox supports restoring SELinux labels
-//config:        when extracting files from tar archives.
+//config:      With this option busybox supports restoring SELinux labels
+//config:      when extracting files from tar archives.
 
 //applet:IF_TAR(APPLET(tar, BB_DIR_BIN, BB_SUID_DROP))
 //kbuild:lib-$(CONFIG_TAR) += tar.o
@@ -296,6 +278,23 @@ static void chksum_and_xwrite(int fd, struct tar_header_t* hp)
        xwrite(fd, hp, sizeof(*hp));
 }
 
+static void replace_symlink_placeholders(llist_t *list)
+{
+       while (list) {
+               char *target;
+
+               target = list->data + strlen(list->data) + 1;
+               if (symlink(target, list->data)) {
+                       /* shared message */
+                       bb_error_msg_and_die("can't create %slink '%s' to '%s'",
+                               "sym",
+                               list->data, target
+                       );
+               }
+               list = list->link;
+       }
+}
+
 #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
 static void writeLongname(int fd, int type, const char *name, int dir)
 {
@@ -1252,6 +1251,8 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
        while (get_header_tar(tar_handle) == EXIT_SUCCESS)
                bb_got_signal = EXIT_SUCCESS; /* saw at least one header, good */
 
+       replace_symlink_placeholders(tar_handle->symlink_placeholders);
+
        /* Check that every file that should have been extracted was */
        while (tar_handle->accept) {
                if (!find_list_entry(tar_handle->reject, tar_handle->accept->data)