Merge pull request #3160 from GeorgH93/fix_zipx_streaming_non_seekable
Fix zipx reading with ZIP_LENGTH_AT_END flag from non-seekable source
This commit is contained in:
4 files changed
+259
-101
No files matched your search
@@ -1121,6 +1121,7 @@ libarchive_test_EXTRA_DIST=\
|
||||
libarchive/test/test_read_format_zip_padded2.zip.uu \
|
||||
libarchive/test/test_read_format_zip_padded3.zip.uu \
|
||||
libarchive/test/test_read_format_zip_ppmd8.zipx.uu \
|
||||
libarchive/test/test_read_format_zip_ppmd8_aes256_streaming.zipx.uu \
|
||||
libarchive/test/test_read_format_zip_ppmd8_crash_1.zipx.uu \
|
||||
libarchive/test/test_read_format_zip_ppmd8_crash_2.zipx.uu \
|
||||
libarchive/test/test_read_format_zip_ppmd8_multi.zipx.uu \
|
||||
|
||||
@@ -482,6 +482,13 @@ zip_read_decrypt(struct zip *zip, const void *compressed_buff,
|
||||
{
|
||||
*sp = compressed_buff;
|
||||
|
||||
/* Safety check to prevent potential OOB reads if something went wrong
|
||||
* previously. We should not have a negative bytes_avail count here.
|
||||
* If we do, set them to zero so that reading the ZIP will fail later,
|
||||
* safely as corrupted instead of crashing. */
|
||||
if (bytes_avail < 0)
|
||||
bytes_avail = 0;
|
||||
|
||||
if (zip->tctx_valid || zip->cctx_valid) {
|
||||
if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) {
|
||||
size_t buff_remaining =
|
||||
@@ -1683,6 +1690,26 @@ consume_end_of_file_marker(struct archive_read *a, struct zip *zip)
|
||||
return;
|
||||
}
|
||||
|
||||
/* None of the exact patterns matched. If entry size was unknown
|
||||
* (ZIP_LENGTH_AT_END flag), before treating this as
|
||||
* corruption, check whether the next ZIP record follows the data
|
||||
* immediately: a length-at-end entry whose compression format has
|
||||
* its own end-of-stream marker (e.g. PPMd) may be written with no
|
||||
* data descriptor at all. In that case the byte counts we measured
|
||||
* during decompression are authoritative, so trust them and leave
|
||||
* the stream untouched. */
|
||||
if (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
|
||||
{
|
||||
const uint32_t sig = archive_le32dec(p);
|
||||
if (sig == 0x04034b50U /* Local file header */
|
||||
|| sig == 0x02014b50U /* Central directory record */
|
||||
|| sig == 0x06054b50U) /* End of central directory */ {
|
||||
zip->entry->compressed_size = compressed_actual;
|
||||
zip->entry->uncompressed_size = uncompressed_actual;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* If none of the above patterns gives us a full exact match,
|
||||
* then there's something definitely amiss. The fallback code
|
||||
* below will parse out some plausible values for error
|
||||
@@ -1986,7 +2013,13 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip)
|
||||
*/
|
||||
|
||||
/* Read magic1,magic2,lzma_params from the ZIPX stream. */
|
||||
if(zip->entry_bytes_remaining < 9) {
|
||||
/* When the compressed size is unknown (e.g. ZIP_LENGTH_AT_END read
|
||||
* from a non-seekable source), entry_bytes_remaining is 0 or negative
|
||||
* here. We can still attempt to read the 9-byte header; if the data
|
||||
* is truly truncated, the __archive_read_ahead calls below will catch
|
||||
* it. */
|
||||
if(zip->entry_bytes_remaining > 0
|
||||
&& zip->entry_bytes_remaining < 9) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Truncated lzma data");
|
||||
return (ARCHIVE_FATAL);
|
||||
@@ -2060,8 +2093,10 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip)
|
||||
|
||||
/* We've already consumed some bytes, so take this into account. */
|
||||
__archive_read_consume(a, 9);
|
||||
zip->entry_bytes_remaining -= 9;
|
||||
zip->entry_compressed_bytes_read += 9;
|
||||
if (zip->entry_bytes_remaining > 0) {
|
||||
zip->entry_bytes_remaining -= 9;
|
||||
}
|
||||
|
||||
zip->decompress_init = 1;
|
||||
return (ARCHIVE_OK);
|
||||
@@ -2076,7 +2111,7 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff,
|
||||
lzma_ret lz_ret;
|
||||
const void* compressed_buf;
|
||||
const void* sp;
|
||||
ssize_t bytes_avail, in_bytes, to_consume = 0;
|
||||
ssize_t bytes_avail, to_consume = 0;
|
||||
|
||||
(void) offset; /* UNUSED */
|
||||
|
||||
@@ -2088,19 +2123,21 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff,
|
||||
}
|
||||
|
||||
compressed_buf = 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;
|
||||
}
|
||||
if (bytes_avail < 0) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Truncated xz file body");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail);
|
||||
|
||||
zip_read_decrypt(zip, compressed_buf, in_bytes,
|
||||
&compressed_buf, &in_bytes, &sp);
|
||||
zip_read_decrypt(zip, compressed_buf, bytes_avail,
|
||||
&compressed_buf, &bytes_avail, &sp);
|
||||
|
||||
zip->zipx_lzma_stream.next_in = compressed_buf;
|
||||
zip->zipx_lzma_stream.avail_in = in_bytes;
|
||||
zip->zipx_lzma_stream.avail_in = bytes_avail;
|
||||
zip->zipx_lzma_stream.total_in = 0;
|
||||
zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;
|
||||
zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size;
|
||||
@@ -2127,8 +2164,10 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff,
|
||||
lzma_end(&zip->zipx_lzma_stream);
|
||||
zip->zipx_lzma_valid = 0;
|
||||
|
||||
if((int64_t) zip->zipx_lzma_stream.total_in !=
|
||||
zip->entry_bytes_remaining)
|
||||
/* This assertion is only possible if the size of the compressed data
|
||||
* stream is known -> !ZIP_LENGTH_AT_END */
|
||||
if((int64_t) zip->zipx_lzma_stream.total_in != zip->entry_bytes_remaining
|
||||
&& !(zip->entry->zip_flags & ZIP_LENGTH_AT_END))
|
||||
{
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
@@ -2172,7 +2211,7 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
|
||||
lzma_ret lz_ret;
|
||||
const void* compressed_buf;
|
||||
const void* sp;
|
||||
ssize_t bytes_avail, in_bytes, to_consume;
|
||||
ssize_t bytes_avail, to_consume;
|
||||
|
||||
(void) offset; /* UNUSED */
|
||||
|
||||
@@ -2192,29 +2231,38 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
|
||||
* data.
|
||||
*/
|
||||
compressed_buf = __archive_read_ahead(a, 1, &bytes_avail);
|
||||
if (zip->entry_bytes_remaining > 0
|
||||
&& bytes_avail > zip->entry_bytes_remaining) {
|
||||
bytes_avail = (ssize_t)zip->entry_bytes_remaining;
|
||||
}
|
||||
if (bytes_avail < 0) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Truncated lzma file body");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
/* Set decompressor parameters. */
|
||||
in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail);
|
||||
|
||||
zip_read_decrypt(zip, compressed_buf, in_bytes,
|
||||
&compressed_buf, &in_bytes, &sp);
|
||||
zip_read_decrypt(zip, compressed_buf, bytes_avail,
|
||||
&compressed_buf, &bytes_avail, &sp);
|
||||
|
||||
zip->zipx_lzma_stream.next_in = compressed_buf;
|
||||
zip->zipx_lzma_stream.avail_in = in_bytes;
|
||||
zip->zipx_lzma_stream.avail_in = bytes_avail;
|
||||
zip->zipx_lzma_stream.total_in = 0;
|
||||
zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;
|
||||
zip->zipx_lzma_stream.avail_out =
|
||||
/* These lzma_alone streams lack end of stream marker, so let's
|
||||
* make sure the unpacker won't try to unpack more than it's
|
||||
* supposed to. */
|
||||
(size_t)zipmin((int64_t) zip->uncompressed_buffer_size,
|
||||
zip->entry->uncompressed_size -
|
||||
zip->entry_uncompressed_bytes_read);
|
||||
/* These lzma_alone streams lack an end of stream marker in some
|
||||
* cases, so when the uncompressed size is known we cap avail_out to
|
||||
* make sure the unpacker won't try to unpack more than it's supposed
|
||||
* to. When the compressed size is unknown (entry_bytes_remaining <= 0,
|
||||
* e.g. ZIP_LENGTH_AT_END from a non-seekable source) we must use the
|
||||
* full buffer and rely on the LZMA stream end marker to detect the end
|
||||
* of the entry. */
|
||||
if (zip->entry_bytes_remaining <= 0) {
|
||||
zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size;
|
||||
} else {
|
||||
zip->zipx_lzma_stream.avail_out =
|
||||
(size_t)zipmin((int64_t) zip->uncompressed_buffer_size,
|
||||
zip->entry->uncompressed_size -
|
||||
zip->entry_uncompressed_bytes_read);
|
||||
}
|
||||
zip->zipx_lzma_stream.total_out = 0;
|
||||
|
||||
/* Perform the decompression. */
|
||||
@@ -2228,8 +2276,11 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
|
||||
/* This case is optional in lzma alone format. It can happen,
|
||||
* but most of the files don't have it. (GitHub #1257) */
|
||||
case LZMA_STREAM_END:
|
||||
/* This assertion is only possible if the size of the
|
||||
* compressed data stream is known. */
|
||||
if((int64_t) zip->zipx_lzma_stream.total_in !=
|
||||
zip->entry_bytes_remaining)
|
||||
zip->entry_bytes_remaining
|
||||
&& zip->entry_bytes_remaining > 0)
|
||||
{
|
||||
archive_set_error(&a->archive,
|
||||
ARCHIVE_ERRNO_MISC,
|
||||
@@ -2245,7 +2296,13 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
|
||||
|
||||
case LZMA_BUF_ERROR:
|
||||
if (zip->zipx_lzma_stream.avail_out == 0) {
|
||||
zip->end_of_entry = 1;
|
||||
/* The output buffer was filled exactly. When
|
||||
* the uncompressed size is known this means we
|
||||
* have decompressed all expected bytes. When
|
||||
* the size is unknown a full buffer just means
|
||||
* we need another iteration. */
|
||||
if (zip->entry_bytes_remaining > 0)
|
||||
zip->end_of_entry = 1;
|
||||
break;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
@@ -2259,14 +2316,16 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
|
||||
|
||||
/* Update pointers. */
|
||||
__archive_read_consume(a, to_consume);
|
||||
zip->entry_bytes_remaining -= to_consume;
|
||||
zip->entry_compressed_bytes_read += to_consume;
|
||||
zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out;
|
||||
|
||||
zip_read_decrypt_update(zip, to_consume, sp);
|
||||
|
||||
if(zip->entry_bytes_remaining == 0) {
|
||||
zip->end_of_entry = 1;
|
||||
if(zip->entry_bytes_remaining > 0) {
|
||||
zip->entry_bytes_remaining -= to_consume;
|
||||
if(zip->entry_bytes_remaining == 0) {
|
||||
zip->end_of_entry = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(zip->end_of_entry && zip->entry_bytes_remaining > 0) {
|
||||
@@ -2537,7 +2596,7 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff,
|
||||
size_t *size, int64_t *offset)
|
||||
{
|
||||
struct zip *zip = (struct zip *)(a->format->data);
|
||||
ssize_t bytes_avail = 0, in_bytes, to_consume;
|
||||
ssize_t bytes_avail = 0, to_consume;
|
||||
const void *compressed_buff;
|
||||
const void *sp;
|
||||
int r;
|
||||
@@ -2554,30 +2613,26 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff,
|
||||
|
||||
/* Fetch more compressed bytes. */
|
||||
compressed_buff = __archive_read_ahead(a, 1, &bytes_avail);
|
||||
if(bytes_avail < 0) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Truncated bzip2 file body");
|
||||
return (ARCHIVE_FATAL);
|
||||
if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
|
||||
&& bytes_avail > zip->entry_bytes_remaining) {
|
||||
bytes_avail = (ssize_t)zip->entry_bytes_remaining;
|
||||
}
|
||||
|
||||
in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail);
|
||||
if(in_bytes < 1) {
|
||||
if(bytes_avail < 1) {
|
||||
/* libbz2 doesn't complain when caller feeds avail_in == 0.
|
||||
* It will actually return success in this case, which is
|
||||
* undesirable. This is why we need to make this check
|
||||
* manually. */
|
||||
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Truncated bzip2 file body");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
zip_read_decrypt(zip, compressed_buff, in_bytes,
|
||||
&compressed_buff, &in_bytes, &sp);
|
||||
zip_read_decrypt(zip, compressed_buff, bytes_avail,
|
||||
&compressed_buff, &bytes_avail, &sp);
|
||||
|
||||
/* Setup buffer boundaries. */
|
||||
zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff;
|
||||
zip->bzstream.avail_in = (uint32_t)in_bytes;
|
||||
zip->bzstream.avail_in = (uint32_t)bytes_avail;
|
||||
zip->bzstream.total_in_hi32 = 0;
|
||||
zip->bzstream.total_in_lo32 = 0;
|
||||
zip->bzstream.next_out = (char*) zip->uncompressed_buffer;
|
||||
@@ -2692,7 +2747,7 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff,
|
||||
size_t *size, int64_t *offset)
|
||||
{
|
||||
struct zip *zip = (struct zip *)(a->format->data);
|
||||
ssize_t bytes_avail = 0, in_bytes, to_consume;
|
||||
ssize_t bytes_avail = 0, to_consume;
|
||||
const void *compressed_buff;
|
||||
const void *sp;
|
||||
int r;
|
||||
@@ -2712,14 +2767,11 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff,
|
||||
|
||||
/* Fetch more compressed bytes */
|
||||
compressed_buff = sp = __archive_read_ahead(a, 1, &bytes_avail);
|
||||
if(bytes_avail < 0) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Truncated zstd file body");
|
||||
return (ARCHIVE_FATAL);
|
||||
if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
|
||||
&& bytes_avail > zip->entry_bytes_remaining) {
|
||||
bytes_avail = (ssize_t)zip->entry_bytes_remaining;
|
||||
}
|
||||
|
||||
in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail);
|
||||
if(in_bytes < 1) {
|
||||
if(bytes_avail < 1) {
|
||||
/* zstd doesn't complain when caller feeds avail_in == 0.
|
||||
* It will actually return success in this case, which is
|
||||
* undesirable. This is why we need to make this check
|
||||
@@ -2729,12 +2781,12 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff,
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
zip_read_decrypt(zip, compressed_buff, in_bytes,
|
||||
&compressed_buff, &in_bytes, &sp);
|
||||
zip_read_decrypt(zip, compressed_buff, bytes_avail,
|
||||
&compressed_buff, &bytes_avail, &sp);
|
||||
|
||||
/* Setup buffer boundaries */
|
||||
in.src = compressed_buff;
|
||||
in.size = in_bytes;
|
||||
in.size = bytes_avail;
|
||||
in.pos = 0;
|
||||
out = (ZSTD_outBuffer) { zip->uncompressed_buffer, zip->uncompressed_buffer_size, 0 };
|
||||
|
||||
@@ -2746,6 +2798,10 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff,
|
||||
ZSTD_getErrorName(ret));
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
/* End of stream handling for zips with ZIP_LENGTH_AT_END flag */
|
||||
if (ret == 0 && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) {
|
||||
zip->end_of_entry = 1;
|
||||
}
|
||||
|
||||
/* Check end of the stream. */
|
||||
if (ret == 0) {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
begin 644 test_read_format_zip_ppmd8_aes256_streaming.zipx
|
||||
M4$L#!#\`"0!C`+:\T%P````````````````2``L`96YC<GEP=&5D7V9I;&4N
|
||||
M='AT`9D'``(`044#8@!0DU<O)`#\`^#5`+;![K',-BB=Y1/1$;^7ZX-Z>LH8
|
||||
MT/WJTW6S)$X63"#%E92J_MH4!H:N8BQR;/??F]NDA!],NJ07_%G39I2[AOO)
|
||||
MYVZ#VPVEDCRXSI217JP'Y5PF\GZUC'Z0\:2YWQ%@E`R4]($CP5)2^!8BFGSV
|
||||
M%7)I0I\5+7./\#\(K@D&[QFV49JSSRFT+'=%<EWYK'Z!?]H)\O_ZA#),OLL[
|
||||
MI9V1VN;\2<,&*+O)OUCF`M)<?BWXN=/A7G6*X\@[X-A5S`*RLV5V"JN@V]75
|
||||
ML`D296YS&G>/`U%6A=SSF`$C`-&F8B[77W4P?*7N.-*`4'TYPI,H?X)Q8^+2
|
||||
MP5KY,(.%H:+XGUL@GS'E21\)U^/?G5Y',@X&98JH@1Z=4FLZ";,H6@I%\%?Q
|
||||
MA6LR]Y$MP7@AJEH]5-QAOF:OX*)*7C/H23QFQ\--HU#7^K<J?3:?<SB9EMF8
|
||||
M;))S,7%<T"+;Y@I8-61DVLX;J/%+!5!+`0(_`S\``0!C`+:\T%P`````<`$`
|
||||
M`/\!```2`"\`````````(("D@0````!E;F-R>7!T961?9FEL92YT>'0*`"``
|
||||
M``````$`&`!TGW-<V/W<`>UY<US8_=P!=)]S7-C]W`$!F0<``@!!10-B`%!+
|
||||
4!08``````0`!`&\```"K`0``````
|
||||
`
|
||||
end
|
||||
@@ -37,9 +37,26 @@ static const char file_data[] =
|
||||
"discotheques provide jukeboxes. Crazy Frederick bought many very\n"
|
||||
"exquisite opal jewels. We promptly judged antique ivory buckles\n"
|
||||
"for the next prize. Sixty zippers were quickly picked from the bag.\n";
|
||||
static const size_t file_size = sizeof(file_data) - 1;
|
||||
|
||||
struct streaming_buffer {
|
||||
const char *data;
|
||||
la_ssize_t len;
|
||||
size_t pos;
|
||||
int served;
|
||||
};
|
||||
|
||||
static la_ssize_t read_streaming_buffer(struct archive *a, void *_client_data, const void **buffer) {
|
||||
(void)a;
|
||||
struct streaming_buffer *buff = _client_data;
|
||||
if (buff->served) return 0;
|
||||
*buffer = buff->data;
|
||||
buff->served = 1;
|
||||
return buff->len;
|
||||
}
|
||||
|
||||
static void
|
||||
validate_entry_read(struct archive *a, const char* msg)
|
||||
validate_entry_read(struct archive *a, const char* msg, const int streaming_reader)
|
||||
{
|
||||
struct archive_entry *ae;
|
||||
size_t total_read;
|
||||
@@ -48,8 +65,10 @@ validate_entry_read(struct archive *a, const char* msg)
|
||||
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
|
||||
assertEqualString("encrypted_file.txt", archive_entry_pathname(ae));
|
||||
assertEqualInt((int)(sizeof(file_data) - 1),
|
||||
archive_entry_size(ae));
|
||||
if (!streaming_reader)
|
||||
assertEqualInt((int)file_size, (int)archive_entry_size(ae));
|
||||
else
|
||||
assertEqualInt(archive_entry_size(ae), 0);
|
||||
assertEqualInt(1, archive_entry_is_data_encrypted(ae));
|
||||
|
||||
total_read = 0;
|
||||
@@ -65,18 +84,79 @@ validate_entry_read(struct archive *a, const char* msg)
|
||||
assertEqualMem(readbuf, file_data + total_read, (size_t)r);
|
||||
total_read += (size_t)r;
|
||||
}
|
||||
assertEqualInt((int)total_read, (int)(sizeof(file_data) - 1));
|
||||
assertEqualInt((int)total_read, (int)file_size);
|
||||
}
|
||||
|
||||
static void
|
||||
test_encrypted_zipx(const char *compression_name)
|
||||
test_encrypted_zipx_read_mem(char* buff, size_t used)
|
||||
{
|
||||
struct archive *a;
|
||||
struct archive_entry *ae;
|
||||
char readbuf[1024];
|
||||
|
||||
/* ---- Read back without password — should fail ---- */
|
||||
assert((a = archive_read_new()) != NULL);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
|
||||
assertEqualString("encrypted_file.txt", archive_entry_pathname(ae));
|
||||
assertEqualInt(1, archive_entry_is_data_encrypted(ae));
|
||||
assertEqualInt(0, archive_entry_is_metadata_encrypted(ae));
|
||||
assertEqualInt(ARCHIVE_FAILED, archive_read_data(a, readbuf, sizeof(readbuf)));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
|
||||
|
||||
/* ---- Read back with correct password ---- */
|
||||
assert((a = archive_read_new()) != NULL);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_add_passphrase(a, password));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used));
|
||||
|
||||
validate_entry_read(a, "archive_read_data returned %d: %s", 0);
|
||||
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
|
||||
|
||||
/* ---- Seeking reader with correct password ---- */
|
||||
assert((a = archive_read_new()) != NULL);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_add_passphrase(a, password));
|
||||
assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, buff, used, 7));
|
||||
|
||||
validate_entry_read(a, "seek: archive_read_data returned %d: %s", 0);
|
||||
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
|
||||
}
|
||||
|
||||
static void
|
||||
test_encrypted_zipx_read_callback(const char* buff, const size_t used)
|
||||
{
|
||||
struct archive *a;
|
||||
struct streaming_buffer stream_buffer = { buff, used, 0, 0 };
|
||||
|
||||
/* ---- Read back with correct password ---- */
|
||||
assert((a = archive_read_new()) != NULL);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_add_passphrase(a, password));
|
||||
/* NOTE: archive_read_open2 with read callback only -> NON-seekable. */
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_open2(a, &stream_buffer, NULL, read_streaming_buffer, NULL, NULL));
|
||||
validate_entry_read(a, "archive_read_data returned %d: %s", 1);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
|
||||
}
|
||||
|
||||
static void
|
||||
test_encrypted_zipx(const char *compression_name, const int streaming_entry)
|
||||
{
|
||||
struct archive *a;
|
||||
struct archive_entry *ae;
|
||||
size_t used;
|
||||
size_t buffsize = 1000000;
|
||||
char *buff;
|
||||
char readbuf[1024];
|
||||
|
||||
buff = malloc(buffsize);
|
||||
assert(buff != NULL);
|
||||
@@ -109,74 +189,79 @@ test_encrypted_zipx(const char *compression_name)
|
||||
archive_entry_set_mtime(ae, 1, 0);
|
||||
archive_entry_copy_pathname(ae, "encrypted_file.txt");
|
||||
archive_entry_set_mode(ae, AE_IFREG | 0644);
|
||||
archive_entry_set_size(ae, sizeof(file_data) - 1);
|
||||
if (!streaming_entry)
|
||||
archive_entry_set_size(ae, file_size);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
|
||||
assertEqualInt(sizeof(file_data) - 1, archive_write_data(a, file_data, sizeof(file_data) - 1));
|
||||
assertEqualInt(file_size, archive_write_data(a, file_data, file_size));
|
||||
archive_entry_free(ae);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
|
||||
assertEqualInt(ARCHIVE_OK, archive_write_free(a));
|
||||
|
||||
/* ---- Read back without password — should fail ---- */
|
||||
assert((a = archive_read_new()) != NULL);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
|
||||
assertEqualString("encrypted_file.txt", archive_entry_pathname(ae));
|
||||
assertEqualInt(1, archive_entry_is_data_encrypted(ae));
|
||||
assertEqualInt(0, archive_entry_is_metadata_encrypted(ae));
|
||||
assertEqualInt(ARCHIVE_FAILED, archive_read_data(a, readbuf, sizeof(readbuf)));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
|
||||
|
||||
/* ---- Read back with correct password ---- */
|
||||
assert((a = archive_read_new()) != NULL);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_add_passphrase(a, password));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used));
|
||||
|
||||
validate_entry_read(a, "archive_read_data returned %d: %s");
|
||||
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
|
||||
|
||||
/* ---- Seeking reader with correct password ---- */
|
||||
assert((a = archive_read_new()) != NULL);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_add_passphrase(a, password));
|
||||
assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, buff, used, 7));
|
||||
|
||||
validate_entry_read(a, "seek: archive_read_data returned %d: %s");
|
||||
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
|
||||
test_encrypted_zipx_read_mem(buff, used);
|
||||
test_encrypted_zipx_read_callback(buff, used);
|
||||
|
||||
free(buff);
|
||||
}
|
||||
|
||||
DEFINE_TEST(test_read_format_zip_deflate_encrypted)
|
||||
{
|
||||
test_encrypted_zipx("zip:compression=deflate");
|
||||
test_encrypted_zipx("zip:compression=deflate", 0);
|
||||
}
|
||||
|
||||
DEFINE_TEST(test_read_format_zip_zipx_bzip2_encrypted)
|
||||
{
|
||||
test_encrypted_zipx("zip:compression=bzip2");
|
||||
test_encrypted_zipx("zip:compression=bzip2", 0);
|
||||
}
|
||||
|
||||
DEFINE_TEST(test_read_format_zip_zipx_lzma_encrypted)
|
||||
{
|
||||
test_encrypted_zipx("zip:compression=lzma");
|
||||
test_encrypted_zipx("zip:compression=lzma", 0);
|
||||
}
|
||||
|
||||
DEFINE_TEST(test_read_format_zip_zipx_xz_encrypted)
|
||||
{
|
||||
test_encrypted_zipx("zip:compression=xz");
|
||||
test_encrypted_zipx("zip:compression=xz", 0);
|
||||
}
|
||||
|
||||
DEFINE_TEST(test_read_format_zip_zipx_zstd_encrypted)
|
||||
{
|
||||
test_encrypted_zipx("zip:compression=zstd");
|
||||
test_encrypted_zipx("zip:compression=zstd", 0);
|
||||
}
|
||||
|
||||
DEFINE_TEST(test_read_format_zip_deflate_encrypted_streaming)
|
||||
{
|
||||
test_encrypted_zipx("zip:compression=deflate", 1);
|
||||
}
|
||||
|
||||
DEFINE_TEST(test_read_format_zip_zipx_bzip2_encrypted_streaming)
|
||||
{
|
||||
test_encrypted_zipx("zip:compression=bzip2", 1);
|
||||
}
|
||||
|
||||
DEFINE_TEST(test_read_format_zip_zipx_lzma_encrypted_streaming)
|
||||
{
|
||||
test_encrypted_zipx("zip:compression=lzma", 1);
|
||||
}
|
||||
|
||||
DEFINE_TEST(test_read_format_zip_zipx_xz_encrypted_streaming)
|
||||
{
|
||||
test_encrypted_zipx("zip:compression=xz", 1);
|
||||
}
|
||||
|
||||
DEFINE_TEST(test_read_format_zip_zipx_zstd_encrypted_streaming)
|
||||
{
|
||||
test_encrypted_zipx("zip:compression=zstd", 1);
|
||||
}
|
||||
|
||||
DEFINE_TEST(test_read_format_zip_ppmd8_aes256_streaming)
|
||||
{
|
||||
const char *refname = "test_read_format_zip_ppmd8_aes256_streaming.zipx";
|
||||
size_t used;
|
||||
char buff[600];
|
||||
extract_reference_file(refname);
|
||||
FILE *f = fopen(refname, "rb");
|
||||
used = fread(buff, 1, sizeof(buff), f);
|
||||
fclose(f);
|
||||
test_encrypted_zipx_read_mem(buff, used);
|
||||
test_encrypted_zipx_read_callback(buff, used);
|
||||
}
|
||||
Reference in new issue
Block a user