From e72bf6daa21b9b0eb0a1bcf951ac4c5091ddb28f Mon Sep 17 00:00:00 2001 From: Tim Waugh Date: Jan 20 2015 12:37:09 +0000 Subject: Apply upstream patch to fix directory traversal via symlinks (bug #1182157, CVE-2015-1196). Resolves: rhbz#1182157 rhbz#1182154 --- diff --git a/patch-CVE-2015-1196.patch b/patch-CVE-2015-1196.patch new file mode 100644 index 0000000..899ee01 --- /dev/null +++ b/patch-CVE-2015-1196.patch @@ -0,0 +1,156 @@ +diff -up patch-2.7.1/NEWS.CVE-2015-1196 patch-2.7.1/NEWS +diff -up patch-2.7.1/src/pch.c.CVE-2015-1196 patch-2.7.1/src/pch.c +--- patch-2.7.1/src/pch.c.CVE-2015-1196 2015-01-20 12:23:34.808516117 +0000 ++++ patch-2.7.1/src/pch.c 2015-01-20 12:24:15.763652714 +0000 +@@ -454,6 +454,60 @@ name_is_valid (char const *name) + return is_valid; + } + ++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++; ++ } ++ } ++ ++ /* Allow any symlink target if we are in the filesystem root. */ ++ return is_valid || cwd_is_root (to); ++} ++ + /* Determine what kind of diff is in the remaining part of the patch file. */ + + static enum diff +diff -up patch-2.7.1/src/pch.h.CVE-2015-1196 patch-2.7.1/src/pch.h +--- patch-2.7.1/src/pch.h.CVE-2015-1196 2012-09-22 18:37:21.000000000 +0100 ++++ patch-2.7.1/src/pch.h 2015-01-20 12:24:15.763652714 +0000 +@@ -37,6 +37,7 @@ bool pch_write_line (lin, FILE *); + bool there_is_another_patch (bool, mode_t *); + char *pfetch (lin) _GL_ATTRIBUTE_PURE; + char pch_char (lin) _GL_ATTRIBUTE_PURE; ++bool symlink_target_is_valid (char const *, char const *); + int another_hunk (enum diff, bool); + int pch_says_nonexistent (bool) _GL_ATTRIBUTE_PURE; + size_t pch_line_len (lin) _GL_ATTRIBUTE_PURE; +diff -up patch-2.7.1/src/util.c.CVE-2015-1196 patch-2.7.1/src/util.c +--- patch-2.7.1/src/util.c.CVE-2015-1196 2015-01-20 12:23:34.808516117 +0000 ++++ patch-2.7.1/src/util.c 2015-01-20 12:24:15.764652717 +0000 +@@ -478,6 +478,13 @@ move_file (char const *from, bool *from_ + read_fatal (); + buffer[size] = 0; + ++ if (! symlink_target_is_valid (buffer, to)) ++ { ++ fprintf (stderr, "symbolic link target '%s' is invalid\n", ++ buffer); ++ fatal_exit (0); ++ } ++ + if (! backup) + { + if (unlink (to) == 0) +diff -up patch-2.7.1/tests/symlinks.CVE-2015-1196 patch-2.7.1/tests/symlinks +--- patch-2.7.1/tests/symlinks.CVE-2015-1196 2012-09-19 02:18:42.000000000 +0100 ++++ patch-2.7.1/tests/symlinks 2015-01-20 12:24:15.764652717 +0000 +@@ -146,6 +146,59 @@ ncheck 'test ! -L symlink' + + # -------------------------------------------------------------- + ++# Patch should not create symlinks which point outside the working directory. ++ ++cat > symlink-target.diff < bad-symlink-target1.diff < bad-symlink-target2.diff < #include @@ -20,7 +20,7 @@ diff -up patch-2.7.1/src/common.h.selinux patch-2.7.1/src/common.h diff -up patch-2.7.1/src/inp.c.selinux patch-2.7.1/src/inp.c --- patch-2.7.1/src/inp.c.selinux 2012-09-19 02:07:31.000000000 +0100 -+++ patch-2.7.1/src/inp.c 2012-10-18 17:53:43.736748619 +0100 ++++ patch-2.7.1/src/inp.c 2015-01-20 12:26:32.914110148 +0000 @@ -138,7 +138,7 @@ get_input_file (char const *filename, ch char *getbuf; @@ -49,7 +49,7 @@ diff -up patch-2.7.1/src/inp.c.selinux patch-2.7.1/src/inp.c && (file_type & S_IFMT) == (instat.st_mode & S_IFMT))) diff -up patch-2.7.1/src/Makefile.am.selinux patch-2.7.1/src/Makefile.am --- patch-2.7.1/src/Makefile.am.selinux 2012-09-14 10:15:41.000000000 +0100 -+++ patch-2.7.1/src/Makefile.am 2012-10-18 17:53:43.736748619 +0100 ++++ patch-2.7.1/src/Makefile.am 2015-01-20 12:26:32.914110148 +0000 @@ -34,7 +34,7 @@ patch_SOURCES = \ AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib @@ -61,7 +61,7 @@ diff -up patch-2.7.1/src/Makefile.am.selinux patch-2.7.1/src/Makefile.am patch_SOURCES += merge.c diff -up patch-2.7.1/src/Makefile.in.selinux patch-2.7.1/src/Makefile.in --- patch-2.7.1/src/Makefile.in.selinux 2012-09-28 17:41:31.000000000 +0100 -+++ patch-2.7.1/src/Makefile.in 2012-10-18 17:53:43.736748619 +0100 ++++ patch-2.7.1/src/Makefile.in 2015-01-20 12:26:32.915110151 +0000 @@ -981,7 +981,7 @@ patch_SOURCES = bestmatch.h common.h inp AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib \ $(am__append_2) @@ -72,9 +72,9 @@ diff -up patch-2.7.1/src/Makefile.in.selinux patch-2.7.1/src/Makefile.in all: all-am diff -up patch-2.7.1/src/patch.c.selinux patch-2.7.1/src/patch.c ---- patch-2.7.1/src/patch.c.selinux 2012-09-28 11:43:12.000000000 +0100 -+++ patch-2.7.1/src/patch.c 2012-10-18 17:57:41.708586721 +0100 -@@ -256,19 +256,19 @@ main (int argc, char **argv) +--- patch-2.7.1/src/patch.c.selinux 2015-01-20 12:26:32.910110134 +0000 ++++ patch-2.7.1/src/patch.c 2015-01-20 12:26:32.915110151 +0000 +@@ -257,19 +257,19 @@ main (int argc, char **argv) if (! strcmp (inname, outname)) { if (inerrno == -1) @@ -97,7 +97,7 @@ diff -up patch-2.7.1/src/patch.c.selinux patch-2.7.1/src/patch.c inerrno = -1; } if (! outerrno) -@@ -563,7 +563,7 @@ main (int argc, char **argv) +@@ -564,7 +564,7 @@ main (int argc, char **argv) mode, &new_time); else { @@ -106,7 +106,7 @@ diff -up patch-2.7.1/src/patch.c.selinux patch-2.7.1/src/patch.c set_file_attributes (TMPOUTNAME, attr, inname, &instat, mode, &new_time); } -@@ -623,7 +623,7 @@ main (int argc, char **argv) +@@ -624,7 +624,7 @@ main (int argc, char **argv) struct stat oldst; int olderrno; @@ -115,7 +115,7 @@ diff -up patch-2.7.1/src/patch.c.selinux patch-2.7.1/src/patch.c if (olderrno && olderrno != ENOENT) write_fatal (); if (! olderrno && lookup_file_id (&oldst) == CREATED) -@@ -1749,7 +1749,7 @@ delete_file_later (const char *name, con +@@ -1751,7 +1751,7 @@ delete_file_later (const char *name, con if (! st) { @@ -125,8 +125,8 @@ diff -up patch-2.7.1/src/patch.c.selinux patch-2.7.1/src/patch.c st = &st_tmp; } diff -up patch-2.7.1/src/pch.c.selinux patch-2.7.1/src/pch.c ---- patch-2.7.1/src/pch.c.selinux 2012-09-22 18:44:33.000000000 +0100 -+++ patch-2.7.1/src/pch.c 2012-10-18 18:04:28.619008832 +0100 +--- patch-2.7.1/src/pch.c.selinux 2015-01-20 12:26:32.911110138 +0000 ++++ patch-2.7.1/src/pch.c 2015-01-20 12:26:32.916110154 +0000 @@ -1,6 +1,6 @@ /* reading patches */ @@ -144,7 +144,7 @@ diff -up patch-2.7.1/src/pch.c.selinux patch-2.7.1/src/pch.c if (inerrno) { perror (inname); -@@ -468,6 +468,7 @@ intuit_diff_type (bool need_header, mode +@@ -522,6 +522,7 @@ intuit_diff_type (bool need_header, mode bool extended_headers = false; enum nametype i; struct stat st[3]; @@ -152,7 +152,7 @@ diff -up patch-2.7.1/src/pch.c.selinux patch-2.7.1/src/pch.c int stat_errno[3]; int version_controlled[3]; enum diff retval; -@@ -507,6 +508,7 @@ intuit_diff_type (bool need_header, mode +@@ -561,6 +562,7 @@ intuit_diff_type (bool need_header, mode version_controlled[OLD] = -1; version_controlled[NEW] = -1; version_controlled[INDEX] = -1; @@ -160,7 +160,7 @@ diff -up patch-2.7.1/src/pch.c.selinux patch-2.7.1/src/pch.c p_rfc934_nesting = 0; p_timestamp[OLD].tv_sec = p_timestamp[NEW].tv_sec = -1; p_says_nonexistent[OLD] = p_says_nonexistent[NEW] = 0; -@@ -914,7 +916,7 @@ intuit_diff_type (bool need_header, mode +@@ -968,7 +970,7 @@ intuit_diff_type (bool need_header, mode } else { @@ -169,7 +169,7 @@ diff -up patch-2.7.1/src/pch.c.selinux patch-2.7.1/src/pch.c if (! stat_errno[i]) { if (lookup_file_id (&st[i]) == DELETE_LATER) -@@ -953,7 +955,7 @@ intuit_diff_type (bool need_header, mode +@@ -1007,7 +1009,7 @@ intuit_diff_type (bool need_header, mode if (cs) { if (version_get (p_name[i], cs, false, readonly, @@ -178,7 +178,7 @@ diff -up patch-2.7.1/src/pch.c.selinux patch-2.7.1/src/pch.c stat_errno[i] = 0; else version_controlled[i] = 0; -@@ -1006,7 +1008,7 @@ intuit_diff_type (bool need_header, mode +@@ -1060,7 +1062,7 @@ intuit_diff_type (bool need_header, mode { if (inname) { @@ -187,7 +187,7 @@ diff -up patch-2.7.1/src/pch.c.selinux patch-2.7.1/src/pch.c if (inerrno || (instat.st_mode & S_IFMT) == file_type) maybe_reverse (inname, inerrno, inerrno || instat.st_size == 0); } -@@ -1019,8 +1021,14 @@ intuit_diff_type (bool need_header, mode +@@ -1073,8 +1075,14 @@ intuit_diff_type (bool need_header, mode inerrno = stat_errno[i]; invc = version_controlled[i]; instat = st[i]; @@ -203,8 +203,8 @@ diff -up patch-2.7.1/src/pch.c.selinux patch-2.7.1/src/pch.c } diff -up patch-2.7.1/src/util.c.selinux patch-2.7.1/src/util.c ---- patch-2.7.1/src/util.c.selinux 2012-09-22 21:09:10.000000000 +0100 -+++ patch-2.7.1/src/util.c 2012-10-18 18:23:51.358951905 +0100 +--- patch-2.7.1/src/util.c.selinux 2015-01-20 12:26:32.912110141 +0000 ++++ patch-2.7.1/src/util.c 2015-01-20 12:26:32.917110158 +0000 @@ -294,6 +294,19 @@ set_file_attributes (char const *to, enu S_ISLNK (mode) ? "symbolic link" : "file", quotearg (to)); @@ -234,7 +234,7 @@ diff -up patch-2.7.1/src/util.c.selinux patch-2.7.1/src/util.c if (backup) create_backup (to, to_errno ? NULL : &to_st, false); if (! to_errno) -@@ -810,7 +823,8 @@ version_controller (char const *filename +@@ -817,7 +830,8 @@ version_controller (char const *filename Return true if successful. */ bool version_get (char const *filename, char const *cs, bool exists, bool readonly, @@ -244,7 +244,7 @@ diff -up patch-2.7.1/src/util.c.selinux patch-2.7.1/src/util.c { if (patch_get < 0) { -@@ -835,6 +849,13 @@ version_get (char const *filename, char +@@ -842,6 +856,13 @@ version_get (char const *filename, char fatal ("Can't get file %s from %s", quotearg (filename), cs); if (stat (filename, filestat) != 0) pfatal ("%s", quotearg (filename)); @@ -258,7 +258,7 @@ diff -up patch-2.7.1/src/util.c.selinux patch-2.7.1/src/util.c } return 1; -@@ -1653,10 +1674,26 @@ make_tempfile (char const **name, char l +@@ -1660,10 +1681,26 @@ make_tempfile (char const **name, char l } } @@ -289,7 +289,7 @@ diff -up patch-2.7.1/src/util.c.selinux patch-2.7.1/src/util.c } diff -up patch-2.7.1/src/util.h.selinux patch-2.7.1/src/util.h --- patch-2.7.1/src/util.h.selinux 2012-09-21 21:21:16.000000000 +0100 -+++ patch-2.7.1/src/util.h 2012-10-18 18:02:38.923626167 +0100 ++++ patch-2.7.1/src/util.h 2015-01-20 12:26:32.917110158 +0000 @@ -45,7 +45,7 @@ char *parse_name (char const *, int, cha char *savebuf (char const *, size_t); char *savestr (char const *); diff --git a/patch.spec b/patch.spec index 99b886c..7609a46 100644 --- a/patch.spec +++ b/patch.spec @@ -1,7 +1,7 @@ Summary: Utility for modifying/upgrading files Name: patch Version: 2.7.1 -Release: 9%{?dist} +Release: 10%{?dist} License: GPLv3+ URL: http://www.gnu.org/software/patch/patch.html Group: Development/Tools @@ -9,6 +9,7 @@ Source: ftp://ftp.gnu.org/gnu/patch/patch-%{version}.tar.xz Patch1: patch-remove-empty-dir.patch Patch2: patch-args.patch Patch3: patch-args-segfault.patch +Patch4: patch-CVE-2015-1196.patch Patch100: patch-selinux.patch Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) @@ -38,6 +39,10 @@ applications. # Don't segfault when given bad arguments (bug #972330). %patch3 -p1 -b .args-segfault +# Apply upstream patch to fix directory traversal via symlinks +# (bug #1182157, CVE-2015-1196). +%patch4 -p1 -b .CVE-2015-1196 + # SELinux support. %patch100 -p1 -b .selinux @@ -66,6 +71,10 @@ rm -rf $RPM_BUILD_ROOT %{_mandir}/*/* %changelog +* Tue Jan 20 2015 Tim Waugh - 2.7.1-10 +- Apply upstream patch to fix directory traversal via symlinks + (bug #1182157, CVE-2015-1196). + * Sun Aug 17 2014 Fedora Release Engineering - 2.7.1-9 - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild