CVE-2022-26307: make hash encoding match decoding
authorCaolán McNamara <caolanm@redhat.com>
Mon, 21 Mar 2022 20:58:34 +0000 (20:58 +0000)
committerBastien Roucariès <rouca@debian.org>
Sat, 25 Mar 2023 10:55:37 +0000 (10:55 +0000)
Seeing as old versions of the hash may be in the users config, add a
StorageVersion field to the office config Passwords section which
defaults to 0 to indicate the old hash is in use.

Try the old varient when StorageVersion is 0. When a new encoded master
password it set write StorageVersion of 1 to indicate a new hash is in
use and use the new style when StorageVersion is 1.

Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132080
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
(cherry picked from commit e890f54dbac57f3ab5acf4fbd31222095d3e8ab6)

svl: fix crash if user cancels/closes master password dialog

(regression from d7ba5614d90381d68f880ca7e7c5ef8bbb1b1c43)

Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133932
Tested-by: Jenkins
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
(cherry picked from commit bbb8617ece6d946957c2eb96287081029bce530f)

Change-Id: I3174c37a5891bfc849984e0ec5c2c392b9c6e7b1
(cherry picked from commit 7e35d53f51bb89ed3cea5f946214afb7d81e1b1e)
origin: https://github.com/LibreOffice/core/commit/c17ba8306704d6d428d673fb0079c4276f0bc256.patch
bug-debian-security: https://security-tracker.debian.org/tracker/CVE-2022-26307
bug: https://www.libreoffice.org/about-us/security/advisories/cve-2022-26307
Signed-off-by: Bastien Roucariès <rouca@debian.org>
Gbp-Pq: Name 0067-CVE-2022-26307-make-hash-encoding-match-decoding.patch

officecfg/registry/schema/org/openoffice/Office/Common.xcs
svl/source/passwordcontainer/passwordcontainer.cxx
svl/source/passwordcontainer/passwordcontainer.hxx
uui/source/iahndl-authentication.cxx

index 97658dc03d998c18901501cd75b6ba7559d884f5..bb04f7ba8d2167e60f4ef52ccf2751adf7f92900 100644 (file)
Binary files a/officecfg/registry/schema/org/openoffice/Office/Common.xcs and b/officecfg/registry/schema/org/openoffice/Office/Common.xcs differ
index 514a01eb2ea5305bf7e6a9e4daf5877b52ed06fd..cc45406175377c3d4aaa51924f32d2daeb00e700 100644 (file)
@@ -17,6 +17,8 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
+#include <sal/config.h>
+#include <sal/log.hxx>
 
 #include "passwordcontainer.hxx"
 
@@ -36,6 +38,7 @@
 #include <rtl/cipher.h>
 #include <rtl/digest.h>
 #include <rtl/byteseq.hxx>
+#include <rtl/ustrbuf.hxx>
 
 using namespace osl;
 using namespace utl;
@@ -261,6 +264,23 @@ bool StorageItem::useStorage()
     return aResult;
 }
 
