Apply upstream patch to fix directory traversal via symlinks (bug #1182157, CVE-2015-1196).

Resolves: rhbz#1182157 rhbz#1182154
This commit is contained in:
Tim Waugh 2015-01-20 12:37:09 +00:00
parent ab31dd6e53
commit e72bf6daa2
3 changed files with 190 additions and 25 deletions

156
patch-CVE-2015-1196.patch Normal file
View File

@ -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 <<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
+new file mode 120000
+index 0000000..cad2309
+--- /dev/null
++++ b/bar
+@@ -0,0 +1 @@
++/bar
+\ No newline at end of file
+EOF
+
+check 'patch -p1 < bad-symlink-target1.diff || echo "Status: $?"' <<EOF
+patching symbolic link bar
+symbolic link target '/bar' is invalid
+Status: 2
+EOF
+
+cat > bad-symlink-target2.diff <<EOF
+diff --git a/baz b/baz
+new file mode 120000
+index 0000000..cad2309
+--- /dev/null
++++ b/baz
+@@ -0,0 +1 @@
++../baz
+\ No newline at end of file
+EOF
+
+check 'patch -p1 < bad-symlink-target2.diff || echo "Status: $?"' <<EOF
+patching symbolic link baz
+symbolic link target '../baz' is invalid
+Status: 2
+EOF
+
+# --------------------------------------------------------------
+
# The backup file of a new symlink is an empty regular file.
check 'patch -p1 --backup < create-symlink.diff || echo "Status: $?"' <<EOF

View File

@ -1,6 +1,6 @@
diff -up patch-2.7.1/src/common.h.selinux patch-2.7.1/src/common.h diff -up patch-2.7.1/src/common.h.selinux patch-2.7.1/src/common.h
--- patch-2.7.1/src/common.h.selinux 2012-09-28 15:00:04.000000000 +0100 --- patch-2.7.1/src/common.h.selinux 2012-09-28 15:00:04.000000000 +0100
+++ patch-2.7.1/src/common.h 2012-10-18 17:53:43.735748614 +0100 +++ patch-2.7.1/src/common.h 2015-01-20 12:26:32.914110148 +0000
@@ -30,6 +30,8 @@ @@ -30,6 +30,8 @@
#include <sys/types.h> #include <sys/types.h>
#include <time.h> #include <time.h>
@ -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 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.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 @@ -138,7 +138,7 @@ get_input_file (char const *filename, ch
char *getbuf; 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))) && (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 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.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 = \ @@ -34,7 +34,7 @@ patch_SOURCES = \
AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib 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 patch_SOURCES += merge.c
diff -up patch-2.7.1/src/Makefile.in.selinux patch-2.7.1/src/Makefile.in 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.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 @@ -981,7 +981,7 @@ patch_SOURCES = bestmatch.h common.h inp
AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib \ AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib \
$(am__append_2) $(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 all: all-am
diff -up patch-2.7.1/src/patch.c.selinux patch-2.7.1/src/patch.c 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.selinux 2015-01-20 12:26:32.910110134 +0000
+++ patch-2.7.1/src/patch.c 2012-10-18 17:57:41.708586721 +0100 +++ patch-2.7.1/src/patch.c 2015-01-20 12:26:32.915110151 +0000
@@ -256,19 +256,19 @@ main (int argc, char **argv) @@ -257,19 +257,19 @@ main (int argc, char **argv)
if (! strcmp (inname, outname)) if (! strcmp (inname, outname))
{ {
if (inerrno == -1) 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; inerrno = -1;
} }
if (! outerrno) if (! outerrno)
@@ -563,7 +563,7 @@ main (int argc, char **argv) @@ -564,7 +564,7 @@ main (int argc, char **argv)
mode, &new_time); mode, &new_time);
else 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, set_file_attributes (TMPOUTNAME, attr, inname, &instat,
mode, &new_time); mode, &new_time);
} }
@@ -623,7 +623,7 @@ main (int argc, char **argv) @@ -624,7 +624,7 @@ main (int argc, char **argv)
struct stat oldst; struct stat oldst;
int olderrno; 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) if (olderrno && olderrno != ENOENT)
write_fatal (); write_fatal ();
if (! olderrno && lookup_file_id (&oldst) == CREATED) 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) 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; st = &st_tmp;
} }
diff -up patch-2.7.1/src/pch.c.selinux patch-2.7.1/src/pch.c 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.selinux 2015-01-20 12:26:32.911110138 +0000
+++ patch-2.7.1/src/pch.c 2012-10-18 18:04:28.619008832 +0100 +++ patch-2.7.1/src/pch.c 2015-01-20 12:26:32.916110154 +0000
@@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/* reading patches */ /* 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) if (inerrno)
{ {
perror (inname); 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; bool extended_headers = false;
enum nametype i; enum nametype i;
struct stat st[3]; 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 stat_errno[3];
int version_controlled[3]; int version_controlled[3];
enum diff retval; 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[OLD] = -1;
version_controlled[NEW] = -1; version_controlled[NEW] = -1;
version_controlled[INDEX] = -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_rfc934_nesting = 0;
p_timestamp[OLD].tv_sec = p_timestamp[NEW].tv_sec = -1; p_timestamp[OLD].tv_sec = p_timestamp[NEW].tv_sec = -1;
p_says_nonexistent[OLD] = p_says_nonexistent[NEW] = 0; 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 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 (! stat_errno[i])
{ {
if (lookup_file_id (&st[i]) == DELETE_LATER) 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 (cs)
{ {
if (version_get (p_name[i], cs, false, readonly, 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; stat_errno[i] = 0;
else else
version_controlled[i] = 0; 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) 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) if (inerrno || (instat.st_mode & S_IFMT) == file_type)
maybe_reverse (inname, inerrno, inerrno || instat.st_size == 0); 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]; inerrno = stat_errno[i];
invc = version_controlled[i]; invc = version_controlled[i];
instat = st[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 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.selinux 2015-01-20 12:26:32.912110141 +0000
+++ patch-2.7.1/src/util.c 2012-10-18 18:23:51.358951905 +0100 +++ 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 @@ -294,6 +294,19 @@ set_file_attributes (char const *to, enu
S_ISLNK (mode) ? "symbolic link" : "file", S_ISLNK (mode) ? "symbolic link" : "file",
quotearg (to)); 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) if (backup)
create_backup (to, to_errno ? NULL : &to_st, false); create_backup (to, to_errno ? NULL : &to_st, false);
if (! to_errno) 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. */ Return true if successful. */
bool bool
version_get (char const *filename, char const *cs, bool exists, bool readonly, 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) 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); fatal ("Can't get file %s from %s", quotearg (filename), cs);
if (stat (filename, filestat) != 0) if (stat (filename, filestat) != 0)
pfatal ("%s", quotearg (filename)); 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; 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 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.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 @@ -45,7 +45,7 @@ char *parse_name (char const *, int, cha
char *savebuf (char const *, size_t); char *savebuf (char const *, size_t);
char *savestr (char const *); char *savestr (char const *);

View File

@ -1,7 +1,7 @@
Summary: Utility for modifying/upgrading files Summary: Utility for modifying/upgrading files
Name: patch Name: patch
Version: 2.7.1 Version: 2.7.1
Release: 9%{?dist} Release: 10%{?dist}
License: GPLv3+ License: GPLv3+
URL: http://www.gnu.org/software/patch/patch.html URL: http://www.gnu.org/software/patch/patch.html
Group: Development/Tools Group: Development/Tools
@ -9,6 +9,7 @@ Source: ftp://ftp.gnu.org/gnu/patch/patch-%{version}.tar.xz
Patch1: patch-remove-empty-dir.patch Patch1: patch-remove-empty-dir.patch
Patch2: patch-args.patch Patch2: patch-args.patch
Patch3: patch-args-segfault.patch Patch3: patch-args-segfault.patch
Patch4: patch-CVE-2015-1196.patch
Patch100: patch-selinux.patch Patch100: patch-selinux.patch
Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
@ -38,6 +39,10 @@ applications.
# Don't segfault when given bad arguments (bug #972330). # Don't segfault when given bad arguments (bug #972330).
%patch3 -p1 -b .args-segfault %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. # SELinux support.
%patch100 -p1 -b .selinux %patch100 -p1 -b .selinux
@ -66,6 +71,10 @@ rm -rf $RPM_BUILD_ROOT
%{_mandir}/*/* %{_mandir}/*/*
%changelog %changelog
* Tue Jan 20 2015 Tim Waugh <twaugh@redhat.com> - 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 <rel-eng@lists.fedoraproject.org> - 2.7.1-9 * Sun Aug 17 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.7.1-9
- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild