diff options
Diffstat (limited to 'src/Common/libzip/zip_dirent.c')
-rw-r--r-- | src/Common/libzip/zip_dirent.c | 218 |
1 files changed, 205 insertions, 13 deletions
diff --git a/src/Common/libzip/zip_dirent.c b/src/Common/libzip/zip_dirent.c index 74f89880..df38afd9 100644 --- a/src/Common/libzip/zip_dirent.c +++ b/src/Common/libzip/zip_dirent.c @@ -44,6 +44,7 @@ static time_t _zip_d2u_time(zip_uint16_t, zip_uint16_t); static zip_string_t *_zip_dirent_process_ef_utf_8(const zip_dirent_t *de, zip_uint16_t id, zip_string_t *str); static zip_extra_field_t *_zip_ef_utf8(zip_uint16_t, zip_string_t *, zip_error_t *); +static bool _zip_dirent_process_winzip_aes(zip_dirent_t *de, zip_error_t *error); void @@ -66,29 +67,58 @@ zip_cdir_t * _zip_cdir_new(zip_uint64_t nentry, zip_error_t *error) { zip_cdir_t *cd; - zip_uint64_t i; if ((cd=(zip_cdir_t *)malloc(sizeof(*cd))) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); return NULL; } - if (nentry == 0) - cd->entry = NULL; - else if ((nentry > SIZE_MAX/sizeof(*(cd->entry))) || (cd->entry=(zip_entry_t *)malloc(sizeof(*(cd->entry))*(size_t)nentry)) == NULL) { - zip_error_set(error, ZIP_ER_MEMORY, 0); - free(cd); + cd->entry = NULL; + cd->nentry = cd->nentry_alloc = 0; + cd->size = cd->offset = 0; + cd->comment = NULL; + cd->is_zip64 = false; + + if (!_zip_cdir_grow(cd, nentry, error)) { + _zip_cdir_free(cd); return NULL; } - for (i=0; i<nentry; i++) + return cd; +} + + +bool +_zip_cdir_grow(zip_cdir_t *cd, zip_uint64_t additional_entries, zip_error_t *error) +{ + zip_uint64_t i, new_alloc; + zip_entry_t *new_entry; + + if (additional_entries == 0) { + return true; + } + + new_alloc = cd->nentry_alloc + additional_entries; + + if (new_alloc < additional_entries || new_alloc > SIZE_MAX/sizeof(*(cd->entry))) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return false; + } + + if ((new_entry = (zip_entry_t *)realloc(cd->entry, sizeof(*(cd->entry))*(size_t)new_alloc)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return false; + } + + cd->entry = new_entry; + + for (i = cd->nentry; i < new_alloc; i++) { _zip_entry_init(cd->entry+i); + } - cd->nentry = cd->nentry_alloc = nentry; - cd->size = cd->offset = 0; - cd->comment = NULL; + cd->nentry = cd->nentry_alloc = new_alloc; - return cd; + return true; } @@ -222,6 +252,13 @@ _zip_dirent_finalize(zip_dirent_t *zde) _zip_string_free(zde->comment); zde->comment = NULL; } + if (!zde->cloned || zde->changed & ZIP_DIRENT_PASSWORD) { + if (zde->password) { + _zip_crypto_clear(zde->password, strlen(zde->password)); + } + free(zde->password); + zde->password = NULL; + } } @@ -243,6 +280,7 @@ _zip_dirent_init(zip_dirent_t *de) de->local_extra_fields_read = 0; de->cloned = 0; + de->crc_valid = true; de->version_madeby = 20 | (ZIP_OPSYS_DEFAULT << 8); de->version_needed = 20; /* 2.0 */ de->bitflags = 0; @@ -258,6 +296,8 @@ _zip_dirent_init(zip_dirent_t *de) de->int_attrib = 0; de->ext_attrib = ZIP_EXT_ATTRIB_DEFAULT; de->offset = 0; + de->encryption_method = ZIP_EM_NONE; + de->password = NULL; } @@ -372,6 +412,19 @@ _zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, boo return -1; } + if (zde->bitflags & ZIP_GPBF_ENCRYPTED) { + if (zde->bitflags & ZIP_GPBF_STRONG_ENCRYPTION) { + /* TODO */ + zde->encryption_method = ZIP_EM_UNKNOWN; + } + else { + zde->encryption_method = ZIP_EM_TRAD_PKWARE; + } + } + else { + zde->encryption_method = ZIP_EM_NONE; + } + zde->filename = NULL; zde->extra_fields = NULL; zde->comment = NULL; @@ -524,6 +577,13 @@ _zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, boo return -1; } + if (!_zip_dirent_process_winzip_aes(zde, error)) { + if (!from_buffer) { + _zip_buffer_free(buffer); + } + return -1; + } + zde->extra_fields = _zip_ef_remove_internal(zde->extra_fields); return (zip_int64_t)(size + variable_size); @@ -566,6 +626,90 @@ _zip_dirent_process_ef_utf_8(const zip_dirent_t *de, zip_uint16_t id, zip_string } +static bool +_zip_dirent_process_winzip_aes(zip_dirent_t *de, zip_error_t *error) +{ + zip_uint16_t ef_len; + zip_buffer_t *buffer; + const zip_uint8_t *ef; + bool crc_valid; + zip_uint16_t enc_method; + + + if (de->comp_method != ZIP_CM_WINZIP_AES) { + return true; + } + + ef = _zip_ef_get_by_id(de->extra_fields, &ef_len, ZIP_EF_WINZIP_AES, 0, ZIP_EF_BOTH, NULL); + + if (ef == NULL || ef_len < 7) { + zip_error_set(error, ZIP_ER_INCONS, 0); + return false; + } + + if ((buffer = _zip_buffer_new((zip_uint8_t *)ef, ef_len)) == NULL) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + return false; + } + + /* version */ + + crc_valid = true; + switch (_zip_buffer_get_16(buffer)) { + case 1: + break; + + case 2: + if (de->uncomp_size < 20 /* TODO: constant */) { + crc_valid = false; + } + break; + + default: + zip_error_set(error, ZIP_ER_ENCRNOTSUPP, 0); + _zip_buffer_free(buffer); + return false; + } + + /* vendor */ + if (memcmp(_zip_buffer_get(buffer, 2), "AE", 2) != 0) { + zip_error_set(error, ZIP_ER_ENCRNOTSUPP, 0); + _zip_buffer_free(buffer); + return false; + } + + /* mode */ + switch (_zip_buffer_get_8(buffer)) { + case 1: + enc_method = ZIP_EM_AES_128; + break; + case 2: + enc_method = ZIP_EM_AES_192; + break; + case 3: + enc_method = ZIP_EM_AES_256; + break; + default: + zip_error_set(error, ZIP_ER_ENCRNOTSUPP, 0); + _zip_buffer_free(buffer); + return false; + } + + if (ef_len != 7) { + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_buffer_free(buffer); + return false; + } + + de->crc_valid = crc_valid; + de->encryption_method = enc_method; + de->comp_method = _zip_buffer_get_16(buffer); + + _zip_buffer_free(buffer); + return true; +} + + zip_int32_t _zip_dirent_size(zip_source_t *src, zip_uint16_t flags, zip_error_t *error) { @@ -621,6 +765,7 @@ _zip_dirent_write(zip_t *za, zip_dirent_t *de, zip_flags_t flags) zip_uint32_t ef_total_size; bool is_zip64; bool is_really_zip64; + bool is_winzip_aes; zip_uint8_t buf[CDENTRYSIZE]; zip_buffer_t *buffer; @@ -651,8 +796,16 @@ _zip_dirent_write(zip_t *za, zip_dirent_t *de, zip_flags_t flags) } } + if (de->encryption_method == ZIP_EM_NONE) { + de->bitflags &= ~ZIP_GPBF_ENCRYPTED; + } + else { + de->bitflags |= ZIP_GPBF_ENCRYPTED; + } + is_really_zip64 = _zip_dirent_needs_zip64(de, flags); is_zip64 = (flags & (ZIP_FL_LOCAL|ZIP_FL_FORCE_ZIP64)) == (ZIP_FL_LOCAL|ZIP_FL_FORCE_ZIP64) || is_really_zip64; + is_winzip_aes = de->encryption_method == ZIP_EM_AES_128 || de->encryption_method == ZIP_EM_AES_192 || de->encryption_method == ZIP_EM_AES_256; if (is_zip64) { zip_uint8_t ef_zip64[EFZIP64SIZE]; @@ -696,6 +849,35 @@ _zip_dirent_write(zip_t *za, zip_dirent_t *de, zip_flags_t flags) ef = ef64; } + if (is_winzip_aes) { + zip_uint8_t data[EF_WINZIP_AES_SIZE]; + zip_buffer_t *ef_buffer = _zip_buffer_new(data, sizeof(data)); + zip_extra_field_t *ef_winzip; + + if (ef_buffer == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + _zip_ef_free(ef); + return -1; + } + + _zip_buffer_put_16(ef_buffer, 2); + _zip_buffer_put(ef_buffer, "AE", 2); + _zip_buffer_put_8(ef_buffer, (de->encryption_method & 0xff)); + _zip_buffer_put_16(ef_buffer, (zip_uint16_t)de->comp_method); + + if (!_zip_buffer_ok(ef_buffer)) { + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + _zip_buffer_free(ef_buffer); + _zip_ef_free(ef); + return -1; + } + + ef_winzip = _zip_ef_new(ZIP_EF_WINZIP_AES, EF_WINZIP_AES_SIZE, data, ZIP_EF_BOTH); + _zip_buffer_free(ef_buffer); + ef_winzip->next = ef; + ef = ef_winzip; + } + if ((buffer = _zip_buffer_new(buf, sizeof(buf))) == NULL) { zip_error_set(&za->error, ZIP_ER_MEMORY, 0); _zip_ef_free(ef); @@ -709,13 +891,23 @@ _zip_dirent_write(zip_t *za, zip_dirent_t *de, zip_flags_t flags) } _zip_buffer_put_16(buffer, (zip_uint16_t)(is_really_zip64 ? 45 : de->version_needed)); _zip_buffer_put_16(buffer, de->bitflags&0xfff9); /* clear compression method specific flags */ - _zip_buffer_put_16(buffer, (zip_uint16_t)de->comp_method); + if (is_winzip_aes) { + _zip_buffer_put_16(buffer, ZIP_CM_WINZIP_AES); + } + else { + _zip_buffer_put_16(buffer, (zip_uint16_t)de->comp_method); + } _zip_u2d_time(de->last_mod, &dostime, &dosdate); _zip_buffer_put_16(buffer, dostime); _zip_buffer_put_16(buffer, dosdate); - _zip_buffer_put_32(buffer, de->crc); + if (is_winzip_aes && de->uncomp_size < 20) { + _zip_buffer_put_32(buffer, 0); + } + else { + _zip_buffer_put_32(buffer, de->crc); + } if (((flags & ZIP_FL_LOCAL) == ZIP_FL_LOCAL) && ((de->comp_size >= ZIP_UINT32_MAX) || (de->uncomp_size >= ZIP_UINT32_MAX))) { /* In local headers, if a ZIP64 EF is written, it MUST contain |