diff options
author | Michael Stahl <michael.stahl@allotropia.de> | 2023-12-06 15:28:14 +0100 |
---|---|---|
committer | Michael Stahl <michael.stahl@allotropia.de> | 2023-12-07 09:28:38 +0100 |
commit | 3b347664b26d58d44f685a607a5e6d10dff89cd4 (patch) | |
tree | 882d5793493bf14b65295773d0129811b9fcb51d /sfx2 | |
parent | f6536f4db61b73cf7fd4a44bb5ba61eff61f8f91 (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>
Diffstat (limited to 'sfx2')
-rw-r--r-- | sfx2/source/appl/appopen.cxx | 18 | ||||
-rw-r--r-- | sfx2/source/doc/docfile.cxx | 167 | ||||
-rw-r--r-- | sfx2/source/doc/objmisc.cxx | 2 | ||||
-rw-r--r-- | sfx2/source/doc/objserv.cxx | 9 | ||||
-rw-r--r-- | sfx2/source/doc/objstor.cxx | 174 |
5 files changed, 336 insertions, 34 deletions
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 |