1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <string_view>
25 #include "passwordcontainer.hxx"
27 #include <cppuhelper/factory.hxx>
28 #include <cppuhelper/supportsservice.hxx>
29 #include <comphelper/processfactory.hxx>
30 #include <comphelper/propertyvalue.hxx>
31 #include <comphelper/sequence.hxx>
32 #include <com/sun/star/beans/PropertyValue.hpp>
33 #include <com/sun/star/task/InteractionHandler.hpp>
34 #include <com/sun/star/task/MasterPasswordRequest.hpp>
35 #include <com/sun/star/task/NoMasterException.hpp>
36 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
38 #include <osl/diagnose.h>
39 #include <rtl/character.hxx>
40 #include <rtl/cipher.h>
41 #include <rtl/digest.h>
42 #include <rtl/byteseq.hxx>
43 #include <rtl/ustrbuf.hxx>
47 using namespace com::sun::star
;
48 using namespace com::sun::star::uno
;
49 using namespace com::sun::star::registry
;
50 using namespace com::sun::star::lang
;
51 using namespace com::sun::star::task
;
52 using namespace com::sun::star::ucb
;
54 static OUString
createIndex(const std::vector
< OUString
>& lines
)
56 OUStringBuffer aResult
;
58 for( size_t i
= 0; i
< lines
.size(); i
++ )
62 OString line
= OUStringToOString( lines
[i
], RTL_TEXTENCODING_UTF8
);
63 const char* pLine
= line
.getStr();
67 if (rtl::isAsciiAlphanumeric(static_cast<unsigned char>(*pLine
)))
69 aResult
.append(*pLine
);
73 aResult
.append("_" + OUString::number(*pLine
, 16) );
80 return aResult
.makeStringAndClear();
84 static std::vector
< OUString
> getInfoFromInd( std::u16string_view aInd
)
86 std::vector
< OUString
> aResult
;
89 OString line
= OUStringToOString( aInd
, RTL_TEXTENCODING_ASCII_US
);
90 const char* pLine
= line
.getStr();
93 OUStringBuffer newItem
;
99 while( *pLine
&& ( pLine
[0] != '_' || pLine
[1] != '_' ))
102 newItem
.append( *pLine
);
108 for( int i
= 1; i
< 3; i
++ )
111 || ( ( pLine
[i
] < '0' || pLine
[i
] > '9' )
112 && ( pLine
[i
] < 'a' || pLine
[i
] > 'f' )
113 && ( pLine
[i
] < 'A' || pLine
[i
] > 'F' ) ) )
115 OSL_FAIL( "Wrong index syntax!" );
119 aNum
+= OUStringChar( pLine
[i
] );
122 newItem
.append( sal_Unicode( aNum
.toUInt32( 16 ) ) );
126 aResult
.push_back( newItem
.makeStringAndClear() );
127 } while( pLine
[0] == '_' && pLine
[1] == '_' );
130 OSL_FAIL( "Wrong index syntax!" );
136 static bool shorterUrl( OUString
& aURL
)
138 sal_Int32 aInd
= aURL
.lastIndexOf( '/' );
139 if( aInd
> 0 && aURL
.indexOf( "://" ) != aInd
-2 )
141 aURL
= aURL
.copy( 0, aInd
);
149 static OUString
getAsciiLine( const ::rtl::ByteSequence
& buf
)
153 ::rtl::ByteSequence
outbuf( buf
.getLength()*2+1 );
155 for( int ind
= 0; ind
< buf
.getLength(); ind
++ )
157 outbuf
[ind
*2] = ( static_cast<sal_uInt8
>(buf
[ind
]) >> 4 ) + 'a';
158 outbuf
[ind
*2+1] = ( static_cast<sal_uInt8
>(buf
[ind
]) & 0x0f ) + 'a';
160 outbuf
[buf
.getLength()*2] = '\0';
162 aResult
= OUString::createFromAscii( reinterpret_cast<char*>(outbuf
.getArray()) );
168 static ::rtl::ByteSequence
getBufFromAsciiLine( const OUString
& line
)
170 OSL_ENSURE( line
.getLength() % 2 == 0, "Wrong syntax!" );
171 OString tmpLine
= OUStringToOString( line
, RTL_TEXTENCODING_ASCII_US
);
172 ::rtl::ByteSequence
aResult(line
.getLength()/2);
174 for( int ind
= 0; ind
< tmpLine
.getLength()/2; ind
++ )
176 aResult
[ind
] = ( static_cast<sal_uInt8
>( tmpLine
[ind
*2] - 'a' ) << 4 ) | static_cast<sal_uInt8
>( tmpLine
[ind
*2+1] - 'a' );
183 PasswordMap
StorageItem::getInfo()
187 const Sequence
< OUString
> aNodeNames
= ConfigItem::GetNodeNames( "Store" );
188 sal_Int32 aNodeCount
= aNodeNames
.getLength();
189 Sequence
< OUString
> aPropNames( aNodeCount
* 2);
191 std::transform(aNodeNames
.begin(), aNodeNames
.end(), aPropNames
.getArray(),
192 [](const OUString
& rName
) -> OUString
{
193 return "Store/Passwordstorage['" + rName
+ "']/Password"; });
194 std::transform(aNodeNames
.begin(), aNodeNames
.end(), aPropNames
.getArray() + aNodeCount
,
195 [](const OUString
& rName
) -> OUString
{
196 return "Store/Passwordstorage['" + rName
+ "']/InitializationVector"; });
198 Sequence
< Any
> aPropertyValues
= ConfigItem::GetProperties( aPropNames
);
200 if( aPropertyValues
.getLength() != aNodeCount
* 2)
202 OSL_FAIL( "Problems during reading" );
206 for( sal_Int32 aNodeInd
= 0; aNodeInd
< aNodeCount
; ++aNodeInd
)
208 std::vector
< OUString
> aUrlUsr
= getInfoFromInd( aNodeNames
[aNodeInd
] );
210 if( aUrlUsr
.size() == 2 )
212 OUString aUrl
= aUrlUsr
[0];
213 OUString aName
= aUrlUsr
[1];
217 aPropertyValues
[aNodeInd
] >>= aEPasswd
;
218 aPropertyValues
[aNodeInd
+ aNodeCount
] >>= aIV
;
220 PasswordMap::iterator aIter
= aResult
.find( aUrl
);
221 if( aIter
!= aResult
.end() )
222 aIter
->second
.emplace_back( aName
, aEPasswd
, aIV
);
225 NamePasswordRecord
aNewRecord( aName
, aEPasswd
, aIV
);
226 std::vector
< NamePasswordRecord
> listToAdd( 1, aNewRecord
);
228 aResult
.insert( PairUrlRecord( aUrl
, listToAdd
) );
232 OSL_FAIL( "Wrong index syntax!" );
239 void StorageItem::setUseStorage( bool bUse
)
241 ConfigItem::SetModified();
242 ConfigItem::PutProperties( { "UseStorage" }, { uno::Any(bUse
) } );
246 bool StorageItem::useStorage()
248 Sequence
<OUString
> aNodeNames
{ "UseStorage" };
250 Sequence
< Any
> aPropertyValues
= ConfigItem::GetProperties( aNodeNames
);
252 if( aPropertyValues
.getLength() != aNodeNames
.getLength() )
254 OSL_FAIL( "Problems during reading" );
258 bool aResult
= false;
259 aPropertyValues
[0] >>= aResult
;
265 sal_Int32
StorageItem::getStorageVersion()
267 Sequence
<OUString
> aNodeNames
{ "StorageVersion" };
269 Sequence
< Any
> aPropertyValues
= ConfigItem::GetProperties( aNodeNames
);
271 if( aPropertyValues
.getLength() != aNodeNames
.getLength() )
273 OSL_FAIL( "Problems during reading" );
277 sal_Int32 nResult
= 0;
278 aPropertyValues
[0] >>= nResult
;
283 bool StorageItem::getEncodedMasterPassword( OUString
& aResult
, OUString
& aResultIV
)
288 aResultIV
= mEncodedIV
;
292 Sequence
< OUString
> aNodeNames
{ "HasMaster", "Master", "MasterInitializationVector" };
294 Sequence
< Any
> aPropertyValues
= ConfigItem::GetProperties( aNodeNames
);
296 if( aPropertyValues
.getLength() != aNodeNames
.getLength() )
298 OSL_FAIL( "Problems during reading" );
302 aPropertyValues
[0] >>= hasEncoded
;
303 aPropertyValues
[1] >>= mEncoded
;
304 aPropertyValues
[2] >>= mEncodedIV
;
307 aResultIV
= mEncodedIV
;
313 void StorageItem::setEncodedMasterPassword( const OUString
& aEncoded
, const OUString
& aEncodedIV
, bool bAcceptEmpty
)
315 bool bHasMaster
= ( !aEncoded
.isEmpty() || bAcceptEmpty
);
317 ConfigItem::SetModified();
318 ConfigItem::PutProperties( { "HasMaster", "Master", "MasterInitializationVector", "StorageVersion" },
319 { uno::Any(bHasMaster
), uno::Any(aEncoded
),
320 uno::Any(aEncodedIV
), uno::Any(nCurrentStorageVersion
) } );
322 hasEncoded
= bHasMaster
;
324 mEncodedIV
= aEncodedIV
;
328 void StorageItem::remove( const OUString
& aURL
, const OUString
& aName
)
330 Sequence
< OUString
> sendSeq
{ createIndex( { aURL
, aName
} ) };
332 ConfigItem::ClearNodeElements( "Store", sendSeq
);
336 void StorageItem::clear()
338 ConfigItem::ClearNodeSet( "Store" );
342 void StorageItem::update( const OUString
& aURL
, const NamePasswordRecord
& aRecord
)
344 if ( !aRecord
.HasPasswords( PERSISTENT_RECORD
) )
346 OSL_FAIL( "Unexpected storing of a record!" );
350 Sequence
< beans::PropertyValue
> sendSeq
{ comphelper::makePropertyValue(
351 "Store/Passwordstorage['" + createIndex( { aURL
, aRecord
.GetUserName() } ) + "']/InitializationVector",
352 aRecord
.GetPersistentIV()), comphelper::makePropertyValue(
353 "Store/Passwordstorage['" + createIndex( { aURL
, aRecord
.GetUserName() } ) + "']/Password",
354 aRecord
.GetPersistentPasswords()) };
356 ConfigItem::SetModified();
357 ConfigItem::SetSetProperties( "Store", sendSeq
);
361 void StorageItem::Notify( const Sequence
< OUString
>& )
363 // this feature still should not be used
369 void StorageItem::ImplCommit()
371 // Do nothing, we stored everything we want already
375 PasswordContainer::PasswordContainer( const Reference
<XComponentContext
>& rxContext
)
377 // m_pStorageFile->Notify() can be called
378 ::osl::MutexGuard
aGuard( mMutex
);
380 mComponent
.set( rxContext
->getServiceManager(), UNO_QUERY
);
381 mComponent
->addEventListener( this );
383 m_xStorageFile
.emplace( this, "Office.Common/Passwords" );
384 if( m_xStorageFile
->useStorage() )
385 m_aContainer
= m_xStorageFile
->getInfo();
389 PasswordContainer::~PasswordContainer()
391 ::osl::MutexGuard
aGuard( mMutex
);
393 m_xStorageFile
.reset();
395 if( mComponent
.is() )
397 mComponent
->removeEventListener(this);
402 void SAL_CALL
PasswordContainer::disposing( const EventObject
& )
404 ::osl::MutexGuard
aGuard( mMutex
);
406 m_xStorageFile
.reset();
408 if( mComponent
.is() )
410 //mComponent->removeEventListener(this);
415 std::vector
< OUString
> PasswordContainer::DecodePasswords( const OUString
& aLine
, const OUString
& aIV
, const OUString
& aMasterPasswd
, css::task::PasswordRequestMode mode
)
417 if( !aMasterPasswd
.isEmpty() )
419 rtlCipher aDecoder
= rtl_cipher_create (rtl_Cipher_AlgorithmBF
, rtl_Cipher_ModeStream
);
420 OSL_ENSURE( aDecoder
, "Can't create decoder" );
424 OSL_ENSURE( aMasterPasswd
.getLength() == RTL_DIGEST_LENGTH_MD5
* 2, "Wrong master password format!" );
426 unsigned char code
[RTL_DIGEST_LENGTH_MD5
];
427 for( int ind
= 0; ind
< RTL_DIGEST_LENGTH_MD5
; ind
++ )
428 code
[ ind
] = static_cast<char>(aMasterPasswd
.copy( ind
*2, 2 ).toUInt32(16));
430 unsigned char iv
[RTL_DIGEST_LENGTH_MD5
] = {0};
433 for( int ind
= 0; ind
< RTL_DIGEST_LENGTH_MD5
; ind
++ )
434 iv
[ ind
] = static_cast<char>(aIV
.copy( ind
*2, 2 ).toUInt32(16));
437 rtlCipherError result
= rtl_cipher_init (
438 aDecoder
, rtl_Cipher_DirectionDecode
,
439 code
, RTL_DIGEST_LENGTH_MD5
, iv
, RTL_DIGEST_LENGTH_MD5
);
441 if( result
== rtl_Cipher_E_None
)
443 ::rtl::ByteSequence aSeq
= getBufFromAsciiLine( aLine
);
445 ::rtl::ByteSequence
resSeq( aSeq
.getLength() );
447 rtl_cipher_decode ( aDecoder
, aSeq
.getArray(), aSeq
.getLength(),
448 reinterpret_cast<sal_uInt8
*>(resSeq
.getArray()), resSeq
.getLength() );
450 OUString
aPasswd( reinterpret_cast<char*>(resSeq
.getArray()), resSeq
.getLength(), RTL_TEXTENCODING_UTF8
);
452 rtl_cipher_destroy (aDecoder
);
454 return getInfoFromInd( aPasswd
);
457 rtl_cipher_destroy (aDecoder
);
462 OSL_FAIL( "No master password provided!" );
463 // throw special exception
466 // problems with decoding
467 OSL_FAIL( "Problem with decoding" );
468 throw css::task::NoMasterException(
469 "Can't decode!", css::uno::Reference
<css::uno::XInterface
>(), mode
);
472 OUString
PasswordContainer::EncodePasswords(const std::vector
< OUString
>& lines
, const OUString
& aIV
, const OUString
& aMasterPasswd
)
474 if( !aMasterPasswd
.isEmpty() )
476 OString aSeq
= OUStringToOString( createIndex( lines
), RTL_TEXTENCODING_UTF8
);
478 rtlCipher aEncoder
= rtl_cipher_create (rtl_Cipher_AlgorithmBF
, rtl_Cipher_ModeStream
);
479 OSL_ENSURE( aEncoder
, "Can't create encoder" );
483 OSL_ENSURE( aMasterPasswd
.getLength() == RTL_DIGEST_LENGTH_MD5
* 2, "Wrong master password format!" );
485 unsigned char code
[RTL_DIGEST_LENGTH_MD5
];
486 for( int ind
= 0; ind
< RTL_DIGEST_LENGTH_MD5
; ind
++ )
487 code
[ ind
] = static_cast<char>(aMasterPasswd
.copy( ind
*2, 2 ).toUInt32(16));
489 unsigned char iv
[RTL_DIGEST_LENGTH_MD5
] = {0};
492 for( int ind
= 0; ind
< RTL_DIGEST_LENGTH_MD5
; ind
++ )
493 iv
[ ind
] = static_cast<char>(aIV
.copy( ind
*2, 2 ).toUInt32(16));
496 rtlCipherError result
= rtl_cipher_init (
497 aEncoder
, rtl_Cipher_DirectionEncode
,
498 code
, RTL_DIGEST_LENGTH_MD5
, iv
, RTL_DIGEST_LENGTH_MD5
);
500 if( result
== rtl_Cipher_E_None
)
502 ::rtl::ByteSequence
resSeq(aSeq
.getLength()+1);
504 result
= rtl_cipher_encode ( aEncoder
, aSeq
.getStr(), aSeq
.getLength()+1,
505 reinterpret_cast<sal_uInt8
*>(resSeq
.getArray()), resSeq
.getLength() );
509 rtlCipherError result = rtl_cipher_init (
510 aEncoder, rtl_Cipher_DirectionDecode,
511 code, RTL_DIGEST_LENGTH_MD5, NULL, 0 );
514 if( result == rtl_Cipher_E_None )
516 OUString testOU = getAsciiLine( resSeq );
517 ::rtl::ByteSequence aSeq1 = getBufFromAsciiLine( testOU );
519 ::rtl::ByteSequence resSeq1( aSeq1.getLength() );
521 if( resSeq.getLength() == aSeq1.getLength() )
523 for( int ind = 0; ind < aSeq1.getLength(); ind++ )
524 if( resSeq[ind] != aSeq1[ind] )
528 result = rtl_cipher_decode ( aEncoder, (sal_uInt8*)aSeq1.getArray(), aSeq1.getLength(),
529 (sal_uInt8*)resSeq1.getArray(), resSeq1.getLength() );
531 OUString aPasswd( ( char* )resSeq1.getArray(), resSeq1.getLength(), RTL_TEXTENCODING_UTF8 );
535 rtl_cipher_destroy (aEncoder
);
537 if( result
== rtl_Cipher_E_None
)
538 return getAsciiLine( resSeq
);
542 rtl_cipher_destroy (aEncoder
);
547 OSL_FAIL( "No master password provided!" );
548 // throw special exception
551 // problems with encoding
552 OSL_FAIL( "Problem with encoding" );
553 throw RuntimeException("Can't encode!" );
556 void PasswordContainer::UpdateVector( const OUString
& aURL
, std::vector
< NamePasswordRecord
>& toUpdate
, NamePasswordRecord
const & aRecord
, bool writeFile
)
558 for (auto & aNPIter
: toUpdate
)
559 if( aNPIter
.GetUserName() == aRecord
.GetUserName() )
561 if( aRecord
.HasPasswords( MEMORY_RECORD
) )
562 aNPIter
.SetMemoryPasswords( aRecord
.GetMemoryPasswords() );
564 if( aRecord
.HasPasswords( PERSISTENT_RECORD
) )
566 aNPIter
.SetPersistentPasswords( aRecord
.GetPersistentPasswords(), aRecord
.GetPersistentIV() );
570 // the password must be already encoded
571 m_xStorageFile
->update( aURL
, aRecord
); // change existing ( aURL, aName ) record in the configfile
579 if( aRecord
.HasPasswords( PERSISTENT_RECORD
) && writeFile
)
581 // the password must be already encoded
582 m_xStorageFile
->update( aURL
, aRecord
); // add new aName to the existing url
585 toUpdate
.insert( toUpdate
.begin(), aRecord
);
589 UserRecord
PasswordContainer::CopyToUserRecord( const NamePasswordRecord
& aRecord
, bool& io_bTryToDecode
, const Reference
< XInteractionHandler
>& aHandler
)
591 ::std::vector
< OUString
> aPasswords
;
592 if( aRecord
.HasPasswords( MEMORY_RECORD
) )
593 aPasswords
= aRecord
.GetMemoryPasswords();
595 if( io_bTryToDecode
&& aRecord
.HasPasswords( PERSISTENT_RECORD
) )
599 ::std::vector
< OUString
> aDecodedPasswords
= DecodePasswords( aRecord
.GetPersistentPasswords(), aRecord
.GetPersistentIV(),
600 GetMasterPassword( aHandler
), css::task::PasswordRequestMode_PASSWORD_ENTER
);
601 aPasswords
.insert( aPasswords
.end(), aDecodedPasswords
.begin(), aDecodedPasswords
.end() );
603 catch( NoMasterException
& )
605 // if master password could not be detected the entry will be just ignored
606 io_bTryToDecode
= false;
610 return UserRecord( aRecord
.GetUserName(), comphelper::containerToSequence( aPasswords
) );
614 Sequence
< UserRecord
> PasswordContainer::CopyToUserRecordSequence( const std::vector
< NamePasswordRecord
>& original
, const Reference
< XInteractionHandler
>& aHandler
)
616 Sequence
< UserRecord
> aResult( original
.size() );
617 auto aResultRange
= asNonConstRange(aResult
);
619 bool bTryToDecode
= true;
621 for (auto const& aNPIter
: original
)
623 aResultRange
[nInd
] = CopyToUserRecord( aNPIter
, bTryToDecode
, aHandler
);
631 void SAL_CALL
PasswordContainer::add( const OUString
& Url
, const OUString
& UserName
, const Sequence
< OUString
>& Passwords
, const Reference
< XInteractionHandler
>& aHandler
)
633 ::osl::MutexGuard
aGuard( mMutex
);
635 PrivateAdd( Url
, UserName
, Passwords
, MEMORY_RECORD
, aHandler
);
639 void SAL_CALL
PasswordContainer::addPersistent( const OUString
& Url
, const OUString
& UserName
, const Sequence
< OUString
>& Passwords
, const Reference
< XInteractionHandler
>& aHandler
)
641 ::osl::MutexGuard
aGuard( mMutex
);
643 PrivateAdd( Url
, UserName
, Passwords
, PERSISTENT_RECORD
, aHandler
);
646 OUString
PasswordContainer::createIV()
648 rtlRandomPool randomPool
= mRandomPool
.get();
649 unsigned char iv
[RTL_DIGEST_LENGTH_MD5
];
650 rtl_random_getBytes(randomPool
, iv
, RTL_DIGEST_LENGTH_MD5
);
651 OUStringBuffer aBuffer
;
652 for (sal_uInt8 i
: iv
)
654 aBuffer
.append(OUString::number(i
>> 4, 16));
655 aBuffer
.append(OUString::number(i
& 15, 16));
657 return aBuffer
.makeStringAndClear();
660 void PasswordContainer::PrivateAdd( const OUString
& Url
, const OUString
& UserName
, const Sequence
< OUString
>& Passwords
, char Mode
, const Reference
< XInteractionHandler
>& aHandler
)
662 NamePasswordRecord
aRecord( UserName
);
663 ::std::vector
< OUString
> aStorePass
= comphelper::sequenceToContainer
< std::vector
<OUString
> >( Passwords
);
665 if( Mode
== PERSISTENT_RECORD
)
667 OUString sIV
= createIV();
668 OUString sEncodedPasswords
= EncodePasswords(aStorePass
, sIV
, GetMasterPassword(aHandler
));
669 aRecord
.SetPersistentPasswords(sEncodedPasswords
, sIV
);
671 else if( Mode
== MEMORY_RECORD
)
672 aRecord
.SetMemoryPasswords( std::move(aStorePass
) );
675 OSL_FAIL( "Unexpected persistence status!" );
679 if( !m_aContainer
.empty() )
681 PasswordMap::iterator aIter
= m_aContainer
.find( Url
);
683 if( aIter
!= m_aContainer
.end() )
685 UpdateVector( aIter
->first
, aIter
->second
, aRecord
, true );
690 std::vector
< NamePasswordRecord
> listToAdd( 1, aRecord
);
691 m_aContainer
.insert( PairUrlRecord( Url
, listToAdd
) );
693 if( Mode
== PERSISTENT_RECORD
&& m_xStorageFile
&& m_xStorageFile
->useStorage() )
694 m_xStorageFile
->update( Url
, aRecord
);
699 UrlRecord SAL_CALL
PasswordContainer::find( const OUString
& aURL
, const Reference
< XInteractionHandler
>& aHandler
)
701 return find( aURL
, u
"", false, aHandler
);
705 UrlRecord SAL_CALL
PasswordContainer::findForName( const OUString
& aURL
, const OUString
& aName
, const Reference
< XInteractionHandler
>& aHandler
)
707 return find( aURL
, aName
, true, aHandler
);
711 Sequence
< UserRecord
> PasswordContainer::FindUsr( const std::vector
< NamePasswordRecord
>& userlist
, std::u16string_view aName
, const Reference
< XInteractionHandler
>& aHandler
)
713 for (auto const& aNPIter
: userlist
)
715 if( aNPIter
.GetUserName() == aName
)
717 bool bTryToDecode
= true;
718 Sequence
< UserRecord
> aResult
{ CopyToUserRecord( aNPIter
, bTryToDecode
, aHandler
) };
724 return Sequence
< UserRecord
>();
728 bool PasswordContainer::createUrlRecord(
729 const PasswordMap::iterator
& rIter
,
731 std::u16string_view aName
,
732 const Reference
< XInteractionHandler
>& aHandler
,
737 Sequence
< UserRecord
> aUsrRec
738 = FindUsr( rIter
->second
, aName
, aHandler
);
739 if( aUsrRec
.hasElements() )
741 rRec
= UrlRecord( rIter
->first
, aUsrRec
);
749 CopyToUserRecordSequence( rIter
->second
, aHandler
) );
756 UrlRecord
PasswordContainer::find(
757 const OUString
& aURL
,
758 std::u16string_view aName
,
759 bool bName
, // only needed to support empty user names
760 const Reference
< XInteractionHandler
>& aHandler
)
762 ::osl::MutexGuard
aGuard( mMutex
);
764 if( !m_aContainer
.empty() && !aURL
.isEmpty() )
766 OUString
aUrl( aURL
);
768 // each iteration remove last '/...' section from the aUrl
769 // while it's possible, up to the most left '://'
772 // first look for <url>/somename and then look for <url>/somename/...
773 PasswordMap::iterator aIter
= m_aContainer
.find( aUrl
);
774 if( aIter
!= m_aContainer
.end() )
777 if ( createUrlRecord( aIter
, bName
, aName
, aHandler
, aRec
) )
782 OUString
tmpUrl( aUrl
);
783 if ( !tmpUrl
.endsWith("/") )
786 aIter
= m_aContainer
.lower_bound( tmpUrl
);
787 if( aIter
!= m_aContainer
.end() && aIter
->first
.match( tmpUrl
) )
790 if ( createUrlRecord( aIter
, bName
, aName
, aHandler
, aRec
) )
795 while( shorterUrl( aUrl
) && !aUrl
.isEmpty() );
801 OUString
PasswordContainer::GetDefaultMasterPassword()
803 OUStringBuffer aResult
;
804 for ( sal_Int32 nInd
= 0; nInd
< RTL_DIGEST_LENGTH_MD5
; nInd
++ )
805 aResult
.append("aa");
807 return aResult
.makeStringAndClear();
810 OUString
PasswordContainer::RequestPasswordFromUser( PasswordRequestMode aRMode
, const uno::Reference
< task::XInteractionHandler
>& xHandler
)
812 // empty string means that the call was cancelled or just failed
817 ::rtl::Reference
< MasterPasswordRequest_Impl
> xRequest
= new MasterPasswordRequest_Impl( aRMode
);
819 xHandler
->handle( xRequest
);
821 ::rtl::Reference
< ucbhelper::InteractionContinuation
> xSelection
= xRequest
->getSelection();
823 if ( xSelection
.is() )
825 Reference
< XInteractionAbort
> xAbort( xSelection
.get(), UNO_QUERY
);
828 const ::rtl::Reference
< ucbhelper::InteractionSupplyAuthentication
> & xSupp
829 = xRequest
->getAuthenticationSupplier();
831 aResult
= xSupp
->getPassword();
839 // Mangle the key to match an old bug
840 static OUString
ReencodeAsOldHash(const OUString
& rPass
)
842 OUStringBuffer aBuffer
;
843 for (int ind
= 0; ind
< RTL_DIGEST_LENGTH_MD5
; ++ind
)
845 unsigned char i
= static_cast<char>(rPass
.copy(ind
* 2, 2).toUInt32(16));
846 aBuffer
.append(static_cast< sal_Unicode
>('a' + (i
>> 4)));
847 aBuffer
.append(static_cast< sal_Unicode
>('a' + (i
& 15)));
849 return aBuffer
.makeStringAndClear();
852 OUString
const & PasswordContainer::GetMasterPassword( const Reference
< XInteractionHandler
>& aHandler
)
854 PasswordRequestMode aRMode
= PasswordRequestMode_PASSWORD_ENTER
;
855 if( !m_xStorageFile
|| !m_xStorageFile
->useStorage() )
856 throw NoMasterException("Password storing is not active!", Reference
< XInterface
>(), aRMode
);
858 if( m_aMasterPassword
.isEmpty() && aHandler
.is() )
860 OUString aEncodedMP
, aEncodedMPIV
;
861 bool bDefaultPassword
= false;
863 if( !m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) )
864 aRMode
= PasswordRequestMode_PASSWORD_CREATE
;
865 else if ( aEncodedMP
.isEmpty() )
867 m_aMasterPassword
= GetDefaultMasterPassword();
868 bDefaultPassword
= true;
871 if ( !bDefaultPassword
)
873 bool bAskAgain
= false;
877 OUString aPass
= RequestPasswordFromUser( aRMode
, aHandler
);
878 if ( !aPass
.isEmpty() )
880 if( aRMode
== PasswordRequestMode_PASSWORD_CREATE
)
882 m_aMasterPassword
= aPass
;
883 std::vector
< OUString
> aMaster( 1, m_aMasterPassword
);
885 OUString sIV
= createIV();
886 m_xStorageFile
->setEncodedMasterPassword(EncodePasswords(aMaster
, sIV
, m_aMasterPassword
), sIV
);
890 if (m_xStorageFile
->getStorageVersion() == 0)
891 aPass
= ReencodeAsOldHash(aPass
);
893 std::vector
< OUString
> aRM( DecodePasswords( aEncodedMP
, aEncodedMPIV
, aPass
, aRMode
) );
894 if( aRM
.empty() || aPass
!= aRM
[0] )
897 aRMode
= PasswordRequestMode_PASSWORD_REENTER
;
900 m_aMasterPassword
= aPass
;
904 } while( bAskAgain
);
908 if ( m_aMasterPassword
.isEmpty() )
909 throw NoMasterException("No master password!", Reference
< XInterface
>(), aRMode
);
911 return m_aMasterPassword
;
915 void SAL_CALL
PasswordContainer::remove( const OUString
& aURL
, const OUString
& aName
)
917 ::osl::MutexGuard
aGuard( mMutex
);
919 OUString
aUrl( aURL
);
920 if( m_aContainer
.empty() )
923 PasswordMap::iterator aIter
= m_aContainer
.find( aUrl
);
925 if( aIter
== m_aContainer
.end() )
927 if( aUrl
.endsWith("/") )
928 aUrl
= aUrl
.copy( 0, aUrl
.getLength() - 1 );
932 aIter
= m_aContainer
.find( aUrl
);
935 if( aIter
== m_aContainer
.end() )
938 auto aNPIter
= std::find_if(aIter
->second
.begin(), aIter
->second
.end(),
939 [&aName
](const NamePasswordRecord
& rNPRecord
) { return rNPRecord
.GetUserName() == aName
; });
941 if (aNPIter
!= aIter
->second
.end())
943 if( aNPIter
->HasPasswords( PERSISTENT_RECORD
) && m_xStorageFile
)
944 m_xStorageFile
->remove( aURL
, aName
); // remove record ( aURL, aName )
946 // the iterator will not be used any more so it can be removed directly
947 aIter
->second
.erase( aNPIter
);
949 if( aIter
->second
.empty() )
950 m_aContainer
.erase( aIter
);
955 void SAL_CALL
PasswordContainer::removePersistent( const OUString
& aURL
, const OUString
& aName
)
957 ::osl::MutexGuard
aGuard( mMutex
);
959 OUString
aUrl( aURL
);
960 if( m_aContainer
.empty() )
963 PasswordMap::iterator aIter
= m_aContainer
.find( aUrl
);
965 if( aIter
== m_aContainer
.end() )
967 if( aUrl
.endsWith("/") )
968 aUrl
= aUrl
.copy( 0, aUrl
.getLength() - 1 );
972 aIter
= m_aContainer
.find( aUrl
);
975 if( aIter
== m_aContainer
.end() )
978 auto aNPIter
= std::find_if(aIter
->second
.begin(), aIter
->second
.end(),
979 [&aName
](const NamePasswordRecord
& rNPRecord
) { return rNPRecord
.GetUserName() == aName
; });
981 if (aNPIter
== aIter
->second
.end())
984 if( aNPIter
->HasPasswords( PERSISTENT_RECORD
) )
986 // TODO/LATER: should the password be converted to MemoryPassword?
987 aNPIter
->RemovePasswords( PERSISTENT_RECORD
);
989 if ( m_xStorageFile
)
990 m_xStorageFile
->remove( aURL
, aName
); // remove record ( aURL, aName )
993 if( !aNPIter
->HasPasswords( MEMORY_RECORD
) )
994 aIter
->second
.erase( aNPIter
);
996 if( aIter
->second
.empty() )
997 m_aContainer
.erase( aIter
);
1000 void SAL_CALL
PasswordContainer::removeAllPersistent()
1002 ::osl::MutexGuard
aGuard( mMutex
);
1004 if( m_xStorageFile
)
1005 m_xStorageFile
->clear();
1007 for( PasswordMap::iterator aIter
= m_aContainer
.begin(); aIter
!= m_aContainer
.end(); )
1009 for( std::vector
< NamePasswordRecord
>::iterator aNPIter
= aIter
->second
.begin(); aNPIter
!= aIter
->second
.end(); )
1011 if( aNPIter
->HasPasswords( PERSISTENT_RECORD
) )
1013 // TODO/LATER: should the password be converted to MemoryPassword?
1014 aNPIter
->RemovePasswords( PERSISTENT_RECORD
);
1016 if ( m_xStorageFile
)
1017 m_xStorageFile
->remove( aIter
->first
, aNPIter
->GetUserName() ); // remove record ( aURL, aName )
1020 if( !aNPIter
->HasPasswords( MEMORY_RECORD
) )
1022 aNPIter
= aIter
->second
.erase(aNPIter
);
1028 if( aIter
->second
.empty() )
1030 aIter
= m_aContainer
.erase(aIter
);
1037 Sequence
< UrlRecord
> SAL_CALL
PasswordContainer::getAllPersistent( const Reference
< XInteractionHandler
>& xHandler
)
1039 Sequence
< UrlRecord
> aResult
;
1041 ::osl::MutexGuard
aGuard( mMutex
);
1042 for( const auto& rEntry
: m_aContainer
)
1044 Sequence
< UserRecord
> aUsers
;
1045 for (auto const& aNP
: rEntry
.second
)
1046 if( aNP
.HasPasswords( PERSISTENT_RECORD
) )
1048 sal_Int32 oldLen
= aUsers
.getLength();
1049 aUsers
.realloc( oldLen
+ 1 );
1050 aUsers
.getArray()[ oldLen
] = UserRecord( aNP
.GetUserName(), comphelper::containerToSequence( DecodePasswords( aNP
.GetPersistentPasswords(), aNP
.GetPersistentIV(),
1051 GetMasterPassword( xHandler
), css::task::PasswordRequestMode_PASSWORD_ENTER
) ) );
1054 if( aUsers
.hasElements() )
1056 sal_Int32 oldLen
= aResult
.getLength();
1057 aResult
.realloc( oldLen
+ 1 );
1058 aResult
.getArray()[ oldLen
] = UrlRecord( rEntry
.first
, aUsers
);
1065 sal_Bool SAL_CALL
PasswordContainer::authorizateWithMasterPassword( const uno::Reference
< task::XInteractionHandler
>& xHandler
)
1067 bool bResult
= false;
1068 OUString aEncodedMP
, aEncodedMPIV
;
1069 uno::Reference
< task::XInteractionHandler
> xTmpHandler
= xHandler
;
1070 ::osl::MutexGuard
aGuard( mMutex
);
1072 // the method should fail if there is no master password
1073 if( m_xStorageFile
&& m_xStorageFile
->useStorage() && m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) )
1075 if ( aEncodedMP
.isEmpty() )
1077 // this is a default master password
1078 // no UI is necessary
1083 if ( !xTmpHandler
.is() )
1085 uno::Reference
< lang::XMultiServiceFactory
> xFactory( mComponent
, uno::UNO_QUERY_THROW
);
1086 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getComponentContext(xFactory
) );
1087 xTmpHandler
.set( InteractionHandler::createWithParent(xContext
, nullptr), uno::UNO_QUERY_THROW
);
1090 if ( !m_aMasterPassword
.isEmpty() )
1092 // there is a password, it should be just rechecked
1093 PasswordRequestMode aRMode
= PasswordRequestMode_PASSWORD_ENTER
;
1097 aPass
= RequestPasswordFromUser( aRMode
, xTmpHandler
);
1099 if (!aPass
.isEmpty() && m_xStorageFile
->getStorageVersion() == 0)
1101 aPass
= ReencodeAsOldHash(aPass
);
1104 bResult
= ( !aPass
.isEmpty() && aPass
== m_aMasterPassword
);
1105 aRMode
= PasswordRequestMode_PASSWORD_REENTER
; // further questions with error notification
1106 } while( !bResult
&& !aPass
.isEmpty() );
1112 // ask for the password, if user provide no correct password an exception will be thrown
1113 bResult
= !GetMasterPassword( xTmpHandler
).isEmpty();
1115 catch( uno::Exception
& )
1124 sal_Bool SAL_CALL
PasswordContainer::changeMasterPassword( const uno::Reference
< task::XInteractionHandler
>& xHandler
)
1126 bool bResult
= false;
1127 uno::Reference
< task::XInteractionHandler
> xTmpHandler
= xHandler
;
1128 ::osl::MutexGuard
aGuard( mMutex
);
1130 if ( m_xStorageFile
&& m_xStorageFile
->useStorage() )
1132 if ( !xTmpHandler
.is() )
1134 uno::Reference
< lang::XMultiServiceFactory
> xFactory( mComponent
, uno::UNO_QUERY_THROW
);
1135 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getComponentContext(xFactory
) );
1136 xTmpHandler
.set( InteractionHandler::createWithParent(xContext
, nullptr), uno::UNO_QUERY_THROW
);
1139 bool bCanChangePassword
= true;
1140 // if there is already a stored master password it should be entered by the user before the change happen
1141 OUString aEncodedMP
, aEncodedMPIV
;
1142 if( !m_aMasterPassword
.isEmpty() || m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) )
1143 bCanChangePassword
= authorizateWithMasterPassword( xTmpHandler
);
1145 if ( bCanChangePassword
)
1147 // ask for the new password, but do not set it
1148 OUString aPass
= RequestPasswordFromUser( PasswordRequestMode_PASSWORD_CREATE
, xTmpHandler
);
1150 if ( !aPass
.isEmpty() )
1152 // get all the persistent entries if it is possible
1153 const Sequence
< UrlRecord
> aPersistent
= getAllPersistent( uno::Reference
< task::XInteractionHandler
>() );
1155 // remove the master password and the entries persistence
1156 removeMasterPassword();
1158 // store the new master password
1159 m_aMasterPassword
= aPass
;
1160 std::vector
< OUString
> aMaster( 1, m_aMasterPassword
);
1161 OUString aIV
= createIV();
1162 m_xStorageFile
->setEncodedMasterPassword(EncodePasswords(aMaster
, aIV
, m_aMasterPassword
), aIV
);
1164 // store all the entries with the new password
1165 for ( const auto& rURL
: aPersistent
)
1166 for ( const auto& rUser
: rURL
.UserList
)
1167 addPersistent( rURL
.Url
, rUser
.UserName
, rUser
.Passwords
,
1168 uno::Reference
< task::XInteractionHandler
>() );
1178 void SAL_CALL
PasswordContainer::removeMasterPassword()
1180 // remove all the stored passwords and the master password
1181 removeAllPersistent();
1183 ::osl::MutexGuard
aGuard( mMutex
);
1184 if ( m_xStorageFile
)
1186 m_aMasterPassword
.clear();
1187 m_xStorageFile
->setEncodedMasterPassword( OUString(), OUString() ); // let the master password be removed from configuration
1191 sal_Bool SAL_CALL
PasswordContainer::hasMasterPassword( )
1193 ::osl::MutexGuard
aGuard( mMutex
);
1195 if ( !m_xStorageFile
)
1196 throw uno::RuntimeException();
1198 OUString aEncodedMP
, aEncodedMPIV
;
1199 return ( m_xStorageFile
->useStorage() && m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) );
1202 sal_Bool SAL_CALL
PasswordContainer::allowPersistentStoring( sal_Bool bAllow
)
1204 ::osl::MutexGuard
aGuard( mMutex
);
1206 if ( !m_xStorageFile
)
1207 throw uno::RuntimeException();
1210 removeMasterPassword();
1212 if (m_xStorageFile
->useStorage() == static_cast<bool>(bAllow
))
1215 m_xStorageFile
->setUseStorage( bAllow
);
1219 sal_Bool SAL_CALL
PasswordContainer::isPersistentStoringAllowed()
1221 ::osl::MutexGuard
aGuard( mMutex
);
1223 if ( !m_xStorageFile
)
1224 throw uno::RuntimeException();
1226 return m_xStorageFile
->useStorage();
1229 sal_Bool SAL_CALL
PasswordContainer::useDefaultMasterPassword( const uno::Reference
< task::XInteractionHandler
>& xHandler
)
1231 bool bResult
= false;
1232 uno::Reference
< task::XInteractionHandler
> xTmpHandler
= xHandler
;
1233 ::osl::MutexGuard
aGuard( mMutex
);
1235 if ( m_xStorageFile
&& m_xStorageFile
->useStorage() )
1237 if ( !xTmpHandler
.is() )
1239 uno::Reference
< lang::XMultiServiceFactory
> xFactory( mComponent
, uno::UNO_QUERY_THROW
);
1240 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getComponentContext(xFactory
) );
1241 xTmpHandler
.set( InteractionHandler::createWithParent(xContext
, nullptr), uno::UNO_QUERY_THROW
);
1244 bool bCanChangePassword
= true;
1245 // if there is already a stored nondefault master password it should be entered by the user before the change happen
1246 OUString aEncodedMP
, aEncodedMPIV
;
1247 if( m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) && !aEncodedMP
.isEmpty() )
1248 bCanChangePassword
= authorizateWithMasterPassword( xTmpHandler
);
1250 if ( bCanChangePassword
)
1252 // generate the default password
1253 OUString aPass
= GetDefaultMasterPassword();
1254 if ( !aPass
.isEmpty() )
1256 // get all the persistent entries if it is possible
1257 const Sequence
< UrlRecord
> aPersistent
= getAllPersistent( uno::Reference
< task::XInteractionHandler
>() );
1259 // remove the master password and the entries persistence
1260 removeMasterPassword();
1262 // store the empty string to flag the default master password
1263 m_aMasterPassword
= aPass
;
1264 m_xStorageFile
->setEncodedMasterPassword( OUString(), OUString(), true );
1266 // store all the entries with the new password
1267 for ( const auto& rURL
: aPersistent
)
1268 for ( const auto& rUser
: rURL
.UserList
)
1269 addPersistent( rURL
.Url
, rUser
.UserName
, rUser
.Passwords
,
1270 uno::Reference
< task::XInteractionHandler
>() );
1281 sal_Bool SAL_CALL
PasswordContainer::isDefaultMasterPasswordUsed()
1283 ::osl::MutexGuard
aGuard( mMutex
);
1285 if ( !m_xStorageFile
)
1286 throw uno::RuntimeException();
1288 OUString aEncodedMP
, aEncodedMPIV
;
1289 return ( m_xStorageFile
->useStorage() && m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) && aEncodedMP
.isEmpty() );
1293 void SAL_CALL
PasswordContainer::addUrl( const OUString
& Url
, sal_Bool MakePersistent
)
1295 mUrlContainer
.add( Url
, MakePersistent
);
1298 OUString SAL_CALL
PasswordContainer::findUrl( const OUString
& Url
)
1300 return mUrlContainer
.find( Url
);
1303 void SAL_CALL
PasswordContainer::removeUrl( const OUString
& Url
)
1305 mUrlContainer
.remove( Url
);
1308 uno::Sequence
< OUString
> SAL_CALL
PasswordContainer::getUrls( sal_Bool OnlyPersistent
)
1310 return mUrlContainer
.list( OnlyPersistent
);
1314 void PasswordContainer::Notify()
1316 ::osl::MutexGuard
aGuard( mMutex
);
1318 // remove the cached persistent values in the memory
1319 for( auto& rEntry
: m_aContainer
)
1321 for( std::vector
< NamePasswordRecord
>::iterator aNPIter
= rEntry
.second
.begin(); aNPIter
!= rEntry
.second
.end(); )
1323 if( aNPIter
->HasPasswords( PERSISTENT_RECORD
) )
1325 aNPIter
->RemovePasswords( PERSISTENT_RECORD
);
1327 if ( m_xStorageFile
)
1328 m_xStorageFile
->remove( rEntry
.first
, aNPIter
->GetUserName() ); // remove record ( aURL, aName )
1331 if( !aNPIter
->HasPasswords( MEMORY_RECORD
) )
1333 aNPIter
= rEntry
.second
.erase(aNPIter
);
1341 if( m_xStorageFile
)
1342 addon
= m_xStorageFile
->getInfo();
1344 for( const auto& rEntry
: addon
)
1346 PasswordMap::iterator aSearchIter
= m_aContainer
.find( rEntry
.first
);
1347 if( aSearchIter
!= m_aContainer
.end() )
1348 for (auto const& aNP
: rEntry
.second
)
1349 UpdateVector( aSearchIter
->first
, aSearchIter
->second
, aNP
, false );
1351 m_aContainer
.insert( PairUrlRecord( rEntry
.first
, rEntry
.second
) );
1355 OUString SAL_CALL
PasswordContainer::getImplementationName( )
1357 return "stardiv.svl.PasswordContainer";
1360 sal_Bool SAL_CALL
PasswordContainer::supportsService( const OUString
& ServiceName
)
1362 return cppu::supportsService( this, ServiceName
);
1365 Sequence
< OUString
> SAL_CALL
PasswordContainer::getSupportedServiceNames( )
1367 return { "com.sun.star.task.PasswordContainer" };
1370 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
1371 svl_PasswordContainer_get_implementation(
1372 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const&)
1374 return cppu::acquire(new PasswordContainer(context
));
1378 MasterPasswordRequest_Impl::MasterPasswordRequest_Impl( PasswordRequestMode Mode
)
1380 MasterPasswordRequest aRequest
;
1382 aRequest
.Classification
= InteractionClassification_ERROR
;
1383 aRequest
.Mode
= Mode
;
1385 setRequest( makeAny( aRequest
) );
1387 // Fill continuations...
1388 Sequence
< RememberAuthentication
> aRememberModes
{ RememberAuthentication_NO
};
1391 = new ::ucbhelper::InteractionSupplyAuthentication(
1393 false, // bCanSetRealm
1394 false, // bCanSetUserName
1395 true, // bCanSetPassword
1396 false, // bCanSetAccount
1397 aRememberModes
, // rRememberPasswordModes
1398 RememberAuthentication_NO
, // eDefaultRememberPasswordMode
1399 aRememberModes
, // rRememberAccountModes
1400 RememberAuthentication_NO
, // eDefaultRememberAccountMode
1401 false // bCanUseSystemCredentials
1405 Reference
< XInteractionContinuation
> > aContinuations
{
1406 new ::ucbhelper::InteractionAbort( this ),
1407 new ::ucbhelper::InteractionRetry( this ),
1411 setContinuations( aContinuations
);
1416 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */