summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/patch/patch/patch-CVE-2015-1196.patch
blob: d408346ac25728de07d5ab7528022ed8dd5d8aff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
From 4e9269a5fc1fe80a1095a92593dd85db871e1fd3 Mon Sep 17 00:00:00 2001
From: Andreas Gruenbacher <andreas.gruenbacher@gmail.com>
Date: Mon, 19 Jan 2015 23:18:30 +0100
Subject: [PATCH] Make sure symlinks don't point outside working directory
 (CVE-2015-1196)

When creating symlinks from git-style patches, make sure the symlinks don't
point above the current working directory.  Otherwise, a subsequent patch could
use the symlink to write outside the working directory.

* src/pch.c (symlink_target_is_valid): New function to check for valid symlink
targets.
* src/util.c (move_file): Use symlink_target_is_valid() here.
* tests/symlinks: Add valid and invalid symlink test cases.

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>

Upstream-Status: Backport

---
 NEWS           |  3 ++
 src/pch.c      | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/pch.h      |  1 +
 src/util.c     |  7 +++++++
 tests/symlinks | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 117 insertions(+)

diff --git a/NEWS b/NEWS
index 42afed7..d3f1c2d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,6 @@
+* With git-style patches, symlinks that point outside the working directory
+  will no longer be created.
+
 Changes in version 2.7.1:
 
 * Two critical bug fixes in the "diff --git" format support.
diff --git a/src/pch.c b/src/pch.c
index 55e1504..f05ef83 100644
--- a/src/pch.c
+++ b/src/pch.c
@@ -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 --git a/src/pch.h b/src/pch.h
index 0c7ff62..58861b0 100644
--- a/src/pch.h
+++ b/src/pch.h
@@ -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 --git a/src/util.c b/src/util.c
index 66ae90f..636eded 100644
--- a/src/util.c
+++ b/src/util.c
@@ -470,6 +470,13 @@ move_file (char const *from, bool *from_needs_removal,
 	    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 --git a/tests/symlinks b/tests/symlinks
index 96626b3..6211026 100644
--- a/tests/symlinks
+++ b/tests/symlinks
@@ -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
-- 
2.1.4