File indexing completed on 2024-12-22 03:47:15
0001 /* 0002 This file is part of the KDE project 0003 0004 SPDX-FileCopyrightText: 2002-2004 George Staikos <staikos@kde.org> 0005 SPDX-FileCopyrightText: 2008 Michael Leupold <lemma@confuego.org> 0006 SPDX-FileCopyrightText: 2010 Frank Osterfeld <osterfeld@kde.org> 0007 0008 SPDX-License-Identifier: LGPL-2.0-or-later 0009 */ 0010 0011 #include "kwallet.h" 0012 #include <KConfigGroup> 0013 #include <KSharedConfig> 0014 0015 #include <QApplication> 0016 #include <QDebug> 0017 #include <QPointer> 0018 #include <QWidget> 0019 0020 #include <Carbon/Carbon.h> 0021 #include <Security/SecKeychain.h> 0022 #include <Security/Security.h> 0023 0024 // TODO: OSX_KEYCHAIN_PORT_DISABLED is never defined, all the enclosing code should be removed 0025 0026 using namespace KWallet; 0027 0028 typedef QMap<QString, QString> StringStringMap; 0029 Q_DECLARE_METATYPE(StringStringMap) 0030 typedef QMap<QString, StringStringMap> StringToStringStringMapMap; 0031 Q_DECLARE_METATYPE(StringToStringStringMapMap) 0032 typedef QMap<QString, QByteArray> StringByteArrayMap; 0033 Q_DECLARE_METATYPE(StringByteArrayMap) 0034 0035 namespace 0036 { 0037 template<typename T> 0038 struct CFReleaser { 0039 explicit CFReleaser(const T &r) 0040 : ref(r) 0041 { 0042 } 0043 ~CFReleaser() 0044 { 0045 CFRelease(ref); 0046 } 0047 T ref; 0048 }; 0049 } 0050 0051 static QString asQString(CFStringRef sr) 0052 { 0053 return QString::fromLatin1(CFStringGetCStringPtr(sr, NULL)); // TODO Latin1 correct? 0054 } 0055 0056 static QString errorString(OSStatus s) 0057 { 0058 const CFReleaser<CFStringRef> ref(SecCopyErrorMessageString(s, NULL)); 0059 return asQString(ref.ref); 0060 } 0061 0062 static bool isError(OSStatus s, QString *errMsg) 0063 { 0064 if (errMsg) { 0065 *errMsg = errorString(s); 0066 } 0067 return s != 0; 0068 } 0069 0070 static QString appid() 0071 { 0072 return qApp->applicationName(); 0073 } 0074 0075 static OSStatus removeEntryImplementation(const QString &walletName, const QString &key) 0076 { 0077 const QByteArray serviceName(walletName.toUtf8()); 0078 const QByteArray accountName(key.toUtf8()); 0079 SecKeychainItemRef itemRef; 0080 QString errMsg; 0081 OSStatus result = 0082 SecKeychainFindGenericPassword(NULL, serviceName.size(), serviceName.constData(), accountName.size(), accountName.constData(), NULL, NULL, &itemRef); 0083 if (isError(result, &errMsg)) { 0084 qWarning() << "Could not retrieve password:" << qPrintable(errMsg); 0085 return result; 0086 } 0087 const CFReleaser<SecKeychainItemRef> itemReleaser(itemRef); 0088 result = SecKeychainItemDelete(itemRef); 0089 if (isError(result, &errMsg)) { 0090 qWarning() << "Could not delete password:" << qPrintable(errMsg); 0091 return result; 0092 } 0093 return result; 0094 } 0095 0096 const QString Wallet::LocalWallet() 0097 { 0098 KConfigGroup cfg(KSharedConfig::openConfig(QStringLiteral("kwalletrc"))->group("Wallet")); 0099 if (!cfg.readEntry("Use One Wallet", true)) { 0100 QString tmp = cfg.readEntry("Local Wallet", "localwallet"); 0101 if (tmp.isEmpty()) { 0102 return QStringLiteral("localwallet"); 0103 } 0104 return tmp; 0105 } 0106 0107 QString tmp = cfg.readEntry("Default Wallet", "kdewallet"); 0108 if (tmp.isEmpty()) { 0109 return QStringLiteral("kdewallet"); 0110 } 0111 return tmp; 0112 } 0113 0114 const QString Wallet::NetworkWallet() 0115 { 0116 KConfigGroup cfg(KSharedConfig::openConfig(QStringLiteral("kwalletrc"))->group("Wallet")); 0117 0118 QString tmp = cfg.readEntry("Default Wallet", "kdewallet"); 0119 if (tmp.isEmpty()) { 0120 return QStringLiteral("kdewallet"); 0121 } 0122 return tmp; 0123 } 0124 0125 const QString Wallet::PasswordFolder() 0126 { 0127 return QStringLiteral("Passwords"); 0128 } 0129 0130 const QString Wallet::FormDataFolder() 0131 { 0132 return QStringLiteral("Form Data"); 0133 } 0134 0135 class Q_DECL_HIDDEN Wallet::WalletPrivate 0136 { 0137 public: 0138 WalletPrivate(Wallet *wallet, int h, const QString &n) 0139 : q(wallet) 0140 , name(n) 0141 , handle(h) 0142 { 0143 } 0144 0145 void walletServiceUnregistered(); 0146 0147 Wallet *q; 0148 QString name; 0149 QString folder; 0150 int handle; 0151 int transactionId; 0152 }; 0153 0154 void Wallet::WalletPrivate::walletServiceUnregistered() 0155 { 0156 if (handle >= 0) { 0157 q->slotWalletClosed(handle); 0158 } 0159 } 0160 0161 Wallet::Wallet(int handle, const QString &name) 0162 : QObject(0L) 0163 , d(new WalletPrivate(this, handle, name)) 0164 { 0165 Q_UNUSED(handle); 0166 } 0167 0168 Wallet::~Wallet() 0169 { 0170 delete d; 0171 } 0172 0173 QStringList Wallet::walletList() 0174 { 0175 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0176 return walletLauncher->getInterface().wallets(); 0177 #else 0178 return QStringList(); 0179 #endif 0180 } 0181 0182 void Wallet::changePassword(const QString &name, WId w) 0183 { 0184 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0185 if (w == 0) { 0186 qDebug() << "Pass a valid window to KWallet::Wallet::changePassword()."; 0187 } 0188 walletLauncher->getInterface().changePassword(name, (qlonglong)w, appid()); 0189 #endif 0190 } 0191 0192 bool Wallet::isEnabled() 0193 { 0194 // PENDING(frank) check 0195 return true; 0196 } 0197 0198 bool Wallet::isOpen(const QString &name) 0199 { 0200 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0201 return walletLauncher->getInterface().isOpen(name); // default is false 0202 #else 0203 return true; 0204 #endif 0205 } 0206 0207 int Wallet::closeWallet(const QString &name, bool force) 0208 { 0209 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0210 QDBusReply<int> r = walletLauncher->getInterface().close(name, force); 0211 return r.isValid() ? r : -1; 0212 #else 0213 return 0; 0214 #endif 0215 } 0216 0217 int Wallet::deleteWallet(const QString &name) 0218 { 0219 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0220 QDBusReply<int> r = walletLauncher->getInterface().deleteWallet(name); 0221 return r.isValid() ? r : -1; 0222 #else 0223 return -1; 0224 #endif 0225 } 0226 0227 Wallet *Wallet::openWallet(const QString &name, WId w, OpenType ot) 0228 { 0229 Q_UNUSED(w); 0230 Q_UNUSED(ot); 0231 Wallet *wallet = new Wallet(-1, name); 0232 QMetaObject::invokeMethod(wallet, "emitWalletOpened", Qt::QueuedConnection); 0233 return wallet; 0234 } 0235 0236 bool Wallet::disconnectApplication(const QString &wallet, const QString &app) 0237 { 0238 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0239 return walletLauncher->getInterface().disconnectApplication(wallet, app); // default is false 0240 #else 0241 return true; 0242 #endif 0243 } 0244 0245 QStringList Wallet::users(const QString &name) 0246 { 0247 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0248 return walletLauncher->getInterface().users(name); // default is QStringList() 0249 #else 0250 return QStringList(); 0251 #endif 0252 } 0253 0254 int Wallet::sync() 0255 { 0256 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0257 if (d->handle == -1) { 0258 return -1; 0259 } 0260 0261 walletLauncher->getInterface().sync(d->handle, appid()); 0262 #endif 0263 return 0; 0264 } 0265 0266 int Wallet::lockWallet() 0267 { 0268 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0269 if (d->handle == -1) { 0270 return -1; 0271 } 0272 0273 QDBusReply<int> r = walletLauncher->getInterface().close(d->handle, true, appid()); 0274 d->handle = -1; 0275 d->folder.clear(); 0276 d->name.clear(); 0277 if (r.isValid()) { 0278 return r; 0279 } 0280 #endif 0281 return -1; 0282 } 0283 0284 const QString &Wallet::walletName() const 0285 { 0286 return d->name; 0287 } 0288 0289 bool Wallet::isOpen() const 0290 { 0291 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0292 return d->handle != -1; 0293 #else 0294 return true; 0295 #endif 0296 } 0297 0298 void Wallet::requestChangePassword(WId w) 0299 { 0300 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0301 if (w == 0) { 0302 qDebug() << "Pass a valid window to KWallet::Wallet::requestChangePassword()."; 0303 } 0304 if (d->handle == -1) { 0305 return; 0306 } 0307 0308 walletLauncher->getInterface().changePassword(d->name, (qlonglong)w, appid()); 0309 #endif 0310 } 0311 0312 void Wallet::slotWalletClosed(int handle) 0313 { 0314 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0315 if (d->handle == handle) { 0316 d->handle = -1; 0317 d->folder.clear(); 0318 d->name.clear(); 0319 Q_EMIT walletClosed(); 0320 } 0321 #endif 0322 } 0323 0324 QStringList Wallet::folderList() 0325 { 0326 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0327 if (d->handle == -1) { 0328 return QStringList(); 0329 } 0330 0331 QDBusReply<QStringList> r = walletLauncher->getInterface().folderList(d->handle, appid()); 0332 return r; 0333 #else 0334 return QStringList(); 0335 #endif 0336 } 0337 0338 QStringList Wallet::entryList() 0339 { 0340 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0341 if (d->handle == -1) { 0342 return QStringList(); 0343 } 0344 0345 QDBusReply<QStringList> r = walletLauncher->getInterface().entryList(d->handle, d->folder, appid()); 0346 return r; 0347 #else 0348 return QStringList(); 0349 #endif 0350 } 0351 0352 bool Wallet::hasFolder(const QString &f) 0353 { 0354 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0355 if (d->handle == -1) { 0356 return false; 0357 } 0358 0359 QDBusReply<bool> r = walletLauncher->getInterface().hasFolder(d->handle, f, appid()); 0360 return r; // default is false 0361 #else 0362 return true; 0363 #endif 0364 } 0365 0366 bool Wallet::createFolder(const QString &f) 0367 { 0368 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0369 if (d->handle == -1) { 0370 return false; 0371 } 0372 0373 if (!hasFolder(f)) { 0374 QDBusReply<bool> r = walletLauncher->getInterface().createFolder(d->handle, f, appid()); 0375 return r; 0376 } 0377 0378 return true; // folder already exists 0379 #else 0380 return true; 0381 #endif 0382 } 0383 0384 bool Wallet::setFolder(const QString &f) 0385 { 0386 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0387 bool rc = false; 0388 0389 if (d->handle == -1) { 0390 return rc; 0391 } 0392 0393 // Don't do this - the folder could have disappeared? 0394 #if 0 0395 if (f == d->folder) { 0396 return true; 0397 } 0398 #endif 0399 0400 if (hasFolder(f)) { 0401 d->folder = f; 0402 rc = true; 0403 } 0404 0405 return rc; 0406 #else 0407 return true; 0408 #endif 0409 } 0410 0411 bool Wallet::removeFolder(const QString &f) 0412 { 0413 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0414 if (d->handle == -1) { 0415 return false; 0416 } 0417 0418 QDBusReply<bool> r = walletLauncher->getInterface().removeFolder(d->handle, f, appid()); 0419 if (d->folder == f) { 0420 setFolder(QString()); 0421 } 0422 0423 return r; // default is false 0424 #else 0425 return true; 0426 #endif 0427 } 0428 0429 const QString &Wallet::currentFolder() const 0430 { 0431 return d->folder; 0432 } 0433 0434 int Wallet::readEntry(const QString &key, QByteArray &value) 0435 { 0436 const QByteArray serviceName(walletName().toUtf8()); 0437 const QByteArray accountName(key.toUtf8()); 0438 UInt32 passwordSize = 0; 0439 void *passwordData = 0; 0440 QString errMsg; 0441 if (isError(SecKeychainFindGenericPassword(NULL, 0442 serviceName.size(), 0443 serviceName.constData(), 0444 accountName.size(), 0445 accountName.constData(), 0446 &passwordSize, 0447 &passwordData, 0448 NULL), 0449 &errMsg)) { 0450 qWarning() << "Could not retrieve password:" << qPrintable(errMsg); 0451 return -1; 0452 } 0453 0454 value = QByteArray(reinterpret_cast<const char *>(passwordData), passwordSize); 0455 SecKeychainItemFreeContent(NULL, passwordData); 0456 return 0; 0457 } 0458 0459 int Wallet::readEntryList(const QString &key, QMap<QString, QByteArray> &value) 0460 { 0461 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0462 registerTypes(); 0463 0464 int rc = -1; 0465 0466 if (d->handle == -1) { 0467 return rc; 0468 } 0469 0470 QDBusReply<QVariantMap> r = walletLauncher->getInterface().readEntryList(d->handle, d->folder, key, appid()); 0471 if (r.isValid()) { 0472 rc = 0; 0473 // convert <QString, QVariant> to <QString, QByteArray> 0474 const QVariantMap val = r.value(); 0475 for (QVariantMap::const_iterator it = val.begin(); it != val.end(); ++it) { 0476 value.insert(it.key(), it.value().toByteArray()); 0477 } 0478 } 0479 0480 return rc; 0481 #else 0482 return -1; 0483 #endif 0484 } 0485 0486 int Wallet::renameEntry(const QString &oldName, const QString &newName) 0487 { 0488 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0489 int rc = -1; 0490 0491 if (d->handle == -1) { 0492 return rc; 0493 } 0494 0495 QDBusReply<int> r = walletLauncher->getInterface().renameEntry(d->handle, d->folder, oldName, newName, appid()); 0496 if (r.isValid()) { 0497 rc = r; 0498 } 0499 0500 return rc; 0501 #else 0502 return -1; 0503 #endif 0504 } 0505 0506 int Wallet::readMap(const QString &key, QMap<QString, QString> &value) 0507 { 0508 QByteArray v; 0509 const int ret = readEntry(key, v); 0510 if (ret != 0) { 0511 return ret; 0512 } 0513 if (!v.isEmpty()) { 0514 QDataStream ds(&v, QIODevice::ReadOnly); 0515 ds >> value; 0516 } 0517 return 0; 0518 } 0519 0520 int Wallet::readMapList(const QString &key, QMap<QString, QMap<QString, QString>> &value) 0521 { 0522 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0523 registerTypes(); 0524 0525 int rc = -1; 0526 0527 if (d->handle == -1) { 0528 return rc; 0529 } 0530 0531 QDBusReply<QVariantMap> r = walletLauncher->getInterface().readMapList(d->handle, d->folder, key, appid()); 0532 if (r.isValid()) { 0533 rc = 0; 0534 const QVariantMap val = r.value(); 0535 for (QVariantMap::const_iterator it = val.begin(); it != val.end(); ++it) { 0536 QByteArray mapData = it.value().toByteArray(); 0537 if (!mapData.isEmpty()) { 0538 QDataStream ds(&mapData, QIODevice::ReadOnly); 0539 QMap<QString, QString> v; 0540 ds >> v; 0541 value.insert(it.key(), v); 0542 } 0543 } 0544 } 0545 0546 return rc; 0547 #else 0548 return -1; 0549 #endif 0550 } 0551 0552 int Wallet::readPassword(const QString &key, QString &value) 0553 { 0554 QByteArray ba; 0555 const int ret = readEntry(key, ba); 0556 if (ret == 0) { 0557 value = QString::fromUtf8(ba.constData()); 0558 } 0559 return ret; 0560 } 0561 0562 int Wallet::readPasswordList(const QString &key, QMap<QString, QString> &value) 0563 { 0564 return -1; 0565 } 0566 0567 static OSStatus writeEntryImplementation(const QString &walletName, const QString &key, const QByteArray &value) 0568 { 0569 const QByteArray serviceName(walletName.toUtf8()); 0570 const QByteArray accountName(key.toUtf8()); 0571 QString errMsg; 0572 OSStatus err = SecKeychainAddGenericPassword(NULL, 0573 serviceName.size(), 0574 serviceName.constData(), 0575 accountName.size(), 0576 accountName.constData(), 0577 value.size(), 0578 value.constData(), 0579 NULL); 0580 if (err == errSecDuplicateItem) { 0581 err = removeEntryImplementation(walletName, key); 0582 if (isError(err, &errMsg)) { 0583 qWarning() << "Could not delete old key in keychain for replacing: " << qPrintable(errMsg); 0584 return err; 0585 } 0586 } 0587 if (isError(err, &errMsg)) { 0588 qWarning() << "Could not store password in keychain: " << qPrintable(errMsg); 0589 return err; 0590 } 0591 // qDebug() << "Successfully written out key:" << key; 0592 return err; 0593 } 0594 0595 int Wallet::writeEntry(const QString &key, const QByteArray &password, EntryType entryType) 0596 { 0597 Q_UNUSED(entryType) 0598 return writeEntryImplementation(walletName(), key, password); 0599 } 0600 0601 int Wallet::writeEntry(const QString &key, const QByteArray &value) 0602 { 0603 return writeEntryImplementation(walletName(), key, value); 0604 } 0605 0606 int Wallet::writeMap(const QString &key, const QMap<QString, QString> &value) 0607 { 0608 QByteArray mapData; 0609 QDataStream ds(&mapData, QIODevice::WriteOnly); 0610 ds << value; 0611 return writeEntry(key, mapData); 0612 } 0613 0614 int Wallet::writePassword(const QString &key, const QString &value) 0615 { 0616 return writeEntry(key, value.toUtf8()); 0617 } 0618 0619 bool Wallet::hasEntry(const QString &key) 0620 { 0621 const QByteArray serviceName(walletName().toUtf8()); 0622 const QByteArray accountName(key.toUtf8()); 0623 return !isError( 0624 SecKeychainFindGenericPassword(NULL, serviceName.size(), serviceName.constData(), accountName.size(), accountName.constData(), NULL, NULL, NULL), 0625 0); 0626 } 0627 0628 int Wallet::removeEntry(const QString &key) 0629 { 0630 return removeEntryImplementation(walletName(), key); 0631 } 0632 0633 Wallet::EntryType Wallet::entryType(const QString &key) 0634 { 0635 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0636 int rc = 0; 0637 0638 if (d->handle == -1) { 0639 return Wallet::Unknown; 0640 } 0641 0642 QDBusReply<int> r = walletLauncher->getInterface().entryType(d->handle, d->folder, key, appid()); 0643 if (r.isValid()) { 0644 rc = r; 0645 } 0646 0647 return static_cast<EntryType>(rc); 0648 #else 0649 return Wallet::Unknown; 0650 #endif 0651 } 0652 0653 void Wallet::slotFolderUpdated(const QString &wallet, const QString &folder) 0654 { 0655 if (d->name == wallet) { 0656 Q_EMIT folderUpdated(folder); 0657 } 0658 } 0659 0660 void Wallet::slotFolderListUpdated(const QString &wallet) 0661 { 0662 if (d->name == wallet) { 0663 Q_EMIT folderListUpdated(); 0664 } 0665 } 0666 0667 void Wallet::slotApplicationDisconnected(const QString &wallet, const QString &application) 0668 { 0669 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0670 if (d->handle >= 0 && d->name == wallet && application == appid()) { 0671 slotWalletClosed(d->handle); 0672 } 0673 #endif 0674 } 0675 0676 void Wallet::walletAsyncOpened(int tId, int handle) 0677 { 0678 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0679 // ignore responses to calls other than ours 0680 if (d->transactionId != tId || d->handle != -1) { 0681 return; 0682 } 0683 0684 // disconnect the async signal 0685 disconnect(this, SLOT(walletAsyncOpened(int, int))); 0686 0687 d->handle = handle; 0688 Q_EMIT walletOpened(handle > 0); 0689 #endif 0690 } 0691 0692 void Wallet::emitWalletAsyncOpenError() 0693 { 0694 Q_EMIT walletOpened(false); 0695 } 0696 0697 void Wallet::emitWalletOpened() 0698 { 0699 Q_EMIT walletOpened(true); 0700 } 0701 0702 bool Wallet::folderDoesNotExist(const QString &wallet, const QString &folder) 0703 { 0704 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0705 QDBusReply<bool> r = walletLauncher->getInterface().folderDoesNotExist(wallet, folder); 0706 return r; 0707 #else 0708 return false; 0709 #endif 0710 } 0711 0712 bool Wallet::keyDoesNotExist(const QString &wallet, const QString &folder, const QString &key) 0713 { 0714 #ifdef OSX_KEYCHAIN_PORT_DISABLED 0715 QDBusReply<bool> r = walletLauncher->getInterface().keyDoesNotExist(wallet, folder, key); 0716 return r; 0717 #else 0718 return false; 0719 #endif 0720 } 0721 0722 void Wallet::slotCollectionStatusChanged(int status) 0723 { 0724 } 0725 0726 void Wallet::slotCollectionDeleted() 0727 { 0728 d->folder.clear(); 0729 d->name.clear(); 0730 Q_EMIT walletClosed(); 0731 } 0732 0733 void Wallet::virtual_hook(int, void *) 0734 { 0735 // BASE::virtual_hook( id, data ); 0736 } 0737 0738 #include "moc_kwallet.cpp"