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"