Submitted By: Ken Moffat <ken@kenmoffat.uklinux.net>
Date: 2005-07-29
Initial Package Version: 2.6
Upstream Status: Unknown
Origin: from Mandrake
Description: Vulnerability fixes, rediffed so that they all apply with
 -p1 and consolidated to single patch.  Also applicable to earlier versions.
(1.) CAN-1999-1572 (still seems to apply to 2.6) cpio uses a 0 umask when
creating files with -O or -F options, which creates the files with mode 0666
and allows local users to overwrite them.  Fix originally fom debian.
(2.) CAN-2005-1111 Race condition in 2.6 and earlier allows local users to
modify permissions of arbitrary files via a hard-link attack.  Fix
originally from fedora.
(3.)  CAN-2005-1229 Directory traversal vulnerability allows remote
attackers to write to arbitrary directories via a dot dot in a cpio file.
Fix by Peter Vrabec at RedHat.

diff -Naur cpio-2.6.vanilla/doc/cpio.1 cpio-2.6/doc/cpio.1
--- cpio-2.6.vanilla/doc/cpio.1	2004-08-30 17:21:48.000000000 +0100
+++ cpio-2.6/doc/cpio.1	2005-07-29 13:46:42.000000000 +0100
@@ -20,7 +20,7 @@
 [\-\-unconditional] [\-\-verbose] [\-\-block-size=blocks] [\-\-swap-halfwords]
 [\-\-io-size=bytes] [\-\-pattern-file=file] [\-\-format=format]
 [\-\-owner=[user][:.][group]] [\-\-no-preserve-owner] [\-\-message=message]
-[\-\-force\-local] [\-\-no\-absolute\-filenames] [\-\-sparse]
+[\-\-force\-local] [\-\-absolute\-filenames] [\-\-sparse]
 [\-\-only\-verify\-crc] [\-\-quiet] [\-\-rsh-command=command] [\-\-help]
 [\-\-version] [pattern...] [< archive]
 
diff -Naur cpio-2.6.vanilla/doc/cpio.info cpio-2.6/doc/cpio.info
--- cpio-2.6.vanilla/doc/cpio.info	2004-02-27 12:42:01.000000000 +0000
+++ cpio-2.6/doc/cpio.info	2005-07-29 13:46:42.000000000 +0100
@@ -203,7 +203,7 @@
      [--swap-halfwords] [--io-size=bytes] [--pattern-file=file]
      [--format=format] [--owner=[user][:.][group]]
      [--no-preserve-owner] [--message=message] [--help] [--version]
-     [-no-absolute-filenames] [--sparse] [-only-verify-crc] [-quiet]
+     [--absolute-filenames] [--sparse] [-only-verify-crc] [-quiet]
      [--rsh-command=command] [pattern...] [< archive]
 
 
@@ -358,9 +358,9 @@
      Show numeric UID and GID instead of translating them into names
      when using the `--verbose option'.
 
-`--no-absolute-filenames'
-     Create all files relative to the current directory in copy-in
-     mode, even if they have an absolute file name in the archive.
+`--absolute-filenames'
+     Do not strip leading file name components that contain ".." 
+     and leading slashes from file names in copy-in mode
 
 `--no-preserve-owner'
      Do not change the ownership of the files; leave them owned by the
diff -Naur cpio-2.6.vanilla/src/copyin.c cpio-2.6/src/copyin.c
--- cpio-2.6.vanilla/src/copyin.c	2004-09-08 12:10:02.000000000 +0100
+++ cpio-2.6/src/copyin.c	2005-07-29 13:46:42.000000000 +0100
@@ -25,6 +25,7 @@
 #include "dstring.h"
 #include "extern.h"
 #include "defer.h"
+#include "dirname.h"
 #include <rmt.h>
 #ifndef	FNM_PATHNAME
 #include <fnmatch.h>
@@ -389,19 +390,26 @@
 	  continue;
 	}
 
-      if (close (out_file_des) < 0)
-	error (0, errno, "%s", d->header.c_name);
-
+      /*
+       *  Avoid race condition.
+       *  Set chown and chmod before closing the file desc.
+       *  pvrabec@redhat.com
+       */
+       
       /* File is now copied; set attributes.  */
       if (!no_chown_flag)
