KWallet

kwalletfreedesktopservice.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2021 Slava Aseev <nullptrnine@basealt.ru>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7#include "kwalletfreedesktopservice.h"
8
9#include "kwalletd.h"
10#include "kwalletd_debug.h"
11#include "kwalletfreedesktopcollection.h"
12#include "kwalletfreedesktopitem.h"
13#include "kwalletfreedesktopprompt.h"
14#include "kwalletfreedesktopserviceadaptor.h"
15#include "kwalletfreedesktopsession.h"
16#include <KConfigGroup>
17#include <QWidget>
18#include <string.h>
19
20#ifdef Q_OS_WIN
21#include <windows.h>
22#endif
23
24[[maybe_unused]] int DBUS_SECRET_SERVICE_META_TYPE_REGISTER = []() {
25 qDBusRegisterMetaType<StrStrMap>();
26 qDBusRegisterMetaType<QMap<QString, QString>>();
27 qDBusRegisterMetaType<FreedesktopSecret>();
28 qDBusRegisterMetaType<FreedesktopSecretMap>();
29 qDBusRegisterMetaType<PropertiesMap>();
30 qDBusRegisterMetaType<QCA::SecureArray>();
31
32 return 0;
33}();
34
35namespace
36{
37QString mangleInvalidObjectPathChars(const QString &str)
38{
39 const auto utf8Str = str.toUtf8();
40 static constexpr char hex[] = "0123456789abcdef";
41 static_assert(sizeof(hex) == 17);
42
43 QString mangled;
44 mangled.reserve(utf8Str.size());
45
46 for (const auto &c : utf8Str) {
47 if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c > '9') && c != '_') {
48 const auto cp = static_cast<quint8>(c);
49 mangled.push_back(QChar::fromLatin1('_'));
50 mangled.push_back(QChar::fromLatin1(hex[cp >> 4]));
51 mangled.push_back(QChar::fromLatin1(hex[cp & 0x0f]));
52 } else {
53 mangled.push_back(QChar::fromLatin1(c));
54 }
55 }
56
57 return mangled;
58}
59}
60
61#define LABEL_NUMBER_PREFIX "__"
62#define LABEL_NUMBER_POSTFIX "_"
63#define LABEL_NUMBER_REGEX "(^.*)" LABEL_NUMBER_PREFIX "(\\d+)" LABEL_NUMBER_POSTFIX "$"
64
65EntryLocation EntryLocation::fromUniqueLabel(const FdoUniqueLabel &uniqLabel)
66{
68 QString name = uniqLabel.label;
69
70 const int slashPos = uniqLabel.label.indexOf(QChar::fromLatin1('/'));
71 if (slashPos == -1 || slashPos == uniqLabel.label.size() - 1) {
72 dir = QStringLiteral(FDO_SECRETS_DEFAULT_DIR);
73 } else {
74 dir = uniqLabel.label.left(slashPos);
75 name = uniqLabel.label.right((uniqLabel.label.size() - dir.size()) - 1);
76 }
77
78 return EntryLocation{dir, FdoUniqueLabel::makeName(name, uniqLabel.copyId)};
79}
80
81FdoUniqueLabel EntryLocation::toUniqueLabel() const
82{
83 return FdoUniqueLabel::fromEntryLocation(*this);
84}
85
86FdoUniqueLabel FdoUniqueLabel::fromEntryLocation(const EntryLocation &entryLocation)
87{
88 const auto uniqLabel = FdoUniqueLabel::fromName(entryLocation.key);
89
90 if (entryLocation.folder == QStringLiteral(FDO_SECRETS_DEFAULT_DIR)) {
91 return uniqLabel;
92 } else {
93 return {entryLocation.folder + QChar::fromLatin1('/') + uniqLabel.label, uniqLabel.copyId};
94 }
95}
96
97FdoUniqueLabel FdoUniqueLabel::fromName(const QString &name)
98{
99 static QRegularExpression regexp(QStringLiteral(LABEL_NUMBER_REGEX));
100
101 const auto match = regexp.match(name);
102 if (match.hasMatch()) {
103 const QString strNum = match.captured(2);
104 bool ok = false;
105 const int n = strNum.toInt(&ok);
106 if (ok) {
107 return FdoUniqueLabel{match.captured(1), n};
108 }
109 }
110 return FdoUniqueLabel{name};
111}
112
113QString FdoUniqueLabel::makeName(const QString &label, int n)
114{
115 if (n == -1) {
116 return label;
117 } else {
118 return label + QStringLiteral(LABEL_NUMBER_PREFIX) + QString::number(n) + QStringLiteral(LABEL_NUMBER_POSTFIX);
119 }
120}
121
122QString FdoUniqueLabel::toName() const
123{
124 return makeName(label, copyId);
125}
126
127EntryLocation FdoUniqueLabel::toEntryLocation() const
128{
129 return EntryLocation::fromUniqueLabel(*this);
130}
131
132QString KWalletFreedesktopService::wrapToCollectionPath(const QString &itemPath)
133{
134 /* Take only /org/freedesktop/secrets/collection/collection_name */
135 return itemPath.section(QChar::fromLatin1('/'), 0, 5);
136}
137
138KWalletFreedesktopService::KWalletFreedesktopService(KWalletD *parent)
139 : QObject(nullptr)
140 , m_parent(parent)
141 , m_kwalletrc(QStringLiteral("kwalletrc"))
142{
143 (void)new KWalletFreedesktopServiceAdaptor(this);
144
145 /* register */
146 QDBusConnection::sessionBus().registerService(QStringLiteral("org.freedesktop.secrets"));
147 QDBusConnection::sessionBus().registerObject(QStringLiteral(FDO_SECRETS_SERVICE_OBJECT), this);
148
149 const KConfigGroup walletGroup(&m_kwalletrc, "Wallet");
150 if (!parent || !walletGroup.readEntry("Enabled", true)) {
151 return;
152 }
153
154 connect(m_parent, static_cast<void (KWalletD::*)(const QString &)>(&KWalletD::walletClosed), this, &KWalletFreedesktopService::lockCollection);
155 connect(m_parent, &KWalletD::entryUpdated, this, &KWalletFreedesktopService::entryUpdated);
156 connect(m_parent, &KWalletD::entryDeleted, this, &KWalletFreedesktopService::entryDeleted);
157 connect(m_parent, &KWalletD::entryRenamed, this, &KWalletFreedesktopService::entryRenamed);
158 connect(m_parent, &KWalletD::walletDeleted, this, &KWalletFreedesktopService::walletDeleted);
159 connect(m_parent, &KWalletD::walletCreated, this, &KWalletFreedesktopService::walletCreated);
160
161 const auto walletNames = backend()->wallets();
162
163 /* Build collections */
164 for (const QString &walletName : walletNames) {
165 const auto objectPath = makeUniqueObjectPath(walletName);
166 auto collection = std::make_unique<KWalletFreedesktopCollection>(this, -1, walletName, objectPath);
167
168 m_collections.emplace(objectPath.path(), std::move(collection));
169 }
170}
171
172KWalletFreedesktopService::~KWalletFreedesktopService() = default;
173
174QList<QDBusObjectPath> KWalletFreedesktopService::collections() const
175{
177 result.reserve(m_collections.size());
178
179 for (const auto &collectionPair : m_collections) {
180 result.push_back(QDBusObjectPath(collectionPair.first));
181 }
182
183 return result;
184}
185
186QDBusObjectPath KWalletFreedesktopService::CreateCollection(const QVariantMap &properties, const QString &alias, QDBusObjectPath &prompt)
187{
188 prompt.setPath(QStringLiteral("/"));
189
190 const auto labelIter = properties.find(QStringLiteral("org.freedesktop.Secret.Collection.Label"));
191 if (labelIter == properties.end()) {
192 sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Collection.Label property is missing"));
193 return QDBusObjectPath("/");
194 }
195 if (!labelIter->canConvert<QString>()) {
196 sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Type of Collection.Label property is invalid"));
197 return QDBusObjectPath("/");
198 }
199
200 prompt = nextPromptPath();
201 auto fdoPromptPtr = std::make_unique<KWalletFreedesktopPrompt>(this, prompt, PromptType::Create, message().service());
202 auto &fdoPrompt = *m_prompts.emplace(prompt.path(), std::move(fdoPromptPtr)).first->second;
203
204 fdoPrompt.appendProperties(labelIter->toString(), QDBusObjectPath("/"), alias);
205 fdoPrompt.subscribeForWalletAsyncOpened();
206
207 return QDBusObjectPath("/");
208}
209
210FreedesktopSecretMap KWalletFreedesktopService::GetSecrets(const QList<QDBusObjectPath> &items, const QDBusObjectPath &session)
211{
213
214 for (const QDBusObjectPath &itemPath : items) {
215 const auto item = getItemByObjectPath(itemPath);
216
217 if (item) {
218 result.insert(itemPath, item->getSecret(connection(), message(), session));
219 } else {
220 sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Can't find item at path ") + itemPath.path());
221 break;
222 }
223 }
224
225 return result;
226}
227
228QList<QDBusObjectPath> KWalletFreedesktopService::Lock(const QList<QDBusObjectPath> &objects, QDBusObjectPath &prompt)
229{
230 prompt = QDBusObjectPath("/");
232
233 /* Try find in active collections */
234 for (const QDBusObjectPath &object : objects) {
235 const QString collectionPath = wrapToCollectionPath(resolveIfAlias(object.path()));
236
237 const auto foundCollection = m_collections.find(collectionPath);
238 if (foundCollection != m_collections.end()) {
239 const int walletHandle = foundCollection->second->walletHandle();
240 const int rc = m_parent->close(walletHandle, true, FDO_APPID, message());
241
242 if (rc == 0) {
243 result.push_back(QDBusObjectPath(collectionPath));
244 } else {
245 sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Can't lock object at path ") + collectionPath);
246 }
247 } else {
248 sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Collection at path ") + collectionPath + QStringLiteral(" does not exist"));
249 }
250 }
251
252 return result;
253}
254
255QDBusVariant KWalletFreedesktopService::OpenSession(const QString &algorithm, const QDBusVariant &input, QDBusObjectPath &result)
256{
257 std::unique_ptr<KWalletFreedesktopSessionAlgorithm> sessionAlgorithm;
258 if (algorithm == QStringLiteral("plain")) {
259 sessionAlgorithm = createSessionAlgorithmPlain();
260 } else if (algorithm == QStringLiteral("dh-ietf1024-sha256-aes128-cbc-pkcs7")) {
261 if (!input.variant().canConvert<QByteArray>()) {
262 sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Second input argument must be a byte array."));
263 return {};
264 }
265
266 sessionAlgorithm = createSessionAlgorithmDhAes(input.variant().toByteArray());
267 } else {
268 sendErrorReply(QDBusError::ErrorType::NotSupported,
269 QStringLiteral("Algorithm ") + algorithm
270 + QStringLiteral(" is not supported. (only plain and dh-ietf1024-sha256-aes128-cbc-pkcs7 are supported)"));
271 return {};
272 }
273
274 if (!sessionAlgorithm) {
275 // Creation of session algorithm failed, createSessionAlgorithm...() method should have sent an error reply.
276 return {};
277 }
278
279 const QString sessionPath = createSession(std::move(sessionAlgorithm));
280 result.setPath(sessionPath);
281 return QDBusVariant(QVariant(m_sessions[sessionPath]->negotiationOutput()));
282}
283
284QDBusObjectPath KWalletFreedesktopService::ReadAlias(const QString &name)
285{
286 QString walletName;
287
288 m_kwalletrc.reparseConfiguration();
289 if (name == QStringLiteral("default")) {
290 KConfigGroup cfg(&m_kwalletrc, "Wallet");
291 walletName = defaultWalletName(cfg);
292
293 } else {
294 KConfigGroup cfg(&m_kwalletrc, "org.freedesktop.secrets.aliases");
295 walletName = cfg.readEntry(name, QString());
296 }
297
298 if (!walletName.isEmpty()) {
299 const auto *collection = getCollectionByWalletName(walletName);
300 if (collection) {
301 return collection->fdoObjectPath();
302 }
303 }
304
305 return QDBusObjectPath("/");
306}
307
308QList<QDBusObjectPath> KWalletFreedesktopService::SearchItems(const StrStrMap &attributes, QList<QDBusObjectPath> &locked)
309{
310 QList<QDBusObjectPath> unlocked;
311
312 for (const auto &collectionPair : m_collections) {
313 auto &collection = *collectionPair.second;
314
315 if (collection.locked()) {
316 locked += collection.SearchItems(attributes);
317 } else {
318 unlocked += collection.SearchItems(attributes);
319 }
320 }
321
322 return unlocked;
323}
324
325void KWalletFreedesktopService::SetAlias(const QString &name, const QDBusObjectPath &collectionPath)
326{
327 const auto foundCollection = m_collections.find(collectionPath.path());
328 if (foundCollection == m_collections.end()) {
329 return;
330 }
331
332 auto *collection = foundCollection->second.get();
333 createCollectionAlias(name, collection);
334}
335
336QString KWalletFreedesktopService::resolveIfAlias(QString alias)
337{
338 if (alias.startsWith(QStringLiteral(FDO_ALIAS_PATH))) {
339 const auto path = ReadAlias(alias.remove(0, QStringLiteral(FDO_ALIAS_PATH).size())).path();
340 if (path != QStringLiteral("/")) {
341 alias = path;
342 } else {
343 sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Alias ") + alias + QStringLiteral(" does not exist"));
344 return {};
345 }
346 }
347
348 if (!alias.startsWith(QStringLiteral(FDO_SECRETS_COLLECTION_PATH))) {
349 sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Collection object path is invalid"));
350 return {};
351 }
352
353 return alias;
354}
355
356struct UnlockedObject {
357 QString walletName;
358 QDBusObjectPath objectPath;
359};
360
361QList<QDBusObjectPath> KWalletFreedesktopService::Unlock(const QList<QDBusObjectPath> &objects, QDBusObjectPath &prompt)
362{
363 prompt = QDBusObjectPath("/");
364
366 QList<UnlockedObject> needUnlock;
367
368 /* Try find in active collections */
369 for (const QDBusObjectPath &object : objects) {
370 const QString strPath = object.path();
371 const QString collectionPath = wrapToCollectionPath(resolveIfAlias(strPath));
372
373 const auto foundCollection = m_collections.find(collectionPath);
374 if (foundCollection != m_collections.end()) {
375 if (foundCollection->second->locked()) {
376 needUnlock.push_back({foundCollection->second->walletName(), QDBusObjectPath(strPath)});
377 } else {
378 result.push_back(QDBusObjectPath(strPath));
379 }
380 } else {
381 sendErrorReply(QDBusError::ErrorType::InvalidObjectPath, QStringLiteral("Object ") + strPath + QStringLiteral(" does not exist"));
382 return {};
383 }
384 }
385
386 if (!needUnlock.empty()) {
387 const auto promptPath = nextPromptPath();
388 auto fdoPromptPtr = std::make_unique<KWalletFreedesktopPrompt>(this, promptPath, PromptType::Open, message().service());
389 auto &fdoPrompt = *m_prompts.emplace(promptPath.path(), std::move(fdoPromptPtr)).first->second;
390
391 prompt = QDBusObjectPath(promptPath);
392
393 for (const auto &[walletName, objectPath] : std::as_const(needUnlock)) {
394 fdoPrompt.appendProperties(walletName, objectPath);
395 }
396
397 fdoPrompt.subscribeForWalletAsyncOpened();
398 }
399 return result;
400}
401
402std::unique_ptr<KWalletFreedesktopSessionAlgorithm> KWalletFreedesktopService::createSessionAlgorithmPlain() const
403{
404 return std::make_unique<KWalletFreedesktopSessionAlgorithmPlain>();
405}
406
407std::unique_ptr<KWalletFreedesktopSessionAlgorithm> KWalletFreedesktopService::createSessionAlgorithmDhAes(const QByteArray &clientKey) const
408{
409 if (clientKey.size() < FDO_DH_PUBLIC_KEY_SIZE) {
410 sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Client public key size is invalid"));
411 return nullptr;
412 }
413
414 QCA::KeyGenerator keygen;
415 const auto dlGroup = QCA::DLGroup(keygen.createDLGroup(QCA::IETF_1024));
416 if (dlGroup.isNull()) {
417 sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("createDLGroup failed: maybe libqca-ossl is missing"));
418 return nullptr;
419 }
420
421 auto privateKey = QCA::PrivateKey(keygen.createDH(dlGroup));
422 const auto publicKey = QCA::PublicKey(privateKey);
423 const auto clientPublicKey = QCA::DHPublicKey(dlGroup, QCA::BigInteger(QCA::SecureArray(clientKey)));
424 const auto commonSecret = privateKey.deriveKey(clientPublicKey);
425 const auto symmetricKey = QCA::HKDF().makeKey(commonSecret, {}, {}, FDO_SECRETS_CIPHER_KEY_SIZE);
426
427 return std::make_unique<KWalletFreedesktopSessionAlgorithmDhAes>(publicKey, symmetricKey);
428}
429
430QString KWalletFreedesktopService::createSession(std::unique_ptr<KWalletFreedesktopSessionAlgorithm> algorithm)
431{
432 const QString sessionPath = QStringLiteral(FDO_SECRETS_SESSION_PATH) + QString::number(++m_session_counter);
433 auto session = std::make_unique<KWalletFreedesktopSession>(this, std::move(algorithm), sessionPath, connection(), message());
434 m_sessions[sessionPath] = std::move(session);
435 return sessionPath;
436}
437
438QString KWalletFreedesktopService::defaultWalletName(KConfigGroup &cfg)
439{
440 auto walletName = cfg.readEntry("Default Wallet", "kdewallet");
441 if (walletName.isEmpty()) {
442 walletName = QStringLiteral("kdewallet");
443 }
444 return walletName;
445}
446
447QDBusObjectPath KWalletFreedesktopService::promptUnlockCollection(const QString &walletName, int handle)
448{
449 auto *collection = getCollectionByWalletName(walletName);
450 QString objectPath;
451
452 if (collection) {
453 collection->onWalletChangeState(handle);
454 onCollectionChanged(collection->fdoObjectPath());
455 objectPath = collection->fdoObjectPath().path();
456 } else {
457 const auto path = makeUniqueObjectPath(walletName);
458 objectPath = path.path();
459 auto newCollection = std::make_unique<KWalletFreedesktopCollection>(this, handle, walletName, path);
460 m_collections[objectPath] = std::move(newCollection);
461 onCollectionCreated(path);
462 }
463
464 return QDBusObjectPath(objectPath);
465}
466
467/* Triggered after KWalletD::walletClosed signal */
468void KWalletFreedesktopService::lockCollection(const QString &name)
469{
470 auto *collection = getCollectionByWalletName(name);
471 if (collection) {
472 collection->onWalletChangeState(-1);
473 onCollectionChanged(collection->fdoObjectPath());
474 }
475}
476
477/* Triggered after KWalletD::entryUpdated signal */
478void KWalletFreedesktopService::entryUpdated(const QString &walletName, const QString &folder, const QString &entryName)
479{
480 auto *collection = getCollectionByWalletName(walletName);
481 if (!collection) {
482 return;
483 }
484
485 const EntryLocation entryLocation{folder, entryName};
486 const auto *item = collection->findItemByEntryLocation(entryLocation);
487 if (item) {
488 collection->onItemChanged(item->fdoObjectPath());
489 } else {
490 auto objectPath = collection->nextItemPath();
491 collection->pushNewItem(entryLocation.toUniqueLabel(), objectPath);
492 collection->onItemCreated(objectPath);
493 }
494}
495
496/* Triggered after KWalletD::entryDeleted signal */
497void KWalletFreedesktopService::entryDeleted(const QString &walletName, const QString &folder, const QString &entryName)
498{
499 auto *collection = getCollectionByWalletName(walletName);
500 if (!collection) {
501 return;
502 }
503
504 const auto *item = collection->findItemByEntryLocation({folder, entryName});
505 if (item) {
506 collection->onItemDeleted(item->fdoObjectPath());
507 }
508}
509
510/* Triggered after KWalletD::entryRenamed signal */
511void KWalletFreedesktopService::entryRenamed(const QString &walletName, const QString &folder, const QString &oldName, const QString &newName)
512{
513 auto *collection = getCollectionByWalletName(walletName);
514 if (!collection) {
515 return;
516 }
517
518 const EntryLocation oldLocation{folder, oldName};
519 const EntryLocation newLocation{folder, newName};
520
521 auto *item = collection->findItemByEntryLocation(oldLocation);
522 if (!item) {
523 /* Warn if label not found and not yet renamed */
524 if (!collection->findItemByEntryLocation(newLocation)) {
525 qCWarning(KWALLETD_LOG) << "Cannot rename secret service label:" << FdoUniqueLabel::fromEntryLocation(oldLocation).label;
526 }
527 return;
528 }
529
530 if (item) {
531 collection->itemAttributes().renameLabel(oldLocation, newLocation);
532 item->uniqueLabel(newLocation.toUniqueLabel());
533 collection->onItemChanged(item->fdoObjectPath());
534 }
535}
536
537/* Triggered after KWalletD::walletDeleted signal */
538void KWalletFreedesktopService::walletDeleted(const QString &walletName)
539{
540 auto *collection = getCollectionByWalletName(walletName);
541 if (collection) {
542 collection->Delete();
543 }
544}
545
546/* Triggered after KWalletD::walletCreated signal */
547void KWalletFreedesktopService::walletCreated(const QString &walletName)
548{
549 const auto objectPath = makeUniqueObjectPath(walletName);
550 auto collection = std::make_unique<KWalletFreedesktopCollection>(this, -1, walletName, objectPath);
551 m_collections.emplace(objectPath.path(), std::move(collection));
552 onCollectionCreated(objectPath);
553}
554
555bool KWalletFreedesktopService::desecret(const QDBusMessage &message, FreedesktopSecret &secret)
556{
557 const auto foundSession = m_sessions.find(secret.session.path());
558
559 if (foundSession != m_sessions.end()) {
560 const KWalletFreedesktopSession &session = *foundSession->second;
561 return session.decrypt(message, secret);
562 }
563
564 return false;
565}
566
567bool KWalletFreedesktopService::ensecret(const QDBusMessage &message, FreedesktopSecret &secret)
568{
569 const auto foundSession = m_sessions.find(secret.session.path());
570
571 if (foundSession != m_sessions.end()) {
572 const KWalletFreedesktopSession &session = *foundSession->second;
573 return session.encrypt(message, secret);
574 }
575
576 return false;
577}
578
579QDBusObjectPath KWalletFreedesktopService::nextPromptPath()
580{
581 static uint64_t id = 0;
582 return QDBusObjectPath(QStringLiteral(FDO_SECRET_SERVICE_PROMPT_PATH) + QStringLiteral("p") + QString::number(id++));
583}
584
585QDBusArgument &operator<<(QDBusArgument &arg, const FreedesktopSecret &secret)
586{
587 arg.beginStructure();
588 arg << secret.session;
589 arg << secret.parameters;
590 arg << secret.value;
591 arg << secret.mimeType;
592 arg.endStructure();
593 return arg;
594}
595
596const QDBusArgument &operator>>(const QDBusArgument &arg, FreedesktopSecret &secret)
597{
598 arg.beginStructure();
599 arg >> secret.session;
600 arg >> secret.parameters;
601 arg >> secret.value;
602 arg >> secret.mimeType;
603 arg.endStructure();
604 return arg;
605}
606
608{
609 QByteArray bytes = value.toByteArray();
610 stream << bytes;
611 explicit_zero_mem(bytes.data(), bytes.size());
612 return stream;
613}
614
616{
617 QByteArray bytes;
618 stream >> bytes;
619 value = QCA::SecureArray(bytes);
620 explicit_zero_mem(bytes.data(), bytes.size());
621 return stream;
622}
623
625{
626 QByteArray bytes = value.toByteArray();
627 arg << bytes;
628 explicit_zero_mem(bytes.data(), bytes.size());
629 return arg;
630}
631
633{
634 QByteArray byteArray;
635 arg >> byteArray;
636 buf = QCA::SecureArray(byteArray);
637 explicit_zero_mem(byteArray.data(), byteArray.size());
638 return arg;
639}
640
641KWalletD *KWalletFreedesktopService::backend() const
642{
643 return m_parent;
644}
645
646QDBusObjectPath KWalletFreedesktopService::fdoObjectPath() const
647{
648 return QDBusObjectPath(FDO_SECRETS_SERVICE_OBJECT);
649}
650
651KWalletFreedesktopItem *KWalletFreedesktopService::getItemByObjectPath(const QDBusObjectPath &path) const
652{
653 const auto str = path.path();
654 if (!str.startsWith(QStringLiteral(FDO_SECRETS_COLLECTION_PATH))) {
655 return nullptr;
656 }
657
658 const QString collectionPath = wrapToCollectionPath(str);
659 const auto collectionPos = m_collections.find(collectionPath);
660 if (collectionPos == m_collections.end()) {
661 return nullptr;
662 }
663
664 const auto &collection = collectionPos->second;
665 return collection->getItemByObjectPath(str);
666}
667
668KWalletFreedesktopPrompt *KWalletFreedesktopService::getPromptByObjectPath(const QDBusObjectPath &path) const
669{
670 const auto foundPrompt = m_prompts.find(path.path());
671 if (foundPrompt != m_prompts.end()) {
672 return foundPrompt->second.get();
673 } else {
674 return nullptr;
675 }
676}
677
678FdoUniqueLabel KWalletFreedesktopService::makeUniqueCollectionLabel(const QString &label)
679{
680 int n = -1;
681 auto walletName = label;
682 const QStringList wallets = backend()->wallets();
683
684 while (wallets.contains(walletName)) {
685 walletName = FdoUniqueLabel::makeName(label, ++n);
686 }
687
688 return {label, n};
689}
690
691QString KWalletFreedesktopService::makeUniqueWalletName(const QString &labelPrefix)
692{
693 return makeUniqueCollectionLabel(labelPrefix).toName();
694}
695
696QDBusObjectPath KWalletFreedesktopService::makeUniqueObjectPath(const QString &walletName) const
697{
698 auto mangled = mangleInvalidObjectPathChars(walletName);
699 mangled.insert(0, QStringLiteral(FDO_SECRETS_COLLECTION_PATH));
700
701 QString result = mangled;
702 int postfix = 0;
703 while (m_collections.count(result)) {
704 result = mangled + QString::number(postfix++);
705 }
706
707 return QDBusObjectPath(result);
708}
709
710QStringList KWalletFreedesktopService::readAliasesFor(const QString &walletName)
711{
712 m_kwalletrc.reparseConfiguration();
713 KConfigGroup cfg(&m_kwalletrc, "org.freedesktop.secrets.aliases");
714 const auto map = cfg.entryMap();
715 QStringList aliases;
716
717 for (auto i = map.begin(); i != map.end(); ++i) {
718 if (i.value() == walletName) {
719 aliases.push_back(i.key());
720 }
721 }
722
723 KConfigGroup cfgWallet(&m_kwalletrc, "Wallet");
724 if (defaultWalletName(cfgWallet) == walletName) {
725 aliases.push_back(QStringLiteral("default"));
726 }
727
728 return aliases;
729}
730
731void KWalletFreedesktopService::updateCollectionAlias(const QString &alias, const QString &walletName)
732{
733 QString sectName = QStringLiteral("org.freedesktop.secrets.aliases");
734 QString sectKey = alias;
735
736 if (alias == QStringLiteral("default")) {
737 sectName = QStringLiteral("Wallet");
738 sectKey = QStringLiteral("Default Wallet");
739 }
740
741 KConfigGroup cfg(&m_kwalletrc, sectName);
742 cfg.writeEntry(sectKey, walletName);
743 m_kwalletrc.sync();
744}
745
746void KWalletFreedesktopService::createCollectionAlias(const QString &alias, const QString &walletName)
747{
748 QString sectName = QStringLiteral("org.freedesktop.secrets.aliases");
749 QString sectKey = alias;
750
751 if (alias == QStringLiteral("default")) {
752 sectName = QStringLiteral("Wallet");
753 sectKey = QStringLiteral("Default Wallet");
754 }
755
756 m_kwalletrc.reparseConfiguration();
757 KConfigGroup cfg(&m_kwalletrc, sectName);
758
759 const QString prevWalletName = cfg.readEntry(sectKey, QString());
760 if (!prevWalletName.isEmpty()) {
761 const auto *prevCollection = getCollectionByWalletName(prevWalletName);
762 if (prevCollection) {
763 QDBusConnection::sessionBus().unregisterObject(QStringLiteral(FDO_ALIAS_PATH) + alias);
764 }
765 }
766
767 cfg.writeEntry(sectKey, walletName);
768 m_kwalletrc.sync();
769
770 auto *collection = getCollectionByWalletName(walletName);
771 if (collection) {
772 QDBusConnection::sessionBus().registerObject(QStringLiteral(FDO_ALIAS_PATH) + alias, collection);
773 }
774}
775
776void KWalletFreedesktopService::createCollectionAlias(const QString &alias, KWalletFreedesktopCollection *collection)
777{
778 QString sectName = QStringLiteral("org.freedesktop.secrets.aliases");
779 QString sectKey = alias;
780
781 if (alias == QStringLiteral("default")) {
782 sectName = QStringLiteral("Wallet");
783 sectKey = QStringLiteral("Default Wallet");
784 }
785
786 m_kwalletrc.reparseConfiguration();
787 KConfigGroup cfg(&m_kwalletrc, sectName);
788
789 const QString prevWalletName = cfg.readEntry(sectKey, "");
790 if (!prevWalletName.isEmpty()) {
791 const auto *prevCollection = getCollectionByWalletName(prevWalletName);
792 if (prevCollection) {
793 QDBusConnection::sessionBus().unregisterObject(QStringLiteral(FDO_ALIAS_PATH) + alias);
794 }
795 }
796
797 cfg.writeEntry(sectKey, collection->walletName());
798 m_kwalletrc.sync();
799 QDBusConnection::sessionBus().registerObject(QStringLiteral(FDO_ALIAS_PATH) + alias, collection);
800}
801
802void KWalletFreedesktopService::removeAlias(const QString &alias)
803{
804 if (alias == QStringLiteral("default")) {
805 return;
806 }
807
808 KConfigGroup cfg(&m_kwalletrc, "org.freedesktop.secrets.aliases");
809 cfg.deleteEntry(alias);
810 m_kwalletrc.sync();
811 QDBusConnection::sessionBus().unregisterObject(QStringLiteral(FDO_ALIAS_PATH) + alias);
812}
813
814KWalletFreedesktopCollection *KWalletFreedesktopService::getCollectionByWalletName(const QString &walletName) const
815{
816 for (const auto &collectionKeyValue : m_collections) {
817 const auto collection = collectionKeyValue.second.get();
818 if (collection->walletName() == walletName) {
819 return collection;
820 }
821 }
822
823 return nullptr;
824}
825
826void KWalletFreedesktopService::deletePrompt(const QString &objectPath)
827{
828 const auto foundPrompt = m_prompts.find(objectPath);
829 if (foundPrompt == m_prompts.end()) {
830 return;
831 }
832
833 /* This can be called in the context of the prompt that is currently being
834 * deleted. Therefore, we should schedule deletion on the next event loop iteration
835 */
836 foundPrompt->second->deleteLater();
837 foundPrompt->second.release();
838 m_prompts.erase(foundPrompt);
839}
840
841void KWalletFreedesktopService::deleteSession(const QString &objectPath)
842{
843 const auto foundSession = m_sessions.find(objectPath);
844 if (foundSession == m_sessions.end()) {
845 return;
846 }
847
848 /* This can be called in the context of the session that is currently being
849 * deleted. Therefore, we should schedule deletion on the next event loop iteration
850 */
851 foundSession->second->deleteLater();
852 foundSession->second.release();
853 m_sessions.erase(foundSession);
854}
855
856void KWalletFreedesktopService::onCollectionCreated(const QDBusObjectPath &path)
857{
858 Q_EMIT CollectionCreated(path);
859
860 QVariantMap props;
861 props.insert(QStringLiteral("Collections"), QVariant::fromValue(collections()));
862 onPropertiesChanged(props);
863}
864
865void KWalletFreedesktopService::onCollectionChanged(const QDBusObjectPath &path)
866{
867 Q_EMIT CollectionChanged(path);
868}
869
870void KWalletFreedesktopService::onCollectionDeleted(const QDBusObjectPath &path)
871{
872 const auto collectionMapPos = m_collections.find(path.path());
873 if (collectionMapPos == m_collections.end()) {
874 return;
875 }
876 auto &collectionPair = *collectionMapPos;
877 collectionPair.second->itemAttributes().deleteFile();
878
879 /* This can be called in the context of the collection that is currently being
880 * deleted. Therefore, we should schedule deletion on the next event loop iteration
881 */
882 collectionPair.second->deleteLater();
883 collectionPair.second.release();
884 m_collections.erase(collectionMapPos);
885
886 Q_EMIT CollectionDeleted(path);
887
888 QVariantMap props;
889 props[QStringLiteral("Collections")] = QVariant::fromValue(collections());
890 onPropertiesChanged(props);
891}
892
893void KWalletFreedesktopService::onPropertiesChanged(const QVariantMap &properties)
894{
895 auto msg = QDBusMessage::createSignal(fdoObjectPath().path(), QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("PropertiesChanged"));
896 auto args = QVariantList();
897 args << QStringLiteral("org.freedesktop.Secret.Service") << properties << QStringList();
898 msg.setArguments(args);
900}
901
903{
904 return stream << value.path();
905}
906
908{
909 QString str;
910 stream >> str;
911 value = QDBusObjectPath(str);
912 return stream;
913}
914
915const QDBusArgument &operator>>(const QDBusArgument &arg, PropertiesMap &value)
916{
917 arg.beginMap();
918 value.map.clear();
919
920 while (!arg.atEnd()) {
921 arg.beginMapEntry();
922 QString key;
923 QVariant val;
924 arg >> key >> val;
925
926 /* For org.freedesktop.Secret.Item.Attributes */
927 if (val.canConvert<QDBusArgument>()) {
928 auto metaArg = val.value<QDBusArgument>();
929 StrStrMap metaMap;
930 metaArg >> metaMap;
931 val = QVariant::fromValue(metaMap);
932 }
933 value.map.insert(key, val);
934
935 arg.endMapEntry();
936 }
937 arg.endMap();
938
939 return arg;
940}
941
942QDBusArgument &operator<<(QDBusArgument &arg, const PropertiesMap &value)
943{
944 arg << value.map;
945 return arg;
946}
947
948void explicit_zero_mem(void *data, size_t size)
949{
950#if defined(KWALLETD_HAVE_EXPLICIT_BZERO)
951 explicit_bzero(data, size);
952#elif defined(KWALLETD_HAVE_RTLSECUREZEROMEMORY)
953 RtlSecureZeroMemory(data, size);
954#else
955 auto p = reinterpret_cast<volatile char *>(data);
956 for (size_t i = 0; i < size; ++i) {
957 p[i] = 0;
958 }
959#endif
960}
961
962#include "moc_kwalletfreedesktopservice.cpp"
void deleteEntry(const char *key, WriteConfigFlags pFlags=Normal)
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
QString readEntry(const char *key, const char *aDefault=nullptr) const
QMap< QString, QString > entryMap() const
void reparseConfiguration()
bool sync() override
SymmetricKey makeKey(const SecureArray &secret, const InitializationVector &salt, const InitializationVector &info, unsigned int keyLength)
PrivateKey createDH(const DLGroup &domain, const QString &provider=QString())
DLGroup createDLGroup(QCA::DLGroupSet set, const QString &provider=QString())
QByteArray toByteArray() const
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
QString path(const QString &relativePath)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KGuiItem properties()
QString label(StandardShortcut id)
QString name(StandardShortcut id)
QDebug operator<<(QDebug dbg, const PerceptualColor::LchaDouble &value)
char * data()
qsizetype size() const const
QChar fromLatin1(char c)
bool atEnd() const const
void beginMap(QMetaType keyMetaType, QMetaType valueMetaType)
void beginMapEntry()
void beginStructure()
void endMapEntry()
void endStructure()
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
bool registerService(const QString &serviceName)
bool send(const QDBusMessage &message) const const
QDBusConnection sessionBus()
void unregisterObject(const QString &path, UnregisterMode mode)
QDBusMessage createSignal(const QString &path, const QString &interface, const QString &name)
QString path() const const
void setPath(const QString &path)
QVariant variant() const const
bool empty() const const
void push_back(parameter_type value)
void reserve(qsizetype size)
iterator insert(const Key &key, const T &value)
Q_EMITQ_EMIT
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
QString & insert(qsizetype position, QChar ch)
bool isEmpty() const const
QString left(qsizetype n) const const
QString number(double n, char format, int precision)
void push_back(QChar ch)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
void reserve(qsizetype size)
QString right(qsizetype n) const const
QString section(QChar sep, qsizetype start, qsizetype end, SectionFlags flags) const const
qsizetype size() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
QByteArray toUtf8() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QTextStream & hex(QTextStream &stream)
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool canConvert() const const
QVariant fromValue(T &&value)
QByteArray toByteArray() const const
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:16:05 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.