Coverage Report

Created: 2026-07-10 11:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/package/source/zipapi/ZipFile.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <com/sun/star/io/BufferSizeExceededException.hpp>
21
#include <com/sun/star/io/NotConnectedException.hpp>
22
#include <com/sun/star/lang/IllegalArgumentException.hpp>
23
#include <com/sun/star/packages/NoEncryptionException.hpp>
24
#include <com/sun/star/packages/WrongPasswordException.hpp>
25
#include <com/sun/star/packages/zip/ZipConstants.hpp>
26
#include <com/sun/star/packages/zip/ZipException.hpp>
27
#include <com/sun/star/packages/zip/ZipIOException.hpp>
28
#include <com/sun/star/xml/crypto/XCipherContext.hpp>
29
#include <com/sun/star/xml/crypto/XDigestContext.hpp>
30
#include <com/sun/star/xml/crypto/CipherID.hpp>
31
#include <com/sun/star/xml/crypto/DigestID.hpp>
32
#include <com/sun/star/xml/crypto/NSSInitializer.hpp>
33
34
#include <comphelper/bytereader.hxx>
35
#include <comphelper/storagehelper.hxx>
36
#include <comphelper/processfactory.hxx>
37
#include <comphelper/threadpool.hxx>
38
#include <rtl/digest.h>
39
#include <rtl/crc.h>
40
#include <sal/log.hxx>
41
#include <o3tl/safeint.hxx>
42
#include <o3tl/string_view.hxx>
43
#include <osl/diagnose.h>
44
45
#include <algorithm>
46
#include <iterator>
47
#include <utility>
48
#include <vector>
49
50
#include <argon2.h>
51
52
#include "blowfishcontext.hxx"
53
#include "sha1context.hxx"
54
#include <ZipFile.hxx>
55
#include <ZipEnumeration.hxx>
56
#include "XUnbufferedStream.hxx"
57
#include "XBufferedThreadedStream.hxx"
58
#include <PackageConstants.hxx>
59
#include <EncryptedDataHeader.hxx>
60
#include <EncryptionData.hxx>
61
#include "MemoryByteGrabber.hxx"
62
63
#include <CRC32.hxx>
64
#include <package/InflateZlib.hxx>
65
#include <InflaterBytesZlib.hxx>
66
67
using namespace com::sun::star;
68
using namespace com::sun::star::io;
69
using namespace com::sun::star::uno;
70
using namespace com::sun::star::lang;
71
using namespace com::sun::star::packages;
72
using namespace com::sun::star::packages::zip;
73
using namespace com::sun::star::packages::zip::ZipConstants;
74
75
using ZipUtils::Inflater;
76
77
/** This class is used to read entries from a zip file
78
 */
79
ZipFile::ZipFile( rtl::Reference< comphelper::RefCountedMutex > aMutexHolder,
80
                  uno::Reference < XInputStream > const &xInput,
81
                  uno::Reference < XComponentContext > xContext,
82
                  bool bInitialise, bool bForceRecovery,
83
                  Checks const checks)
84
21.8k
: m_aMutexHolder(std::move( aMutexHolder ))
85
21.8k
, m_Checks(checks)
86
21.8k
, aGrabber( xInput )
87
21.8k
, xStream(xInput)
88
21.8k
, m_xContext (std::move( xContext ))
89
21.8k
, bRecoveryMode( bForceRecovery )
90
21.8k
{
91
21.8k
    if (bInitialise)
92
21.8k
    {
93
21.8k
        if ( bForceRecovery )
94
0
        {
95
0
            recover();
96
0
        }
97
21.8k
        else if ( readCEN() == -1 )
98
0
        {
99
0
            aEntries.clear();
100
0
            m_EntriesInsensitive.clear();
101
0
            throw ZipException(u"stream data looks to be broken"_ustr );
102
0
        }
103
21.8k
    }
104
21.8k
}
105
106
ZipFile::~ZipFile()
107
19.9k
{
108
19.9k
    aEntries.clear();
109
19.9k
}
110
111
void ZipFile::setInputStream ( const uno::Reference < XInputStream >& xNewStream )
112
0
{
113
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
114
115
0
    xStream = xNewStream;
116
0
    aGrabber.setInputStream ( xStream );
117
0
}
118
119
uno::Reference< xml::crypto::XDigestContext > ZipFile::StaticGetDigestContextForChecksum( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData )
120
0
{
121
0
    assert(xEncryptionData->m_oCheckAlg); // callers checked it already
122
123
0
    uno::Reference< xml::crypto::XDigestContext > xDigestContext;
124
0
    if (*xEncryptionData->m_oCheckAlg == xml::crypto::DigestID::SHA256_1K)
125
0
    {
126
0
        uno::Reference< uno::XComponentContext > xContext = xArgContext;
127
0
        if ( !xContext.is() )
128
0
            xContext = comphelper::getProcessComponentContext();
129
130
0
        uno::Reference< xml::crypto::XNSSInitializer > xDigestContextSupplier = xml::crypto::NSSInitializer::create( xContext );
131
132
0
        xDigestContext.set(xDigestContextSupplier->getDigestContext(
133
0
                *xEncryptionData->m_oCheckAlg, uno::Sequence<beans::NamedValue>()),
134
0
            uno::UNO_SET_THROW);
135
0
    }
136
0
    else if (*xEncryptionData->m_oCheckAlg == xml::crypto::DigestID::SHA1_1K)
137
0
    {
138
0
        if (xEncryptionData->m_bTryWrongSHA1)
139
0
        {
140
0
            xDigestContext.set(StarOfficeSHA1DigestContext::Create(), uno::UNO_SET_THROW);
141
0
        }
142
0
        else
143
0
        {
144
0
            xDigestContext.set(CorrectSHA1DigestContext::Create(), uno::UNO_SET_THROW);
145
0
        }
146
0
    }
147
148
0
    return xDigestContext;
149
0
}
150
151
uno::Reference< xml::crypto::XCipherContext > ZipFile::StaticGetCipher( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData, bool bEncrypt )
152
0
{
153
0
    uno::Reference< xml::crypto::XCipherContext > xResult;
154
155
0
    if (xEncryptionData->m_nDerivedKeySize < 0)
156
0
    {
157
0
        throw ZipIOException(u"Invalid derived key length!"_ustr );
158
0
    }
159
160
0
    uno::Sequence< sal_Int8 > aDerivedKey( xEncryptionData->m_nDerivedKeySize );
161
0
    if (!xEncryptionData->m_oPBKDFIterationCount && !xEncryptionData->m_oArgon2Args
162
0
        && xEncryptionData->m_nDerivedKeySize == xEncryptionData->m_aKey.getLength())
163
0
    {
164
        // gpg4libre: no need to derive key, m_aKey is already
165
        // usable as symmetric session key
166
0
        aDerivedKey = xEncryptionData->m_aKey;
167
0
    }
168
0
    else if (xEncryptionData->m_oArgon2Args)
169
0
    {
170
        // apparently multiple lanes cannot be processed in parallel (the
171
        // implementation will clamp), but it doesn't make sense to have more
172
        // threads than CPUs
173
0
        uint32_t const threads(::comphelper::ThreadPool::getPreferredConcurrency());
174
        // need to use context to set a fixed version
175
0
        argon2_context context = {
176
0
            .out = reinterpret_cast<uint8_t *>(aDerivedKey.getArray()),
177
0
            .outlen = ::sal::static_int_cast<uint32_t>(aDerivedKey.getLength()),
178
0
            .pwd = reinterpret_cast<uint8_t *>(xEncryptionData->m_aKey.getArray()),
179
0
            .pwdlen = ::sal::static_int_cast<uint32_t>(xEncryptionData->m_aKey.getLength()),
180
0
            .salt = reinterpret_cast<uint8_t *>(xEncryptionData->m_aSalt.getArray()),
181
0
            .saltlen = ::sal::static_int_cast<uint32_t>(xEncryptionData->m_aSalt.getLength()),
182
0
            .secret = nullptr, .secretlen = 0,
183
0
            .ad = nullptr, .adlen = 0,
184
0
            .t_cost = ::sal::static_int_cast<uint32_t>(::std::get<0>(*xEncryptionData->m_oArgon2Args)),
185
0
            .m_cost = ::sal::static_int_cast<uint32_t>(::std::get<1>(*xEncryptionData->m_oArgon2Args)),
186
0
            .lanes = ::sal::static_int_cast<uint32_t>(::std::get<2>(*xEncryptionData->m_oArgon2Args)),
187
0
            .threads = threads,
188
0
            .version = ARGON2_VERSION_13,
189
0
            .allocate_cbk = nullptr, .free_cbk = nullptr,
190
0
            .flags = ARGON2_DEFAULT_FLAGS
191
0
        };
192
        // libargon2 validates all the arguments so don't need to do it here
193
0
        int const rc = argon2id_ctx(&context);
194
0
        if (rc != ARGON2_OK)
195
0
        {
196
0
            SAL_WARN("package", "argon2id_ctx failed to derive key: " << argon2_error_message(rc));
197
0
            throw ZipIOException(u"argon2id_ctx failed to derive key"_ustr);
198
0
        }
199
0
    }
200
0
    else if ( rtl_Digest_E_None != rtl_digest_PBKDF2( reinterpret_cast< sal_uInt8* >( aDerivedKey.getArray() ),
201
0
                        aDerivedKey.getLength(),
202
0
                        reinterpret_cast< const sal_uInt8 * > (xEncryptionData->m_aKey.getConstArray() ),
203
0
                        xEncryptionData->m_aKey.getLength(),
204
0
                        reinterpret_cast< const sal_uInt8 * > ( xEncryptionData->m_aSalt.getConstArray() ),
205
0
                        xEncryptionData->m_aSalt.getLength(),
206
0
                        *xEncryptionData->m_oPBKDFIterationCount) )
207
0
    {
208
0
        throw ZipIOException(u"Can not create derived key!"_ustr );
209
0
    }
210
211
0
    if (xEncryptionData->m_nEncAlg == xml::crypto::CipherID::AES_CBC_W3C_PADDING
212
0
        || xEncryptionData->m_nEncAlg == xml::crypto::CipherID::AES_GCM_W3C)
213
0
    {
214
0
        uno::Reference< uno::XComponentContext > xContext = xArgContext;
215
0
        if ( !xContext.is() )
216
0
            xContext = comphelper::getProcessComponentContext();
217
218
0
        uno::Reference< xml::crypto::XNSSInitializer > xCipherContextSupplier = xml::crypto::NSSInitializer::create( xContext );
219
220
0
        xResult = xCipherContextSupplier->getCipherContext( xEncryptionData->m_nEncAlg, aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt, uno::Sequence< beans::NamedValue >() );
221
0
    }
222
0
    else if ( xEncryptionData->m_nEncAlg == xml::crypto::CipherID::BLOWFISH_CFB_8 )
223
0
    {
224
0
        xResult = BlowfishCFB8CipherContext::Create( aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt );
225
0
    }
226
0
    else
227
0
    {
228
0
        throw ZipIOException(u"Unknown cipher algorithm is requested!"_ustr );
229
0
    }
230
231
0
    return xResult;
232
0
}
233
234
void ZipFile::StaticFillHeader( const ::rtl::Reference< EncryptionData >& rData,
235
                                sal_Int64 nSize,
236
                                const OUString& aMediaType,
237
                                sal_Int8 * & pHeader )
238
0
{
239
    // I think it's safe to restrict vector and salt length to 2 bytes !
240
0
    sal_Int16 nIVLength = static_cast < sal_Int16 > ( rData->m_aInitVector.getLength() );
241
0
    sal_Int16 nSaltLength = static_cast < sal_Int16 > ( rData->m_aSalt.getLength() );
242
0
    sal_Int16 nDigestLength = static_cast < sal_Int16 > ( rData->m_aDigest.getLength() );
243
0
    sal_Int16 nMediaTypeLength = static_cast < sal_Int16 > ( aMediaType.getLength() * sizeof( sal_Unicode ) );
244
245
    // First the header
246
0
    *(pHeader++) = ( n_ConstHeader >> 0 ) & 0xFF;
247
0
    *(pHeader++) = ( n_ConstHeader >> 8 ) & 0xFF;
248
0
    *(pHeader++) = ( n_ConstHeader >> 16 ) & 0xFF;
249
0
    *(pHeader++) = ( n_ConstHeader >> 24 ) & 0xFF;
250
251
    // Then the version
252
0
    *(pHeader++) = ( n_ConstCurrentVersion >> 0 ) & 0xFF;
253
0
    *(pHeader++) = ( n_ConstCurrentVersion >> 8 ) & 0xFF;
254
255
    // Then the iteration Count
256
0
    sal_Int32 const nIterationCount = rData->m_oPBKDFIterationCount ? *rData->m_oPBKDFIterationCount : 0;
257
0
    *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 0 ) & 0xFF);
