bug-tar
[Top][All Lists]
Advanced

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

[Bug-tar] [PATCH] Option to detect tarbombs while extracting


From: Connor Behan
Subject: [Bug-tar] [PATCH] Option to detect tarbombs while extracting
Date: Fri, 24 Jan 2014 01:32:11 -0800

* src/common.h (one_top_level_option): New global.
(one_top_level): New global.
* src/extract.c (extr_init): If one_top_level_option is set, determine
the name one_top_level that might have to be prepended.
(extract_archive): If one_top_level_option is set, prepend one_top_level
to all names that don't already start with it.
* src/tar.c (ONE_TOP_LEVEL_OPTION): New contant.
(options): New option --one-top-level.
(parse_opt): Handle this option.
(decode_options): Make it conflict with --absolute-names.

* NEWS: Update.
* doc/tar.texi: Document new option.
---
 NEWS          |  8 ++++++++
 doc/tar.texi  | 11 +++++++++++
 src/common.h  |  4 ++++
 src/extract.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/tar.c     | 11 +++++++++++
 5 files changed, 93 insertions(+)

diff --git a/NEWS b/NEWS
index 1444143..52e1284 100644
--- a/NEWS
+++ b/NEWS
@@ -25,6 +25,14 @@ sign and the specifier letter.
               current screen width, if {N} is not given.
   %c        -  a shortcut for "%{%Y-%m-%d %H:%M:%S}t: %ds, %{read,wrote}T%*\r"
 
+* 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.  Instead, a newly created subdirectory is
+used whose name is equal to the archive name without the extension.
+For example, foo.tar.gz would be extracted to foo.
+
 
 version 1.27.1 - Sergey Poznyakoff, 2013-11-17
 
diff --git a/doc/tar.texi b/doc/tar.texi
index 424617c..ece40f4 100644
--- a/doc/tar.texi
+++ b/doc/tar.texi
@@ -3086,6 +3086,17 @@ 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} to create a new directory beneath the extraction directory
+(or the one passed to @option{-C}) and use it to guard against tarbombs.  The
+name of the new directory will be equal to the name of the archive with the
+extension stripped off.  If any archive names (after transformations from
address@hidden and @option{--strip-components}) do not already begin with
+it, the new directory will be prepended to the names immediately before
+extraction.  Recognized extensions are @samp{.tar}, @samp{.taz}, @samp{.tbz},
address@hidden, @samp{.tgz}, @samp{.tlz} and @samp{.txz}.
+
 @opsummary{overwrite}
 @item --overwrite
 
diff --git a/src/common.h b/src/common.h
index 8f8a337..365379a 100644
--- a/src/common.h
+++ b/src/common.h
@@ -235,6 +235,10 @@ GLOBAL bool numeric_owner_option;
 
 GLOBAL bool one_file_system_option;
 
+/* Create a top-level directory for extracting based on the archive name.  */
+GLOBAL bool one_top_level_option;
+GLOBAL char *one_top_level;
+
 /* 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 9b6b7f9..b6fdbbb 100644
--- a/src/extract.c
+++ b/src/extract.c
@@ -191,6 +191,35 @@ extr_init (void)
       umask (newdir_umask);    /* restore the kernel umask */
       current_umask = newdir_umask;
     }
+
+  /* If the user wants to guarantee that everything is under one directory,
+     determine its name now and let it be created later.  */
+  if (one_top_level_option)
+    {
+      int i;
+      char *base = base_name (archive_name_array[0]);
+
+      for (i = strlen (base) - 1; i > 2; i--)
+        if (!strncmp (base + i - 3, ".tar", 4) ||
+           !strncmp (base + i - 3, ".taz", 4) ||
+           !strncmp (base + i - 3, ".tbz", 4) ||
+           !strncmp (base + i - 3, ".tb2", 4) ||
+           !strncmp (base + i - 3, ".tgz", 4) ||
+           !strncmp (base + i - 3, ".tlz", 4) ||
+           !strncmp (base + i - 3, ".txz", 4)) break;
+
+      if (i <= 3)
+        {
+         one_top_level_option = false;
+         free (base);
+         return;
+       }
+
+      one_top_level = xmalloc (i - 2);
+      strncpy (one_top_level, base, i - 3);
+      one_top_level[i - 3] = '\0';
+      free (base);
+    }
 }
 
 /* Use fchmod if possible, fchmodat otherwise.  */
@@ -1578,6 +1607,33 @@ prepare_to_extract (char const *file_name, int typeflag, 
tar_extractor_t *fun)
   return 1;
 }
 
+void
+maybe_prepend_name (char **file_name)
+{
+  int i;
+
+  for (i = 0; i < strlen (*file_name); i++)
+    if (!ISSLASH ((*file_name)[i]) && (*file_name)[i] != '.') break;
+
+  if (i == strlen (*file_name))
+    return;
+
+  if (!strncmp (*file_name + i, one_top_level, strlen (one_top_level)))
+    {
+      int pos = i + strlen (one_top_level);
+      if (ISSLASH ((*file_name)[pos]) || (*file_name)[pos] == '\0') return;
+    }
+
+  char *new_name = xmalloc (strlen (one_top_level) + strlen (*file_name) + 2);
+
+  strcpy (new_name, one_top_level);
+  strcat (new_name, "/");
+  strcat (new_name, *file_name);
+
+  free (*file_name);
+  *file_name = new_name;
+}
+
 /* Extract a file from the archive.  */
 void
 extract_archive (void)
@@ -1628,6 +1684,9 @@ extract_archive (void)
   typeflag = sparse_member_p (&current_stat_info) ?
                   GNUTYPE_SPARSE : current_header->header.typeflag;
 
+  if (one_top_level_option)
+    maybe_prepend_name (&current_stat_info.file_name);
+
   if (prepare_to_extract (current_stat_info.file_name, typeflag, &fun))
     {
       if (fun && (*fun) (current_stat_info.file_name, typeflag)
diff --git a/src/tar.c b/src/tar.c
index 300a834..b972da7 100644
--- a/src/tar.c
+++ b/src/tar.c
@@ -319,6 +319,7 @@ enum
   OCCURRENCE_OPTION,
   OLD_ARCHIVE_OPTION,
   ONE_FILE_SYSTEM_OPTION,
+  ONE_TOP_LEVEL_OPTION,
   OVERWRITE_DIR_OPTION,
   OVERWRITE_OPTION,
   OWNER_OPTION,
@@ -489,6 +490,9 @@ static struct argp_option options[] = {
   {"keep-directory-symlink", KEEP_DIRECTORY_SYMLINK_OPTION, 0, 0,
    N_("preserve existing symlinks to directories when extracting"),
    GRID+1 },
+  {"one-top-level", ONE_TOP_LEVEL_OPTION, 0, 0,
+   N_("create a subdirectory to avoid having loose files extracted"),
+   GRID+1 },
 #undef GRID
 
 #define GRID 40
@@ -1438,6 +1442,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;
@@ -2390,6 +2398,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.5.2




reply via email to

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