diff options
Diffstat (limited to 'src/imitatepass.cpp')
-rw-r--r-- | src/imitatepass.cpp | 130 |
1 files changed, 115 insertions, 15 deletions
diff --git a/src/imitatepass.cpp b/src/imitatepass.cpp index 1cdb8575..8300cb36 100644 --- a/src/imitatepass.cpp +++ b/src/imitatepass.cpp @@ -1,5 +1,6 @@ #include "imitatepass.h" #include "qtpasssettings.h" +#include "util.h" #include <QDirIterator> #include <QRegularExpression> #include <utility> @@ -89,6 +90,12 @@ void ImitatePass::OtpGenerate(QString file) { */ void ImitatePass::Insert(QString file, QString newValue, bool overwrite) { file = file + ".gpg"; + QString gpgIdPath = Pass::getGpgIdPath(file); + if (!verifyGpgIdFile(gpgIdPath)) { + emit critical(tr("Check .gpgid file signature!"), + tr("Signature for %1 is invalid.").arg(gpgIdPath)); + return; + } transactionHelper trans(this, PASS_INSERT); QStringList recipients = Pass::getRecipientList(file); if (recipients.isEmpty()) { @@ -112,7 +119,7 @@ void ImitatePass::Insert(QString file, QString newValue, bool overwrite) { if (!overwrite) executeGit(GIT_ADD, {"add", pgit(file)}); QString path = QDir(QtPassSettings::getPassStore()).relativeFilePath(file); - path.replace(QRegularExpression("\\.gpg$"), ""); + path.replace(Util::endsWithGpg(), ""); QString msg = QString(overwrite ? "Edit" : "Add") + " for " + path + " using QtPass."; GitCommit(file, msg); @@ -147,12 +154,8 @@ void ImitatePass::Remove(QString file, bool isDir) { GitCommit(file, "Remove for " + file + " using QtPass."); } else { if (isDir) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) QDir dir(file); dir.removeRecursively(); -#else - removeDir(QtPassSettings::getPassStore() + file); -#endif } else QFile(file).remove(); } @@ -166,6 +169,38 @@ void ImitatePass::Remove(QString file, bool isDir) { * path */ void ImitatePass::Init(QString path, const QList<UserInfo> &users) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + QStringList signingKeys = + QtPassSettings::getPassSigningKey().split(" ", Qt::SkipEmptyParts); +#else + QStringList signingKeys = + QtPassSettings::getPassSigningKey().split(" ", QString::SkipEmptyParts); +#endif + QString gpgIdSigFile = path + ".gpg-id.sig"; + bool addSigFile = false; + if (!signingKeys.isEmpty()) { + QString out; + QStringList args = + QStringList{"--status-fd=1", "--list-secret-keys"} + signingKeys; + exec.executeBlocking(QtPassSettings::getGpgExecutable(), args, &out); + bool found = false; + for (auto &key : signingKeys) { + if (out.contains("[GNUPG:] KEY_CONSIDERED " + key)) { + found = true; + break; + } + } + if (!found) { + emit critical(tr("No signing key!"), + tr("None of the secret signing keys is available.\n" + "You will not be able to change the user list!")); + return; + } + QFileInfo checkFile(gpgIdSigFile); + if (!checkFile.exists() || !checkFile.isFile()) + addSigFile = true; + } + QString gpgIdFile = path + ".gpg-id"; QFile gpgId(gpgIdFile); bool addFile = false; @@ -196,18 +231,73 @@ void ImitatePass::Init(QString path, const QList<UserInfo> &users) { return; } + if (!signingKeys.isEmpty()) { + QStringList args; + for (auto &key : signingKeys) { + args.append(QStringList{"--default-key", key}); + } + args.append(QStringList{"--yes", "--detach-sign", gpgIdFile}); + exec.executeBlocking(QtPassSettings::getGpgExecutable(), args); + if (!verifyGpgIdFile(gpgIdFile)) { + emit critical(tr("Check .gpgid file signature!"), + tr("Signature for %1 is invalid.").arg(gpgIdFile)); + return; + } + } + if (!QtPassSettings::isUseWebDav() && QtPassSettings::isUseGit() && !QtPassSettings::getGitExecutable().isEmpty()) { if (addFile) executeGit(GIT_ADD, {"add", pgit(gpgIdFile)}); QString commitPath = gpgIdFile; - commitPath.replace(QRegularExpression("\\.gpg$"), ""); + commitPath.replace(Util::endsWithGpg(), ""); GitCommit(gpgIdFile, "Added " + commitPath + " using QtPass."); + if (!signingKeys.isEmpty()) { + if (addSigFile) + executeGit(GIT_ADD, {"add", pgit(gpgIdSigFile)}); + commitPath = gpgIdSigFile; + commitPath.replace(QRegularExpression("\\.gpg$"), ""); + GitCommit(gpgIdSigFile, "Added " + commitPath + " using QtPass."); + } } reencryptPath(path); } /** + * @brief ImitatePass::verifyGpgIdFile verify detached gpgid file signature. + * @param file which gpgid file. + * @return was verification succesful? + */ +bool ImitatePass::verifyGpgIdFile(const QString &file) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + QStringList signingKeys = + QtPassSettings::getPassSigningKey().split(" ", Qt::SkipEmptyParts); +#else + QStringList signingKeys = + QtPassSettings::getPassSigningKey().split(" ", QString::SkipEmptyParts); +#endif + if (signingKeys.isEmpty()) + return true; + QString out; + QStringList args = + QStringList{"--verify", "--status-fd=1", pgpg(file) + ".sig", pgpg(file)}; + exec.executeBlocking(QtPassSettings::getGpgExecutable(), args, &out); + QRegularExpression re( + "^\\[GNUPG:\\] VALIDSIG ([A-F0-9]{40}) .* ([A-F0-9]{40})\\r?$", + QRegularExpression::MultilineOption); + QRegularExpressionMatch m = re.match(out); + if (!m.hasMatch()) + return false; + QStringList fingerprints = m.capturedTexts(); + fingerprints.removeFirst(); + for (auto &key : signingKeys) { + if (fingerprints.contains(key)) + return true; + } + return false; +} + +/** * @brief ImitatePass::removeDir delete folder recursive. * @param dirName which folder. * @return was removal succesful? @@ -252,10 +342,21 @@ void ImitatePass::reencryptPath(const QString &dir) { QDir currentDir; QDirIterator gpgFiles(dir, QStringList() << "*.gpg", QDir::Files, QDirIterator::Subdirectories); + QStringList gpgIdFilesVerified; QStringList gpgId; while (gpgFiles.hasNext()) { QString fileName = gpgFiles.next(); if (gpgFiles.fileInfo().path() != currentDir.path()) { + QString gpgIdPath = Pass::getGpgIdPath(fileName); + if (!gpgIdFilesVerified.contains(gpgIdPath)) { + if (!verifyGpgIdFile(gpgIdPath)) { + emit critical(tr("Check .gpgid file signature!"), + tr("Signature for %1 is invalid.").arg(gpgIdPath)); + emit endReencryptPath(); + return; + } + gpgIdFilesVerified.append(gpgIdPath); + } gpgId = getRecipientList(fileName); gpgId.sort(); } @@ -267,12 +368,11 @@ void ImitatePass::reencryptPath(const QString &dir) { exec.executeBlocking(QtPassSettings::getGpgExecutable(), args, &keys, &err); QStringList actualKeys; keys += err; + static const QRegularExpression newLines{"[\r\n]"}; #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) - QStringList key = - keys.split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts); + QStringList key = keys.split(newLines, Qt::SkipEmptyParts); #else - QStringList key = - keys.split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); + QStringList key = keys.split(newLines, QString::SkipEmptyParts); #endif QListIterator<QString> itr(key); while (itr.hasNext()) { @@ -325,7 +425,7 @@ void ImitatePass::reencryptPath(const QString &dir) { {"add", pgit(fileName)}); QString path = QDir(QtPassSettings::getPassStore()).relativeFilePath(fileName); - path.replace(QRegularExpression("\\.gpg$"), ""); + path.replace(Util::endsWithGpg(), ""); exec.executeBlocking(QtPassSettings::getGitExecutable(), {"commit", pgit(fileName), "-m", "Edit for " + path + " using QtPass."}); @@ -405,12 +505,12 @@ void ImitatePass::Move(const QString src, const QString dest, executeGit(GIT_MOVE, args); QString relSrc = QDir(QtPassSettings::getPassStore()).relativeFilePath(src); - relSrc.replace(QRegularExpression("\\.gpg$"), ""); + relSrc.replace(Util::endsWithGpg(), ""); QString relDest = QDir(QtPassSettings::getPassStore()).relativeFilePath(destFile); - relDest.replace(QRegularExpression("\\.gpg$"), ""); + relDest.replace(Util::endsWithGpg(), ""); QString message = QString("Moved for %1 to %2 using QtPass."); - message = message.arg(relSrc).arg(relDest); + message = message.arg(relSrc, relDest); GitCommit("", message); } else { QDir qDir; @@ -436,7 +536,7 @@ void ImitatePass::Copy(const QString src, const QString dest, executeGit(GIT_COPY, args); QString message = QString("copied from %1 to %2 using QTPass."); - message = message.arg(src).arg(dest); + message = message.arg(src, dest); GitCommit("", message); } else { QDir qDir; |