]> Dogcows Code - chaz/tar/blobdiff - src/extract.c
Detect tarbombs while extracting
[chaz/tar] / src / extract.c
index 2d8e175985e2cb0d9b2719f305628884e81b47fd..b6fdbbba96534b61daf6acd7766757e32c32183a 100644 (file)
@@ -1,24 +1,24 @@
 /* Extract files from a tar archive.
 
-   Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000,
-   2001, 2003, 2004, 2005, 2006, 2007, 2010, 2012
+   Copyright 1988, 1992-1994, 1996-2001, 2003-2007, 2010, 2012-2013
    Free Software Foundation, Inc.
 
-   Written by John Gilmore, on 1985-11-19.
+   This file is part of GNU tar.
 
-   This program is free software; you can redistribute it and/or modify it
-   under the terms of the GNU General Public License as published by the
-   Free Software Foundation; either version 3, or (at your option) any later
-   version.
+   GNU tar is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
 
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
-   Public License for more details.
+   GNU tar is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU General Public License along
-   with this program; if not, write to the Free Software Foundation, Inc.,
-   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   Written by John Gilmore, on 1985-11-19.  */
 
 #include <system.h>
 #include <quotearg.h>
@@ -100,6 +100,11 @@ struct delayed_set_stat
     int change_dir;
 
     /* extended attributes*/
+    char *cntx_name;
+    char *acls_a_ptr;
+    size_t acls_a_len;
+    char *acls_d_ptr;
+    size_t acls_d_len;
     size_t xattr_map_size;
     struct xattr_array *xattr_map;
     /* Length and contents of name.  */
@@ -142,6 +147,15 @@ struct delayed_link
        hard-linked together.  */
     struct string_list *sources;
 
+    /* SELinux context */
+    char *cntx_name;
+
+    /* ACLs */
+    char *acls_a_ptr;
+    size_t acls_a_len;
+    char *acls_d_ptr;
+    size_t acls_d_len;
+
     size_t xattr_map_size;
     struct xattr_array *xattr_map;
 
