diff --git a/INSTALL.md b/INSTALL.md index 2690e6091..86048b501 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -25,7 +25,7 @@ The following libraries are required: * zlib * libmicrohttpd * libxi, libxtst, qtx11extras (optional for auto-type on X11) -* libsodium (>= 1.0.12, optional for keepassxc-browser support) +* libsodium (>= 1.0.12, optional for KeePassXC-Browser support) * libargon2 @@ -88,7 +88,7 @@ These steps place the compiled KeePassXC binary inside the `./build/src/` direct -DWITH_XC_AUTOTYPE=[ON|OFF] Enable/Disable Auto-Type (default: ON) -DWITH_XC_HTTP=[ON|OFF] Enable/Disable KeePassHTTP and custom icon downloads (default: OFF) -DWITH_XC_YUBIKEY=[ON|OFF] Enable/Disable YubiKey HMAC-SHA1 authentication support (default: OFF) - -DWITH_XC_BROWSER=[ON|OFF] Enable/Disable keepassxc-browser extension support (default: OFF) + -DWITH_XC_BROWSER=[ON|OFF] Enable/Disable KeePassXC-Browser extension support (default: OFF) -DWITH_TESTS=[ON|OFF] Enable/Disable building of unit tests (default: ON) -DWITH_GUI_TESTS=[ON|OFF] Enable/Disable building of GUI tests (default: OFF) diff --git a/README.md b/README.md index 5c2373259..9f81795c5 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ so please check out your distribution's package list to see if KeePassXC is avai [Mozilla Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepasshttp-connector/) and [Google Chrome or Chromium](https://chrome.google.com/webstore/detail/keepasshttp-connector/dafgdjggglmmknipkhngniifhplpcldb), and [passafari](https://github.com/mmichaa/passafari.safariextension/) in Safari. [[See note about KeePassHTTP]](#Note_about_KeePassHTTP) -- Browser integration with keepassxc-browser using [native messaging](https://developer.chrome.com/extensions/nativeMessaging) for [Mozilla Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) and [Google Chrome or Chromium](https://chrome.google.com/webstore/detail/keepassxc-browser/iopaggbpplllidnfmcghoonnokmjoicf) +- Browser integration with KeePassXC-Browser using [native messaging](https://developer.chrome.com/extensions/nativeMessaging) for [Mozilla Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) and [Google Chrome or Chromium](https://chrome.google.com/webstore/detail/keepassxc-browser/iopaggbpplllidnfmcghoonnokmjoicf) - Many bug fixes For a full list of features and changes, read the [CHANGELOG](CHANGELOG) document. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7abbfc823..80ca54a49 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -189,7 +189,7 @@ set(keepassx_SOURCES_MAINEXE add_feature_info(Auto-Type WITH_XC_AUTOTYPE "Automatic password typing") add_feature_info(Networking WITH_XC_NETWORKING "Compile KeePassXC with network access code (e.g. for downloading website icons)") -add_feature_info(keepassxc-browser WITH_XC_BROWSER "Browser integration with keepassxc-browser") +add_feature_info(KeePassXC-Browser WITH_XC_BROWSER "Browser integration with KeePassXC-Browser") add_feature_info(KeePassHTTP WITH_XC_HTTP "Browser integration compatible with ChromeIPass and PassIFox (deprecated, implies Networking)") add_feature_info(SSHAgent WITH_XC_SSHAGENT "SSH agent integration compatible with KeeAgent") add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response") diff --git a/src/browser/BrowserAccessControlDialog.ui b/src/browser/BrowserAccessControlDialog.ui index d4f104544..29715314d 100755 --- a/src/browser/BrowserAccessControlDialog.ui +++ b/src/browser/BrowserAccessControlDialog.ui @@ -11,7 +11,7 @@ - keepassxc-browser Confirm Access + KeePassXC-Browser Confirm Access diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index ebd9e2d9a..b15d8ed59 100755 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -39,6 +39,12 @@ QJsonObject BrowserAction::readResponse(const QJsonObject& json) return QJsonObject(); } + bool triggerUnlock = false; + const QString trigger = json.value("triggerUnlock").toString(); + if (!trigger.isEmpty() && trigger.compare("true", Qt::CaseSensitive) == 0) { + triggerUnlock = true; + } + const QString action = json.value("action").toString(); if (action.isEmpty()) { return QJsonObject(); @@ -48,7 +54,7 @@ QJsonObject BrowserAction::readResponse(const QJsonObject& json) if (action.compare("change-public-keys", Qt::CaseSensitive) != 0 && !m_browserService.isDatabaseOpened()) { if (m_clientPublicKey.isEmpty()) { return getErrorReply(action, ERROR_KEEPASS_CLIENT_PUBLIC_KEY_NOT_RECEIVED); - } else if (!m_browserService.openDatabase()) { + } else if (!m_browserService.openDatabase(triggerUnlock)) { return getErrorReply(action, ERROR_KEEPASS_DATABASE_NOT_OPENED); } } @@ -134,10 +140,11 @@ QJsonObject BrowserAction::handleGetDatabaseHash(const QJsonObject& json, const QString command = decrypted.value("action").toString(); if (!command.isEmpty() && command.compare("get-databasehash", Qt::CaseSensitive) == 0) { - QJsonObject message; + const QString newNonce = incrementNonce(nonce); + + QJsonObject message = buildMessage(newNonce); message["hash"] = hash; - message["version"] = KEEPASSX_VERSION; - return buildResponse(action, message, incrementNonce(nonce)); + return buildResponse(action, message, newNonce); } return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE); @@ -237,7 +244,7 @@ QJsonObject BrowserAction::handleGetLogins(const QJsonObject& json, const QStrin const QJsonArray users = m_browserService.findMatchingEntries(id, url, submit, ""); if (users.isEmpty()) { - return QJsonObject(); // No logins found. Not an error, return an empty JSON object. + return getErrorReply(action, ERROR_KEEPASS_NO_LOGINS_FOUND); } const QString newNonce = incrementNonce(nonce); @@ -379,21 +386,22 @@ QJsonObject BrowserAction::buildResponse(const QString& action, const QJsonObjec QString BrowserAction::getErrorMessage(const int errorCode) const { switch (errorCode) { - case ERROR_KEEPASS_DATABASE_NOT_OPENED: return "Database not opened"; - case ERROR_KEEPASS_DATABASE_HASH_NOT_RECEIVED: return "Database hash not available"; - case ERROR_KEEPASS_CLIENT_PUBLIC_KEY_NOT_RECEIVED: return "Client public key not received"; - case ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE: return "Cannot decrypt message"; - case ERROR_KEEPASS_TIMEOUT_OR_NOT_CONNECTED: return "Timeout or cannot connect to KeePassXC"; - case ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED: return "Action cancelled or denied"; - case ERROR_KEEPASS_CANNOT_ENCRYPT_MESSAGE: return "Cannot encrypt message or public key not found. Is Native Messaging enabled in KeePassXC?"; - case ERROR_KEEPASS_ASSOCIATION_FAILED: return "KeePassXC association failed, try again"; - case ERROR_KEEPASS_KEY_CHANGE_FAILED: return "Key change was not successful"; - case ERROR_KEEPASS_ENCRYPTION_KEY_UNRECOGNIZED: return "Encryption key is not recognized"; - case ERROR_KEEPASS_NO_SAVED_DATABASES_FOUND: return "No saved databases found"; - case ERROR_KEEPASS_INCORRECT_ACTION: return "Incorrect action"; - case ERROR_KEEPASS_EMPTY_MESSAGE_RECEIVED: return "Empty message received"; - case ERROR_KEEPASS_NO_URL_PROVIDED: return "No URL provided"; - default: return "Unknown error"; + case ERROR_KEEPASS_DATABASE_NOT_OPENED: return QObject::tr("Database not opened"); + case ERROR_KEEPASS_DATABASE_HASH_NOT_RECEIVED: return QObject::tr("Database hash not available"); + case ERROR_KEEPASS_CLIENT_PUBLIC_KEY_NOT_RECEIVED: return QObject::tr("Client public key not received"); + case ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE: return QObject::tr("Cannot decrypt message"); + case ERROR_KEEPASS_TIMEOUT_OR_NOT_CONNECTED: return QObject::tr("Timeout or cannot connect to KeePassXC"); + case ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED: return QObject::tr("Action cancelled or denied"); + case ERROR_KEEPASS_CANNOT_ENCRYPT_MESSAGE: return QObject::tr("Cannot encrypt message or public key not found. Is Native Messaging enabled in KeePassXC?"); + case ERROR_KEEPASS_ASSOCIATION_FAILED: return QObject::tr("KeePassXC association failed, try again"); + case ERROR_KEEPASS_KEY_CHANGE_FAILED: return QObject::tr("Key change was not successful"); + case ERROR_KEEPASS_ENCRYPTION_KEY_UNRECOGNIZED: return QObject::tr("Encryption key is not recognized"); + case ERROR_KEEPASS_NO_SAVED_DATABASES_FOUND: return QObject::tr("No saved databases found"); + case ERROR_KEEPASS_INCORRECT_ACTION: return QObject::tr("Incorrect action"); + case ERROR_KEEPASS_EMPTY_MESSAGE_RECEIVED: return QObject::tr("Empty message received"); + case ERROR_KEEPASS_NO_URL_PROVIDED: return QObject::tr("No URL provided"); + case ERROR_KEEPASS_NO_LOGINS_FOUND: return QObject::tr("No logins found"); + default: return QObject::tr("Unknown error"); } } diff --git a/src/browser/BrowserAction.h b/src/browser/BrowserAction.h index 83d89fb7f..c4d59d3c9 100755 --- a/src/browser/BrowserAction.h +++ b/src/browser/BrowserAction.h @@ -43,7 +43,8 @@ class BrowserAction : public QObject ERROR_KEEPASS_NO_SAVED_DATABASES_FOUND = 11, ERROR_KEEPASS_INCORRECT_ACTION = 12, ERROR_KEEPASS_EMPTY_MESSAGE_RECEIVED = 13, - ERROR_KEEPASS_NO_URL_PROVIDED = 14 + ERROR_KEEPASS_NO_URL_PROVIDED = 14, + ERROR_KEEPASS_NO_LOGINS_FOUND = 15 }; public: diff --git a/src/browser/BrowserEntryConfig.cpp b/src/browser/BrowserEntryConfig.cpp index a390e5a75..36d0c7339 100644 --- a/src/browser/BrowserEntryConfig.cpp +++ b/src/browser/BrowserEntryConfig.cpp @@ -21,7 +21,7 @@ #include "core/Entry.h" #include "core/EntryAttributes.h" -static const char KEEPASSBROWSER_NAME[] = "keepassxc-browser Settings"; //TODO: duplicated string (also in Service.cpp) +static const char KEEPASSBROWSER_NAME[] = "KeePassXC-Browser Settings"; BrowserEntryConfig::BrowserEntryConfig(QObject* parent) : diff --git a/src/browser/BrowserOptionDialog.ui b/src/browser/BrowserOptionDialog.ui index e5352b0fa..690ae3955 100755 --- a/src/browser/BrowserOptionDialog.ui +++ b/src/browser/BrowserOptionDialog.ui @@ -29,7 +29,7 @@ - This is required for accessing your databases with keepassxc-browser + This is required for accessing your databases with KeePassXC-Browser Enable KeepassXC browser integration diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 9e2b86cb3..0a0ff2961 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -40,9 +40,9 @@ static const unsigned char KEEPASSXCBROWSER_UUID_DATA[] = { 0x97, 0x4b, 0x59, 0x11, 0xb8, 0x81, 0x62, 0x24 }; static const Uuid KEEPASSXCBROWSER_UUID = Uuid(QByteArray::fromRawData(reinterpret_cast(KEEPASSXCBROWSER_UUID_DATA), sizeof(KEEPASSXCBROWSER_UUID_DATA))); -static const char KEEPASSXCBROWSER_NAME[] = "keepassxc-browser Settings"; +static const char KEEPASSXCBROWSER_NAME[] = "KeePassXC-Browser Settings"; static const char ASSOCIATE_KEY_PREFIX[] = "Public Key: "; -static const char KEEPASSXCBROWSER_GROUP_NAME[] = "keepassxc-browser Passwords"; +static const char KEEPASSXCBROWSER_GROUP_NAME[] = "KeePassXC-Browser Passwords"; static int KEEPASSXCBROWSER_DEFAULT_ICON = 1; BrowserService::BrowserService(DatabaseTabWidget* parent) : @@ -65,7 +65,7 @@ bool BrowserService::isDatabaseOpened() const } -bool BrowserService::openDatabase() +bool BrowserService::openDatabase(bool triggerUnlock) { if (!BrowserSettings::unlockDatabase()) { return false; @@ -80,7 +80,9 @@ bool BrowserService::openDatabase() return true; } - KEEPASSXC_MAIN_WINDOW->bringToFront(); + if (triggerUnlock) { + KEEPASSXC_MAIN_WINDOW->bringToFront(); + } return false; } @@ -189,6 +191,7 @@ QString BrowserService::storeKey(const QString& key) keyDialog.show(); keyDialog.activateWindow(); keyDialog.raise(); + keyDialog.setWindowFlags(keyDialog.windowFlags() | Qt::WindowStaysOnTopHint); auto ok = keyDialog.exec(); id = keyDialog.textValue(); @@ -221,7 +224,6 @@ QString BrowserService::getKey(const QString& id) return config->attributes()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + id); } -// No need to use KeepassHttpProtocol. Just return a JSON array. QJsonArray BrowserService::findMatchingEntries(const QString& id, const QString& url, const QString& submitUrl, const QString& realm) { QJsonArray result; diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index 859daee2a..5a96e1ecd 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -35,7 +35,7 @@ public: explicit BrowserService(DatabaseTabWidget* parent); bool isDatabaseOpened() const; - bool openDatabase(); + bool openDatabase(bool triggerUnlock); QString getDatabaseRootUuid(); QString getDatabaseRecycleBinUuid(); Entry* getConfigEntry(bool create = false); diff --git a/src/browser/NativeMessagingBase.cpp b/src/browser/NativeMessagingBase.cpp index fb4959688..743953e95 100644 --- a/src/browser/NativeMessagingBase.cpp +++ b/src/browser/NativeMessagingBase.cpp @@ -121,7 +121,7 @@ void NativeMessagingBase::sendReply(const QJsonObject& json) void NativeMessagingBase::sendReply(const QString& reply) { if (!reply.isEmpty()) { - uint len = reply.length(); + uint len = reply.length(); std::cout << char(((len>>0) & 0xFF)) << char(((len>>8) & 0xFF)) << char(((len>>16) & 0xFF)) << char(((len>>24) & 0xFF)); std::cout << reply.toStdString() << std::flush; } diff --git a/src/browser/NativeMessagingHost.cpp b/src/browser/NativeMessagingHost.cpp index f6d2bd9e7..4dfa87d51 100755 --- a/src/browser/NativeMessagingHost.cpp +++ b/src/browser/NativeMessagingHost.cpp @@ -72,6 +72,11 @@ void NativeMessagingHost::run() if (BrowserSettings::supportBrowserProxy()) { QString serverPath = getLocalServerPath(); QFile::remove(serverPath); + + if (m_localServer->isListening()) { + m_localServer->close(); + } + m_localServer->listen(serverPath); connect(m_localServer.data(), SIGNAL(newConnection()), this, SLOT(newLocalConnection())); } else { @@ -120,8 +125,10 @@ void NativeMessagingHost::readStdIn(const quint32 length) void NativeMessagingHost::newLocalConnection() { QLocalSocket* socket = m_localServer->nextPendingConnection(); - connect(socket, SIGNAL(readyRead()), this, SLOT(newLocalMessage())); - connect(socket, SIGNAL(disconnected()), this, SLOT(disconnectSocket())); + if (socket) { + connect(socket, SIGNAL(readyRead()), this, SLOT(newLocalMessage())); + connect(socket, SIGNAL(disconnected()), this, SLOT(disconnectSocket())); + } } void NativeMessagingHost::newLocalMessage()