-	if ((chown (d->header.c_name,
+	if ((fchown (out_file_des,
 		    set_owner_flag ? set_owner : d->header.c_uid,
 	       set_group_flag ? set_group : d->header.c_gid) < 0)
 	    && errno != EPERM)
 	  error (0, errno, "%s", d->header.c_name);
       /* chown may have turned off some permissions we wanted. */
-      if (chmod (d->header.c_name, (int) d->header.c_mode) < 0)
+      if (fchmod (out_file_des, (int) d->header.c_mode) < 0)
 	error (0, errno, "%s", d->header.c_name);
+
+      if (close (out_file_des) < 0)
+	error (0, errno, "%s", d->header.c_name);
+
       if (retain_time_flag)
 	{
 	  times.actime = times.modtime = d->header.c_mtime;
@@ -557,6 +565,25 @@
       write (out_file_des, "", 1);
       delayed_seek_count = 0;
     }
+    
+  /*
+   *  Avoid race condition.
+   *  Set chown and chmod before closing the file desc.
+   *  pvrabec@redhat.com
+   */
+   
+  /* File is now copied; set attributes.  */
+  if (!no_chown_flag)
+    if ((fchown (out_file_des,
+		set_owner_flag ? set_owner : file_hdr->c_uid,
+	   set_group_flag ? set_group : file_hdr->c_gid) < 0)
+	&& errno != EPERM)
+      error (0, errno, "%s", file_hdr->c_name);
+  
+  /* chown may have turned off some permissions we wanted. */
+  if (fchmod (out_file_des, (int) file_hdr->c_mode) < 0)
+    error (0, errno, "%s", file_hdr->c_name);
+     
   if (close (out_file_des) < 0)
     error (0, errno, "%s", file_hdr->c_name);
 
@@ -567,18 +594,6 @@
 	       file_hdr->c_name, crc, file_hdr->c_chksum);
     }
 
-  /* File is now copied; set attributes.  */
-  if (!no_chown_flag)
-    if ((chown (file_hdr->c_name,
-		set_owner_flag ? set_owner : file_hdr->c_uid,
-	   set_group_flag ? set_group : file_hdr->c_gid) < 0)
-	&& errno != EPERM)
-      error (0, errno, "%s", file_hdr->c_name);
-  
-  /* chown may have turned off some permissions we wanted. */
-  if (chmod (file_hdr->c_name, (int) file_hdr->c_mode) < 0)
-    error (0, errno, "%s", file_hdr->c_name);
-  
   if (retain_time_flag)
     {
       struct utimbuf times;		/* For setting file times.  */
@@ -589,7 +604,7 @@
       if (utime (file_hdr->c_name, &times) < 0)
 	error (0, errno, "%s", file_hdr->c_name);
     }
-  
+    
   tape_skip_padding (in_file_des, file_hdr->c_filesize);
   if (file_hdr->c_nlink > 1
       && (archive_format == arf_newascii || archive_format == arf_crcascii) )
@@ -1335,6 +1350,53 @@
     }
 }
 
+/* Return a safer suffix of FILE_NAME, or "." if it has no safer
+   suffix.  Check for fully specified file names and other atrocities.  */
+
+static const char *
+safer_name_suffix (char const *file_name)
+{
+  char const *p;
+
+  /* Skip file system prefixes, leading file name components that contain
+     "..", and leading slashes.  */
+
+  size_t prefix_len = FILE_SYSTEM_PREFIX_LEN (file_name);
+
+  for (p = file_name + prefix_len; *p;)
+    {
+      if (p[0] == '.' && p[1] == '.' && (ISSLASH (p[2]) || !p[2]))
+	prefix_len = p + 2 - file_name;
+
+      do
+	{
+	  char c = *p++;
+	  if (ISSLASH (c))
+	    break;
+	}
+      while (*p);
+    }
+
+  for (p = file_name + prefix_len; ISSLASH (*p); p++)
+    continue;
+  prefix_len = p - file_name;
+
+  if (prefix_len)
+    {
+      char *prefix = alloca (prefix_len + 1);
+      memcpy (prefix, file_name, prefix_len);
+      prefix[prefix_len] = '\0';
+
+
+      error (0, 0, _("Removing leading `%s' from member names"), prefix);
+    }
+
+  if (!*p)
+    p = ".";
+
+  return p;
+}
+
 /* Read the collection from standard input and create files
    in the file system.  */
 
