From b111590d2bcbd48108b690fbe5a671db1d8097ca Mon Sep 17 00:00:00 2001 From: Toni Spets Date: Sat, 2 Nov 2019 11:25:13 +0200 Subject: [PATCH] Implement SSH key file path env substitution Supports all platforms, including Windows with %FOO% syntax. Fixes #3523 --- src/core/Tools.cpp | 24 ++++++++++++++++++++++++ src/core/Tools.h | 2 ++ src/gui/entry/EditEntryWidget.cpp | 2 +- src/sshagent/KeeAgentSettings.cpp | 6 ++++++ src/sshagent/KeeAgentSettings.h | 1 + src/sshagent/SSHAgent.cpp | 2 +- tests/TestTools.cpp | 21 +++++++++++++++++++++ tests/TestTools.h | 1 + 8 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/core/Tools.cpp b/src/core/Tools.cpp index 9035a96a7e..9e949eedd3 100644 --- a/src/core/Tools.cpp +++ b/src/core/Tools.cpp @@ -293,6 +293,30 @@ namespace Tools return QUuid::fromRfc4122(QByteArray::fromHex(uuid.toLatin1())); } + QString envSubst(const QString& path, QProcessEnvironment environment) + { + QString fileName = path; + +#if defined(Q_OS_WIN) + QRegularExpression varRe("\\%([A-Za-z][A-Za-z0-9_]*)\\%"); +#else + QRegularExpression varRe("\\$([A-Za-z][A-Za-z0-9_]*)"); + fileName.replace("~", environment.value("HOME")); +#endif + + QRegularExpressionMatch match; + + do { + match = varRe.match(fileName); + if (match.hasMatch()) { + fileName = fileName.left(match.capturedStart()) + environment.value(match.captured(1)) + + fileName.mid(match.capturedStart() + match.capturedLength()); + } + } while (match.hasMatch()); + + return fileName; + } + Buffer::Buffer() : raw(nullptr) , size(0) diff --git a/src/core/Tools.h b/src/core/Tools.h index 1fa5e6a9ad..30674d29b7 100644 --- a/src/core/Tools.h +++ b/src/core/Tools.h @@ -22,6 +22,7 @@ #include "core/Global.h" #include +#include #include #include @@ -47,6 +48,7 @@ namespace Tools bool useWildcards = false, bool exactMatch = false, bool caseSensitive = false); + QString envSubst(const QString& path, QProcessEnvironment environment = QProcessEnvironment::systemEnvironment()); template RandomAccessIterator binaryFind(RandomAccessIterator begin, RandomAccessIterator end, const T& value) diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 3fa75e3b8e..c862efc8f8 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -682,7 +682,7 @@ bool EditEntryWidget::getOpenSSHKey(OpenSSHKey& key, bool decrypt) fileName = m_sshAgentUi->attachmentComboBox->currentText(); privateKeyData = m_advancedUi->attachmentsWidget->getAttachment(fileName); } else { - QFile localFile(m_sshAgentUi->externalFileEdit->text()); + QFile localFile(Tools::envSubst(m_sshAgentUi->externalFileEdit->text())); QFileInfo localFileInfo(localFile); fileName = localFileInfo.fileName(); diff --git a/src/sshagent/KeeAgentSettings.cpp b/src/sshagent/KeeAgentSettings.cpp index 8c22780058..721e081496 100644 --- a/src/sshagent/KeeAgentSettings.cpp +++ b/src/sshagent/KeeAgentSettings.cpp @@ -17,6 +17,7 @@ */ #include "KeeAgentSettings.h" +#include "core/Tools.h" KeeAgentSettings::KeeAgentSettings() : m_allowUseOfSshKey(false) @@ -108,6 +109,11 @@ const QString KeeAgentSettings::fileName() const return m_fileName; } +const QString KeeAgentSettings::fileNameEnvSubst(QProcessEnvironment environment) const +{ + return Tools::envSubst(m_fileName, environment); +} + void KeeAgentSettings::setAllowUseOfSshKey(bool allowUseOfSshKey) { m_allowUseOfSshKey = allowUseOfSshKey; diff --git a/src/sshagent/KeeAgentSettings.h b/src/sshagent/KeeAgentSettings.h index 484dee88d2..ca403cae60 100644 --- a/src/sshagent/KeeAgentSettings.h +++ b/src/sshagent/KeeAgentSettings.h @@ -45,6 +45,7 @@ class KeeAgentSettings const QString attachmentName() const; bool saveAttachmentToTempFile() const; const QString fileName() const; + const QString fileNameEnvSubst(QProcessEnvironment environment = QProcessEnvironment::systemEnvironment()) const; void setAllowUseOfSshKey(bool allowUseOfSshKey); void setAddAtDatabaseOpen(bool addAtDatabaseOpen); diff --git a/src/sshagent/SSHAgent.cpp b/src/sshagent/SSHAgent.cpp index ac25a7066c..3fc8db3435 100644 --- a/src/sshagent/SSHAgent.cpp +++ b/src/sshagent/SSHAgent.cpp @@ -331,7 +331,7 @@ void SSHAgent::databaseModeChanged() fileName = settings.attachmentName(); keyData = e->attachments()->value(fileName); } else if (!settings.fileName().isEmpty()) { - QFile file(settings.fileName()); + QFile file(settings.fileNameEnvSubst()); QFileInfo fileInfo(file); fileName = fileInfo.fileName(); diff --git a/tests/TestTools.cpp b/tests/TestTools.cpp index 100eb63064..473e7658fd 100644 --- a/tests/TestTools.cpp +++ b/tests/TestTools.cpp @@ -64,3 +64,24 @@ void TestTools::testIsBase64() QVERIFY(not Tools::isBase64(QByteArray("abc_"))); QVERIFY(not Tools::isBase64(QByteArray("123"))); } + +void TestTools::testEnvSubst() +{ + QProcessEnvironment environment; + +#if defined(Q_OS_WIN) + environment.insert("HOMEDRIVE", "C:"); + environment.insert("HOMEPATH", "\\Users\\User"); + + QCOMPARE(Tools::envSubst("%HOMEDRIVE%%HOMEPATH%\\.ssh\\id_rsa", environment), + QString("C:\\Users\\User\\.ssh\\id_rsa")); + QCOMPARE(Tools::envSubst("start%EMPTY%%EMPTY%%%HOMEDRIVE%%end", environment), QString("start%C:%end")); +#else + environment.insert("HOME", QString("/home/user")); + environment.insert("USER", QString("user")); + + QCOMPARE(Tools::envSubst("~/.ssh/id_rsa", environment), QString("/home/user/.ssh/id_rsa")); + QCOMPARE(Tools::envSubst("$HOME/.ssh/id_rsa", environment), QString("/home/user/.ssh/id_rsa")); + QCOMPARE(Tools::envSubst("start/$EMPTY$$EMPTY$HOME/end", environment), QString("start/$/home/user/end")); +#endif +} diff --git a/tests/TestTools.h b/tests/TestTools.h index 56d354eca0..0d456620f6 100644 --- a/tests/TestTools.h +++ b/tests/TestTools.h @@ -27,6 +27,7 @@ private slots: void testHumanReadableFileSize(); void testIsHex(); void testIsBase64(); + void testEnvSubst(); }; #endif // KEEPASSX_TESTTOOLS_H