From 9faebd9c522e4c4095cba3196517899bb158d1ca Mon Sep 17 00:00:00 2001 From: Feng Hao Date: Thu, 20 Sep 2018 10:16:03 +0800 Subject: [PATCH] fix: Cannot open certain zip files, #2467 --- Zip/include/Poco/Zip/ZipUtil.h | 3 ++ Zip/src/SkipCallback.cpp | 2 +- Zip/src/ZipUtil.cpp | 63 ++++++++++++++++++++++++++++ Zip/testsuite/data/encapsulated.zip | Bin 0 -> 364 bytes Zip/testsuite/src/ZipTest.cpp | 29 +++++++++++++ Zip/testsuite/src/ZipTest.h | 1 + 6 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 Zip/testsuite/data/encapsulated.zip diff --git a/Zip/include/Poco/Zip/ZipUtil.h b/Zip/include/Poco/Zip/ZipUtil.h index 0721237d9..6ac74adeb 100644 --- a/Zip/include/Poco/Zip/ZipUtil.h +++ b/Zip/include/Poco/Zip/ZipUtil.h @@ -54,6 +54,9 @@ public: static void sync(std::istream& in); /// Searches the next valid header in the input stream, stops right before it + static void syncDataDescriptor(std::istream& in, bool force64); + /// Searches the next data descriptor + static void verifyZipEntryFileName(const std::string& zipPath); /// Verifies that the name of the ZipEntry is a valid path diff --git a/Zip/src/SkipCallback.cpp b/Zip/src/SkipCallback.cpp index 2d9bc3ef1..6755a9db6 100644 --- a/Zip/src/SkipCallback.cpp +++ b/Zip/src/SkipCallback.cpp @@ -37,7 +37,7 @@ bool SkipCallback::handleZipEntry(std::istream& zipStream, const ZipLocalFileHea if (!hdr.searchCRCAndSizesAfterData()) zipStream.seekg(hdr.getCompressedSize(), std::ios_base::cur); else - ZipUtil::sync(zipStream); + ZipUtil::syncDataDescriptor(zipStream, hdr.needsZip64()); if (!zipStream.good()) throw Poco::IOException("Failed to seek on input stream"); return true; } diff --git a/Zip/src/ZipUtil.cpp b/Zip/src/ZipUtil.cpp index 9641b3c73..acacfe791 100644 --- a/Zip/src/ZipUtil.cpp +++ b/Zip/src/ZipUtil.cpp @@ -160,6 +160,69 @@ void ZipUtil::sync(std::istream& in) } } +void ZipUtil::syncDataDescriptor(std::istream & in, bool force64) +{ + std::streampos start = in.tellg(); + const int eof = std::char_traits::eof(); + + int c = in.get(); + do + { + while (c != eof && c != ZipDataInfo::HEADER[0]) { c = in.get(); } + + if (c == eof) return; + + bool match = true; + for (int i = 1; i < 4 && match; i++) + { + c = in.get(); + if (c != ZipDataInfo::HEADER[i]) match = false; + } + + if (match) + { + std::streampos end = in.tellg(); + + if (force64) + { + ZipDataInfo64 nfo(in, true); + if (nfo.isValid()) + { + if (end - start == nfo.getCompressedSize() + 4) + { + in.seekg(-static_cast(ZipDataInfo64::getFullHeaderSize()), std::ios::cur); + if (!in.good()) throw Poco::IOException("Failed to seek on input stream"); + break; + } + else + { + in.seekg(-static_cast(ZipDataInfo64::getFullHeaderSize()) + 4, std::ios::cur); + if (!in.good()) throw Poco::IOException("Failed to seek on input stream"); + } + } + } + else + { + ZipDataInfo nfo(in, true); + if (nfo.isValid()) + { + if (end - start == nfo.getCompressedSize() + 4) + { + in.seekg(-static_cast(ZipDataInfo::getFullHeaderSize()), std::ios::cur); + if (!in.good()) throw Poco::IOException("Failed to seek on input stream"); + break; + } + else + { + in.seekg(-static_cast(ZipDataInfo::getFullHeaderSize()) + 4, std::ios::cur); + if (!in.good()) throw Poco::IOException("Failed to seek on input stream"); + } + } + } + } + } while (c != eof); +} + void ZipUtil::verifyZipEntryFileName(const std::string& fn) { diff --git a/Zip/testsuite/data/encapsulated.zip b/Zip/testsuite/data/encapsulated.zip new file mode 100644 index 0000000000000000000000000000000000000000..9cd3c5919764d1d8beb0d11ffcbd4bde83a5aa22 GIT binary patch literal 364 zcmWIWW@h1H;9y{22=g}b1yT$bpQYW literal 0 HcmV?d00001 diff --git a/Zip/testsuite/src/ZipTest.cpp b/Zip/testsuite/src/ZipTest.cpp index aa72a7fb3..e3de06bb2 100644 --- a/Zip/testsuite/src/ZipTest.cpp +++ b/Zip/testsuite/src/ZipTest.cpp @@ -65,6 +65,34 @@ void ZipTest::testSkipSingleFile() const std::string& POCO_UNUSED fileName = hdr.getFileName(); } +void ZipTest::testCrcAndSizeAfterDataEncapsulated() +{ + // touch empty.txt + // zip -fd foo.zip empty.txt + // zip -fd encapsulated.zip foo.zip + std::string testFile = getTestFile("data", "encapsulated.zip"); + Poco::FileInputStream inp(testFile); + assertTrue(inp.good()); + + ZipArchive arch(inp); + ZipArchive::FileHeaders::const_iterator it = arch.findHeader("foo.zip"); + assertTrue(it != arch.headerEnd()); + inp.clear(); // inp eof(), should clear + + ZipInputStream zipin(inp, it->second); + std::ostringstream out(std::ios::binary); + Poco::StreamCopier::copyStream(zipin, out); + + std::string result = out.str(); + // sub zip + std::istringstream istr(result); + ZipArchive subArch(istr); + it = subArch.findHeader("empty.txt"); + assertTrue(it != subArch.headerEnd()); + assertTrue(it->second.getCompressedSize() == 0); + assertTrue(it->second.getUncompressedSize() == 0); +} + void ZipTest::testDecompressSingleFile() { @@ -325,6 +353,7 @@ CppUnit::Test* ZipTest::suite() CppUnit_addTest(pSuite, ZipTest, testDecompressFlatVuln); CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterData); CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterDataWithArchive); + CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterDataEncapsulated); CppUnit_addTest(pSuite, ZipTest, testDecompressZip64); CppUnit_addTest(pSuite, ZipTest, testValidPath); diff --git a/Zip/testsuite/src/ZipTest.h b/Zip/testsuite/src/ZipTest.h index 2ac09ad94..d749b933f 100644 --- a/Zip/testsuite/src/ZipTest.h +++ b/Zip/testsuite/src/ZipTest.h @@ -34,6 +34,7 @@ public: void testDecompressFlatVuln(); void testCrcAndSizeAfterData(); void testCrcAndSizeAfterDataWithArchive(); + void testCrcAndSizeAfterDataEncapsulated(); static const Poco::UInt64 KB = 1024; static const Poco::UInt64 MB = 1024*KB;