File indexing completed on 2024-11-24 03:41:07

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2021 Slava Aseev <nullptrnine@basealt.ru>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 #include "kwalletfreedesktopservice.h"
0008 
0009 #include "kwalletd.h"
0010 #include "kwalletd_debug.h"
0011 #include "kwalletfreedesktopcollection.h"
0012 #include "kwalletfreedesktopitem.h"
0013 #include "kwalletfreedesktopprompt.h"
0014 #include "kwalletfreedesktopserviceadaptor.h"
0015 #include "kwalletfreedesktopsession.h"
0016 #include <KConfigGroup>
0017 #include <QWidget>
0018 #include <string.h>
0019 
0020 #ifdef Q_OS_WIN
0021 #include <windows.h>
0022 #endif
0023 
0024 [[maybe_unused]] int DBUS_SECRET_SERVICE_META_TYPE_REGISTER = []() {
0025     qDBusRegisterMetaType<StrStrMap>();
0026     qDBusRegisterMetaType<QMap<QString, QString>>();
0027     qDBusRegisterMetaType<FreedesktopSecret>();
0028     qDBusRegisterMetaType<FreedesktopSecretMap>();
0029     qDBusRegisterMetaType<PropertiesMap>();
0030     qDBusRegisterMetaType<QCA::SecureArray>();
0031 
0032     return 0;
0033 }();
0034 
0035 namespace
0036 {
0037 QString mangleInvalidObjectPathChars(const QString &str)
0038 {
0039     const auto utf8Str = str.toUtf8();
0040     static constexpr char hex[] = "0123456789abcdef";
0041     static_assert(sizeof(hex) == 17);
0042 
0043     QString mangled;
0044     mangled.reserve(utf8Str.size());
0045 
0046     for (const auto &c : utf8Str) {
0047         if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c > '9') && c != '_') {
0048             const auto cp = static_cast<quint8>(c);
0049             mangled.push_back(QChar::fromLatin1('_'));
0050             mangled.push_back(QChar::fromLatin1(hex[cp >> 4]));
0051             mangled.push_back(QChar::fromLatin1(hex[cp & 0x0f]));
0052         } else {
0053             mangled.push_back(QChar::fromLatin1(c));
0054         }
0055     }
0056 
0057     return mangled;
0058 }
0059 }
0060 
0061 #define LABEL_NUMBER_PREFIX "__"
0062 #define LABEL_NUMBER_POSTFIX "_"
0063 #define LABEL_NUMBER_REGEX "(^.*)" LABEL_NUMBER_PREFIX "(\\d+)" LABEL_NUMBER_POSTFIX "$"
0064 
0065 EntryLocation EntryLocation::fromUniqueLabel(const FdoUniqueLabel &uniqLabel)
0066 {
0067     QString dir;
0068     QString name = uniqLabel.label;
0069 
0070     const int slashPos = uniqLabel.label.indexOf(QChar::fromLatin1('/'));
0071     if (slashPos == -1 || slashPos == uniqLabel.label.size() - 1) {
0072         dir = QStringLiteral(FDO_SECRETS_DEFAULT_DIR);
0073     } else {
0074         dir = uniqLabel.label.left(slashPos);
0075         name = uniqLabel.label.right((uniqLabel.label.size() - dir.size()) - 1);
0076     }
0077 
0078     return EntryLocation{dir, FdoUniqueLabel::makeName(name, uniqLabel.copyId)};
0079 }
0080 
0081 FdoUniqueLabel EntryLocation::toUniqueLabel() const
0082 {
0083     return FdoUniqueLabel::fromEntryLocation(*this);
0084 }
0085 
0086 FdoUniqueLabel FdoUniqueLabel::fromEntryLocation(const EntryLocation &entryLocation)
0087 {
0088     const auto uniqLabel = FdoUniqueLabel::fromName(entryLocation.key);
0089 
0090     if (entryLocation.folder == QStringLiteral(FDO_SECRETS_DEFAULT_DIR)) {
0091         return uniqLabel;
0092     } else {
0093         return {entryLocation.folder + QChar::fromLatin1('/') + uniqLabel.label, uniqLabel.copyId};
0094     }
0095 }
0096 
0097 FdoUniqueLabel FdoUniqueLabel::fromName(const QString &name)
0098 {
0099     static QRegularExpression regexp(QStringLiteral(LABEL_NUMBER_REGEX));
0100 
0101     const auto match = regexp.match(name);
0102     if (match.hasMatch()) {
0103         const QString strNum = match.captured(2);
0104         bool ok = false;
0105         const int n = strNum.toInt(&ok);
0106         if (ok) {
0107             return FdoUniqueLabel{match.captured(1), n};
0108         }
0109     }
0110     return FdoUniqueLabel{name};
0111 }
0112 
0113 QString FdoUniqueLabel::makeName(const QString &label, int n)
0114 {
0115     if (n == -1) {
0116         return label;
0117     } else {
0118         return label + QStringLiteral(LABEL_NUMBER_PREFIX) + QString::number(n) + QStringLiteral(LABEL_NUMBER_POSTFIX);
0119     }
0120 }
0121 
0122 QString FdoUniqueLabel::toName() const
0123 {
0124     return makeName(label, copyId);
0125 }
0126 
0127 EntryLocation FdoUniqueLabel::toEntryLocation() const
0128 {
0129     return EntryLocation::fromUniqueLabel(*this);
0130 }
0131 
0132 QString KWalletFreedesktopService::wrapToCollectionPath(const QString &itemPath)
0133 {
0134     /* Take only /org/freedesktop/secrets/collection/collection_name */
0135     return itemPath.section(QChar::fromLatin1('/'), 0, 5);
0136 }
0137 
0138 KWalletFreedesktopService::KWalletFreedesktopService(KWalletD *parent)
0139     : QObject(nullptr)
0140     , m_parent(parent)
0141     , m_kwalletrc(QStringLiteral("kwalletrc"))
0142 {
0143     (void)new KWalletFreedesktopServiceAdaptor(this);
0144 
0145     /* register */
0146     QDBusConnection::sessionBus().registerService(QStringLiteral("org.freedesktop.secrets"));
0147     QDBusConnection::sessionBus().registerObject(QStringLiteral(FDO_SECRETS_SERVICE_OBJECT), this);
0148 
0149     const KConfigGroup walletGroup(&m_kwalletrc, "Wallet");
0150     if (!parent || !walletGroup.readEntry("Enabled", true)) {
0151         return;
0152     }
0153 
0154     connect(m_parent, static_cast<void (KWalletD::*)(const QString &)>(&KWalletD::walletClosed), this, &KWalletFreedesktopService::lockCollection);
0155     connect(m_parent, &KWalletD::entryUpdated, this, &KWalletFreedesktopService::entryUpdated);
0156     connect(m_parent, &KWalletD::entryDeleted, this, &KWalletFreedesktopService::entryDeleted);
0157     connect(m_parent, &KWalletD::entryRenamed, this, &KWalletFreedesktopService::entryRenamed);
0158     connect(m_parent, &KWalletD::walletDeleted, this, &KWalletFreedesktopService::walletDeleted);
0159     connect(m_parent, &KWalletD::walletCreated, this, &KWalletFreedesktopService::walletCreated);
0160 
0161     const auto walletNames = backend()->wallets();
0162 
0163     /* Build collections */
0164     for (const QString &walletName : walletNames) {
0165         const auto objectPath = makeUniqueObjectPath(walletName);
0166         auto collection = std::make_unique<KWalletFreedesktopCollection>(this, -1, walletName, objectPath);
0167 
0168         m_collections.emplace(objectPath.path(), std::move(collection));
0169     }
0170 }
0171 
0172 KWalletFreedesktopService::~KWalletFreedesktopService() = default;
0173 
0174 QList<QDBusObjectPath> KWalletFreedesktopService::collections() const
0175 {
0176     QList<QDBusObjectPath> result;
0177     result.reserve(m_collections.size());
0178 
0179     for (const auto &collectionPair : m_collections) {
0180         result.push_back(QDBusObjectPath(collectionPair.first));
0181     }
0182 
0183     return result;
0184 }
0185 
0186 QDBusObjectPath KWalletFreedesktopService::CreateCollection(const QVariantMap &properties, const QString &alias, QDBusObjectPath &prompt)
0187 {
0188     prompt.setPath(QStringLiteral("/"));
0189 
0190     const auto labelIter = properties.find(QStringLiteral("org.freedesktop.Secret.Collection.Label"));
0191     if (labelIter == properties.end()) {
0192         sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Collection.Label property is missing"));
0193         return QDBusObjectPath("/");
0194     }
0195     if (!labelIter->canConvert<QString>()) {
0196         sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Type of Collection.Label property is invalid"));
0197         return QDBusObjectPath("/");
0198     }
0199 
0200     prompt = nextPromptPath();
0201     auto fdoPromptPtr = std::make_unique<KWalletFreedesktopPrompt>(this, prompt, PromptType::Create, message().service());
0202     auto &fdoPrompt = *m_prompts.emplace(prompt.path(), std::move(fdoPromptPtr)).first->second;
0203 
0204     fdoPrompt.appendProperties(labelIter->toString(), QDBusObjectPath("/"), alias);
0205     fdoPrompt.subscribeForWalletAsyncOpened();
0206 
0207     return QDBusObjectPath("/");
0208 }
0209 
0210 FreedesktopSecretMap KWalletFreedesktopService::GetSecrets(const QList<QDBusObjectPath> &items, const QDBusObjectPath &session)
0211 {
0212     FreedesktopSecretMap result;
0213 
0214     for (const QDBusObjectPath &itemPath : items) {
0215         const auto item = getItemByObjectPath(itemPath);
0216 
0217         if (item) {
0218             result.insert(itemPath, item->getSecret(connection(), message(), session));
0219         } else {
0220             sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Can't find item at path ") + itemPath.path());
0221             break;
0222         }
0223     }
0224 
0225     return result;
0226 }
0227 
0228 QList<QDBusObjectPath> KWalletFreedesktopService::Lock(const QList<QDBusObjectPath> &objects, QDBusObjectPath &prompt)
0229 {
0230     prompt = QDBusObjectPath("/");
0231     QList<QDBusObjectPath> result;
0232 
0233     /* Try find in active collections */
0234     for (const QDBusObjectPath &object : objects) {
0235         const QString collectionPath = wrapToCollectionPath(resolveIfAlias(object.path()));
0236 
0237         const auto foundCollection = m_collections.find(collectionPath);
0238         if (foundCollection != m_collections.end()) {
0239             const int walletHandle = foundCollection->second->walletHandle();
0240             const int rc = m_parent->close(walletHandle, true, FDO_APPID, message());
0241 
0242             if (rc == 0) {
0243                 result.push_back(QDBusObjectPath(collectionPath));
0244             } else {
0245                 sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Can't lock object at path ") + collectionPath);
0246             }
0247         } else {
0248             sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Collection at path ") + collectionPath + QStringLiteral(" does not exist"));
0249         }
0250     }
0251 
0252     return result;
0253 }
0254 
0255 QDBusVariant KWalletFreedesktopService::OpenSession(const QString &algorithm, const QDBusVariant &input, QDBusObjectPath &result)
0256 {
0257     std::unique_ptr<KWalletFreedesktopSessionAlgorithm> sessionAlgorithm;
0258     if (algorithm == QStringLiteral("plain")) {
0259         sessionAlgorithm = createSessionAlgorithmPlain();
0260     } else if (algorithm == QStringLiteral("dh-ietf1024-sha256-aes128-cbc-pkcs7")) {
0261         if (!input.variant().canConvert<QByteArray>()) {
0262             sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Second input argument must be a byte array."));
0263             return {};
0264         }
0265 
0266         sessionAlgorithm = createSessionAlgorithmDhAes(input.variant().toByteArray());
0267     } else {
0268         sendErrorReply(QDBusError::ErrorType::NotSupported,
0269                        QStringLiteral("Algorithm ") + algorithm
0270                            + QStringLiteral(" is not supported. (only plain and dh-ietf1024-sha256-aes128-cbc-pkcs7 are supported)"));
0271         return {};
0272     }
0273 
0274     if (!sessionAlgorithm) {
0275         // Creation of session algorithm failed, createSessionAlgorithm...() method should have sent an error reply.
0276         return {};
0277     }
0278 
0279     const QString sessionPath = createSession(std::move(sessionAlgorithm));
0280     result.setPath(sessionPath);
0281     return QDBusVariant(QVariant(m_sessions[sessionPath]->negotiationOutput()));
0282 }
0283 
0284 QDBusObjectPath KWalletFreedesktopService::ReadAlias(const QString &name)
0285 {
0286     QString walletName;
0287 
0288     m_kwalletrc.reparseConfiguration();
0289     if (name == QStringLiteral("default")) {
0290         KConfigGroup cfg(&m_kwalletrc, "Wallet");
0291         walletName = defaultWalletName(cfg);
0292 
0293     } else {
0294         KConfigGroup cfg(&m_kwalletrc, "org.freedesktop.secrets.aliases");
0295         walletName = cfg.readEntry(name, QString());
0296     }
0297 
0298     if (!walletName.isEmpty()) {
0299         const auto *collection = getCollectionByWalletName(walletName);
0300         if (collection) {
0301             return collection->fdoObjectPath();
0302         }
0303     }
0304 
0305     return QDBusObjectPath("/");
0306 }
0307 
0308 QList<QDBusObjectPath> KWalletFreedesktopService::SearchItems(const StrStrMap &attributes, QList<QDBusObjectPath> &locked)
0309 {
0310     QList<QDBusObjectPath> unlocked;
0311 
0312     for (const auto &collectionPair : m_collections) {
0313         auto &collection = *collectionPair.second;
0314 
0315         if (collection.locked()) {
0316             locked += collection.SearchItems(attributes);
0317         } else {
0318             unlocked += collection.SearchItems(attributes);
0319         }
0320     }
0321 
0322     return unlocked;
0323 }
0324 
0325 void KWalletFreedesktopService::SetAlias(const QString &name, const QDBusObjectPath &collectionPath)
0326 {
0327     const auto foundCollection = m_collections.find(collectionPath.path());
0328     if (foundCollection == m_collections.end()) {
0329         return;
0330     }
0331 
0332     auto *collection = foundCollection->second.get();
0333     createCollectionAlias(name, collection);
0334 }
0335 
0336 QString KWalletFreedesktopService::resolveIfAlias(QString alias)
0337 {
0338     if (alias.startsWith(QStringLiteral(FDO_ALIAS_PATH))) {
0339         const auto path = ReadAlias(alias.remove(0, QStringLiteral(FDO_ALIAS_PATH).size())).path();
0340         if (path != QStringLiteral("/")) {
0341             alias = path;
0342         } else {
0343             sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Alias ") + alias + QStringLiteral(" does not exist"));
0344             return {};
0345         }
0346     }
0347 
0348     if (!alias.startsWith(QStringLiteral(FDO_SECRETS_COLLECTION_PATH))) {
0349         sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Collection object path is invalid"));
0350         return {};
0351     }
0352 
0353     return alias;
0354 }
0355 
0356 struct UnlockedObject {
0357     QString walletName;
0358     QDBusObjectPath objectPath;
0359 };
0360 
0361 QList<QDBusObjectPath> KWalletFreedesktopService::Unlock(const QList<QDBusObjectPath> &objects, QDBusObjectPath &prompt)
0362 {
0363     prompt = QDBusObjectPath("/");
0364 
0365     QList<QDBusObjectPath> result;
0366     QList<UnlockedObject> needUnlock;
0367 
0368     /* Try find in active collections */
0369     for (const QDBusObjectPath &object : objects) {
0370         const QString strPath = object.path();
0371         const QString collectionPath = wrapToCollectionPath(resolveIfAlias(strPath));
0372 
0373         const auto foundCollection = m_collections.find(collectionPath);
0374         if (foundCollection != m_collections.end()) {
0375             if (foundCollection->second->locked()) {
0376                 needUnlock.push_back({foundCollection->second->walletName(), QDBusObjectPath(strPath)});
0377             } else {
0378                 result.push_back(QDBusObjectPath(strPath));
0379             }
0380         } else {
0381             sendErrorReply(QDBusError::ErrorType::InvalidObjectPath, QStringLiteral("Object ") + strPath + QStringLiteral(" does not exist"));
0382             return {};
0383         }
0384     }
0385 
0386     if (!needUnlock.empty()) {
0387         const auto promptPath = nextPromptPath();
0388         auto fdoPromptPtr = std::make_unique<KWalletFreedesktopPrompt>(this, promptPath, PromptType::Open, message().service());
0389         auto &fdoPrompt = *m_prompts.emplace(promptPath.path(), std::move(fdoPromptPtr)).first->second;
0390 
0391         prompt = QDBusObjectPath(promptPath);
0392 
0393         for (const auto &[walletName, objectPath] : std::as_const(needUnlock)) {
0394             fdoPrompt.appendProperties(walletName, objectPath);
0395         }
0396 
0397         fdoPrompt.subscribeForWalletAsyncOpened();
0398     }
0399     return result;
0400 }
0401 
0402 std::unique_ptr<KWalletFreedesktopSessionAlgorithm> KWalletFreedesktopService::createSessionAlgorithmPlain() const
0403 {
0404     return std::make_unique<KWalletFreedesktopSessionAlgorithmPlain>();
0405 }
0406 
0407 std::unique_ptr<KWalletFreedesktopSessionAlgorithm> KWalletFreedesktopService::createSessionAlgorithmDhAes(const QByteArray &clientKey) const
0408 {
0409     if (clientKey.size() < FDO_DH_PUBLIC_KEY_SIZE) {
0410         sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("Client public key size is invalid"));
0411         return nullptr;
0412     }
0413 
0414     QCA::KeyGenerator keygen;
0415     const auto dlGroup = QCA::DLGroup(keygen.createDLGroup(QCA::IETF_1024));
0416     if (dlGroup.isNull()) {
0417         sendErrorReply(QDBusError::ErrorType::InvalidArgs, QStringLiteral("createDLGroup failed: maybe libqca-ossl is missing"));
0418         return nullptr;
0419     }
0420 
0421     auto privateKey = QCA::PrivateKey(keygen.createDH(dlGroup));
0422     const auto publicKey = QCA::PublicKey(privateKey);
0423     const auto clientPublicKey = QCA::DHPublicKey(dlGroup, QCA::BigInteger(QCA::SecureArray(clientKey)));
0424     const auto commonSecret = privateKey.deriveKey(clientPublicKey);
0425     const auto symmetricKey = QCA::HKDF().makeKey(commonSecret, {}, {}, FDO_SECRETS_CIPHER_KEY_SIZE);
0426 
0427     return std::make_unique<KWalletFreedesktopSessionAlgorithmDhAes>(publicKey, symmetricKey);
0428 }
0429 
0430 QString KWalletFreedesktopService::createSession(std::unique_ptr<KWalletFreedesktopSessionAlgorithm> algorithm)
0431 {
0432     const QString sessionPath = QStringLiteral(FDO_SECRETS_SESSION_PATH) + QString::number(++m_session_counter);
0433     auto session = std::make_unique<KWalletFreedesktopSession>(this, std::move(algorithm), sessionPath, connection(), message());
0434     m_sessions[sessionPath] = std::move(session);
0435     return sessionPath;
0436 }
0437 
0438 QString KWalletFreedesktopService::defaultWalletName(KConfigGroup &cfg)
0439 {
0440     auto walletName = cfg.readEntry("Default Wallet", "kdewallet");
0441     if (walletName.isEmpty()) {
0442         walletName = QStringLiteral("kdewallet");
0443     }
0444     return walletName;
0445 }
0446 
0447 QDBusObjectPath KWalletFreedesktopService::promptUnlockCollection(const QString &walletName, int handle)
0448 {
0449     auto *collection = getCollectionByWalletName(walletName);
0450     QString objectPath;
0451 
0452     if (collection) {
0453         collection->onWalletChangeState(handle);
0454         onCollectionChanged(collection->fdoObjectPath());
0455         objectPath = collection->fdoObjectPath().path();
0456     } else {
0457         const auto path = makeUniqueObjectPath(walletName);
0458         objectPath = path.path();
0459         auto newCollection = std::make_unique<KWalletFreedesktopCollection>(this, handle, walletName, path);
0460         m_collections[objectPath] = std::move(newCollection);
0461         onCollectionCreated(path);
0462     }
0463 
0464     return QDBusObjectPath(objectPath);
0465 }
0466 
0467 /* Triggered after KWalletD::walletClosed signal */
0468 void KWalletFreedesktopService::lockCollection(const QString &name)
0469 {
0470     auto *collection = getCollectionByWalletName(name);
0471     if (collection) {
0472         collection->onWalletChangeState(-1);
0473         onCollectionChanged(collection->fdoObjectPath());
0474     }
0475 }
0476 
0477 /* Triggered after KWalletD::entryUpdated signal */
0478 void KWalletFreedesktopService::entryUpdated(const QString &walletName, const QString &folder, const QString &entryName)
0479 {
0480     auto *collection = getCollectionByWalletName(walletName);
0481     if (!collection) {
0482         return;
0483     }
0484 
0485     const EntryLocation entryLocation{folder, entryName};
0486     const auto *item = collection->findItemByEntryLocation(entryLocation);
0487     if (item) {
0488         collection->onItemChanged(item->fdoObjectPath());
0489     } else {
0490         auto objectPath = collection->nextItemPath();
0491         collection->pushNewItem(entryLocation.toUniqueLabel(), objectPath);
0492         collection->onItemCreated(objectPath);
0493     }
0494 }
0495 
0496 /* Triggered after KWalletD::entryDeleted signal */
0497 void KWalletFreedesktopService::entryDeleted(const QString &walletName, const QString &folder, const QString &entryName)
0498 {
0499     auto *collection = getCollectionByWalletName(walletName);
0500     if (!collection) {
0501         return;
0502     }
0503 
0504     const auto *item = collection->findItemByEntryLocation({folder, entryName});
0505     if (item) {
0506         collection->onItemDeleted(item->fdoObjectPath());
0507     }
0508 }
0509 
0510 /* Triggered after KWalletD::entryRenamed signal */
0511 void KWalletFreedesktopService::entryRenamed(const QString &walletName, const QString &folder, const QString &oldName, const QString &newName)
0512 {
0513     auto *collection = getCollectionByWalletName(walletName);
0514     if (!collection) {
0515         return;
0516     }
0517 
0518     const EntryLocation oldLocation{folder, oldName};
0519     const EntryLocation newLocation{folder, newName};
0520 
0521     auto *item = collection->findItemByEntryLocation(oldLocation);
0522     if (!item) {
0523         /* Warn if label not found and not yet renamed */
0524         if (!collection->findItemByEntryLocation(newLocation)) {
0525             qCWarning(KWALLETD_LOG) << "Cannot rename secret service label:" << FdoUniqueLabel::fromEntryLocation(oldLocation).label;
0526         }
0527         return;
0528     }
0529 
0530     if (item) {
0531         collection->itemAttributes().renameLabel(oldLocation, newLocation);
0532         item->uniqueLabel(newLocation.toUniqueLabel());
0533         collection->onItemChanged(item->fdoObjectPath());
0534     }
0535 }
0536 
0537 /* Triggered after KWalletD::walletDeleted signal */
0538 void KWalletFreedesktopService::walletDeleted(const QString &walletName)
0539 {
0540     auto *collection = getCollectionByWalletName(walletName);
0541     if (collection) {
0542         collection->Delete();
0543     }
0544 }
0545 
0546 /* Triggered after KWalletD::walletCreated signal */
0547 void KWalletFreedesktopService::walletCreated(const QString &walletName)
0548 {
0549     const auto objectPath = makeUniqueObjectPath(walletName);
0550     auto collection = std::make_unique<KWalletFreedesktopCollection>(this, -1, walletName, objectPath);
0551     m_collections.emplace(objectPath.path(), std::move(collection));
0552     onCollectionCreated(objectPath);
0553 }
0554 
0555 bool KWalletFreedesktopService::desecret(const QDBusMessage &message, FreedesktopSecret &secret)
0556 {
0557     const auto foundSession = m_sessions.find(secret.session.path());
0558 
0559     if (foundSession != m_sessions.end()) {
0560         const KWalletFreedesktopSession &session = *foundSession->second;
0561         return session.decrypt(message, secret);
0562     }
0563 
0564     return false;
0565 }
0566 
0567 bool KWalletFreedesktopService::ensecret(const QDBusMessage &message, FreedesktopSecret &secret)
0568 {
0569     const auto foundSession = m_sessions.find(secret.session.path());
0570 
0571     if (foundSession != m_sessions.end()) {
0572         const KWalletFreedesktopSession &session = *foundSession->second;
0573         return session.encrypt(message, secret);
0574     }
0575 
0576     return false;
0577 }
0578 
0579 QDBusObjectPath KWalletFreedesktopService::nextPromptPath()
0580 {
0581     static uint64_t id = 0;
0582     return QDBusObjectPath(QStringLiteral(FDO_SECRET_SERVICE_PROMPT_PATH) + QStringLiteral("p") + QString::number(id++));
0583 }
0584 
0585 QDBusArgument &operator<<(QDBusArgument &arg, const FreedesktopSecret &secret)
0586 {
0587     arg.beginStructure();
0588     arg << secret.session;
0589     arg << secret.parameters;
0590     arg << secret.value;
0591     arg << secret.mimeType;
0592     arg.endStructure();
0593     return arg;
0594 }
0595 
0596 const QDBusArgument &operator>>(const QDBusArgument &arg, FreedesktopSecret &secret)
0597 {
0598     arg.beginStructure();
0599     arg >> secret.session;
0600     arg >> secret.parameters;
0601     arg >> secret.value;
0602     arg >> secret.mimeType;
0603     arg.endStructure();
0604     return arg;
0605 }
0606 
0607 QDataStream &operator<<(QDataStream &stream, const QCA::SecureArray &value)
0608 {
0609     QByteArray bytes = value.toByteArray();
0610     stream << bytes;
0611     explicit_zero_mem(bytes.data(), bytes.size());
0612     return stream;
0613 }
0614 
0615 QDataStream &operator>>(QDataStream &stream, QCA::SecureArray &value)
0616 {
0617     QByteArray bytes;
0618     stream >> bytes;
0619     value = QCA::SecureArray(bytes);
0620     explicit_zero_mem(bytes.data(), bytes.size());
0621     return stream;
0622 }
0623 
0624 QDBusArgument &operator<<(QDBusArgument &arg, const QCA::SecureArray &value)
0625 {
0626     QByteArray bytes = value.toByteArray();
0627     arg << bytes;
0628     explicit_zero_mem(bytes.data(), bytes.size());
0629     return arg;
0630 }
0631 
0632 const QDBusArgument &operator>>(const QDBusArgument &arg, QCA::SecureArray &buf)
0633 {
0634     QByteArray byteArray;
0635     arg >> byteArray;
0636     buf = QCA::SecureArray(byteArray);
0637     explicit_zero_mem(byteArray.data(), byteArray.size());
0638     return arg;
0639 }
0640 
0641 KWalletD *KWalletFreedesktopService::backend() const
0642 {
0643     return m_parent;
0644 }
0645 
0646 QDBusObjectPath KWalletFreedesktopService::fdoObjectPath() const
0647 {
0648     return QDBusObjectPath(FDO_SECRETS_SERVICE_OBJECT);
0649 }
0650 
0651 KWalletFreedesktopItem *KWalletFreedesktopService::getItemByObjectPath(const QDBusObjectPath &path) const
0652 {
0653     const auto str = path.path();
0654     if (!str.startsWith(QStringLiteral(FDO_SECRETS_COLLECTION_PATH))) {
0655         return nullptr;
0656     }
0657 
0658     const QString collectionPath = wrapToCollectionPath(str);
0659     const auto collectionPos = m_collections.find(collectionPath);
0660     if (collectionPos == m_collections.end()) {
0661         return nullptr;
0662     }
0663 
0664     const auto &collection = collectionPos->second;
0665     return collection->getItemByObjectPath(str);
0666 }
0667 
0668 KWalletFreedesktopPrompt *KWalletFreedesktopService::getPromptByObjectPath(const QDBusObjectPath &path) const
0669 {
0670     const auto foundPrompt = m_prompts.find(path.path());
0671     if (foundPrompt != m_prompts.end()) {
0672         return foundPrompt->second.get();
0673     } else {
0674         return nullptr;
0675     }
0676 }
0677 
0678 FdoUniqueLabel KWalletFreedesktopService::makeUniqueCollectionLabel(const QString &label)
0679 {
0680     int n = -1;
0681     auto walletName = label;
0682     const QStringList wallets = backend()->wallets();
0683 
0684     while (wallets.contains(walletName)) {
0685         walletName = FdoUniqueLabel::makeName(label, ++n);
0686     }
0687 
0688     return {label, n};
0689 }
0690 
0691 QString KWalletFreedesktopService::makeUniqueWalletName(const QString &labelPrefix)
0692 {
0693     return makeUniqueCollectionLabel(labelPrefix).toName();
0694 }
0695 
0696 QDBusObjectPath KWalletFreedesktopService::makeUniqueObjectPath(const QString &walletName) const
0697 {
0698     auto mangled = mangleInvalidObjectPathChars(walletName);
0699     mangled.insert(0, QStringLiteral(FDO_SECRETS_COLLECTION_PATH));
0700 
0701     QString result = mangled;
0702     int postfix = 0;
0703     while (m_collections.count(result)) {
0704         result = mangled + QString::number(postfix++);
0705     }
0706 
0707     return QDBusObjectPath(result);
0708 }
0709 
0710 QStringList KWalletFreedesktopService::readAliasesFor(const QString &walletName)
0711 {
0712     m_kwalletrc.reparseConfiguration();
0713     KConfigGroup cfg(&m_kwalletrc, "org.freedesktop.secrets.aliases");
0714     const auto map = cfg.entryMap();
0715     QStringList aliases;
0716 
0717     for (auto i = map.begin(); i != map.end(); ++i) {
0718         if (i.value() == walletName) {
0719             aliases.push_back(i.key());
0720         }
0721     }
0722 
0723     KConfigGroup cfgWallet(&m_kwalletrc, "Wallet");
0724     if (defaultWalletName(cfgWallet) == walletName) {
0725         aliases.push_back(QStringLiteral("default"));
0726     }
0727 
0728     return aliases;
0729 }
0730 
0731 void KWalletFreedesktopService::updateCollectionAlias(const QString &alias, const QString &walletName)
0732 {
0733     QString sectName = QStringLiteral("org.freedesktop.secrets.aliases");
0734     QString sectKey = alias;
0735 
0736     if (alias == QStringLiteral("default")) {
0737         sectName = QStringLiteral("Wallet");
0738         sectKey = QStringLiteral("Default Wallet");
0739     }
0740 
0741     KConfigGroup cfg(&m_kwalletrc, sectName);
0742     cfg.writeEntry(sectKey, walletName);
0743     m_kwalletrc.sync();
0744 }
0745 
0746 void KWalletFreedesktopService::createCollectionAlias(const QString &alias, const QString &walletName)
0747 {
0748     QString sectName = QStringLiteral("org.freedesktop.secrets.aliases");
0749     QString sectKey = alias;
0750 
0751     if (alias == QStringLiteral("default")) {
0752         sectName = QStringLiteral("Wallet");
0753         sectKey = QStringLiteral("Default Wallet");
0754     }
0755 
0756     m_kwalletrc.reparseConfiguration();
0757     KConfigGroup cfg(&m_kwalletrc, sectName);
0758 
0759     const QString prevWalletName = cfg.readEntry(sectKey, QString());
0760     if (!prevWalletName.isEmpty()) {
0761         const auto *prevCollection = getCollectionByWalletName(prevWalletName);
0762         if (prevCollection) {
0763             QDBusConnection::sessionBus().unregisterObject(QStringLiteral(FDO_ALIAS_PATH) + alias);
0764         }
0765     }
0766 
0767     cfg.writeEntry(sectKey, walletName);
0768     m_kwalletrc.sync();
0769 
0770     auto *collection = getCollectionByWalletName(walletName);
0771     if (collection) {
0772         QDBusConnection::sessionBus().registerObject(QStringLiteral(FDO_ALIAS_PATH) + alias, collection);
0773     }
0774 }
0775 
0776 void KWalletFreedesktopService::createCollectionAlias(const QString &alias, KWalletFreedesktopCollection *collection)
0777 {
0778     QString sectName = QStringLiteral("org.freedesktop.secrets.aliases");
0779     QString sectKey = alias;
0780 
0781     if (alias == QStringLiteral("default")) {
0782         sectName = QStringLiteral("Wallet");
0783         sectKey = QStringLiteral("Default Wallet");
0784     }
0785 
0786     m_kwalletrc.reparseConfiguration();
0787     KConfigGroup cfg(&m_kwalletrc, sectName);
0788 
0789     const QString prevWalletName = cfg.readEntry(sectKey, "");
0790     if (!prevWalletName.isEmpty()) {
0791         const auto *prevCollection = getCollectionByWalletName(prevWalletName);
0792         if (prevCollection) {
0793             QDBusConnection::sessionBus().unregisterObject(QStringLiteral(FDO_ALIAS_PATH) + alias);
0794         }
0795     }
0796 
0797     cfg.writeEntry(sectKey, collection->walletName());
0798     m_kwalletrc.sync();
0799     QDBusConnection::sessionBus().registerObject(QStringLiteral(FDO_ALIAS_PATH) + alias, collection);
0800 }
0801 
0802 void KWalletFreedesktopService::removeAlias(const QString &alias)
0803 {
0804     if (alias == QStringLiteral("default")) {
0805         return;
0806     }
0807 
0808     KConfigGroup cfg(&m_kwalletrc, "org.freedesktop.secrets.aliases");
0809     cfg.deleteEntry(alias);
0810     m_kwalletrc.sync();
0811     QDBusConnection::sessionBus().unregisterObject(QStringLiteral(FDO_ALIAS_PATH) + alias);
0812 }
0813 
0814 KWalletFreedesktopCollection *KWalletFreedesktopService::getCollectionByWalletName(const QString &walletName) const
0815 {
0816     for (const auto &collectionKeyValue : m_collections) {
0817         const auto collection = collectionKeyValue.second.get();
0818         if (collection->walletName() == walletName) {
0819             return collection;
0820         }
0821     }
0822 
0823     return nullptr;
0824 }
0825 
0826 void KWalletFreedesktopService::deletePrompt(const QString &objectPath)
0827 {
0828     const auto foundPrompt = m_prompts.find(objectPath);
0829     if (foundPrompt == m_prompts.end()) {
0830         return;
0831     }
0832 
0833     /* This can be called in the context of the prompt that is currently being
0834      * deleted. Therefore, we should schedule deletion on the next event loop iteration
0835      */
0836     foundPrompt->second->deleteLater();
0837     foundPrompt->second.release();
0838     m_prompts.erase(foundPrompt);
0839 }
0840 
0841 void KWalletFreedesktopService::deleteSession(const QString &objectPath)
0842 {
0843     const auto foundSession = m_sessions.find(objectPath);
0844     if (foundSession == m_sessions.end()) {
0845         return;
0846     }
0847 
0848     /* This can be called in the context of the session that is currently being
0849      * deleted. Therefore, we should schedule deletion on the next event loop iteration
0850      */
0851     foundSession->second->deleteLater();
0852     foundSession->second.release();
0853     m_sessions.erase(foundSession);
0854 }
0855 
0856 void KWalletFreedesktopService::onCollectionCreated(const QDBusObjectPath &path)
0857 {
0858     Q_EMIT CollectionCreated(path);
0859 
0860     QVariantMap props;
0861     props.insert(QStringLiteral("Collections"), QVariant::fromValue(collections()));
0862     onPropertiesChanged(props);
0863 }
0864 
0865 void KWalletFreedesktopService::onCollectionChanged(const QDBusObjectPath &path)
0866 {
0867     Q_EMIT CollectionChanged(path);
0868 }
0869 
0870 void KWalletFreedesktopService::onCollectionDeleted(const QDBusObjectPath &path)
0871 {
0872     const auto collectionMapPos = m_collections.find(path.path());
0873     if (collectionMapPos == m_collections.end()) {
0874         return;
0875     }
0876     auto &collectionPair = *collectionMapPos;
0877     collectionPair.second->itemAttributes().deleteFile();
0878 
0879     /* This can be called in the context of the collection that is currently being
0880      * deleted. Therefore, we should schedule deletion on the next event loop iteration
0881      */
0882     collectionPair.second->deleteLater();
0883     collectionPair.second.release();
0884     m_collections.erase(collectionMapPos);
0885 
0886     Q_EMIT CollectionDeleted(path);
0887 
0888     QVariantMap props;
0889     props[QStringLiteral("Collections")] = QVariant::fromValue(collections());
0890     onPropertiesChanged(props);
0891 }
0892 
0893 void KWalletFreedesktopService::onPropertiesChanged(const QVariantMap &properties)
0894 {
0895     auto msg = QDBusMessage::createSignal(fdoObjectPath().path(), QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("PropertiesChanged"));
0896     auto args = QVariantList();
0897     args << QStringLiteral("org.freedesktop.Secret.Service") << properties << QStringList();
0898     msg.setArguments(args);
0899     QDBusConnection::sessionBus().send(msg);
0900 }
0901 
0902 QDataStream &operator<<(QDataStream &stream, const QDBusObjectPath &value)
0903 {
0904     return stream << value.path();
0905 }
0906 
0907 QDataStream &operator>>(QDataStream &stream, QDBusObjectPath &value)
0908 {
0909     QString str;
0910     stream >> str;
0911     value = QDBusObjectPath(str);
0912     return stream;
0913 }
0914 
0915 const QDBusArgument &operator>>(const QDBusArgument &arg, PropertiesMap &value)
0916 {
0917     arg.beginMap();
0918     value.map.clear();
0919 
0920     while (!arg.atEnd()) {
0921         arg.beginMapEntry();
0922         QString key;
0923         QVariant val;
0924         arg >> key >> val;
0925 
0926         /* For org.freedesktop.Secret.Item.Attributes */
0927         if (val.canConvert<QDBusArgument>()) {
0928             auto metaArg = val.value<QDBusArgument>();
0929             StrStrMap metaMap;
0930             metaArg >> metaMap;
0931             val = QVariant::fromValue(metaMap);
0932         }
0933         value.map.insert(key, val);
0934 
0935         arg.endMapEntry();
0936     }
0937     arg.endMap();
0938 
0939     return arg;
0940 }
0941 
0942 QDBusArgument &operator<<(QDBusArgument &arg, const PropertiesMap &value)
0943 {
0944     arg << value.map;
0945     return arg;
0946 }
0947 
0948 void explicit_zero_mem(void *data, size_t size)
0949 {
0950 #if defined(KWALLETD_HAVE_EXPLICIT_BZERO)
0951     explicit_bzero(data, size);
0952 #elif defined(KWALLETD_HAVE_RTLSECUREZEROMEMORY)
0953     RtlSecureZeroMemory(data, size);
0954 #else
0955     auto p = reinterpret_cast<volatile char *>(data);
0956     for (size_t i = 0; i < size; ++i) {
0957         p[i] = 0;
0958     }
0959 #endif
0960 }
0961 
0962 #include "moc_kwalletfreedesktopservice.cpp"