diff options
Diffstat (limited to '')
-rw-r--r-- | archival/Config.src | 11 | ||||
-rw-r--r-- | archival/bbunzip.c | 6 | ||||
-rw-r--r-- | archival/cpio.c | 7 | ||||
-rw-r--r-- | archival/libarchive/data_extract_all.c | 8 | ||||
-rw-r--r-- | archival/libarchive/open_transformer.c | 26 | ||||
-rw-r--r-- | archival/libarchive/unsafe_prefix.c | 6 | ||||
-rw-r--r-- | archival/rpm.c | 9 | ||||
-rw-r--r-- | archival/tar.c | 2 |
8 files changed, 56 insertions, 19 deletions
diff --git a/archival/Config.src b/archival/Config.src index 6f4f30c43..cbcd7217c 100644 --- a/archival/Config.src +++ b/archival/Config.src | |||
@@ -35,4 +35,15 @@ config FEATURE_LZMA_FAST | |||
35 | This option reduces decompression time by about 25% at the cost of | 35 | This option reduces decompression time by about 25% at the cost of |
36 | a 1K bigger binary. | 36 | a 1K bigger binary. |
37 | 37 | ||
38 | config FEATURE_PATH_TRAVERSAL_PROTECTION | ||
39 | bool "Prevent extraction of filenames with /../ path component" | ||
40 | default n | ||
41 | help | ||
42 | busybox tar and unzip remove "PREFIX/../" (if it exists) | ||
43 | from extracted names. | ||
44 | This option enables this behavior for all other unpacking applets, | ||
45 | such as cpio, ar, rpm. | ||
46 | GNU cpio 2.15 has NO such sanity check. | ||
47 | # try other archivers and document their behavior? | ||
48 | |||
38 | endmenu | 49 | endmenu |
diff --git a/archival/bbunzip.c b/archival/bbunzip.c index b7944a62a..42b4baf88 100644 --- a/archival/bbunzip.c +++ b/archival/bbunzip.c | |||
@@ -71,8 +71,8 @@ int FAST_FUNC bbunpack(char **argv, | |||
71 | goto err; | 71 | goto err; |
72 | } else { | 72 | } else { |
73 | /* "clever zcat" with FILE */ | 73 | /* "clever zcat" with FILE */ |
74 | /* fail_if_not_compressed because zcat refuses uncompressed input */ | 74 | /* die_if_not_compressed because zcat refuses uncompressed input */ |
75 | int fd = open_zipped(filename, /*fail_if_not_compressed:*/ 1); | 75 | int fd = open_zipped(filename, /*die_if_not_compressed:*/ 1); |
76 | if (fd < 0) | 76 | if (fd < 0) |
77 | goto err_name; | 77 | goto err_name; |
78 | xmove_fd(fd, STDIN_FILENO); | 78 | xmove_fd(fd, STDIN_FILENO); |
@@ -80,7 +80,7 @@ int FAST_FUNC bbunpack(char **argv, | |||
80 | } else | 80 | } else |
81 | if (option_mask32 & BBUNPK_SEAMLESS_MAGIC) { | 81 | if (option_mask32 & BBUNPK_SEAMLESS_MAGIC) { |
82 | /* "clever zcat" on stdin */ | 82 | /* "clever zcat" on stdin */ |
83 | if (setup_unzip_on_fd(STDIN_FILENO, /*fail_if_not_compressed*/ 1)) | 83 | if (setup_unzip_on_fd(STDIN_FILENO, /*die_if_not_compressed*/ 1)) |
84 | goto err; | 84 | goto err; |
85 | } | 85 | } |
86 | 86 | ||
diff --git a/archival/cpio.c b/archival/cpio.c index f0d990048..b033b3733 100644 --- a/archival/cpio.c +++ b/archival/cpio.c | |||
@@ -350,6 +350,12 @@ static NOINLINE int cpio_o(void) | |||
350 | st.st_dev = st.st_rdev = 0; | 350 | st.st_dev = st.st_rdev = 0; |
351 | #endif | 351 | #endif |
352 | 352 | ||
353 | if (sizeof(st.st_size) > 4 | ||
354 | && st.st_size > (off_t)0xffffffff | ||
355 | ) { | ||
356 | bb_error_msg_and_die("error: file '%s' is larger than 4GB", name); | ||
357 | } | ||
358 | |||
353 | bytes += printf("070701" | 359 | bytes += printf("070701" |
354 | "%08X%08X%08X%08X%08X%08X%08X" | 360 | "%08X%08X%08X%08X%08X%08X%08X" |
355 | "%08X%08X%08X%08X" /* GNU cpio uses uppercase hex */ | 361 | "%08X%08X%08X%08X" /* GNU cpio uses uppercase hex */ |
@@ -421,6 +427,7 @@ int cpio_main(int argc UNUSED_PARAM, char **argv) | |||
421 | #endif | 427 | #endif |
422 | #endif | 428 | #endif |
423 | "owner\0" Required_argument "R" | 429 | "owner\0" Required_argument "R" |
430 | "file\0" Required_argument "F" | ||
424 | "verbose\0" No_argument "v" | 431 | "verbose\0" No_argument "v" |
425 | "null\0" No_argument "0" | 432 | "null\0" No_argument "0" |
426 | "quiet\0" No_argument "\xff" | 433 | "quiet\0" No_argument "\xff" |
diff --git a/archival/libarchive/data_extract_all.c b/archival/libarchive/data_extract_all.c index 049c2c156..8a69711c1 100644 --- a/archival/libarchive/data_extract_all.c +++ b/archival/libarchive/data_extract_all.c | |||
@@ -65,6 +65,14 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |||
65 | } while (--n != 0); | 65 | } while (--n != 0); |
66 | } | 66 | } |
67 | #endif | 67 | #endif |
68 | #if ENABLE_FEATURE_PATH_TRAVERSAL_PROTECTION | ||
69 | /* Strip leading "/" and up to last "/../" path component */ | ||
70 | dst_name = (char *)strip_unsafe_prefix(dst_name); | ||
71 | #endif | ||
72 | // ^^^ This may be a problem if some applets do need to extract absolute names. | ||
73 | // (Probably will need to invent ARCHIVE_ALLOW_UNSAFE_NAME flag). | ||
74 | // You might think that rpm needs it, but in my tests rpm's internal cpio | ||
75 | // archive has names like "./usr/bin/FOO", not "/usr/bin/FOO". | ||
68 | 76 | ||
69 | if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) { | 77 | if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) { |
70 | char *slash = strrchr(dst_name, '/'); | 78 | char *slash = strrchr(dst_name, '/'); |
diff --git a/archival/libarchive/open_transformer.c b/archival/libarchive/open_transformer.c index 44715ef25..353f68217 100644 --- a/archival/libarchive/open_transformer.c +++ b/archival/libarchive/open_transformer.c | |||
@@ -157,7 +157,7 @@ void FAST_FUNC fork_transformer(int fd, const char *transform_prog) | |||
157 | /* Used by e.g. rpm which gives us a fd without filename, | 157 | /* Used by e.g. rpm which gives us a fd without filename, |
158 | * thus we can't guess the format from filename's extension. | 158 | * thus we can't guess the format from filename's extension. |
159 | */ | 159 | */ |
160 | static transformer_state_t *setup_transformer_on_fd(int fd, int fail_if_not_compressed) | 160 | static transformer_state_t *setup_transformer_on_fd(int fd, int die_if_not_compressed) |
161 | { | 161 | { |
162 | transformer_state_t *xstate; | 162 | transformer_state_t *xstate; |
163 | 163 | ||
@@ -204,7 +204,7 @@ static transformer_state_t *setup_transformer_on_fd(int fd, int fail_if_not_comp | |||
204 | } | 204 | } |
205 | 205 | ||
206 | /* No known magic seen */ | 206 | /* No known magic seen */ |
207 | if (fail_if_not_compressed) | 207 | if (die_if_not_compressed) |
208 | bb_simple_error_msg_and_die("no gzip" | 208 | bb_simple_error_msg_and_die("no gzip" |
209 | IF_FEATURE_SEAMLESS_BZ2("/bzip2") | 209 | IF_FEATURE_SEAMLESS_BZ2("/bzip2") |
210 | IF_FEATURE_SEAMLESS_XZ("/xz") | 210 | IF_FEATURE_SEAMLESS_XZ("/xz") |
@@ -240,13 +240,15 @@ static void fork_transformer_and_free(transformer_state_t *xstate) | |||
240 | /* Used by e.g. rpm which gives us a fd without filename, | 240 | /* Used by e.g. rpm which gives us a fd without filename, |
241 | * thus we can't guess the format from filename's extension. | 241 | * thus we can't guess the format from filename's extension. |
242 | */ | 242 | */ |
243 | int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_compressed) | 243 | int FAST_FUNC setup_unzip_on_fd(int fd, int die_if_not_compressed) |
244 | { | 244 | { |
245 | transformer_state_t *xstate = setup_transformer_on_fd(fd, fail_if_not_compressed); | 245 | transformer_state_t *xstate = setup_transformer_on_fd(fd, die_if_not_compressed); |
246 | 246 | ||
247 | if (!xstate->xformer) { | 247 | if (!xstate->xformer) { |
248 | /* Not compressed */ | ||
249 | int retval = xstate->signature_skipped; /* never zero */ | ||
248 | free(xstate); | 250 | free(xstate); |
249 | return 1; | 251 | return retval; |
250 | } | 252 | } |
251 | 253 | ||
252 | fork_transformer_and_free(xstate); | 254 | fork_transformer_and_free(xstate); |
@@ -264,7 +266,7 @@ void FAST_FUNC setup_lzma_on_fd(int fd) | |||
264 | } | 266 | } |
265 | #endif | 267 | #endif |
266 | 268 | ||
267 | static transformer_state_t *open_transformer(const char *fname, int fail_if_not_compressed) | 269 | static transformer_state_t *open_transformer(const char *fname, int die_if_not_compressed) |
268 | { | 270 | { |
269 | transformer_state_t *xstate; | 271 | transformer_state_t *xstate; |
270 | int fd; | 272 | int fd; |
@@ -284,18 +286,18 @@ static transformer_state_t *open_transformer(const char *fname, int fail_if_not_ | |||
284 | } | 286 | } |
285 | } | 287 | } |
286 | 288 | ||
287 | xstate = setup_transformer_on_fd(fd, fail_if_not_compressed); | 289 | xstate = setup_transformer_on_fd(fd, die_if_not_compressed); |
288 | 290 | ||
289 | return xstate; | 291 | return xstate; |
290 | } | 292 | } |
291 | 293 | ||
292 | int FAST_FUNC open_zipped(const char *fname, int fail_if_not_compressed) | 294 | int FAST_FUNC open_zipped(const char *fname, int die_if_not_compressed) |
293 | { | 295 | { |
294 | int fd; | 296 | int fd; |
295 | transformer_state_t *xstate; | 297 | transformer_state_t *xstate; |
296 | 298 | ||
297 | xstate = open_transformer(fname, fail_if_not_compressed); | 299 | xstate = open_transformer(fname, die_if_not_compressed); |
298 | if (!xstate) | 300 | if (!xstate) /* open error */ |
299 | return -1; | 301 | return -1; |
300 | 302 | ||
301 | fd = xstate->src_fd; | 303 | fd = xstate->src_fd; |
@@ -326,7 +328,7 @@ void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_ | |||
326 | transformer_state_t *xstate; | 328 | transformer_state_t *xstate; |
327 | char *image; | 329 | char *image; |
328 | 330 | ||
329 | xstate = open_transformer(fname, /*fail_if_not_compressed:*/ 0); | 331 | xstate = open_transformer(fname, /*die_if_not_compressed:*/ 0); |
330 | if (!xstate) /* file open error */ | 332 | if (!xstate) /* file open error */ |
331 | return NULL; | 333 | return NULL; |
332 | 334 | ||
@@ -371,7 +373,7 @@ void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_ | |||
371 | int fd; | 373 | int fd; |
372 | char *image; | 374 | char *image; |
373 | 375 | ||
374 | fd = open_zipped(fname, /*fail_if_not_compressed:*/ 0); | 376 | fd = open_zipped(fname, /*die_if_not_compressed:*/ 0); |
375 | if (fd < 0) | 377 | if (fd < 0) |
376 | return NULL; | 378 | return NULL; |
377 | 379 | ||
diff --git a/archival/libarchive/unsafe_prefix.c b/archival/libarchive/unsafe_prefix.c index 33e487bf9..667081195 100644 --- a/archival/libarchive/unsafe_prefix.c +++ b/archival/libarchive/unsafe_prefix.c | |||
@@ -14,7 +14,11 @@ const char* FAST_FUNC strip_unsafe_prefix(const char *str) | |||
14 | cp++; | 14 | cp++; |
15 | continue; | 15 | continue; |
16 | } | 16 | } |
17 | if (is_prefixed_with(cp, "/../"+1)) { | 17 | /* We are called lots of times. |
18 | * is_prefixed_with(cp, "../") is slower than open-coding it, | ||
19 | * with minimal code growth (~few bytes). | ||
20 | */ | ||
21 | if (cp[0] == '.' && cp[1] == '.' && cp[2] == '/') { | ||
18 | cp += 3; | 22 | cp += 3; |
19 | continue; | 23 | continue; |
20 | } | 24 | } |
diff --git a/archival/rpm.c b/archival/rpm.c index af8db99a6..95a8c79b6 100644 --- a/archival/rpm.c +++ b/archival/rpm.c | |||
@@ -316,7 +316,7 @@ static void extract_cpio(int fd, const char *source_rpm) | |||
316 | archive_handle->src_fd = fd; | 316 | archive_handle->src_fd = fd; |
317 | /*archive_handle->offset = 0; - init_handle() did it */ | 317 | /*archive_handle->offset = 0; - init_handle() did it */ |
318 | 318 | ||
319 | setup_unzip_on_fd(archive_handle->src_fd, /*fail_if_not_compressed:*/ 1); | 319 | setup_unzip_on_fd(archive_handle->src_fd, /*die_if_not_compressed:*/ 1); |
320 | while (get_header_cpio(archive_handle) == EXIT_SUCCESS) | 320 | while (get_header_cpio(archive_handle) == EXIT_SUCCESS) |
321 | continue; | 321 | continue; |
322 | } | 322 | } |
@@ -533,6 +533,7 @@ int rpm2cpio_main(int argc UNUSED_PARAM, char **argv) | |||
533 | // /* We need to know whether child (gzip/bzip/etc) exits abnormally */ | 533 | // /* We need to know whether child (gzip/bzip/etc) exits abnormally */ |
534 | // signal(SIGCHLD, check_errors_in_children); | 534 | // signal(SIGCHLD, check_errors_in_children); |
535 | 535 | ||
536 | str = NULL; | ||
536 | if (ENABLE_FEATURE_SEAMLESS_LZMA | 537 | if (ENABLE_FEATURE_SEAMLESS_LZMA |
537 | && (str = rpm_getstr0(TAG_PAYLOADCOMPRESSOR)) != NULL | 538 | && (str = rpm_getstr0(TAG_PAYLOADCOMPRESSOR)) != NULL |
538 | && strcmp(str, "lzma") == 0 | 539 | && strcmp(str, "lzma") == 0 |
@@ -541,7 +542,11 @@ int rpm2cpio_main(int argc UNUSED_PARAM, char **argv) | |||
541 | // set up decompressor without detection | 542 | // set up decompressor without detection |
542 | setup_lzma_on_fd(rpm_fd); | 543 | setup_lzma_on_fd(rpm_fd); |
543 | } else { | 544 | } else { |
544 | setup_unzip_on_fd(rpm_fd, /*fail_if_not_compressed:*/ 1); | 545 | int signature_bytes = setup_unzip_on_fd(rpm_fd, /*die_if_not_compressed:*/ 0); |
546 | if (signature_bytes != 0) { | ||
547 | xlseek(rpm_fd, - signature_bytes, SEEK_CUR); | ||
548 | bb_error_msg("warning, unknown compression '%s'", str); | ||
549 | } | ||
545 | } | 550 | } |
546 | 551 | ||
547 | if (bb_copyfd_eof(rpm_fd, STDOUT_FILENO) < 0) | 552 | if (bb_copyfd_eof(rpm_fd, STDOUT_FILENO) < 0) |
diff --git a/archival/tar.c b/archival/tar.c index d6ca6c1e0..38906ba02 100644 --- a/archival/tar.c +++ b/archival/tar.c | |||
@@ -1164,7 +1164,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv) | |||
1164 | * on e.g. tarball with 1st file named "BZh5". | 1164 | * on e.g. tarball with 1st file named "BZh5". |
1165 | */ | 1165 | */ |
1166 | ) { | 1166 | ) { |
1167 | tar_handle->src_fd = open_zipped(tar_filename, /*fail_if_not_compressed:*/ 0); | 1167 | tar_handle->src_fd = open_zipped(tar_filename, /*die_if_not_compressed:*/ 0); |
1168 | if (tar_handle->src_fd < 0) | 1168 | if (tar_handle->src_fd < 0) |
1169 | bb_perror_msg_and_die("can't open '%s'", tar_filename); | 1169 | bb_perror_msg_and_die("can't open '%s'", tar_filename); |
1170 | } else { | 1170 | } else { |