From 53263cde0c471d9cb60484fe5eec72378f69a3e2 Mon Sep 17 00:00:00 2001 From: Alexandre Petit <62973006+AlexpFr@users.noreply.github.com> Date: Wed, 1 May 2024 16:14:43 +0200 Subject: [PATCH] SSH Agent: Add ssh-add -D function (#8346) --- share/translations/keepassxc_en.ts | 20 +++++++++++ src/gui/DatabaseWidget.cpp | 10 ++++++ src/gui/DatabaseWidget.h | 1 + src/gui/MainWindow.cpp | 16 +++++++++ src/gui/MainWindow.ui | 12 +++++++ src/gui/entry/EditEntryWidget.cpp | 12 +++++++ src/gui/entry/EditEntryWidget.h | 1 + src/gui/entry/EditEntryWidgetSSHAgent.ui | 11 +++++-- src/sshagent/SSHAgent.cpp | 42 ++++++++++++++++++++++++ src/sshagent/SSHAgent.h | 3 ++ 10 files changed, 126 insertions(+), 2 deletions(-) diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index 2b55841e12..5b06b0aa81 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -3106,6 +3106,10 @@ Would you like to correct it? Certificate + + Clear agent + + EditGroupWidget @@ -5891,6 +5895,14 @@ We recommend you use the AppImage available on our downloads page. Toggle Allow Screen Capture + + Clear SSH Agent + + + + Clear all identities like ssh-add -D + + ManageDatabase @@ -9297,6 +9309,14 @@ This option is deprecated, use --set-key-file instead. No agent running, cannot list identities. + + Failed to remove all SSH identities from agent. + + + + All SSH identities removed from agent. + + Agent refused this identity certificate. Possible reasons include: diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 5e7f0c4b0a..2a72ea3ac8 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -810,6 +810,16 @@ void DatabaseWidget::removeFromAgent() m_messageWidget->showMessage(settings.errorString(), MessageWidget::Error); } } + +void DatabaseWidget::clearSSHAgent() +{ + SSHAgent* agent = SSHAgent::instance(); + if (!agent->clearAllAgentIdentities()) { + showMessage(agent->errorString(), MessageWidget::Error); + } else { + showMessage(agent->errorString(), MessageWidget::Positive); + } +} #endif void DatabaseWidget::performAutoType(const QString& sequence) diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 6812600092..6fbdf24107 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -186,6 +186,7 @@ public slots: #ifdef WITH_XC_SSHAGENT void addToAgent(); void removeFromAgent(); + void clearSSHAgent(); #endif void performAutoType(const QString& sequence = {}); void performAutoTypeUsername(); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index c89e08f0b9..dc52863d37 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -206,6 +206,13 @@ MainWindow::MainWindow() connect(sshAgent(), SIGNAL(error(QString)), this, SLOT(showErrorMessage(QString))); connect(sshAgent(), SIGNAL(enabledChanged(bool)), this, SLOT(agentEnabled(bool))); m_ui->settingsWidget->addSettingsPage(new AgentSettingsPage()); + if (!sshAgent()->isEnabled()) { + m_ui->actionClearSSHAgent->setEnabled(false); + m_ui->actionClearSSHAgent->setVisible(false); + } +#else + m_ui->actionClearSSHAgent->setVisible(false); + m_ui->actionClearSSHAgent->setEnabled(false); #endif #if defined(WITH_XC_KEESHARE) @@ -397,6 +404,9 @@ MainWindow::MainWindow() m_ui->actionGroupDownloadFavicons->setIcon(icons()->icon("favicon-download")); m_ui->actionSettings->setIcon(icons()->icon("configure")); +#ifdef WITH_XC_SSHAGENT + m_ui->actionClearSSHAgent->setIcon(icons()->icon("utilities-terminal")); +#endif m_ui->actionPasswordGenerator->setIcon(icons()->icon("password-generator")); m_ui->actionAbout->setIcon(icons()->icon("help-about")); @@ -509,6 +519,7 @@ MainWindow::MainWindow() #ifdef WITH_XC_SSHAGENT m_actionMultiplexer.connect(m_ui->actionEntryAddToAgent, SIGNAL(triggered()), SLOT(addToAgent())); m_actionMultiplexer.connect(m_ui->actionEntryRemoveFromAgent, SIGNAL(triggered()), SLOT(removeFromAgent())); + m_actionMultiplexer.connect(m_ui->actionClearSSHAgent, SIGNAL(triggered()), SLOT(clearSSHAgent())); #endif m_actionMultiplexer.connect(m_ui->actionGroupNew, SIGNAL(triggered()), SLOT(createGroup())); @@ -1601,6 +1612,8 @@ void MainWindow::agentEnabled(bool enabled) { m_ui->actionEntryAddToAgent->setVisible(enabled); m_ui->actionEntryRemoveFromAgent->setVisible(enabled); + m_ui->actionClearSSHAgent->setEnabled(enabled); + m_ui->actionClearSSHAgent->setVisible(enabled); } void MainWindow::showEntryContextMenu(const QPoint& globalPos) @@ -2110,6 +2123,9 @@ void MainWindow::initActionCollection() m_ui->actionGroupEmptyRecycleBin, // Tools Menu m_ui->actionPasswordGenerator, +#ifdef WITH_XC_SSHAGENT + m_ui->actionClearSSHAgent, +#endif m_ui->actionSettings, // View Menu m_ui->actionThemeAuto, diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index 69606b856e..506912f419 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -364,6 +364,7 @@ &Tools + @@ -1291,6 +1292,17 @@ Import… + + + Clear SSH Agent + + + Clear all identities like ssh-add -D + + + QAction::TextHeuristicRole + + diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 7aee16b832..81b1238647 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -572,6 +572,7 @@ void EditEntryWidget::setupSSHAgent() connect(m_sshAgentUi->browseButton, &QPushButton::clicked, this, &EditEntryWidget::browsePrivateKey); connect(m_sshAgentUi->addToAgentButton, &QPushButton::clicked, this, &EditEntryWidget::addKeyToAgent); connect(m_sshAgentUi->removeFromAgentButton, &QPushButton::clicked, this, &EditEntryWidget::removeKeyFromAgent); + connect(m_sshAgentUi->clearAgentButton, &QPushButton::clicked, this, &EditEntryWidget::clearAgent); connect(m_sshAgentUi->decryptButton, &QPushButton::clicked, this, &EditEntryWidget::decryptPrivateKey); connect(m_sshAgentUi->copyToClipboardButton, &QPushButton::clicked, this, &EditEntryWidget::copyPublicKey); connect(m_sshAgentUi->generateButton, &QPushButton::clicked, this, &EditEntryWidget::generatePrivateKey); @@ -740,6 +741,7 @@ void EditEntryWidget::updateSSHAgentKeyInfo() if (sshAgent()->isAgentRunning()) { m_sshAgentUi->addToAgentButton->setEnabled(true); m_sshAgentUi->removeFromAgentButton->setEnabled(true); + m_sshAgentUi->clearAgentButton->setEnabled(true); sshAgent()->setAutoRemoveOnLock(key, m_sshAgentUi->removeKeyFromAgentCheckBox->isChecked()); } @@ -872,6 +874,16 @@ void EditEntryWidget::removeKeyFromAgent() } } +void EditEntryWidget::clearAgent() +{ + if (!sshAgent()->clearAllAgentIdentities()) { + showMessage(sshAgent()->errorString(), MessageWidget::Error); + return; + } + + showMessage(sshAgent()->errorString(), MessageWidget::Positive); +} + void EditEntryWidget::decryptPrivateKey() { OpenSSHKey key; diff --git a/src/gui/entry/EditEntryWidget.h b/src/gui/entry/EditEntryWidget.h index e44bb74305..a1ee1e7032 100644 --- a/src/gui/entry/EditEntryWidget.h +++ b/src/gui/entry/EditEntryWidget.h @@ -122,6 +122,7 @@ private slots: void browsePrivateKey(); void addKeyToAgent(); void removeKeyFromAgent(); + void clearAgent(); void decryptPrivateKey(); void copyPublicKey(); void generatePrivateKey(); diff --git a/src/gui/entry/EditEntryWidgetSSHAgent.ui b/src/gui/entry/EditEntryWidgetSSHAgent.ui index 7253e73e8f..286ec1e5df 100644 --- a/src/gui/entry/EditEntryWidgetSSHAgent.ui +++ b/src/gui/entry/EditEntryWidgetSSHAgent.ui @@ -27,7 +27,7 @@ 0 - + @@ -42,6 +42,13 @@ + + + + Clear agent + + + @@ -221,7 +228,7 @@ 0 - + Private key diff --git a/src/sshagent/SSHAgent.cpp b/src/sshagent/SSHAgent.cpp index 3469117bcc..a436aa6478 100644 --- a/src/sshagent/SSHAgent.cpp +++ b/src/sshagent/SSHAgent.cpp @@ -432,6 +432,48 @@ bool SSHAgent::removeIdentity(OpenSSHKey& key) sendMessage(requestCertificateData, responseCertificateData)); } +/** + * Remove all identities from the SSH agent. + * + * Since the agent might be forwarded, old or non-OpenSSH, when asked + * to remove all keys, attempt to remove both protocol v.1 and v.2 + * keys. + * + * @return true on success + */ +bool SSHAgent::clearAllAgentIdentities() +{ + if (!isAgentRunning()) { + m_error = tr("No agent running, cannot remove identity."); + return false; + } + + bool ret = true; + QByteArray requestData; + QByteArray responseData; + BinaryStream request(&requestData); + + // Same request order as OpenBSD ssh-add: useful? + request.write(SSH2_AGENTC_REMOVE_ALL_IDENTITIES); + + if (!sendMessage(requestData, responseData)) { + m_error = tr("Failed to remove all SSH identities from agent."); + ret = false; + } + + request.flush(); + responseData.clear(); + + // Same request order as OpenBSD ssh-add: useful? + request.write(SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES); + + // ignore error-code for ssh1 + sendMessage(requestData, responseData); + + m_error = tr("All SSH identities removed from agent."); + return ret; +} + /** * Get a list of identities from the SSH agent. * diff --git a/src/sshagent/SSHAgent.h b/src/sshagent/SSHAgent.h index 8ef6173a64..d3eeb4ebc9 100644 --- a/src/sshagent/SSHAgent.h +++ b/src/sshagent/SSHAgent.h @@ -56,6 +56,7 @@ class SSHAgent : public QObject bool checkIdentity(const OpenSSHKey& key, bool& loaded); bool removeIdentity(OpenSSHKey& key); void removeAllIdentities(); + bool clearAllAgentIdentities(); void setAutoRemoveOnLock(const OpenSSHKey& key, bool autoRemove); signals: @@ -74,6 +75,8 @@ public slots: const quint8 SSH_AGENTC_ADD_IDENTITY = 17; const quint8 SSH_AGENTC_REMOVE_IDENTITY = 18; const quint8 SSH_AGENTC_ADD_ID_CONSTRAINED = 25; + const quint8 SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES = 9; + const quint8 SSH2_AGENTC_REMOVE_ALL_IDENTITIES = 19; const quint8 SSH_AGENT_CONSTRAIN_LIFETIME = 1; const quint8 SSH_AGENT_CONSTRAIN_CONFIRM = 2;