This is an automated email from the ASF dual-hosted git repository.
ardovm pushed a commit to branch AOO42X
in repository https://gitbox.apache.org/repos/asf/openoffice.git
The following commit(s) were added to refs/heads/AOO42X by this push:
new c66237e067 Correct possible issues with ZIP files
c66237e067 is described below
commit c66237e0677a01c28df303159222410472ecfcdf
Author: Arrigo Marchiori <[email protected]>
AuthorDate: Thu Mar 12 19:18:44 2026 +0100
Correct possible issues with ZIP files
(cherry picked from commit 1972b29432a5b05e40ec16d939ddb345b981e240)
---
main/comphelper/source/misc/storagehelper.cxx | 2 +-
main/package/inc/ZipEntry.hxx | 38 ++-
main/package/inc/ZipFile.hxx | 10 +
.../source/package/zipapi/XUnbufferedStream.cxx | 6 +-
main/package/source/package/zipapi/ZipFile.cxx | 276 +++++++++++++++++----
.../source/package/zipapi/ZipOutputStream.cxx | 52 ++--
.../source/package/zippackage/ZipPackage.cxx | 5 +
.../source/package/zippackage/ZipPackageFolder.cxx | 15 +-
.../source/package/zippackage/ZipPackageStream.cxx | 14 +-
9 files changed, 331 insertions(+), 87 deletions(-)
diff --git a/main/comphelper/source/misc/storagehelper.cxx
b/main/comphelper/source/misc/storagehelper.cxx
index 0c4a1da91c..140e99b299 100644
--- a/main/comphelper/source/misc/storagehelper.cxx
+++ b/main/comphelper/source/misc/storagehelper.cxx
@@ -506,7 +506,7 @@ sal_Bool OStorageHelper::IsValidZipEntryFileName(
case ':':
return sal_False;
case '/':
- if ( !bSlashAllowed )
+ if ( ( i == 0 ) || !bSlashAllowed )
return sal_False;
break;
default:
diff --git a/main/package/inc/ZipEntry.hxx b/main/package/inc/ZipEntry.hxx
index ffa3c79931..c06efacb44 100644
--- a/main/package/inc/ZipEntry.hxx
+++ b/main/package/inc/ZipEntry.hxx
@@ -25,18 +25,52 @@
#include <rtl/ustring.hxx>
+/// Data from a central directory header.
struct ZipEntry
{
+ /// version needed to extract
sal_Int16 nVersion;
+ /// general purpose bit flag
sal_Int16 nFlag;
+ /// compression method
sal_Int16 nMethod;
+ /// last mod file date & time
sal_Int32 nTime;
+ /// crc-32
sal_Int32 nCrc;
+ /// compressed size
sal_Int32 nCompressedSize;
+ /// uncompressed size
sal_Int32 nSize;
- sal_Int32 nOffset;
+ /// (Absolute) offset to the local file header
+ sal_Int32 nFileHeaderOffset;
+ /// (Absolute) offset to the file data
+ sal_Int32 nFileDataOffset;
+ /// file name length
sal_Int16 nPathLen;
- sal_Int16 nExtraLen;
+ /// extra field length of local file header
+ sal_Int16 nLOCExtraLen;
+ /// extra field length of central directory header
+ sal_Int16 nCENExtraLen;
+ /// file name
::rtl::OUString sPath;
+ /// True if the entry has a data descriptor after the file data
+ sal_Bool bHasDataDescriptor;
+ /// Default constructor
+ ZipEntry():
+ nVersion(-1),
+ nFlag(0),
+ nMethod(0),
+ nTime(-1),
+ nCrc(0),
+ nCompressedSize(0),
+ nSize(0),
+ nFileHeaderOffset(-1),
+ nFileDataOffset(-1),
+ nPathLen(0),
+ nLOCExtraLen(0),
+ nCENExtraLen(0),
+ bHasDataDescriptor(sal_False) {
+ }
};
#endif
diff --git a/main/package/inc/ZipFile.hxx b/main/package/inc/ZipFile.hxx
index 51c1c322a2..340a566cb4 100644
--- a/main/package/inc/ZipFile.hxx
+++ b/main/package/inc/ZipFile.hxx
@@ -194,10 +194,20 @@ public:
ZipEnumeration * SAL_CALL entries( );
protected:
+ /** Decode a local file header of an entry and update a ZipEntry
+ *
+ * @param rEntry entry with the nFileHeaderOffset field populated. At
return, the ZipEntry::nFileDataOffset and ZipEntry::nLOCExtraLen fields will be
set.
+ *
+ * Also searches for a data descriptor after the file data and
validates it.
+ *
+ * @return sal_True if successful, otherwise throws exceptions.
+ */
sal_Bool readLOC ( ZipEntry &rEntry)
throw(::com::sun::star::io::IOException,
com::sun::star::packages::zip::ZipException,
com::sun::star::uno::RuntimeException);
+ /// Decode the End of central directory record
sal_Int32 readCEN()
throw(::com::sun::star::io::IOException,
com::sun::star::packages::zip::ZipException,
com::sun::star::uno::RuntimeException);
+ /// Find the End of central directory record
sal_Int32 findEND()
throw(::com::sun::star::io::IOException,
com::sun::star::packages::zip::ZipException,
com::sun::star::uno::RuntimeException);
sal_Int32 recover()
diff --git a/main/package/source/package/zipapi/XUnbufferedStream.cxx
b/main/package/source/package/zipapi/XUnbufferedStream.cxx
index f4f38978b6..accf8caa3c 100644
--- a/main/package/source/package/zipapi/XUnbufferedStream.cxx
+++ b/main/package/source/package/zipapi/XUnbufferedStream.cxx
@@ -80,16 +80,16 @@ XUnbufferedStream::XUnbufferedStream(
, mnMyCurrent ( 0 )
, mbCheckCRC( !bRecoveryMode )
{
- mnZipCurrent = maEntry.nOffset;
+ mnZipCurrent = maEntry.nFileDataOffset;
if ( mbRawStream )
{
mnZipSize = maEntry.nMethod == DEFLATED ?
maEntry.nCompressedSize : maEntry.nSize;
- mnZipEnd = maEntry.nOffset + mnZipSize;
+ mnZipEnd = maEntry.nFileDataOffset + mnZipSize;
}
else
{
mnZipSize = maEntry.nSize;
- mnZipEnd = maEntry.nMethod == DEFLATED ? maEntry.nOffset +
maEntry.nCompressedSize : maEntry.nOffset + maEntry.nSize;
+ mnZipEnd = maEntry.nMethod == DEFLATED ?
maEntry.nFileDataOffset + maEntry.nCompressedSize : maEntry.nFileDataOffset +
maEntry.nSize;
}
sal_Bool bHaveEncryptData = ( rData.is() && rData->m_aSalt.getLength()
&& rData->m_aInitVector.getLength() && rData->m_nIterationCount != 0 ) ?
sal_True : sal_False;
sal_Bool bMustDecrypt = ( nStreamMode == UNBUFF_STREAM_DATA &&
bHaveEncryptData && bIsEncrypted ) ? sal_True : sal_False;
diff --git a/main/package/source/package/zipapi/ZipFile.cxx
b/main/package/source/package/zipapi/ZipFile.cxx
index 1e0ca9ee8b..c58ef3bd0a 100644
--- a/main/package/source/package/zipapi/ZipFile.cxx
+++ b/main/package/source/package/zipapi/ZipFile.cxx
@@ -498,7 +498,7 @@ sal_Bool ZipFile::hasValidPassword ( ZipEntry & rEntry,
const ::rtl::Reference<
sal_Bool bRet = sal_False;
if ( rData.is() && rData->m_aKey.getLength() )
{
- xSeek->seek( rEntry.nOffset );
+ xSeek->seek( rEntry.nFileDataOffset );
sal_Int32 nSize = rEntry.nMethod == DEFLATED ?
rEntry.nCompressedSize : rEntry.nSize;
// Only want to read enough to verify the digest
@@ -523,6 +523,10 @@ uno::Reference< XInputStream >
ZipFile::createUnbufferedStream(
sal_Bool bIsEncrypted,
::rtl::OUString aMediaType )
{
+
+ // Only encrypted entries can be STORED and have a data descriptor
+ if ( !bIsEncrypted && ( rEntry.nMethod == STORED ) &&
rEntry.bHasDataDescriptor )
+ throw ZipException( OUString( RTL_CONSTASCII_USTRINGPARAM (
"entry has spurious file descriptor" ) ), uno::Reference < XInterface > () );
::osl::MutexGuard aGuard( m_aMutex );
return new XUnbufferedStream ( m_xFactory, aMutexHolder, rEntry,
xStream, rData, nStreamMode, bIsEncrypted, aMediaType, bRecoveryMode );
@@ -542,7 +546,7 @@ uno::Reference< XInputStream > SAL_CALL
ZipFile::getInputStream( ZipEntry& rEntr
{
::osl::MutexGuard aGuard( m_aMutex );
- if ( rEntry.nOffset <= 0 )
+ if ( rEntry.nFileDataOffset <= 0 )
readLOC( rEntry );
// We want to return a rawStream if we either don't have a key or if the
@@ -573,7 +577,7 @@ uno::Reference< XInputStream > SAL_CALL
ZipFile::getDataStream( ZipEntry& rEntry
{
::osl::MutexGuard aGuard( m_aMutex );
- if ( rEntry.nOffset <= 0 )
+ if ( rEntry.nFileDataOffset <= 0 )
readLOC( rEntry );
// An exception must be thrown in case stream is encrypted and
@@ -611,7 +615,7 @@ uno::Reference< XInputStream > SAL_CALL
ZipFile::getRawData( ZipEntry& rEntry,
{
::osl::MutexGuard aGuard( m_aMutex );
- if ( rEntry.nOffset <= 0 )
+ if ( rEntry.nFileDataOffset <= 0 )
readLOC( rEntry );
return createUnbufferedStream ( aMutexHolder, rEntry, rData,
UNBUFF_STREAM_RAW, bIsEncrypted );
@@ -632,20 +636,63 @@ uno::Reference< XInputStream > SAL_CALL
ZipFile::getWrappedRawStream(
if ( !rData.is() )
throw packages::NoEncryptionException( ::rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ), uno::Reference<
uno::XInterface >() );
- if ( rEntry.nOffset <= 0 )
+ if ( rEntry.nFileDataOffset <= 0 )
readLOC( rEntry );
return createUnbufferedStream ( aMutexHolder, rEntry, rData,
UNBUFF_STREAM_WRAPPEDRAW, sal_True, aMediaType );
}
+/** Read extra fields
+ *
+ * @param rGrabber Grabber object for reading data.
+ * @param nLength extra field length
+ * @param rEntry entry data.
+ *
+ * Expects rGrabber to point to the beginning of the extra fields.
+ * Advances it until all fields are read (i.e. after nLength bytes).
+ *
+ * @throw ZipException in case of problems.
+ */
+static void readExtraFields( MemoryByteGrabber &rMemGrabber, sal_Int16
nLength, ZipEntry &rEntry )
+{
+ sal_Int16 nHeaderID, nDataSize;
+ sal_Int16 nReadBytes = 0;
+ sal_Int8 n8;
+ rtl::OUString s;
+ while ( ( nLength - nReadBytes ) >= 2 ) {
+ rMemGrabber >> nHeaderID;
+ rMemGrabber >> nDataSize;
+ nReadBytes += 2;
+ if (( nDataSize > ( nLength - nReadBytes ) ) ||
+ ( nDataSize > rMemGrabber.available() ) )
+ throw ZipException( OUString(
RTL_CONSTASCII_USTRINGPARAM ( "Invalid ZIP extra fields" ) ), uno::Reference <
XInterface > () );
+ switch ( nHeaderID ) {
+ case 0x7075: // Info-ZIP Unicode Path Extra Field
+ rMemGrabber >> n8; // Version
+ rMemGrabber.skipBytes( 4 ); // skip NameCRC32
+ s = rtl::OUString::intern ( (sal_Char *)
rMemGrabber.getCurrentPos(),
+
nDataSize - 5,
+
RTL_TEXTENCODING_UTF8 );
+ if ( n8 != 1 )
+ throw ZipException( OUString(
RTL_CONSTASCII_USTRINGPARAM ( "Invalid ZIP unicode path extra field version" )
), uno::Reference < XInterface > () );
+ if ( s != rEntry.sPath )
+ throw ZipException( OUString(
RTL_CONSTASCII_USTRINGPARAM ( "Invalid ZIP unicode path extra field" ) ),
uno::Reference < XInterface > () );
+ rMemGrabber.skipBytes( nDataSize - 5 );
+ break;
+ default: // We are not interested in this field
+ rMemGrabber.skipBytes( nDataSize );
+ }
+ }
+}
+
sal_Bool ZipFile::readLOC( ZipEntry &rEntry )
throw(IOException, ZipException, RuntimeException)
{
::osl::MutexGuard aGuard( m_aMutex );
sal_Int32 nTestSig, nTime, nCRC, nSize, nCompressedSize;
- sal_Int16 nVersion, nFlag, nHow, nPathLen, nExtraLen;
- sal_Int32 nPos = -rEntry.nOffset;
+ sal_Int16 nVersion, nFlag, nHow, nPathLen;
+ sal_Int32 nPos = rEntry.nFileHeaderOffset;
aGrabber.seek(nPos);
aGrabber >> nTestSig;
@@ -660,8 +707,8 @@ sal_Bool ZipFile::readLOC( ZipEntry &rEntry )
aGrabber >> nCompressedSize;
aGrabber >> nSize;
aGrabber >> nPathLen;
- aGrabber >> nExtraLen;
- rEntry.nOffset = static_cast < sal_Int32 > (aGrabber.getPosition()) +
nPathLen + nExtraLen;
+ aGrabber >> rEntry.nLOCExtraLen;
+ rEntry.nFileDataOffset = static_cast < sal_Int32 >
(aGrabber.getPosition()) + nPathLen + rEntry.nLOCExtraLen;
// read always in UTF8, some tools seem not to set UTF8 bit
uno::Sequence < sal_Int8 > aNameBuffer( nPathLen );
@@ -682,6 +729,7 @@ sal_Bool ZipFile::readLOC( ZipEntry &rEntry )
// the method can be reset for internal use so it is not checked
sal_Bool bBroken = rEntry.nVersion != nVersion
|| rEntry.nFlag != nFlag
+ || rEntry.nMethod != nHow
|| rEntry.nTime != nTime
|| rEntry.nPathLen != nPathLen
|| !rEntry.sPath.equals( sLOCPath );
@@ -690,6 +738,37 @@ sal_Bool ZipFile::readLOC( ZipEntry &rEntry )
throw ZipIOException( OUString( RTL_CONSTASCII_USTRINGPARAM(
"The stream seems to be broken!" ) ),
uno::Reference<
XInterface >() );
+ Sequence < sal_Int8 > aExtraFields ( rEntry.nLOCExtraLen );
+ nRead = aGrabber.readBytes( aExtraFields, rEntry.nLOCExtraLen );
+ if ( nRead != rEntry.nLOCExtraLen )
+ throw ZipException ( OUString ( RTL_CONSTASCII_USTRINGPARAM (
"Error reading LOC extra fields into memory buffer!") ), uno::Reference <
XInterface > () );
+ MemoryByteGrabber aMemGrabber ( aExtraFields );
+ readExtraFields( aMemGrabber, rEntry.nLOCExtraLen, rEntry );
+
+ // Check for a data descriptor
+ rEntry.bHasDataDescriptor = rEntry.nFlag & ( 1 << 3 );
+ if ( rEntry.bHasDataDescriptor ) {
+ // some fields of the local file header must be zero
+ if ( ( nCRC | nCompressedSize | nSize ) != 0 )
+ throw ZipException ( OUString (
RTL_CONSTASCII_USTRINGPARAM ( "Data descriptor mismatch!") ), uno::Reference <
XInterface > () );
+ }
+ sal_Int32 n32;
+ aGrabber.seek( rEntry.nFileDataOffset + rEntry.nCompressedSize );
+ aGrabber >> n32;
+ if ( n32 == EXTSIG ) { // Signature detected
+ rEntry.bHasDataDescriptor = sal_True;
+ aGrabber >> n32;
+ }
+ if ( rEntry.bHasDataDescriptor ) {
+ if ( n32 != rEntry.nCrc )
+ throw ZipException ( OUString (
RTL_CONSTASCII_USTRINGPARAM ( "Data descriptor CRC mismatch!") ),
uno::Reference < XInterface > () );
+ aGrabber >> n32;
+ if ( n32 != rEntry.nCompressedSize )
+ throw ZipException ( OUString (
RTL_CONSTASCII_USTRINGPARAM ( "Data descriptor compressed size mismatch!") ),
uno::Reference < XInterface > () );
+ aGrabber >> n32;
+ if ( n32 != rEntry.nSize )
+ throw ZipException ( OUString (
RTL_CONSTASCII_USTRINGPARAM ( "Data descriptor uncompressed size mismatch!") ),
uno::Reference < XInterface > () );
+ }
return sal_True;
}
@@ -735,6 +814,18 @@ sal_Int32 ZipFile::findEND( )
throw ZipException( OUString( RTL_CONSTASCII_USTRINGPARAM ( "Zip END
signature not found!") ), uno::Reference < XInterface > () );
}
+static sal_Bool entriesContainPath( const EntryHash &rEntries,
+ const
OUString &sPath )
+{
+ for ( EntryHash::const_iterator aIt = rEntries.begin();
+ aIt != rEntries.end(); ++aIt)
+ {
+ if ( aIt->first.equalsIgnoreAsciiCase( sPath ) )
+ return sal_True;
+ }
+ return sal_False;
+}
+
sal_Int32 ZipFile::readCEN()
throw(IOException, ZipException, RuntimeException)
{
@@ -747,9 +838,18 @@ sal_Int32 ZipFile::readCEN()
nEndPos = findEND();
if (nEndPos == -1)
return -1;
+ /*
+ Ignored fields:
+ - number of this disk
+ - number of the disk with the start of the central directory
+ - total number of entries in the central directory on this
disk
+ */
aGrabber.seek(nEndPos + ENDTOT);
+ // total number of entries in the central directory
aGrabber >> nTotal;
+ // size of the central directory
aGrabber >> nCenLen;
+ // offset of start of central directory
aGrabber >> nCenOff;
if ( nTotal * CENHDR > nCenLen )
@@ -766,6 +866,7 @@ sal_Int32 ZipFile::readCEN()
if ( nCenOff < 0 || nCenOff > nCenPos )
throw ZipException(OUString(
RTL_CONSTASCII_USTRINGPARAM ( "Invalid END header (bad central directory
size)") ), uno::Reference < XInterface > () );
+ // Read the central directory headers
nLocPos = nCenPos - nCenOff;
aGrabber.seek( nCenPos );
Sequence < sal_Int8 > aCENBuffer ( nCenLen );
@@ -778,6 +879,7 @@ sal_Int32 ZipFile::readCEN()
ZipEntry aEntry;
sal_Int32 nTestSig;
sal_Int16 nCommentLen;
+ rtl::OUString sFirstFilePath;
for (nCount = 0 ; nCount < nTotal; nCount++)
{
@@ -785,30 +887,31 @@ sal_Int32 ZipFile::readCEN()
if ( nTestSig != CENSIG )
throw ZipException(OUString(
RTL_CONSTASCII_USTRINGPARAM ( "Invalid CEN header (bad signature)") ),
uno::Reference < XInterface > () );
- aMemGrabber.skipBytes ( 2 );
- aMemGrabber >> aEntry.nVersion;
-
- if ( ( aEntry.nVersion & 1 ) == 1 )
+ aMemGrabber.skipBytes ( 2 ); // skip: versione made by
+ aMemGrabber >> aEntry.nVersion; // version needed to
extract
+ aMemGrabber >> aEntry.nFlag; // general purpose bit flag
+ if ( ( aEntry.nFlag & 1 ) == 1 )
throw ZipException(OUString(
RTL_CONSTASCII_USTRINGPARAM ( "Invalid CEN header (encrypted entry)") ),
uno::Reference < XInterface > () );
- aMemGrabber >> aEntry.nFlag;
- aMemGrabber >> aEntry.nMethod;
+ aMemGrabber >> aEntry.nMethod; // compression method
if ( aEntry.nMethod != STORED && aEntry.nMethod !=
DEFLATED)
throw ZipException(OUString(
RTL_CONSTASCII_USTRINGPARAM ( "Invalid CEN header (bad compression method)") ),
uno::Reference < XInterface > () );
- aMemGrabber >> aEntry.nTime;
- aMemGrabber >> aEntry.nCrc;
- aMemGrabber >> aEntry.nCompressedSize;
- aMemGrabber >> aEntry.nSize;
- aMemGrabber >> aEntry.nPathLen;
- aMemGrabber >> aEntry.nExtraLen;
- aMemGrabber >> nCommentLen;
+ aMemGrabber >> aEntry.nTime; // last mod file date &
time
+ aMemGrabber >> aEntry.nCrc; // crc-32
+ aMemGrabber >> aEntry.nCompressedSize; // compressed
size
+ aMemGrabber >> aEntry.nSize; // uncompressed size
+ if ( aEntry.nMethod == STORED && (
aEntry.nCompressedSize != aEntry.nSize ) )
+ throw ZipException(OUString(
RTL_CONSTASCII_USTRINGPARAM ( "Size mismatch of STORED entry") ),
uno::Reference < XInterface > () );
+ aMemGrabber >> aEntry.nPathLen; // file name length
+ aMemGrabber >> aEntry.nCENExtraLen; // extra field
length
+ aMemGrabber >> nCommentLen; // file comment length
+ // skip: disk number start, internal & external file
attributes
aMemGrabber.skipBytes ( 8 );
- aMemGrabber >> aEntry.nOffset;
+ aMemGrabber >> aEntry.nFileHeaderOffset; // relative
offset of local header
- aEntry.nOffset += nLocPos;
- aEntry.nOffset *= -1;
+ aEntry.nFileHeaderOffset += nLocPos;
if ( aEntry.nPathLen < 0 )
throw ZipException( OUString(
RTL_CONSTASCII_USTRINGPARAM ( "unexpected name length" ) ), uno::Reference <
XInterface > () );
@@ -816,23 +919,103 @@ sal_Int32 ZipFile::readCEN()
if ( nCommentLen < 0 )
throw ZipException( OUString(
RTL_CONSTASCII_USTRINGPARAM ( "unexpected comment length" ) ), uno::Reference <
XInterface > () );
- if ( aEntry.nExtraLen < 0 )
+ if ( aEntry.nCENExtraLen < 0 )
throw ZipException( OUString(
RTL_CONSTASCII_USTRINGPARAM ( "unexpected extra header info length") ),
uno::Reference < XInterface > () );
- // read always in UTF8, some tools seem not to set UTF8 bit
+ // read file name always in UTF8, some tools seem not to set UTF8
bit
aEntry.sPath = rtl::OUString::intern ( (sal_Char *)
aMemGrabber.getCurrentPos(),
aEntry.nPathLen,
RTL_TEXTENCODING_UTF8 );
if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName(
aEntry.sPath, sal_True ) )
throw ZipException( OUString(
RTL_CONSTASCII_USTRINGPARAM ( "Zip entry has an invalid name.") ),
uno::Reference < XInterface > () );
-
- aMemGrabber.skipBytes( aEntry.nPathLen +
aEntry.nExtraLen + nCommentLen );
+ // Simplify path removing leading "./"
+ while ( aEntry.sPath.compareToAscii( "./", 2 ) == 0 )
+ aEntry.sPath = aEntry.sPath.copy( 2 );
+
+ // skip: file name (already processed)
+ aMemGrabber.skipBytes( aEntry.nPathLen );
+ readExtraFields( aMemGrabber, aEntry.nCENExtraLen,
aEntry );
+ // Search for Zip64 data among the comments.
+ // The Zip64 EOCDL takes 20 bytes; the ZIP64 EOCDR
takes > 56 bytes
+ const sal_Int8* pCommentStart =
aMemGrabber.getCurrentPos();
+ for ( const sal_Int8* pCommentEnd = pCommentStart +
nCommentLen;
+ pCommentEnd >= ( pCommentStart + 76 );
+ --pCommentEnd) {
+ const sal_Int8 * p = ( pCommentEnd - 20 );
+ sal_Int32 n32;
+ if (p[0] == 0x50)
+ // Detect EOCDL signature 0x07064b50
+ if ( ( p[0] == 0x50 ) &&
+ ( p[1] == 0x4b ) &&
+ ( p[2] == 0x06 ) &&
+ ( p[3] == 0x07 ) ) {
+ // the relative offset of the zip64 end
of central directory record starts at offset 8...
+ n32 = p[8] | ( p[9] << 8 ) | ( p[10] <<
16 ) |
+ ( p[11] << 24 );
+ // ...and it must be a 32-bit number
represented with 64 bits
+ if ( ( p[12] | p[13] | p[14] | p[15] )
== 0 ) {
+ n32 -= nCenPos +
aMemGrabber.getPosition();
+ // Detect EOCDR signature
0x06064b50
+ if ( ( n32 > 0 ) &&
+ ( pCommentStart[n32]
== 0x50 ) &&
+ ( pCommentStart[n32 +
1] == 0x4b ) &&
+ ( pCommentStart[n32 +
2] == 0x06 ) &&
+ ( pCommentStart[n32 +
3] == 0x06 ) ) {
+ throw
ZipException ( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "Zip64 data found
inside comments section") ), uno::Reference < XInterface > () );
+ }
+ }
+ }
+ }
+ aMemGrabber.skipBytes( nCommentLen );
+ if ( entriesContainPath( aEntries, aEntry.sPath ) )
+ throw ZipException( OUString(
RTL_CONSTASCII_USTRINGPARAM ( "Duplicated zip entry") ), uno::Reference <
XInterface > () );
+ readLOC( aEntry );
+ if ( aEntry.nFileHeaderOffset == 0 )
+ sFirstFilePath = aEntry.sPath;
aEntries[aEntry.sPath] = aEntry;
}
if (nCount != nTotal)
throw ZipException(OUString(
RTL_CONSTASCII_USTRINGPARAM ( "Count != Total") ), uno::Reference < XInterface
> () );
+
+ if ( sFirstFilePath.getLength() == 0 )
+ throw ZipException(OUString(
RTL_CONSTASCII_USTRINGPARAM ( "File contains junk before compressed data" ) ),
uno::Reference < XInterface > () );
+
+ // Verify the proper sequence of fields
+ sal_Int32 nPos, nPos2;
+ EntryHash::const_iterator aIt = aEntries.find( sFirstFilePath );
+ nCount = 0;
+ nPos = 0;
+ do {
+ if ( aIt == aEntries.end() ) throw
ZipException(OUString( RTL_CONSTASCII_USTRINGPARAM ( "Internal error" ) ),
uno::Reference < XInterface > () );
+ // Local file header
+ nPos += 30 + aIt->first.getLength() +
aIt->second.nLOCExtraLen;
+ if ( nPos != aIt->second.nFileDataOffset )
+ throw ZipException(OUString(
RTL_CONSTASCII_USTRINGPARAM ( "ZIP file contains a hole between local
descriptor and file data" ) ), uno::Reference < XInterface > () );
+ // File data
+ nPos += aIt->second.nCompressedSize;
+ if ( aIt->second.nFlag & ( 1 << 3 ) ) {
+ // Data descriptor...
+ nPos += 16; // ...with signature
+ nPos2 = nPos - 4; // ...without signature
+ } else nPos2 = nPos;
+ ++nCount;
+ if ( nCount == nTotal ) {
+ break;
+ }
+ // Find next entry, starting at either nPos or nPos2
+ for ( aIt = aEntries.begin(); aIt != aEntries.end();
++aIt )
+ if (( aIt->second.nFileHeaderOffset == nPos ) ||
+ ( aIt->second.nFileHeaderOffset ==
nPos2 ))
+ break;
+ if ( aIt == aEntries.end() )
+ throw ZipException(OUString(
RTL_CONSTASCII_USTRINGPARAM ( "ZIP file contains holes among files" ) ),
uno::Reference < XInterface > () );
+ nPos = aIt->second.nFileHeaderOffset;
+ } while ( 1 );
+ // After all files, we should have arrived at the central
directory header
+ if ( nPos != nCenPos )
+ throw ZipException(OUString(
RTL_CONSTASCII_USTRINGPARAM ( "ZIP file contains a hole before the central
directory" ) ), uno::Reference < XInterface > () );
}
catch ( IllegalArgumentException & )
{
@@ -874,13 +1057,14 @@ sal_Int32 ZipFile::recover()
{
if ( nPos < nBufSize - 30 && pBuffer[nPos] ==
'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 3 && pBuffer[nPos+3] == 4 )
{
+ // Local file header
ZipEntry aEntry;
MemoryByteGrabber aMemGrabber (
Sequence< sal_Int8 >( ((sal_Int8*)(&(pBuffer[nPos+4]))), 26 ) );
aMemGrabber >> aEntry.nVersion;
- if ( ( aEntry.nVersion & 1 ) != 1 )
+ aMemGrabber >> aEntry.nFlag;
+ if ( ( aEntry.nFlag & 1 ) != 1 ) //
Must not be encrypted
{
- aMemGrabber >> aEntry.nFlag;
aMemGrabber >> aEntry.nMethod;
if ( aEntry.nMethod == STORED
|| aEntry.nMethod == DEFLATED )
@@ -890,7 +1074,7 @@ sal_Int32 ZipFile::recover()
aMemGrabber >>
aEntry.nCompressedSize;
aMemGrabber >>
aEntry.nSize;
aMemGrabber >>
aEntry.nPathLen;
- aMemGrabber >>
aEntry.nExtraLen;
+ aMemGrabber >>
aEntry.nLOCExtraLen;
sal_Int32 nDescrLength =
(
aEntry.nMethod == DEFLATED && ( aEntry.nFlag & 8 ) ) ?
@@ -902,12 +1086,12 @@ sal_Int32 ZipFile::recover()
if (
aEntry.nCompressedSize < 0 ) aEntry.nCompressedSize = 0x7FFFFFFF;
if ( aEntry.nSize < 0 )
aEntry.nSize = 0x7FFFFFFF;
if ( aEntry.nPathLen <
0 ) aEntry.nPathLen = 0x7FFF;
- if ( aEntry.nExtraLen <
0 ) aEntry.nExtraLen = 0x7FFF;
+ if (
aEntry.nLOCExtraLen < 0 ) aEntry.nLOCExtraLen = 0x7FFF;
// End of quick fix
sal_Int32 nDataSize = (
aEntry.nMethod == DEFLATED ) ? aEntry.nCompressedSize : aEntry.nSize;
- sal_Int32 nBlockLength
= nDataSize + aEntry.nPathLen + aEntry.nExtraLen + 30 + nDescrLength;
- if ( aEntry.nPathLen >=
0 && aEntry.nExtraLen >= 0
+ sal_Int32 nBlockLength
= nDataSize + aEntry.nPathLen + aEntry.nLOCExtraLen + 30 + nDescrLength;
+ if ( aEntry.nPathLen >=
0 && aEntry.nLOCExtraLen >= 0
&& ( nGenPos +
nPos + nBlockLength ) <= nLength )
{
// read always in UTF8, some tools seem not to
set UTF8 bit
@@ -926,7 +1110,7 @@ sal_Int32 ZipFile::recover()
aEntry.nPathLen = static_cast< sal_Int16 >(aFileName.getLength());
}
- aEntry.nOffset
= nGenPos + nPos + 30 + aEntry.nPathLen + aEntry.nExtraLen;
+
aEntry.nFileDataOffset = nGenPos + nPos + 30 + aEntry.nPathLen +
aEntry.nLOCExtraLen;
if ( (
aEntry.nSize || aEntry.nCompressedSize ) && !checkSizeAndCRC( aEntry ) )
{
@@ -944,7 +1128,7 @@ sal_Int32 ZipFile::recover()
nPos += 4;
}
else if (pBuffer[nPos] == 'P' &&
pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 7 && pBuffer[nPos+3] == 8 )
- {
+ { // This is a data descriptor
sal_Int32 nCompressedSize, nSize,
nCRC32;
MemoryByteGrabber aMemGrabber (
Sequence< sal_Int8 >( ((sal_Int8*)(&(pBuffer[nPos+4]))), 12 ) );
aMemGrabber >> nCRC32;
@@ -959,7 +1143,7 @@ sal_Int32 ZipFile::recover()
if( (*aIter).second.nFlag & 8 )
{
sal_Int32 nStreamOffset
= nGenPos + nPos - nCompressedSize;
- if ( nStreamOffset ==
(*aIter).second.nOffset && nCompressedSize > (*aIter).second.nCompressedSize )
+ if ( nStreamOffset ==
(*aIter).second.nFileDataOffset && nCompressedSize >
(*aIter).second.nCompressedSize )
{
// only DEFLATED blocks need to be checked
sal_Bool bAcceptBlock = (
(*aIter).second.nMethod == STORED && nCompressedSize == nSize );
@@ -983,8 +1167,8 @@ sal_Int32 ZipFile::recover()
else if(
!(*aIter).second.nCompressedSize )
{
(*aIter).second.nCrc = nCRC32;
- sal_Int32
nRealStreamSize = nGenPos + nPos - (*aIter).second.nOffset;
-
(*aIter).second.nCompressedSize = nGenPos + nPos - (*aIter).second.nOffset;
+ sal_Int32
nRealStreamSize = nGenPos + nPos - (*aIter).second.nFileDataOffset;
+
(*aIter).second.nCompressedSize = nGenPos + nPos -
(*aIter).second.nFileDataOffset;
(*aIter).second.nSize = nSize;
}
#endif
@@ -1024,13 +1208,13 @@ sal_Bool ZipFile::checkSizeAndCRC( const ZipEntry&
aEntry )
sal_Int32 nSize = 0, nCRC = 0;
if( aEntry.nMethod == STORED )
- return ( getCRC( aEntry.nOffset, aEntry.nSize ) == aEntry.nCrc
);
+ return ( getCRC( aEntry.nFileDataOffset, aEntry.nSize ) ==
aEntry.nCrc );
- getSizeAndCRC( aEntry.nOffset, aEntry.nCompressedSize, &nSize, &nCRC );
+ getSizeAndCRC( aEntry.nFileDataOffset, aEntry.nCompressedSize, &nSize,
&nCRC );
return ( aEntry.nSize == nSize && aEntry.nCrc == nCRC );
}
-sal_Int32 ZipFile::getCRC( sal_Int32 nOffset, sal_Int32 nSize )
+sal_Int32 ZipFile::getCRC( sal_Int32 nFileDataOffset, sal_Int32 nSize )
{
::osl::MutexGuard aGuard( m_aMutex );
@@ -1038,7 +1222,7 @@ sal_Int32 ZipFile::getCRC( sal_Int32 nOffset, sal_Int32
nSize )
CRC32 aCRC;
sal_Int32 nBlockSize = ::std::min( nSize, static_cast< sal_Int32 >(
32000 ) );
- aGrabber.seek( nOffset );
+ aGrabber.seek( nFileDataOffset );
for ( int ind = 0;
aGrabber.readBytes( aBuffer, nBlockSize ) && ind * nBlockSize
< nSize;
ind++ )
@@ -1049,7 +1233,7 @@ sal_Int32 ZipFile::getCRC( sal_Int32 nOffset, sal_Int32
nSize )
return aCRC.getValue();
}
-void ZipFile::getSizeAndCRC( sal_Int32 nOffset, sal_Int32 nCompressedSize,
sal_Int32 *nSize, sal_Int32 *nCRC )
+void ZipFile::getSizeAndCRC( sal_Int32 nFileDataOffset, sal_Int32
nCompressedSize, sal_Int32 *nSize, sal_Int32 *nCRC )
{
::osl::MutexGuard aGuard( m_aMutex );
@@ -1059,7 +1243,7 @@ void ZipFile::getSizeAndCRC( sal_Int32 nOffset, sal_Int32
nCompressedSize, sal_I
Inflater aInflaterLocal( sal_True );
sal_Int32 nBlockSize = ::std::min( nCompressedSize, static_cast<
sal_Int32 >( 32000 ) );
- aGrabber.seek( nOffset );
+ aGrabber.seek( nFileDataOffset );
for ( int ind = 0;
!aInflaterLocal.finished() && aGrabber.readBytes( aBuffer,
nBlockSize ) && ind * nBlockSize < nCompressedSize;
ind++ )
diff --git a/main/package/source/package/zipapi/ZipOutputStream.cxx
b/main/package/source/package/zipapi/ZipOutputStream.cxx
index ea1bbfc64e..9cbc68e418 100644
--- a/main/package/source/package/zipapi/ZipOutputStream.cxx
+++ b/main/package/source/package/zipapi/ZipOutputStream.cxx
@@ -110,7 +110,7 @@ void SAL_CALL ZipOutputStream::putNextEntry( ZipEntry&
rEntry,
m_pCurrentStream = pStream;
}
sal_Int32 nLOCLength = writeLOC(rEntry);
- rEntry.nOffset = static_cast < sal_Int32 > (aChucker.GetPosition()) -
nLOCLength;
+ rEntry.nFileHeaderOffset = static_cast < sal_Int32 >
(aChucker.GetPosition()) - nLOCLength;
aZipList.push_back( &rEntry );
pCurrentEntry = &rEntry;
}
@@ -239,10 +239,10 @@ void SAL_CALL ZipOutputStream::finish( )
if (aZipList.size() < 1)
OSL_ENSURE(false,"Zip file must have at least one entry!\n");
- sal_Int32 nOffset= static_cast < sal_Int32 > (aChucker.GetPosition());
+ sal_Int32 nCENOffset= static_cast < sal_Int32 >
(aChucker.GetPosition());
for (sal_Int32 i =0, nEnd = aZipList.size(); i < nEnd; i++)
writeCEN( *aZipList[i] );
- writeEND( nOffset, static_cast < sal_Int32 > (aChucker.GetPosition()) -
nOffset);
+ writeEND( nCENOffset, static_cast < sal_Int32 >
(aChucker.GetPosition()) - nCENOffset);
bFinished = sal_True;
xStream->flush();
}
@@ -296,7 +296,7 @@ void ZipOutputStream::doDeflate()
}
}
-void ZipOutputStream::writeEND(sal_uInt32 nOffset, sal_uInt32 nLength)
+void ZipOutputStream::writeEND(sal_uInt32 nCENOffset, sal_uInt32 nLength)
throw(IOException, RuntimeException)
{
aChucker << ENDSIG;
@@ -305,7 +305,7 @@ void ZipOutputStream::writeEND(sal_uInt32 nOffset,
sal_uInt32 nLength)
aChucker << static_cast < sal_Int16 > ( aZipList.size() );
aChucker << static_cast < sal_Int16 > ( aZipList.size() );
aChucker << nLength;
- aChucker << nOffset;
+ aChucker << nCENOffset;
aChucker << static_cast < sal_Int16 > ( 0 );
}
void ZipOutputStream::writeCEN( const ZipEntry &rEntry )
@@ -338,12 +338,12 @@ void ZipOutputStream::writeCEN( const ZipEntry &rEntry )
aChucker << rEntry.nCompressedSize;
aChucker << rEntry.nSize;
aChucker << nNameLength;
- aChucker << static_cast < sal_Int16> (0);
- aChucker << static_cast < sal_Int16> (0);
- aChucker << static_cast < sal_Int16> (0);
- aChucker << static_cast < sal_Int16> (0);
- aChucker << static_cast < sal_Int32> (0);
- aChucker << rEntry.nOffset;
+ aChucker << static_cast < sal_Int16> (0); // extra field length
+ aChucker << static_cast < sal_Int16> (0); // file comment length
+ aChucker << static_cast < sal_Int16> (0); // disk number start
+ aChucker << static_cast < sal_Int16> (0); // internal file attributes
+ aChucker << static_cast < sal_Int32> (0); // external file attributes
+ aChucker << rEntry.nFileHeaderOffset;
Sequence < sal_Int8 > aSequence( (sal_Int8*)sUTF8Name.getStr(),
sUTF8Name.getLength() );
aChucker.WriteBytes( aSequence );
@@ -367,40 +367,40 @@ sal_Int32 ZipOutputStream::writeLOC( const ZipEntry
&rEntry )
sal_Int16 nNameLength = static_cast < sal_Int16 > (
sUTF8Name.getLength() );
aChucker << LOCSIG;
- aChucker << rEntry.nVersion;
+ aChucker << rEntry.nVersion; // version needed to extract
if (rEntry.nFlag & (1 << 4) )
{
// If it's an encrypted entry, we pretend its stored plain text
sal_Int16 nTmpFlag = rEntry.nFlag;
nTmpFlag &= ~(1 <<4 );
- aChucker << nTmpFlag;
- aChucker << static_cast < sal_Int16 > ( STORED );
+ aChucker << nTmpFlag; // general purpose bit flag
+ aChucker << static_cast < sal_Int16 > ( STORED ); //
compression method
}
else
{
- aChucker << rEntry.nFlag;
- aChucker << rEntry.nMethod;
+ aChucker << rEntry.nFlag; // general purpose bit flag
+ aChucker << rEntry.nMethod; // compression method
}
- aChucker << static_cast < sal_uInt32 > (rEntry.nTime);
+ aChucker << static_cast < sal_uInt32 > (rEntry.nTime); // last mod file
time & date
if ((rEntry.nFlag & 8) == 8 )
{
- aChucker << static_cast < sal_Int32 > (0);
- aChucker << static_cast < sal_Int32 > (0);
- aChucker << static_cast < sal_Int32 > (0);
+ aChucker << static_cast < sal_Int32 > (0); // crc-32
+ aChucker << static_cast < sal_Int32 > (0); // compressed size
+ aChucker << static_cast < sal_Int32 > (0); // uncompressed size
}
else
{
- aChucker << static_cast < sal_uInt32 > (rEntry.nCrc);
- aChucker << rEntry.nCompressedSize;
- aChucker << rEntry.nSize;
+ aChucker << static_cast < sal_uInt32 > (rEntry.nCrc); // crc-32
+ aChucker << rEntry.nCompressedSize; // compressed size
+ aChucker << rEntry.nSize; // uncompressed size
}
- aChucker << nNameLength;
- aChucker << static_cast < sal_Int16 > (0);
+ aChucker << nNameLength; // file name length
+ aChucker << static_cast < sal_Int16 > (0); // extra field length
Sequence < sal_Int8 > aSequence( (sal_Int8*)sUTF8Name.getStr(),
sUTF8Name.getLength() );
- aChucker.WriteBytes( aSequence );
+ aChucker.WriteBytes( aSequence ); // file name
return LOCHDR + nNameLength;
}
diff --git a/main/package/source/package/zippackage/ZipPackage.cxx
b/main/package/source/package/zippackage/ZipPackage.cxx
index 9885debfdf..3a4da44702 100644
--- a/main/package/source/package/zippackage/ZipPackage.cxx
+++ b/main/package/source/package/zippackage/ZipPackage.cxx
@@ -536,6 +536,7 @@ void ZipPackage::getZipFileContents()
OUString sTemp, sDirName;
sal_Int32 nOldIndex, nIndex, nStreamIndex;
FolderHash::iterator aIter;
+ sal_Bool mustHaveEncryption = sal_False;
while ( pEnum->hasMoreElements() )
{
@@ -591,12 +592,16 @@ void ZipPackage::getZipFileContents()
pPkgStream->setName( sTemp );
pPkgStream->doSetParent( pCurrent, sal_True );
}
+ if ( ( rEntry.nMethod == STORED ) && rEntry.bHasDataDescriptor )
+ mustHaveEncryption = sal_True;
}
if ( m_nFormat == embed::StorageFormats::PACKAGE )
parseManifest();
else if ( m_nFormat == embed::StorageFormats::OFOPXML )
parseContentType();
+ if ( mustHaveEncryption && !m_bHasEncryptedEntries )
+ throw ZipException( OUString( RTL_CONSTASCII_USTRINGPARAM (
"inconsistent ZIP entries" ) ), uno::Reference < XInterface > () );
}
//--------------------------------------------------------
diff --git a/main/package/source/package/zippackage/ZipPackageFolder.cxx
b/main/package/source/package/zippackage/ZipPackageFolder.cxx
index 71f8552788..fc8deecfe5 100644
--- a/main/package/source/package/zippackage/ZipPackageFolder.cxx
+++ b/main/package/source/package/zippackage/ZipPackageFolder.cxx
@@ -75,7 +75,9 @@ ZipPackageFolder::ZipPackageFolder ( const uno::Reference<
XMultiServiceFactory
aEntry.nCrc = 0;
aEntry.nCompressedSize = 0;
aEntry.nSize = 0;
- aEntry.nOffset = -1;
+ aEntry.nFileHeaderOffset = -1;
+ aEntry.nFileDataOffset = -1;
+ aEntry.bHasDataDescriptor = sal_False;
uno::Sequence < sal_Int8 > &rCachedImplId = lcl_CachedImplId::get();
if ( !rCachedImplId.getLength() )
rCachedImplId = getImplementationId();
@@ -176,10 +178,13 @@ void ZipPackageFolder::copyZipEntry( ZipEntry &rDest,
const ZipEntry &rSource)
rDest.nCrc = rSource.nCrc;
rDest.nCompressedSize = rSource.nCompressedSize;
rDest.nSize = rSource.nSize;
- rDest.nOffset = rSource.nOffset;
+ rDest.nFileHeaderOffset = rSource.nFileHeaderOffset;
+ rDest.nFileDataOffset = rSource.nFileDataOffset;
rDest.sPath = rSource.sPath;
rDest.nPathLen = rSource.nPathLen;
- rDest.nExtraLen = rSource.nExtraLen;
+ rDest.nLOCExtraLen = rSource.nLOCExtraLen;
+ rDest.nCENExtraLen = rSource.nCENExtraLen;
+ rDest.bHasDataDescriptor= rSource.bHasDataDescriptor;
}
const ::com::sun::star::uno::Sequence < sal_Int8 >&
ZipPackageFolder::static_getImplementationId()
@@ -669,7 +674,7 @@ bool ZipPackageFolder::saveChild( const ::rtl::OUString
&rShortName, const Conte
if ( rInfo.pStream->IsEncrypted() )
rInfo.pStream->setSize( nOwnStreamOrigSize );
- rInfo.pStream->aEntry.nOffset *= -1;
+ rInfo.pStream->aEntry.nFileDataOffset =
rInfo.pStream->aEntry.nFileHeaderOffset;
}
}
@@ -692,7 +697,7 @@ void ZipPackageFolder::saveContents( ::rtl::OUString
&rPath, std::vector < uno::
ZipEntry* pTempEntry = new ZipEntry();
ZipPackageFolder::copyZipEntry ( *pTempEntry, aEntry );
pTempEntry->nPathLen = (sal_Int16)( ::rtl::OUStringToOString(
rPath, RTL_TEXTENCODING_UTF8 ).getLength() );
- pTempEntry->nExtraLen = -1;
+ pTempEntry->nLOCExtraLen = -1;
pTempEntry->sPath = rPath;
try
diff --git a/main/package/source/package/zippackage/ZipPackageStream.cxx
b/main/package/source/package/zippackage/ZipPackageStream.cxx
index 7cb6caff72..3bfa7bd7bb 100644
--- a/main/package/source/package/zippackage/ZipPackageStream.cxx
+++ b/main/package/source/package/zippackage/ZipPackageStream.cxx
@@ -96,9 +96,12 @@ ZipPackageStream::ZipPackageStream ( ZipPackage &
rNewPackage,
aEntry.nCrc = -1;
aEntry.nCompressedSize = -1;
aEntry.nSize = -1;
- aEntry.nOffset = -1;
+ aEntry.nFileHeaderOffset = -1;
+ aEntry.nFileDataOffset = -1;
aEntry.nPathLen = -1;
- aEntry.nExtraLen = -1;
+ aEntry.nLOCExtraLen = -1;
+ aEntry.nCENExtraLen = -1;
+ aEntry.bHasDataDescriptor = sal_False;
Sequence < sal_Int8 > &rCachedImplId = lcl_CachedImplId::get();
if ( !rCachedImplId.getLength() )
@@ -118,10 +121,13 @@ void ZipPackageStream::setZipEntryOnLoading( const
ZipEntry &rInEntry )
aEntry.nCrc = rInEntry.nCrc;
aEntry.nCompressedSize = rInEntry.nCompressedSize;
aEntry.nSize = rInEntry.nSize;
- aEntry.nOffset = rInEntry.nOffset;
+ aEntry.nFileHeaderOffset = rInEntry.nFileHeaderOffset;
+ aEntry.nFileDataOffset = rInEntry.nFileDataOffset;
aEntry.sPath = rInEntry.sPath;
aEntry.nPathLen = rInEntry.nPathLen;
- aEntry.nExtraLen = rInEntry.nExtraLen;
+ aEntry.nLOCExtraLen = rInEntry.nLOCExtraLen;
+ aEntry.nCENExtraLen = rInEntry.nCENExtraLen;
+ aEntry.bHasDataDescriptor = rInEntry.bHasDataDescriptor;
if ( aEntry.nMethod == STORED )
bToBeCompressed = sal_False;