tdf#105844 package,sfx2: wholesome ODF package wrapping encryption
Redo the ODF encryption by storing an ODF package and wrapping it as a
stream "encrypted-package" in another ODF package, such that there is
only one encrypted stream - this requires only one KDF computation.
* This is only enabled in Experimental mode for now.
* Avoid storing unencrypted data in the pTempFile of SfxMedium, as it
is usually created in the same directory as the target file, which
may be on a network share or similar less trusted location.
* SfxMedium::SetEncryptionDataToStorage_Impl() should just set an
error status if it fails (how can it fail anyway)
* when loading a document, SfxDocPasswordVerifier extracts an encrypted
inner package (by calling SfxMedium::TryEncryptedInnerPackage())
* SfxMedium::GetStorage() automatically decrypts an encrypted inner
storage and sets it as the SfxMedium's xStorage
* when storing a document, SfxObjectShell::SaveTo_Impl() creates
the wrapped storages
* One challenge is to keep the macro/scripting signature working; this
can only be put in the inner storage, whereas the document signature
should continue to be on the outer storage; also it must use a Zip
storage, to see the "META-INF" directory. This needs a new
SfxMedium::GetScriptingStorageToSign_Impl() and changes in
SfxMedium::SignContents_Impl().
Change-Id: Ibfee36ce3a9cd030f2aa2ce1484b6d001cba2389
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160401
Tested-by: Jenkins
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
diff --git a/include/sfx2/docfile.hxx b/include/sfx2/docfile.hxx
index 377bab62..fb8039f 100644
--- a/include/sfx2/docfile.hxx
+++ b/include/sfx2/docfile.hxx
@@ -70,7 +70,7 @@ class SFX2_DLLPUBLIC SfxMedium final : public SvRefBase
SAL_DLLPRIVATE void CloseOutStream_Impl();
SAL_DLLPRIVATE void CloseStreams_Impl(bool bInDestruction = false);
SAL_DLLPRIVATE void SetEncryptionDataToStorage_Impl();
SAL_DLLPRIVATE bool SetEncryptionDataToStorage_Impl();
public:
@@ -218,10 +218,14 @@ public:
SAL_DLLPRIVATE OUString const & GetBackup_Impl();
SAL_DLLPRIVATE css::uno::Reference< css::embed::XStorage > const & GetZipStorageToSign_Impl( bool bReadOnly = true );
SAL_DLLPRIVATE css::uno::Reference<css::embed::XStorage> GetScriptingStorageToSign_Impl();
SAL_DLLPRIVATE void CloseZipStorage_Impl();
// the storage that will be returned by the medium on GetStorage request
SAL_DLLPRIVATE void SetStorage_Impl( const css::uno::Reference< css::embed::XStorage >& xNewStorage );
SAL_DLLPRIVATE void SetInnerStorage_Impl(const css::uno::Reference<css::embed::XStorage>& xStorage);
SAL_DLLPRIVATE css::uno::Reference<css::embed::XStorage>
TryEncryptedInnerPackage(css::uno::Reference<css::embed::XStorage> xStorage);
SAL_DLLPRIVATE void CloseAndReleaseStreams_Impl();
SAL_DLLPRIVATE void AddVersion_Impl( css::util::RevisionTag& rVersion );
diff --git a/package/source/zippackage/ZipPackage.cxx b/package/source/zippackage/ZipPackage.cxx
index 4dc2021..46e87f4 100644
--- a/package/source/zippackage/ZipPackage.cxx
+++ b/package/source/zippackage/ZipPackage.cxx
@@ -287,7 +287,9 @@ void ZipPackage::parseManifest()
const sal_Int32 nStartKeyAlg = xml::crypto::DigestID::SHA256;
pStream->SetImportedStartKeyAlgorithm( nStartKeyAlg );
if ( !m_bHasEncryptedEntries && pStream->getName() == "content.xml" )
if (!m_bHasEncryptedEntries
&& (pStream->getName() == "content.xml"
|| pStream->getName() == "encrypted-package"))
{
m_bHasEncryptedEntries = true;
m_nChecksumDigestID = nDigestAlg;
@@ -336,7 +338,9 @@ void ZipPackage::parseManifest()
pStream->SetToBeCompressed ( true );
pStream->SetToBeEncrypted ( true );
pStream->SetIsEncrypted ( true );
if ( !m_bHasEncryptedEntries && pStream->getName() == "content.xml" )
if (!m_bHasEncryptedEntries
&& (pStream->getName() == "content.xml"
|| pStream->getName() == "encrypted-package"))
{
m_bHasEncryptedEntries = true;
m_nStartKeyGenerationID = nStartKeyAlg;
diff --git a/sfx2/source/appl/appopen.cxx b/sfx2/source/appl/appopen.cxx
index 8e069c1..9693a5f 100644
--- a/sfx2/source/appl/appopen.cxx
+++ b/sfx2/source/appl/appopen.cxx
@@ -115,16 +115,19 @@ namespace {
class SfxDocPasswordVerifier : public ::comphelper::IDocPasswordVerifier
{
public:
explicit SfxDocPasswordVerifier( const Reference< embed::XStorage >& rxStorage ) :
mxStorage( rxStorage ) {}
explicit SfxDocPasswordVerifier(SfxMedium& rMedium)
: m_rMedium(rMedium)
, mxStorage(rMedium.GetStorage())
{
}
virtual ::comphelper::DocPasswordVerifierResult
verifyPassword( const OUString& rPassword, uno::Sequence< beans::NamedValue >& o_rEncryptionData ) override;
virtual ::comphelper::DocPasswordVerifierResult
verifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData ) override;
private:
SfxMedium & m_rMedium;
Reference< embed::XStorage > mxStorage;
};
@@ -147,9 +150,14 @@ private:
// and immediately closed
::comphelper::OStorageHelper::SetCommonStorageEncryptionData( mxStorage, rEncryptionData );
mxStorage->openStreamElement(
// for new ODF encryption, try to extract the encrypted inner package
// (it will become the SfxObjectShell storage)
if (!m_rMedium.TryEncryptedInnerPackage(mxStorage))
{ // ... old ODF encryption:
mxStorage->openStreamElement(
"content.xml",
embed::ElementModes::READ | embed::ElementModes::NOCREATE );
}
// no exception -> success
eResult = ::comphelper::DocPasswordVerifierResult::OK;
@@ -247,7 +255,7 @@ ErrCode CheckPasswd_Impl
{ "ForSalvage", css::uno::Any(true) } });
}
SfxDocPasswordVerifier aVerifier( xStorage );
SfxDocPasswordVerifier aVerifier(*pFile);
aEncryptionData = ::comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
aVerifier, aEncryptionData, aPassword, xInteractionHandler, pFile->GetOrigURL(), comphelper::DocPasswordRequestType::Standard );
diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx
index f4dd7b6..c801fb5 100644
--- a/sfx2/source/doc/docfile.cxx
+++ b/sfx2/source/doc/docfile.cxx
@@ -62,9 +62,11 @@
#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/io/XTruncate.hpp>
#include <com/sun/star/io/XSeekable.hpp>
#include <com/sun/star/io/TempFile.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/ucb/InsertCommandArgument.hpp>
#include <com/sun/star/ucb/NameClash.hpp>
#include <com/sun/star/util/XModifiable.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
@@ -374,6 +376,8 @@ public:
bool m_bInCheckIn:1;
bool m_bDisableFileSync = false;
bool m_bNotifyWhenEditable = false;
/// if true, xStorage is an inner package and not directly from xStream
bool m_bODFWholesomeEncryption = false;
OUString m_aName;
OUString m_aLogicName;
@@ -410,6 +414,9 @@ public:
uno::Reference<io::XStream> xStream;
uno::Reference<io::XStream> m_xLockingStream;
uno::Reference<task::XInteractionHandler> xInteraction;
uno::Reference<io::XStream> m_xODFDecryptedInnerPackageStream;
uno::Reference<embed::XStorage> m_xODFEncryptedOuterStorage;
uno::Reference<embed::XStorage> m_xODFDecryptedInnerZipStorage;
ErrCodeMsg nLastStorageError;
@@ -670,6 +677,7 @@ bool SfxMedium::IsSkipImages() const
SvStream* SfxMedium::GetInStream()
{
//assert(!pImpl->xStorage); // either SvStream or Storage
if ( pImpl->m_pInStream )
return pImpl->m_pInStream.get();
@@ -740,6 +748,7 @@ void SfxMedium::CloseInStream_Impl(bool bInDestruction)
SvStream* SfxMedium::GetOutStream()
{
assert(!pImpl->xStorage); // either SvStream or Storage
if ( !pImpl->m_pOutStream )
{
// Create a temp. file if there is none because we always
@@ -953,8 +962,11 @@ uno::Reference < embed::XStorage > SfxMedium::GetOutputStorage()
// if the medium was constructed with a Storage: use this one, not a temp. storage
// if a temporary storage already exists: use it
if ( pImpl->xStorage.is() && ( pImpl->m_aLogicName.isEmpty() || pImpl->pTempFile ) )
if (pImpl->xStorage.is()
&& (pImpl->m_bODFWholesomeEncryption || pImpl->m_aLogicName.isEmpty() || pImpl->pTempFile))
{
return pImpl->xStorage;
}
// if necessary close stream that was used for reading
if ( pImpl->m_pInStream && !pImpl->m_pInStream->IsWritable() )
@@ -971,15 +983,15 @@ uno::Reference < embed::XStorage > SfxMedium::GetOutputStorage()
}
void SfxMedium::SetEncryptionDataToStorage_Impl()
bool SfxMedium::SetEncryptionDataToStorage_Impl()
{
// in case media-descriptor contains password it should be used on opening
if ( !pImpl->xStorage.is() || !pImpl->m_pSet )
return;
return false;
uno::Sequence< beans::NamedValue > aEncryptionData;
if ( !GetEncryptionData_Impl( pImpl->m_pSet.get(), aEncryptionData ) )
return;
return false;
// replace the password with encryption data
pImpl->m_pSet->ClearItem( SID_PASSWORD );
@@ -992,9 +1004,10 @@ void SfxMedium::SetEncryptionDataToStorage_Impl()
catch( const uno::Exception& )
{
SAL_WARN( "sfx.doc", "It must be possible to set a common password for the storage" );
// TODO/LATER: set the error code in case of problem
// SetError(ERRCODE_IO_GENERAL);
SetError(ERRCODE_IO_GENERAL);
return false;
}
return true;
}
#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
@@ -1699,12 +1712,54 @@ SfxMedium::LockFileResult SfxMedium::LockOrigFileOnDemand(bool bLoading, bool bN
#endif
}
// this either returns non-null or throws exception
uno::Reference<embed::XStorage>
SfxMedium::TryEncryptedInnerPackage(uno::Reference<embed::XStorage> const xStorage)
{
uno::Reference<embed::XStorage> xRet;
if (xStorage->hasByName("encrypted-package"))
{
uno::Reference<io::XStream> const
xDecryptedInnerPackage = xStorage->openStreamElement(
"encrypted-package",
embed::ElementModes::READ | embed::ElementModes::NOCREATE);
assert(xDecryptedInnerPackage.is()); // just for testing? not if wrong pwd
// need a seekable stream => copy
Reference<uno::XComponentContext> const xContext(::comphelper::getProcessComponentContext());
uno::Reference<io::XStream> const xDecryptedInnerPackageStream(
xContext->getServiceManager()->createInstanceWithContext(
"com.sun.star.comp.MemoryStream", xContext),
UNO_QUERY_THROW);
comphelper::OStorageHelper::CopyInputToOutput(xDecryptedInnerPackage->getInputStream(), xDecryptedInnerPackageStream->getOutputStream());
xDecryptedInnerPackageStream->getOutputStream()->closeOutput();
#if 0
// debug: dump to temp file
uno::Reference<io::XTempFile> const xTempFile(io::TempFile::create(xContext), uno::UNO_SET_THROW);
xTempFile->setRemoveFile(false);
comphelper::OStorageHelper::CopyInputToOutput(xDecryptedInnerPackageStream->getInputStream(), xTempFile->getOutputStream());
xTempFile->getOutputStream()->closeOutput();
SAL_DE BUG("AAA tempfile " << xTempFile->getResourceName());
uno::Reference<io::XSeekable>(xDecryptedInnerPackageStream, uno::UNO_QUERY_THROW)->seek(0);
#endif
// create storage, if this succeeds assume password is correct
xRet = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
PACKAGE_STORAGE_FORMAT_STRING, xDecryptedInnerPackageStream,
embed::ElementModes::READWRITE, xContext, false);
assert(xRet.is());
pImpl->m_bODFWholesomeEncryption = true;
pImpl->m_xODFDecryptedInnerPackageStream = xDecryptedInnerPackageStream;
pImpl->m_xODFEncryptedOuterStorage = xStorage;
pImpl->xStorage = xRet;
}
return xRet;
}
uno::Reference < embed::XStorage > SfxMedium::GetStorage( bool bCreateTempFile )
{
if ( pImpl->xStorage.is() || pImpl->m_bTriedStorage )
return pImpl->xStorage;
assert(!pImpl->m_pOutStream /*&& !pImpl->m_pInStream*/); // either SvStream or Storage
uno::Sequence< uno::Any > aArgs( 2 );
auto pArgs = aArgs.getArray();
@@ -1797,10 +1852,26 @@ uno::Reference < embed::XStorage > SfxMedium::GetStorage( bool bCreateTempFile )
pImpl->m_bTriedStorage = true;
if (pImpl->xStorage.is())
{
pImpl->m_bODFWholesomeEncryption = false;
if (SetEncryptionDataToStorage_Impl())
{
try
{
TryEncryptedInnerPackage(pImpl->xStorage);
}
catch (Exception const&)
{
TOOLS_WARN_EXCEPTION("sfx.doc", "exception from TryEncryptedInnerPackage: ");
SetError(ERRCODE_IO_GENERAL);
}
}
}
// TODO/LATER: Get versionlist on demand
if ( pImpl->xStorage.is() )
{
SetEncryptionDataToStorage_Impl();
GetVersionList();
}
@@ -1867,6 +1938,8 @@ uno::Reference < embed::XStorage > SfxMedium::GetStorage( bool bCreateTempFile )
if ( bResetStorage )
{
pImpl->xStorage.clear();
pImpl->m_xODFDecryptedInnerPackageStream.clear();
pImpl->m_xODFEncryptedOuterStorage.clear();
if ( pImpl->m_pInStream )
pImpl->m_pInStream->Seek( 0 );
}
@@ -1875,7 +1948,39 @@ uno::Reference < embed::XStorage > SfxMedium::GetStorage( bool bCreateTempFile )
return pImpl->xStorage;
}
uno::Reference<embed::XStorage> SfxMedium::GetScriptingStorageToSign_Impl()
{
// this was set when it was initially loaded
if (pImpl->m_bODFWholesomeEncryption)
{
// (partial) scripting signature can only be in inner storage!
// Note: a "PackageFormat" storage like pImpl->xStorage doesn't work
// (even if it's not encrypted) because it hides the "META-INF" dir.
// This "ZipFormat" storage is used only read-only; a writable one is
// created manually in SignContents_Impl().
if (!pImpl->m_xODFDecryptedInnerZipStorage.is())
{
GetStorage(false);
// don't care about xStorage here because Zip is readonly
SAL_WARN_IF(!pImpl->m_xODFDecryptedInnerPackageStream.is(), "sfx.doc", "no inner package stream?");
if (pImpl->m_xODFDecryptedInnerPackageStream.is())
{
pImpl->m_xODFDecryptedInnerZipStorage =
::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream(
ZIP_STORAGE_FORMAT_STRING,
pImpl->m_xODFDecryptedInnerPackageStream->getInputStream());
}
}
return pImpl->m_xODFDecryptedInnerZipStorage;
}
else
{
return GetZipStorageToSign_Impl(true);
}
}
// note: currently nobody who calls this with "false" writes into an ODF
// storage that is returned here, that is only for OOXML
uno::Reference< embed::XStorage > const & SfxMedium::GetZipStorageToSign_Impl( bool bReadOnly )
{
if ( !GetErrorIgnoreWarning() && !pImpl->m_xZipStorage.is() )
@@ -1919,6 +2024,7 @@ void SfxMedium::CloseZipStorage_Impl()
pImpl->m_xZipStorage.clear();
}
pImpl->m_xODFDecryptedInnerZipStorage.clear();
}
void SfxMedium::CloseStorage()
@@ -1938,6 +2044,9 @@ void SfxMedium::CloseStorage()
}
pImpl->xStorage.clear();
pImpl->m_xODFDecryptedInnerPackageStream.clear();
// pImpl->m_xODFDecryptedInnerZipStorage.clear();
pImpl->m_xODFEncryptedOuterStorage.clear();
pImpl->bStorageBasedOnInStream = false;
}
@@ -3632,12 +3741,17 @@ void SfxMedium::SetLoadTargetFrame(SfxFrame* pFrame )
pImpl->wLoadTargetFrame = pFrame;
}
void SfxMedium::SetStorage_Impl( const uno::Reference < embed::XStorage >& rStor )
void SfxMedium::SetStorage_Impl(const uno::Reference<embed::XStorage>& xStorage)
{
pImpl->xStorage = rStor;
pImpl->xStorage = xStorage;
pImpl->m_bODFWholesomeEncryption = false;
}
void SfxMedium::SetInnerStorage_Impl(const uno::Reference<embed::XStorage>& xStorage)
{
pImpl->xStorage = xStorage;
pImpl->m_bODFWholesomeEncryption = true;
}
SfxItemSet& SfxMedium::GetItemSet() const
{
@@ -4173,7 +4287,18 @@ bool SfxMedium::SignContents_Impl(weld::Window* pDialogParent,
bool bODF = GetFilter()->IsOwnFormat();
try
{
xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
if (pImpl->m_bODFWholesomeEncryption && bSignScriptingContent)
{
assert(pImpl->xStorage); // GetStorage was called above
assert(pImpl->m_xODFDecryptedInnerPackageStream);
xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
ZIP_STORAGE_FORMAT_STRING, pImpl->m_xODFDecryptedInnerPackageStream);
}
else
{
xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
}
}
catch (const io::IOException&)
{
@@ -4205,7 +4330,11 @@ bool SfxMedium::SignContents_Impl(weld::Window* pDialogParent,
embed::ElementModes::READWRITE ),
uno::UNO_SET_THROW );
if ( xSigner->signScriptingContent( GetZipStorageToSign_Impl(), xStream ) )
// note: the storage passed here must be independent from the
// xWriteableZipStor because a writable storage can't have 2
// instances of sub-storage for the same directory open, but with
// independent storages it somehow works
if (xSigner->signScriptingContent(GetScriptingStorageToSign_Impl(), xStream))
{
// remove the document signature if any
OUString aDocSigName = xSigner->getDocumentContentSignatureDefaultStreamName();
@@ -4217,6 +4346,20 @@ bool SfxMedium::SignContents_Impl(weld::Window* pDialogParent,
xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
xTransact->commit();
if (pImpl->m_bODFWholesomeEncryption)
{ // manually copy the inner package to the outer one
uno::Reference<io::XSeekable>(pImpl->m_xODFDecryptedInnerPackageStream, uno::UNO_QUERY_THROW)->seek(0);
uno::Reference<io::XStream> const xEncryptedPackage =
pImpl->m_xODFEncryptedOuterStorage->openStreamElement(
"encrypted-package",
embed::ElementModes::WRITE|embed::ElementModes::TRUNCATE);
comphelper::OStorageHelper::CopyInputToOutput(pImpl->m_xODFDecryptedInnerPackageStream->getInputStream(), xEncryptedPackage->getOutputStream());
xTransact.set(pImpl->m_xODFEncryptedOuterStorage, uno::UNO_QUERY_THROW);
xTransact->commit(); // Commit() below won't do this
}
assert(!pImpl->xStorage.is() // ensure this doesn't overwrite
|| !uno::Reference<util::XModifiable>(pImpl->xStorage, uno::UNO_QUERY_THROW)->isModified());
// the temporary file has been written, commit it to the original file
Commit();
bChanges = true;
diff --git a/sfx2/source/doc/objmisc.cxx b/sfx2/source/doc/objmisc.cxx
index ede8983..5b7aa98 100644
--- a/sfx2/source/doc/objmisc.cxx
+++ b/sfx2/source/doc/objmisc.cxx
@@ -1924,7 +1924,7 @@ bool SfxObjectShell_Impl::hasTrustedScriptingSignature(
{
task::DocumentMacroConfirmationRequest aRequest;
aRequest.DocumentURL = getDocumentLocation();
aRequest.DocumentStorage = rDocShell.GetMedium()->GetZipStorageToSign_Impl();
aRequest.DocumentStorage = rDocShell.GetMedium()->GetScriptingStorageToSign_Impl();
aRequest.DocumentSignatureInformation = aInfo;
aRequest.DocumentVersion = aVersion;
aRequest.Classification = task::InteractionClassification_QUERY;
diff --git a/sfx2/source/doc/objserv.cxx b/sfx2/source/doc/objserv.cxx
index fcdc66f..5372807 100644
--- a/sfx2/source/doc/objserv.cxx
+++ b/sfx2/source/doc/objserv.cxx
@@ -1840,8 +1840,11 @@ uno::Sequence< security::DocumentSignatureInformation > SfxObjectShell::GetDocum
}
if ( bScriptingContent )
aResult = xLocSigner->verifyScriptingContentSignatures( GetMedium()->GetZipStorageToSign_Impl(),
uno::Reference< io::XInputStream >() );
{
aResult = xLocSigner->verifyScriptingContentSignatures(
GetMedium()->GetScriptingStorageToSign_Impl(),
uno::Reference<io::XInputStream>());
}
else
{
if (GetMedium()->GetStorage(false).is())
@@ -2034,7 +2037,7 @@ bool SfxObjectShell::CheckIsReadonly(bool bSignScriptingContent, weld::Window* p
xSigner->setParentWindow(pDialogParent->GetXWindow());
if (bSignScriptingContent)
xSigner->showScriptingContentSignatures(GetMedium()->GetZipStorageToSign_Impl(),
xSigner->showScriptingContentSignatures(GetMedium()->GetScriptingStorageToSign_Impl(),
uno::Reference<io::XInputStream>());
else
{
diff --git a/sfx2/source/doc/objstor.cxx b/sfx2/source/doc/objstor.cxx
index b39b5d1..9ae2c8f 100644
--- a/sfx2/source/doc/objstor.cxx
+++ b/sfx2/source/doc/objstor.cxx
@@ -1150,6 +1150,9 @@ bool SfxObjectShell::SaveTo_Impl
// tdf#41063, tdf#135244: prevent jumping to cursor at any temporary modification
auto aViewGuard(LockAllViews());
uno::Reference<uno::XComponentContext> const xContext(
::comphelper::getProcessComponentContext());
std::shared_ptr<const SfxFilter> pFilter = rMedium.GetFilter();
if ( !pFilter )
{
@@ -1173,6 +1176,12 @@ bool SfxObjectShell::SaveTo_Impl
return false;
}
SvtSaveOptions::ODFSaneDefaultVersion nVersion(SvtSaveOptions::ODFSVER_LATEST_EXTENDED);
if (bOwnTarget && !utl::ConfigManager::IsFuzzing())
{
nVersion = GetODFSaneDefaultVersion();
}
bool bNeedsDisconnectionOnFail = false;
bool bStoreToSameLocation = false;
@@ -1193,7 +1202,6 @@ bool SfxObjectShell::SaveTo_Impl
if ( bTryToPreserveScriptSignature )
{
// check that the storage format stays the same
SvtSaveOptions::ODFSaneDefaultVersion nVersion = GetODFSaneDefaultVersion();
OUString aODFVersion;
try
@@ -1220,6 +1228,33 @@ bool SfxObjectShell::SaveTo_Impl
}
}
uno::Reference<io::XStream> xODFDecryptedInnerPackageStream;
uno::Reference<embed::XStorage> xODFDecryptedInnerPackage;
uno::Sequence<beans::NamedValue> aEncryptionData;
if (GetEncryptionData_Impl(&rMedium.GetItemSet(), aEncryptionData))
{
assert(aEncryptionData.getLength() != 0);
if (bOwnTarget && nVersion == SvtSaveOptions::ODFSVER_LATEST_EXTENDED
&& officecfg::Office::Common::Misc::ExperimentalMode::get())
{
// when embedded objects are stored here, it should be called from
// this function for the root document and encryption data was cleared
assert(GetCreateMode() != SfxObjectCreateMode::EMBEDDED);
// clear now to store inner package (+ embedded objects) unencrypted
rMedium.GetItemSet().ClearItem(SID_ENCRYPTIONDATA);
rMedium.GetItemSet().ClearItem(SID_PASSWORD);
xODFDecryptedInnerPackageStream.set(
xContext->getServiceManager()->createInstanceWithContext(
"com.sun.star.comp.MemoryStream", xContext),
UNO_QUERY_THROW);
xODFDecryptedInnerPackage = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
PACKAGE_STORAGE_FORMAT_STRING, xODFDecryptedInnerPackageStream,
css::embed::ElementModes::WRITE, xContext, false);
assert(xODFDecryptedInnerPackage.is());
}
}
bool isStreamAndInputStreamCleared(false);
// use UCB for case sensitive/insensitive file name comparison
if ( !pMedium->GetName().equalsIgnoreAsciiCase("private:stream")
&& !rMedium.GetName().equalsIgnoreAsciiCase("private:stream")
@@ -1280,6 +1315,7 @@ bool SfxObjectShell::SaveTo_Impl
|| ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
{
pMedium->CloseAndRelease();
isStreamAndInputStreamCleared = true;
// TODO/LATER: for now the medium must be closed since it can already contain streams from old medium
// in future those streams should not be copied in case a valid target url is provided,
@@ -1287,7 +1323,15 @@ bool SfxObjectShell::SaveTo_Impl
// reachable.
rMedium.CloseAndRelease();
rMedium.SetHasEmbeddedObjects(GetEmbeddedObjectContainer().HasEmbeddedObjects());
rMedium.GetOutputStorage();
if (xODFDecryptedInnerPackageStream.is())
{
assert(!rMedium.GetItemSet().GetItem(SID_STREAM));
rMedium.SetInnerStorage_Impl(xODFDecryptedInnerPackage);
}
else
{
rMedium.GetOutputStorage();
}
rMedium.SetHasEmbeddedObjects(false);
}
}
@@ -1299,6 +1343,7 @@ bool SfxObjectShell::SaveTo_Impl
pMedium->CloseAndRelease();
rMedium.CloseAndRelease();
isStreamAndInputStreamCleared = true;
rMedium.CreateTempFileNoCopy();
rMedium.GetOutStream();
}
@@ -1310,7 +1355,16 @@ bool SfxObjectShell::SaveTo_Impl
pMedium->CloseAndRelease();
rMedium.CloseAndRelease();
rMedium.GetOutputStorage();
isStreamAndInputStreamCleared = true;
if (xODFDecryptedInnerPackageStream.is())
{
assert(!rMedium.GetItemSet().GetItem(SID_STREAM));
rMedium.SetInnerStorage_Impl(xODFDecryptedInnerPackage);
}
else
{
rMedium.GetOutputStorage();
}
}
else // means if ( bStorageBasedSource && !bStorageBasedTarget )
{
@@ -1325,6 +1379,7 @@ bool SfxObjectShell::SaveTo_Impl
{
pMedium->CloseAndRelease();
rMedium.CloseAndRelease();
isStreamAndInputStreamCleared = true;
rMedium.CreateTempFileNoCopy();
rMedium.GetOutStream();
}
@@ -1339,10 +1394,20 @@ bool SfxObjectShell::SaveTo_Impl
// TODO/LATER: let the medium be prepared for alien formats as well
rMedium.CloseAndRelease();
isStreamAndInputStreamCleared = true;
if ( bStorageBasedTarget )
{
rMedium.SetHasEmbeddedObjects(GetEmbeddedObjectContainer().HasEmbeddedObjects());
rMedium.GetOutputStorage();
if (xODFDecryptedInnerPackageStream.is())
{
assert(!rMedium.GetItemSet().GetItem(SID_STREAM));
// this should set only xStorage, all of the streams remain null
rMedium.SetInnerStorage_Impl(xODFDecryptedInnerPackage);
}
else
{
rMedium.GetOutputStorage();
}
rMedium.SetHasEmbeddedObjects(false);
}
}
@@ -1354,6 +1419,9 @@ bool SfxObjectShell::SaveTo_Impl
return false;
}
// these have been cleared on all paths that don't take above error return
assert(isStreamAndInputStreamCleared); (void) isStreamAndInputStreamCleared;
rMedium.LockOrigFileOnDemand( false, false );
if ( bStorageBasedTarget )
@@ -1416,19 +1484,26 @@ bool SfxObjectShell::SaveTo_Impl
}
// transfer password from the parameters to the storage
uno::Sequence< beans::NamedValue > aEncryptionData;
bool bPasswdProvided = false;
if ( GetEncryptionData_Impl( &rMedSet, aEncryptionData ) )
if (aEncryptionData.getLength() != 0)
{
bPasswdProvided = true;
try {
::comphelper::OStorageHelper::SetCommonStorageEncryptionData( xMedStorage, aEncryptionData );
if (xODFDecryptedInnerPackageStream.is())
{
bOk = true;
}
catch( uno::Exception& )
else
{
SAL_WARN( "sfx.doc", "Setting of common encryption key failed!" );
SetError(ERRCODE_IO_GENERAL);
// TODO: GetStorage() already did that?
try {
::comphelper::OStorageHelper::SetCommonStorageEncryptionData( xMedStorage, aEncryptionData );
bOk = true;
}
catch( uno::Exception& )
{
SAL_WARN( "sfx.doc", "Setting of common encryption key failed!" );
SetError(ERRCODE_IO_GENERAL);
}
}
}
else
@@ -1576,6 +1651,13 @@ bool SfxObjectShell::SaveTo_Impl
if ( bOk )
{
uno::Any mediaType;
if (xODFDecryptedInnerPackageStream.is())
{ // before the signature copy closes it
mediaType = uno::Reference<beans::XPropertySet>(xODFDecryptedInnerPackage,
uno::UNO_QUERY_THROW)->getPropertyValue("MediaType");
}
// if ODF version of oasis format changes on saving the signature should not be preserved
if ( bTryToPreserveScriptSignature && bNoPreserveForOasis )
bTryToPreserveScriptSignature = ( SotStorage::GetVersion( rMedium.GetStorage() ) == SOFFICE_FILEFORMAT_60 );
@@ -1610,14 +1692,26 @@ bool SfxObjectShell::SaveTo_Impl
rMedium.StorageCommit_Impl();
rMedium.CloseStorage();
uno::Reference< embed::XStorage > xReadOrig = pMedium->GetZipStorageToSign_Impl();
// signature must use Zip storage, not Package storage
uno::Reference<embed::XStorage> const xReadOrig(
pMedium->GetScriptingStorageToSign_Impl());
uno::Reference<embed::XStorage> xTarget;
if (xODFDecryptedInnerPackageStream.is())
{
xTarget = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
ZIP_STORAGE_FORMAT_STRING, xODFDecryptedInnerPackageStream);
}
else
{
xTarget = rMedium.GetZipStorageToSign_Impl(false);
}
if ( !xReadOrig.is() )
throw uno::RuntimeException();
uno::Reference< embed::XStorage > xMetaInf = xReadOrig->openStorageElement(
"META-INF",
embed::ElementModes::READ );
uno::Reference< embed::XStorage > xTarget = rMedium.GetZipStorageToSign_Impl( false );
if ( !xTarget.is() )
throw uno::RuntimeException();
uno::Reference< embed::XStorage > xTargetMetaInf = xTarget->openStorageElement(
@@ -1648,6 +1742,12 @@ bool SfxObjectShell::SaveTo_Impl
xTransact.set( xTarget, uno::UNO_QUERY );
if ( xTransact.is() )
xTransact->commit();
if (xODFDecryptedInnerPackageStream.is())
{ // recreate, to have it with copied sig
xODFDecryptedInnerPackage = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
PACKAGE_STORAGE_FORMAT_STRING, xODFDecryptedInnerPackageStream,
css::embed::ElementModes::WRITE, xContext, false);
}
}
else
{
@@ -1665,6 +1765,49 @@ bool SfxObjectShell::SaveTo_Impl
rMedium.CloseZipStorage_Impl();
}
if (xODFDecryptedInnerPackageStream.is())
{
rMedium.StorageCommit_Impl();
// prevent dispose as inner storage will be needed later
assert(!rMedium.WillDisposeStorageOnClose_Impl());
rMedium.CloseStorage();
// restore encryption for outer package, note: disable for debugging
rMedium.GetItemSet().Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, uno::Any(aEncryptionData)));
assert(xODFDecryptedInnerPackageStream.is());
// now create the outer storage
uno::Reference<embed::XStorage> const xOuterStorage(rMedium.GetOutputStorage());
assert(xOuterStorage.is());
// the outer storage needs the same properties as the inner one
SetupStorage(xOuterStorage, SOFFICE_FILEFORMAT_CURRENT, false);
#if 0
// does this need to happen here? - GetStorage already did it
try {
::comphelper::OStorageHelper::SetCommonStorageEncryptionData(xOuterStorage, aEncryptionData);
}
catch (uno::Exception&)
{
SAL_WARN("sfx.doc", "Setting of common encryption key failed!");
SetError(ERRCODE_IO_GENERAL);
bOk = false;
}
#endif
uno::Reference<io::XStream> const xEncryptedInnerPackage =
xOuterStorage->openStreamElement(
"encrypted-package", embed::ElementModes::WRITE);
uno::Reference<beans::XPropertySet> const xEncryptedPackageProps(
xEncryptedInnerPackage, uno::UNO_QUERY_THROW);
xEncryptedPackageProps->setPropertyValue("MediaType", mediaType);
// encryption: just copy into package stream
uno::Reference<io::XSeekable>(xODFDecryptedInnerPackageStream, uno::UNO_QUERY_THROW)->seek(0);
comphelper::OStorageHelper::CopyInputToOutput(
xODFDecryptedInnerPackageStream->getInputStream(),
xEncryptedInnerPackage->getOutputStream());
// rely on Commit() below
}
const OUString sName( rMedium.GetName( ) );
bOk = rMedium.Commit();
const OUString sNewName( rMedium.GetName( ) );
@@ -1672,6 +1815,11 @@ bool SfxObjectShell::SaveTo_Impl
if ( sName != sNewName )
GetMedium( )->SwitchDocumentToFile( sNewName );
if (xODFDecryptedInnerPackageStream.is())
{ // set the inner storage on the medium again, after Switch
rMedium.SetInnerStorage_Impl(xODFDecryptedInnerPackage);
}
if ( bOk )
{
// if the target medium is an alien format and the "old" medium was an own format and the "old" medium