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

KDE's Doxygen guidelines are available online.