258
0
    *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 8 ) & 0xFF);
259
0
    *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 16 ) & 0xFF);
260
0
    *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 24 ) & 0xFF);
261
262
0
    sal_Int32 const nArgon2t = rData->m_oArgon2Args ? ::std::get<0>(*rData->m_oArgon2Args) : 0;
263
0
    *(pHeader++) = static_cast<sal_Int8>((nArgon2t >> 0) & 0xFF);
264
0
    *(pHeader++) = static_cast<sal_Int8>((nArgon2t >> 8) & 0xFF);
265
0
    *(pHeader++) = static_cast<sal_Int8>((nArgon2t >> 16) & 0xFF);
266
0
    *(pHeader++) = static_cast<sal_Int8>((nArgon2t >> 24) & 0xFF);
267
268
0
    sal_Int32 const nArgon2m = rData->m_oArgon2Args ? ::std::get<1>(*rData->m_oArgon2Args) : 0;
269
0
    *(pHeader++) = static_cast<sal_Int8>((nArgon2m >> 0) & 0xFF);
270
0
    *(pHeader++) = static_cast<sal_Int8>((nArgon2m >> 8) & 0xFF);
271
0
    *(pHeader++) = static_cast<sal_Int8>((nArgon2m >> 16) & 0xFF);
272
0
    *(pHeader++) = static_cast<sal_Int8>((nArgon2m >> 24) & 0xFF);
273
274
0
    sal_Int32 const nArgon2p = rData->m_oArgon2Args ? ::std::get<2>(*rData->m_oArgon2Args) : 0;
275
0
    *(pHeader++) = static_cast<sal_Int8>((nArgon2p >> 0) & 0xFF);
276
0
    *(pHeader++) = static_cast<sal_Int8>((nArgon2p >> 8) & 0xFF);
277
0
    *(pHeader++) = static_cast<sal_Int8>((nArgon2p >> 16) & 0xFF);
278
0
    *(pHeader++) = static_cast<sal_Int8>((nArgon2p >> 24) & 0xFF);
279
280
    // FIXME64: need to handle larger sizes
281
    // Then the size:
282
0
    *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 0 ) & 0xFF);
283
0
    *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 8 ) & 0xFF);
284
0
    *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 16 ) & 0xFF);
285
0
    *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 24 ) & 0xFF);
286
287
    // Then the encryption algorithm
288
0
    sal_Int32 nEncAlgID = rData->m_nEncAlg;
289
0
    *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 0 ) & 0xFF);
290
0
    *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 8 ) & 0xFF);
291
0
    *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 16 ) & 0xFF);
292
0
    *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 24 ) & 0xFF);
293
294
    // Then the checksum algorithm
295
0
    sal_Int32 nChecksumAlgID = rData->m_oCheckAlg ? *rData->m_oCheckAlg : 0;
296
0
    *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 0 ) & 0xFF);
297
0
    *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 8 ) & 0xFF);
298
0
    *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 16 ) & 0xFF);
299
0
    *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 24 ) & 0xFF);
300
301
    // Then the derived key size
302
0
    sal_Int32 nDerivedKeySize = rData->m_nDerivedKeySize;
303
0
    *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 0 ) & 0xFF);
304
0
    *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 8 ) & 0xFF);
305
0
    *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 16 ) & 0xFF);
306
0
    *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 24 ) & 0xFF);
307
308
    // Then the start key generation algorithm
309
0
    sal_Int32 nKeyAlgID = rData->m_nStartKeyGenID;
310
0
    *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 0 ) & 0xFF);
311
0
    *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 8 ) & 0xFF);
312
0
    *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 16 ) & 0xFF);
313
0
    *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 24 ) & 0xFF);
314
315
    // Then the salt length
316
0
    *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 0 ) & 0xFF);
317
0
    *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 8 ) & 0xFF);
318
319
    // Then the IV length
320
0
    *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 0 ) & 0xFF);
321
0
    *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 8 ) & 0xFF);
322
323
    // Then the digest length
324
0
    *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 0 ) & 0xFF);
325
0
    *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 8 ) & 0xFF);
326
327
    // Then the mediatype length
328
0
    *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 0 ) & 0xFF);
329
0
    *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 8 ) & 0xFF);
330
331
    // Then the salt content
332
0
    memcpy ( pHeader, rData->m_aSalt.getConstArray(), nSaltLength );
333
0
    pHeader += nSaltLength;
334
335
    // Then the IV content
336
0
    memcpy ( pHeader, rData->m_aInitVector.getConstArray(), nIVLength );
337
0
    pHeader += nIVLength;
338
339
    // Then the digest content
340
0
    memcpy ( pHeader, rData->m_aDigest.getConstArray(), nDigestLength );
341
0
    pHeader += nDigestLength;
342
343
    // Then the mediatype itself
344
0
    memcpy ( pHeader, aMediaType.getStr(), nMediaTypeLength );
345
0
    pHeader += nMediaTypeLength;
346
0
}
347
348
bool ZipFile::StaticFillData (  ::rtl::Reference< BaseEncryptionData > const & rData,
349
                                    sal_Int32 &rEncAlg,
350
                                    sal_Int32 &rChecksumAlg,
351
                                    sal_Int32 &rDerivedKeySize,
352
                                    sal_Int32 &rStartKeyGenID,
353
                                    sal_Int32 &rSize,
354
                                    OUString& aMediaType,
355
                                    const uno::Reference< XInputStream >& rStream )
356
0
{
357
0
    bool bOk = false;
358
0
    const sal_Int32 nHeaderSize = n_ConstHeaderSize - 4;
359
0
    Sequence < sal_Int8 > aBuffer ( nHeaderSize );
360
0
    if ( nHeaderSize == rStream->readBytes ( aBuffer, nHeaderSize ) )
361
0
    {
362
0
        sal_Int16 nPos = 0;
363
0
        sal_Int8 *pBuffer = aBuffer.getArray();
364
0
        sal_Int16 nVersion = pBuffer[nPos++] & 0xFF;
365
0
        nVersion |= ( pBuffer[nPos++] & 0xFF ) << 8;
366
0
        if ( nVersion == n_ConstCurrentVersion )
367
0
        {
368
0
            sal_Int32 nCount = pBuffer[nPos++] & 0xFF;
369
0
            nCount |= ( pBuffer[nPos++] & 0xFF ) << 8;
370
0
            nCount |= ( pBuffer[nPos++] & 0xFF ) << 16;
371
0
            nCount |= ( pBuffer[nPos++] & 0xFF ) << 24;
372
0
            if (nCount != 0)
373
0
            {
374
0
                rData->m_oPBKDFIterationCount.emplace(nCount);
375
0
            }
376
0
            else
377
0
            {
378
0
                rData->m_oPBKDFIterationCount.reset();
379
0
            }
380
381
0
            sal_Int32 nArgon2t = pBuffer[nPos++] & 0xFF;
382
0
            nArgon2t |= ( pBuffer[nPos++] & 0xFF ) << 8;
383
0
            nArgon2t |= ( pBuffer[nPos++] & 0xFF ) << 16;
384
0
            nArgon2t |= ( pBuffer[nPos++] & 0xFF ) << 24;
385
386
0
            sal_Int32 nArgon2m = pBuffer[nPos++] & 0xFF;
387
0
            nArgon2m |= ( pBuffer[nPos++] & 0xFF ) << 8;
388
0
            nArgon2m |= ( pBuffer[nPos++] & 0xFF ) << 16;
389
0
            nArgon2m |= ( pBuffer[nPos++] & 0xFF ) << 24;
390
391
0
            sal_Int32 nArgon2p = pBuffer[nPos++] & 0xFF;
392
0
            nArgon2p |= ( pBuffer[nPos++] & 0xFF ) << 8;
393
0
            nArgon2p |= ( pBuffer[nPos++] & 0xFF ) << 16;
394
0
            nArgon2p |= ( pBuffer[nPos++] & 0xFF ) << 24;
395
396
0
            if (nArgon2t != 0 && nArgon2m != 0 && nArgon2p != 0)
397
0
            {
398
0
                rData->m_oArgon2Args.emplace(nArgon2t, nArgon2m, nArgon2p);
399
0
            }
400
0
            else
401
0
            {
402
0
                rData->m_oArgon2Args.reset();
403
0
            }
404
405
0
            rSize  =   pBuffer[nPos++] & 0xFF;
406
0
            rSize |= ( pBuffer[nPos++] & 0xFF ) << 8;
407
0
            rSize |= ( pBuffer[nPos++] & 0xFF ) << 16;
408
0
            rSize |= ( pBuffer[nPos++] & 0xFF ) << 24;
409
410
0
            rEncAlg   =   pBuffer[nPos++] & 0xFF;
411
0
            rEncAlg  |= ( pBuffer[nPos++] & 0xFF ) << 8;
412
0
            rEncAlg  |= ( pBuffer[nPos++] & 0xFF ) << 16;
413
0
            rEncAlg  |= ( pBuffer[nPos++] & 0xFF ) << 24;
414
415
0
            rChecksumAlg   =   pBuffer[nPos++] & 0xFF;
416
0
            rChecksumAlg  |= ( pBuffer[nPos++] & 0xFF ) << 8;
417
0
            rChecksumAlg  |= ( pBuffer[nPos++] & 0xFF ) << 16;
418
0
            rChecksumAlg  |= ( pBuffer[nPos++] & 0xFF ) << 24;
419
420
0
            rDerivedKeySize   =   pBuffer[nPos++] & 0xFF;
421
0
            rDerivedKeySize  |= ( pBuffer[nPos++] & 0xFF ) << 8;
422
0
            rDerivedKeySize  |= ( pBuffer[nPos++] & 0xFF ) << 16;
423
0
            rDerivedKeySize  |= ( pBuffer[nPos++] & 0xFF ) << 24;
424
425
0
            rStartKeyGenID   =   pBuffer[nPos++] & 0xFF;
426
0
            rStartKeyGenID  |= ( pBuffer[nPos++] & 0xFF ) << 8;
427
0
            rStartKeyGenID  |= ( pBuffer[nPos++] & 0xFF ) << 16;
428
0
            rStartKeyGenID  |= ( pBuffer[nPos++] & 0xFF ) << 24;
429
430
0
            sal_Int16 nSaltLength =   pBuffer[nPos++] & 0xFF;
431
0
            nSaltLength          |= ( pBuffer[nPos++] & 0xFF ) << 8;
432
0
            sal_Int16 nIVLength   = ( pBuffer[nPos++] & 0xFF );
433
0
            nIVLength            |= ( pBuffer[nPos++] & 0xFF ) << 8;
434
0
            sal_Int16 nDigestLength = pBuffer[nPos++] & 0xFF;
435
0
            nDigestLength        |= ( pBuffer[nPos++] & 0xFF ) << 8;
436
437
0
            sal_Int16 nMediaTypeLength = pBuffer[nPos++] & 0xFF;
438
0
            nMediaTypeLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
439
440
0
            if ( nSaltLength == rStream->readBytes ( aBuffer, nSaltLength ) )
441
0
            {
442
0
                rData->m_aSalt.realloc ( nSaltLength );
443
0
                memcpy ( rData->m_aSalt.getArray(), aBuffer.getConstArray(), nSaltLength );
444
0
                if ( nIVLength == rStream->readBytes ( aBuffer, nIVLength ) )
445
0
                {
446
0
                    rData->m_aInitVector.realloc ( nIVLength );
447
0
                    memcpy ( rData->m_aInitVector.getArray(), aBuffer.getConstArray(), nIVLength );
448
0
                    if ( nDigestLength == rStream->readBytes ( aBuffer, nDigestLength ) )
449
0
                    {
450
0
                        rData->m_aDigest.realloc ( nDigestLength );
451
0
                        memcpy ( rData->m_aDigest.getArray(), aBuffer.getConstArray(), nDigestLength );
452
453
0
                        if ( nMediaTypeLength == rStream->readBytes ( aBuffer, nMediaTypeLength ) )
454
0
                        {
455
0
                            aMediaType = OUString( reinterpret_cast<sal_Unicode const *>(aBuffer.getConstArray()),
456
0
                                                            nMediaTypeLength / sizeof( sal_Unicode ) );
457
0
                            bOk = true;
458
0
                        }
459
0
                    }
460
0
                }
461
0
            }
462
0
        }
463
0
    }
464
0
    return bOk;
465
0
}
466
467
#if 0
468
// for debugging purposes
469
void CheckSequence( const uno::Sequence< sal_Int8 >& aSequence )
470
{
471
    if ( aSequence.getLength() )
472
    {
473
        sal_Int32* pPointer = *( (sal_Int32**)&aSequence );
474
        sal_Int32 nSize = *( pPointer + 1 );
475
        sal_Int32 nMemSize = *( pPointer - 2 );
476
        sal_Int32 nUsedMemSize = ( nSize + 4 * sizeof( sal_Int32 ) );
477
        OSL_ENSURE( nSize == aSequence.getLength() && nUsedMemSize + 7 - ( nUsedMemSize - 1 ) % 8 == nMemSize, "Broken Sequence!" );
478
    }
479
}
480
#endif
481
482
bool ZipFile::StaticHasValidPassword( const uno::Reference< uno::XComponentContext >& rxContext, const Sequence< sal_Int8 > &aReadBuffer, const ::rtl::Reference< EncryptionData > &rData )
483
0
{
484
0
    assert(rData->m_nEncAlg != xml::crypto::CipherID::AES_GCM_W3C); // should not be called for AEAD
485
486
0
    if ( !rData.is() || !rData->m_aKey.hasElements() )
487
0
        return false;
488
489
0
    bool bRet = false;
490
491
0
    uno::Reference< xml::crypto::XCipherContext > xCipher( StaticGetCipher( rxContext, rData, false ), uno::UNO_SET_THROW );
492
493
0
    uno::Sequence< sal_Int8 > aDecryptBuffer;
494
0
    uno::Sequence< sal_Int8 > aDecryptBuffer2;
495
0
    try
496
0
    {
497
0
        aDecryptBuffer = xCipher->convertWithCipherContext( aReadBuffer );
498
0
        aDecryptBuffer2 = xCipher->finalizeCipherContextAndDispose();
499
0
    }
500
0
    catch( uno::Exception& )
501
0
    {
502
        // decryption with padding will throw the exception in finalizing if the buffer represent only part of the stream
503
        // it is no problem, actually this is why we read 32 additional bytes ( two of maximal possible encryption blocks )
504
0
    }
505
506
0
    if ( aDecryptBuffer2.hasElements() )
507
0
    {
508
0
        sal_Int32 nOldLen = aDecryptBuffer.getLength();
509
0
        aDecryptBuffer.realloc( nOldLen + aDecryptBuffer2.getLength() );
510
0
        memcpy( aDecryptBuffer.getArray() + nOldLen, aDecryptBuffer2.getConstArray(), aDecryptBuffer2.getLength() );
511
0
    }
512
513
0
    if ( aDecryptBuffer.getLength() > n_ConstDigestLength )
514
0
        aDecryptBuffer.realloc( n_ConstDigestLength );
515
516
0
    uno::Sequence< sal_Int8 > aDigestSeq;
517
0
    uno::Reference< xml::crypto::XDigestContext > xDigestContext( StaticGetDigestContextForChecksum( rxContext, rData ), uno::UNO_SET_THROW );
518
519
0
    xDigestContext->updateDigest( aDecryptBuffer );
520
0
    aDigestSeq = xDigestContext->finalizeDigestAndDispose();
521
522
    // If we don't have a digest, then we have to assume that the password is correct
523
0
    if (  rData->m_aDigest.hasElements() &&
524
0
          ( aDigestSeq.getLength() != rData->m_aDigest.getLength() ||
525
0
            0 != memcmp ( aDigestSeq.getConstArray(),
526
0
                                     rData->m_aDigest.getConstArray(),
527
0
                                    aDigestSeq.getLength() ) ) )
528
0
    {
529
        // We should probably tell the user that the password they entered was wrong
530
0
    }
531
0
    else
532
0
        bRet = true;
533
534
0
    return bRet;
535
0
}
536
537
uno::Reference<io::XInputStream> ZipFile::checkValidPassword(
538
    ZipEntry const& rEntry, ::rtl::Reference<EncryptionData> const& rData,
539
    sal_Int64 const nDecryptedSize,
540
    rtl::Reference<comphelper::RefCountedMutex> const& rMutex)
