aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--archival/libarchive/Kbuild.src2
-rw-r--r--archival/libarchive/data_extract_all.c37
-rw-r--r--archival/libarchive/unsafe_symlink_target.c48
-rw-r--r--archival/tar.c19
-rw-r--r--archival/unzip.c10
-rw-r--r--coreutils/link.c5
-rw-r--r--include/bb_archive.h4
-rw-r--r--libbb/copy_file.c5
-rwxr-xr-xtestsuite/tar.tests10
9 files changed, 85 insertions, 55 deletions
diff --git a/archival/libarchive/Kbuild.src b/archival/libarchive/Kbuild.src
index 942e755b9..e1a8a7529 100644
--- a/archival/libarchive/Kbuild.src
+++ b/archival/libarchive/Kbuild.src
@@ -12,6 +12,8 @@ COMMON_FILES:= \
12 data_extract_all.o \ 12 data_extract_all.o \
13 data_extract_to_stdout.o \ 13 data_extract_to_stdout.o \
14\ 14\
15 unsafe_symlink_target.o \
16\
15 filter_accept_all.o \ 17 filter_accept_all.o \
16 filter_accept_list.o \ 18 filter_accept_list.o \
17 filter_accept_reject_list.o \ 19 filter_accept_reject_list.o \
diff --git a/archival/libarchive/data_extract_all.c b/archival/libarchive/data_extract_all.c
index 1ce927c2f..e658444e0 100644
--- a/archival/libarchive/data_extract_all.c
+++ b/archival/libarchive/data_extract_all.c
@@ -129,9 +129,7 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
129 if (res != 0 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) { 129 if (res != 0 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) {
130 /* shared message */ 130 /* shared message */
131 bb_perror_msg("can't create %slink '%s' to '%s'", 131 bb_perror_msg("can't create %slink '%s' to '%s'",
132 "hard", 132 "hard", dst_name, hard_link
133 dst_name,
134 hard_link
135 ); 133 );
136 } 134 }
137 /* Hardlinks have no separate mode/ownership, skip chown/chmod */ 135 /* Hardlinks have no separate mode/ownership, skip chown/chmod */
@@ -181,7 +179,9 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
181//TODO: what if file_header->link_target == NULL (say, corrupted tarball?) 179//TODO: what if file_header->link_target == NULL (say, corrupted tarball?)
182 180
183 /* To avoid a directory traversal attack via symlinks, 181 /* To avoid a directory traversal attack via symlinks,
184 * for certain link targets postpone creation of symlinks. 182 * do not restore symlinks with ".." components
183 * or symlinks starting with "/", unless a magic
184 * envvar is set.
185 * 185 *
186 * For example, consider a .tar created via: 186 * For example, consider a .tar created via:
187 * $ tar cvf bug.tar anything.txt 187 * $ tar cvf bug.tar anything.txt
@@ -199,24 +199,17 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
199 * 199 *
200 * Untarring bug.tar would otherwise place evil.py in '/tmp'. 200 * Untarring bug.tar would otherwise place evil.py in '/tmp'.
201 */ 201 */
202 if (file_header->link_target[0] == '/' 202 if (!unsafe_symlink_target(file_header->link_target)) {
203 || strstr(file_header->link_target, "..") 203 res = symlink(file_header->link_target, dst_name);
204 ) { 204 if (res != 0
205 llist_add_to(&archive_handle->symlink_placeholders, 205 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
206 xasprintf("%s%c%s", file_header->name, '\0', file_header->link_target) 206 ) {
207 ); 207 /* shared message */
208 break; 208 bb_perror_msg("can't create %slink '%s' to '%s'",
209 } 209 "sym",
210 res = symlink(file_header->link_target, dst_name); 210 dst_name, file_header->link_target
211 if (res != 0 211 );
212 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) 212 }
213 ) {
214 /* shared message */
215 bb_perror_msg("can't create %slink '%s' to '%s'",
216 "sym",
217 dst_name,
218 file_header->link_target
219 );
220 } 213 }
221 break; 214 break;
222 case S_IFSOCK: 215 case S_IFSOCK:
diff --git a/archival/libarchive/unsafe_symlink_target.c b/archival/libarchive/unsafe_symlink_target.c
new file mode 100644
index 000000000..441ba8b24
--- /dev/null
+++ b/archival/libarchive/unsafe_symlink_target.c
@@ -0,0 +1,48 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4 */
5#include "libbb.h"
6#include "bb_archive.h"
7
8int FAST_FUNC unsafe_symlink_target(const char *target)
9{
10 const char *dot;
11
12 if (target[0] == '/') {
13 const char *var;
14 unsafe:
15 var = getenv("EXTRACT_UNSAFE_SYMLINKS");
16 if (var) {
17 if (LONE_CHAR(var, '1'))
18 return 0; /* pretend it's safe */
19 return 1; /* "UNSAFE!" */
20 }
21 bb_error_msg("skipping unsafe symlink to '%s' in archive,"
22 " set %s=1 to extract",
23 target,
24 "EXTRACT_UNSAFE_SYMLINKS"
25 );
26 /* Prevent further messages */
27 setenv("EXTRACT_UNSAFE_SYMLINKS", "0", 0);
28 return 1; /* "UNSAFE!" */
29 }
30
31 dot = target;
32 for (;;) {
33 dot = strchr(dot, '.');
34 if (!dot)
35 return 0; /* safe target */
36
37 /* Is it a path component starting with ".."? */
38 if ((dot[1] == '.')
39 && (dot == target || dot[-1] == '/')
40 /* Is it exactly ".."? */
41 && (dot[2] == '/' || dot[2] == '\0')
42 ) {
43 goto unsafe;
44 }
45 /* NB: it can even be trailing ".", should only add 1 */
46 dot += 1;
47 }
48}
diff --git a/archival/tar.c b/archival/tar.c
index 73c14ca81..9a5bcc7fe 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -278,23 +278,6 @@ static void chksum_and_xwrite(int fd, struct tar_header_t* hp)
278 xwrite(fd, hp, sizeof(*hp)); 278 xwrite(fd, hp, sizeof(*hp));
279} 279}
280 280
281static void replace_symlink_placeholders(llist_t *list)
282{
283 while (list) {
284 char *target;
285
286 target = list->data + strlen(list->data) + 1;
287 if (symlink(target, list->data)) {
288 /* shared message */
289 bb_error_msg_and_die("can't create %slink '%s' to '%s'",
290 "sym",
291 list->data, target
292 );
293 }
294 list = list->link;
295 }
296}
297
298#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS 281#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
299static void writeLongname(int fd, int type, const char *name, int dir) 282static void writeLongname(int fd, int type, const char *name, int dir)
300{ 283{
@@ -1255,8 +1238,6 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
1255 while (get_header_tar(tar_handle) == EXIT_SUCCESS) 1238 while (get_header_tar(tar_handle) == EXIT_SUCCESS)
1256 bb_got_signal = EXIT_SUCCESS; /* saw at least one header, good */ 1239 bb_got_signal = EXIT_SUCCESS; /* saw at least one header, good */
1257 1240
1258 replace_symlink_placeholders(tar_handle->symlink_placeholders);
1259
1260 /* Check that every file that should have been extracted was */ 1241 /* Check that every file that should have been extracted was */
1261 while (tar_handle->accept) { 1242 while (tar_handle->accept) {
1262 if (!find_list_entry(tar_handle->reject, tar_handle->accept->data) 1243 if (!find_list_entry(tar_handle->reject, tar_handle->accept->data)
diff --git a/archival/unzip.c b/archival/unzip.c
index 8ed9ae7d5..604166063 100644
--- a/archival/unzip.c
+++ b/archival/unzip.c
@@ -368,9 +368,15 @@ static void unzip_extract_symlink(zip_header_t *zip, const char *dst_fn)
368 target[xstate.mem_output_size] = '\0'; 368 target[xstate.mem_output_size] = '\0';
369#endif 369#endif
370 } 370 }
371 if (!unsafe_symlink_target(target)) {
371//TODO: libbb candidate 372//TODO: libbb candidate
372 if (symlink(target, dst_fn)) 373 if (symlink(target, dst_fn)) {
373 bb_perror_msg_and_die("can't create symlink '%s'", dst_fn); 374 /* shared message */
375 bb_perror_msg_and_die("can't create %slink '%s' to '%s'",
376 "sym", dst_fn, target
377 );
378 }
379 }
374 free(target); 380 free(target);
375} 381}
376#endif 382#endif
diff --git a/coreutils/link.c b/coreutils/link.c
index 81808b778..d8d583b7b 100644
--- a/coreutils/link.c
+++ b/coreutils/link.c
@@ -31,9 +31,8 @@ int link_main(int argc UNUSED_PARAM, char **argv)
31 argv += optind; 31 argv += optind;
32 if (link(argv[0], argv[1]) != 0) { 32 if (link(argv[0], argv[1]) != 0) {
33 /* shared message */ 33 /* shared message */
34 bb_perror_msg_and_die("can't create %slink " 34 bb_perror_msg_and_die("can't create %slink '%s' to '%s'",
35 "'%s' to '%s'", "hard", 35 "hard", argv[1], argv[0]
36 argv[1], argv[0]
37 ); 36 );
38 } 37 }
39 return EXIT_SUCCESS; 38 return EXIT_SUCCESS;
diff --git a/include/bb_archive.h b/include/bb_archive.h
index d3762415f..d3a02cf18 100644
--- a/include/bb_archive.h
+++ b/include/bb_archive.h
@@ -64,9 +64,6 @@ typedef struct archive_handle_t {
64 /* Currently processed file's header */ 64 /* Currently processed file's header */
65 file_header_t *file_header; 65 file_header_t *file_header;
66 66
67 /* List of symlink placeholders */
68 llist_t *symlink_placeholders;
69
70 /* Process the header component, e.g. tar -t */ 67 /* Process the header component, e.g. tar -t */
71 void FAST_FUNC (*action_header)(const file_header_t *); 68 void FAST_FUNC (*action_header)(const file_header_t *);
72 69
@@ -200,6 +197,7 @@ void seek_by_jump(int fd, off_t amount) FAST_FUNC;
200void seek_by_read(int fd, off_t amount) FAST_FUNC; 197void seek_by_read(int fd, off_t amount) FAST_FUNC;
201 198
202const char *strip_unsafe_prefix(const char *str) FAST_FUNC; 199const char *strip_unsafe_prefix(const char *str) FAST_FUNC;
200int unsafe_symlink_target(const char *target) FAST_FUNC;
203 201
204void data_align(archive_handle_t *archive_handle, unsigned boundary) FAST_FUNC; 202void data_align(archive_handle_t *archive_handle, unsigned boundary) FAST_FUNC;
205const llist_t *find_list_entry(const llist_t *list, const char *filename) FAST_FUNC; 203const llist_t *find_list_entry(const llist_t *list, const char *filename) FAST_FUNC;
diff --git a/libbb/copy_file.c b/libbb/copy_file.c
index 23c0f8320..be9006631 100644
--- a/libbb/copy_file.c
+++ b/libbb/copy_file.c
@@ -371,7 +371,10 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
371 int r = symlink(lpath, dest); 371 int r = symlink(lpath, dest);
372 free(lpath); 372 free(lpath);
373 if (r < 0) { 373 if (r < 0) {
374 bb_perror_msg("can't create symlink '%s'", dest); 374 /* shared message */
375 bb_perror_msg("can't create %slink '%s' to '%s'",
376 "sym", dest, lpath
377 );
375 return -1; 378 return -1;
376 } 379 }
377 if (flags & FILEUTILS_PRESERVE_STATUS) 380 if (flags & FILEUTILS_PRESERVE_STATUS)
diff --git a/testsuite/tar.tests b/testsuite/tar.tests
index 1675b07b1..b7cd74ca5 100755
--- a/testsuite/tar.tests
+++ b/testsuite/tar.tests
@@ -279,7 +279,7 @@ optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2
279testing "tar does not extract into symlinks" "\ 279testing "tar does not extract into symlinks" "\
280>>/tmp/passwd && uudecode -o input && tar xf input 2>&1 && rm passwd; cat /tmp/passwd; echo \$? 280>>/tmp/passwd && uudecode -o input && tar xf input 2>&1 && rm passwd; cat /tmp/passwd; echo \$?
281" "\ 281" "\
282tar: can't create symlink 'passwd' to '/tmp/passwd' 282tar: skipping unsafe symlink to '/tmp/passwd' in archive, set EXTRACT_UNSAFE_SYMLINKS=1 to extract
2830 2830
284" \ 284" \
285"" "\ 285"" "\
@@ -299,7 +299,7 @@ optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2
299testing "tar -k does not extract into symlinks" "\ 299testing "tar -k does not extract into symlinks" "\
300>>/tmp/passwd && uudecode -o input && tar xf input -k 2>&1 && rm passwd; cat /tmp/passwd; echo \$? 300>>/tmp/passwd && uudecode -o input && tar xf input -k 2>&1 && rm passwd; cat /tmp/passwd; echo \$?
301" "\ 301" "\
302tar: can't create symlink 'passwd' to '/tmp/passwd' 302tar: skipping unsafe symlink to '/tmp/passwd' in archive, set EXTRACT_UNSAFE_SYMLINKS=1 to extract
3030 3030
304" \ 304" \
305"" "\ 305"" "\
@@ -324,11 +324,11 @@ rm -rf etc usr
324' "\ 324' "\
325etc/ssl/certs/3b2716e5.0 325etc/ssl/certs/3b2716e5.0
326etc/ssl/certs/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem 326etc/ssl/certs/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem
327tar: 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
327etc/ssl/certs/f80cc7f6.0 328etc/ssl/certs/f80cc7f6.0
328usr/share/ca-certificates/mozilla/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.crt 329usr/share/ca-certificates/mozilla/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.crt
3290 3300
330etc/ssl/certs/3b2716e5.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem 331etc/ssl/certs/3b2716e5.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem
331etc/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
332etc/ssl/certs/f80cc7f6.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem 332etc/ssl/certs/f80cc7f6.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem
333" \ 333" \
334"" "" 334"" ""
@@ -346,9 +346,9 @@ ls symlink/bb_test_evilfile
346' "\ 346' "\
347anything.txt 347anything.txt
348symlink 348symlink
349tar: skipping unsafe symlink to '/tmp' in archive, set EXTRACT_UNSAFE_SYMLINKS=1 to extract
349symlink/bb_test_evilfile 350symlink/bb_test_evilfile
350tar: can't create symlink 'symlink' to '/tmp' 3510
3511
352ls: /tmp/bb_test_evilfile: No such file or directory 352ls: /tmp/bb_test_evilfile: No such file or directory
353ls: bb_test_evilfile: No such file or directory 353ls: bb_test_evilfile: No such file or directory
354symlink/bb_test_evilfile 354symlink/bb_test_evilfile