From 541ca09b0571b335c8672be455248b10c6b8ef52 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Wed, 20 Oct 2021 04:22:10 +0000 Subject: [PATCH] import libreoffice-6.4.7.2-7.el8 --- ...01-backports-to-ease-CVE-backporting.patch | 259 ++ SOURCES/0002-CVE-2021-25635.patch | 56 + ...ty-replace-XSecParser-implementation.patch | 2172 +++++++++++++++++ SOURCES/0004-CVE-2021-25634.patch | 397 +++ SOURCES/0005-CVE-2021-25633.patch | 2120 ++++++++++++++++ ...eplace-OOXMLSecParser-implementation.patch | 1670 +++++++++++++ SPECS/libreoffice.spec | 13 +- 7 files changed, 6686 insertions(+), 1 deletion(-) create mode 100644 SOURCES/0001-backports-to-ease-CVE-backporting.patch create mode 100644 SOURCES/0002-CVE-2021-25635.patch create mode 100644 SOURCES/0003-xmlsecurity-replace-XSecParser-implementation.patch create mode 100644 SOURCES/0004-CVE-2021-25634.patch create mode 100644 SOURCES/0005-CVE-2021-25633.patch create mode 100644 SOURCES/0006-xmlsecurity-replace-OOXMLSecParser-implementation.patch diff --git a/SOURCES/0001-backports-to-ease-CVE-backporting.patch b/SOURCES/0001-backports-to-ease-CVE-backporting.patch new file mode 100644 index 0000000..56ac48e --- /dev/null +++ b/SOURCES/0001-backports-to-ease-CVE-backporting.patch @@ -0,0 +1,259 @@ +From c2cf13da3bbf756ef3f78251c40d45fc23c27f36 Mon Sep 17 00:00:00 2001 +From: Samuel Mehrbrodt +Date: Wed, 29 Apr 2020 07:42:24 +0200 +Subject: [PATCH 1/6] backports to ease CVE backporting + +Remove unnecessary if block + +And format code inside + +Change-Id: Ied0d98935134bf6f7bc8c929645ad5faac9affa3 +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/93116 +Tested-by: Jenkins +Reviewed-by: Samuel Mehrbrodt +(cherry picked from commit cf36fe5eb41910c26d58fb25e54ccf2e0ee01365) + +space out the namespace constant values + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/88938 +(cherry picked from commit 5352d45dd4a04f8f02cf7f6ad4169126d3b3586a) + +Change-Id: I30f54bfc1389e91b18e4fee8b83e1b297419899b +--- + include/xmloff/xmlnmspe.hxx | 16 +-- + .../component/documentdigitalsignatures.cxx | 130 +++++++++--------- + .../source/helper/xmlsignaturehelper.cxx | 1 + + 3 files changed, 75 insertions(+), 72 deletions(-) + +diff --git a/include/xmloff/xmlnmspe.hxx b/include/xmloff/xmlnmspe.hxx +index a00628b6b999..b079053c38d3 100644 +--- a/include/xmloff/xmlnmspe.hxx ++++ b/include/xmloff/xmlnmspe.hxx +@@ -69,7 +69,7 @@ XML_NAMESPACE( XML_NAMESPACE_VERSIONS_LIST, 37U ) + + // namespaces for odf extended formats + +-#define XML_NAMESPACE_EXT_BASE 38U ++#define XML_NAMESPACE_EXT_BASE 50U + #define XML_NAMESPACE_EXT( prefix, index ) \ + const sal_uInt16 prefix = (XML_NAMESPACE_EXT_BASE+index); + +@@ -82,7 +82,7 @@ XML_NAMESPACE_EXT( XML_NAMESPACE_LO_EXT, 5U ) + + // namespaces for OOo formats + +-#define XML_NAMESPACE_OOO_BASE 44U ++#define XML_NAMESPACE_OOO_BASE 60U + #define XML_NAMESPACE_OOO( prefix, index ) \ + const sal_uInt16 prefix = (XML_NAMESPACE_OOO_BASE+index); + +@@ -100,7 +100,7 @@ XML_NAMESPACE_OOO( XML_NAMESPACE_CONFIG_OOO, 10U ) + XML_NAMESPACE_OOO( XML_NAMESPACE_FORM_OOO, 11U ) + XML_NAMESPACE_OOO( XML_NAMESPACE_SCRIPT_OOO, 12U ) + +-#define XML_NAMESPACE_COMPAT_BASE 57U ++#define XML_NAMESPACE_COMPAT_BASE 80U + #define XML_NAMESPACE_COMPAT( prefix, index ) \ + const sal_uInt16 prefix = (XML_NAMESPACE_COMPAT_BASE+index); + +@@ -108,14 +108,14 @@ XML_NAMESPACE_COMPAT( XML_NAMESPACE_SVG_COMPAT, 0U ) + XML_NAMESPACE_COMPAT( XML_NAMESPACE_FO_COMPAT, 1U ) + XML_NAMESPACE_COMPAT( XML_NAMESPACE_SMIL_COMPAT, 2U ) + +-#define XML_NAMESPACE_OASIS_BASE 60U ++#define XML_NAMESPACE_OASIS_BASE 90U + #define XML_NAMESPACE_OASIS( prefix, index ) \ + const sal_uInt16 prefix = (XML_NAMESPACE_OASIS_BASE+index); + + XML_NAMESPACE_OASIS( XML_NAMESPACE_DB_OASIS, 0U ) + XML_NAMESPACE_OASIS( XML_NAMESPACE_REPORT_OASIS, 1U ) + +-#define XML_OLD_NAMESPACE_BASE 62U ++#define XML_OLD_NAMESPACE_BASE 100U + #define XML_OLD_NAMESPACE( prefix, index ) \ + const sal_uInt16 prefix = (XML_OLD_NAMESPACE_BASE+index); + +@@ -134,9 +134,9 @@ XML_OLD_NAMESPACE( XML_OLD_NAMESPACE_CHART, 10U ) + XML_OLD_NAMESPACE( XML_OLD_NAMESPACE_SMIL, 11U ) + + // experimental namespaces +-XML_NAMESPACE( XML_NAMESPACE_FIELD, 100U ) +-XML_NAMESPACE( XML_NAMESPACE_CSS3TEXT, 103U ) // CSS Text Level 3 +-XML_NAMESPACE( XML_NAMESPACE_FORMX, 101U ) // form interop extensions ++XML_NAMESPACE( XML_NAMESPACE_FIELD, 120U ) ++XML_NAMESPACE( XML_NAMESPACE_CSS3TEXT, 123U ) // CSS Text Level 3 ++XML_NAMESPACE( XML_NAMESPACE_FORMX, 121U ) // form interop extensions + + + #endif // INCLUDED_XMLOFF_XMLNMSPE_HXX +diff --git a/xmlsecurity/source/component/documentdigitalsignatures.cxx b/xmlsecurity/source/component/documentdigitalsignatures.cxx +index dcfaad0af773..52cb938a8e0a 100644 +--- a/xmlsecurity/source/component/documentdigitalsignatures.cxx ++++ b/xmlsecurity/source/component/documentdigitalsignatures.cxx +@@ -500,85 +500,87 @@ DocumentDigitalSignatures::ImplVerifySignatures( + Sequence< css::security::DocumentSignatureInformation > aInfos(nInfos); + css::security::DocumentSignatureInformation* arInfos = aInfos.getArray(); + +- if ( nInfos ) ++ for (int n = 0; n < nInfos; ++n) + { +- for( int n = 0; n < nInfos; ++n ) +- { +- DocumentSignatureAlgorithm mode = DocumentSignatureHelper::getDocumentAlgorithm( +- m_sODFVersion, aSignInfos[n]); +- const std::vector< OUString > aElementsToBeVerified = +- DocumentSignatureHelper::CreateElementList( +- rxStorage, eMode, mode); ++ DocumentSignatureAlgorithm mode ++ = DocumentSignatureHelper::getDocumentAlgorithm(m_sODFVersion, aSignInfos[n]); ++ const std::vector aElementsToBeVerified ++ = DocumentSignatureHelper::CreateElementList(rxStorage, eMode, mode); + +- const SignatureInformation& rInfo = aSignInfos[n]; +- css::security::DocumentSignatureInformation& rSigInfo = arInfos[n]; ++ const SignatureInformation& rInfo = aSignInfos[n]; ++ css::security::DocumentSignatureInformation& rSigInfo = arInfos[n]; + +- if (rInfo.ouGpgCertificate.isEmpty()) // X.509 ++ if (rInfo.ouGpgCertificate.isEmpty()) // X.509 ++ { ++ if (!rInfo.ouX509Certificate.isEmpty()) ++ rSigInfo.Signer = xSecEnv->createCertificateFromAscii(rInfo.ouX509Certificate); ++ if (!rSigInfo.Signer.is()) ++ rSigInfo.Signer = xSecEnv->getCertificate( ++ rInfo.ouX509IssuerName, ++ xmlsecurity::numericStringToBigInteger(rInfo.ouX509SerialNumber)); ++ ++ // On Windows checking the certificate path is buggy. It does name matching (issuer, subject name) ++ // to find the parent certificate. It does not take into account that there can be several certificates ++ // with the same subject name. ++ try + { +- if (!rInfo.ouX509Certificate.isEmpty()) +- rSigInfo.Signer = xSecEnv->createCertificateFromAscii( rInfo.ouX509Certificate ) ; +- if (!rSigInfo.Signer.is()) +- rSigInfo.Signer = xSecEnv->getCertificate( rInfo.ouX509IssuerName, +- xmlsecurity::numericStringToBigInteger( rInfo.ouX509SerialNumber ) ); +- +- // On Windows checking the certificate path is buggy. It does name matching (issuer, subject name) +- // to find the parent certificate. It does not take into account that there can be several certificates +- // with the same subject name. +- +- try { +- rSigInfo.CertificateStatus = xSecEnv->verifyCertificate(rSigInfo.Signer, +- Sequence >()); +- } catch (SecurityException& ) { +- OSL_FAIL("Verification of certificate failed"); +- rSigInfo.CertificateStatus = css::security::CertificateValidity::INVALID; +- } ++ rSigInfo.CertificateStatus = xSecEnv->verifyCertificate( ++ rSigInfo.Signer, Sequence>()); + } +- else if (xGpgSecEnv.is()) // GPG ++ catch (SecurityException&) + { +- // TODO not ideal to retrieve cert by keyID, might +- // collide, or PGPKeyID format might change - can't we +- // keep the xCert itself in rInfo? +- rSigInfo.Signer = xGpgSecEnv->getCertificate( rInfo.ouGpgKeyID, xmlsecurity::numericStringToBigInteger("") ); +- rSigInfo.CertificateStatus = xGpgSecEnv->verifyCertificate(rSigInfo.Signer, +- Sequence >()); ++ OSL_FAIL("Verification of certificate failed"); ++ rSigInfo.CertificateStatus = css::security::CertificateValidity::INVALID; + } ++ } ++ else if (xGpgSecEnv.is()) // GPG ++ { ++ // TODO not ideal to retrieve cert by keyID, might ++ // collide, or PGPKeyID format might change - can't we ++ // keep the xCert itself in rInfo? ++ rSigInfo.Signer = xGpgSecEnv->getCertificate( ++ rInfo.ouGpgKeyID, xmlsecurity::numericStringToBigInteger("")); ++ rSigInfo.CertificateStatus = xGpgSecEnv->verifyCertificate( ++ rSigInfo.Signer, Sequence>()); ++ } + +- // Time support again (#i38744#) +- Date aDate( rInfo.stDateTime.Day, rInfo.stDateTime.Month, rInfo.stDateTime.Year ); +- tools::Time aTime( rInfo.stDateTime.Hours, rInfo.stDateTime.Minutes, +- rInfo.stDateTime.Seconds, rInfo.stDateTime.NanoSeconds ); +- rSigInfo.SignatureDate = aDate.GetDate(); +- rSigInfo.SignatureTime = aTime.GetTime() / tools::Time::nanoPerCenti; ++ // Time support again (#i38744#) ++ Date aDate(rInfo.stDateTime.Day, rInfo.stDateTime.Month, rInfo.stDateTime.Year); ++ tools::Time aTime(rInfo.stDateTime.Hours, rInfo.stDateTime.Minutes, ++ rInfo.stDateTime.Seconds, rInfo.stDateTime.NanoSeconds); ++ rSigInfo.SignatureDate = aDate.GetDate(); ++ rSigInfo.SignatureTime = aTime.GetTime() / tools::Time::nanoPerCenti; + +- rSigInfo.SignatureIsValid = ( rInfo.nStatus == css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED ); ++ rSigInfo.SignatureIsValid ++ = (rInfo.nStatus == css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED); + +- // Signature line info (ID + Images) +- if (!rInfo.ouSignatureLineId.isEmpty()) +- rSigInfo.SignatureLineId = rInfo.ouSignatureLineId; ++ // Signature line info (ID + Images) ++ if (!rInfo.ouSignatureLineId.isEmpty()) ++ rSigInfo.SignatureLineId = rInfo.ouSignatureLineId; + +- if (rInfo.aValidSignatureImage.is()) +- rSigInfo.ValidSignatureLineImage = rInfo.aValidSignatureImage; ++ if (rInfo.aValidSignatureImage.is()) ++ rSigInfo.ValidSignatureLineImage = rInfo.aValidSignatureImage; + +- if (rInfo.aInvalidSignatureImage.is()) +- rSigInfo.InvalidSignatureLineImage = rInfo.aInvalidSignatureImage; +- +- // OOXML intentionally doesn't sign metadata. +- if ( rSigInfo.SignatureIsValid && aStreamHelper.nStorageFormat != embed::StorageFormats::OFOPXML) +- { +- rSigInfo.SignatureIsValid = +- DocumentSignatureHelper::checkIfAllFilesAreSigned( +- aElementsToBeVerified, rInfo, mode); +- } +- if (eMode == DocumentSignatureMode::Content) +- { +- if (aStreamHelper.nStorageFormat == embed::StorageFormats::OFOPXML) +- rSigInfo.PartialDocumentSignature = true; +- else +- rSigInfo.PartialDocumentSignature = !DocumentSignatureHelper::isOOo3_2_Signature(aSignInfos[n]); +- } ++ if (rInfo.aInvalidSignatureImage.is()) ++ rSigInfo.InvalidSignatureLineImage = rInfo.aInvalidSignatureImage; + ++ // OOXML intentionally doesn't sign metadata. ++ if (rSigInfo.SignatureIsValid ++ && aStreamHelper.nStorageFormat != embed::StorageFormats::OFOPXML) ++ { ++ rSigInfo.SignatureIsValid = DocumentSignatureHelper::checkIfAllFilesAreSigned( ++ aElementsToBeVerified, rInfo, mode); ++ } ++ if (eMode == DocumentSignatureMode::Content) ++ { ++ if (aStreamHelper.nStorageFormat == embed::StorageFormats::OFOPXML) ++ rSigInfo.PartialDocumentSignature = true; ++ else ++ rSigInfo.PartialDocumentSignature ++ = !DocumentSignatureHelper::isOOo3_2_Signature(aSignInfos[n]); + } + } ++ + return aInfos; + + } +diff --git a/xmlsecurity/source/helper/xmlsignaturehelper.cxx b/xmlsecurity/source/helper/xmlsignaturehelper.cxx +index 6ec834053a17..22c056e70da1 100644 +--- a/xmlsecurity/source/helper/xmlsignaturehelper.cxx ++++ b/xmlsecurity/source/helper/xmlsignaturehelper.cxx +@@ -402,6 +402,7 @@ bool XMLSignatureHelper::ReadAndVerifySignatureStorageStream(const css::uno::Ref + catch(const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("xmlsecurity.helper"); ++ mbError = true; + } + + mpXSecController->releaseSignatureReader(); +-- +2.32.0 + diff --git a/SOURCES/0002-CVE-2021-25635.patch b/SOURCES/0002-CVE-2021-25635.patch new file mode 100644 index 0000000..cee888d --- /dev/null +++ b/SOURCES/0002-CVE-2021-25635.patch @@ -0,0 +1,56 @@ +From a9102a384893fd084011e8451867071452031ece Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= +Date: Mon, 8 Feb 2021 17:05:28 +0000 +Subject: [PATCH 2/6] CVE-2021-25635 + +default to CertificateValidity::INVALID + +so if CertGetCertificateChain fails we don't want validity to be +css::security::CertificateValidity::VALID which is what the old default +of 0 equates to + +notably + +commit 1e0bc66d16aee28ce8bd9582ea32178c63841902 +Date: Thu Nov 5 16:55:26 2009 +0100 + + jl137: #103420# better logging + +turned the nss equivalent of SecurityEnvironment_NssImpl::verifyCertificate +from 0 to CertificateValidity::INVALID like this change does + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/110561 +Tested-by: Jenkins +Reviewed-by: Miklos Vajna +(cherry picked from commit edeb164c1d8ab64116afee4e2140403a362a1358) + +Change-Id: I5350dbc22d1b9b378da2976d3b0abd728f1f4c27 +--- + .../source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx b/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx +index d9b657891b96..4031df289f44 100644 +--- a/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx ++++ b/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx +@@ -755,7 +755,7 @@ sal_Int32 SecurityEnvironment_MSCryptImpl::verifyCertificate( + const uno::Reference< css::security::XCertificate >& aCert, + const uno::Sequence< uno::Reference< css::security::XCertificate > >& seqCerts) + { +- sal_Int32 validity = 0; ++ sal_Int32 validity = css::security::CertificateValidity::INVALID; + PCCERT_CHAIN_CONTEXT pChainContext = nullptr; + PCCERT_CONTEXT pCertContext = nullptr; + +@@ -899,7 +899,7 @@ sal_Int32 SecurityEnvironment_MSCryptImpl::verifyCertificate( + } + else + { +- SAL_INFO("xmlsecurity.xmlsec", "CertGetCertificateChaine failed."); ++ SAL_INFO("xmlsecurity.xmlsec", "CertGetCertificateChain failed."); + } + } + +-- +2.32.0 + diff --git a/SOURCES/0003-xmlsecurity-replace-XSecParser-implementation.patch b/SOURCES/0003-xmlsecurity-replace-XSecParser-implementation.patch new file mode 100644 index 0000000..08266f4 --- /dev/null +++ b/SOURCES/0003-xmlsecurity-replace-XSecParser-implementation.patch @@ -0,0 +1,2172 @@ +From 4201f1be45b4567ba64655439dcc39ac403c28e0 Mon Sep 17 00:00:00 2001 +From: Michael Stahl +Date: Fri, 12 Feb 2021 16:42:51 +0100 +Subject: [PATCH 3/6] xmlsecurity: replace XSecParser implementation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Implement Namespaces in XML and follow xmldsig-core and XAdES schemas. + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/110833 +Tested-by: Jenkins +Reviewed-by: Michael Stahl +(cherry picked from commit 12b15be8f4f930a04d8056b9219ac969b42a9784) + +xmlsecurity: move XSecParser state into contexts + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111158 +Tested-by: Jenkins +Reviewed-by: Michael Stahl +(cherry picked from commit 59df9e70ce1a7ec797b836bda7f9642912febc53) + +xmlsecurity: move XSecParser Reference state into contexts + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111159 +Tested-by: Jenkins +Reviewed-by: Michael Stahl +(cherry picked from commit cfeb89a758b5f0ec406f0d72444e52ed2f47b85e) + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111907 +Tested-by: Jenkins +Reviewed-by: Caolán McNamara +(cherry picked from commit ad5930e87e788780a255523f106deb1dde5d7b37) + +Change-Id: I03537b51bb757ecbfa63a826b38de543c70ba032 +--- + include/xmloff/xmlimp.hxx | 6 +- + include/xmloff/xmlnmspe.hxx | 7 + + include/xmloff/xmltoken.hxx | 13 + + xmloff/source/core/xmlimp.cxx | 26 +- + xmloff/source/core/xmltoken.cxx | 13 + + xmloff/source/token/tokens.txt | 10 + + xmlsecurity/source/helper/xsecparser.cxx | 1629 +++++++++++++++++----- + xmlsecurity/source/helper/xsecparser.hxx | 99 +- + 8 files changed, 1425 insertions(+), 378 deletions(-) + +diff --git a/include/xmloff/xmlimp.hxx b/include/xmloff/xmlimp.hxx +index 6d0dda96596a..8d557789aa4c 100644 +--- a/include/xmloff/xmlimp.hxx ++++ b/include/xmloff/xmlimp.hxx +@@ -229,8 +229,12 @@ class XMLOFF_DLLPUBLIC SvXMLImport : public cppu::WeakImplHelper< + + static void initializeNamespaceMaps(); + void registerNamespaces(); +- std::unique_ptr processNSAttributes( ++public: ++ static std::unique_ptr processNSAttributes( ++ std::unique_ptr & rpNamespaceMap, ++ SvXMLImport *const pImport, + const css::uno::Reference< css::xml::sax::XAttributeList >& xAttrList); ++private: + void Characters(const OUString& aChars); + + css::uno::Reference< css::task::XStatusIndicator > mxStatusIndicator; +diff --git a/include/xmloff/xmlnmspe.hxx b/include/xmloff/xmlnmspe.hxx +index b079053c38d3..302a134f92fe 100644 +--- a/include/xmloff/xmlnmspe.hxx ++++ b/include/xmloff/xmlnmspe.hxx +@@ -66,6 +66,13 @@ XML_NAMESPACE( XML_NAMESPACE_OF, 34U ) // OpenFormula aka ODFF + XML_NAMESPACE( XML_NAMESPACE_XHTML, 35U ) + XML_NAMESPACE( XML_NAMESPACE_GRDDL, 36U ) + XML_NAMESPACE( XML_NAMESPACE_VERSIONS_LIST, 37U ) ++// OOo extension digital signatures, used in ODF 1.1 ++XML_NAMESPACE( XML_NAMESPACE_DSIG_OOO, 38U ) ++// ODF 1.2 digital signature namespaces ++XML_NAMESPACE( XML_NAMESPACE_DSIG, 39U ) ++XML_NAMESPACE( XML_NAMESPACE_DS, 40U ) ++XML_NAMESPACE( XML_NAMESPACE_XADES132, 41U ) ++XML_NAMESPACE( XML_NAMESPACE_XADES141, 42U ) + + // namespaces for odf extended formats + +diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx +index b5105e5da0f3..a0e52903060a 100644 +--- a/include/xmloff/xmltoken.hxx ++++ b/include/xmloff/xmltoken.hxx +@@ -133,6 +133,19 @@ namespace xmloff { namespace token { + XML_NP_GRDDL, + XML_N_GRDDL, + ++ // OOo extension digital signatures, used in ODF 1.1 ++ XML_NP_DSIG_OOO, ++ XML_N_DSIG_OOO, ++ // ODF 1.2 digital signatures ++ XML_NP_DSIG, ++ XML_N_DSIG, ++ XML_NP_DS, ++ XML_N_DS, ++ XML_NP_XADES132, ++ XML_N_XADES132, ++ XML_NP_XADES141, ++ XML_N_XADES141, ++ + // ODF Enhanced namespaces + XML_NP_OFFICE_EXT, + XML_N_OFFICE_EXT, +diff --git a/xmloff/source/core/xmlimp.cxx b/xmloff/source/core/xmlimp.cxx +index ef63550ff2be..bfc9d3fe819a 100644 +--- a/xmloff/source/core/xmlimp.cxx ++++ b/xmloff/source/core/xmlimp.cxx +@@ -653,6 +653,8 @@ void SAL_CALL SvXMLImport::endDocument() + } + + std::unique_ptr SvXMLImport::processNSAttributes( ++ std::unique_ptr & rpNamespaceMap, ++ SvXMLImport *const pImport, // TODO??? + const uno::Reference< xml::sax::XAttributeList >& xAttrList) + { + std::unique_ptr pRewindMap; +@@ -660,12 +662,13 @@ std::unique_ptr SvXMLImport::processNSAttributes( + for( sal_Int16 i=0; i < nAttrCount; i++ ) + { + const OUString& rAttrName = xAttrList->getNameByIndex( i ); +- if ( rAttrName == "office:version" ) ++ if (pImport && rAttrName == "office:version") + { +- mpImpl->aODFVersion = xAttrList->getValueByIndex( i ); ++ pImport->mpImpl->aODFVersion = xAttrList->getValueByIndex( i ); + + // the ODF version in content.xml and manifest.xml must be the same starting from ODF1.2 +- if ( mpImpl->mStreamName == "content.xml" && !IsODFVersionConsistent( mpImpl->aODFVersion ) ) ++ if (pImport->mpImpl->mStreamName == "content.xml" ++ && !pImport->IsODFVersionConsistent(pImport->mpImpl->aODFVersion)) + { + throw xml::sax::SAXException("Inconsistent ODF versions in content.xml and manifest.xml!", + uno::Reference< uno::XInterface >(), +@@ -679,8 +682,8 @@ std::unique_ptr SvXMLImport::processNSAttributes( + { + if( !pRewindMap ) + { +- pRewindMap = std::move(mpNamespaceMap); +- mpNamespaceMap.reset(new SvXMLNamespaceMap(*pRewindMap)); ++ pRewindMap = std::move(rpNamespaceMap); ++ rpNamespaceMap.reset(new SvXMLNamespaceMap(*pRewindMap)); + } + const OUString& rAttrValue = xAttrList->getValueByIndex( i ); + +@@ -688,18 +691,18 @@ std::unique_ptr SvXMLImport::processNSAttributes( + ? OUString() + : rAttrName.copy( 6 ) ); + // Add namespace, but only if it is known. +- sal_uInt16 nKey = mpNamespaceMap->AddIfKnown( aPrefix, rAttrValue ); ++ sal_uInt16 nKey = rpNamespaceMap->AddIfKnown( aPrefix, rAttrValue ); + // If namespace is unknown, try to match a name with similar + // TC Id and version + if( XML_NAMESPACE_UNKNOWN == nKey ) + { + OUString aTestName( rAttrValue ); + if( SvXMLNamespaceMap::NormalizeURI( aTestName ) ) +- nKey = mpNamespaceMap->AddIfKnown( aPrefix, aTestName ); ++ nKey = rpNamespaceMap->AddIfKnown( aPrefix, aTestName ); + } + // If that namespace is not known, too, add it as unknown + if( XML_NAMESPACE_UNKNOWN == nKey ) +- mpNamespaceMap->Add( aPrefix, rAttrValue ); ++ rpNamespaceMap->Add( aPrefix, rAttrValue ); + + } + } +@@ -712,7 +715,8 @@ void SAL_CALL SvXMLImport::startElement( const OUString& rName, + // SAL_INFO("svg", "startElement " << rName); + // Process namespace attributes. This must happen before creating the + // context, because namespace declaration apply to the element name itself. +- std::unique_ptr pRewindMap(processNSAttributes(xAttrList)); ++ std::unique_ptr pRewindMap( ++ processNSAttributes(mpNamespaceMap, this, xAttrList)); + + // Get element's namespace and local name. + OUString aLocalName; +@@ -885,7 +889,7 @@ void SAL_CALL SvXMLImport::startFastElement (sal_Int32 Element, + + maNamespaceHandler->addNSDeclAttributes( maNamespaceAttrList ); + std::unique_ptr pRewindMap( +- processNSAttributes( maNamespaceAttrList.get() )); ++ processNSAttributes(mpNamespaceMap, this, maNamespaceAttrList.get())); + assert( dynamic_cast( xContext.get() ) != nullptr ); + SvXMLImportContext *pContext = static_cast( xContext.get() ); + if (pRewindMap) +@@ -2231,7 +2235,7 @@ void SAL_CALL SvXMLLegacyToFastDocHandler::endDocument() + void SAL_CALL SvXMLLegacyToFastDocHandler::startElement( const OUString& rName, + const uno::Reference< xml::sax::XAttributeList >& xAttrList ) + { +- mrImport->processNSAttributes(xAttrList); ++ SvXMLImport::processNSAttributes(mrImport->mpNamespaceMap, mrImport.get(), xAttrList); + OUString aLocalName; + sal_uInt16 nPrefix = mrImport->mpNamespaceMap->GetKeyByAttrName( rName, &aLocalName ); + Sequence< sal_Int8 > aLocalNameSeq( reinterpret_cast( +diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx +index fb760c6307c5..13f1415b8cc4 100644 +--- a/xmloff/source/core/xmltoken.cxx ++++ b/xmloff/source/core/xmltoken.cxx +@@ -137,6 +137,19 @@ namespace xmloff { namespace token { + TOKEN( "grddl", XML_NP_GRDDL ), + TOKEN( "http://www.w3.org/2003/g/data-view#", XML_N_GRDDL ), + ++ // OOo extension digital signatures, used in ODF 1.1 ++ TOKEN( "dsigooo", XML_NP_DSIG_OOO ), ++ TOKEN( "http://openoffice.org/2004/documentsignatures", XML_N_DSIG_OOO ), ++ // ODF 1.2 digital signature namespaces ++ TOKEN( "dsig", XML_NP_DSIG ), ++ TOKEN( "urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0", XML_N_DSIG ), ++ TOKEN( "ds", XML_NP_DS ), ++ TOKEN( "http://www.w3.org/2000/09/xmldsig#", XML_N_DS ), ++ TOKEN( "xades132", XML_NP_XADES132 ), ++ TOKEN( "http://uri.etsi.org/01903/v1.3.2#", XML_N_XADES132 ), ++ TOKEN( "xades141", XML_NP_XADES141 ), ++ TOKEN( "http://uri.etsi.org/01903/v1.4.1#", XML_N_XADES141 ), ++ + // ODF Enhanced namespaces + TOKEN( "officeooo", XML_NP_OFFICE_EXT ), + TOKEN( "http://openoffice.org/2009/office", XML_N_OFFICE_EXT ), +diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt +index 024877e8cf45..b2b95e956bd1 100644 +--- a/xmloff/source/token/tokens.txt ++++ b/xmloff/source/token/tokens.txt +@@ -72,6 +72,16 @@ xhtml + N_XHTML_DUMMY + grddl + N_GRDDL_DUMMY ++dsigooo ++N_DSIG_OOO_DUMMY ++dsig ++N_DSIG_DUMMY ++ds ++N_DS_DUMMY ++xades132 ++N_XADES132_DUMMY ++xades141 ++N_XADES141_DUMMY + officeooo + N_OFFICE_EXT_DUMMY + formx +diff --git a/xmlsecurity/source/helper/xsecparser.cxx b/xmlsecurity/source/helper/xsecparser.cxx +index 82f347bff976..5c92e5efa104 100644 +--- a/xmlsecurity/source/helper/xsecparser.cxx ++++ b/xmlsecurity/source/helper/xsecparser.cxx +@@ -21,6 +21,10 @@ + #include "xsecparser.hxx" + #include + #include ++ ++#include ++#include ++ + #include + #include + #include +@@ -29,471 +33,1460 @@ namespace cssu = com::sun::star::uno; + namespace cssxc = com::sun::star::xml::crypto; + namespace cssxs = com::sun::star::xml::sax; + +-XSecParser::XSecParser(XMLSignatureHelper& rXMLSignatureHelper, +- XSecController* pXSecController) +- : m_bInX509IssuerName(false) +- , m_bInX509SerialNumber(false) +- , m_bInX509Certificate(false) +- , m_bInGpgCertificate(false) +- , m_bInGpgKeyID(false) +- , m_bInGpgOwner(false) +- , m_bInCertDigest(false) +- , m_bInEncapsulatedX509Certificate(false) +- , m_bInSigningTime(false) +- , m_bInDigestValue(false) +- , m_bInSignatureValue(false) +- , m_bInDate(false) +- , m_bInDescription(false) +- , m_bInSignatureLineId(false) +- , m_bInSignatureLineValidImage(false) +- , m_bInSignatureLineInvalidImage(false) +- , m_pXSecController(pXSecController) +- , m_bReferenceUnresolved(false) +- , m_nReferenceDigestID(cssxc::DigestID::SHA1) +- , m_rXMLSignatureHelper(rXMLSignatureHelper) ++class XSecParser::Context + { +-} ++ protected: ++ friend class XSecParser; ++ XSecParser & m_rParser; ++ private: ++ std::unique_ptr m_pOldNamespaceMap; ++ ++ public: ++ Context(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : m_rParser(rParser) ++ , m_pOldNamespaceMap(std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual ~Context() = default; + +-OUString XSecParser::getIdAttr(const cssu::Reference< cssxs::XAttributeList >& xAttribs ) ++ virtual void StartElement( ++ css::uno::Reference const& /*xAttrs*/) ++ { ++ } ++ ++ virtual void EndElement() ++ { ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/); ++ ++ virtual void Characters(OUString const& /*rChars*/) ++ { ++ } ++}; ++ ++// it's possible that an unsupported element has an Id attribute and a ++// ds:Reference digesting it - probably this means XSecController needs to know ++// about it. (For known elements, the Id attribute is only processed according ++// to the schema.) ++class XSecParser::UnknownContext ++ : public XSecParser::Context + { +- OUString ouIdAttr = xAttribs->getValueByName("id"); ++ public: ++ UnknownContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } + +- if (ouIdAttr.isEmpty()) +- { +- ouIdAttr = xAttribs->getValueByName("Id"); +- } ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ m_rParser.HandleIdAttr(xAttrs); ++ } ++}; + +- return ouIdAttr; ++auto XSecParser::Context::CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/) ++-> std::unique_ptr ++{ ++ // default: create new base context ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); + } + +-/* +- * XDocumentHandler +- */ +-void SAL_CALL XSecParser::startDocument( ) ++class XSecParser::LoPGPOwnerContext ++ : public XSecParser::Context + { +- m_bInX509IssuerName = false; +- m_bInX509SerialNumber = false; +- m_bInX509Certificate = false; +- m_bInGpgCertificate = false; +- m_bInGpgKeyID = false; +- m_bInGpgOwner = false; +- m_bInSignatureValue = false; +- m_bInDigestValue = false; +- m_bInDate = false; +- m_bInDescription = false; ++ private: ++ OUString m_Value; + +- if (m_xNextHandler.is()) +- { +- m_xNextHandler->startDocument(); +- } +-} ++ public: ++ LoPGPOwnerContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } + +-void SAL_CALL XSecParser::endDocument( ) ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->setGpgOwner(m_Value); ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_Value += rChars; ++ } ++}; ++ ++class XSecParser::DsPGPKeyPacketContext ++ : public XSecParser::Context + { +- if (m_xNextHandler.is()) +- { +- m_xNextHandler->endDocument(); +- } +-} ++ private: ++ OUString m_Value; ++ ++ public: ++ DsPGPKeyPacketContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->setGpgCertificate(m_Value); ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_Value += rChars; ++ } ++}; ++ ++class XSecParser::DsPGPKeyIDContext ++ : public XSecParser::Context ++{ ++ private: ++ OUString m_Value; ++ ++ public: ++ DsPGPKeyIDContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->setGpgKeyID(m_Value); ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_Value += rChars; ++ } ++}; ++ ++class XSecParser::DsPGPDataContext ++ : public XSecParser::Context ++{ ++ public: ++ DsPGPDataContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& /*xAttrs*/) override ++ { ++ m_rParser.m_pXSecController->switchGpgSignature(); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "PGPKeyID") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "PGPKeyPacket") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "PGPOwner") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class XSecParser::DsX509CertificateContext ++ : public XSecParser::Context ++{ ++ private: ++ OUString m_Value; ++ ++ public: ++ DsX509CertificateContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->setX509Certificate(m_Value); ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_Value += rChars; ++ } ++}; ++ ++class XSecParser::DsX509SerialNumberContext ++ : public XSecParser::Context ++{ ++ private: ++ OUString m_Value; ++ ++ public: ++ DsX509SerialNumberContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->setX509SerialNumber(m_Value); ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_Value += rChars; ++ } ++}; ++ ++class XSecParser::DsX509IssuerNameContext ++ : public XSecParser::Context ++{ ++ private: ++ OUString m_Value; ++ ++ public: ++ DsX509IssuerNameContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->setX509IssuerName(m_Value); ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_Value += rChars; ++ } ++}; ++ ++class XSecParser::DsX509IssuerSerialContext ++ : public XSecParser::Context ++{ ++ public: ++ DsX509IssuerSerialContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerName") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "X509SerialNumber") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class XSecParser::DsX509DataContext ++ : public XSecParser::Context ++{ ++ public: ++ DsX509DataContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerSerial") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Certificate") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class XSecParser::DsKeyInfoContext ++ : public XSecParser::Context ++{ ++ public: ++ DsKeyInfoContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ m_rParser.HandleIdAttr(xAttrs); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Data") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "PGPData") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ // missing: ds:KeyName, ds:KeyValue, ds:RetrievalMethod, ds:SPKIData, ds:MgmtData ++ // (old code would read ds:Transform inside ds:RetrievalMethod but ++ // presumably that was a bug) ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++ ++}; ++ ++class XSecParser::DsSignatureValueContext ++ : public XSecParser::Context ++{ ++ private: ++ OUString m_Value; ++ ++ public: ++ DsSignatureValueContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ m_rParser.HandleIdAttr(xAttrs); ++ } ++ ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->setSignatureValue(m_Value); ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_Value += rChars; ++ } ++}; ++ ++class XSecParser::DsDigestValueContext ++ : public XSecParser::Context ++{ ++ private: ++ OUString & m_rValue; ++ ++ public: ++ DsDigestValueContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rValue) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rValue(rValue) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& /*xAttrs*/) override ++ { ++ m_rValue.clear(); ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_rValue += rChars; ++ } ++}; ++ ++class XSecParser::DsDigestMethodContext ++ : public XSecParser::Context ++{ ++ private: ++ sal_Int32 & m_rReferenceDigestID; ++ ++ public: ++ DsDigestMethodContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ sal_Int32 & rReferenceDigestID) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rReferenceDigestID(rReferenceDigestID) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm"); ++ ++ SAL_WARN_IF( ouAlgorithm.isEmpty(), "xmlsecurity.helper", "no Algorithm in Reference" ); ++ if (!ouAlgorithm.isEmpty()) ++ { ++ SAL_WARN_IF( ouAlgorithm != ALGO_XMLDSIGSHA1 ++ && ouAlgorithm != ALGO_XMLDSIGSHA256 ++ && ouAlgorithm != ALGO_XMLDSIGSHA512, ++ "xmlsecurity.helper", "Algorithm neither SHA1, SHA256 nor SHA512"); ++ if (ouAlgorithm == ALGO_XMLDSIGSHA1) ++ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA1; ++ else if (ouAlgorithm == ALGO_XMLDSIGSHA256) ++ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA256; ++ else if (ouAlgorithm == ALGO_XMLDSIGSHA512) ++ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA512; ++ else ++ m_rReferenceDigestID = 0; ++ } ++ } ++}; ++ ++class XSecParser::DsTransformContext ++ : public XSecParser::Context ++{ ++ private: ++ bool & m_rIsC14N; ++ ++ public: ++ DsTransformContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool & rIsC14N) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rIsC14N(rIsC14N) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm"); ++ ++ if (ouAlgorithm == ALGO_C14N) ++ /* ++ * a xml stream ++ */ ++ { ++ m_rIsC14N = true; ++ } ++ } ++}; ++ ++class XSecParser::DsTransformsContext ++ : public XSecParser::Context ++{ ++ private: ++ bool & m_rIsC14N; ++ ++ public: ++ DsTransformsContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool & rIsC14N) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rIsC14N(rIsC14N) ++ { ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "Transform") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_rIsC14N); ++ } ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class XSecParser::DsReferenceContext ++ : public XSecParser::Context ++{ ++ private: ++ OUString m_URI; ++ OUString m_Type; ++ OUString m_DigestValue; ++ bool m_IsC14N = false; ++ // Relevant for ODF. The digest algorithm selected by the DigestMethod ++ // element's Algorithm attribute. @see css::xml::crypto::DigestID. ++ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1; ++ ++ public: ++ DsReferenceContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ m_rParser.HandleIdAttr(xAttrs); ++ ++ m_URI = xAttrs->getValueByName("URI"); ++ SAL_WARN_IF(m_URI.isEmpty(), "xmlsecurity.helper", "URI is empty"); ++ // Remember the type of this reference. ++ m_Type = xAttrs->getValueByName("Type"); ++ } ++ ++ virtual void EndElement() override ++ { ++ if (m_URI.startsWith("#")) ++ { ++ /* ++ * remove the first character '#' from the attribute value ++ */ ++ m_rParser.m_pXSecController->addReference(m_URI.copy(1), m_nReferenceDigestID, m_Type); ++ } ++ else ++ { ++ if (m_IsC14N) // this is determined by nested ds:Transform ++ { ++ m_rParser.m_pXSecController->addStreamReference(m_URI, false, m_nReferenceDigestID); ++ } ++ else ++ /* ++ * it must be an octet stream ++ */ ++ { ++ m_rParser.m_pXSecController->addStreamReference(m_URI, true, m_nReferenceDigestID); ++ } ++ } ++ ++ m_rParser.m_pXSecController->setDigestValue(m_nReferenceDigestID, m_DigestValue); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "Transforms") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_IsC14N); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_nReferenceDigestID); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_DigestValue); ++ } ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class XSecParser::DsSignatureMethodContext ++ : public XSecParser::Context ++{ ++ public: ++ DsSignatureMethodContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm"); ++ if (ouAlgorithm == ALGO_ECDSASHA1 || ouAlgorithm == ALGO_ECDSASHA256 ++ || ouAlgorithm == ALGO_ECDSASHA512) ++ { ++ m_rParser.m_pXSecController->setSignatureMethod(svl::crypto::SignatureMethodAlgorithm::ECDSA); ++ } ++ } ++}; ++ ++class XSecParser::DsSignedInfoContext ++ : public XSecParser::Context ++{ ++ public: ++ DsSignedInfoContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ m_rParser.HandleIdAttr(xAttrs); ++ } ++ ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->setReferenceCount(); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureMethod") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "Reference") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ // missing: ds:CanonicalizationMethod ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class XSecParser::XadesEncapsulatedX509CertificateContext ++ : public XSecParser::Context ++{ ++ private: ++ OUString m_Value; ++ ++ public: ++ XadesEncapsulatedX509CertificateContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ m_rParser.HandleIdAttr(xAttrs); ++ } ++ ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->addEncapsulatedX509Certificate(m_Value); ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_Value += rChars; ++ } ++}; ++ ++class XSecParser::XadesCertificateValuesContext ++ : public XSecParser::Context ++{ ++ public: ++ XadesCertificateValuesContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ m_rParser.HandleIdAttr(xAttrs); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "EncapsulatedX509Certificate") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ // missing: xades:OtherCertificate ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class XSecParser::XadesUnsignedSignaturePropertiesContext ++ : public XSecParser::Context ++{ ++ public: ++ XadesUnsignedSignaturePropertiesContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ m_rParser.HandleIdAttr(xAttrs); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "CertificateValues") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ // missing: ++ // xades:CounterSignature ++ // ^ old code would read a ds:Signature inside it? ++ // xades:SignatureTimeStamp ++ // xades:CompleteCertificateRefs ++ // xades:CompleteRevocationRefs ++ // xades:AttributeCertificateRefs ++ // xades:AttributeRevocationRefs ++ // xades:SigAndRefsTimeStamp ++ // xades:RefsOnlyTimeStamp ++ // xades:RevocationValues ++ // xades:AttrAuthoritiesCertValues ++ // ^ old code: was equivalent to CertificateValues ??? ++ // xades:AttributeRevocationValues ++ // xades:ArchiveTimeStamp ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class XSecParser::XadesUnsignedPropertiesContext ++ : public XSecParser::Context ++{ ++ public: ++ XadesUnsignedPropertiesContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ m_rParser.HandleIdAttr(xAttrs); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "UnsignedSignatureProperties") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ // missing: xades:UnsignedDataObjectProperties ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class XSecParser::LoSignatureLineIdContext ++ : public XSecParser::Context ++{ ++ private: ++ OUString m_Value; ++ ++ public: ++ LoSignatureLineIdContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->setSignatureLineId(m_Value); ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_Value += rChars; ++ } ++}; ++ ++class XSecParser::LoSignatureLineValidImageContext ++ : public XSecParser::Context ++{ ++ private: ++ OUString m_Value; ++ ++ public: ++ LoSignatureLineValidImageContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->setValidSignatureImage(m_Value); ++ } + +-void SAL_CALL XSecParser::startElement( +- const OUString& aName, +- const cssu::Reference< cssxs::XAttributeList >& xAttribs ) ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_Value += rChars; ++ } ++}; ++ ++class XSecParser::LoSignatureLineInvalidImageContext ++ : public XSecParser::Context + { +- try +- { +- OUString ouIdAttr = getIdAttr(xAttribs); +- if (!ouIdAttr.isEmpty()) ++ private: ++ OUString m_Value; ++ ++ public: ++ LoSignatureLineInvalidImageContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + { +- m_pXSecController->collectToVerify( ouIdAttr ); + } + +- if ( aName == "Signature" ) ++ virtual void EndElement() override + { +- m_rXMLSignatureHelper.StartVerifySignatureElement(); +- m_pXSecController->addSignature(); +- if (!ouIdAttr.isEmpty()) +- { +- m_pXSecController->setId( ouIdAttr ); +- } ++ m_rParser.m_pXSecController->setInvalidSignatureImage(m_Value); + } +- else if (aName == "SignatureMethod") ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_Value += rChars; ++ } ++}; ++ ++class XSecParser::LoSignatureLineContext ++ : public XSecParser::Context ++{ ++ public: ++ LoSignatureLineContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + { +- OUString ouAlgorithm = xAttribs->getValueByName("Algorithm"); +- if (ouAlgorithm == ALGO_ECDSASHA1 || ouAlgorithm == ALGO_ECDSASHA256 +- || ouAlgorithm == ALGO_ECDSASHA512) +- m_pXSecController->setSignatureMethod(svl::crypto::SignatureMethodAlgorithm::ECDSA); + } +- else if ( aName == "Reference" ) ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override + { +- OUString ouUri = xAttribs->getValueByName("URI"); +- SAL_WARN_IF( ouUri.isEmpty(), "xmlsecurity.helper", "URI is empty" ); +- // Remember the type of this reference. +- OUString ouType = xAttribs->getValueByName("Type"); +- if (ouUri.startsWith("#")) ++ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLineId") + { +- /* +- * remove the first character '#' from the attribute value +- */ +- m_pXSecController->addReference( ouUri.copy(1), m_nReferenceDigestID, ouType ); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); + } +- else ++ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLineValidImage") + { +- /* +- * remember the uri +- */ +- m_currentReferenceURI = ouUri; +- m_bReferenceUnresolved = true; ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); + } +- } +- else if (aName == "DigestMethod") +- { +- OUString ouAlgorithm = xAttribs->getValueByName("Algorithm"); +- +- SAL_WARN_IF( ouAlgorithm.isEmpty(), "xmlsecurity.helper", "no Algorithm in Reference" ); +- if (!ouAlgorithm.isEmpty()) ++ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLineInvalidImage") + { +- SAL_WARN_IF( ouAlgorithm != ALGO_XMLDSIGSHA1 +- && ouAlgorithm != ALGO_XMLDSIGSHA256 +- && ouAlgorithm != ALGO_XMLDSIGSHA512, +- "xmlsecurity.helper", "Algorithm neither SHA1, SHA256 nor SHA512"); +- if (ouAlgorithm == ALGO_XMLDSIGSHA1) +- m_nReferenceDigestID = cssxc::DigestID::SHA1; +- else if (ouAlgorithm == ALGO_XMLDSIGSHA256) +- m_nReferenceDigestID = cssxc::DigestID::SHA256; +- else if (ouAlgorithm == ALGO_XMLDSIGSHA512) +- m_nReferenceDigestID = cssxc::DigestID::SHA512; +- else +- m_nReferenceDigestID = 0; ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); + } ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } +- else if (aName == "Transform") +- { +- if ( m_bReferenceUnresolved ) +- { +- OUString ouAlgorithm = xAttribs->getValueByName("Algorithm"); ++}; + +- if (ouAlgorithm == ALGO_C14N) +- /* +- * a xml stream +- */ +- { +- m_pXSecController->addStreamReference( m_currentReferenceURI, false, m_nReferenceDigestID ); +- m_bReferenceUnresolved = false; +- } +- } ++class XSecParser::XadesCertDigestContext ++ : public XSecParser::Context ++{ ++ private: ++ OUString m_Value; ++ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1; ++ ++ public: ++ XadesCertDigestContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { + } +- else if (aName == "X509IssuerName") ++ ++ virtual void EndElement() override + { +- m_ouX509IssuerName.clear(); +- m_bInX509IssuerName = true; ++ m_rParser.m_pXSecController->setCertDigest(m_Value/* FIXME , m_nReferenceDigestID*/); + } +- else if (aName == "X509SerialNumber") ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override + { +- m_ouX509SerialNumber.clear(); +- m_bInX509SerialNumber = true; ++ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_nReferenceDigestID); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_Value); ++ } ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } +- else if (aName == "X509Certificate") ++}; ++ ++class XSecParser::XadesCertContext ++ : public XSecParser::Context ++{ ++ public: ++ XadesCertContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + { +- m_ouX509Certificate.clear(); +- m_bInX509Certificate = true; + } +- else if (aName == "PGPData") ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override + { +- m_pXSecController->switchGpgSignature(); ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "CertDigest") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "IssuerSerial") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } +- else if (aName == "PGPKeyID") ++}; ++ ++class XSecParser::XadesSigningCertificateContext ++ : public XSecParser::Context ++{ ++ public: ++ XadesSigningCertificateContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + { +- m_ouGpgKeyID.clear(); +- m_bInGpgKeyID = true; + } +- else if (aName == "PGPKeyPacket") ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override + { +- m_ouGpgCertificate.clear(); +- m_bInGpgCertificate = true; ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "Cert") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } +- else if (aName == "loext:PGPOwner") ++}; ++ ++class XSecParser::XadesSigningTimeContext ++ : public XSecParser::Context ++{ ++ public: ++ XadesSigningTimeContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + { +- m_ouGpgOwner.clear(); +- m_bInGpgOwner = true; + } +- else if (aName == "SignatureValue") ++ ++ virtual void StartElement( ++ css::uno::Reference const& /*xAttrs*/) override + { +- m_ouSignatureValue.clear(); +- m_bInSignatureValue = true; ++ m_rParser.m_ouDate.clear(); + } +- else if (aName == "DigestValue" && !m_bInCertDigest) ++ ++ virtual void EndElement() override + { +- m_ouDigestValue.clear(); +- m_bInDigestValue = true; ++ m_rParser.m_pXSecController->setDate( m_rParser.m_ouDate ); + } +- else if (aName == "xd:CertDigest") ++ ++ virtual void Characters(OUString const& rChars) override + { +- m_ouCertDigest.clear(); +- m_bInCertDigest = true; ++ m_rParser.m_ouDate += rChars; + } +- // FIXME: Existing code here in xmlsecurity uses "xd" as the namespace prefix for XAdES, +- // while the sample document attached to tdf#76142 uses "xades". So accept either here. Of +- // course this is idiotic and wrong, the right thing would be to use a proper way to parse +- // XML that would handle namespaces correctly. I have no idea how substantial re-plumbing of +- // this code that would require. +- else if (aName == "xd:EncapsulatedX509Certificate" || aName == "xades:EncapsulatedX509Certificate") ++}; ++ ++class XSecParser::XadesSignedSignaturePropertiesContext ++ : public XSecParser::Context ++{ ++ public: ++ XadesSignedSignaturePropertiesContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + { +- m_ouEncapsulatedX509Certificate.clear(); +- m_bInEncapsulatedX509Certificate = true; + } +- else if (aName == "xd:SigningTime" || aName == "xades:SigningTime") ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override + { +- m_ouDate.clear(); +- m_bInSigningTime = true; ++ m_rParser.HandleIdAttr(xAttrs); + } +- else if ( aName == "SignatureProperty" ) ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override + { +- if (!ouIdAttr.isEmpty()) ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningTime") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningCertificate") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLine") + { +- m_pXSecController->setPropertyId( ouIdAttr ); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); + } ++ // missing: xades:SignaturePolicyIdentifier, xades:SignatureProductionPlace, xades:SignerRole ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } +- else if (aName == "dc:date") ++}; ++ ++class XSecParser::XadesSignedPropertiesContext ++ : public XSecParser::Context ++{ ++ public: ++ XadesSignedPropertiesContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + { +- if (m_ouDate.isEmpty()) +- m_bInDate = true; + } +- else if (aName == "dc:description") ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override + { +- m_ouDescription.clear(); +- m_bInDescription = true; ++ m_rParser.HandleIdAttr(xAttrs); + } +- else if (aName == "loext:SignatureLineId") ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override + { +- m_ouSignatureLineId.clear(); +- m_bInSignatureLineId = true; ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedSignatureProperties") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ // missing: xades:SignedDataObjectProperties ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } +- else if (aName == "loext:SignatureLineValidImage") ++}; ++ ++class XSecParser::XadesQualifyingPropertiesContext ++ : public XSecParser::Context ++{ ++ public: ++ XadesQualifyingPropertiesContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + { +- m_ouSignatureLineValidImage.clear(); +- m_bInSignatureLineValidImage = true; + } +- else if (aName == "loext:SignatureLineInvalidImage") ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override + { +- m_ouSignatureLineInvalidImage.clear(); +- m_bInSignatureLineInvalidImage = true; ++ m_rParser.HandleIdAttr(xAttrs); + } + +- if (m_xNextHandler.is()) ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override + { +- m_xNextHandler->startElement(aName, xAttribs); ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedProperties") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "UnsignedProperties") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } +- } +- catch (cssu::Exception& ) +- {//getCaughtException MUST be the first line in the catch block +- cssu::Any exc = cppu::getCaughtException(); +- throw cssxs::SAXException( +- "xmlsecurity: Exception in XSecParser::startElement", +- nullptr, exc); +- } +- catch (...) +- { +- throw cssxs::SAXException( +- "xmlsecurity: unexpected exception in XSecParser::startElement", nullptr, +- cssu::Any()); +- } +-} ++}; + +-void SAL_CALL XSecParser::endElement( const OUString& aName ) ++class XSecParser::DcDateContext ++ : public XSecParser::Context + { +- try +- { +- if (aName == "DigestValue" && !m_bInCertDigest) ++ private: ++ bool m_isIgnore = false; ++ ++ public: ++ DcDateContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + { +- m_bInDigestValue = false; + } +- else if ( aName == "Reference" ) ++ ++ virtual void StartElement( ++ css::uno::Reference const& /*xAttrs*/) override + { +- if ( m_bReferenceUnresolved ) +- /* +- * it must be an octet stream +- */ ++ m_isIgnore = !m_rParser.m_ouDate.isEmpty(); ++ } ++ ++ virtual void EndElement() override ++ { ++ if (!m_isIgnore) + { +- m_pXSecController->addStreamReference( m_currentReferenceURI, true, m_nReferenceDigestID ); +- m_bReferenceUnresolved = false; ++ m_rParser.m_pXSecController->setDate( m_rParser.m_ouDate ); + } ++ } + +- m_pXSecController->setDigestValue( m_nReferenceDigestID, m_ouDigestValue ); ++ virtual void Characters(OUString const& rChars) override ++ { ++ if (!m_isIgnore) ++ { ++ m_rParser.m_ouDate += rChars; ++ } + } +- else if ( aName == "SignedInfo" ) ++}; ++ ++class XSecParser::DcDescriptionContext ++ : public XSecParser::Context ++{ ++ private: ++ OUString m_Value; ++ ++ public: ++ DcDescriptionContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + { +- m_pXSecController->setReferenceCount(); + } +- else if ( aName == "SignatureValue" ) ++ ++ virtual void EndElement() override + { +- m_pXSecController->setSignatureValue( m_ouSignatureValue ); +- m_bInSignatureValue = false; ++ m_rParser.m_pXSecController->setDescription(m_Value); + } +- else if (aName == "X509IssuerName") ++ ++ virtual void Characters(OUString const& rChars) override + { +- m_pXSecController->setX509IssuerName( m_ouX509IssuerName ); +- m_bInX509IssuerName = false; ++ m_Value += rChars; + } +- else if (aName == "X509SerialNumber") ++}; ++ ++class XSecParser::DsSignaturePropertyContext ++ : public XSecParser::Context ++{ ++ public: ++ DsSignaturePropertyContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + { +- m_pXSecController->setX509SerialNumber( m_ouX509SerialNumber ); +- m_bInX509SerialNumber = false; + } +- else if (aName == "X509Certificate") ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override + { +- m_pXSecController->setX509Certificate( m_ouX509Certificate ); +- m_bInX509Certificate = false; ++ OUString const ouIdAttr(m_rParser.HandleIdAttr(xAttrs)); ++ if (!ouIdAttr.isEmpty()) ++ { ++ m_rParser.m_pXSecController->setPropertyId( ouIdAttr ); ++ } + } +- else if (aName == "PGPKeyID") ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override + { +- m_pXSecController->setGpgKeyID( m_ouGpgKeyID ); +- m_bInGpgKeyID = false; ++ if (nNamespace == XML_NAMESPACE_DC && rName == "date") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_DC && rName == "description") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } +- else if (aName == "PGPKeyPacket") ++}; ++ ++class XSecParser::DsSignaturePropertiesContext ++ : public XSecParser::Context ++{ ++ public: ++ DsSignaturePropertiesContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + { +- m_pXSecController->setGpgCertificate( m_ouGpgCertificate ); +- m_bInGpgCertificate = false; + } +- else if (aName == "loext:PGPOwner") ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override + { +- m_pXSecController->setGpgOwner( m_ouGpgOwner ); +- m_bInGpgOwner = false; ++ m_rParser.HandleIdAttr(xAttrs); + } +- else if (aName == "xd:CertDigest") ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override + { +- m_pXSecController->setCertDigest( m_ouCertDigest ); +- m_bInCertDigest = false; ++ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperty") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } +- else if (aName == "xd:EncapsulatedX509Certificate" || aName == "xades:EncapsulatedX509Certificate") ++}; ++ ++class XSecParser::DsObjectContext ++ : public XSecParser::Context ++{ ++ public: ++ DsObjectContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + { +- m_pXSecController->addEncapsulatedX509Certificate( m_ouEncapsulatedX509Certificate ); +- m_bInEncapsulatedX509Certificate = false; + } +- else if (aName == "xd:SigningTime" || aName == "xades:SigningTime") ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override + { +- m_pXSecController->setDate( m_ouDate ); +- m_bInSigningTime = false; ++ m_rParser.HandleIdAttr(xAttrs); + } +- else if (aName == "dc:date") ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override + { +- if (m_bInDate) ++ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperties") + { +- m_pXSecController->setDate( m_ouDate ); +- m_bInDate = false; ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); + } ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "QualifyingProperties") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ // missing: ds:Manifest ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } +- else if (aName == "dc:description") ++}; ++ ++class XSecParser::DsSignatureContext ++ : public XSecParser::Context ++{ ++ public: ++ DsSignatureContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + { +- m_pXSecController->setDescription( m_ouDescription ); +- m_bInDescription = false; + } +- else if (aName == "loext:SignatureLineId") ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override + { +- m_pXSecController->setSignatureLineId( m_ouSignatureLineId ); +- m_bInSignatureLineId = false; ++ OUString const ouIdAttr(m_rParser.HandleIdAttr(xAttrs)); ++ m_rParser.m_rXMLSignatureHelper.StartVerifySignatureElement(); ++ m_rParser.m_pXSecController->addSignature(); ++ if (!ouIdAttr.isEmpty()) ++ { ++ m_rParser.m_pXSecController->setId( ouIdAttr ); ++ } + } +- else if (aName == "loext:SignatureLineValidImage") ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override + { +- m_pXSecController->setValidSignatureImage( m_ouSignatureLineValidImage ); +- m_bInSignatureLineValidImage = false; ++ if (nNamespace == XML_NAMESPACE_DS && rName == "SignedInfo") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureValue") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "KeyInfo") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "Object") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } +- else if (aName == "loext:SignatureLineInvalidImage") ++}; ++ ++class XSecParser::DsigSignaturesContext ++ : public XSecParser::Context ++{ ++ public: ++ DsigSignaturesContext(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + { +- m_pXSecController->setInvalidSignatureImage( m_ouSignatureLineInvalidImage ); +- m_bInSignatureLineInvalidImage = false; + } + +- if (m_xNextHandler.is()) ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override + { +- m_xNextHandler->endElement(aName); ++ if (nNamespace == XML_NAMESPACE_DS && rName == "Signature") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } +- } +- catch (cssu::Exception& ) +- {//getCaughtException MUST be the first line in the catch block +- cssu::Any exc = cppu::getCaughtException(); +- throw cssxs::SAXException( +- "xmlsecurity: Exception in XSecParser::endElement", +- nullptr, exc); +- } +- catch (...) +- { +- throw cssxs::SAXException( +- "xmlsecurity: unexpected exception in XSecParser::endElement", nullptr, +- cssu::Any()); +- } ++}; ++ ++ ++XSecParser::XSecParser(XMLSignatureHelper& rXMLSignatureHelper, ++ XSecController* pXSecController) ++ : m_pNamespaceMap(new SvXMLNamespaceMap) ++ , m_pXSecController(pXSecController) ++ , m_rXMLSignatureHelper(rXMLSignatureHelper) ++{ ++ using namespace xmloff::token; ++ m_pNamespaceMap->Add( GetXMLToken(XML_XML), GetXMLToken(XML_N_XML), XML_NAMESPACE_XML ); ++ m_pNamespaceMap->Add( "_dsig_ooo", GetXMLToken(XML_N_DSIG_OOO), XML_NAMESPACE_DSIG_OOO ); ++ m_pNamespaceMap->Add( "_dsig", GetXMLToken(XML_N_DSIG), XML_NAMESPACE_DSIG ); ++ m_pNamespaceMap->Add( "_ds", GetXMLToken(XML_N_DS), XML_NAMESPACE_DS ); ++ m_pNamespaceMap->Add( "_xades132", GetXMLToken(XML_N_XADES132), XML_NAMESPACE_XADES132); ++ m_pNamespaceMap->Add( "_xades141", GetXMLToken(XML_N_XADES141), XML_NAMESPACE_XADES141); ++ m_pNamespaceMap->Add( "_dc", GetXMLToken(XML_N_DC), XML_NAMESPACE_DC ); ++ m_pNamespaceMap->Add( "_office_libo", ++ GetXMLToken(XML_N_LO_EXT), XML_NAMESPACE_LO_EXT); + } + +-void SAL_CALL XSecParser::characters( const OUString& aChars ) ++OUString XSecParser::HandleIdAttr(css::uno::Reference const& xAttrs) + { +- if (m_bInX509IssuerName) +- { +- m_ouX509IssuerName += aChars; +- } +- else if (m_bInX509SerialNumber) ++ OUString ouIdAttr = getIdAttr(xAttrs); ++ if (!ouIdAttr.isEmpty()) + { +- m_ouX509SerialNumber += aChars; ++ m_pXSecController->collectToVerify( ouIdAttr ); + } +- else if (m_bInX509Certificate) +- { +- m_ouX509Certificate += aChars; +- } +- else if (m_bInGpgCertificate) +- { +- m_ouGpgCertificate += aChars; +- } +- else if (m_bInGpgKeyID) ++ return ouIdAttr; ++} ++ ++OUString XSecParser::getIdAttr(const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs ) ++{ ++ OUString ouIdAttr = xAttribs->getValueByName("id"); ++ ++ if (ouIdAttr.isEmpty()) + { +- m_ouGpgKeyID += aChars; ++ ouIdAttr = xAttribs->getValueByName("Id"); + } +- else if (m_bInGpgOwner) ++ ++ return ouIdAttr; ++} ++ ++/* ++ * XDocumentHandler ++ */ ++void SAL_CALL XSecParser::startDocument( ) ++{ ++ if (m_xNextHandler.is()) + { +- m_ouGpgOwner += aChars; ++ m_xNextHandler->startDocument(); + } +- else if (m_bInSignatureValue) ++} ++ ++void SAL_CALL XSecParser::endDocument( ) ++{ ++ if (m_xNextHandler.is()) + { +- m_ouSignatureValue += aChars; ++ m_xNextHandler->endDocument(); + } +- else if (m_bInDigestValue && !m_bInCertDigest) ++} ++ ++void SAL_CALL XSecParser::startElement( ++ const OUString& rName, ++ const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs ) ++{ ++ assert(m_pNamespaceMap); ++ std::unique_ptr pRewindMap( ++ SvXMLImport::processNSAttributes(m_pNamespaceMap, nullptr, xAttribs)); ++ ++ OUString localName; ++ sal_uInt16 const nPrefix(m_pNamespaceMap->GetKeyByAttrName(rName, &localName)); ++ ++ std::unique_ptr pContext; ++ ++ if (m_ContextStack.empty()) + { +- m_ouDigestValue += aChars; ++ if ((nPrefix == XML_NAMESPACE_DSIG || nPrefix == XML_NAMESPACE_DSIG_OOO) ++ && localName == "document-signatures") ++ { ++ pContext.reset(new DsigSignaturesContext(*this, std::move(pRewindMap))); ++ } ++ else ++ { ++ throw css::xml::sax::SAXException( ++ "xmlsecurity: unexpected root element", nullptr, ++ css::uno::Any()); ++ } + } +- else if (m_bInDate) ++ else + { +- m_ouDate += aChars; ++ pContext = m_ContextStack.top()->CreateChildContext( ++ std::move(pRewindMap), nPrefix, localName); + } +- else if (m_bInDescription) ++ ++ m_ContextStack.push(std::move(pContext)); ++ assert(!pRewindMap); ++ ++ try + { +- m_ouDescription += aChars; ++ m_ContextStack.top()->StartElement(xAttribs); ++ ++ if (m_xNextHandler.is()) ++ { ++ m_xNextHandler->startElement(rName, xAttribs); ++ } + } +- else if (m_bInCertDigest) +- { +- m_ouCertDigest += aChars; ++ catch (css::uno::Exception& ) ++ {//getCaughtException MUST be the first line in the catch block ++ css::uno::Any exc = cppu::getCaughtException(); ++ throw css::xml::sax::SAXException( ++ "xmlsecurity: Exception in XSecParser::startElement", ++ nullptr, exc); + } +- else if (m_bInEncapsulatedX509Certificate) ++ catch (...) + { +- m_ouEncapsulatedX509Certificate += aChars; ++ throw css::xml::sax::SAXException( ++ "xmlsecurity: unexpected exception in XSecParser::startElement", nullptr, ++ css::uno::Any()); + } +- else if (m_bInSigningTime) ++} ++ ++void SAL_CALL XSecParser::endElement(const OUString& rName) ++{ ++ assert(!m_ContextStack.empty()); // this should be checked by sax parser? ++ ++ try + { +- m_ouDate += aChars; ++ m_ContextStack.top()->EndElement(); ++ ++ if (m_xNextHandler.is()) ++ { ++ m_xNextHandler->endElement(rName); ++ } + } +- else if (m_bInSignatureLineId) +- { +- m_ouSignatureLineId += aChars; ++ catch (css::uno::Exception& ) ++ {//getCaughtException MUST be the first line in the catch block ++ css::uno::Any exc = cppu::getCaughtException(); ++ throw css::xml::sax::SAXException( ++ "xmlsecurity: Exception in XSecParser::endElement", ++ nullptr, exc); + } +- else if (m_bInSignatureLineValidImage) ++ catch (...) + { +- m_ouSignatureLineValidImage += aChars; ++ throw css::xml::sax::SAXException( ++ "xmlsecurity: unexpected exception in XSecParser::endElement", nullptr, ++ css::uno::Any()); + } +- else if (m_bInSignatureLineInvalidImage) ++ ++ if (m_ContextStack.top()->m_pOldNamespaceMap) + { +- m_ouSignatureLineInvalidImage += aChars; ++ m_pNamespaceMap = std::move(m_ContextStack.top()->m_pOldNamespaceMap); + } ++ m_ContextStack.pop(); ++} ++ ++void SAL_CALL XSecParser::characters(const OUString& rChars) ++{ ++ assert(!m_ContextStack.empty()); // this should be checked by sax parser? ++ m_ContextStack.top()->Characters(rChars); + + if (m_xNextHandler.is()) + { +- m_xNextHandler->characters(aChars); ++ m_xNextHandler->characters(rChars); + } + } + +diff --git a/xmlsecurity/source/helper/xsecparser.hxx b/xmlsecurity/source/helper/xsecparser.hxx +index d9b079aa3116..93efcb766e3e 100644 +--- a/xmlsecurity/source/helper/xsecparser.hxx ++++ b/xmlsecurity/source/helper/xsecparser.hxx +@@ -25,6 +25,10 @@ + + #include + ++#include ++ ++#include ++ + class XMLSignatureHelper; + class XSecController; + +@@ -48,47 +52,59 @@ class XSecParser: public cppu::WeakImplHelper + ******************************************************************************/ + { + friend class XSecController; ++public: ++ class Context; + private: ++ class UnknownContext; ++ class LoPGPOwnerContext; ++ class DsPGPKeyPacketContext; ++ class DsPGPKeyIDContext; ++ class DsPGPDataContext; ++ class DsX509CertificateContext; ++ class DsX509SerialNumberContext; ++ class DsX509IssuerNameContext; ++ class DsX509IssuerSerialContext; ++ class DsX509DataContext; ++ class DsKeyInfoContext; ++ class DsSignatureValueContext; ++ class DsDigestValueContext; ++ class DsDigestMethodContext; ++ class DsTransformContext; ++ class DsTransformsContext; ++ class DsReferenceContext; ++ class DsSignatureMethodContext; ++ class DsSignedInfoContext; ++ class XadesEncapsulatedX509CertificateContext; ++ class XadesCertificateValuesContext; ++ class XadesUnsignedSignaturePropertiesContext; ++ class XadesUnsignedPropertiesContext; ++ class LoSignatureLineIdContext; ++ class LoSignatureLineValidImageContext; ++ class LoSignatureLineInvalidImageContext; ++ class LoSignatureLineContext; ++ class XadesCertDigestContext; ++ class XadesCertContext; ++ class XadesSigningCertificateContext; ++ class XadesSigningTimeContext; ++ class XadesSignedSignaturePropertiesContext; ++ class XadesSignedPropertiesContext; ++ class XadesQualifyingPropertiesContext; ++ class DcDateContext; ++ class DcDescriptionContext; ++ class DsSignaturePropertyContext; ++ class DsSignaturePropertiesContext; ++ class DsObjectContext; ++ class DsSignatureContext; ++ class DsigSignaturesContext; ++ + /* + * the following members are used to reserve the signature information, + * including X509IssuerName, X509SerialNumber, and X509Certificate,etc. + */ +- OUString m_ouX509IssuerName; +- OUString m_ouX509SerialNumber; +- OUString m_ouX509Certificate; +- OUString m_ouGpgCertificate; +- OUString m_ouGpgKeyID; +- OUString m_ouGpgOwner; +- OUString m_ouCertDigest; +- OUString m_ouEncapsulatedX509Certificate; +- OUString m_ouDigestValue; +- OUString m_ouSignatureValue; + OUString m_ouDate; +- /// Characters of a element, as just read from XML. +- OUString m_ouDescription; +- OUString m_ouSignatureLineId; +- OUString m_ouSignatureLineValidImage; +- OUString m_ouSignatureLineInvalidImage; + +- /* +- * whether inside a particular element +- */ +- bool m_bInX509IssuerName; +- bool m_bInX509SerialNumber; +- bool m_bInX509Certificate; +- bool m_bInGpgCertificate; +- bool m_bInGpgKeyID; +- bool m_bInGpgOwner; +- bool m_bInCertDigest; +- bool m_bInEncapsulatedX509Certificate; +- bool m_bInSigningTime; +- bool m_bInDigestValue; +- bool m_bInSignatureValue; +- bool m_bInDate; +- bool m_bInDescription; +- bool m_bInSignatureLineId; +- bool m_bInSignatureLineValidImage; +- bool m_bInSignatureLineInvalidImage; ++ std::stack> m_ContextStack; ++ std::unique_ptr m_pNamespaceMap; + + /* + * the XSecController collaborating with XSecParser +@@ -101,22 +117,9 @@ private: + css::uno::Reference< + css::xml::sax::XDocumentHandler > m_xNextHandler; + +- /* +- * this string is used to remember the current handled reference's URI, +- * +- * because it can be decided whether a stream reference is xml based or binary based +- * only after the Transforms element is read in, so we have to reserve the reference's +- * URI when the startElement event is met. +- */ +- OUString m_currentReferenceURI; +- bool m_bReferenceUnresolved; +- +- // Relevant for ODF. The digest algorithm selected by the current DigestMethod element's +- // Algorithm attribute in the current Reference element. From css::xml::crypto::DigestID. +- sal_Int32 m_nReferenceDigestID; + XMLSignatureHelper& m_rXMLSignatureHelper; + +-private: ++ OUString HandleIdAttr(css::uno::Reference const& xAttrs); + static OUString getIdAttr(const css::uno::Reference< + css::xml::sax::XAttributeList >& xAttribs ); + +-- +2.32.0 + diff --git a/SOURCES/0004-CVE-2021-25634.patch b/SOURCES/0004-CVE-2021-25634.patch new file mode 100644 index 0000000..af2c9a1 --- /dev/null +++ b/SOURCES/0004-CVE-2021-25634.patch @@ -0,0 +1,397 @@ +From 9ef423d4e7c85629772131b3216b98e17d7b8d7e Mon Sep 17 00:00:00 2001 +From: Michael Stahl +Date: Thu, 18 Feb 2021 19:22:31 +0100 +Subject: [PATCH 4/6] CVE-2021-25634 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +xmlsecurity: XSecParser confused about multiple timestamps + +LO writes timestamp both to dc:date and xades:SigningTime elements. + +The parser tries to avoid reading multiple dc:date, preferring the first +one, but doesn't care about multiple xades:SigningTime, for undocumented +reasons. + +Ideally something should check all read values for consistency. + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111160 +Tested-by: Jenkins +Reviewed-by: Michael Stahl +(cherry picked from commit 4ab8d9c09a5873ca0aea56dafa1ab34758d52ef7) + +xmlsecurity: remove XSecController::setPropertyId() + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111252 +Tested-by: Jenkins +Reviewed-by: Michael Stahl +(cherry picked from commit d2a345e1163616fe3201ef1d6c758e2e819214e0) + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111908 +Tested-by: Jenkins +Reviewed-by: Caolán McNamara +(cherry picked from commit abe77c4fcb9ea97d9fff07eaea6d8863bcba5b02) + +Change-Id: Ic018ee89797a1c8a4f870ae102af48006de930ef +--- + include/svl/sigstruct.hxx | 7 +- + xmlsecurity/inc/xsecctl.hxx | 5 +- + xmlsecurity/source/helper/ooxmlsecparser.cxx | 4 +- + xmlsecurity/source/helper/xsecctl.cxx | 2 +- + xmlsecurity/source/helper/xsecparser.cxx | 81 ++++++++++---------- + xmlsecurity/source/helper/xsecparser.hxx | 6 -- + xmlsecurity/source/helper/xsecsign.cxx | 4 +- + xmlsecurity/source/helper/xsecverify.cxx | 39 ++++------ + 8 files changed, 68 insertions(+), 80 deletions(-) + +diff --git a/include/svl/sigstruct.hxx b/include/svl/sigstruct.hxx +index f6ee242c84d1..7a0296fa9fae 100644 +--- a/include/svl/sigstruct.hxx ++++ b/include/svl/sigstruct.hxx +@@ -103,6 +103,9 @@ struct SignatureInformation + // XAdES EncapsulatedX509Certificate values + std::set maEncapsulatedX509Certificates; + ++ OUString ouSignatureId; ++ // signature may contain multiple time stamps - check they're consistent ++ bool hasInconsistentSigningTime = false; + //We also keep the date and time as string. This is done when this + //structure is created as a result of a XML signature being read. + //When then a signature is added or another removed, then the original +@@ -115,8 +118,8 @@ struct SignatureInformation + //and the converted time is written back, then the string looks different + //and the signature is broken. + OUString ouDateTime; +- OUString ouSignatureId; +- OUString ouPropertyId; ++ /// The Id attribute of the element that contains the . ++ OUString ouDateTimePropertyId; + /// Characters of the element inside the signature. + OUString ouDescription; + /// The Id attribute of the element that contains the . +diff --git a/xmlsecurity/inc/xsecctl.hxx b/xmlsecurity/inc/xsecctl.hxx +index 351c94a2a3e6..7baa219fb13c 100644 +--- a/xmlsecurity/inc/xsecctl.hxx ++++ b/xmlsecurity/inc/xsecctl.hxx +@@ -271,8 +271,8 @@ private: + void setGpgCertificate( OUString const & ouGpgCert ); + void setGpgOwner( OUString const & ouGpgOwner ); + +- void setDate( OUString const & ouDate ); +- void setDescription(const OUString& rDescription); ++ void setDate(OUString const& rId, OUString const& ouDate); ++ void setDescription(OUString const& rId, OUString const& rDescription); + void setCertDigest(const OUString& rCertDigest); + void setValidSignatureImage(const OUString& rValidSigImg); + void setInvalidSignatureImage(const OUString& rInvalidSigImg); +@@ -283,7 +283,6 @@ public: + + private: + void setId( OUString const & ouId ); +- void setPropertyId( OUString const & ouPropertyId ); + + css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener > prepareSignatureToRead( + sal_Int32 nSecurityId ); +diff --git a/xmlsecurity/source/helper/ooxmlsecparser.cxx b/xmlsecurity/source/helper/ooxmlsecparser.cxx +index c22e8c2261bf..a200de60c07a 100644 +--- a/xmlsecurity/source/helper/ooxmlsecparser.cxx ++++ b/xmlsecurity/source/helper/ooxmlsecparser.cxx +@@ -192,12 +192,12 @@ void SAL_CALL OOXMLSecParser::endElement(const OUString& rName) + } + else if (rName == "mdssi:Value") + { +- m_pXSecController->setDate(m_aMdssiValue); ++ m_pXSecController->setDate("", m_aMdssiValue); + m_bInMdssiValue = false; + } + else if (rName == "SignatureComments") + { +- m_pXSecController->setDescription(m_aSignatureComments); ++ m_pXSecController->setDescription("", m_aSignatureComments); + m_bInSignatureComments = false; + } + else if (rName == "X509IssuerName") +diff --git a/xmlsecurity/source/helper/xsecctl.cxx b/xmlsecurity/source/helper/xsecctl.cxx +index ab108d13c224..8d5ea68c768b 100644 +--- a/xmlsecurity/source/helper/xsecctl.cxx ++++ b/xmlsecurity/source/helper/xsecctl.cxx +@@ -819,7 +819,7 @@ void XSecController::exportSignature( + pAttributeList = new SvXMLAttributeList(); + pAttributeList->AddAttribute( + "Id", +- signatureInfo.ouPropertyId); ++ signatureInfo.ouDateTimePropertyId); + pAttributeList->AddAttribute( + "Target", + "#" + signatureInfo.ouSignatureId); +diff --git a/xmlsecurity/source/helper/xsecparser.cxx b/xmlsecurity/source/helper/xsecparser.cxx +index 5c92e5efa104..9cc9312b4d9f 100644 +--- a/xmlsecurity/source/helper/xsecparser.cxx ++++ b/xmlsecurity/source/helper/xsecparser.cxx +@@ -978,6 +978,9 @@ class XSecParser::XadesSigningCertificateContext + class XSecParser::XadesSigningTimeContext + : public XSecParser::Context + { ++ private: ++ OUString m_Value; ++ + public: + XadesSigningTimeContext(XSecParser & rParser, + std::unique_ptr pOldNamespaceMap) +@@ -985,20 +988,14 @@ class XSecParser::XadesSigningTimeContext + { + } + +- virtual void StartElement( +- css::uno::Reference const& /*xAttrs*/) override +- { +- m_rParser.m_ouDate.clear(); +- } +- + virtual void EndElement() override + { +- m_rParser.m_pXSecController->setDate( m_rParser.m_ouDate ); ++ m_rParser.m_pXSecController->setDate("", m_Value); + } + + virtual void Characters(OUString const& rChars) override + { +- m_rParser.m_ouDate += rChars; ++ m_Value += rChars; + } + }; + +@@ -1104,35 +1101,20 @@ class XSecParser::DcDateContext + : public XSecParser::Context + { + private: +- bool m_isIgnore = false; ++ OUString & m_rValue; + + public: + DcDateContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rValue) + : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rValue(rValue) + { + } + +- virtual void StartElement( +- css::uno::Reference const& /*xAttrs*/) override +- { +- m_isIgnore = !m_rParser.m_ouDate.isEmpty(); +- } +- +- virtual void EndElement() override +- { +- if (!m_isIgnore) +- { +- m_rParser.m_pXSecController->setDate( m_rParser.m_ouDate ); +- } +- } +- + virtual void Characters(OUString const& rChars) override + { +- if (!m_isIgnore) +- { +- m_rParser.m_ouDate += rChars; +- } ++ m_rValue += rChars; + } + }; + +@@ -1140,29 +1122,32 @@ class XSecParser::DcDescriptionContext + : public XSecParser::Context + { + private: +- OUString m_Value; ++ OUString & m_rValue; + + public: + DcDescriptionContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rValue) + : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rValue(rValue) + { + } + +- virtual void EndElement() override +- { +- m_rParser.m_pXSecController->setDescription(m_Value); +- } +- + virtual void Characters(OUString const& rChars) override + { +- m_Value += rChars; ++ m_rValue += rChars; + } + }; + + class XSecParser::DsSignaturePropertyContext + : public XSecParser::Context + { ++ private: ++ enum class SignatureProperty { Unknown, Date, Description }; ++ SignatureProperty m_Property = SignatureProperty::Unknown; ++ OUString m_Id; ++ OUString m_Value; ++ + public: + DsSignaturePropertyContext(XSecParser & rParser, + std::unique_ptr pOldNamespaceMap) +@@ -1173,10 +1158,22 @@ class XSecParser::DsSignaturePropertyContext + virtual void StartElement( + css::uno::Reference const& xAttrs) override + { +- OUString const ouIdAttr(m_rParser.HandleIdAttr(xAttrs)); +- if (!ouIdAttr.isEmpty()) ++ m_Id = m_rParser.HandleIdAttr(xAttrs); ++ } ++ ++ virtual void EndElement() override ++ { ++ switch (m_Property) + { +- m_rParser.m_pXSecController->setPropertyId( ouIdAttr ); ++ case SignatureProperty::Unknown: ++ SAL_INFO("xmlsecurity.helper", "Unknown property in ds:Object ignored"); ++ break; ++ case SignatureProperty::Date: ++ m_rParser.m_pXSecController->setDate(m_Id, m_Value); ++ break; ++ case SignatureProperty::Description: ++ m_rParser.m_pXSecController->setDescription(m_Id, m_Value); ++ break; + } + } + +@@ -1186,11 +1183,13 @@ class XSecParser::DsSignaturePropertyContext + { + if (nNamespace == XML_NAMESPACE_DC && rName == "date") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ m_Property = SignatureProperty::Date; ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_Value); + } + if (nNamespace == XML_NAMESPACE_DC && rName == "description") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ m_Property = SignatureProperty::Description; ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_Value); + } + return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } +diff --git a/xmlsecurity/source/helper/xsecparser.hxx b/xmlsecurity/source/helper/xsecparser.hxx +index 93efcb766e3e..7a0eb08bca28 100644 +--- a/xmlsecurity/source/helper/xsecparser.hxx ++++ b/xmlsecurity/source/helper/xsecparser.hxx +@@ -97,12 +97,6 @@ private: + class DsSignatureContext; + class DsigSignaturesContext; + +- /* +- * the following members are used to reserve the signature information, +- * including X509IssuerName, X509SerialNumber, and X509Certificate,etc. +- */ +- OUString m_ouDate; +- + std::stack> m_ContextStack; + std::unique_ptr m_pNamespaceMap; + +diff --git a/xmlsecurity/source/helper/xsecsign.cxx b/xmlsecurity/source/helper/xsecsign.cxx +index 4d1b89949feb..5ed23281f083 100644 +--- a/xmlsecurity/source/helper/xsecsign.cxx ++++ b/xmlsecurity/source/helper/xsecsign.cxx +@@ -132,8 +132,8 @@ cssu::Reference< cssxc::sax::XReferenceResolvedListener > XSecController::prepar + if (nStorageFormat != embed::StorageFormats::OFOPXML) + { + internalSignatureInfor.signatureInfor.ouSignatureId = createId(); +- internalSignatureInfor.signatureInfor.ouPropertyId = createId(); +- internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, internalSignatureInfor.signatureInfor.ouPropertyId, -1, OUString() ); ++ internalSignatureInfor.signatureInfor.ouDateTimePropertyId = createId(); ++ internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, internalSignatureInfor.signatureInfor.ouDateTimePropertyId, -1, OUString() ); + size++; + + if (bXAdESCompliantIfODF) +diff --git a/xmlsecurity/source/helper/xsecverify.cxx b/xmlsecurity/source/helper/xsecverify.cxx +index 1f7fa9ac8ca8..5f5840334254 100644 +--- a/xmlsecurity/source/helper/xsecverify.cxx ++++ b/xmlsecurity/source/helper/xsecverify.cxx +@@ -321,7 +321,7 @@ void XSecController::setGpgOwner( OUString const & ouGpgOwner ) + isi.signatureInfor.ouGpgOwner = ouGpgOwner; + } + +-void XSecController::setDate( OUString const & ouDate ) ++void XSecController::setDate(OUString const& rId, OUString const& ouDate) + { + if (m_vInternalSignatureInformations.empty()) + { +@@ -329,17 +329,31 @@ void XSecController::setDate( OUString const & ouDate ) + return; + } + InternalSignatureInformation &isi = m_vInternalSignatureInformations.back(); ++ // there may be multiple timestamps in a signature - check them for consistency ++ if (!isi.signatureInfor.ouDateTime.isEmpty() ++ && isi.signatureInfor.ouDateTime != ouDate) ++ { ++ isi.signatureInfor.hasInconsistentSigningTime = true; ++ } + (void)utl::ISO8601parseDateTime( ouDate, isi.signatureInfor.stDateTime); + isi.signatureInfor.ouDateTime = ouDate; ++ if (!rId.isEmpty()) ++ { ++ isi.signatureInfor.ouDateTimePropertyId = rId; ++ } + } + +-void XSecController::setDescription(const OUString& rDescription) ++void XSecController::setDescription(OUString const& rId, OUString const& rDescription) + { + if (m_vInternalSignatureInformations.empty()) + return; + + InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back(); + rInformation.signatureInfor.ouDescription = rDescription; ++ if (!rId.isEmpty()) ++ { ++ rInformation.signatureInfor.ouDescriptionPropertyId = rId; ++ } + } + + void XSecController::setSignatureBytes(const uno::Sequence& rBytes) +@@ -433,27 +447,6 @@ void XSecController::setId( OUString const & ouId ) + isi.signatureInfor.ouSignatureId = ouId; + } + +-void XSecController::setPropertyId( OUString const & ouPropertyId ) +-{ +- if (m_vInternalSignatureInformations.empty()) +- { +- SAL_INFO("xmlsecurity.helper","XSecController::setPropertyId: no signature"); +- return; +- } +- InternalSignatureInformation &isi = m_vInternalSignatureInformations.back(); +- +- if (isi.signatureInfor.ouPropertyId.isEmpty()) +- { +- // ID attribute is for the date. +- isi.signatureInfor.ouPropertyId = ouPropertyId; +- } +- else +- { +- // ID attribute is for the description. +- isi.signatureInfor.ouDescriptionPropertyId = ouPropertyId; +- } +-} +- + /* public: for signature verify */ + void XSecController::collectToVerify( const OUString& referenceId ) + { +-- +2.32.0 + diff --git a/SOURCES/0005-CVE-2021-25633.patch b/SOURCES/0005-CVE-2021-25633.patch new file mode 100644 index 0000000..1edc27a --- /dev/null +++ b/SOURCES/0005-CVE-2021-25633.patch @@ -0,0 +1,2120 @@ +From b970e561433f1cbeebd43c92c92c98c0468cc483 Mon Sep 17 00:00:00 2001 +From: Michael Stahl +Date: Fri, 19 Feb 2021 17:56:21 +0100 +Subject: [PATCH 5/6] CVE-2021-25633 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +xmlsecurity: ignore elements in ds:Object that aren't signed + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111253 +Tested-by: Jenkins +Reviewed-by: Michael Stahl +(cherry picked from commit 2bfa00e6bf4b2a310a8b8f5060acec85b5f7a3ce) +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111909 +Reviewed-by: Caolán McNamara +(cherry picked from commit 94ce59dd02fcfcaa1eb4f195b45a9a2edbd58242) + +Change-Id: I2e4411f0907b89e7ad6e0185cee8f12b600515e8 + +xmlsecurity: improve handling of multiple X509Data elements + +Combine everything related to a certificate in a new struct X509Data. + +The CertDigest is not actually written in the X509Data element but in +xades:Cert, so try to find the matching entry in +XSecController::setX509CertDigest(). + +There was a confusing interaction with PGP signatures, where ouGpgKeyID +was used for import, but export wrote the value from ouCertDigest +instead - this needed fixing. + +The main point of this is enforcing a constraint from xmldsig-core 4.5.4: + + All certificates appearing in an X509Data element MUST relate to the + validation key by either containing it or being part of a certification + chain that terminates in a certificate containing the validation key. + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111254 +Tested-by: Jenkins +Reviewed-by: Michael Stahl +(cherry picked from commit 9e82509b09f5fe2eb77bcdb8fd193c71923abb67) + +xmlsecurity: improve handling of multiple certificates per X509Data + +It turns out that an X509Data element can contain an arbitrary number of +each of its child elements. + +How exactly certificates of an issuer chain may or should be distributed +across multiple X509Data elements isn't terribly obvious. + +One thing that is clear is that any element that refers to or contains +one particular certificate has to be a child of the same X509Data +element, although in no particular order, so try to match the 2 such +elements that the parser supports in XSecController::setX509Data(). + +Presumably the only way it makes sense to have multiple signing +certificates is if they all contain the same key but are signed by +different CAs. This case isn't handled currently; CheckX509Data() will +complain there's not a single chain and validation of the certificates +will fail. + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111500 +Tested-by: Jenkins +Reviewed-by: Michael Stahl +(cherry picked from commit 5af5ea893bcb8a8eb472ac11133da10e5a604e66) + +xmlsecurity: add EqualDistinguishedNames() + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111545 +Tested-by: Jenkins +Reviewed-by: Michael Stahl +(cherry picked from commit 1d3da3486d827dd5e7a3bf1c7a533f5aa9860e42) + +xmlsecurity: avoid exception in DigitalSignaturesDialog::getCertificate() + +Fallback to PGP if there's no X509 signing certificate because +CheckX509Data() failed prevents the dialog from popping up. + +To avoid confusing the user in this situation, the dialog should +show no certificate, which is already the case. + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111664 +Tested-by: Jenkins +Reviewed-by: Michael Stahl +(cherry picked from commit 90b725675c2964f4a151d802d9afedd8bc2ae1a7) + +xmlsecurity: fix crash in DocumentDigitalSignatures::isAuthorTrusted() + +If the argument is null. + +This function also should use EqualDistinguishedNames(). + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111667 +Tested-by: Jenkins +Reviewed-by: Michael Stahl +(cherry picked from commit ca98e505cd69bf95d8ddb9387cf3f8e03ae4577d) + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111910 +Tested-by: Jenkins +Reviewed-by: Caolán McNamara +(cherry picked from commit a1cf770c2d7ca3e153e0b1f01ddcc313bc2bed7f) + +Change-Id: I9633a980b0c18d58dfce24fc59396a833498a77d +--- + include/svl/sigstruct.hxx | 32 +- + svl/source/crypto/cryptosign.cxx | 16 +- + sw/source/core/edit/edfcol.cxx | 3 +- + xmlsecurity/inc/biginteger.hxx | 3 + + xmlsecurity/inc/xmlsignaturehelper.hxx | 12 + + xmlsecurity/inc/xsecctl.hxx | 15 +- + .../component/documentdigitalsignatures.cxx | 54 +-- + .../dialogs/digitalsignaturesdialog.cxx | 15 +- + .../source/helper/documentsignaturehelper.cxx | 63 ++-- + .../helper/documentsignaturemanager.cxx | 12 + + .../source/helper/ooxmlsecexporter.cxx | 23 +- + xmlsecurity/source/helper/ooxmlsecparser.cxx | 22 +- + .../source/helper/pdfsignaturehelper.cxx | 8 +- + .../source/helper/xmlsignaturehelper.cxx | 161 +++++++++ + xmlsecurity/source/helper/xsecctl.cxx | 84 +++-- + xmlsecurity/source/helper/xsecparser.cxx | 334 ++++++++++++------ + xmlsecurity/source/helper/xsecparser.hxx | 1 + + xmlsecurity/source/helper/xsecsign.cxx | 30 +- + xmlsecurity/source/helper/xsecverify.cxx | 143 ++++++-- + .../mscrypt/x509certificate_mscryptimpl.cxx | 47 +++ + .../mscrypt/xmlsignature_mscryptimpl.cxx | 2 + + .../xmlsec/nss/x509certificate_nssimpl.cxx | 25 ++ + .../xmlsec/nss/xmlsignature_nssimpl.cxx | 3 + + 23 files changed, 853 insertions(+), 255 deletions(-) + +diff --git a/include/svl/sigstruct.hxx b/include/svl/sigstruct.hxx +index 7a0296fa9fae..f00cbce6e4b8 100644 +--- a/include/svl/sigstruct.hxx ++++ b/include/svl/sigstruct.hxx +@@ -89,9 +89,30 @@ struct SignatureInformation + sal_Int32 nSecurityId; + css::xml::crypto::SecurityOperationStatus nStatus; + SignatureReferenceInformations vSignatureReferenceInfors; +- OUString ouX509IssuerName; +- OUString ouX509SerialNumber; +- OUString ouX509Certificate; ++ struct X509CertInfo ++ { ++ OUString X509IssuerName; ++ OUString X509SerialNumber; ++ OUString X509Certificate; ++ /// OOXML certificate SHA-256 digest, empty for ODF except when doing XAdES signature. ++ OUString CertDigest; ++ /// The certificate owner (aka subject). ++ OUString X509Subject; ++ }; ++ typedef std::vector X509Data; ++ // note: at parse time, it's unkown which one is the signing certificate; ++ // ImplVerifySignatures() figures it out and puts it at the back ++ std::vector X509Datas; ++ ++ X509CertInfo const* GetSigningCertificate() const ++ { ++ if (X509Datas.empty()) ++ { ++ return nullptr; ++ } ++ assert(!X509Datas.back().empty()); ++ return & X509Datas.back().back(); ++ } + + OUString ouGpgKeyID; + OUString ouGpgCertificate; +@@ -124,8 +145,6 @@ struct SignatureInformation + OUString ouDescription; + /// The Id attribute of the element that contains the . + OUString ouDescriptionPropertyId; +- /// OOXML certificate SHA-256 digest, empty for ODF except when doing XAdES signature. +- OUString ouCertDigest; + /// Valid and invalid signature line images + css::uno::Reference aValidSignatureImage; + css::uno::Reference aInvalidSignatureImage; +@@ -140,9 +159,6 @@ struct SignatureInformation + /// For PDF: the byte range doesn't cover the whole document. + bool bPartialDocumentSignature; + +- /// The certificate owner (aka subject). +- OUString ouSubject; +- + svl::crypto::SignatureMethodAlgorithm eAlgorithmID; + + SignatureInformation( sal_Int32 nId ) +diff --git a/svl/source/crypto/cryptosign.cxx b/svl/source/crypto/cryptosign.cxx +index 5a3f0271c40d..1b882bb89deb 100644 +--- a/svl/source/crypto/cryptosign.cxx ++++ b/svl/source/crypto/cryptosign.cxx +@@ -2097,8 +2097,12 @@ bool Signing::Verify(const std::vector& aData, + aDerCert[i] = pCertificate->derCert.data[i]; + OUStringBuffer aBuffer; + comphelper::Base64::encode(aBuffer, aDerCert); +- rInformation.ouX509Certificate = aBuffer.makeStringAndClear(); +- rInformation.ouSubject = OUString(pCertificate->subjectName, PL_strlen(pCertificate->subjectName), RTL_TEXTENCODING_UTF8); ++ SignatureInformation::X509Data temp; ++ temp.emplace_back(); ++ temp.back().X509Certificate = aBuffer.makeStringAndClear(); ++ temp.back().X509Subject = OUString(pCertificate->subjectName, PL_strlen(pCertificate->subjectName), RTL_TEXTENCODING_UTF8); ++ rInformation.X509Datas.clear(); ++ rInformation.X509Datas.emplace_back(temp); + } + + PRTime nSigningTime; +@@ -2277,8 +2281,12 @@ bool Signing::Verify(const std::vector& aData, + aDerCert[i] = pSignerCertContext->pbCertEncoded[i]; + OUStringBuffer aBuffer; + comphelper::Base64::encode(aBuffer, aDerCert); +- rInformation.ouX509Certificate = aBuffer.makeStringAndClear(); +- rInformation.ouSubject = GetSubjectName(pSignerCertContext); ++ SignatureInformation::X509Data temp; ++ temp.emplace_back(); ++ temp.back().X509Certificate = aBuffer.makeStringAndClear(); ++ temp.back().X509Subject = GetSubjectName(pSignerCertContext); ++ rInformation.X509Datas.clear(); ++ rInformation.X509Datas.emplace_back(temp); + } + + if (bNonDetached) +diff --git a/sw/source/core/edit/edfcol.cxx b/sw/source/core/edit/edfcol.cxx +index 2b49ee16ecc8..abbed5e40e94 100644 +--- a/sw/source/core/edit/edfcol.cxx ++++ b/sw/source/core/edit/edfcol.cxx +@@ -411,7 +411,8 @@ std::pair lcl_MakeParagraphSignatureFieldText(const SignatureDes + valid = valid + && aInfo.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED; + +- msg = SwResId(STR_SIGNED_BY) + ": " + aInfo.ouSubject + ", " + ++ assert(aInfo.GetSigningCertificate()); // it was valid ++ msg = SwResId(STR_SIGNED_BY) + ": " + aInfo.GetSigningCertificate()->X509Subject + ", " + + aDescr.msDate; + msg += (!aDescr.msUsage.isEmpty() ? (" (" + aDescr.msUsage + "): ") : OUString(": ")); + msg += (valid ? SwResId(STR_VALID) : SwResId(STR_INVALID)); +diff --git a/xmlsecurity/inc/biginteger.hxx b/xmlsecurity/inc/biginteger.hxx +index d07ecf45d8af..8b4d8a9143b5 100644 +--- a/xmlsecurity/inc/biginteger.hxx ++++ b/xmlsecurity/inc/biginteger.hxx +@@ -31,6 +31,9 @@ namespace xmlsecurity + { + XSECXMLSEC_DLLPUBLIC OUString bigIntegerToNumericString( const css::uno::Sequence< sal_Int8 >& serial ); + XSECXMLSEC_DLLPUBLIC css::uno::Sequence< sal_Int8 > numericStringToBigInteger ( const OUString& serialNumber ); ++ ++XSECXMLSEC_DLLPUBLIC bool EqualDistinguishedNames(OUString const& rName1, ++ OUString const& rName2); + } + + #endif +diff --git a/xmlsecurity/inc/xmlsignaturehelper.hxx b/xmlsecurity/inc/xmlsignaturehelper.hxx +index 0fcbd665251f..2456ddd437ec 100644 +--- a/xmlsecurity/inc/xmlsignaturehelper.hxx ++++ b/xmlsecurity/inc/xmlsignaturehelper.hxx +@@ -28,6 +28,9 @@ + #include "xmlsignaturehelper.hxx" + #include "xsecctl.hxx" + ++#include ++#include ++ + class DateTime; + class UriBindingHelper; + +@@ -93,6 +96,15 @@ public: + // After signing/verifying, get information about signatures + SignatureInformation GetSignatureInformation( sal_Int32 nSecurityId ) const; + SignatureInformations GetSignatureInformations() const; ++ /// ImplVerifySignature calls this to figure out which X509Data is the ++ /// signing certificate and update the internal state with the result. ++ /// @return ++ /// A sequence with the signing certificate at the back on success. ++ /// An empty sequence on failure. ++ std::vector> ++ CheckAndUpdateSignatureInformation( ++ css::uno::Reference const& xSecEnv, ++ SignatureInformation const& rInfo); + + // See XSecController for documentation + void StartMission(const css::uno::Reference& xSecurityContext); +diff --git a/xmlsecurity/inc/xsecctl.hxx b/xmlsecurity/inc/xsecctl.hxx +index 7baa219fb13c..7ce35cea22bf 100644 +--- a/xmlsecurity/inc/xsecctl.hxx ++++ b/xmlsecurity/inc/xsecctl.hxx +@@ -252,6 +252,7 @@ private: + /// Sets algorithm from . + void setSignatureMethod(svl::crypto::SignatureMethodAlgorithm eAlgorithmID); + void switchGpgSignature(); ++ bool haveReferenceForId(OUString const& rId) const; + void addReference( + const OUString& ouUri, + sal_Int32 nDigestID, +@@ -262,9 +263,13 @@ private: + sal_Int32 nDigestID ); + void setReferenceCount() const; + +- void setX509IssuerName( OUString const & ouX509IssuerName ); +- void setX509SerialNumber( OUString const & ouX509SerialNumber ); +- void setX509Certificate( OUString const & ouX509Certificate ); ++ void setX509Data( ++ std::vector> & rX509IssuerSerials, ++ std::vector const& rX509Certificates); ++ void setX509CertDigest( ++ OUString const& rCertDigest, sal_Int32 const nReferenceDigestID, ++ OUString const& rX509IssuerName, OUString const& rX509SerialNumber); ++ + void setSignatureValue( OUString const & ouSignatureValue ); + void setDigestValue( sal_Int32 nDigestID, OUString const & ouDigestValue ); + void setGpgKeyID( OUString const & ouKeyID ); +@@ -273,7 +278,6 @@ private: + + void setDate(OUString const& rId, OUString const& ouDate); + void setDescription(OUString const& rId, OUString const& rDescription); +- void setCertDigest(const OUString& rCertDigest); + void setValidSignatureImage(const OUString& rValidSigImg); + void setInvalidSignatureImage(const OUString& rInvalidSigImg); + void setSignatureLineId(const OUString& rSignatureLineId); +@@ -302,6 +306,9 @@ public: + + SignatureInformation getSignatureInformation( sal_Int32 nSecurityId ) const; + SignatureInformations getSignatureInformations() const; ++ /// only verify can figure out which X509Data is the signing certificate ++ void UpdateSignatureInformation(sal_Int32 nSecurityId, ++ std::vector const& rDatas); + + static void exportSignature( + const css::uno::Reference< css::xml::sax::XDocumentHandler >& xDocumentHandler, +diff --git a/xmlsecurity/source/component/documentdigitalsignatures.cxx b/xmlsecurity/source/component/documentdigitalsignatures.cxx +index 52cb938a8e0a..8a5bf37a0432 100644 +--- a/xmlsecurity/source/component/documentdigitalsignatures.cxx ++++ b/xmlsecurity/source/component/documentdigitalsignatures.cxx +@@ -510,30 +510,36 @@ DocumentDigitalSignatures::ImplVerifySignatures( + const SignatureInformation& rInfo = aSignInfos[n]; + css::security::DocumentSignatureInformation& rSigInfo = arInfos[n]; + +- if (rInfo.ouGpgCertificate.isEmpty()) // X.509 ++ if (!rInfo.X509Datas.empty()) // X.509 + { +- if (!rInfo.ouX509Certificate.isEmpty()) +- rSigInfo.Signer = xSecEnv->createCertificateFromAscii(rInfo.ouX509Certificate); +- if (!rSigInfo.Signer.is()) +- rSigInfo.Signer = xSecEnv->getCertificate( +- rInfo.ouX509IssuerName, +- xmlsecurity::numericStringToBigInteger(rInfo.ouX509SerialNumber)); +- +- // On Windows checking the certificate path is buggy. It does name matching (issuer, subject name) +- // to find the parent certificate. It does not take into account that there can be several certificates +- // with the same subject name. +- try ++ std::vector> certs( ++ rSignatureHelper.CheckAndUpdateSignatureInformation( ++ xSecEnv, rInfo)); ++ if (certs.empty()) + { +- rSigInfo.CertificateStatus = xSecEnv->verifyCertificate( +- rSigInfo.Signer, Sequence>()); ++ rSigInfo.CertificateStatus = css::security::CertificateValidity::INVALID; + } +- catch (SecurityException&) ++ else + { +- OSL_FAIL("Verification of certificate failed"); +- rSigInfo.CertificateStatus = css::security::CertificateValidity::INVALID; ++ rSigInfo.Signer = certs.back(); ++ // get only intermediates ++ certs.pop_back(); ++ // On Windows checking the certificate path is buggy. It does name matching (issuer, subject name) ++ // to find the parent certificate. It does not take into account that there can be several certificates ++ // with the same subject name. ++ try ++ { ++ rSigInfo.CertificateStatus = xSecEnv->verifyCertificate( ++ rSigInfo.Signer, comphelper::containerToSequence(certs)); ++ } ++ catch (SecurityException&) ++ { ++ SAL_WARN("xmlsecurity.comp", "Verification of certificate failed"); ++ rSigInfo.CertificateStatus = css::security::CertificateValidity::INVALID; ++ } + } + } +- else if (xGpgSecEnv.is()) // GPG ++ else if (!rInfo.ouGpgCertificate.isEmpty() && xGpgSecEnv.is()) // GPG + { + // TODO not ideal to retrieve cert by keyID, might + // collide, or PGPKeyID format might change - can't we +@@ -619,15 +625,19 @@ void DocumentDigitalSignatures::showCertificate( + } + + sal_Bool DocumentDigitalSignatures::isAuthorTrusted( +- const Reference< css::security::XCertificate >& Author ) ++ const Reference& xAuthor) + { +- OUString sSerialNum = xmlsecurity::bigIntegerToNumericString( Author->getSerialNumber() ); ++ if (!xAuthor.is()) ++ { ++ return false; ++ } ++ OUString sSerialNum = xmlsecurity::bigIntegerToNumericString(xAuthor->getSerialNumber()); + + Sequence< SvtSecurityOptions::Certificate > aTrustedAuthors = SvtSecurityOptions().GetTrustedAuthors(); + + return std::any_of(aTrustedAuthors.begin(), aTrustedAuthors.end(), +- [&Author, &sSerialNum](const SvtSecurityOptions::Certificate& rAuthor) { +- return ( rAuthor[0] == Author->getIssuerName() ) ++ [&xAuthor, &sSerialNum](const SvtSecurityOptions::Certificate& rAuthor) { ++ return xmlsecurity::EqualDistinguishedNames(rAuthor[0], xAuthor->getIssuerName()) + && ( rAuthor[1] == sSerialNum ); + }); + } +diff --git a/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx +index ef67c7167c04..18ccaf2d2166 100644 +--- a/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx ++++ b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx +@@ -588,7 +588,7 @@ void DigitalSignaturesDialog::ImplFillSignaturesBox() + if (!rInfo.ouGpgCertificate.isEmpty()) + aType = "OpenPGP"; + // XML based: XAdES or not. +- else if (!rInfo.ouCertDigest.isEmpty()) ++ else if (rInfo.GetSigningCertificate() && !rInfo.GetSigningCertificate()->CertDigest.isEmpty()) + aType = "XAdES"; + else + aType = "XML-DSig"; +@@ -700,8 +700,8 @@ uno::Reference DigitalSignaturesDialog::getCertificate(c + uno::Reference xCert; + + //First we try to get the certificate which is embedded in the XML Signature +- if (xSecEnv.is() && !rInfo.ouX509Certificate.isEmpty()) +- xCert = xSecEnv->createCertificateFromAscii(rInfo.ouX509Certificate); ++ if (xSecEnv.is() && rInfo.GetSigningCertificate() && !rInfo.GetSigningCertificate()->X509Certificate.isEmpty()) ++ xCert = xSecEnv->createCertificateFromAscii(rInfo.GetSigningCertificate()->X509Certificate); + else { + //There must be an embedded certificate because we use it to get the + //issuer name. We cannot use /Signature/KeyInfo/X509Data/X509IssuerName +@@ -713,9 +713,12 @@ uno::Reference DigitalSignaturesDialog::getCertificate(c + } + + //In case there is no embedded certificate we try to get it from a local store +- if (!xCert.is() && xSecEnv.is()) +- xCert = xSecEnv->getCertificate( rInfo.ouX509IssuerName, xmlsecurity::numericStringToBigInteger( rInfo.ouX509SerialNumber ) ); +- if (!xCert.is() && xGpgSecEnv.is()) ++ if (!xCert.is() && xSecEnv.is() && rInfo.GetSigningCertificate()) ++ { ++ xCert = xSecEnv->getCertificate(rInfo.GetSigningCertificate()->X509IssuerName, ++ xmlsecurity::numericStringToBigInteger(rInfo.GetSigningCertificate()->X509SerialNumber)); ++ } ++ if (!xCert.is() && xGpgSecEnv.is() && !rInfo.ouGpgKeyID.isEmpty()) + xCert = xGpgSecEnv->getCertificate( rInfo.ouGpgKeyID, xmlsecurity::numericStringToBigInteger("") ); + + SAL_WARN_IF( !xCert.is(), "xmlsecurity.dialogs", "Certificate not found and can't be created!" ); +diff --git a/xmlsecurity/source/helper/documentsignaturehelper.cxx b/xmlsecurity/source/helper/documentsignaturehelper.cxx +index 482ae6cc4126..ddff308ee52f 100644 +--- a/xmlsecurity/source/helper/documentsignaturehelper.cxx ++++ b/xmlsecurity/source/helper/documentsignaturehelper.cxx +@@ -492,6 +492,29 @@ void DocumentSignatureHelper::writeDigestMethod( + xDocumentHandler->endElement("DigestMethod"); + } + ++static void WriteXadesCert( ++ uno::Reference const& xDocumentHandler, ++ SignatureInformation::X509CertInfo const& rCertInfo) ++{ ++ xDocumentHandler->startElement("xd:Cert", uno::Reference(new SvXMLAttributeList())); ++ xDocumentHandler->startElement("xd:CertDigest", uno::Reference(new SvXMLAttributeList())); ++ DocumentSignatureHelper::writeDigestMethod(xDocumentHandler); ++ xDocumentHandler->startElement("DigestValue", uno::Reference(new SvXMLAttributeList())); ++ assert(!rCertInfo.CertDigest.isEmpty()); ++ xDocumentHandler->characters(rCertInfo.CertDigest); ++ xDocumentHandler->endElement("DigestValue"); ++ xDocumentHandler->endElement("xd:CertDigest"); ++ xDocumentHandler->startElement("xd:IssuerSerial", uno::Reference(new SvXMLAttributeList())); ++ xDocumentHandler->startElement("X509IssuerName", uno::Reference(new SvXMLAttributeList())); ++ xDocumentHandler->characters(rCertInfo.X509IssuerName); ++ xDocumentHandler->endElement("X509IssuerName"); ++ xDocumentHandler->startElement("X509SerialNumber", uno::Reference(new SvXMLAttributeList())); ++ xDocumentHandler->characters(rCertInfo.X509SerialNumber); ++ xDocumentHandler->endElement("X509SerialNumber"); ++ xDocumentHandler->endElement("xd:IssuerSerial"); ++ xDocumentHandler->endElement("xd:Cert"); ++} ++ + void DocumentSignatureHelper::writeSignedProperties( + const uno::Reference& xDocumentHandler, + const SignatureInformation& signatureInfo, +@@ -508,26 +531,26 @@ void DocumentSignatureHelper::writeSignedProperties( + xDocumentHandler->characters(sDate); + xDocumentHandler->endElement("xd:SigningTime"); + xDocumentHandler->startElement("xd:SigningCertificate", uno::Reference(new SvXMLAttributeList())); +- xDocumentHandler->startElement("xd:Cert", uno::Reference(new SvXMLAttributeList())); +- xDocumentHandler->startElement("xd:CertDigest", uno::Reference(new SvXMLAttributeList())); +- writeDigestMethod(xDocumentHandler); +- +- xDocumentHandler->startElement("DigestValue", uno::Reference(new SvXMLAttributeList())); +- // TODO: this is empty for gpg signatures currently +- //assert(!signatureInfo.ouCertDigest.isEmpty()); +- xDocumentHandler->characters(signatureInfo.ouCertDigest); +- xDocumentHandler->endElement("DigestValue"); +- +- xDocumentHandler->endElement("xd:CertDigest"); +- xDocumentHandler->startElement("xd:IssuerSerial", uno::Reference(new SvXMLAttributeList())); +- xDocumentHandler->startElement("X509IssuerName", uno::Reference(new SvXMLAttributeList())); +- xDocumentHandler->characters(signatureInfo.ouX509IssuerName); +- xDocumentHandler->endElement("X509IssuerName"); +- xDocumentHandler->startElement("X509SerialNumber", uno::Reference(new SvXMLAttributeList())); +- xDocumentHandler->characters(signatureInfo.ouX509SerialNumber); +- xDocumentHandler->endElement("X509SerialNumber"); +- xDocumentHandler->endElement("xd:IssuerSerial"); +- xDocumentHandler->endElement("xd:Cert"); ++ assert(signatureInfo.GetSigningCertificate() || !signatureInfo.ouGpgKeyID.isEmpty()); ++ if (signatureInfo.GetSigningCertificate()) ++ { ++ // how should this deal with multiple X509Data elements? ++ // for now, let's write all of the certificates ... ++ for (auto const& rData : signatureInfo.X509Datas) ++ { ++ for (auto const& it : rData) ++ { ++ WriteXadesCert(xDocumentHandler, it); ++ } ++ } ++ } ++ else ++ { ++ // for PGP, write empty mandatory X509IssuerName, X509SerialNumber ++ SignatureInformation::X509CertInfo temp; ++ temp.CertDigest = signatureInfo.ouGpgKeyID; ++ WriteXadesCert(xDocumentHandler, temp); ++ } + xDocumentHandler->endElement("xd:SigningCertificate"); + xDocumentHandler->startElement("xd:SignaturePolicyIdentifier", uno::Reference(new SvXMLAttributeList())); + xDocumentHandler->startElement("xd:SignaturePolicyImplied", uno::Reference(new SvXMLAttributeList())); +diff --git a/xmlsecurity/source/helper/documentsignaturemanager.cxx b/xmlsecurity/source/helper/documentsignaturemanager.cxx +index a0e674c3bd1b..aa08dc5c499e 100644 +--- a/xmlsecurity/source/helper/documentsignaturemanager.cxx ++++ b/xmlsecurity/source/helper/documentsignaturemanager.cxx +@@ -585,6 +585,18 @@ void DocumentSignatureManager::read(bool bUseTempStream, bool bCacheLastSignatur + bCacheLastSignature); + maSignatureHelper.EndMission(); + ++ // this parses the XML independently from ImplVerifySignatures() - check ++ // certificates here too ... ++ for (auto const& it : maSignatureHelper.GetSignatureInformations()) ++ { ++ if (!it.X509Datas.empty()) ++ { ++ uno::Reference const xSecEnv( ++ getSecurityEnvironment()); ++ getSignatureHelper().CheckAndUpdateSignatureInformation(xSecEnv, it); ++ } ++ } ++ + maCurrentSignatureInformations = maSignatureHelper.GetSignatureInformations(); + } + else +diff --git a/xmlsecurity/source/helper/ooxmlsecexporter.cxx b/xmlsecurity/source/helper/ooxmlsecexporter.cxx +index cf87d6e1ad17..cae2ef3f5b16 100644 +--- a/xmlsecurity/source/helper/ooxmlsecexporter.cxx ++++ b/xmlsecurity/source/helper/ooxmlsecexporter.cxx +@@ -194,12 +194,23 @@ void OOXMLSecExporter::Impl::writeSignatureValue() + + void OOXMLSecExporter::Impl::writeKeyInfo() + { +- m_xDocumentHandler->startElement("KeyInfo", uno::Reference(new SvXMLAttributeList())); +- m_xDocumentHandler->startElement("X509Data", uno::Reference(new SvXMLAttributeList())); +- m_xDocumentHandler->startElement("X509Certificate", uno::Reference(new SvXMLAttributeList())); +- m_xDocumentHandler->characters(m_rInformation.ouX509Certificate); +- m_xDocumentHandler->endElement("X509Certificate"); +- m_xDocumentHandler->endElement("X509Data"); ++ m_xDocumentHandler->startElement( ++ "KeyInfo", uno::Reference(new SvXMLAttributeList())); ++ assert(m_rInformation.GetSigningCertificate()); ++ for (auto const& rData : m_rInformation.X509Datas) ++ { ++ m_xDocumentHandler->startElement( ++ "X509Data", uno::Reference(new SvXMLAttributeList())); ++ for (auto const& it : rData) ++ { ++ m_xDocumentHandler->startElement( ++ "X509Certificate", ++ uno::Reference(new SvXMLAttributeList())); ++ m_xDocumentHandler->characters(it.X509Certificate); ++ m_xDocumentHandler->endElement("X509Certificate"); ++ } ++ m_xDocumentHandler->endElement("X509Data"); ++ } + m_xDocumentHandler->endElement("KeyInfo"); + } + +diff --git a/xmlsecurity/source/helper/ooxmlsecparser.cxx b/xmlsecurity/source/helper/ooxmlsecparser.cxx +index a200de60c07a..a25872fc057d 100644 +--- a/xmlsecurity/source/helper/ooxmlsecparser.cxx ++++ b/xmlsecurity/source/helper/ooxmlsecparser.cxx +@@ -185,9 +185,22 @@ void SAL_CALL OOXMLSecParser::endElement(const OUString& rName) + m_pXSecController->setSignatureValue(m_aSignatureValue); + m_bInSignatureValue = false; + } ++ else if (rName == "X509Data") ++ { ++ std::vector> X509IssuerSerials; ++ std::vector X509Certificates; ++ if (!m_aX509Certificate.isEmpty()) ++ { ++ X509Certificates.emplace_back(m_aX509Certificate); ++ } ++ if (!m_aX509IssuerName.isEmpty() && !m_aX509SerialNumber.isEmpty()) ++ { ++ X509IssuerSerials.emplace_back(m_aX509IssuerName, m_aX509SerialNumber); ++ } ++ m_pXSecController->setX509Data(X509IssuerSerials, X509Certificates); ++ } + else if (rName == "X509Certificate") + { +- m_pXSecController->setX509Certificate(m_aX509Certificate); + m_bInX509Certificate = false; + } + else if (rName == "mdssi:Value") +@@ -202,17 +215,18 @@ void SAL_CALL OOXMLSecParser::endElement(const OUString& rName) + } + else if (rName == "X509IssuerName") + { +- m_pXSecController->setX509IssuerName(m_aX509IssuerName); + m_bInX509IssuerName = false; + } + else if (rName == "X509SerialNumber") + { +- m_pXSecController->setX509SerialNumber(m_aX509SerialNumber); + m_bInX509SerialNumber = false; + } ++ else if (rName == "xd:Cert") ++ { ++ m_pXSecController->setX509CertDigest(m_aCertDigest, css::xml::crypto::DigestID::SHA1, m_aX509IssuerName, m_aX509SerialNumber); ++ } + else if (rName == "xd:CertDigest") + { +- m_pXSecController->setCertDigest(m_aCertDigest); + m_bInCertDigest = false; + } + else if (rName == "Object") +diff --git a/xmlsecurity/source/helper/pdfsignaturehelper.cxx b/xmlsecurity/source/helper/pdfsignaturehelper.cxx +index f10f29c61840..8a90b73901db 100644 +--- a/xmlsecurity/source/helper/pdfsignaturehelper.cxx ++++ b/xmlsecurity/source/helper/pdfsignaturehelper.cxx +@@ -82,8 +82,12 @@ PDFSignatureHelper::GetDocumentSignatureInformations( + security::DocumentSignatureInformation& rExternal = aRet[i]; + rExternal.SignatureIsValid + = rInternal.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED; +- if (!rInternal.ouX509Certificate.isEmpty()) +- rExternal.Signer = xSecEnv->createCertificateFromAscii(rInternal.ouX509Certificate); ++ if (rInternal.GetSigningCertificate() ++ && !rInternal.GetSigningCertificate()->X509Certificate.isEmpty()) ++ { ++ rExternal.Signer = xSecEnv->createCertificateFromAscii( ++ rInternal.GetSigningCertificate()->X509Certificate); ++ } + rExternal.PartialDocumentSignature = rInternal.bPartialDocumentSignature; + + // Verify certificate. +diff --git a/xmlsecurity/source/helper/xmlsignaturehelper.cxx b/xmlsecurity/source/helper/xmlsignaturehelper.cxx +index 22c056e70da1..bcb79039e342 100644 +--- a/xmlsecurity/source/helper/xmlsignaturehelper.cxx ++++ b/xmlsecurity/source/helper/xmlsignaturehelper.cxx +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + + #include + +@@ -45,6 +46,8 @@ + #include + #include + ++#include ++ + #define NS_DOCUMENTSIGNATURES "http://openoffice.org/2004/documentsignatures" + #define NS_DOCUMENTSIGNATURES_ODF_1_2 "urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0" + #define OOXML_SIGNATURE_ORIGIN "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin" +@@ -547,4 +550,162 @@ void XMLSignatureHelper::CreateAndWriteOOXMLSignature(const uno::ReferenceendDocument(); + } + ++/** check this constraint from xmldsig-core 4.5.4: ++ ++ All certificates appearing in an X509Data element MUST relate to the ++ validation key by either containing it or being part of a certification ++ chain that terminates in a certificate containing the validation key. ++ */ ++static auto CheckX509Data( ++ uno::Reference const& xSecEnv, ++ std::vector const& rX509CertInfos, ++ std::vector> & rCerts, ++ std::vector & rSorted) -> bool ++{ ++ assert(rCerts.empty()); ++ assert(rSorted.empty()); ++ if (rX509CertInfos.empty()) ++ { ++ SAL_WARN("xmlsecurity.comp", "no X509Data"); ++ return false; ++ } ++ std::vector> certs; ++ for (SignatureInformation::X509CertInfo const& it : rX509CertInfos) ++ { ++ if (!it.X509Certificate.isEmpty()) ++ { ++ certs.emplace_back(xSecEnv->createCertificateFromAscii(it.X509Certificate)); ++ } ++ else ++ { ++ certs.emplace_back(xSecEnv->getCertificate( ++ it.X509IssuerName, ++ xmlsecurity::numericStringToBigInteger(it.X509SerialNumber))); ++ } ++ if (!certs.back().is()) ++ { ++ SAL_WARN("xmlsecurity.comp", "X509Data cannot be parsed"); ++ return false; ++ } ++ } ++ ++ // first, search one whose issuer isn't in the list, or a self-signed one ++ std::optional start; ++ for (size_t i = 0; i < certs.size(); ++i) ++ { ++ for (size_t j = 0; ; ++j) ++ { ++ if (j == certs.size()) ++ { ++ if (start) ++ { ++ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate has no issuer but already have start of chain: " << certs[i]->getSubjectName()); ++ return false; ++ } ++ start = i; // issuer isn't in the list ++ break; ++ } ++ if (xmlsecurity::EqualDistinguishedNames(certs[i]->getIssuerName(), certs[j]->getSubjectName())) ++ { ++ if (i == j) // self signed ++ { ++ if (start) ++ { ++ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate is self-signed but already have start of chain: " << certs[i]->getSubjectName()); ++ return false; ++ } ++ start = i; ++ } ++ break; ++ } ++ } ++ } ++ std::vector chain; ++ if (!start) ++ { ++ // this can only be a cycle? ++ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: cycle detected"); ++ return false; ++ } ++ chain.emplace_back(*start); ++ ++ // second, check that there is a chain, no tree or cycle... ++ for (size_t i = 0; i < certs.size(); ++i) ++ { ++ assert(chain.size() == i + 1); ++ for (size_t j = 0; j < certs.size(); ++j) ++ { ++ if (chain[i] != j) ++ { ++ if (xmlsecurity::EqualDistinguishedNames( ++ certs[chain[i]]->getSubjectName(), certs[j]->getIssuerName())) ++ { ++ if (chain.size() != i + 1) // already found issuee? ++ { ++ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate issued 2 others: " << certs[chain[i]]->getSubjectName()); ++ return false; ++ } ++ chain.emplace_back(j); ++ } ++ } ++ } ++ if (i == certs.size() - 1) ++ { // last one: must be a leaf ++ if (chain.size() != i + 1) ++ { ++ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate in cycle: " << certs[chain[i]]->getSubjectName()); ++ return false; ++ } ++ } ++ else if (chain.size() != i + 2) ++ { // not issuer of another? ++ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate issued 0 others: " << certs[chain[i]]->getSubjectName()); ++ return false; ++ } ++ } ++ ++ // success ++ assert(chain.size() == rX509CertInfos.size()); ++ for (auto const& it : chain) ++ { ++ rSorted.emplace_back(rX509CertInfos[it]); ++ rCerts.emplace_back(certs[it]); ++ } ++ return true; ++} ++ ++std::vector> ++XMLSignatureHelper::CheckAndUpdateSignatureInformation( ++ uno::Reference const& xSecEnv, ++ SignatureInformation const& rInfo) ++{ ++ // if the check fails, it's not possible to determine which X509Data ++ // contained the signing certificate - the UI cannot display something ++ // useful in this case, so prevent anything misleading by clearing the ++ // X509Datas. ++ ++ std::vector> certs; ++ std::vector datas; ++ // TODO: for now, just merge all X509Datas together for checking... ++ // (this will probably break round-trip of signature with multiple X509Data, ++ // no idea if that is a problem) ++ SignatureInformation::X509Data temp; ++ SignatureInformation::X509Data tempResult; ++ for (auto const& rData : rInfo.X509Datas) ++ { ++ for (auto const& it : rData) ++ { ++ temp.emplace_back(it); ++ } ++ } ++ if (CheckX509Data(xSecEnv, temp, certs, tempResult)) ++ { ++ datas.emplace_back(tempResult); ++ } ++ ++ // rInfo is a copy, update the original ++ mpXSecController->UpdateSignatureInformation(rInfo.nSecurityId, datas); ++ return certs; ++} ++ + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ +diff --git a/xmlsecurity/source/helper/xsecctl.cxx b/xmlsecurity/source/helper/xsecctl.cxx +index 8d5ea68c768b..0901d05dbdbd 100644 +--- a/xmlsecurity/source/helper/xsecctl.cxx ++++ b/xmlsecurity/source/helper/xsecctl.cxx +@@ -737,8 +737,8 @@ void XSecController::exportSignature( + /* Write keyid element */ + xDocumentHandler->startElement( + "PGPKeyID", +- cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); +- xDocumentHandler->characters( signatureInfo.ouCertDigest ); ++ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); ++ xDocumentHandler->characters(signatureInfo.ouGpgKeyID); + xDocumentHandler->endElement( "PGPKeyID" ); + + /* Write PGPKeyPacket element */ +@@ -762,43 +762,50 @@ void XSecController::exportSignature( + } + else + { +- /* Write X509Data element */ +- xDocumentHandler->startElement( +- "X509Data", +- cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); ++ assert(signatureInfo.GetSigningCertificate()); ++ for (auto const& rData : signatureInfo.X509Datas) + { +- /* Write X509IssuerSerial element */ ++ /* Write X509Data element */ + xDocumentHandler->startElement( +- "X509IssuerSerial", +- cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); +- { +- /* Write X509IssuerName element */ +- xDocumentHandler->startElement( +- "X509IssuerName", +- cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); +- xDocumentHandler->characters( signatureInfo.ouX509IssuerName ); +- xDocumentHandler->endElement( "X509IssuerName" ); +- +- /* Write X509SerialNumber element */ +- xDocumentHandler->startElement( +- "X509SerialNumber", +- cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); +- xDocumentHandler->characters( signatureInfo.ouX509SerialNumber ); +- xDocumentHandler->endElement( "X509SerialNumber" ); +- } +- xDocumentHandler->endElement( "X509IssuerSerial" ); +- +- /* Write X509Certificate element */ +- if (!signatureInfo.ouX509Certificate.isEmpty()) ++ "X509Data", ++ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); + { +- xDocumentHandler->startElement( +- "X509Certificate", +- cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); +- xDocumentHandler->characters( signatureInfo.ouX509Certificate ); +- xDocumentHandler->endElement( "X509Certificate" ); ++ for (auto const& it : rData) ++ { ++ /* Write X509IssuerSerial element */ ++ xDocumentHandler->startElement( ++ "X509IssuerSerial", ++ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); ++ { ++ /* Write X509IssuerName element */ ++ xDocumentHandler->startElement( ++ "X509IssuerName", ++ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); ++ xDocumentHandler->characters(it.X509IssuerName); ++ xDocumentHandler->endElement( "X509IssuerName" ); ++ ++ /* Write X509SerialNumber element */ ++ xDocumentHandler->startElement( ++ "X509SerialNumber", ++ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); ++ xDocumentHandler->characters(it.X509SerialNumber); ++ xDocumentHandler->endElement( "X509SerialNumber" ); ++ } ++ xDocumentHandler->endElement( "X509IssuerSerial" ); ++ ++ /* Write X509Certificate element */ ++ if (!it.X509Certificate.isEmpty()) ++ { ++ xDocumentHandler->startElement( ++ "X509Certificate", ++ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); ++ xDocumentHandler->characters(it.X509Certificate); ++ xDocumentHandler->endElement( "X509Certificate" ); ++ } ++ } + } ++ xDocumentHandler->endElement( "X509Data" ); + } +- xDocumentHandler->endElement( "X509Data" ); + } + } + xDocumentHandler->endElement( "KeyInfo" ); +@@ -917,6 +924,15 @@ void XSecController::exportOOXMLSignature(const uno::Reference& + aExporter.writeSignature(); + } + ++void XSecController::UpdateSignatureInformation(sal_Int32 const nSecurityId, ++ std::vector const& rDatas) ++{ ++ SignatureInformation aInf( 0 ); ++ int const nIndex = findSignatureInfor(nSecurityId); ++ assert(nIndex != -1); // nothing should touch this between parsing and verify ++ m_vInternalSignatureInformations[nIndex].signatureInfor.X509Datas = rDatas; ++} ++ + SignatureInformation XSecController::getSignatureInformation( sal_Int32 nSecurityId ) const + { + SignatureInformation aInf( 0 ); +diff --git a/xmlsecurity/source/helper/xsecparser.cxx b/xmlsecurity/source/helper/xsecparser.cxx +index 9cc9312b4d9f..595a02ed687d 100644 +--- a/xmlsecurity/source/helper/xsecparser.cxx ++++ b/xmlsecurity/source/helper/xsecparser.cxx +@@ -99,6 +99,42 @@ auto XSecParser::Context::CreateChildContext( + return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); + } + ++/** ++note: anything in ds:Object should be trusted *only* if there is a ds:Reference ++ to it so it is signed (exception: the xades:EncapsulatedX509Certificate). ++ ds:SignedInfo precedes all ds:Object. ++ ++ There may be multiple ds:Signature for purpose of counter-signatures ++ but the way XAdES describes these, only the ds:SignatureValue element ++ would be referenced, so requiring a ds:Reference for anything in ++ ds:Object shouldn't cause issues. ++ */ ++class XSecParser::ReferencedContextImpl ++ : public XSecParser::Context ++{ ++ protected: ++ bool m_isReferenced; ++ ++ public: ++ ReferencedContextImpl(XSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_isReferenced(isReferenced) ++ { ++ } ++ ++ OUString CheckIdAttrReferenced(css::uno::Reference const& xAttrs) ++ { ++ OUString const id(m_rParser.HandleIdAttr(xAttrs)); ++ if (!id.isEmpty() && m_rParser.m_pXSecController->haveReferenceForId(id)) ++ { ++ m_isReferenced = true; ++ } ++ return id; ++ } ++}; ++ + class XSecParser::LoPGPOwnerContext + : public XSecParser::Context + { +@@ -211,23 +247,20 @@ class XSecParser::DsX509CertificateContext + : public XSecParser::Context + { + private: +- OUString m_Value; ++ OUString & m_rValue; + + public: + DsX509CertificateContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rValue) + : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rValue(rValue) + { + } + +- virtual void EndElement() override +- { +- m_rParser.m_pXSecController->setX509Certificate(m_Value); +- } +- + virtual void Characters(OUString const& rChars) override + { +- m_Value += rChars; ++ m_rValue += rChars; + } + }; + +@@ -235,23 +268,20 @@ class XSecParser::DsX509SerialNumberContext + : public XSecParser::Context + { + private: +- OUString m_Value; ++ OUString & m_rValue; + + public: + DsX509SerialNumberContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rValue) + : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rValue(rValue) + { + } + +- virtual void EndElement() override +- { +- m_rParser.m_pXSecController->setX509SerialNumber(m_Value); +- } +- + virtual void Characters(OUString const& rChars) override + { +- m_Value += rChars; ++ m_rValue += rChars; + } + }; + +@@ -259,33 +289,37 @@ class XSecParser::DsX509IssuerNameContext + : public XSecParser::Context + { + private: +- OUString m_Value; ++ OUString & m_rValue; + + public: + DsX509IssuerNameContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rValue) + : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rValue(rValue) + { + } + +- virtual void EndElement() override +- { +- m_rParser.m_pXSecController->setX509IssuerName(m_Value); +- } +- + virtual void Characters(OUString const& rChars) override + { +- m_Value += rChars; ++ m_rValue += rChars; + } + }; + + class XSecParser::DsX509IssuerSerialContext + : public XSecParser::Context + { ++ private: ++ OUString & m_rX509IssuerName; ++ OUString & m_rX509SerialNumber; ++ + public: + DsX509IssuerSerialContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rIssuerName, OUString & rSerialNumber) + : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rX509IssuerName(rIssuerName) ++ , m_rX509SerialNumber(rSerialNumber) + { + } + +@@ -295,20 +329,27 @@ class XSecParser::DsX509IssuerSerialContext + { + if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerName") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_rX509IssuerName); + } + if (nNamespace == XML_NAMESPACE_DS && rName == "X509SerialNumber") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_rX509SerialNumber); + } + // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL + return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } + }; + ++/// can't be sure what is supposed to happen here because the spec is clear as mud + class XSecParser::DsX509DataContext + : public XSecParser::Context + { ++ private: ++ // sigh... "No ordering is implied by the above constraints." ++ // so store the ball of mud in vectors and try to figure it out later. ++ std::vector> m_X509IssuerSerials; ++ std::vector m_X509Certificates; ++ + public: + DsX509DataContext(XSecParser & rParser, + std::unique_ptr pOldNamespaceMap) +@@ -316,17 +357,24 @@ class XSecParser::DsX509DataContext + { + } + ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->setX509Data(m_X509IssuerSerials, m_X509Certificates); ++ } ++ + virtual std::unique_ptr CreateChildContext( + std::unique_ptr pOldNamespaceMap, + sal_uInt16 const nNamespace, OUString const& rName) override + { + if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerSerial") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ m_X509IssuerSerials.emplace_back(); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerSerials.back().first, m_X509IssuerSerials.back().second); + } + if (nNamespace == XML_NAMESPACE_DS && rName == "X509Certificate") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ m_X509Certificates.emplace_back(); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_X509Certificates.back()); + } + // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL + return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); +@@ -791,21 +839,29 @@ class XSecParser::XadesUnsignedPropertiesContext + }; + + class XSecParser::LoSignatureLineIdContext +- : public XSecParser::Context ++ : public XSecParser::ReferencedContextImpl + { + private: + OUString m_Value; + + public: + LoSignatureLineIdContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) +- : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) + { + } + + virtual void EndElement() override + { +- m_rParser.m_pXSecController->setSignatureLineId(m_Value); ++ if (m_isReferenced) ++ { ++ m_rParser.m_pXSecController->setSignatureLineId(m_Value); ++ } ++ else ++ { ++ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineId"); ++ } + } + + virtual void Characters(OUString const& rChars) override +@@ -815,21 +871,29 @@ class XSecParser::LoSignatureLineIdContext + }; + + class XSecParser::LoSignatureLineValidImageContext +- : public XSecParser::Context ++ : public XSecParser::ReferencedContextImpl + { + private: + OUString m_Value; + + public: + LoSignatureLineValidImageContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) +- : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) + { + } + + virtual void EndElement() override + { +- m_rParser.m_pXSecController->setValidSignatureImage(m_Value); ++ if (m_isReferenced) ++ { ++ m_rParser.m_pXSecController->setValidSignatureImage(m_Value); ++ } ++ else ++ { ++ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineValidImage"); ++ } + } + + virtual void Characters(OUString const& rChars) override +@@ -839,21 +903,29 @@ class XSecParser::LoSignatureLineValidImageContext + }; + + class XSecParser::LoSignatureLineInvalidImageContext +- : public XSecParser::Context ++ : public XSecParser::ReferencedContextImpl + { + private: + OUString m_Value; + + public: + LoSignatureLineInvalidImageContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) +- : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) + { + } + + virtual void EndElement() override + { +- m_rParser.m_pXSecController->setInvalidSignatureImage(m_Value); ++ if (m_isReferenced) ++ { ++ m_rParser.m_pXSecController->setInvalidSignatureImage(m_Value); ++ } ++ else ++ { ++ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineInvalidImage"); ++ } + } + + virtual void Characters(OUString const& rChars) override +@@ -863,12 +935,13 @@ class XSecParser::LoSignatureLineInvalidImageContext + }; + + class XSecParser::LoSignatureLineContext +- : public XSecParser::Context ++ : public XSecParser::ReferencedContextImpl + { + public: + LoSignatureLineContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) +- : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) + { + } + +@@ -878,15 +951,15 @@ class XSecParser::LoSignatureLineContext + { + if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLineId") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + } + if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLineValidImage") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + } + if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLineInvalidImage") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + } + return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } +@@ -896,70 +969,88 @@ class XSecParser::XadesCertDigestContext + : public XSecParser::Context + { + private: +- OUString m_Value; +- sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1; ++ OUString & m_rDigestValue; ++ sal_Int32 & m_rReferenceDigestID; + + public: + XadesCertDigestContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rDigestValue, sal_Int32 & rReferenceDigestID) + : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rDigestValue(rDigestValue) ++ , m_rReferenceDigestID(rReferenceDigestID) + { + } + +- virtual void EndElement() override +- { +- m_rParser.m_pXSecController->setCertDigest(m_Value/* FIXME , m_nReferenceDigestID*/); +- } +- + virtual std::unique_ptr CreateChildContext( + std::unique_ptr pOldNamespaceMap, + sal_uInt16 const nNamespace, OUString const& rName) override + { + if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_nReferenceDigestID); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_rReferenceDigestID); + } + if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_Value); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_rDigestValue); + } + return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } + }; + + class XSecParser::XadesCertContext +- : public XSecParser::Context ++ : public XSecParser::ReferencedContextImpl + { ++ private: ++ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1; ++ OUString m_CertDigest; ++ OUString m_X509IssuerName; ++ OUString m_X509SerialNumber; ++ + public: + XadesCertContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) +- : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) + { + } + ++ virtual void EndElement() override ++ { ++ if (m_isReferenced) ++ { ++ m_rParser.m_pXSecController->setX509CertDigest(m_CertDigest, m_nReferenceDigestID, m_X509IssuerName, m_X509SerialNumber); ++ } ++ else ++ { ++ SAL_INFO("xmlsecurity.helper", "ignoring unsigned xades:Cert"); ++ } ++ } ++ + virtual std::unique_ptr CreateChildContext( + std::unique_ptr pOldNamespaceMap, + sal_uInt16 const nNamespace, OUString const& rName) override + { + if (nNamespace == XML_NAMESPACE_XADES132 && rName == "CertDigest") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_CertDigest, m_nReferenceDigestID); + } + if (nNamespace == XML_NAMESPACE_XADES132 && rName == "IssuerSerial") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerName, m_X509SerialNumber); + } + return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } + }; + + class XSecParser::XadesSigningCertificateContext +- : public XSecParser::Context ++ : public XSecParser::ReferencedContextImpl + { + public: + XadesSigningCertificateContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) +- : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) + { + } + +@@ -969,28 +1060,36 @@ class XSecParser::XadesSigningCertificateContext + { + if (nNamespace == XML_NAMESPACE_XADES132 && rName == "Cert") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + } + return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } + }; + + class XSecParser::XadesSigningTimeContext +- : public XSecParser::Context ++ : public XSecParser::ReferencedContextImpl + { + private: + OUString m_Value; + + public: + XadesSigningTimeContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) +- : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) + { + } + + virtual void EndElement() override + { +- m_rParser.m_pXSecController->setDate("", m_Value); ++ if (m_isReferenced) ++ { ++ m_rParser.m_pXSecController->setDate("", m_Value); ++ } ++ else ++ { ++ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SigningTime"); ++ } + } + + virtual void Characters(OUString const& rChars) override +@@ -1000,19 +1099,20 @@ class XSecParser::XadesSigningTimeContext + }; + + class XSecParser::XadesSignedSignaturePropertiesContext +- : public XSecParser::Context ++ : public XSecParser::ReferencedContextImpl + { + public: + XadesSignedSignaturePropertiesContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) +- : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) + { + } + + virtual void StartElement( + css::uno::Reference const& xAttrs) override + { +- m_rParser.HandleIdAttr(xAttrs); ++ CheckIdAttrReferenced(xAttrs); + } + + virtual std::unique_ptr CreateChildContext( +@@ -1021,15 +1121,15 @@ class XSecParser::XadesSignedSignaturePropertiesContext + { + if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningTime") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + } + if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningCertificate") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + } + if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLine") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + } + // missing: xades:SignaturePolicyIdentifier, xades:SignatureProductionPlace, xades:SignerRole + return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); +@@ -1037,19 +1137,20 @@ class XSecParser::XadesSignedSignaturePropertiesContext + }; + + class XSecParser::XadesSignedPropertiesContext +- : public XSecParser::Context ++ : public XSecParser::ReferencedContextImpl + { + public: + XadesSignedPropertiesContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) +- : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) + { + } + + virtual void StartElement( + css::uno::Reference const& xAttrs) override + { +- m_rParser.HandleIdAttr(xAttrs); ++ CheckIdAttrReferenced(xAttrs); + } + + virtual std::unique_ptr CreateChildContext( +@@ -1058,7 +1159,7 @@ class XSecParser::XadesSignedPropertiesContext + { + if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedSignatureProperties") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + } + // missing: xades:SignedDataObjectProperties + return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); +@@ -1066,19 +1167,20 @@ class XSecParser::XadesSignedPropertiesContext + }; + + class XSecParser::XadesQualifyingPropertiesContext +- : public XSecParser::Context ++ : public XSecParser::ReferencedContextImpl + { + public: + XadesQualifyingPropertiesContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) +- : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) + { + } + + virtual void StartElement( + css::uno::Reference const& xAttrs) override + { +- m_rParser.HandleIdAttr(xAttrs); ++ CheckIdAttrReferenced(xAttrs); + } + + virtual std::unique_ptr CreateChildContext( +@@ -1087,7 +1189,7 @@ class XSecParser::XadesQualifyingPropertiesContext + { + if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedProperties") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + } + if (nNamespace == XML_NAMESPACE_XADES132 && rName == "UnsignedProperties") + { +@@ -1140,7 +1242,7 @@ class XSecParser::DcDescriptionContext + }; + + class XSecParser::DsSignaturePropertyContext +- : public XSecParser::Context ++ : public XSecParser::ReferencedContextImpl + { + private: + enum class SignatureProperty { Unknown, Date, Description }; +@@ -1150,30 +1252,38 @@ class XSecParser::DsSignaturePropertyContext + + public: + DsSignaturePropertyContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) +- : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) + { + } + + virtual void StartElement( + css::uno::Reference const& xAttrs) override + { +- m_Id = m_rParser.HandleIdAttr(xAttrs); ++ m_Id = CheckIdAttrReferenced(xAttrs); + } + + virtual void EndElement() override + { +- switch (m_Property) ++ if (m_isReferenced) ++ { ++ switch (m_Property) ++ { ++ case SignatureProperty::Unknown: ++ SAL_INFO("xmlsecurity.helper", "Unknown property in ds:Object ignored"); ++ break; ++ case SignatureProperty::Date: ++ m_rParser.m_pXSecController->setDate(m_Id, m_Value); ++ break; ++ case SignatureProperty::Description: ++ m_rParser.m_pXSecController->setDescription(m_Id, m_Value); ++ break; ++ } ++ } ++ else + { +- case SignatureProperty::Unknown: +- SAL_INFO("xmlsecurity.helper", "Unknown property in ds:Object ignored"); +- break; +- case SignatureProperty::Date: +- m_rParser.m_pXSecController->setDate(m_Id, m_Value); +- break; +- case SignatureProperty::Description: +- m_rParser.m_pXSecController->setDescription(m_Id, m_Value); +- break; ++ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureProperty"); + } + } + +@@ -1196,19 +1306,20 @@ class XSecParser::DsSignaturePropertyContext + }; + + class XSecParser::DsSignaturePropertiesContext +- : public XSecParser::Context ++ : public XSecParser::ReferencedContextImpl + { + public: + DsSignaturePropertiesContext(XSecParser & rParser, +- std::unique_ptr pOldNamespaceMap) +- : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) + { + } + + virtual void StartElement( + css::uno::Reference const& xAttrs) override + { +- m_rParser.HandleIdAttr(xAttrs); ++ CheckIdAttrReferenced(xAttrs); + } + + virtual std::unique_ptr CreateChildContext( +@@ -1217,26 +1328,27 @@ class XSecParser::DsSignaturePropertiesContext + { + if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperty") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + } + return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); + } + }; + + class XSecParser::DsObjectContext +- : public XSecParser::Context ++ : public XSecParser::ReferencedContextImpl + { + public: + DsObjectContext(XSecParser & rParser, + std::unique_ptr pOldNamespaceMap) +- : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ // init with "false" here - the Signature element can't be referenced by its child ++ : XSecParser::ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), false) + { + } + + virtual void StartElement( + css::uno::Reference const& xAttrs) override + { +- m_rParser.HandleIdAttr(xAttrs); ++ CheckIdAttrReferenced(xAttrs); + } + + virtual std::unique_ptr CreateChildContext( +@@ -1245,11 +1357,11 @@ class XSecParser::DsObjectContext + { + if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperties") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + } + if (nNamespace == XML_NAMESPACE_XADES132 && rName == "QualifyingProperties") + { +- return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + } + // missing: ds:Manifest + return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); +diff --git a/xmlsecurity/source/helper/xsecparser.hxx b/xmlsecurity/source/helper/xsecparser.hxx +index 7a0eb08bca28..7674bf28b84d 100644 +--- a/xmlsecurity/source/helper/xsecparser.hxx ++++ b/xmlsecurity/source/helper/xsecparser.hxx +@@ -56,6 +56,7 @@ public: + class Context; + private: + class UnknownContext; ++ class ReferencedContextImpl; + class LoPGPOwnerContext; + class DsPGPKeyPacketContext; + class DsPGPKeyIDContext; +diff --git a/xmlsecurity/source/helper/xsecsign.cxx b/xmlsecurity/source/helper/xsecsign.cxx +index 5ed23281f083..38a420a370aa 100644 +--- a/xmlsecurity/source/helper/xsecsign.cxx ++++ b/xmlsecurity/source/helper/xsecsign.cxx +@@ -197,6 +197,7 @@ void XSecController::signAStream( sal_Int32 securityId, const OUString& uri, boo + } + } + ++// note: this is called when creating a new signature from scratch + void XSecController::setX509Certificate( + sal_Int32 nSecurityId, + const OUString& ouX509IssuerName, +@@ -210,10 +211,13 @@ void XSecController::setX509Certificate( + if ( index == -1 ) + { + InternalSignatureInformation isi(nSecurityId, nullptr); +- isi.signatureInfor.ouX509IssuerName = ouX509IssuerName; +- isi.signatureInfor.ouX509SerialNumber = ouX509SerialNumber; +- isi.signatureInfor.ouX509Certificate = ouX509Cert; +- isi.signatureInfor.ouCertDigest = ouX509CertDigest; ++ isi.signatureInfor.X509Datas.clear(); ++ isi.signatureInfor.X509Datas.emplace_back(); ++ isi.signatureInfor.X509Datas.back().emplace_back(); ++ isi.signatureInfor.X509Datas.back().back().X509IssuerName = ouX509IssuerName; ++ isi.signatureInfor.X509Datas.back().back().X509SerialNumber = ouX509SerialNumber; ++ isi.signatureInfor.X509Datas.back().back().X509Certificate = ouX509Cert; ++ isi.signatureInfor.X509Datas.back().back().CertDigest = ouX509CertDigest; + isi.signatureInfor.eAlgorithmID = eAlgorithmID; + m_vInternalSignatureInformations.push_back( isi ); + } +@@ -221,16 +225,19 @@ void XSecController::setX509Certificate( + { + SignatureInformation &si + = m_vInternalSignatureInformations[index].signatureInfor; +- si.ouX509IssuerName = ouX509IssuerName; +- si.ouX509SerialNumber = ouX509SerialNumber; +- si.ouX509Certificate = ouX509Cert; +- si.ouCertDigest = ouX509CertDigest; ++ si.X509Datas.clear(); ++ si.X509Datas.emplace_back(); ++ si.X509Datas.back().emplace_back(); ++ si.X509Datas.back().back().X509IssuerName = ouX509IssuerName; ++ si.X509Datas.back().back().X509SerialNumber = ouX509SerialNumber; ++ si.X509Datas.back().back().X509Certificate = ouX509Cert; ++ si.X509Datas.back().back().CertDigest = ouX509CertDigest; + } + } + + void XSecController::setGpgCertificate( + sal_Int32 nSecurityId, +- const OUString& ouCertDigest, ++ const OUString& ouKeyDigest, + const OUString& ouCert, + const OUString& ouOwner) + { +@@ -241,16 +248,17 @@ void XSecController::setGpgCertificate( + InternalSignatureInformation isi(nSecurityId, nullptr); + isi.signatureInfor.ouGpgCertificate = ouCert; + isi.signatureInfor.ouGpgOwner = ouOwner; +- isi.signatureInfor.ouCertDigest = ouCertDigest; ++ isi.signatureInfor.ouGpgKeyID = ouKeyDigest; + m_vInternalSignatureInformations.push_back( isi ); + } + else + { + SignatureInformation &si + = m_vInternalSignatureInformations[index].signatureInfor; ++ si.X509Datas.clear(); // it is a PGP signature now + si.ouGpgCertificate = ouCert; + si.ouGpgOwner = ouOwner; +- si.ouCertDigest = ouCertDigest; ++ si.ouGpgKeyID = ouKeyDigest; + } + } + +diff --git a/xmlsecurity/source/helper/xsecverify.cxx b/xmlsecurity/source/helper/xsecverify.cxx +index 5f5840334254..1f0445c35c14 100644 +--- a/xmlsecurity/source/helper/xsecverify.cxx ++++ b/xmlsecurity/source/helper/xsecverify.cxx +@@ -22,6 +22,7 @@ + #include + #include "xsecparser.hxx" + #include "ooxmlsecparser.hxx" ++#include + #include + #include + #include +@@ -147,6 +148,25 @@ void XSecController::switchGpgSignature() + #endif + } + ++bool XSecController::haveReferenceForId(OUString const& rId) const ++{ ++ if (m_vInternalSignatureInformations.empty()) ++ { ++ SAL_INFO("xmlsecurity.helper","XSecController::haveReferenceForId: no signature"); ++ return false; ++ } ++ InternalSignatureInformation const& rIsi(m_vInternalSignatureInformations.back()); ++ for (SignatureReferenceInformation const& rSri : rIsi.signatureInfor.vSignatureReferenceInfors) ++ { ++ if (rSri.nType == SignatureReferenceType::SAMEDOCUMENT ++ && rSri.ouURI == rId) // ouUri has # stripped ++ { ++ return true; ++ } ++ } ++ return false; ++} ++ + void XSecController::addReference( const OUString& ouUri, sal_Int32 nDigestID, const OUString& ouType ) + { + if (m_vInternalSignatureInformations.empty()) +@@ -225,7 +245,9 @@ void XSecController::setReferenceCount() const + } + } + +-void XSecController::setX509IssuerName( OUString const & ouX509IssuerName ) ++void XSecController::setX509Data( ++ std::vector> & rX509IssuerSerials, ++ std::vector const& rX509Certificates) + { + if (m_vInternalSignatureInformations.empty()) + { +@@ -233,29 +255,52 @@ void XSecController::setX509IssuerName( OUString const & ouX509IssuerName ) + return; + } + InternalSignatureInformation &isi = m_vInternalSignatureInformations.back(); +- isi.signatureInfor.ouX509IssuerName = ouX509IssuerName; +-} +- +-void XSecController::setX509SerialNumber( OUString const & ouX509SerialNumber ) +-{ +- if (m_vInternalSignatureInformations.empty()) ++ SignatureInformation::X509Data data; ++ // due to the excessive flexibility of the spec it's possible that there ++ // is both a reference to a cert and the cert itself in one X509Data ++ for (OUString const& it : rX509Certificates) + { +- SAL_INFO("xmlsecurity.helper","XSecController::setX509SerialNumber: no signature"); +- return; ++ try ++ { ++ data.emplace_back(); ++ data.back().X509Certificate = it; ++ uno::Reference const xSecEnv(m_xSecurityContext->getSecurityEnvironment()); ++ uno::Reference const xCert(xSecEnv->createCertificateFromAscii(it)); ++ if (!xCert.is()) ++ { ++ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate"); ++ continue; // will be handled in CheckX509Data ++ } ++ OUString const issuerName(xCert->getIssuerName()); ++ OUString const serialNumber(xmlsecurity::bigIntegerToNumericString(xCert->getSerialNumber())); ++ auto const iter = std::find_if(rX509IssuerSerials.begin(), rX509IssuerSerials.end(), ++ [&](auto const& rX509IssuerSerial) { ++ return xmlsecurity::EqualDistinguishedNames(issuerName, rX509IssuerSerial.first) ++ && serialNumber == rX509IssuerSerial.second; ++ }); ++ if (iter != rX509IssuerSerials.end()) ++ { ++ data.back().X509IssuerName = iter->first; ++ data.back().X509SerialNumber = iter->second; ++ rX509IssuerSerials.erase(iter); ++ } ++ } ++ catch (uno::Exception const&) ++ { ++ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate"); ++ } + } +- InternalSignatureInformation &isi = m_vInternalSignatureInformations.back(); +- isi.signatureInfor.ouX509SerialNumber = ouX509SerialNumber; +-} +- +-void XSecController::setX509Certificate( OUString const & ouX509Certificate ) +-{ +- if (m_vInternalSignatureInformations.empty()) ++ // now handle any that are left... ++ for (auto const& it : rX509IssuerSerials) + { +- SAL_INFO("xmlsecurity.helper","XSecController::setX509Certificate: no signature"); +- return; ++ data.emplace_back(); ++ data.back().X509IssuerName = it.first; ++ data.back().X509SerialNumber = it.second; ++ } ++ if (!data.empty()) ++ { ++ isi.signatureInfor.X509Datas.push_back(data); + } +- InternalSignatureInformation &isi = m_vInternalSignatureInformations.back(); +- isi.signatureInfor.ouX509Certificate = ouX509Certificate; + } + + void XSecController::setSignatureValue( OUString const & ouSignatureValue ) +@@ -365,13 +410,67 @@ void XSecController::setSignatureBytes(const uno::Sequence& rBytes) + rInformation.signatureInfor.aSignatureBytes = rBytes; + } + +-void XSecController::setCertDigest(const OUString& rCertDigest) ++void XSecController::setX509CertDigest( ++ OUString const& rCertDigest, sal_Int32 const /*TODO nReferenceDigestID*/, ++ OUString const& rX509IssuerName, OUString const& rX509SerialNumber) + { + if (m_vInternalSignatureInformations.empty()) + return; + + InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back(); +- rInformation.signatureInfor.ouCertDigest = rCertDigest; ++ for (auto & rData : rInformation.signatureInfor.X509Datas) ++ { ++ for (auto & it : rData) ++ { ++ if (xmlsecurity::EqualDistinguishedNames(it.X509IssuerName, rX509IssuerName) ++ && it.X509SerialNumber == rX509SerialNumber) ++ { ++ it.CertDigest = rCertDigest; ++ return; ++ } ++ } ++ } ++ // fall-back: read the actual certificates ++ for (auto & rData : rInformation.signatureInfor.X509Datas) ++ { ++ for (auto & it : rData) ++ { ++ if (!it.X509Certificate.isEmpty()) ++ { ++ try ++ { ++ uno::Reference const xSecEnv(m_xSecurityContext->getSecurityEnvironment()); ++ uno::Reference const xCert(xSecEnv->createCertificateFromAscii(it.X509Certificate)); ++ if (!xCert.is()) ++ { ++ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate"); ++ } ++ else if (xmlsecurity::EqualDistinguishedNames(xCert->getIssuerName(),rX509IssuerName) ++ && xmlsecurity::bigIntegerToNumericString(xCert->getSerialNumber()) == rX509SerialNumber) ++ { ++ it.CertDigest = rCertDigest; ++ // note: testInsertCertificate_PEM_DOCX requires these! ++ it.X509SerialNumber = rX509SerialNumber; ++ it.X509IssuerName = rX509IssuerName; ++ return; ++ } ++ } ++ catch (uno::Exception const&) ++ { ++ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate"); ++ } ++ } ++ } ++ } ++ if (!rInformation.signatureInfor.ouGpgCertificate.isEmpty()) ++ { ++ SAL_INFO_IF(rCertDigest != rInformation.signatureInfor.ouGpgKeyID, ++ "xmlsecurity.helper", "PGPKeyID vs CertDigest mismatch"); ++ } ++ else ++ { ++ SAL_INFO("xmlsecurity.helper", "cannot find X509Data for CertDigest"); ++ } + } + + namespace { +diff --git a/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx b/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx +index 0e619b2802f8..244cd46ac564 100644 +--- a/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx ++++ b/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx +@@ -26,6 +26,7 @@ + #include + #include "x509certificate_mscryptimpl.hxx" + #include ++#include + #include "sanextension_mscryptimpl.hxx" + + #include "oid.hxx" +@@ -649,4 +650,50 @@ Sequence SAL_CALL X509Certificate_MSCryptImpl::getSupportedServiceName + return { OUString() }; + } + ++namespace xmlsecurity { ++ ++static bool EncodeDistinguishedName(OUString const& rName, CERT_NAME_BLOB & rBlob) ++{ ++ LPCWSTR pszError; ++ if (!CertStrToNameW(X509_ASN_ENCODING, ++ reinterpret_cast(rName.getStr()), CERT_X500_NAME_STR, ++ nullptr, nullptr, &rBlob.cbData, &pszError)) ++ { ++ SAL_INFO("xmlsecurity.xmlsec", "CertStrToNameW failed: " << WindowsErrorString(GetLastError()) << "; " << reinterpret_cast(pszError)); ++ return false; ++ } ++ rBlob.pbData = new BYTE[rBlob.cbData]; ++ if (!CertStrToNameW(X509_ASN_ENCODING, ++ reinterpret_cast(rName.getStr()), CERT_X500_NAME_STR, ++ nullptr, rBlob.pbData, &rBlob.cbData, &pszError)) ++ { ++ SAL_INFO("xmlsecurity.xmlsec", "CertStrToNameW failed: " << WindowsErrorString(GetLastError()) << "; " << reinterpret_cast(pszError)); ++ return false; ++ } ++ return true; ++} ++ ++bool EqualDistinguishedNames( ++ OUString const& rName1, OUString const& rName2) ++{ ++ CERT_NAME_BLOB blob1; ++ if (!EncodeDistinguishedName(rName1, blob1)) ++ { ++ return false; ++ } ++ CERT_NAME_BLOB blob2; ++ if (!EncodeDistinguishedName(rName2, blob2)) ++ { ++ delete[] blob1.pbData; ++ return false; ++ } ++ bool const ret(CertCompareCertificateName(X509_ASN_ENCODING, ++ &blob1, &blob2) == TRUE); ++ delete[] blob2.pbData; ++ delete[] blob1.pbData; ++ return ret; ++} ++ ++} // namespace xmlsecurity ++ + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ +diff --git a/xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx b/xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx +index a0acd81786d7..db400e6f1ed9 100644 +--- a/xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx ++++ b/xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx +@@ -18,6 +18,7 @@ + */ + + #include ++#include + #include + + #include +@@ -250,6 +251,7 @@ SAL_CALL XMLSignature_MSCryptImpl::validate( + ++nReferenceGood; + } + } ++ SAL_INFO("xmlsecurity.xmlsec", "xmlSecDSigCtxVerify status " << pDsigCtx->status << ", references good " << nReferenceGood << " of " << nReferenceCount); + + if (rs == 0 && nReferenceCount == nReferenceGood) + { +diff --git a/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx b/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx +index 1a323d33f32f..d6143a81883c 100644 +--- a/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx ++++ b/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx +@@ -31,6 +31,7 @@ + #include + #include "x509certificate_nssimpl.hxx" + ++#include + #include + + #include "sanextension_nssimpl.hxx" +@@ -533,4 +534,28 @@ sal_Bool SAL_CALL X509Certificate_NssImpl::supportsService(const OUString& servi + /* XServiceInfo */ + Sequence SAL_CALL X509Certificate_NssImpl::getSupportedServiceNames() { return { OUString() }; } + ++namespace xmlsecurity { ++ ++bool EqualDistinguishedNames( ++ OUString const& rName1, OUString const& rName2) ++{ ++ CERTName *const pName1(CERT_AsciiToName(OUStringToOString(rName1, RTL_TEXTENCODING_UTF8).getStr())); ++ if (pName1 == nullptr) ++ { ++ return false; ++ } ++ CERTName *const pName2(CERT_AsciiToName(OUStringToOString(rName2, RTL_TEXTENCODING_UTF8).getStr())); ++ if (pName2 == nullptr) ++ { ++ CERT_DestroyName(pName1); ++ return false; ++ } ++ bool const ret(CERT_CompareName(pName1, pName2) == SECEqual); ++ CERT_DestroyName(pName2); ++ CERT_DestroyName(pName1); ++ return ret; ++} ++ ++} // namespace xmlsecurity ++ + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ +diff --git a/xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx b/xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx +index c39f6cc72f76..827580bcc7d7 100644 +--- a/xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx ++++ b/xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx +@@ -26,6 +26,8 @@ + #include "securityenvironment_nssimpl.hxx" + + #include ++#include ++ + #include + #include + +@@ -257,6 +259,7 @@ SAL_CALL XMLSignature_NssImpl::validate( + ++nReferenceGood; + } + } ++ SAL_INFO("xmlsecurity.xmlsec", "xmlSecDSigCtxVerify status " << pDsigCtx->status << ", references good " << nReferenceGood << " of " << nReferenceCount); + + if (rs == 0 && pDsigCtx->status == xmlSecDSigStatusSucceeded && nReferenceCount == nReferenceGood) + { +-- +2.32.0 + diff --git a/SOURCES/0006-xmlsecurity-replace-OOXMLSecParser-implementation.patch b/SOURCES/0006-xmlsecurity-replace-OOXMLSecParser-implementation.patch new file mode 100644 index 0000000..3baded8 --- /dev/null +++ b/SOURCES/0006-xmlsecurity-replace-OOXMLSecParser-implementation.patch @@ -0,0 +1,1670 @@ +From fd2574fc4f095e5a46a5277d260782c570afc8e8 Mon Sep 17 00:00:00 2001 +From: Michael Stahl +Date: Tue, 30 Mar 2021 17:37:31 +0200 +Subject: [PATCH 6/6] xmlsecurity: replace OOXMLSecParser implementation + +This is similar to 12b15be8f4f930a04d8056b9219ac969b42a9784 and following +commits, but OOXMLSecParser has some differences to XSecParser, such as +using a ds:Manifest, and requires a couple extra namespaces. + +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/113381 +Tested-by: Jenkins +Reviewed-by: Miklos Vajna +(cherry picked from commit cc1d19f7bbaefa5fb22ebd1344112755068b93c9) +Reviewed-on: https://gerrit.libreoffice.org/c/core/+/113360 +(cherry picked from commit 5e2c137c27310e76050f2247077b1311baee4381) + +Change-Id: I56e39d9609db8fcad50ca1632ff482c1f0a30ff5 +--- + include/xmloff/xmlnmspe.hxx | 3 + + xmlsecurity/source/helper/ooxmlsecparser.cxx | 1473 +++++++++++++++--- + xmlsecurity/source/helper/ooxmlsecparser.hxx | 74 +- + 3 files changed, 1314 insertions(+), 236 deletions(-) + +diff --git a/include/xmloff/xmlnmspe.hxx b/include/xmloff/xmlnmspe.hxx +index 302a134f92fe..bebb1d656b40 100644 +--- a/include/xmloff/xmlnmspe.hxx ++++ b/include/xmloff/xmlnmspe.hxx +@@ -73,6 +73,9 @@ XML_NAMESPACE( XML_NAMESPACE_DSIG, 39U ) + XML_NAMESPACE( XML_NAMESPACE_DS, 40U ) + XML_NAMESPACE( XML_NAMESPACE_XADES132, 41U ) + XML_NAMESPACE( XML_NAMESPACE_XADES141, 42U ) ++// OOXML digital signature extension namespaces, also based on xmldsig-core ++XML_NAMESPACE( XML_NAMESPACE_MDSSI, 43U ) ++XML_NAMESPACE( XML_NAMESPACE_MSODIGSIG, 44U ) + + // namespaces for odf extended formats + +diff --git a/xmlsecurity/source/helper/ooxmlsecparser.cxx b/xmlsecurity/source/helper/ooxmlsecparser.cxx +index a25872fc057d..42f226f57d14 100644 +--- a/xmlsecurity/source/helper/ooxmlsecparser.cxx ++++ b/xmlsecurity/source/helper/ooxmlsecparser.cxx +@@ -11,32 +11,1241 @@ + #include "ooxmlsecparser.hxx" + #include + #include ++ ++#include ++#include ++ ++#include ++ + #include + +-using namespace com::sun::star; ++using namespace com::sun::star; ++ ++class OOXMLSecParser::Context ++{ ++ protected: ++ friend class OOXMLSecParser; ++ OOXMLSecParser & m_rParser; ++ private: ++ std::unique_ptr m_pOldNamespaceMap; ++ ++ public: ++ Context(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : m_rParser(rParser) ++ , m_pOldNamespaceMap(std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual ~Context() = default; ++ ++ virtual void StartElement( ++ css::uno::Reference const& /*xAttrs*/) ++ { ++ } ++ ++ virtual void EndElement() ++ { ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/); ++ ++ virtual void Characters(OUString const& /*rChars*/) ++ { ++ } ++}; ++ ++// it's possible that an unsupported element has an Id attribute and a ++// ds:Reference digesting it - probably this means XSecController needs to know ++// about it. (For known elements, the Id attribute is only processed according ++// to the schema.) ++class OOXMLSecParser::UnknownContext ++ : public OOXMLSecParser::Context ++{ ++ public: ++ UnknownContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ m_rParser.HandleIdAttr(xAttrs); ++ } ++}; ++ ++auto OOXMLSecParser::Context::CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/) ++-> std::unique_ptr ++{ ++ // default: create new base context ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++} ++ ++/** ++note: anything in ds:Object should be trusted *only* if there is a ds:Reference ++ to it so it is signed (exception: the xades:EncapsulatedX509Certificate). ++ ds:SignedInfo precedes all ds:Object. ++ ++ There may be multiple ds:Signature for purpose of counter-signatures ++ but the way XAdES describes these, only the ds:SignatureValue element ++ would be referenced, so requiring a ds:Reference for anything in ++ ds:Object shouldn't cause issues. ++ */ ++class OOXMLSecParser::ReferencedContextImpl ++ : public OOXMLSecParser::Context ++{ ++ protected: ++ bool m_isReferenced; ++ ++ public: ++ ReferencedContextImpl(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_isReferenced(isReferenced) ++ { ++ } ++ ++ OUString CheckIdAttrReferenced(css::uno::Reference const& xAttrs) ++ { ++ OUString const id(m_rParser.HandleIdAttr(xAttrs)); ++ if (!id.isEmpty() && m_rParser.m_pXSecController->haveReferenceForId(id)) ++ { ++ m_isReferenced = true; ++ } ++ return id; ++ } ++}; ++ ++class OOXMLSecParser::DsX509CertificateContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ OUString & m_rValue; ++ ++ public: ++ DsX509CertificateContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rValue) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rValue(rValue) ++ { ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_rValue += rChars; ++ } ++}; ++ ++class OOXMLSecParser::DsX509SerialNumberContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ OUString & m_rValue; ++ ++ public: ++ DsX509SerialNumberContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rValue) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rValue(rValue) ++ { ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_rValue += rChars; ++ } ++}; ++ ++class OOXMLSecParser::DsX509IssuerNameContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ OUString & m_rValue; ++ ++ public: ++ DsX509IssuerNameContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rValue) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rValue(rValue) ++ { ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_rValue += rChars; ++ } ++}; ++ ++class OOXMLSecParser::DsX509IssuerSerialContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ OUString & m_rX509IssuerName; ++ OUString & m_rX509SerialNumber; ++ ++ public: ++ DsX509IssuerSerialContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rIssuerName, OUString & rSerialNumber) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rX509IssuerName(rIssuerName) ++ , m_rX509SerialNumber(rSerialNumber) ++ { ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerName") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_rX509IssuerName); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "X509SerialNumber") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_rX509SerialNumber); ++ } ++ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++/// can't be sure what is supposed to happen here because the spec is clear as mud ++class OOXMLSecParser::DsX509DataContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ // sigh... "No ordering is implied by the above constraints." ++ // so store the ball of mud in vectors and try to figure it out later. ++ std::vector> m_X509IssuerSerials; ++ std::vector m_X509Certificates; ++ ++ public: ++ DsX509DataContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->setX509Data(m_X509IssuerSerials, m_X509Certificates); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerSerial") ++ { ++ m_X509IssuerSerials.emplace_back(); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerSerials.back().first, m_X509IssuerSerials.back().second); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Certificate") ++ { ++ m_X509Certificates.emplace_back(); ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_X509Certificates.back()); ++ } ++ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class OOXMLSecParser::DsKeyInfoContext ++ : public OOXMLSecParser::Context ++{ ++ public: ++ DsKeyInfoContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ m_rParser.HandleIdAttr(xAttrs); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Data") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ // missing: ds:PGPData ++ // missing: ds:KeyName, ds:KeyValue, ds:RetrievalMethod, ds:SPKIData, ds:MgmtData ++ // (old code would read ds:Transform inside ds:RetrievalMethod but ++ // presumably that was a bug) ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++ ++}; ++ ++class OOXMLSecParser::DsSignatureValueContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ OUString m_Value; ++ ++ public: ++ DsSignatureValueContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ m_rParser.HandleIdAttr(xAttrs); ++ } ++ ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->setSignatureValue(m_Value); ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_Value += rChars; ++ } ++}; ++ ++class OOXMLSecParser::DsDigestValueContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ OUString & m_rValue; ++ ++ public: ++ DsDigestValueContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rValue) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rValue(rValue) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& /*xAttrs*/) override ++ { ++ m_rValue.clear(); ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_rValue += rChars; ++ } ++}; ++ ++class OOXMLSecParser::DsDigestMethodContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ sal_Int32 & m_rReferenceDigestID; ++ ++ public: ++ DsDigestMethodContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ sal_Int32 & rReferenceDigestID) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rReferenceDigestID(rReferenceDigestID) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm"); ++ ++ SAL_WARN_IF( ouAlgorithm.isEmpty(), "xmlsecurity.helper", "no Algorithm in Reference" ); ++ if (!ouAlgorithm.isEmpty()) ++ { ++ SAL_WARN_IF( ouAlgorithm != ALGO_XMLDSIGSHA1 ++ && ouAlgorithm != ALGO_XMLDSIGSHA256 ++ && ouAlgorithm != ALGO_XMLDSIGSHA512, ++ "xmlsecurity.helper", "Algorithm neither SHA1, SHA256 nor SHA512"); ++ if (ouAlgorithm == ALGO_XMLDSIGSHA1) ++ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA1; ++ else if (ouAlgorithm == ALGO_XMLDSIGSHA256) ++ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA256; ++ else if (ouAlgorithm == ALGO_XMLDSIGSHA512) ++ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA512; ++ else ++ m_rReferenceDigestID = 0; ++ } ++ } ++}; ++ ++class OOXMLSecParser::DsTransformContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ bool & m_rIsC14N; ++ ++ public: ++ DsTransformContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool & rIsC14N) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rIsC14N(rIsC14N) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ OUString aAlgorithm = xAttrs->getValueByName("Algorithm"); ++ ++ if (aAlgorithm == ALGO_RELATIONSHIP) ++ { ++ m_rIsC14N = true; ++ } ++ } ++}; ++ ++class OOXMLSecParser::DsTransformsContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ bool & m_rIsC14N; ++ ++ public: ++ DsTransformsContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool & rIsC14N) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rIsC14N(rIsC14N) ++ { ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "Transform") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_rIsC14N); ++ } ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class OOXMLSecParser::DsReferenceContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ OUString m_URI; ++ OUString m_Type; ++ OUString m_DigestValue; ++ bool m_IsC14N = false; ++ // Relevant for ODF. The digest algorithm selected by the DigestMethod ++ // element's Algorithm attribute. @see css::xml::crypto::DigestID. ++ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA256; ++ ++ public: ++ DsReferenceContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ m_rParser.HandleIdAttr(xAttrs); ++ ++ m_URI = xAttrs->getValueByName("URI"); ++ SAL_WARN_IF(m_URI.isEmpty(), "xmlsecurity.helper", "URI is empty"); ++ // Remember the type of this reference. ++ m_Type = xAttrs->getValueByName("Type"); ++ } ++ ++ virtual void EndElement() override ++ { ++ if (m_URI.startsWith("#")) ++ { ++ /* ++ * remove the first character '#' from the attribute value ++ */ ++ m_rParser.m_pXSecController->addReference(m_URI.copy(1), m_nReferenceDigestID, m_Type); ++ } ++ else ++ { ++ if (m_IsC14N) // this is determined by nested ds:Transform ++ { ++ m_rParser.m_pXSecController->addStreamReference(m_URI, false, m_nReferenceDigestID); ++ } ++ else ++ /* ++ * it must be an octet stream ++ */ ++ { ++ m_rParser.m_pXSecController->addStreamReference(m_URI, true, m_nReferenceDigestID); ++ } ++ } ++ ++ m_rParser.m_pXSecController->setDigestValue(m_nReferenceDigestID, m_DigestValue); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "Transforms") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_IsC14N); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_nReferenceDigestID); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_DigestValue); ++ } ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class OOXMLSecParser::DsSignatureMethodContext ++ : public OOXMLSecParser::Context ++{ ++ public: ++ DsSignatureMethodContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm"); ++ if (ouAlgorithm == ALGO_ECDSASHA1 || ouAlgorithm == ALGO_ECDSASHA256 ++ || ouAlgorithm == ALGO_ECDSASHA512) ++ { ++ m_rParser.m_pXSecController->setSignatureMethod(svl::crypto::SignatureMethodAlgorithm::ECDSA); ++ } ++ } ++}; ++ ++class OOXMLSecParser::DsSignedInfoContext ++ : public OOXMLSecParser::Context ++{ ++ public: ++ DsSignedInfoContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ m_rParser.HandleIdAttr(xAttrs); ++ } ++ ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->setReferenceCount(); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureMethod") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "Reference") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ // missing: ds:CanonicalizationMethod ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class OOXMLSecParser::XadesCertDigestContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ OUString & m_rDigestValue; ++ sal_Int32 & m_rReferenceDigestID; ++ ++ public: ++ XadesCertDigestContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rDigestValue, sal_Int32 & rReferenceDigestID) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rDigestValue(rDigestValue) ++ , m_rReferenceDigestID(rReferenceDigestID) ++ { ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_rReferenceDigestID); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_rDigestValue); ++ } ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class OOXMLSecParser::XadesCertContext ++ : public OOXMLSecParser::ReferencedContextImpl ++{ ++ private: ++ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1; ++ OUString m_CertDigest; ++ OUString m_X509IssuerName; ++ OUString m_X509SerialNumber; ++ ++ public: ++ XadesCertContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) ++ { ++ } ++ ++ virtual void EndElement() override ++ { ++ if (m_isReferenced) ++ { ++ m_rParser.m_pXSecController->setX509CertDigest(m_CertDigest, m_nReferenceDigestID, m_X509IssuerName, m_X509SerialNumber); ++ } ++ else ++ { ++ SAL_INFO("xmlsecurity.helper", "ignoring unsigned xades:Cert"); ++ } ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "CertDigest") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_CertDigest, m_nReferenceDigestID); ++ } ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "IssuerSerial") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerName, m_X509SerialNumber); ++ } ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class OOXMLSecParser::XadesSigningCertificateContext ++ : public OOXMLSecParser::ReferencedContextImpl ++{ ++ public: ++ XadesSigningCertificateContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) ++ { ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "Cert") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); ++ } ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class OOXMLSecParser::XadesSigningTimeContext ++ : public OOXMLSecParser::ReferencedContextImpl ++{ ++ private: ++ OUString m_Value; ++ ++ public: ++ XadesSigningTimeContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) ++ { ++ } ++ ++ virtual void EndElement() override ++ { ++ if (m_isReferenced) ++ { ++ m_rParser.m_pXSecController->setDate("", m_Value); ++ } ++ else ++ { ++ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SigningTime"); ++ } ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_Value += rChars; ++ } ++}; ++ ++class OOXMLSecParser::XadesSignedSignaturePropertiesContext ++ : public OOXMLSecParser::ReferencedContextImpl ++{ ++ public: ++ XadesSignedSignaturePropertiesContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ CheckIdAttrReferenced(xAttrs); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningTime") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); ++ } ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningCertificate") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); ++ } ++ // missing: xades:SignaturePolicyIdentifier, xades:SignatureProductionPlace, xades:SignerRole ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class OOXMLSecParser::XadesSignedPropertiesContext ++ : public OOXMLSecParser::ReferencedContextImpl ++{ ++ public: ++ XadesSignedPropertiesContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ CheckIdAttrReferenced(xAttrs); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedSignatureProperties") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); ++ } ++ // missing: xades:SignedDataObjectProperties ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class OOXMLSecParser::XadesQualifyingPropertiesContext ++ : public OOXMLSecParser::ReferencedContextImpl ++{ ++ public: ++ XadesQualifyingPropertiesContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ CheckIdAttrReferenced(xAttrs); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedProperties") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); ++ } ++ // missing: xades:UnsignedSignatureProperties ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class OOXMLSecParser::MsodigsigSetupIDContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ OUString & m_rValue; ++ ++ public: ++ MsodigsigSetupIDContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rValue) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rValue(rValue) ++ { ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_rValue += rChars; ++ } ++}; ++ ++class OOXMLSecParser::MsodigsigSignatureCommentsContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ OUString & m_rValue; ++ ++ public: ++ MsodigsigSignatureCommentsContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rValue) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rValue(rValue) ++ { ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_rValue += rChars; ++ } ++}; ++ ++class OOXMLSecParser::MsodigsigSignatureInfoV1Context ++ : public OOXMLSecParser::ReferencedContextImpl ++{ ++ private: ++ OUString m_SetupID; ++ OUString m_SignatureComments; ++ ++ public: ++ MsodigsigSignatureInfoV1Context(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ CheckIdAttrReferenced(xAttrs); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_MSODIGSIG && rName == "SetupID") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_SetupID); ++ } ++ if (nNamespace == XML_NAMESPACE_MSODIGSIG && rName == "SignatureComments") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_SignatureComments); ++ } ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++ ++ virtual void EndElement() override ++ { ++ if (m_isReferenced) ++ { ++ if (!m_SetupID.isEmpty()) ++ { ++ m_rParser.m_pXSecController->setSignatureLineId(m_SetupID); ++ } ++ if (!m_SignatureComments.isEmpty()) ++ { ++ m_rParser.m_pXSecController->setDescription("", m_SignatureComments); ++ ++ } ++ } ++ else ++ { ++ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureInfoV1"); ++ } ++ } ++}; ++ ++class OOXMLSecParser::MdssiValueContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ OUString & m_rValue; ++ ++ public: ++ MdssiValueContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rValue) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rValue(rValue) ++ { ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_rValue += rChars; ++ } ++}; ++ ++class OOXMLSecParser::MdssiSignatureTimeContext ++ : public OOXMLSecParser::Context ++{ ++ private: ++ OUString & m_rValue; ++ ++ public: ++ MdssiSignatureTimeContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ OUString & rValue) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ , m_rValue(rValue) ++ { ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_MDSSI && rName == "Value") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_rValue); ++ } ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++ ++class OOXMLSecParser::DsSignaturePropertyContext ++ : public OOXMLSecParser::ReferencedContextImpl ++{ ++ private: ++ enum class SignatureProperty { Unknown, Date, Info }; ++ SignatureProperty m_Property = SignatureProperty::Unknown; ++ OUString m_Id; ++ OUString m_Value; ++ ++ public: ++ DsSignaturePropertyContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ m_Id = CheckIdAttrReferenced(xAttrs); ++ } ++ ++ virtual void EndElement() override ++ { ++ if (m_isReferenced) ++ { ++ switch (m_Property) ++ { ++ case SignatureProperty::Unknown: ++ SAL_INFO("xmlsecurity.helper", "Unknown property in ds:Object ignored"); ++ break; ++ case SignatureProperty::Info: ++ break; // handled by child context ++ case SignatureProperty::Date: ++ m_rParser.m_pXSecController->setDate(m_Id, m_Value); ++ break; ++ } ++ } ++ else ++ { ++ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureProperty"); ++ } ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_MDSSI && rName == "SignatureTime") ++ { ++ m_Property = SignatureProperty::Date; ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_Value); ++ } ++ if (nNamespace == XML_NAMESPACE_MSODIGSIG && rName == "SignatureInfoV1") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); ++ } ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class OOXMLSecParser::DsSignaturePropertiesContext ++ : public OOXMLSecParser::ReferencedContextImpl ++{ ++ public: ++ DsSignaturePropertiesContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ CheckIdAttrReferenced(xAttrs); ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperty") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); ++ } ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class OOXMLSecParser::DsManifestContext ++ : public OOXMLSecParser::ReferencedContextImpl ++{ ++ public: ++ DsManifestContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap, ++ bool const isReferenced) ++ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ CheckIdAttrReferenced(xAttrs); ++ } ++ ++#if 0 ++ ??? ++ virtual void EndElement() override ++ { ++ m_rParser.m_pXSecController->setReferenceCount(); ++ } ++#endif ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "Reference") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ // missing: ds:CanonicalizationMethod ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class OOXMLSecParser::DsObjectContext ++ : public OOXMLSecParser::ReferencedContextImpl ++{ ++ enum class Mode { Default, ValidSignatureLineImage, InvalidSignatureLineImage }; ++ Mode m_Mode = Mode::Default; ++ OUString m_Value; ++ ++ public: ++ DsObjectContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ // init with "false" here - the Signature element can't be referenced by its child ++ : OOXMLSecParser::ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), false) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ OUString const id(CheckIdAttrReferenced(xAttrs)); ++ if (id == "idValidSigLnImg") ++ { ++ m_Mode = Mode::ValidSignatureLineImage; ++ } ++ else if (id == "idInvalidSigLnImg") ++ { ++ m_Mode = Mode::InvalidSignatureLineImage; ++ } ++ } ++ ++ virtual void EndElement() override ++ { ++ switch (m_Mode) ++ { ++ case Mode::ValidSignatureLineImage: ++ if (m_isReferenced) ++ { ++ m_rParser.m_pXSecController->setValidSignatureImage(m_Value); ++ } ++ else ++ { ++ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineValidImage"); ++ } ++ break; ++ case Mode::InvalidSignatureLineImage: ++ if (m_isReferenced) ++ { ++ m_rParser.m_pXSecController->setInvalidSignatureImage(m_Value); ++ } ++ else ++ { ++ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineInvalidImage"); ++ } ++ break; ++ case Mode::Default: ++ break; ++ } ++ } ++ ++ virtual void Characters(OUString const& rChars) override ++ { ++ m_Value += rChars; ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperties") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); ++ } ++ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "QualifyingProperties") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "Manifest") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); ++ } ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ ++class OOXMLSecParser::DsSignatureContext ++ : public OOXMLSecParser::Context ++{ ++ public: ++ DsSignatureContext(OOXMLSecParser & rParser, ++ std::unique_ptr pOldNamespaceMap) ++ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap)) ++ { ++ } ++ ++ virtual void StartElement( ++ css::uno::Reference const& xAttrs) override ++ { ++ OUString const ouIdAttr(m_rParser.HandleIdAttr(xAttrs)); ++ m_rParser.m_rXMLSignatureHelper.StartVerifySignatureElement(); ++ m_rParser.m_pXSecController->addSignature(); ++ if (!ouIdAttr.isEmpty()) ++ { ++ m_rParser.m_pXSecController->setId( ouIdAttr ); ++ } ++ } ++ ++ virtual std::unique_ptr CreateChildContext( ++ std::unique_ptr pOldNamespaceMap, ++ sal_uInt16 const nNamespace, OUString const& rName) override ++ { ++ if (nNamespace == XML_NAMESPACE_DS && rName == "SignedInfo") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureValue") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "KeyInfo") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ if (nNamespace == XML_NAMESPACE_DS && rName == "Object") ++ { ++ return std::make_unique(m_rParser, std::move(pOldNamespaceMap)); ++ } ++ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); ++ } ++}; ++ + + OOXMLSecParser::OOXMLSecParser(XMLSignatureHelper& rXMLSignatureHelper, XSecController* pXSecController) +- : m_pXSecController(pXSecController) +- ,m_bInDigestValue(false) +- ,m_bInSignatureValue(false) +- ,m_bInX509Certificate(false) +- ,m_bInMdssiValue(false) +- ,m_bInSignatureComments(false) +- ,m_bInX509IssuerName(false) +- ,m_bInX509SerialNumber(false) +- ,m_bInCertDigest(false) +- ,m_bInValidSignatureImage(false) +- ,m_bInInvalidSignatureImage(false) +- ,m_bInSignatureLineId(false) +- ,m_bReferenceUnresolved(false) ++ : m_pNamespaceMap(new SvXMLNamespaceMap) ++ , m_pXSecController(pXSecController) + ,m_rXMLSignatureHelper(rXMLSignatureHelper) + { ++ using namespace xmloff::token; ++ m_pNamespaceMap->Add( GetXMLToken(XML_XML), GetXMLToken(XML_N_XML), XML_NAMESPACE_XML ); ++ m_pNamespaceMap->Add( "_ds", GetXMLToken(XML_N_DS), XML_NAMESPACE_DS ); ++ m_pNamespaceMap->Add( "_xades132", GetXMLToken(XML_N_XADES132), XML_NAMESPACE_XADES132); ++ m_pNamespaceMap->Add( "_xades141", GetXMLToken(XML_N_XADES141), XML_NAMESPACE_XADES141); ++ m_pNamespaceMap->Add( "_dc", GetXMLToken(XML_N_DC), XML_NAMESPACE_DC ); ++ m_pNamespaceMap->Add( "_mdssi", NS_MDSSI, XML_NAMESPACE_MDSSI ); ++ m_pNamespaceMap->Add( "_msodigsig", "http://schemas.microsoft.com/office/2006/digsig", XML_NAMESPACE_MSODIGSIG ); ++ m_pNamespaceMap->Add( "_office_libo", ++ GetXMLToken(XML_N_LO_EXT), XML_NAMESPACE_LO_EXT); + } + + OOXMLSecParser::~OOXMLSecParser() + { + } + ++OUString OOXMLSecParser::HandleIdAttr(css::uno::Reference const& xAttrs) ++{ ++ OUString const aId = xAttrs->getValueByName("Id"); ++ if (!aId.isEmpty()) ++ { ++ m_pXSecController->collectToVerify(aId); ++ } ++ return aId; ++} ++ + void SAL_CALL OOXMLSecParser::startDocument() + { + if (m_xNextHandler.is()) +@@ -51,231 +1260,69 @@ void SAL_CALL OOXMLSecParser::endDocument() + + void SAL_CALL OOXMLSecParser::startElement(const OUString& rName, const uno::Reference& xAttribs) + { +- OUString aId = xAttribs->getValueByName("Id"); +- if (!aId.isEmpty()) +- m_pXSecController->collectToVerify(aId); ++ assert(m_pNamespaceMap); ++ std::unique_ptr pRewindMap( ++ SvXMLImport::processNSAttributes(m_pNamespaceMap, nullptr, xAttribs)); + +- if (rName == "Signature") +- { +- m_rXMLSignatureHelper.StartVerifySignatureElement(); +- m_pXSecController->addSignature(); +- if (!aId.isEmpty()) +- m_pXSecController->setId(aId); +- } +- else if (rName == "SignatureMethod") +- { +- OUString ouAlgorithm = xAttribs->getValueByName("Algorithm"); +- if (ouAlgorithm == ALGO_ECDSASHA1 || ouAlgorithm == ALGO_ECDSASHA256 +- || ouAlgorithm == ALGO_ECDSASHA512) +- m_pXSecController->setSignatureMethod(svl::crypto::SignatureMethodAlgorithm::ECDSA); +- } +- else if (rName == "Reference") +- { +- OUString aURI = xAttribs->getValueByName("URI"); +- if (aURI.startsWith("#")) +- m_pXSecController->addReference(aURI.copy(1), xml::crypto::DigestID::SHA1, OUString()); +- else +- { +- m_aReferenceURI = aURI; +- m_bReferenceUnresolved = true; +- } +- } +- else if (rName == "Transform") +- { +- if (m_bReferenceUnresolved) +- { +- OUString aAlgorithm = xAttribs->getValueByName("Algorithm"); +- if (aAlgorithm == ALGO_RELATIONSHIP) +- { +- m_pXSecController->addStreamReference(m_aReferenceURI, /*isBinary=*/false, /*nDigestID=*/xml::crypto::DigestID::SHA256); +- m_bReferenceUnresolved = false; +- } +- } +- } +- else if (rName == "DigestValue" && !m_bInCertDigest) +- { +- m_aDigestValue.clear(); +- m_bInDigestValue = true; +- } +- else if (rName == "SignatureValue") +- { +- m_aSignatureValue.clear(); +- m_bInSignatureValue = true; +- } +- else if (rName == "X509Certificate") +- { +- m_aX509Certificate.clear(); +- m_bInX509Certificate = true; +- } +- else if (rName == "mdssi:Value") +- { +- m_aMdssiValue.clear(); +- m_bInMdssiValue = true; +- } +- else if (rName == "SignatureComments") +- { +- m_aSignatureComments.clear(); +- m_bInSignatureComments = true; +- } +- else if (rName == "X509IssuerName") +- { +- m_aX509IssuerName.clear(); +- m_bInX509IssuerName = true; +- } +- else if (rName == "X509SerialNumber") +- { +- m_aX509SerialNumber.clear(); +- m_bInX509SerialNumber = true; +- } +- else if (rName == "xd:CertDigest") +- { +- m_aCertDigest.clear(); +- m_bInCertDigest = true; +- } +- else if (rName == "Object") ++ OUString localName; ++ sal_uInt16 const nPrefix(m_pNamespaceMap->GetKeyByAttrName(rName, &localName)); ++ ++ std::unique_ptr pContext; ++ ++ if (m_ContextStack.empty()) + { +- OUString sId = xAttribs->getValueByName("Id"); +- if (sId == "idValidSigLnImg") +- { +- m_aValidSignatureImage.clear(); +- m_bInValidSignatureImage = true; +- } +- else if (sId == "idInvalidSigLnImg") ++ if (nPrefix == XML_NAMESPACE_DS ++ && localName == "Signature") + { +- m_aInvalidSignatureImage.clear(); +- m_bInInvalidSignatureImage = true; ++ pContext.reset(new DsSignatureContext(*this, std::move(pRewindMap))); + } + else + { +- SAL_INFO("xmlsecurity.ooxml", "Unknown 'Object' child element: " << rName); ++ throw css::xml::sax::SAXException( ++ "xmlsecurity: unexpected root element", nullptr, ++ css::uno::Any()); + } + } +- else if (rName == "SetupID") +- { +- m_aSignatureLineId.clear(); +- m_bInSignatureLineId = true; +- } + else + { +- SAL_INFO("xmlsecurity.ooxml", "Unknown xml element: " << rName); ++ pContext = m_ContextStack.top()->CreateChildContext( ++ std::move(pRewindMap), nPrefix, localName); + } + ++ m_ContextStack.push(std::move(pContext)); ++ assert(!pRewindMap); ++ ++ m_ContextStack.top()->StartElement(xAttribs); ++ + if (m_xNextHandler.is()) ++ { + m_xNextHandler->startElement(rName, xAttribs); ++ } ++ + } + + void SAL_CALL OOXMLSecParser::endElement(const OUString& rName) + { +- if (rName == "SignedInfo") +- m_pXSecController->setReferenceCount(); +- else if (rName == "Reference") +- { +- if (m_bReferenceUnresolved) +- { +- // No transform algorithm found, assume binary. +- m_pXSecController->addStreamReference(m_aReferenceURI, /*isBinary=*/true, /*nDigestID=*/xml::crypto::DigestID::SHA256); +- m_bReferenceUnresolved = false; +- } +- m_pXSecController->setDigestValue(xml::crypto::DigestID::SHA256, m_aDigestValue); +- } +- else if (rName == "DigestValue" && !m_bInCertDigest) +- m_bInDigestValue = false; +- else if (rName == "SignatureValue") +- { +- m_pXSecController->setSignatureValue(m_aSignatureValue); +- m_bInSignatureValue = false; +- } +- else if (rName == "X509Data") +- { +- std::vector> X509IssuerSerials; +- std::vector X509Certificates; +- if (!m_aX509Certificate.isEmpty()) +- { +- X509Certificates.emplace_back(m_aX509Certificate); +- } +- if (!m_aX509IssuerName.isEmpty() && !m_aX509SerialNumber.isEmpty()) +- { +- X509IssuerSerials.emplace_back(m_aX509IssuerName, m_aX509SerialNumber); +- } +- m_pXSecController->setX509Data(X509IssuerSerials, X509Certificates); +- } +- else if (rName == "X509Certificate") +- { +- m_bInX509Certificate = false; +- } +- else if (rName == "mdssi:Value") +- { +- m_pXSecController->setDate("", m_aMdssiValue); +- m_bInMdssiValue = false; +- } +- else if (rName == "SignatureComments") +- { +- m_pXSecController->setDescription("", m_aSignatureComments); +- m_bInSignatureComments = false; +- } +- else if (rName == "X509IssuerName") +- { +- m_bInX509IssuerName = false; +- } +- else if (rName == "X509SerialNumber") +- { +- m_bInX509SerialNumber = false; +- } +- else if (rName == "xd:Cert") +- { +- m_pXSecController->setX509CertDigest(m_aCertDigest, css::xml::crypto::DigestID::SHA1, m_aX509IssuerName, m_aX509SerialNumber); +- } +- else if (rName == "xd:CertDigest") +- { +- m_bInCertDigest = false; +- } +- else if (rName == "Object") ++ assert(!m_ContextStack.empty()); // this should be checked by sax parser? ++ ++ m_ContextStack.top()->EndElement(); ++ ++ if (m_xNextHandler.is()) + { +- if (m_bInValidSignatureImage) +- { +- m_pXSecController->setValidSignatureImage(m_aValidSignatureImage); +- m_bInValidSignatureImage = false; +- } +- else if (m_bInInvalidSignatureImage) +- { +- m_pXSecController->setInvalidSignatureImage(m_aInvalidSignatureImage); +- m_bInInvalidSignatureImage = false; +- } ++ m_xNextHandler->endElement(rName); + } +- else if (rName == "SetupID") ++ ++ if (m_ContextStack.top()->m_pOldNamespaceMap) + { +- m_pXSecController->setSignatureLineId(m_aSignatureLineId); +- m_bInSignatureLineId = false; ++ m_pNamespaceMap = std::move(m_ContextStack.top()->m_pOldNamespaceMap); + } +- +- if (m_xNextHandler.is()) +- m_xNextHandler->endElement(rName); ++ m_ContextStack.pop(); + } + + void SAL_CALL OOXMLSecParser::characters(const OUString& rChars) + { +- if (m_bInDigestValue && !m_bInCertDigest) +- m_aDigestValue += rChars; +- else if (m_bInSignatureValue) +- m_aSignatureValue += rChars; +- else if (m_bInX509Certificate) +- m_aX509Certificate += rChars; +- else if (m_bInMdssiValue) +- m_aMdssiValue += rChars; +- else if (m_bInSignatureComments) +- m_aSignatureComments += rChars; +- else if (m_bInX509IssuerName) +- m_aX509IssuerName += rChars; +- else if (m_bInX509SerialNumber) +- m_aX509SerialNumber += rChars; +- else if (m_bInCertDigest) +- m_aCertDigest += rChars; +- else if (m_bInValidSignatureImage) +- m_aValidSignatureImage += rChars; +- else if (m_bInInvalidSignatureImage) +- m_aInvalidSignatureImage += rChars; +- else if (m_bInSignatureLineId) +- m_aSignatureLineId += rChars; ++ assert(!m_ContextStack.empty()); // this should be checked by sax parser? ++ m_ContextStack.top()->Characters(rChars); + + if (m_xNextHandler.is()) + m_xNextHandler->characters(rChars); +diff --git a/xmlsecurity/source/helper/ooxmlsecparser.hxx b/xmlsecurity/source/helper/ooxmlsecparser.hxx +index d3c199147255..540028b22fc9 100644 +--- a/xmlsecurity/source/helper/ooxmlsecparser.hxx ++++ b/xmlsecurity/source/helper/ooxmlsecparser.hxx +@@ -15,6 +15,10 @@ + + #include + ++#include ++ ++#include ++ + class XSecController; + class XMLSignatureHelper; + +@@ -25,38 +29,62 @@ class OOXMLSecParser: public cppu::WeakImplHelper + css::lang::XInitialization + > + { ++public: ++ class Context; ++private: ++ class UnknownContext; ++ class ReferencedContextImpl; ++ class DsX509CertificateContext; ++ class DsX509SerialNumberContext; ++ class DsX509IssuerNameContext; ++ class DsX509IssuerSerialContext; ++ class DsX509DataContext; ++ class DsKeyInfoContext; ++ class DsSignatureValueContext; ++ class DsDigestValueContext; ++ class DsDigestMethodContext; ++ class DsTransformContext; ++ class DsTransformsContext; ++ class DsReferenceContext; ++ class DsSignatureMethodContext; ++ class DsSignedInfoContext; ++ class XadesEncapsulatedX509CertificateContext; ++ class XadesCertificateValuesContext; ++ class XadesUnsignedSignaturePropertiesContext; ++ class XadesUnsignedPropertiesContext; ++ class XadesCertDigestContext; ++ class XadesCertContext; ++ class XadesSigningCertificateContext; ++ class XadesSigningTimeContext; ++ class XadesSignedSignaturePropertiesContext; ++ class XadesSignedPropertiesContext; ++ class XadesQualifyingPropertiesContext; ++ class MdssiValueContext; ++ class MdssiSignatureTimeContext; ++ class MsodigsigSetupIDContext; ++ class MsodigsigSignatureCommentsContext; ++ class MsodigsigSignatureInfoV1Context; ++ class DsSignaturePropertyContext; ++ class DsSignaturePropertiesContext; ++ class DsManifestContext; ++ class DsObjectContext; ++ class DsSignatureContext; ++ class DsigSignaturesContext; ++ ++ std::stack> m_ContextStack; ++ std::unique_ptr m_pNamespaceMap; ++ + XSecController* m_pXSecController; + css::uno::Reference m_xNextHandler; + +- bool m_bInDigestValue; +- OUString m_aDigestValue; +- bool m_bInSignatureValue; +- OUString m_aSignatureValue; +- bool m_bInX509Certificate; +- OUString m_aX509Certificate; +- bool m_bInMdssiValue; +- OUString m_aMdssiValue; +- bool m_bInSignatureComments; +- OUString m_aSignatureComments; +- bool m_bInX509IssuerName; +- OUString m_aX509IssuerName; +- bool m_bInX509SerialNumber; +- OUString m_aX509SerialNumber; +- bool m_bInCertDigest; +- OUString m_aCertDigest; +- bool m_bInValidSignatureImage; +- OUString m_aValidSignatureImage; +- bool m_bInInvalidSignatureImage; +- OUString m_aInvalidSignatureImage; +- bool m_bInSignatureLineId; +- OUString m_aSignatureLineId; +- + /// Last seen . + OUString m_aReferenceURI; + /// Already called addStreamReference() for this reference. + bool m_bReferenceUnresolved; + XMLSignatureHelper& m_rXMLSignatureHelper; + ++ OUString HandleIdAttr(css::uno::Reference const& xAttrs); ++ + public: + explicit OOXMLSecParser(XMLSignatureHelper& rXMLSignatureHelper, XSecController* pXSecController); + virtual ~OOXMLSecParser() override; +-- +2.32.0 + diff --git a/SPECS/libreoffice.spec b/SPECS/libreoffice.spec index eb8009e..c49d9ac 100644 --- a/SPECS/libreoffice.spec +++ b/SPECS/libreoffice.spec @@ -54,7 +54,7 @@ Summary: Free Software Productivity Suite Name: libreoffice Epoch: 1 Version: %{libo_version}.2 -Release: 6%{?libo_prerelease}%{?dist} +Release: 7%{?libo_prerelease}%{?dist} License: (MPLv1.1 or LGPLv3+) and LGPLv3 and LGPLv2+ and BSD and (MPLv1.1 or GPLv2 or LGPLv2 or Netscape) and Public Domain and ASL 2.0 and MPLv2.0 and CC0 URL: http://www.libreoffice.org/ @@ -269,6 +269,12 @@ Patch25: 0008-Resolves-tdf-137215-restore-original-modality-before.patch Patch26: 0001-rhbz-1882616-move-cursor-one-step-at-a-time-in-the-d.patch Patch27: 0001-rhbz-1980800-allow-convert-to-csv-to-write-each-shee.patch Patch28: 0001-Resolves-tdf-132739-two-style-tags-where-there-shoul.patch +Patch29: 0001-backports-to-ease-CVE-backporting.patch +Patch30: 0002-CVE-2021-25635.patch +Patch31: 0003-xmlsecurity-replace-XSecParser-implementation.patch +Patch32: 0004-CVE-2021-25634.patch +Patch33: 0005-CVE-2021-25633.patch +Patch34: 0006-xmlsecurity-replace-OOXMLSecParser-implementation.patch %if 0%{?rhel} # not upstreamed @@ -2253,6 +2259,11 @@ done %{_includedir}/LibreOfficeKit %changelog +* Fri Oct 15 2021 Caolán McNamara - 1:6.4.7.2-7 +- Resolves: rhbz#2013858 CVE-2021-25633 +- Resolves: rhbz#2014215 CVE-2021-25634 +- Resolves: rhbz#2014209 CVE-2021-25635 + * Mon Sep 06 2021 Caolán McNamara - 1:6.4.7.2-6 - Resolves: rhbz#1980800 allow convert to csv to write each sheet to separate file