541
0
{
542
0
    if (rData.is() && rData->m_nEncAlg == xml::crypto::CipherID::AES_GCM_W3C)
543
0
    {
544
0
        try // the only way to find out: decrypt the whole stream, which will
545
0
        {   // check the tag
546
0
            uno::Reference<io::XInputStream> const xRet =
547
0
                createStreamForZipEntry(rMutex, rEntry, rData, UNBUFF_STREAM_DATA, nDecryptedSize);
548
            // currently XBufferedStream reads the whole stream in its ctor (to
549
            // verify the tag) - in case this gets changed, explicitly seek here
550
0
            uno::Reference<io::XSeekable> const xSeek(xRet, uno::UNO_QUERY_THROW);
551
0
            xSeek->seek(xSeek->getLength());
552
0
            xSeek->seek(0);
553
0
            return xRet;
554
0
        }
555
0
        catch (uno::Exception const&)
556
0
        {
557
0
            return {};
558
0
        }
559
0
    }
560
0
    else if (rData.is() && rData->m_aKey.hasElements())
561
0
    {
562
0
        ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
563
564
0
        css::uno::Reference < css::io::XSeekable > xSeek(xStream, UNO_QUERY_THROW);
565
0
        xSeek->seek( rEntry.nOffset );
566
0
        sal_Int64 nSize = rEntry.nMethod == DEFLATED ? rEntry.nCompressedSize : rEntry.nSize;
567
568
        // Only want to read enough to verify the digest
569
0
        if ( nSize > n_ConstDigestDecrypt )
570
0
            nSize = n_ConstDigestDecrypt;
571
572
0
        assert(nSize <= n_ConstDigestDecrypt && nSize >= 0 && "silence bogus coverity overflow_sink");
573
0
        Sequence<sal_Int8> aReadBuffer(nSize);
574
575
0
        xStream->readBytes( aReadBuffer, nSize );
576
577
0
        if (StaticHasValidPassword(m_xContext, aReadBuffer, rData))
578
0
        {
579
0
            return createStreamForZipEntry(
580
0
                    rMutex, rEntry, rData, UNBUFF_STREAM_DATA, nDecryptedSize);
581
0
        }
582
0
    }
583
584
0
    return {};
585
0
}
586
587
namespace {
588
589
class XBufferedStream : public cppu::WeakImplHelper<css::io::XInputStream, css::io::XSeekable>,
590
                        public comphelper::ByteReader
591
{
592
    std::vector<sal_Int8> maBytes;
593
    size_t mnPos;
594
595
    size_t remainingSize() const
596
134k
    {
597
134k
        return maBytes.size() - mnPos;
598
134k
    }
599
600
    bool hasBytes() const
601
254k
    {
602
254k
        return mnPos < maBytes.size();
603
254k
    }
604
605
public:
606
178k
    XBufferedStream( const uno::Reference<XInputStream>& xSrcStream ) : mnPos(0)
607
178k
    {
608
178k
        sal_Int32 nRemaining = xSrcStream->available();
609
178k
        maBytes.reserve(nRemaining);
610
611
178k
        if (auto pByteReader = dynamic_cast< comphelper::ByteReader* >( xSrcStream.get() ))
612
0
        {
613
0
            maBytes.resize(nRemaining);
614
615
0
            sal_Int8* pData = maBytes.data();
616
0
            while (nRemaining > 0)
617
0
            {
618
0
                sal_Int32 nRead = pByteReader->readSomeBytes(pData, nRemaining);
619
0
                nRemaining -= nRead;
620
0
                pData += nRead;
621
0
            }
622
0
            return;
623
0
        }
624
625
178k
        const sal_Int32 nBufSize = 8192;
626
178k
        uno::Sequence<sal_Int8> aBuf(nBufSize);
627
365k
        while (nRemaining > 0)
628
186k
        {
629
186k
            const sal_Int32 nBytes = xSrcStream->readBytes(aBuf, std::min(nBufSize, nRemaining));
630
186k
            if (!nBytes)
631
0
                break;
632
186k
            maBytes.insert(maBytes.end(), aBuf.begin(), aBuf.begin() + nBytes);
633
186k
            nRemaining -= nBytes;
634
186k
        }
635
178k
    }
636
637
    virtual sal_Int32 SAL_CALL readBytes( uno::Sequence<sal_Int8>& rData, sal_Int32 nBytesToRead ) override
638
253k
    {
639
253k
        if (!hasBytes())
640
119k
            return 0;
641
642
133k
        sal_Int32 nReadSize = std::min<sal_Int32>(nBytesToRead, remainingSize());
643
133k
        rData.realloc(nReadSize);
644
133k
        auto pData = rData.getArray();
645
133k
        std::vector<sal_Int8>::const_iterator it = maBytes.cbegin();
646
133k
        std::advance(it, mnPos);
647
214M
        for (sal_Int32 i = 0; i < nReadSize; ++i, ++it)
648
213M
            pData[i] = *it;
649
650
133k
        mnPos += nReadSize;
651
652
133k
        return nReadSize;
653
253k
    }
654
655
    virtual sal_Int32 readSomeBytes(sal_Int8* pData, sal_Int32 nBytesToRead) override
656
902
    {
657
902
        if (!hasBytes())
658
57
            return 0;
659
660
845
        sal_Int32 nReadSize = std::min<sal_Int32>(nBytesToRead, remainingSize());
661
845
        std::vector<sal_Int8>::const_iterator it = maBytes.cbegin();
662
845
        std::advance(it, mnPos);
663
1.19M
        for (sal_Int32 i = 0; i < nReadSize; ++i, ++it)
664
1.19M
            pData[i] = *it;
665
666
845
        mnPos += nReadSize;
667
668
845
        return nReadSize;
669
902
    }
670
671
    virtual sal_Int32 SAL_CALL readSomeBytes( ::css::uno::Sequence<sal_Int8>& rData, sal_Int32 nMaxBytesToRead ) override
672
234k
    {
673
234k
        return readBytes(rData, nMaxBytesToRead);
674
234k
    }
675
676
    virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override
677
291
    {
678
291
        if (!hasBytes())
679
15
            return;
680
681
276
        mnPos += nBytesToSkip;
682
276
    }
683
684
    virtual sal_Int32 SAL_CALL available() override
685
0
    {
686
0
        if (!hasBytes())
687
0
            return 0;
688
689
0
        return remainingSize();
690
0
    }
691
692
    virtual void SAL_CALL closeInput() override
693
113k
    {
694
113k
    }
695
    // XSeekable
696
    virtual void SAL_CALL seek( sal_Int64 location ) override
697
0
    {
698
0
        if ( location < 0 || o3tl::make_unsigned(location) > maBytes.size() )
699
0
            throw IllegalArgumentException(u""_ustr, uno::Reference< uno::XInterface >(), 1 );
700
0
        mnPos = location;
701
0
    }
702
    virtual sal_Int64 SAL_CALL getPosition() override
703
0
    {
704
0
        return mnPos;
705
0
    }
706
    virtual sal_Int64 SAL_CALL getLength() override
707
0
    {
708
0
        return maBytes.size();
709
0
    }
710
};
711
712
}
713
714
uno::Reference< XInputStream > ZipFile::createStreamForZipEntry(
715
            const rtl::Reference< comphelper::RefCountedMutex >& aMutexHolder,
716
            ZipEntry const & rEntry,
717
            const ::rtl::Reference< EncryptionData > &rData,
718
            sal_Int8 nStreamMode,
719
            ::std::optional<sal_Int64> const oDecryptedSize,
720
            const bool bUseBufferedStream,
721
            const OUString& aMediaType )
722
200k
{
723
200k
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
724
725
200k
    rtl::Reference< XUnbufferedStream > xSrcStream = new XUnbufferedStream(
726
200k
        m_xContext, aMutexHolder, rEntry, xStream, rData, nStreamMode, oDecryptedSize, aMediaType, bRecoveryMode);
727
728
200k
    if (!bUseBufferedStream)
729
0
        return xSrcStream;
730
731
200k
#ifndef __EMSCRIPTEN__
732
200k
    static const sal_Int32 nThreadingThreshold = 10000;
733
734
    // "encrypted-package" is the only data stream, no point in threading it
735
200k
    if (nThreadingThreshold < xSrcStream->available()
736
21.5k
        && rEntry.sPath != "encrypted-package"
737
        // tdf#160888 no threading for AEAD streams:
738
        // 1. the whole stream must be read immediately to verify tag
739
        // 2. XBufferedThreadedStream uses same m_aMutexHolder->GetMutex()
740
        //    => caller cannot read without deadlock
741
21.5k
        && (nStreamMode != UNBUFF_STREAM_DATA
742
21.3k
            || !rData.is()
743
0
            || rData->m_nEncAlg != xml::crypto::CipherID::AES_GCM_W3C))
744
21.5k
    {
745
21.5k
        return new XBufferedThreadedStream(xSrcStream, xSrcStream->getSize());
746
21.5k
    }
747
178k
#endif
748
749
178k
    return new XBufferedStream(xSrcStream);
750
200k
}
751
752
uno::Reference< XInputStream > ZipFile::StaticGetDataFromRawStream(
753
        const rtl::Reference<comphelper::RefCountedMutex>& rMutexHolder,
754
        const uno::Reference<uno::XComponentContext>& rxContext,
755
        const uno::Reference<XInputStream>& xStream,
756
        const ::rtl::Reference<EncryptionData> &rData)
