bug-tar
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Bug-tar] [PATCH v2] Option to treat tarbombs as errors


From: Connor Behan
Subject: [Bug-tar] [PATCH v2] Option to treat tarbombs as errors
Date: Sat, 21 Sep 2013 22:57:20 -0700

This adds the --one-top-level option to reject tarballs that do not
follow the practice of having everything in one directory. The only
option that appears to be in conflict is --absolute-names because the
code assumes that unsafe prefixes like ".." and leading slashes are
removed. Archives that fail the check cause an error that aborts
extraction. This leaves it up to the user to apply an appropriate
workaround.

Signed-off-by: Connor Behan <address@hidden>
---
 NEWS          |  8 ++++++++
 doc/tar.texi  |  9 +++++++++
 src/common.h  |  3 +++
 src/extract.c | 43 +++++++++++++++++++++++++++++++++++++++++++
 src/tar.c     | 10 ++++++++++
 5 files changed, 73 insertions(+)

diff --git a/NEWS b/NEWS
index 3108798..0abc2e5 100644
--- a/NEWS
+++ b/NEWS
@@ -39,6 +39,14 @@ errors.  Instead it just silently skips them.  An additional 
level of
 verbosity can be obtained by using the option --warning=existing-file
 together with this option.
 
+* The --one-top-level option.
+
+This new command line option tells tar that the working directory
+(or the one passed to -C) should not be populated with more than one
+name directly under it. A fatal error is thrown if, after name
+transformations, a second member that would be extracted to this location
+is found.
+
 * Support for POSIX ACLs, extended attributes and SELinux context.
 
 Starting with this version tar is able to store, extract and list
diff --git a/doc/tar.texi b/doc/tar.texi
index ddfa055..b3b1e77 100644
--- a/doc/tar.texi
+++ b/doc/tar.texi
@@ -3071,6 +3071,15 @@ Used when creating an archive.  Prevents @command{tar} 
from recursing into
 directories that are on different file systems from the current
 directory.
 
address@hidden
address@hidden --one-top-level
+
+Tells @command{tar} that only one name from the archive should be placed 
directly
+under the extraction directory (the working directory or the one specified with
address@hidden). A fatal error is thrown if a second one is found. The 
top-level of
+a member is determined after the transformations from 
@option{--strip-components}
+and @option{--transform} are applied.
+
 @opsummary{overwrite}
 @item --overwrite
 
diff --git a/src/common.h b/src/common.h
index 723ad90..8a5e6f3 100644
--- a/src/common.h
+++ b/src/common.h
@@ -233,6 +233,9 @@ GLOBAL bool numeric_owner_option;
 
 GLOBAL bool one_file_system_option;
 
+/* Refuse to extract more than one top-level name.  */
+GLOBAL bool one_top_level_option;
+
 /* Specified value to be put into tar file in place of stat () results, or
    just null and -1 if such an override should not take place.  */
 GLOBAL char const *owner_name_option;
diff --git a/src/extract.c b/src/extract.c
index 3d8ba10..0292f0c 100644
--- a/src/extract.c
+++ b/src/extract.c
@@ -1559,6 +1559,45 @@ prepare_to_extract (char const *file_name, int typeflag, 
tar_extractor_t *fun)
   return 1;
 }
 
+/* Throws an error if passed file name is the second top-level name.  */
+void
+depth_check (char *file_name)
+{
+  size_t i;
+  size_t start = 0;
+
+  static char *first_top_level;
+  static bool first_top_level_found = false;
+
+  /* Leading slashes have already been removed so it is safe to start at 1.  */
+  for (i = 1; i <= strlen (file_name); i++)
+    {
+      if (ISSLASH (file_name[i]) || file_name[i] == '\0')
+        {
+         if ((i == start) || (i - start == 1 && file_name[start] == '.'))
+           start = i + 1;
+         else
+           {
+             if (first_top_level_found)
+               {
+                 /* Still reject two top-levels if one is a substring of the 
other.  */
+                 if (strncmp (first_top_level, file_name + start, i - start)
+                     || strlen (first_top_level) != i - start)
+                     FATAL_ERROR  ((0, 0, _("Found multiple top-level names, 
exiting")));
+               }
+             else
+               {
+                 first_top_level_found = true;
+                 first_top_level = xmalloc (i - start + 1);
+                 strncpy (first_top_level, file_name + start, i - start);
+                 first_top_level[i - start] = '\0';
+               }
+             return;
+           }
+       }
+    }
+}
+
 /* Extract a file from the archive.  */
 void
 extract_archive (void)
@@ -1604,6 +1643,10 @@ extract_archive (void)
        return;
       }
 
+  /* Check for names where the depth is too high.  */
+  if (one_top_level_option)
+    depth_check (current_stat_info.file_name);
+
   /* Extract the archive entry according to its type.  */
   /* KLUDGE */
   typeflag = sparse_member_p (&current_stat_info) ?
diff --git a/src/tar.c b/src/tar.c
index 6dc2ec5..10ff4c8 100644
--- a/src/tar.c
+++ b/src/tar.c
@@ -318,6 +318,7 @@ enum
   OCCURRENCE_OPTION,
   OLD_ARCHIVE_OPTION,
   ONE_FILE_SYSTEM_OPTION,
+  ONE_TOP_LEVEL_OPTION,
   OVERWRITE_DIR_OPTION,
   OVERWRITE_OPTION,
   OWNER_OPTION,
@@ -485,6 +486,8 @@ static struct argp_option options[] = {
   {"overwrite-dir", OVERWRITE_DIR_OPTION, 0, 0,
    N_("overwrite metadata of existing directories when extracting (default)"),
    GRID+1 },
+  {"one-top-level", ONE_TOP_LEVEL_OPTION, 0, 0,
+   N_("allow at most one top-level name when extracting"), GRID+1 },
 #undef GRID
 
 #define GRID 40
@@ -1433,6 +1436,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
       one_file_system_option = true;
       break;
 
+    case ONE_TOP_LEVEL_OPTION:
+      one_top_level_option = true;
+      break;
+
     case 'l':
       check_links_option = 1;
       break;
@@ -2381,6 +2388,9 @@ decode_options (int argc, char **argv)
                      subcommand_string (subcommand_option)));
     }
 
+  if (one_top_level_option && absolute_names_option)
+    USAGE_ERROR ((0, 0, _("--one-top-level cannot be used with 
--absolute-names")));
+
   if (archive_names == 0)
     {
       /* If no archive file name given, try TAPE from the environment, or
-- 
1.8.4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]