/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/config.h>
#include <sal/log.hxx>
#include <string_view>
#include "passwordcontainer.hxx"
#include <cppuhelper/factory.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/sequence.hxx>
#include <o3tl/string_view.hxx>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/task/MasterPasswordRequest.hpp>
#include <com/sun/star/task/NoMasterException.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <osl/diagnose.h>
#include <rtl/character.hxx>
#include <rtl/cipher.h>
#include <rtl/digest.h>
#include <rtl/byteseq.hxx>
#include <rtl/ustrbuf.hxx>
using namespace utl;
using namespace com::sun::star;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::task;
using namespace com::sun::star::ucb;
static OUString createIndex(
const std::vector< OUString >& lines)
{
OUStringBuffer aResult;
for ( size_t i = 0; i < lines.size(); i++ )
{
if ( i )
aResult.append(
"__" );
OString line = OUStringToOString( lines[i], RTL_TEXTENCODING_UTF8 );
const char * pLine = line.getStr();
while ( *pLine )
{
if (rtl::isAsciiAlphanumeric(
static_cast <
unsigned char >(*pLine)))
{
aResult.append(*pLine);
}
else
{
aResult.append(
"_" + OUString::number(*pLine, 16) );
}
pLine++;
}
}
return aResult.makeStringAndClear();
}
static std::vector< OUString > getInfoFromInd( std::u16string_view aInd )
{
std::vector< OUString > aResult;
bool aStart =
true ;
OString line = OUStringToOString( aInd, RTL_TEXTENCODING_ASCII_US );
const char * pLine = line.getStr();
do
{
OUStringBuffer newItem;
if ( !aStart )
pLine += 2;
else
aStart =
false ;
while ( *pLine && ( pLine[0] !=
'_' || pLine[1] !=
'_' ))
if ( *pLine !=
'_' )
{
newItem.append( *pLine );
pLine++;
}
else
{
OUString aNum;
for (
int i = 1; i < 3; i++ )
{
if ( !pLine[i]
|| ( ( pLine[i] <
'0' || pLine[i] >
'9' )
&& ( pLine[i] <
'a' || pLine[i] >
'f' )
&& ( pLine[i] <
'A' || pLine[i] >
'F' ) ) )
{
OSL_FAIL(
"Wrong index syntax!" );
return aResult;
}
aNum += OUStringChar( pLine[i] );
}
newItem.append( sal_Unicode( aNum.toUInt32( 16 ) ) );
pLine += 3;
}
aResult.push_back( newItem.makeStringAndClear() );
}
while ( pLine[0] ==
'_' && pLine[1] ==
'_' );
if ( *pLine )
OSL_FAIL(
"Wrong index syntax!" );
return aResult;
}
static bool shorterUrl( OUString& aURL )
{
sal_Int32 aInd = aURL.lastIndexOf(
'/' );
if ( aInd > 0 && aURL.indexOf(
"://" ) != aInd-2 )
{
aURL = aURL.copy( 0, aInd );
return true ;
}
return false ;
}
static OUString getAsciiLine(
const ::rtl::ByteSequence& buf )
{
OUString aResult;
::rtl::ByteSequence outbuf( buf.getLength()*2+1 );
for (
int ind = 0; ind < buf.getLength(); ind++ )
{
outbuf[ind*2] = (
static_cast <sal_uInt8>(buf[ind]) >> 4 ) +
'a' ;
outbuf[ind*2+1] = (
static_cast <sal_uInt8>(buf[ind]) & 0x0f ) +
'a' ;
}
outbuf[buf.getLength()*2] =
'\0' ;
aResult = OUString::createFromAscii(
reinterpret_cast <
char *>(outbuf.getArray()) );
return aResult;
}
static ::rtl::ByteSequence getBufFromAsciiLine( std::u16string_view line )
{
OSL_ENSURE( line.size() % 2 == 0,
"Wrong syntax!" );
OString tmpLine = OUStringToOString( line, RTL_TEXTENCODING_ASCII_US );
::rtl::ByteSequence aResult(line.size()/2);
for (
int ind = 0; ind < tmpLine.getLength()/2; ind++ )
{
aResult[ind] = (
static_cast <sal_uInt8>( tmpLine[ind*2] -
'a' ) << 4 ) |
static_cast <sal_uInt8>( tm
pLine[ind*2+1] - 'a' );
}
return aResult;
}
PasswordMap StorageItem::getInfo()
{
PasswordMap aResult;
const Sequence< OUString > aNodeNames = ConfigItem::GetNodeNames( u"Store" _ustr );
sal_Int32 aNodeCount = aNodeNames.getLength();
Sequence< OUString > aPropNames( aNodeCount * 2);
std::transform(aNodeNames.begin(), aNodeNames.end(), aPropNames.getArray(),
[](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 * 2)
{
OSL_FAIL( "Problems during reading" );
return aResult;
}
for ( sal_Int32 aNodeInd = 0; aNodeInd < aNodeCount; ++aNodeInd )
{
std::vector< OUString > aUrlUsr = getInfoFromInd( aNodeNames[aNodeInd] );
if ( aUrlUsr.size() == 2 )
{
OUString aUrl = aUrlUsr[0];
OUString aName = aUrlUsr[1];
OUString aEPasswd;
OUString aIV;
aPropertyValues[aNodeInd] >>= aEPasswd;
aPropertyValues[aNodeInd + aNodeCount] >>= aIV;
PasswordMap::iterator aIter = aResult.find( aUrl );
if ( aIter != aResult.end() )
aIter->second.emplace_back( aName, aEPasswd, aIV );
else
{
NamePasswordRecord aNewRecord( aName, aEPasswd, aIV );
std::vector< NamePasswordRecord > listToAdd( 1, aNewRecord );
aResult.insert( PairUrlRecord( aUrl, listToAdd ) );
}
}
else
OSL_FAIL( "Wrong index syntax!" );
}
return aResult;
}
void StorageItem::setUseStorage( bool bUse )
{
ConfigItem::SetModified();
ConfigItem::PutProperties( { u"UseStorage" _ustr }, { uno::Any(bUse) } );
}
bool StorageItem::useStorage()
{
Sequence<OUString> aNodeNames { u"UseStorage" _ustr };
Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aNodeNames );
if ( aPropertyValues.getLength() != aNodeNames.getLength() )
{
OSL_FAIL( "Problems during reading" );
return false ;
}
bool aResult = false ;
aPropertyValues[0] >>= aResult;
return aResult;
}
sal_Int32 StorageItem::getStorageVersion()
{
Sequence<OUString> aNodeNames { u"StorageVersion" _ustr };
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::getEncodedMasterPassword( OUString& aResult, OUString& aResultIV )
{
if ( hasEncoded )
{
aResult = mEncoded;
aResultIV = mEncodedIV;
return true ;
}
Sequence< OUString > aNodeNames{ u"HasMaster" _ustr, u"Master" _ustr, u"MasterInitializationVector" _ustr };
Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aNodeNames );
if ( aPropertyValues.getLength() != aNodeNames.getLength() )
{
OSL_FAIL( "Problems during reading" );
return false ;
}
aPropertyValues[0] >>= hasEncoded;
aPropertyValues[1] >>= mEncoded;
aPropertyValues[2] >>= mEncodedIV;
aResult = mEncoded;
aResultIV = mEncodedIV;
return hasEncoded;
}
void StorageItem::setEncodedMasterPassword( const OUString& aEncoded, const OUString& aEncodedIV, bool bAcceptEmpty )
{
bool bHasMaster = ( !aEncoded.isEmpty() || bAcceptEmpty );
ConfigItem::SetModified();
ConfigItem::PutProperties( { u"HasMaster" _ustr, u"Master" _ustr, u"MasterInitializationVector" _ustr, u"StorageVersion" _ustr },
{ uno::Any(bHasMaster), uno::Any(aEncoded),
uno::Any(aEncodedIV), uno::Any(nCurrentStorageVersion) } );
hasEncoded = bHasMaster;
mEncoded = aEncoded;
mEncodedIV = aEncodedIV;
}
void StorageItem::remove( const OUString& aURL, const OUString& aName )
{
Sequence< OUString > sendSeq { createIndex( { aURL, aName } ) };
ConfigItem::ClearNodeElements( u"Store" _ustr, sendSeq );
}
void StorageItem::clear()
{
ConfigItem::ClearNodeSet( u"Store" _ustr );
}
void StorageItem::update( const OUString& aURL, const NamePasswordRecord& aRecord )
{
if ( !aRecord.HasPasswords( PERSISTENT_RECORD ) )
{
OSL_FAIL( "Unexpected storing of a record!" );
return ;
}
Sequence< beans::PropertyValue > sendSeq{ comphelper::makePropertyValue(
"Store/Passwordstorage['" + createIndex( { aURL, aRecord.GetUserName() } ) + "']/InitializationVector" ,
aRecord.GetPersistentIV()), comphelper::makePropertyValue(
"Store/Passwordstorage['" + createIndex( { aURL, aRecord.GetUserName() } ) + "']/Password" ,
aRecord.GetPersistentPasswords()) };
ConfigItem::SetModified();
ConfigItem::SetSetProperties( u"Store" _ustr, sendSeq );
}
void StorageItem::Notify( const Sequence< OUString >& )
{
// this feature still should not be used
if ( mainCont )
mainCont->Notify();
}
void StorageItem::ImplCommit()
{
// Do nothing, we stored everything we want already
}
PasswordContainer::PasswordContainer( const Reference<XComponentContext>& rxContext )
{
// m_pStorageFile->Notify() can be called
std::unique_lock aGuard( mMutex );
mComponent.set( rxContext->getServiceManager(), UNO_QUERY );
mComponent->addEventListener( this );
m_xStorageFile.emplace( this , "Office.Common/Passwords" );
if ( m_xStorageFile->useStorage() )
m_aContainer = m_xStorageFile->getInfo();
}
PasswordContainer::~PasswordContainer()
{
std::unique_lock aGuard( mMutex );
m_xStorageFile.reset();
if ( mComponent.is() )
{
mComponent->removeEventListener(this );
mComponent.clear();
}
}
void SAL_CALL PasswordContainer::disposing( const EventObject& )
{
std::unique_lock aGuard( mMutex );
m_xStorageFile.reset();
if ( mComponent.is() )
{
//mComponent->removeEventListener(this);
mComponent.clear();
}
}
std::vector< OUString > PasswordContainer::DecodePasswords( std::u16string_view aLine, std::u16string_view aIV, std::u16string_view aMasterPasswd, css::task::PasswordRequestMode mode )
{
if ( !aMasterPasswd.empty() )
{
rtlCipher aDecoder = rtl_cipher_create (rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeStream );
OSL_ENSURE( aDecoder, "Can't create decoder" );
if ( aDecoder )
{
OSL_ENSURE( aMasterPasswd.size() == RTL_DIGEST_LENGTH_MD5 * 2, "Wrong master password format!" );
unsigned char code[RTL_DIGEST_LENGTH_MD5];
for ( int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ind++ )
code[ ind ] = static_cast <char >(o3tl::toUInt32(aMasterPasswd.substr( ind*2, 2 ), 16));
unsigned char iv[RTL_DIGEST_LENGTH_MD5] = {0};
if (!aIV.empty())
{
for ( int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ind++ )
{
auto tmp = aIV.substr( ind*2, 2 );
iv[ ind ] = static_cast <char >(rtl_ustr_toInt64_WithLength(tmp.data(), 16, tmp.size()));
}
}
rtlCipherError result = rtl_cipher_init (
aDecoder, rtl_Cipher_DirectionDecode,
code, RTL_DIGEST_LENGTH_MD5, iv, RTL_DIGEST_LENGTH_MD5 );
if ( result == rtl_Cipher_E_None )
{
::rtl::ByteSequence aSeq = getBufFromAsciiLine( aLine );
::rtl::ByteSequence resSeq( aSeq.getLength() );
rtl_cipher_decode ( aDecoder, aSeq.getArray(), aSeq.getLength(),
reinterpret_cast <sal_uInt8*>(resSeq.getArray()), resSeq.getLength() );
OUString aPasswd( reinterpret_cast <char *>(resSeq.getArray()), resSeq.getLength(), RTL_TEXTENCODING_UTF8 );
rtl_cipher_destroy (aDecoder);
return getInfoFromInd( aPasswd );
}
rtl_cipher_destroy (aDecoder);
}
}
else
{
OSL_FAIL( "No master password provided!" );
// throw special exception
}
// problems with decoding
OSL_FAIL( "Problem with decoding" );
throw css::task::NoMasterException(
u"Can't decode!" _ustr, css::uno::Reference<css::uno::XInterface>(), mode);
}
OUString PasswordContainer::EncodePasswords(const std::vector< OUString >& lines, std::u16string_view aIV, std::u16string_view aMasterPasswd)
{
if ( !aMasterPasswd.empty() )
{
OString aSeq = OUStringToOString( createIndex( lines ), RTL_TEXTENCODING_UTF8 );
rtlCipher aEncoder = rtl_cipher_create (rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeStream );
OSL_ENSURE( aEncoder, "Can't create encoder" );
if ( aEncoder )
{
OSL_ENSURE( aMasterPasswd.size() == RTL_DIGEST_LENGTH_MD5 * 2, "Wrong master password format!" );
unsigned char code[RTL_DIGEST_LENGTH_MD5];
for ( int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ind++ )
code[ ind ] = static_cast <char >(o3tl::toUInt32(aMasterPasswd.substr( ind*2, 2 ), 16));
unsigned char iv[RTL_DIGEST_LENGTH_MD5] = {0};
if (!aIV.empty())
{
for ( int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ind++ )
{
auto tmp = aIV.substr( ind*2, 2 );
iv[ ind ] = static_cast <char >(rtl_ustr_toInt64_WithLength(tmp.data(), 16, tmp.size()));
}
}
rtlCipherError result = rtl_cipher_init (
aEncoder, rtl_Cipher_DirectionEncode,
code, RTL_DIGEST_LENGTH_MD5, iv, RTL_DIGEST_LENGTH_MD5 );
if ( result == rtl_Cipher_E_None )
{
::rtl::ByteSequence resSeq(aSeq.getLength()+1);
result = rtl_cipher_encode ( aEncoder, aSeq.getStr(), aSeq.getLength()+1,
reinterpret_cast <sal_uInt8*>(resSeq.getArray()), resSeq.getLength() );
/*
//test
rtlCipherError result = rtl_cipher_init (
aEncoder, rtl_Cipher_DirectionDecode,
code, RTL_DIGEST_LENGTH_MD5, NULL, 0 );
if( result == rtl_Cipher_E_None )
{
OUString testOU = getAsciiLine( resSeq );
::rtl::ByteSequence aSeq1 = getBufFromAsciiLine( testOU );
::rtl::ByteSequence resSeq1( aSeq1.getLength() );
if( resSeq.getLength() == aSeq1.getLength() )
{
for( int ind = 0; ind < aSeq1.getLength(); ind++ )
if( resSeq[ind] != aSeq1[ind] )
testOU = "";
}
result = rtl_cipher_decode ( aEncoder, (sal_uInt8*)aSeq1.getArray(), aSeq1.getLength(),
(sal_uInt8*)resSeq1.getArray(), resSeq1.getLength() );
OUString aPasswd( ( char* )resSeq1.getArray(), resSeq1.getLength(), RTL_TEXTENCODING_UTF8 );
}
*/
rtl_cipher_destroy (aEncoder);
if ( result == rtl_Cipher_E_None )
return getAsciiLine( resSeq );
}
rtl_cipher_destroy (aEncoder);
}
}
else
{
OSL_FAIL( "No master password provided!" );
// throw special exception
}
// problems with encoding
OSL_FAIL( "Problem with encoding" );
throw RuntimeException(u"Can't encode!" _ustr );
}
void PasswordContainer::UpdateVector( const OUString& aURL, std::vector< NamePasswordRecord >& toUpdate, NamePasswordRecord const & aRecord, bool writeFile )
{
for (auto & aNPIter : toUpdate)
if ( aNPIter.GetUserName() == aRecord.GetUserName() )
{
if ( aRecord.HasPasswords( MEMORY_RECORD ) )
aNPIter.SetMemoryPasswords( aRecord.GetMemoryPasswords() );
if ( aRecord.HasPasswords( PERSISTENT_RECORD ) )
{
aNPIter.SetPersistentPasswords( aRecord.GetPersistentPasswords(), aRecord.GetPersistentIV() );
if ( writeFile )
{
// the password must be already encoded
m_xStorageFile->update( aURL, aRecord ); // change existing ( aURL, aName ) record in the configfile
}
}
return ;
}
if ( aRecord.HasPasswords( PERSISTENT_RECORD ) && writeFile )
{
// the password must be already encoded
m_xStorageFile->update( aURL, aRecord ); // add new aName to the existing url
}
toUpdate.insert( toUpdate.begin(), aRecord );
}
UserRecord PasswordContainer::CopyToUserRecord( const NamePasswordRecord& aRecord, bool & io_bTryToDecode, const Reference< XInteractionHandler >& aHandler )
{
::std::vector< OUString > aPasswords;
if ( aRecord.HasPasswords( MEMORY_RECORD ) )
aPasswords = aRecord.GetMemoryPasswords();
if ( io_bTryToDecode && aRecord.HasPasswords( PERSISTENT_RECORD ) )
{
try
{
::std::vector< OUString > aDecodedPasswords = DecodePasswords( aRecord.GetPersistentPasswords(), aRecord.GetPersistentIV(),
GetMasterPassword( aHandler ), css::task::PasswordRequestMode_PASSWORD_ENTER );
aPasswords.insert( aPasswords.end(), aDecodedPasswords.begin(), aDecodedPasswords.end() );
}
catch ( NoMasterException& )
{
// if master password could not be detected the entry will be just ignored
io_bTryToDecode = false ;
}
}
return UserRecord( aRecord.GetUserName(), comphelper::containerToSequence( aPasswords ) );
}
Sequence< UserRecord > PasswordContainer::CopyToUserRecordSequence( const std::vector< NamePasswordRecord >& original, const Reference< XInteractionHandler >& aHandler )
{
Sequence< UserRecord > aResult( original.size() );
auto aResultRange = asNonConstRange(aResult);
sal_uInt32 nInd = 0;
bool bTryToDecode = true ;
for (auto const & aNPIter : original)
{
aResultRange[nInd] = CopyToUserRecord( aNPIter, bTryToDecode, aHandler );
++nInd;
}
return aResult;
}
void SAL_CALL PasswordContainer::add( const OUString& Url, const OUString& UserName, const Sequence< OUString >& Passwords, const Reference< XInteractionHandler >& aHandler )
{
std::unique_lock aGuard( mMutex );
PrivateAdd( Url, UserName, Passwords, MEMORY_RECORD, aHandler );
}
void SAL_CALL PasswordContainer::addPersistent( const OUString& Url, const OUString& UserName, const Sequence< OUString >& Passwords, const Reference< XInteractionHandler >& aHandler )
{
std::unique_lock aGuard( mMutex );
PrivateAdd( Url, UserName, Passwords, PERSISTENT_RECORD, aHandler );
}
OUString PasswordContainer::createIV()
{
unsigned char iv[RTL_DIGEST_LENGTH_MD5];
if (rtl_random_getBytes(nullptr, iv, RTL_DIGEST_LENGTH_MD5) != rtl_Random_E_None)
{
throw uno::RuntimeException(u"rtl_random_getBytes failed" _ustr);
}
OUStringBuffer aBuffer;
for (sal_uInt8 i : iv)
{
aBuffer.append(OUString::number(i >> 4, 16) + 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 )
{
NamePasswordRecord aRecord( UserName );
::std::vector< OUString > aStorePass = comphelper::sequenceToContainer< std::vector<OUString> >( Passwords );
if ( Mode == PERSISTENT_RECORD )
{
OUString sIV = createIV();
OUString sEncodedPasswords = EncodePasswords(aStorePass, sIV, GetMasterPassword(aHandler));
aRecord.SetPersistentPasswords(sEncodedPasswords, sIV);
}
else if ( Mode == MEMORY_RECORD )
aRecord.SetMemoryPasswords( std::move(aStorePass) );
else
{
OSL_FAIL( "Unexpected persistence status!" );
return ;
}
if ( !m_aContainer.empty() )
{
PasswordMap::iterator aIter = m_aContainer.find( Url );
if ( aIter != m_aContainer.end() )
{
UpdateVector( aIter->first, aIter->second, aRecord, true );
return ;
}
}
std::vector< NamePasswordRecord > listToAdd( 1, aRecord );
m_aContainer.insert( PairUrlRecord( Url, listToAdd ) );
if ( Mode == PERSISTENT_RECORD && m_xStorageFile && m_xStorageFile->useStorage() )
m_xStorageFile->update( Url, aRecord );
}
UrlRecord SAL_CALL PasswordContainer::find( const OUString& aURL, const Reference< XInteractionHandler >& aHandler )
{
return find( aURL, u"" , false , aHandler );
}
UrlRecord SAL_CALL PasswordContainer::findForName( const OUString& aURL, const OUString& aName, const Reference< XInteractionHandler >& aHandler )
{
return find( aURL, aName, true , aHandler );
}
Sequence< UserRecord > PasswordContainer::FindUsr( const std::vector< NamePasswordRecord >& userlist, std::u16string_view aName, const Reference< XInteractionHandler >& aHandler )
{
for (auto const & aNPIter : userlist)
{
if ( aNPIter.GetUserName() == aName )
{
bool bTryToDecode = true ;
Sequence< UserRecord > aResult { CopyToUserRecord( aNPIter, bTryToDecode, aHandler ) };
return aResult;
}
}
return Sequence< UserRecord >();
}
bool PasswordContainer::createUrlRecord(
const PasswordMap::iterator & rIter,
bool bName,
std::u16string_view aName,
const Reference< XInteractionHandler >& aHandler,
UrlRecord & rRec )
{
if ( bName )
{
Sequence< UserRecord > aUsrRec
= FindUsr( rIter->second, aName, aHandler );
if ( aUsrRec.hasElements() )
{
rRec = UrlRecord( rIter->first, aUsrRec );
return true ;
}
}
else
{
rRec = UrlRecord(
rIter->first,
CopyToUserRecordSequence( rIter->second, aHandler ) );
return true ;
}
return false ;
}
UrlRecord PasswordContainer::find(
const OUString& aURL,
std::u16string_view aName,
bool bName, // only needed to support empty user names
const Reference< XInteractionHandler >& aHandler )
{
std::unique_lock aGuard( mMutex );
if ( !m_aContainer.empty() && !aURL.isEmpty() )
{
OUString aUrl( aURL );
// each iteration remove last '/...' section from the aUrl
// while it's possible, up to the most left '://'
do
{
// first look for <url>/somename and then look for <url>/somename/...
PasswordMap::iterator aIter = m_aContainer.find( aUrl );
if ( aIter != m_aContainer.end() )
{
UrlRecord aRec;
if ( createUrlRecord( aIter, bName, aName, aHandler, aRec ) )
return aRec;
}
else
{
OUString tmpUrl( aUrl );
if ( !tmpUrl.endsWith("/" ) )
tmpUrl += "/" ;
aIter = m_aContainer.lower_bound( tmpUrl );
if ( aIter != m_aContainer.end() && aIter->first.match( tmpUrl ) )
{
UrlRecord aRec;
if ( createUrlRecord( aIter, bName, aName, aHandler, aRec ) )
return aRec;
}
}
}
while ( shorterUrl( aUrl ) && !aUrl.isEmpty() );
}
return UrlRecord();
}
OUString PasswordContainer::GetDefaultMasterPassword()
{
OUStringBuffer aResult;
for ( sal_Int32 nInd = 0; nInd < RTL_DIGEST_LENGTH_MD5; nInd++ )
aResult.append("aa" );
return aResult.makeStringAndClear();
}
OUString PasswordContainer::RequestPasswordFromUser( PasswordRequestMode aRMode, const uno::Reference< task::XInteractionHandler >& xHandler )
{
// empty string means that the call was cancelled or just failed
OUString aResult;
if ( xHandler.is() )
{
::rtl::Reference< MasterPasswordRequest_Impl > xRequest = new MasterPasswordRequest_Impl( aRMode );
xHandler->handle( xRequest );
::rtl::Reference< ucbhelper::InteractionContinuation > xSelection = xRequest->getSelection();
if ( xSelection.is() )
{
Reference< XInteractionAbort > xAbort( xSelection->getXWeak(), UNO_QUERY );
if ( !xAbort.is() )
{
const ::rtl::Reference< ucbhelper::InteractionSupplyAuthentication > & xSupp
= xRequest->getAuthenticationSupplier();
aResult = xSupp->getPassword();
}
}
}
return aResult;
}
// Mangle the key to match an old bug
static OUString ReencodeAsOldHash(std::u16string_view rPass)
{
OUStringBuffer aBuffer;
for (int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ++ind)
{
auto tmp = rPass.substr(ind * 2, 2);
unsigned char i = static_cast <char >(rtl_ustr_toInt64_WithLength(tmp.data(), 16, tmp.size()));
aBuffer.append(OUStringChar(static_cast < sal_Unicode >('a' + (i >> 4)))
+ OUStringChar(static_cast < sal_Unicode >('a' + (i & 15))));
}
return aBuffer.makeStringAndClear();
}
OUString const & PasswordContainer::GetMasterPassword( const Reference< XInteractionHandler >& aHandler )
{
PasswordRequestMode aRMode = PasswordRequestMode_PASSWORD_ENTER;
if ( !m_xStorageFile || !m_xStorageFile->useStorage() )
throw NoMasterException(u"Password storing is not active!" _ustr, Reference< XInterface >(), aRMode );
if ( m_aMasterPassword.isEmpty() && aHandler.is() )
{
OUString aEncodedMP, aEncodedMPIV;
bool bDefaultPassword = false ;
if ( !m_xStorageFile->getEncodedMasterPassword( aEncodedMP, aEncodedMPIV ) )
aRMode = PasswordRequestMode_PASSWORD_CREATE;
else if ( aEncodedMP.isEmpty() )
{
m_aMasterPassword = GetDefaultMasterPassword();
bDefaultPassword = true ;
}
if ( !bDefaultPassword )
{
bool bAskAgain = false ;
do {
bAskAgain = false ;
OUString aPass = RequestPasswordFromUser( aRMode, aHandler );
if ( !aPass.isEmpty() )
{
if ( aRMode == PasswordRequestMode_PASSWORD_CREATE )
{
m_aMasterPassword = aPass;
std::vector< OUString > aMaster( 1, m_aMasterPassword );
OUString sIV = createIV();
m_xStorageFile->setEncodedMasterPassword(EncodePasswords(aMaster, sIV, m_aMasterPassword), sIV);
}
else
{
if (m_xStorageFile->getStorageVersion() == 0)
aPass = ReencodeAsOldHash(aPass);
std::vector< OUString > aRM( DecodePasswords( aEncodedMP, aEncodedMPIV, aPass, aRMode ) );
if ( aRM.empty() || aPass != aRM[0] )
{
bAskAgain = true ;
aRMode = PasswordRequestMode_PASSWORD_REENTER;
}
else
m_aMasterPassword = aPass;
}
}
} while ( bAskAgain );
}
}
if ( m_aMasterPassword.isEmpty() )
throw NoMasterException(u"No master password!" _ustr, Reference< XInterface >(), aRMode );
return m_aMasterPassword;
}
void SAL_CALL PasswordContainer::remove( const OUString& aURL, const OUString& aName )
{
std::unique_lock aGuard( mMutex );
OUString aUrl( aURL );
if ( m_aContainer.empty() )
return ;
PasswordMap::iterator aIter = m_aContainer.find( aUrl );
if ( aIter == m_aContainer.end() )
{
if ( aUrl.endsWith("/" ) )
aUrl = aUrl.copy( 0, aUrl.getLength() - 1 );
else
aUrl += "/" ;
aIter = m_aContainer.find( aUrl );
}
if ( aIter == m_aContainer.end() )
return ;
auto aNPIter = std::find_if(aIter->second.begin(), aIter->second.end(),
[&aName](const NamePasswordRecord& rNPRecord) { return rNPRecord.GetUserName() == aName; });
if (aNPIter != aIter->second.end())
{
if ( aNPIter->HasPasswords( PERSISTENT_RECORD ) && m_xStorageFile )
m_xStorageFile->remove( aURL, aName ); // remove record ( aURL, aName )
// the iterator will not be used any more so it can be removed directly
aIter->second.erase( aNPIter );
if ( aIter->second.empty() )
m_aContainer.erase( aIter );
}
}
void SAL_CALL PasswordContainer::removePersistent( const OUString& aURL, const OUString& aName )
{
std::unique_lock aGuard( mMutex );
OUString aUrl( aURL );
if ( m_aContainer.empty() )
return ;
PasswordMap::iterator aIter = m_aContainer.find( aUrl );
if ( aIter == m_aContainer.end() )
{
if ( aUrl.endsWith("/" ) )
aUrl = aUrl.copy( 0, aUrl.getLength() - 1 );
else
aUrl += "/" ;
aIter = m_aContainer.find( aUrl );
}
if ( aIter == m_aContainer.end() )
return ;
auto aNPIter = std::find_if(aIter->second.begin(), aIter->second.end(),
[&aName](const NamePasswordRecord& rNPRecord) { return rNPRecord.GetUserName() == aName; });
if (aNPIter == aIter->second.end())
return ;
if ( aNPIter->HasPasswords( PERSISTENT_RECORD ) )
{
// TODO/LATER: should the password be converted to MemoryPassword?
aNPIter->RemovePasswords( PERSISTENT_RECORD );
if ( m_xStorageFile )
m_xStorageFile->remove( aURL, aName ); // remove record ( aURL, aName )
}
if ( !aNPIter->HasPasswords( MEMORY_RECORD ) )
aIter->second.erase( aNPIter );
if ( aIter->second.empty() )
m_aContainer.erase( aIter );
}
void SAL_CALL PasswordContainer::removeAllPersistent()
{
std::unique_lock aGuard(mMutex);
removeAllPersistent(aGuard);
}
void PasswordContainer::removeAllPersistent(std::unique_lock<std::mutex>& /*rGuard*/)
{
if ( m_xStorageFile )
m_xStorageFile->clear();
for ( PasswordMap::iterator aIter = m_aContainer.begin(); aIter != m_aContainer.end(); )
{
for ( std::vector< NamePasswordRecord >::iterator aNPIter = aIter->second.begin(); aNPIter != aIter->second.end(); )
{
if ( aNPIter->HasPasswords( PERSISTENT_RECORD ) )
{
// TODO/LATER: should the password be converted to MemoryPassword?
aNPIter->RemovePasswords( PERSISTENT_RECORD );
if ( m_xStorageFile )
m_xStorageFile->remove( aIter->first, aNPIter->GetUserName() ); // remove record ( aURL, aName )
}
if ( !aNPIter->HasPasswords( MEMORY_RECORD ) )
{
aNPIter = aIter->second.erase(aNPIter);
}
else
++aNPIter;
}
if ( aIter->second.empty() )
{
aIter = m_aContainer.erase(aIter);
}
else
++aIter;
}
}
Sequence< UrlRecord > SAL_CALL PasswordContainer::getAllPersistent( const Reference< XInteractionHandler >& xHandler )
{
std::unique_lock aGuard( mMutex );
return getAllPersistent(aGuard, xHandler);
}
Sequence< UrlRecord > PasswordContainer::getAllPersistent( std::unique_lock<std::mutex>& /*rGuard*/, const Reference< XInteractionHandler >& xHandler )
{
Sequence< UrlRecord > aResult;
for ( const auto & rEntry : m_aContainer )
{
Sequence< UserRecord > aUsers;
for (auto const & aNP : rEntry.second)
if ( aNP.HasPasswords( PERSISTENT_RECORD ) )
{
sal_Int32 oldLen = aUsers.getLength();
aUsers.realloc( oldLen + 1 );
aUsers.getArray()[ oldLen ] = UserRecord( aNP.GetUserName(), comphelper::containerToSequence( DecodePasswords( aNP.GetPersistentPasswords(), aNP.GetPersistentIV(),
GetMasterPassword( xHandler ), css::task::PasswordRequestMode_PASSWORD_ENTER ) ) );
}
if ( aUsers.hasElements() )
{
sal_Int32 oldLen = aResult.getLength();
aResult.realloc( oldLen + 1 );
aResult.getArray()[ oldLen ] = UrlRecord( rEntry.first, aUsers );
}
}
return aResult;
}
sal_Bool SAL_CALL PasswordContainer::authorizateWithMasterPassword( const uno::Reference< task::XInteractionHandler >& xHandler )
{
std::unique_lock aGuard( mMutex );
return authorizateWithMasterPassword(aGuard, xHandler);
}
bool PasswordContainer::authorizateWithMasterPassword( std::unique_lock<std::mutex>& /*rGuard*/, const uno::Reference< task::XInteractionHandler >& xHandler )
{
bool bResult = false ;
OUString aEncodedMP, aEncodedMPIV;
uno::Reference< task::XInteractionHandler > xTmpHandler = xHandler;
// the method should fail if there is no master password
if ( m_xStorageFile && m_xStorageFile->useStorage() && m_xStorageFile->getEncodedMasterPassword( aEncodedMP, aEncodedMPIV ) )
{
if ( aEncodedMP.isEmpty() )
{
// this is a default master password
// no UI is necessary
bResult = true ;
}
else
{
if ( !xTmpHandler.is() )
{
uno::Reference< lang::XMultiServiceFactory > xFactory( mComponent, uno::UNO_QUERY_THROW );
uno::Reference< uno::XComponentContext > xContext( comphelper::getComponentContext(xFactory) );
xTmpHandler.set( InteractionHandler::createWithParent(xContext, nullptr), uno::UNO_QUERY_THROW );
}
if ( !m_aMasterPassword.isEmpty() )
{
// there is a password, it should be just rechecked
PasswordRequestMode aRMode = PasswordRequestMode_PASSWORD_ENTER;
OUString aPass;
do {
aPass = RequestPasswordFromUser( aRMode, xTmpHandler );
if (!aPass.isEmpty() && m_xStorageFile->getStorageVersion() == 0)
{
aPass = ReencodeAsOldHash(aPass);
}
bResult = ( !aPass.isEmpty() && aPass == m_aMasterPassword );
aRMode = PasswordRequestMode_PASSWORD_REENTER; // further questions with error notification
} while ( !bResult && !aPass.isEmpty() );
}
else
{
try
{
// ask for the password, if user provide no correct password an exception will be thrown
bResult = !GetMasterPassword( xTmpHandler ).isEmpty();
}
catch ( uno::Exception& )
{}
}
}
}
return bResult;
}
sal_Bool SAL_CALL PasswordContainer::changeMasterPassword( const uno::Reference< task::XInteractionHandler >& xHandler )
{
bool bResult = false ;
uno::Reference< task::XInteractionHandler > xTmpHandler = xHandler;
std::unique_lock aGuard( mMutex );
if ( m_xStorageFile && m_xStorageFile->useStorage() )
{
if ( !xTmpHandler.is() )
{
uno::Reference< lang::XMultiServiceFactory > xFactory( mComponent, uno::UNO_QUERY_THROW );
uno::Reference< uno::XComponentContext > xContext( comphelper::getComponentContext(xFactory) );
xTmpHandler.set( InteractionHandler::createWithParent(xContext, nullptr), uno::UNO_QUERY_THROW );
}
bool bCanChangePassword = true ;
// if there is already a stored master password it should be entered by the user before the change happen
OUString aEncodedMP, aEncodedMPIV;
if ( !m_aMasterPassword.isEmpty() || m_xStorageFile->getEncodedMasterPassword( aEncodedMP, aEncodedMPIV ) )
bCanChangePassword = authorizateWithMasterPassword( aGuard, xTmpHandler );
if ( bCanChangePassword )
{
// ask for the new password, but do not set it
OUString aPass = RequestPasswordFromUser( PasswordRequestMode_PASSWORD_CREATE, xTmpHandler );
if ( !aPass.isEmpty() )
{
// get all the persistent entries if it is possible
const Sequence< UrlRecord > aPersistent = getAllPersistent( aGuard, uno::Reference< task::XInteractionHandler >() );
// remove the master password and the entries persistence
removeMasterPassword(aGuard);
// store the new master password
m_aMasterPassword = aPass;
std::vector< OUString > aMaster( 1, m_aMasterPassword );
OUString aIV = createIV();
m_xStorageFile->setEncodedMasterPassword(EncodePasswords(aMaster, aIV, m_aMasterPassword), aIV);
// store all the entries with the new password
for ( const auto & rURL : aPersistent )
for ( const auto & rUser : rURL.UserList )
PrivateAdd( rURL.Url, rUser.UserName, rUser.Passwords, PERSISTENT_RECORD,
uno::Reference< task::XInteractionHandler >() );
bResult = true ;
}
}
}
return bResult;
}
void SAL_CALL PasswordContainer::removeMasterPassword()
{
std::unique_lock aGuard(mMutex);
removeMasterPassword(aGuard);
}
void PasswordContainer::removeMasterPassword(std::unique_lock<std::mutex>& rGuard)
{
// remove all the stored passwords and the master password
removeAllPersistent(rGuard);
if ( m_xStorageFile )
{
m_aMasterPassword.clear();
m_xStorageFile->setEncodedMasterPassword( OUString(), OUString() ); // let the master password be removed from configuration
}
}
sal_Bool SAL_CALL PasswordContainer::hasMasterPassword( )
{
std::unique_lock aGuard( mMutex );
if ( !m_xStorageFile )
throw uno::RuntimeException(u"storage file not set" _ustr);
OUString aEncodedMP, aEncodedMPIV;
return ( m_xStorageFile->useStorage() && m_xStorageFile->getEncodedMasterPassword( aEncodedMP, aEncodedMPIV ) );
}
sal_Bool SAL_CALL PasswordContainer::allowPersistentStoring( sal_Bool bAllow )
{
std::unique_lock aGuard( mMutex );
if ( !m_xStorageFile )
throw uno::RuntimeException(u"storage file not set" _ustr);
if ( !bAllow )
removeMasterPassword(aGuard);
if (m_xStorageFile->useStorage() == static_cast <bool >(bAllow))
return bAllow;
m_xStorageFile->setUseStorage( bAllow );
return !bAllow;
}
sal_Bool SAL_CALL PasswordContainer::isPersistentStoringAllowed()
{
std::unique_lock aGuard( mMutex );
if ( !m_xStorageFile )
throw uno::RuntimeException(u"storage file not set" _ustr);
return m_xStorageFile->useStorage();
}
sal_Bool SAL_CALL PasswordContainer::useDefaultMasterPassword( const uno::Reference< task::XInteractionHandler >& xHandler )
{
bool bResult = false ;
uno::Reference< task::XInteractionHandler > xTmpHandler = xHandler;
std::unique_lock aGuard( mMutex );
if ( m_xStorageFile && m_xStorageFile->useStorage() )
{
if ( !xTmpHandler.is() )
{
uno::Reference< lang::XMultiServiceFactory > xFactory( mComponent, uno::UNO_QUERY_THROW );
uno::Reference< uno::XComponentContext > xContext( comphelper::getComponentContext(xFactory) );
xTmpHandler.set( InteractionHandler::createWithParent(xContext, nullptr), uno::UNO_QUERY_THROW );
}
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, aEncodedMPIV;
if ( m_xStorageFile->getEncodedMasterPassword( aEncodedMP, aEncodedMPIV ) && !aEncodedMP.isEmpty() )
bCanChangePassword = authorizateWithMasterPassword( aGuard, xTmpHandler );
if ( bCanChangePassword )
{
// generate the default password
OUString aPass = GetDefaultMasterPassword();
if ( !aPass.isEmpty() )
{
// get all the persistent entries if it is possible
const Sequence< UrlRecord > aPersistent = getAllPersistent( aGuard, uno::Reference< task::XInteractionHandler >() );
// remove the master password and the entries persistence
removeMasterPassword(aGuard);
// store the empty string to flag the default master password
m_aMasterPassword = aPass;
m_xStorageFile->setEncodedMasterPassword( OUString(), OUString(), true );
// store all the entries with the new password
for ( const auto & rURL : aPersistent )
for ( const auto & rUser : rURL.UserList )
PrivateAdd( rURL.Url, rUser.UserName, rUser.Passwords, PERSISTENT_RECORD,
uno::Reference< task::XInteractionHandler >() );
bResult = true ;
}
}
}
return bResult;
}
sal_Bool SAL_CALL PasswordContainer::isDefaultMasterPasswordUsed()
{
std::unique_lock aGuard( mMutex );
if ( !m_xStorageFile )
throw uno::RuntimeException(u"storage file not set" _ustr);
OUString aEncodedMP, aEncodedMPIV;
return ( m_xStorageFile->useStorage() && m_xStorageFile->getEncodedMasterPassword( aEncodedMP, aEncodedMPIV ) && aEncodedMP.isEmpty() );
}
void SAL_CALL PasswordContainer::addUrl( const OUString& Url, sal_Bool MakePersistent )
{
mUrlContainer.add( Url, MakePersistent );
}
OUString SAL_CALL PasswordContainer::findUrl( const OUString& Url )
{
return mUrlContainer.find( Url );
}
void SAL_CALL PasswordContainer::removeUrl( const OUString& Url )
{
mUrlContainer.remove( Url );
}
uno::Sequence< OUString > SAL_CALL PasswordContainer::getUrls( sal_Bool OnlyPersistent )
{
return mUrlContainer.list( OnlyPersistent );
}
void PasswordContainer::Notify()
{
std::unique_lock aGuard( mMutex );
// remove the cached persistent values in the memory
for ( auto & rEntry : m_aContainer )
{
for ( std::vector< NamePasswordRecord >::iterator aNPIter = rEntry.second.begin(); aNPIter != rEntry.second.end(); )
{
if ( aNPIter->HasPasswords( PERSISTENT_RECORD ) )
{
aNPIter->RemovePasswords( PERSISTENT_RECORD );
if ( m_xStorageFile )
m_xStorageFile->remove( rEntry.first, aNPIter->GetUserName() ); // remove record ( aURL, aName )
}
if ( !aNPIter->HasPasswords( MEMORY_RECORD ) )
{
aNPIter = rEntry.second.erase(aNPIter);
}
else
++aNPIter;
}
}
PasswordMap addon;
if ( m_xStorageFile )
addon = m_xStorageFile->getInfo();
for ( const auto & rEntry : addon )
{
PasswordMap::iterator aSearchIter = m_aContainer.find( rEntry.first );
if ( aSearchIter != m_aContainer.end() )
for (auto const & aNP : rEntry.second)
UpdateVector( aSearchIter->first, aSearchIter->second, aNP, false );
else
m_aContainer.insert( PairUrlRecord( rEntry.first, rEntry.second ) );
}
}
OUString SAL_CALL PasswordContainer::getImplementationName( )
{
return u"stardiv.svl.PasswordContainer" _ustr;
}
sal_Bool SAL_CALL PasswordContainer::supportsService( const OUString& ServiceName )
{
return cppu::supportsService( this , ServiceName );
}
Sequence< OUString > SAL_CALL PasswordContainer::getSupportedServiceNames( )
{
return { u"com.sun.star.task.PasswordContainer" _ustr };
}
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
svl_PasswordContainer_get_implementation(
css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
{
return cppu::acquire(new PasswordContainer(context));
}
MasterPasswordRequest_Impl::MasterPasswordRequest_Impl( PasswordRequestMode Mode )
{
MasterPasswordRequest aRequest;
aRequest.Classification = InteractionClassification_ERROR;
aRequest.Mode = Mode;
setRequest( Any( aRequest ) );
// Fill continuations...
Sequence< RememberAuthentication > aRememberModes{ RememberAuthentication_NO };
m_xAuthSupplier
= new ::ucbhelper::InteractionSupplyAuthentication(
this ,
false , // bCanSetRealm
false , // bCanSetUserName
true , // bCanSetPassword
false , // bCanSetAccount
aRememberModes, // rRememberPasswordModes
RememberAuthentication_NO, // eDefaultRememberPasswordMode
aRememberModes, // rRememberAccountModes
RememberAuthentication_NO, // eDefaultRememberAccountMode
false // bCanUseSystemCredentials
);
Sequence<
Reference< XInteractionContinuation > > aContinuations{
new ::ucbhelper::InteractionAbort( this ),
new ::ucbhelper::InteractionRetry( this ),
m_xAuthSupplier
};
setContinuations( aContinuations );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 C=97 H=96 G=96
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland