tsafe.cpp - sailfish-safe - Sailfish frontend for safe(1)
(HTM) git clone git://git.z3bra.org/sailfish-safe.git
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
tsafe.cpp (5685B)
---
1 /*
2 * Copyright (C) 2021 Willy Goiffon <contact@z3bra.org>
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "safe.h"
19
20 #include <QStandardPaths>
21 #include <QProcess>
22 #include <QIODevice>
23 #include <QRegularExpression>
24 #include <QRegularExpressionMatch>
25 #include <QTimer>
26 #include <QtConcurrent>
27 #include <QFutureWatcher>
28
29 #define SAFE_DIR "SAFE_DIR"
30 #define SAFE_PID "SAFE_PID"
31 #define SAFE_SOCK "SAFE_SOCK"
32 #define SAFE_ASKPASS "SAFE_ASKPASS"
33
34 namespace {
35 } // namespace
36
37 Safe::LockTask *Safe::lock()
38 {
39 return new LockTask();
40 }
41
42 Safe::UnlockTask *Safe::unlock(const QString &passphrase)
43 {
44 return new UnlockTask(passphrase);
45 }
46
47 Safe::EncryptTask *Safe::encrypt(const QString &file, const QString &content)
48 {
49 return new EncryptTask(file, content);
50 }
51
52 Safe::DecryptTask *Safe::decrypt(const QString &file)
53 {
54 return new DecryptTask(file);
55 }
56
57
58 Safe::Task::Task(QObject *parent)
59 : QObject(parent)
60 {
61 QTimer::singleShot(0, this, &Task::start);
62 }
63
64 bool Safe::Task::error() const
65 {
66 return !mError.isNull();
67 }
68
69 QString Safe::Task::errorString() const
70 {
71 return mError;
72 }
73
74 void Safe::Task::setError(const QString &error)
75 {
76 mError = error;
77 }
78
79 void Safe::Task::start()
80 {
81 qDebug() << "Starting task" << this;
82 auto future = QtConcurrent::run(this, &Task::run);
83 auto *watcher = new QFutureWatcher<void>;
84 connect(watcher, &QFutureWatcher<void>::finished, watcher, &QObject::deleteLater);
85 connect(watcher, &QFutureWatcher<void>::finished, this, &Safe::Task::finished);
86 connect(watcher, &QFutureWatcher<void>::finished, this, &QObject::deleteLater);
87 watcher->setFuture(future);
88 }
89
90 Safe::LockTask::LockTask()
91 {}
92
93 void Safe::LockTask::run()
94 {
95 if (qEnvironmentVariableIsSet(SAFE_PID))
96 return;
97
98 const auto kill = QStandardPaths::findExecutable(QStringLiteral("kill"));
99 const auto agent = QString::fromUtf8(qgetenv(SAFE_PID));
100
101 QProcess process;
102 process.setProgram(kill);
103 process.setArguments({
104 QStringLiteral("-USR1"),
105 QStringLiteral("%1").arg(agent)
106 });
107 process.start();
108 process.waitForStarted();
109 process.waitForFinished();
110 if (process.exitCode() != 0) {
111 const auto err = process.readAllStandardError();
112 qWarning() << "Failed to forget key:" << err;
113 setError(QString::fromUtf8(err));
114 }
115 }
116
117 Safe::UnlockTask::UnlockTask(const QString &passphrase)
118 : mPassphrase(passphrase)
119 {}
120
121 void Safe::UnlockTask::run()
122 {
123 const QString safe = QStandardPaths::findExecutable(QStringLiteral("safe"));
124
125 if (!qEnvironmentVariableIsSet(SAFE_PID)) {
126 qWarning() << "Agent is not running";
127 return;
128 }
129
130 if (!qEnvironmentVariableIsSet(SAFE_SOCK)) {
131 qWarning() << "Agent is not reachable";
132 return;
133 }
134
135 // Temporary hack until we can properly pass the passphrase to safe(1)
136 // Expect the "askpass" command to be available and reading from stdin
137 // ex. `read pass; echo $pass`
138 qputenv(SAFE_ASKPASS, "/usr/local/bin/askpass");
139
140 QProcess process;
141 process.setProgram(safe);
142 process.setArguments({
143 QStringLiteral("-r"),
144 QStringLiteral("-k")
145 });
146 process.start();
147 process.waitForStarted();
148 QThread::currentThread()->sleep(1);
149 process.write(mPassphrase.toUtf8());
150 process.write("\n");
151 process.waitForFinished();
152 if (process.exitCode() != 0) {
153 const auto err = process.readAllStandardError();
154 qWarning() << "Failed to unlock safe:" << err;
155 setError(QString::fromUtf8(err));
156 }
157 }
158
159 Safe::EncryptTask::EncryptTask(const QString &file, const QString &content)
160 : mFile(file), mContent(content)
161 {}
162
163 void Safe::EncryptTask::run()
164 {
165 const QString safe = QStandardPaths::findExecutable(QStringLiteral("safe"));
166 QProcess process;
167 process.setProgram(safe);
168 process.setArguments({
169 QStringLiteral("-a"),
170 QStringLiteral("%1").arg(mFile)
171 });
172 process.start();
173 process.waitForStarted();
174 process.write(mContent.toUtf8());
175 process.closeWriteChannel();
176 process.waitForFinished();
177 if (process.exitCode() != 0) {
178 const auto err = process.readAllStandardError();
179 qWarning() << "Failed to encrypt data:" << err;
180 setError(QString::fromUtf8(err));
181 }
182 }
183
184 Safe::DecryptTask::DecryptTask(const QString &file)
185 : Task(), mFile(file)
186 {}
187
188 QString Safe::DecryptTask::content() const
189 {
190 return mContent;
191 }
192
193 void Safe::DecryptTask::run()
194 {
195 const QString safe = QStandardPaths::findExecutable(QStringLiteral("safe"));
196 QProcess process;
197 process.setProgram(safe);
198 process.setArguments({QStringLiteral("%1").arg(mFile)});
199 process.start();
200 process.waitForStarted();
201 process.closeWriteChannel();
202 process.waitForFinished();
203 if (process.exitCode() != 0) {
204 const auto err = process.readAllStandardError();
205 qWarning() << "Failed to decrypt data:" << err;
206 setError(QString::fromUtf8(err));
207 } else {
208 mContent = QString::fromUtf8(process.readAllStandardOutput());
209 }
210 }