zip_uint64_t _zip_buffer_read(zip_buffer_t *buffer, zip_uint8_t *data, zip_uint64_t length) { if (_zip_buffer_left(buffer) < length) { length = _zip_buffer_left(buffer); } memcpy(data, _zip_buffer_get(buffer, length), length); return length; }
static zip_cdir_t * _zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) { zip_cdir_t *cd; zip_uint64_t i, nentry, size, offset, eocd_offset; if (_zip_buffer_left(buffer) < EOCDLEN) { zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } eocd_offset = _zip_buffer_offset(buffer); _zip_buffer_get(buffer, 4); /* magic already verified */ if (_zip_buffer_get_32(buffer) != 0) { zip_error_set(error, ZIP_ER_MULTIDISK, 0); return NULL; } /* number of cdir-entries on this disk */ i = _zip_buffer_get_16(buffer); /* number of cdir-entries */ nentry = _zip_buffer_get_16(buffer); if (nentry != i) { zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } size = _zip_buffer_get_32(buffer); offset = _zip_buffer_get_32(buffer); if (offset+size < offset) { zip_error_set(error, ZIP_ER_SEEK, EFBIG); return NULL; } if (offset+size > buf_offset + eocd_offset) { /* cdir spans past EOCD record */ zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } if ((flags & ZIP_CHECKCONS) && offset+size != buf_offset + eocd_offset) { zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } if ((cd=_zip_cdir_new(nentry, error)) == NULL) return NULL; cd->is_zip64 = false; cd->size = size; cd->offset = offset; return cd; }
bool _zip_ef_parse(const zip_uint8_t *data, zip_uint16_t len, zip_flags_t flags, zip_extra_field_t **ef_head_p, zip_error_t *error) { zip_buffer_t *buffer; zip_extra_field_t *ef, *ef2, *ef_head; if ((buffer = _zip_buffer_new((zip_uint8_t *)data, len)) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); return false; } ef_head = ef = NULL; while (_zip_buffer_ok(buffer) && _zip_buffer_left(buffer) >= 4) { zip_uint16_t fid, flen; zip_uint8_t *ef_data; fid = _zip_buffer_get_16(buffer); flen = _zip_buffer_get_16(buffer); ef_data = _zip_buffer_get(buffer, flen); if (ef_data == NULL) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(buffer); _zip_ef_free(ef_head); return false; } if ((ef2=_zip_ef_new(fid, flen, ef_data, flags)) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); _zip_buffer_free(buffer); _zip_ef_free(ef_head); return false; } if (ef_head) { ef->next = ef2; ef = ef2; } else ef_head = ef = ef2; } if (!_zip_buffer_eof(buffer)) { /* Android APK files align stored file data with padding in extra fields; ignore. */ /* see https://android.googlesource.com/platform/build/+/master/tools/zipalign/ZipAlign.cpp */ size_t glen = _zip_buffer_left(buffer); zip_uint8_t *garbage; garbage = _zip_buffer_get(buffer, glen); if (glen >= 4 || garbage == NULL || memcmp(garbage, "\0\0\0", glen) != 0) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(buffer); _zip_ef_free(ef_head); return false; } } _zip_buffer_free(buffer); if (ef_head_p) { *ef_head_p = ef_head; } else { _zip_ef_free(ef_head); } return true; }
static zip_cdir_t * _zip_find_central_dir(zip_t *za, zip_uint64_t len) { zip_cdir_t *cdir, *cdirnew; zip_uint8_t *match; zip_int64_t buf_offset; zip_uint64_t buflen; zip_int64_t a; zip_int64_t best; zip_error_t error; zip_buffer_t *buffer; if (len < EOCDLEN) { zip_error_set(&za->error, ZIP_ER_NOZIP, 0); return NULL; } buflen = (len < CDBUFSIZE ? len : CDBUFSIZE); if (zip_source_seek(za->src, -(zip_int64_t)buflen, SEEK_END) < 0) { zip_error_t *src_error = zip_source_error(za->src); if (zip_error_code_zip(src_error) != ZIP_ER_SEEK || zip_error_code_system(src_error) != EFBIG) { /* seek before start of file on my machine */ _zip_error_copy(&za->error, src_error); return NULL; } } if ((buf_offset = zip_source_tell(za->src)) < 0) { _zip_error_set_from_source(&za->error, za->src); return NULL; } if ((buffer = _zip_buffer_new_from_source(za->src, buflen, NULL, &za->error)) == NULL) { return NULL; } best = -1; cdir = NULL; if (buflen >= CDBUFSIZE) { /* EOCD64 locator is before EOCD, so leave place for it */ _zip_buffer_set_offset(buffer, EOCD64LOCLEN); } zip_error_set(&error, ZIP_ER_NOZIP, 0); match = _zip_buffer_get(buffer, 0); while ((match=_zip_memmem(match, _zip_buffer_left(buffer)-(EOCDLEN-4), (const unsigned char *)EOCD_MAGIC, 4)) != NULL) { _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer))); if ((cdirnew = _zip_read_cdir(za, buffer, (zip_uint64_t)buf_offset, &error)) != NULL) { if (cdir) { if (best <= 0) { best = _zip_checkcons(za, cdir, &error); } a = _zip_checkcons(za, cdirnew, &error); if (best < a) { _zip_cdir_free(cdir); cdir = cdirnew; best = a; } else { _zip_cdir_free(cdirnew); } } else { cdir = cdirnew; if (za->open_flags & ZIP_CHECKCONS) best = _zip_checkcons(za, cdir, &error); else { best = 0; } } cdirnew = NULL; } match++; _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer))); } _zip_buffer_free(buffer); if (best < 0) { _zip_error_copy(&za->error, &error); _zip_cdir_free(cdir); return NULL; } return cdir; }
static zip_cdir_t * _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) { zip_cdir_t *cd; zip_uint16_t comment_len; zip_uint64_t i, left; zip_uint64_t eocd_offset = _zip_buffer_offset(buffer); zip_buffer_t *cd_buffer; if (_zip_buffer_left(buffer) < EOCDLEN) { /* not enough bytes left for comment */ zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } /* check for end-of-central-dir magic */ if (memcmp(_zip_buffer_get(buffer, 4), EOCD_MAGIC, 4) != 0) { zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) { _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN); cd = _zip_read_eocd64(za->src, buffer, buf_offset, za->flags, error); } else { _zip_buffer_set_offset(buffer, eocd_offset); cd = _zip_read_eocd(buffer, buf_offset, za->flags, error); } if (cd == NULL) return NULL; _zip_buffer_set_offset(buffer, eocd_offset + 20); comment_len = _zip_buffer_get_16(buffer); if (cd->offset + cd->size > buf_offset + eocd_offset) { /* cdir spans past EOCD record */ zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if (comment_len || (za->open_flags & ZIP_CHECKCONS)) { zip_uint64_t tail_len; _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN); tail_len = _zip_buffer_left(buffer); if (tail_len < comment_len || ((za->open_flags & ZIP_CHECKCONS) && tail_len != comment_len)) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if (comment_len) { if ((cd->comment=_zip_string_new(_zip_buffer_get(buffer, comment_len), comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) { _zip_cdir_free(cd); return NULL; } } } if (cd->offset >= buf_offset) { zip_uint8_t *data; /* if buffer already read in, use it */ _zip_buffer_set_offset(buffer, cd->offset - buf_offset); if ((data = _zip_buffer_get(buffer, cd->size)) == NULL) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if ((cd_buffer = _zip_buffer_new(data, cd->size)) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); _zip_cdir_free(cd); return NULL; } } else { cd_buffer = NULL; if (zip_source_seek(za->src, (zip_int64_t)cd->offset, SEEK_SET) < 0) { _zip_error_set_from_source(error, za->src); _zip_cdir_free(cd); return NULL; } /* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */ if (zip_source_tell(za->src) != (zip_int64_t)cd->offset) { zip_error_set(error, ZIP_ER_NOZIP, 0); _zip_cdir_free(cd); return NULL; } } left = (zip_uint64_t)cd->size; i=0; while (left > 0) { bool grown = false; zip_int64_t entry_size; if (i == cd->nentry) { /* InfoZIP has a hack to avoid using Zip64: it stores nentries % 0x10000 */ /* This hack isn't applicable if we're using Zip64, or if there is no central directory entry following. */ if (cd->is_zip64 || left < CDENTRYSIZE) { break; } if (!_zip_cdir_grow(cd, 0x10000, error)) { _zip_cdir_free(cd); _zip_buffer_free(cd_buffer); return NULL; } grown = true; } if ((cd->entry[i].orig=_zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, error)) < 0) { if (grown && zip_error_code_zip(error) == ZIP_ER_NOZIP) { zip_error_set(error, ZIP_ER_INCONS, 0); } _zip_cdir_free(cd); _zip_buffer_free(cd_buffer); return NULL; } i++; left -= (zip_uint64_t)entry_size; } if (i != cd->nentry || left > 0) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } if (za->open_flags & ZIP_CHECKCONS) { bool ok; if (cd_buffer) { ok = _zip_buffer_eof(cd_buffer); } else { zip_int64_t offset = zip_source_tell(za->src); if (offset < 0) { _zip_error_set_from_source(error, za->src); _zip_cdir_free(cd); return NULL; } ok = ((zip_uint64_t)offset == cd->offset + cd->size); } if (!ok) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } } _zip_buffer_free(cd_buffer); return cd; }
static zip_cdir_t * _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) { zip_cdir_t *cd; zip_uint16_t comment_len; zip_uint64_t i, left; zip_uint64_t eocd_offset = _zip_buffer_offset(buffer); zip_buffer_t *cd_buffer; if (_zip_buffer_left(buffer) < EOCDLEN) { /* not enough bytes left for comment */ zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } /* check for end-of-central-dir magic */ if (memcmp(_zip_buffer_get(buffer, 4), EOCD_MAGIC, 4) != 0) { zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } if (_zip_buffer_get_32(buffer) != 0) { zip_error_set(error, ZIP_ER_MULTIDISK, 0); return NULL; } if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) { _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN); cd = _zip_read_eocd64(za->src, buffer, buf_offset, za->flags, error); } else { _zip_buffer_set_offset(buffer, eocd_offset); cd = _zip_read_eocd(buffer, buf_offset, za->flags, error); } if (cd == NULL) return NULL; _zip_buffer_set_offset(buffer, eocd_offset + 20); comment_len = _zip_buffer_get_16(buffer); if (cd->offset + cd->size > buf_offset + eocd_offset) { /* cdir spans past EOCD record */ zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if (comment_len || (za->open_flags & ZIP_CHECKCONS)) { zip_uint64_t tail_len; _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN); tail_len = _zip_buffer_left(buffer); if (tail_len < comment_len || ((za->open_flags & ZIP_CHECKCONS) && tail_len != comment_len)) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if (comment_len) { if ((cd->comment=_zip_string_new(_zip_buffer_get(buffer, comment_len), comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) { _zip_cdir_free(cd); return NULL; } } } if (cd->offset >= buf_offset) { zip_uint8_t *data; /* if buffer already read in, use it */ _zip_buffer_set_offset(buffer, cd->offset - buf_offset); if ((data = _zip_buffer_get(buffer, cd->size)) == NULL) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if ((cd_buffer = _zip_buffer_new(data, cd->size)) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); _zip_cdir_free(cd); return NULL; } } else { cd_buffer = NULL; if (zip_source_seek(za->src, (zip_int64_t)cd->offset, SEEK_SET) < 0) { _zip_error_set_from_source(error, za->src); _zip_cdir_free(cd); return NULL; } /* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */ if (zip_source_tell(za->src) != (zip_int64_t)cd->offset) { zip_error_set(error, ZIP_ER_NOZIP, 0); _zip_cdir_free(cd); return NULL; } } left = (zip_uint64_t)cd->size; i=0; while (i<cd->nentry && left > 0) { zip_int64_t entry_size; if ((cd->entry[i].orig=_zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, error)) < 0) { _zip_cdir_free(cd); _zip_buffer_free(cd_buffer); return NULL; } i++; left -= (zip_uint64_t)entry_size; } if (i != cd->nentry) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } if (za->open_flags & ZIP_CHECKCONS) { bool ok; if (cd_buffer) { ok = _zip_buffer_eof(cd_buffer); } else { zip_int64_t offset = zip_source_tell(za->src); if (offset < 0) { _zip_error_set_from_source(error, za->src); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } ok = ((zip_uint64_t)offset == cd->offset + cd->size); } if (!ok) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } } _zip_buffer_free(cd_buffer); return cd; }