diff options
author | Michael Stahl <michael.stahl@allotropia.de> | 2023-12-13 21:53:55 +0100 |
---|---|---|
committer | Michael Stahl <michael.stahl@allotropia.de> | 2023-12-14 15:38:13 +0100 |
commit | 91f35f22f0447769c08ca89e27a39b40df18fffa (patch) | |
tree | b3e0f8344ae6277332bf1b0933b39591e62dc89e /package | |
parent | f618e9b8c1d4748a9ff9ee97405af8e43c6ae294 (diff) |
tdf#105844 package: remove root document from manifest ...
... for ODF wholesome encryption.
4.3 <manifest:file-entry>
For directories, the manifest file should contain a <manifest:file-entry> element only if a directory contains a document or a sub document.
Because the "encrypted-package" is not a document but a package, we
should probably omit the file-entry for the root document.
ZipPackage::writeTempFile() always generates the root document becuase
it's needed for GPG properties, and ManifestExport filters it out.
A bit tricky to implement, because there isn't a clean distinction
between the package and the root document/storage in the package module,
in particular there's no other place than the root storage to store the
MediaType property.
Change-Id: Id7e72a64e2faa074dce80cd5fefb2fa189e2e3ee
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160717
Tested-by: Jenkins
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
Diffstat (limited to 'package')
-rw-r--r-- | package/source/manifest/ManifestExport.cxx | 33 | ||||
-rw-r--r-- | package/source/zipapi/ZipFile.cxx | 3 | ||||
-rw-r--r-- | package/source/zippackage/ZipPackage.cxx | 23 |
3 files changed, 50 insertions, 9 deletions
diff --git a/package/source/manifest/ManifestExport.cxx b/package/source/manifest/ManifestExport.cxx index 950e47f49741..cf60614ccd58 100644 --- a/package/source/manifest/ManifestExport.cxx +++ b/package/source/manifest/ManifestExport.cxx @@ -81,6 +81,7 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con // find the mediatype of the document if any OUString aDocMediaType; OUString aDocVersion; + bool isWholesomeEncryption(false); const uno::Sequence<beans::PropertyValue>* pRootFolderPropSeq = nullptr; for (const uno::Sequence < beans::PropertyValue >& rSequence : rManList) { @@ -109,12 +110,27 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con if ( aPath == "/" ) { + assert(aDocMediaType.isEmpty()); + // unfortunately no aMediaType in some cases where non-documents + // are stored as StorageFormats::PACKAGE instead of sensible + // StorageFormats::ZIP, such as SvxXMLXTableExportComponent and + // SwXMLTextBlocks, which results in an empty "mimetype" etc but + // can't be easily fixed; try to exclude these cases by checking + // for aVersion, but of course then forgetting to set both version + // and type on an actual document can't be found :( + assert(!aMediaType.isEmpty() || aVersion.isEmpty()); aDocMediaType = aMediaType; aDocVersion = aVersion; pRootFolderPropSeq = &rSequence; - break; + } + + if (aPath == "encrypted-package") + { + isWholesomeEncryption = true; + assert(aDocMediaType.isEmpty() || aDocMediaType == aMediaType); } } + assert(pRootFolderPropSeq); bool bProvideDTD = false; bool bAcceptNonemptyVersion = false; @@ -293,7 +309,12 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con // now write individual file entries for (const uno::Sequence<beans::PropertyValue>& rSequence : rManList) { + if (&rSequence == pRootFolderPropSeq && isWholesomeEncryption) + { + continue; // no root document, but embedded package => omit + } rtl::Reference<::comphelper::AttributeList> pAttrList = new ::comphelper::AttributeList; + OUString fullPath; OUString aString; const uno::Any *pVector = nullptr, *pSalt = nullptr, *pIterationCount = nullptr, *pDigest = nullptr, *pDigestAlg = nullptr, *pEncryptAlg = nullptr, *pStartKeyAlg = nullptr, *pDerivedKeySize = nullptr; for (const beans::PropertyValue& rValue : rSequence) @@ -312,8 +333,8 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con } else if (rValue.Name == sFullPathProperty ) { - rValue.Value >>= aString; - pAttrList->AddAttribute ( ATTRIBUTE_FULL_PATH, aString ); + rValue.Value >>= fullPath; + pAttrList->AddAttribute(ATTRIBUTE_FULL_PATH, fullPath); } else if (rValue.Name == sSizeProperty ) { @@ -338,6 +359,11 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con else if (rValue.Name == sDerivedKeySizeProperty ) pDerivedKeySize = &rValue.Value; } + assert(!fullPath.isEmpty()); + if (isWholesomeEncryption) + { // there may be signatures in META-INF too + assert(fullPath == "encrypted-package" || fullPath.startsWith("META-INF/")); + } xHandler->ignorableWhitespace ( sWhiteSpace ); xHandler->startElement( ELEMENT_FILE_ENTRY , pAttrList); @@ -390,6 +416,7 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con } else if (nEncAlgID == xml::crypto::CipherID::AES_GCM_W3C) { + assert(bStoreStartKeyGeneration || pKeyInfoProperty); SAL_WARN_IF(nDerivedKeySize != 32, "package.manifest", "Unexpected key size is provided!"); if (nDerivedKeySize != 32) { diff --git a/package/source/zipapi/ZipFile.cxx b/package/source/zipapi/ZipFile.cxx index 664cfa7f1e17..bdcd8610be60 100644 --- a/package/source/zipapi/ZipFile.cxx +++ b/package/source/zipapi/ZipFile.cxx @@ -647,7 +647,8 @@ uno::Reference< XInputStream > ZipFile::createStreamForZipEntry( #ifndef EMSCRIPTEN static const sal_Int32 nThreadingThreshold = 10000; - if( xSrcStream->available() > nThreadingThreshold ) + // "encrypted-package" is the only data stream, no point in threading it + if (rEntry.sPath != "encrypted-package" && nThreadingThreshold < xSrcStream->available()) xBufStream = new XBufferedThreadedStream(xSrcStream, xSrcStream->getSize()); else #endif diff --git a/package/source/zippackage/ZipPackage.cxx b/package/source/zippackage/ZipPackage.cxx index 1bae902c177a..f95731f368d1 100644 --- a/package/source/zippackage/ZipPackage.cxx +++ b/package/source/zippackage/ZipPackage.cxx @@ -417,25 +417,36 @@ void ZipPackage::parseManifest() } } - if ( !bManifestParsed ) + if (!bManifestParsed || m_xRootFolder->GetMediaType().isEmpty()) { // the manifest.xml could not be successfully parsed, this is an inconsistent package if ( aPackageMediatype.startsWith("application/vnd.") ) { // accept only types that look similar to own mediatypes m_xRootFolder->SetMediaType( aPackageMediatype ); - m_bMediaTypeFallbackUsed = true; + // if there is an encrypted inner package, there is no root + // document, because instead there is a package, and it is not + // an error + if (!m_xRootFolder->hasByName("encrypted-package")) + { + m_bMediaTypeFallbackUsed = true; + } } } else if ( !m_bForceRecovery ) { - // the mimetype stream should contain the information from manifest.xml - if ( m_xRootFolder->GetMediaType() != aPackageMediatype ) + // the mimetype stream should contain the same information as manifest.xml + OUString const mediaTypeXML(m_xRootFolder->hasByName("encrypted-package") + ? m_xRootFolder->doGetByName("encrypted-package").xPackageEntry->GetMediaType() + : m_xRootFolder->GetMediaType()); + if (mediaTypeXML != aPackageMediatype) + { throw ZipIOException( THROW_WHERE "mimetype conflicts with manifest.xml, \"" - + m_xRootFolder->GetMediaType() + "\" vs. \"" + + mediaTypeXML + "\" vs. \"" + aPackageMediatype + "\"" ); + } } m_xRootFolder->removeByName( sMimetype ); @@ -1269,6 +1280,8 @@ uno::Reference< io::XInputStream > ZipPackage::writeTempFile() static constexpr OUStringLiteral sFullPath(u"FullPath"); const bool bIsGpgEncrypt = m_aGpgProps.hasElements(); + // note: this is always created here (needed for GPG), possibly + // filtered out later in ManifestExport if ( m_nFormat == embed::StorageFormats::PACKAGE ) { uno::Sequence < PropertyValue > aPropSeq( |