757
0
{
758
0
    if (!rData.is())
759
0
        throw ZipIOException(u"Encrypted stream without encryption data!"_ustr );
760
761
0
    if (!rData->m_aKey.hasElements())
762
0
        throw packages::WrongPasswordException();
763
764
0
    uno::Reference<XSeekable> xSeek(xStream, UNO_QUERY);
765
0
    if (!xSeek.is())
766
0
        throw ZipIOException(u"The stream must be seekable!"_ustr);
767
768
    // if we have a digest, then this file is an encrypted one and we should
769
    // check if we can decrypt it or not
770
0
    SAL_WARN_IF(rData->m_nEncAlg != xml::crypto::CipherID::AES_GCM_W3C && !rData->m_aDigest.hasElements(),
771
0
            "package", "Can't detect password correctness without digest!");
772
0
    if (rData->m_nEncAlg == xml::crypto::CipherID::AES_GCM_W3C)
773
0
    {
774
        // skip header
775
0
        xSeek->seek(n_ConstHeaderSize + rData->m_aInitVector.getLength()
776
0
                + rData->m_aSalt.getLength() + rData->m_aDigest.getLength());
777
778
0
        try
779
0
        {   // XUnbufferedStream does not support XSeekable so wrap it
780
0
            ::rtl::Reference<XBufferedStream> const pRet(
781
0
                new XBufferedStream(new XUnbufferedStream(rMutexHolder, xStream, rData)));
782
            // currently XBufferedStream reads the whole stream in its ctor (to
783
            // verify the tag) - in case this gets changed, explicitly seek here
784
0
            pRet->seek(pRet->getLength());
785
0
            pRet->seek(0);
786
0
            return pRet;
787
0
        }
788
0
        catch (uno::Exception const&)
789
0
        {
790
0
            throw packages::WrongPasswordException();
791
0
        }
792
0
    }
793
0
    else if (rData->m_aDigest.hasElements())
794
0
    {
795
0
        sal_Int32 nSize = sal::static_int_cast<sal_Int32>(xSeek->getLength());
796
0
        if (nSize > n_ConstDigestLength + 32)
797
0
            nSize = n_ConstDigestLength + 32;
798
799
        // skip header
800
0
        xSeek->seek(n_ConstHeaderSize + rData->m_aInitVector.getLength() +
801
0
                    rData->m_aSalt.getLength() + rData->m_aDigest.getLength());
802
803
        // Only want to read enough to verify the digest
804
0
        Sequence<sal_Int8> aReadBuffer(nSize);
805
806
0
        xStream->readBytes(aReadBuffer, nSize);
807
808
0
        if (!StaticHasValidPassword(rxContext, aReadBuffer, rData))
809
0
            throw packages::WrongPasswordException();
810
0
    }
811
812
0
    return new XUnbufferedStream(rMutexHolder, xStream, rData);
813
0
}
814
815
ZipEnumeration ZipFile::entries()
816
32.1k
{
817
32.1k
    return aEntries;
818
32.1k
}
819
820
uno::Reference< XInputStream > ZipFile::getInputStream( ZipEntry& rEntry,
821
        const ::rtl::Reference< EncryptionData > &rData,
822
        ::std::optional<sal_Int64> const oDecryptedSize,
823
        const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder )
824
12.2k
{
825
12.2k
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
826
827
12.2k
    if ( rEntry.nOffset <= 0 )
828
0
        readLOC( rEntry );
829
830
    // We want to return a rawStream if we either don't have a key or if the
831
    // key is wrong
832
833
12.2k
    bool bNeedRawStream = rEntry.nMethod == STORED;
834
835
12.2k
    if (oDecryptedSize && rData.is())
836
0
    {
837
0
        uno::Reference<XInputStream> const xRet(
838
0
            checkValidPassword(rEntry, rData, *oDecryptedSize, aMutexHolder));
839
0
        if (xRet.is())
840
0
        {
841
0
            return xRet;
842
0
        }
843
0
        bNeedRawStream = true;
844
0
    }
845
846
12.2k
    return createStreamForZipEntry ( aMutexHolder,
847
12.2k
                                    rEntry,
848
12.2k
                                    rData,
849
12.2k
                                    bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA,
850
12.2k
                                    oDecryptedSize);
851
12.2k
}
852
853
uno::Reference< XInputStream > ZipFile::getDataStream( ZipEntry& rEntry,
854
        const ::rtl::Reference< EncryptionData > &rData,
855
        ::std::optional<sal_Int64> const oDecryptedSize,
856
        const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder )
857
188k
{
858
188k
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
859
860
188k
    if ( rEntry.nOffset <= 0 )
861
0
        readLOC( rEntry );
862
863
    // An exception must be thrown in case stream is encrypted and
864
    // there is no key or the key is wrong
865
188k
    bool bNeedRawStream = false;
866
188k
    if (oDecryptedSize)
867
0
    {
868
        // in case no digest is provided there is no way
869
        // to detect password correctness
870
0
        if ( !rData.is() )
871
0
            throw ZipException(u"Encrypted stream without encryption data!"_ustr );
872
873
        // if we have a digest, then this file is an encrypted one and we should
874
        // check if we can decrypt it or not
875
0
        SAL_WARN_IF(rData->m_nEncAlg != xml::crypto::CipherID::AES_GCM_W3C && !rData->m_aDigest.hasElements(),
876
0
            "package", "Can't detect password correctness without digest!");
877
0
        uno::Reference<XInputStream> const xRet(checkValidPassword(rEntry, rData, *oDecryptedSize, aMutexHolder));
878
0
        if (!xRet.is())
879
0
        {
880
0
            throw packages::WrongPasswordException();
881
0
        }
882
0
        return xRet;
883
0
    }
884
188k
    else
885
188k
        bNeedRawStream = ( rEntry.nMethod == STORED );
886
887
188k
    return createStreamForZipEntry ( aMutexHolder,
888
188k
                                    rEntry,
889
188k
                                    rData,
890
188k
                                    bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA,
891
188k
                                    oDecryptedSize);
892
188k
}
893
894
uno::Reference< XInputStream > ZipFile::getRawData( ZipEntry& rEntry,
895
        const ::rtl::Reference< EncryptionData >& rData,
896
        ::std::optional<sal_Int64> const oDecryptedSize,
897
        const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder,
898
        const bool bUseBufferedStream )
899
0
{
900
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
901
902
0
    if ( rEntry.nOffset <= 0 )
903
0
        readLOC( rEntry );
904
905
0
    return createStreamForZipEntry(aMutexHolder, rEntry, rData,
906
0
            UNBUFF_STREAM_RAW, oDecryptedSize, bUseBufferedStream);
907
0
}
908
909
uno::Reference< XInputStream > ZipFile::getWrappedRawStream(
910
        ZipEntry& rEntry,
911
        const ::rtl::Reference< EncryptionData >& rData,
912
        sal_Int64 const nDecryptedSize,
913
        const OUString& aMediaType,
914
        const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder )
915
0
{
916
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
917
918
0
    if ( !rData.is() )
919
0
        throw packages::NoEncryptionException();
920
921
0
    if ( rEntry.nOffset <= 0 )
922
0
        readLOC( rEntry );
923
924
0
    return createStreamForZipEntry(aMutexHolder, rEntry, rData,
925
0
            UNBUFF_STREAM_WRAPPEDRAW, nDecryptedSize, true, aMediaType);
926
0
}
927
928
sal_uInt64 ZipFile::readLOC(ZipEntry &rEntry)
929
0
{
930
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
931
0
    std::vector<sal_Int8> aNameBuffer;
932
0
    std::vector<sal_Int8> aExtraBuffer;
933
0
    return readLOC_Impl(rEntry, aNameBuffer, aExtraBuffer);
934
0
}
935
936
// Pass in a shared name buffer to reduce the number of allocations
937
// we do when reading the CEN.
938
sal_uInt64 ZipFile::readLOC_Impl(ZipEntry &rEntry, std::vector<sal_Int8>& rNameBuffer, std::vector<sal_Int8>& rExtraBuffer)
939
374k
{
940
374k
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
941
942
374k
    sal_Int64 nPos = -rEntry.nOffset;
943
944
374k
    aGrabber.seek(nPos);
945
374k
    std::array<sal_Int8, 30> aHeader;
946
374k
    if (aGrabber.readBytes(aHeader.data(), 30) != 30)
947
80
        throw uno::RuntimeException();
948
374k
    MemoryByteGrabber headerMemGrabber(aHeader.data(), 30);
949
950
374k
    sal_Int32 nTestSig = headerMemGrabber.ReadInt32();
951
374k
    if (nTestSig != LOCSIG)
952
36
        throw ZipIOException(u"Invalid LOC header (bad signature)"_ustr );
953
954
    // Ignore all (duplicated) information from the local file header.
955
    // various programs produced "broken" zip files; even LO at some point.
956
    // Just verify the path and calculate the data offset and otherwise
957
    // rely on the central directory info.
958
959
    // version - ignore any mismatch (Maven created JARs)
960
374k
    sal_uInt16 const nVersion = headerMemGrabber.ReadUInt16();
961
374k
    sal_uInt16 const nLocFlag = headerMemGrabber.ReadUInt16(); // general purpose bit flag
962
374k
    sal_uInt16 const nLocMethod = headerMemGrabber.ReadUInt16(); // compression method
963
    // Do *not* compare timestamps, since MSO 2010 can produce documents
964
    // with timestamp difference in the central directory entry and local
965
    // file header.
966
374k
    headerMemGrabber.ReadInt32(); //time
967
374k
    sal_uInt32 nLocCrc = headerMemGrabber.ReadUInt32(); //crc
968
374k
    sal_uInt64 nLocCompressedSize = headerMemGrabber.ReadUInt32(); //compressed size
969
374k
    sal_uInt64 nLocSize = headerMemGrabber.ReadUInt32(); //size
970
374k
    sal_Int16 nPathLen = headerMemGrabber.ReadInt16();
971
374k
    sal_Int16 nExtraLen = headerMemGrabber.ReadInt16();
972
973
374k
    if (nPathLen < 0)
974
31
    {
975
31
        SAL_WARN("package", "bogus path len of: " << nPathLen);
976
31
        nPathLen = 0;
977
31
    }
978
979
374k
    rEntry.nOffset = aGrabber.getPosition() + nPathLen + nExtraLen;
980
981
374k
    sal_Int64 nEnd = {}; // avoid -Werror=maybe-uninitialized
982
374k
    bool bBroken = false;
983
984
374k
    try
985
374k
    {
986
        // read always in UTF8, some tools seem not to set UTF8 bit
987
        // coverity[tainted_data] - we've checked negative lens, and up to max short is ok here
988
374k
        rNameBuffer.resize(nPathLen);
989
374k
        sal_Int32 nRead = aGrabber.readBytes(rNameBuffer.data(), nPathLen);
990
374k
        std::string_view aNameView(reinterpret_cast<const char *>(rNameBuffer.data()), nRead);
991
992
374k
        OUString sLOCPath( aNameView.data(), aNameView.size(), RTL_TEXTENCODING_UTF8 );
993
994
374k
        if ( rEntry.nPathLen == -1 ) // the file was created
995
0
        {
996
0
            rEntry.nPathLen = nPathLen;
997
0
            rEntry.sPath = sLOCPath;
998
0
        }
999
1000
374k
        if (rEntry.nPathLen != nPathLen || rEntry.sPath != sLOCPath)
1001
466
        {
1002
466
            SAL_INFO("package", "LOC inconsistent name: \"" << rEntry.sPath << "\"");
1003
466
            bBroken = true;
1004
466
        }
1005
1006
374k
        bool isZip64{false};
1007
374k
        ::std::optional<sal_uInt64> oOffset64;
1008
374k
        if (nExtraLen != 0)
1009
56.2k
        {
1010
56.2k
            rExtraBuffer.resize(nExtraLen);
1011
56.2k
            aGrabber.readBytes(rExtraBuffer.data(), nExtraLen);
1012
56.2k
            MemoryByteGrabber extraMemGrabber(rExtraBuffer.data(), nExtraLen);
1013
1014
56.2k
            isZip64 = readExtraFields(extraMemGrabber, nExtraLen,
1015
56.2k
                    nLocSize, nLocCompressedSize, oOffset64, &aNameView);
1016
56.2k
        }
1017
374k
        if (!isZip64 && 45 <= nVersion)
1018
285
        {
1019
            // for Excel compatibility, assume Zip64 - https://rzymek.github.io/post/excel-zip64/
1020
285
            isZip64 = true;
1021
285
        }
1022
1023
        // Just plain ignore bits 1 & 2 of the flag field - they are either
1024
        // purely informative, or even fully undefined (depending on method).
1025
        // Also ignore bit 11 ("Language encoding flag"): tdf125300.docx is
1026
        // example with mismatch - and the actual file names are compared in
1027
        // any case and required to be UTF-8.
1028
374k
        if ((rEntry.nFlag & ~0x806U) != (nLocFlag & ~0x806U))
1029
78
        {
1030
78
            SAL_INFO("package", "LOC inconsistent flag: \"" << rEntry.sPath << "\"");
1031
78
            bBroken = true;
1032
78
        }
1033
1034
        // TODO: "older versions with encrypted streams write mismatching DEFLATE/STORE" ???
1035
374k
        if (rEntry.nMethod != nLocMethod)
1036
98
        {
1037
98
            SAL_INFO("package", "LOC inconsistent method: \"" << rEntry.sPath << "\"");
1038
98
            bBroken = true;
1039
98
        }
1040
1041
374k
        if (o3tl::checked_add<sal_Int64>(rEntry.nOffset, rEntry.nCompressedSize, nEnd))
1042
0
        {
1043
0
            throw ZipException(u"Integer-overflow"_ustr);
1044
0
        }
1045
1046
        // read "data descriptor" - this can be 12, 16, 20, or 24 bytes in size
1047
374k
        if ((rEntry.nFlag & 0x08) != 0)
1048
165k
        {
1049
#if 0
1050
            // Unfortunately every encrypted ODF package entry hits this,
1051
            // because ODF requires deflated entry with value STORED and OOo/LO
1052
            // has always written compressed streams with data descriptor.
1053
            // So it is checked later in ZipPackage::checkZipEntriesWithDD()
1054
            if (nLocMethod == STORED)
1055
            {
1056
                SAL_INFO("package", "LOC STORED with data descriptor: \"" << rEntry.sPath << "\"");
1057
                bBroken = true;
1058
            }
1059
            else
1060
#endif
1061
165k
            {
1062
165k
                decltype(nLocCrc) nDDCrc;
1063
165k
                decltype(nLocCompressedSize) nDDCompressedSize;
1064
165k
                decltype(nLocSize) nDDSize;
1065
165k
                aGrabber.seek(aGrabber.getPosition() + rEntry.nCompressedSize);
1066
165k
                sal_uInt32 nTemp = aGrabber.ReadUInt32();
1067
165k
                if (nTemp == 0x08074b50) // APPNOTE says PK78 is optional???
1068
165k
                {
1069
165k
                    nDDCrc = aGrabber.ReadUInt32();
1070
165k
                }
1071
289
                else
1072
289
                {
1073
289
                    nDDCrc = nTemp;
1074
289
                }
1075
165k
                if (isZip64)
1076
63
                {
1077
63
                    nDDCompressedSize = aGrabber.ReadUInt64();
1078
63
                    nDDSize = aGrabber.ReadUInt64();
1079
63
                }
1080
165k
                else
1081
165k
                {
1082
165k
                    nDDCompressedSize = aGrabber.ReadUInt32();
1083
165k
                    nDDSize = aGrabber.ReadUInt32();
1084
165k
                }
1085
165k
                if (nEnd < aGrabber.getPosition())
1086
165k
                {
1087
165k
                    nEnd = aGrabber.getPosition();
1088
165k
                }
1089
166
                else
1090
166
                {
1091
166
                    SAL_INFO("package", "LOC invalid size: \"" << rEntry.sPath << "\"");
1092
166
                    bBroken = true;
1093
166
                }
1094
                // tdf91429.docx has same values in LOC and in (superfluous) DD
1095
165k
                if ((nLocCrc == 0 || nLocCrc == nDDCrc)
1096
165k
                    && (nLocCompressedSize == 0 || nLocCompressedSize == sal_uInt64(-1) || nLocCompressedSize == nDDCompressedSize)
1097
165k
                    && (nLocSize == 0 || nLocSize == sal_uInt64(-1) || nLocSize == nDDSize))
1098
1099
165k
                {
1100
165k
                    nLocCrc = nDDCrc;
1101
165k
                    nLocCompressedSize = nDDCompressedSize;
1102
165k
                    nLocSize = nDDSize;
1103
165k
                }
1104
224
                else
1105
224
                {
1106
224
                    SAL_INFO("package", "LOC non-0 with data descriptor: \"" << rEntry.sPath << "\"");
1107
224
                    bBroken = true;
1108
224
                }
1109
165k
            }
1110
165k
        }
1111
1112
        // unit test file export64.zip has nLocCrc/nLocCS/nLocSize = 0 on mimetype
1113
374k
        if (nLocCrc != 0 && static_cast<sal_uInt32>(rEntry.nCrc) != nLocCrc)
1114
196
        {
1115
196
            SAL_INFO("package", "LOC inconsistent CRC: \"" << rEntry.sPath << "\"");
1116
196
            bBroken = true;
1117
196
        }
1118
1119
374k
        if (nLocCompressedSize != 0 && static_cast<sal_uInt64>(rEntry.nCompressedSize) != nLocCompressedSize)
1120
289
        {
1121
289
            SAL_INFO("package", "LOC inconsistent compressed size: \"" << rEntry.sPath << "\"");
1122
289
            bBroken = true;
1123
289
        }
1124
1125
374k
        if (nLocSize != 0 && static_cast<sal_uInt64>(rEntry.nSize) != nLocSize)
1126
362
        {
1127
362
            SAL_INFO("package", "LOC inconsistent size: \"" << rEntry.sPath << "\"");
1128
362
            bBroken = true;
1129
362
        }
1130
1131
374k
        if (oOffset64 && o3tl::make_unsigned(nPos) != *oOffset64)
1132
23
        {
1133
23
            SAL_INFO("package", "LOC inconsistent offset: \"" << rEntry.sPath << "\"");
1134
23
            bBroken = true;
1135
23
        }
1136
374k
    }
1137
374k
    catch(...)
1138
374k
    {
1139
70
        bBroken = true;
1140
70
    }
1141
1142
374k
    if ( bBroken && !bRecoveryMode )
1143
623
        throw ZipIOException(u"The stream seems to be broken!"_ustr );
1144
1145
373k
    return nEnd;
1146
374k
}
1147
1148
std::tuple<sal_Int64, sal_Int64, sal_Int64> ZipFile::findCentralDirectory()
1149
21.8k
{
1150
    // this method is called in constructor only, no need for mutex
1151
21.8k
    try
1152
21.8k
    {
1153
21.8k
        sal_Int64 const nLength = aGrabber.getLength();
1154
21.8k
        if (nLength < ENDHDR)
1155
133
        {
1156
133
            throw ZipException(u"Zip too small!"_ustr);
1157
133
        }
1158
21.6k
        sal_Int64 nPos = nLength - ENDHDR - ZIP_MAXNAMELEN;
1159
21.6k
        sal_Int64 nEnd = nPos >= 0 ? nPos : 0;
1160
1161
21.6k
        aGrabber.seek( nEnd );
1162
1163
21.6k
        auto nSize = nLength - nEnd;
1164
21.6k
        std::unique_ptr<sal_Int8[]> aBuffer(new sal_Int8[nSize]);
1165
21.6k
        if (nSize != aGrabber.readBytes(aBuffer.get(), nSize))
1166
0
            throw ZipException(u"Zip END signature not found!"_ustr );
1167
1168
21.6k
        const sal_Int8 *pBuffer = aBuffer.get();
1169
1170
21.6k
        sal_Int64 nEndPos = {};
1171
21.6k
        nPos = nSize - ENDHDR;
1172
58.2k
        while ( nPos >= 0 )
1173
58.2k
        {
1174
58.2k
            if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 5 && pBuffer[nPos+3] == 6 )
1175
21.5k
            {
1176
21.5k
                nEndPos = nPos + nEnd;
1177
21.5k
                break;
1178
21.5k
            }
1179
36.7k
            if (nPos == 0)
1180
178
            {
1181
178
                throw ZipException(u"Zip END signature not found!"_ustr);
1182
178
            }
1183
36.5k
            nPos--;
1184
36.5k
        }
1185
1186
21.5k
        aGrabber.seek(nEndPos + 4);
1187
21.5k
        sal_uInt16 const nEndDisk = aGrabber.ReadUInt16();
1188
21.5k
        if (nEndDisk != 0 && nEndDisk != 0xFFFF)
1189
27
        {   // only single disk is supported!
1190
27
            throw ZipException(u"invalid end (disk)"_ustr );
1191
27
        }
1192
21.4k
        sal_uInt16 const nEndDirDisk = aGrabber.ReadUInt16();
1193
21.4k
        if (nEndDirDisk != 0 && nEndDisk != 0xFFFF)
1194
11
        {
1195
11
            throw ZipException(u"invalid end (directory disk)"_ustr );
1196
11
        }
1197
21.4k
        sal_uInt16 const nEndDiskEntries = aGrabber.ReadUInt16();
1198
21.4k
        sal_uInt16 const nEndEntries = aGrabber.ReadUInt16();
1199
21.4k
        if (nEndDiskEntries != nEndEntries)
1200
12
        {
1201
12
            throw ZipException(u"invalid end (entries)"_ustr );
1202
12
        }
1203
21.4k
        sal_Int32 const nEndDirSize = aGrabber.ReadInt32();
1204
21.4k
        sal_Int32 const nEndDirOffset = aGrabber.ReadInt32();
1205
1206
        // Zip64 end of central directory locator must immediately precede
1207
        // end of central directory record
1208
21.4k
        if (20 <= nEndPos)
1209
21.3k
        {
1210
21.3k
            aGrabber.seek(nEndPos - 20);
1211
21.3k
            std::array<sal_Int8, 20> aZip64EndLocator;
1212
21.3k
            if (20 != aGrabber.readBytes(aZip64EndLocator.data(), 20))
1213
0
                throw uno::RuntimeException();
1214
21.3k
            MemoryByteGrabber loc64Grabber(aZip64EndLocator.data(), 20);
1215
21.3k
            if (loc64Grabber.ReadUInt8() == 'P'
1216
388
                && loc64Grabber.ReadUInt8() == 'K'
1217
321
                && loc64Grabber.ReadUInt8() == 6
1218
307
                && loc64Grabber.ReadUInt8() == 7)
1219
295
            {
1220
295
                sal_uInt32 const nLoc64Disk = loc64Grabber.ReadUInt32();
1221
295
                if (nLoc64Disk != 0)
1222
14
                {
1223
14
                    throw ZipException(u"invalid Zip64 end locator (disk)"_ustr);
1224
14
                }
1225
281
                sal_Int64 const nLoc64End64Offset = loc64Grabber.ReadUInt64();
1226
281
                if (nEndPos < 20 + 56 || (nEndPos - 20 - 56) < nLoc64End64Offset
1227
275
                    || nLoc64End64Offset < 0)
1228
66
                {
1229
66
                    throw ZipException(u"invalid Zip64 end locator (offset)"_ustr);
1230
66
                }
1231
215
                sal_uInt32 const nLoc64Disks = loc64Grabber.ReadUInt32();
1232
215
                if (nLoc64Disks != 1)
1233
7
                {
1234
7
                    throw ZipException(u"invalid Zip64 end locator (number of disks)"_ustr);
1235
7
                }
1236
208
                aGrabber.seek(nLoc64End64Offset);
1237
208
                std::vector<sal_Int8> aZip64EndDirectory(nEndPos - 20 - nLoc64End64Offset);
1238
208
                aGrabber.readBytes(aZip64EndDirectory.data(), nEndPos - 20 - nLoc64End64Offset);
1239
208
                MemoryByteGrabber end64Grabber(aZip64EndDirectory.data(), nEndPos - 20 - nLoc64End64Offset);
1240
208
                if (end64Grabber.ReadUInt8() != 'P'
1241
201
                    || end64Grabber.ReadUInt8() != 'K'
1242
195
                    || end64Grabber.ReadUInt8() != 6
1243
187
                    || end64Grabber.ReadUInt8() != 6)
1244
24
                {
1245
24
                    throw ZipException(u"invalid Zip64 end (signature)"_ustr);
1246
24
                }
1247
184
                sal_Int64 const nEnd64Size = end64Grabber.ReadUInt64();
1248
184
                if (nEnd64Size != nEndPos - 20 - nLoc64End64Offset - 12)
1249
59
                {
1250
59
                    throw ZipException(u"invalid Zip64 end (size)"_ustr);
1251
59
                }
1252
125
                end64Grabber.ReadUInt16(); // ignore version made by
1253
125
                end64Grabber.ReadUInt16(); // ignore version needed to extract
1254
125
                sal_uInt32 const nEnd64Disk = end64Grabber.ReadUInt32();
1255
125
                if (nEnd64Disk != 0)
1256
5
                {
1257
5
                    throw ZipException(u"invalid Zip64 end (disk)"_ustr);
1258
5
                }
1259
120
                sal_uInt32 const nEnd64EndDisk = end64Grabber.ReadUInt32();
1260
120
                if (nEnd64EndDisk != 0)
1261
3
                {
1262
3
                    throw ZipException(u"invalid Zip64 end (directory disk)"_ustr);
1263
3
                }
1264
117
                sal_uInt64 const nEnd64DiskEntries = end64Grabber.ReadUInt64();
1265
117
                sal_uInt64 const nEnd64Entries = end64Grabber.ReadUInt64();
1266
117
                if (nEnd64DiskEntries != nEnd64Entries)
1267
13
                {
1268
13
                    throw ZipException(u"invalid Zip64 end (entries)"_ustr);
1269
13
                }
1270
104
                sal_Int64 const nEnd64DirSize = end64Grabber.ReadUInt64();
1271
104
                sal_Int64 const nEnd64DirOffset = end64Grabber.ReadUInt64();
1272
104
                if (nEndEntries != sal_uInt16(-1) && nEnd64Entries != nEndEntries)
1273
3
                {
1274
3
                    throw ZipException(u"inconsistent Zip/Zip64 end (entries)"_ustr);
1275
3
                }
1276
101
                if (nEndDirSize != -1
1277
25
                    && nEnd64DirSize != nEndDirSize)
1278
19
                {
1279
19
                    throw ZipException(u"inconsistent Zip/Zip64 end (size)"_ustr);
1280
19
                }
1281
82
                if (nEndDirOffset != -1
1282
33
                    && nEnd64DirOffset != nEndDirOffset)
1283
28
                {
1284
28
                    throw ZipException(u"inconsistent Zip/Zip64 end (offset)"_ustr);
1285
28
                }
1286
1287
54
                sal_Int64 end;
1288
54
                if (o3tl::checked_add<sal_Int64>(nEnd64DirOffset, nEnd64DirSize, end)
1289
54
                    || nLoc64End64Offset < end
1290
46
                    || nEnd64DirOffset < 0
1291
41
                    || nEnd64DirSize < 0
1292
7
                    || nLoc64End64Offset - nEnd64DirSize != nEnd64DirOffset)
1293
53
                {
1294
53
                    throw ZipException(u"Invalid Zip64 end (bad central directory size)"_ustr);
1295
53
                }
1296
1297
1
                return { nEnd64Entries, nEnd64DirSize, nEnd64DirOffset };
1298
54
            }
1299
21.3k
        }
1300
1301
21.1k
        sal_Int32 end;
1302
21.1k
        if (o3tl::checked_add<sal_Int32>(nEndDirOffset, nEndDirSize, end)
1303
21.1k
            || nEndPos < end
1304
21.1k
            || nEndDirOffset < 0
1305
21.0k
            || nEndPos - nEndDirSize != nEndDirOffset)
1306
163
        {
1307
163
            throw ZipException(u"Invalid END header (bad central directory size)"_ustr);
1308
163
        }
1309
1310
20.9k
        return { nEndEntries, nEndDirSize, nEndDirOffset };
1311
21.1k
    }