@@ -1445,18 +1507,11 @@
 
       /* Do we have to ignore absolute paths, and if so, does the filename
          have an absolute path?  */
-      if (no_abs_paths_flag && file_hdr.c_name && file_hdr.c_name [0] == '/')
+      if (!abs_paths_flag && file_hdr.c_name && file_hdr.c_name [0])
 	{
-	  char *p;
+	  const char *p = safer_name_suffix (file_hdr.c_name);
 
-	  p = file_hdr.c_name;
-	  while (*p == '/')
-	    ++p;
-	  if (*p == '\0')
-	    {
-	      strcpy (file_hdr.c_name, ".");
-	    }
-	  else
+	  if (p != file_hdr.c_name)
 	    {
               /* Debian hack: file_hrd.c_name is sometimes set to
                  point to static memory by code in tar.c.  This
diff -Naur cpio-2.6.vanilla/src/copypass.c cpio-2.6/src/copypass.c
--- cpio-2.6.vanilla/src/copypass.c	2004-09-06 13:09:04.000000000 +0100
+++ cpio-2.6/src/copypass.c	2005-07-29 13:46:07.000000000 +0100
@@ -181,19 +181,25 @@
 		}
 	      if (close (in_file_des) < 0)
 		error (0, errno, "%s", input_name.ds_string);
-	      if (close (out_file_des) < 0)
-		error (0, errno, "%s", output_name.ds_string);
-
+	      /*
+	       *  Avoid race condition.
+	       *  Set chown and chmod before closing the file desc.
+	       *  pvrabec@redhat.com
+	       */
 	      /* Set the attributes of the new file.  */
 	      if (!no_chown_flag)
-		if ((chown (output_name.ds_string,
+		if ((fchown (out_file_des,
 			    set_owner_flag ? set_owner : in_file_stat.st_uid,
 		      set_group_flag ? set_group : in_file_stat.st_gid) < 0)
 		    && errno != EPERM)
 		  error (0, errno, "%s", output_name.ds_string);
 	      /* chown may have turned off some permissions we wanted. */
-	      if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
+	      if (fchmod (out_file_des, in_file_stat.st_mode) < 0)
+		error (0, errno, "%s", output_name.ds_string);
+		
+	      if (close (out_file_des) < 0)
 		error (0, errno, "%s", output_name.ds_string);
+
 	      if (reset_time_flag)
 		{
 		  times.actime = in_file_stat.st_atime;
diff -Naur cpio-2.6.vanilla/src/extern.h cpio-2.6/src/extern.h
--- cpio-2.6.vanilla/src/extern.h	2004-09-08 11:49:57.000000000 +0100
+++ cpio-2.6/src/extern.h	2005-07-29 13:47:34.000000000 +0100
@@ -46,7 +46,7 @@
 extern int sparse_flag;
 extern int quiet_flag;
 extern int only_verify_crc_flag;
-extern int no_abs_paths_flag;
+extern int abs_paths_flag;
 extern unsigned int warn_option;
 
 /* Values for warn_option */
@@ -91,6 +91,7 @@
 extern char input_is_seekable;
 extern char output_is_seekable;
 extern char *program_name;
+extern mode_t sys_umask;
 extern int (*xstat) ();
 extern void (*copy_function) ();
 
diff -Naur cpio-2.6.vanilla/src/global.c cpio-2.6/src/global.c
--- cpio-2.6.vanilla/src/global.c	2004-09-08 11:23:44.000000000 +0100
+++ cpio-2.6/src/global.c	2005-07-29 13:47:34.000000000 +0100
@@ -100,7 +100,7 @@
 int only_verify_crc_flag = false;
 
 /* If true, don't use any absolute paths, prefix them by `./'.  */
-int no_abs_paths_flag = false;
+int abs_paths_flag = false;
 
 #ifdef DEBUG_CPIO
 /* If true, print debugging information.  */
@@ -195,6 +195,9 @@
 /* The name this program was run with.  */
 char *program_name;
 
+/* Debian hack to make the -d option honor the umask.  */
+mode_t sys_umask;
+
 /* A pointer to either lstat or stat, depending on whether
    dereferencing of symlinks is done for input files.  */
 int (*xstat) ();
diff -Naur cpio-2.6.vanilla/src/main.c cpio-2.6/src/main.c
--- cpio-2.6.vanilla/src/main.c	2004-11-23 00:42:18.000000000 +0000
+++ cpio-2.6/src/main.c	2005-07-29 13:47:34.000000000 +0100
@@ -41,6 +41,7 @@
 
 enum cpio_options {
   NO_ABSOLUTE_FILENAMES_OPTION=256,  
+  ABSOLUTE_FILENAMES_OPTION,  
   NO_PRESERVE_OWNER_OPTION,      
   ONLY_VERIFY_CRC_OPTION,        
   RENAME_BATCH_FILE_OPTION,      
@@ -134,6 +135,8 @@
    N_("In copy-in mode, read additional patterns specifying filenames to extract or list from FILE"), 210},
   {"no-absolute-filenames", NO_ABSOLUTE_FILENAMES_OPTION, 0, 0,
    N_("Create all files relative to the current directory"), 210},
+  {"absolute-filenames", ABSOLUTE_FILENAMES_OPTION, 0, 0,
+   N_("do not strip leading file name components that contain \"..\" and leading slashes from file names"), 210},
   {"only-verify-crc", ONLY_VERIFY_CRC_OPTION, 0, 0,
    N_("When reading a CRC format archive in copy-in mode, only verify the CRC's of each file in the archive, don't actually extract the files"), 210},
   {"rename", 'r', 0, 0,
@@ -392,7 +395,11 @@
       break;
 
     case NO_ABSOLUTE_FILENAMES_OPTION:		/* --no-absolute-filenames */
-      no_abs_paths_flag = true;
+      abs_paths_flag = false;
+      break;
+	
+    case ABSOLUTE_FILENAMES_OPTION:		/* --absolute-filenames */
+      abs_paths_flag = true;
       break;
 	
     case NO_PRESERVE_OWNER_OPTION:		/* --no-preserve-owner */
@@ -631,7 +638,7 @@
 		      _("--append is used but no archive file name is given (use -F or -O options")));
 
       CHECK_USAGE(rename_batch_file, "--rename-batch-file", "--create");
-      CHECK_USAGE(no_abs_paths_flag, "--no-absolute-pathnames", "--create");
+      CHECK_USAGE(abs_paths_flag, "--absolute-pathnames", "--create");
       CHECK_USAGE(input_archive_name, "-I", "--create");
       if (archive_name && output_archive_name)
 	USAGE_ERROR ((0, 0, _("Both -O and -F are used in copy-out mode")));
@@ -658,7 +665,7 @@
       CHECK_USAGE(rename_flag, "--rename", "--pass-through");
       CHECK_USAGE(append_flag, "--append", "--pass-through");
       CHECK_USAGE(rename_batch_file, "--rename-batch-file", "--pass-through");
-      CHECK_USAGE(no_abs_paths_flag, "--no-absolute-pathnames",
+      CHECK_USAGE(abs_paths_flag, "--absolute-pathnames",
 		  "--pass-through");
       CHECK_USAGE(to_stdout_option, "--to-stdout", "--pass-through");
       
@@ -740,7 +747,6 @@
   textdomain (PACKAGE);
 
   program_name = argv[0];
-  umask (0);
 
 #ifdef __TURBOC__
   _fmode = O_BINARY;		/* Put stdin and stdout in binary mode.  */
@@ -751,6 +757,7 @@
 #endif
 
   process_args (argc, argv);
+  sys_umask = umask (0);
 
   initialize_buffers ();
 
