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 | 164 +++++++++---- svl/source/passwordcontainer/passwordcontainer.hxx | 69 ++++- uui/source/iahndl-authentication.cxx | 5 xmlsecurity/source/component/documentdigitalsignatures.cxx | 23 + 9 files changed, 267 insertions(+), 55 deletions(-)
New commits: commit a49d125f7a655e9d6caad4fd3018a79d15a77b14 Author: Andras Timar <andras.ti...@collabora.com> AuthorDate: Tue Jul 26 14:58:56 2022 +0200 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Tue Jul 26 14:58:56 2022 +0200 Bump version to 6.4.7.2.M6 Change-Id: Ifab8a66733ac79e4eff2784862f46280c06728e9 diff --git a/configure.ac b/configure.ac index 8b253b07b235..ddc8cf8f2bc0 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.4.7.2.M5],[],[],[http://documentfoundation.org/]) +AC_INIT([LibreOffice],[6.4.7.2.M6],[],[],[http://documentfoundation.org/]) dnl libnumbertext needs autoconf 2.68, but that can pick up autoconf268 just fine if it is installed dnl whereas aclocal (as run by autogen.sh) insists on using autoconf and fails hard commit e129fc57b0a64a38f5b018eb6a6863a1f4afbeb8 Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Wed Mar 23 13:03:30 2022 +0000 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Tue Jul 26 14:34:38 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 <michael.st...@allotropia.de> (cherry picked from commit bbd196ff82bda9f66b4ba32a412f10cefe6da60e) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132307 Reviewed-by: Sophie Gautier <so...@libreoffice.org> Reviewed-by: Christian Lohmaier <lohmaier+libreoff...@googlemail.com> (cherry picked from commit c5d01b11db3c83cb4a89d3b388d78e20dd3990b5) diff --git a/include/sfx2/strings.hrc b/include/sfx2/strings.hrc index 1f21f0a0f186..1db36e733c0c 100644 --- a/include/sfx2/strings.hrc +++ b/include/sfx2/strings.hrc @@ -297,6 +297,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 fe336ba5f091..cc6a7dae7047 100644 --- a/include/sfx2/viewfrm.hxx +++ b/include/sfx2/viewfrm.hxx @@ -61,6 +61,7 @@ protected: DECL_LINK(WhatsNewHandler, 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 5a64599e5894..93e60371d4a1 100644 --- a/sfx2/source/view/viewfrm.cxx +++ b/sfx2/source/view/viewfrm.cxx @@ -32,6 +32,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> @@ -1390,6 +1391,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, false); + 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; @@ -1565,6 +1584,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 3bf822f65e560bef2cd5bca595e96f1348a885fb Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Tue Mar 22 17:22:22 2022 +0000 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Tue Jul 26 14:34:30 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 Change-Id: I6fe3b02fafcce1b5e7133e77e76a5118177d77af Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131974 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.st...@allotropia.de> (cherry picked from commit 192fa1e3bfc6269f2ebb91716471485a56074aea) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132306 Reviewed-by: Thorsten Behrens <thorsten.behr...@allotropia.de> (cherry picked from commit ab77587ec300f5c30084471000663c46ddf25dad) diff --git a/officecfg/registry/schema/org/openoffice/Office/Common.xcs b/officecfg/registry/schema/org/openoffice/Office/Common.xcs index 9d6df5193734..27e36bfe9e04 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> @@ -923,6 +928,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 4e42aa4a6644..3c400e551b1d 100644 --- a/svl/source/passwordcontainer/passwordcontainer.cxx +++ b/svl/source/passwordcontainer/passwordcontainer.cxx @@ -181,15 +181,18 @@ PassMap StorageItem::getInfo() Sequence< OUString > aNodeNames = ConfigItem::GetNodeNames( "Store" ); sal_Int32 aNodeCount = aNodeNames.getLength(); - Sequence< OUString > aPropNames( aNodeCount ); + Sequence< OUString > aPropNames( aNodeCount * 2); 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() != aNodeCount ) + if( aPropertyValues.getLength() != aNodeCount * 2) { OSL_FAIL( "Problems during reading" ); return aResult; @@ -205,14 +208,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 ) ); @@ -276,17 +281,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 ); @@ -298,32 +305,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; } @@ -359,11 +371,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 ); @@ -424,7 +438,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() ) { @@ -439,9 +453,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 ) { @@ -474,7 +495,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() ) { @@ -491,9 +512,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 ) { @@ -561,7 +589,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 ) { @@ -594,7 +622,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& ) @@ -639,6 +668,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 ) { @@ -646,7 +688,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 @@ -839,10 +885,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() ) { @@ -864,14 +910,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; @@ -1028,7 +1075,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.hasElements() ) @@ -1045,12 +1093,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() ) { @@ -1118,8 +1166,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 ) @@ -1138,7 +1186,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 ( const auto& rURL : aPersistent ) @@ -1163,7 +1212,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 } } @@ -1174,8 +1223,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 ) @@ -1222,8 +1271,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 ) @@ -1240,7 +1289,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 ( const auto& rURL : aPersistent ) @@ -1264,8 +1313,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 cf5c717d0c9e..4e3a6629139e 100644 --- a/svl/source/passwordcontainer/passwordcontainer.hxx +++ b/svl/source/passwordcontainer/passwordcontainer.hxx @@ -33,6 +33,7 @@ #include <unotools/configitem.hxx> #include <ucbhelper/interactionrequest.hxx> +#include <rtl/random.h> #include <rtl/ref.hxx> #include <osl/mutex.hxx> @@ -51,11 +52,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 ) @@ -63,7 +65,10 @@ class NamePassRecord m_bHasPersPass = bHasPersistentList; if ( bHasPersistentList ) + { m_aPersPass = aPersistentList; + m_aPersistentIV = aPersistentIV; + } } public: @@ -75,11 +80,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 ) { } @@ -88,7 +94,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 ) @@ -99,7 +106,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; } @@ -135,15 +144,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; } @@ -158,6 +176,7 @@ public: { m_bHasPersPass = false; m_aPersPass.clear(); + m_aPersistentIV.clear(); } } @@ -181,6 +200,7 @@ private: PasswordContainer* mainCont; bool hasEncoded; OUString mEncoded; + OUString mEncodedIV; virtual void ImplCommit() override; @@ -201,8 +221,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(); @@ -223,6 +243,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, @@ -273,10 +316,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 af928fc2d60af9345c9dc6febe0f96b887d62435 Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Mon Mar 21 20:58:34 2022 +0000 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Tue Jul 26 14:34:21 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 <sberg...@redhat.com> (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 cebb7d7ec0ef..9d6df5193734 100644 --- a/officecfg/registry/schema/org/openoffice/Office/Common.xcs +++ b/officecfg/registry/schema/org/openoffice/Office/Common.xcs @@ -911,6 +911,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 02947cd3892c..4e42aa4a6644 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 <cppuhelper/factory.hxx> @@ -259,6 +258,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 ) { @@ -291,15 +307,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 ); @@ -800,6 +818,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 ) { @@ -838,6 +868,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] ) { @@ -1042,6 +1075,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 09fb7e03629d..cf5c717d0c9e 100644 --- a/svl/source/passwordcontainer/passwordcontainer.hxx +++ b/svl/source/passwordcontainer/passwordcontainer.hxx @@ -167,6 +167,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; @@ -195,6 +199,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 4835a485dd2a..5764e62cb1c6 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()); } commit 3e3e94ad5fe03f3302a8481ee267437be05dd90c Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Thu Mar 3 14:22:37 2022 +0000 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Tue Jul 26 14:34:10 2022 +0200 CVE-2022-26305 compare authors using Thumbprint Change-Id: I338f58eb07cbf0a3d13a7dafdaddac09252a8546 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/130929 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> (cherry picked from commit 65442205b5b274ad309308162f150f8d41648f72) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/130866 Reviewed-by: Michael Stahl <michael.st...@allotropia.de> (cherry picked from commit a7aaa78acea4c1d51283c2fce54ff9f5339026f8) diff --git a/xmlsecurity/source/component/documentdigitalsignatures.cxx b/xmlsecurity/source/component/documentdigitalsignatures.cxx index 59759e70999b..e13c10618e43 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> @@ -651,9 +652,23 @@ sal_Bool DocumentDigitalSignatures::isAuthorTrusted( Sequence< SvtSecurityOptions::Certificate > aTrustedAuthors = SvtSecurityOptions().GetTrustedAuthors(); return std::any_of(aTrustedAuthors.begin(), aTrustedAuthors.end(), - [&xAuthor, &sSerialNum](const SvtSecurityOptions::Certificate& rAuthor) { - return xmlsecurity::EqualDistinguishedNames(rAuthor[0], xAuthor->getIssuerName()) - && ( rAuthor[1] == sSerialNum ); + [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(); }); }