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