summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Stahl <michael.stahl@allotropia.de>2023-12-06 15:28:14 +0100
committerMichael Stahl <michael.stahl@allotropia.de>2023-12-07 09:28:38 +0100
commit3b347664b26d58d44f685a607a5e6d10dff89cd4 (patch)
tree882d5793493bf14b65295773d0129811b9fcb51d
parentf6536f4db61b73cf7fd4a44bb5ba61eff61f8f91 (diff)
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>
-rw-r--r--include/sfx2/docfile.hxx6
-rw-r--r--package/source/zippackage/ZipPackage.cxx8
-rw-r--r--sfx2/source/appl/appopen.cxx18
-rw-r--r--sfx2/source/doc/docfile.cxx167
-rw-r--r--sfx2/source/doc/objmisc.cxx2
-rw-r--r--sfx2/source/doc/objserv.cxx9
-rw-r--r--sfx2/source/doc/objstor.cxx174
7 files changed, 347 insertions, 37 deletions
diff --git a/include/sfx2/docfile.hxx b/include/sfx2/docfile.hxx
index 377bab62830f..fb8039f6b8f1 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 4dc2021a1904..46e87f437c7b 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 8e069c14e256..9693a5f74b04 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 f4dd7b62e10f..c801fb52bc41 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 ede8983608c6..5b7aa98df38e 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 fcdc66f139b5..5372807fba67 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 b39b5d14ee38..9ae2c8faad63 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