diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /svl/source/passwordcontainer | |
parent | Initial commit. (diff) | |
download | libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'svl/source/passwordcontainer')
-rw-r--r-- | svl/source/passwordcontainer/passwordcontainer.component | 26 | ||||
-rw-r--r-- | svl/source/passwordcontainer/passwordcontainer.cxx | 1400 | ||||
-rw-r--r-- | svl/source/passwordcontainer/passwordcontainer.hxx | 370 | ||||
-rw-r--r-- | svl/source/passwordcontainer/syscreds.cxx | 268 | ||||
-rw-r--r-- | svl/source/passwordcontainer/syscreds.hxx | 82 |
5 files changed, 2146 insertions, 0 deletions
diff --git a/svl/source/passwordcontainer/passwordcontainer.component b/svl/source/passwordcontainer/passwordcontainer.component new file mode 100644 index 000000000..165efbe7d --- /dev/null +++ b/svl/source/passwordcontainer/passwordcontainer.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="passwordcontainer" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="stardiv.svl.PasswordContainer"> + <service name="com.sun.star.task.PasswordContainer"/> + </implementation> +</component> diff --git a/svl/source/passwordcontainer/passwordcontainer.cxx b/svl/source/passwordcontainer/passwordcontainer.cxx new file mode 100644 index 000000000..8bdc1adb7 --- /dev/null +++ b/svl/source/passwordcontainer/passwordcontainer.cxx @@ -0,0 +1,1400 @@ +/* -*- 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 "passwordcontainer.hxx" + +#include <cppuhelper/factory.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.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 <com/sun/star/lang/XSingleServiceFactory.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 osl; +using namespace utl; +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::registry; +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("_").append( OUString::number( *pLine, 16 ) ); + } + + pLine++; + } + } + + return aResult.makeStringAndClear(); +} + + +static std::vector< OUString > getInfoFromInd( const OUString& 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( const OUString& line ) +{ + OSL_ENSURE( line.getLength() % 2 == 0, "Wrong syntax!" ); + OString tmpLine = OUStringToOString( line, RTL_TEXTENCODING_ASCII_US ); + ::rtl::ByteSequence aResult(line.getLength()/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>( tmpLine[ind*2+1] - 'a' ); + } + + return aResult; +} + + +PassMap StorageItem::getInfo() +{ + PassMap aResult; + + Sequence< OUString > aNodeNames = ConfigItem::GetNodeNames( "Store" ); + sal_Int32 aNodeCount = aNodeNames.getLength(); + Sequence< OUString > aPropNames( aNodeCount ); + + std::transform(aNodeNames.begin(), aNodeNames.end(), aPropNames.begin(), + [](const OUString& rName) -> OUString { + return "Store/Passwordstorage['" + rName + "']/Password"; }); + + Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aPropNames ); + + if( aPropertyValues.getLength() != aNodeCount ) + { + 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; + aPropertyValues[aNodeInd] >>= aEPasswd; + + PassMap::iterator aIter = aResult.find( aUrl ); + if( aIter != aResult.end() ) + aIter->second.emplace_back( aName, aEPasswd ); + else + { + NamePassRecord aNewRecord( aName, aEPasswd ); + std::vector< NamePassRecord > listToAdd( 1, aNewRecord ); + + aResult.insert( PairUrlRecord( aUrl, listToAdd ) ); + } + } + else + OSL_FAIL( "Wrong index syntax!" ); + } + + return aResult; +} + + +void StorageItem::setUseStorage( bool bUse ) +{ + Sequence< OUString > sendNames(1); + Sequence< uno::Any > sendVals(1); + + sendNames[0] = "UseStorage"; + + sendVals[0] <<= bUse; + + ConfigItem::SetModified(); + ConfigItem::PutProperties( sendNames, sendVals ); +} + + +bool StorageItem::useStorage() +{ + Sequence<OUString> aNodeNames { "UseStorage" }; + + 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; +} + + +bool StorageItem::getEncodedMP( OUString& aResult ) +{ + if( hasEncoded ) + { + aResult = mEncoded; + return true; + } + + Sequence< OUString > aNodeNames( 2 ); + aNodeNames[0] = "HasMaster"; + aNodeNames[1] = "Master"; + + Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aNodeNames ); + + if( aPropertyValues.getLength() != aNodeNames.getLength() ) + { + OSL_FAIL( "Problems during reading" ); + return false; + } + + aPropertyValues[0] >>= hasEncoded; + aPropertyValues[1] >>= mEncoded; + + aResult = mEncoded; + + return hasEncoded; +} + + +void StorageItem::setEncodedMP( const OUString& aEncoded, bool bAcceptEmpty ) +{ + Sequence< OUString > sendNames(2); + Sequence< uno::Any > sendVals(2); + + sendNames[0] = "HasMaster"; + sendNames[1] = "Master"; + + bool bHasMaster = ( !aEncoded.isEmpty() || bAcceptEmpty ); + sendVals[0] <<= bHasMaster; + sendVals[1] <<= aEncoded; + + ConfigItem::SetModified(); + ConfigItem::PutProperties( sendNames, sendVals ); + + hasEncoded = bHasMaster; + mEncoded = aEncoded; +} + + +void StorageItem::remove( const OUString& aURL, const OUString& aName ) +{ + std::vector < OUString > forIndex; + forIndex.push_back( aURL ); + forIndex.push_back( aName ); + + Sequence< OUString > sendSeq(1); + + sendSeq[0] = createIndex( forIndex ); + + ConfigItem::ClearNodeElements( "Store", sendSeq ); +} + + +void StorageItem::clear() +{ + ConfigItem::ClearNodeSet( "Store" ); +} + + +void StorageItem::update( const OUString& aURL, const NamePassRecord& aRecord ) +{ + if ( !aRecord.HasPasswords( PERSISTENT_RECORD ) ) + { + OSL_FAIL( "Unexpected storing of a record!" ); + return; + } + + std::vector < OUString > forIndex; + forIndex.push_back( aURL ); + forIndex.push_back( aRecord.GetUserName() ); + + Sequence< beans::PropertyValue > sendSeq(1); + + sendSeq[0].Name = "Store/Passwordstorage['" + createIndex( forIndex ) + "']/Password"; + + sendSeq[0].Value <<= aRecord.GetPersPasswords(); + + ConfigItem::SetModified(); + ConfigItem::SetSetProperties( "Store", 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<XMultiServiceFactory>& xServiceFactory ) +{ + // m_pStorageFile->Notify() can be called + ::osl::MutexGuard aGuard( mMutex ); + + mComponent.set( xServiceFactory, UNO_QUERY ); + mComponent->addEventListener( this ); + + m_pStorageFile.reset( new StorageItem( this, "Office.Common/Passwords" ) ); + if( m_pStorageFile->useStorage() ) + m_aContainer = m_pStorageFile->getInfo(); +} + + +PasswordContainer::~PasswordContainer() +{ + ::osl::MutexGuard aGuard( mMutex ); + + m_pStorageFile.reset(); + + if( mComponent.is() ) + { + mComponent->removeEventListener(this); + mComponent.clear(); + } +} + +void SAL_CALL PasswordContainer::disposing( const EventObject& ) +{ + ::osl::MutexGuard aGuard( mMutex ); + + m_pStorageFile.reset(); + + if( mComponent.is() ) + { + //mComponent->removeEventListener(this); + mComponent.clear(); + } +} + +std::vector< OUString > PasswordContainer::DecodePasswords( const OUString& aLine, const OUString& aMasterPasswd, css::task::PasswordRequestMode mode ) +{ + if( !aMasterPasswd.isEmpty() ) + { + rtlCipher aDecoder = rtl_cipher_create (rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeStream ); + OSL_ENSURE( aDecoder, "Can't create decoder" ); + + if( aDecoder ) + { + OSL_ENSURE( aMasterPasswd.getLength() == 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>(aMasterPasswd.copy( ind*2, 2 ).toUInt32(16)); + + rtlCipherError result = rtl_cipher_init ( + aDecoder, rtl_Cipher_DirectionDecode, + code, RTL_DIGEST_LENGTH_MD5, nullptr, 0 ); + + 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( + "Can't decode!", css::uno::Reference<css::uno::XInterface>(), mode); +} + +OUString PasswordContainer::EncodePasswords(const std::vector< OUString >& lines, const OUString& aMasterPasswd ) +{ + if( !aMasterPasswd.isEmpty() ) + { + 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.getLength() == 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>(aMasterPasswd.copy( ind*2, 2 ).toUInt32(16)); + + rtlCipherError result = rtl_cipher_init ( + aEncoder, rtl_Cipher_DirectionEncode, + code, RTL_DIGEST_LENGTH_MD5, nullptr, 0 ); + + 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("Can't encode!" ); +} + +void PasswordContainer::UpdateVector( const OUString& aURL, std::vector< NamePassRecord >& toUpdate, NamePassRecord const & aRecord, bool writeFile ) +{ + for (auto & aNPIter : toUpdate) + if( aNPIter.GetUserName() == aRecord.GetUserName() ) + { + if( aRecord.HasPasswords( MEMORY_RECORD ) ) + aNPIter.SetMemPasswords( aRecord.GetMemPasswords() ); + + if( aRecord.HasPasswords( PERSISTENT_RECORD ) ) + { + aNPIter.SetPersPasswords( aRecord.GetPersPasswords() ); + + if( writeFile ) + { + // the password must be already encoded + m_pStorageFile->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_pStorageFile->update( aURL, aRecord ); // add new aName to the existing url + } + + toUpdate.insert( toUpdate.begin(), aRecord ); +} + + +UserRecord PasswordContainer::CopyToUserRecord( const NamePassRecord& aRecord, bool& io_bTryToDecode, const Reference< XInteractionHandler >& aHandler ) +{ + ::std::vector< OUString > aPasswords; + if( aRecord.HasPasswords( MEMORY_RECORD ) ) + aPasswords = aRecord.GetMemPasswords(); + + if( io_bTryToDecode && aRecord.HasPasswords( PERSISTENT_RECORD ) ) + { + try + { + ::std::vector< OUString > aDecodedPasswords = DecodePasswords( aRecord.GetPersPasswords(), 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< NamePassRecord >& original, const Reference< XInteractionHandler >& aHandler ) +{ + Sequence< UserRecord > aResult( original.size() ); + sal_uInt32 nInd = 0; + bool bTryToDecode = true; + + for (auto const& aNPIter : original) + { + aResult[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 ) +{ + ::osl::MutexGuard 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 ) +{ + ::osl::MutexGuard aGuard( mMutex ); + + PrivateAdd( Url, UserName, Passwords, PERSISTENT_RECORD, aHandler ); +} + + +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 ) ) ); + else if( Mode == MEMORY_RECORD ) + aRecord.SetMemPasswords( aStorePass ); + else + { + OSL_FAIL( "Unexpected persistence status!" ); + return; + } + + if( !m_aContainer.empty() ) + { + PassMap::iterator aIter = m_aContainer.find( Url ); + + if( aIter != m_aContainer.end() ) + { + UpdateVector( aIter->first, aIter->second, aRecord, true ); + return; + } + } + + std::vector< NamePassRecord > listToAdd( 1, aRecord ); + m_aContainer.insert( PairUrlRecord( Url, listToAdd ) ); + + if( Mode == PERSISTENT_RECORD && m_pStorageFile && m_pStorageFile->useStorage() ) + m_pStorageFile->update( Url, aRecord ); + +} + + +UrlRecord SAL_CALL PasswordContainer::find( const OUString& aURL, const Reference< XInteractionHandler >& aHandler ) +{ + return find( aURL, OUString(), 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< NamePassRecord >& userlist, const OUString& aName, const Reference< XInteractionHandler >& aHandler ) +{ + sal_uInt32 nInd = 0; + for (auto const& aNPIter : userlist) + { + if( aNPIter.GetUserName() == aName ) + { + Sequence< UserRecord > aResult(1); + bool bTryToDecode = true; + aResult[0] = CopyToUserRecord( aNPIter, bTryToDecode, aHandler ); + + return aResult; + } + ++nInd; + } + + return Sequence< UserRecord >(); +} + + +bool PasswordContainer::createUrlRecord( + const PassMap::iterator & rIter, + bool bName, + const OUString & 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, + const OUString& aName, + bool bName, // only needed to support empty user names + const Reference< XInteractionHandler >& aHandler ) +{ + ::osl::MutexGuard 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/... + PassMap::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.get() ); + + ::rtl::Reference< ucbhelper::InteractionContinuation > xSelection = xRequest->getSelection(); + + if ( xSelection.is() ) + { + Reference< XInteractionAbort > xAbort( xSelection.get(), UNO_QUERY ); + if ( !xAbort.is() ) + { + const ::rtl::Reference< ucbhelper::InteractionSupplyAuthentication > & xSupp + = xRequest->getAuthenticationSupplier(); + + aResult = xSupp->getPassword(); + } + } + } + + return aResult; +} + + +OUString const & PasswordContainer::GetMasterPassword( const Reference< XInteractionHandler >& aHandler ) +{ + PasswordRequestMode aRMode = PasswordRequestMode_PASSWORD_ENTER; + if( !m_pStorageFile || !m_pStorageFile->useStorage() ) + throw NoMasterException("Password storing is not active!", Reference< XInterface >(), aRMode ); + + if( m_aMasterPasswd.isEmpty() && aHandler.is() ) + { + OUString aEncodedMP; + bool bDefaultPassword = false; + + if( !m_pStorageFile->getEncodedMP( aEncodedMP ) ) + aRMode = PasswordRequestMode_PASSWORD_CREATE; + else if ( aEncodedMP.isEmpty() ) + { + m_aMasterPasswd = 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_aMasterPasswd = aPass; + std::vector< OUString > aMaster( 1, m_aMasterPasswd ); + + m_pStorageFile->setEncodedMP( EncodePasswords( aMaster, m_aMasterPasswd ) ); + } + else + { + std::vector< OUString > aRM( DecodePasswords( aEncodedMP, aPass, aRMode ) ); + if( aRM.empty() || aPass != aRM[0] ) + { + bAskAgain = true; + aRMode = PasswordRequestMode_PASSWORD_REENTER; + } + else + m_aMasterPasswd = aPass; + } + } + + } while( bAskAgain ); + } + } + + if ( m_aMasterPasswd.isEmpty() ) + throw NoMasterException("No master password!", Reference< XInterface >(), aRMode ); + + return m_aMasterPasswd; +} + + +void SAL_CALL PasswordContainer::remove( const OUString& aURL, const OUString& aName ) +{ + ::osl::MutexGuard aGuard( mMutex ); + + OUString aUrl( aURL ); + if( m_aContainer.empty() ) + return; + + PassMap::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 NamePassRecord& rNPRecord) { return rNPRecord.GetUserName() == aName; }); + + if (aNPIter != aIter->second.end()) + { + if( aNPIter->HasPasswords( PERSISTENT_RECORD ) && m_pStorageFile ) + m_pStorageFile->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 ) +{ + ::osl::MutexGuard aGuard( mMutex ); + + OUString aUrl( aURL ); + if( m_aContainer.empty() ) + return; + + PassMap::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 NamePassRecord& rNPRecord) { return rNPRecord.GetUserName() == aName; }); + + if (aNPIter != aIter->second.end()) + { + if( aNPIter->HasPasswords( PERSISTENT_RECORD ) ) + { + // TODO/LATER: should the password be converted to MemoryPassword? + aNPIter->RemovePasswords( PERSISTENT_RECORD ); + + if ( m_pStorageFile ) + m_pStorageFile->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() +{ + ::osl::MutexGuard aGuard( mMutex ); + + if( m_pStorageFile ) + m_pStorageFile->clear(); + + for( PassMap::iterator aIter = m_aContainer.begin(); aIter != m_aContainer.end(); ) + { + for( std::vector< NamePassRecord >::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_pStorageFile ) + m_pStorageFile->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 ) +{ + Sequence< UrlRecord > aResult; + + ::osl::MutexGuard aGuard( mMutex ); + 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[ oldLen ] = UserRecord( aNP.GetUserName(), comphelper::containerToSequence( DecodePasswords( aNP.GetPersPasswords(), GetMasterPassword( xHandler ), css::task::PasswordRequestMode_PASSWORD_ENTER ) ) ); + } + + if( aUsers.hasElements() ) + { + sal_Int32 oldLen = aResult.getLength(); + aResult.realloc( oldLen + 1 ); + aResult[ oldLen ] = UrlRecord( rEntry.first, aUsers ); + } + } + + return aResult; +} + +sal_Bool SAL_CALL PasswordContainer::authorizateWithMasterPassword( const uno::Reference< task::XInteractionHandler >& xHandler ) +{ + bool bResult = false; + OUString aEncodedMP; + 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 ( 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_aMasterPasswd.isEmpty() ) + { + // there is a password, it should be just rechecked + PasswordRequestMode aRMode = PasswordRequestMode_PASSWORD_ENTER; + OUString aPass; + + do { + aPass = RequestPasswordFromUser( aRMode, xTmpHandler ); + bResult = ( !aPass.isEmpty() && aPass == m_aMasterPasswd ); + 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; + ::osl::MutexGuard aGuard( mMutex ); + + if ( m_pStorageFile && m_pStorageFile->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; + if( !m_aMasterPasswd.isEmpty() || m_pStorageFile->getEncodedMP( aEncodedMP ) ) + bCanChangePassword = authorizateWithMasterPassword( 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( uno::Reference< task::XInteractionHandler >() ); + + // remove the master password and the entries persistence + removeMasterPassword(); + + // store the new master password + m_aMasterPasswd = aPass; + std::vector< OUString > aMaster( 1, m_aMasterPasswd ); + m_pStorageFile->setEncodedMP( EncodePasswords( aMaster, m_aMasterPasswd ) ); + + // store all the entries with the new password + for ( const auto& rURL : aPersistent ) + for ( const auto& rUser : rURL.UserList ) + addPersistent( rURL.Url, rUser.UserName, rUser.Passwords, + uno::Reference< task::XInteractionHandler >() ); + + bResult = true; + } + } + } + + return bResult; +} + +void SAL_CALL PasswordContainer::removeMasterPassword() +{ + // remove all the stored passwords and the master password + removeAllPersistent(); + + ::osl::MutexGuard aGuard( mMutex ); + if ( m_pStorageFile ) + { + m_aMasterPasswd.clear(); + m_pStorageFile->setEncodedMP( OUString() ); // let the master password be removed from configuration + } +} + +sal_Bool SAL_CALL PasswordContainer::hasMasterPassword( ) +{ + ::osl::MutexGuard aGuard( mMutex ); + + if ( !m_pStorageFile ) + throw uno::RuntimeException(); + + OUString aEncodedMP; + return ( m_pStorageFile->useStorage() && m_pStorageFile->getEncodedMP( aEncodedMP ) ); +} + +sal_Bool SAL_CALL PasswordContainer::allowPersistentStoring( sal_Bool bAllow ) +{ + ::osl::MutexGuard aGuard( mMutex ); + + if ( !m_pStorageFile ) + throw uno::RuntimeException(); + + if ( !bAllow ) + removeMasterPassword(); + + if (m_pStorageFile->useStorage() == static_cast<bool>(bAllow)) + return bAllow; + + m_pStorageFile->setUseStorage( bAllow ); + return !bAllow; +} + +sal_Bool SAL_CALL PasswordContainer::isPersistentStoringAllowed() +{ + ::osl::MutexGuard aGuard( mMutex ); + + if ( !m_pStorageFile ) + throw uno::RuntimeException(); + + return m_pStorageFile->useStorage(); +} + +sal_Bool SAL_CALL PasswordContainer::useDefaultMasterPassword( const uno::Reference< task::XInteractionHandler >& xHandler ) +{ + bool bResult = false; + uno::Reference< task::XInteractionHandler > xTmpHandler = xHandler; + ::osl::MutexGuard aGuard( mMutex ); + + if ( m_pStorageFile && m_pStorageFile->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; + if( m_pStorageFile->getEncodedMP( aEncodedMP ) && !aEncodedMP.isEmpty() ) + bCanChangePassword = authorizateWithMasterPassword( 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( uno::Reference< task::XInteractionHandler >() ); + + // remove the master password and the entries persistence + removeMasterPassword(); + + // store the empty string to flag the default master password + m_aMasterPasswd = aPass; + m_pStorageFile->setEncodedMP( OUString(), true ); + + // store all the entries with the new password + for ( const auto& rURL : aPersistent ) + for ( const auto& rUser : rURL.UserList ) + addPersistent( rURL.Url, rUser.UserName, rUser.Passwords, + uno::Reference< task::XInteractionHandler >() ); + + bResult = true; + } + } + } + + return bResult; + +} + +sal_Bool SAL_CALL PasswordContainer::isDefaultMasterPasswordUsed() +{ + ::osl::MutexGuard aGuard( mMutex ); + + if ( !m_pStorageFile ) + throw uno::RuntimeException(); + + OUString aEncodedMP; + return ( m_pStorageFile->useStorage() && m_pStorageFile->getEncodedMP( aEncodedMP ) && 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() +{ + ::osl::MutexGuard aGuard( mMutex ); + + // remove the cached persistent values in the memory + for( auto& rEntry : m_aContainer ) + { + for( std::vector< NamePassRecord >::iterator aNPIter = rEntry.second.begin(); aNPIter != rEntry.second.end(); ) + { + if( aNPIter->HasPasswords( PERSISTENT_RECORD ) ) + { + aNPIter->RemovePasswords( PERSISTENT_RECORD ); + + if ( m_pStorageFile ) + m_pStorageFile->remove( rEntry.first, aNPIter->GetUserName() ); // remove record ( aURL, aName ) + } + + if( !aNPIter->HasPasswords( MEMORY_RECORD ) ) + { + aNPIter = rEntry.second.erase(aNPIter); + } + else + ++aNPIter; + } + } + + PassMap addon; + if( m_pStorageFile ) + addon = m_pStorageFile->getInfo(); + + for( const auto& rEntry : addon ) + { + PassMap::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 impl_getStaticImplementationName(); +} + +sal_Bool SAL_CALL PasswordContainer::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL PasswordContainer::getSupportedServiceNames( ) +{ + return impl_getStaticSupportedServiceNames(); +} + +Sequence< OUString > PasswordContainer::impl_getStaticSupportedServiceNames( ) +{ + return { "com.sun.star.task.PasswordContainer" }; +} + +OUString PasswordContainer::impl_getStaticImplementationName() +{ + return "stardiv.svl.PasswordContainer"; +} + +Reference< XInterface > SAL_CALL PasswordContainer::impl_createInstance( const Reference< XMultiServiceFactory >& xServiceManager ) +{ + return Reference< XInterface >( *new PasswordContainer( xServiceManager ) ); +} + +Reference< XSingleServiceFactory > PasswordContainer::impl_createFactory( const Reference< XMultiServiceFactory >& ServiceManager ) +{ + Reference< XSingleServiceFactory > xReturn( ::cppu::createOneInstanceFactory( ServiceManager, + PasswordContainer::impl_getStaticImplementationName(), + PasswordContainer::impl_createInstance, + PasswordContainer::impl_getStaticSupportedServiceNames())); + return xReturn ; + +} + + +MasterPasswordRequest_Impl::MasterPasswordRequest_Impl( PasswordRequestMode Mode ) +{ + MasterPasswordRequest aRequest; + + aRequest.Classification = InteractionClassification_ERROR; + aRequest.Mode = Mode; + + setRequest( makeAny( aRequest ) ); + + // Fill continuations... + Sequence< RememberAuthentication > aRememberModes( 1 ); + aRememberModes[ 0 ] = 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( 3 ); + aContinuations[ 0 ] = new ::ucbhelper::InteractionAbort( this ); + aContinuations[ 1 ] = new ::ucbhelper::InteractionRetry( this ); + aContinuations[ 2 ] = m_xAuthSupplier.get(); + + setContinuations( aContinuations ); +} + + +extern "C" +{ +SAL_DLLPUBLIC_EXPORT void * passwordcontainer_component_getFactory ( + const char * pImplementationName, + SAL_UNUSED_PARAMETER void * pServiceManager, + SAL_UNUSED_PARAMETER void * /* pRegistryKey */) +{ + void * pResult = nullptr; + if (pServiceManager) + { + Reference< XSingleServiceFactory > xFactory; + if (PasswordContainer::impl_getStaticImplementationName().equalsAscii(pImplementationName)) + { + xFactory = PasswordContainer::impl_createFactory ( + static_cast< XMultiServiceFactory* >(pServiceManager)); + } + if (xFactory.is()) + { + xFactory->acquire(); + pResult = xFactory.get(); + } + } + return pResult; +} + +} // extern "C" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svl/source/passwordcontainer/passwordcontainer.hxx b/svl/source/passwordcontainer/passwordcontainer.hxx new file mode 100644 index 000000000..09fb7e036 --- /dev/null +++ b/svl/source/passwordcontainer/passwordcontainer.hxx @@ -0,0 +1,370 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_SVL_SOURCE_PASSWORDCONTAINER_PASSWORDCONTAINER_HXX +#define INCLUDED_SVL_SOURCE_PASSWORDCONTAINER_PASSWORDCONTAINER_HXX + +#include <vector> +#include <map> +#include <com/sun/star/task/XPasswordContainer2.hpp> +#include <com/sun/star/task/PasswordRequestMode.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XEventListener.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <cppuhelper/implbase.hxx> + +#include <unotools/configitem.hxx> +#include <ucbhelper/interactionrequest.hxx> + +#include <rtl/ref.hxx> +#include <osl/mutex.hxx> + +#include "syscreds.hxx" + +#define MEMORY_RECORD 0 +#define PERSISTENT_RECORD 1 + + +class NamePassRecord +{ + OUString m_aName; + + // there are two lists of passwords, memory passwords and persistent passwords + bool m_bHasMemPass; + ::std::vector< OUString > m_aMemPass; + + // persistent passwords are encrypted in one string + bool m_bHasPersPass; + OUString m_aPersPass; + + void InitArrays( bool bHasMemoryList, const ::std::vector< OUString >& aMemoryList, + bool bHasPersistentList, const OUString& aPersistentList ) + { + m_bHasMemPass = bHasMemoryList; + if ( bHasMemoryList ) + m_aMemPass = aMemoryList; + + m_bHasPersPass = bHasPersistentList; + if ( bHasPersistentList ) + m_aPersPass = aPersistentList; + } + +public: + + NamePassRecord( const OUString& aName ) + : m_aName( aName ) + , m_bHasMemPass( false ) + , m_bHasPersPass( false ) + { + } + + NamePassRecord( const OUString& aName, const OUString& aPersistentList ) + : m_aName( aName ) + , m_bHasMemPass( false ) + , m_bHasPersPass( true ) + , m_aPersPass( aPersistentList ) + { + } + + NamePassRecord( const NamePassRecord& aRecord ) + : m_aName( aRecord.m_aName ) + , m_bHasMemPass( false ) + , m_bHasPersPass( false ) + { + InitArrays( aRecord.m_bHasMemPass, aRecord.m_aMemPass, aRecord.m_bHasPersPass, aRecord.m_aPersPass ); + } + + NamePassRecord& operator=( const NamePassRecord& aRecord ) + { + if (this != &aRecord) + { + m_aName = aRecord.m_aName; + + m_aMemPass.clear(); + m_aPersPass.clear(); + InitArrays( aRecord.m_bHasMemPass, aRecord.m_aMemPass, aRecord.m_bHasPersPass, aRecord.m_aPersPass ); + } + return *this; + } + + const OUString& GetUserName() const + { + return m_aName; + } + + bool HasPasswords( sal_Int8 nStatus ) const + { + if ( nStatus == MEMORY_RECORD ) + return m_bHasMemPass; + if ( nStatus == PERSISTENT_RECORD ) + return m_bHasPersPass; + + return false; + } + + ::std::vector< OUString > GetMemPasswords() const + { + if ( m_bHasMemPass ) + return m_aMemPass; + + return ::std::vector< OUString >(); + } + + OUString GetPersPasswords() const + { + if ( m_bHasPersPass ) + return m_aPersPass; + + return OUString(); + } + + void SetMemPasswords( const ::std::vector< OUString >& aMemList ) + { + m_aMemPass = aMemList; + m_bHasMemPass = true; + } + + void SetPersPasswords( const OUString& aPersList ) + { + m_aPersPass = aPersList; + m_bHasPersPass = true; + } + + void RemovePasswords( sal_Int8 nStatus ) + { + if ( nStatus == MEMORY_RECORD ) + { + m_bHasMemPass = false; + m_aMemPass.clear(); + } + else if ( nStatus == PERSISTENT_RECORD ) + { + m_bHasPersPass = false; + m_aPersPass.clear(); + } + } + +}; + + +typedef ::std::pair< const OUString, ::std::vector< NamePassRecord > > PairUrlRecord; +typedef ::std::map< OUString, ::std::vector< NamePassRecord > > PassMap; + + +class PasswordContainer; + +class StorageItem + : public ::utl::ConfigItem +{ +private: + PasswordContainer* mainCont; + bool hasEncoded; + OUString mEncoded; + + virtual void ImplCommit() override; + +public: + StorageItem( PasswordContainer* point, const OUString& path ) : + ConfigItem( path, ConfigItemMode::NONE ), + mainCont( point ), + hasEncoded( false ) + { + css::uno::Sequence< OUString > aNode { path + "/Store" }; + EnableNotification( aNode ); + } + + PassMap getInfo(); + void update( const OUString& url, const NamePassRecord& rec ); + void remove( const OUString& url, const OUString& rec ); + void clear(); + + bool getEncodedMP( OUString& aResult ); + void setEncodedMP( const OUString& aResult, bool bAcceptEnmpty = false ); + void setUseStorage( bool bUse ); + bool useStorage(); + + virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override; +}; + + +class PasswordContainer : public ::cppu::WeakImplHelper< + css::task::XPasswordContainer2, + css::lang::XServiceInfo, + css::lang::XEventListener > +{ +private: + PassMap m_aContainer; + std::unique_ptr<StorageItem> m_pStorageFile; + ::osl::Mutex mMutex; + OUString m_aMasterPasswd; // master password is set when the string is not empty + css::uno::Reference< css::lang::XComponent > mComponent; + SysCredentialsConfig mUrlContainer; + + /// @throws css::uno::RuntimeException + css::uno::Sequence< css::task::UserRecord > CopyToUserRecordSequence( + const ::std::vector< NamePassRecord >& original, + const css::uno::Reference< css::task::XInteractionHandler >& Handler ); + + css::task::UserRecord CopyToUserRecord( + const NamePassRecord& aRecord, + bool& io_bTryToDecode, + const css::uno::Reference< css::task::XInteractionHandler >& aHandler ); + + /// @throws css::uno::RuntimeException + css::uno::Sequence< css::task::UserRecord > FindUsr( + const ::std::vector< NamePassRecord >& userlist, + const OUString& name, + const css::uno::Reference< css::task::XInteractionHandler >& Handler ); +/// @throws css::uno::RuntimeException +bool createUrlRecord( + const PassMap::iterator & rIter, + bool bName, + const OUString & aName, + const css::uno::Reference< css::task::XInteractionHandler >& aHandler, + css::task::UrlRecord & rRec ); + +/// @throws css::uno::RuntimeException +css::task::UrlRecord find( + const OUString& aURL, + const OUString& aName, + bool bName, // only needed to support empty user names + const css::uno::Reference< css::task::XInteractionHandler >& aHandler ); + + static OUString GetDefaultMasterPassword(); + + static OUString RequestPasswordFromUser( + css::task::PasswordRequestMode aRMode, + const css::uno::Reference< css::task::XInteractionHandler >& xHandler ); + + /// @throws css::uno::RuntimeException + OUString const & GetMasterPassword( const css::uno::Reference< css::task::XInteractionHandler >& Handler ); + + /// @throws css::uno::RuntimeException + void UpdateVector( const OUString& url, ::std::vector< NamePassRecord >& toUpdate, NamePassRecord const & rec, bool writeFile ); + + /// @throws css::uno::RuntimeException + void PrivateAdd( const OUString& aUrl, + const OUString& aUserName, + const css::uno::Sequence< OUString >& aPasswords, + char aMode, + const css::uno::Reference< css::task::XInteractionHandler >& Handler ); + + /// @throws css::uno::RuntimeException + static ::std::vector< OUString > DecodePasswords( const OUString& aLine, const OUString& aMasterPassword, css::task::PasswordRequestMode mode ); + + /// @throws css::uno::RuntimeException + static OUString EncodePasswords(const std::vector< OUString >& lines, const OUString& aMasterPassword ); + +public: + PasswordContainer( const css::uno::Reference< css::lang::XMultiServiceFactory >& ); + virtual ~PasswordContainer() override; + + virtual void SAL_CALL add( const OUString& aUrl, + const OUString& aUserName, + const css::uno::Sequence< OUString >& aPasswords, + const css::uno::Reference< css::task::XInteractionHandler >& Handler ) override; + + virtual void SAL_CALL addPersistent( const OUString& aUrl, + const OUString& aUserName, + const css::uno::Sequence< OUString >& aPasswords, + const css::uno::Reference< css::task::XInteractionHandler >& Handler ) override; + + virtual css::task::UrlRecord SAL_CALL + find( const OUString& aUrl, + const css::uno::Reference< css::task::XInteractionHandler >& Handler ) override; + + virtual css::task::UrlRecord SAL_CALL + findForName( const OUString& aUrl, + const OUString& aUserName, + const css::uno::Reference< css::task::XInteractionHandler >& Handler ) override; + + virtual void SAL_CALL remove( const OUString& aUrl, + const OUString& aUserName ) override; + + virtual void SAL_CALL removePersistent( const OUString& aUrl, + const OUString& aUserName ) override; + + virtual void SAL_CALL removeAllPersistent() override; + + virtual css::uno::Sequence< css::task::UrlRecord > SAL_CALL + getAllPersistent( const css::uno::Reference< css::task::XInteractionHandler >& Handler ) override; + + + // provide factory + /// @throws css::uno::RuntimeException + static OUString impl_getStaticImplementationName( ); + /// @throws css::uno::RuntimeException + static css::uno::Sequence< OUString > + impl_getStaticSupportedServiceNames( ); + /// @throws css::uno::RuntimeException + static css::uno::Reference< css::lang::XSingleServiceFactory > + impl_createFactory( const css::uno::Reference< css::lang::XMultiServiceFactory >& ServiceManager ); + /// @throws css::uno::RuntimeException + static css::uno::Reference< css::uno::XInterface > SAL_CALL + impl_createInstance( const css::uno::Reference< css::lang::XMultiServiceFactory >& xServiceManager ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames( ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XMasterPasswordHandling + virtual sal_Bool SAL_CALL authorizateWithMasterPassword( const css::uno::Reference< css::task::XInteractionHandler >& xHandler ) override; + virtual sal_Bool SAL_CALL changeMasterPassword( const css::uno::Reference< css::task::XInteractionHandler >& xHandler ) override; + virtual void SAL_CALL removeMasterPassword() override; + virtual sal_Bool SAL_CALL hasMasterPassword( ) override; + virtual sal_Bool SAL_CALL allowPersistentStoring( sal_Bool bAllow ) override; + virtual sal_Bool SAL_CALL isPersistentStoringAllowed( ) override; + + // XMasterPasswordHandling2 + virtual sal_Bool SAL_CALL useDefaultMasterPassword( const css::uno::Reference< css::task::XInteractionHandler >& xHandler ) override; + virtual sal_Bool SAL_CALL isDefaultMasterPasswordUsed( ) override; + + // XUrlContainer + virtual void SAL_CALL addUrl( const OUString& Url, sal_Bool MakePersistent ) override; + virtual OUString SAL_CALL findUrl( const OUString& Url ) override; + virtual void SAL_CALL removeUrl( const OUString& Url ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getUrls( sal_Bool OnlyPersistent ) override; + + void Notify(); +}; + + +class MasterPasswordRequest_Impl : public ucbhelper::InteractionRequest +{ + ::rtl::Reference< ucbhelper::InteractionSupplyAuthentication > m_xAuthSupplier; + +public: + MasterPasswordRequest_Impl( css::task::PasswordRequestMode Mode ); + + const ::rtl::Reference< ucbhelper::InteractionSupplyAuthentication > & + getAuthenticationSupplier() const { return m_xAuthSupplier; } + +}; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svl/source/passwordcontainer/syscreds.cxx b/svl/source/passwordcontainer/syscreds.cxx new file mode 100644 index 000000000..096046310 --- /dev/null +++ b/svl/source/passwordcontainer/syscreds.cxx @@ -0,0 +1,268 @@ +/* -*- 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 "syscreds.hxx" +#include <osl/diagnose.h> +#include <comphelper/sequence.hxx> +#include <iterator> + +using namespace com::sun::star; + +SysCredentialsConfigItem::SysCredentialsConfigItem( + SysCredentialsConfig * pOwner ) +: utl::ConfigItem( "Office.Common/Passwords", ConfigItemMode::NONE ), + m_bInited( false ), + m_pOwner( pOwner ) +{ + uno::Sequence<OUString> aNode { "Office.Common/Passwords/AuthenticateUsingSystemCredentials" }; + EnableNotification( aNode ); +} + +//virtual +void SysCredentialsConfigItem::Notify( + const uno::Sequence< OUString > & /*seqPropertyNames*/ ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + m_bInited = false; + // rebuild m_seqURLs + getSystemCredentialsURLs(); + } + m_pOwner->persistentConfigChanged(); +} + +void SysCredentialsConfigItem::ImplCommit() +{ + // does nothing +} + +uno::Sequence< OUString > +SysCredentialsConfigItem::getSystemCredentialsURLs() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !m_bInited ) + { + // read config item + uno::Sequence<OUString> aPropNames { "AuthenticateUsingSystemCredentials" }; + uno::Sequence< uno::Any > aAnyValues( + utl::ConfigItem::GetProperties( aPropNames ) ); + + OSL_ENSURE( + aAnyValues.getLength() == 1, + "SysCredentialsConfigItem::getSystemCredentialsURLs: " + "Error reading config item!" ); + + uno::Sequence< OUString > aValues; + if ( ( aAnyValues[ 0 ] >>= aValues ) || + ( !aAnyValues[ 0 ].hasValue() ) ) + { + m_seqURLs = aValues; + m_bInited = true; + } + } + return m_seqURLs; +} + +void SysCredentialsConfigItem::setSystemCredentialsURLs( + const uno::Sequence< OUString > & seqURLList ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + // write config item. + uno::Sequence< OUString > aPropNames( 1 ); + uno::Sequence< uno::Any > aPropValues( 1 ); + aPropNames[ 0 ] = "AuthenticateUsingSystemCredentials"; + aPropValues[ 0 ] <<= seqURLList; + + utl::ConfigItem::SetModified(); + utl::ConfigItem::PutProperties( aPropNames, aPropValues ); + + m_seqURLs = seqURLList; + m_bInited = true; +} + + +namespace +{ + // TODO: This code is actually copied from svl/source/passwordcontainer.cxx + bool removeLastSegment( OUString & aURL ) + { + sal_Int32 aInd = aURL.lastIndexOf( '/' ); + + if( aInd > 0 ) + { + sal_Int32 aPrevInd = aURL.lastIndexOf( '/', aInd ); + if ( aURL.indexOf( "://" ) != aPrevInd - 2 || + aInd != aURL.getLength() - 1 ) + { + aURL = aURL.copy( 0, aInd ); + return true; + } + } + + return false; + } + + bool findURL( StringSet const & rContainer, OUString const & aURL, OUString & aResult ) + { + // TODO: This code is actually copied from svl/source/passwordcontainer.cxx + if( !rContainer.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/... + StringSet::const_iterator aIter = rContainer.find( aUrl ); + if( aIter != rContainer.end() ) + { + aResult = *aIter; + return true; + } + else + { + OUString tmpUrl( aUrl ); + if ( !tmpUrl.endsWith("/") ) + tmpUrl += "/"; + + aIter = rContainer.lower_bound( tmpUrl ); + if( aIter != rContainer.end() && aIter->match( tmpUrl ) ) + { + aResult = *aIter; + return true; + } + } + } + while( removeLastSegment( aUrl ) && !aUrl.isEmpty() ); + } + aResult.clear(); + return false; + } + +} // namespace + +SysCredentialsConfig::SysCredentialsConfig() +: m_aConfigItem( this ), + m_bCfgInited( false ) +{ +} + +void SysCredentialsConfig::initCfg() +{ + osl::MutexGuard aGuard( m_aMutex ); + if ( !m_bCfgInited ) + { + uno::Sequence< OUString > aURLs( + m_aConfigItem.getSystemCredentialsURLs() ); + std::copy(aURLs.begin(), aURLs.end(), + std::inserter(m_aCfgContainer, m_aCfgContainer.end())); + + m_bCfgInited = true; + } +} + +void SysCredentialsConfig::writeCfg() +{ + osl::MutexGuard aGuard( m_aMutex ); + + OSL_ENSURE( m_bCfgInited, "SysCredentialsConfig::writeCfg : not initialized!" ); + + m_aConfigItem.setSystemCredentialsURLs( comphelper::containerToSequence(m_aCfgContainer) ); +} + +OUString SysCredentialsConfig::find( OUString const & aURL ) +{ + osl::MutexGuard aGuard( m_aMutex ); + OUString aResult; + if ( findURL( m_aMemContainer, aURL, aResult ) ) + return aResult; + + initCfg(); + if ( findURL( m_aCfgContainer, aURL, aResult ) ) + return aResult; + + return OUString(); +} + +void SysCredentialsConfig::add( OUString const & rURL, bool bPersistent ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( bPersistent ) + { + m_aMemContainer.erase( rURL ); + + initCfg(); + m_aCfgContainer.insert( rURL ); + writeCfg(); + } + else + { + initCfg(); + if ( m_aCfgContainer.erase( rURL ) > 0 ) + writeCfg(); + + m_aMemContainer.insert( rURL ); + } +} + +void SysCredentialsConfig::remove( OUString const & rURL ) +{ + m_aMemContainer.erase( rURL ); + + initCfg(); + if ( m_aCfgContainer.erase( rURL ) > 0 ) + writeCfg(); +} + +uno::Sequence< OUString > SysCredentialsConfig::list( bool bOnlyPersistent ) +{ + initCfg(); + sal_Int32 nCount = m_aCfgContainer.size() + + ( bOnlyPersistent ? 0 : m_aMemContainer.size() ); + uno::Sequence< OUString > aResult( nCount ); + + sal_Int32 n = 0; + + for ( const auto& rItem : m_aCfgContainer ) + { + aResult[ n ] = rItem; + ++n; + } + + if ( !bOnlyPersistent ) + { + for ( const auto& rItem : m_aMemContainer ) + { + aResult[ n ] = rItem; + ++n; + } + } + return aResult; +} + +void SysCredentialsConfig::persistentConfigChanged() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + m_bCfgInited = false; // re-init on demand. +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svl/source/passwordcontainer/syscreds.hxx b/svl/source/passwordcontainer/syscreds.hxx new file mode 100644 index 000000000..d3605c262 --- /dev/null +++ b/svl/source/passwordcontainer/syscreds.hxx @@ -0,0 +1,82 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SVL_SOURCE_PASSWORDCONTAINER_SYSCREDS_HXX +#define INCLUDED_SVL_SOURCE_PASSWORDCONTAINER_SYSCREDS_HXX + +#include <set> +#include <memory> +#include <osl/mutex.hxx> +#include <rtl/ustring.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <unotools/configitem.hxx> + +class SysCredentialsConfig; + +class SysCredentialsConfigItem : public utl::ConfigItem +{ + public: + explicit SysCredentialsConfigItem( SysCredentialsConfig * pOwner ); + //virtual ~SysCredentialsConfigItem(); + + virtual void Notify( const css::uno::Sequence< OUString > & seqPropertyNames ) override; + + css::uno::Sequence< OUString > getSystemCredentialsURLs(); + + void setSystemCredentialsURLs( const css::uno::Sequence< OUString > & seqURLList ); + + //bool isSystemCredentialsURL( const OUString & rURL ) const; + + private: + virtual void ImplCommit() override; + + ::osl::Mutex m_aMutex; + bool m_bInited; + css::uno::Sequence< OUString > m_seqURLs; + SysCredentialsConfig * m_pOwner; +}; + +typedef std::set< OUString > StringSet; + +class SysCredentialsConfig +{ + public: + SysCredentialsConfig(); + + OUString find( OUString const & rURL ); + void add( OUString const & rURL, bool bPersistent ); + void remove( OUString const & rURL ); + css::uno::Sequence< OUString > list( bool bOnlyPersistent ); + + void persistentConfigChanged(); + + private: + void initCfg(); + void writeCfg(); + + ::osl::Mutex m_aMutex; + StringSet m_aMemContainer; + StringSet m_aCfgContainer; + SysCredentialsConfigItem m_aConfigItem; + bool m_bCfgInited; +}; + +#endif // INCLUDED_SVL_SOURCE_PASSWORDCONTAINER_SYSCREDS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |