Ticket #44473: use-osx-keychain.patch

File use-osx-keychain.patch, 61.4 KB (added by RJVB (René Bertin), 10 years ago)
  • kdelibs-4.12.5//kdeui/

    old new  
    309309
    310310if (Q_WS_MAC AND MAC_USE_OSXKEYCHAIN)
    311311    FIND_LIBRARY(SECURITY_LIBRARY Security)
    312     set(kdeui_LIB_SRCS ${kdeui_LIB_SRCS} util/kwallet_mac.cpp)
     312        set(kdeui_LIB_SRCS ${kdeui_LIB_SRCS} util/kwallet_mac.cpp util/qosxkeychain.cpp)
     313           add_definitions(-DMAC_USE_OSXKEYCHAIN)
     314    else(Q_WS_MAC AND MAC_USE_OSXKEYCHAIN)
     315         set(kdeui_LIB_SRCS ${kdeui_LIB_SRCS} util/kwallet.cpp)
    313316else(Q_WS_MAC AND MAC_USE_OSXKEYCHAIN)
    314      set(kdeui_LIB_SRCS ${kdeui_LIB_SRCS} util/kwallet.cpp)
    315 else(Q_WS_MAC AND MAC_USE_OSXKEYCHAIN)
    316   set(kdeui_LIB_SRCS ${kdeui_LIB_SRCS} util/kwallet.cpp)
     317    set(kdeui_LIB_SRCS ${kdeui_LIB_SRCS} util/kwallet.cpp)
    317318endif(Q_WS_MAC AND MAC_USE_OSXKEYCHAIN)
    318319
    319320if(NOT WINCE)
  • kdelibs-4.12.5/kdeui/util/

    old new  
    1 /* This file is part of the KDE project
     1/* @file kwallet_mac.cpp
     2 * This file is part of the KDE project
    23 *
    34 * Copyright (C) 2002-2004 George Staikos <staikos@kde.org>
    45 * Copyright (C) 2008 Michael Leupold <lemma@confuego.org>
    56 * Copyright (C) 2010 Frank Osterfeld <osterfeld@kde.org>
     7 * Copyright (C) 2014 René Bertin <rjvbertin@gmail.com>
    68 *
    79 * This library is free software; you can redistribute it and/or
    810 * modify it under the terms of the GNU Library General Public
     
    3638
    3739#include <cassert>
    3840
    39 #include <Carbon/Carbon.h>
    40 #include <Security/Security.h>
    41 #include <Security/SecKeychain.h>
     41#include <sys/param.h>
     42
     43#include "qosxkeychain.h"
    4244
    4345using namespace KWallet;
    4446
     
    4951typedef QMap<QString, QByteArray> StringByteArrayMap;
    5052Q_DECLARE_METATYPE(StringByteArrayMap)
    5153
    52 namespace {
    53     template <typename T>
    54     struct CFReleaser {
    55         explicit CFReleaser( const T& r ) : ref( r ) {}
    56         ~CFReleaser() { CFRelease( ref ); }
    57         T ref;
    58     };
    59 }
    60 
    61 static QString asQString( CFStringRef sr ) {
    62     return QString::fromLatin1( CFStringGetCStringPtr( sr, NULL ) ); //TODO Latin1 correct?
    63 }
    64 
    65 static QString errorString( OSStatus s ) {
    66     const CFReleaser<CFStringRef> ref( SecCopyErrorMessageString( s, NULL ) );
    67     return asQString( ref.ref );
    68 }
    69 
    70 static bool isError( OSStatus s, QString* errMsg ) {
    71     if ( errMsg )
    72         *errMsg = errorString( s );
    73     return s != 0;
    74 }
    75 
     54#ifdef OSX_KEYCHAIN_PORT_DISABLED
    7655static QString appid()
    7756{
    7857    KComponentData cData = KGlobal::mainComponent();
     
    8564    }
    8665    return qApp->applicationName();
    8766}
     67#endif
    8868
    89 static OSStatus removeEntryImplementation(const QString& walletName, const QString& key) {
    90     const QByteArray serviceName( walletName.toUtf8() );
    91     const QByteArray accountName( key.toUtf8() );
    92     SecKeychainItemRef itemRef;
    93     QString errMsg;
    94     OSStatus result = SecKeychainFindGenericPassword( NULL, serviceName.size(), serviceName.constData(), accountName.size(), accountName.constData(), NULL, NULL, &itemRef );
    95     if ( isError( result, &errMsg ) ) {
    96         qWarning() << "Could not retrieve password:"  << qPrintable(errMsg);
    97         return result;
    98     }
    99     const CFReleaser<SecKeychainItemRef> itemReleaser( itemRef );
    100     result = SecKeychainItemDelete( itemRef );
    101     if ( isError( result, &errMsg ) ) {
    102         qWarning() << "Could not delete password:"  << qPrintable(errMsg);
    103         return result;
    104     }
    105     return result;
    106 }
    107 
    108 
    109 const QString Wallet::LocalWallet() {
     69/*static*/ const QString Wallet::LocalWallet()
     70{
    11071    KConfigGroup cfg(KSharedConfig::openConfig("kwalletrc")->group("Wallet"));
    11172    if (!cfg.readEntry("Use One Wallet", true)) {
    11273        QString tmp = cfg.readEntry("Local Wallet", "localwallet");
     
    12384    return tmp;
    12485}
    12586
    126 const QString Wallet::NetworkWallet() {
     87/*static*/ const QString Wallet::NetworkWallet()
     88{
    12789    KConfigGroup cfg(KSharedConfig::openConfig("kwalletrc")->group("Wallet"));
    12890
    12991    QString tmp = cfg.readEntry("Default Wallet", "kdewallet");
     
    13395    return tmp;
    13496}
    13597
    136 const QString Wallet::PasswordFolder() {
     98/*static*/ const QString Wallet::PasswordFolder()
     99{
    137100    return "Passwords";
    138101}
    139102
    140 const QString Wallet::FormDataFolder() {
     103/*static*/ const QString Wallet::FormDataFolder()
     104{
    141105    return "Form Data";
    142106}
    143107
    144 class Wallet::WalletPrivate
     108#pragma mark ==== Wallet::WalletPrivate ====
     109class Wallet::WalletPrivate : public OSXKeychain
    145110{
    146111public:
    147112    explicit WalletPrivate(const QString &n)
    148      : name(n)
    149     {}
     113        : OSXKeychain(n)
     114    {
     115        isKDEChain = ( n == LocalWallet() || n == NetworkWallet() || n.contains( "wallet", Qt::CaseInsensitive ) );
     116    }
    150117
    151118    // needed for compilation reasons
    152     void walletServiceUnregistered() {
     119    void walletServiceUnregistered()
     120    {
    153121    }
    154 
    155     QString name;
    156     QString folder;
    157122};
    158123
    159124Wallet::Wallet(int handle, const QString& name)
    160     : QObject(0L), d(new WalletPrivate(name)) {
     125    : QObject(0L), d(new WalletPrivate(name))
     126{
    161127    Q_UNUSED(handle);
    162128}
    163129
    164 Wallet::~Wallet() {
     130Wallet::~Wallet()
     131{
    165132    delete d;
    166133}
    167134
    168 
    169 QStringList Wallet::walletList() {
     135/*static*/ QStringList Wallet::walletList()
     136{
    170137#ifdef OSX_KEYCHAIN_PORT_DISABLED
    171138    return walletLauncher->getInterface().wallets();
    172139#else
    173     return QStringList();
     140    // RJVB: Mac OS X's Keychain supports multiple keychains, but they can only be accesses by full path, not
     141    // found by name. That makes it cumbersome to map to multiple wallets when using only the wallet name.
     142    // However, it would be perfectly possible to create OS X Keychains called Wallet::LocalWallet() and
     143    // Wallet::NetworkWallet() in the equivalent of ~/.kde/share/apps/kwallet .
     144    QStringList l;
     145    OSXKeychain::KeychainList(l);
     146    return l;
    174147#endif
    175148}
    176149
    177150
    178 void Wallet::changePassword(const QString& name, WId w) {
     151/*static*/ void Wallet::changePassword(const QString& name, WId w)
     152{
    179153#ifdef OSX_KEYCHAIN_PORT_DISABLED
    180154    if( w == 0 )
    181155        kDebug(285) << "Pass a valid window to KWallet::Wallet::changePassword().";
    182156    walletLauncher->getInterface().changePassword(name, (qlonglong)w, appid());
     157#else
     158    Q_UNUSED(w);
     159    kWarning() << "Wallet::changePassword unimplemented '" << name << "'";
    183160#endif
    184161}
    185162
    186163
    187 bool Wallet::isEnabled() {
     164/*static*/ bool Wallet::isEnabled()
     165{
    188166    //PENDING(frank) check
    189167    return true;
    190168}
    191169
    192170
    193 bool Wallet::isOpen(const QString& name) {
     171/*static*/ bool Wallet::isOpen(const QString& name)
     172{
    194173#ifdef OSX_KEYCHAIN_PORT_DISABLED
    195174    return walletLauncher->getInterface().isOpen(name); // default is false
    196175#else
    197     return true;
     176    return OSXKeychain::IsOpen(name);
     177#endif
     178}
     179
     180bool Wallet::isOpen() const
     181{
     182#ifdef OSX_KEYCHAIN_PORT_DISABLED
     183    return d->handle != -1;
     184#else
     185    return d->isOpen();
    198186#endif
    199187}
    200188
    201189
    202 int Wallet::closeWallet(const QString& name, bool force) {
     190/*static*/ int Wallet::closeWallet(const QString& name, bool force)
     191{
    203192#ifdef OSX_KEYCHAIN_PORT_DISABLED
    204193    QDBusReply<int> r = walletLauncher->getInterface().close(name, force);
    205194    return r.isValid() ? r : -1;
    206195#else
    207     return 0;
     196    Q_UNUSED(force);
     197    return OSXKeychain::Lock(name);
    208198#endif
    209199}
    210200
    211201
    212 int Wallet::deleteWallet(const QString& name) {
     202/*static*/ int Wallet::deleteWallet(const QString& name)
     203{
    213204#ifdef OSX_KEYCHAIN_PORT_DISABLED
    214205    QDBusReply<int> r = walletLauncher->getInterface().deleteWallet(name);
    215206    return r.isValid() ? r : -1;
    216207#else
    217     return -1;
     208    return OSXKeychain::Destroy(name);
    218209#endif
    219210}
    220211
    221212
    222 Wallet *Wallet::openWallet(const QString& name, WId w, OpenType ot) {
     213/*static*/ Wallet *Wallet::openWallet(const QString& name, WId w, OpenType ot)
     214{
    223215    Q_UNUSED(w);
    224216    Q_UNUSED(ot);
    225217    Wallet *wallet = new Wallet(-1, name);
    226218    QMetaObject::invokeMethod( wallet, "emitWalletOpened", Qt::QueuedConnection );
     219    OSStatus err = wallet->d->unLock();
     220    kDebug() << "Opened wallet '" << name << "': " << wallet << " error=" << err;
    227221    return wallet;
    228222}
    229223
    230224
    231 bool Wallet::disconnectApplication(const QString& wallet, const QString& app) {
     225/*static*/ bool Wallet::disconnectApplication(const QString& wallet, const QString& app)
     226{
    232227#ifdef OSX_KEYCHAIN_PORT_DISABLED
    233228    return walletLauncher->getInterface().disconnectApplication(wallet, app); // default is false
    234229#else
     230    kWarning() << "Wallet::disconnectApplication unimplemented, '" << app << "' from '" << wallet << "'";
    235231    return true;
    236232#endif
    237233}
    238234
    239235
    240 QStringList Wallet::users(const QString& name) {
     236/*static*/ QStringList Wallet::users(const QString& name)
     237{
    241238#ifdef OSX_KEYCHAIN_PORT_DISABLED
    242239    return walletLauncher->getInterface().users(name); // default is QStringList()
    243240#else
     241    kWarning() << "Wallet::users unimplemented, '" << name << "'";
    244242    return QStringList();
    245243#endif
    246244}
    247245
    248246
    249 int Wallet::sync() {
     247int Wallet::sync()
     248{
    250249#ifdef OSX_KEYCHAIN_PORT_DISABLED
    251250    if (d->handle == -1) {
    252251        return -1;
     
    258257}
    259258
    260259
    261 int Wallet::lockWallet() {
     260int Wallet::lockWallet()
     261{
    262262#ifdef OSX_KEYCHAIN_PORT_DISABLED
    263263    if (d->handle == -1) {
    264264        return -1;
     
    271271    if (r.isValid()) {
    272272        return r;
    273273    }
     274#else
     275    d->currentService.clear();
    274276#endif
    275     return -1;
     277    return d->lock();
    276278}
    277279
    278280
    279 const QString& Wallet::walletName() const {
     281const QString& Wallet::walletName() const
     282{
    280283    return d->name;
    281284}
    282285
    283286
    284 bool Wallet::isOpen() const {
    285 #ifdef OSX_KEYCHAIN_PORT_DISABLED
    286     return d->handle != -1;
    287 #else
    288     return true;
    289 #endif
    290 }
    291 
    292 
    293 void Wallet::requestChangePassword(WId w) {
     287void Wallet::requestChangePassword(WId w)
     288{
    294289#ifdef OSX_KEYCHAIN_PORT_DISABLED
    295290    if( w == 0 )
    296291        kDebug(285) << "Pass a valid window to KWallet::Wallet::requestChangePassword().";
     
    299294    }
    300295
    301296    walletLauncher->getInterface().changePassword(d->name, (qlonglong)w, appid());
     297#else
     298    Q_UNUSED(w);
     299    kWarning() << "Wallet::requestChangePassword unimplemented '" << d->name << "'";
    302300#endif
    303301}
    304302
    305303
    306 void Wallet::slotWalletClosed(int handle) {
     304void Wallet::slotWalletClosed(int handle)
     305{
    307306#ifdef OSX_KEYCHAIN_PORT_DISABLED
    308307    if (d->handle == handle) {
    309308        d->handle = -1;
     
    311310        d->name.clear();
    312311        emit walletClosed();
    313312    }
     313#else
     314    Q_UNUSED(handle);
     315    kWarning() << "Wallet::slotWalletClosed unimplemented '" << d->name << "'";
     316    d->currentService.clear();
    314317#endif
    315318}
    316319
    317320
    318 QStringList Wallet::folderList() {
     321QStringList Wallet::folderList()
     322{
    319323#ifdef OSX_KEYCHAIN_PORT_DISABLED
    320324    if (d->handle == -1) {
    321325        return QStringList();
     
    324328    QDBusReply<QStringList> r = walletLauncher->getInterface().folderList(d->handle, appid());
    325329    return r;
    326330#else
    327     return QStringList();
     331    return QStringList(d->folderList());
    328332#endif
    329333}
    330334
    331335
    332 QStringList Wallet::entryList() {
     336QStringList Wallet::entryList()
     337{
    333338#ifdef OSX_KEYCHAIN_PORT_DISABLED
    334339    if (d->handle == -1) {
    335340        return QStringList();
     
    338343    QDBusReply<QStringList> r = walletLauncher->getInterface().entryList(d->handle, d->folder, appid());
    339344    return r;
    340345#else
    341     return QStringList();
     346    QStringList r = QStringList();
     347    d->itemList(r);
     348    return r;
    342349#endif
    343350}
    344351
    345352
    346 bool Wallet::hasFolder(const QString& f) {
     353bool Wallet::hasFolder(const QString& f)
     354{
    347355#ifdef OSX_KEYCHAIN_PORT_DISABLED
    348356    if (d->handle == -1) {
    349357        return false;
     
    352360    QDBusReply<bool> r = walletLauncher->getInterface().hasFolder(d->handle, f, appid());
    353361    return r; // default is false
    354362#else
    355     return true;
     363    d->folderList();
     364    return d->serviceList.contains(f);
    356365#endif
    357366}
    358367
    359368
    360 bool Wallet::createFolder(const QString& f) {
     369bool Wallet::createFolder(const QString& f)
     370{
    361371#ifdef OSX_KEYCHAIN_PORT_DISABLED
    362372    if (d->handle == -1) {
    363373        return false;
     
    370380
    371381    return true;                                // folder already exists
    372382#else
    373     return true;
     383    return setFolder(f);
    374384#endif
    375385}
    376386
    377387
    378 bool Wallet::setFolder(const QString& f) {
     388bool Wallet::setFolder(const QString &f)
     389{
    379390#ifdef OSX_KEYCHAIN_PORT_DISABLED
    380391    bool rc = false;
    381392
     
    397408
    398409    return rc;
    399410#else
     411    // act as if we just changed folders even if we have no such things; the property
     412    // is stored as the ServiceItemAttr (which shows up as the "Where" field in the Keychain Utility).
     413    if( f.size() == 0 ){
     414        d->currentService.clear();
     415    }
     416    else{
     417        d->currentService = QString(f);
     418    }
    400419    return true;
    401420#endif
    402421}
    403422
    404423
    405 bool Wallet::removeFolder(const QString& f) {
     424bool Wallet::removeFolder(const QString& f)
     425{
    406426#ifdef OSX_KEYCHAIN_PORT_DISABLED
    407427    if (d->handle == -1) {
    408428        return false;
     
    415435
    416436    return r;                                   // default is false
    417437#else
     438    kWarning() << "Wallet::removeFolder unimplemented (returns true) '" << d->name << "'";
     439    if( d->currentService == f ){
     440        d->currentService.clear();
     441    }
    418442    return true;
    419443#endif
    420444}
    421445
    422446
    423 const QString& Wallet::currentFolder() const {
     447const QString& Wallet::currentFolder() const
     448{
     449#ifdef OSX_KEYCHAIN_PORT_DISABLED
    424450    return d->folder;
     451#else
     452    return d->currentService;
     453#endif
    425454}
    426455
    427456
    428 int Wallet::readEntry(const QString& key, QByteArray& value) {
    429     const QByteArray serviceName( walletName().toUtf8() );
    430     const QByteArray accountName( key.toUtf8() );
    431     UInt32 passwordSize = 0;
    432     void* passwordData = 0;
    433     QString errMsg;
    434     if ( isError( SecKeychainFindGenericPassword( NULL, serviceName.size(), serviceName.constData(), accountName.size(), accountName.constData(), &passwordSize, &passwordData, NULL ), &errMsg ) ) {
    435         qWarning() << "Could not retrieve password:"  << qPrintable(errMsg);
    436         return -1;
    437     }
    438 
    439     value = QByteArray( reinterpret_cast<const char*>( passwordData ), passwordSize );
    440     SecKeychainItemFreeContent( NULL, passwordData );
    441     return 0;
     457int Wallet::readEntry(const QString &key, QByteArray &value)
     458{   OSStatus err = d->readItem( key, &value, NULL );
     459    kDebug() << "Wallet::readEntry '" << key << "' from wallet " << d->name << ", error=" << ((err)? -1 : 0);
     460    return (err)? -1 : 0;
    442461}
    443462
    444463
    445 int Wallet::readEntryList(const QString& key, QMap<QString, QByteArray>& value) {
     464int Wallet::readEntryList(const QString& key, QMap<QString, QByteArray>& value)
     465{
    446466#ifdef OSX_KEYCHAIN_PORT_DISABLED
    447467    registerTypes();
    448468
     
    464484
    465485    return rc;
    466486#else
     487    Q_UNUSED(key);
     488    Q_UNUSED(value);
     489    kWarning() << "Wallet::readEntryList unimplemented (returns -1) '" << d->name << "'";
    467490    return -1;
    468491#endif
    469492}
    470493
    471494
    472 int Wallet::renameEntry(const QString& oldName, const QString& newName) {
     495int Wallet::renameEntry(const QString& oldName, const QString& newName)
     496{
    473497#ifdef OSX_KEYCHAIN_PORT_DISABLED
    474498    int rc = -1;
    475499
     
    484508
    485509    return rc;
    486510#else
    487     return -1;
     511    return d->renameItem( oldName, newName );
    488512#endif
    489513}
    490514
    491515
    492 int Wallet::readMap(const QString& key, QMap<QString,QString>& value) {
     516int Wallet::readMap(const QString &key, QMap<QString,QString> &value)
     517{
    493518    QByteArray v;
    494     const int ret = readEntry( key, v );
    495     if ( ret != 0 )
     519    const int ret = (d->readItem( key, &v, NULL ))? -1 : 0;
     520    if( ret != 0 ){
    496521        return ret;
    497     if ( !v.isEmpty() ) {
    498         QDataStream ds( &v, QIODevice::ReadOnly );
     522    }
     523    if( !v.isEmpty() ){
     524        QByteArray w = QByteArray::fromBase64(v);
     525        QDataStream ds( &w, QIODevice::ReadOnly );
    499526        ds >> value;
    500527    }
     528    kDebug() << "Wallet::readMap '" << key << "' from wallet " << d->name << ", error=0";
    501529    return 0;
    502530}
    503531
    504532
    505 int Wallet::readMapList(const QString& key, QMap<QString, QMap<QString, QString> >& value) {
     533int Wallet::readMapList(const QString& key, QMap<QString, QMap<QString, QString> >& value)
     534{
    506535#ifdef OSX_KEYCHAIN_PORT_DISABLED
    507536    registerTypes();
    508537
     
    530559
    531560    return rc;
    532561#else
     562    Q_UNUSED(key);
     563    Q_UNUSED(value);
     564    kWarning() << "Wallet::readMapList unimplemented (returns -1) '" << d->name << "'";
    533565    return -1;
    534566#endif
    535567}
    536568
    537569
    538 int Wallet::readPassword(const QString& key, QString& value) {
     570int Wallet::readPassword(const QString& key, QString& value)
     571{
    539572    QByteArray ba;
    540     const int ret = readEntry( key, ba );
    541     if ( ret == 0 )
     573    const int ret = (d->readItem( key, &ba, NULL ))? -1 : 0;
     574    if ( ret == 0 ){
    542575        value = QString::fromUtf8( ba.constData() );
     576    }
     577    kDebug() << "Wallet::readPassword '" << key << "' from wallet " << d->name << ", error=" << ret;
    543578    return ret;
    544579}
    545580
    546581
    547 int Wallet::readPasswordList(const QString& key, QMap<QString, QString>& value) {
     582int Wallet::readPasswordList(const QString& key, QMap<QString, QString>& value)
     583{
     584    Q_UNUSED(key);
     585    Q_UNUSED(value);
     586    kWarning() << "Wallet::readPasswordList unimplemented (returns -1) '" << d->name << "'";
    548587    return -1;
    549588}
    550589
    551 static OSStatus writeEntryImplementation( const QString& walletName, const QString& key, const QByteArray& value ) {
    552     const QByteArray serviceName( walletName.toUtf8() );
    553     const QByteArray accountName( key.toUtf8() );
    554     QString errMsg;
    555     OSStatus err = SecKeychainAddGenericPassword( NULL, serviceName.size(), serviceName.constData(), accountName.size(), accountName.constData(), value.size(), value.constData(), NULL );
    556     if (err == errSecDuplicateItem) {
    557         err = removeEntryImplementation( walletName, key );
    558         if ( isError( err, &errMsg ) ) {
    559             kWarning() << "Could not delete old key in keychain for replacing: " << qPrintable(errMsg);
    560             return err;
    561         }
    562     }
    563     if ( isError( err, &errMsg ) ) {
    564         kWarning() << "Could not store password in keychain: " << qPrintable(errMsg);
    565         return err;
    566     }
    567     kDebug() << "Succesfully written out key:" << key;
    568     return err;
    569 
    570 }
    571 
    572 int Wallet::writeEntry(const QString& key, const QByteArray& password, EntryType entryType) {
    573     Q_UNUSED( entryType )
    574     return writeEntryImplementation( walletName(), key, password );
     590int Wallet::writeEntry(const QString& key, const QByteArray& password )
     591{   int ret = d->writeItem( key, password );
     592    kDebug() << "wrote entry '" << key << "' to wallet " << d->name << ", error=" << ret;
     593    return ret;
    575594}
    576595
    577 
    578 int Wallet::writeEntry(const QString& key, const QByteArray& value) {
    579     return writeEntryImplementation( walletName(), key, value );
     596int Wallet::writeEntry(const QString& key, const QByteArray& password, EntryType entryType)
     597{
     598    OSXKeychain::EntryType entryCode;
     599        switch( entryType ){
     600                case Wallet::Password:
     601                        entryCode = OSXKeychain::Password;
     602                        break;
     603                case Wallet::Map:
     604                        entryCode = OSXKeychain::Map;
     605                        break;
     606        case Wallet::Stream:
     607            entryCode = OSXKeychain::Stream;
     608            break;
     609                default:
     610                        entryCode = OSXKeychain::Unknown;
     611                        break;
     612        }
     613        int ret = d->writeItem( key, password, &entryCode );
     614    kDebug() << "wrote entry '" << key << "' of type=" << (int) entryType << "to wallet " << d->name << ", error=" << ret;
     615    return ret;
    580616}
    581617
    582 
    583 int Wallet::writeMap(const QString& key, const QMap<QString,QString>& value) {
     618int Wallet::writeMap(const QString& key, const QMap<QString,QString>& value)
     619{
    584620    QByteArray mapData;
    585621    QDataStream ds(&mapData, QIODevice::WriteOnly);
    586622    ds << value;
    587     return writeEntry( key, mapData );
     623    OSXKeychain::EntryType etype = OSXKeychain::Map;
     624    int ret = d->writeItem( key, mapData.toBase64(),
     625                           "This is a KDE Wallet::Map item. Its password\n"
     626                           "cannot be read in the OS X Keychain Utility.\n"
     627                           "Use KDE's own kwalletmanager for that.", &etype );
     628    kDebug() << "wrote map '" << key << "' to wallet " << d->name << ", error=" << ret;
     629    return ret;
    588630}
    589631
    590632
    591 int Wallet::writePassword(const QString& key, const QString& value) {
    592     return writeEntry( key, value.toUtf8() );
     633int Wallet::writePassword(const QString &key, const QString& value)
     634{   OSXKeychain::EntryType etype = OSXKeychain::Password;
     635    int ret = d->writeItem( key, value.toUtf8(), &etype );
     636    kDebug() << "wrote password '" << key << "' to wallet " << d->name << ", error=" << ret;
     637    return ret;
    593638}
    594639
    595640
    596 bool Wallet::hasEntry(const QString& key) {
    597     const QByteArray serviceName( walletName().toUtf8() );
    598     const QByteArray accountName( key.toUtf8() );
    599     return !isError( SecKeychainFindGenericPassword( NULL, serviceName.size(), serviceName.constData(), accountName.size(), accountName.constData(), NULL, NULL, NULL ), 0 );
     641bool Wallet::hasEntry(const QString &key)
     642{   bool ret = d->hasItem( key, NULL );
     643    kDebug() << "wallet '" << d->name << "'" << ((ret)? " has" : " does not have") << " entry '" << key << "'";
     644    return ret;
    600645}
    601646
    602 int Wallet::removeEntry(const QString& key) {
    603     return removeEntryImplementation( walletName(), key );
     647int Wallet::removeEntry(const QString& key)
     648{   int ret = d->removeItem( key );
     649    kDebug() << "removed entry '" << key << "' from wallet " << d->name << ", error=" << ret;
     650    return ret;
    604651}
    605652
    606653
    607 Wallet::EntryType Wallet::entryType(const QString& key) {
     654Wallet::EntryType Wallet::entryType(const QString& key)
     655{
    608656#ifdef OSX_KEYCHAIN_PORT_DISABLED
    609657    int rc = 0;
    610658
     
    619667
    620668    return static_cast<EntryType>(rc);
    621669#else
     670    // RJVB: a priori, entries are always 'password' on OS X, but since we also do use them for storing
     671    // maps, it may be best to return Wallet::Unknown to leave some uncertainty and not mislead our caller.
     672    OSXKeychain::EntryType etype;
     673    if( !d->itemType( key, &etype ) ){
     674        switch( etype ){
     675            case OSXKeychain::Password:
     676                return Wallet::Password;
     677                break;
     678            case OSXKeychain::Map:
     679                return Wallet::Map;
     680                break;
     681            case OSXKeychain::Stream:
     682                return Wallet::Stream;
     683                break;
     684        }
     685    }
    622686    return Wallet::Unknown;
    623687#endif
    624688}
    625689
    626690
    627 void Wallet::slotFolderUpdated(const QString& wallet, const QString& folder) {
     691void Wallet::slotFolderUpdated(const QString& wallet, const QString& folder)
     692{
    628693    if (d->name == wallet) {
    629694        emit folderUpdated(folder);
    630695    }
    631696}
    632697
    633698
    634 void Wallet::slotFolderListUpdated(const QString& wallet) {
     699void Wallet::slotFolderListUpdated(const QString& wallet)
     700{
    635701    if (d->name == wallet) {
    636702        emit folderListUpdated();
    637703    }
    638704}
    639705
    640706
    641 void Wallet::slotApplicationDisconnected(const QString& wallet, const QString& application) {
     707void Wallet::slotApplicationDisconnected(const QString& wallet, const QString& application)
     708{
    642709#ifdef OSX_KEYCHAIN_PORT_DISABLED
    643710    if (d->handle >= 0
    644711        && d->name == wallet
    645712        && application == appid()) {
    646713        slotWalletClosed(d->handle);
    647714    }
     715#else
     716    Q_UNUSED(wallet);
     717    Q_UNUSED(application);
     718        kWarning() << "Wallet::slotApplicationDisconnected unimplemented '" << d->name << "'";
    648719#endif
    649720}
    650721
    651 void Wallet::walletAsyncOpened(int tId, int handle) {
     722void Wallet::walletAsyncOpened(int tId, int handle)
     723{
    652724#ifdef OSX_KEYCHAIN_PORT_DISABLED
    653725    // ignore responses to calls other than ours
    654726    if (d->transactionId != tId || d->handle != -1) {
    655727        return;
    656728    }
    657    
     729
    658730    // disconnect the async signal
    659731    disconnect(this, SLOT(walletAsyncOpened(int,int)));
    660    
     732
    661733    d->handle = handle;
    662734    emit walletOpened(handle > 0);
     735#else
     736    Q_UNUSED(tId);
     737    Q_UNUSED(handle);
     738        kWarning() << "Wallet::walletAsyncOpened unimplemented '" << d->name << "'";
    663739#endif
    664740}
    665741
    666 void Wallet::emitWalletAsyncOpenError() {
     742void Wallet::emitWalletAsyncOpenError()
     743{
    667744    emit walletOpened(false);
    668745}
    669746
    670 void Wallet::emitWalletOpened() {
     747void Wallet::emitWalletOpened()
     748{
    671749  emit walletOpened(true);
    672750}
    673751
     
    678756    QDBusReply<bool> r = walletLauncher->getInterface().folderDoesNotExist(wallet, folder);
    679757    return r;
    680758#else
    681     return false;
     759    bool ret = true;
     760    if( Wallet::walletList().contains(wallet) ){
     761        ret = !Wallet(-1, wallet).hasFolder(folder);
     762    }
     763    return ret;
    682764#endif
    683765}
    684766
     
    689771    QDBusReply<bool> r = walletLauncher->getInterface().keyDoesNotExist(wallet, folder, key);
    690772    return r;
    691773#else
    692     return false;
     774    bool ret = true;
     775    if( Wallet::walletList().contains(wallet) ){
     776        Wallet w(-1, wallet);
     777        if( w.hasFolder(folder) ){
     778            ret = !w.hasEntry(key);
     779        }
     780    }
     781    return ret;
    693782#endif
    694783}
    695784
    696785void Wallet::slotCollectionStatusChanged(int status)
    697786{
     787    Q_UNUSED(status);
     788        kWarning() << "Wallet::slotCollectionStatusChanged unimplemented '" << d->name << "' status=" << status;
    698789}
    699790
    700791void Wallet::slotCollectionDeleted()
    701792{
     793#ifdef OSX_KEYCHAIN_PORT_DISABLED
    702794    d->folder.clear();
    703     d->name.clear();
     795#else
     796    d->currentService.clear();
     797#endif
     798    kDebug() << "Wallet::slotCollectionDeleted: closing private data '" << d->name;
     799    d->close();
    704800    emit walletClosed();
    705801}
    706802
    707803
    708 void Wallet::virtual_hook(int, void*) {
     804void Wallet::virtual_hook(int, void*)
     805{
    709806    //BASE::virtual_hook( id, data );
    710807}
    711808
  • kdelibs-4.12.5/kdeui/util/

    old new  
     1/*
     2 *  @file qosxkeychain.h
     3 *  This file is part of the KDE project
     4 *
     5 *  Created by René J.V. Bertin on 20140809.
     6 *  Copyright 2014 RJVB.
     7 *
     8 * This library is free software; you can redistribute it and/or
     9 * modify it under the terms of the GNU Library General Public
     10 * License as published by the Free Software Foundation; either
     11 * version 2 of the License, or (at your option) any later version.
     12 *
     13 * This library is distributed in the hope that it will be useful,
     14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16 * Library General Public License for more details.
     17 *
     18 * You should have received a copy of the GNU Library General Public License
     19 * along with this library; see the file COPYING.LIB.  If not, write to
     20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     21 * Boston, MA 02110-1301, USA.
     22 */
     23
     24#include <Security/Security.h>
     25#include <Security/SecKeychain.h>
     26
     27namespace {
     28    template <typename T>
     29    struct CFReleaser {
     30        explicit CFReleaser( const T& r ) : ref( r ) {}
     31        ~CFReleaser() { if( ref ){ CFRelease( ref ); } }
     32        T ref;
     33    };
     34
     35    template <typename T>
     36    struct CPPDeleter {
     37        explicit CPPDeleter( const T& r ) : ptr( r ) {}
     38        ~CPPDeleter() { if( ptr ){ delete ptr; } }
     39        T ptr;
     40    };
     41
     42    template <typename T>
     43    struct CPPArrayDeleter {
     44        explicit CPPArrayDeleter( const T& r ) : ptr( r ) {}
     45        ~CPPArrayDeleter() { if( ptr ){ delete[] ptr; } }
     46        T ptr;
     47    };
     48
     49    template <typename T>
     50    struct CacheOldValue {
     51        explicit CacheOldValue( T &var, const T newVal )
     52            : oldVal(var), varRef(var)
     53        {
     54            var = newVal;
     55        }
     56        ~CacheOldValue()
     57        {
     58            varRef = oldVal;
     59        }
     60        T oldVal, &varRef;
     61    };
     62}
     63
     64static inline QString asQString( CFStringRef sr )
     65{   CFIndex len = CFStringGetLength(sr)*2;
     66    const CPPArrayDeleter<char*> buff(new char[len]);
     67    if( CFStringGetCString( sr, buff.ptr, len, kCFStringEncodingUTF8 ) ){
     68        return QString::fromUtf8(buff.ptr); //RJVB: use UTF8
     69    }
     70    else if( CFStringGetCString( sr, buff.ptr, len, kCFStringEncodingNonLossyASCII ) ){
     71        return QString::fromLocal8Bit(buff.ptr);
     72    }
     73    else{
     74        CFStringGetCString( sr, buff.ptr, len, NULL );
     75        return QString::fromLatin1(buff.ptr);
     76    }
     77}
     78
     79static inline QString errorString( OSStatus s )
     80{
     81    const CFReleaser<CFStringRef> ref( SecCopyErrorMessageString( s, NULL ) );
     82    return asQString( ref.ref );
     83}
     84
     85static inline bool isError( OSStatus s, QString *errMsg )
     86{
     87    if( errMsg ){
     88        *errMsg = errorString(s);
     89    }
     90    return s != 0;
     91}
     92
     93class OSXKeychain
     94{
     95private:
     96    SecKeychainRef keyChainRef;
     97    QString keyChainPath;
     98    bool isDefaultKeychain, generateFolderList;
     99
     100public:
     101        enum EntryType { Unknown='K\?\?\?', Password='KPWD', Map='KMAP', Stream='KSTR' };
     102    QString name;
     103    QString currentService, lastReadService;
     104    QStringList serviceList;
     105    bool isKDEChain;
     106
     107    OSXKeychain();
     108    OSXKeychain(const QString &name);
     109    virtual ~OSXKeychain();
     110
     111    inline SecKeychainRef reference()
     112    {
     113        return keyChainRef;
     114    }
     115    inline QString &path()
     116    {
     117        return keyChainPath;
     118    }
     119    inline bool isDefault()
     120    {
     121            return isDefaultKeychain;
     122    }
     123    inline bool isOpen()
     124    {
     125        return IsOpen(keyChainRef);
     126    }
     127    inline OSStatus lock()
     128    {
     129        return Lock(keyChainRef);
     130    }
     131    inline OSStatus unLock()
     132    {
     133        return UnLock(keyChainRef);
     134    }
     135    void close();
     136    inline bool hasItem(const QString &key, OSStatus *errReturn, SecKeychainItemRef *itemRef=NULL)
     137    {
     138            // qDebug() << "OSXKeychain::hasItem(" << key << "): scanning '" << name << "'=" << (void*) keyChainRef;
     139            return OSXKeychain::HasItem( key, keyChainRef, errReturn, itemRef );
     140    }
     141    inline OSStatus readItem(const QString &key, QByteArray *value, SecKeychainItemRef *itemRef=NULL)
     142    {
     143        return ReadItem( key, value, keyChainRef, itemRef, this );
     144    }
     145    inline OSStatus itemType(const QString &key, EntryType *entryType)
     146    {
     147        return ItemType( key, entryType, keyChainRef );
     148    }
     149    inline OSStatus removeItem(const QString &key)
     150    {
     151        return RemoveItem( key, keyChainRef );
     152    }
     153    inline OSStatus writeItem( const QString &key, const QByteArray &value, EntryType *entryType=NULL )
     154    {
     155        return WriteItem( key, value, keyChainRef, NULL, entryType, this );
     156    }
     157    inline OSStatus writeItem( const QString &key, const QByteArray &value, const QString &comment,
     158                               EntryType *entryType=NULL )
     159    {
     160        return WriteItem( key, value, comment, keyChainRef, entryType, this );
     161    }
     162    inline OSStatus itemList( QStringList &keyList )
     163    {
     164        return ItemList( keyChainRef, keyList, this );
     165    }
     166    inline QStringList folderList()
     167    {
     168        QStringList r;
     169        CacheOldValue<bool> gFL(generateFolderList, true);
     170        ItemList( keyChainRef, r, this );
     171        r.clear();
     172        return serviceList;
     173    }
     174    OSStatus renameItem(const QString &currentKey, const QString &newKey);
     175
     176#pragma mark ==== class methods aka static member functions ====
     177    static OSStatus KeychainList(QStringList &theList);
     178    static QString Path(const SecKeychainRef keychain);
     179    static bool IsOpen(const SecKeychainRef keychain);
     180    static bool IsOpen(const QString& name);
     181    static OSStatus UnLock(const SecKeychainRef keychain);
     182    static OSStatus Lock(const SecKeychainRef keychain);
     183    static OSStatus Lock(const QString &walletName);
     184    /** use the keychain search functions to find the first matching item, if any, returning True if found.
     185     The OS X error code is returned through @p errReturn when not NULL, the item itself through @p itemRef.
     186     This reference will have to be released with CFRelease() when done with it (when @p itemRef==NULL the
     187     function does this release itself).
     188     */
     189    static bool HasItem(const QString &key,
     190                         const SecKeychainRef keychain, OSStatus *errReturn, SecKeychainItemRef *itemRef);
     191    static OSStatus ReadItem(const QString &key, QByteArray *value,
     192                              const SecKeychainRef keychain, SecKeychainItemRef *itemRef=NULL, OSXKeychain *osxKeyChain=NULL);
     193    static OSStatus ItemType(const QString &key, EntryType *entryType,
     194                               const SecKeychainRef keychain);
     195    static OSStatus RemoveItem(const QString &key, const SecKeychainRef keychain);
     196    static OSStatus WriteItem( const QString &key, const QByteArray &value,
     197                               const SecKeychainRef keychain, SecKeychainItemRef *itemRef=NULL, EntryType *entryType=NULL, OSXKeychain *osxKeyChain=NULL );
     198    static OSStatus WriteItem( const QString& key, const QByteArray& value,
     199                               const QString& comment, const SecKeychainRef keychain, EntryType *entryType, OSXKeychain *osxKeyChain=NULL );
     200    static OSStatus ItemList( const SecKeychainRef keychain, QStringList &keyList, OSXKeychain *osxKeyChain=NULL );
     201    static OSStatus Destroy( SecKeychainRef *keychain );
     202    static OSStatus Destroy( const QString &walletName );
     203};
  • kdelibs-4.12.5/kdeui/util/

    old new  
     1/*
     2 *  @file qosxkeychain.cpp
     3 *  This file is part of the KDE project
     4 *
     5 *  Created by René J.V. Bertin on 20140809.
     6 * Copyright (C) 2014 René Bertin <rjvbertin@gmail.com>
     7 *
     8 * This library is free software; you can redistribute it and/or
     9 * modify it under the terms of the GNU Library General Public
     10 * License as published by the Free Software Foundation; either
     11 * version 2 of the License, or (at your option) any later version.
     12 *
     13 * This library is distributed in the hope that it will be useful,
     14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16 * Library General Public License for more details.
     17 *
     18 * You should have received a copy of the GNU Library General Public License
     19 * along with this library; see the file COPYING.LIB.  If not, write to
     20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     21 * Boston, MA 02110-1301, USA.
     22 */
     23
     24#include <cassert>
     25#include <sys/param.h>
     26
     27#include <QtGui/QApplication>
     28#include <QtCore/QtCore>
     29#include <QtCore/QPointer>
     30#include <QtGui/QWidget>
     31
     32#include "kwallet.h"
     33#include <kdebug.h>
     34using namespace KWallet;
     35#include "qosxkeychain.h"
     36
     37#include <CoreServices/CoreServices.h>
     38
     39//! Define INTERNET_TOO=1 in order to build read-access to the kSecInternetPasswordItemClass items
     40#define INTERNET_TOO    0
     41
     42// #undef kWarning
     43// #undef kDebug
     44// #define kWarning    qWarning
     45// #define kDebug      qDebug
     46
     47// returns the textual representation of a FourCharCode (e.g. 'JPEG')
     48static QString OSTStr( FourCharCode etype )
     49{   union OSTStr {
     50        struct {
     51            char startquote;
     52            uint32_t four;
     53            char endquote;
     54        } __attribute__ ((packed)) value;
     55        char representation[7];
     56    }  __attribute__ ((packed)) ltype;
     57    ltype.value.four = EndianU32_BtoN(etype);
     58    ltype.representation[0] = ltype.representation[5] = '\'';
     59    ltype.representation[6] = '\0';
     60    return QString::fromAscii(ltype.representation);
     61}
     62
     63static SecKeychainRef defaultChain()
     64{   QString errMsg;
     65    SecKeychainRef keychain;
     66    if( isError( SecKeychainCopyDefault(&keychain), &errMsg ) ){
     67        kWarning() << "Could not retrieve reference to default keychain:"  << qPrintable(errMsg);
     68        keychain = NULL;
     69    }
     70    return keychain;
     71}
     72
     73/*! Return a name for @p keychain, and possibly the full path to its file
     74 * The name  will be the equivalent of the `basename path .keychain` shell
     75 * command.
     76 */
     77static QString keyChainName( SecKeychainRef keychain, QString *path=NULL )
     78{   QFileInfo keyFile;
     79    QString p = OSXKeychain::Path(keychain);
     80    int ext = p.lastIndexOf(".keychain");
     81    keyFile = QFileInfo( ((ext > 0)? p.left(ext) : p) );
     82    if( path ){
     83        *path = QString(p);
     84    }
     85    return keyFile.fileName();
     86}
     87
     88/*! Open an OS X keychain with name @p n.
     89 * OS X keychains can be created without a full path (say, "kdewallet"), in which case they
     90 * are stored e.g. as ~/Library/Keychains/kdewallet . However, opening a preexisting keychain like "login"
     91 * without using the full path seems to fail even if e.g. ~/Library/Keychains/login exists.
     92 * We try to work around that issue by matching @p n against the known keychain names.
     93 */
     94static OSStatus openKeychain( const QString &n, SecKeychainRef *keychain )
     95{   OSStatus err;
     96    CFArrayRef list = NULL;
     97
     98    *keychain = NULL;
     99    err = SecKeychainCopySearchList( &list );
     100    if( !err && list ){
     101        CFIndex len = CFArrayGetCount(list), i;
     102        for( i = 0 ; i < len && !*keychain ; ++i ){
     103            SecKeychainRef kr = (SecKeychainRef) CFArrayGetValueAtIndex( list, i );
     104            QString path, name = keyChainName( kr, &path );
     105            if( name == n ){
     106                // a hit, try to open it!
     107                err = SecKeychainOpen( path.toUtf8(), keychain );
     108                if( err ){
     109                    kWarning() << "openKeychain(" << n << ") error" << err << "opening matching" << path;
     110                }
     111                else{
     112                    kDebug() << "openKeychain(" << n << ") opened matching" << path;
     113                }
     114            }
     115        }
     116        CFRelease(list);
     117    }
     118    if( !*keychain ){
     119        err = SecKeychainOpen( n.toUtf8(), keychain );
     120    }
     121    // we actually need to query the keychain's status to know if we succeeded
     122    // in opening an existing keychain!
     123    if( !err ){
     124        SecKeychainStatus status;
     125        err = SecKeychainGetStatus( *keychain, &status );
     126    }
     127    return err;
     128}
     129
     130static OSStatus basicWriteItem( const QByteArray *serviceName, const QByteArray &accountName, const QByteArray &value,
     131                               const SecKeychainRef keychain, SecKeychainItemRef *itemRef=NULL )
     132{   OSStatus err;
     133    QString errMsg;
     134    if( serviceName ){
     135        err = SecKeychainAddGenericPassword( keychain, serviceName->size(), serviceName->constData(),
     136                                                     accountName.size(), accountName.constData(),
     137                                                     value.size(), value.constData(), itemRef );
     138    }
     139    else{
     140        err = SecKeychainAddGenericPassword( keychain, 0, NULL,
     141                                                     accountName.size(), accountName.constData(),
     142                                                     value.size(), value.constData(), itemRef );
     143    }
     144    if( err != errSecDuplicateItem && isError( err, &errMsg ) ){
     145        kWarning() << "Could not store password in keychain: " << qPrintable(errMsg);
     146    }
     147    return err;
     148}
     149
     150OSXKeychain::OSXKeychain()
     151    : name("default")
     152{ QString errMsg;
     153    keyChainRef = defaultChain();
     154    if( keyChainRef ){
     155        keyChainPath = OSXKeychain::Path(keyChainRef);
     156        kDebug() << "Retrieved reference to default keychain" << (void*) keyChainRef << "in " << keyChainPath;
     157        name = keyChainName(keyChainRef);
     158        isDefaultKeychain = true;
     159    }
     160    else{
     161        keyChainPath = QString::fromUtf8("<undefined>");
     162    }
     163    serviceList.clear();
     164    serviceList.append("");
     165}
     166
     167OSXKeychain::OSXKeychain(const QString &n)
     168    : name(n)
     169{   QString errMsg;
     170    OSStatus err = openKeychain( n, &keyChainRef );
     171
     172    if( err == errSecNoSuchKeychain ){
     173        kWarning() << "Keychain '" << n << "' does not exist: attempting to create it";
     174        err = SecKeychainCreate( n.toUtf8(), 0, NULL, true, NULL, &keyChainRef );
     175        isKDEChain = true;
     176    }
     177
     178    if( isError( err, &errMsg ) ){
     179        // the protocol cannot handle failure to open a keychain, so we have to return the default.
     180        keyChainRef = defaultChain();
     181        kWarning() << "Error opening keychain '" << n << "' (falling back to default keychain): " << qPrintable(errMsg);
     182        name = keyChainName(keyChainRef);
     183        isDefaultKeychain = true;
     184    }
     185    else{
     186        isDefaultKeychain = false;
     187    }
     188
     189    if( keyChainRef ){
     190        keyChainPath = OSXKeychain::Path(keyChainRef);
     191        kDebug() << "Retrieved reference to keychain" << name << (void*) keyChainRef << "in " << keyChainPath;
     192    }
     193    else{
     194        keyChainPath = QString::fromUtf8("<undefined>");
     195    }
     196    serviceList.clear();
     197    serviceList.append("");
     198}
     199
     200void OSXKeychain::close()
     201{
     202    if( keyChainRef ){
     203        CFRelease(keyChainRef);
     204        keyChainRef = NULL;
     205    }
     206}
     207
     208OSXKeychain::~OSXKeychain()
     209{
     210    close();
     211}
     212
     213OSStatus OSXKeychain::renameItem(const QString &currentKey, const QString &newKey)
     214{   OSStatus err;
     215    SecKeychainItemRef itemRef = NULL;
     216    err = ReadItem( currentKey, NULL, keyChainRef, &itemRef, this );
     217    if( !err && itemRef ){
     218        const QByteArray accountName( newKey.toUtf8() );
     219        // store the new key in the account and label attributes
     220        SecKeychainAttribute attr[] = { { kSecAccountItemAttr, accountName.size(), (void*) accountName.constData() },
     221                                        { kSecLabelItemAttr, accountName.size(), (void*) accountName.constData() } };
     222        SecKeychainAttributeList attrList = { 2, &attr[0] };
     223        QString errMsg;
     224        if( isError( (err = SecKeychainItemModifyAttributesAndData( itemRef, &attrList, 0, NULL )), &errMsg ) ){
     225            kWarning() << "OSXKeychain::renameItem(" << currentKey << ") couldn't change name & label to" << accountName
     226            << ":" << err << "=" << qPrintable(errMsg);
     227        }
     228        CFRelease(itemRef);
     229    }
     230    return err;
     231}
     232
     233#pragma mark ========= static member functions =========
     234
     235OSStatus OSXKeychain::KeychainList(QStringList &theList)
     236{   CFArrayRef list = NULL;
     237    OSStatus err = SecKeychainCopySearchList( &list );
     238    theList.clear();
     239    if( !err && list ){
     240        CFIndex len = CFArrayGetCount(list), i;
     241        for( i = 0 ; i < len ; ++i ){
     242            SecKeychainRef keychain = (SecKeychainRef) CFArrayGetValueAtIndex( list, i );
     243            QString name = keyChainName(keychain);
     244            if( name.size() > 0 ){
     245                theList.append(name);
     246            }
     247        }
     248        CFRelease(list);
     249    }
     250    return err;
     251}
     252
     253QString OSXKeychain::Path(const SecKeychainRef keychain)
     254{   char pathName[MAXPATHLEN];
     255    UInt32 plen = MAXPATHLEN;
     256    if( SecKeychainGetPath( (keychain)? keychain : OSXKeychain().reference(), &plen, pathName ) == errSecSuccess ){
     257        return QString::fromUtf8(pathName);
     258    }
     259    else{
     260        return QString();
     261    }
     262}
     263
     264bool OSXKeychain::IsOpen(const SecKeychainRef keychain)
     265{   bool isOpen = false;
     266    SecKeychainStatus status;
     267    QString errMsg;
     268    if( isError( SecKeychainGetStatus( keychain, &status ), &errMsg ) ){
     269        if( keychain ){
     270            kDebug() << "Could not get the status of keychain" << OSXKeychain::Path(keychain) << ":"  << qPrintable(errMsg);
     271        }
     272        else{
     273            kWarning() << "Could not get the default keychain's status:"  << qPrintable(errMsg);
     274        }
     275    }
     276    else{
     277        if( (status & kSecUnlockStateStatus) && (status & kSecReadPermStatus) ){
     278            isOpen = true;
     279        }
     280        else{
     281            kDebug() << "Keychain" << OSXKeychain::Path(keychain) << " has status" << status;
     282        }
     283    }
     284    return isOpen;
     285}
     286
     287bool OSXKeychain::IsOpen(const QString &walletName)
     288{   SecKeychainRef keychain = NULL;
     289    OSStatus err = openKeychain( walletName.toUtf8(), &keychain );
     290    bool ret = false;
     291    if( !err && keychain ){
     292        ret = IsOpen(keychain);
     293        CFRelease(keychain);
     294    }
     295    return ret;
     296}
     297
     298OSStatus OSXKeychain::UnLock(const SecKeychainRef keychain)
     299{   QString errMsg;
     300    OSStatus err;
     301    err = SecKeychainUnlock( keychain, 0, NULL, false );
     302    if( isError( err, &errMsg ) ){
     303        if( keychain ){
     304            kDebug() << "Could not unlock the keychain at '" << OSXKeychain::Path(keychain) << "': " << qPrintable(errMsg);
     305        }
     306        else{
     307            kDebug() << "Could not unlock the default keychain:"  << qPrintable(errMsg);
     308        }
     309    }
     310    return err;
     311}
     312
     313OSStatus OSXKeychain::Lock(const SecKeychainRef keychain)
     314{   QString errMsg;
     315    OSStatus err;
     316    if( keychain ){
     317        err = SecKeychainLock(keychain);
     318        if( isError( err, &errMsg ) ){
     319            kDebug() << "Could not lock the keychain at '" << OSXKeychain::Path(keychain) << "': " << qPrintable(errMsg);
     320        }
     321    }
     322    else{
     323        err = SecKeychainLockAll();
     324        if( isError( err, &errMsg ) ){
     325            kDebug() << "Could not lock all keychains:" << qPrintable(errMsg);
     326        }
     327    }
     328    return err;
     329}
     330
     331OSStatus OSXKeychain::Lock(const QString &walletName)
     332{   SecKeychainRef keychain = NULL;
     333    OSStatus err = openKeychain( walletName, &keychain );
     334    if( !err && keychain ){
     335        err = Lock(keychain);
     336           CFRelease(keychain);
     337    }
     338    return err;
     339}
     340
     341/** use the keychain search functions to find the first matching item, if any, @return returning True if found.
     342 The OS X error code is returned through @p errReturn when not NULL, the item itself through @p itemRef.
     343 This reference will have to be released with CFRelease() when done with it (when @p itemRef==NULL the
     344 function does this release itself).
     345 */
     346bool OSXKeychain::HasItem(const QString &key,
     347                     const SecKeychainRef keychain, OSStatus *errReturn, SecKeychainItemRef *itemRef)
     348{   const QByteArray accountName( key.toUtf8() );
     349    OSStatus err;
     350    SecKeychainSearchRef searchRef;
     351    SecKeychainAttribute attrs = { kSecAccountItemAttr, accountName.size(), (void*) accountName.constData() };
     352    SecKeychainAttributeList attrList = { 1, &attrs };
     353    err = SecKeychainSearchCreateFromAttributes( keychain, kSecGenericPasswordItemClass,
     354                                                (const SecKeychainAttributeList*) &attrList, &searchRef );
     355    const CFReleaser<SecKeychainSearchRef> releaseSR(searchRef);
     356    bool found;
     357    SecKeychainItemRef item;
     358    QString errMsg;
     359    if( err ){
     360        found = false;
     361        errMsg = errorString(err);
     362        kDebug() << "OSXKeychain::HasItem(" << key << "," << (void*) keychain << "): SecKeychainSearchCreateFromAttributes failed";
     363    }
     364    else{
     365            if( !(err = SecKeychainSearchCopyNext( searchRef, &item )) ){
     366                found = true;
     367                if( itemRef ){
     368                    *itemRef = item;
     369                }
     370                else if( item ){
     371                    CFRelease(item);
     372                }
     373                errMsg = QString();
     374            }
     375            else{
     376                found = false;
     377                errMsg = errorString(err);
     378            }
     379            if( errReturn ){
     380                *errReturn = err;
     381            }
     382    }
     383    kDebug() << ((found)? "Found" : "Did not find") << "item '" << key << "' in keychain " << (void*) keychain << ", error=" << err << " " << qPrintable(errMsg);
     384    return found;
     385}
     386
     387OSStatus OSXKeychain::ReadItem(const QString &key, QByteArray *value,
     388                          const SecKeychainRef keychain, SecKeychainItemRef *itemRef, OSXKeychain *osxKeyChain)
     389{   const QByteArray accountName( key.toUtf8() );
     390    UInt32 passwordSize = 0;
     391    void* passwordData = 0;
     392    QString errMsg;
     393    SecKeychainItemRef theItem;
     394    OSStatus err = SecKeychainFindGenericPassword( keychain, 0, NULL,
     395                                                  accountName.size(), accountName.constData(),
     396                                                  &passwordSize, &passwordData, &theItem );
     397    if( isError( err, &errMsg ) ){
     398        kDebug() << "Error" << err << "retrieving password for '" << accountName << "' :" << qPrintable(errMsg);
     399#if INTERNET_TOO
     400        if( SecKeychainFindInternetPassword( keychain, 0, NULL,
     401                                                      0, NULL,
     402                                                      accountName.size(), accountName.constData(),
     403                                                      0, NULL, 0,
     404                                                      kSecProtocolTypeAny, kSecAuthenticationTypeDefault,
     405                                                      &passwordSize, &passwordData, &theItem ) ){
     406            // just to be sure:
     407            theItem = NULL;
     408        }
     409        else{
     410            err = 0;
     411            errMsg = QString();
     412        }
     413#else
     414        theItem = NULL;
     415#endif
     416    }
     417    if( !err && theItem ){
     418        if( value ){
     419            *value = QByteArray( reinterpret_cast<const char*>( passwordData ), passwordSize );
     420        }
     421        SecKeychainItemFreeContent( NULL, passwordData );
     422        if( osxKeyChain && osxKeyChain->isKDEChain ){
     423            SecKeychainAttribute attr = { kSecServiceItemAttr, 0, NULL };
     424            SecKeychainAttributeList attrList = { 1, &attr };
     425            UInt32 len = 0;
     426            // try to fetch the item's ServiceItem attribute
     427            if( !SecKeychainItemCopyContent( theItem, NULL, &attrList, &len, NULL ) ){
     428                if( attr.length > 0 ){
     429                    osxKeyChain->lastReadService.clear();
     430                    osxKeyChain->lastReadService = QString::fromUtf8( (char*)attr.data, attr.length );
     431                }
     432                SecKeychainItemFreeContent( &attrList, NULL );
     433            }
     434        }
     435        if( itemRef ){
     436            *itemRef = theItem;
     437        }
     438        else if( theItem ){
     439            CFRelease(theItem);
     440        }
     441    }
     442    kDebug() << "OSXKeychain::ReadItem '" << key << "' from keychain " << OSXKeychain::Path(keychain) << ", error=" << err;
     443    return err;
     444}
     445
     446OSStatus OSXKeychain::ItemType(const QString &key, EntryType *entryType,
     447                          const SecKeychainRef keychain)
     448{   const QByteArray accountName( key.toUtf8() );
     449    QString errMsg;
     450    EntryType etype = (EntryType) 0;
     451    SecKeychainItemRef itemRef;
     452#if INTERNET_TOO
     453    bool isInternetPW = false;
     454#endif
     455    OSStatus err = SecKeychainFindGenericPassword( keychain, 0, NULL,
     456                                                  accountName.size(), accountName.constData(),
     457                                                  NULL, NULL, &itemRef );
     458    if( isError( err, &errMsg ) ){
     459        kDebug() << "Error" << err << "retrieving type for '" << accountName << "' :" << qPrintable(errMsg);
     460#if INTERNET_TOO
     461        if( SecKeychainFindInternetPassword( keychain, 0, NULL,
     462                                            0, NULL,
     463                                            accountName.size(), accountName.constData(),
     464                                            0, NULL, 0,
     465                                            kSecProtocolTypeAny, kSecAuthenticationTypeDefault,
     466                                            0, NULL, &itemRef ) ){
     467            // just to be sure:
     468            itemRef = NULL;
     469        }
     470        else{
     471            isInternetPW = true;
     472            err = 0;
     473            errMsg = QString();
     474        }
     475#else
     476        itemRef = NULL;
     477#endif
     478    }
     479    if( itemRef ){
     480                UInt32 tags[] = { kSecTypeItemAttr };
     481                UInt32 formats[] = { CSSM_DB_ATTRIBUTE_FORMAT_STRING };
     482        SecKeychainAttributeInfo attrGet = { 1, tags, formats };
     483        SecKeychainAttributeList *attrList = NULL;
     484        err = SecKeychainItemCopyAttributesAndData( itemRef, &attrGet, NULL, &attrList, NULL, NULL );
     485        if( !err ){
     486            if( attrList->attr[0].length == sizeof(EntryType) ){
     487                memcpy( &etype, attrList->attr[0].data, sizeof(EntryType) );
     488            }
     489            else if( attrList->attr[0].length ){
     490                kDebug() << "Error: key" << key << "item type retrieved is of size" << attrList->attr[0].length << "!=" << sizeof(EntryType);
     491            }
     492#if INTERNET_TOO
     493            else if( isInternetPW ){
     494                // this is just a wild guess ...
     495                etype = Password;
     496            }
     497#endif
     498            if( entryType ){
     499                *entryType = etype;
     500            }
     501        }
     502        SecKeychainItemFreeAttributesAndData( attrList, NULL );
     503        CFRelease(itemRef);
     504    }
     505    kDebug() << "OSXKeychain::ItemType '" << key << "' from keychain " << OSXKeychain::Path(keychain) << "=" << OSTStr(etype) << ", error=" << err;
     506    return err;
     507}
     508
     509OSStatus OSXKeychain::RemoveItem(const QString &key, const SecKeychainRef keychain)
     510{   const QByteArray accountName( key.toUtf8() );
     511    SecKeychainItemRef itemRef;
     512    QString errMsg;
     513    OSStatus result = SecKeychainFindGenericPassword( keychain, 0, NULL,
     514                                                     accountName.size(), accountName.constData(), NULL, NULL, &itemRef );
     515    if( isError( result, &errMsg ) ){
     516        kDebug() << "Could not find entry" << key << ":"  << qPrintable(errMsg);
     517    }
     518    else{
     519        const CFReleaser<SecKeychainItemRef> itemReleaser(itemRef);
     520        result = SecKeychainItemDelete(itemRef);
     521        if( isError( result, &errMsg ) ){
     522            kWarning() << "Could not delete entry" << key << ":"  << qPrintable(errMsg);
     523        }
     524    }
     525    return result;
     526}
     527
     528OSStatus OSXKeychain::WriteItem( const QString &key, const QByteArray &value,
     529                                                   const SecKeychainRef keychain, SecKeychainItemRef *itemRef, EntryType *entryType, OSXKeychain *osxKeyChain )
     530{   const QByteArray accountName( key.toUtf8() );
     531    OSStatus err;
     532    QString errMsg;
     533    SecKeychainItemRef theItem = NULL;
     534    bool saveLabel;
     535    if( osxKeyChain && osxKeyChain->currentService.size() ){
     536        const QByteArray serviceName( osxKeyChain->currentService.toUtf8() );
     537        // save the "GenericPassword" item using the service name, which appears to be the only way to write
     538        // to the "Where" field shown in the Keychain Utility.
     539        err = basicWriteItem( &serviceName, accountName, value, keychain, &theItem );
     540        // the service (folder!) string will also appear on the "Name" field, which however can be changed
     541        // independently, via the Label attribute.
     542        saveLabel = true;
     543    }
     544    else{
     545        err = basicWriteItem( NULL, accountName, value, keychain, &theItem );
     546        saveLabel = false;
     547    }
     548    if( err == errSecDuplicateItem ){
     549        // RJVB: the previous implementation was wrong. errSecDuplicateItem means the write failed because of an existing item.
     550        // So we have to find that item, and modify it.
     551        if( !(err = ReadItem( key, NULL, keychain, &theItem )) ){
     552            err = SecKeychainItemModifyAttributesAndData( theItem, NULL, value.size(), value.constData() );
     553            if( isError( err, &errMsg ) ){
     554                kDebug() << "Key '" << key
     555                    << "'already exists in keychain but error modifying the existing item: " << qPrintable(errMsg);
     556            }
     557        }
     558        if( !err ){
     559            kDebug() << "Key '" << key << "'already existed in keychain: modified the existing item";
     560        }
     561    }
     562    if( !err && saveLabel ){
     563        // store the desired text in the label attribute
     564        SecKeychainAttribute attr = { kSecLabelItemAttr, accountName.size(), (void*) accountName.constData() };
     565        SecKeychainAttributeList attrList = { 1, &attr };
     566        QString errMsg;
     567        if( isError( (err = SecKeychainItemModifyAttributesAndData( theItem, &attrList, 0, NULL )), &errMsg ) ){
     568            kWarning() << "OSXKeychain::WriteItem(" << key << ") couldn't set the desired name/label" << accountName
     569                << ":" << err << "=" << qPrintable(errMsg);
     570        }
     571    }
     572    if( !err ){
     573        EntryType defType = Stream;
     574        if( !entryType ){
     575            entryType = &defType;
     576        }
     577        SecKeychainAttribute attr = { kSecTypeItemAttr, sizeof(EntryType), (void*) entryType };
     578        SecKeychainAttributeList attrList = { 1, &attr };
     579        QString errMsg;
     580        if( isError( (err = SecKeychainItemModifyAttributesAndData( theItem, &attrList, 0, NULL )), &errMsg ) ){
     581            kWarning() << "OSXKeychain::WriteItem(" << key << ") couldn't set type to" << OSTStr(*entryType)
     582                << ":" << qPrintable(errMsg);
     583        }
     584    }
     585    if( itemRef ){
     586        *itemRef = theItem;
     587    }
     588    else if( theItem ){
     589        CFRelease(theItem);
     590    }
     591    kDebug() << "OSXKeychain::WriteItem '" << key << "' to keychain " << (void*) keychain << ", error=" << err;
     592    return err;
     593}
     594
     595OSStatus OSXKeychain::WriteItem( const QString &key, const QByteArray &value,
     596                                 const QString &comment, const SecKeychainRef keychain, EntryType *entryType, OSXKeychain *osxKeyChain )
     597{   SecKeychainItemRef itemRef = NULL;
     598    OSStatus err = WriteItem( key, value, keychain, &itemRef, entryType, osxKeyChain );
     599    if( !err && itemRef ){
     600        const QByteArray commentString(comment.toUtf8());
     601        if( commentString.size() ){
     602            SecKeychainAttribute attr = { kSecCommentItemAttr, commentString.size(), (void*) commentString.constData() };
     603            SecKeychainAttributeList attrList = { 1, &attr };
     604            QString errMsg;
     605            if( isError( (err = SecKeychainItemModifyAttributesAndData( itemRef, &attrList, 0, NULL )), &errMsg ) ){
     606                kWarning() << "OSXKeychain::WriteItem(" << key << ") couldn't add comment" << comment
     607                    << ":" << qPrintable(errMsg);
     608            }
     609        }
     610        CFRelease(itemRef);
     611    }
     612    return err;
     613}
     614
     615// returns the kSecAccountItemAttr's of all items in the keychain
     616OSStatus OSXKeychain::ItemList( SecKeychainRef keychain, QStringList &keyList, OSXKeychain *osxKeyChain )
     617{   OSStatus err;
     618    SecKeychainSearchRef searchRef[2];
     619    bool generateFolderList = ( osxKeyChain && osxKeyChain->isKDEChain && osxKeyChain->generateFolderList );
     620
     621    keyList.clear();
     622    if( generateFolderList ){
     623        osxKeyChain->serviceList.clear();
     624        if( osxKeyChain->currentService.size() > 0 ){
     625            osxKeyChain->serviceList.append(osxKeyChain->currentService);
     626        }
     627    }
     628
     629    err = SecKeychainSearchCreateFromAttributes( keychain, kSecGenericPasswordItemClass, NULL, &searchRef[0] );
     630#if INTERNET_TOO
     631    if( SecKeychainSearchCreateFromAttributes( keychain, kSecInternetPasswordItemClass, NULL, &searchRef[1] ) ){
     632        searchRef[1] = NULL;
     633    }
     634#else
     635    searchRef[1] = NULL;
     636#endif
     637    SecKeychainItemRef item;
     638    QString errMsg;
     639    if( isError(err, &errMsg) ){
     640        kDebug() << "OSXKeychain::ItemList(" << (void*) keychain << "): SecKeychainSearchCreateFromAttributes failed" << qPrintable(errMsg);
     641    }
     642    else{
     643        for( size_t i = 0 ; i < sizeof(searchRef)/sizeof(SecKeychainSearchRef) && !err ; ++i ){
     644            if( searchRef[i] ){
     645                while( !(err = SecKeychainSearchCopyNext( searchRef[i], &item )) ){
     646                    if( item ){
     647                        // whether the item will be listed in the keyList we return: by default it is
     648                        // (better an item shows up multiple times than not at all).
     649                        bool listItem = true;
     650                        SecKeychainAttribute attr = { kSecAccountItemAttr, 0, NULL };
     651                        SecKeychainAttributeList attrList = { 1, &attr };
     652                        UInt32 len = 0;
     653                        if( osxKeyChain && osxKeyChain->isKDEChain ){
     654                            // try to fetch the item's ServiceItem attribute
     655                            attr.tag = kSecServiceItemAttr;
     656                            if( !SecKeychainItemCopyContent( item, NULL, &attrList, &len, NULL ) ){
     657                                QString lbl = QString::fromUtf8( (char*)attr.data, attr.length );
     658                                // we got a service item attribute, which is where we store the kwallet folder info.
     659                                // If we disallow empty attributes, keychain items without service item attribute will
     660                                // appear in each folder that has a non-empty name. In other words, we allow a folder without name.
     661                                if( generateFolderList ){
     662                                    // add the "folder" to the list if not already listed
     663                                    if( !osxKeyChain->serviceList.contains(lbl) ){
     664                                        osxKeyChain->serviceList.append(lbl);
     665                                    }
     666                                }
     667                                else{
     668                                    // only list the item if it's in the current "folder"
     669                                    listItem = (lbl == osxKeyChain->currentService);
     670                                }
     671                                SecKeychainItemFreeContent( &attrList, NULL );
     672                            }
     673                        }
     674                        else{
     675                            // errors retrieving the service item attribute are ignored
     676                        }
     677                        if( listItem ){
     678                            attr.tag = kSecAccountItemAttr;
     679                            if( !(err = SecKeychainItemCopyContent( item, NULL, &attrList, &len, NULL )) ){
     680                                if( attr.length > 0 ){
     681                                    keyList.append(QString::fromUtf8( (char*)attr.data, attr.length ));
     682                                }
     683                                SecKeychainItemFreeContent( &attrList, NULL );
     684                            }
     685                            else{
     686                                errMsg = errorString(err);
     687                                kDebug() << "SecKeychainItemCopyContent returned" << err << "=" << qPrintable(errMsg);
     688                            }
     689                        }
     690                        CFRelease(item);
     691                    }
     692                }
     693                if( err ){
     694                    errMsg = errorString(err);
     695                }
     696                CFRelease(searchRef[i]);
     697            }
     698        }
     699    }
     700    return err;
     701}
     702
     703OSStatus OSXKeychain::Destroy( SecKeychainRef *keychain )
     704{   OSStatus err = SecKeychainDelete(*keychain);
     705    QString errMsg;
     706    if( isError( err, &errMsg ) ){
     707        kWarning() << "OSXKeychain::Destroy " << (void*) *keychain << ", error " << qPrintable(errMsg);
     708    }
     709    else{
     710        kWarning() << "OSXKeychain::Destroy " << (void*) *keychain << ", error=" << err;
     711    }
     712    if( keychain ){
     713        CFRelease(*keychain);
     714        *keychain = NULL;
     715    }
     716    return err;
     717}
     718
     719OSStatus OSXKeychain::Destroy( const QString &walletName )
     720{   SecKeychainRef keychain;
     721    OSStatus err = openKeychain( walletName, &keychain );
     722    if( !err && keychain ){
     723        err = Destroy(&keychain);
     724    }
     725    return err;
     726}