Skip to content

Commit

Permalink
add Initialization Vectors to password storage
Browse files Browse the repository at this point in the history
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.stahl@allotropia.de>
(cherry picked from commit 192fa1e)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132306
Reviewed-by: Thorsten Behrens <thorsten.behrens@allotropia.de>
  • Loading branch information
Caolán McNamara authored and thorstenb committed Apr 2, 2022
1 parent 1d9205e commit ab77587
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 47 deletions.
10 changes: 10 additions & 0 deletions officecfg/registry/schema/org/openoffice/Office/Common.xcs
Expand Up @@ -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>
Expand Down Expand Up @@ -945,6 +950,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>
Expand Down
122 changes: 85 additions & 37 deletions svl/source/passwordcontainer/passwordcontainer.cxx
Expand Up @@ -185,15 +185,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;
Expand All @@ -209,14 +212,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 ) );
Expand Down Expand Up @@ -273,17 +278,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 );

Expand All @@ -295,23 +302,27 @@ 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 )
{
bool bHasMaster = ( !aEncoded.isEmpty() || bAcceptEmpty );

ConfigItem::SetModified();
ConfigItem::PutProperties( { "HasMaster", "Master", "StorageVersion" },
{ uno::Any(bHasMaster), uno::Any(aEncoded), uno::Any(nCurrentStorageVersion) } );
ConfigItem::PutProperties( { "HasMaster", "Master", "MasterInitializationVector", "StorageVersion" },
{ uno::Any(bHasMaster), uno::Any(aEncoded),
uno::Any(aEncodedIV), uno::Any(nCurrentStorageVersion) } );

hasEncoded = bHasMaster;
mEncoded = aEncoded;
mEncodedIV = aEncodedIV;
}


Expand All @@ -337,11 +348,13 @@ void StorageItem::update( const OUString& aURL, const NamePassRecord& aRecord )
return;
}

Sequence< beans::PropertyValue > sendSeq(1);
Sequence< beans::PropertyValue > sendSeq(2);

sendSeq[0].Name = "Store/Passwordstorage['" + createIndex( { aURL, aRecord.GetUserName() } ) + "']/Password";
sendSeq[0].Name = "Store/Passwordstorage['" + createIndex( { aURL, aRecord.GetUserName() } ) + "']/InitializationVector";
sendSeq[0].Value <<= aRecord.GetPersistentIV();

sendSeq[0].Value <<= aRecord.GetPersPasswords();
sendSeq[1].Name = "Store/Passwordstorage['" + createIndex( { aURL, aRecord.GetUserName() } ) + "']/Password";
sendSeq[1].Value <<= aRecord.GetPersPasswords();

ConfigItem::SetModified();
ConfigItem::SetSetProperties( "Store", sendSeq );
Expand Down Expand Up @@ -402,7 +415,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() )
{
Expand All @@ -417,9 +430,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 )
{
Expand Down Expand Up @@ -452,7 +472,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() )
{
Expand All @@ -469,9 +489,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 )
{
Expand Down Expand Up @@ -539,7 +566,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 )
{
Expand Down Expand Up @@ -572,7 +599,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& )
Expand Down Expand Up @@ -617,14 +645,31 @@ 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 )
{
NamePassRecord aRecord( UserName );
::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
Expand Down Expand Up @@ -814,10 +859,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() )
{
Expand All @@ -839,14 +884,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;
Expand Down Expand Up @@ -1003,7 +1049,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() )
Expand All @@ -1020,12 +1067,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() )
{
Expand Down Expand Up @@ -1091,8 +1138,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 )
Expand All @@ -1111,7 +1158,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 )
Expand All @@ -1136,7 +1184,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
}
}

Expand All @@ -1147,8 +1195,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 )
Expand Down Expand Up @@ -1195,8 +1243,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 )
Expand All @@ -1213,7 +1261,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 )
Expand All @@ -1237,8 +1285,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() );
}


Expand Down

0 comments on commit ab77587

Please sign in to comment.