diff options
author | Martin Jansa <martin.jansa@gmail.com> | 2019-03-10 20:12:01 +0000 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2019-03-25 23:12:03 +0000 |
commit | 2333147fa6be430bbb64381d7c3eaf2f3f4ead1f (patch) | |
tree | 1cf981764be45bf32619d7d327c83a9beeb1bd6c /meta/recipes-core/busybox/busybox | |
parent | e0fdb98b0f09d6a5c7e2f72dce480c56710f3697 (diff) | |
download | poky-2333147fa6be430bbb64381d7c3eaf2f3f4ead1f.tar.gz |
busybox: backport fix for issues introduced by CVE-2011-5325.patch
(From OE-Core rev: d0555e89514f2641387ef061f9ffcd1c8ced008c)
Signed-off-by: Martin Jansa <Martin.Jansa@gmail.com>
Reviewed-by: Tom Rini <trini@konsulko.com>
Signed-off-by: Armin Kuster <akuster808@gmail.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/recipes-core/busybox/busybox')
-rw-r--r-- | meta/recipes-core/busybox/busybox/CVE-2011-5325-fix.patch | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/meta/recipes-core/busybox/busybox/CVE-2011-5325-fix.patch b/meta/recipes-core/busybox/busybox/CVE-2011-5325-fix.patch new file mode 100644 index 0000000000..a8d7e4bd50 --- /dev/null +++ b/meta/recipes-core/busybox/busybox/CVE-2011-5325-fix.patch | |||
@@ -0,0 +1,393 @@ | |||
1 | From 3e1e224fd031ae3927acda70f6e1fa55193e5b68 Mon Sep 17 00:00:00 2001 | ||
2 | From: Denys Vlasenko <vda.linux@googlemail.com> | ||
3 | Date: Tue, 20 Feb 2018 15:57:45 +0100 | ||
4 | Subject: [PATCH] tar,unzip: postpone creation of symlinks with "suspicious" | ||
5 | targets | ||
6 | |||
7 | This mostly reverts commit bc9bbeb2b81001e8731cd2ae501c8fccc8d87cc7 | ||
8 | "libarchive: do not extract unsafe symlinks unless $EXTRACT_UNSAFE_SYMLINKS=1" | ||
9 | |||
10 | Users report that it is somewhat too restrictive. See | ||
11 | https://bugs.busybox.net/show_bug.cgi?id=8411 | ||
12 | |||
13 | In particular, this interferes with unpacking of busybox-based | ||
14 | filesystems with links like "sbin/applet" -> "../bin/busybox". | ||
15 | |||
16 | The change is made smaller by deleting ARCHIVE_EXTRACT_QUIET flag - | ||
17 | it is unused since 2010, and removing conditionals on it | ||
18 | allows commonalizing some error message codes. | ||
19 | |||
20 | function old new delta | ||
21 | create_or_remember_symlink - 94 +94 | ||
22 | create_symlinks_from_list - 64 +64 | ||
23 | tar_main 1002 1006 +4 | ||
24 | unzip_main 2732 2724 -8 | ||
25 | data_extract_all 984 891 -93 | ||
26 | unsafe_symlink_target 147 - -147 | ||
27 | ------------------------------------------------------------------------------ | ||
28 | (add/remove: 2/1 grow/shrink: 1/2 up/down: 162/-248) Total: -86 bytes | ||
29 | |||
30 | Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> | ||
31 | |||
32 | Upstream-Status: Backport from 1.28.2 [https://git.busybox.net/busybox/commit/?h=1_28_stable&id=37277a23fe48b13313f5d96084d890ed21d5fd8b] | ||
33 | |||
34 | Signed-off-by: Martin Jansa <Martin.Jansa@gmail.com> | ||
35 | --- | ||
36 | archival/libarchive/data_extract_all.c | 50 +++++++++------- | ||
37 | archival/libarchive/unsafe_symlink_target.c | 63 +++++++++------------ | ||
38 | archival/tar.c | 2 + | ||
39 | archival/unzip.c | 29 ++++++---- | ||
40 | include/bb_archive.h | 23 +++++--- | ||
41 | testsuite/tar.tests | 10 ++-- | ||
42 | 6 files changed, 95 insertions(+), 82 deletions(-) | ||
43 | |||
44 | diff --git a/archival/libarchive/data_extract_all.c b/archival/libarchive/data_extract_all.c | ||
45 | index b828b656d..dad8d7d87 100644 | ||
46 | --- a/archival/libarchive/data_extract_all.c | ||
47 | +++ b/archival/libarchive/data_extract_all.c | ||
48 | @@ -108,9 +108,7 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | ||
49 | } | ||
50 | } | ||
51 | else if (existing_sb.st_mtime >= file_header->mtime) { | ||
52 | - if (!(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) | ||
53 | - && !S_ISDIR(file_header->mode) | ||
54 | - ) { | ||
55 | + if (!S_ISDIR(file_header->mode)) { | ||
56 | bb_error_msg("%s not created: newer or " | ||
57 | "same age file exists", dst_name); | ||
58 | } | ||
59 | @@ -126,7 +124,7 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | ||
60 | /* Handle hard links separately */ | ||
61 | if (hard_link) { | ||
62 | res = link(hard_link, dst_name); | ||
63 | - if (res != 0 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) { | ||
64 | + if (res != 0) { | ||
65 | /* shared message */ | ||
66 | bb_perror_msg("can't create %slink '%s' to '%s'", | ||
67 | "hard", dst_name, hard_link | ||
68 | @@ -166,10 +164,9 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | ||
69 | } | ||
70 | case S_IFDIR: | ||
71 | res = mkdir(dst_name, file_header->mode); | ||
72 | - if ((res == -1) | ||
73 | + if ((res != 0) | ||
74 | && (errno != EISDIR) /* btw, Linux doesn't return this */ | ||
75 | && (errno != EEXIST) | ||
76 | - && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) | ||
77 | ) { | ||
78 | bb_perror_msg("can't make dir %s", dst_name); | ||
79 | } | ||
80 | @@ -177,27 +174,38 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | ||
81 | case S_IFLNK: | ||
82 | /* Symlink */ | ||
83 | //TODO: what if file_header->link_target == NULL (say, corrupted tarball?) | ||
84 | - if (!unsafe_symlink_target(file_header->link_target)) { | ||
85 | - res = symlink(file_header->link_target, dst_name); | ||
86 | - if (res != 0 | ||
87 | - && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) | ||
88 | - ) { | ||
89 | - /* shared message */ | ||
90 | - bb_perror_msg("can't create %slink '%s' to '%s'", | ||
91 | - "sym", | ||
92 | - dst_name, file_header->link_target | ||
93 | - ); | ||
94 | - } | ||
95 | - } | ||
96 | + | ||
97 | + /* To avoid a directory traversal attack via symlinks, | ||
98 | + * do not restore symlinks with ".." components | ||
99 | + * or symlinks starting with "/", unless a magic | ||
100 | + * envvar is set. | ||
101 | + * | ||
102 | + * For example, consider a .tar created via: | ||
103 | + * $ tar cvf bug.tar anything.txt | ||
104 | + * $ ln -s /tmp symlink | ||
105 | + * $ tar --append -f bug.tar symlink | ||
106 | + * $ rm symlink | ||
107 | + * $ mkdir symlink | ||
108 | + * $ tar --append -f bug.tar symlink/evil.py | ||
109 | + * | ||
110 | + * This will result in an archive that contains: | ||
111 | + * $ tar --list -f bug.tar | ||
112 | + * anything.txt | ||
113 | + * symlink [-> /tmp] | ||
114 | + * symlink/evil.py | ||
115 | + * | ||
116 | + * Untarring bug.tar would otherwise place evil.py in '/tmp'. | ||
117 | + */ | ||
118 | + create_or_remember_symlink(&archive_handle->symlink_placeholders, | ||
119 | + file_header->link_target, | ||
120 | + dst_name); | ||
121 | break; | ||
122 | case S_IFSOCK: | ||
123 | case S_IFBLK: | ||
124 | case S_IFCHR: | ||
125 | case S_IFIFO: | ||
126 | res = mknod(dst_name, file_header->mode, file_header->device); | ||
127 | - if ((res == -1) | ||
128 | - && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) | ||
129 | - ) { | ||
130 | + if (res != 0) { | ||
131 | bb_perror_msg("can't create node %s", dst_name); | ||
132 | } | ||
133 | break; | ||
134 | diff --git a/archival/libarchive/unsafe_symlink_target.c b/archival/libarchive/unsafe_symlink_target.c | ||
135 | index ee46e28f8..8dcafeaa1 100644 | ||
136 | --- a/archival/libarchive/unsafe_symlink_target.c | ||
137 | +++ b/archival/libarchive/unsafe_symlink_target.c | ||
138 | @@ -5,44 +5,37 @@ | ||
139 | #include "libbb.h" | ||
140 | #include "bb_archive.h" | ||
141 | |||
142 | -int FAST_FUNC unsafe_symlink_target(const char *target) | ||
143 | +void FAST_FUNC create_or_remember_symlink(llist_t **symlink_placeholders, | ||
144 | + const char *target, | ||
145 | + const char *linkname) | ||
146 | { | ||
147 | - const char *dot; | ||
148 | - | ||
149 | - if (target[0] == '/') { | ||
150 | - const char *var; | ||
151 | -unsafe: | ||
152 | - var = getenv("EXTRACT_UNSAFE_SYMLINKS"); | ||
153 | - if (var) { | ||
154 | - if (LONE_CHAR(var, '1')) | ||
155 | - return 0; /* pretend it's safe */ | ||
156 | - return 1; /* "UNSAFE!" */ | ||
157 | - } | ||
158 | - bb_error_msg("skipping unsafe symlink to '%s' in archive," | ||
159 | - " set %s=1 to extract", | ||
160 | - target, | ||
161 | - "EXTRACT_UNSAFE_SYMLINKS" | ||
162 | + if (target[0] == '/' || strstr(target, "..")) { | ||
163 | + llist_add_to(symlink_placeholders, | ||
164 | + xasprintf("%s%c%s", linkname, '\0', target) | ||
165 | + ); | ||
166 | + return; | ||
167 | + } | ||
168 | + if (symlink(target, linkname) != 0) { | ||
169 | + /* shared message */ | ||
170 | + bb_perror_msg_and_die("can't create %slink '%s' to '%s'", | ||
171 | + "sym", linkname, target | ||
172 | ); | ||
173 | - /* Prevent further messages */ | ||
174 | - setenv("EXTRACT_UNSAFE_SYMLINKS", "0", 0); | ||
175 | - return 1; /* "UNSAFE!" */ | ||
176 | } | ||
177 | +} | ||
178 | |||
179 | - dot = target; | ||
180 | - for (;;) { | ||
181 | - dot = strchr(dot, '.'); | ||
182 | - if (!dot) | ||
183 | - return 0; /* safe target */ | ||
184 | +void FAST_FUNC create_symlinks_from_list(llist_t *list) | ||
185 | +{ | ||
186 | + while (list) { | ||
187 | + char *target; | ||
188 | |||
189 | - /* Is it a path component starting with ".."? */ | ||
190 | - if ((dot[1] == '.') | ||
191 | - && (dot == target || dot[-1] == '/') | ||
192 | - /* Is it exactly ".."? */ | ||
193 | - && (dot[2] == '/' || dot[2] == '\0') | ||
194 | - ) { | ||
195 | - goto unsafe; | ||
196 | - } | ||
197 | - /* NB: it can even be trailing ".", should only add 1 */ | ||
198 | - dot += 1; | ||
199 | + target = list->data + strlen(list->data) + 1; | ||
200 | + if (symlink(target, list->data)) { | ||
201 | + /* shared message */ | ||
202 | + bb_error_msg_and_die("can't create %slink '%s' to '%s'", | ||
203 | + "sym", | ||
204 | + list->data, target | ||
205 | + ); | ||
206 | + } | ||
207 | + list = list->link; | ||
208 | } | ||
209 | -} | ||
210 | \ No newline at end of file | ||
211 | +} | ||
212 | diff --git a/archival/tar.c b/archival/tar.c | ||
213 | index 7598b71e3..bde86330c 100644 | ||
214 | --- a/archival/tar.c | ||
215 | +++ b/archival/tar.c | ||
216 | @@ -1252,6 +1252,8 @@ int tar_main(int argc UNUSED_PARAM, char **argv) | ||
217 | while (get_header_tar(tar_handle) == EXIT_SUCCESS) | ||
218 | bb_got_signal = EXIT_SUCCESS; /* saw at least one header, good */ | ||
219 | |||
220 | + create_symlinks_from_list(tar_handle->symlink_placeholders); | ||
221 | + | ||
222 | /* Check that every file that should have been extracted was */ | ||
223 | while (tar_handle->accept) { | ||
224 | if (!find_list_entry(tar_handle->reject, tar_handle->accept->data) | ||
225 | diff --git a/archival/unzip.c b/archival/unzip.c | ||
226 | index 270e261b7..2f53ab7a4 100644 | ||
227 | --- a/archival/unzip.c | ||
228 | +++ b/archival/unzip.c | ||
229 | @@ -335,7 +335,10 @@ static void unzip_create_leading_dirs(const char *fn) | ||
230 | free(name); | ||
231 | } | ||
232 | |||
233 | -static void unzip_extract_symlink(zip_header_t *zip, const char *dst_fn) | ||
234 | +#if ENABLE_FEATURE_UNZIP_CDF | ||
235 | +static void unzip_extract_symlink(llist_t **symlink_placeholders, | ||
236 | + zip_header_t *zip, | ||
237 | + const char *dst_fn) | ||
238 | { | ||
239 | char *target; | ||
240 | |||
241 | @@ -361,17 +364,12 @@ static void unzip_extract_symlink(zip_header_t *zip, const char *dst_fn) | ||
242 | target[xstate.mem_output_size] = '\0'; | ||
243 | #endif | ||
244 | } | ||
245 | - if (!unsafe_symlink_target(target)) { | ||
246 | -//TODO: libbb candidate | ||
247 | - if (symlink(target, dst_fn)) { | ||
248 | - /* shared message */ | ||
249 | - bb_perror_msg_and_die("can't create %slink '%s' to '%s'", | ||
250 | - "sym", dst_fn, target | ||
251 | - ); | ||
252 | - } | ||
253 | - } | ||
254 | + create_or_remember_symlink(symlink_placeholders, | ||
255 | + target, | ||
256 | + dst_fn); | ||
257 | free(target); | ||
258 | } | ||
259 | +#endif | ||
260 | |||
261 | static void unzip_extract(zip_header_t *zip, int dst_fd) | ||
262 | { | ||
263 | @@ -464,6 +462,9 @@ int unzip_main(int argc, char **argv) | ||
264 | llist_t *zaccept = NULL; | ||
265 | llist_t *zreject = NULL; | ||
266 | char *base_dir = NULL; | ||
267 | +#if ENABLE_FEATURE_UNZIP_CDF | ||
268 | + llist_t *symlink_placeholders = NULL; | ||
269 | +#endif | ||
270 | int i, opt; | ||
271 | char key_buf[80]; /* must match size used by my_fgets80 */ | ||
272 | struct stat stat_buf; | ||
273 | @@ -894,8 +895,8 @@ int unzip_main(int argc, char **argv) | ||
274 | } | ||
275 | #if ENABLE_FEATURE_UNZIP_CDF | ||
276 | if (S_ISLNK(file_mode)) { | ||
277 | - if (dst_fd != STDOUT_FILENO) /* no -p */ | ||
278 | - unzip_extract_symlink(&zip, dst_fn); | ||
279 | + if (dst_fd != STDOUT_FILENO) /* not -p? */ | ||
280 | + unzip_extract_symlink(&symlink_placeholders, &zip, dst_fn); | ||
281 | } else | ||
282 | #endif | ||
283 | { | ||
284 | @@ -931,6 +932,10 @@ int unzip_main(int argc, char **argv) | ||
285 | total_entries++; | ||
286 | } | ||
287 | |||
288 | +#if ENABLE_FEATURE_UNZIP_CDF | ||
289 | + create_symlinks_from_list(symlink_placeholders); | ||
290 | +#endif | ||
291 | + | ||
292 | if (listing && quiet <= 1) { | ||
293 | if (!verbose) { | ||
294 | // " Length Date Time Name\n" | ||
295 | diff --git a/include/bb_archive.h b/include/bb_archive.h | ||
296 | index 1e4da3c33..436eb0fe3 100644 | ||
297 | --- a/include/bb_archive.h | ||
298 | +++ b/include/bb_archive.h | ||
299 | @@ -64,6 +64,9 @@ typedef struct archive_handle_t { | ||
300 | /* Currently processed file's header */ | ||
301 | file_header_t *file_header; | ||
302 | |||
303 | + /* List of symlink placeholders */ | ||
304 | + llist_t *symlink_placeholders; | ||
305 | + | ||
306 | /* Process the header component, e.g. tar -t */ | ||
307 | void FAST_FUNC (*action_header)(const file_header_t *); | ||
308 | |||
309 | @@ -119,15 +122,14 @@ typedef struct archive_handle_t { | ||
310 | #define ARCHIVE_RESTORE_DATE (1 << 0) | ||
311 | #define ARCHIVE_CREATE_LEADING_DIRS (1 << 1) | ||
312 | #define ARCHIVE_UNLINK_OLD (1 << 2) | ||
313 | -#define ARCHIVE_EXTRACT_QUIET (1 << 3) | ||
314 | -#define ARCHIVE_EXTRACT_NEWER (1 << 4) | ||
315 | -#define ARCHIVE_DONT_RESTORE_OWNER (1 << 5) | ||
316 | -#define ARCHIVE_DONT_RESTORE_PERM (1 << 6) | ||
317 | -#define ARCHIVE_NUMERIC_OWNER (1 << 7) | ||
318 | -#define ARCHIVE_O_TRUNC (1 << 8) | ||
319 | -#define ARCHIVE_REMEMBER_NAMES (1 << 9) | ||
320 | +#define ARCHIVE_EXTRACT_NEWER (1 << 3) | ||
321 | +#define ARCHIVE_DONT_RESTORE_OWNER (1 << 4) | ||
322 | +#define ARCHIVE_DONT_RESTORE_PERM (1 << 5) | ||
323 | +#define ARCHIVE_NUMERIC_OWNER (1 << 6) | ||
324 | +#define ARCHIVE_O_TRUNC (1 << 7) | ||
325 | +#define ARCHIVE_REMEMBER_NAMES (1 << 8) | ||
326 | #if ENABLE_RPM | ||
327 | -#define ARCHIVE_REPLACE_VIA_RENAME (1 << 10) | ||
328 | +#define ARCHIVE_REPLACE_VIA_RENAME (1 << 9) | ||
329 | #endif | ||
330 | |||
331 | |||
332 | @@ -196,7 +198,10 @@ void seek_by_jump(int fd, off_t amount) FAST_FUNC; | ||
333 | void seek_by_read(int fd, off_t amount) FAST_FUNC; | ||
334 | |||
335 | const char *strip_unsafe_prefix(const char *str) FAST_FUNC; | ||
336 | -int unsafe_symlink_target(const char *target) FAST_FUNC; | ||
337 | +void create_or_remember_symlink(llist_t **symlink_placeholders, | ||
338 | + const char *target, | ||
339 | + const char *linkname) FAST_FUNC; | ||
340 | +void create_symlinks_from_list(llist_t *list) FAST_FUNC; | ||
341 | |||
342 | void data_align(archive_handle_t *archive_handle, unsigned boundary) FAST_FUNC; | ||
343 | const llist_t *find_list_entry(const llist_t *list, const char *filename) FAST_FUNC; | ||
344 | diff --git a/testsuite/tar.tests b/testsuite/tar.tests | ||
345 | index 127eeaaee..21cef49fe 100755 | ||
346 | --- a/testsuite/tar.tests | ||
347 | +++ b/testsuite/tar.tests | ||
348 | @@ -279,7 +279,7 @@ optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2 | ||
349 | testing "tar does not extract into symlinks" "\ | ||
350 | >>/tmp/passwd && uudecode -o input && tar xf input 2>&1 && rm passwd; cat /tmp/passwd; echo \$? | ||
351 | " "\ | ||
352 | -tar: skipping unsafe symlink to '/tmp/passwd' in archive, set EXTRACT_UNSAFE_SYMLINKS=1 to extract | ||
353 | +tar: can't create symlink 'passwd' to '/tmp/passwd' | ||
354 | 0 | ||
355 | " \ | ||
356 | "" "\ | ||
357 | @@ -299,7 +299,7 @@ optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2 | ||
358 | testing "tar -k does not extract into symlinks" "\ | ||
359 | >>/tmp/passwd && uudecode -o input && tar xf input -k 2>&1 && rm passwd; cat /tmp/passwd; echo \$? | ||
360 | " "\ | ||
361 | -tar: skipping unsafe symlink to '/tmp/passwd' in archive, set EXTRACT_UNSAFE_SYMLINKS=1 to extract | ||
362 | +tar: can't create symlink 'passwd' to '/tmp/passwd' | ||
363 | 0 | ||
364 | " \ | ||
365 | "" "\ | ||
366 | @@ -324,11 +324,11 @@ rm -rf etc usr | ||
367 | ' "\ | ||
368 | etc/ssl/certs/3b2716e5.0 | ||
369 | etc/ssl/certs/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem | ||
370 | -tar: skipping unsafe symlink to '/usr/share/ca-certificates/mozilla/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.crt' in archive, set EXTRACT_UNSAFE_SYMLINKS=1 to extract | ||
371 | etc/ssl/certs/f80cc7f6.0 | ||
372 | usr/share/ca-certificates/mozilla/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.crt | ||
373 | 0 | ||
374 | etc/ssl/certs/3b2716e5.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem | ||
375 | +etc/ssl/certs/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem -> /usr/share/ca-certificates/mozilla/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.crt | ||
376 | etc/ssl/certs/f80cc7f6.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem | ||
377 | " \ | ||
378 | "" "" | ||
379 | @@ -346,9 +346,9 @@ ls symlink/bb_test_evilfile | ||
380 | ' "\ | ||
381 | anything.txt | ||
382 | symlink | ||
383 | -tar: skipping unsafe symlink to '/tmp' in archive, set EXTRACT_UNSAFE_SYMLINKS=1 to extract | ||
384 | symlink/bb_test_evilfile | ||
385 | -0 | ||
386 | +tar: can't create symlink 'symlink' to '/tmp' | ||
387 | +1 | ||
388 | ls: /tmp/bb_test_evilfile: No such file or directory | ||
389 | ls: bb_test_evilfile: No such file or directory | ||
390 | symlink/bb_test_evilfile | ||
391 | -- | ||
392 | 2.17.1 | ||
393 | |||