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 }