diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2017-08-10 11:52:42 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2017-08-10 11:52:42 +0200 |
commit | bc9bbeb2b81001e8731cd2ae501c8fccc8d87cc7 (patch) | |
tree | 72672bb0c187b93f1fba99012cf0c4e716214298 /archival/libarchive | |
parent | 0cf64c8b5d86d603903397bfce87dea5a862caec (diff) | |
download | busybox-w32-bc9bbeb2b81001e8731cd2ae501c8fccc8d87cc7.tar.gz busybox-w32-bc9bbeb2b81001e8731cd2ae501c8fccc8d87cc7.tar.bz2 busybox-w32-bc9bbeb2b81001e8731cd2ae501c8fccc8d87cc7.zip |
libarchive: do not extract unsafe symlinks unless $EXTRACT_UNSAFE_SYMLINKS=1
function old new delta
unsafe_symlink_target - 147 +147
unzip_main 2711 2732 +21
copy_file 1657 1678 +21
tar_main 999 971 -28
data_extract_all 1038 984 -54
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 2/2 up/down: 189/-82) Total: 107 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'archival/libarchive')
-rw-r--r-- | archival/libarchive/Kbuild.src | 2 | ||||
-rw-r--r-- | archival/libarchive/data_extract_all.c | 37 | ||||
-rw-r--r-- | archival/libarchive/unsafe_symlink_target.c | 48 |
3 files changed, 65 insertions, 22 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 | |||
8 | int 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 | } | ||