From 235d50d41b1e278812b5e659153c387491264185 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Caol=C3=A1n=20McNamara?= Date: Mon, 21 Mar 2022 20:58:34 +0000 Subject: [PATCH] CVE-2022-26307: make hash encoding match decoding MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 (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 (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 Gbp-Pq: Name 0067-CVE-2022-26307-make-hash-encoding-match-decoding.patch --- .../schema/org/openoffice/Office/Common.xcs | Bin 258007 -> 258249 bytes .../passwordcontainer/passwordcontainer.cxx | 48 +++++++++++++++++- .../passwordcontainer/passwordcontainer.hxx | 6 +++ uui/source/iahndl-authentication.cxx | 5 +- 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/officecfg/registry/schema/org/openoffice/Office/Common.xcs b/officecfg/registry/schema/org/openoffice/Office/Common.xcs index 97658dc03d998c18901501cd75b6ba7559d884f5..bb04f7ba8d2167e60f4ef52ccf2751adf7f92900 100644 GIT binary patch delta 120 zcmcb9pa0}T{te%Xxr0mcixSgQ!%~ZiryIsFnND92&#Y)!o{?ChPzDss%+FKEPg6+E zOU_Tp%u81&PR>ZpO;sq#NL46EEG{n3FG^8J&d)1J%*;zIQYbB+u5g-BWpcnIw(0(X W%zDk=irc>xGXgQw_HV_^532!o>@es6 delta 26 hcmX^4kpKF9{te%Xn_rZ)zbIh@Vy5jcN|+y30|3xl4zd6M diff --git a/svl/source/passwordcontainer/passwordcontainer.cxx b/svl/source/passwordcontainer/passwordcontainer.cxx index 514a01eb2ea..cc454061753 100644 --- a/svl/source/passwordcontainer/passwordcontainer.cxx +++ b/svl/source/passwordcontainer/passwordcontainer.cxx @@ -17,6 +17,8 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ +#include +#include #include "passwordcontainer.hxx" @@ -36,6 +38,7 @@ #include #include #include +#include using namespace osl; using namespace utl; @@ -261,6 +264,23 @@ bool StorageItem::useStorage() return aResult; } +sal_Int32 StorageItem::getStorageVersion() +{ + Sequence 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(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() ); diff --git a/svl/source/passwordcontainer/passwordcontainer.hxx b/svl/source/passwordcontainer/passwordcontainer.hxx index 3da1e606d4d..c947c853487 100644 --- a/svl/source/passwordcontainer/passwordcontainer.hxx +++ b/svl/source/passwordcontainer/passwordcontainer.hxx @@ -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 ); diff --git a/uui/source/iahndl-authentication.cxx b/uui/source/iahndl-authentication.cxx index ddaaf10e5e5..a4742b59dcb 100644 --- a/uui/source/iahndl-authentication.cxx +++ b/uui/source/iahndl-authentication.cxx @@ -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()); } -- 2.30.2