in windows/CodePush/miniz/miniz.c [5179:5383]
mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags)
{
mz_zip_archive_file_stat file_stat;
mz_zip_internal_state *pState;
const mz_uint8 *pCentral_dir_header;
mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE;
mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE;
mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];
mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
mz_uint64 local_header_ofs = 0;
mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32;
mz_uint64 local_header_comp_size, local_header_uncomp_size;
mz_uint32 uncomp_crc32 = MZ_CRC32_INIT;
mz_bool has_data_descriptor;
mz_uint32 local_header_bit_flags;
mz_zip_array file_data_array;
mz_zip_array_init(&file_data_array, 1);
if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead))
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
if (file_index > pZip->m_total_files)
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
pState = pZip->m_pState;
pCentral_dir_header = mz_zip_get_cdh(pZip, file_index);
if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir))
return MZ_FALSE;
/* A directory or zero length file */
if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size))
return MZ_TRUE;
/* Encryption and patch files are not supported. */
if (file_stat.m_is_encrypted)
return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
/* This function only supports stored and deflate. */
if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);
if (!file_stat.m_is_supported)
return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);
/* Read and parse the local directory entry. */
local_header_ofs = file_stat.m_local_header_ofs;
if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS);
local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS);
local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS);
local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS);
local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS);
has_data_descriptor = (local_header_bit_flags & 8) != 0;
if (local_header_filename_len != strlen(file_stat.m_filename))
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size)
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE))
return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
if (local_header_filename_len)
{
if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len)
{
mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
goto handle_failure;
}
/* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */
if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0)
{
mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
goto handle_failure;
}
}
if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX)))
{
mz_uint32 extra_size_remaining = local_header_extra_len;
const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p;
if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len)
{
mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
goto handle_failure;
}
do
{
mz_uint32 field_id, field_data_size, field_total_size;
if (extra_size_remaining < (sizeof(mz_uint16) * 2))
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
field_id = MZ_READ_LE16(pExtra_data);
field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
field_total_size = field_data_size + sizeof(mz_uint16) * 2;
if (field_total_size > extra_size_remaining)
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
{
const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32);
if (field_data_size < sizeof(mz_uint64) * 2)
{
mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
goto handle_failure;
}
local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data);
local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64));
found_zip64_ext_data_in_ldir = MZ_TRUE;
break;
}
pExtra_data += field_total_size;
extra_size_remaining -= field_total_size;
} while (extra_size_remaining);
}
/* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */
/* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */
if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32))
{
mz_uint8 descriptor_buf[32];
mz_bool has_id;
const mz_uint8 *pSrc;
mz_uint32 file_crc32;
mz_uint64 comp_size = 0, uncomp_size = 0;
mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4;
if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s))
{
mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
goto handle_failure;
}
has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID);
pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf;
file_crc32 = MZ_READ_LE32(pSrc);
if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir))
{
comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32));
uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64));
}
else
{
comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32));
uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32));
}
if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size))
{
mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
goto handle_failure;
}
}
else
{
if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size))
{
mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
goto handle_failure;
}
}
mz_zip_array_clear(pZip, &file_data_array);
if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0)
{
if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0))
return MZ_FALSE;
/* 1 more check to be sure, although the extract checks too. */
if (uncomp_crc32 != file_stat.m_crc32)
{
mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
return MZ_FALSE;
}
}
return MZ_TRUE;
handle_failure:
mz_zip_array_clear(pZip, &file_data_array);
return MZ_FALSE;
}