diff --git a/include/sfx2/digitalsignatures.hxx b/include/sfx2/digitalsignatures.hxx index b364c9518931..856e1c5bf61a 100644 --- a/include/sfx2/digitalsignatures.hxx +++ b/include/sfx2/digitalsignatures.hxx @@ -45,6 +45,11 @@ public: const std::function& rCallback) = 0; + /// Create a scripting signature before creating a document signature. + virtual void + SetSignScriptingContent(const css::uno::Reference& xScriptingSignStream) + = 0; + protected: ~DigitalSignatures() noexcept = default; }; diff --git a/officecfg/registry/schema/org/openoffice/Office/Common.xcs b/officecfg/registry/schema/org/openoffice/Office/Common.xcs index 068df683a2c5..3c60f6f38252 100644 --- a/officecfg/registry/schema/org/openoffice/Office/Common.xcs +++ b/officecfg/registry/schema/org/openoffice/Office/Common.xcs @@ -2358,6 +2358,13 @@ true + + + Specifies whether to implicitly sign macros when adding the first signature + to an ODF document with macros. + + false + Contains the path to the users NSS certificate directory. diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx index acef518d97f9..441ae3b432f6 100644 --- a/sfx2/source/doc/docfile.cxx +++ b/sfx2/source/doc/docfile.cxx @@ -4439,12 +4439,39 @@ void SfxMedium::SignContents_Impl(weld::Window* pDialogParent, } else { + // Signing the entire document. if (xMetaInf.is()) { // ODF. uno::Reference< io::XStream > xStream; + uno::Reference< io::XStream > xScriptingStream; if (GetFilter() && GetFilter()->IsOwnFormat()) + { + bool bImplicitScriptSign = officecfg::Office::Common::Security::Scripting::ImplicitScriptSign::get(); + if (comphelper::LibreOfficeKit::isActive()) + { + bImplicitScriptSign = true; + } + + OUString aDocSigName = xSigner->getDocumentContentSignatureDefaultStreamName(); + bool bHasSignatures = xMetaInf->hasByName(aDocSigName); + + // C.f. DocumentSignatureHelper::CreateElementList() for the + // DocumentSignatureMode::Macros case. + bool bHasMacros = xWriteableZipStor->hasByName(u"Basic"_ustr) + || xWriteableZipStor->hasByName(u"Dialogs"_ustr) + || xWriteableZipStor->hasByName(u"Scripts"_ustr); + xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW); + if (bImplicitScriptSign && bHasMacros && !bHasSignatures) + { + xScriptingStream.set( + xMetaInf->openStreamElement( + xSigner->getScriptingContentSignatureDefaultStreamName(), + embed::ElementModes::READWRITE), + uno::UNO_SET_THROW); + } + } bool bSuccess = false; auto onODFSignDocumentContentFinished = [this, xMetaInf, xWriteableZipStor]() { @@ -4462,6 +4489,11 @@ void SfxMedium::SignContents_Impl(weld::Window* pDialogParent, xValidGraphic, xInvalidGraphic, aComment); else { + if (xScriptingStream.is()) + { + xModelSigner->SetSignScriptingContent(xScriptingStream); + } + // Async, all code before return has to go into the callback. xModelSigner->SignDocumentContentAsync(GetZipStorageToSign_Impl(), xStream, [onODFSignDocumentContentFinished, onSignDocumentContentFinished](bool bRet) { diff --git a/xmlsecurity/inc/UriBindingHelper.hxx b/xmlsecurity/inc/UriBindingHelper.hxx index 67c9ae69f8ba..f8a07d4d4264 100644 --- a/xmlsecurity/inc/UriBindingHelper.hxx +++ b/xmlsecurity/inc/UriBindingHelper.hxx @@ -26,7 +26,10 @@ #include namespace com::sun::star { - namespace io { class XInputStream; } + namespace io { + class XStream; + class XInputStream; + } namespace embed { class XStorage; } } @@ -36,16 +39,17 @@ class UriBindingHelper final : public cppu::WeakImplHelper< css::xml::crypto::XU { private: css::uno::Reference < css::embed::XStorage > mxStorage; + css::uno::Reference mxScriptingSignatureStream; public: UriBindingHelper(); - explicit UriBindingHelper( const css::uno::Reference < css::embed::XStorage >& rxStorage ); + explicit UriBindingHelper( const css::uno::Reference < css::embed::XStorage >& rxStorage, const css::uno::Reference& xScriptingSignatureStream ); void SAL_CALL setUriBinding( const OUString& uri, const css::uno::Reference< css::io::XInputStream >& aInputStream ) override; css::uno::Reference< css::io::XInputStream > SAL_CALL getUriBinding( const OUString& uri ) override; - static css::uno::Reference < css::io::XInputStream > OpenInputStream( const css::uno::Reference < css::embed::XStorage >& rxStore, const OUString& rURI ); + static css::uno::Reference < css::io::XInputStream > OpenInputStream( const css::uno::Reference < css::embed::XStorage >& rxStore, const OUString& rURI, const css::uno::Reference& xScriptingSignatureStream ); }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlsecurity/inc/digitalsignaturesdialog.hxx b/xmlsecurity/inc/digitalsignaturesdialog.hxx index 3f515a36706d..a56175fff5b4 100644 --- a/xmlsecurity/inc/digitalsignaturesdialog.hxx +++ b/xmlsecurity/inc/digitalsignaturesdialog.hxx @@ -39,6 +39,7 @@ class DigitalSignaturesDialog final : public weld::GenericDialogController { private: DocumentSignatureManager maSignatureManager; + std::optional moScriptSignatureManager; bool mbVerifySignatures; bool mbSignaturesChanged; @@ -108,6 +109,7 @@ public: // Set the storage which should be signed or verified void SetStorage( const css::uno::Reference < css::embed::XStorage >& rxStore ); void SetSignatureStream( const css::uno::Reference < css::io::XStream >& rxStream ); + void SetScriptingSignatureStream( const css::uno::Reference < css::io::XStream >& rxStream ); // Execute the dialog... void beforeRun(); diff --git a/xmlsecurity/inc/documentsignaturemanager.hxx b/xmlsecurity/inc/documentsignaturemanager.hxx index 9f0c5d61f9e2..a66d97e3c7dd 100644 --- a/xmlsecurity/inc/documentsignaturemanager.hxx +++ b/xmlsecurity/inc/documentsignaturemanager.hxx @@ -68,6 +68,7 @@ private: DocumentSignatureMode const meSignatureMode; css::uno::Sequence> m_manifest; css::uno::Reference mxSignatureStream; + css::uno::Reference mxScriptingSignatureStream; css::uno::Reference mxModel; rtl::Reference mxTempSignatureStream; /// Storage containing all OOXML signatures, unused for ODF. @@ -126,6 +127,12 @@ public: { mxSignatureStream = xSignatureStream; } + css::uno::Reference getSignatureStream() const { return mxSignatureStream; } + void setScriptingSignatureStream( + const css::uno::Reference& xScriptingSignatureStream) + { + mxScriptingSignatureStream = xScriptingSignatureStream; + } void setModel(const css::uno::Reference& xModel); const css::uno::Reference& getStore() const { return mxStore; } DocumentSignatureMode getSignatureMode() const { return meSignatureMode; } diff --git a/xmlsecurity/inc/xmlsignaturehelper.hxx b/xmlsecurity/inc/xmlsignaturehelper.hxx index d3c5479e4b61..cf31636cac74 100644 --- a/xmlsecurity/inc/xmlsignaturehelper.hxx +++ b/xmlsecurity/inc/xmlsignaturehelper.hxx @@ -36,6 +36,7 @@ namespace com::sun::star { namespace io { class XOutputStream; class XInputStream; + class XStream; } namespace embed { class XStorage; } } @@ -82,7 +83,7 @@ public: // Set the storage which should be used by the default UriBinding // Must be set before StartMission(). //sODFVersion indicates the ODF version - XMLSECURITY_DLLPUBLIC void SetStorage( const css::uno::Reference < css::embed::XStorage >& rxStorage, std::u16string_view sODFVersion ); + XMLSECURITY_DLLPUBLIC void SetStorage( const css::uno::Reference < css::embed::XStorage >& rxStorage, std::u16string_view sODFVersion, const css::uno::Reference& xScriptStream = css::uno::Reference() ); // Argument for the Link is a uno::Reference< xml::sax::XAttributeList >* // Return 1 to verify, 0 to skip. diff --git a/xmlsecurity/qa/unit/signing/data/macro.odt b/xmlsecurity/qa/unit/signing/data/macro.odt new file mode 100644 index 000000000000..b6e470076bef Binary files /dev/null and b/xmlsecurity/qa/unit/signing/data/macro.odt differ diff --git a/xmlsecurity/qa/unit/signing/signing.cxx b/xmlsecurity/qa/unit/signing/signing.cxx index 038bf78ce193..5646a2afa6b6 100644 --- a/xmlsecurity/qa/unit/signing/signing.cxx +++ b/xmlsecurity/qa/unit/signing/signing.cxx @@ -1149,6 +1149,68 @@ CPPUNIT_TEST_FIXTURE(SigningTest, testSignatureLineODF) CPPUNIT_ASSERT(xSignatureInfo[0].InvalidSignatureLineImage.is()); } +CPPUNIT_TEST_FIXTURE(SigningTest, testImplicitScriptSign) +{ + // Given an ODT file with macros, and two signature managers to create macro + doc signatures: + OUString aFileURL = createFileURL(u"macro.odt"); + uno::Reference xWriteableZipStor + = comphelper::OStorageHelper::GetStorageOfFormatFromURL(ZIP_STORAGE_FORMAT_STRING, aFileURL, + embed::ElementModes::READWRITE); + uno::Reference xMetaInf + = xWriteableZipStor->openStorageElement(u"META-INF"_ustr, embed::ElementModes::READWRITE); + uno::Reference xStream = xMetaInf->openStreamElement( + u"documentsignatures.xml"_ustr, embed::ElementModes::READWRITE); + uno::Reference xScriptingStream + = xMetaInf->openStreamElement(u"macrosignatures.xml"_ustr, embed::ElementModes::READWRITE); + uno::Reference xZipStor + = comphelper::OStorageHelper::GetStorageOfFormatFromURL(ZIP_STORAGE_FORMAT_STRING, aFileURL, + embed::ElementModes::READ); + DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content); + CPPUNIT_ASSERT(aManager.init()); + aManager.setStore(xZipStor); + aManager.setSignatureStream(xStream); + aManager.getSignatureHelper().SetStorage(xZipStor, u"1.2", xScriptingStream); + DocumentSignatureManager aScriptManager(m_xContext, DocumentSignatureMode::Macros); + CPPUNIT_ASSERT(aScriptManager.init()); + aScriptManager.setStore(xZipStor); + aScriptManager.getSignatureHelper().SetStorage(xZipStor, u"1.2"); + aScriptManager.setSignatureStream(xScriptingStream); + uno::Reference xCertificate + = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA); + if (!xCertificate.is()) + return; + + // When adding those signatures and writing them to the streams from the read-write storage: + OUString aDescription; + sal_Int32 nSecurityId; + bool bAdESCompliant = true; + aScriptManager.add(xCertificate, mxSecurityContext, aDescription, nSecurityId, bAdESCompliant); + aScriptManager.read(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false); + aScriptManager.write(bAdESCompliant); + aManager.setScriptingSignatureStream(xScriptingStream); + aManager.add(xCertificate, mxSecurityContext, aDescription, nSecurityId, bAdESCompliant); + aManager.read(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false); + aManager.write(bAdESCompliant); + + // Then make sure both signatures are created correctly: + std::unique_ptr pStream(utl::UcbStreamHelper::CreateStream(xScriptingStream, true)); + xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get()); + OUString aScriptDigest = getXPathContent( + pXmlDoc, "/odfds:document-signatures/dsig:Signature[1]/dsig:SignedInfo/" + "dsig:Reference[@URI='Basic/script-lc.xml']/dsig:DigestValue"_ostr); + // Without the accompanying fix in place, this test would have failed, the digest value was just a + // " " placeholder. + CPPUNIT_ASSERT_GREATER(static_cast(1), aScriptDigest.getLength()); + pStream = utl::UcbStreamHelper::CreateStream(xStream, true); + pXmlDoc = parseXmlStream(pStream.get()); + // Without the accompanying fix in place, this test would have failed, the macro signature was + // not part of the signed data of the document signature. + assertXPath(pXmlDoc, + "/odfds:document-signatures/dsig:Signature[1]/dsig:SignedInfo/" + "dsig:Reference[@URI='META-INF/macrosignatures.xml']"_ostr, + 1); +} + #if HAVE_FEATURE_GPGVERIFY /// Test a typical ODF where all streams are GPG-signed. CPPUNIT_TEST_FIXTURE(SigningTest, testODFGoodGPG) diff --git a/xmlsecurity/source/component/documentdigitalsignatures.cxx b/xmlsecurity/source/component/documentdigitalsignatures.cxx index 5cd2e6e5bb44..9c89b293bb39 100644 --- a/xmlsecurity/source/component/documentdigitalsignatures.cxx +++ b/xmlsecurity/source/component/documentdigitalsignatures.cxx @@ -70,6 +70,7 @@ class DocumentDigitalSignatures private: css::uno::Reference mxCtx; css::uno::Reference mxParentWindow; + uno::Reference mxScriptingSignStream; /// will be set by XInitialization. If not we assume true. false means an earlier version (whatever that means, /// this is a string, not a boolean). @@ -221,6 +222,10 @@ public: void SignScriptingContentAsync(const css::uno::Reference& xStorage, const css::uno::Reference& xSignStream, const std::function& rCallback) override; + + /// See sfx2::DigitalSignatures::SetSignScriptingContent(). + void SetSignScriptingContent( + const css::uno::Reference& xScriptingSignStream) override; }; } @@ -444,6 +449,7 @@ void DocumentDigitalSignatures::ImplViewSignatures( xSignaturesDialog->SetStorage(rxStorage); xSignaturesDialog->SetSignatureStream( xSignStream ); + xSignaturesDialog->SetScriptingSignatureStream( mxScriptingSignStream ); xSignaturesDialog->beforeRun(); weld::DialogController::runAsync(xSignaturesDialog, [xSignaturesDialog, rxStorage, xSignStream, rCallback] (sal_Int32 nRet) { @@ -849,6 +855,12 @@ void DocumentDigitalSignatures::SignScriptingContentAsync( ImplViewSignatures( rxStorage, xSignStream, DocumentSignatureMode::Macros, false, rCallback ); } +void DocumentDigitalSignatures::SetSignScriptingContent( + const css::uno::Reference& xScriptingSignStream) +{ + mxScriptingSignStream = xScriptingSignStream; +} + sal_Bool DocumentDigitalSignatures::signPackageWithCertificate( css::uno::Reference const& xCertificate, css::uno::Reference const& xStorage, diff --git a/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx index 9c3081cdca34..0b1173f70e7a 100644 --- a/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx +++ b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx @@ -311,7 +311,7 @@ void DigitalSignaturesDialog::SetStorage( const css::uno::Reference < css::embed || !DocumentSignatureHelper::isODFPre_1_2(m_sODFVersion); maSignatureManager.setStore(rxStore); - maSignatureManager.getSignatureHelper().SetStorage( maSignatureManager.getStore(), m_sODFVersion); + maSignatureManager.getSignatureHelper().SetStorage( maSignatureManager.getStore(), m_sODFVersion, {}); } void DigitalSignaturesDialog::SetSignatureStream( const css::uno::Reference < css::io::XStream >& rxStream ) @@ -319,6 +319,26 @@ void DigitalSignaturesDialog::SetSignatureStream( const css::uno::Reference < cs maSignatureManager.setSignatureStream(rxStream); } +void DigitalSignaturesDialog::SetScriptingSignatureStream( const css::uno::Reference < css::io::XStream >& rxStream ) +{ + if (!rxStream.is()) + { + return; + } + + uno::Reference xContext = comphelper::getProcessComponentContext(); + moScriptSignatureManager.emplace(xContext, DocumentSignatureMode::Macros); + if (!moScriptSignatureManager->init()) + { + return; + } + moScriptSignatureManager->setStore(maSignatureManager.getStore()); + // This is the storage used by UriBindingHelper::getUriBinding(). + moScriptSignatureManager->getSignatureHelper().SetStorage(maSignatureManager.getStore(), m_sODFVersion); + maSignatureManager.getSignatureHelper().SetStorage(maSignatureManager.getStore(), m_sODFVersion, rxStream); + moScriptSignatureManager->setSignatureStream(rxStream); +} + bool DigitalSignaturesDialog::canAddRemove() { //FIXME: this func needs some cleanup, such as real split between @@ -469,6 +489,23 @@ IMPL_LINK_NOARG(DigitalSignaturesDialog, AddButtonHdl, weld::Button&, void) while (aChooser->run() == RET_OK) { sal_Int32 nSecurityId; + + if (moScriptSignatureManager) + { + if (!moScriptSignatureManager->add(aChooser->GetSelectedCertificates()[0], + aChooser->GetSelectedSecurityContext(), + aChooser->GetDescription(), nSecurityId, + m_bAdESCompliant)) + { + return; + } + + moScriptSignatureManager->read(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false); + moScriptSignatureManager->write(m_bAdESCompliant); + + maSignatureManager.setScriptingSignatureStream(moScriptSignatureManager->getSignatureStream()); + } + if (!maSignatureManager.add(aChooser->GetSelectedCertificates()[0], aChooser->GetSelectedSecurityContext(), aChooser->GetDescription(), nSecurityId, m_bAdESCompliant)) return; diff --git a/xmlsecurity/source/helper/UriBindingHelper.cxx b/xmlsecurity/source/helper/UriBindingHelper.cxx index 52cf6d07a4fa..86948b682aee 100644 --- a/xmlsecurity/source/helper/UriBindingHelper.cxx +++ b/xmlsecurity/source/helper/UriBindingHelper.cxx @@ -28,6 +28,8 @@ #include #include +#include + using namespace com::sun::star; // XUriBinding @@ -36,9 +38,10 @@ UriBindingHelper::UriBindingHelper() { } -UriBindingHelper::UriBindingHelper( const css::uno::Reference < css::embed::XStorage >& rxStorage ) +UriBindingHelper::UriBindingHelper( const css::uno::Reference < css::embed::XStorage >& rxStorage, const uno::Reference& xScriptingSignatureStream ) { mxStorage = rxStorage; + mxScriptingSignatureStream = xScriptingSignatureStream; } void SAL_CALL UriBindingHelper::setUriBinding( const OUString& /*uri*/, const uno::Reference< io::XInputStream >&) @@ -50,7 +53,7 @@ uno::Reference< io::XInputStream > SAL_CALL UriBindingHelper::getUriBinding( con uno::Reference< io::XInputStream > xInputStream; if ( mxStorage.is() ) { - xInputStream = OpenInputStream( mxStorage, uri ); + xInputStream = OpenInputStream( mxStorage, uri, mxScriptingSignatureStream ); } else { @@ -60,7 +63,7 @@ uno::Reference< io::XInputStream > SAL_CALL UriBindingHelper::getUriBinding( con return xInputStream; } -uno::Reference < io::XInputStream > UriBindingHelper::OpenInputStream( const uno::Reference < embed::XStorage >& rxStore, const OUString& rURI ) +uno::Reference < io::XInputStream > UriBindingHelper::OpenInputStream( const uno::Reference < embed::XStorage >& rxStore, const OUString& rURI, const css::uno::Reference& xScriptingSignatureStream ) { OSL_ASSERT(!rURI.isEmpty()); uno::Reference < io::XInputStream > xInStream; @@ -87,7 +90,23 @@ uno::Reference < io::XInputStream > UriBindingHelper::OpenInputStream( const uno uno::Reference< io::XStream > xStream; if (!rxStore->hasByName(sName)) - SAL_WARN("xmlsecurity.helper", "expected stream, but not found: " << sName); + { + if (xScriptingSignatureStream.is() && sName == DocumentSignatureHelper::GetScriptingContentSignatureDefaultStreamName()) + { + xStream = xScriptingSignatureStream; + uno::Reference xSeekable(xScriptingSignatureStream, uno::UNO_QUERY); + if (xSeekable.is()) + { + // Cloned streams are always positioned at the start, do the same in the overlay + // case. + xSeekable->seek(0); + } + } + else + { + SAL_WARN("xmlsecurity.helper", "expected stream, but not found: " << sName); + } + } else xStream = rxStore->cloneStreamElement( sName ); if ( !xStream.is() ) @@ -103,7 +122,7 @@ uno::Reference < io::XInputStream > UriBindingHelper::OpenInputStream( const uno OUString aElement = aURI.copy( nSepPos+1 ); uno::Reference < embed::XStorage > xSubStore = rxStore->openStorageElement( aStoreName, embed::ElementModes::READ ); - xInStream = OpenInputStream( xSubStore, aElement ); + xInStream = OpenInputStream( xSubStore, aElement, xScriptingSignatureStream ); } return xInStream; } diff --git a/xmlsecurity/source/helper/documentsignaturemanager.cxx b/xmlsecurity/source/helper/documentsignaturemanager.cxx index f3931a9cdd31..d74c9a12f900 100644 --- a/xmlsecurity/source/helper/documentsignaturemanager.cxx +++ b/xmlsecurity/source/helper/documentsignaturemanager.cxx @@ -436,6 +436,15 @@ bool DocumentSignatureManager::add( std::vector aElements = DocumentSignatureHelper::CreateElementList( mxStore, meSignatureMode, DocumentSignatureAlgorithm::OOo3_2); + + if (mxScriptingSignatureStream.is()) + { + aElements.emplace_back( + u"META-INF/"_ustr + + DocumentSignatureHelper::GetScriptingContentSignatureDefaultStreamName()); + std::sort(aElements.begin(), aElements.end()); + } + DocumentSignatureHelper::AppendContentTypes(mxStore, aElements); for (OUString const& rUri : aElements) diff --git a/xmlsecurity/source/helper/xmlsignaturehelper.cxx b/xmlsecurity/source/helper/xmlsignaturehelper.cxx index 71c6ebdbce56..1510af12a83a 100644 --- a/xmlsecurity/source/helper/xmlsignaturehelper.cxx +++ b/xmlsecurity/source/helper/xmlsignaturehelper.cxx @@ -70,10 +70,11 @@ XMLSignatureHelper::~XMLSignatureHelper() void XMLSignatureHelper::SetStorage( const Reference < css::embed::XStorage >& rxStorage, - std::u16string_view sODFVersion) + std::u16string_view sODFVersion, + const css::uno::Reference& xScriptStream) { SAL_WARN_IF( mxUriBinding.is(), "xmlsecurity.helper", "SetStorage - UriBinding already set!" ); - mxUriBinding = new UriBindingHelper( rxStorage ); + mxUriBinding = new UriBindingHelper( rxStorage, xScriptStream ); SAL_WARN_IF(!rxStorage.is(), "xmlsecurity.helper", "SetStorage - empty storage!"); mbODFPre1_2 = DocumentSignatureHelper::isODFPre_1_2(sODFVersion); }