Blame SOURCES/0002-CVE-2022-26307-make-hash-encoding-match-decoding.patch

2f4c65
From 780c42cdd8006dc60e281be2fe6566f101e909bc Mon Sep 17 00:00:00 2001
2f4c65
From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= <caolanm@redhat.com>
2f4c65
Date: Mon, 21 Mar 2022 20:58:34 +0000
2f4c65
Subject: [PATCH 2/5] CVE-2022-26307 make hash encoding match decoding
2f4c65
2f4c65
Seeing as old versions of the hash may be in the users config, add a
2f4c65
StorageVersion field to the office config Passwords section which
2f4c65
defaults to 0 to indicate the old hash is in use.
2f4c65
2f4c65
Try the old varient when StorageVersion is 0. When a new encoded master
2f4c65
password it set write StorageVersion of 1 to indicate a new hash is in
2f4c65
use and use the new style when StorageVersion is 1.
2f4c65
2f4c65
Change-Id: I3174c37a5891bfc849984e0ec5c2c392b9c6e7b1
2f4c65
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132080
2f4c65
Tested-by: Jenkins
2f4c65
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2f4c65
(cherry picked from commit e890f54dbac57f3ab5acf4fbd31222095d3e8ab6)
2f4c65
---
2f4c65
 .../schema/org/openoffice/Office/Common.xcs   |  6 +++
2f4c65
 .../passwordcontainer/passwordcontainer.cxx   | 45 +++++++++++++++++--
2f4c65
 .../passwordcontainer/passwordcontainer.hxx   |  6 +++
2f4c65
 uui/source/iahndl-authentication.cxx          |  5 ++-
2f4c65
 4 files changed, 57 insertions(+), 5 deletions(-)
2f4c65
2f4c65
diff --git a/officecfg/registry/schema/org/openoffice/Office/Common.xcs b/officecfg/registry/schema/org/openoffice/Office/Common.xcs
2f4c65
index 9097c23c3c6a..922efc33cca7 100644
2f4c65
--- a/officecfg/registry/schema/org/openoffice/Office/Common.xcs
2f4c65
+++ b/officecfg/registry/schema/org/openoffice/Office/Common.xcs
2f4c65
@@ -942,6 +942,12 @@
2f4c65
         </info>
2f4c65
         <value>false</value>
2f4c65
       </prop>
2f4c65
+      <prop oor:name="StorageVersion" oor:type="xs:int" oor:nillable="false">
2f4c65
+        <info>
2f4c65
+          <desc>Specifies what version of encoding scheme the password container uses.</desc>
2f4c65
+        </info>
2f4c65
+        <value>0</value>
2f4c65
+      </prop>
2f4c65
       <prop oor:name="HasMaster" oor:type="xs:boolean" oor:nillable="false">
2f4c65
         <info>
2f4c65
           <desc>Specifies if there is a valid master password.</desc>
2f4c65
diff --git a/svl/source/passwordcontainer/passwordcontainer.cxx b/svl/source/passwordcontainer/passwordcontainer.cxx
2f4c65
index 51fb129cddb1..b674844f91d3 100644
2f4c65
--- a/svl/source/passwordcontainer/passwordcontainer.cxx
2f4c65
+++ b/svl/source/passwordcontainer/passwordcontainer.cxx
2f4c65
@@ -17,7 +17,6 @@
2f4c65
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
2f4c65
  */
2f4c65
 
2f4c65
-
2f4c65
 #include "passwordcontainer.hxx"
2f4c65
 
2f4c65
 #include <cppuhelper/factory.hxx>
2f4c65
@@ -259,6 +258,23 @@ bool StorageItem::useStorage()
2f4c65
     return aResult;
2f4c65
 }
2f4c65
 
2f4c65
+sal_Int32 StorageItem::getStorageVersion()
2f4c65
+{
2f4c65
+    Sequence<OUString> aNodeNames { "StorageVersion" };
2f4c65
+
2f4c65
+    Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aNodeNames );
2f4c65
+
2f4c65
+    if( aPropertyValues.getLength() != aNodeNames.getLength() )
2f4c65
+    {
2f4c65
+        OSL_FAIL( "Problems during reading" );
2f4c65
+        return 0;
2f4c65
+    }
2f4c65
+
2f4c65
+    sal_Int32 nResult = 0;
2f4c65
+    aPropertyValues[0] >>= nResult;
2f4c65
+
2f4c65
+    return nResult;
2f4c65
+}
2f4c65
 
2f4c65
 bool StorageItem::getEncodedMP( OUString& aResult )
