Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/sshagent/SSHAgent.cpp')
-rw-r--r--src/sshagent/SSHAgent.cpp310
1 files changed, 212 insertions, 98 deletions
diff --git a/src/sshagent/SSHAgent.cpp b/src/sshagent/SSHAgent.cpp
index ac25a7066..20284c685 100644
--- a/src/sshagent/SSHAgent.cpp
+++ b/src/sshagent/SSHAgent.cpp
@@ -29,43 +29,75 @@
#include <windows.h>
#endif
-SSHAgent* SSHAgent::m_instance;
+Q_GLOBAL_STATIC(SSHAgent, s_sshAgent);
-SSHAgent::SSHAgent(QObject* parent)
- : QObject(parent)
+SSHAgent::~SSHAgent()
{
-#ifndef Q_OS_WIN
- m_socketPath = QProcessEnvironment::systemEnvironment().value("SSH_AUTH_SOCK");
-#else
- m_socketPath = "\\\\.\\pipe\\openssh-ssh-agent";
-#endif
+ removeAllIdentities();
}
-SSHAgent::~SSHAgent()
+SSHAgent* SSHAgent::instance()
{
- auto it = m_addedKeys.begin();
- while (it != m_addedKeys.end()) {
- // Remove key if requested to remove on lock
- if (it.value()) {
- OpenSSHKey key = it.key();
- removeIdentity(key);
- }
- it = m_addedKeys.erase(it);
- }
+ return s_sshAgent;
}
-SSHAgent* SSHAgent::instance()
+bool SSHAgent::isEnabled() const
{
- if (!m_instance) {
- qFatal("Race condition: instance wanted before it was initialized, this is a bug.");
+ return config()->get(Config::SSHAgent_Enabled).toBool();
+}
+
+void SSHAgent::setEnabled(bool enabled)
+{
+ if (isEnabled() && !enabled) {
+ removeAllIdentities();
}
- return m_instance;
+ config()->set(Config::SSHAgent_Enabled, enabled);
+
+ emit enabledChanged(enabled);
}
-void SSHAgent::init(QObject* parent)
+QString SSHAgent::authSockOverride() const
{
- m_instance = new SSHAgent(parent);
+ return config()->get(Config::SSHAgent_AuthSockOverride).toString();
+}
+
+void SSHAgent::setAuthSockOverride(QString& authSockOverride)
+{
+ config()->set(Config::SSHAgent_AuthSockOverride, authSockOverride);
+}
+
+#ifdef Q_OS_WIN
+bool SSHAgent::useOpenSSH() const
+{
+ return config()->get(Config::SSHAgent_UseOpenSSH).toBool();
+}
+
+void SSHAgent::setUseOpenSSH(bool useOpenSSH)
+{
+ config()->set(Config::SSHAgent_UseOpenSSH, useOpenSSH);
+}
+#endif
+
+QString SSHAgent::socketPath(bool allowOverride) const
+{
+ QString socketPath;
+
+#ifndef Q_OS_WIN
+ if (allowOverride) {
+ socketPath = authSockOverride();
+ }
+
+ // if the overridden path is empty (no override set), default to environment
+ if (socketPath.isEmpty()) {
+ socketPath = QProcessEnvironment::systemEnvironment().value("SSH_AUTH_SOCK");
+ }
+#else
+ Q_UNUSED(allowOverride)
+ socketPath = "\\\\.\\pipe\\openssh-ssh-agent";
+#endif
+
+ return socketPath;
}
const QString SSHAgent::errorString() const
@@ -76,12 +108,13 @@ const QString SSHAgent::errorString() const
bool SSHAgent::isAgentRunning() const
{
#ifndef Q_OS_WIN
- return !m_socketPath.isEmpty();
+ QFileInfo socketFileInfo(socketPath());
+ return !socketFileInfo.path().isEmpty() && socketFileInfo.exists();
#else
- if (!config()->get("SSHAgentOpenSSH").toBool()) {
+ if (!useOpenSSH()) {
return (FindWindowA("Pageant", "Pageant") != nullptr);
} else {
- return WaitNamedPipe(m_socketPath.toLatin1().data(), 100);
+ return WaitNamedPipe(socketPath().toLatin1().data(), 100);
}
#endif
}
@@ -89,7 +122,7 @@ bool SSHAgent::isAgentRunning() const
bool SSHAgent::sendMessage(const QByteArray& in, QByteArray& out)
{
#ifdef Q_OS_WIN
- if (!config()->get("SSHAgentOpenSSH").toBool()) {
+ if (!useOpenSSH()) {
return sendMessagePageant(in, out);
}
#endif
@@ -97,7 +130,7 @@ bool SSHAgent::sendMessage(const QByteArray& in, QByteArray& out)
QLocalSocket socket;
BinaryStream stream(&socket);
- socket.connectToServer(m_socketPath);
+ socket.connectToServer(socketPath());
if (!socket.waitForConnected(500)) {
m_error = tr("Agent connection failed.");
return false;
@@ -185,18 +218,22 @@ bool SSHAgent::sendMessagePageant(const QByteArray& in, QByteArray& out)
* Add the identity to the SSH agent.
*
* @param key identity / key to add
- * @param lifetime time after which the key should expire
- * @param confirm ask for confirmation before adding the key
- * @param removeOnLock autoremove from agent when the Database is locked
+ * @param settings constraints (lifetime, confirm), remove-on-lock
+ * @param databaseUuid database that owns the key for remove-on-lock
* @return true on success
*/
-bool SSHAgent::addIdentity(OpenSSHKey& key, KeeAgentSettings& settings)
+bool SSHAgent::addIdentity(OpenSSHKey& key, const KeeAgentSettings& settings, const QUuid& databaseUuid)
{
if (!isAgentRunning()) {
m_error = tr("No agent running, cannot add identity.");
return false;
}
+ if (m_addedKeys.contains(key) && m_addedKeys[key].first != databaseUuid) {
+ m_error = tr("Key identity ownership conflict. Refusing to add.");
+ return false;
+ }
+
QByteArray requestData;
BinaryStream request(&requestData);
@@ -236,7 +273,7 @@ bool SSHAgent::addIdentity(OpenSSHKey& key, KeeAgentSettings& settings)
OpenSSHKey keyCopy = key;
keyCopy.clearPrivate();
- m_addedKeys[keyCopy] = settings.removeAtDatabaseClose();
+ m_addedKeys[keyCopy] = qMakePair(databaseUuid, settings.removeAtDatabaseClose());
return true;
}
@@ -268,6 +305,115 @@ bool SSHAgent::removeIdentity(OpenSSHKey& key)
}
/**
+ * Get a list of identities from the SSH agent.
+ *
+ * @param list list of keys to append
+ * @return true on success
+ */
+bool SSHAgent::listIdentities(QList<QSharedPointer<OpenSSHKey>>& list)
+{
+ if (!isAgentRunning()) {
+ m_error = tr("No agent running, cannot list identities.");
+ return false;
+ }
+
+ QByteArray requestData;
+ BinaryStream request(&requestData);
+
+ request.write(SSH_AGENTC_REQUEST_IDENTITIES);
+
+ QByteArray responseData;
+ if (!sendMessage(requestData, responseData)) {
+ return false;
+ }
+
+ BinaryStream response(&responseData);
+
+ quint8 responseType;
+ if (!response.read(responseType) || responseType != SSH_AGENT_IDENTITIES_ANSWER) {
+ m_error = tr("Agent protocol error.");
+ return false;
+ }
+
+ quint32 nKeys;
+ if (!response.read(nKeys)) {
+ m_error = tr("Agent protocol error.");
+ return false;
+ }
+
+ for (quint32 i = 0; i < nKeys; i++) {
+ QByteArray publicData;
+ QString comment;
+
+ if (!response.readString(publicData)) {
+ m_error = tr("Agent protocol error.");
+ return false;
+ }
+
+ if (!response.readString(comment)) {
+ m_error = tr("Agent protocol error.");
+ return false;
+ }
+
+ OpenSSHKey* key = new OpenSSHKey();
+ key->setComment(comment);
+
+ list.append(QSharedPointer<OpenSSHKey>(key));
+
+ BinaryStream publicDataStream(&publicData);
+ if (!key->readPublic(publicDataStream)) {
+ m_error = key->errorString();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Check if this identity is loaded in the SSH Agent.
+ *
+ * @param key identity to remove
+ * @param loaded is the key laoded
+ * @return true on success
+ */
+bool SSHAgent::checkIdentity(const OpenSSHKey& key, bool& loaded)
+{
+ QList<QSharedPointer<OpenSSHKey>> list;
+
+ if (!listIdentities(list)) {
+ return false;
+ }
+
+ loaded = false;
+
+ for (const auto& it : list) {
+ if (*it == key) {
+ loaded = true;
+ break;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Remove all identities known to this instance
+ */
+void SSHAgent::removeAllIdentities()
+{
+ auto it = m_addedKeys.begin();
+ while (it != m_addedKeys.end()) {
+ // Remove key if requested to remove on lock
+ if (it.value().second) {
+ OpenSSHKey key = it.key();
+ removeIdentity(key);
+ }
+ it = m_addedKeys.erase(it);
+ }
+}
+
+/**
* Change "remove identity on lock" setting for a key already added to the agent.
* Will to nothing if the key has not been added to the agent.
*
@@ -277,104 +423,72 @@ bool SSHAgent::removeIdentity(OpenSSHKey& key)
void SSHAgent::setAutoRemoveOnLock(const OpenSSHKey& key, bool autoRemove)
{
if (m_addedKeys.contains(key)) {
- m_addedKeys[key] = autoRemove;
+ m_addedKeys[key].second = autoRemove;
}
}
-void SSHAgent::databaseModeChanged()
+void SSHAgent::databaseLocked()
{
auto* widget = qobject_cast<DatabaseWidget*>(sender());
if (!widget) {
return;
}
- if (widget->isLocked()) {
- auto it = m_addedKeys.begin();
- while (it != m_addedKeys.end()) {
- OpenSSHKey key = it.key();
- if (it.value()) {
- if (!removeIdentity(key)) {
- emit error(m_error);
- }
- it = m_addedKeys.erase(it);
- } else {
- // don't remove it yet
- m_addedKeys[key] = false;
- ++it;
+ QUuid databaseUuid = widget->database()->uuid();
+
+ auto it = m_addedKeys.begin();
+ while (it != m_addedKeys.end()) {
+ if (it.value().first != databaseUuid) {
+ ++it;
+ continue;
+ }
+ OpenSSHKey key = it.key();
+ if (it.value().second) {
+ if (!removeIdentity(key)) {
+ emit error(m_error);
}
+ it = m_addedKeys.erase(it);
+ } else {
+ // don't remove it yet
+ m_addedKeys[key].second = false;
+ ++it;
}
+ }
+}
+void SSHAgent::databaseUnlocked()
+{
+ auto* widget = qobject_cast<DatabaseWidget*>(sender());
+ if (!widget) {
return;
}
for (Entry* e : widget->database()->rootGroup()->entriesRecursive()) {
-
if (widget->database()->metadata()->recycleBinEnabled()
&& e->group() == widget->database()->metadata()->recycleBin()) {
continue;
}
- if (!e->attachments()->hasKey("KeeAgent.settings")) {
- continue;
- }
-
KeeAgentSettings settings;
- settings.fromXml(e->attachments()->value("KeeAgent.settings"));
- if (!settings.allowUseOfSshKey()) {
+ if (!settings.fromEntry(e)) {
continue;
}
- QByteArray keyData;
- QString fileName;
- if (settings.selectedType() == "attachment") {
- fileName = settings.attachmentName();
- keyData = e->attachments()->value(fileName);
- } else if (!settings.fileName().isEmpty()) {
- QFile file(settings.fileName());
- QFileInfo fileInfo(file);
-
- fileName = fileInfo.fileName();
-
- if (file.size() > 1024 * 1024) {
- continue;
- }
-
- if (!file.open(QIODevice::ReadOnly)) {
- continue;
- }
-
- keyData = file.readAll();
- }
-
- if (keyData.isEmpty()) {
+ if (!settings.allowUseOfSshKey() || !settings.addAtDatabaseOpen()) {
continue;
}
OpenSSHKey key;
- if (!key.parsePKCS1PEM(keyData)) {
+ if (!settings.toOpenSSHKey(e, key, true)) {
continue;
}
- if (!key.openKey(e->password())) {
- continue;
- }
-
- if (key.comment().isEmpty()) {
- key.setComment(e->username());
- }
-
- if (key.comment().isEmpty()) {
- key.setComment(fileName);
- }
-
- if (settings.addAtDatabaseOpen()) {
- // Add key to agent; ignore errors if we have previously added the key
- bool known_key = m_addedKeys.contains(key);
- if (!addIdentity(key, settings) && !known_key) {
- emit error(m_error);
- }
+ // Add key to agent; ignore errors if we have previously added the key
+ bool known_key = m_addedKeys.contains(key);
+ if (!addIdentity(key, settings, widget->database()->uuid()) && !known_key) {
+ emit error(m_error);
}
}
}