/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
if ( aEntry.nMethod == STORED )
m_bToBeCompressed = false;
// this is called first, parseManifest may overwrite it if it's encrypted
assert(m_nOwnStreamOrigSize == 0);
m_nOwnStreamOrigSize = aEntry.nSize;
}
uno::Reference< io::XInputStream > const & ZipPackageStream::GetOwnSeekStream()
{ if ( !m_bHasSeekable && m_xStream.is() )
{ // The package component requires that every stream either be FROM a package or it must support XSeekable! // The only exception is a nonseekable stream that is provided only for storing, if such a stream // is accessed before commit it MUST be wrapped. // Wrap the stream in case it is not seekable
m_xStream = ::comphelper::OSeekableInputWrapper::CheckSeekableCanWrap( m_xStream, m_xContext );
uno::Reference< io::XSeekable > xSeek( m_xStream, UNO_QUERY_THROW );
if ( m_xBaseEncryptionData.is() ) throw ZipIOException(THROW_WHERE "Encrypted stream without encryption data!" );
uno::Reference< io::XSeekable > xSeek( GetOwnSeekStream(), UNO_QUERY ); if ( !xSeek.is() ) throw ZipIOException(THROW_WHERE "The stream must be seekable!" );
// copy the raw stream to the temporary file starting from the current position
::comphelper::OStorageHelper::CopyInputToOutput( GetOwnSeekStream(), xTempFile );
xTempFile->closeOutput();
xTempFile->seek( 0 );
for (constauto& rKey : m_aStorageEncryptionKeys) if ( rKey.Name == aNameToFind )
rKey.Value >>= aResult;
// empty keys are not allowed here // so it is not important whether there is no key, or the key is empty, it is an error if ( !aResult.hasElements() ) throw uno::RuntimeException(THROW_WHERE "No expected key is provided!" );
} else
aResult = m_aEncryptionKey;
if ( !aResult.hasElements() || !m_bHaveOwnKey )
aResult = m_rZipPackage.GetEncryptionKey();
return aResult;
}
sal_Int32 ZipPackageStream::GetStartKeyGenID() const
{ // generally should all the streams use the same Start Key // but if raw copy without password takes place, we should preserve the imported algorithm return m_oImportedAlgorithms
? m_oImportedAlgorithms->nImportedStartKeyAlgorithm
: m_rZipPackage.GetStartKeyGenID();
}
// copy all the properties of this stream to the new stream
xNewPSProps->setPropertyValue(u"MediaType"_ustr, Any( msMediaType ) );
xNewPSProps->setPropertyValue(u"Compressed"_ustr, Any( m_bToBeCompressed ) ); if ( m_bToBeEncrypted )
{
xNewPSProps->setPropertyValue(ENCRYPTION_KEY_PROPERTY, Any( aKey ) );
xNewPSProps->setPropertyValue(u"Encrypted"_ustr, Any( true ) );
}
// insert a new stream in the package
Any aRoot = pPackage->getByHierarchicalName(u"/"_ustr); auto xRootNameContainer = aRoot.queryThrow<container::XNameContainer>();
// commit the temporary package
pPackage->commitChanges();
// get raw stream from the temporary package
uno::Reference< io::XInputStream > xInRaw; if ( bAddHeaderForEncr )
xInRaw = xNewPackStream->getRawStream(); else
xInRaw = xNewPackStream->getPlainRawStream();
// create another temporary file
rtl::Reference < utl::TempFileFastService > xTempOut = new utl::TempFileFastService;
// copy the raw stream to the temporary file
::comphelper::OStorageHelper::CopyInputToOutput( xInRaw, xTempOut );
xTempOut->closeOutput();
xTempOut->seek( 0 );
// close raw stream, package stream and folder
xInRaw.clear();
xNewPSProps.clear();
xNewPackStream.clear();
xRootNameContainer.clear();
// return the stream representing the first temporary file return xTempOut;
} catch ( RuntimeException& )
{ throw;
} catch ( Exception& )
{
}
throw io::IOException(THROW_WHERE );
}
// presumably the purpose of this is to transfer encrypted streams between // storages, needed for password-protected macros in documents, which is // tragically a feature that exists bool ZipPackageStream::ParsePackageRawStream()
{
OSL_ENSURE( GetOwnSeekStream().is(), "A stream must be provided!" );
staticvoid ImplSetStoredData( ZipEntry & rEntry, uno::Reference< io::XInputStream> const & rStream )
{ // It's very annoying that we have to do this, but lots of zip packages // don't allow data descriptors for STORED streams, meaning we have to // know the size and CRC32 of uncompressed streams before we actually // write them !
CRC32 aCRC32;
rEntry.nMethod = STORED;
rEntry.nCompressedSize = rEntry.nSize = aCRC32.updateStream ( rStream );
rEntry.nCrc = aCRC32.getValue();
}
// In case the entry we are reading is also the entry we are writing, we will // store the ZipEntry data in pTempEntry
// if pTempEntry is necessary, it will be released and passed to the ZipOutputStream // and be deleted in the ZipOutputStream destructor
std::unique_ptr < ZipEntry > pAutoTempEntry ( new ZipEntry(aEntry) );
ZipEntry* pTempEntry = pAutoTempEntry.get();
bool bBackgroundThreadDeflate = false; bool bTransportOwnEncrStreamAsRaw = false; // During the storing the original size of the stream can be changed // TODO/LATER: get rid of this hack if (!m_bRawStream)
{
m_nOwnStreamOrigSize = aEntry.nSize;
}
bool bUseNonSeekableAccess = false;
uno::Reference < io::XInputStream > xStream; if ( !IsPackageMember() && !m_bRawStream && !bToBeEncrypted && bToBeCompressed )
{ // the stream is not a package member, not a raw stream, // it should not be encrypted and it should be compressed, // in this case nonseekable access can be used
if (bUseNonSeekableAccess)
{ // this should work for XUnbufferedStream/OInputCompStream at least if (pTempEntry->nSize == -1)
{ // this is needed in writeLOC to detect Zip64
pTempEntry->nSize = xStream->available();
}
} else
{
xStream = getRawData();
if ( !xStream.is() )
{
OSL_FAIL( "ZipPackageStream didn't have a stream associated with it, skipping!" ); returnfalse;
}
uno::Reference < io::XSeekable > xSeek ( xStream, uno::UNO_QUERY ); try
{ if ( xSeek.is() )
{ // If the stream is a raw one, then we should be positioned // at the beginning of the actual data if ( !bToBeCompressed || m_bRawStream )
{ // The raw stream can neither be encrypted nor connected
OSL_ENSURE( !m_bRawStream || !(bToBeCompressed || bToBeEncrypted), "The stream is already encrypted!" );
xSeek->seek ( m_bRawStream ? m_nMagicalHackPos : 0 );
ImplSetStoredData ( *pTempEntry, xStream );
} elseif ( bToBeEncrypted )
{ // this is the correct original size
m_nOwnStreamOrigSize = xSeek->getLength();
}
if (pTempEntry->nSize == -1)
{ // this is needed in writeLOC to detect Zip64
pTempEntry->nSize = xSeek->getLength();
}
xSeek->seek ( 0 );
} else
{ // Okay, we don't have an xSeekable stream. This is possibly bad. // check if it's one of our own streams, if it is then we know that // each time we ask for it we'll get a new stream that will be // at position zero...otherwise, assert and skip this stream... if ( IsPackageMember() )
{ // if the password has been changed then the stream should not be package member any more if ( m_bIsEncrypted && m_bToBeEncrypted )
{ // Should be handled close to the raw stream handling
bTransportOwnEncrStreamAsRaw = true;
pTempEntry->nMethod = STORED;
// TODO/LATER: get rid of this situation // this size should be different from the one that will be stored in manifest.xml // it is used in storing algorithms and after storing the correct size will be set
pTempEntry->nSize = pTempEntry->nCompressedSize;
}
} else
{ returnfalse;
}
}
} catch ( uno::Exception& )
{ returnfalse;
}
if ( bToBeEncrypted || m_bRawStream || bTransportOwnEncrStreamAsRaw )
{ if ( bToBeEncrypted && !bTransportOwnEncrStreamAsRaw )
{
uno::Sequence<sal_Int8> aSalt(16); // note: for GCM it's particularly important that IV is unique
uno::Sequence<sal_Int8> aVector(GetIVSize()); if (rtl_random_getBytes(nullptr, aSalt.getArray(), 16) != rtl_Random_E_None)
{ throw uno::RuntimeException(u"rtl_random_getBytes failed"_ustr);
} if (rtl_random_getBytes(nullptr, aVector.getArray(), aVector.getLength()) != rtl_Random_E_None)
{ throw uno::RuntimeException(u"rtl_random_getBytes failed"_ustr);
} if ( !m_bHaveOwnKey )
{
m_aEncryptionKey = rEncryptionKey;
m_aStorageEncryptionKeys.realloc( 0 );
}
// last property is digest, which is inserted later if we didn't have // a magic header
aPropSet.realloc(PKG_SIZE_ENCR_MNFST);
pPropSet = aPropSet.getArray();
pPropSet[PKG_MNFST_INIVECTOR].Name = "InitialisationVector";
pPropSet[PKG_MNFST_INIVECTOR].Value <<= m_xBaseEncryptionData->m_aInitVector;
pPropSet[PKG_MNFST_SALT].Name = "Salt";
pPropSet[PKG_MNFST_SALT].Value <<= m_xBaseEncryptionData->m_aSalt; if (m_xBaseEncryptionData->m_oArgon2Args)
{
pPropSet[PKG_MNFST_KDF].Name = "KeyDerivationFunction";
pPropSet[PKG_MNFST_KDF].Value <<= xml::crypto::KDFID::Argon2id;
pPropSet[PKG_MNFST_ARGON2ARGS].Name = "Argon2Args";
uno::Sequence<sal_Int32> const args{
::std::get<0>(*m_xBaseEncryptionData->m_oArgon2Args),
::std::get<1>(*m_xBaseEncryptionData->m_oArgon2Args),
::std::get<2>(*m_xBaseEncryptionData->m_oArgon2Args) };
pPropSet[PKG_MNFST_ARGON2ARGS].Value <<= args;
} elseif (m_xBaseEncryptionData->m_oPBKDFIterationCount)
{
pPropSet[PKG_MNFST_KDF].Name = "KeyDerivationFunction";
pPropSet[PKG_MNFST_KDF].Value <<= xml::crypto::KDFID::PBKDF2;
pPropSet[PKG_MNFST_ITERATION].Name = "IterationCount";
pPropSet[PKG_MNFST_ITERATION].Value <<= *m_xBaseEncryptionData->m_oPBKDFIterationCount;
} else
{
pPropSet[PKG_MNFST_KDF].Name = "KeyDerivationFunction";
pPropSet[PKG_MNFST_KDF].Value <<= xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P;
}
// Need to store the uncompressed size in the manifest
OSL_ENSURE( m_nOwnStreamOrigSize >= 0, "The stream size was not correctly initialized!" );
pPropSet[PKG_MNFST_UCOMPSIZE].Name = "Size";
pPropSet[PKG_MNFST_UCOMPSIZE].Value <<= m_nOwnStreamOrigSize;
if ( m_bRawStream || bTransportOwnEncrStreamAsRaw )
{
::rtl::Reference< EncryptionData > xEncData = GetEncryptionData(); if ( !xEncData.is() ) throw uno::RuntimeException();
bool bSuccess = true; // If the entry is already stored in the zip file in the format we // want for this write...copy it raw if ( !bUseNonSeekableAccess
&& ( m_bRawStream || bTransportOwnEncrStreamAsRaw
|| ( IsPackageMember() && !bToBeEncrypted
&& ( ( aEntry.nMethod == DEFLATED && bToBeCompressed )
|| ( aEntry.nMethod == STORED && !bToBeCompressed ) ) ) ) )
{ // If it's a PackageMember, then it's an unbuffered stream and we need // to get a new version of it as we can't seek backwards. if ( IsPackageMember() )
{
xStream = getRawData(); if ( !xStream.is() )
{ // Make sure that we actually _got_ a new one ! returnfalse;
}
}
try
{ if ( m_bRawStream )
xStream->skipBytes( m_nMagicalHackPos );
do
{
nLength = xStream->readBytes( aSeq, n_ConstBufferSize ); if (nLength != n_ConstBufferSize)
aSeq.realloc(nLength);
rZipOut.rawWrite(aSeq);
} while ( nLength == n_ConstBufferSize );
rZipOut.rawCloseEntry();
} catch ( ZipException& )
{
bSuccess = false;
} catch ( io::IOException& )
{
bSuccess = false;
}
} else
{ // This stream is definitely not a raw stream
// If nonseekable access is used the stream should be at the beginning and // is useless after the storing. Thus if the storing fails the package should // be thrown away ( as actually it is done currently )! // To allow to reuse the package after the error, the optimization must be removed!
// If it's a PackageMember, then our previous reference held a 'raw' stream // so we need to re-get it, unencrypted, uncompressed and positioned at the // beginning of the stream if ( IsPackageMember() )
{
xStream = getInputStream(); if ( !xStream.is() )
{ // Make sure that we actually _got_ a new one ! returnfalse;
}
}
uno::Reference< io::XSeekable > xSeek(xStream, uno::UNO_QUERY); // It's not worth to deflate jpegs to save ~1% in a slow process // Unfortunately, does not work for streams protected by password if (xSeek.is() && msMediaType.endsWith("/jpeg") && !m_bToBeEncrypted && !m_bToBeCompressed)
{
ImplSetStoredData(*pTempEntry, xStream);
xSeek->seek(0);
}
try
{
ZipOutputStream::setEntry(*pTempEntry); // the entry is provided to the ZipOutputStream that will delete it
if (pTempEntry->nMethod == STORED)
{
sal_Int32 nLength;
uno::Sequence< sal_Int8 > aSeq(n_ConstBufferSize);
rZipOut.writeLOC(std::move(pAutoTempEntry), bToBeEncrypted); do
{
nLength = xStream->readBytes(aSeq, n_ConstBufferSize); if (nLength != n_ConstBufferSize)
aSeq.realloc(nLength);
rZipOut.rawWrite(aSeq);
} while ( nLength == n_ConstBufferSize );
rZipOut.rawCloseEntry(bToBeEncrypted);
} else
{ // tdf#89236 Encrypting in a background thread does not work
bBackgroundThreadDeflate = !bToBeEncrypted; // Do not deflate small streams using threads. XSeekable's getLength() // gives the full size, XInputStream's available() may not be // the full size, but it appears that at this point it usually is.
sal_Int64 estimatedSize = xSeek.is() ? xSeek->getLength() : xStream->available();
if (estimatedSize > 1000000)
{ // Use ThreadDeflater which will split the stream into blocks and compress // them in threads, but not in background (i.e. writeStream() will block). // This is suitable for large data.
bBackgroundThreadDeflate = false;
rZipOut.writeLOC(std::move(pAutoTempEntry), bToBeEncrypted);
ZipOutputEntryParallel aZipEntry(rZipOut.getStream(), m_xContext, pTempEntry, this, bToBeEncrypted);
aZipEntry.writeStream(xStream);
rZipOut.rawCloseEntry(bToBeEncrypted);
} elseif (bBackgroundThreadDeflate && estimatedSize > 100000)
{ // tdf#93553 limit to a useful amount of pending tasks. Having way too many // tasks pending may use a lot of memory. Take number of available // cores and allow 4-times the amount for having the queue well filled. The // 2nd parameter is the time to wait between cleanups in 10th of a second. // Both values may be added to the configuration settings if needed. static std::size_t nAllowedTasks(comphelper::ThreadPool::getPreferredConcurrency() * 4); //TODO: overflow
rZipOut.reduceScheduledThreadTasksToGivenNumberOrLess(nAllowedTasks);
if ( bToBeEncrypted )
{
::rtl::Reference< EncryptionData > xEncData = GetEncryptionData(); if ( !xEncData.is() ) throw uno::RuntimeException();
// very confusing: half the encryption properties are // unconditionally added above and the other half conditionally; // assert that we have the expected group and not duplicates
assert(std::any_of(aPropSet.begin(), aPropSet.end(), [](autoconst& it){ return it.Name == "Salt"; }));
assert(!std::any_of(aPropSet.begin(), aPropSet.end(), [](autoconst& it){ return it.Name == sEncryptionAlgProperty; }));
// XDataSinkEncrSupport
uno::Reference< io::XInputStream > SAL_CALL ZipPackageStream::getDataStream()
{ // There is no stream attached to this object if ( m_nStreamMode == PACKAGE_STREAM_NOTSET ) return uno::Reference< io::XInputStream >();
// this method can not be used together with old approach if ( m_nStreamMode == PACKAGE_STREAM_DETECT ) throw packages::zip::ZipIOException(THROW_WHERE );
if ( IsPackageMember() )
{
uno::Reference< io::XInputStream > xResult;
::std::optional<sal_Int64> oDecryptedSize; if (m_bIsEncrypted)
{
oDecryptedSize.emplace(m_nOwnStreamOrigSize);
} try
{
xResult = m_rZipPackage.getZipFile().getDataStream(aEntry,
GetEncryptionData(Bugs::None), oDecryptedSize,
m_rZipPackage.GetSharedMutexRef());
} catch( const packages::WrongPasswordException& )
{ // note: due to SHA1 check this fallback is only done for // * ODF 1.2 files written by OOo < 3.4beta / LO < 3.5 // * ODF 1.1/OOoXML files written by any version if (m_oImportedAlgorithms
&& m_oImportedAlgorithms->nImportedStartKeyAlgorithm == xml::crypto::DigestID::SHA1)
{
SAL_WARN("package", "ZipPackageStream::getDataStream(): SHA1 mismatch, trying fallbacks..."); try
{ // tdf#114939 try with legacy StarOffice SHA1 bug
xResult = m_rZipPackage.getZipFile().getDataStream(aEntry,
GetEncryptionData(Bugs::WrongSHA1), oDecryptedSize,
m_rZipPackage.GetSharedMutexRef()); return xResult;
} catch (const packages::WrongPasswordException&)
{ /* ignore and try next... */
}
try
{ // rhbz#1013844 / fdo#47482 workaround for the encrypted // OpenOffice.org 1.0 documents generated by Libreoffice <= // 3.6 with the new encryption format and using SHA256, but // missing a specified startkey of SHA256
// force SHA256 and see if that works
m_oImportedAlgorithms->nImportedStartKeyAlgorithm = xml::crypto::DigestID::SHA256;
xResult = m_rZipPackage.getZipFile().getDataStream(aEntry,
GetEncryptionData(), oDecryptedSize,
m_rZipPackage.GetSharedMutexRef()); return xResult;
} catch (const packages::WrongPasswordException&)
{ // if that didn't work, restore to SHA1 and trundle through the *other* earlier // bug fix
m_oImportedAlgorithms->nImportedStartKeyAlgorithm = xml::crypto::DigestID::SHA1;
}
uno::Reference< io::XInputStream > SAL_CALL ZipPackageStream::getRawStream()
{ // There is no stream attached to this object if ( m_nStreamMode == PACKAGE_STREAM_NOTSET ) return uno::Reference< io::XInputStream >();
// this method can not be used together with old approach if ( m_nStreamMode == PACKAGE_STREAM_DETECT ) throw packages::zip::ZipIOException(THROW_WHERE );
if ( IsPackageMember() )
{ if ( !m_bIsEncrypted || !GetEncryptionData().is() ) throw packages::NoEncryptionException(THROW_WHERE );
uno::Reference< io::XInputStream > SAL_CALL ZipPackageStream::getPlainRawStream()
{ // There is no stream attached to this object if ( m_nStreamMode == PACKAGE_STREAM_NOTSET ) return uno::Reference< io::XInputStream >();
// this method can not be used together with old approach if ( m_nStreamMode == PACKAGE_STREAM_DETECT ) throw packages::zip::ZipIOException(THROW_WHERE );
if ( IsPackageMember() )
{
::std::optional<sal_Int64> oDecryptedSize; if (m_bIsEncrypted)
{
oDecryptedSize.emplace(m_nOwnStreamOrigSize);
} return m_rZipPackage.getZipFile().getRawData(aEntry, GetEncryptionData(),
oDecryptedSize, m_rZipPackage.GetSharedMutexRef());
} elseif ( GetOwnSeekStream().is() )
{ if ( m_nStreamMode == PACKAGE_STREAM_RAW )
{ // the header should not be returned here return GetRawEncrStreamNoHeaderCopy();
} elseif ( m_nStreamMode == PACKAGE_STREAM_DATA ) return TryToGetRawFromDataStream( false );
}
bool bEnc = false; if ( !(aValue >>= bEnc) ) throw IllegalArgumentException(THROW_WHERE "Wrong type for Encrypted property!",
uno::Reference< XInterface >(),
2 );
// In case of new raw stream, the stream must not be encrypted on storing if ( bEnc && m_nStreamMode == PACKAGE_STREAM_RAW ) throw IllegalArgumentException(THROW_WHERE "Raw stream can not be encrypted on storing",
uno::Reference< XInterface >(),
2 );
m_bToBeEncrypted = bEnc; if ( m_bToBeEncrypted && !m_xBaseEncryptionData.is() )
m_xBaseEncryptionData = new BaseEncryptionData;
if ( aNewKey.hasElements() )
{ if ( !m_xBaseEncryptionData.is() )
m_xBaseEncryptionData = new BaseEncryptionData;
m_aEncryptionKey = std::move(aNewKey); // In case of new raw stream, the stream must not be encrypted on storing
m_bHaveOwnKey = true; if ( m_nStreamMode != PACKAGE_STREAM_RAW )
m_bToBeEncrypted = true;
} else
{
m_bHaveOwnKey = false;
m_aEncryptionKey.realloc( 0 );
}
uno::Sequence< beans::NamedValue > aKeys; if ( !( aValue >>= aKeys ) )
{ throw IllegalArgumentException(THROW_WHERE "Wrong type for StorageEncryptionKeys property!",
uno::Reference< XInterface >(),
2 );
}
if ( aKeys.hasElements() )
{ if ( !m_xBaseEncryptionData.is() )
m_xBaseEncryptionData = new BaseEncryptionData;
m_aStorageEncryptionKeys = std::move(aKeys);
// In case of new raw stream, the stream must not be encrypted on storing
m_bHaveOwnKey = true; if ( m_nStreamMode != PACKAGE_STREAM_RAW )
m_bToBeEncrypted = true;
} else
{
m_bHaveOwnKey = false;
m_aStorageEncryptionKeys.realloc( 0 );
}
if ( !(aValue >>= bCompr) ) throw IllegalArgumentException(THROW_WHERE "Wrong type for Compressed property!",
uno::Reference< XInterface >(),
2 );
// In case of new raw stream, the stream must not be encrypted on storing if ( bCompr && m_nStreamMode == PACKAGE_STREAM_RAW ) throw IllegalArgumentException(THROW_WHERE "Raw stream can not be encrypted on storing",
uno::Reference< XInterface >(),
2 );
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.