qtkeychain-0.1/0000775000175000017500000000000012077636342011336 5ustar jrjrqtkeychain-0.1/ReadMe.markdown0000777000175000017500000000000012115675462016127 2ReadMe.txtustar jrjrqtkeychain-0.1/keychain.h0000664000175000017500000000732312077636342013307 0ustar jrjr/****************************************************************************** * Copyright (C) 2011 Frank Osterfeld * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * * details, check the accompanying file 'COPYING'. * *****************************************************************************/ #ifndef KEYCHAIN_H #define KEYCHAIN_H #include "qkeychain_export.h" #include #include class QSettings; #define QTKEYCHAIN_VERSION 0x000100 namespace QKeychain { /** * Error codes */ enum Error { NoError=0, /**< No error occurred, operation was successful */ EntryNotFound, /**< For the given key no data was found */ CouldNotDeleteEntry, /**< Could not delete existing secret data */ AccessDeniedByUser, /**< User denied access to keychain */ AccessDenied, /**< Access denied for other reasons */ NoBackendAvailable, /**< No platform-specific keychain service available */ NotImplemented, /**< Not implemented on platform */ OtherError /**< Something else went wrong (errorString() might provide details) */ }; class JobExecutor; class JobPrivate; class QKEYCHAIN_EXPORT Job : public QObject { Q_OBJECT public: explicit Job( const QString& service, QObject* parent=0 ); ~Job(); QSettings* settings() const; void setSettings( QSettings* settings ); void start(); QString service() const; Error error() const; QString errorString() const; bool autoDelete() const; void setAutoDelete( bool autoDelete ); bool insecureFallback() const; void setInsecureFallback( bool insecureFallback ); Q_SIGNALS: void finished( QKeychain::Job* ); protected: Q_INVOKABLE virtual void doStart() = 0; void setError( Error error ); void setErrorString( const QString& errorString ); void emitFinished(); void emitFinishedWithError(Error, const QString& errorString); private: JobPrivate* const d; }; class ReadPasswordJobPrivate; class QKEYCHAIN_EXPORT ReadPasswordJob : public Job { Q_OBJECT public: explicit ReadPasswordJob( const QString& service, QObject* parent=0 ); ~ReadPasswordJob(); QString key() const; void setKey( const QString& key ); QByteArray binaryData() const; QString textData() const; protected: void doStart(); private: friend class QKeychain::ReadPasswordJobPrivate; friend class QKeychain::JobExecutor; ReadPasswordJobPrivate* const d; }; class WritePasswordJobPrivate; class QKEYCHAIN_EXPORT WritePasswordJob : public Job { Q_OBJECT public: explicit WritePasswordJob( const QString& service, QObject* parent=0 ); ~WritePasswordJob(); QString key() const; void setKey( const QString& key ); void setBinaryData( const QByteArray& data ); void setTextData( const QString& data ); protected: void doStart(); private: friend class QKeychain::JobExecutor; friend class QKeychain::WritePasswordJobPrivate; WritePasswordJobPrivate* const d; }; class DeletePasswordJobPrivate; class QKEYCHAIN_EXPORT DeletePasswordJob : public Job { Q_OBJECT public: explicit DeletePasswordJob( const QString& service, QObject* parent=0 ); ~DeletePasswordJob(); QString key() const; void setKey( const QString& key ); protected: void doStart(); private: friend class QKeychain::DeletePasswordJobPrivate; DeletePasswordJobPrivate* const d; }; } // namespace QtKeychain #endif qtkeychain-0.1/QtKeychainConfigVersion.cmake.in0000664000175000017500000000057612077636342017511 0ustar jrjrset(PACKAGE_VERSION "@QTKEYCHAIN_VERSION@") # Check whether the requested PACKAGE_FIND_VERSION is compatible if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") set(PACKAGE_VERSION_COMPATIBLE FALSE) else() set(PACKAGE_VERSION_COMPATIBLE TRUE) if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") set(PACKAGE_VERSION_EXACT TRUE) endif() endif() qtkeychain-0.1/ReadMe.txt0000664000175000017500000000221712077636342013236 0ustar jrjrQtKeychain ========== QtKeychain is a Qt API to store passwords and other secret data securely. How the data is stored depends on the platform: * **Mac OS X:** Passwords are stored in the OS X Keychain. * **Linux/Unix:** If running, KWallet (via D-Bus) is used. Support for the GNOME Keyring via freedesktop.org's [Secret Storage D-Bus specification](http://freedesktop.org/wiki/Specifications/secret-storage-spec "Secret Storage specification") is planned but not yet implemented. * **Windows:** Windows does not provide a service for secure storage. QtKeychain uses the Windows API function [CryptProtectData](http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261%28v=vs.85%29.aspx "CryptProtectData function") to encrypt the password with the user's logon credentials. The encrypted data is then persisted via QSettings. In unsupported environments QtKeychain will report an error. It will not store any data unencrypted unless explicitly requested (setInsecureFallback( true )). **License:** QtKeychain is available under the [Modified BSD License](http://www.gnu.org/licenses/license-list.html#ModifiedBSD). See the file COPYING for details. qtkeychain-0.1/cmake/0000775000175000017500000000000012077636342012416 5ustar jrjrqtkeychain-0.1/cmake/Modules/0000775000175000017500000000000012077636342014026 5ustar jrjrqtkeychain-0.1/cmake/Modules/GNUInstallDirs.cmake0000664000175000017500000001626612077636342017645 0ustar jrjr# - Define GNU standard installation directories # Provides install directory variables as defined for GNU software: # http://www.gnu.org/prep/standards/html_node/Directory-Variables.html # Inclusion of this module defines the following variables: # CMAKE_INSTALL_ - destination for files of a given type # CMAKE_INSTALL_FULL_ - corresponding absolute path # where is one of: # BINDIR - user executables (bin) # SBINDIR - system admin executables (sbin) # LIBEXECDIR - program executables (libexec) # SYSCONFDIR - read-only single-machine data (etc) # SHAREDSTATEDIR - modifiable architecture-independent data (com) # LOCALSTATEDIR - modifiable single-machine data (var) # LIBDIR - object code libraries (lib or lib64 or lib/ on Debian) # INCLUDEDIR - C header files (include) # OLDINCLUDEDIR - C header files for non-gcc (/usr/include) # DATAROOTDIR - read-only architecture-independent data root (share) # DATADIR - read-only architecture-independent data (DATAROOTDIR) # INFODIR - info documentation (DATAROOTDIR/info) # LOCALEDIR - locale-dependent data (DATAROOTDIR/locale) # MANDIR - man documentation (DATAROOTDIR/man) # DOCDIR - documentation root (DATAROOTDIR/doc/PROJECT_NAME) # Each CMAKE_INSTALL_ value may be passed to the DESTINATION options of # install() commands for the corresponding file type. If the includer does # not define a value the above-shown default will be used and the value will # appear in the cache for editing by the user. # Each CMAKE_INSTALL_FULL_ value contains an absolute path constructed # from the corresponding destination by prepending (if necessary) the value # of CMAKE_INSTALL_PREFIX. #============================================================================= # Copyright 2011 Nikita Krupen'ko # Copyright 2011 Kitware, Inc. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) # Installation directories # if(NOT DEFINED CMAKE_INSTALL_BINDIR) set(CMAKE_INSTALL_BINDIR "bin" CACHE PATH "user executables (bin)") endif() if(NOT DEFINED CMAKE_INSTALL_SBINDIR) set(CMAKE_INSTALL_SBINDIR "sbin" CACHE PATH "system admin executables (sbin)") endif() if(NOT DEFINED CMAKE_INSTALL_LIBEXECDIR) set(CMAKE_INSTALL_LIBEXECDIR "libexec" CACHE PATH "program executables (libexec)") endif() if(NOT DEFINED CMAKE_INSTALL_SYSCONFDIR) set(CMAKE_INSTALL_SYSCONFDIR "etc" CACHE PATH "read-only single-machine data (etc)") endif() if(NOT DEFINED CMAKE_INSTALL_SHAREDSTATEDIR) set(CMAKE_INSTALL_SHAREDSTATEDIR "com" CACHE PATH "modifiable architecture-independent data (com)") endif() if(NOT DEFINED CMAKE_INSTALL_LOCALSTATEDIR) set(CMAKE_INSTALL_LOCALSTATEDIR "var" CACHE PATH "modifiable single-machine data (var)") endif() if(NOT DEFINED CMAKE_INSTALL_LIBDIR) set(_LIBDIR_DEFAULT "lib") # Override this default 'lib' with 'lib64' iff: # - we are on Linux system but NOT cross-compiling # - we are NOT on debian # - we are on a 64 bits system # reason is: amd64 ABI: http://www.x86-64.org/documentation/abi.pdf # For Debian with multiarch, use 'lib/${CMAKE_LIBRARY_ARCHITECTURE}' if # CMAKE_LIBRARY_ARCHITECTURE is set (which contains e.g. "i386-linux-gnu" # See http://wiki.debian.org/Multiarch if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT CMAKE_CROSSCOMPILING) if (EXISTS "/etc/debian_version") # is this a debian system ? if(CMAKE_LIBRARY_ARCHITECTURE) set(_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}") endif() else() # not debian, rely on CMAKE_SIZEOF_VOID_P: if(NOT DEFINED CMAKE_SIZEOF_VOID_P) message(AUTHOR_WARNING "Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. " "Please enable at least one language before including GNUInstallDirs.") else() if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") set(_LIBDIR_DEFAULT "lib64") endif() endif() endif() endif() set(CMAKE_INSTALL_LIBDIR "${_LIBDIR_DEFAULT}" CACHE PATH "object code libraries (${_LIBDIR_DEFAULT})") endif() if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR) set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "C header files (include)") endif() if(NOT DEFINED CMAKE_INSTALL_OLDINCLUDEDIR) set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "C header files for non-gcc (/usr/include)") endif() if(NOT DEFINED CMAKE_INSTALL_DATAROOTDIR) set(CMAKE_INSTALL_DATAROOTDIR "share" CACHE PATH "read-only architecture-independent data root (share)") endif() #----------------------------------------------------------------------------- # Values whose defaults are relative to DATAROOTDIR. Store empty values in # the cache and store the defaults in local variables if the cache values are # not set explicitly. This auto-updates the defaults as DATAROOTDIR changes. if(NOT CMAKE_INSTALL_DATADIR) set(CMAKE_INSTALL_DATADIR "" CACHE PATH "read-only architecture-independent data (DATAROOTDIR)") set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}") endif() if(NOT CMAKE_INSTALL_INFODIR) set(CMAKE_INSTALL_INFODIR "" CACHE PATH "info documentation (DATAROOTDIR/info)") set(CMAKE_INSTALL_INFODIR "${CMAKE_INSTALL_DATAROOTDIR}/info") endif() if(NOT CMAKE_INSTALL_LOCALEDIR) set(CMAKE_INSTALL_LOCALEDIR "" CACHE PATH "locale-dependent data (DATAROOTDIR/locale)") set(CMAKE_INSTALL_LOCALEDIR "${CMAKE_INSTALL_DATAROOTDIR}/locale") endif() if(NOT CMAKE_INSTALL_MANDIR) set(CMAKE_INSTALL_MANDIR "" CACHE PATH "man documentation (DATAROOTDIR/man)") set(CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_DATAROOTDIR}/man") endif() if(NOT CMAKE_INSTALL_DOCDIR) set(CMAKE_INSTALL_DOCDIR "" CACHE PATH "documentation root (DATAROOTDIR/doc/PROJECT_NAME)") set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}") endif() #----------------------------------------------------------------------------- mark_as_advanced( CMAKE_INSTALL_BINDIR CMAKE_INSTALL_SBINDIR CMAKE_INSTALL_LIBEXECDIR CMAKE_INSTALL_SYSCONFDIR CMAKE_INSTALL_SHAREDSTATEDIR CMAKE_INSTALL_LOCALSTATEDIR CMAKE_INSTALL_LIBDIR CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_OLDINCLUDEDIR CMAKE_INSTALL_DATAROOTDIR CMAKE_INSTALL_DATADIR CMAKE_INSTALL_INFODIR CMAKE_INSTALL_LOCALEDIR CMAKE_INSTALL_MANDIR CMAKE_INSTALL_DOCDIR ) # Result directories # foreach(dir BINDIR SBINDIR LIBEXECDIR SYSCONFDIR SHAREDSTATEDIR LOCALSTATEDIR LIBDIR INCLUDEDIR OLDINCLUDEDIR DATAROOTDIR DATADIR INFODIR LOCALEDIR MANDIR DOCDIR ) if(NOT IS_ABSOLUTE ${CMAKE_INSTALL_${dir}}) set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_${dir}}") else() set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_${dir}}") endif() endforeach() qtkeychain-0.1/qkeychain_export.h0000664000175000017500000000050412077636342015063 0ustar jrjr#ifndef QKEYCHAIN_EXPORT_H #define QKEYCHAIN_EXPORT_H #include # ifdef QKEYCHAIN_STATICLIB # undef QKEYCHAIN_SHAREDLIB # define QKEYCHAIN_EXPORT # else # ifdef QKEYCHAIN_BUILD_QKEYCHAIN_LIB # define QKEYCHAIN_EXPORT Q_DECL_EXPORT # else # define QKEYCHAIN_EXPORT Q_DECL_IMPORT # endif # endif #endif qtkeychain-0.1/keychain.cpp0000664000175000017500000001322712077636342013642 0ustar jrjr/****************************************************************************** * Copyright (C) 2011 Frank Osterfeld * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * * details, check the accompanying file 'COPYING'. * *****************************************************************************/ #include "keychain.h" #include "keychain_p.h" using namespace QKeychain; Job::Job( const QString& service, QObject *parent ) : QObject( parent ) , d ( new JobPrivate( service ) ) { } Job::~Job() { delete d; } QString Job::service() const { return d->service; } QSettings* Job::settings() const { return d->settings; } void Job::setSettings( QSettings* settings ) { d->settings = settings; } void Job::start() { QMetaObject::invokeMethod( this, "doStart", Qt::QueuedConnection ); } bool Job::autoDelete() const { return d->autoDelete; } void Job::setAutoDelete( bool autoDelete ) { d->autoDelete = autoDelete; } bool Job::insecureFallback() const { return d->insecureFallback; } void Job::setInsecureFallback( bool insecureFallback ) { d->insecureFallback = insecureFallback; } void Job::emitFinished() { emit finished( this ); if ( d->autoDelete ) deleteLater(); } void Job::emitFinishedWithError( Error error, const QString& errorString ) { d->error = error; d->errorString = errorString; emitFinished(); } Error Job::error() const { return d->error; } QString Job::errorString() const { return d->errorString; } void Job::setError( Error error ) { d->error = error; } void Job::setErrorString( const QString& errorString ) { d->errorString = errorString; } ReadPasswordJob::ReadPasswordJob( const QString& service, QObject* parent ) : Job( service, parent ) , d( new ReadPasswordJobPrivate( this ) ) {} ReadPasswordJob::~ReadPasswordJob() { delete d; } QString ReadPasswordJob::textData() const { return QString::fromUtf8( d->data ); } QByteArray ReadPasswordJob::binaryData() const { return d->data; } QString ReadPasswordJob::key() const { return d->key; } void ReadPasswordJob::setKey( const QString& key ) { d->key = key; } void ReadPasswordJob::doStart() { JobExecutor::instance()->enqueue( this ); } WritePasswordJob::WritePasswordJob( const QString& service, QObject* parent ) : Job( service, parent ) , d( new WritePasswordJobPrivate( this ) ) { } WritePasswordJob::~WritePasswordJob() { delete d; } QString WritePasswordJob::key() const { return d->key; } void WritePasswordJob::setKey( const QString& key ) { d->key = key; } void WritePasswordJob::setBinaryData( const QByteArray& data ) { d->binaryData = data; d->mode = WritePasswordJobPrivate::Binary; } void WritePasswordJob::setTextData( const QString& data ) { d->textData = data; d->mode = WritePasswordJobPrivate::Text; } void WritePasswordJob::doStart() { JobExecutor::instance()->enqueue( this ); } DeletePasswordJob::DeletePasswordJob( const QString& service, QObject* parent ) : Job( service, parent ) , d( new DeletePasswordJobPrivate( this ) ) { } DeletePasswordJob::~DeletePasswordJob() { delete d; } void DeletePasswordJob::doStart() { //Internally, to delete a password we just execute a write job with no data set (null byte array). //In all current implementations, this deletes the entry so this is sufficient WritePasswordJob* job = new WritePasswordJob( service(), this ); connect( job, SIGNAL(finished(QKeychain::Job*)), d, SLOT(jobFinished(QKeychain::Job*)) ); job->setKey( d->key ); job->start(); } QString DeletePasswordJob::key() const { return d->key; } void DeletePasswordJob::setKey( const QString& key ) { d->key = key; } void DeletePasswordJobPrivate::jobFinished( Job* job ) { q->setError( job->error() ); q->setErrorString( job->errorString() ); q->emitFinished(); } JobExecutor::JobExecutor() : QObject( 0 ) , m_runningJob( 0 ) { } void JobExecutor::enqueue( Job* job ) { m_queue.append( job ); startNextIfNoneRunning(); } void JobExecutor::startNextIfNoneRunning() { if ( m_queue.isEmpty() || m_runningJob ) return; QPointer next; while ( !next && !m_queue.isEmpty() ) { next = m_queue.first(); m_queue.pop_front(); } if ( next ) { connect( next, SIGNAL(finished(QKeychain::Job*)), this, SLOT(jobFinished(QKeychain::Job*)) ); connect( next, SIGNAL(destroyed(QObject*)), this, SLOT(jobDestroyed(QObject*)) ); m_runningJob = next; if ( ReadPasswordJob* rpj = qobject_cast( m_runningJob ) ) rpj->d->scheduledStart(); else if ( WritePasswordJob* wpj = qobject_cast( m_runningJob) ) wpj->d->scheduledStart(); } } void JobExecutor::jobDestroyed( QObject* object ) { Q_UNUSED( object ) // for release mode Q_ASSERT( object == m_runningJob ); m_runningJob->disconnect( this ); m_runningJob = 0; startNextIfNoneRunning(); } void JobExecutor::jobFinished( Job* job ) { Q_UNUSED( job ) // for release mode Q_ASSERT( job == m_runningJob ); m_runningJob->disconnect( this ); m_runningJob = 0; startNextIfNoneRunning(); } JobExecutor* JobExecutor::s_instance = 0; JobExecutor* JobExecutor::instance() { if ( !s_instance ) s_instance = new JobExecutor; return s_instance; } qtkeychain-0.1/CMakeLists.txt0000664000175000017500000001021112077636342014071 0ustar jrjrcmake_minimum_required(VERSION 2.8) project(qtkeychain) ### set(QTKEYCHAIN_VERSION 0.1.0) set(QTKEYCHAIN_SOVERSION 0) ### set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cmake/Modules") include(GNUInstallDirs) # try Qt5 first, and prefer that if found find_package(Qt5Core QUIET) if (Qt5Core_FOUND) if(UNIX AND NOT APPLE) find_package(Qt5DBus REQUIRED) include_directories(${Qt5DBus_INCLUDE_DIRS}) set(QTDBUS_LIBRARIES ${Qt5DBus_LIBRARIES}) macro(qt_add_dbus_interface) qt5_add_dbus_interface(${ARGN}) endmacro() endif() macro(qt_wrap_cpp) qt5_wrap_cpp(${ARGN}) endmacro() set(QTCORE_LIBRARIES ${Qt5Core_LIBRARIES}) include_directories(${Qt5Core_INCLUDE_DIRS}) if (Qt5_POSITION_INDEPENDENT_CODE) if (CMAKE_VERSION VERSION_LESS 2.8.9) # TODO remove once we increase the cmake requirement set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") else() set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() endif() else() if(UNIX AND NOT APPLE) find_package(Qt4 COMPONENTS QtCore QtDBus REQUIRED) set(QTDBUS_LIBRARIES ${QT_QTDBUS_LIBRARY}) macro(qt_add_dbus_interface) qt4_add_dbus_interface(${ARGN}) endmacro() else() find_package(Qt4 COMPONENTS QtCore REQUIRED) endif() include_directories(${QT_INCLUDES}) set(QTCORE_LIBRARIES ${QT_QTCORE_LIBRARY}) macro(qt_wrap_cpp) qt4_wrap_cpp(${ARGN}) endmacro() endif() include_directories(${CMAKE_CURRENT_BINARY_DIR}) list(APPEND qtkeychain_LIBRARIES ${QTCORE_LIBRARIES}) set(qtkeychain_SOURCES keychain.cpp ) if(WIN32) list(APPEND qtkeychain_SOURCES keychain_win.cpp) list(APPEND qtkeychain_LIBRARIES crypt32) #FIXME: mingw bug; otherwise getting undefined refs to RtlSecureZeroMemory there if(MINGW) add_definitions( -O2 ) endif() endif() if(APPLE) list(APPEND qtkeychain_SOURCES keychain_mac.cpp) find_library(COREFOUNDATION_LIBRARY CoreFoundation) list(APPEND qtkeychain_LIBRARIES ${COREFOUNDATION_LIBRARY}) find_library(SECURITY_LIBRARY Security) list(APPEND qtkeychain_LIBRARIES ${SECURITY_LIBRARY}) endif() if(UNIX AND NOT APPLE) list(APPEND qtkeychain_SOURCES keychain_dbus.cpp) qt_add_dbus_interface(qtkeychain_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KWallet.xml kwallet_interface KWalletInterface) list(APPEND qtkeychain_LIBRARIES ${QTDBUS_LIBRARIES}) endif() QT_WRAP_CPP(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h) if(NOT QTKEYCHAIN_STATIC) add_library(qtkeychain SHARED ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES}) set_target_properties(qtkeychain PROPERTIES COMPILE_DEFINITIONS QKEYCHAIN_BUILD_QKEYCHAIN_LIB) target_link_libraries(qtkeychain ${qtkeychain_LIBRARIES}) else() add_library(qtkeychain STATIC ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES}) set_target_properties(qtkeychain PROPERTIES COMPILE_DEFINITIONS QKEYCHAIN_STATICLIB) endif() set_target_properties(qtkeychain PROPERTIES VERSION ${QTKEYCHAIN_VERSION} SOVERSION ${QTKEYCHAIN_SOVERSION} ) install(FILES keychain.h qkeychain_export.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qtkeychain/ ) install(TARGETS qtkeychain EXPORT QtKeychainLibraryDepends RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) add_executable( testclient testclient.cpp ) target_link_libraries( testclient qtkeychain) ### ### CMake config file ### export(TARGETS qtkeychain FILE "${PROJECT_BINARY_DIR}/QtKeychainLibraryDepends.cmake") export(PACKAGE QtKeychain) configure_file(QtKeychainBuildTreeSettings.cmake.in "${PROJECT_BINARY_DIR}/QtKeychainBuildTreeSettings.cmake" @ONLY) configure_file(QtKeychainConfig.cmake.in "${PROJECT_BINARY_DIR}/QtKeychainConfig.cmake" @ONLY) configure_file(QtKeychainConfigVersion.cmake.in "${PROJECT_BINARY_DIR}/QtKeychainConfigVersion.cmake" @ONLY) install(EXPORT QtKeychainLibraryDepends DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/QtKeychain" ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/QtKeychainConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/QtKeychainConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/QtKeychain ) qtkeychain-0.1/keychain_dbus.cpp0000664000175000017500000002057512077636342014663 0ustar jrjr/****************************************************************************** * Copyright (C) 2011 Frank Osterfeld * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * * details, check the accompanying file 'COPYING'. * *****************************************************************************/ #include "keychain_p.h" #include #include using namespace QKeychain; void ReadPasswordJobPrivate::scheduledStart() { iface = new org::kde::KWallet( QLatin1String("org.kde.kwalletd"), QLatin1String("/modules/kwalletd"), QDBusConnection::sessionBus(), this ); const QDBusPendingReply reply = iface->open( QLatin1String("kdewallet"), 0, q->service() ); QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, this ); connect( watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletOpenFinished(QDBusPendingCallWatcher*)) ); } void ReadPasswordJobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) { watcher->deleteLater(); const QDBusPendingReply reply = *watcher; std::auto_ptr local( !q->settings() ? new QSettings( q->service() ) : 0 ); QSettings* actual = q->settings() ? q->settings() : local.get(); WritePasswordJobPrivate::Mode mode; const QString typeKey = QString( "%1/type" ).arg( key ); const QString dataKey = QString( "%1/data" ).arg( key ); if ( reply.isError() ) { const QDBusError err = reply.error(); if ( q->insecureFallback() && actual->contains( dataKey ) ) { mode = (WritePasswordJobPrivate::Mode)actual->value( typeKey ).toInt(); data = actual->value( dataKey ).toByteArray(); q->emitFinished(); return; } else { if ( err.type() == QDBusError::ServiceUnknown ) //KWalletd not running q->emitFinishedWithError( NoBackendAvailable, tr("No keychain service available") ); else q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) ); return; } } if ( actual->contains( dataKey ) ) { // We previously stored data in the insecure QSettings, but now have KWallet available. // Do the migration data = actual->value( dataKey ).toByteArray(); mode = (WritePasswordJobPrivate::Mode)actual->value( typeKey ).toInt(); actual->remove( key ); q->emitFinished(); WritePasswordJob* j = new WritePasswordJob( q->service(), 0 ); j->setSettings( q->settings() ); j->setKey( key ); j->setAutoDelete( true ); if ( mode == WritePasswordJobPrivate::Binary ) j->setBinaryData( data ); else if ( mode == WritePasswordJobPrivate::Text ) j->setTextData( QString::fromUtf8( data ) ); else Q_ASSERT( false ); j->start(); return; } walletHandle = reply.value(); if ( walletHandle < 0 ) { q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") ); return; } const QDBusPendingReply nextReply = iface->entryType( walletHandle, q->service(), key, q->service() ); QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this ); connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletEntryTypeFinished(QDBusPendingCallWatcher*)) ); } void ReadPasswordJobPrivate::kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher ) { watcher->deleteLater(); if ( watcher->isError() ) { const QDBusError err = watcher->error(); q->emitFinishedWithError( OtherError, tr("Could not determine data type: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) ); return; } const QDBusPendingReply reply = *watcher; dataType = reply.value() == 1/*Password*/ ? Text : Binary; const QDBusPendingCall nextReply = dataType == Text ? QDBusPendingCall( iface->readPassword( walletHandle, q->service(), key, q->service() ) ) : QDBusPendingCall( iface->readEntry( walletHandle, q->service(), key, q->service() ) ); QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this ); connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletReadFinished(QDBusPendingCallWatcher*)) ); } void ReadPasswordJobPrivate::kwalletReadFinished( QDBusPendingCallWatcher* watcher ) { watcher->deleteLater(); if ( watcher->isError() ) { const QDBusError err = watcher->error(); q->emitFinishedWithError( OtherError, tr("Could not read password: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) ); return; } if ( dataType == Binary ) { QDBusPendingReply reply = *watcher; data = reply.value(); } else { QDBusPendingReply reply = *watcher; data = reply.value().toUtf8(); } q->emitFinished(); } void WritePasswordJobPrivate::scheduledStart() { iface = new org::kde::KWallet( QLatin1String("org.kde.kwalletd"), QLatin1String("/modules/kwalletd"), QDBusConnection::sessionBus(), this ); const QDBusPendingReply reply = iface->open( QLatin1String("kdewallet"), 0, q->service() ); QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, this ); connect( watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletOpenFinished(QDBusPendingCallWatcher*)) ); } void WritePasswordJobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) { watcher->deleteLater(); QDBusPendingReply reply = *watcher; std::auto_ptr local( !q->settings() ? new QSettings( q->service() ) : 0 ); QSettings* actual = q->settings() ? q->settings() : local.get(); if ( reply.isError() ) { if ( q->insecureFallback() ) { if ( mode == Delete ) { actual->remove( key ); actual->sync(); q->emitFinished(); return; } actual->setValue( QString( "%1/type" ).arg( key ), (int)mode ); if ( mode == Text ) actual->setValue( QString( "%1/data" ).arg( key ), textData.toUtf8() ); else if ( mode == Binary ) actual->setValue( QString( "%1/data" ).arg( key ), binaryData ); actual->sync(); q->emitFinished(); } else { const QDBusError err = reply.error(); q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) ); } return; } if ( actual->contains( key ) ) { // If we had previously written to QSettings, but we now have a kwallet available, migrate and delete old insecure data actual->remove( key ); actual->sync(); } const int handle = reply.value(); if ( handle < 0 ) { q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") ); return; } QDBusPendingReply nextReply; if ( !textData.isEmpty() ) nextReply = iface->writePassword( handle, q->service(), key, textData, q->service() ); else if ( !binaryData.isEmpty() ) nextReply = iface->writeEntry( handle, q->service(), key, binaryData, q->service() ); else nextReply = iface->removeEntry( handle, q->service(), key, q->service() ); QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this ); connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletWriteFinished(QDBusPendingCallWatcher*)) ); } void WritePasswordJobPrivate::kwalletWriteFinished( QDBusPendingCallWatcher* watcher ) { watcher->deleteLater(); QDBusPendingReply reply = *watcher; if ( reply.isError() ) { const QDBusError err = reply.error(); q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) ); return; } q->emitFinished(); } qtkeychain-0.1/org.kde.KWallet.xml0000664000175000017500000002501212077636342014753 0ustar jrjr qtkeychain-0.1/keychain_mac.cpp0000664000175000017500000001331312077636342014456 0ustar jrjr/****************************************************************************** * Copyright (C) 2011 Frank Osterfeld * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * * details, check the accompanying file 'COPYING'. * *****************************************************************************/ #include "keychain_p.h" #include #include #include using namespace QKeychain; template struct Releaser { explicit Releaser( const T& v ) : value( v ) {} ~Releaser() { CFRelease( value ); } const T value; }; static QString strForStatus( OSStatus os ) { const Releaser str( SecCopyErrorMessageString( os, 0 ) ); const char * const buf = CFStringGetCStringPtr( str.value, kCFStringEncodingUTF8 ); if ( !buf ) return QString(); return QString::fromUtf8( buf, strlen( buf ) ); } static OSStatus readPw( QByteArray* pw, const QString& service, const QString& account, SecKeychainItemRef* ref ) { Q_ASSERT( pw ); pw->clear(); const QByteArray serviceData = service.toUtf8(); const QByteArray accountData = account.toUtf8(); void* data = 0; UInt32 len = 0; const OSStatus ret = SecKeychainFindGenericPassword( NULL, // default keychain serviceData.size(), serviceData.constData(), accountData.size(), accountData.constData(), &len, &data, ref ); if ( ret == noErr ) { *pw = QByteArray( reinterpret_cast( data ), len ); const OSStatus ret2 = SecKeychainItemFreeContent ( 0, data ); if ( ret2 != noErr ) qWarning() << "Could not free item content: " << strForStatus( ret2 ); } return ret; } void ReadPasswordJobPrivate::scheduledStart() { QString errorString; Error error = NoError; const OSStatus ret = readPw( &data, q->service(), q->key(), 0 ); switch ( ret ) { case noErr: break; case errSecItemNotFound: errorString = tr("Password not found"); error = EntryNotFound; break; default: errorString = strForStatus( ret ); error = OtherError; break; } q->emitFinishedWithError( error, errorString ); } static QKeychain::Error deleteEntryImpl( const QString& service, const QString& account, QString* err ) { SecKeychainItemRef ref; QByteArray pw; const OSStatus ret1 = readPw( &pw, service, account, &ref ); if ( ret1 == errSecItemNotFound ) return NoError; // No item stored, we're done if ( ret1 != noErr ) { *err = strForStatus( ret1 ); //TODO map error code, set errstr return OtherError; } const Releaser releaser( ref ); const OSStatus ret2 = SecKeychainItemDelete( ref ); if ( ret2 == noErr ) return NoError; //TODO map error code *err = strForStatus( ret2 ); return CouldNotDeleteEntry; } static QKeychain::Error writeEntryImpl( const QString& service, const QString& account, const QByteArray& data, QString* err ) { Q_ASSERT( err ); err->clear(); const QByteArray serviceData = service.toUtf8(); const QByteArray accountData = account.toUtf8(); const OSStatus ret = SecKeychainAddGenericPassword( NULL, //default keychain serviceData.size(), serviceData.constData(), accountData.size(), accountData.constData(), data.size(), data.constData(), NULL //item reference ); if ( ret != noErr ) { switch ( ret ) { case errSecDuplicateItem: { Error derr = deleteEntryImpl( service, account, err ); if ( derr != NoError ) return CouldNotDeleteEntry; else return writeEntryImpl( service, account, data, err ); } default: *err = strForStatus( ret ); return OtherError; } } return NoError; } void WritePasswordJobPrivate::scheduledStart() { QString errorString; Error error = NoError; if ( mode == Delete ) { const Error derr = deleteEntryImpl( q->service(), key, &errorString ); if ( derr != NoError ) error = CouldNotDeleteEntry; q->emitFinishedWithError( error, errorString ); return; } const QByteArray data = mode == Text ? textData.toUtf8() : binaryData; error = writeEntryImpl( q->service(), key, data, &errorString ); q->emitFinishedWithError( error, errorString ); } qtkeychain-0.1/QtKeychainConfig.cmake.in0000664000175000017500000000142612077636342016136 0ustar jrjr# - Config file for the QtKeychain package # It defines the following variables # QTKEYCHAIN_INCLUDE_DIRS - include directories for QtKeychain # QTKEYCHAIN_LIBRARIES - libraries to link against # Compute paths get_filename_component(QTKEYCHAIN_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) if(EXISTS "${QTKEYCHAIN_CMAKE_DIR}/CMakeCache.txt") # In build tree include("${QTKEYCHAIN_CMAKE_DIR}/QtKeychainBuildTreeSettings.cmake") else() set(QTKEYCHAIN_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@") endif() # Our library dependencies (contains definitions for IMPORTED targets) include("${QTKEYCHAIN_CMAKE_DIR}/QtKeychainLibraryDepends.cmake") # These are IMPORTED targets created by FooBarLibraryDepends.cmake set(QTKEYCHAIN_LIBRARIES qtkeychain) set(QTKEYCHAIN_FOUND TRUE) qtkeychain-0.1/testclient.cpp0000664000175000017500000000714512077636342014227 0ustar jrjr/****************************************************************************** * Copyright (C) 2011 Frank Osterfeld * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * * details, check the accompanying file 'COPYING'. * *****************************************************************************/ #include #include #include "keychain.h" #include using namespace QKeychain; static int printUsage() { std::cerr << "testclient store " << std::endl; std::cerr << "testclient restore " << std::endl; std::cerr << "testclient delete " << std::endl; return 1; } int main( int argc, char** argv ) { QCoreApplication app( argc, argv ); const QStringList args = app.arguments(); if ( args.count() < 2 ) return printUsage(); QStringList::ConstIterator it = args.constBegin(); ++it; if ( *it == QLatin1String("store") ) { if ( ++it == args.constEnd() ) return printUsage(); const QString acc = *it; if ( ++it == args.constEnd() ) return printUsage(); const QString pass = *it; if ( ++it != args.constEnd() ) return printUsage(); WritePasswordJob job( QLatin1String("qtkeychain-testclient") ); job.setAutoDelete( false ); job.setKey( acc ); job.setTextData( pass ); QEventLoop loop; job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); job.start(); loop.exec(); if ( job.error() ) { std::cerr << "Storing password failed: " << qPrintable(job.errorString()) << std::endl; return 1; } std::cout << "Password stored successfully" << std::endl; } else if ( *it == QLatin1String("restore") ) { if ( ++it == args.constEnd() ) return printUsage(); const QString acc = *it; if ( ++it != args.constEnd() ) return printUsage(); ReadPasswordJob job( QLatin1String("qtkeychain-testclient") ); job.setAutoDelete( false ); job.setKey( acc ); QEventLoop loop; job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); job.start(); loop.exec(); const QString pw = job.textData(); if ( job.error() ) { std::cerr << "Restoring password failed: " << qPrintable(job.errorString()) << std::endl; return 1; } std::cout << qPrintable(pw) << std::endl; } else if ( *it == QLatin1String("delete") ) { if ( ++it == args.constEnd() ) return printUsage(); const QString acc = *it; if ( ++it != args.constEnd() ) return printUsage(); DeletePasswordJob job( QLatin1String("qtkeychain-testclient") ); job.setAutoDelete( false ); job.setKey( acc ); QEventLoop loop; job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); job.start(); loop.exec(); if ( job.error() ) { std::cerr << "Deleting password failed: " << qPrintable(job.errorString()) << std::endl; return 1; } std::cout << "Password deleted successfully" << std::endl; } else { return printUsage(); } } qtkeychain-0.1/COPYING0000664000175000017500000000225012077636342012370 0ustar jrjrRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. qtkeychain-0.1/keychain_p.h0000664000175000017500000000753612077636342013634 0ustar jrjr/****************************************************************************** * Copyright (C) 2011 Frank Osterfeld * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * * details, check the accompanying file 'COPYING'. * *****************************************************************************/ #ifndef KEYCHAIN_P_H #define KEYCHAIN_P_H #include #include #include #include #include #if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) #include #include "kwallet_interface.h" #else class QDBusPendingCallWatcher; #endif #include "keychain.h" namespace QKeychain { class JobExecutor; class JobPrivate : public QObject { Q_OBJECT public: JobPrivate( const QString& service_ ) : error( NoError ) , service( service_ ) , autoDelete( true ) , insecureFallback( false ) {} QKeychain::Error error; QString errorString; QString service; bool autoDelete; bool insecureFallback; QPointer settings; }; class ReadPasswordJobPrivate : public QObject { Q_OBJECT public: explicit ReadPasswordJobPrivate( ReadPasswordJob* qq ) : q( qq ), walletHandle( 0 ), dataType( Text ) {} void scheduledStart(); ReadPasswordJob* const q; QByteArray data; QString key; int walletHandle; enum DataType { Binary, Text }; DataType dataType; #if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) org::kde::KWallet* iface; friend class QKeychain::JobExecutor; private Q_SLOTS: void kwalletOpenFinished( QDBusPendingCallWatcher* watcher ); void kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher ); void kwalletReadFinished( QDBusPendingCallWatcher* watcher ); #else //moc's too dumb to respect above macros, so just define empty slot implementations private Q_SLOTS: void kwalletOpenFinished( QDBusPendingCallWatcher* ) {} void kwalletEntryTypeFinished( QDBusPendingCallWatcher* ) {} void kwalletReadFinished( QDBusPendingCallWatcher* ) {} #endif }; class WritePasswordJobPrivate : public QObject { Q_OBJECT public: explicit WritePasswordJobPrivate( WritePasswordJob* qq ) : q( qq ), mode( Delete ) {} void scheduledStart(); enum Mode { Delete, Text, Binary }; WritePasswordJob* const q; Mode mode; QString key; QByteArray binaryData; QString textData; #if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) org::kde::KWallet* iface; friend class QKeychain::JobExecutor; private Q_SLOTS: void kwalletOpenFinished( QDBusPendingCallWatcher* watcher ); void kwalletWriteFinished( QDBusPendingCallWatcher* watcher ); #else private Q_SLOTS: void kwalletOpenFinished( QDBusPendingCallWatcher* ) {} void kwalletWriteFinished( QDBusPendingCallWatcher* ) {} #endif }; class DeletePasswordJobPrivate : public QObject { Q_OBJECT public: explicit DeletePasswordJobPrivate( DeletePasswordJob* qq ) : q( qq ) {} void doStart(); DeletePasswordJob* const q; QString key; private Q_SLOTS: void jobFinished( QKeychain::Job* ); }; class JobExecutor : public QObject { Q_OBJECT public: static JobExecutor* instance(); void enqueue( Job* job ); private: explicit JobExecutor(); void startNextIfNoneRunning(); private Q_SLOTS: void jobFinished( QKeychain::Job* ); void jobDestroyed( QObject* object ); private: static JobExecutor* s_instance; Job* m_runningJob; QVector > m_queue; }; } #endif // KEYCHAIN_P_H qtkeychain-0.1/QtKeychainBuildTreeSettings.cmake.in0000664000175000017500000000012012077636342020317 0ustar jrjrset(QTKEYCHAIN_INCLUDE_DIRS "@PROJECT_SOURCE_DIR@" "@PROJECT_BINARY_DIR@" ) qtkeychain-0.1/keychain_win.cpp0000664000175000017500000001044212077636342014513 0ustar jrjr/****************************************************************************** * Copyright (C) 2011 Frank Osterfeld * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * * details, check the accompanying file 'COPYING'. * *****************************************************************************/ #include "keychain_p.h" #include #include #include #include using namespace QKeychain; void ReadPasswordJobPrivate::scheduledStart() { //Use settings member if there, create local settings object if not std::auto_ptr local( !q->settings() ? new QSettings( q->service() ) : 0 ); QSettings* actual = q->settings() ? q->settings() : local.get(); QByteArray encrypted = actual->value( key ).toByteArray(); if ( encrypted.isNull() ) { q->emitFinishedWithError( EntryNotFound, tr("Entry not found") ); return; } DATA_BLOB blob_in, blob_out; blob_in.pbData = reinterpret_cast( encrypted.data() ); blob_in.cbData = encrypted.size(); const BOOL ret = CryptUnprotectData( &blob_in, NULL, NULL, NULL, NULL, 0, &blob_out ); if ( !ret ) { q->emitFinishedWithError( OtherError, tr("Could not decrypt data") ); return; } data = QByteArray( reinterpret_cast( blob_out.pbData ), blob_out.cbData ); SecureZeroMemory( blob_out.pbData, blob_out.cbData ); LocalFree( blob_out.pbData ); q->emitFinished(); } void WritePasswordJobPrivate::scheduledStart() { if ( mode == Delete ) { //Use settings member if there, create local settings object if not std::auto_ptr local( !q->settings() ? new QSettings( q->service() ) : 0 ); QSettings* actual = q->settings() ? q->settings() : local.get(); actual->remove( key ); actual->sync(); if ( actual->status() != QSettings::NoError ) { const QString err = actual->status() == QSettings::AccessError ? tr("Could not delete encrypted data from settings: access error") : tr("Could not delete encrypted data from settings: format error"); q->emitFinishedWithError( OtherError, err ); } else { q->emitFinished(); } return; } QByteArray data = mode == Binary ? binaryData : textData.toUtf8(); DATA_BLOB blob_in, blob_out; blob_in.pbData = reinterpret_cast( data.data() ); blob_in.cbData = data.size(); const BOOL res = CryptProtectData( &blob_in, L"QKeychain-encrypted data", NULL, NULL, NULL, 0, &blob_out ); if ( !res ) { q->emitFinishedWithError( OtherError, tr("Encryption failed") ); //TODO more details available? return; } const QByteArray encrypted( reinterpret_cast( blob_out.pbData ), blob_out.cbData ); LocalFree( blob_out.pbData ); //Use settings member if there, create local settings object if not std::auto_ptr local( !q->settings() ? new QSettings( q->service() ) : 0 ); QSettings* actual = q->settings() ? q->settings() : local.get(); actual->setValue( key, encrypted ); actual->sync(); if ( actual->status() != QSettings::NoError ) { const QString errorString = actual->status() == QSettings::AccessError ? tr("Could not store encrypted data in settings: access error") : tr("Could not store encrypted data in settings: format error"); q->emitFinishedWithError( OtherError, errorString ); return; } q->emitFinished(); }