CVE-2020-12801 tdf#118639: store ODF encryption data for autorecovery
authorMike Kaganski <mike.kaganski@collabora.com>
Fri, 29 Nov 2019 10:07:57 +0000 (13:07 +0300)
committerBastien Roucariès <rouca@debian.org>
Fri, 29 Dec 2023 09:39:36 +0000 (09:39 +0000)
When saving autorecovery information, ODF is used. If the original
document is password-protected, its autorecovery is also generated
password-protected (since ef87ff6680f79362a431db6e7ef2f40cfc576219).
But when the stored encryption data for non-ODF document does not
contain "PackageSHA256UTF8EncryptionKey" value, following
ZipPackage::GetEncryptionKey fails, so the whole save fails.

So just generate and append ODF encryption keys where we still have
user password.

Reviewed-on: https://gerrit.libreoffice.org/84052
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
(cherry picked from commit 63634738dd03cc74806ce6843c16ff5e51a371a0)
Reviewed-on: https://gerrit.libreoffice.org/84133
Reviewed-by: Xisco Faulí <xiscofauli@libreoffice.org>
(cherry picked from commit e569dc9824e95617d921bb8f115d243aea0125b9)
Reviewed-on: https://gerrit.libreoffice.org/84232
Reviewed-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
(cherry picked from commit d1450f5bddd0f108078e0dfb11c9f130175fafe7)

Conflicts:
comphelper/source/misc/docpasswordhelper.cxx

Change-Id: I776e28de784489521e4941d1075690f90c056014

origin: https://github.com/LibreOffice/core/commit/b838986c6edfb4e5fb1e30bf47d8ad54ae2b4098.patch

Gbp-Pq: Name 0095-CVE-2020-12801-tdf-118639-store-ODF-encryption-data-.patch

comphelper/source/misc/docpasswordhelper.cxx
sfx2/source/dialog/filedlghelper.cxx

index 3f470520fbf5e0384d0e1f1fdab066ae6f9bb250..f75997cfe440d1275dfd0e87005c9df3eee1bc8d 100644 (file)
@@ -426,6 +426,7 @@ OUString DocPasswordHelper::GetOoxHashAsBase64(
         bool* pbIsDefaultPassword )
 {
     css::uno::Sequence< css::beans::NamedValue > aEncData;
+    OUString aPassword;
     DocPasswordVerifierResult eResult = DocPasswordVerifierResult::WrongPassword;
 
     // first, try provided default passwords
@@ -439,8 +440,12 @@ OUString DocPasswordHelper::GetOoxHashAsBase64(
             if( !aIt->isEmpty() )
             {
                 eResult = rVerifier.verifyPassword( *aIt, aEncData );
-                if( pbIsDefaultPassword )
-                    *pbIsDefaultPassword = eResult == DocPasswordVerifierResult::OK;
+                if (eResult == DocPasswordVerifierResult::OK)
+                {
+                    aPassword = *aIt;
+                    if (pbIsDefaultPassword)
+                        *pbIsDefaultPassword = true;
+                }
             }
         }
     }
@@ -460,7 +465,11 @@ OUString DocPasswordHelper::GetOoxHashAsBase64(
     if( eResult == DocPasswordVerifierResult::WrongPassword )
     {
         if( !rMediaPassword.isEmpty() )
+        {
             eResult = rVerifier.verifyPassword( rMediaPassword, aEncData );
+            if (eResult == DocPasswordVerifierResult::OK)
+                aPassword = rMediaPassword;
+        }
     }
 
     // request a password (skip, if result is OK or ABORT)
@@ -476,6 +485,8 @@ OUString DocPasswordHelper::GetOoxHashAsBase64(
             {
                 if( !pRequest->getPassword().isEmpty() )
                     eResult = rVerifier.verifyPassword( pRequest->getPassword(), aEncData );
+                if (eResult == DocPasswordVerifierResult::OK)
+                    aPassword = pRequest->getPassword();
             }
             else
             {
@@ -488,6 +499,21 @@ OUString DocPasswordHelper::GetOoxHashAsBase64(
     {
     }
 
+    if (eResult == DocPasswordVerifierResult::OK && !aPassword.isEmpty())
+    {
+        if (std::find_if(std::cbegin(aEncData), std::cend(aEncData),
+                         [](const css::beans::NamedValue& val) {
+                             return val.Name == PACKAGE_ENCRYPTIONDATA_SHA256UTF8;
+                         })
+            == std::cend(aEncData))
+        {
+            // tdf#118639: We need ODF encryption data for autorecovery, where password
+            // will already be unavailable, so generate and append it here
+            aEncData = comphelper::concatSequences(
+                aEncData, OStorageHelper::CreatePackageEncryptionData(aPassword));
+        }
+    }
+
     return (eResult == DocPasswordVerifierResult::OK) ? aEncData : uno::Sequence< beans::NamedValue >();
 }
 
index 5e4055195eb515009f28de4310c9fd29dec5f102..eaf7f484ae8514f8a3dd9fa5a71fd47bd625be6d 100644 (file)
@@ -2704,6 +2704,8 @@ ErrCode RequestPassword(const std::shared_ptr<const SfxFilter>& pCurrentFilter,
     {
         if ( pPasswordRequest->getPassword().getLength() )
         {
+            css::uno::Sequence< css::beans::NamedValue > aEncryptionData;
+
             // TODO/LATER: The filters should show the password dialog themself in future
             if ( bMSType )
             {
@@ -2712,7 +2714,7 @@ ErrCode RequestPassword(const std::shared_ptr<const SfxFilter>& pCurrentFilter,
                 {
                     ::comphelper::SequenceAsHashMap aHashData;
                     aHashData[ OUString( "OOXPassword"  ) ] <<= pPasswordRequest->getPassword();
-                    pSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aHashData.getAsConstNamedValueList() ) ) );
+                    aEncryptionData = aHashData.getAsConstNamedValueList();
                 }
                 else
                 {
@@ -2725,7 +2727,7 @@ ErrCode RequestPassword(const std::shared_ptr<const SfxFilter>& pCurrentFilter,
                         aHashData[ OUString( "STD97EncryptionKey"  ) ] <<= aEncryptionKey;
                         aHashData[ OUString( "STD97UniqueID"  ) ] <<= aUniqueID;
 
-                        pSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aHashData.getAsConstNamedValueList() ) ) );
+                        aEncryptionData = aHashData.getAsConstNamedValueList();
                     }
                     else
                     {
@@ -2733,10 +2735,14 @@ ErrCode RequestPassword(const std::shared_ptr<const SfxFilter>& pCurrentFilter,
                     }
                 }
             }
-            else
-            {
-                pSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( ::comphelper::OStorageHelper::CreatePackageEncryptionData( pPasswordRequest->getPassword() ) ) ) );
-            }
+
+            // tdf#118639: We need ODF encryption data for autorecovery where password will already
+            // be unavailable, even for non-ODF documents, so append it here unconditionally
+            pSet->Put(SfxUnoAnyItem(
+                SID_ENCRYPTIONDATA,
+                uno::makeAny(comphelper::concatSequences(
+                    aEncryptionData, comphelper::OStorageHelper::CreatePackageEncryptionData(
+                                         pPasswordRequest->getPassword())))));
         }
 
         if ( pPasswordRequest->getRecommendReadOnly() )