summaryrefslogtreecommitdiff
path: root/libarchive/archive_read_support_format_7zip.c
diff options
context:
space:
mode:
Diffstat (limited to 'libarchive/archive_read_support_format_7zip.c')
-rw-r--r--libarchive/archive_read_support_format_7zip.c212
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,