1312
21.8k
    catch ( IllegalArgumentException& )
1313
21.8k
    {
1314
0
        throw ZipException(u"Zip END signature not found!"_ustr );
1315
0
    }
1316
21.8k
    catch ( NotConnectedException& )
1317
21.8k
    {
1318
0
        throw ZipException(u"Zip END signature not found!"_ustr );
1319
0
    }
1320
21.8k
    catch ( BufferSizeExceededException& )
1321
21.8k
    {
1322
0
        throw ZipException(u"Zip END signature not found!"_ustr );
1323
0
    }
1324
21.8k
}
1325
1326
sal_Int32 ZipFile::readCEN()
1327
21.8k
{
1328
    // this method is called in constructor only, no need for mutex
1329
21.8k
    sal_Int32 nCenPos = -1;
1330
1331
21.8k
    try
1332
21.8k
    {
1333
21.8k
        auto [nTotal, nCenLen, nCenOff] = findCentralDirectory();
1334
21.8k
        nCenPos = nCenOff; // data before start of zip is not supported
1335
1336
21.8k
        if ( nTotal > ZIP_MAXENTRIES )
1337
5
            throw ZipException(u"too many entries in ZIP File"_ustr );
1338
1339
21.8k
        if (nCenLen < nTotal * CENHDR) // prevent overflow with ZIP_MAXENTRIES
1340
7
            throw ZipException(u"invalid END header (bad entry count)"_ustr );
1341
1342
21.8k
        if (nCenLen > SAL_MAX_INT32 || nCenLen < 0)
1343
0
            throw ZipException(u"central directory too big"_ustr);
1344
1345
21.8k
        aGrabber.seek(nCenPos);
1346
21.8k
        std::vector<sal_Int8> aCENBuffer(nCenLen);
1347
21.8k
        sal_Int64 nRead = aGrabber.readBytes ( aCENBuffer.data(), nCenLen );
1348
21.8k
        if (nCenLen != nRead)
1349
0
            throw ZipException (u"Error reading CEN into memory buffer!"_ustr );
1350
1351
21.8k
        MemoryByteGrabber aMemGrabber(aCENBuffer.data(), nCenLen);
1352
1353
21.8k
        ZipEntry aEntry;
1354
21.8k
        sal_Int16 nCommentLen;
1355
21.8k
        ::std::vector<std::pair<sal_uInt64, sal_uInt64>> unallocated = { { 0, nCenPos } };
1356
1357
21.8k
        aEntries.reserve(nTotal);
1358
21.8k
        sal_Int64 nCount;
1359
21.8k
        std::vector<sal_Int8> aTempNameBuffer;
1360
21.8k
        std::vector<sal_Int8> aTempExtraBuffer;
1361
396k
        for (nCount = 0 ; nCount < nTotal; nCount++)
1362
374k
        {
1363
374k
            sal_Int32 nTestSig = aMemGrabber.ReadInt32();
1364
374k
            if ( nTestSig != CENSIG )
1365
25
                throw ZipException(u"Invalid CEN header (bad signature)"_ustr );
1366
1367
374k
            sal_uInt16 versionMadeBy = aMemGrabber.ReadUInt16();
1368
374k
            aEntry.nVersion = aMemGrabber.ReadInt16();
1369
374k
            aEntry.nFlag = aMemGrabber.ReadInt16();
1370
1371
374k
            if ( ( aEntry.nFlag & 1 ) == 1 )
1372
6
                throw ZipException(u"Invalid CEN header (encrypted entry)"_ustr );
1373
1374
374k
            aEntry.nMethod = aMemGrabber.ReadInt16();
1375
1376
374k
            if ( aEntry.nMethod != STORED && aEntry.nMethod != DEFLATED)
1377
7
                throw ZipException(u"Invalid CEN header (bad compression method)"_ustr );
1378
1379
374k
            aEntry.nTime = aMemGrabber.ReadInt32();
1380
374k
            aEntry.nCrc = aMemGrabber.ReadInt32();
1381
1382
374k
            sal_uInt64 nCompressedSize = aMemGrabber.ReadUInt32();
1383
374k
            sal_uInt64 nSize = aMemGrabber.ReadUInt32();
1384
374k
            aEntry.nPathLen = aMemGrabber.ReadInt16();
1385
374k
            aEntry.nExtraLen = aMemGrabber.ReadInt16();
1386
374k
            nCommentLen = aMemGrabber.ReadInt16();
1387
374k
            aMemGrabber.skipBytes ( 4 );
1388
374k
            sal_uInt32 externalFileAttributes = aMemGrabber.ReadUInt32();
1389
374k
            sal_uInt64 nOffset = aMemGrabber.ReadUInt32();
1390
1391
374k
            if ( aEntry.nPathLen < 0 )
1392
8
                throw ZipException(u"unexpected name length"_ustr );
1393
1394
374k
            if ( nCommentLen < 0 )
1395
6
                throw ZipException(u"unexpected comment length"_ustr );
1396
1397
374k
            if ( aEntry.nExtraLen < 0 )
1398
7
                throw ZipException(u"unexpected extra header info length"_ustr );
1399
1400
374k
            if (aEntry.nPathLen > aMemGrabber.remainingSize())
1401
7
                throw ZipException(u"name too long"_ustr);
1402
1403
            // read always in UTF8, some tools seem not to set UTF8 bit
1404
374k
            std::string_view aPathView(reinterpret_cast<char const *>(aMemGrabber.getCurrentPos()), aEntry.nPathLen);
1405
374k
            aEntry.sPath = OUString( aPathView.data(), aPathView.size(), RTL_TEXTENCODING_UTF8 );
1406
1407
374k
            if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( aEntry.sPath, true ) )
1408
92
                throw ZipException(u"Zip entry has an invalid name."_ustr );
1409
1410
374k
            aMemGrabber.skipBytes(aEntry.nPathLen);
1411
1412
374k
            if (aEntry.nExtraLen>0)
1413
7.22k
            {
1414
7.22k
                ::std::optional<sal_uInt64> oOffset64;
1415
7.22k
                readExtraFields(aMemGrabber, aEntry.nExtraLen, nSize, nCompressedSize, oOffset64, &aPathView);
1416
7.22k
                if (oOffset64)
1417
29
                {
1418
29
                    nOffset = *oOffset64;
1419
29
                }
1420
7.22k
            }
1421
374k
            aEntry.nCompressedSize = nCompressedSize;
1422
374k
            aEntry.nSize = nSize;
1423
374k
            aEntry.nOffset = nOffset;
1424
1425
374k
            if (o3tl::checked_multiply<sal_Int64>(aEntry.nOffset, -1, aEntry.nOffset))
1426
0
                throw ZipException(u"Integer-overflow"_ustr);
1427
1428
374k
            if (aEntry.nMethod == STORED && aEntry.nCompressedSize != aEntry.nSize)
1429
28
            {
1430
28
                throw ZipException(u"entry STORED with inconsistent size"_ustr);
1431
28
            }
1432
1433
374k
            aMemGrabber.skipBytes(nCommentLen);
1434
1435
            // unfortunately readLOC is required now to check the consistency
1436
374k
            assert(aEntry.nOffset <= 0);
1437
374k
            sal_uInt64 const nStart{ o3tl::make_unsigned(-aEntry.nOffset) };
1438
374k
            sal_uInt64 const nEnd = readLOC_Impl(aEntry, aTempNameBuffer, aTempExtraBuffer);
1439
374k
            assert(nStart < nEnd);
1440
374k
            for (auto it = unallocated.begin(); ; ++it)
1441
373k
            {
1442
373k
                if (it == unallocated.end())
1443
2
                {
1444
2
                    throw ZipException(u"overlapping entries"_ustr);
1445
2
                }
1446
373k
                if (nStart < it->first)
1447
6
                {
1448
6
                    throw ZipException(u"overlapping entries"_ustr);
1449
6
                }
1450
373k
                else if (it->first == nStart)
1451
373k
                {
1452
373k
                    if (it->second == nEnd)
1453
20.1k
                    {
1454
20.1k
                        unallocated.erase(it);
1455
20.1k
                        break;
1456
20.1k
                    }
1457
353k
                    else if (nEnd < it->second)
1458
353k
                    {
1459
353k
                        it->first = nEnd;
1460
353k
                        break;
1461
353k
                    }
1462
12
                    else
1463
12
                    {
1464
12
                        throw ZipException(u"overlapping entries"_ustr);
1465
12
                    }
1466
373k
                }
1467
642
                else if (nStart < it->second)
1468
189
                {
1469
189
                    if (nEnd < it->second)
1470
181
                    {
1471
181
                        auto const temp{it->first};
1472
181
                        it->first = nEnd;
1473
181
                        unallocated.insert(it, { temp, nStart });
1474
181
                        break;
1475
181
                    }
1476
8
                    else if (nEnd == it->second)
1477
4
                    {
1478
4
                        it->second = nStart;
1479
4
                        break;
1480
4
                    }
1481
4
                    else
1482
4
                    {
1483
4
                        throw ZipException(u"overlapping entries"_ustr);
1484
4
                    }
1485
189
                }
1486
373k
            }
1487
1488
            // Is this a FAT-compatible empty entry?
1489
374k
            if (aEntry.nSize == 0 && (versionMadeBy & 0xff00) == 0)
1490
4.89k
            {
1491
4.89k
                constexpr sal_uInt32 FILE_ATTRIBUTE_DIRECTORY = 16;
1492
4.89k
                if (externalFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1493
4.50k
                    continue; // This is a directory entry, not a stream - skip it
1494
4.89k
            }
1495
1496
369k
            if (aEntries.find(aEntry.sPath) != aEntries.end())
1497
3
            {
1498
3
                SAL_INFO("package", "Duplicate CEN entry: \"" << aEntry.sPath << "\"");
1499
3
                throw ZipException(u"Duplicate CEN entry"_ustr);
1500
3
            }
1501
369k
            if (aEntries.empty() && m_Checks == Checks::TryCheckInsensitive)
1502
0
            {
1503
0
                if (aEntry.sPath == "mimetype" && aEntry.nSize == 0)
1504
0
                {   // tdf#162866 AutoCorrect uses ODF package, directories are
1505
0
                    m_Checks = Checks::Default; // user-defined => ignore!
1506
0
                }
1507
0
                else
1508
0
                {
1509
0
                    m_Checks = Checks::CheckInsensitive;
1510
0
                }
1511
0
            }
1512
            // this is required for OOXML, but not for ODF
1513
369k
            auto const lowerPath(aEntry.sPath.toAsciiLowerCase());
1514
369k
            if (!m_EntriesInsensitive.insert(lowerPath).second && m_Checks == Checks::CheckInsensitive)
1515
0
            {
1516
0
                SAL_INFO("package", "Duplicate CEN entry (case insensitive): \"" << aEntry.sPath << "\"");
1517
0
                throw ZipException(u"Duplicate CEN entry (case insensitive)"_ustr);
1518
0
            }
1519
369k
            aEntries[aEntry.sPath] = aEntry;
1520
369k
        }
1521
1522
21.5k
        if (nCount != nTotal)
1523
0
            throw ZipException(u"Count != Total"_ustr );
1524
21.5k
        if (!unallocated.empty())
1525
35
        {
1526
35
            if (std::all_of(unallocated.begin(), unallocated.end(), [](auto const& it) {
1527
35
                    return it.second - it.first == 12 || it.second - it.first == 16;
1528
35
                }))
1529
5
            {
1530
5
                throw ZipException(u"Zip file has holes the size of data descriptors; producer forgot to set flag bit 3?"_ustr);
1531
5
            }
1532
30
            throw ZipException(u"Zip file has holes! It will leak!"_ustr);
1533
35
        }
1534
21.5k
    }
1535
21.8k
    catch ( IllegalArgumentException & )
1536
21.8k
    {
1537
        // seek can throw this...
1538
0
        nCenPos = -1; // make sure we return -1 to indicate an error
1539
0
    }
1540
19.9k
    return nCenPos;
1541
21.8k
}
1542
1543
bool ZipFile::readExtraFields(MemoryByteGrabber& aMemGrabber, sal_Int16 nExtraLen,
1544
        sal_uInt64& nSize, sal_uInt64& nCompressedSize,
1545
        std::optional<sal_uInt64> & roOffset,
1546
        std::string_view const * pCENFilenameToCheck)
