9#include <QDBusConnection>
13#include <KConfigGroup>
14#include <KLocalizedString>
15#include <KPasswordDialog>
17#include "kwalletadaptor.h"
18#include "secretserviceclient.h"
20#include "../kwalletbackend/kwalletbackend.h"
21#include "../kwalletbackend/kwalletentry.h"
23unsigned int KWalletD::s_lastTransaction = 0;
25static void startManagerForKwalletd()
28 QProcess::startDetached(QStringLiteral(
"kstart"), {QStringLiteral(
"kwalletmanager5"), QStringLiteral(
"--"), QStringLiteral(
"--kwalletd")});
34KWalletD::KWalletD(
QObject *parent)
37 m_backend =
new SecretServiceClient(
this);
39 new KWalletAdaptor(
this);
46 auto readStructure = [
this]() {
48 for (
const QString &wallet : m_backend->listCollections(&ok)) {
49 for (
const QString &folder : m_backend->listFolders(wallet, &ok)) {
50 m_structure.insert(wallet, folder);
53 qCDebug(KWALLETD_LOG) <<
"Structure:" << m_structure;
54 qCDebug(KWALLETD_LOG) <<
"Default wallet:" << m_backend->defaultCollection(&ok);
61 connect(m_backend, &SecretServiceClient::serviceChanged,
this, [
this, readStructure]() {
62 if (m_backend->isAvailable()) {
64 Q_EMIT walletListDirty();
70 connect(m_backend, &SecretServiceClient::collectionDirty,
this, [
this, readStructure](
const QString &wallet) {
74 folders = m_backend->listFolders(wallet, &ok);
81 Q_EMIT folderListUpdated(wallet);
83 for (
const QString &folder : m_structure.values(wallet)) {
84 Q_EMIT folderUpdated(folder, wallet);
88 connect(m_backend, &SecretServiceClient::collectionCreated,
this, &KWalletD::walletCreated);
89 connect(m_backend, &SecretServiceClient::collectionDeleted,
this, &KWalletD::walletDeleted);
90 connect(m_backend, &SecretServiceClient::collectionListDirty,
this, &KWalletD::walletListDirty);
97 KConfig cfg(QStringLiteral(
"kwalletrc"));
98 KConfigGroup walletGroup(&cfg, QStringLiteral(
"Wallet"));
99 const QString defaultCollection = m_backend->defaultCollection(&ok);
100 const QString newDefaultWallet = walletGroup.readEntry(QStringLiteral(
"Default Wallet"), defaultCollection);
102 if (newDefaultWallet != defaultCollection) {
103 m_backend->setDefaultCollection(newDefaultWallet, &ok);
112bool KWalletD::migrateWallet(
const QString &sourceWallet,
const QString &destWallet)
114 qCWarning(KWALLETD_LOG) <<
"Migrating" << sourceWallet;
115 KWallet::Backend backend(sourceWallet, sourceWallet.
endsWith(QStringLiteral(
".kwl")));
116 backend.open(QByteArray());
117 while (!backend.isOpen()) {
119 dlg.
setPrompt(
i18n(
"Migrating the wallet \"%1\". Please provide the password to unlock.", sourceWallet));
130 const QStringList allDestWallets = m_backend->listCollections(&ok);
134 if (!allDestWallets.
contains(destWallet)) {
135 qCDebug(KWALLETD_LOG) <<
"Creating" << destWallet;
136 m_backend->createCollection(destWallet, &ok);
141 qCDebug(KWALLETD_LOG) <<
"Unlocking" << destWallet;
142 bool unlocked = m_backend->unlockCollection(destWallet, &ok);
143 if (!ok || !unlocked) {
147 for (
const QString &folder : backend.folderList()) {
148 backend.setFolder(folder);
149 for (KWallet::Entry *entry : backend.entriesList()) {
150 switch (entry->type()) {
151 case KWallet::Wallet::Password:
152 writeString(entry->key(), entry->password(), folder, destWallet, &ok);
154 case KWallet::Wallet::Stream:
155 writeBinary(entry->key(), entry->value(), folder, destWallet, &ok);
157 case KWallet::Wallet::Map: {
158 QByteArray mapData = entry->map();
161 QMap<QString, QString> v;
165 obj[it.key()] = it.
value();
180void KWalletD::migrateData()
182 if (!m_backend->isAvailable() || m_backend->useKSecretBackend()) {
185 KConfig cfg(QStringLiteral(
"kwalletrc"));
186 KConfigGroup walletGroup(&cfg, QStringLiteral(
"Wallet"));
187 KConfigGroup migrationGroup(&cfg, QStringLiteral(
"Migration"));
189 if (!migrationGroup.readEntry(QStringLiteral(
"MigrateTo3rdParty"),
false)) {
193 QStringList walletsMigrated = migrationGroup.readEntry(QStringLiteral(
"WalletsMigratedToSecretService"), QStringList());
196 const QString
path = KWallet::Backend::getSaveLocation();
197 QDir
dir(path, QStringLiteral(
"*.kwl"));
202 const auto list =
dir.entryInfoList();
205 for (
const QFileInfo &fi : list) {
206 QString fn = fi.fileName();
207 if (fn.
endsWith(QLatin1String(
".kwl"))) {
210 wallets += KWallet::Backend::decodeWalletName(fn);
214 const QString oldDefaultWallet = walletGroup.readEntry(QStringLiteral(
"Default Wallet"), QStringLiteral(
"kdewallet"));
215 for (
const QString &sourceWallet : std::as_const(wallets)) {
216 QString destWallet = sourceWallet;
218 if (sourceWallet == oldDefaultWallet) {
219 destWallet = m_backend->defaultCollection(&ok);
221 if (walletsMigrated.
contains(sourceWallet)) {
224 if (migrateWallet(sourceWallet, destWallet)) {
225 walletsMigrated << sourceWallet;
230 walletGroup.writeEntry(QStringLiteral(
"Default Wallet"), m_backend->defaultCollection(&ok));
232 migrationGroup.writeEntry(QStringLiteral(
"WalletsMigratedToSecretService"), walletsMigrated);
236QString KWalletD::walletForHandle(
int handle,
const QString &appId)
240 const QPair<int, QString> key(handle, appId);
245 const QString walletName = m_openWallets.value(QPair<int, QString>(handle, appId));
252 return folder % QStringLiteral(
"/") % key;
255KWalletD::EntryType KWalletD::keyType(
const QString &wallet,
const QString &folder,
const QString &key)
258 const QHash<QString, QString> hash = m_backend->readMetadata(key, folder, wallet, &ok);
264 const QString typeStr = hash.
value(QStringLiteral(
"type"));
266 if (typeStr == QStringLiteral(
"map")) {
268 }
else if (typeStr == QStringLiteral(
"base64") || typeStr == QStringLiteral(
"binary")) {
277 return QString::fromUtf8(m_backend->readEntry(key, SecretServiceClient::PlainText, folder, wallet, ok));
282 return m_backend->readEntry(key, SecretServiceClient::Map, folder, wallet, ok);
288 QByteArray ret = m_backend->readEntry(key, SecretServiceClient::Base64, folder, wallet, ok);
290 ret = m_backend->readEntry(key, SecretServiceClient::Binary, folder, wallet, ok);
297 m_backend->writeEntry(folderPath(folder, key), key, value.
toUtf8(), SecretServiceClient::PlainText, folder, wallet, ok);
302 m_backend->writeEntry(folderPath(folder, key), key, value, SecretServiceClient::Base64, folder, wallet, ok);
307 m_backend->writeEntry(folderPath(folder, key), key, value, SecretServiceClient::Map, folder, wallet, ok);
312 m_backend->deleteEntry(key, folder, wallet, ok);
317 const int timer = ev->
timerId();
319 for (
auto it = m_idleTimers.constBegin(); it != m_idleTimers.end(); it++) {
320 if (it.value() == timer) {
321 close(it.key().first,
true, it.key().second);
330bool KWalletD::isEnabled()
const
332 return m_enabled && m_backend->isAvailable();
335int KWalletD::openInternal(
const QString &wallet, qlonglong wId,
const QString &appId)
342 const QStringList wallets = m_backend->listCollections(&ok);
346 if (!wallets.contains(wallet)) {
347 m_backend->createCollection(wallet, &ok);
352 bool unlocked = m_backend->unlockCollection(wallet, &ok);
353 if (!ok || !unlocked) {
357 for (
const QString &folder : m_backend->listFolders(wallet, &ok)) {
358 m_structure.insert(wallet, folder);
362 const int rnd = std::abs(
int(rand.
generate()));
364 m_openWallets[QPair<int, QString>(rnd, appId)] = wallet;
367 m_idleTimers[QPair<int, QString>(rnd, appId)] =
startTimer(m_idleTime);
373int KWalletD::open(
const QString &wallet, qlonglong wId,
const QString &appId)
375 int rnd = openInternal(wallet, wId, appId);
380 Q_EMIT walletOpened(wallet);
384int KWalletD::openPath(
const QString &path, qlonglong wId,
const QString &appId)
393int KWalletD::openAsync(
const QString &wallet, qlonglong wId,
const QString &appId,
bool handleSession)
395 Q_UNUSED(handleSession);
401 const int tid = ++s_lastTransaction;
404 int rnd = openInternal(wallet, wId, appId);
408 Q_EMIT walletAsyncOpened(tid, rnd);
414int KWalletD::openPathAsync(
const QString &path, qlonglong wId,
const QString &appId,
bool handleSession)
420 Q_UNUSED(handleSession);
424int KWalletD::close(
const QString &wallet,
bool force)
427 auto it = m_openWallets.begin();
429 while (it != m_openWallets.end()) {
430 if (it.value() == wallet) {
432 apps << it.key().second;
433 it = m_openWallets.
erase(it);
442 Q_EMIT walletClosed(wallet);
443 if (m_openWallets.isEmpty()) {
444 Q_EMIT allWalletsClosed();
446 QSet<QString> newApps;
447 for (
auto it = m_openWallets.constBegin(); it != m_openWallets.constEnd(); it++) {
448 newApps << it.key().second;
450 QSet<QString> disconnected = apps.
subtract(newApps);
451 for (
const QString &appId : std::as_const(disconnected)) {
452 Q_EMIT applicationDisconnected(wallet, appId);
458int KWalletD::close(
int handle,
bool force,
const QString &appId)
461 const QString wallet = walletForHandle(handle, appId);
465 auto it = m_openWallets.begin();
467 while (it != m_openWallets.end()) {
468 if (it.key().first == handle && it.key().second == appId) {
470 it = m_openWallets.erase(it);
479 Q_EMIT walletClosed(handle);
480 Q_EMIT walletClosedId(handle);
481 if (m_openWallets.isEmpty()) {
482 Q_EMIT allWalletsClosed();
484 QSet<QString> newApps;
485 for (
auto it = m_openWallets.constBegin(); it != m_openWallets.constEnd(); it++) {
486 newApps << it.key().second;
489 Q_EMIT applicationDisconnected(wallet, appId);
495void KWalletD::sync(
int handle,
const QString &appId)
502int KWalletD::deleteWallet(
const QString &wallet)
505 m_backend->deleteCollection(wallet, &ok);
508 m_structure.remove(wallet);
514bool KWalletD::isOpen(
const QString &wallet)
516 for (
const QString &w : std::as_const(m_openWallets)) {
524bool KWalletD::isOpen(
int handle)
526 for (
auto it = m_openWallets.constBegin(); it != m_openWallets.constEnd(); it++) {
527 if (it.key().first == handle) {
536 QSet<QString> allApps;
538 for (
auto it = m_openWallets.constBegin(); it != m_openWallets.constEnd(); it++) {
539 if (it.value() == wallet) {
540 allApps.
insert(it.key().second);
547void KWalletD::changePassword(
const QString &wallet, qlonglong wId,
const QString &appId)
549 if (!m_backend->useKSecretBackend()) {
553 QDBusInterface legacyKWalletInterface(QStringLiteral(
"org.kde.ksecretd"),
554 QStringLiteral(
"/ksecretd"),
555 QStringLiteral(
"org.kde.KWallet"),
558 if (!legacyKWalletInterface.isValid()) {
562 legacyKWalletInterface.asyncCall(QStringLiteral(
"changePassword"), wallet, wId, appId);
568 return m_backend->listCollections(&ok);
573 const QString wallet = walletForHandle(handle, appId);
575 return QStringList();
578 return m_structure.values(wallet);
581bool KWalletD::hasFolder(
int handle,
const QString &folder,
const QString &appId)
583 const QString wallet = walletForHandle(handle, appId);
587 for (
auto it = m_structure.constBegin(); it != m_structure.constEnd(); it++) {
588 if (it.value().contains(folder)) {
592 return m_structure.contains(folder);
595bool KWalletD::createFolder(
int handle,
const QString &folder,
const QString &appId)
597 const QString wallet = walletForHandle(handle, appId);
604 m_structure.insert(wallet, folder);
608bool KWalletD::removeFolder(
int handle,
const QString &folder,
const QString &appId)
610 const QString wallet = walletForHandle(handle, appId);
616 m_backend->deleteFolder(folder, wallet, &ok);
618 m_structure.remove(wallet, folder);
625 const QString wallet = walletForHandle(handle, appId);
627 return QStringList();
631 return m_backend->listEntries(folder, wallet, &ok);
636 const QString wallet = walletForHandle(handle, appId);
642 EntryType
type = keyType(wallet, folder, key);
646 return readBinary(key, folder, wallet, &ok);
648 return readMap(handle, folder, key, appId);
650 return readString(key, folder, wallet, &ok).toUtf8();
656 const QString wallet = walletForHandle(handle, appId);
668 QMap<QString, QString>
map;
670 map[it.key()] = it.value().toString();
682 const QString wallet = walletForHandle(handle, appId);
688 return readString(key, folder, wallet, &ok);
691QVariantMap KWalletD::readEntryList(
int handle,
const QString &folder,
const QString &key,
const QString &appId)
694 const QString wallet = walletForHandle(handle, appId);
701 EntryType
type = keyType(wallet, folder, key);
704 map[key] = readBinary(key, folder, wallet, &ok);
707 map[key] = readMapList(handle, folder, key, appId);
710 map[key] = readString(key, folder, wallet, &ok).toUtf8();
716QVariantMap KWalletD::entriesList(
int handle,
const QString &folder,
const QString &appId)
719 const QString wallet = walletForHandle(handle, appId);
726 const QStringList keys = m_backend->listEntries(folder, wallet, &ok);
731 for (
const QString &key : std::as_const(keys)) {
732 EntryType
type = keyType(wallet, folder, key);
735 map[key] = readBinary(key, folder, wallet, &ok);
738 map[key] = readMap(handle, folder, key, appId);
741 map[key] = readString(key, folder, wallet, &ok).toUtf8();
744 return QVariantMap();
751QVariantMap KWalletD::readMapList(
int handle,
const QString &folder,
const QString &key,
const QString &appId)
754 const QString wallet = walletForHandle(handle, appId);
761 EntryType
type = keyType(wallet, folder, key);
772 map[it.key()] = it.value().toString();
778QVariantMap KWalletD::mapList(
int handle,
const QString &folder,
const QString &appId)
781 const QString wallet = walletForHandle(handle, appId);
788 const QStringList keys = m_backend->listEntries(folder, wallet, &ok);
793 for (
const QString &key : std::as_const(keys)) {
794 EntryType
type = keyType(wallet, folder, key);
796 map[key] = readMap(handle, folder, key, appId);
803QVariantMap KWalletD::readPasswordList(
int handle,
const QString &folder,
const QString &key,
const QString &appId)
806 const QString wallet = walletForHandle(handle, appId);
813 EntryType
type = keyType(wallet, folder, key);
814 if (type == Password) {
815 map[key] = readString(key, folder, wallet, &ok);
817 return QVariantMap();
824QVariantMap KWalletD::passwordList(
int handle,
const QString &folder,
const QString &appId)
827 const QString wallet = walletForHandle(handle, appId);
834 const QStringList keys = m_backend->listEntries(folder, wallet, &ok);
839 for (
const QString &key : std::as_const(keys)) {
840 EntryType
type = keyType(wallet, folder, key);
841 if (type == Password) {
842 map[key] = readString(key, folder, wallet, &ok);
844 return QVariantMap();
854 const QString wallet = walletForHandle(handle, appId);
860 m_backend->renameEntry(folderPath(folder, newName), oldName, newName, folder, wallet, &ok);
871 const QString wallet = walletForHandle(handle, appId);
878 QStringList oldFolders = m_backend->listFolders(wallet, &ok);
879 QStringList oldEntries = m_backend->listEntries(wallet, folder, &ok);
886 writeBinary(key, value, folder, wallet, &ok);
889 QMap<QString, QString>
map;
891 QDataStream ds(value);
895 for (
auto it =
map.constBegin(); it !=
map.constEnd(); it++) {
896 obj.
insert(it.key(), it.value());
911 m_structure.insert(wallet, folder);
912 Q_EMIT folderListUpdated(wallet);
915 Q_EMIT folderUpdated(wallet, folder);
917 Q_EMIT entryUpdated(wallet, folder, key);
924 return writeEntry(handle, folder, key, value, Stream, appId);
929 return writeEntry(handle, folder, key, value, Map, appId);
934 return writeEntry(handle, folder, key, value.
toUtf8(), Password, appId);
939 const QString wallet = walletForHandle(handle, appId);
945 const QStringList entries = m_backend->listEntries(folder, wallet, &ok);
956 const QString wallet = walletForHandle(handle, appId);
961 return keyType(wallet, folder, key);
964int KWalletD::removeEntry(
int handle,
const QString &folder,
const QString &key,
const QString &appId)
966 const QString wallet = walletForHandle(handle, appId);
972 removeItem(key, folder, wallet, &ok);
974 QStringList folders = m_backend->listFolders(wallet, &ok);
975 if (ok && !folders.
contains(folder)) {
976 m_structure.remove(wallet, folder);
977 folderListUpdated(wallet);
980 Q_EMIT entryDeleted(wallet, folder, key);
987bool KWalletD::disconnectApplication(
const QString &wallet,
const QString &application)
990 auto it = m_openWallets.begin();
992 while (it != m_openWallets.end()) {
993 if (it.key().second == application && it.value() == wallet) {
995 it = m_openWallets.erase(it);
1002 Q_EMIT walletClosed(wallet);
1003 if (m_openWallets.isEmpty()) {
1004 Q_EMIT allWalletsClosed();
1007 Q_EMIT applicationDisconnected(wallet, application);
1013void KWalletD::reconfigure()
1015 KConfig cfg(QStringLiteral(
"kwalletrc"));
1016 KConfigGroup walletGroup(&cfg, QStringLiteral(
"Wallet"));
1017 const bool wasEnabled = m_enabled;
1018 m_enabled = walletGroup.readEntry(
"Enabled",
true);
1019 m_launchManager = walletGroup.readEntry(
"Launch Manager",
false);
1020 const bool closeIdle = m_closeIdle;
1021 m_closeIdle = walletGroup.readEntry(
"Close When Idle",
false);
1022 const int idleTime = m_idleTime;
1024 m_idleTime = walletGroup.readEntry(
"Idle Timeout", 10) * 60 * 1000;
1026 if (wasEnabled != m_enabled) {
1028 Q_EMIT walletListDirty();
1034 if (closeIdle != m_closeIdle) {
1036 for (
auto it = m_openWallets.constBegin(); it != m_openWallets.constEnd(); it++) {
1037 m_idleTimers[it.key()] =
startTimer(m_idleTime);
1040 for (
auto it = m_idleTimers.constBegin(); it != m_idleTimers.constEnd(); it++) {
1043 m_idleTimers.clear();
1045 }
else if (idleTime != m_idleTime) {
1046 for (
auto it = m_idleTimers.begin(); it != m_idleTimers.constEnd(); it++) {
1052 if (m_launchManager) {
1053 startManagerForKwalletd();
1057bool KWalletD::folderDoesNotExist(
const QString &wallet,
const QString &folder)
1060 const QStringList entries = m_backend->listFolders(wallet, &ok);
1072 const QStringList entries = m_backend->listEntries(folder, wallet, &ok);
1081void KWalletD::closeAllWallets()
1083 QSet<QString> openWallets;
1084 QSet<QPair<QString, QString>> apps;
1086 for (
auto it = m_openWallets.constBegin(); it != m_openWallets.constEnd(); it++) {
1087 openWallets.
insert(it.value());
1088 apps.
insert({it.value(), it.key().second});
1091 m_openWallets.
clear();
1095 for (
const QString &wallet : openWallets.
values()) {
1096 Q_EMIT walletClosed(wallet);
1098 Q_EMIT allWalletsClosed();
1101QString KWalletD::networkWallet()
1104 const QString defaultWallet = m_backend->defaultCollection(&ok);
1106 KConfig cfg(QStringLiteral(
"kwalletrc"));
1107 KConfigGroup walletGroup(&cfg, QStringLiteral(
"Wallet"));
1108 return walletGroup.readEntry(QStringLiteral(
"Default Wallet"), defaultWallet);
1111QString KWalletD::localWallet()
1113 KConfig cfg(QStringLiteral(
"kwalletrc"));
1114 KConfigGroup walletGroup(&cfg, QStringLiteral(
"Wallet"));
1115 return walletGroup.readEntry(QStringLiteral(
"Local Wallet"), networkWallet());
1118int KWalletD::pamOpen(
const QString &wallet,
const QByteArray &passwordHash,
int sessionTimeout)
1120 if (!m_backend->useKSecretBackend()) {
1124 QDBusInterface secretdInterface(QStringLiteral(
"org.kde.ksecretd"),
1125 QStringLiteral(
"/ksecretd"),
1126 QStringLiteral(
"org.kde.KWallet"),
1129 secretdInterface.call(QStringLiteral(
"pamOpen"), wallet, passwordHash, sessionTimeout);
1135#include "moc_kwalletd.cpp"
static Ptr create(const KSharedConfig::Ptr &config)
void configChanged(const KConfigGroup &group, const QByteArrayList &names)
void setPrompt(const QString &prompt)
void setRevealPasswordMode(KPassword::RevealMode revealPasswordMode)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QString i18n(const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
QString path(const QString &relativePath)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
bool isEmpty() const const
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
bool registerService(const QString &serviceName)
QDBusConnection sessionBus()
QDBusConnection connection() const const
T value(const Key &key) const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
const_iterator constBegin() const const
const_iterator constEnd() const const
iterator insert(QLatin1StringView key, const QJsonValue &value)
QJsonValue value(QLatin1StringView key) const const
const_iterator constBegin() const const
const_iterator constEnd() const const
const_iterator constBegin() const const
const_iterator constEnd() const const
int startTimer(int interval, Qt::TimerType timerType)
bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
QRandomGenerator securelySeeded()
bool contains(const QSet< T > &other) const const
iterator erase(const_iterator pos)
iterator insert(const T &value)
QSet< T > & subtract(const QSet< T > &other)
QList< T > values() const const
QString findExecutable(const QString &executableName, const QStringList &paths)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
qsizetype length() const const
QByteArray toUtf8() const const
void truncate(qsizetype position)
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
int timerId() const const