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 309 309 310 310 if (Q_WS_MAC AND MAC_USE_OSXKEYCHAIN) 311 311 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) 313 316 else(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) 317 318 endif(Q_WS_MAC AND MAC_USE_OSXKEYCHAIN) 318 319 319 320 if(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 2 3 * 3 4 * Copyright (C) 2002-2004 George Staikos <staikos@kde.org> 4 5 * Copyright (C) 2008 Michael Leupold <lemma@confuego.org> 5 6 * Copyright (C) 2010 Frank Osterfeld <osterfeld@kde.org> 7 * Copyright (C) 2014 René Bertin <rjvbertin@gmail.com> 6 8 * 7 9 * This library is free software; you can redistribute it and/or 8 10 * modify it under the terms of the GNU Library General Public … … 36 38 37 39 #include <cassert> 38 40 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" 42 44 43 45 using namespace KWallet; 44 46 … … 49 51 typedef QMap<QString, QByteArray> StringByteArrayMap; 50 52 Q_DECLARE_METATYPE(StringByteArrayMap) 51 53 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 76 55 static QString appid() 77 56 { 78 57 KComponentData cData = KGlobal::mainComponent(); … … 85 64 } 86 65 return qApp->applicationName(); 87 66 } 67 #endif 88 68 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 { 110 71 KConfigGroup cfg(KSharedConfig::openConfig("kwalletrc")->group("Wallet")); 111 72 if (!cfg.readEntry("Use One Wallet", true)) { 112 73 QString tmp = cfg.readEntry("Local Wallet", "localwallet"); … … 123 84 return tmp; 124 85 } 125 86 126 const QString Wallet::NetworkWallet() { 87 /*static*/ const QString Wallet::NetworkWallet() 88 { 127 89 KConfigGroup cfg(KSharedConfig::openConfig("kwalletrc")->group("Wallet")); 128 90 129 91 QString tmp = cfg.readEntry("Default Wallet", "kdewallet"); … … 133 95 return tmp; 134 96 } 135 97 136 const QString Wallet::PasswordFolder() { 98 /*static*/ const QString Wallet::PasswordFolder() 99 { 137 100 return "Passwords"; 138 101 } 139 102 140 const QString Wallet::FormDataFolder() { 103 /*static*/ const QString Wallet::FormDataFolder() 104 { 141 105 return "Form Data"; 142 106 } 143 107 144 class Wallet::WalletPrivate 108 #pragma mark ==== Wallet::WalletPrivate ==== 109 class Wallet::WalletPrivate : public OSXKeychain 145 110 { 146 111 public: 147 112 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 } 150 117 151 118 // needed for compilation reasons 152 void walletServiceUnregistered() { 119 void walletServiceUnregistered() 120 { 153 121 } 154 155 QString name;156 QString folder;157 122 }; 158 123 159 124 Wallet::Wallet(int handle, const QString& name) 160 : QObject(0L), d(new WalletPrivate(name)) { 125 : QObject(0L), d(new WalletPrivate(name)) 126 { 161 127 Q_UNUSED(handle); 162 128 } 163 129 164 Wallet::~Wallet() { 130 Wallet::~Wallet() 131 { 165 132 delete d; 166 133 } 167 134 168 169 QStringList Wallet::walletList(){135 /*static*/ QStringList Wallet::walletList() 136 { 170 137 #ifdef OSX_KEYCHAIN_PORT_DISABLED 171 138 return walletLauncher->getInterface().wallets(); 172 139 #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; 174 147 #endif 175 148 } 176 149 177 150 178 void Wallet::changePassword(const QString& name, WId w) { 151 /*static*/ void Wallet::changePassword(const QString& name, WId w) 152 { 179 153 #ifdef OSX_KEYCHAIN_PORT_DISABLED 180 154 if( w == 0 ) 181 155 kDebug(285) << "Pass a valid window to KWallet::Wallet::changePassword()."; 182 156 walletLauncher->getInterface().changePassword(name, (qlonglong)w, appid()); 157 #else 158 Q_UNUSED(w); 159 kWarning() << "Wallet::changePassword unimplemented '" << name << "'"; 183 160 #endif 184 161 } 185 162 186 163 187 bool Wallet::isEnabled() { 164 /*static*/ bool Wallet::isEnabled() 165 { 188 166 //PENDING(frank) check 189 167 return true; 190 168 } 191 169 192 170 193 bool Wallet::isOpen(const QString& name) { 171 /*static*/ bool Wallet::isOpen(const QString& name) 172 { 194 173 #ifdef OSX_KEYCHAIN_PORT_DISABLED 195 174 return walletLauncher->getInterface().isOpen(name); // default is false 196 175 #else 197 return true; 176 return OSXKeychain::IsOpen(name); 177 #endif 178 } 179 180 bool Wallet::isOpen() const 181 { 182 #ifdef OSX_KEYCHAIN_PORT_DISABLED 183 return d->handle != -1; 184 #else 185 return d->isOpen(); 198 186 #endif 199 187 } 200 188 201 189 202 int Wallet::closeWallet(const QString& name, bool force) { 190 /*static*/ int Wallet::closeWallet(const QString& name, bool force) 191 { 203 192 #ifdef OSX_KEYCHAIN_PORT_DISABLED 204 193 QDBusReply<int> r = walletLauncher->getInterface().close(name, force); 205 194 return r.isValid() ? r : -1; 206 195 #else 207 return 0; 196 Q_UNUSED(force); 197 return OSXKeychain::Lock(name); 208 198 #endif 209 199 } 210 200 211 201 212 int Wallet::deleteWallet(const QString& name) { 202 /*static*/ int Wallet::deleteWallet(const QString& name) 203 { 213 204 #ifdef OSX_KEYCHAIN_PORT_DISABLED 214 205 QDBusReply<int> r = walletLauncher->getInterface().deleteWallet(name); 215 206 return r.isValid() ? r : -1; 216 207 #else 217 return -1;208 return OSXKeychain::Destroy(name); 218 209 #endif 219 210 } 220 211 221 212 222 Wallet *Wallet::openWallet(const QString& name, WId w, OpenType ot) { 213 /*static*/ Wallet *Wallet::openWallet(const QString& name, WId w, OpenType ot) 214 { 223 215 Q_UNUSED(w); 224 216 Q_UNUSED(ot); 225 217 Wallet *wallet = new Wallet(-1, name); 226 218 QMetaObject::invokeMethod( wallet, "emitWalletOpened", Qt::QueuedConnection ); 219 OSStatus err = wallet->d->unLock(); 220 kDebug() << "Opened wallet '" << name << "': " << wallet << " error=" << err; 227 221 return wallet; 228 222 } 229 223 230 224 231 bool Wallet::disconnectApplication(const QString& wallet, const QString& app) { 225 /*static*/ bool Wallet::disconnectApplication(const QString& wallet, const QString& app) 226 { 232 227 #ifdef OSX_KEYCHAIN_PORT_DISABLED 233 228 return walletLauncher->getInterface().disconnectApplication(wallet, app); // default is false 234 229 #else 230 kWarning() << "Wallet::disconnectApplication unimplemented, '" << app << "' from '" << wallet << "'"; 235 231 return true; 236 232 #endif 237 233 } 238 234 239 235 240 QStringList Wallet::users(const QString& name) { 236 /*static*/ QStringList Wallet::users(const QString& name) 237 { 241 238 #ifdef OSX_KEYCHAIN_PORT_DISABLED 242 239 return walletLauncher->getInterface().users(name); // default is QStringList() 243 240 #else 241 kWarning() << "Wallet::users unimplemented, '" << name << "'"; 244 242 return QStringList(); 245 243 #endif 246 244 } 247 245 248 246 249 int Wallet::sync() { 247 int Wallet::sync() 248 { 250 249 #ifdef OSX_KEYCHAIN_PORT_DISABLED 251 250 if (d->handle == -1) { 252 251 return -1; … … 258 257 } 259 258 260 259 261 int Wallet::lockWallet() { 260 int Wallet::lockWallet() 261 { 262 262 #ifdef OSX_KEYCHAIN_PORT_DISABLED 263 263 if (d->handle == -1) { 264 264 return -1; … … 271 271 if (r.isValid()) { 272 272 return r; 273 273 } 274 #else 275 d->currentService.clear(); 274 276 #endif 275 return -1;277 return d->lock(); 276 278 } 277 279 278 280 279 const QString& Wallet::walletName() const { 281 const QString& Wallet::walletName() const 282 { 280 283 return d->name; 281 284 } 282 285 283 286 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) { 287 void Wallet::requestChangePassword(WId w) 288 { 294 289 #ifdef OSX_KEYCHAIN_PORT_DISABLED 295 290 if( w == 0 ) 296 291 kDebug(285) << "Pass a valid window to KWallet::Wallet::requestChangePassword()."; … … 299 294 } 300 295 301 296 walletLauncher->getInterface().changePassword(d->name, (qlonglong)w, appid()); 297 #else 298 Q_UNUSED(w); 299 kWarning() << "Wallet::requestChangePassword unimplemented '" << d->name << "'"; 302 300 #endif 303 301 } 304 302 305 303 306 void Wallet::slotWalletClosed(int handle) { 304 void Wallet::slotWalletClosed(int handle) 305 { 307 306 #ifdef OSX_KEYCHAIN_PORT_DISABLED 308 307 if (d->handle == handle) { 309 308 d->handle = -1; … … 311 310 d->name.clear(); 312 311 emit walletClosed(); 313 312 } 313 #else 314 Q_UNUSED(handle); 315 kWarning() << "Wallet::slotWalletClosed unimplemented '" << d->name << "'"; 316 d->currentService.clear(); 314 317 #endif 315 318 } 316 319 317 320 318 QStringList Wallet::folderList() { 321 QStringList Wallet::folderList() 322 { 319 323 #ifdef OSX_KEYCHAIN_PORT_DISABLED 320 324 if (d->handle == -1) { 321 325 return QStringList(); … … 324 328 QDBusReply<QStringList> r = walletLauncher->getInterface().folderList(d->handle, appid()); 325 329 return r; 326 330 #else 327 return QStringList( );331 return QStringList(d->folderList()); 328 332 #endif 329 333 } 330 334 331 335 332 QStringList Wallet::entryList() { 336 QStringList Wallet::entryList() 337 { 333 338 #ifdef OSX_KEYCHAIN_PORT_DISABLED 334 339 if (d->handle == -1) { 335 340 return QStringList(); … … 338 343 QDBusReply<QStringList> r = walletLauncher->getInterface().entryList(d->handle, d->folder, appid()); 339 344 return r; 340 345 #else 341 return QStringList(); 346 QStringList r = QStringList(); 347 d->itemList(r); 348 return r; 342 349 #endif 343 350 } 344 351 345 352 346 bool Wallet::hasFolder(const QString& f) { 353 bool Wallet::hasFolder(const QString& f) 354 { 347 355 #ifdef OSX_KEYCHAIN_PORT_DISABLED 348 356 if (d->handle == -1) { 349 357 return false; … … 352 360 QDBusReply<bool> r = walletLauncher->getInterface().hasFolder(d->handle, f, appid()); 353 361 return r; // default is false 354 362 #else 355 return true; 363 d->folderList(); 364 return d->serviceList.contains(f); 356 365 #endif 357 366 } 358 367 359 368 360 bool Wallet::createFolder(const QString& f) { 369 bool Wallet::createFolder(const QString& f) 370 { 361 371 #ifdef OSX_KEYCHAIN_PORT_DISABLED 362 372 if (d->handle == -1) { 363 373 return false; … … 370 380 371 381 return true; // folder already exists 372 382 #else 373 return true;383 return setFolder(f); 374 384 #endif 375 385 } 376 386 377 387 378 bool Wallet::setFolder(const QString& f) { 388 bool Wallet::setFolder(const QString &f) 389 { 379 390 #ifdef OSX_KEYCHAIN_PORT_DISABLED 380 391 bool rc = false; 381 392 … … 397 408 398 409 return rc; 399 410 #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 } 400 419 return true; 401 420 #endif 402 421 } 403 422 404 423 405 bool Wallet::removeFolder(const QString& f) { 424 bool Wallet::removeFolder(const QString& f) 425 { 406 426 #ifdef OSX_KEYCHAIN_PORT_DISABLED 407 427 if (d->handle == -1) { 408 428 return false; … … 415 435 416 436 return r; // default is false 417 437 #else 438 kWarning() << "Wallet::removeFolder unimplemented (returns true) '" << d->name << "'"; 439 if( d->currentService == f ){ 440 d->currentService.clear(); 441 } 418 442 return true; 419 443 #endif 420 444 } 421 445 422 446 423 const QString& Wallet::currentFolder() const { 447 const QString& Wallet::currentFolder() const 448 { 449 #ifdef OSX_KEYCHAIN_PORT_DISABLED 424 450 return d->folder; 451 #else 452 return d->currentService; 453 #endif 425 454 } 426 455 427 456 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; 457 int 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; 442 461 } 443 462 444 463 445 int Wallet::readEntryList(const QString& key, QMap<QString, QByteArray>& value) { 464 int Wallet::readEntryList(const QString& key, QMap<QString, QByteArray>& value) 465 { 446 466 #ifdef OSX_KEYCHAIN_PORT_DISABLED 447 467 registerTypes(); 448 468 … … 464 484 465 485 return rc; 466 486 #else 487 Q_UNUSED(key); 488 Q_UNUSED(value); 489 kWarning() << "Wallet::readEntryList unimplemented (returns -1) '" << d->name << "'"; 467 490 return -1; 468 491 #endif 469 492 } 470 493 471 494 472 int Wallet::renameEntry(const QString& oldName, const QString& newName) { 495 int Wallet::renameEntry(const QString& oldName, const QString& newName) 496 { 473 497 #ifdef OSX_KEYCHAIN_PORT_DISABLED 474 498 int rc = -1; 475 499 … … 484 508 485 509 return rc; 486 510 #else 487 return -1;511 return d->renameItem( oldName, newName ); 488 512 #endif 489 513 } 490 514 491 515 492 int Wallet::readMap(const QString& key, QMap<QString,QString>& value) { 516 int Wallet::readMap(const QString &key, QMap<QString,QString> &value) 517 { 493 518 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 ){ 496 521 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 ); 499 526 ds >> value; 500 527 } 528 kDebug() << "Wallet::readMap '" << key << "' from wallet " << d->name << ", error=0"; 501 529 return 0; 502 530 } 503 531 504 532 505 int Wallet::readMapList(const QString& key, QMap<QString, QMap<QString, QString> >& value) { 533 int Wallet::readMapList(const QString& key, QMap<QString, QMap<QString, QString> >& value) 534 { 506 535 #ifdef OSX_KEYCHAIN_PORT_DISABLED 507 536 registerTypes(); 508 537 … … 530 559 531 560 return rc; 532 561 #else 562 Q_UNUSED(key); 563 Q_UNUSED(value); 564 kWarning() << "Wallet::readMapList unimplemented (returns -1) '" << d->name << "'"; 533 565 return -1; 534 566 #endif 535 567 } 536 568 537 569 538 int Wallet::readPassword(const QString& key, QString& value) { 570 int Wallet::readPassword(const QString& key, QString& value) 571 { 539 572 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 ){ 542 575 value = QString::fromUtf8( ba.constData() ); 576 } 577 kDebug() << "Wallet::readPassword '" << key << "' from wallet " << d->name << ", error=" << ret; 543 578 return ret; 544 579 } 545 580 546 581 547 int Wallet::readPasswordList(const QString& key, QMap<QString, QString>& value) { 582 int 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 << "'"; 548 587 return -1; 549 588 } 550 589 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 ); 590 int 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; 575 594 } 576 595 577 578 int Wallet::writeEntry(const QString& key, const QByteArray& value) { 579 return writeEntryImplementation( walletName(), key, value ); 596 int 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; 580 616 } 581 617 582 583 int Wallet::writeMap(const QString& key, const QMap<QString,QString>& value){618 int Wallet::writeMap(const QString& key, const QMap<QString,QString>& value) 619 { 584 620 QByteArray mapData; 585 621 QDataStream ds(&mapData, QIODevice::WriteOnly); 586 622 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; 588 630 } 589 631 590 632 591 int Wallet::writePassword(const QString& key, const QString& value) { 592 return writeEntry( key, value.toUtf8() ); 633 int 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; 593 638 } 594 639 595 640 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 );641 bool 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; 600 645 } 601 646 602 int Wallet::removeEntry(const QString& key) { 603 return removeEntryImplementation( walletName(), key ); 647 int 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; 604 651 } 605 652 606 653 607 Wallet::EntryType Wallet::entryType(const QString& key) { 654 Wallet::EntryType Wallet::entryType(const QString& key) 655 { 608 656 #ifdef OSX_KEYCHAIN_PORT_DISABLED 609 657 int rc = 0; 610 658 … … 619 667 620 668 return static_cast<EntryType>(rc); 621 669 #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 } 622 686 return Wallet::Unknown; 623 687 #endif 624 688 } 625 689 626 690 627 void Wallet::slotFolderUpdated(const QString& wallet, const QString& folder) { 691 void Wallet::slotFolderUpdated(const QString& wallet, const QString& folder) 692 { 628 693 if (d->name == wallet) { 629 694 emit folderUpdated(folder); 630 695 } 631 696 } 632 697 633 698 634 void Wallet::slotFolderListUpdated(const QString& wallet) { 699 void Wallet::slotFolderListUpdated(const QString& wallet) 700 { 635 701 if (d->name == wallet) { 636 702 emit folderListUpdated(); 637 703 } 638 704 } 639 705 640 706 641 void Wallet::slotApplicationDisconnected(const QString& wallet, const QString& application) { 707 void Wallet::slotApplicationDisconnected(const QString& wallet, const QString& application) 708 { 642 709 #ifdef OSX_KEYCHAIN_PORT_DISABLED 643 710 if (d->handle >= 0 644 711 && d->name == wallet 645 712 && application == appid()) { 646 713 slotWalletClosed(d->handle); 647 714 } 715 #else 716 Q_UNUSED(wallet); 717 Q_UNUSED(application); 718 kWarning() << "Wallet::slotApplicationDisconnected unimplemented '" << d->name << "'"; 648 719 #endif 649 720 } 650 721 651 void Wallet::walletAsyncOpened(int tId, int handle) { 722 void Wallet::walletAsyncOpened(int tId, int handle) 723 { 652 724 #ifdef OSX_KEYCHAIN_PORT_DISABLED 653 725 // ignore responses to calls other than ours 654 726 if (d->transactionId != tId || d->handle != -1) { 655 727 return; 656 728 } 657 729 658 730 // disconnect the async signal 659 731 disconnect(this, SLOT(walletAsyncOpened(int,int))); 660 732 661 733 d->handle = handle; 662 734 emit walletOpened(handle > 0); 735 #else 736 Q_UNUSED(tId); 737 Q_UNUSED(handle); 738 kWarning() << "Wallet::walletAsyncOpened unimplemented '" << d->name << "'"; 663 739 #endif 664 740 } 665 741 666 void Wallet::emitWalletAsyncOpenError() { 742 void Wallet::emitWalletAsyncOpenError() 743 { 667 744 emit walletOpened(false); 668 745 } 669 746 670 void Wallet::emitWalletOpened() { 747 void Wallet::emitWalletOpened() 748 { 671 749 emit walletOpened(true); 672 750 } 673 751 … … 678 756 QDBusReply<bool> r = walletLauncher->getInterface().folderDoesNotExist(wallet, folder); 679 757 return r; 680 758 #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; 682 764 #endif 683 765 } 684 766 … … 689 771 QDBusReply<bool> r = walletLauncher->getInterface().keyDoesNotExist(wallet, folder, key); 690 772 return r; 691 773 #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; 693 782 #endif 694 783 } 695 784 696 785 void Wallet::slotCollectionStatusChanged(int status) 697 786 { 787 Q_UNUSED(status); 788 kWarning() << "Wallet::slotCollectionStatusChanged unimplemented '" << d->name << "' status=" << status; 698 789 } 699 790 700 791 void Wallet::slotCollectionDeleted() 701 792 { 793 #ifdef OSX_KEYCHAIN_PORT_DISABLED 702 794 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(); 704 800 emit walletClosed(); 705 801 } 706 802 707 803 708 void Wallet::virtual_hook(int, void*) { 804 void Wallet::virtual_hook(int, void*) 805 { 709 806 //BASE::virtual_hook( id, data ); 710 807 } 711 808 -
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 27 namespace { 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 64 static 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 79 static inline QString errorString( OSStatus s ) 80 { 81 const CFReleaser<CFStringRef> ref( SecCopyErrorMessageString( s, NULL ) ); 82 return asQString( ref.ref ); 83 } 84 85 static inline bool isError( OSStatus s, QString *errMsg ) 86 { 87 if( errMsg ){ 88 *errMsg = errorString(s); 89 } 90 return s != 0; 91 } 92 93 class OSXKeychain 94 { 95 private: 96 SecKeychainRef keyChainRef; 97 QString keyChainPath; 98 bool isDefaultKeychain, generateFolderList; 99 100 public: 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 ¤tKey, 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> 34 using 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') 48 static 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 63 static 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 */ 77 static 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 */ 94 static 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 130 static 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 150 OSXKeychain::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 167 OSXKeychain::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 200 void OSXKeychain::close() 201 { 202 if( keyChainRef ){ 203 CFRelease(keyChainRef); 204 keyChainRef = NULL; 205 } 206 } 207 208 OSXKeychain::~OSXKeychain() 209 { 210 close(); 211 } 212 213 OSStatus OSXKeychain::renameItem(const QString ¤tKey, 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 235 OSStatus 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 253 QString 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 264 bool 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 287 bool 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 298 OSStatus 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 313 OSStatus 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 331 OSStatus 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 */ 346 bool 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 387 OSStatus 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 446 OSStatus 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 509 OSStatus 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 528 OSStatus 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 595 OSStatus 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 616 OSStatus 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 703 OSStatus 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 719 OSStatus 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 }