@@ -177,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.  */
@@ -283,7 +326,7 @@ set_mode (char const *file_name,
 static void
 check_time (char const *file_name, struct timespec t)
 {
-  if (t.tv_sec <= 0)
+  if (t.tv_sec < 0)
     WARNOPT (WARN_TIMESTAMP,
             (0, 0, _("%s: implausibly old time stamp %s"),
              file_name, tartime (t, true)));
@@ -375,6 +418,8 @@ set_stat (char const *file_name,
   /* these three calls must be done *after* fd_chown() call because fd_chown
      causes that linux capabilities becomes cleared. */
   xattrs_xattrs_set (st, file_name, typeflag, 1);
+  xattrs_acls_set (st, file_name, typeflag);
+  xattrs_selinux_set (st, file_name, typeflag);
 }
 
 /* For each entry H in the leading prefix of entries in HEAD that do
@@ -446,6 +491,29 @@ delay_set_stat (char const *file_name, struct tar_stat_info const *st,
   data->atflag = atflag;
   data->after_links = 0;
   data->change_dir = chdir_current;
+  data->cntx_name = NULL;
+  if (st)
+    assign_string (&data->cntx_name, st->cntx_name);
+  if (st && st->acls_a_ptr)
+    {
+      data->acls_a_ptr = xmemdup (st->acls_a_ptr, st->acls_a_len + 1);
+      data->acls_a_len = st->acls_a_len;
+    }
+  else
+    {
+      data->acls_a_ptr = NULL;
+      data->acls_a_len = 0;
+    }
+  if (st && st->acls_d_ptr)
+    {
+      data->acls_d_ptr = xmemdup (st->acls_d_ptr, st->acls_d_len + 1);
+      data->acls_d_len = st->acls_d_len;
+    }
+  else
+    {
+      data->acls_d_ptr = NULL;
+      data->acls_d_len = 0;
+    }
   if (st)
     xheader_xattr_copy (st, &data->xattr_map, &data->xattr_map_size);
   else
@@ -794,6 +862,11 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links)
          sb.stat.st_gid = data->gid;
          sb.atime = data->atime;
          sb.mtime = data->mtime;
+         sb.cntx_name = data->cntx_name;
+         sb.acls_a_ptr = data->acls_a_ptr;
+         sb.acls_a_len = data->acls_a_len;
+         sb.acls_d_ptr = data->acls_d_ptr;
+         sb.acls_d_len = data->acls_d_len;
          sb.xattr_map = data->xattr_map;
          sb.xattr_map_size = data->xattr_map_size;
          set_stat (data->file_name, &sb,
@@ -803,12 +876,29 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links)
 
       delayed_set_stat_head = data->next;
       xheader_xattr_free (data->xattr_map, data->xattr_map_size);
+      free (data->cntx_name);
+      free (data->acls_a_ptr);
+      free (data->acls_d_ptr);
       free (data);
     }
 }
 
 \f
-
+static bool
+is_directory_link (const char *file_name)
+{
+  struct stat st;
+  int e = errno;
+  int res;
+  
+  res = (fstatat (chdir_fd, file_name, &st, AT_SYMLINK_NOFOLLOW) == 0 &&
+        S_ISLNK (st.st_mode) &&
+        fstatat (chdir_fd, file_name, &st, 0) == 0 &&
+        S_ISDIR (st.st_mode));
+  errno = e;
+  return res;
+}
+\f
 /* Extractor functions for various member types */
 
 static int
@@ -864,10 +954,15 @@ extract_dir (char *file_name, int typeflag)
 
       if (errno == EEXIST
          && (interdir_made
+             || keep_directory_symlink_option
              || old_files_option == DEFAULT_OLD_FILES
              || old_files_option == OVERWRITE_OLD_FILES))
        {
          struct stat st;
+
+         if (keep_directory_symlink_option && is_directory_link (file_name))
+           return 0;
+         
          if (deref_stat (file_name, &st) == 0)
            {
              current_mode = st.st_mode;
@@ -1173,6 +1268,12 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made)
                            + strlen (file_name) + 1);
       p->sources->next = 0;
       strcpy (p->sources->string, file_name);
+      p->cntx_name = NULL;
+      assign_string (&p->cntx_name, current_stat_info.cntx_name);
+      p->acls_a_ptr = NULL;
+      p->acls_a_len = 0;
+      p->acls_d_ptr = NULL;
+      p->acls_d_len = 0;
       xheader_xattr_copy (&current_stat_info, &p->xattr_map, &p->xattr_map_size);
       strcpy (p->target, current_stat_info.link_name);
 
@@ -1288,7 +1389,7 @@ extract_symlink (char *file_name, int typeflag)
   if (!warned_once)
     {
       warned_once = 1;
-      WARNOPT (WARN_SYMBOLIC_CAST,
+      WARNOPT (WARN_SYMLINK_CAST,
               (0, 0,
                _("Attempting extraction of symbolic links as hard links")));
     }
@@ -1368,6 +1469,13 @@ extract_failure (char *file_name, int typeflag)
   return 1;
 }
 
+static int
+extract_skip (char *file_name, int typeflag)
+{
+  skip_member ();
+  return 0;
+}
+
 typedef int (*tar_extractor_t) (char *file_name, int typeflag);
 
 \f
@@ -1448,7 +1556,7 @@ prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun)
       ERROR ((0, 0,
              _("%s: Cannot extract -- file is continued from another volume"),
              quotearg_colon (current_stat_info.file_name)));
-      *fun = extract_failure;
+      *fun = extract_skip;
       break;
 
     case GNUTYPE_LONGNAME:
@@ -1499,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)
@@ -1549,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)
@@ -1609,6 +1747,11 @@ apply_delayed_links (void)
                  st1.stat.st_gid = ds->gid;
                  st1.atime = ds->atime;
                  st1.mtime = ds->mtime;
+                  st1.cntx_name = ds->cntx_name;
+                  st1.acls_a_ptr = ds->acls_a_ptr;
+                  st1.acls_a_len = ds->acls_a_len;
+                  st1.acls_d_ptr = ds->acls_d_ptr;
+                  st1.acls_d_len = ds->acls_d_len;
                   st1.xattr_map = ds->xattr_map;
                   st1.xattr_map_size = ds->xattr_map_size;
                  set_stat (source, &st1, -1, 0, 0, SYMTYPE,
@@ -1626,6 +1769,7 @@ apply_delayed_links (void)
        }
 
    xheader_xattr_free (ds->xattr_map, ds->xattr_map_size);
+   free (ds->cntx_name);
 
       {
        struct delayed_link *next = ds->next;
This page took 0.033006 seconds and 4 git commands to generate.