diff options
Diffstat (limited to 'libarchive/archive_read_support_format_7zip.c')
| -rw-r--r-- | libarchive/archive_read_support_format_7zip.c | 212 |
1 files changed, 172 insertions, 40 deletions
diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c index 194b8d51c96a..90901acb710f 100644 --- a/libarchive/archive_read_support_format_7zip.c +++ b/libarchive/archive_read_support_format_7zip.c @@ -69,7 +69,11 @@ __FBSDID("$FreeBSD$"); #define _7Z_BZ2 0x040202 #define _7Z_PPMD 0x030401 #define _7Z_DELTA 0x03 -#define _7Z_CRYPTO 0x06F10701 +#define _7Z_CRYPTO_MAIN_ZIP 0x06F10101 /* Main Zip crypto algo */ +#define _7Z_CRYPTO_RAR_29 0x06F10303 /* Rar29 AES-128 + (modified SHA-1) */ +#define _7Z_CRYPTO_AES_256_SHA_256 0x06F10701 /* AES-256 + SHA-256 */ + + #define _7Z_X86 0x03030103 #define _7Z_X86_BCJ2 0x0303011B #define _7Z_POWERPC 0x03030205 @@ -104,6 +108,7 @@ __FBSDID("$FreeBSD$"); #define kMTime 0x14 #define kAttributes 0x15 #define kEncodedHeader 0x17 +#define kDummy 0x19 struct _7z_digests { unsigned char *defineds; @@ -322,8 +327,18 @@ struct _7zip { struct archive_string_conv *sconv; char format_name[64]; + + /* Custom value that is non-zero if this archive contains encrypted entries. */ + int has_encrypted_entries; }; +/* Maximum entry size. This limitation prevents reading intentional + * corrupted 7-zip files on assuming there are not so many entries in + * the files. */ +#define UMAX_ENTRY ARCHIVE_LITERAL_ULL(100000000) + +static int archive_read_format_7zip_has_encrypted_entries(struct archive_read *); +static int archive_read_support_format_7zip_capabilities(struct archive_read *a); static int archive_read_format_7zip_bid(struct archive_read *, int); static int archive_read_format_7zip_cleanup(struct archive_read *); static int archive_read_format_7zip_read_data(struct archive_read *, @@ -401,6 +416,13 @@ archive_read_support_format_7zip(struct archive *_a) return (ARCHIVE_FATAL); } + /* + * Until enough data has been read, we cannot tell about + * any encrypted entries yet. + */ + zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; + + r = __archive_read_register_format(a, zip, "7zip", @@ -410,7 +432,9 @@ archive_read_support_format_7zip(struct archive *_a) archive_read_format_7zip_read_data, archive_read_format_7zip_read_data_skip, NULL, - archive_read_format_7zip_cleanup); + archive_read_format_7zip_cleanup, + archive_read_support_format_7zip_capabilities, + archive_read_format_7zip_has_encrypted_entries); if (r != ARCHIVE_OK) free(zip); @@ -418,6 +442,27 @@ archive_read_support_format_7zip(struct archive *_a) } static int +archive_read_support_format_7zip_capabilities(struct archive_read * a) +{ + (void)a; /* UNUSED */ + return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | + ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); +} + + +static int +archive_read_format_7zip_has_encrypted_entries(struct archive_read *_a) +{ + if (_a && _a->format) { + struct _7zip * zip = (struct _7zip *)_a->format->data; + if (zip) { + return zip->has_encrypted_entries; + } + } + return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; +} + +static int archive_read_format_7zip_bid(struct archive_read *a, int best_bid) { const char *p; @@ -476,7 +521,7 @@ check_7zip_header_in_sfx(const char *p) switch ((unsigned char)p[5]) { case 0x1C: if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) - return (6); + return (6); /* * Test the CRC because its extraction code has 7-Zip * Magic Code, so we should do this in order not to @@ -484,15 +529,15 @@ check_7zip_header_in_sfx(const char *p) */ if (crc32(0, (const unsigned char *)p + 12, 20) != archive_le32dec(p + 8)) - return (6); + return (6); /* Hit the header! */ return (0); - case 0x37: return (5); - case 0x7A: return (4); - case 0xBC: return (3); - case 0xAF: return (2); - case 0x27: return (1); - default: return (6); + case 0x37: return (5); + case 0x7A: return (4); + case 0xBC: return (3); + case 0xAF: return (2); + case 0x27: return (1); + default: return (6); } } @@ -568,6 +613,19 @@ archive_read_format_7zip_read_header(struct archive_read *a, struct _7zip *zip = (struct _7zip *)a->format->data; struct _7zip_entry *zip_entry; int r, ret = ARCHIVE_OK; + struct _7z_folder *folder = 0; + uint64_t fidx = 0; + + /* + * It should be sufficient to call archive_read_next_header() for + * a reader to determine if an entry is encrypted or not. If the + * encryption of an entry is only detectable when calling + * archive_read_data(), so be it. We'll do the same check there + * as well. + */ + if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { + zip->has_encrypted_entries = 0; + } a->archive.archive_format = ARCHIVE_FORMAT_7ZIP; if (a->archive.archive_format_name == NULL) @@ -588,7 +646,7 @@ archive_read_format_7zip_read_header(struct archive_read *a, } zip_entry = zip->entry; - if (zip->entries_remaining <= 0) + if (zip->entries_remaining <= 0 || zip_entry == NULL) return ARCHIVE_EOF; --zip->entries_remaining; @@ -604,6 +662,32 @@ archive_read_format_7zip_read_header(struct archive_read *a, return (ARCHIVE_FATAL); } + /* Figure out if the entry is encrypted by looking at the folder + that is associated to the current 7zip entry. If the folder + has a coder with a _7Z_CRYPTO codec then the folder is encrypted. + Hence the entry must also be encrypted. */ + if (zip_entry && zip_entry->folderIndex < zip->si.ci.numFolders) { + folder = &(zip->si.ci.folders[zip_entry->folderIndex]); + for (fidx=0; folder && fidx<folder->numCoders; fidx++) { + switch(folder->coders[fidx].codec) { + case _7Z_CRYPTO_MAIN_ZIP: + case _7Z_CRYPTO_RAR_29: + case _7Z_CRYPTO_AES_256_SHA_256: { + archive_entry_set_is_data_encrypted(entry, 1); + zip->has_encrypted_entries = 1; + break; + } + } + } + } + + /* Now that we've checked for encryption, if there were still no + * encrypted entries found we can say for sure that there are none. + */ + if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { + zip->has_encrypted_entries = 0; + } + if (archive_entry_copy_pathname_l(entry, (const char *)zip_entry->utf16name, zip_entry->name_len, zip->sconv) != 0) { @@ -707,6 +791,10 @@ archive_read_format_7zip_read_data(struct archive_read *a, zip = (struct _7zip *)(a->format->data); + if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { + zip->has_encrypted_entries = 0; + } + if (zip->pack_stream_bytes_unconsumed) read_consume(a); @@ -969,7 +1057,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, { lzma_options_delta delta_opt; lzma_filter filters[LZMA_FILTERS_MAX]; -#if LZMA_VERSION < 50000030 +#if LZMA_VERSION < 50010000 lzma_filter *ff; #endif int fi = 0; @@ -994,7 +1082,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, * for BCJ+LZMA. If we were able to tell the uncompressed * size to liblzma when using lzma_raw_decoder() liblzma * could correctly deal with BCJ+LZMA. But unfortunately - * there is no way to do that. + * there is no way to do that. * Discussion about this can be found at XZ Utils forum. */ if (coder2 != NULL) { @@ -1056,7 +1144,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, else filters[fi].id = LZMA_FILTER_LZMA1; filters[fi].options = NULL; -#if LZMA_VERSION < 50000030 +#if LZMA_VERSION < 50010000 ff = &filters[fi]; #endif r = lzma_properties_decode(&filters[fi], NULL, @@ -1070,7 +1158,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, filters[fi].id = LZMA_VLI_UNKNOWN; filters[fi].options = NULL; r = lzma_raw_decoder(&(zip->lzstream), filters); -#if LZMA_VERSION < 50000030 +#if LZMA_VERSION < 50010000 free(ff->options); #endif if (r != LZMA_OK) { @@ -1113,7 +1201,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, } archive_set_error(&a->archive, err, "Internal error initializing decompressor: %s", - detail == NULL ? "??" : detail); + detail != NULL ? detail : "??"); zip->bzstream_valid = 0; return (ARCHIVE_FAILED); } @@ -1203,6 +1291,17 @@ init_decompression(struct archive_read *a, struct _7zip *zip, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unexpected codec ID: %lX", zip->codec); return (ARCHIVE_FAILED); + case _7Z_CRYPTO_MAIN_ZIP: + case _7Z_CRYPTO_RAR_29: + case _7Z_CRYPTO_AES_256_SHA_256: + if (a->entry) { + archive_entry_set_is_metadata_encrypted(a->entry, 1); + archive_entry_set_is_data_encrypted(a->entry, 1); + zip->has_encrypted_entries = 1; + } + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Crypto codec not supported yet (ID: 0x%lX)", zip->codec); + return (ARCHIVE_FAILED); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unknown codec ID: %lX", zip->codec); @@ -1426,7 +1525,7 @@ decompress(struct archive_read *a, struct _7zip *zip, do { int sym; - + sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol( &(zip->ppmd7_context), &(zip->range_dec.p)); if (sym < 0) { @@ -1570,7 +1669,7 @@ parse_7zip_uint64(struct archive_read *a, uint64_t *val) mask >>= 1; continue; } - *val += (avail & (mask -1)) << (8 * i); + *val += ((uint64_t)(avail & (mask -1))) << (8 * i); break; } return (0); @@ -1670,7 +1769,7 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi) return (-1); if (pi->numPackStreams == 0) return (-1); - if (1000000 < pi->numPackStreams) + if (UMAX_ENTRY < pi->numPackStreams) return (-1); /* @@ -1799,12 +1898,12 @@ read_Folder(struct archive_read *a, struct _7z_folder *f) if (parse_7zip_uint64( a, &(f->coders[i].numInStreams)) < 0) return (-1); - if (1000000 < f->coders[i].numInStreams) + if (UMAX_ENTRY < f->coders[i].numInStreams) return (-1); if (parse_7zip_uint64( a, &(f->coders[i].numOutStreams)) < 0) return (-1); - if (1000000 < f->coders[i].numOutStreams) + if (UMAX_ENTRY < f->coders[i].numOutStreams) return (-1); } @@ -1844,11 +1943,11 @@ read_Folder(struct archive_read *a, struct _7z_folder *f) for (i = 0; i < f->numBindPairs; i++) { if (parse_7zip_uint64(a, &(f->bindPairs[i].inIndex)) < 0) return (-1); - if (1000000 < f->bindPairs[i].inIndex) + if (UMAX_ENTRY < f->bindPairs[i].inIndex) return (-1); if (parse_7zip_uint64(a, &(f->bindPairs[i].outIndex)) < 0) return (-1); - if (1000000 < f->bindPairs[i].outIndex) + if (UMAX_ENTRY < f->bindPairs[i].outIndex) return (-1); } @@ -1874,7 +1973,7 @@ read_Folder(struct archive_read *a, struct _7z_folder *f) for (i = 0; i < f->numPackedStreams; i++) { if (parse_7zip_uint64(a, &(f->packedStreams[i])) < 0) return (-1); - if (1000000 < f->packedStreams[i]) + if (UMAX_ENTRY < f->packedStreams[i]) return (-1); } } @@ -1916,8 +2015,8 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci) */ if (parse_7zip_uint64(a, &(ci->numFolders)) < 0) goto failed; - if (1000000 < ci->numFolders) - return (-1); + if (UMAX_ENTRY < ci->numFolders) + return (-1); /* * Read External. @@ -1938,9 +2037,18 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci) case 1: if (parse_7zip_uint64(a, &(ci->dataStreamIndex)) < 0) return (-1); - if (1000000 < ci->dataStreamIndex) + if (UMAX_ENTRY < ci->dataStreamIndex) return (-1); + if (ci->numFolders > 0) { + archive_set_error(&a->archive, -1, + "Malformed 7-Zip archive"); + goto failed; + } break; + default: + archive_set_error(&a->archive, -1, + "Malformed 7-Zip archive"); + goto failed; } if ((p = header_bytes(a, 1)) == NULL) @@ -2043,7 +2151,7 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, for (i = 0; i < numFolders; i++) { if (parse_7zip_uint64(a, &(f[i].numUnpackStreams)) < 0) return (-1); - if (1000000 < f[i].numUnpackStreams) + if (UMAX_ENTRY < f[i].numUnpackStreams) return (-1); unpack_streams += (size_t)f[i].numUnpackStreams; } @@ -2292,8 +2400,8 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, if (parse_7zip_uint64(a, &(zip->numFiles)) < 0) return (-1); - if (1000000 < zip->numFiles) - return (-1); + if (UMAX_ENTRY < zip->numFiles) + return (-1); zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries)); if (zip->entries == NULL) @@ -2452,6 +2560,9 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, } break; } + case kDummy: + if (ll == 0) + break; default: if (header_bytes(a, ll) == NULL) return (-1); @@ -2591,7 +2702,7 @@ read_Times(struct archive_read *a, struct _7z_header_info *h, int type) if (*p) { if (parse_7zip_uint64(a, &(h->dataIndex)) < 0) goto failed; - if (1000000 < h->dataIndex) + if (UMAX_ENTRY < h->dataIndex) goto failed; } @@ -2755,6 +2866,7 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip, zip->header_crc32 = 0; zip->header_is_encoded = 0; zip->header_is_being_read = 1; + zip->has_encrypted_entries = 0; check_header_crc = 1; if ((p = header_bytes(a, 1)) == NULL) { @@ -3170,7 +3282,7 @@ read_stream(struct archive_read *a, const void **buff, size_t size, return (r); /* - * Skip the bytes we alrady has skipped in skip_stream(). + * Skip the bytes we alrady has skipped in skip_stream(). */ while (skip_bytes) { ssize_t skipped; @@ -3235,16 +3347,36 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, * Check coder types. */ for (i = 0; i < folder->numCoders; i++) { - if (folder->coders[i].codec == _7Z_CRYPTO) { - archive_set_error(&(a->archive), - ARCHIVE_ERRNO_MISC, - "The %s is encrypted, " - "but currently not supported", cname); - return (ARCHIVE_FATAL); + switch(folder->coders[i].codec) { + case _7Z_CRYPTO_MAIN_ZIP: + case _7Z_CRYPTO_RAR_29: + case _7Z_CRYPTO_AES_256_SHA_256: { + /* For entry that is associated with this folder, mark + it as encrypted (data+metadata). */ + zip->has_encrypted_entries = 1; + if (a->entry) { + archive_entry_set_is_data_encrypted(a->entry, 1); + archive_entry_set_is_metadata_encrypted(a->entry, 1); + } + archive_set_error(&(a->archive), + ARCHIVE_ERRNO_MISC, + "The %s is encrypted, " + "but currently not supported", cname); + return (ARCHIVE_FATAL); + } + case _7Z_X86_BCJ2: { + found_bcj2++; + break; + } } - if (folder->coders[i].codec == _7Z_X86_BCJ2) - found_bcj2++; } + /* Now that we've checked for encryption, if there were still no + * encrypted entries found we can say for sure that there are none. + */ + if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { + zip->has_encrypted_entries = 0; + } + if ((folder->numCoders > 2 && !found_bcj2) || found_bcj2 > 1) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, |