+sal_Int32 StorageItem::getStorageVersion()
+{
+    Sequence<OUString> aNodeNames { "StorageVersion" };
+
+    Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aNodeNames );
+
+    if( aPropertyValues.getLength() != aNodeNames.getLength() )
+    {
+        OSL_FAIL( "Problems during reading" );
+        return 0;
+    }
+
+    sal_Int32 nResult = 0;
+    aPropertyValues[0] >>= nResult;
+
+    return nResult;
+}
 
 bool StorageItem::getEncodedMP( OUString& aResult )
 {
@@ -293,15 +313,17 @@ bool StorageItem::getEncodedMP( OUString& aResult )
 
 void StorageItem::setEncodedMP( const OUString& aEncoded, bool bAcceptEmpty )
 {
-    Sequence< OUString > sendNames(2);
-    Sequence< uno::Any > sendVals(2);
+    Sequence< OUString > sendNames(3);
+    Sequence< uno::Any > sendVals(3);
 
     sendNames[0] = "HasMaster";
     sendNames[1] = "Master";
+    sendNames[2] = "StorageVersion";
 
     bool bHasMaster = ( !aEncoded.isEmpty() || bAcceptEmpty );
     sendVals[0] <<= bHasMaster;
     sendVals[1] <<= aEncoded;
+    sendVals[2] <<= nCurrentStorageVersion;
 
     ConfigItem::SetModified();
     ConfigItem::PutProperties( sendNames, sendVals );
@@ -803,6 +825,18 @@ OUString PasswordContainer::RequestPasswordFromUser( PasswordRequestMode aRMode,
     return aResult;
 }
 
+// Mangle the key to match an old bug
+static OUString ReencodeAsOldHash(const OUString& rPass)
+{
+    OUStringBuffer aBuffer;
+    for (int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ++ind)
+    {
+        unsigned char i = static_cast<char>(rPass.copy(ind * 2, 2).toUInt32(16));
+        aBuffer.append(static_cast< sal_Unicode >('a' + (i >> 4)));
+        aBuffer.append(static_cast< sal_Unicode >('a' + (i & 15)));
+    }
+    return aBuffer.makeStringAndClear();
+}
 
 OUString const & PasswordContainer::GetMasterPassword( const Reference< XInteractionHandler >& aHandler )
 {
@@ -841,6 +875,9 @@ OUString const & PasswordContainer::GetMasterPassword( const Reference< XInterac
                     }
                     else
                     {
+                        if (m_pStorageFile->getStorageVersion() == 0)
+                            aPass = ReencodeAsOldHash(aPass);
+
                         std::vector< OUString > aRM( DecodePasswords( aEncodedMP, aPass, aRMode ) );
                         if( aRM.empty() || aPass != aRM[0] )
                         {
@@ -1045,6 +1082,13 @@ sal_Bool SAL_CALL PasswordContainer::authorizateWithMasterPassword( const uno::R
 
                 do {
                     aPass = RequestPasswordFromUser( aRMode, xTmpHandler );
+
+
+                    if (!aPass.isEmpty() && m_pStorageFile->getStorageVersion() == 0)
+                    {
+                        aPass = ReencodeAsOldHash(aPass);
+                    }
+
                     bResult = ( !aPass.isEmpty() && aPass == m_aMasterPasswd );
                     aRMode = PasswordRequestMode_PASSWORD_REENTER; // further questions with error notification
                 } while( !bResult && !aPass.isEmpty() );
index 3da1e606d4d1b5ebcc509f7ec3f2718bb5ee4db9..c947c8534876a618b4c4cd7d0b5a101b57053cda 100644 (file)
@@ -168,6 +168,10 @@ public:
 typedef ::std::pair< const OUString, ::std::vector< NamePassRecord > > PairUrlRecord;
 typedef ::std::map< OUString, ::std::vector< NamePassRecord > > PassMap;
 
+// org.openoffice.Office.Common/Passwords/StorageVersion bump if details of
+// how password details are saved changes. Enables migration from previous
+// schemes.
+constexpr sal_Int32 nCurrentStorageVersion = 1;
 
 class PasswordContainer;
 
@@ -196,6 +200,8 @@ public:
     void remove( const OUString& url, const OUString& rec );
     void clear();
 
+    sal_Int32 getStorageVersion();
+
     bool getEncodedMP( OUString& aResult );
     void setEncodedMP( const OUString& aResult, bool bAcceptEnmpty = false );
     void setUseStorage( bool bUse );
index ddaaf10e5e525fff50c0c35dfe88480179d32c33..a4742b59dcbc772b28bbb53fadd5025e4f41d65f 100644 (file)
@@ -436,8 +436,9 @@ executeMasterPasswordDialog(
     OUStringBuffer aBuffer;
     for (sal_uInt8 i : aKey)
     {
-        aBuffer.append(static_cast< sal_Unicode >('a' + (i >> 4)));
-        aBuffer.append(static_cast< sal_Unicode >('a' + (i & 15)));
+        // match PasswordContainer::DecodePasswords aMasterPasswd.copy(index * 2, 2).toUInt32(16));
+        aBuffer.append(OUString::number(i >> 4, 16));
+        aBuffer.append(OUString::number(i & 15, 16));
     }
     rInfo.SetPassword(aBuffer.makeStringAndClear());
 }