1547
63.3k
{
1548
63.3k
    bool isZip64{false};
1549
1.17M
    while (nExtraLen > 0) // Extensible data fields
1550
1.10M
    {
1551
1.10M
        sal_Int16 nheaderID = aMemGrabber.ReadInt16();
1552
1.10M
        sal_uInt16 dataSize = aMemGrabber.ReadUInt16();
1553
1.10M
        if (nheaderID == 1) // Load Zip64 Extended Information Extra Field
1554
554
        {
1555
            // Datasize should be 28byte but some files have less (maybe non standard?)
1556
554
            nSize = aMemGrabber.ReadUInt64();
1557
554
            sal_uInt16 nReadSize = 8;
1558
554
            if (dataSize >= 16)
1559
497
            {
1560
497
                nCompressedSize = aMemGrabber.ReadUInt64();
1561
497
                nReadSize = 16;
1562
497
                if (dataSize >= 24)
1563
73
                {
1564
73
                    roOffset.emplace(aMemGrabber.ReadUInt64());
1565
73
                    nReadSize = 24;
1566
                    // 4 byte should be "Disk Start Number" but we not need it
1567
73
                }
1568
497
            }
1569
554
            if (dataSize > nReadSize)
1570
64
                aMemGrabber.skipBytes(dataSize - nReadSize);
1571
554
            isZip64 = true;
1572
554
        }
1573
        // Info-ZIP Unicode Path Extra Field - pointless as we expect UTF-8 in CEN already
1574
1.10M
        else if (nheaderID == 0x7075 && pCENFilenameToCheck) // ignore in recovery mode
1575
14
        {
1576
14
            if (aMemGrabber.remainingSize() < dataSize)
1577
3
            {
1578
3
                SAL_INFO("package", "Invalid Info-ZIP Unicode Path Extra Field: invalid TSize");
1579
3
                throw ZipException(u"Invalid Info-ZIP Unicode Path Extra Field"_ustr);
1580
3
            }
1581
11
            auto const nVersion = aMemGrabber.ReadUInt8();
1582
11
            if (nVersion != 1)
1583
3
            {
1584
3
                SAL_INFO("package", "Invalid Info-ZIP Unicode Path Extra Field: unexpected Version");
1585
3
                throw ZipException(u"Invalid Info-ZIP Unicode Path Extra Field"_ustr);
1586
3
            }
1587
            // this CRC32 is actually of the pCENFilenameToCheck
1588
            // so it's pointless to check it if we require the UnicodeName
1589
            // to be equal to the CEN name anyway (and pCENFilenameToCheck
1590
            // is already converted to UTF-16 here)
1591
8
            (void) aMemGrabber.ReadUInt32();
1592
            // this is required to be UTF-8
1593
8
            std::string_view unicodePath(reinterpret_cast<char const *>(aMemGrabber.getCurrentPos()),
1594
8
                    dataSize - 5);
1595
8
            aMemGrabber.skipBytes(dataSize - 5);
1596
8
            if (unicodePath != *pCENFilenameToCheck)
1597
8
            {
1598
8
                SAL_INFO("package", "Invalid Info-ZIP Unicode Path Extra Field: unexpected UnicodeName");
1599
8
                throw ZipException(u"Invalid Info-ZIP Unicode Path Extra Field"_ustr);
1600
8
            }
1601
8
        }
1602
1.10M
        else
1603
1.10M
        {
1604
1.10M
            aMemGrabber.skipBytes(dataSize);
1605
1.10M
        }
1606
1.10M
        nExtraLen -= dataSize + 4;
1607
1.10M
    }
1608
63.3k
    return isZip64;
1609
63.3k
}
1610
1611
// PK34: Local file header
1612
bool ZipFile::HandlePK34(std::span<const sal_Int8> data, sal_Int64 dataOffset, sal_Int64 totalSize)
1613
0
{
1614
0
    ZipEntry aEntry;
1615
0
    Sequence<sal_Int8> aTmpBuffer(data.data() + 4, 26);
1616
0
    MemoryByteGrabber aMemGrabber(aTmpBuffer);
1617
1618
0
    aEntry.nVersion = aMemGrabber.ReadInt16();
1619
0
    aEntry.nFlag = aMemGrabber.ReadInt16();
1620
0
    if ((aEntry.nFlag & 1) == 1)
1621
0
        return false;
1622
1623
0
    aEntry.nMethod = aMemGrabber.ReadInt16();
1624
0
    if (aEntry.nMethod != STORED && aEntry.nMethod != DEFLATED)
1625
0
        return false;
1626
1627
0
    aEntry.nTime = aMemGrabber.ReadInt32();
1628
0
    aEntry.nCrc = aMemGrabber.ReadInt32();
1629
0
    sal_uInt64 nCompressedSize = aMemGrabber.ReadUInt32();
1630
0
    sal_uInt64 nSize = aMemGrabber.ReadUInt32();
1631
0
    aEntry.nPathLen = aMemGrabber.ReadInt16();
1632
0
    aEntry.nExtraLen = aMemGrabber.ReadInt16();
1633
1634
0
    const sal_Int32 nDescrLength = (aEntry.nMethod == DEFLATED && (aEntry.nFlag & 8)) ? 16 : 0;
1635
0
    const sal_Int64 nBlockHeaderLength = aEntry.nPathLen + aEntry.nExtraLen + 30 + nDescrLength;
1636
0
    if (aEntry.nPathLen < 0 || aEntry.nExtraLen < 0 || dataOffset + nBlockHeaderLength > totalSize)
1637
0
        return false;
1638
1639
    // read always in UTF8, some tools seem not to set UTF8 bit
1640
0
    if (o3tl::make_unsigned(30 + aEntry.nPathLen) <= data.size())
1641
0
        aEntry.sPath = OUString(reinterpret_cast<char const*>(data.data() + 30), aEntry.nPathLen,
1642
0
                                RTL_TEXTENCODING_UTF8);
1643
0
    else
1644
0
    {
1645
0
        std::vector<sal_Int8> aFileName(aEntry.nPathLen);
1646
0
        aGrabber.seek(dataOffset + 30);
1647
0
        aEntry.nPathLen = aGrabber.readBytes(aFileName.data(), aEntry.nPathLen);
1648
0
        aEntry.sPath = OUString(reinterpret_cast<const char*>(aFileName.data()),
1649
0
                                aEntry.nPathLen, RTL_TEXTENCODING_UTF8);
1650
0
    }
1651
0
    aEntry.sPath = aEntry.sPath.replace('\\', '/');
1652
1653
    // read 64bit header
1654
0
    if (aEntry.nExtraLen > 0)
1655
0
    {
1656
0
        std::vector<sal_Int8> aExtraBuffer(aEntry.nExtraLen);
1657
0
        if (o3tl::make_unsigned(30 + aEntry.nPathLen) + aEntry.nExtraLen <= data.size())
1658
0
        {
1659
0
            auto it = data.begin() + 30 + aEntry.nPathLen;
1660
0
            std::copy(it, it + aEntry.nExtraLen, aExtraBuffer.begin());
1661
0
        }
1662
0
        else
1663
0
        {
1664
0
            aGrabber.seek(dataOffset + 30 + aEntry.nExtraLen);
1665
0
            aGrabber.readBytes(aExtraBuffer.data(), aEntry.nExtraLen);
1666
0
        }
1667
0
        MemoryByteGrabber aMemGrabberExtra(aExtraBuffer.data(), aEntry.nExtraLen);
1668
0
        if (aEntry.nExtraLen > 0)
1669
0
        {
1670
0
            ::std::optional<sal_uInt64> oOffset64;
1671
0
            readExtraFields(aMemGrabberExtra, aEntry.nExtraLen, nSize, nCompressedSize, oOffset64, nullptr);
1672
0
        }
1673
0
    }
1674
1675
0
    sal_Int64 nDataSize = (aEntry.nMethod == DEFLATED) ? nCompressedSize : nSize;
1676
0
    sal_Int64 nBlockLength = nDataSize + nBlockHeaderLength;
1677
1678
0
    if (dataOffset + nBlockLength > totalSize)
1679
0
        return false;
1680
1681
0
    aEntry.nCompressedSize = nCompressedSize;
1682
0
    aEntry.nSize = nSize;
1683
1684
0
    aEntry.nOffset = dataOffset + 30 + aEntry.nPathLen + aEntry.nExtraLen;
1685
1686
0
    if ((aEntry.nSize || aEntry.nCompressedSize) && !checkSizeAndCRC(aEntry))
1687
0
    {
1688
0
        aEntry.nCrc = 0;
1689
0
        aEntry.nCompressedSize = 0;
1690
0
        aEntry.nSize = 0;
1691
0
    }
1692
1693
    // Do not add this entry, if it is empty and is a directory of an already existing entry
1694
0
    if (aEntry.nSize == 0 && aEntry.nCompressedSize == 0
1695
0
        && std::find_if(aEntries.begin(), aEntries.end(),
1696
0
                        [path = OUString(aEntry.sPath + "/")](const auto& r)
1697
0
                        { return r.first.startsWith(path); })
1698
0
               != aEntries.end())
1699
0
        return false;
1700
1701
0
    auto const lowerPath(aEntry.sPath.toAsciiLowerCase());
1702
0
    if (m_EntriesInsensitive.find(lowerPath) != m_EntriesInsensitive.end())
1703
0
    {   // this is required for OOXML, but not for ODF
1704
0
        return false;
1705
0
    }
1706
0
    m_EntriesInsensitive.insert(lowerPath);
1707
0
    aEntries.emplace(aEntry.sPath, aEntry);
1708
1709
    // Drop any "directory" entry corresponding to this one's path; since we don't use
1710
    // central directory, we don't see external file attributes, so sanitize here
1711
0
    sal_Int32 i = 0;
1712
0
    for (OUString subdir = aEntry.sPath.getToken(0, '/', i); i >= 0;
1713
0
         subdir += OUString::Concat("/") + o3tl::getToken(aEntry.sPath, 0, '/', i))
1714
0
    {
1715
0
        if (auto it = aEntries.find(subdir); it != aEntries.end())
1716
0
        {
1717
            // if not empty, let it fail later in ZipPackage::getZipFileContents
1718
0
            if (it->second.nSize == 0 && it->second.nCompressedSize == 0)
1719
0
                aEntries.erase(it);
1720
0
        }
1721
0
    }
1722
0
    return (aEntry.nFlag & 8) && (aEntry.nCompressedSize == 0);
1723
0
}
1724
1725
// PK78: Data descriptor
1726
void ZipFile::HandlePK78(std::span<const sal_Int8> data, sal_Int64 dataOffset)
1727
0
{
1728
0
    sal_Int64 nCompressedSize, nSize;
1729
0
    Sequence<sal_Int8> aTmpBuffer(data.data() + 4, 12 + 8 + 4);
1730
0
    MemoryByteGrabber aMemGrabber(aTmpBuffer);
1731
0
    sal_Int32 nCRC32 = aMemGrabber.ReadInt32();
1732
1733
    // FIXME64: find a better way to recognize if Zip64 mode is used
1734
    // Now we check if the memory at +16 byte seems to be a signature
1735
    // if not, then probably Zip64 mode is used here, except
1736
    // if memory at +24 byte seems not to be a signature.
1737
    // Normally Data Descriptor should followed by the next Local File header
1738
    // that should start with PK34, except for the last file, then it may
1739
    // followed by Central directory that start with PK12, or
1740
    // followed by "archive decryption header" that don't have a signature.
1741
0
    if ((data[16] == 'P' && data[17] == 'K' && data[19] == data[18] + 1
1742
0
         && (data[18] == 3 || data[18] == 1))
1743
0
        || !(data[24] == 'P' && data[25] == 'K' && data[27] == data[26] + 1
1744
0
             && (data[26] == 3 || data[26] == 1)))
1745
0
    {
1746
0
        nCompressedSize = aMemGrabber.ReadUInt32();
1747
0
        nSize = aMemGrabber.ReadUInt32();
1748
0
    }
1749
0
    else
1750
0
    {
1751
0
        nCompressedSize = aMemGrabber.ReadUInt64();
1752
0
        nSize = aMemGrabber.ReadUInt64();
1753
0
    }
1754
0
    TryDDImpl(dataOffset, nCRC32, nCompressedSize, nSize);
1755
0
}
1756
1757
bool ZipFile::TryDDImpl(sal_Int64 const dataOffset, sal_Int32 const nCRC32,
1758
        sal_Int64 const nCompressedSize, sal_Int64 const nSize)
