commit c47b28af98cf0e370d42100ae736f71802f0cf50
parent dd3e65b8ef4a24517f1c7f62132fab50154f660f
Author: Willy Goiffon <contact@z3bra.org>
Date: Tue, 13 Jul 2021 15:52:34 +0200
Fetch and Create safe(1) secrets using safe-agent
Diffstat:
6 files changed, 137 insertions(+), 83 deletions(-)
diff --git a/qml/components/PasswordDelegate.qml b/qml/components/PasswordDelegate.qml
@@ -109,20 +109,16 @@ ListItem {
listItem.folderSelected()
} else {
modelData.password.requestPassword()
- var dialog = pageStack.push(Qt.resolvedUrl("../pages/PassphraseRequester.qml"),
- { "requester": modelData.password })
- dialog.done.connect(function() {
- listItem.password = modelData.password
- listItem.password.validChanged.connect(function() {
- if (listItem.password.valid) {
- remorse.execute(listItem, qsTr("Password will expire"),
- function() {
- if (listItem.password) {
- listItem.password.expirePassword();
- }
- }, listItem.password.defaultTimeout);
- }
- });
+ listItem.password = modelData.password
+ listItem.password.validChanged.connect(function() {
+ if (listItem.password.valid) {
+ remorse.execute(listItem, qsTr("Password will expire"),
+ function() {
+ if (listItem.password) {
+ listItem.password.expirePassword();
+ }
+ }, listItem.password.defaultTimeout);
+ }
});
}
}
diff --git a/src/passwordprovider.cpp b/src/passwordprovider.cpp
@@ -93,6 +93,21 @@ void PasswordProvider::requestPassword()
Q_EMIT validChanged();
Q_EMIT passwordChanged();
+ auto *job = Safe::decrypt(mPath);
+ connect(job, &Safe::DecryptTask::finished,
+ this, [this, job]() {
+ if (job->error()) {
+ qWarning() << "Failed to unlock safe: " << job->errorString();
+ setError(job->errorString());
+ return;
+ }
+ // .split() always return one argument, so it's used here to remove
+ // trailing \n if any, or keep the whole content otherwise
+ const QStringList lines = job->content().split(QLatin1Char('\n'));
+ setPassword(lines[0]);
+ qDebug() << "requestPassword() got: " << lines[0];
+ });
+
mTimer.setInterval(PasswordTimeoutUpdateInterval);
connect(&mTimer, &QTimer::timeout,
this, [this]() {
@@ -102,8 +117,6 @@ void PasswordProvider::requestPassword()
expirePassword();
}
});
-
-
}
int PasswordProvider::timeout() const
@@ -137,34 +150,6 @@ void PasswordProvider::cancel()
setError(tr("Cancelled by user."));
}
-void PasswordProvider::setPassphrase(const QString &passphrase)
-{
- const QString root = qEnvironmentVariableIsSet(SAFE_DIR)
- ? QString::fromUtf8(qgetenv(SAFE_DIR))
- : QStringLiteral("%1/.safe.d").arg(QDir::homePath());
-
- const QString socket = qEnvironmentVariableIsSet(SAFE_SOCK)
- ? QString::fromUtf8(qgetenv(SAFE_SOCK))
- : QStringLiteral("%1/.safe.sock").arg(QDir::homePath());
-
- QFile safeAgentSocket(socket);
- if (!safeAgentSocket.exists()) {
- qWarning() << "Missing socket file (" << safeAgentSocket.fileName() << ")";
- return;
- }
- /*
- auto *job = Safe::unlock(passphrase);
- connect(job, &Safe::UnlockTask::finished,
- this, [this, job]() {
- if (job->error()) {
- qWarning() << "Failed to unlock safe: " << job->errorString();
- setError(job->errorString());
- return;
- }
- });
- */
-}
-
void PasswordProvider::removePasswordFromClipboard(const QString &password)
{
// Clear the WS clipboard itself
diff --git a/src/passwordprovider.h b/src/passwordprovider.h
@@ -50,7 +50,6 @@ public:
public Q_SLOTS:
void requestPassword();
void cancel();
- void setPassphrase(const QString &passphrase);
void expirePassword();
Q_SIGNALS:
diff --git a/src/passwordsmodel.cpp b/src/passwordsmodel.cpp
@@ -186,7 +186,7 @@ QVariant PasswordsModel::data(const QModelIndex &index, int role) const
return node->fullName();
case PasswordRole:
if (!node->provider) {
- node->provider = new PasswordProvider(node->path());
+ node->provider = new PasswordProvider(node->fullName());
}
return QVariant::fromValue(node->provider.data());
case HasPasswordRole:
@@ -226,27 +226,20 @@ void PasswordsModel::populateDir(const QDir& dir, Node *parent)
void PasswordsModel::addPassword(const QModelIndex &parent, const QString &name,
const QString &password, const QString &extras)
{
- auto node = this->node(parent);
- if (!node) {
- node = mRoot;
- }
-
- // Escape forward slash to avoid the name "escaping" the current folder
- QString safeName = name;
- safeName.replace(QLatin1Char('/'), QLatin1Char(' '));
QString data = password;
+ QString secret = name;
if (!extras.isEmpty()) {
data += QStringLiteral("\n%1").arg(extras);
}
- auto *task = Safe::encrypt(QStringLiteral("%1/%2").arg(node->path(), safeName), data);
+ auto *task = Safe::encrypt(secret, data);
connect(task, &Safe::EncryptTask::finished,
- this, [safeName, task]() {
+ this, [secret, task]() {
if (task->error()) {
qWarning() << "Error:" << task->errorString();
return;
}
- qDebug() << "Successfully encrypted password for" << safeName;
+ qDebug() << "Successfully stored secret " << secret;
});
}
diff --git a/src/safe.cpp b/src/safe.cpp
@@ -9,21 +9,23 @@
#include <QtConcurrent>
#include <QFutureWatcher>
-namespace {
+#define SAFE_DIR "SAFE_DIR"
+#define SAFE_PID "SAFE_PID"
+#define SAFE_SOCK "SAFE_SOCK"
+#define SAFE_ASKPASS "SAFE_ASKPASS"
-struct SafeExecutable {
- SafeExecutable(const QString &path)
- : path(path)
- {}
- QString path = {};
-};
+namespace {
+} // namespace
-SafeExecutable findSafeExecutable()
+Safe::LockTask *Safe::lock()
{
- return QStandardPaths::findExecutable(QStringLiteral("safe"));
+ return new LockTask();
}
-} // namespace
+Safe::UnlockTask *Safe::unlock(const QString &passphrase)
+{
+ return new UnlockTask(passphrase);
+}
Safe::EncryptTask *Safe::encrypt(const QString &file, const QString &content)
{
@@ -68,15 +70,74 @@ void Safe::Task::start()
watcher->setFuture(future);
}
+Safe::LockTask::LockTask()
+{}
+
+void Safe::LockTask::run()
+{
+ if (qEnvironmentVariableIsSet(SAFE_PID))
+ return;
+
+ const auto kill = QStandardPaths::findExecutable(QStringLiteral("kill"));
+ const auto agent = QString::fromUtf8(qgetenv(SAFE_PID));
+
+ QProcess process;
+ process.setProgram(kill);
+ process.setArguments({
+ QStringLiteral("-USR1"),
+ QStringLiteral("%1").arg(agent)
+ });
+ process.start();
+ process.waitForStarted();
+ process.waitForFinished();
+ if (process.exitCode() != 0) {
+ const auto err = process.readAllStandardError();
+ qWarning() << "Failed to forget key:" << err;
+ setError(QString::fromUtf8(err));
+ }
+}
+
+Safe::UnlockTask::UnlockTask(const QString &passphrase)
+ : mPassphrase(passphrase)
+{}
+
+void Safe::UnlockTask::run()
+{
+ const QString safe = QStandardPaths::findExecutable(QStringLiteral("safe"));
+
+ if (!qEnvironmentVariableIsSet(SAFE_PID)) {
+ qWarning() << "Agent is not running";
+ return;
+ }
+
+ if (!qEnvironmentVariableIsSet(SAFE_SOCK)) {
+ qWarning() << "Agent is not found";
+ return;
+ }
+
+ QProcess process;
+ process.setProgram(safe);
+ process.setArguments({QStringLiteral("-r")});
+ process.start();
+ process.waitForStarted();
+ process.write(mPassphrase.toUtf8());
+ process.waitForFinished();
+ if (process.exitCode() != 0) {
+ const auto err = process.readAllStandardError();
+ qWarning() << "Failed to unlock safe:" << err;
+ setError(QString::fromUtf8(err));
+ }
+}
+
Safe::EncryptTask::EncryptTask(const QString &file, const QString &content)
: mFile(file), mContent(content)
{}
void Safe::EncryptTask::run()
{
- const auto safe = findSafeExecutable();
+ const QString safe = QStandardPaths::findExecutable(QStringLiteral("safe"));
QProcess process;
- process.setProgram(safe.path);
+ process.setProgram(safe);
process.setArguments({
QStringLiteral("-a"),
QStringLiteral("%1").arg(mFile)
@@ -104,9 +165,9 @@ QString Safe::DecryptTask::content() const
void Safe::DecryptTask::run()
{
- const auto safe = findSafeExecutable();
+ const QString safe = QStandardPaths::findExecutable(QStringLiteral("safe"));
QProcess process;
- process.setProgram(safe.path);
+ process.setProgram(safe);
process.setArguments({QStringLiteral("%1").arg(mFile)});
process.start();
process.waitForStarted();
diff --git a/src/safe.h b/src/safe.h
@@ -6,21 +6,13 @@
namespace Safe
{
+class LockTask;
+class UnlockTask;
class DecryptTask;
class EncryptTask;
-struct Key {
- enum class Trust {
- Unknown = 1,
- Never = 2,
- Marginal = 3,
- Full = 4,
- Ultimate = 5
- };
-
- QString id;
-};
-
+LockTask *lock();
+UnlockTask *unlock(const QString &passphrase);
DecryptTask *decrypt(const QString &file);
EncryptTask *encrypt(const QString &data, const QString &file);
@@ -48,6 +40,34 @@ private:
QString mError;
};
+class LockTask : public Task {
+ Q_OBJECT
+ friend LockTask *Safe::lock();
+public:
+ QString content() const;
+
+protected:
+ void run() override;
+
+private:
+ LockTask();
+};
+
+class UnlockTask : public Task {
+ Q_OBJECT
+ friend UnlockTask *Safe::unlock(const QString &);
+public:
+ QString content() const;
+
+protected:
+ void run() override;
+
+private:
+ UnlockTask(const QString &passphrase);
+
+ QString mPassphrase;
+};
+
class DecryptTask : public Task {
Q_OBJECT
friend DecryptTask *Safe::decrypt(const QString &);