diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index c86e69d48..d647e3c39 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -1122,6 +1122,14 @@ Do you want to delete the entry? Select native messaging host folder location + + Allow keepassxc-proxy to list all entries with their title, URL and UUID in connected databases. + + + + Allow limited access to all entries in connected databases (ignores site access restrictions) + + CloneDialog @@ -7925,6 +7933,10 @@ Kernel: %3 %4 Invalid KDF + + Access to all entries is denied + + QtIOCompressor diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index b8b5b9879..8176e599a 100644 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -17,6 +17,7 @@ #include "BrowserAction.h" #include "BrowserService.h" +#include "BrowserSettings.h" #include "core/Global.h" #include "core/Tools.h" @@ -101,6 +102,8 @@ QJsonObject BrowserAction::handleAction(QLocalSocket* socket, const QJsonObject& return handleDeleteEntry(json, action); } else if (action.compare(BROWSER_REQUEST_REQUEST_AUTOTYPE) == 0) { return handleGlobalAutoType(json, action); + } else if (action.compare("get-database-entries", Qt::CaseSensitive) == 0) { + return handleGetDatabaseEntries(json, action); } // Action was not recognized @@ -376,6 +379,40 @@ QJsonObject BrowserAction::handleGetDatabaseGroups(const QJsonObject& json, cons return buildResponse(action, browserRequest.incrementedNonce, params); } +QJsonObject BrowserAction::handleGetDatabaseEntries(const QJsonObject& json, const QString& action) +{ + const QString hash = browserService()->getDatabaseHash(); + const QString nonce = json.value("nonce").toString(); + const QString encrypted = json.value("message").toString(); + + if (!m_associated) { + return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED); + } + + const auto browserRequest = decodeRequest(json); + if (browserRequest.isEmpty()) { + return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE); + } + + const auto command = browserRequest.getString("action"); + if (command.isEmpty() || command.compare("get-database-entries") != 0) { + return getErrorReply(action, ERROR_KEEPASS_INCORRECT_ACTION); + } + + if (!browserSettings()->allowGetDatabaseEntriesRequest()) { + return getErrorReply(action, ERROR_KEEPASS_ACCESS_TO_ALL_ENTRIES_DENIED); + } + + const QJsonArray entries = browserService()->getDatabaseEntries(); + if (entries.isEmpty()) { + return getErrorReply(action, ERROR_KEEPASS_NO_GROUPS_FOUND); + } + + const Parameters params{{"entries", entries}}; + + return buildResponse(action, browserRequest.incrementedNonce, params); +} + QJsonObject BrowserAction::handleCreateNewGroup(const QJsonObject& json, const QString& action) { if (!m_associated) { diff --git a/src/browser/BrowserAction.h b/src/browser/BrowserAction.h index 5013f3d3b..fe65c977a 100644 --- a/src/browser/BrowserAction.h +++ b/src/browser/BrowserAction.h @@ -68,6 +68,7 @@ private: QJsonObject handleSetLogin(const QJsonObject& json, const QString& action); QJsonObject handleLockDatabase(const QJsonObject& json, const QString& action); QJsonObject handleGetDatabaseGroups(const QJsonObject& json, const QString& action); + QJsonObject handleGetDatabaseEntries(const QJsonObject& json, const QString& action); QJsonObject handleCreateNewGroup(const QJsonObject& json, const QString& action); QJsonObject handleGetTotp(const QJsonObject& json, const QString& action); QJsonObject handleDeleteEntry(const QJsonObject& json, const QString& action); diff --git a/src/browser/BrowserMessageBuilder.cpp b/src/browser/BrowserMessageBuilder.cpp index 4069ae29e..efbaf8cc2 100644 --- a/src/browser/BrowserMessageBuilder.cpp +++ b/src/browser/BrowserMessageBuilder.cpp @@ -123,6 +123,8 @@ QString BrowserMessageBuilder::getErrorMessage(const int errorCode) const return QObject::tr("Cannot create new group"); case ERROR_KEEPASS_NO_VALID_UUID_PROVIDED: return QObject::tr("No valid UUID provided"); + case ERROR_KEEPASS_ACCESS_TO_ALL_ENTRIES_DENIED: + return QObject::tr("Access to all entries is denied"); default: return QObject::tr("Unknown error"); } diff --git a/src/browser/BrowserMessageBuilder.h b/src/browser/BrowserMessageBuilder.h index c8605ab58..1248522af 100644 --- a/src/browser/BrowserMessageBuilder.h +++ b/src/browser/BrowserMessageBuilder.h @@ -47,7 +47,8 @@ namespace ERROR_KEEPASS_NO_LOGINS_FOUND = 15, ERROR_KEEPASS_NO_GROUPS_FOUND = 16, ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP = 17, - ERROR_KEEPASS_NO_VALID_UUID_PROVIDED = 18 + ERROR_KEEPASS_NO_VALID_UUID_PROVIDED = 18, + ERROR_KEEPASS_ACCESS_TO_ALL_ENTRIES_DENIED = 19 }; } diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 50b1a5cc3..07273540c 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -219,6 +219,35 @@ QJsonObject BrowserService::getDatabaseGroups() return result; } +QJsonArray BrowserService::getDatabaseEntries() +{ + auto db = getDatabase(); + if (!db) { + return {}; + } + + Group* rootGroup = db->rootGroup(); + if (!rootGroup) { + return {}; + } + + QJsonArray entries; + for (const auto& group : rootGroup->groupsRecursive(true)) { + if (group == db->metadata()->recycleBin()) { + continue; + } + + for (const auto& entry : group->entries()) { + QJsonObject jentry; + jentry["title"] = entry->resolveMultiplePlaceholders(entry->title()); + jentry["uuid"] = entry->resolveMultiplePlaceholders(entry->uuidToHex()); + jentry["url"] = entry->resolveMultiplePlaceholders(entry->url()); + entries.push_back(jentry); + } + } + return entries; +} + QJsonObject BrowserService::createNewGroup(const QString& groupName) { auto db = getDatabase(); diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index 82a8f6a1a..0d804bd91 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -76,6 +76,7 @@ public: void lockDatabase(); QJsonObject getDatabaseGroups(); + QJsonArray getDatabaseEntries(); QJsonObject createNewGroup(const QString& groupName); QString getCurrentTotp(const QString& uuid); void showPasswordGenerator(const KeyPairMessage& keyPairMessage); diff --git a/src/browser/BrowserSettings.cpp b/src/browser/BrowserSettings.cpp index 7b5704e78..329659c05 100644 --- a/src/browser/BrowserSettings.cpp +++ b/src/browser/BrowserSettings.cpp @@ -222,6 +222,16 @@ void BrowserSettings::setUpdateBinaryPath(bool enabled) config()->set(Config::Browser_UpdateBinaryPath, enabled); } +bool BrowserSettings::allowGetDatabaseEntriesRequest() +{ + return config()->get(Config::Browser_AllowGetDatabaseEntriesRequest).toBool(); +} + +void BrowserSettings::setAllowGetDatabaseEntriesRequest(bool enabled) +{ + config()->set(Config::Browser_AllowGetDatabaseEntriesRequest, enabled); +} + bool BrowserSettings::allowExpiredCredentials() { return config()->get(Config::Browser_AllowExpiredCredentials).toBool(); diff --git a/src/browser/BrowserSettings.h b/src/browser/BrowserSettings.h index 18ededb4c..a961a56ae 100644 --- a/src/browser/BrowserSettings.h +++ b/src/browser/BrowserSettings.h @@ -63,6 +63,8 @@ public: #endif bool updateBinaryPath(); void setUpdateBinaryPath(bool enabled); + bool allowGetDatabaseEntriesRequest(); + void setAllowGetDatabaseEntriesRequest(bool enabled); bool allowExpiredCredentials(); void setAllowExpiredCredentials(bool enabled); diff --git a/src/browser/BrowserSettingsWidget.cpp b/src/browser/BrowserSettingsWidget.cpp index 8dd26d324..9ce4ae613 100644 --- a/src/browser/BrowserSettingsWidget.cpp +++ b/src/browser/BrowserSettingsWidget.cpp @@ -125,6 +125,7 @@ void BrowserSettingsWidget::loadSettings() m_ui->useCustomProxy->setChecked(settings->useCustomProxy()); m_ui->customProxyLocation->setText(settings->replaceHomePath(settings->customProxyLocation())); m_ui->updateBinaryPath->setChecked(settings->updateBinaryPath()); + m_ui->allowGetDatabaseEntriesRequest->setChecked(settings->allowGetDatabaseEntriesRequest()); m_ui->allowExpiredCredentials->setChecked(settings->allowExpiredCredentials()); m_ui->chromeSupport->setChecked(settings->browserSupport(BrowserShared::CHROME)); m_ui->chromiumSupport->setChecked(settings->browserSupport(BrowserShared::CHROMIUM)); @@ -217,6 +218,7 @@ void BrowserSettingsWidget::saveSettings() settings->setCustomProxyLocation(browserSettings()->replaceTildeHomePath(m_ui->customProxyLocation->text())); settings->setUpdateBinaryPath(m_ui->updateBinaryPath->isChecked()); + settings->setAllowGetDatabaseEntriesRequest(m_ui->allowGetDatabaseEntriesRequest->isChecked()); settings->setAllowExpiredCredentials(m_ui->allowExpiredCredentials->isChecked()); settings->setAlwaysAllowAccess(m_ui->alwaysAllowAccess->isChecked()); settings->setAlwaysAllowUpdate(m_ui->alwaysAllowUpdate->isChecked()); diff --git a/src/browser/BrowserSettingsWidget.ui b/src/browser/BrowserSettingsWidget.ui index 1f8902be9..13af62a40 100644 --- a/src/browser/BrowserSettingsWidget.ui +++ b/src/browser/BrowserSettingsWidget.ui @@ -327,6 +327,16 @@ + + + + Allow keepassxc-proxy to list all entries with their title, URL and UUID in connected databases. + + + Allow limited access to all entries in connected databases (ignores site access restrictions) + + + diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 56bd92cbf..2e81ad771 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -155,6 +155,7 @@ static const QHash configStrings = { {Config::Browser_UseCustomProxy, {QS("Browser/UseCustomProxy"), Roaming, false}}, {Config::Browser_CustomProxyLocation, {QS("Browser/CustomProxyLocation"), Roaming, {}}}, {Config::Browser_UpdateBinaryPath, {QS("Browser/UpdateBinaryPath"), Roaming, true}}, + {Config::Browser_AllowGetDatabaseEntriesRequest, {QS("Browser/AllowGetDatabaseEntriesRequest"), Roaming, false}}, {Config::Browser_AllowExpiredCredentials, {QS("Browser/AllowExpiredCredentials"), Roaming, false}}, {Config::Browser_AlwaysAllowAccess, {QS("Browser/AlwaysAllowAccess"), Roaming, false}}, {Config::Browser_AlwaysAllowUpdate, {QS("Browser/AlwaysAllowUpdate"), Roaming, false}}, diff --git a/src/core/Config.h b/src/core/Config.h index e0b42b35c..28a32545c 100644 --- a/src/core/Config.h +++ b/src/core/Config.h @@ -134,6 +134,7 @@ public: Browser_UseCustomProxy, Browser_CustomProxyLocation, Browser_UpdateBinaryPath, + Browser_AllowGetDatabaseEntriesRequest, Browser_AllowExpiredCredentials, Browser_AlwaysAllowAccess, Browser_AlwaysAllowUpdate,