1759
0
{
1760
0
    for (auto& rEntry : aEntries)
1761
0
    {
1762
        // this is a broken package, accept this block not only for DEFLATED streams
1763
0
        if ((rEntry.second.nFlag & 8) == 0)
1764
0
            continue;
1765
0
        sal_Int64 nStreamOffset = dataOffset - nCompressedSize;
1766
0
        if (nStreamOffset == rEntry.second.nOffset
1767
0
            && nCompressedSize > rEntry.second.nCompressedSize)
1768
0
        {
1769
            // only DEFLATED blocks need to be checked
1770
0
            bool bAcceptBlock = (rEntry.second.nMethod == STORED && nCompressedSize == nSize);
1771
1772
0
            if (!bAcceptBlock)
1773
0
            {
1774
0
                sal_Int64 nRealSize = 0;
1775
0
                sal_Int32 nRealCRC = 0;
1776
0
                getSizeAndCRC(nStreamOffset, nCompressedSize, &nRealSize, &nRealCRC);
1777
0
                bAcceptBlock = (nRealSize == nSize && nRealCRC == nCRC32);
1778
0
            }
1779
1780
0
            if (bAcceptBlock)
1781
0
            {
1782
0
                rEntry.second.nCrc = nCRC32;
1783
0
                rEntry.second.nCompressedSize = nCompressedSize;
1784
0
                rEntry.second.nSize = nSize;
1785
0
                return true;
1786
0
            }
1787
0
        }
1788
#if 0
1789
// for now ignore clearly broken streams
1790
        else if( !rEntry.second.nCompressedSize )
1791
        {
1792
            rEntry.second.nCrc = nCRC32;
1793
            sal_Int32 nRealStreamSize = dataOffset - rEntry.second.nOffset;
1794
            rEntry.second.nCompressedSize = nRealStreamSize;
1795
            rEntry.second.nSize = nSize;
1796
        }
1797
#endif
1798
0
    }
1799
0
    return false;
1800
0
}
1801
1802
bool ZipFile::TryDDEndAt(std::span<const sal_Int8> const data, sal_Int64 const dataOffset)
1803
0
{
1804
0
    assert(!aEntries.empty()); // HandlePK34 must ensure this
1805
1806
0
    Sequence<sal_Int8> const buf32{data.data() + 8, 12};
1807
0
    MemoryByteGrabber mbg32{buf32};
1808
0
    sal_uInt32 const nCrc32{mbg32.ReadUInt32()};
1809
0
    sal_uInt32 const nCompressedSize32{mbg32.ReadUInt32()};
1810
0
    sal_uInt32 const nSize32{mbg32.ReadUInt32()};
1811
1812
0
    if (TryDDImpl(dataOffset + 8, nCrc32, nCompressedSize32, nSize32))
1813
0
    {
1814
0
        return true;
1815
0
    }
1816
1817
    // then try if Zip64 crc/sizes are plausible
1818
0
    Sequence<sal_Int8> const buf64{data.data(), 20};
1819
0
    MemoryByteGrabber mbg64{buf64};
1820
0
    sal_uInt32 const nCrc64{mbg64.ReadUInt32()};
1821
0
    sal_uInt64 const nCompressedSize64{mbg64.ReadUInt64()};
1822
0
    sal_uInt64 const nSize64{mbg64.ReadUInt64()};
1823
1824
0
    return TryDDImpl(dataOffset, nCrc64, nCompressedSize64, nSize64);
1825
0
}
1826
1827
void ZipFile::recover()
1828
0
{
1829
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
1830
1831
0
    const sal_Int64 nToRead = 32000;
1832
0
    std::vector<sal_Int8> aBuffer(nToRead);
1833
1834
0
    try
1835
0
    {
1836
0
        const sal_Int64 nLength = aGrabber.getLength();
1837
0
        if (nLength < ENDHDR)
1838
0
            return;
1839
1840
0
        aGrabber.seek( 0 );
1841
1842
0
        bool findDD{false};
1843
0
        sal_Int32 nRead;
1844
0
        for( sal_Int64 nGenPos = 0; (nRead = aGrabber.readBytes( aBuffer.data(), nToRead )) && nRead > 16; )
1845
0
        {
1846
0
            const sal_Int8 *pBuffer = aBuffer.data();
1847
0
            const sal_Int32 nBufSize = nRead;
1848
1849
0
            sal_Int64 nPos = 0;
1850
            // the buffer should contain at least one header,
1851
            // or if it is end of the file, at least the postheader with sizes and hash
1852
0
            while( nPos < nBufSize - 30
1853
0
                || ( nBufSize < nToRead && nPos < nBufSize - 16 ) )
1854
1855
0
            {
1856
0
                if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K')
1857
0
                {
1858
0
                    if (pBuffer[nPos+2] == 7 && pBuffer[nPos+3] == 8)
1859
0
                    {
1860
0
                        findDD = false;
1861
0
                        HandlePK78(std::span(pBuffer + nPos, nBufSize - nPos), nGenPos + nPos);
1862
0
                        nPos += 4;
1863
0
                    }
1864
0
                    else
1865
0
                    {
1866
0
                        if (findDD && 30 < nGenPos+nPos
1867
0
                            && TryDDEndAt(std::span(pBuffer + nPos - 20, 20), nGenPos + nPos - 20))
1868
0
                        {
1869
0
                            findDD = false;
1870
0
                        }
1871
0
                        if (nPos < nBufSize - 30
1872
0
                            && pBuffer[nPos+2] == 3 && pBuffer[nPos+3] == 4)
1873
0
                        {
1874
0
                            findDD = HandlePK34(std::span(pBuffer + nPos, nBufSize - nPos), nGenPos + nPos, nLength);
1875
0
                            nPos += 4;
1876
0
                        }
1877
0
                        else
1878
0
                        {
1879
0
                            ++nPos;
1880
0
                        }
1881
0
                    }
1882
0
                }
1883
0
                else
1884
0
                    nPos++;
1885
0
            }
1886
1887
0
            nGenPos += nPos;
1888
0
            aGrabber.seek( nGenPos );
1889
0
        }
1890
0
    }
1891
0
    catch ( IllegalArgumentException& )
1892
0
    {
1893
0
        throw ZipException(u"Zip END signature not found!"_ustr );
1894
0
    }
1895
0
    catch ( NotConnectedException& )
1896
0
    {
1897
0
        throw ZipException(u"Zip END signature not found!"_ustr );
1898
0
    }
1899
0
    catch ( BufferSizeExceededException& )
1900
0
    {
1901
0
        throw ZipException(u"Zip END signature not found!"_ustr );
1902
0
    }
1903
0
}
1904
1905
bool ZipFile::checkSizeAndCRC( const ZipEntry& aEntry )
1906
0
{
1907
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
1908
1909
0
    try
1910
0
    {
1911
0
        sal_Int32 nCRC = 0;
1912
0
        sal_Int64 nSize = 0;
1913
1914
0
        if( aEntry.nMethod == STORED )
1915
0
            return ( getCRC( aEntry.nOffset, aEntry.nSize ) == aEntry.nCrc );
1916
1917
0
        if (aEntry.nCompressedSize < 0)
1918
0
        {
1919
0
            SAL_WARN("package", "bogus compressed size of: " << aEntry.nCompressedSize);
1920
0
            return false;
1921
0
        }
1922
1923
0
        getSizeAndCRC( aEntry.nOffset, aEntry.nCompressedSize, &nSize, &nCRC );
1924
0
        return ( aEntry.nSize == nSize && aEntry.nCrc == nCRC );
1925
0
    }
1926
0
    catch (uno::Exception const&)
1927
0
    {
1928
0
        return false;
1929
0
    }
1930
0
}
1931
1932
sal_Int32 ZipFile::getCRC( sal_Int64 nOffset, sal_Int64 nSize )
1933
0
{
1934
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
1935
1936
0
    CRC32 aCRC;
1937
0
    sal_Int64 nBlockSize = ::std::min(nSize, static_cast< sal_Int64 >(32000));
1938
0
    std::vector<sal_Int8> aBuffer(nBlockSize);
1939
1940
0
    aGrabber.seek( nOffset );
1941
0
    sal_Int64 nRead = 0;
1942
0
    while (nRead < nSize)
1943
0
    {
1944
0
        sal_Int64 nToRead = std::min(nSize - nRead, nBlockSize);
1945
0
        sal_Int64 nReadThisTime = aGrabber.readBytes(aBuffer.data(), nToRead);
1946
0
        aCRC.updateSegment(aBuffer.data(), nReadThisTime);
1947
0
        nRead += nReadThisTime;
1948
0
    }
1949
1950
0
    return aCRC.getValue();
1951
0
}
1952
1953
void ZipFile::getSizeAndCRC( sal_Int64 nOffset, sal_Int64 nCompressedSize, sal_Int64 *nSize, sal_Int32 *nCRC )
1954
0
{
1955
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
1956
1957
0
    CRC32 aCRC;
1958
0
    sal_Int64 nRealSize = 0;
1959
0
    ZipUtils::InflaterBytesZlib aInflaterLocal;
1960
0
    sal_Int32 nBlockSize = static_cast< sal_Int32 > (::std::min( nCompressedSize, static_cast< sal_Int64 >( 32000 ) ) );
1961
0
    std::vector < sal_Int8 > aBuffer(nBlockSize);
1962
0
    std::vector< sal_Int8 > aData( nBlockSize );
1963
1964
0
    aGrabber.seek( nOffset );
1965
0
    sal_Int32 nRead;
1966
0
    for ( sal_Int64 ind = 0;
1967
0
          !aInflaterLocal.finished()
1968
0
          && (nRead = aGrabber.readBytes( aBuffer.data(), nBlockSize ))
1969
0
          && ind * nBlockSize < nCompressedSize;
1970
0
          ind++ )
1971
0
    {
1972
0
        sal_Int32 nLastInflated = 0;
1973
0
        sal_Int64 nInBlock = 0;
1974
1975
0
        aInflaterLocal.setInput( aBuffer.data(), nRead );
1976
0
        do
1977
0
        {
1978
0
            nLastInflated = aInflaterLocal.doInflateSegment( aData.data(), nBlockSize, 0, nBlockSize );
1979
0
            aCRC.updateSegment( aData.data(), nLastInflated );
1980
0
            nInBlock += nLastInflated;
1981
0
        } while( !aInflaterLocal.finished() && nLastInflated );
1982
1983
0
        nRealSize += nInBlock;
1984
0
    }
1985
1986
0
    *nSize = nRealSize;
1987
0
    *nCRC = aCRC.getValue();
1988
0
}
1989
1990
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */