From f233191d540989ac7ad7433ac11ac0906338d272 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Wed, 28 Jun 2023 17:39:39 +0100 Subject: [PATCH] [cmp] update Bled to latest * This adds ZIP64 support, which is required to extract zip archives that are larger than 4GB. * Closes #2264 * Also fix a MinGW warning in pki.c and improve the UEFI revocation messages. --- res/loc/rufus.loc | 4 +- src/bled/bb_archive.h | 2 + src/bled/bled.c | 33 +- src/bled/decompress_bunzip2.c | 2 +- src/bled/decompress_gunzip.c | 4 +- src/bled/decompress_unzip.c | 670 ++++++++++++++++++++++++---------- src/bled/init_handle.c | 4 + src/bled/libbb.h | 48 ++- src/bled/platform.h | 4 - src/pki.c | 2 +- src/rufus.rc | 10 +- 11 files changed, 546 insertions(+), 237 deletions(-) diff --git a/res/loc/rufus.loc b/res/loc/rufus.loc index 460bf255..ba7a4877 100644 --- a/res/loc/rufus.loc +++ b/res/loc/rufus.loc @@ -598,8 +598,8 @@ t MSG_337 "An additional file ('diskcopy.dll') must be downloaded from Microsoft "- Select 'No' to cancel the operation\n\n" "Note: The file will be downloaded in the application's directory and will be reused automatically if present." t MSG_338 "Revoked UEFI bootloader detected" -t MSG_339 "Rufus detected that the ISO you have selected contains a UEFI bootloader that has been revoked and that will produce %s, on a fully up to date UEFI system with Secure Boot enabled.\n\n" - "- If you obtained this ISO image from a non reputable source, you should consider the possibility that it may contain UEFI malware and avoid booting from it.\n" +t MSG_339 "Rufus detected that the ISO you have selected contains a UEFI bootloader that has been revoked and that will produce %s, when Secure Boot is enabled on a fully up to date UEFI system.\n\n" + "- If you obtained this ISO image from a non reputable source, you should consider the possibility that it might contain UEFI malware and avoid booting from it.\n" "- If you obtained it from a trusted source, you should try to locate a more up to date version, that will not produce this warning." t MSG_340 "a \"Security Violation\" screen" t MSG_341 "a Windows Recovery Screen (BSOD) with '%s'" diff --git a/src/bled/bb_archive.h b/src/bled/bb_archive.h index fc58dc5f..5e059a5a 100644 --- a/src/bled/bb_archive.h +++ b/src/bled/bb_archive.h @@ -261,6 +261,8 @@ static inline int transformer_switch_file(transformer_state_t* xstate) xstate->dst_fd = -1; } _snprintf_s(dst, sizeof(dst), _TRUNCATE, "%s/%s", xstate->dst_dir, xstate->dst_name); + free(xstate->dst_name); + xstate->dst_name = NULL; for (i = 0; i < strlen(dst); i++) { if (dst[i] == '/') dst[i] = '\\'; diff --git a/src/bled/bled.c b/src/bled/bled.c index 28b87557..f0bf30e6 100644 --- a/src/bled/bled.c +++ b/src/bled/bled.c @@ -1,7 +1,7 @@ /* * Bled (Base Library for Easy Decompression) * - * Copyright © 2014-2020 Pete Batard + * Copyright © 2014-2023 Pete Batard * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ @@ -54,7 +54,7 @@ unpacker_t unpacker[BLED_COMPRESSION_MAX] = { int64_t bled_uncompress(const char* src, const char* dst, int type) { transformer_state_t xstate; - int64_t ret; + int64_t ret = -1; if (!bled_initialized) { bb_error_msg("The library has not been initialized"); @@ -85,17 +85,16 @@ int64_t bled_uncompress(const char* src, const char* dst, int type) if (setjmp(bb_error_jmp)) goto err; + ret = unpacker[type](&xstate); - _close(xstate.src_fd); - _close(xstate.dst_fd); - return ret; err: + free(xstate.dst_name); if (xstate.src_fd > 0) _close(xstate.src_fd); if (xstate.dst_fd > 0) _close(xstate.dst_fd); - return -1; + return ret; } /* Uncompress using Windows handles */ @@ -132,6 +131,7 @@ int64_t bled_uncompress_with_handles(HANDLE hSrc, HANDLE hDst, int type) if (setjmp(bb_error_jmp)) return -1; + return unpacker[type](&xstate); } @@ -139,7 +139,7 @@ int64_t bled_uncompress_with_handles(HANDLE hSrc, HANDLE hDst, int type) int64_t bled_uncompress_to_buffer(const char* src, char* buf, size_t size, int type) { transformer_state_t xstate; - int64_t ret; + int64_t ret = -1; if (!bled_initialized) { bb_error_msg("The library has not been initialized"); @@ -177,22 +177,21 @@ int64_t bled_uncompress_to_buffer(const char* src, char* buf, size_t size, int t if (setjmp(bb_error_jmp)) goto err; + ret = unpacker[type](&xstate); - if (src[0] != 0) - _close(xstate.src_fd); - return ret; err: - if (xstate.src_fd > 0) + free(xstate.dst_name); + if ((src[0] != 0) && (xstate.src_fd > 0)) _close(xstate.src_fd); - return -1; + return ret; } /* Uncompress all files from archive 'src', compressed using 'type', to destination dir 'dir' */ int64_t bled_uncompress_to_dir(const char* src, const char* dir, int type) { transformer_state_t xstate; - int64_t ret; + int64_t ret = -1; if (!bled_initialized) { bb_error_msg("The library has not been initialized"); @@ -220,18 +219,16 @@ int64_t bled_uncompress_to_dir(const char* src, const char* dir, int type) if (setjmp(bb_error_jmp)) goto err; + ret = unpacker[type](&xstate); - _close(xstate.src_fd); - if (xstate.dst_fd > 0) - _close(xstate.dst_fd); - return ret; err: + free(xstate.dst_name); if (xstate.src_fd > 0) _close(xstate.src_fd); if (xstate.dst_fd > 0) _close(xstate.dst_fd); - return -1; + return ret; } int64_t bled_uncompress_from_buffer_to_buffer(const char* src, const size_t src_len, char* dst, size_t dst_len, int type) diff --git a/src/bled/decompress_bunzip2.c b/src/bled/decompress_bunzip2.c index 0f7a554f..39ea43e7 100644 --- a/src/bled/decompress_bunzip2.c +++ b/src/bled/decompress_bunzip2.c @@ -42,7 +42,7 @@ #include "bb_archive.h" #if 0 -# define dbg(...) bb_error_msg(__VA_ARGS__) +# define dbg(...) bb_printf(__VA_ARGS__) #else # define dbg(...) ((void)0) #endif diff --git a/src/bled/decompress_gunzip.c b/src/bled/decompress_gunzip.c index c2eda288..52f12cb6 100644 --- a/src/bled/decompress_gunzip.c +++ b/src/bled/decompress_gunzip.c @@ -1029,7 +1029,7 @@ inflate_unzip_internal(STATE_PARAM transformer_state_t *xstate) error_msg = "corrupted data"; if (setjmp(error_jmp)) { /* Error from deep inside zip machinery */ - bb_simple_error_msg("corrupted data"); + bb_simple_error_msg("%s", error_msg); n = -1; goto ret; } @@ -1272,7 +1272,7 @@ unpack_gz_stream(transformer_state_t *xstate) n = inflate_unzip_internal(PASS_STATE xstate); if (n < 0) { - total = (n == -ENOSPC)?xstate->mem_output_size_max:n; + total = (n == -ENOSPC) ? xstate->mem_output_size_max : n; goto ret; } total += n; diff --git a/src/bled/decompress_unzip.c b/src/bled/decompress_unzip.c index 746005e9..186df546 100644 --- a/src/bled/decompress_unzip.c +++ b/src/bled/decompress_unzip.c @@ -1,31 +1,36 @@ /* * unzip implementation for Bled/busybox * - * Copyright © 2015-2020 Pete Batard + * Copyright © 2015-2023 Pete Batard * Based on mini unzip implementation for busybox © Ed Clark * Loosely based on original busybox unzip applet © Laurence Anderson. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ -/* The following are not currently supported: - * - Store (uncompressed) - * - Zip64 - */ - #include "libbb.h" #include "bb_archive.h" +#if 0 +# define dbg(...) bb_printf(__VA_ARGS__) +#else +# define dbg(...) ((void)0) +#endif + +#define xread safe_read + enum { #if BB_BIG_ENDIAN ZIP_FILEHEADER_MAGIC = 0x504b0304, - ZIP_CDF_MAGIC = 0x504b0102, /* central directory's file header */ - ZIP_CDE_MAGIC = 0x504b0506, /* "end of central directory" record */ + ZIP_CDF_MAGIC = 0x504b0102, /* CDF item */ + ZIP_CDE_MAGIC = 0x504b0506, /* End of CDF */ + ZIP64_CDE_MAGIC = 0x504b0606, /* End of Zip64 CDF */ ZIP_DD_MAGIC = 0x504b0708, #else ZIP_FILEHEADER_MAGIC = 0x04034b50, ZIP_CDF_MAGIC = 0x02014b50, ZIP_CDE_MAGIC = 0x06054b50, + ZIP64_CDE_MAGIC = 0x06064b50, ZIP_DD_MAGIC = 0x08074b50, #endif }; @@ -41,36 +46,29 @@ typedef union { uint16_t method; /* 4-5 */ uint16_t modtime; /* 6-7 */ uint16_t moddate; /* 8-9 */ - uint32_t crc32; /* 10-13 */ - uint32_t cmpsize; /* 14-17 */ - uint32_t ucmpsize; /* 18-21 */ + uint32_t crc32 PACKED; /* 10-13 */ + uint32_t cmpsize PACKED; /* 14-17 */ + uint32_t ucmpsize PACKED; /* 18-21 */ uint16_t filename_len; /* 22-23 */ uint16_t extra_len; /* 24-25 */ - } PACKED formatted; -} PACKED zip_header_t; + /* filename follows (not NUL terminated) */ + /* extra field follows */ + /* data follows */ + } fmt PACKED; +} zip_header_t; /* PACKED - gcc 4.2.1 doesn't like it (spews warning) */ PRAGMA_END_PACKED -/* Check the offset of the last element, not the length. This leniency - * allows for poor packing, whereby the overall struct may be too long, - * even though the elements are all in the right place. - */ -struct BUG_zip_header_must_be_26_bytes { - char BUG_zip_header_must_be_26_bytes[ - offsetof(zip_header_t, formatted.extra_len) + 2 - == ZIP_HEADER_LEN ? 1 : -1]; -}; - -#define FIX_ENDIANNESS_ZIP(zip_header) do { \ - (zip_header).formatted.version = SWAP_LE16((zip_header).formatted.version ); \ - (zip_header).formatted.method = SWAP_LE16((zip_header).formatted.method ); \ - (zip_header).formatted.modtime = SWAP_LE16((zip_header).formatted.modtime ); \ - (zip_header).formatted.moddate = SWAP_LE16((zip_header).formatted.moddate ); \ - (zip_header).formatted.crc32 = SWAP_LE32((zip_header).formatted.crc32 ); \ - (zip_header).formatted.cmpsize = SWAP_LE32((zip_header).formatted.cmpsize ); \ - (zip_header).formatted.ucmpsize = SWAP_LE32((zip_header).formatted.ucmpsize ); \ - (zip_header).formatted.filename_len = SWAP_LE16((zip_header).formatted.filename_len); \ - (zip_header).formatted.extra_len = SWAP_LE16((zip_header).formatted.extra_len ); \ -} while (0) +#define FIX_ENDIANNESS_ZIP(zip) \ +do { if (BB_BIG_ENDIAN) { \ + (zip).fmt.method = SWAP_LE16((zip).fmt.method ); \ + (zip).fmt.modtime = SWAP_LE16((zip).fmt.modtime ); \ + (zip).fmt.moddate = SWAP_LE16((zip).fmt.moddate ); \ + (zip).fmt.crc32 = SWAP_LE32((zip).fmt.crc32 ); \ + (zip).fmt.cmpsize = SWAP_LE32((zip).fmt.cmpsize ); \ + (zip).fmt.ucmpsize = SWAP_LE32((zip).fmt.ucmpsize ); \ + (zip).fmt.filename_len = SWAP_LE16((zip).fmt.filename_len); \ + (zip).fmt.extra_len = SWAP_LE16((zip).fmt.extra_len ); \ +}} while (0) #define CDF_HEADER_LEN 42 @@ -83,46 +81,46 @@ typedef union { uint16_t version_needed; /* 2-3 */ uint16_t cdf_flags; /* 4-5 */ uint16_t method; /* 6-7 */ - uint16_t mtime; /* 8-9 */ - uint16_t mdate; /* 10-11 */ + uint16_t modtime; /* 8-9 */ + uint16_t moddate; /* 10-11 */ uint32_t crc32; /* 12-15 */ uint32_t cmpsize; /* 16-19 */ uint32_t ucmpsize; /* 20-23 */ - uint16_t file_name_length; /* 24-25 */ - uint16_t extra_field_length; /* 26-27 */ + uint16_t filename_len; /* 24-25 */ + uint16_t extra_len; /* 26-27 */ uint16_t file_comment_length; /* 28-29 */ uint16_t disk_number_start; /* 30-31 */ - uint16_t internal_file_attributes; /* 32-33 */ - uint32_t external_file_attributes; /* 34-37 */ - uint32_t relative_offset_of_local_header; /* 38-41 */ - } PACKED formatted; + uint16_t internal_attributes; /* 32-33 */ + uint32_t external_attributes PACKED; /* 34-37 */ + uint32_t relative_offset_of_local_header PACKED; /* 38-41 */ + /* filename follows (not NUL terminated) */ + /* extra field follows */ + /* file comment follows */ + } fmt PACKED; } cdf_header_t; PRAGMA_END_PACKED -struct BUG_cdf_header_must_be_42_bytes { - char BUG_cdf_header_must_be_42_bytes[ - offsetof(cdf_header_t, formatted.relative_offset_of_local_header) + 4 - == CDF_HEADER_LEN ? 1 : -1]; -}; +#define FIX_ENDIANNESS_CDF(cdf) \ +do { if (BB_BIG_ENDIAN) { \ + (cdf).fmt.version_made_by = SWAP_LE16((cdf).fmt.version_made_by); \ + (cdf).fmt.version_needed = SWAP_LE16((cdf).fmt.version_needed ); \ + (cdf).fmt.method = SWAP_LE16((cdf).fmt.method ); \ + (cdf).fmt.modtime = SWAP_LE16((cdf).fmt.modtime ); \ + (cdf).fmt.moddate = SWAP_LE16((cdf).fmt.moddate ); \ + (cdf).fmt.crc32 = SWAP_LE32((cdf).fmt.crc32 ); \ + (cdf).fmt.cmpsize = SWAP_LE32((cdf).fmt.cmpsize ); \ + (cdf).fmt.ucmpsize = SWAP_LE32((cdf).fmt.ucmpsize ); \ + (cdf).fmt.filename_len = SWAP_LE16((cdf).fmt.filename_len ); \ + (cdf).fmt.extra_len = SWAP_LE16((cdf).fmt.extra_len ); \ + (cdf).fmt.file_comment_length = SWAP_LE16((cdf).fmt.file_comment_length); \ + (cdf).fmt.external_attributes = SWAP_LE32((cdf).fmt.external_attributes); \ +}} while (0) -#define FIX_ENDIANNESS_CDF(cdf_header) do { \ - (cdf_header).formatted.crc32 = SWAP_LE32((cdf_header).formatted.crc32 ); \ - (cdf_header).formatted.cmpsize = SWAP_LE32((cdf_header).formatted.cmpsize ); \ - (cdf_header).formatted.ucmpsize = SWAP_LE32((cdf_header).formatted.ucmpsize ); \ - (cdf_header).formatted.file_name_length = SWAP_LE16((cdf_header).formatted.file_name_length); \ - (cdf_header).formatted.extra_field_length = SWAP_LE16((cdf_header).formatted.extra_field_length); \ - (cdf_header).formatted.file_comment_length = SWAP_LE16((cdf_header).formatted.file_comment_length); \ - IF_DESKTOP( \ - (cdf_header).formatted.version_made_by = SWAP_LE16((cdf_header).formatted.version_made_by); \ - (cdf_header).formatted.external_file_attributes = SWAP_LE32((cdf_header).formatted.external_file_attributes); \ - ) \ -} while (0) - -#define CDE_HEADER_LEN 16 +#define CDE_LEN 16 PRAGMA_BEGIN_PACKED typedef union { - uint8_t raw[CDE_HEADER_LEN]; + uint8_t raw[CDE_LEN]; struct { /* uint32_t signature; 50 4b 05 06 */ uint16_t this_disk_no; @@ -131,24 +129,96 @@ typedef union { uint16_t cdf_entries_total; uint32_t cdf_size; uint32_t cdf_offset; - /* uint16_t file_comment_length; */ - /* .ZIP file comment (variable size) */ - } PACKED formatted; -} cde_header_t; + /* uint16_t archive_comment_length; */ + /* archive comment follows */ + } fmt PACKED; +} cde_t; PRAGMA_END_PACKED -struct BUG_cde_header_must_be_16_bytes { - char BUG_cde_header_must_be_16_bytes[ - sizeof(cde_header_t) == CDE_HEADER_LEN ? 1 : -1]; +#define FIX_ENDIANNESS_CDE(cde) \ +do { if (BB_BIG_ENDIAN) { \ + (cde).fmt.cdf_offset = SWAP_LE32((cde).fmt.cdf_offset); \ +}} while (0) + +/* ZIP64 records */ +#define ZIP64_LEN 20 + +PRAGMA_BEGIN_PACKED +typedef union { + uint8_t raw[ZIP64_LEN]; + struct { + /* uint16_t signature; 00 01 */ + uint16_t signature; + uint16_t length; + uint64_t ucmpsize PACKED; + uint64_t cmpsize PACKED; + } fmt PACKED; +} zip64_t; +PRAGMA_END_PACKED + +#define FIX_ENDIANNESS_ZIP64(z64) \ +do { if (BB_BIG_ENDIAN) { \ + (z64).fmt.signature = SWAP_LE16((z64).fmt.signature); \ + (z64).fmt.length = SWAP_LE32((z64).fmt.length); \ + (z64).fmt.cmpsize = SWAP_LE64((z64).fmt.cmpsize); \ + (z64).fmt.ucmpsize = SWAP_LE64((z64).fmt.ucmpsize); \ +}} while (0) + +#define CDE64_LEN 52 + +PRAGMA_BEGIN_PACKED +typedef union { + uint8_t raw[CDE64_LEN]; + struct { + /* uint32_t signature; 50 4b 06 06 */ + uint64_t size PACKED; + uint16_t version_made_by; + uint16_t version_needed; + uint32_t this_disk_no; + uint32_t disk_with_cdf_no; + uint64_t cdf_entries_on_this_disk PACKED; + uint64_t cdf_entries_total PACKED; + uint64_t cdf_size PACKED; + uint64_t cdf_offset PACKED; + /* archive comment follows */ + } fmt PACKED; +} cde64_t; +PRAGMA_END_PACKED + +#define FIX_ENDIANNESS_CDE64(c64) \ +do { if (BB_BIG_ENDIAN) { \ + (c64).fmt.size = SWAP_LE64((c64).fmt.size); \ + (c64).fmt.cdf_offset = SWAP_LE64((c64).fmt.cdf_offset); \ +}} while (0) + +struct BUG { + /* Check the offset of the last element, not the length. This leniency + * allows for poor packing, whereby the overall struct may be too long, + * even though the elements are all in the right place. + */ + char BUG_zip_header_must_be_26_bytes[ + offsetof(zip_header_t, fmt.extra_len) + 2 + == ZIP_HEADER_LEN ? 1 : -1]; + char BUG_cdf_header_must_be_42_bytes[ + offsetof(cdf_header_t, fmt.relative_offset_of_local_header) + 4 + == CDF_HEADER_LEN ? 1 : -1]; + char BUG_cde_must_be_16_bytes[ + sizeof(cde_t) == CDE_LEN ? 1 : -1]; + char BUG_zip64_must_be_20_bytes[ + sizeof(zip64_t) == ZIP64_LEN ? 1 : -1]; + char BUG_cde64_must_be_52_bytes[ + sizeof(cde64_t) == CDE64_LEN ? 1 : -1]; }; -#define FIX_ENDIANNESS_CDE(cde_header) do { \ - (cde_header).formatted.cdf_offset = SWAP_LE32((cde_header).formatted.cdf_offset); \ -} while (0) +/* This value means that we failed to find CDF */ +#define BAD_CDF_OFFSET ((uint32_t)0xffffffff) -#if ENABLE_DESKTOP +#if !ENABLE_FEATURE_UNZIP_CDF +# define find_cdf_offset(fd) BAD_CDF_OFFSET + +#else /* Seen in the wild: * Self-extracting PRO2K3XP_32.exe contains 19078464 byte zip archive, * where CDE was nearly 48 kbytes before EOF. @@ -157,81 +227,149 @@ struct BUG_cde_header_must_be_16_bytes { * To make extraction work, bumped PEEK_FROM_END from 16k to 64k. */ #define PEEK_FROM_END (64*1024) - -/* This value means that we failed to find CDF */ -#define BAD_CDF_OFFSET ((uint32_t)0xffffffff) - /* NB: does not preserve file position! */ -static uint32_t find_cdf_offset(int fd) +static uint64_t find_cdf_offset(int fd) { - cde_header_t cde_header; - unsigned char *p; - off_t end; - unsigned char *buf = xzalloc(PEEK_FROM_END); + cde_t cde; + cde64_t cde64; + unsigned char *buf; + unsigned char *p, c; + uint64_t end = 0, found, size; - if (buf == NULL) - return 0; - end = lseek(fd, 0, SEEK_END); - end -= PEEK_FROM_END; - if (end < 0) + size = lseek(fd, 0, SEEK_END); + if (size == (off_t) -1) + return BAD_CDF_OFFSET; + + if (size > PEEK_FROM_END) + end = size - PEEK_FROM_END; + else end = 0; - lseek(fd, end, SEEK_SET); - full_read(fd, buf, PEEK_FROM_END); - cde_header.formatted.cdf_offset = BAD_CDF_OFFSET; + size = MIN(size, PEEK_FROM_END); + + lseek(fd, end, SEEK_SET); + buf = xzalloc((size_t)size); + if (buf == NULL) + return 0; + full_read(fd, buf, (unsigned int)size); + + found = BAD_CDF_OFFSET; p = buf; - while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) { + while (p <= buf + size - CDE_LEN - 4) { if (*p != 'P') { p++; continue; } if (*++p != 'K') continue; - if (*++p != 5) + c = *++p; + if (c != 5 && c != 6) continue; if (*++p != 6) continue; /* we found CDE! */ - memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN); - FIX_ENDIANNESS_CDE(cde_header); - /* - * I've seen .ZIP files with seemingly valid CDEs - * where cdf_offset points past EOF - ?? - * Ignore such CDEs: - */ - if (cde_header.formatted.cdf_offset < end + (p - buf)) + switch (c) { + case 5: /* 32-bit CDE */ + memcpy(cde.raw, p + 1, CDE_LEN); + dbg("cde.this_disk_no:%d", cde.fmt.this_disk_no); + dbg("cde.disk_with_cdf_no:%d", cde.fmt.disk_with_cdf_no); + dbg("cde.cdf_entries_on_this_disk:%d", cde.fmt.cdf_entries_on_this_disk); + dbg("cde.cdf_entries_total:%d", cde.fmt.cdf_entries_total); + dbg("cde.cdf_size:%d", cde.fmt.cdf_size); + dbg("cde.cdf_offset:%x", cde.fmt.cdf_offset); + FIX_ENDIANNESS_CDE(cde); + /* + * I've seen .ZIP files with seemingly valid CDEs + * where cdf_offset points past EOF - ?? + * This check ignores such CDEs: + */ + if (cde.fmt.cdf_offset != 0xffffffffL && + cde.fmt.cdf_offset < end + (p - buf)) { + found = cde.fmt.cdf_offset; + dbg("Possible cdf_offset:0x%"OFF_FMT"x at 0x%"OFF_FMT"x", + found, end + (p - 3 - buf)); + dbg(" cdf_offset+cdf_size:0x%"OFF_FMT"x", + (found + SWAP_LE32(cde.fmt.cdf_size))); + /* + * We do not "break" here because only the last CDE is valid. + * I've seen a .zip archive which contained a .zip file, + * uncompressed, and taking the first CDE was using + * the CDE inside that file! + */ + } break; - cde_header.formatted.cdf_offset = BAD_CDF_OFFSET; + case 6: /* 64-bit CDE */ + memcpy(cde64.raw, p + 1, CDE64_LEN); + FIX_ENDIANNESS_CDE64(cde64); + dbg("cde64.this_disk_no:%d", cde64.fmt.this_disk_no); + dbg("cde64.disk_with_cdf_no:%d", cde64.fmt.disk_with_cdf_no); + dbg("cde64.cdf_entries_on_this_disk:%lld", cde64.fmt.cdf_entries_on_this_disk); + dbg("cde64.cdf_entries_total:%lld", cde64.fmt.cdf_entries_total); + dbg("cde64.cdf_size:%lld", cde64.fmt.cdf_size); + dbg("cde64.cdf_offset:%llx", cde64.fmt.cdf_offset); + if (cde64.fmt.cdf_offset < end + (p - buf)) { + found = cde64.fmt.cdf_offset; + dbg("Possible cdf_offset:0x%"OFF_FMT"x at 0x%"OFF_FMT"x", + found, end + (p - 3 - buf)); + dbg(" cdf_offset+cdf_size:0x%"OFF_FMT"x", + (found + SWAP_LE64(cde64.fmt.cdf_size))); + } + break; + } } free(buf); - return cde_header.formatted.cdf_offset; + dbg("Found cdf_offset:0x%"OFF_FMT"x", found); + return found; }; -static uint32_t read_next_cdf(int fd, uint32_t cdf_offset, cdf_header_t *cdf_ptr) +static uint64_t read_next_cdf(int fd, uint64_t cdf_offset, cdf_header_t *cdf) { - off_t org; + uint32_t magic; - org = lseek(fd, 0, SEEK_CUR); + if (cdf_offset == BAD_CDF_OFFSET) + return cdf_offset; - if (!cdf_offset) - cdf_offset = find_cdf_offset(fd); - - if (cdf_offset != BAD_CDF_OFFSET) { - lseek(fd, cdf_offset + 4, SEEK_SET); - if (_read(fd, cdf_ptr->raw, CDF_HEADER_LEN) != CDF_HEADER_LEN) - return 0; - FIX_ENDIANNESS_CDF(*cdf_ptr); - cdf_offset += 4 + CDF_HEADER_LEN - + cdf_ptr->formatted.file_name_length - + cdf_ptr->formatted.extra_field_length - + cdf_ptr->formatted.file_comment_length; + dbg("Reading CDF at 0x%"OFF_FMT"x", cdf_offset); + lseek(fd, cdf_offset, SEEK_SET); + (void)_read(fd, &magic, 4); + /* Central Directory End? Assume CDF has ended. + * (more correct method is to use cde.cdf_entries_total counter) + */ + if (magic == ZIP_CDE_MAGIC) { + dbg("got ZIP_CDE_MAGIC"); + return 0; /* EOF */ } + if (magic == ZIP64_CDE_MAGIC) { /* seen in .zip with >4GB files */ + dbg("got ZIP64_CDE_MAGIC"); + return 0; /* EOF */ + } + (void)_read(fd, cdf->raw, CDF_HEADER_LEN); - lseek(fd, org, SEEK_SET); + FIX_ENDIANNESS_CDF(*cdf); + dbg(" magic:%08x filename_len:%u extra_len:%u file_comment_length:%u", + magic, + (unsigned)cdf->fmt.filename_len, + (unsigned)cdf->fmt.extra_len, + (unsigned)cdf->fmt.file_comment_length + ); +//TODO: require that magic == ZIP_CDF_MAGIC? + + cdf_offset += 4 + CDF_HEADER_LEN + + cdf->fmt.filename_len + + cdf->fmt.extra_len + + cdf->fmt.file_comment_length; + + dbg("Next cdf_offset 0x%"OFF_FMT"x", cdf_offset); return cdf_offset; }; #endif +static void die_if_bad_fnamesize(unsigned sz) +{ + if (sz > 0xfff) /* more than 4k?! no funny business please */ + bb_simple_error_msg_and_die("bad archive"); +} + static void unzip_skip(int fd, off_t skip) { if (skip != 0) @@ -239,109 +377,241 @@ static void unzip_skip(int fd, off_t skip) bb_copyfd_exact_size(fd, -1, skip); } -IF_DESKTOP(long long) int FAST_FUNC unpack_zip_stream(transformer_state_t *xstate) +/* Set the filename and process extra ZIP64 data */ +static void unzip_set_xstate(transformer_state_t* xstate, zip_header_t* zip) +{ + uint8_t* buf = NULL; + zip64_t* zip64; + + /* Set the default sizes for non ZIP64 content */ + xstate->dst_size = zip->fmt.ucmpsize; + xstate->bytes_in = zip->fmt.cmpsize; + + /* Set the filename */ + die_if_bad_fnamesize(zip->fmt.filename_len); + xstate->dst_name = xzalloc(zip->fmt.filename_len + 1); + if (xstate->dst_name == NULL) + goto err; + xread(xstate->src_fd, xstate->dst_name, zip->fmt.filename_len); + xstate->dst_name[zip->fmt.filename_len] = 0; + + /* Read the extra data */ + if (zip->fmt.extra_len != 0) { + buf = malloc(zip->fmt.extra_len); + if (buf == NULL) + goto err; + xread(xstate->src_fd, buf, zip->fmt.extra_len); + } + + /* Process the ZIP64 data */ + zip64 = (zip64_t*)buf; + FIX_ENDIANNESS_ZIP64(*zip64); + if (zip->fmt.cmpsize == 0xffffffffL && zip->fmt.ucmpsize == 0xffffffffL && + zip->fmt.extra_len >= sizeof(zip64_t) && zip64->fmt.signature == SWAP_LE16(0x0001)) { + xstate->dst_size = zip64->fmt.ucmpsize; + xstate->bytes_in = zip64->fmt.cmpsize; + } + +err: + free(buf); +} + +static IF_DESKTOP(long long) int +unzip_extract(zip_header_t* zip, transformer_state_t* xstate) { IF_DESKTOP(long long) int n = -EFAULT; - zip_header_t zip_header; -#if ENABLE_DESKTOP - uint32_t cdf_offset = 0; + + if (zip->fmt.method == 0) { + /* Method 0 - stored (not compressed) */ + if (xstate->dst_size) { + bb_copyfd_exact_size(xstate->src_fd, xstate->dst_fd, xstate->dst_size); + } + return xstate->dst_size; + } + + if (zip->fmt.method == 8) { + /* Method 8 - inflate */ + n = inflate_unzip(xstate); + + /* Validate decompression */ + if (n >= 0) { + if (zip->fmt.crc32 != (xstate->crc32 ^ 0xffffffffL)) { + bb_simple_error_msg_and_die("crc error"); + } + } else if (n != -ENOSPC) { + bb_simple_error_msg_and_die("inflate error"); + } + } +#if ENABLE_FEATURE_UNZIP_BZIP2 + else if (zip->fmt.method == 12) { + /* Tested. Unpacker reads too much, but we use CDF + * and will seek to the correct beginning of next file. + */ + xstate->bytes_out = unpack_bz2_stream(xstate); + if (xstate->bytes_out < 0) + bb_simple_error_msg_and_die("inflate error"); + } #endif +#if ENABLE_FEATURE_UNZIP_LZMA + else if (zip->fmt.method == 14) { + /* Not tested yet */ + xstate->bytes_out = unpack_lzma_stream(xstate); + if (xstate->bytes_out < 0) + bb_simple_error_msg_and_die("inflate error"); + } +#endif +#if ENABLE_FEATURE_UNZIP_XZ + else if (zip->fmt.method == 95) { + /* Not tested yet */ + xstate->bytes_out = unpack_xz_stream(xstate); + if (xstate->bytes_out < 0) + bb_simple_error_msg_and_die("inflate error"); + } +#endif + else { + bb_error_msg_and_die("unsupported method %u", zip->fmt.method); + } + + /* Validate decompression - size */ + if (n != -ENOSPC && xstate->dst_size != xstate->bytes_out) { + /* Don't die. Who knows, maybe len calculation + * was botched somewhere. After all, crc matched! */ + bb_simple_error_msg("bad length"); + } + return n; +} + + +IF_DESKTOP(long long) int FAST_FUNC +unpack_zip_stream(transformer_state_t *xstate) +{ + IF_DESKTOP(long long) int n = -EFAULT; + bool is_dir = false; + uint64_t cdf_offset = find_cdf_offset(xstate->src_fd); /* try to seek to the end, find CDE and CDF start */ while (1) { - uint32_t magic; - bool is_dir = false; - /* Check magic number */ - safe_read(xstate->src_fd, &magic, 4); - /* Central directory? It's at the end, so exit */ - if (magic == ZIP_CDF_MAGIC) - break; -#if ENABLE_DESKTOP - /* Data descriptor? It was a streaming file, go on */ - if (magic == ZIP_DD_MAGIC) { - /* skip over duplicate crc32, cmpsize and ucmpsize */ - unzip_skip(xstate->src_fd, 3 * 4); - continue; - } -#endif - if (magic != ZIP_FILEHEADER_MAGIC) - bb_error_msg_and_err("invalid zip magic 0x%08X", magic); - - /* Read the file header */ - safe_read(xstate->src_fd, zip_header.raw, ZIP_HEADER_LEN); - FIX_ENDIANNESS_ZIP(zip_header); - if ((zip_header.formatted.method != 8) && (zip_header.formatted.method != 0)) { - bb_error_msg_and_err("zip method method %d is not supported", zip_header.formatted.method); - } -#if !ENABLE_DESKTOP - if (zip_header.formatted.zip_flags & SWAP_LE16(0x0009)) { - bb_error_msg_and_err("zip flags 1 and 8 are not supported"); - } -#else - if (zip_header.formatted.zip_flags & SWAP_LE16(0x0001)) { - /* 0x0001 - encrypted */ - bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); - } - - if (cdf_offset != BAD_CDF_OFFSET) { - cdf_header_t cdf_header; - cdf_offset = read_next_cdf(xstate->src_fd, cdf_offset, &cdf_header); - /* - * Note: cdf_offset can become BAD_CDF_OFFSET after the above call. + zip_header_t zip; + if (!ENABLE_FEATURE_UNZIP_CDF || cdf_offset == BAD_CDF_OFFSET) { + /* Normally happens when input is unseekable. + * + * Valid ZIP file has Central Directory at the end + * with central directory file headers (CDFs). + * After it, there is a Central Directory End structure. + * CDFs identify what files are in the ZIP and where + * they are located. This allows ZIP readers to load + * the list of files without reading the entire ZIP archive. + * ZIP files may be appended to, only files specified in + * the CD are valid. Scanning for local file headers is + * not a correct algorithm. + * + * We try to do the above, and resort to "linear" reading + * of ZIP file only if seek failed or CDE wasn't found. */ - if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) { + uint32_t magic; + + /* Check magic number */ + xread(xstate->src_fd, &magic, 4); + /* CDF item? Assume there are no more files, exit */ + if (magic == ZIP_CDF_MAGIC) { + dbg("got ZIP_CDF_MAGIC"); + break; + } + /* Data descriptor? It was a streaming file, go on */ + if (magic == ZIP_DD_MAGIC) { + dbg("got ZIP_DD_MAGIC"); + /* skip over duplicate crc32, cmpsize and ucmpsize */ + unzip_skip(xstate->src_fd, 3 * 4); + continue; + } + if (magic != ZIP_FILEHEADER_MAGIC) + bb_error_msg_and_die("invalid zip magic %08X", (int)magic); + dbg("got ZIP_FILEHEADER_MAGIC"); + + xread(xstate->src_fd, zip.raw, ZIP_HEADER_LEN); + FIX_ENDIANNESS_ZIP(zip); + if (zip.fmt.zip_flags & SWAP_LE16(0x0008)) { + bb_error_msg_and_die("zip flag %s is not supported", + "8 (streaming)"); + } + } +#if ENABLE_FEATURE_UNZIP_CDF + else { + /* cdf_offset is valid (and we know the file is seekable) */ + cdf_header_t cdf; + cdf_offset = read_next_cdf(xstate->src_fd, cdf_offset, &cdf); + if (cdf_offset == 0) /* EOF? */ + break; +# if 1 + lseek(xstate->src_fd, + SWAP_LE32(cdf.fmt.relative_offset_of_local_header) + 4, + SEEK_SET); + xread(xstate->src_fd, zip.raw, ZIP_HEADER_LEN); + FIX_ENDIANNESS_ZIP(zip); + if (zip.fmt.zip_flags & SWAP_LE16(0x0008)) { /* 0x0008 - streaming. [u]cmpsize can be reliably gotten - * only from Central Directory. See unzip_doc.txt + * only from Central Directory. */ - zip_header.formatted.crc32 = cdf_header.formatted.crc32; - zip_header.formatted.cmpsize = cdf_header.formatted.cmpsize; - zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize; + zip.fmt.crc32 = cdf.fmt.crc32; + zip.fmt.cmpsize = cdf.fmt.cmpsize; + zip.fmt.ucmpsize = cdf.fmt.ucmpsize; } /* Check for UNIX/DOS/WIN directory */ - is_dir = cdf_header.formatted.external_file_attributes & 0x40000010; + is_dir = cdf.fmt.external_attributes & 0x40000010; +// Seen in some zipfiles: central directory 9 byte extra field contains +// a subfield with ID 0x5455 and 5 data bytes, which is a Unix-style UTC mtime. +// Local header version: +// u16 0x5455 ("UT") +// u16 size (1 + 4 * n) +// u8 flags: bit 0:mtime is present, bit 1:atime is present, bit 2:ctime is present +// u32 mtime +// u32 atime +// u32 ctime +// Central header version: +// u16 0x5455 ("UT") +// u16 size (5 (or 1?)) +// u8 flags: bit 0:mtime is present, bit 1:atime is present, bit 2:ctime is present +// u32 mtime (CDF does not store atime/ctime) +# else + /* CDF has the same data as local header, no need to read the latter... + * ...not really. An archive was seen with cdf.extra_len == 6 but + * zip.extra_len == 0. + */ + memcpy(&zip.fmt.version, + &cdf.fmt.version_needed, ZIP_HEADER_LEN); + xlseek(zip_fd, + SWAP_LE32(cdf.fmt.relative_offset_of_local_header) + 4 + ZIP_HEADER_LEN, + SEEK_SET); +# endif } +#endif if (cdf_offset == BAD_CDF_OFFSET - && (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) - ) { + && (zip.fmt.zip_flags & SWAP_LE16(0x0008)) + ) { /* If it's a streaming zip, we _require_ CDF */ bb_error_msg_and_die("can't find file table"); } -#endif + if (zip.fmt.zip_flags & SWAP_LE16(0x0001)) { + /* 0x0001 - encrypted */ + bb_error_msg_and_die("zip flag %s is not supported", + "1 (encryption)"); + } + dbg("File cmpsize:0x%x extra_len:0x%x ucmpsize:0x%x", + (unsigned)zip.fmt.cmpsize, + (unsigned)zip.fmt.extra_len, + (unsigned)zip.fmt.ucmpsize + ); + + /* Sets the file name and set the file sizes using ZIP64 if present */ + unzip_set_xstate(xstate, &zip); /* Handle multiple file switching */ - if ((!is_dir) && (xstate->dst_dir != NULL)) { - xstate->dst_size = zip_header.formatted.ucmpsize; - xstate->dst_name = xzalloc(zip_header.formatted.filename_len + 1); - safe_read(xstate->src_fd, xstate->dst_name, zip_header.formatted.filename_len); - xstate->dst_name[zip_header.formatted.filename_len] = 0; - n = transformer_switch_file(xstate); - free(xstate->dst_name); - if (n < 0) + if ((!is_dir) && (xstate->dst_dir != NULL) && + (transformer_switch_file(xstate) < 0)) { goto err; - } else { - unzip_skip(xstate->src_fd, zip_header.formatted.filename_len); } - /* Skip extra header bytes */ - unzip_skip(xstate->src_fd, zip_header.formatted.extra_len); + n = unzip_extract(&zip, xstate); - if (zip_header.formatted.method == 0) { - if (!is_dir) - bb_error_msg_and_err("zip method method 0 is only supported for directories"); - } else { - /* Method 8 - inflate */ - xstate->bytes_in = zip_header.formatted.cmpsize; - n = inflate_unzip(xstate); - - /* Validate decompression */ - if (n >= 0) { - if (zip_header.formatted.ucmpsize != 0xffffffffL && zip_header.formatted.ucmpsize != xstate->bytes_out) - bb_error_msg_and_err("bad length"); - else if (zip_header.formatted.crc32 != (xstate->crc32 ^ 0xffffffffL)) - bb_error_msg_and_err("crc error"); - } else if (n != -ENOSPC) { - bb_error_msg_and_err("inflate error"); - } - } /* Only process the first file if not extracting to a dir */ if (xstate->dst_dir == NULL) break; diff --git a/src/bled/init_handle.c b/src/bled/init_handle.c index 92a1cfa6..c9c3e511 100644 --- a/src/bled/init_handle.c +++ b/src/bled/init_handle.c @@ -19,6 +19,10 @@ archive_handle_t* FAST_FUNC init_handle(void) archive_handle->action_data = data_skip; archive_handle->filter = filter_accept_all; archive_handle->seek = seek_by_jump; +#if ENABLE_CPIO || ENABLE_RPM2CPIO || ENABLE_RPM + archive_handle->cpio__owner.uid = (uid_t)-1L; + archive_handle->cpio__owner.gid = (gid_t)-1L; +#endif return archive_handle; } diff --git a/src/bled/libbb.h b/src/bled/libbb.h index 6ecb40ce..983c7c6d 100644 --- a/src/bled/libbb.h +++ b/src/bled/libbb.h @@ -2,7 +2,7 @@ * Library header for busybox/Bled * * Rewritten for Bled (Base Library for Easy Decompression) - * Copyright © 2014-2022 Pete Batard + * Copyright © 2014-2023 Pete Batard * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ @@ -50,9 +50,13 @@ #define IF_NOT_DESKTOP(x) x #endif #define IF_NOT_FEATURE_LZMA_FAST(x) x +#define ENABLE_FEATURE_UNZIP_CDF 1 +#define ENABLE_FEATURE_UNZIP_BZIP2 1 +#define ENABLE_FEATURE_UNZIP_LZMA 1 +#define ENABLE_FEATURE_UNZIP_XZ 1 #define uoff_t unsigned off_t -#define OFF_FMT PRIi64 +#define OFF_FMT "ll" #ifndef _MODE_T_ #define _MODE_T_ @@ -143,7 +147,7 @@ extern unsigned long* bled_cancel_request; #define xfunc_die() longjmp(bb_error_jmp, 1) #define bb_printf(...) do { if (bled_printf != NULL) bled_printf(__VA_ARGS__); \ else { printf(__VA_ARGS__); putchar('\n'); } } while(0) -#define bb_error_msg(...) bb_printf("\nError: " __VA_ARGS__) +#define bb_error_msg(...) bb_printf("Error: " __VA_ARGS__) #define bb_error_msg_and_die(...) do {bb_error_msg(__VA_ARGS__); xfunc_die();} while(0) #define bb_error_msg_and_err(...) do {bb_error_msg(__VA_ARGS__); goto err;} while(0) #define bb_perror_msg bb_error_msg @@ -163,7 +167,6 @@ static inline void *xrealloc(void *ptr, size_t size) { #define bb_msg_read_error "read error" #define bb_msg_write_error "write error" #define bb_mode_string(str, mode) "[not implemented]" -#define bb_copyfd_exact_size(fd1, fd2, size) bb_error_msg("Not implemented") #define bb_make_directory(path, mode, flags) SHCreateDirectoryExU(NULL, path, NULL) static inline int link(const char *oldpath, const char *newpath) {errno = ENOSYS; return -1;} @@ -215,6 +218,43 @@ static inline int full_write(int fd, const void* buffer, unsigned int count) return (bled_write != NULL) ? bled_write(fd, buffer, count) : _write(fd, buffer, count); } +static inline void bb_copyfd_exact_size(int fd1, int fd2, off_t size) +{ + off_t rb = 0; + uint8_t* buf = NULL; + + if (fd1 < 0 || fd2 < 0) + bb_error_msg_and_die("invalid fd"); + + buf = malloc(BB_BUFSIZE); + if (buf == NULL) + bb_error_msg_and_die("out of memory"); + + while (rb < size) { + int r, w; + r = full_read(fd1, buf, (unsigned int)MIN(size - rb, BB_BUFSIZE)); + if (r < 0) { + free(buf); + bb_error_msg_and_die("read error"); + } + if (r == 0) { + bb_error_msg("short read"); + break; + } + w = full_write(fd2, buf, r); + if (w < 0) { + free(buf); + bb_error_msg_and_die("write error"); + } + if (w == 0) { + bb_error_msg("short write"); + break; + } + rb += r; + } + free(buf); +} + static inline struct tm *localtime_r(const time_t *timep, struct tm *result) { if (localtime_s(result, timep) != 0) result = NULL; diff --git a/src/bled/platform.h b/src/bled/platform.h index f8819589..ce0cf5f3 100644 --- a/src/bled/platform.h +++ b/src/bled/platform.h @@ -57,11 +57,7 @@ */ #if defined(__GNUC__) #define RETURNS_MALLOC __attribute__ ((malloc)) -#ifdef __MINGW32__ -#define PACKED __attribute__ ((packed, gcc_struct)) -#else #define PACKED __attribute__ ((__packed__)) -#endif #define ALIGNED(m) __attribute__ ((__aligned__(m))) #define PRAGMA_BEGIN_PACKED #define PRAGMA_END_PACKED diff --git a/src/pki.c b/src/pki.c index 1419c854..8f327325 100644 --- a/src/pki.c +++ b/src/pki.c @@ -98,7 +98,7 @@ typedef struct { BYTE Hash[0]; } CIFileRuleData; -typedef enum { +enum { CI_DENY = 0, CI_ALLOW, CI_FILE_ATTRIBUTES, diff --git a/src/rufus.rc b/src/rufus.rc index c6a5df6a..1131f02f 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 232, 326 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 4.2.2055" +CAPTION "Rufus 4.2.2056" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -392,8 +392,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,2,2055,0 - PRODUCTVERSION 4,2,2055,0 + FILEVERSION 4,2,2056,0 + PRODUCTVERSION 4,2,2056,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -411,13 +411,13 @@ BEGIN VALUE "Comments", "https://rufus.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "4.2.2055" + VALUE "FileVersion", "4.2.2056" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2023 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "OriginalFilename", "rufus-4.2.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "4.2.2055" + VALUE "ProductVersion", "4.2.2056" END END BLOCK "VarFileInfo"