KWallet

kwalletd.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2002-2004 George Staikos <staikos@kde.org>
4 SPDX-FileCopyrightText: 2008 Michael Leupold <lemma@confuego.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "kwalletd.h"
10#include "kwalletd_debug.h"
11
12#include "kbetterthankdialog.h"
13#include "kwalletfreedesktopcollection.h"
14#include "kwalletfreedesktopitem.h"
15#include "kwalletfreedesktopprompt.h"
16#include "kwalletfreedesktopservice.h"
17#include "kwalletfreedesktopsession.h"
18#include "kwalletportalsecrets.h"
19#include "kwalletwizard.h"
20
21#ifdef HAVE_GPGMEPP
22#include "knewwalletdialog.h"
23#endif
24
25#include <KColorScheme>
26#include <KConfig>
27#include <KConfigGroup>
28#include <KDirWatch>
29#include <KLocalizedString>
30#include <KMessageBox>
31#include <KNewPasswordDialog>
32#include <KNotification>
33#include <KPasswordDialog>
34#include <KPluginFactory>
35#include <KSharedConfig>
36#include <kwalletentry.h>
37#include <kwindowsystem.h>
38
39#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC)
40#define HAVE_X11 1
41#include <KX11Extras>
42#else
43#define HAVE_X11 0
44#endif
45
46#ifdef HAVE_GPGMEPP
47#include <gpgme++/key.h>
48#endif
49
50#include <QApplication>
51#include <QDir>
52#include <QIcon>
53#include <QTimer>
54
55#include <assert.h>
56
57#include "kwalletadaptor.h"
58
59static void startManagerForKwalletd()
60{
61 if (!QStandardPaths::findExecutable(QStringLiteral("kstart")).isEmpty()) {
62 QProcess::startDetached(QStringLiteral("kstart"), {QStringLiteral("kwalletmanager5"), QStringLiteral("--"), QStringLiteral("--kwalletd")});
63 } else if (!QStandardPaths::findExecutable(QStringLiteral("kstart5")).isEmpty()) {
64 QProcess::startDetached(QStringLiteral("kstart"), {QStringLiteral("kwalletmanager5"), QStringLiteral("--"), QStringLiteral("--kwalletd")});
65 } else {
66 QProcess::startDetached(QStringLiteral("kwalletmanager5"), QStringList{QStringLiteral("--kwalletd")});
67 }
68}
69
70class KWalletTransaction
71{
72public:
73 explicit KWalletTransaction(QDBusConnection conn)
74 : tId(nextTransactionId)
75 , res(-1)
76 , connection(conn)
77 {
78 nextTransactionId++;
79 // make sure the id is never < 0 as that's used for the
80 // error conditions.
81 if (nextTransactionId < 0) {
82 nextTransactionId = 0;
83 }
84 }
85
86 static int getTransactionId()
87 {
88 return nextTransactionId;
89 }
90
91 ~KWalletTransaction()
92 {
93 }
94
95 enum Type {
96 Unknown,
97 Open,
98 ChangePassword,
99 OpenFail,
100 CloseCancelled,
101 };
102 Type tType = Unknown;
103 QString appid;
104 qlonglong wId;
105 QString wallet;
106 QString service;
107 bool cancelled = false; // set true if the client dies before open
108 bool modal;
109 bool isPath;
110 int tId; // transaction id
111 int res;
112 QDBusMessage message;
113 QDBusConnection connection;
114
115private:
116 static int nextTransactionId;
117};
118
119int KWalletTransaction::nextTransactionId = 0;
120
121KWalletD::KWalletD()
122 : QObject(nullptr)
123 , _failed(0)
124 , _syncTime(5000)
125 , _curtrans(nullptr)
126 , _useGpg(false)
127{
128#ifdef HAVE_GPGMEPP
129 _useGpg = true;
130#endif
131
132 srand(time(nullptr));
133 _showingFailureNotify = false;
134 _closeIdle = false;
135 _idleTime = 0;
136 connect(&_closeTimers, &KTimeout::timedOut, this, &KWalletD::timedOutClose);
137 connect(&_syncTimers, &KTimeout::timedOut, this, &KWalletD::timedOutSync);
138
139 KConfig kwalletrc(QStringLiteral("kwalletrc"));
140 KConfigGroup cfgWallet(&kwalletrc, "Wallet");
141 KConfigGroup cfgSecrets(&kwalletrc, "org.freedesktop.secrets");
142
143 if (cfgWallet.readEntry<bool>("apiEnabled", true)) {
144 (void)new KWalletAdaptor(this);
145
146 // register services
147 QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.kwalletd6"));
148 QDBusConnection::sessionBus().registerObject(QStringLiteral("/modules/kwalletd6"), this);
149 // register also with the KF5 names for backward compatibility
151 QDBusConnection::sessionBus().registerObject(QStringLiteral("/modules/kwalletd5"), this);
152
153 new KWalletPortalSecrets(this);
154 }
155
156#ifdef Q_WS_X11
157 screensaver = 0;
158#endif
159
160 reconfigure();
161 // KGlobal::dirs()->addResourceType("kwallet", 0, "share/apps/kwallet");
162 _dw = new KDirWatch(this);
163 _dw->setObjectName(QStringLiteral("KWallet Directory Watcher"));
164 // _dw->addDir(KGlobal::dirs()->saveLocation("kwallet"));
165 _dw->addDir(KWallet::Backend::getSaveLocation());
166
167 _dw->startScan(true);
168 connect(_dw, &KDirWatch::dirty, this, &KWalletD::emitWalletListDirty);
169 connect(_dw, &KDirWatch::deleted, this, &KWalletD::emitWalletListDirty);
170
171 _serviceWatcher.setWatchMode(QDBusServiceWatcher::WatchForOwnerChange);
172 connect(&_serviceWatcher, &QDBusServiceWatcher::serviceOwnerChanged, this, &KWalletD::slotServiceOwnerChanged);
173
174 if (cfgSecrets.readEntry<bool>("apiEnabled", true)) {
175 _fdoService.reset(new KWalletFreedesktopService(this));
176 }
177}
178
179KWalletD::~KWalletD()
180{
181#ifdef Q_WS_X11
182 delete screensaver;
183 screensaver = 0;
184#endif
185 closeAllWallets();
186 qDeleteAll(_transactions);
187}
188
189QString KWalletD::encodeWalletName(const QString &name)
190{
191 return KWallet::Backend::encodeWalletName(name);
192}
193
194QString KWalletD::decodeWalletName(const QString &mangledName)
195{
196 return KWallet::Backend::decodeWalletName(mangledName);
197}
198
199#ifdef Q_WS_X11
200void KWalletD::connectToScreenSaver()
201{
202 screensaver = new QDBusInterface("org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver");
203 if (!screensaver->isValid()) {
204 qCDebug(KWALLETD_LOG) << "Service org.freedesktop.ScreenSaver not found. Retrying in 10 seconds...";
205 // keep attempting every 10 seconds
206 QTimer::singleShot(10000, this, SLOT(connectToScreenSaver()));
207 } else {
208 connect(screensaver, SIGNAL(ActiveChanged(bool)), SLOT(screenSaverChanged(bool)));
209 qCDebug(KWALLETD_LOG) << "connected to screen saver service.";
210 }
211}
212#endif
213
214int KWalletD::generateHandle()
215{
216 int rc;
217
218 // ASSUMPTION: RAND_MAX is fairly large.
219 do {
220 rc = rand();
221 } while (_wallets.contains(rc) || rc == 0);
222
223 return rc;
224}
225
226QPair<int, KWallet::Backend *> KWalletD::findWallet(const QString &walletName) const
227{
228 Wallets::const_iterator it = _wallets.constBegin();
229 const Wallets::const_iterator end = _wallets.constEnd();
230 for (; it != end; ++it) {
231 if (it.value()->walletName() == walletName) {
232 return qMakePair(it.key(), it.value());
233 }
234 }
235 return qMakePair(-1, static_cast<KWallet::Backend *>(nullptr));
236}
237
238bool KWalletD::_processing = false;
239
240void KWalletD::processTransactions()
241{
242 if (_processing) {
243 return;
244 }
245
246 _processing = true;
247
248 // Process remaining transactions
249 while (!_transactions.isEmpty()) {
250 _curtrans = _transactions.takeFirst();
251 int res;
252
253 assert(_curtrans->tType != KWalletTransaction::Unknown);
254
255 switch (_curtrans->tType) {
256 case KWalletTransaction::Open:
257 res = doTransactionOpen(_curtrans->appid, _curtrans->wallet, _curtrans->isPath, _curtrans->wId, _curtrans->modal, _curtrans->service);
258
259 // multiple requests from the same client
260 // should not produce multiple password
261 // dialogs on a failure
262 if (res < 0) {
264 for (it = _transactions.begin(); it != _transactions.end(); ++it) {
265 KWalletTransaction *x = *it;
266 if (_curtrans->appid == x->appid && x->tType == KWalletTransaction::Open && x->wallet == _curtrans->wallet && x->wId == _curtrans->wId) {
267 x->tType = KWalletTransaction::OpenFail;
268 }
269 }
270 } else if (_curtrans->cancelled) {
271 // the wallet opened successfully but the application
272 // opening exited/crashed while the dialog was still shown.
273 KWalletTransaction *_xact = new KWalletTransaction(_curtrans->connection);
274 _xact->tType = KWalletTransaction::CloseCancelled;
275 _xact->appid = _curtrans->appid;
276 _xact->wallet = _curtrans->wallet;
277 _xact->service = _curtrans->service;
278 _transactions.append(_xact);
279 }
280
281 // emit the AsyncOpened signal as a reply
282 _curtrans->res = res;
283 Q_EMIT walletAsyncOpened(_curtrans->tId, res);
284 break;
285
286 case KWalletTransaction::OpenFail:
287 // emit the AsyncOpened signal with an invalid handle
288 _curtrans->res = -1;
289 Q_EMIT walletAsyncOpened(_curtrans->tId, -1);
290 break;
291
292 case KWalletTransaction::ChangePassword:
293 doTransactionChangePassword(_curtrans->appid, _curtrans->wallet, _curtrans->wId);
294 break;
295
296 case KWalletTransaction::CloseCancelled:
297 doTransactionOpenCancelled(_curtrans->appid, _curtrans->wallet, _curtrans->service);
298 break;
299
300 case KWalletTransaction::Unknown:
301 break;
302 default:
303 break;
304 }
305
306 // send delayed dbus message reply to the caller
307 if (_curtrans->message.type() != QDBusMessage::InvalidMessage) {
308 if (_curtrans->connection.isConnected()) {
309 QDBusMessage reply = _curtrans->message.createReply();
310 reply << _curtrans->res;
311 _curtrans->connection.send(reply);
312 }
313 }
314
315 delete _curtrans;
316 _curtrans = nullptr;
317 }
318
319 _processing = false;
320}
321
322int KWalletD::openPath(const QString &path, qlonglong wId, const QString &appid)
323{
324 int tId = openPathAsync(path, wId, appid, false);
325 if (tId < 0) {
326 return tId;
327 }
328
329 // NOTE the real return value will be sent by the dbusmessage delayed
330 // reply
331 return 0;
332 // wait for the open-transaction to be processed
333 // KWalletOpenLoop loop(this);
334 // return loop.waitForAsyncOpen(tId);
335}
336
337int KWalletD::open(const QString &wallet, qlonglong wId, const QString &appid)
338{
339 if (!_enabled) { // guard
340 return -1;
341 }
342
343 KWalletTransaction *xact = new KWalletTransaction(connection());
344 _transactions.append(xact);
345
346 message().setDelayedReply(true);
347 xact->message = message();
348
349 xact->appid = appid;
350 xact->wallet = wallet;
351 xact->wId = wId;
352 xact->modal = true; // mark dialogs as modal, the app has blocking wait
353 xact->tType = KWalletTransaction::Open;
354 xact->isPath = false;
355
356 QTimer::singleShot(0, this, SLOT(processTransactions()));
357 checkActiveDialog();
358 // NOTE the real return value will be sent by the dbusmessage delayed
359 // reply
360 return 0;
361}
362
363int KWalletD::nextTransactionId() const
364{
365 return KWalletTransaction::getTransactionId();
366}
367
368int KWalletD::openAsync(const QString &wallet,
369 qlonglong wId,
370 const QString &appid,
371 bool handleSession,
372 const QDBusConnection &connection,
373 const QDBusMessage &message)
374{
375 if (!_enabled) { // guard
376 return -1;
377 }
378
379 KWalletTransaction *xact = new KWalletTransaction(connection);
380 _transactions.append(xact);
381
382 xact->appid = appid;
383 xact->wallet = wallet;
384 xact->wId = wId;
385 xact->modal = true; // mark dialogs as modal, the app has blocking wait
386 xact->tType = KWalletTransaction::Open;
387 xact->isPath = false;
388 if (handleSession) {
389 qCDebug(KWALLETD_LOG) << "openAsync for " << message.service();
390 _serviceWatcher.setConnection(connection);
391 _serviceWatcher.addWatchedService(message.service());
392 xact->service = message.service();
393 }
394 QTimer::singleShot(0, this, SLOT(processTransactions()));
395 checkActiveDialog();
396 // opening is in progress. return the transaction number
397 return xact->tId;
398}
399
400int KWalletD::openAsync(const QString &wallet, qlonglong wId, const QString &appid, bool handleSession)
401{
402 return openAsync(wallet, wId, appid, handleSession, connection(), message());
403}
404
405int KWalletD::openPathAsync(const QString &path, qlonglong wId, const QString &appid, bool handleSession)
406{
407 if (!_enabled) { // guard
408 return -1;
409 }
410
411 KWalletTransaction *xact = new KWalletTransaction(connection());
412 _transactions.append(xact);
413
414 xact->appid = appid;
415 xact->wallet = path;
416 xact->wId = wId;
417 xact->modal = true;
418 xact->tType = KWalletTransaction::Open;
419 xact->isPath = true;
420 if (handleSession) {
421 qCDebug(KWALLETD_LOG) << "openPathAsync " << message().service();
422 _serviceWatcher.setConnection(connection());
423 _serviceWatcher.addWatchedService(message().service());
424 xact->service = message().service();
425 }
426 QTimer::singleShot(0, this, SLOT(processTransactions()));
427 checkActiveDialog();
428 // opening is in progress. return the transaction number
429 return xact->tId;
430}
431
432// Sets up a dialog that will be shown by kwallet.
433void KWalletD::setupDialog(QWidget *dialog, WId wId, const QString &appid, bool modal)
434{
435 if (wId != 0) {
436 // correct, set dialog parent
437 dialog->setAttribute(Qt::WA_NativeWindow, true);
439 } else {
440 if (appid.isEmpty()) {
441 qWarning() << "Using kwallet without parent window!";
442 } else {
443 qWarning() << "Application '" << appid << "' using kwallet without parent window!";
444 }
445 // allow dialog activation even if it interrupts, better than trying
446 // hacks
447 // with keeping the dialog on top or on all desktops
448 // KF5 FIXME what should we use now instead of this:
449 // kapp->updateUserTimestamp();
450 }
451
452#if HAVE_X11
454 if (modal) {
456 } else {
458 }
459 }
460#endif
461 activeDialog = dialog;
462}
463
464// If there's a dialog already open and another application tries some
465// operation that'd lead to
466// opening a dialog, that application will be blocked by this dialog. A proper
467// solution would
468// be to set the second application's window also as a parent for the active
469// dialog, so that
470// KWin properly handles focus changes and so on, but there's currently no
471// support for multiple
472// dialog parents. In the absence of this support, we use all kinds of bad
473// hacks to make
474// sure the user doesn't overlook the active dialog.
475void KWalletD::checkActiveDialog()
476{
477 if (!activeDialog) {
478 return;
479 }
480
481 // KF5 FIXME what should we use now instead of this:
482 // kapp->updateUserTimestamp();
483
484 activeDialog->show();
485
486#if HAVE_X11
488 WId window = activeDialog->winId();
490 KX11Extras::setOnAllDesktops(window, true);
492 }
493#endif
494}
495
496int KWalletD::doTransactionOpen(const QString &appid, const QString &wallet, bool isPath, qlonglong wId, bool modal, const QString &service)
497{
498 if (_firstUse && !isPath) {
499 // if the user specifies a wallet name, the use it as the default
500 // wallet name
501 if (wallet != KWallet::Wallet::LocalWallet()) {
502 KConfig kwalletrc(QStringLiteral("kwalletrc"));
503 KConfigGroup cfg(&kwalletrc, "Wallet");
504 cfg.writeEntry("Default Wallet", wallet);
505 }
506 if (wallets().contains(KWallet::Wallet::LocalWallet())) {
507 KConfig kwalletrc(QStringLiteral("kwalletrc"));
508 KConfigGroup cfg(&kwalletrc, "Wallet");
509 _firstUse = false;
510 cfg.writeEntry("First Use", false);
511 }
512 // else {
513 // // First use wizard
514 // // TODO GPG adjust new smartcard options gathered by
515 // the wizard
516 // QPointer<KWalletWizard> wiz = new KWalletWizard(0);
517 // wiz->setWindowTitle(i18n("KDE Wallet Service"));
518 // setupDialog(wiz, (WId)wId, appid, modal);
519 // int rc = wiz->exec();
520 // if (rc == QDialog::Accepted && wiz) {
521 // bool useWallet = wiz->field("useWallet").toBool();
522 // KConfig kwalletrc("kwalletrc");
523 // KConfigGroup cfg(&kwalletrc, "Wallet");
524 // cfg.writeEntry("First Use", false);
525 // cfg.writeEntry("Enabled", useWallet);
526 // cfg.writeEntry("Close When Idle",
527 // wiz->field("closeWhenIdle").toBool());
528 // cfg.writeEntry("Use One Wallet",
529 // !wiz->field("networkWallet").toBool());
530 // cfg.sync();
531 // reconfigure();
532 //
533 // if (!useWallet) {
534 // delete wiz;
535 // return -1;
536 // }
537 //
538 // // Create the wallet
539 // // TODO GPG select the correct wallet type upon
540 // cretion (GPG or blowfish based)
541 // KWallet::Backend *b = new
542 // KWallet::Backend(KWallet::Wallet::LocalWallet());
543 // #ifdef HAVE_GPGMEPP
544 // if (wiz->field("useBlowfish").toBool()) {
545 // b->setCipherType(KWallet::BACKEND_CIPHER_BLOWFISH);
546 // #endif
547 // QString pass = wiz->field("pass1").toString();
548 // QByteArray p(pass.toUtf8(), pass.length());
549 // b->open(p);
550 // p.fill(0);
551 // #ifdef HAVE_GPGMEPP
552 // } else {
553 // assert(wiz->field("useGpg").toBool());
554 // b->setCipherType(KWallet::BACKEND_CIPHER_GPG);
555 // b->open(wiz->gpgKey());
556 // }
557 // #endif
558 // b->createFolder(KWallet::Wallet::PasswordFolder());
559 // b->createFolder(KWallet::Wallet::FormDataFolder());
560 // b->close(true);
561 // delete b;
562 // delete wiz;
563 // } else {
564 // delete wiz;
565 // return -1;
566 // }
567 // }
568 }
569
570 int rc = internalOpen(appid, wallet, isPath, WId(wId), modal, service);
571 return rc;
572}
573
574int KWalletD::internalOpen(const QString &appid, const QString &wallet, bool isPath, WId w, bool modal, const QString &service)
575{
576 bool brandNew = false;
577
578 QString thisApp;
579 if (appid.isEmpty()) {
580 thisApp = QStringLiteral("KDE System");
581 } else {
582 thisApp = appid;
583 }
584
585 if (implicitDeny(wallet, thisApp)) {
586 return -1;
587 }
588
589 QPair<int, KWallet::Backend *> walletInfo = findWallet(wallet);
590 int rc = walletInfo.first;
591 if (rc == -1) {
592 if (_wallets.count() > 20) {
593 qCDebug(KWALLETD_LOG) << "Too many wallets open.";
594 return -1;
595 }
596
597 KWallet::Backend *b = new KWallet::Backend(wallet, isPath);
598 QString password;
599 bool emptyPass = false;
600 if ((isPath && QFile::exists(wallet)) || (!isPath && KWallet::Backend::exists(wallet))) {
601 // this open attempt will set wallet type from the file header,
602 // even if password is needed
603 int pwless = b->open(QByteArray(), w);
604#ifdef HAVE_GPGMEPP
605 assert(b->cipherType() != KWallet::BACKEND_CIPHER_UNKNOWN);
606 if (b->cipherType() == KWallet::BACKEND_CIPHER_GPG) {
607 // GPG based wallets do not prompt for password here. Instead,
608 // GPG should already have popped pinentry utility for wallet
609 // decryption
610 if (!b->isOpen()) {
611 // for some reason, GPG operation failed
612 delete b;
613 return -1;
614 }
615 emptyPass = true;
616 } else {
617#endif
618 if (0 != pwless || !b->isOpen()) {
619 if (pwless == 0) {
620 // release, start anew
621 delete b;
622 b = new KWallet::Backend(wallet, isPath);
623 }
624 KPasswordDialog *kpd = new KPasswordDialog();
625 if (appid.isEmpty()) {
626 kpd->setPrompt(i18n("<qt>KDE has requested to open the wallet '<b>%1</b>'. Please enter the password for this wallet below.</qt>",
627 wallet.toHtmlEscaped()));
628 } else {
629 kpd->setPrompt(
630 i18n("<qt>The application '<b>%1</b>' has requested to open the wallet '<b>%2</b>'. Please enter the password for this wallet "
631 "below.</qt>",
632 appid.toHtmlEscaped(),
633 wallet.toHtmlEscaped()));
634 }
635 brandNew = false;
636 // don't use KStdGuiItem::open() here which has trailing
637 // ellipsis!
638 // KF5 FIXME what should we use now instead of this:
639 // kpd->setButtonGuiItem(KDialog::Ok,KGuiItem(
640 // i18n( "&Open" ), "wallet-open"));
641 kpd->setWindowTitle(i18n("KDE Wallet Service"));
642 kpd->setIcon(QIcon::fromTheme(QStringLiteral("kwalletmanager")));
643
644#if HAVE_X11
645 if (KWindowSystem::isPlatformX11() && w != KX11Extras::activeWindow() && w != 0L) {
646 // If the dialog is modal to a minimized window it
647 // might not be visible
648 // (but still blocking the calling application).
649 // Notify the user about
650 // the request to open the wallet.
651 KNotification *notification =
653 notification->setWindow(kpd->windowHandle());
654 QString actionText;
655 if (appid.isEmpty()) {
656 notification->setText(i18n("An application has requested to open a wallet (%1).", wallet.toHtmlEscaped()));
657 actionText = i18nc("Text of a button for switching to the (unnamed) application requesting a password", "Switch there");
658 } else {
659 notification->setText(i18n("<b>%1</b> has requested to open a wallet (%2).", appid.toHtmlEscaped(), wallet.toHtmlEscaped()));
660 actionText =
661 i18nc("Text of a button for switching to the application requesting a password", "Switch to %1", appid.toHtmlEscaped());
662 }
663
664 KNotificationAction *action = notification->addAction(actionText);
665 connect(action, &KNotificationAction::activated, this, &KWalletD::activatePasswordDialog);
666 notification->sendEvent();
667 }
668#endif
669
670 while (!b->isOpen()) {
671 setupDialog(kpd, w, appid, modal);
672 if (kpd->exec() == QDialog::Accepted) {
673 password = kpd->password();
674 int rc = b->open(password.toUtf8());
675 if (!b->isOpen()) {
676 const auto errorStr = KWallet::Backend::openRCToString(rc);
677 qCWarning(KWALLETD_LOG) << "Failed to open wallet" << wallet << errorStr;
678 kpd->setPrompt(i18n("<qt>Error opening the wallet '<b>%1</b>'. Please try again.<br />(Error code %2: %3)</qt>",
679 wallet.toHtmlEscaped(),
680 rc,
681 errorStr));
682 kpd->setPassword(QLatin1String(""));
683 }
684 } else {
685 break;
686 }
687 }
688 delete kpd;
689 } else {
690 emptyPass = true;
691 }
692#ifdef HAVE_GPGMEPP
693 }
694#endif
695 } else {
696 brandNew = true;
697#ifdef HAVE_GPGMEPP
698 // prompt the user for the new wallet format here
699 KWallet::BackendCipherType newWalletType = KWallet::BACKEND_CIPHER_UNKNOWN;
700
701 std::shared_ptr<KWallet::KNewWalletDialog> newWalletDlg(new KWallet::KNewWalletDialog(appid, wallet, QWidget::find(w)));
702 GpgME::Key gpgKey;
703 setupDialog(newWalletDlg.get(), (WId)w, appid, true);
704 if (newWalletDlg->exec() == QDialog::Accepted) {
705 newWalletType = newWalletDlg->isBlowfish() ? KWallet::BACKEND_CIPHER_BLOWFISH : KWallet::BACKEND_CIPHER_GPG;
706 gpgKey = newWalletDlg->gpgKey();
707 } else {
708 // user cancelled the dialog box
709 delete b;
710 return -1;
711 }
712
713 if (newWalletType == KWallet::BACKEND_CIPHER_GPG) {
714 b->setCipherType(newWalletType);
715 b->open(gpgKey);
716 } else if (newWalletType == KWallet::BACKEND_CIPHER_BLOWFISH) {
717#endif // HAVE_GPGMEPP
718 b->setCipherType(KWallet::BACKEND_CIPHER_BLOWFISH);
721 kpd->setBackgroundWarningColor(colorScheme.background(KColorScheme::NegativeBackground).color());
722 if (wallet == KWallet::Wallet::LocalWallet() || wallet == KWallet::Wallet::NetworkWallet()) {
723 // Auto create these wallets.
724 if (appid.isEmpty()) {
725 kpd->setPrompt(
726 i18n("KDE has requested to open the wallet. This is used to store sensitive data in a "
727 "secure fashion. Please enter a password to use with this wallet or click cancel to "
728 "deny the application's request."));
729 } else {
730 kpd->setPrompt(
731 i18n("<qt>The application '<b>%1</b>' has requested to open the KDE wallet. This is "
732 "used to store sensitive data in a secure fashion. Please enter a password to use "
733 "with this wallet or click cancel to deny the application's request.</qt>",
734 appid.toHtmlEscaped()));
735 }
736 } else {
737 if (appid.length() == 0) {
738 kpd->setPrompt(
739 i18n("<qt>KDE has requested to create a new wallet named '<b>%1</b>'. Please choose a "
740 "password for this wallet, or cancel to deny the application's request.</qt>",
741 wallet.toHtmlEscaped()));
742 } else {
743 kpd->setPrompt(
744 i18n("<qt>The application '<b>%1</b>' has requested to create a new wallet named '<b>%2</b>'. "
745 "Please choose a password for this wallet, or cancel to deny the application's request.</qt>",
746 appid.toHtmlEscaped(),
747 wallet.toHtmlEscaped()));
748 }
749 }
750 kpd->setWindowTitle(i18n("KDE Wallet Service"));
751 // KF5 FIXME what should we use now instead of this:
752 // kpd->setButtonGuiItem(KDialog::Ok,KGuiItem(i18n("C&reate"),"document-new"));
753 kpd->setIcon(QIcon::fromTheme(QStringLiteral("kwalletmanager")));
754 while (!b->isOpen()) {
755 setupDialog(kpd, w, appid, modal);
756 if (kpd->exec() == QDialog::Accepted) {
757 password = kpd->password();
758 int rc = b->open(password.toUtf8());
759 if (!b->isOpen()) {
760 kpd->setPrompt(i18n("<qt>Error opening the wallet '<b>%1</b>'. Please try again.<br />(Error code %2: %3)</qt>",
761 wallet.toHtmlEscaped(),
762 rc,
763 KWallet::Backend::openRCToString(rc)));
764 }
765 } else {
766 break;
767 }
768 }
769 delete kpd;
770#ifdef HAVE_GPGMEPP
771 }
772#endif
773 }
774
775 if ((b->cipherType() == KWallet::BACKEND_CIPHER_BLOWFISH) && !emptyPass && (password.isNull() || !b->isOpen())) {
776 delete b;
777 return -1;
778 }
779
780 if (emptyPass && !isAuthorizedApp(appid, wallet, w)) {
781 delete b;
782 return -1;
783 }
784
785 _wallets.insert(rc = generateHandle(), b);
786 _sessions.addSession(appid, service, rc);
787 _syncTimers.addTimer(rc, _syncTime);
788
789 if (brandNew) {
790 createFolder(rc, KWallet::Wallet::PasswordFolder(), appid);
791 createFolder(rc, KWallet::Wallet::FormDataFolder(), appid);
792 }
793
794 b->ref();
795 if (_closeIdle) {
796 _closeTimers.addTimer(rc, _idleTime);
797 }
798 if (brandNew) {
799 Q_EMIT walletCreated(wallet);
800 }
801 Q_EMIT walletOpened(wallet);
802 if (_wallets.count() == 1 && _launchManager) {
803 startManagerForKwalletd();
804 }
805 } else {
806 // prematurely add a reference so that the wallet does not close while
807 // the
808 // authorization dialog is being shown.
809 walletInfo.second->ref();
810 bool isAuthorized = _sessions.hasSession(appid, rc) || isAuthorizedApp(appid, wallet, w);
811 // as the wallet might have been forcefully closed, find it again to
812 // make sure it's
813 // still available (isAuthorizedApp might show a dialog).
814 walletInfo = findWallet(wallet);
815 if (!isAuthorized) {
816 if (walletInfo.first != -1) {
817 walletInfo.second->deref();
818 // check if the wallet should be closed now.
819 internalClose(walletInfo.second, walletInfo.first, false);
820 }
821 return -1;
822 } else {
823 if (walletInfo.first != -1) {
824 _sessions.addSession(appid, service, rc);
825 } else {
826 // wallet was forcefully closed.
827 return -1;
828 }
829 }
830 }
831
832 return rc;
833}
834
835bool KWalletD::isAuthorizedApp(const QString &appid, const QString &wallet, WId w)
836{
837 if (!_openPrompt) {
838 return true;
839 }
840
841 int response = 0;
842
843 QString thisApp;
844 if (appid.isEmpty()) {
845 thisApp = QStringLiteral("KDE System");
846 } else {
847 thisApp = appid;
848 }
849
850 if (!implicitAllow(wallet, thisApp)) {
851 KConfigGroup cfg = KSharedConfig::openConfig(QStringLiteral("kwalletrc"))->group("Auto Allow");
852 if (!cfg.isEntryImmutable(wallet)) {
853 KBetterThanKDialog *dialog = new KBetterThanKDialog;
854 dialog->setWindowTitle(i18n("KDE Wallet Service"));
855 if (appid.isEmpty()) {
856 dialog->setLabel(i18n("<qt>KDE has requested access to the open wallet '<b>%1</b>'.</qt>", wallet.toHtmlEscaped()));
857 } else {
858 dialog->setLabel(i18n("<qt>The application '<b>%1</b>' has requested access to the open wallet '<b>%2</b>'.</qt>",
859 appid.toHtmlEscaped(),
860 wallet.toHtmlEscaped()));
861 }
862 setupDialog(dialog, w, appid, false);
863 response = dialog->exec();
864 delete dialog;
865 }
866 }
867
868 if (response == 0 || response == 1) {
869 if (response == 1) {
870 KConfigGroup cfg = KSharedConfig::openConfig(QStringLiteral("kwalletrc"))->group("Auto Allow");
871 QStringList apps = cfg.readEntry(wallet, QStringList());
872 if (!apps.contains(thisApp)) {
873 if (cfg.isEntryImmutable(wallet)) {
874 return false;
875 }
876 apps += thisApp;
877 _implicitAllowMap[wallet] += thisApp;
878 cfg.writeEntry(wallet, apps);
879 cfg.sync();
880 }
881 }
882 } else if (response == 3) {
883 KConfigGroup cfg = KSharedConfig::openConfig(QStringLiteral("kwalletrc"))->group("Auto Deny");
884 QStringList apps = cfg.readEntry(wallet, QStringList());
885 if (!apps.contains(thisApp)) {
886 apps += thisApp;
887 _implicitDenyMap[wallet] += thisApp;
888 cfg.writeEntry(wallet, apps);
889 cfg.sync();
890 }
891 return false;
892 } else {
893 return false;
894 }
895 return true;
896}
897
898int KWalletD::deleteWallet(const QString &wallet)
899{
900 int result = -1;
901 QString path = KWallet::Backend::getSaveLocation() + "/" + encodeWalletName(wallet) + ".kwl";
902 QString pathSalt = KWallet::Backend::getSaveLocation() + "/" + encodeWalletName(wallet) + ".salt";
903
904 if (QFile::exists(path)) {
905 const QPair<int, KWallet::Backend *> walletInfo = findWallet(wallet);
906 internalClose(walletInfo.second, walletInfo.first, true);
907 QFile::remove(path);
908 Q_EMIT walletDeleted(wallet);
909 // also delete access control entries
910 KConfigGroup cfgAllow = KSharedConfig::openConfig(QStringLiteral("kwalletrc"))->group("Auto Allow");
911 cfgAllow.deleteEntry(wallet);
912
913 KConfigGroup cfgDeny = KSharedConfig::openConfig(QStringLiteral("kwalletrc"))->group("Auto Deny");
914 cfgDeny.deleteEntry(wallet);
915
916 if (QFile::exists(pathSalt)) {
917 QFile::remove(pathSalt);
918 }
919
920 result = 0;
921 }
922
923 return result;
924}
925
926void KWalletD::changePassword(const QString &wallet, qlonglong wId, const QString &appid)
927{
928 KWalletTransaction *xact = new KWalletTransaction(connection());
929
930 message().setDelayedReply(true);
931 xact->message = message();
932 // TODO GPG this shouldn't be allowed on a GPG managed wallet; a warning
933 // should be displayed about this
934
935 xact->appid = appid;
936 xact->wallet = wallet;
937 xact->wId = wId;
938 xact->modal = false;
939 xact->tType = KWalletTransaction::ChangePassword;
940
941 _transactions.append(xact);
942
943 QTimer::singleShot(0, this, SLOT(processTransactions()));
944 checkActiveDialog();
945 checkActiveDialog();
946}
947
948void KWalletD::initiateSync(int handle)
949{
950 // add a timer and reset it right away
951 _syncTimers.addTimer(handle, _syncTime);
952 _syncTimers.resetTimer(handle, _syncTime);
953}
954
955void KWalletD::doTransactionChangePassword(const QString &appid, const QString &wallet, qlonglong wId)
956{
957 const QPair<int, KWallet::Backend *> walletInfo = findWallet(wallet);
958 int handle = walletInfo.first;
959 KWallet::Backend *w = walletInfo.second;
960
961 bool reclose = false;
962 if (!w) {
963 handle = doTransactionOpen(appid, wallet, false, wId, false, QLatin1String(""));
964 if (-1 == handle) {
965 KMessageBox::errorWId((WId)wId,
966 i18n("Unable to open wallet. The wallet must be opened in order to change the password."),
967 i18n("KDE Wallet Service"));
968 return;
969 }
970
971 w = _wallets.value(handle);
972 reclose = true;
973 }
974
975 assert(w);
976
977#ifdef HAVE_GPGMEPP
978 if (w->cipherType() == KWallet::BACKEND_CIPHER_GPG) {
979 QString keyID = w->gpgKey().shortKeyID();
980 assert(!keyID.isNull());
981 KMessageBox::errorWId((WId)wId,
982 i18n("<qt>The <b>%1</b> wallet is encrypted using GPG key <b>%2</b>. Please use <b>GPG</b> tools (such "
983 "as <b>kleopatra</b>) to change the passphrase associated to that key.</qt>",
984 wallet.toHtmlEscaped(),
985 keyID));
986 } else {
987#endif
989 kpd->setPrompt(i18n("<qt>Please choose a new password for the wallet '<b>%1</b>'.</qt>", wallet.toHtmlEscaped()));
990 kpd->setWindowTitle(i18n("KDE Wallet Service"));
991 kpd->setAllowEmptyPasswords(true);
993 kpd->setBackgroundWarningColor(colorScheme.background(KColorScheme::NegativeBackground).color());
994 setupDialog(kpd, (WId)wId, appid, false);
995 if (kpd->exec() == QDialog::Accepted && kpd) {
996 QString p = kpd->password();
997 if (!p.isNull()) {
998 w->setPassword(p.toUtf8());
999 int rc = w->close(true);
1000 if (rc < 0) {
1001 KMessageBox::errorWId((WId)wId, i18n("Error re-encrypting the wallet. Password was not changed."), i18n("KDE Wallet Service"));
1002 reclose = true;
1003 } else {
1004 rc = w->open(p.toUtf8());
1005 if (rc < 0) {
1006 KMessageBox::errorWId((WId)wId, i18n("Error reopening the wallet. Data may be lost."), i18n("KDE Wallet Service"));
1007 reclose = true;
1008 }
1009 }
1010 }
1011 }
1012
1013 delete kpd;
1014#ifdef HAVE_GPGMEPP
1015 }
1016#endif
1017
1018 if (reclose) {
1019 internalClose(w, handle, true);
1020 }
1021}
1022
1023int KWalletD::close(const QString &wallet, bool force)
1024{
1025 const QPair<int, KWallet::Backend *> walletInfo = findWallet(wallet);
1026 int handle = walletInfo.first;
1027 KWallet::Backend *w = walletInfo.second;
1028
1029 return internalClose(w, handle, force);
1030}
1031
1032int KWalletD::internalClose(KWallet::Backend *const w, const int handle, const bool force, const bool saveBeforeClose)
1033{
1034 if (w) {
1035 const QString &wallet = w->walletName();
1036 if ((w->refCount() == 0 && !_leaveOpen) || force) {
1037 // this is only a safety measure. sessions should be gone already.
1038 _sessions.removeAllSessions(handle);
1039 if (_closeIdle) {
1040 _closeTimers.removeTimer(handle);
1041 }
1042 _syncTimers.removeTimer(handle);
1043 _wallets.remove(handle);
1044 w->close(saveBeforeClose);
1045 doCloseSignals(handle, wallet);
1046 delete w;
1047 return 0;
1048 }
1049 return 1;
1050 }
1051
1052 return -1;
1053}
1054
1055int KWalletD::close(int handle, bool force, const QString &appid, const QDBusMessage &message)
1056{
1057 KWallet::Backend *w = _wallets.value(handle);
1058
1059 if (w) {
1060 if (_sessions.hasSession(appid, handle)) {
1061 // remove one handle for the application
1062 bool removed = _sessions.removeSession(appid, message.service(), handle);
1063 // alternatively try sessionless
1064 if (removed || _sessions.removeSession(appid, QLatin1String(""), handle)) {
1065 w->deref();
1066 }
1067 return internalClose(w, handle, force);
1068 }
1069 return 1; // not closed, handle unknown
1070 }
1071 return -1; // not open to begin with, or other error
1072}
1073
1074int KWalletD::close(int handle, bool force, const QString &appid)
1075{
1076 return close(handle, force, appid, message());
1077}
1078
1079bool KWalletD::isOpen(const QString &wallet)
1080{
1081 const QPair<int, KWallet::Backend *> walletInfo = findWallet(wallet);
1082 return walletInfo.second != nullptr;
1083}
1084
1085bool KWalletD::isOpen(int handle)
1086{
1087 if (handle == 0) {
1088 return false;
1089 }
1090
1091 KWallet::Backend *rc = _wallets.value(handle);
1092
1093 if (rc == nullptr && ++_failed > 5) {
1094 _failed = 0;
1095 QTimer::singleShot(0, this, SLOT(notifyFailures()));
1096 } else if (rc != nullptr) {
1097 _failed = 0;
1098 }
1099
1100 return rc != nullptr;
1101}
1102
1103QStringList KWalletD::wallets() const
1104{
1105 QString path = KWallet::Backend::getSaveLocation();
1106 QDir dir(path, QStringLiteral("*.kwl"));
1107 QStringList rc;
1108
1109 dir.setFilter(QDir::Files | QDir::Hidden);
1110
1111 const auto list = dir.entryInfoList();
1112 for (const QFileInfo &fi : list) {
1113 QString fn = fi.fileName();
1114 if (fn.endsWith(QLatin1String(".kwl"))) {
1115 fn.truncate(fn.length() - 4);
1116 }
1117 rc += decodeWalletName(fn);
1118 }
1119 return rc;
1120}
1121
1122void KWalletD::sync(int handle, const QString &appid)
1123{
1124 KWallet::Backend *b;
1125
1126 // get the wallet and check if we have a password for it (safety measure)
1127 if ((b = getWallet(appid, handle))) {
1128 QString wallet = b->walletName();
1129 b->sync(0);
1130 }
1131}
1132
1133void KWalletD::timedOutSync(int handle)
1134{
1135 _syncTimers.removeTimer(handle);
1136 if (_wallets.contains(handle) && _wallets[handle]) {
1137 _wallets[handle]->sync(0);
1138 } else {
1139 qDebug("wallet not found for sync!");
1140 }
1141}
1142
1143void KWalletD::doTransactionOpenCancelled(const QString &appid, const QString &wallet, const QString &service)
1144{
1145 // there will only be one session left to remove - all others
1146 // have already been removed in slotServiceOwnerChanged and all
1147 // transactions for opening new sessions have been deleted.
1148 if (!_sessions.hasSession(appid)) {
1149 return;
1150 }
1151
1152 const QPair<int, KWallet::Backend *> walletInfo = findWallet(wallet);
1153 int handle = walletInfo.first;
1154 KWallet::Backend *b = walletInfo.second;
1155 if (handle != -1 && b) {
1156 b->deref();
1157 internalClose(b, handle, false);
1158 }
1159
1160 // close the session in case the wallet hasn't been closed yet
1161 _sessions.removeSession(appid, service, handle);
1162}
1163
1164QStringList KWalletD::folderList(int handle, const QString &appid)
1165{
1166 KWallet::Backend *b;
1167
1168 if ((b = getWallet(appid, handle))) {
1169 return b->folderList();
1170 }
1171
1172 return QStringList();
1173}
1174
1175bool KWalletD::hasFolder(int handle, const QString &f, const QString &appid)
1176{
1177 KWallet::Backend *b;
1178
1179 if ((b = getWallet(appid, handle))) {
1180 return b->hasFolder(f);
1181 }
1182
1183 return false;
1184}
1185
1186bool KWalletD::removeFolder(int handle, const QString &f, const QString &appid)
1187{
1188 KWallet::Backend *b;
1189
1190 if ((b = getWallet(appid, handle))) {
1191 bool rc = b->removeFolder(f);
1192 initiateSync(handle);
1193 Q_EMIT folderListUpdated(b->walletName());
1194 return rc;
1195 }
1196
1197 return false;
1198}
1199
1200bool KWalletD::createFolder(int handle, const QString &f, const QString &appid)
1201{
1202 KWallet::Backend *b;
1203
1204 if ((b = getWallet(appid, handle))) {
1205 bool rc = b->createFolder(f);
1206 initiateSync(handle);
1207 Q_EMIT folderListUpdated(b->walletName());
1208 return rc;
1209 }
1210
1211 return false;
1212}
1213
1214QByteArray KWalletD::readMap(int handle, const QString &folder, const QString &key, const QString &appid)
1215{
1216 KWallet::Backend *b;
1217
1218 if ((b = getWallet(appid, handle))) {
1219 b->setFolder(folder);
1220 KWallet::Entry *e = b->readEntry(key);
1221 if (e && e->type() == KWallet::Wallet::Map) {
1222 return e->map();
1223 }
1224 }
1225
1226 return QByteArray();
1227}
1228
1229#if KWALLET_BUILD_DEPRECATED_SINCE(5, 72)
1230QVariantMap KWalletD::readMapList(int handle, const QString &folder, const QString &key, const QString &appid)
1231{
1232 KWallet::Backend *b;
1233
1234 if ((b = getWallet(appid, handle))) {
1235 b->setFolder(folder);
1236 QVariantMap rc;
1237 const auto lst = b->readEntryList(key);
1238 for (KWallet::Entry *entry : lst) {
1239 if (entry->type() == KWallet::Wallet::Map) {
1240 rc.insert(entry->key(), entry->map());
1241 }
1242 }
1243 return rc;
1244 }
1245
1246 return QVariantMap();
1247}
1248#endif
1249
1250QVariantMap KWalletD::mapList(int handle, const QString &folder, const QString &appid)
1251{
1252 QVariantMap rc;
1253
1254 KWallet::Backend *backend = getWallet(appid, handle);
1255 if (backend) {
1256 backend->setFolder(folder);
1257 const QList<KWallet::Entry *> lst = backend->entriesList();
1258 for (KWallet::Entry *entry : lst) {
1259 if (entry->type() == KWallet::Wallet::Map) {
1260 rc.insert(entry->key(), entry->map());
1261 }
1262 }
1263 }
1264
1265 return rc;
1266}
1267
1268QByteArray KWalletD::readEntry(int handle, const QString &folder, const QString &key, const QString &appid)
1269{
1270 KWallet::Backend *b;
1271
1272 if ((b = getWallet(appid, handle))) {
1273 b->setFolder(folder);
1274 KWallet::Entry *e = b->readEntry(key);
1275 if (e) {
1276 return e->value();
1277 }
1278 }
1279
1280 return QByteArray();
1281}
1282
1283#if KWALLET_BUILD_DEPRECATED_SINCE(5, 72)
1284QVariantMap KWalletD::readEntryList(int handle, const QString &folder, const QString &key, const QString &appid)
1285{
1286 KWallet::Backend *b;
1287
1288 if ((b = getWallet(appid, handle))) {
1289 b->setFolder(folder);
1290 QVariantMap rc;
1291 const auto lst = b->readEntryList(key);
1292 for (KWallet::Entry *entry : lst) {
1293 rc.insert(entry->key(), entry->value());
1294 }
1295 return rc;
1296 }
1297
1298 return QVariantMap();
1299}
1300#endif
1301
1302QVariantMap KWalletD::entriesList(int handle, const QString &folder, const QString &appid)
1303{
1304 QVariantMap rc;
1305
1306 KWallet::Backend *backend = getWallet(appid, handle);
1307 if (backend) {
1308 backend->setFolder(folder);
1309 const QList<KWallet::Entry *> lst = backend->entriesList();
1310 for (KWallet::Entry *entry : lst) {
1311 rc.insert(entry->key(), entry->value());
1312 }
1313 }
1314
1315 return rc;
1316}
1317
1318QStringList KWalletD::entryList(int handle, const QString &folder, const QString &appid)
1319{
1320 KWallet::Backend *b;
1321
1322 if ((b = getWallet(appid, handle))) {
1323 b->setFolder(folder);
1324 return b->entryList();
1325 }
1326
1327 return QStringList();
1328}
1329
1330QString KWalletD::readPassword(int handle, const QString &folder, const QString &key, const QString &appid)
1331{
1332 KWallet::Backend *b;
1333
1334 if ((b = getWallet(appid, handle))) {
1335 b->setFolder(folder);
1336 KWallet::Entry *e = b->readEntry(key);
1337 if (e && e->type() == KWallet::Wallet::Password) {
1338 return e->password();
1339 }
1340 }
1341
1342 return QString();
1343}
1344
1345#if KWALLET_BUILD_DEPRECATED_SINCE(5, 72)
1346QVariantMap KWalletD::readPasswordList(int handle, const QString &folder, const QString &key, const QString &appid)
1347{
1348 KWallet::Backend *b;
1349
1350 if ((b = getWallet(appid, handle))) {
1351 b->setFolder(folder);
1352 QVariantMap rc;
1353 const auto lst = b->readEntryList(key);
1354 for (KWallet::Entry *entry : lst) {
1355 if (entry->type() == KWallet::Wallet::Password) {
1356 rc.insert(entry->key(), entry->password());
1357 }
1358 }
1359 return rc;
1360 }
1361
1362 return QVariantMap();
1363}
1364#endif
1365
1366QVariantMap KWalletD::passwordList(int handle, const QString &folder, const QString &appid)
1367{
1368 QVariantMap rc;
1369
1370 KWallet::Backend *backend = getWallet(appid, handle);
1371 if (backend) {
1372 backend->setFolder(folder);
1373 const QList<KWallet::Entry *> lst = backend->entriesList();
1374 for (KWallet::Entry *entry : lst) {
1375 if (entry->type() == KWallet::Wallet::Password) {
1376 rc.insert(entry->key(), entry->password());
1377 }
1378 }
1379 }
1380
1381 return rc;
1382}
1383
1384int KWalletD::writeMap(int handle, const QString &folder, const QString &key, const QByteArray &value, const QString &appid)
1385{
1386 KWallet::Backend *b;
1387
1388 if ((b = getWallet(appid, handle))) {
1389 b->setFolder(folder);
1390 KWallet::Entry e;
1391 e.setKey(key);
1392 e.setValue(value);
1393 e.setType(KWallet::Wallet::Map);
1394 b->writeEntry(&e);
1395 initiateSync(handle);
1396 emitFolderUpdated(b->walletName(), folder);
1397 emitEntryUpdated(b->walletName(), folder, key);
1398 return 0;
1399 }
1400
1401 return -1;
1402}
1403
1404int KWalletD::writeEntry(int handle, const QString &folder, const QString &key, const QByteArray &value, const QString &appid)
1405{
1406 KWallet::Backend *b;
1407
1408 if ((b = getWallet(appid, handle))) {
1409 b->setFolder(folder);
1410 KWallet::Entry e;
1411 e.setKey(key);
1412 e.setValue(value);
1413 e.setType(KWallet::Wallet::Stream);
1414 b->writeEntry(&e);
1415 initiateSync(handle);
1416 emitFolderUpdated(b->walletName(), folder);
1417 emitEntryUpdated(b->walletName(), folder, key);
1418 return 0;
1419 }
1420
1421 return -1;
1422}
1423
1424int KWalletD::writeEntry(int handle, const QString &folder, const QString &key, const QByteArray &value, int entryType, const QString &appid)
1425{
1426 KWallet::Backend *b;
1427
1428 if ((b = getWallet(appid, handle))) {
1429 b->setFolder(folder);
1430 KWallet::Entry e;
1431 e.setKey(key);
1432 e.setValue(value);
1433 e.setType(KWallet::Wallet::EntryType(entryType));
1434 b->writeEntry(&e);
1435 initiateSync(handle);
1436 emitFolderUpdated(b->walletName(), folder);
1437 return 0;
1438 }
1439
1440 return -1;
1441}
1442
1443int KWalletD::writePassword(int handle, const QString &folder, const QString &key, const QString &value, const QString &appid)
1444{
1445 KWallet::Backend *b;
1446
1447 if ((b = getWallet(appid, handle))) {
1448 b->setFolder(folder);
1449 KWallet::Entry e;
1450 e.setKey(key);
1451 e.setValue(value);
1452 e.setType(KWallet::Wallet::Password);
1453 b->writeEntry(&e);
1454 initiateSync(handle);
1455 emitFolderUpdated(b->walletName(), folder);
1456 emitEntryUpdated(b->walletName(), folder, key);
1457 return 0;
1458 }
1459
1460 return -1;
1461}
1462
1463int KWalletD::entryType(int handle, const QString &folder, const QString &key, const QString &appid)
1464{
1465 KWallet::Backend *b;
1466
1467 if ((b = getWallet(appid, handle))) {
1468 if (!b->hasFolder(folder)) {
1469 return KWallet::Wallet::Unknown;
1470 }
1471 b->setFolder(folder);
1472 if (b->hasEntry(key)) {
1473 return b->readEntry(key)->type();
1474 }
1475 }
1476
1477 return KWallet::Wallet::Unknown;
1478}
1479
1480bool KWalletD::hasEntry(int handle, const QString &folder, const QString &key, const QString &appid)
1481{
1482 KWallet::Backend *b;
1483
1484 if ((b = getWallet(appid, handle))) {
1485 if (!b->hasFolder(folder)) {
1486 return false;
1487 }
1488 b->setFolder(folder);
1489 return b->hasEntry(key);
1490 }
1491
1492 return false;
1493}
1494
1495int KWalletD::removeEntry(int handle, const QString &folder, const QString &key, const QString &appid)
1496{
1497 KWallet::Backend *b;
1498
1499 if ((b = getWallet(appid, handle))) {
1500 if (!b->hasFolder(folder)) {
1501 return 0;
1502 }
1503 b->setFolder(folder);
1504 bool rc = b->removeEntry(key);
1505 initiateSync(handle);
1506 emitFolderUpdated(b->walletName(), folder);
1507 emitEntryDeleted(b->walletName(), folder, key);
1508 return rc ? 0 : -3;
1509 }
1510
1511 return -1;
1512}
1513
1514void KWalletD::slotServiceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
1515{
1516 Q_UNUSED(name);
1517 qCDebug(KWALLETD_LOG) << "slotServiceOwnerChanged " << name << ", " << oldOwner << ", " << newOwner;
1518
1519 if (!newOwner.isEmpty()) {
1520 return; // no application exit, don't care.
1521 }
1522
1523 // as we don't have the application id we have to cycle
1524 // all sessions. As an application can basically open wallets
1525 // with several appids, we can't stop if we found one.
1526 QString service(oldOwner);
1527 const QList<KWalletAppHandlePair> sessremove(_sessions.findSessions(service));
1528 KWallet::Backend *b = nullptr;
1529
1530 // check all sessions for wallets to close
1531 for (const KWalletAppHandlePair &s : sessremove) {
1532 b = getWallet(s.first, s.second);
1533 if (b) {
1534 b->deref();
1535 internalClose(b, s.second, false);
1536 }
1537 }
1538
1539 // remove all the sessions in case they aren't gone yet
1540 for (const KWalletAppHandlePair &s : sessremove) {
1541 _sessions.removeSession(s.first, service, s.second);
1542 }
1543
1544 // cancel all open-transactions still running for the service
1546 for (tit = _transactions.begin(); tit != _transactions.end(); ++tit) {
1547 if ((*tit)->tType == KWalletTransaction::Open && (*tit)->service == oldOwner) {
1548 delete (*tit);
1549 *tit = nullptr;
1550 }
1551 }
1552 _transactions.removeAll(nullptr);
1553
1554 // if there's currently an open-transaction being handled,
1555 // mark it as cancelled.
1556 if (_curtrans && _curtrans->tType == KWalletTransaction::Open && _curtrans->service == oldOwner) {
1557 qCDebug(KWALLETD_LOG) << "Cancelling current transaction!";
1558 _curtrans->cancelled = true;
1559 }
1560 _serviceWatcher.removeWatchedService(oldOwner);
1561}
1562
1563KWallet::Backend *KWalletD::getWallet(const QString &appid, int handle)
1564{
1565 if (handle == 0) {
1566 return nullptr;
1567 }
1568
1569 KWallet::Backend *w = _wallets.value(handle);
1570
1571 if (w) { // the handle is valid
1572 if (_sessions.hasSession(appid, handle)) {
1573 // the app owns this handle
1574 _failed = 0;
1575 if (_closeIdle) {
1576 _closeTimers.resetTimer(handle, _idleTime);
1577 }
1578 return w;
1579 }
1580 }
1581
1582 if (++_failed > 5) {
1583 _failed = 0;
1584 QTimer::singleShot(0, this, SLOT(notifyFailures()));
1585 }
1586
1587 return nullptr;
1588}
1589
1590void KWalletD::notifyFailures()
1591{
1592 if (!_showingFailureNotify) {
1593 _showingFailureNotify = true;
1595 i18n("There have been repeated failed attempts to gain access to a wallet. An application may be misbehaving."),
1596 i18n("KDE Wallet Service"));
1597 _showingFailureNotify = false;
1598 }
1599}
1600
1601void KWalletD::doCloseSignals(int handle, const QString &wallet)
1602{
1603 Q_EMIT walletClosed(handle);
1604 Q_EMIT walletClosedId(handle);
1605
1606 Q_EMIT walletClosed(wallet);
1607 if (_wallets.isEmpty()) {
1608 Q_EMIT allWalletsClosed();
1609 }
1610}
1611
1612int KWalletD::renameEntry(int handle, const QString &folder, const QString &oldName, const QString &newName, const QString &appid)
1613{
1614 KWallet::Backend *b;
1615
1616 if ((b = getWallet(appid, handle))) {
1617 b->setFolder(folder);
1618 int rc = b->renameEntry(oldName, newName);
1619 initiateSync(handle);
1620 emitFolderUpdated(b->walletName(), folder);
1621 emitEntryRenamed(b->walletName(), folder, oldName, newName);
1622 return rc;
1623 }
1624
1625 return -1;
1626}
1627
1628int KWalletD::renameWallet(const QString &oldName, const QString &newName)
1629{
1630 const QPair<int, KWallet::Backend *> walletInfo = findWallet(oldName);
1631 return walletInfo.second->renameWallet(newName);
1632}
1633
1634QStringList KWalletD::users(const QString &wallet) const
1635{
1636 const QPair<int, KWallet::Backend *> walletInfo = findWallet(wallet);
1637 return _sessions.getApplications(walletInfo.first);
1638}
1639
1640bool KWalletD::disconnectApplication(const QString &wallet, const QString &application)
1641{
1642 const QPair<int, KWallet::Backend *> walletInfo = findWallet(wallet);
1643 int handle = walletInfo.first;
1644 KWallet::Backend *backend = walletInfo.second;
1645
1646 if (handle != -1 && _sessions.hasSession(application, handle)) {
1647 int removed = _sessions.removeAllSessions(application, handle);
1648
1649 for (int i = 0; i < removed; ++i) {
1650 backend->deref();
1651 }
1652 internalClose(backend, handle, false);
1653
1654 Q_EMIT applicationDisconnected(wallet, application);
1655 return true;
1656 }
1657
1658 return false;
1659}
1660
1661void KWalletD::emitFolderUpdated(const QString &wallet, const QString &folder)
1662{
1663 Q_EMIT folderUpdated(wallet, folder);
1664}
1665
1666void KWalletD::emitEntryUpdated(const QString &wallet, const QString &folder, const QString &key)
1667{
1668 Q_EMIT entryUpdated(wallet, folder, key);
1669}
1670
1671void KWalletD::emitEntryRenamed(const QString &wallet, const QString &folder, const QString &oldName, const QString &newName)
1672{
1673 Q_EMIT entryRenamed(wallet, folder, oldName, newName);
1674}
1675
1676void KWalletD::emitEntryDeleted(const QString &wallet, const QString &folder, const QString &key)
1677{
1678 Q_EMIT entryDeleted(wallet, folder, key);
1679}
1680
1681void KWalletD::emitWalletListDirty()
1682{
1683 const QStringList walletsInDisk = wallets();
1684 const auto lst = _wallets.values();
1685 for (auto i : lst) {
1686 if (!walletsInDisk.contains(i->walletName())) {
1687 internalClose(i, _wallets.key(i), true, false);
1688 }
1689 }
1690 Q_EMIT walletListDirty();
1691}
1692
1693void KWalletD::reconfigure()
1694{
1695 KConfig cfg(QStringLiteral("kwalletrc"));
1696 KConfigGroup walletGroup(&cfg, "Wallet");
1697 _firstUse = walletGroup.readEntry("First Use", true);
1698 _enabled = walletGroup.readEntry("Enabled", true);
1699 _launchManager = walletGroup.readEntry("Launch Manager", false);
1700 _leaveOpen = walletGroup.readEntry("Leave Open", true);
1701 bool idleSave = _closeIdle;
1702 _closeIdle = walletGroup.readEntry("Close When Idle", false);
1703 _openPrompt = walletGroup.readEntry("Prompt on Open", false);
1704 int timeSave = _idleTime;
1705 // in minutes!
1706 _idleTime = walletGroup.readEntry("Idle Timeout", 10) * 60 * 1000;
1707#ifdef Q_WS_X11
1708 if (walletGroup.readEntry("Close on Screensaver", false)) {
1709 // BUG 254273 : if kwalletd starts before the screen saver, then the
1710 // connection fails and kwalletd never receives it's notifications
1711 // To fix this, we use a timer and perform periodic connection
1712 // attempts until connection succeeds
1713 QTimer::singleShot(0, this, SLOT(connectToScreenSaver()));
1714 } else {
1715 if (screensaver && screensaver->isValid()) {
1716 screensaver->disconnect(SIGNAL(ActiveChanged(bool)), this, SLOT(screenSaverChanged(bool)));
1717 delete screensaver;
1718 screensaver = 0;
1719 }
1720 }
1721#endif
1722 // Handle idle changes
1723 if (_closeIdle) {
1724 if (_idleTime != timeSave) { // Timer length changed
1725 Wallets::const_iterator it = _wallets.constBegin();
1726 const Wallets::const_iterator end = _wallets.constEnd();
1727 for (; it != end; ++it) {
1728 _closeTimers.resetTimer(it.key(), _idleTime);
1729 }
1730 }
1731
1732 if (!idleSave) { // add timers for all the wallets
1733 Wallets::const_iterator it = _wallets.constBegin();
1734 const Wallets::const_iterator end = _wallets.constEnd();
1735 for (; it != end; ++it) {
1736 _closeTimers.addTimer(it.key(), _idleTime);
1737 }
1738 }
1739 } else {
1740 _closeTimers.clear();
1741 }
1742
1743 // Update the implicit allow stuff
1744 _implicitAllowMap.clear();
1745 const KConfigGroup autoAllowGroup(&cfg, "Auto Allow");
1746 QStringList entries = autoAllowGroup.entryMap().keys();
1747 for (QStringList::const_iterator i = entries.constBegin(); i != entries.constEnd(); ++i) {
1748 _implicitAllowMap[*i] = autoAllowGroup.readEntry(*i, QStringList());
1749 }
1750
1751 // Update the implicit allow stuff
1752 _implicitDenyMap.clear();
1753 const KConfigGroup autoDenyGroup(&cfg, "Auto Deny");
1754 entries = autoDenyGroup.entryMap().keys();
1755 for (QStringList::const_iterator i = entries.constBegin(); i != entries.constEnd(); ++i) {
1756 _implicitDenyMap[*i] = autoDenyGroup.readEntry(*i, QStringList());
1757 }
1758
1759 // Update if wallet was enabled/disabled
1760 if (!_enabled) { // close all wallets
1761 while (!_wallets.isEmpty()) {
1762 Wallets::const_iterator it = _wallets.constBegin();
1763 internalClose(it.value(), it.key(), true);
1764 }
1766 }
1767}
1768
1769bool KWalletD::isEnabled() const
1770{
1771 return _enabled;
1772}
1773
1774bool KWalletD::folderDoesNotExist(const QString &wallet, const QString &folder)
1775{
1776 if (!wallets().contains(wallet)) {
1777 return true;
1778 }
1779
1780 const QPair<int, KWallet::Backend *> walletInfo = findWallet(wallet);
1781 if (walletInfo.second) {
1782 return walletInfo.second->folderDoesNotExist(folder);
1783 }
1784
1785 KWallet::Backend *b = new KWallet::Backend(wallet);
1786 b->open(QByteArray());
1787 bool rc = b->folderDoesNotExist(folder);
1788 delete b;
1789 return rc;
1790}
1791
1792bool KWalletD::keyDoesNotExist(const QString &wallet, const QString &folder, const QString &key)
1793{
1794 if (!wallets().contains(wallet)) {
1795 return true;
1796 }
1797
1798 const QPair<int, KWallet::Backend *> walletInfo = findWallet(wallet);
1799 if (walletInfo.second) {
1800 return walletInfo.second->entryDoesNotExist(folder, key);
1801 }
1802
1803 KWallet::Backend *b = new KWallet::Backend(wallet);
1804 b->open(QByteArray());
1805 bool rc = b->entryDoesNotExist(folder, key);
1806 delete b;
1807 return rc;
1808}
1809
1810bool KWalletD::implicitAllow(const QString &wallet, const QString &app)
1811{
1812 return _implicitAllowMap[wallet].contains(app);
1813}
1814
1815bool KWalletD::implicitDeny(const QString &wallet, const QString &app)
1816{
1817 return _implicitDenyMap[wallet].contains(app);
1818}
1819
1820void KWalletD::timedOutClose(int id)
1821{
1822 KWallet::Backend *w = _wallets.value(id);
1823 if (w) {
1824 internalClose(w, id, true);
1825 }
1826}
1827
1828void KWalletD::closeAllWallets()
1829{
1830 Wallets walletsCopy = _wallets;
1831
1832 Wallets::const_iterator it = walletsCopy.constBegin();
1833 const Wallets::const_iterator end = walletsCopy.constEnd();
1834 for (; it != end; ++it) {
1835 internalClose(it.value(), it.key(), true);
1836 }
1837
1838 walletsCopy.clear();
1839
1840 // All of this should be basically noop. Let's just be safe.
1841 _wallets.clear();
1842}
1843
1844QString KWalletD::networkWallet()
1845{
1847}
1848
1849QString KWalletD::localWallet()
1850{
1852}
1853
1854void KWalletD::screenSaverChanged(bool s)
1855{
1856 if (s) {
1857 closeAllWallets();
1858 }
1859}
1860
1861void KWalletD::activatePasswordDialog()
1862{
1863 checkActiveDialog();
1864}
1865
1866int KWalletD::pamOpen(const QString &wallet, const QByteArray &passwordHash, int sessionTimeout)
1867{
1868 if (_processing) {
1869 return -1;
1870 }
1871
1872 bool brandNew = false;
1873
1874 // check if the wallet is already open
1875 QPair<int, KWallet::Backend *> walletInfo = findWallet(wallet);
1876 int rc = walletInfo.first;
1877 if (rc != -1) {
1878 return rc; // Wallet already opened, return handle
1879 }
1880 if (_wallets.count() > 20) {
1881 return -1;
1882 }
1883
1884 KWallet::Backend *b = nullptr;
1885 // If the wallet we want to open does not exists. create it and set pam
1886 // hash
1887 if (!wallets().contains(wallet)) {
1888 b = new KWallet::Backend(wallet);
1889 b->setCipherType(KWallet::BACKEND_CIPHER_BLOWFISH);
1890 brandNew = true;
1891 } else {
1892 b = new KWallet::Backend(wallet);
1893 }
1894
1895 int openrc = b->openPreHashed(passwordHash);
1896 if (openrc != 0 || !b->isOpen()) {
1897 delete b;
1898 return openrc;
1899 }
1900
1901 // opening the wallet was successful
1902 int handle = generateHandle();
1903 _wallets.insert(handle, b);
1904 _syncTimers.addTimer(handle, _syncTime);
1905
1906 // don't reference the wallet or add a session so it
1907 // can be reclosed easily.
1908
1909 if (sessionTimeout > 0) {
1910 _closeTimers.addTimer(handle, sessionTimeout);
1911 } else if (_closeIdle) {
1912 _closeTimers.addTimer(handle, _idleTime);
1913 }
1914 if (brandNew) {
1915 Q_EMIT walletCreated(wallet);
1916 }
1917 Q_EMIT walletOpened(wallet);
1918
1919 if (_wallets.count() == 1 && _launchManager) {
1920 startManagerForKwalletd();
1921 }
1922
1923 return handle;
1924}
1925
1926// vim: tw=220:ts=4
1927
1928#include "moc_kwalletd.cpp"
KConfigGroup group(const QString &group)
void deleteEntry(const char *key, WriteConfigFlags pFlags=Normal)
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
bool isEntryImmutable(const char *key) const
QString readEntry(const char *key, const char *aDefault=nullptr) const
bool sync() override
void deleted(const QString &path)
void dirty(const QString &path)
void setBackgroundWarningColor(const QColor &color)
void setIcon(const QIcon &icon)
void setPrompt(const QString &prompt)
QString password() const
void sendEvent()
KNotificationAction * addAction(const QString &label)
void setWindow(QWindow *window)
void setText(const QString &text)
QString password() const
void setPrompt(const QString &prompt)
void setIcon(const QIcon &icon)
void setPassword(const QString &password)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
static const QString PasswordFolder()
The standardized name of the password folder.
Definition kwallet.cpp:87
static const QString NetworkWallet()
The name of the wallet used to store network passwords.
Definition kwallet.cpp:76
static const QString LocalWallet()
The name of the wallet used to store local passwords.
Definition kwallet.cpp:58
static const QString FormDataFolder()
The standardized name of the form data folder.
Definition kwallet.cpp:92
static bool isPlatformX11()
static void setMainWindow(QWindow *subwindow, const QString &mainwindow)
static void setOnAllDesktops(WId win, bool b)
static void setState(WId win, NET::States state)
static void clearState(WId win, NET::States state)
static WId activeWindow()
static Q_INVOKABLE void forceActiveWindow(QWindow *window, long time=0)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
KGUIADDONS_EXPORT QWindow * window(QObject *job)
QString path(const QString &relativePath)
void errorWId(WId parent_id, const QString &text, const QString &title=QString(), Options options=Notify)
void information(QWidget *parent, const QString &text, const QString &title=QString(), const QString &dontShowAgainName=QString(), Options options=Notify)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
const QList< QKeySequence > & end()
QString name(StandardShortcut id)
void exit(int returnCode)
QDBusConnectionInterface * interface() const const
bool isConnected() const const
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
bool registerService(const QString &serviceName)
bool send(const QDBusMessage &message) const const
QDBusConnection sessionBus()
QDBusReply< QDBusConnectionInterface::RegisterServiceReply > registerService(const QString &serviceName, ServiceQueueOptions qoption, ServiceReplacementOptions roption)
QDBusConnection connection() const const
const QDBusMessage & message() const const
QDBusMessage createReply(const QList< QVariant > &arguments) const const
QString service() const const
void setDelayedReply(bool enable) const const
MessageType type() const const
void addWatchedService(const QString &newService)
bool removeWatchedService(const QString &service)
void serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner)
void setConnection(const QDBusConnection &connection)
virtual int exec()
bool exists() const const
bool remove()
void clear()
const_iterator constBegin() const const
const_iterator constEnd() const const
bool contains(const Key &key) const const
qsizetype count() const const
iterator insert(const Key &key, const T &value)
bool isEmpty() const const
Key key(const T &value) const const
bool remove(const Key &key)
T value(const Key &key) const const
QList< T > values() const const
QIcon fromTheme(const QString &name)
void append(QList< T > &&value)
iterator begin()
const_iterator constBegin() const const
const_iterator constEnd() const const
iterator end()
bool isEmpty() const const
qsizetype removeAll(const AT &t)
value_type takeFirst()
void clear()
bool contains(const Key &key) const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
QString findExecutable(const QString &executableName, const QStringList &paths)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool isNull() const const
qsizetype length() const const
QString toHtmlEscaped() const const
QByteArray toUtf8() const const
void truncate(qsizetype position)
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
WA_NativeWindow
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QWidget * find(WId id)
void setAttribute(Qt::WidgetAttribute attribute, bool on)
WId winId() const const
QWindow * windowHandle() const const
void setWindowTitle(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 10 2024 11:47:59 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.