configure.ac | 2 include/sfx2/strings.hrc | 2 include/sfx2/viewfrm.hxx | 1 officecfg/registry/schema/org/openoffice/Office/Common.xcs | 16 + sfx2/source/view/viewfrm.cxx | 40 ++ svl/source/passwordcontainer/passwordcontainer.cxx | 176 +++++++++---- svl/source/passwordcontainer/passwordcontainer.hxx | 69 ++++- uui/source/iahndl-authentication.cxx | 5 xmlsecurity/source/component/documentdigitalsignatures.cxx | 35 +- 9 files changed, 274 insertions(+), 72 deletions(-)
New commits: commit 86c57d3e6826169d5be53472fa21590fc8364748 Author: Andras Timar <[email protected]> AuthorDate: Tue Jul 26 15:19:06 2022 +0200 Commit: Andras Timar <[email protected]> CommitDate: Tue Jul 26 15:40:02 2022 +0200 Bump version to 6.2.8.2.M9 Change-Id: I87168a2e9be351774c53bb1ba49dbcc3bd6687fb diff --git a/configure.ac b/configure.ac index 73e4cbfc2b7f..44144ae01383 100644 --- a/configure.ac +++ b/configure.ac @@ -9,7 +9,7 @@ dnl in order to create a configure script. # several non-alphanumeric characters, those are split off and used only for the # ABOUTBOXPRODUCTVERSIONSUFFIX in openoffice.lst. Why that is necessary, no idea. -AC_INIT([LibreOffice],[6.2.8.2.M8],[],[],[http://documentfoundation.org/]) +AC_INIT([LibreOffice],[6.2.8.2.M9],[],[],[http://documentfoundation.org/]) AC_PREREQ([2.59]) commit feeeff8b228c34680373f2e3fe5b9b6cd7786731 Author: Caolán McNamara <[email protected]> AuthorDate: Wed Mar 23 13:03:30 2022 +0000 Commit: Andras Timar <[email protected]> CommitDate: Tue Jul 26 15:40:02 2022 +0200 CVE-2022-2630[6|7] add infobar to prompt to refresh to replace old format Change-Id: Id99cbf2b50a4ebf289dae6fc67e22e20afcda35b Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131976 Tested-by: Jenkins Reviewed-by: Michael Stahl <[email protected]> (cherry picked from commit bbd196ff82bda9f66b4ba32a412f10cefe6da60e) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132307 Reviewed-by: Sophie Gautier <[email protected]> Reviewed-by: Christian Lohmaier <[email protected]> (cherry picked from commit c5d01b11db3c83cb4a89d3b388d78e20dd3990b5) diff --git a/include/sfx2/strings.hrc b/include/sfx2/strings.hrc index a77dbc86d77d..c196bcd19989 100644 --- a/include/sfx2/strings.hrc +++ b/include/sfx2/strings.hrc @@ -266,6 +266,8 @@ #define STR_SIGNATURE_NOTVALIDATED_PARTIAL_OK NC_("STR_SIGNATURE_NOTVALIDATED_PARTIAL_OK", "The certificate could not be validated and the document is only partially signed.") #define STR_SIGNATURE_OK NC_("STR_SIGNATURE_OK", "This document is digitally signed and the signature is valid.") #define STR_SIGNATURE_SHOW NC_("STR_SIGNATURE_SHOW", "Show Signatures") +#define STR_REFRESH_MASTER_PASSWORD NC_("STR_REFRESH_MASTER_PASSWORD", "The master password is stored in an outdated format, you should refresh it") +#define STR_REFRESH_PASSWORD NC_("STR_REFRESH_PASSWORD", "Refresh Password") #define STR_CLOSE_PANE NC_("STR_CLOSE_PANE", "Close Pane") #define STR_SFX_DOCK NC_("STR_SFX_DOCK", "Dock") diff --git a/include/sfx2/viewfrm.hxx b/include/sfx2/viewfrm.hxx index 6d9ef75b8ffe..b78a0c92cd3a 100644 --- a/include/sfx2/viewfrm.hxx +++ b/include/sfx2/viewfrm.hxx @@ -80,6 +80,7 @@ protected: DECL_LINK(GetDonateHandler, Button*, void); DECL_LINK(SwitchReadOnlyHandler, Button*, void); DECL_LINK(SignDocumentHandler, Button*, void); + DECL_DLLPRIVATE_LINK(RefreshMasterPasswordHdl, Button*, void); SAL_DLLPRIVATE void KillDispatcher_Impl(); virtual ~SfxViewFrame() override; diff --git a/sfx2/source/view/viewfrm.cxx b/sfx2/source/view/viewfrm.cxx index 814a45f42b6f..e4666de79a27 100644 --- a/sfx2/source/view/viewfrm.cxx +++ b/sfx2/source/view/viewfrm.cxx @@ -30,6 +30,7 @@ #include <com/sun/star/frame/XLoadable.hpp> #include <com/sun/star/frame/XLayoutManager.hpp> #include <com/sun/star/frame/XComponentLoader.hpp> +#include <com/sun/star/task/PasswordContainer.hpp> #include <officecfg/Office/Common.hxx> #include <officecfg/Setup.hxx> #include <toolkit/helper/vclunohelper.hxx> @@ -1274,6 +1275,24 @@ void SfxViewFrame::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint ) batch->commit(); } + if (officecfg::Office::Common::Passwords::HasMaster::get() && + officecfg::Office::Common::Passwords::StorageVersion::get() == 0) + { + // master password stored in deprecated format + VclPtr<SfxInfoBarWindow> pOldMasterPasswordInfoBar = + AppendInfoBar("oldmasterpassword", + SfxResId(STR_REFRESH_MASTER_PASSWORD), InfoBarType::Danger); + if (pOldMasterPasswordInfoBar) + { + VclPtrInstance<PushButton> const xBtn(&GetWindow()); + xBtn->SetText(SfxResId(STR_REFRESH_PASSWORD)); + xBtn->SetSizePixel(xBtn->GetOptimalSize()); + xBtn->SetClickHdl(LINK(this, + SfxViewFrame, RefreshMasterPasswordHdl)); + pOldMasterPasswordInfoBar->addButton(xBtn); + } + } + // read-only infobar if necessary const SfxViewShell *pVSh; const SfxShell *pFSh; @@ -1423,6 +1442,27 @@ IMPL_LINK_NOARG(SfxViewFrame, SignDocumentHandler, Button*, void) GetDispatcher()->Execute(SID_SIGNATURE); } +IMPL_LINK_NOARG(SfxViewFrame, RefreshMasterPasswordHdl, Button*, void) +{ + bool bChanged = false; + try + { + Reference< task::XPasswordContainer2 > xMasterPasswd( + task::PasswordContainer::create(comphelper::getProcessComponentContext())); + + css::uno::Reference<css::frame::XFrame> xFrame = GetFrame().GetFrameInterface(); + css::uno::Reference<css::awt::XWindow> xContainerWindow = xFrame->getContainerWindow(); + + uno::Reference<task::XInteractionHandler> xTmpHandler(task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), + xContainerWindow)); + bChanged = xMasterPasswd->changeMasterPassword(xTmpHandler); + } + catch (const Exception&) + {} + if (bChanged) + RemoveInfoBar(u"oldmasterpassword"); +} + void SfxViewFrame::Construct_Impl( SfxObjectShell *pObjSh ) { m_pImpl->bResizeInToOut = true; commit cca849d3da95dfe76124053e46536471e4f9fb47 Author: Caolán McNamara <[email protected]> AuthorDate: Tue Mar 22 17:22:22 2022 +0000 Commit: Andras Timar <[email protected]> CommitDate: Tue Jul 26 15:18:24 2022 +0200 CVE-2022-26306 add Initialization Vectors to password storage old ones default to the current all zero case and continue to work as before Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131974 Tested-by: Jenkins Reviewed-by: Michael Stahl <[email protected]> (cherry picked from commit 192fa1e3bfc6269f2ebb91716471485a56074aea) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132306 Reviewed-by: Thorsten Behrens <[email protected]> (cherry picked from commit ab77587ec300f5c30084471000663c46ddf25dad) Change-Id: I6fe3b02fafcce1b5e7133e77e76a5118177d77af diff --git a/officecfg/registry/schema/org/openoffice/Office/Common.xcs b/officecfg/registry/schema/org/openoffice/Office/Common.xcs index cb1c57120731..34f441e9eedf 100644 --- a/officecfg/registry/schema/org/openoffice/Office/Common.xcs +++ b/officecfg/registry/schema/org/openoffice/Office/Common.xcs @@ -27,6 +27,11 @@ <info> <desc>Contains a container for passwords.</desc> </info> + <prop oor:name="InitializationVector" oor:type="xs:string"> + <info> + <desc>Contains an initialization vector for the password encryption.</desc> + </info> + </prop> <prop oor:name="Password" oor:type="xs:string" oor:localized="false"> <info> <desc>Contains a password encoded with the master password.</desc> @@ -915,6 +920,11 @@ </info> <value>false</value> </prop> + <prop oor:name="MasterInitializationVector" oor:type="xs:string"> + <info> + <desc>Contains an initialization vector for the master password encryption.</desc> + </info> + </prop> <prop oor:name="Master" oor:type="xs:string" oor:nillable="false"> <info> <desc>Contains the master password encrypted by itself.</desc> diff --git a/svl/source/passwordcontainer/passwordcontainer.cxx b/svl/source/passwordcontainer/passwordcontainer.cxx index e7a007a7342b..8b7cf0ad582c 100644 --- a/svl/source/passwordcontainer/passwordcontainer.cxx +++ b/svl/source/passwordcontainer/passwordcontainer.cxx @@ -182,23 +182,24 @@ PassMap StorageItem::getInfo() Sequence< OUString > aNodeNames = ConfigItem::GetNodeNames( "Store" ); sal_Int32 aNodeCount = aNodeNames.getLength(); - Sequence< OUString > aPropNames( aNodeCount ); - sal_Int32 aNodeInd; + Sequence< OUString > aPropNames( aNodeCount * 2); - for( aNodeInd = 0; aNodeInd < aNodeCount; ++aNodeInd ) - { - aPropNames[aNodeInd] = "Store/Passwordstorage['" + aNodeNames[aNodeInd] + "']/Password"; - } + std::transform(aNodeNames.begin(), aNodeNames.end(), aPropNames.begin(), + [](const OUString& rName) -> OUString { + return "Store/Passwordstorage['" + rName + "']/Password"; }); + std::transform(aNodeNames.begin(), aNodeNames.end(), aPropNames.getArray() + aNodeCount, + [](const OUString& rName) -> OUString { + return "Store/Passwordstorage['" + rName + "']/InitializationVector"; }); Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aPropNames ); - if( aPropertyValues.getLength() != aNodeNames.getLength() ) + if( aPropertyValues.getLength() != aNodeCount * 2) { - OSL_ENSURE( aPropertyValues.getLength() == aNodeNames.getLength(), "Problems during reading" ); + OSL_FAIL( "Problems during reading" ); return aResult; } - for( aNodeInd = 0; aNodeInd < aNodeCount; ++aNodeInd ) + for( sal_Int32 aNodeInd = 0; aNodeInd < aNodeCount; ++aNodeInd ) { std::vector< OUString > aUrlUsr = getInfoFromInd( aNodeNames[aNodeInd] ); @@ -208,14 +209,16 @@ PassMap StorageItem::getInfo() OUString aName = aUrlUsr[1]; OUString aEPasswd; + OUString aIV; aPropertyValues[aNodeInd] >>= aEPasswd; + aPropertyValues[aNodeInd + aNodeCount] >>= aIV; PassMap::iterator aIter = aResult.find( aUrl ); if( aIter != aResult.end() ) - aIter->second.emplace_back( aName, aEPasswd ); + aIter->second.emplace_back( aName, aEPasswd, aIV ); else { - NamePassRecord aNewRecord( aName, aEPasswd ); + NamePassRecord aNewRecord( aName, aEPasswd, aIV ); std::vector< NamePassRecord > listToAdd( 1, aNewRecord ); aResult.insert( PairUrlRecord( aUrl, listToAdd ) ); @@ -279,17 +282,19 @@ sal_Int32 StorageItem::getStorageVersion() return nResult; } -bool StorageItem::getEncodedMP( OUString& aResult ) +bool StorageItem::getEncodedMP( OUString& aResult, OUString& aResultIV ) { if( hasEncoded ) { aResult = mEncoded; + aResultIV = mEncodedIV; return true; } - Sequence< OUString > aNodeNames( 2 ); + Sequence< OUString > aNodeNames( 3 ); aNodeNames[0] = "HasMaster"; aNodeNames[1] = "Master"; + aNodeNames[2] = "MasterInitializationVector"; Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aNodeNames ); @@ -301,32 +306,37 @@ bool StorageItem::getEncodedMP( OUString& aResult ) aPropertyValues[0] >>= hasEncoded; aPropertyValues[1] >>= mEncoded; + aPropertyValues[2] >>= mEncodedIV; aResult = mEncoded; + aResultIV = mEncodedIV; return hasEncoded; } -void StorageItem::setEncodedMP( const OUString& aEncoded, bool bAcceptEmpty ) +void StorageItem::setEncodedMP( const OUString& aEncoded, const OUString& aEncodedIV, bool bAcceptEmpty ) { - Sequence< OUString > sendNames(3); - Sequence< uno::Any > sendVals(3); + Sequence< OUString > sendNames(4); + Sequence< uno::Any > sendVals(4); sendNames[0] = "HasMaster"; sendNames[1] = "Master"; - sendNames[2] = "StorageVersion"; + sendNames[2] = "MasterInitializationVector"; + sendNames[3] = "StorageVersion"; bool bHasMaster = ( !aEncoded.isEmpty() || bAcceptEmpty ); sendVals[0] <<= bHasMaster; sendVals[1] <<= aEncoded; - sendVals[2] <<= nCurrentStorageVersion; + sendVals[2] <<= aEncodedIV; + sendVals[3] <<= nCurrentStorageVersion; ConfigItem::SetModified(); ConfigItem::PutProperties( sendNames, sendVals ); hasEncoded = bHasMaster; mEncoded = aEncoded; + mEncodedIV = aEncodedIV; } @@ -362,11 +372,13 @@ void StorageItem::update( const OUString& aURL, const NamePassRecord& aRecord ) forIndex.push_back( aURL ); forIndex.push_back( aRecord.GetUserName() ); - Sequence< beans::PropertyValue > sendSeq(1); + Sequence< beans::PropertyValue > sendSeq(2); - sendSeq[0].Name = "Store/Passwordstorage['" + createIndex( forIndex ) + "']/Password"; + sendSeq[0].Name = "Store/Passwordstorage['" + createIndex( forIndex ) + "']/InitializationVector"; + sendSeq[0].Value <<= aRecord.GetPersistentIV(); - sendSeq[0].Value <<= aRecord.GetPersPasswords(); + sendSeq[1].Name = "Store/Passwordstorage['" + createIndex( forIndex ) + "']/Password"; + sendSeq[1].Value <<= aRecord.GetPersPasswords(); ConfigItem::SetModified(); ConfigItem::SetSetProperties( "Store", sendSeq ); @@ -427,7 +439,7 @@ void SAL_CALL PasswordContainer::disposing( const EventObject& ) } } -std::vector< OUString > PasswordContainer::DecodePasswords( const OUString& aLine, const OUString& aMasterPasswd, css::task::PasswordRequestMode mode ) +std::vector< OUString > PasswordContainer::DecodePasswords( const OUString& aLine, const OUString& aIV, const OUString& aMasterPasswd, css::task::PasswordRequestMode mode ) { if( !aMasterPasswd.isEmpty() ) { @@ -442,9 +454,16 @@ std::vector< OUString > PasswordContainer::DecodePasswords( const OUString& aLin for( int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ind++ ) code[ ind ] = static_cast<char>(aMasterPasswd.copy( ind*2, 2 ).toUInt32(16)); + unsigned char iv[RTL_DIGEST_LENGTH_MD5] = {0}; + if (!aIV.isEmpty()) + { + for( int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ind++ ) + iv[ ind ] = static_cast<char>(aIV.copy( ind*2, 2 ).toUInt32(16)); + } + rtlCipherError result = rtl_cipher_init ( aDecoder, rtl_Cipher_DirectionDecode, - code, RTL_DIGEST_LENGTH_MD5, nullptr, 0 ); + code, RTL_DIGEST_LENGTH_MD5, iv, RTL_DIGEST_LENGTH_MD5 ); if( result == rtl_Cipher_E_None ) { @@ -477,7 +496,7 @@ std::vector< OUString > PasswordContainer::DecodePasswords( const OUString& aLin "Can't decode!", css::uno::Reference<css::uno::XInterface>(), mode); } -OUString PasswordContainer::EncodePasswords(const std::vector< OUString >& lines, const OUString& aMasterPasswd ) +OUString PasswordContainer::EncodePasswords(const std::vector< OUString >& lines, const OUString& aIV, const OUString& aMasterPasswd) { if( !aMasterPasswd.isEmpty() ) { @@ -494,9 +513,16 @@ OUString PasswordContainer::EncodePasswords(const std::vector< OUString >& lines for( int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ind++ ) code[ ind ] = static_cast<char>(aMasterPasswd.copy( ind*2, 2 ).toUInt32(16)); + unsigned char iv[RTL_DIGEST_LENGTH_MD5] = {0}; + if (!aIV.isEmpty()) + { + for( int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ind++ ) + iv[ ind ] = static_cast<char>(aIV.copy( ind*2, 2 ).toUInt32(16)); + } + rtlCipherError result = rtl_cipher_init ( aEncoder, rtl_Cipher_DirectionEncode, - code, RTL_DIGEST_LENGTH_MD5, nullptr, 0 ); + code, RTL_DIGEST_LENGTH_MD5, iv, RTL_DIGEST_LENGTH_MD5 ); if( result == rtl_Cipher_E_None ) { @@ -564,7 +590,7 @@ void PasswordContainer::UpdateVector( const OUString& aURL, std::vector< NamePas if( aRecord.HasPasswords( PERSISTENT_RECORD ) ) { - aNPIter.SetPersPasswords( aRecord.GetPersPasswords() ); + aNPIter.SetPersPasswords( aRecord.GetPersPasswords(), aRecord.GetPersistentIV() ); if( writeFile ) { @@ -597,7 +623,8 @@ UserRecord PasswordContainer::CopyToUserRecord( const NamePassRecord& aRecord, b { try { - ::std::vector< OUString > aDecodedPasswords = DecodePasswords( aRecord.GetPersPasswords(), GetMasterPassword( aHandler ), css::task::PasswordRequestMode_PASSWORD_ENTER ); + ::std::vector< OUString > aDecodedPasswords = DecodePasswords( aRecord.GetPersPasswords(), aRecord.GetPersistentIV(), + GetMasterPassword( aHandler ), css::task::PasswordRequestMode_PASSWORD_ENTER ); aPasswords.insert( aPasswords.end(), aDecodedPasswords.begin(), aDecodedPasswords.end() ); } catch( NoMasterException& ) @@ -642,6 +669,19 @@ void SAL_CALL PasswordContainer::addPersistent( const OUString& Url, const OUStr PrivateAdd( Url, UserName, Passwords, PERSISTENT_RECORD, aHandler ); } +OUString PasswordContainer::createIV() +{ + rtlRandomPool randomPool = mRandomPool.get(); + unsigned char iv[RTL_DIGEST_LENGTH_MD5]; + rtl_random_getBytes(randomPool, iv, RTL_DIGEST_LENGTH_MD5); + OUStringBuffer aBuffer; + for (sal_uInt8 i : iv) + { + aBuffer.append(OUString::number(i >> 4, 16)); + aBuffer.append(OUString::number(i & 15, 16)); + } + return aBuffer.makeStringAndClear(); +} void PasswordContainer::PrivateAdd( const OUString& Url, const OUString& UserName, const Sequence< OUString >& Passwords, char Mode, const Reference< XInteractionHandler >& aHandler ) { @@ -649,7 +689,11 @@ void PasswordContainer::PrivateAdd( const OUString& Url, const OUString& UserNam ::std::vector< OUString > aStorePass = comphelper::sequenceToContainer< std::vector<OUString> >( Passwords ); if( Mode == PERSISTENT_RECORD ) - aRecord.SetPersPasswords( EncodePasswords( aStorePass, GetMasterPassword( aHandler ) ) ); + { + OUString sIV = createIV(); + OUString sEncodedPasswords = EncodePasswords( aStorePass, sIV, GetMasterPassword( aHandler ) ); + aRecord.SetPersPasswords( sEncodedPasswords, sIV ); + } else if( Mode == MEMORY_RECORD ) aRecord.SetMemPasswords( aStorePass ); else @@ -842,10 +886,10 @@ OUString const & PasswordContainer::GetMasterPassword( const Reference< XInterac if( m_aMasterPasswd.isEmpty() && aHandler.is() ) { - OUString aEncodedMP; + OUString aEncodedMP, aEncodedMPIV; bool bDefaultPassword = false; - if( !m_pStorageFile->getEncodedMP( aEncodedMP ) ) + if( !m_pStorageFile->getEncodedMP( aEncodedMP, aEncodedMPIV ) ) aRMode = PasswordRequestMode_PASSWORD_CREATE; else if ( aEncodedMP.isEmpty() ) { @@ -867,14 +911,15 @@ OUString const & PasswordContainer::GetMasterPassword( const Reference< XInterac m_aMasterPasswd = aPass; std::vector< OUString > aMaster( 1, m_aMasterPasswd ); - m_pStorageFile->setEncodedMP( EncodePasswords( aMaster, m_aMasterPasswd ) ); + OUString sIV = createIV(); + m_pStorageFile->setEncodedMP( EncodePasswords( aMaster, sIV, m_aMasterPasswd ), sIV ); } else { if (m_pStorageFile->getStorageVersion() == 0) aPass = ReencodeAsOldHash(aPass); - std::vector< OUString > aRM( DecodePasswords( aEncodedMP, aPass, aRMode ) ); + std::vector< OUString > aRM( DecodePasswords( aEncodedMP, aEncodedMPIV, aPass, aRMode ) ); if( aRM.empty() || aPass != aRM[0] ) { bAskAgain = true; @@ -1031,7 +1076,8 @@ Sequence< UrlRecord > SAL_CALL PasswordContainer::getAllPersistent( const Refere { sal_Int32 oldLen = aUsers.getLength(); aUsers.realloc( oldLen + 1 ); - aUsers[ oldLen ] = UserRecord( aNP.GetUserName(), comphelper::containerToSequence( DecodePasswords( aNP.GetPersPasswords(), GetMasterPassword( xHandler ), css::task::PasswordRequestMode_PASSWORD_ENTER ) ) ); + aUsers[ oldLen ] = UserRecord( aNP.GetUserName(), comphelper::containerToSequence( DecodePasswords( aNP.GetPersPasswords(), aNP.GetPersistentIV(), + GetMasterPassword( xHandler ), css::task::PasswordRequestMode_PASSWORD_ENTER ) ) ); } if( aUsers.getLength() ) @@ -1048,12 +1094,12 @@ Sequence< UrlRecord > SAL_CALL PasswordContainer::getAllPersistent( const Refere sal_Bool SAL_CALL PasswordContainer::authorizateWithMasterPassword( const uno::Reference< task::XInteractionHandler >& xHandler ) { bool bResult = false; - OUString aEncodedMP; + OUString aEncodedMP, aEncodedMPIV; uno::Reference< task::XInteractionHandler > xTmpHandler = xHandler; ::osl::MutexGuard aGuard( mMutex ); // the method should fail if there is no master password - if( m_pStorageFile && m_pStorageFile->useStorage() && m_pStorageFile->getEncodedMP( aEncodedMP ) ) + if( m_pStorageFile && m_pStorageFile->useStorage() && m_pStorageFile->getEncodedMP( aEncodedMP, aEncodedMPIV ) ) { if ( aEncodedMP.isEmpty() ) { @@ -1121,8 +1167,8 @@ sal_Bool SAL_CALL PasswordContainer::changeMasterPassword( const uno::Reference< bool bCanChangePassword = true; // if there is already a stored master password it should be entered by the user before the change happen - OUString aEncodedMP; - if( !m_aMasterPasswd.isEmpty() || m_pStorageFile->getEncodedMP( aEncodedMP ) ) + OUString aEncodedMP, aEncodedMPIV; + if( !m_aMasterPasswd.isEmpty() || m_pStorageFile->getEncodedMP( aEncodedMP, aEncodedMPIV ) ) bCanChangePassword = authorizateWithMasterPassword( xTmpHandler ); if ( bCanChangePassword ) @@ -1141,7 +1187,8 @@ sal_Bool SAL_CALL PasswordContainer::changeMasterPassword( const uno::Reference< // store the new master password m_aMasterPasswd = aPass; std::vector< OUString > aMaster( 1, m_aMasterPasswd ); - m_pStorageFile->setEncodedMP( EncodePasswords( aMaster, m_aMasterPasswd ) ); + OUString aIV = createIV(); + m_pStorageFile->setEncodedMP( EncodePasswords( aMaster, aIV, m_aMasterPasswd ), aIV ); // store all the entries with the new password for ( int nURLInd = 0; nURLInd < aPersistent.getLength(); nURLInd++ ) @@ -1168,7 +1215,7 @@ void SAL_CALL PasswordContainer::removeMasterPassword() if ( m_pStorageFile ) { m_aMasterPasswd.clear(); - m_pStorageFile->setEncodedMP( OUString() ); // let the master password be removed from configuration + m_pStorageFile->setEncodedMP( OUString(), OUString() ); // let the master password be removed from configuration } } @@ -1179,8 +1226,8 @@ sal_Bool SAL_CALL PasswordContainer::hasMasterPassword( ) if ( !m_pStorageFile ) throw uno::RuntimeException(); - OUString aEncodedMP; - return ( m_pStorageFile->useStorage() && m_pStorageFile->getEncodedMP( aEncodedMP ) ); + OUString aEncodedMP, aEncodedMPIV; + return ( m_pStorageFile->useStorage() && m_pStorageFile->getEncodedMP( aEncodedMP, aEncodedMPIV ) ); } sal_Bool SAL_CALL PasswordContainer::allowPersistentStoring( sal_Bool bAllow ) @@ -1227,8 +1274,8 @@ sal_Bool SAL_CALL PasswordContainer::useDefaultMasterPassword( const uno::Refere bool bCanChangePassword = true; // if there is already a stored nondefault master password it should be entered by the user before the change happen - OUString aEncodedMP; - if( m_pStorageFile->getEncodedMP( aEncodedMP ) && !aEncodedMP.isEmpty() ) + OUString aEncodedMP, aEncodedMPIV; + if( m_pStorageFile->getEncodedMP( aEncodedMP, aEncodedMPIV ) && !aEncodedMP.isEmpty() ) bCanChangePassword = authorizateWithMasterPassword( xTmpHandler ); if ( bCanChangePassword ) @@ -1245,7 +1292,7 @@ sal_Bool SAL_CALL PasswordContainer::useDefaultMasterPassword( const uno::Refere // store the empty string to flag the default master password m_aMasterPasswd = aPass; - m_pStorageFile->setEncodedMP( OUString(), true ); + m_pStorageFile->setEncodedMP( OUString(), OUString(), true ); // store all the entries with the new password for ( int nURLInd = 0; nURLInd < aPersistent.getLength(); nURLInd++ ) @@ -1271,8 +1318,8 @@ sal_Bool SAL_CALL PasswordContainer::isDefaultMasterPasswordUsed() if ( !m_pStorageFile ) throw uno::RuntimeException(); - OUString aEncodedMP; - return ( m_pStorageFile->useStorage() && m_pStorageFile->getEncodedMP( aEncodedMP ) && aEncodedMP.isEmpty() ); + OUString aEncodedMP, aEncodedMPIV; + return ( m_pStorageFile->useStorage() && m_pStorageFile->getEncodedMP( aEncodedMP, aEncodedMPIV ) && aEncodedMP.isEmpty() ); } diff --git a/svl/source/passwordcontainer/passwordcontainer.hxx b/svl/source/passwordcontainer/passwordcontainer.hxx index 6ad1eee3d0d5..94ca40dcf90b 100644 --- a/svl/source/passwordcontainer/passwordcontainer.hxx +++ b/svl/source/passwordcontainer/passwordcontainer.hxx @@ -36,6 +36,7 @@ #include <unotools/configitem.hxx> #include <ucbhelper/interactionrequest.hxx> +#include <rtl/random.h> #include <rtl/ref.hxx> #include <osl/mutex.hxx> @@ -54,11 +55,12 @@ class NamePassRecord ::std::vector< OUString > m_aMemPass; // persistent passwords are encrypted in one string - bool m_bHasPersPass; + bool m_bHasPersPass; OUString m_aPersPass; + OUString m_aPersistentIV; void InitArrays( bool bHasMemoryList, const ::std::vector< OUString >& aMemoryList, - bool bHasPersistentList, const OUString& aPersistentList ) + bool bHasPersistentList, const OUString& aPersistentList, const OUString& aPersistentIV ) { m_bHasMemPass = bHasMemoryList; if ( bHasMemoryList ) @@ -66,7 +68,10 @@ class NamePassRecord m_bHasPersPass = bHasPersistentList; if ( bHasPersistentList ) + { m_aPersPass = aPersistentList; + m_aPersistentIV = aPersistentIV; + } } public: @@ -78,11 +83,12 @@ public: { } - NamePassRecord( const OUString& aName, const OUString& aPersistentList ) + NamePassRecord( const OUString& aName, const OUString& aPersistentList, const OUString& aPersistentIV ) : m_aName( aName ) , m_bHasMemPass( false ) , m_bHasPersPass( true ) , m_aPersPass( aPersistentList ) + , m_aPersistentIV( aPersistentIV ) { } @@ -91,7 +97,8 @@ public: , m_bHasMemPass( false ) , m_bHasPersPass( false ) { - InitArrays( aRecord.m_bHasMemPass, aRecord.m_aMemPass, aRecord.m_bHasPersPass, aRecord.m_aPersPass ); + InitArrays( aRecord.m_bHasMemPass, aRecord.m_aMemPass, + aRecord.m_bHasPersPass, aRecord.m_aPersPass, aRecord.m_aPersistentIV ); } NamePassRecord& operator=( const NamePassRecord& aRecord ) @@ -102,7 +109,9 @@ public: m_aMemPass.clear(); m_aPersPass.clear(); - InitArrays( aRecord.m_bHasMemPass, aRecord.m_aMemPass, aRecord.m_bHasPersPass, aRecord.m_aPersPass ); + m_aPersistentIV.clear(); + InitArrays( aRecord.m_bHasMemPass, aRecord.m_aMemPass, + aRecord.m_bHasPersPass, aRecord.m_aPersPass, aRecord.m_aPersistentIV ); } return *this; } @@ -138,15 +147,24 @@ public: return OUString(); } + OUString GetPersistentIV() const + { + if ( m_bHasPersPass ) + return m_aPersistentIV; + + return OUString(); + } + void SetMemPasswords( const ::std::vector< OUString >& aMemList ) { m_aMemPass = aMemList; m_bHasMemPass = true; } - void SetPersPasswords( const OUString& aPersList ) + void SetPersPasswords( const OUString& aPersList, const OUString& aPersIV ) { m_aPersPass = aPersList; + m_aPersistentIV = aPersIV; m_bHasPersPass = true; } @@ -161,6 +179,7 @@ public: { m_bHasPersPass = false; m_aPersPass.clear(); + m_aPersistentIV.clear(); } } @@ -184,6 +203,7 @@ private: PasswordContainer* mainCont; bool hasEncoded; OUString mEncoded; + OUString mEncodedIV; virtual void ImplCommit() override; @@ -204,8 +224,8 @@ public: sal_Int32 getStorageVersion(); - bool getEncodedMP( OUString& aResult ); - void setEncodedMP( const OUString& aResult, bool bAcceptEnmpty = false ); + bool getEncodedMP( OUString& aResult, OUString& aResultIV ); + void setEncodedMP( const OUString& aResult, const OUString& aResultIV, bool bAcceptEmpty = false ); void setUseStorage( bool bUse ); bool useStorage(); @@ -226,6 +246,29 @@ private: css::uno::Reference< css::lang::XComponent > mComponent; SysCredentialsConfig mUrlContainer; + class RandomPool + { + private: + rtlRandomPool m_aRandomPool; + public: + RandomPool() : m_aRandomPool(rtl_random_createPool()) + { + } + rtlRandomPool get() + { + return m_aRandomPool; + } + ~RandomPool() + { + // Clean up random pool memory + rtl_random_destroyPool(m_aRandomPool); + } + }; + + RandomPool mRandomPool; + + OUString createIV(); + /// @throws css::uno::RuntimeException css::uno::Sequence< css::task::UserRecord > CopyToUserRecordSequence( const ::std::vector< NamePassRecord >& original, @@ -276,10 +319,10 @@ css::task::UrlRecord find( const css::uno::Reference< css::task::XInteractionHandler >& Handler ); /// @throws css::uno::RuntimeException - static ::std::vector< OUString > DecodePasswords( const OUString& aLine, const OUString& aMasterPassword, css::task::PasswordRequestMode mode ); + static ::std::vector< OUString > DecodePasswords( const OUString& aLine, const OUString& aIV, const OUString& aMasterPassword, css::task::PasswordRequestMode mode ); /// @throws css::uno::RuntimeException - static OUString EncodePasswords(const std::vector< OUString >& lines, const OUString& aMasterPassword ); + static OUString EncodePasswords(const std::vector< OUString >& lines, const OUString& aIV, const OUString& aMasterPassword ); public: PasswordContainer( const css::uno::Reference< css::lang::XMultiServiceFactory >& ); commit 9bbb6a495a3e7181601de488bec18b78e48010e5 Author: Caolán McNamara <[email protected]> AuthorDate: Mon Mar 21 20:58:34 2022 +0000 Commit: Andras Timar <[email protected]> CommitDate: Tue Jul 26 15:16:49 2022 +0200 CVE-2022-26307 make hash encoding match decoding 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. Change-Id: I3174c37a5891bfc849984e0ec5c2c392b9c6e7b1 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132080 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <[email protected]> (cherry picked from commit e890f54dbac57f3ab5acf4fbd31222095d3e8ab6) diff --git a/officecfg/registry/schema/org/openoffice/Office/Common.xcs b/officecfg/registry/schema/org/openoffice/Office/Common.xcs index 225567c53a06..cb1c57120731 100644 --- a/officecfg/registry/schema/org/openoffice/Office/Common.xcs +++ b/officecfg/registry/schema/org/openoffice/Office/Common.xcs @@ -903,6 +903,12 @@ </info> <value>false</value> </prop> + <prop oor:name="StorageVersion" oor:type="xs:int" oor:nillable="false"> + <info> + <desc>Specifies what version of encoding scheme the password container uses.</desc> + </info> + <value>0</value> + </prop> <prop oor:name="HasMaster" oor:type="xs:boolean" oor:nillable="false"> <info> <desc>Specifies if there is a valid master password.</desc> diff --git a/svl/source/passwordcontainer/passwordcontainer.cxx b/svl/source/passwordcontainer/passwordcontainer.cxx index eb52bc98e520..e7a007a7342b 100644 --- a/svl/source/passwordcontainer/passwordcontainer.cxx +++ b/svl/source/passwordcontainer/passwordcontainer.cxx @@ -17,7 +17,6 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ - #include "passwordcontainer.hxx" #include <unotools/pathoptions.hxx> @@ -262,6 +261,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 ) { @@ -294,15 +310,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 +821,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 +871,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 +1078,12 @@ 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 ad62ac0c91bd..6ad1eee3d0d5 100644 --- a/svl/source/passwordcontainer/passwordcontainer.hxx +++ b/svl/source/passwordcontainer/passwordcontainer.hxx @@ -170,6 +170,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; @@ -198,6 +202,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 d151a36138e7..ecb06a182daf 100644 --- a/uui/source/iahndl-authentication.cxx +++ b/uui/source/iahndl-authentication.cxx @@ -437,8 +437,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()); } commit 47be08bbd23852b3dcc4b24f6a5a72873ce2610f Author: Caolán McNamara <[email protected]> AuthorDate: Thu Mar 3 14:22:37 2022 +0000 Commit: Andras Timar <[email protected]> CommitDate: Tue Jul 26 15:16:30 2022 +0200 CVE-2022-26305 compare authors using Thumbprint Reviewed-on: https://gerrit.libreoffice.org/c/core/+/130929 Tested-by: Jenkins Reviewed-by: Miklos Vajna <[email protected]> (cherry picked from commit 65442205b5b274ad309308162f150f8d41648f72) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/130866 Reviewed-by: Michael Stahl <[email protected]> (cherry picked from commit a7aaa78acea4c1d51283c2fce54ff9f5339026f8) Change-Id: I338f58eb07cbf0a3d13a7dafdaddac09252a8546 diff --git a/xmlsecurity/source/component/documentdigitalsignatures.cxx b/xmlsecurity/source/component/documentdigitalsignatures.cxx index fdd08892d24a..073ad0646d52 100644 --- a/xmlsecurity/source/component/documentdigitalsignatures.cxx +++ b/xmlsecurity/source/component/documentdigitalsignatures.cxx @@ -19,9 +19,10 @@ #include <resourcemanager.hxx> -#include <digitalsignaturesdialog.hxx> +#include <certificate.hxx> #include <certificatechooser.hxx> #include <certificateviewer.hxx> +#include <digitalsignaturesdialog.hxx> #include <macrosecurity.hxx> #include <biginteger.hxx> #include <strings.hrc> @@ -624,20 +625,26 @@ sal_Bool DocumentDigitalSignatures::isAuthorTrusted( OUString sSerialNum = xmlsecurity::bigIntegerToNumericString(xAuthor->getSerialNumber()); Sequence< SvtSecurityOptions::Certificate > aTrustedAuthors = SvtSecurityOptions().GetTrustedAuthors(); - const SvtSecurityOptions::Certificate* pAuthors = aTrustedAuthors.getConstArray(); - const SvtSecurityOptions::Certificate* pAuthorsEnd = pAuthors + aTrustedAuthors.getLength(); - for ( ; pAuthors != pAuthorsEnd; ++pAuthors ) - { - SvtSecurityOptions::Certificate aAuthor = *pAuthors; - if (xmlsecurity::EqualDistinguishedNames(aAuthor[0], xAuthor->getIssuerName()) - && (aAuthor[1] == sSerialNum)) - { - bFound = true; - break; - } - } - return bFound; + return std::any_of(aTrustedAuthors.begin(), aTrustedAuthors.end(), + [this, &xAuthor, &sSerialNum](const SvtSecurityOptions::Certificate& rAuthor) { + if (!xmlsecurity::EqualDistinguishedNames(rAuthor[0], xAuthor->getIssuerName())) + return false; + if (rAuthor[1] != sSerialNum) + return false; + + DocumentSignatureManager aSignatureManager(mxCtx, {}); + if (!aSignatureManager.init()) + return false; + uno::Reference<css::security::XCertificate> xCert = aSignatureManager.getSecurityEnvironment()->createCertificateFromAscii(rAuthor[2]); + + auto pAuthor = dynamic_cast<xmlsecurity::Certificate*>(xAuthor.get()); + auto pCert = dynamic_cast<xmlsecurity::Certificate*>(xCert.get()); + if (pAuthor && pCert) + return pCert->getSHA256Thumbprint() == pAuthor->getSHA256Thumbprint(); + + return xCert->getSHA1Thumbprint() == xAuthor->getSHA1Thumbprint(); + }); } uno::Sequence<Reference<css::security::XCertificate>>
