diff options
Diffstat (limited to 'Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c')
-rw-r--r-- | Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c | 1074 |
1 files changed, 943 insertions, 131 deletions
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c index 5ef2952..62bf5e3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2004-2013 Tim Kientzle - * Copyright (c) 2011-2012 Michihiro NAKAJIMA + * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA * Copyright (c) 2013 Konrad Kleine * All rights reserved. * @@ -53,9 +53,12 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102 #endif #include "archive.h" +#include "archive_digest_private.h" +#include "archive_cryptor_private.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" +#include "archive_hmac_private.h" #include "archive_private.h" #include "archive_rb.h" #include "archive_read_private.h" @@ -64,10 +67,6 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102 #include "archive_crc32.h" #endif -#if defined(_WIN32) && !defined(__CYGWIN__) -# define snprintf _snprintf -#endif - struct zip_entry { struct archive_rb_node node; struct zip_entry *next; @@ -86,6 +85,25 @@ struct zip_entry { unsigned char compression; unsigned char system; /* From "version written by" */ unsigned char flags; /* Our extra markers. */ + unsigned char decdat;/* Used for Decryption check */ + + /* WinZip AES encryption extra field should be available + * when compression is 99. */ + struct { + /* Vendor version: AE-1 - 0x0001, AE-2 - 0x0002 */ + unsigned vendor; +#define AES_VENDOR_AE_1 0x0001 +#define AES_VENDOR_AE_2 0x0002 + /* AES encryption strength: + * 1 - 128 bits, 2 - 192 bits, 2 - 256 bits. */ + unsigned strength; + /* Actual compression method. */ + unsigned char compression; + } aes_extra; +}; + +struct trad_enc_ctx { + uint32_t keys[3]; }; /* Bits used in zip_flags. */ @@ -101,9 +119,20 @@ struct zip_entry { #define LA_USED_ZIP64 (1 << 0) #define LA_FROM_CENTRAL_DIRECTORY (1 << 1) +/* + * See "WinZip - AES Encryption Information" + * http://www.winzip.com/aes_info.htm + */ +/* Value used in compression method. */ +#define WINZIP_AES_ENCRYPTION 99 +/* Authentication code size. */ +#define AUTH_CODE_SIZE 10 +/**/ +#define MAX_DERIVED_KEY_BUF_SIZE (AES_MAX_KEY_SIZE * 2 + 2) + struct zip { /* Structural information about the archive. */ - char format_name[64]; + struct archive_string format_name; int64_t central_directory_offset; size_t central_directory_entries_total; size_t central_directory_entries_on_this_disk; @@ -127,7 +156,8 @@ struct zip { /* Running CRC32 of the decompressed data */ unsigned long entry_crc32; - unsigned long (*crc32func)(unsigned long, const void *, size_t); + unsigned long (*crc32func)(unsigned long, const void *, + size_t); char ignore_crc32; /* Flags to mark progress of decompression. */ @@ -146,6 +176,38 @@ struct zip { struct archive_string_conv *sconv_utf8; int init_default_conversion; int process_mac_extensions; + + char init_decryption; + + /* Decryption buffer. */ + unsigned char *decrypted_buffer; + unsigned char *decrypted_ptr; + size_t decrypted_buffer_size; + size_t decrypted_bytes_remaining; + size_t decrypted_unconsumed_bytes; + + /* Traditional PKWARE decryption. */ + struct trad_enc_ctx tctx; + char tctx_valid; + + /* WinZip AES decyption. */ + /* Contexts used for AES decryption. */ + archive_crypto_ctx cctx; + char cctx_valid; + archive_hmac_sha1_ctx hctx; + char hctx_valid; + + /* Strong encryption's decryption header information. */ + unsigned iv_size; + unsigned alg_id; + unsigned bit_len; + unsigned flags; + unsigned erd_size; + unsigned v_size; + unsigned v_crc32; + uint8_t *iv; + uint8_t *erd; + uint8_t *v_data; }; /* Many systems define min or MIN, but not all. */ @@ -154,6 +216,106 @@ struct zip { /* ------------------------------------------------------------------------ */ /* + Traditional PKWARE Decryption functions. + */ + +static void +trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c) +{ + uint8_t t; +#define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL) + + ctx->keys[0] = CRC32(ctx->keys[0], c); + ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1; + t = (ctx->keys[1] >> 24) & 0xff; + ctx->keys[2] = CRC32(ctx->keys[2], t); +#undef CRC32 +} + +static uint8_t +trad_enc_decypt_byte(struct trad_enc_ctx *ctx) +{ + unsigned temp = ctx->keys[2] | 2; + return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff; +} + +static void +trad_enc_decrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in, + size_t in_len, uint8_t *out, size_t out_len) +{ + unsigned i, max; + + max = (unsigned)((in_len < out_len)? in_len: out_len); + + for (i = 0; i < max; i++) { + uint8_t t = in[i] ^ trad_enc_decypt_byte(ctx); + out[i] = t; + trad_enc_update_keys(ctx, t); + } +} + +static int +trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len, + const uint8_t *key, size_t key_len, uint8_t *crcchk) +{ + uint8_t header[12]; + + if (key_len < 12) { + *crcchk = 0xff; + return -1; + } + + ctx->keys[0] = 305419896L; + ctx->keys[1] = 591751049L; + ctx->keys[2] = 878082192L; + + for (;pw_len; --pw_len) + trad_enc_update_keys(ctx, *pw++); + + trad_enc_decrypt_update(ctx, key, 12, header, 12); + /* Return the last byte for CRC check. */ + *crcchk = header[11]; + return 0; +} + +#if 0 +static void +crypt_derive_key_sha1(const void *p, int size, unsigned char *key, + int key_size) +{ +#define MD_SIZE 20 + archive_sha1_ctx ctx; + unsigned char md1[MD_SIZE]; + unsigned char md2[MD_SIZE * 2]; + unsigned char mkb[64]; + int i; + + archive_sha1_init(&ctx); + archive_sha1_update(&ctx, p, size); + archive_sha1_final(&ctx, md1); + + memset(mkb, 0x36, sizeof(mkb)); + for (i = 0; i < MD_SIZE; i++) + mkb[i] ^= md1[i]; + archive_sha1_init(&ctx); + archive_sha1_update(&ctx, mkb, sizeof(mkb)); + archive_sha1_final(&ctx, md2); + + memset(mkb, 0x5C, sizeof(mkb)); + for (i = 0; i < MD_SIZE; i++) + mkb[i] ^= md1[i]; + archive_sha1_init(&ctx); + archive_sha1_update(&ctx, mkb, sizeof(mkb)); + archive_sha1_final(&ctx, md2 + MD_SIZE); + + if (key_size > 32) + key_size = 32; + memcpy(key, md2, key_size); +#undef MD_SIZE +} +#endif + +/* * Common code for streaming or seeking modes. * * Includes code to read local file headers, decompress data @@ -163,9 +325,10 @@ struct zip { static unsigned long real_crc32(unsigned long crc, const void *buff, size_t len) { - return crc32(crc, buff, len); + return crc32(crc, buff, (unsigned int)len); } +/* Used by "ignorecrc32" option to speed up tests. */ static unsigned long fake_crc32(unsigned long crc, const void *buff, size_t len) { @@ -185,33 +348,36 @@ static struct { {3, "reduced-2"}, /* The file is Reduced with compression factor 2 */ {4, "reduced-3"}, /* The file is Reduced with compression factor 3 */ {5, "reduced-4"}, /* The file is Reduced with compression factor 4 */ - {6, "imploded"}, /* The file is Imploded */ - {7, "reserved"}, /* Reserved for Tokenizing compression algorithm */ + {6, "imploded"}, /* The file is Imploded */ + {7, "reserved"}, /* Reserved for Tokenizing compression algorithm */ {8, "deflation"}, /* The file is Deflated */ {9, "deflation-64-bit"}, /* Enhanced Deflating using Deflate64(tm) */ - {10, "ibm-terse"}, /* PKWARE Data Compression Library Imploding (old IBM TERSE) */ + {10, "ibm-terse"},/* PKWARE Data Compression Library Imploding + * (old IBM TERSE) */ {11, "reserved"}, /* Reserved by PKWARE */ - {12, "bzip"}, /* File is compressed using BZIP2 algorithm */ + {12, "bzip"}, /* File is compressed using BZIP2 algorithm */ {13, "reserved"}, /* Reserved by PKWARE */ - {14, "lzma"}, /* LZMA (EFS) */ + {14, "lzma"}, /* LZMA (EFS) */ {15, "reserved"}, /* Reserved by PKWARE */ {16, "reserved"}, /* Reserved by PKWARE */ {17, "reserved"}, /* Reserved by PKWARE */ {18, "ibm-terse-new"}, /* File is compressed using IBM TERSE (new) */ - {19, "ibm-lz777"}, /* IBM LZ77 z Architecture (PFS) */ + {19, "ibm-lz777"},/* IBM LZ77 z Architecture (PFS) */ {97, "wav-pack"}, /* WavPack compressed data */ - {98, "ppmd-1"} /* PPMd version I, Rev 1 */ + {98, "ppmd-1"}, /* PPMd version I, Rev 1 */ + {99, "aes"} /* WinZip AES encryption */ }; static const char * compression_name(const int compression) { - static const int num_compression_methods = sizeof(compression_methods)/sizeof(compression_methods[0]); + static const int num_compression_methods = + sizeof(compression_methods)/sizeof(compression_methods[0]); int i=0; + while(compression >= 0 && i < num_compression_methods) { - if (compression_methods[i].id == compression) { + if (compression_methods[i].id == compression) return compression_methods[i].name; - } i++; } return "??"; @@ -248,15 +414,15 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) { unsigned offset = 0; - while (offset < extra_length - 4) - { + while (offset < extra_length - 4) { unsigned short headerid = archive_le16dec(p + offset); unsigned short datasize = archive_le16dec(p + offset + 2); + offset += 4; if (offset + datasize > extra_length) break; #ifdef DEBUG - fprintf(stderr, "Header id 0x%x, length %d\n", + fprintf(stderr, "Header id 0x%04x, length %d\n", headerid, datasize); #endif switch (headerid) { @@ -291,6 +457,23 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) * on which file starts, but we don't handle * multi-volume Zip files. */ break; +#ifdef DEBUG + case 0x0017: + { + /* Strong encryption field. */ + if (archive_le16dec(p + offset) == 2) { + unsigned algId = + archive_le16dec(p + offset + 2); + unsigned bitLen = + archive_le16dec(p + offset + 4); + int flags = + archive_le16dec(p + offset + 6); + fprintf(stderr, "algId=0x%04x, bitLen=%u, " + "flgas=%d\n", algId, bitLen,flags); + } + break; + } +#endif case 0x5455: { /* Extended time field "UT". */ @@ -345,16 +528,34 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) } break; } - case 0x6c65: + case 0x6c78: { - /* Experimental 'el' field */ + /* Experimental 'xl' field */ /* * Introduced Dec 2013 to provide a way to - * include external file attributes in local file - * header. This provides file type and permission - * information necessary to support full streaming - * extraction. Currently being discussed with - * other Zip developers... subject to change. + * include external file attributes (and other + * fields that ordinarily appear only in + * central directory) in local file header. + * This provides file type and permission + * information necessary to support full + * streaming extraction. Currently being + * discussed with other Zip developers + * ... subject to change. + * + * Format: + * The field starts with a bitmap that specifies + * which additional fields are included. The + * bitmap is variable length and can be extended in + * the future. + * + * n bytes - feature bitmap: first byte has low-order + * 7 bits. If high-order bit is set, a subsequent + * byte holds the next 7 bits, etc. + * + * if bitmap & 1, 2 byte "version made by" + * if bitmap & 2, 2 byte "internal file attributes" + * if bitmap & 4, 4 byte "external file attributes" + * if bitmap * 7, 2 byte comment length + n byte comment */ int bitmap, bitmap_last; @@ -373,7 +574,7 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) } if (bitmap & 1) { - // 2 byte "version made by" + /* 2 byte "version made by" */ if (datasize < 2) break; zip_entry->system @@ -382,19 +583,19 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) datasize -= 2; } if (bitmap & 2) { - // 2 byte "internal file attributes" + /* 2 byte "internal file attributes" */ uint32_t internal_attributes; if (datasize < 2) break; internal_attributes = archive_le16dec(p + offset); - // Not used by libarchive at present. + /* Not used by libarchive at present. */ (void)internal_attributes; /* UNUSED */ offset += 2; datasize -= 2; } if (bitmap & 4) { - // 4 byte "external file attributes" + /* 4 byte "external file attributes" */ uint32_t external_attributes; if (datasize < 4) break; @@ -408,7 +609,7 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) datasize -= 4; } if (bitmap & 8) { - // 2 byte comment length + comment + /* 2 byte comment length + comment */ uint32_t comment_length; if (datasize < 2) break; @@ -419,7 +620,7 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) if (datasize < comment_length) break; - // Comment is not supported by libarchive + /* Comment is not supported by libarchive */ offset += comment_length; datasize -= comment_length; } @@ -447,7 +648,7 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) if (datasize >= 1 && p[offset] == 1) {/* version=1 */ if (datasize >= 4) { /* get a uid size. */ - uidsize = p[offset+1]; + uidsize = 0xff & (int)p[offset+1]; if (uidsize == 2) zip_entry->uid = archive_le16dec( @@ -459,7 +660,7 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) } if (datasize >= (2 + uidsize + 3)) { /* get a gid size. */ - gidsize = p[offset+2+uidsize]; + gidsize = 0xff & (int)p[offset+2+uidsize]; if (gidsize == 2) zip_entry->gid = archive_le16dec( @@ -473,6 +674,19 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) } break; } + case 0x9901: + /* WinZIp AES extra data field. */ + if (p[offset + 2] == 'A' && p[offset + 3] == 'E') { + /* Vendor version. */ + zip_entry->aes_extra.vendor = + archive_le16dec(p + offset); + /* AES encryption strength. */ + zip_entry->aes_extra.strength = p[offset + 4]; + /* Actual compression method. */ + zip_entry->aes_extra.compression = + p[offset + 5]; + } + break; default: break; } @@ -544,9 +758,14 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, return ARCHIVE_FATAL; } } + zip->init_decryption = (zip_entry->zip_flags & ZIP_ENCRYPTED); zip_entry->compression = (char)archive_le16dec(p + 8); zip_entry->mtime = zip_time(p + 10); zip_entry->crc32 = archive_le32dec(p + 14); + if (zip_entry->zip_flags & ZIP_LENGTH_AT_END) + zip_entry->decdat = p[11]; + else + zip_entry->decdat = p[17]; zip_entry->compressed_size = archive_le32dec(p + 18); zip_entry->uncompressed_size = archive_le32dec(p + 22); filename_length = archive_le16dec(p + 26); @@ -687,7 +906,15 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, archive_entry_set_atime(entry, zip_entry->atime, 0); if ((zip->entry->mode & AE_IFMT) == AE_IFLNK) { - size_t linkname_length = zip_entry->compressed_size; + size_t linkname_length; + + if (zip_entry->compressed_size > 64 * 1024) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Zip file with oversized link entry"); + return ARCHIVE_FATAL; + } + + linkname_length = (size_t)zip_entry->compressed_size; archive_entry_set_size(entry, 0); p = __archive_read_ahead(a, linkname_length, NULL); @@ -696,11 +923,6 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, "Truncated Zip file"); return ARCHIVE_FATAL; } - if (__archive_read_consume(a, linkname_length) < 0) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Read error skipping symlink target name"); - return ARCHIVE_FATAL; - } sconv = zip->sconv; if (sconv == NULL && (zip->entry->zip_flags & ZIP_UTF8_NAME)) @@ -735,6 +957,12 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, } } zip_entry->uncompressed_size = zip_entry->compressed_size = 0; + + if (__archive_read_consume(a, linkname_length) < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Read error skipping symlink target name"); + return ARCHIVE_FATAL; + } } else if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END) || zip_entry->uncompressed_size > 0) { /* Set the size only if it's meaningful. */ @@ -748,14 +976,51 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, zip->end_of_entry = 1; /* Set up a more descriptive format name. */ - snprintf(zip->format_name, sizeof(zip->format_name), "ZIP %d.%d (%s)", + archive_string_sprintf(&zip->format_name, "ZIP %d.%d (%s)", version / 10, version % 10, compression_name(zip->entry->compression)); - a->archive.archive_format_name = zip->format_name; + a->archive.archive_format_name = zip->format_name.s; return (ret); } +static int +check_authentication_code(struct archive_read *a, const void *_p) +{ + struct zip *zip = (struct zip *)(a->format->data); + + /* Check authentication code. */ + if (zip->hctx_valid) { + const void *p; + uint8_t hmac[20]; + size_t hmac_len = 20; + int cmp; + + archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len); + if (_p == NULL) { + /* Read authentication code. */ + p = __archive_read_ahead(a, AUTH_CODE_SIZE, NULL); + if (p == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file data"); + return (ARCHIVE_FATAL); + } + } else { + p = _p; + } + cmp = memcmp(hmac, p, AUTH_CODE_SIZE); + __archive_read_consume(a, AUTH_CODE_SIZE); + if (cmp != 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "ZIP bad Authentication code"); + return (ARCHIVE_WARN); + } + } + return (ARCHIVE_OK); +} + /* * Read "uncompressed" data. There are three cases: * 1) We know the size of the data. This is always true for the @@ -787,6 +1052,7 @@ zip_read_data_none(struct archive_read *a, const void **_buff, struct zip *zip; const char *buff; ssize_t bytes_avail; + int r; (void)offset; /* UNUSED */ @@ -794,10 +1060,13 @@ zip_read_data_none(struct archive_read *a, const void **_buff, if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) { const char *p; + ssize_t grabbing_bytes = 24; + if (zip->hctx_valid) + grabbing_bytes += AUTH_CODE_SIZE; /* Grab at least 24 bytes. */ - buff = __archive_read_ahead(a, 24, &bytes_avail); - if (bytes_avail < 24) { + buff = __archive_read_ahead(a, grabbing_bytes, &bytes_avail); + if (bytes_avail < grabbing_bytes) { /* Zip archives have end-of-archive markers that are longer than this, so a failure to get at least 24 bytes really does indicate a truncated @@ -810,21 +1079,34 @@ zip_read_data_none(struct archive_read *a, const void **_buff, /* Check for a complete PK\007\010 signature, followed * by the correct 4-byte CRC. */ p = buff; + if (zip->hctx_valid) + p += AUTH_CODE_SIZE; if (p[0] == 'P' && p[1] == 'K' && p[2] == '\007' && p[3] == '\010' && (archive_le32dec(p + 4) == zip->entry_crc32 - || zip->ignore_crc32)) { + || zip->ignore_crc32 + || (zip->hctx_valid + && zip->entry->aes_extra.vendor == AES_VENDOR_AE_2))) { if (zip->entry->flags & LA_USED_ZIP64) { zip->entry->crc32 = archive_le32dec(p + 4); - zip->entry->compressed_size = archive_le64dec(p + 8); - zip->entry->uncompressed_size = archive_le64dec(p + 16); + zip->entry->compressed_size = + archive_le64dec(p + 8); + zip->entry->uncompressed_size = + archive_le64dec(p + 16); zip->unconsumed = 24; } else { zip->entry->crc32 = archive_le32dec(p + 4); - zip->entry->compressed_size = archive_le32dec(p + 8); - zip->entry->uncompressed_size = archive_le32dec(p + 12); + zip->entry->compressed_size = + archive_le32dec(p + 8); + zip->entry->uncompressed_size = + archive_le32dec(p + 12); zip->unconsumed = 16; } + if (zip->hctx_valid) { + r = check_authentication_code(a, buff); + if (r != ARCHIVE_OK) + return (r); + } zip->end_of_entry = 1; return (ARCHIVE_OK); } @@ -841,6 +1123,8 @@ zip_read_data_none(struct archive_read *a, const void **_buff, else if (p[3] == '\007') { p += 1; } else if (p[3] == '\010' && p[2] == '\007' && p[1] == 'K' && p[0] == 'P') { + if (zip->hctx_valid) + p -= AUTH_CODE_SIZE; break; } else { p += 4; } } @@ -848,6 +1132,11 @@ zip_read_data_none(struct archive_read *a, const void **_buff, } else { if (zip->entry_bytes_remaining == 0) { zip->end_of_entry = 1; + if (zip->hctx_valid) { + r = check_authentication_code(a, NULL); + if (r != ARCHIVE_OK) + return (r); + } return (ARCHIVE_OK); } /* Grab a bunch of bytes. */ @@ -861,6 +1150,26 @@ zip_read_data_none(struct archive_read *a, const void **_buff, if (bytes_avail > zip->entry_bytes_remaining) bytes_avail = (ssize_t)zip->entry_bytes_remaining; } + if (zip->tctx_valid || zip->cctx_valid) { + size_t dec_size = bytes_avail; + + if (dec_size > zip->decrypted_buffer_size) + dec_size = zip->decrypted_buffer_size; + if (zip->tctx_valid) { + trad_enc_decrypt_update(&zip->tctx, + (const uint8_t *)buff, dec_size, + zip->decrypted_buffer, dec_size); + } else { + size_t dsize = dec_size; + archive_hmac_sha1_update(&zip->hctx, + (const uint8_t *)buff, dec_size); + archive_decrypto_aes_ctr_update(&zip->cctx, + (const uint8_t *)buff, dec_size, + zip->decrypted_buffer, &dsize); + } + bytes_avail = dec_size; + buff = (const char *)zip->decrypted_buffer; + } *size = bytes_avail; zip->entry_bytes_remaining -= bytes_avail; zip->entry_uncompressed_bytes_read += bytes_avail; @@ -902,7 +1211,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, { struct zip *zip; ssize_t bytes_avail; - const void *compressed_buff; + const void *compressed_buff, *sp; int r; (void)offset; /* UNUSED */ @@ -931,7 +1240,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ - compressed_buff = __archive_read_ahead(a, 1, &bytes_avail); + compressed_buff = sp = __archive_read_ahead(a, 1, &bytes_avail); if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && bytes_avail > zip->entry_bytes_remaining) { bytes_avail = (ssize_t)zip->entry_bytes_remaining; @@ -942,6 +1251,51 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, return (ARCHIVE_FATAL); } + if (zip->tctx_valid || zip->cctx_valid) { + if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) { + size_t buff_remaining = zip->decrypted_buffer_size + - (zip->decrypted_ptr - zip->decrypted_buffer); + + if (buff_remaining > (size_t)bytes_avail) + buff_remaining = (size_t)bytes_avail; + + if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && + zip->entry_bytes_remaining > 0) { + if ((int64_t)(zip->decrypted_bytes_remaining + + buff_remaining) + > zip->entry_bytes_remaining) { + if (zip->entry_bytes_remaining < + (int64_t)zip->decrypted_bytes_remaining) + buff_remaining = 0; + else + buff_remaining = + (size_t)zip->entry_bytes_remaining + - zip->decrypted_bytes_remaining; + } + } + if (buff_remaining > 0) { + if (zip->tctx_valid) { + trad_enc_decrypt_update(&zip->tctx, + compressed_buff, buff_remaining, + zip->decrypted_ptr + + zip->decrypted_bytes_remaining, + buff_remaining); + } else { + size_t dsize = buff_remaining; + archive_decrypto_aes_ctr_update( + &zip->cctx, + compressed_buff, buff_remaining, + zip->decrypted_ptr + + zip->decrypted_bytes_remaining, + &dsize); + } + zip->decrypted_bytes_remaining += buff_remaining; + } + } + bytes_avail = zip->decrypted_bytes_remaining; + compressed_buff = (const char *)zip->decrypted_ptr; + } + /* * A bug in zlib.h: stream.next_in should be marked 'const' * but isn't (the library never alters data through the @@ -974,6 +1328,16 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, /* Consume as much as the compressor actually used. */ bytes_avail = zip->stream.total_in; + if (zip->tctx_valid || zip->cctx_valid) { + zip->decrypted_bytes_remaining -= bytes_avail; + if (zip->decrypted_bytes_remaining == 0) + zip->decrypted_ptr = zip->decrypted_buffer; + else + zip->decrypted_ptr += bytes_avail; + } + /* Calculate compressed data as much as we used.*/ + if (zip->hctx_valid) + archive_hmac_sha1_update(&zip->hctx, sp, bytes_avail); __archive_read_consume(a, bytes_avail); zip->entry_bytes_remaining -= bytes_avail; zip->entry_compressed_bytes_read += bytes_avail; @@ -982,6 +1346,12 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, zip->entry_uncompressed_bytes_read += zip->stream.total_out; *buff = zip->uncompressed_buffer; + if (zip->end_of_entry && zip->hctx_valid) { + r = check_authentication_code(a, NULL); + if (r != ARCHIVE_OK) + return (r); + } + if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { const char *p; @@ -1015,13 +1385,392 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, #endif static int +read_decryption_header(struct archive_read *a) +{ + struct zip *zip = (struct zip *)(a->format->data); + const char *p; + unsigned int remaining_size; + unsigned int ts; + + /* + * Read an initialization vector data field. + */ + p = __archive_read_ahead(a, 2, NULL); + if (p == NULL) + goto truncated; + ts = zip->iv_size; + zip->iv_size = archive_le16dec(p); + __archive_read_consume(a, 2); + if (ts < zip->iv_size) { + free(zip->iv); + zip->iv = NULL; + } + p = __archive_read_ahead(a, zip->iv_size, NULL); + if (p == NULL) + goto truncated; + if (zip->iv == NULL) { + zip->iv = malloc(zip->iv_size); + if (zip->iv == NULL) + goto nomem; + } + memcpy(zip->iv, p, zip->iv_size); + __archive_read_consume(a, zip->iv_size); + + /* + * Read a size of remaining decryption header field. + */ + p = __archive_read_ahead(a, 14, NULL); + if (p == NULL) + goto truncated; + remaining_size = archive_le32dec(p); + if (remaining_size < 16 || remaining_size > (1 << 18)) + goto corrupted; + + /* Check if format version is supported. */ + if (archive_le16dec(p+4) != 3) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported encryption format version: %u", + archive_le16dec(p+4)); + return (ARCHIVE_FAILED); + } + + /* + * Read an encryption algorithm field. + */ + zip->alg_id = archive_le16dec(p+6); + switch (zip->alg_id) { + case 0x6601:/* DES */ + case 0x6602:/* RC2 */ + case 0x6603:/* 3DES 168 */ + case 0x6609:/* 3DES 112 */ + case 0x660E:/* AES 128 */ + case 0x660F:/* AES 192 */ + case 0x6610:/* AES 256 */ + case 0x6702:/* RC2 (version >= 5.2) */ + case 0x6720:/* Blowfish */ + case 0x6721:/* Twofish */ + case 0x6801:/* RC4 */ + /* Suuported encryption algorithm. */ + break; + default: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unknown encryption algorithm: %u", zip->alg_id); + return (ARCHIVE_FAILED); + } + + /* + * Read a bit length field. + */ + zip->bit_len = archive_le16dec(p+8); + + /* + * Read a flags field. + */ + zip->flags = archive_le16dec(p+10); + switch (zip->flags & 0xf000) { + case 0x0001: /* Password is required to decrypt. */ + case 0x0002: /* Certificates only. */ + case 0x0003: /* Password or certificate required to decrypt. */ + break; + default: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unknown encryption flag: %u", zip->flags); + return (ARCHIVE_FAILED); + } + if ((zip->flags & 0xf000) == 0 || + (zip->flags & 0xf000) == 0x4000) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unknown encryption flag: %u", zip->flags); + return (ARCHIVE_FAILED); + } + + /* + * Read an encrypted random data field. + */ + ts = zip->erd_size; + zip->erd_size = archive_le16dec(p+12); + __archive_read_consume(a, 14); + if ((zip->erd_size & 0xf) != 0 || + (zip->erd_size + 16) > remaining_size || + (zip->erd_size + 16) < zip->erd_size) + goto corrupted; + + if (ts < zip->erd_size) { + free(zip->erd); + zip->erd = NULL; + } + p = __archive_read_ahead(a, zip->erd_size, NULL); + if (p == NULL) + goto truncated; + if (zip->erd == NULL) { + zip->erd = malloc(zip->erd_size); + if (zip->erd == NULL) + goto nomem; + } + memcpy(zip->erd, p, zip->erd_size); + __archive_read_consume(a, zip->erd_size); + + /* + * Read a reserved data field. + */ + p = __archive_read_ahead(a, 4, NULL); + if (p == NULL) + goto truncated; + /* Reserved data size should be zero. */ + if (archive_le32dec(p) != 0) + goto corrupted; + __archive_read_consume(a, 4); + + /* + * Read a password validation data field. + */ + p = __archive_read_ahead(a, 2, NULL); + if (p == NULL) + goto truncated; + ts = zip->v_size; + zip->v_size = archive_le16dec(p); + __archive_read_consume(a, 2); + if ((zip->v_size & 0x0f) != 0 || + (zip->erd_size + zip->v_size + 16) > remaining_size || + (zip->erd_size + zip->v_size + 16) < (zip->erd_size + zip->v_size)) + goto corrupted; + if (ts < zip->v_size) { + free(zip->v_data); + zip->v_data = NULL; + } + p = __archive_read_ahead(a, zip->v_size, NULL); + if (p == NULL) + goto truncated; + if (zip->v_data == NULL) { + zip->v_data = malloc(zip->v_size); + if (zip->v_data == NULL) + goto nomem; + } + memcpy(zip->v_data, p, zip->v_size); + __archive_read_consume(a, zip->v_size); + + p = __archive_read_ahead(a, 4, NULL); + if (p == NULL) + goto truncated; + zip->v_crc32 = archive_le32dec(p); + __archive_read_consume(a, 4); + + /*return (ARCHIVE_OK); + * This is not fully implemnted yet.*/ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Encrypted file is unsupported"); + return (ARCHIVE_FAILED); +truncated: + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file data"); + return (ARCHIVE_FATAL); +corrupted: + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Corrupted ZIP file data"); + return (ARCHIVE_FATAL); +nomem: + archive_set_error(&a->archive, ENOMEM, + "No memory for ZIP decryption"); + return (ARCHIVE_FATAL); +} + +static int +zip_alloc_decryption_buffer(struct archive_read *a) +{ + struct zip *zip = (struct zip *)(a->format->data); + size_t bs = 256 * 1024; + + if (zip->decrypted_buffer == NULL) { + zip->decrypted_buffer_size = bs; + zip->decrypted_buffer = malloc(bs); + if (zip->decrypted_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for ZIP decryption"); + return (ARCHIVE_FATAL); + } + } + zip->decrypted_ptr = zip->decrypted_buffer; + return (ARCHIVE_OK); +} + +static int +init_traditional_PKWARE_decryption(struct archive_read *a) +{ + struct zip *zip = (struct zip *)(a->format->data); + const void *p; + int retry; + int r; + + if (zip->tctx_valid) + return (ARCHIVE_OK); + + /* + Read the 12 bytes encryption header stored at + the start of the data area. + */ +#define ENC_HEADER_SIZE 12 + if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) + && zip->entry_bytes_remaining < ENC_HEADER_SIZE) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated Zip encrypted body: only %jd bytes available", + (intmax_t)zip->entry_bytes_remaining); + return (ARCHIVE_FATAL); + } + + p = __archive_read_ahead(a, ENC_HEADER_SIZE, NULL); + if (p == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file data"); + return (ARCHIVE_FATAL); + } + + for (retry = 0;; retry++) { + const char *passphrase; + uint8_t crcchk; + + passphrase = __archive_read_next_passphrase(a); + if (passphrase == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + (retry > 0)? + "Incorrect passphrase": + "Passphrase required for this entry"); + return (ARCHIVE_FAILED); + } + + /* + * Initialize ctx for Traditional PKWARE Decyption. + */ + r = trad_enc_init(&zip->tctx, passphrase, strlen(passphrase), + p, ENC_HEADER_SIZE, &crcchk); + if (r == 0 && crcchk == zip->entry->decdat) + break;/* The passphrase is OK. */ + if (retry > 10000) { + /* Avoid infinity loop. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Too many incorrect passphrases"); + return (ARCHIVE_FAILED); + } + } + + __archive_read_consume(a, ENC_HEADER_SIZE); + zip->tctx_valid = 1; + if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { + zip->entry_bytes_remaining -= ENC_HEADER_SIZE; + } + /*zip->entry_uncompressed_bytes_read += ENC_HEADER_SIZE;*/ + zip->entry_compressed_bytes_read += ENC_HEADER_SIZE; + zip->decrypted_bytes_remaining = 0; + + return (zip_alloc_decryption_buffer(a)); +#undef ENC_HEADER_SIZE +} + +static int +init_WinZip_AES_decryption(struct archive_read *a) +{ + struct zip *zip = (struct zip *)(a->format->data); + const void *p; + const uint8_t *pv; + size_t key_len, salt_len; + uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE]; + int retry; + int r; + + if (zip->cctx_valid || zip->hctx_valid) + return (ARCHIVE_OK); + + switch (zip->entry->aes_extra.strength) { + case 1: salt_len = 8; key_len = 16; break; + case 2: salt_len = 12; key_len = 24; break; + case 3: salt_len = 16; key_len = 32; break; + default: goto corrupted; + } + p = __archive_read_ahead(a, salt_len + 2, NULL); + if (p == NULL) + goto truncated; + + for (retry = 0;; retry++) { + const char *passphrase; + + passphrase = __archive_read_next_passphrase(a); + if (passphrase == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + (retry > 0)? + "Incorrect passphrase": + "Passphrase required for this entry"); + return (ARCHIVE_FAILED); + } + memset(derived_key, 0, sizeof(derived_key)); + r = archive_pbkdf2_sha1(passphrase, strlen(passphrase), + p, salt_len, 1000, derived_key, key_len * 2 + 2); + if (r != 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Decryption is unsupported due to lack of " + "crypto library"); + return (ARCHIVE_FAILED); + } + + /* Check password verification value. */ + pv = ((const uint8_t *)p) + salt_len; + if (derived_key[key_len * 2] == pv[0] && + derived_key[key_len * 2 + 1] == pv[1]) + break;/* The passphrase is OK. */ + if (retry > 10000) { + /* Avoid infinity loop. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Too many incorrect passphrases"); + return (ARCHIVE_FAILED); + } + } + + r = archive_decrypto_aes_ctr_init(&zip->cctx, derived_key, key_len); + if (r != 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Decryption is unsupported due to lack of crypto library"); + return (ARCHIVE_FAILED); + } + r = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len, key_len); + if (r != 0) { + archive_decrypto_aes_ctr_release(&zip->cctx); + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Failed to initialize HMAC-SHA1"); + return (ARCHIVE_FAILED); + } + zip->cctx_valid = zip->hctx_valid = 1; + __archive_read_consume(a, salt_len + 2); + zip->entry_bytes_remaining -= salt_len + 2 + AUTH_CODE_SIZE; + if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) + && zip->entry_bytes_remaining < 0) + goto corrupted; + zip->entry_compressed_bytes_read += salt_len + 2 + AUTH_CODE_SIZE; + zip->decrypted_bytes_remaining = 0; + + zip->entry->compression = zip->entry->aes_extra.compression; + return (zip_alloc_decryption_buffer(a)); + +truncated: + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file data"); + return (ARCHIVE_FATAL); +corrupted: + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Corrupted ZIP file data"); + return (ARCHIVE_FATAL); +} + +static int archive_read_format_zip_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { int r; struct zip *zip = (struct zip *)(a->format->data); - if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { + if (zip->has_encrypted_entries == + ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } @@ -1037,16 +1786,22 @@ archive_read_format_zip_read_data(struct archive_read *a, if (AE_IFREG != (zip->entry->mode & AE_IFMT)) return (ARCHIVE_EOF); - if (zip->entry->zip_flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)) { - zip->has_encrypted_entries = 1; - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Encrypted file is unsupported"); - return (ARCHIVE_FAILED); - } - __archive_read_consume(a, zip->unconsumed); zip->unconsumed = 0; + if (zip->init_decryption) { + zip->has_encrypted_entries = 1; + if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED) + r = read_decryption_header(a); + else if (zip->entry->compression == WINZIP_AES_ENCRYPTION) + r = init_WinZip_AES_decryption(a); + else + r = init_traditional_PKWARE_decryption(a); + if (r != ARCHIVE_OK) + return (r); + zip->init_decryption = 0; + } + switch(zip->entry->compression) { case 0: /* No compression. */ r = zip_read_data_none(a, buff, size, offset); @@ -1096,7 +1851,9 @@ archive_read_format_zip_read_data(struct archive_read *a, return (ARCHIVE_WARN); } /* Check computed CRC against header */ - if (zip->entry->crc32 != zip->entry_crc32 + if ((!zip->hctx_valid || + zip->entry->aes_extra.vendor != AES_VENDOR_AE_2) && + zip->entry->crc32 != zip->entry_crc32 && !zip->ignore_crc32) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP bad CRC: 0x%lx should be 0x%lx", @@ -1130,6 +1887,15 @@ archive_read_format_zip_cleanup(struct archive_read *a) zip_entry = next_zip_entry; } } + free(zip->decrypted_buffer); + if (zip->cctx_valid) + archive_decrypto_aes_ctr_release(&zip->cctx); + if (zip->hctx_valid) + archive_hmac_sha1_cleanup(&zip->hctx); + free(zip->iv); + free(zip->erd); + free(zip->v_data); + archive_string_free(&zip->format_name); free(zip); (a->format->data) = NULL; return (ARCHIVE_OK); @@ -1217,7 +1983,8 @@ static int archive_read_support_format_zip_capabilities_streamable(struct archive_read * a) { (void)a; /* UNUSED */ - return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); + return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | + ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); } static int @@ -1275,9 +2042,9 @@ archive_read_format_zip_streamable_read_header(struct archive_read *a, * 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) { + if (zip->has_encrypted_entries == + ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) zip->has_encrypted_entries = 0; - } /* Make sure we have a zip_entry structure to use. */ if (zip->zip_entries == NULL) { @@ -1291,6 +2058,13 @@ archive_read_format_zip_streamable_read_header(struct archive_read *a, zip->entry = zip->zip_entries; memset(zip->entry, 0, sizeof(struct zip_entry)); + if (zip->cctx_valid) + archive_decrypto_aes_ctr_release(&zip->cctx); + if (zip->hctx_valid) + archive_hmac_sha1_cleanup(&zip->hctx); + zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0; + __archive_read_reset_passphrase(a); + /* Search ahead for the next local file header. */ __archive_read_consume(a, zip->unconsumed); zip->unconsumed = 0; @@ -1363,12 +2137,28 @@ archive_read_format_zip_read_data_skip_streamable(struct archive_read *a) if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) || zip->entry->compressed_size > 0) { /* We know the compressed length, so we can just skip. */ - bytes_skipped = __archive_read_consume(a, zip->entry_bytes_remaining); + bytes_skipped = __archive_read_consume(a, + zip->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); return (ARCHIVE_OK); } + if (zip->init_decryption) { + int r; + + zip->has_encrypted_entries = 1; + if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED) + r = read_decryption_header(a); + else if (zip->entry->compression == WINZIP_AES_ENCRYPTION) + r = init_WinZip_AES_decryption(a); + else + r = init_traditional_PKWARE_decryption(a); + if (r != ARCHIVE_OK) + return (r); + zip->init_decryption = 0; + } + /* We're streaming and we don't know the length. */ /* If the body is compressed and we know the format, we can * find an exact end-of-entry by decompressing it. */ @@ -1406,9 +2196,11 @@ archive_read_format_zip_read_data_skip_streamable(struct archive_read *a) else if (p[3] == '\010' && p[2] == '\007' && p[1] == 'K' && p[0] == 'P') { if (zip->entry->flags & LA_USED_ZIP64) - __archive_read_consume(a, p - buff + 24); + __archive_read_consume(a, + p - buff + 24); else - __archive_read_consume(a, p - buff + 16); + __archive_read_consume(a, + p - buff + 16); return ARCHIVE_OK; } else { p += 4; } } @@ -1427,13 +2219,12 @@ archive_read_support_format_zip_streamable(struct archive *_a) archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_zip"); - zip = (struct zip *)malloc(sizeof(*zip)); + zip = (struct zip *)calloc(1, sizeof(*zip)); if (zip == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data"); return (ARCHIVE_FATAL); } - memset(zip, 0, sizeof(*zip)); /* Streamable reader doesn't support mac extensions. */ zip->process_mac_extensions = 0; @@ -1473,7 +2264,8 @@ static int archive_read_support_format_zip_capabilities_seekable(struct archive_read * a) { (void)a; /* UNUSED */ - return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); + return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | + ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); } /* @@ -1514,7 +2306,11 @@ read_eocd(struct zip *zip, const char *p, int64_t current_offset) return 32; } -static int +/* + * Examine Zip64 EOCD locator: If it's valid, store the information + * from it. + */ +static void read_zip64_eocd(struct archive_read *a, struct zip *zip, const char *p) { int64_t eocd64_offset; @@ -1524,37 +2320,35 @@ read_zip64_eocd(struct archive_read *a, struct zip *zip, const char *p) /* Central dir must be on first volume. */ if (archive_le32dec(p + 4) != 0) - return 0; + return; /* Must be only a single volume. */ if (archive_le32dec(p + 16) != 1) - return 0; + return; /* Find the Zip64 EOCD record. */ eocd64_offset = archive_le64dec(p + 8); if (__archive_read_seek(a, eocd64_offset, SEEK_SET) < 0) - return 0; + return; if ((p = __archive_read_ahead(a, 56, NULL)) == NULL) - return 0; + return; /* Make sure we can read all of it. */ eocd64_size = archive_le64dec(p + 4) + 12; if (eocd64_size < 56 || eocd64_size > 16384) - return 0; - if ((p = __archive_read_ahead(a, eocd64_size, NULL)) == NULL) - return 0; + return; + if ((p = __archive_read_ahead(a, (size_t)eocd64_size, NULL)) == NULL) + return; /* Sanity-check the EOCD64 */ if (archive_le32dec(p + 16) != 0) /* Must be disk #0 */ - return 0; + return; if (archive_le32dec(p + 20) != 0) /* CD must be on disk #0 */ - return 0; + return; /* CD can't be split. */ if (archive_le64dec(p + 24) != archive_le64dec(p + 32)) - return 0; + return; /* Save the central directory offset for later use. */ zip->central_directory_offset = archive_le64dec(p + 48); - - return 32; } static int @@ -1575,40 +2369,39 @@ archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid) return 0; /* Search last 16k of file for end-of-central-directory - * record (which starts with PK\005\006) or Zip64 locator - * record (which begins with PK\006\007) */ - tail = zipmin(1024 * 16, file_size); + * record (which starts with PK\005\006) */ + tail = (int)zipmin(1024 * 16, file_size); current_offset = __archive_read_seek(a, -tail, SEEK_END); if (current_offset < 0) return 0; if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL) return 0; - /* TODO: Rework this to search backwards from the end. We - * normally expect the EOCD record to be at the very end, so - * that should be significantly faster. Tricky part: Make - * sure we still prefer the Zip64 locator if it's present. */ - for (i = 0; i <= tail - 22;) { - switch (p[i + 3]) { - case 'P': i += 3; break; - case 'K': i += 2; break; - case 005: i += 1; break; - case 006: + /* Boyer-Moore search backwards from the end, since we want + * to match the last EOCD in the file (there can be more than + * one if there is an uncompressed Zip archive as a member + * within this Zip archive). */ + for (i = tail - 22; i > 0;) { + switch (p[i]) { + case 'P': if (memcmp(p + i, "PK\005\006", 4) == 0) { - int ret = read_eocd(zip, p + i, current_offset + i); - if (ret > 0) - return (ret); - } - i += 1; /* Look for PK\006\007 next */ - break; - case 007: - if (memcmp(p + i, "PK\006\007", 4) == 0) { - int ret = read_zip64_eocd(a, zip, p + i); - if (ret > 0) + int ret = read_eocd(zip, p + i, + current_offset + i); + if (ret > 0) { + /* Zip64 EOCD locator precedes + * regular EOCD if present. */ + if (i >= 20 + && memcmp(p + i - 20, "PK\006\007", 4) == 0) { + read_zip64_eocd(a, zip, p + i - 20); + } return (ret); + } } - i += 4; + i -= 4; break; - default: i += 4; break; + case 'K': i -= 1; break; + case 005: i -= 2; break; + case 006: i -= 3; break; + default: i -= 4; break; } } return 0; @@ -1760,7 +2553,8 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) } __archive_read_consume(a, i); } - correction = archive_filter_bytes(&a->archive, 0) - zip->central_directory_offset; + correction = archive_filter_bytes(&a->archive, 0) + - zip->central_directory_offset; __archive_rb_tree_init(&zip->tree, &rb_ops); __archive_rb_tree_init(&zip->tree_rsrc, &rb_rsrc_ops); @@ -1795,12 +2589,17 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) zip_entry->system = p[5]; /* version_required = archive_le16dec(p + 6); */ zip_entry->zip_flags = archive_le16dec(p + 8); - if (zip_entry->zip_flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)){ + if (zip_entry->zip_flags + & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)){ zip->has_encrypted_entries = 1; } zip_entry->compression = (char)archive_le16dec(p + 10); zip_entry->mtime = zip_time(p + 12); zip_entry->crc32 = archive_le32dec(p + 16); + if (zip_entry->zip_flags & ZIP_LENGTH_AT_END) + zip_entry->decdat = p[13]; + else + zip_entry->decdat = p[19]; zip_entry->compressed_size = archive_le32dec(p + 20); zip_entry->uncompressed_size = archive_le32dec(p + 24); filename_length = archive_le16dec(p + 28); @@ -1823,9 +2622,11 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) /* We're done with the regular data; get the filename and * extra data. */ __archive_read_consume(a, 46); - if ((p = __archive_read_ahead(a, filename_length + extra_length, NULL)) - == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + p = __archive_read_ahead(a, filename_length + extra_length, + NULL); + if (p == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return ARCHIVE_FATAL; } @@ -1850,26 +2651,30 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) * resource fork file to expose it. */ if (name[filename_length-1] != '/' && (r - name < 3 || r[0] != '.' || r[1] != '_')) { - __archive_rb_tree_insert_node(&zip->tree, - &zip_entry->node); + __archive_rb_tree_insert_node( + &zip->tree, &zip_entry->node); /* Expose its parent directories. */ - expose_parent_dirs(zip, name, filename_length); + expose_parent_dirs(zip, name, + filename_length); } else { /* This file is a resource fork file or * a directory. */ - archive_strncpy(&(zip_entry->rsrcname), name, - filename_length); - __archive_rb_tree_insert_node(&zip->tree_rsrc, - &zip_entry->node); + archive_strncpy(&(zip_entry->rsrcname), + name, filename_length); + __archive_rb_tree_insert_node( + &zip->tree_rsrc, &zip_entry->node); } } else { - /* Generate resource fork name to find its resource - * file at zip->tree_rsrc. */ - archive_strcpy(&(zip_entry->rsrcname), "__MACOSX/"); - archive_strncat(&(zip_entry->rsrcname), name, r - name); + /* Generate resource fork name to find its + * resource file at zip->tree_rsrc. */ + archive_strcpy(&(zip_entry->rsrcname), + "__MACOSX/"); + archive_strncat(&(zip_entry->rsrcname), + name, r - name); archive_strcat(&(zip_entry->rsrcname), "._"); archive_strncat(&(zip_entry->rsrcname), - name + (r - name), filename_length - (r - name)); + name + (r - name), + filename_length - (r - name)); /* Register an entry to RB tree to sort it by * file offset. */ __archive_rb_tree_insert_node(&zip->tree, @@ -2060,9 +2865,9 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a, * 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) { + if (zip->has_encrypted_entries == + ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) zip->has_encrypted_entries = 0; - } a->archive.archive_format = ARCHIVE_FORMAT_ZIP; if (a->archive.archive_format_name == NULL) @@ -2091,6 +2896,13 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a, else rsrc = NULL; + if (zip->cctx_valid) + archive_decrypto_aes_ctr_release(&zip->cctx); + if (zip->hctx_valid) + archive_hmac_sha1_cleanup(&zip->hctx); + zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0; + __archive_read_reset_passphrase(a); + /* File entries are sorted by the header offset, we should mostly * use __archive_read_consume to advance a read point to avoid redundant * data reading. */ @@ -2099,7 +2911,8 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a, __archive_read_consume(a, zip->entry->local_header_offset - offset); else if (offset != zip->entry->local_header_offset) { - __archive_read_seek(a, zip->entry->local_header_offset, SEEK_SET); + __archive_read_seek(a, zip->entry->local_header_offset, + SEEK_SET); } zip->unconsumed = 0; r = zip_read_local_file_header(a, entry, zip); @@ -2137,13 +2950,12 @@ archive_read_support_format_zip_seekable(struct archive *_a) archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_zip_seekable"); - zip = (struct zip *)malloc(sizeof(*zip)); + zip = (struct zip *)calloc(1, sizeof(*zip)); if (zip == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data"); return (ARCHIVE_FATAL); } - memset(zip, 0, sizeof(*zip)); #ifdef HAVE_COPYFILE_H /* Set this by default on Mac OS. */ |