2f4c65
 {
2f4c65
@@ -291,15 +307,17 @@ bool StorageItem::getEncodedMP( OUString& aResult )
2f4c65
 
2f4c65
 void StorageItem::setEncodedMP( const OUString& aEncoded, bool bAcceptEmpty )
2f4c65
 {
2f4c65
-    Sequence< OUString > sendNames(2);
2f4c65
-    Sequence< uno::Any > sendVals(2);
2f4c65
+    Sequence< OUString > sendNames(3);
2f4c65
+    Sequence< uno::Any > sendVals(3);
2f4c65
 
2f4c65
     sendNames[0] = "HasMaster";
2f4c65
     sendNames[1] = "Master";
2f4c65
+    sendNames[2] = "StorageVersion";
2f4c65
 
2f4c65
     bool bHasMaster = ( !aEncoded.isEmpty() || bAcceptEmpty );
2f4c65
     sendVals[0] <<= bHasMaster;
2f4c65
     sendVals[1] <<= aEncoded;
2f4c65
+    sendVals[2] <<= nCurrentStorageVersion;
2f4c65
 
2f4c65
     ConfigItem::SetModified();
2f4c65
     ConfigItem::PutProperties( sendNames, sendVals );
2f4c65
@@ -800,6 +818,18 @@ OUString PasswordContainer::RequestPasswordFromUser( PasswordRequestMode aRMode,
2f4c65
     return aResult;
2f4c65
 }
2f4c65
 
2f4c65
+// Mangle the key to match an old bug
2f4c65
+static OUString ReencodeAsOldHash(const OUString& rPass)
2f4c65
+{
2f4c65
+    OUStringBuffer aBuffer;
2f4c65
+    for (int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ++ind)
2f4c65
+    {
2f4c65
+        unsigned char i = static_cast<char>(rPass.copy(ind * 2, 2).toUInt32(16));
2f4c65
+        aBuffer.append(static_cast< sal_Unicode >('a' + (i >> 4)));
2f4c65
+        aBuffer.append(static_cast< sal_Unicode >('a' + (i & 15)));
2f4c65
+    }
2f4c65
+    return aBuffer.makeStringAndClear();
2f4c65
+}
2f4c65
 
2f4c65
 OUString const & PasswordContainer::GetMasterPassword( const Reference< XInteractionHandler >& aHandler )
2f4c65
 {
2f4c65
@@ -838,6 +868,9 @@ OUString const & PasswordContainer::GetMasterPassword( const Reference< XInterac
2f4c65
                     }
2f4c65
                     else
2f4c65
                     {
2f4c65
+                        if (m_pStorageFile->getStorageVersion() == 0)
2f4c65
+                            aPass = ReencodeAsOldHash(aPass);
2f4c65
+
2f4c65
                         std::vector< OUString > aRM( DecodePasswords( aEncodedMP, aPass, aRMode ) );
2f4c65
                         if( aRM.empty() || aPass != aRM[0] )
2f4c65
                         {
2f4c65
@@ -1042,6 +1075,12 @@ sal_Bool SAL_CALL PasswordContainer::authorizateWithMasterPassword( const uno::R
2f4c65
 
2f4c65
                 do {
2f4c65
                     aPass = RequestPasswordFromUser( aRMode, xTmpHandler );
2f4c65
+
2f4c65
+                    if (!aPass.isEmpty() && m_pStorageFile->getStorageVersion() == 0)
2f4c65
+                    {
2f4c65
+                        aPass = ReencodeAsOldHash(aPass);
2f4c65
+                    }
2f4c65
+
2f4c65
                     bResult = ( !aPass.isEmpty() && aPass == m_aMasterPasswd );
2f4c65
                     aRMode = PasswordRequestMode_PASSWORD_REENTER; // further questions with error notification
2f4c65
                 } while( !bResult && !aPass.isEmpty() );
2f4c65
diff --git a/svl/source/passwordcontainer/passwordcontainer.hxx b/svl/source/passwordcontainer/passwordcontainer.hxx
2f4c65
index 46ffec888602..bf43b5903602 100644
2f4c65
--- a/svl/source/passwordcontainer/passwordcontainer.hxx
2f4c65
+++ b/svl/source/passwordcontainer/passwordcontainer.hxx
2f4c65
@@ -168,6 +168,10 @@ public:
2f4c65
 typedef ::std::pair< const OUString, ::std::vector< NamePassRecord > > PairUrlRecord;
2f4c65
 typedef ::std::map< OUString, ::std::vector< NamePassRecord > > PassMap;
2f4c65
 
2f4c65
+// org.openoffice.Office.Common/Passwords/StorageVersion bump if details of
2f4c65
+// how password details are saved changes. Enables migration from previous
2f4c65
+// schemes.
2f4c65
+constexpr sal_Int32 nCurrentStorageVersion = 1;
2f4c65
 
2f4c65
 class PasswordContainer;
2f4c65
 
2f4c65
@@ -196,6 +200,8 @@ public:
2f4c65
     void remove( const OUString& url, const OUString& rec );
2f4c65
     void clear();
2f4c65
 
2f4c65
+    sal_Int32 getStorageVersion();
2f4c65
+
2f4c65
     bool getEncodedMP( OUString& aResult );
2f4c65
     void setEncodedMP( const OUString& aResult, bool bAcceptEmpty = false );
2f4c65
     void setUseStorage( bool bUse );
2f4c65
diff --git a/uui/source/iahndl-authentication.cxx b/uui/source/iahndl-authentication.cxx
2f4c65
index ad975d3f9ae7..951f0b8a1c6b 100644
2f4c65
--- a/uui/source/iahndl-authentication.cxx
2f4c65
+++ b/uui/source/iahndl-authentication.cxx
2f4c65
@@ -436,8 +436,9 @@ executeMasterPasswordDialog(
2f4c65
     OUStringBuffer aBuffer;
2f4c65
     for (sal_uInt8 i : aKey)
2f4c65
     {
2f4c65
-        aBuffer.append(static_cast< sal_Unicode >('a' + (i >> 4)));
2f4c65
-        aBuffer.append(static_cast< sal_Unicode >('a' + (i & 15)));
2f4c65
+        // match PasswordContainer::DecodePasswords aMasterPasswd.copy(index * 2, 2).toUInt32(16));
2f4c65
+        aBuffer.append(OUString::number(i >> 4, 16));
2f4c65
+        aBuffer.append(OUString::number(i & 15, 16));
2f4c65
     }
2f4c65
     rInfo.SetPassword(aBuffer.makeStringAndClear());
2f4c65
 }
2f4c65
-- 
2f4c65
2.37.3
2f4c65