Imported Upstream version 2.7.3 upstream/2.7.3
authorDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 20 Jan 2022 05:17:58 +0000 (14:17 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 20 Jan 2022 05:17:58 +0000 (14:17 +0900)
.tarball-version
ChangeLog
NEWS
configure
src/patch.c
src/pch.c
src/util.c
src/util.h
tests/symlinks

index 37c2961c2430f357166156e7ddf1c590eb8d4ce1..2c9b4ef42ecbc54d7c946c9ad26424b89d1f792d 100644 (file)
@@ -1 +1 @@
-2.7.2
+2.7.3
index 24785e350bc1c75960dac5450b7c63a303cf0448..c21081a8cd01e9484bed684e5cc595d7eceff09f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2015-01-22  Andreas Gruenbacher  <agruen@gnu.org>
+
+       Fix the fix for CVE-2015-1196
+       * src/util.c (filename_is_safe): New function split off from name_is_valid().
+       (symlink_target_is_valid): Explain why we cannot have absolute symlinks or
+       symlinks with ".." components for now.
+       (move_file): Move absolute filename check here and explain.
+       * tests/symlinks: Put test case with ".." symlink in comments for now.
+       * NEWS: Add CVE number.
+
+2015-01-21  Andreas Gruenbacher  <agruen@gnu.org>
+
+       For renames and copies, make sure that both file names are valid
+       * src/patch.c (main): Allow there_is_another_patch() to set the
+       skip_rest_of_patch flag.
+       * src/pch.c (intuit_diff_type): For renames and copies, also check the "other"
+       file name.
+       (pch_copy, pch_rename): Now that both names are checked in intuit_diff_type(),
+       we know they are defined here.
+
 2015-01-20  Andreas Gruenbacher  <agruen@gnu.org>
 
        Fail when out of memory in set_hunkmax()
diff --git a/NEWS b/NEWS
index d3f1c2d00b8b39de53a7a98b8a1142ea1a4ba2d5..d79cead94d5b4d675c41e9970e06d4a9318dd360 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,7 +4,7 @@
   deleting".
 * Function names in hunks (from diff -p) are now preserved in reject files.
 * With git-style patches, symlinks that point outside the working directory
-  will no longer be created.
+  will no longer be created (CVE-2015-1196).
 
 Changes in version 2.7.1:
 
index a5f0571ac84d8e1f81ee3c2854cf81272f68e690..a09350c3d602a030607f303d7a500661379e14c2 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for GNU patch 2.7.2.
+# Generated by GNU Autoconf 2.69 for GNU patch 2.7.3.
 #
 # Report bugs to <bug-patch@gnu.org>.
 #
@@ -580,8 +580,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='GNU patch'
 PACKAGE_TARNAME='patch'
-PACKAGE_VERSION='2.7.2'
-PACKAGE_STRING='GNU patch 2.7.2'
+PACKAGE_VERSION='2.7.3'
+PACKAGE_STRING='GNU patch 2.7.3'
 PACKAGE_BUGREPORT='bug-patch@gnu.org'
 PACKAGE_URL='http://www.gnu.org/software/patch/'
 
@@ -2011,7 +2011,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures GNU patch 2.7.2 to adapt to many kinds of systems.
+\`configure' configures GNU patch 2.7.3 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -2081,7 +2081,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of GNU patch 2.7.2:";;
+     short | recursive ) echo "Configuration of GNU patch 2.7.3:";;
    esac
   cat <<\_ACEOF
 
@@ -2185,7 +2185,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-GNU patch configure 2.7.2
+GNU patch configure 2.7.3
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2894,7 +2894,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by GNU patch $as_me 2.7.2, which was
+It was created by GNU patch $as_me 2.7.3, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -3828,7 +3828,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='patch'
- VERSION='2.7.2'
+ VERSION='2.7.3'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -23003,7 +23003,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by GNU patch $as_me 2.7.2, which was
+This file was extended by GNU patch $as_me 2.7.3, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -23075,7 +23075,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-GNU patch config.status 2.7.2
+GNU patch config.status 2.7.3
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
index 441732e8e8fed3b4cfd0580d4bce18e38c465111..cb4dbb20396234130f4d543740481cb421ba4ec6 100644 (file)
@@ -196,6 +196,9 @@ main (int argc, char **argv)
       bool mismatch = false;
       char const *outname = NULL;
 
+      if (skip_rest_of_patch)
+       somefailed = true;
+
       if (have_git_diff != pch_git_diff ())
        {
          if (have_git_diff)
index 33facd9a6fdc0bc5494769e5adc91fea0a9a1da7..028d51fc1bfda29b56948c9b694bd9cf762fbc16 100644 (file)
--- a/src/pch.c
+++ b/src/pch.c
@@ -401,21 +401,7 @@ name_is_valid (char const *name)
        return false;
     }
 
-  if (IS_ABSOLUTE_FILE_NAME (name))
-    is_valid = false;
-  else
-    for (n = name; *n; )
-      {
-       if (*n == '.' && *++n == '.' && ( ! *++n || ISSLASH (*n)))
-         {
-           is_valid = false;
-           break;
-         }
-       while (*n && ! ISSLASH (*n))
-         n++;
-       while (ISSLASH (*n))
-         n++;
-      }
+  is_valid = filename_is_safe (name);
 
   /* Allow any filename if we are in the filesystem root.  */
   if (! is_valid && cwd_is_root (name))
@@ -978,6 +964,16 @@ intuit_diff_type (bool need_header, mode_t *p_file_type)
          }
       }
 
+    if ((pch_rename () || pch_copy ())
+       && ! inname
+       && ! ((i == OLD || i == NEW) &&
+             p_name[! reverse] &&
+             name_is_valid (p_name[! reverse])))
+      {
+       say ("Cannot %s file without two valid file names\n", pch_rename () ? "rename" : "copy");
+       skip_rest_of_patch = true;
+      }
+
     if (i == NONE)
       {
        if (inname)
@@ -2178,14 +2174,12 @@ pch_name (enum nametype type)
 
 bool pch_copy (void)
 {
-  return p_copy[OLD] && p_copy[NEW]
-        && p_name[OLD] && p_name[NEW];
+  return p_copy[OLD] && p_copy[NEW];
 }
 
 bool pch_rename (void)
 {
-  return p_rename[OLD] && p_rename[NEW]
-        && p_name[OLD] && p_name[NEW];
+  return p_rename[OLD] && p_rename[NEW];
 }
 
 /* Return the specified line position in the old file of the old context. */
index 94c7582345afb833e2c1e240389f8399fccec909..ae05caa675912ae1bf595ef0af4c77af4ffcc15d 100644 (file)
@@ -423,55 +423,18 @@ create_backup (char const *to, const struct stat *to_st, bool leave_original)
     }
 }
 
+/* Only allow symlink targets which are relative and free of ".." components:
+ * otherwise, the operating system may follow one of those symlinks in a
+ * pathname component, leading to a path traversal vulnerability.
+ *
+ * An alternative to disallowing many kinds of symlinks would be to implement
+ * path traversal in user space using openat() without following symlinks
+ * altogether.
+ */
 static bool
 symlink_target_is_valid (char const *target, char const *to)
 {
-  bool is_valid;
-
-  if (IS_ABSOLUTE_FILE_NAME (to))
-    is_valid = true;
-  else if (IS_ABSOLUTE_FILE_NAME (target))
-    is_valid = false;
-  else
-    {
-      unsigned int depth = 0;
-      char const *t;
-
-      is_valid = true;
-      t = to;
-      while (*t)
-       {
-         while (*t && ! ISSLASH (*t))
-           t++;
-         if (ISSLASH (*t))
-           {
-             while (ISSLASH (*t))
-               t++;
-             depth++;
-           }
-       }
-
-      t = target;
-      while (*t)
-       {
-         if (*t == '.' && *++t == '.' && (! *++t || ISSLASH (*t)))
-           {
-             if (! depth--)
-               {
-                 is_valid = false;
-                 break;
-               }
-           }
-         else
-           {
-             while (*t && ! ISSLASH (*t))
-               t++;
-             depth++;
-           }
-         while (ISSLASH (*t))
-           t++;
-       }
-    }
+  bool is_valid = filename_is_safe (target);
 
   /* Allow any symlink target if we are in the filesystem root.  */
   return is_valid || cwd_is_root (to);
@@ -520,7 +483,11 @@ move_file (char const *from, bool *from_needs_removal,
            read_fatal ();
          buffer[size] = 0;
 
-         if (! symlink_target_is_valid (buffer, to))
+         /* If we are allowed to create a file with an absolute path name,
+            anywhere, we also don't need to worry about symlinks that can
+            leave the working directory.  */
+         if (! (IS_ABSOLUTE_FILE_NAME (to)
+                || symlink_target_is_valid (buffer, to)))
            {
              fprintf (stderr, "symbolic link target '%s' is invalid\n",
                       buffer);
@@ -1720,6 +1687,28 @@ int stat_file (char const *filename, struct stat *st)
   return xstat (filename, st) == 0 ? 0 : errno;
 }
 
+/* Check if a filename is relative and free of ".." components.
+   Such a path cannot lead to files outside the working tree
+   as long as the working tree only contains symlinks that are
+   "filename_is_safe" when followed.  */
+bool
+filename_is_safe (char const *name)
+{
+  if (IS_ABSOLUTE_FILE_NAME (name))
+    return false;
+  while (*name)
+    {
+      if (*name == '.' && *++name == '.'
+         && ( ! *++name || ISSLASH (*name)))
+       return false;
+      while (*name && ! ISSLASH (*name))
+       name++;
+      while (ISSLASH (*name))
+       name++;
+    }
+  return true;
+}
+
 /* Check if we are in the root of a particular filesystem namespace ("/" on
    UNIX or a particular drive's root on DOS-like systems).  */
 bool
index 579c5dee3bccddf435cc7097976c6ca39cf652f0..6b3308a0ec2ad1f4bed41cdba66140a00e84d079 100644 (file)
@@ -69,6 +69,7 @@ enum file_id_type lookup_file_id (struct stat const *);
 void set_queued_output (struct stat const *, bool);
 bool has_queued_output (struct stat const *);
 int stat_file (char const *, struct stat *);
+bool filename_is_safe (char const *);
 bool cwd_is_root (char const *);
 
 enum file_attributes {
index 621102613a1b6a559759f3bd058bb432b09ad1d5..04a9b7307292fb4b4c45a20b11f376af34b38be3 100644 (file)
@@ -148,20 +148,24 @@ ncheck 'test ! -L symlink'
 
 # Patch should not create symlinks which point outside the working directory.
 
-cat > symlink-target.diff <<EOF
-diff --git a/dir/foo b/dir/foo
-new file mode 120000
-index 0000000..cad2309
---- /dev/null
-+++ b/dir/foo
-@@ -0,0 +1 @@
-+../foo
-\ No newline at end of file
-EOF
-
-check 'patch -p1 < symlink-target.diff || echo "Status: $?"' <<EOF
-patching symbolic link dir/foo
-EOF
+# We cannot even ensure that symlinks with ".." components are safe: we cannot
+# guarantee that they won't end up higher up in the working tree than we think;
+# the path to the symlink may follow symlinks itself.
+#
+#cat > symlink-target.diff <<EOF
+#diff --git a/dir/foo b/dir/foo
+#new file mode 120000
+#index 0000000..cad2309
+#--- /dev/null
+#+++ b/dir/foo
+#@@ -0,0 +1 @@
+#+../foo
+#\ No newline at end of file
+#EOF
+#
+#check 'patch -p1 < symlink-target.diff || echo "Status: $?"' <<EOF
+#patching symbolic link dir/foo
+#EOF
 
 cat > bad-symlink-target1.diff <<EOF
 diff --git a/bar b/bar