diff --git a/src/common/ZipHandler.cxx b/src/common/ZipHandler.cxx index 72e345d98..ca6f9977e 100644 --- a/src/common/ZipHandler.cxx +++ b/src/common/ZipHandler.cxx @@ -393,6 +393,9 @@ void ZipHandler::ZipFile::decompress(BytePtr& out, uInt64 length) case 14: throw ZipError::LZMA_UNSUPPORTED; // FIXME - LZMA format not yet supported + + default: + throw ZipError::UNSUPPORTED; } } catch(const ZipError&) @@ -404,18 +407,27 @@ void ZipHandler::ZipFile::decompress(BytePtr& out, uInt64 length) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt64 ZipHandler::ZipFile::getCompressedDataOffset() { + // Don't support a number of features + GeneralFlagReader const flags(myHeader.bitFlag); + if(myHeader.startDiskNumber != myEcd.diskNumber || + myHeader.versionNeeded > 63 || flags.patchData() || + flags.encrypted() || flags.strongEncryption()) + throw ZipError::UNSUPPORTED; + // Read the fixed-sized part of the local file header uInt64 read_length = 0; bool success = readStream(myBuffer, myHeader.localHeaderOffset, 0x1e, read_length); if(!success) throw ZipError::FILE_ERROR; - else if(read_length != 0x1e) + else if(read_length != LocalFileHeaderReader::minimumLength()) throw ZipError::FILE_TRUNCATED; // Compute the final offset - return myHeader.localHeaderOffset + 0x1e + - read_word(myBuffer.get() + 0x1a) + - read_word(myBuffer.get() + 0x1c); + LocalFileHeaderReader reader(&myBuffer[0]); + if(!reader.signatureCorrect()) + throw ZipError::BAD_SIGNATURE; + + return myHeader.localHeaderOffset + reader.totalLength(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/common/ZipHandler.hxx b/src/common/ZipHandler.hxx index 7ba453c2d..c07ad61b2 100644 --- a/src/common/ZipHandler.hxx +++ b/src/common/ZipHandler.hxx @@ -151,64 +151,145 @@ class ZipHandler }; using ZipFilePtr = unique_ptr; - class EcdReader + /** Classes to parse the ZIP metadata in an abstracted way */ + class ReaderBase { - public: - explicit EcdReader(const uInt8* const b) : myBuf(b) { } + protected: + explicit ReaderBase(const uInt8* const b) : myBuf(b) { } - uInt32 signature() const { return read_dword(myBuf + 0x00); } - uInt16 thisDiskNo() const { return read_word(myBuf + 0x04); } - uInt16 dirStartDisk() const { return read_word(myBuf + 0x06); } - uInt16 dirDiskEntries() const { return read_word(myBuf + 0x08); } - uInt16 dirTotalEntries() const { return read_word(myBuf + 0x0a); } - uInt32 dirSize() const { return read_dword(myBuf + 0x0c); } - uInt32 dirOffset() const { return read_dword(myBuf + 0x10); } - uInt16 commentLength() const { return read_word(myBuf + 0x14); } - string comment() const { return read_string(myBuf + 0x16, commentLength()); } - - bool signatureCorrect() const { return signature() == 0x06054b50; } - - size_t totalLength() const { return minimumLength() + commentLength(); } - static size_t minimumLength() { return 0x16; } + uInt8 read_byte(size_t offs) const + { + return myBuf[offs]; + } + uInt16 read_word(size_t offs) const + { + return (uInt16(myBuf[offs + 1]) << 8) | + (uInt16(myBuf[offs + 0]) << 0); + } + uInt32 read_dword(std::size_t offs) const + { + return (uInt32(myBuf[offs + 3]) << 24) | + (uInt32(myBuf[offs + 2]) << 16) | + (uInt32(myBuf[offs + 1]) << 8) | + (uInt32(myBuf[offs + 0]) << 0); + } + uInt64 read_qword(size_t offs) const + { + return (uInt64(myBuf[offs + 7]) << 56) | + (uInt64(myBuf[offs + 6]) << 48) | + (uInt64(myBuf[offs + 5]) << 40) | + (uInt64(myBuf[offs + 4]) << 32) | + (uInt64(myBuf[offs + 3]) << 24) | + (uInt64(myBuf[offs + 2]) << 16) | + (uInt64(myBuf[offs + 1]) << 8) | + (uInt64(myBuf[offs + 0]) << 0); + } + string read_string(size_t offs, size_t len = string::npos) const + { + return string(reinterpret_cast(myBuf + offs), len); + } private: const uInt8* const myBuf; }; - class CentralDirEntryReader + class LocalFileHeaderReader : public ReaderBase { public: - explicit CentralDirEntryReader(const uInt8* const b) : myBuf(b) { } + explicit LocalFileHeaderReader(const uInt8* const b) : ReaderBase(b) { } - uInt32 signature() const { return read_dword(myBuf + 0x00); } - uInt8 versionCreated() const { return myBuf[0x04]; } - uInt8 osCreated() const { return myBuf[0x05]; } - uInt8 versionNeeded() const { return myBuf[0x06]; } - uInt8 osNeeded() const { return myBuf[0x07]; } - uInt16 generalFlag() const { return read_word(myBuf + 0x08); } - uInt16 compressionMethod() const { return read_word(myBuf + 0x0a); } - uInt16 modifiedTime() const { return read_word(myBuf + 0x0c); } - uInt16 modifiedDate() const { return read_word(myBuf + 0x0e); } - uInt32 crc32() const { return read_dword(myBuf + 0x10); } - uInt32 compressedSize() const { return read_dword(myBuf + 0x14); } - uInt32 uncompressedSize() const { return read_dword(myBuf + 0x18); } - uInt16 filenameLength() const { return read_word(myBuf + 0x1c); } - uInt16 extraFieldLength() const { return read_word(myBuf + 0x1e); } - uInt16 fileCommentLength() const { return read_word(myBuf + 0x20); } - uInt16 startDisk() const { return read_word(myBuf + 0x22); } - uInt16 intFileAttr() const { return read_word(myBuf + 0x24); } - uInt32 extFileAttr() const { return read_dword(myBuf + 0x26); } - uInt32 headerOffset() const { return read_dword(myBuf + 0x2a); } - string filename() const { return read_string(myBuf + 0x2e, filenameLength()); } - string fileComment() const { return read_string(myBuf + 0x2e + filenameLength() + extraFieldLength(), fileCommentLength()); } + uInt32 signature() const { return read_dword(0x00); } + uInt8 versionNeeded() const { return read_byte(0x04); } + uInt8 osNeeded() const { return read_byte(0x05); } + uInt16 generalFlag() const { return read_word(0x06); } + uInt16 compressionMethod() const { return read_word(0x08); } + uInt16 modifiedTime() const { return read_word(0x0a); } + uInt16 modifiedDate() const { return read_word(0x0c); } + uInt32 crc32() const { return read_dword(0x0e); } + uInt32 compressedSize() const { return read_dword(0x12); } + uInt32 uncompressedSize() const { return read_dword(0x16); } + uInt16 filenameLength() const { return read_word(0x1a); } + uInt16 extraFieldLength() const { return read_word(0x1c); } + string filename() const { return read_string(0x1e, filenameLength()); } + + bool signatureCorrect() const { return signature() == 0x04034b50; } + + size_t totalLength() const { return minimumLength() + filenameLength() + extraFieldLength(); } + static size_t minimumLength() { return 0x1e; } + }; + + class CentralDirEntryReader : public ReaderBase + { + public: + explicit CentralDirEntryReader(const uInt8* const b) : ReaderBase(b) { } + + uInt32 signature() const { return read_dword(0x00); } + uInt8 versionCreated() const { return read_byte(0x04); } + uInt8 osCreated() const { return read_byte(0x05); } + uInt8 versionNeeded() const { return read_byte(0x06); } + uInt8 osNeeded() const { return read_byte(0x07); } + uInt16 generalFlag() const { return read_word(0x08); } + uInt16 compressionMethod() const { return read_word(0x0a); } + uInt16 modifiedTime() const { return read_word(0x0c); } + uInt16 modifiedDate() const { return read_word(0x0e); } + uInt32 crc32() const { return read_dword(0x10); } + uInt32 compressedSize() const { return read_dword(0x14); } + uInt32 uncompressedSize() const { return read_dword(0x18); } + uInt16 filenameLength() const { return read_word(0x1c); } + uInt16 extraFieldLength() const { return read_word(0x1e); } + uInt16 fileCommentLength() const { return read_word(0x20); } + uInt16 startDisk() const { return read_word(0x22); } + uInt16 intFileAttr() const { return read_word(0x24); } + uInt32 extFileAttr() const { return read_dword(0x26); } + uInt32 headerOffset() const { return read_dword(0x2a); } + string filename() const { return read_string(0x2e, filenameLength()); } + string fileComment() const { return read_string(0x2e + filenameLength() + extraFieldLength(), fileCommentLength()); } bool signatureCorrect() const { return signature() == 0x02014b50; } size_t totalLength() const { return minimumLength() + filenameLength() + extraFieldLength() + fileCommentLength(); } static size_t minimumLength() { return 0x2e; } + }; + + class EcdReader : public ReaderBase + { + public: + explicit EcdReader(const uInt8* const b) : ReaderBase(b) { } + + uInt32 signature() const { return read_dword(0x00); } + uInt16 thisDiskNo() const { return read_word(0x04); } + uInt16 dirStartDisk() const { return read_word(0x06); } + uInt16 dirDiskEntries() const { return read_word(0x08); } + uInt16 dirTotalEntries() const { return read_word(0x0a); } + uInt32 dirSize() const { return read_dword(0x0c); } + uInt32 dirOffset() const { return read_dword(0x10); } + uInt16 commentLength() const { return read_word(0x14); } + string comment() const { return read_string(0x16, commentLength()); } + + bool signatureCorrect() const { return signature() == 0x06054b50; } + + size_t totalLength() const { return minimumLength() + commentLength(); } + static size_t minimumLength() { return 0x16; } + }; + + class GeneralFlagReader + { + public: + explicit GeneralFlagReader(uInt16 val) : myValue(val) { } + + bool encrypted() const { return bool(myValue & 0x0001); } + bool implode8kDict() const { return bool(myValue & 0x0002); } + bool implode3Trees() const { return bool(myValue & 0x0004); } + uInt32 deflateOption() const { return uInt32((myValue >> 1) & 0x0003); } + bool lzmaEosMark() const { return bool(myValue & 0x0002); } + bool useDescriptor() const { return bool(myValue & 0x0008); } + bool patchData() const { return bool(myValue & 0x0020); } + bool strongEncryption() const { return bool(myValue & 0x0040); } + bool utf8Encoding() const { return bool(myValue & 0x0800); } + bool directoryEncryption() const { return bool(myValue & 0x2000); } private: - const uInt8* const myBuf; + uInt16 myValue; }; private: @@ -221,22 +302,6 @@ class ZipHandler /** Close a ZIP file and add it to the cache */ void addToCache(); - /** Convenience functions to read specific datatypes */ - static inline uInt16 read_word(const uInt8* const buf) - { - uInt16 p0 = uInt16(buf[0]), p1 = uInt16(buf[1]); - return (p1 << 8) | p0; - } - static inline uInt32 read_dword(const uInt8* const buf) - { - return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; - } - static inline string read_string(const uInt8* const buf, size_t len = string::npos) - { - return (buf == nullptr || *buf == '\0') ? "" : - string(reinterpret_cast(buf), len); - } - private: static constexpr uInt32 DECOMPRESS_BUFSIZE = 16384; static constexpr uInt32 CACHE_SIZE = 8; // number of open files to cache