diff --git a/src/utils/gnupg.cpp b/src/utils/gnupg.cpp index 5a27fa301..e33b81f0f 100644 --- a/src/utils/gnupg.cpp +++ b/src/utils/gnupg.cpp @@ -1,818 +1,809 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/gnupg.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-FileCopyrightText: 2020-2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "gnupg.h" #include "assuan.h" #include "compat.h" #include "compliance.h" #include "cryptoconfig.h" #include "hex.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN #include "gnupg-registry.h" #endif // Q_OS_WIN #include #include using namespace GpgME; QString Kleo::gnupgHomeDirectory() { static const QString homeDir = QString::fromUtf8(GpgME::dirInfo("homedir")); return homeDir; } QString Kleo::gnupgPrivateKeysDirectory() { static const QString dir = QDir{gnupgHomeDirectory()}.filePath(QStringLiteral("private-keys-v1.d")); return dir; } int Kleo::makeGnuPGError(int code) { return gpg_error(static_cast(code)); } static QString findGpgExe(GpgME::Engine engine, const QString &exe) { const GpgME::EngineInfo info = GpgME::engineInfo(engine); return info.fileName() ? QFile::decodeName(info.fileName()) : QStandardPaths::findExecutable(exe); } QString Kleo::gpgConfPath() { static const auto path = findGpgExe(GpgME::GpgConfEngine, QStringLiteral("gpgconf")); return path; } QString Kleo::gpgSmPath() { static const auto path = findGpgExe(GpgME::GpgSMEngine, QStringLiteral("gpgsm")); return path; } QString Kleo::gpgPath() { static const auto path = findGpgExe(GpgME::GpgEngine, QStringLiteral("gpg")); return path; } QStringList Kleo::gnupgFileWhitelist() { return { // The obvious pubring QStringLiteral("pubring.gpg"), // GnuPG 2.1 pubring QStringLiteral("pubring.kbx"), // Trust in X509 Certificates QStringLiteral("trustlist.txt"), // Trustdb controls ownertrust and thus WOT validity QStringLiteral("trustdb.gpg"), // We want to update when smartcard status changes QStringLiteral("reader*.status"), // No longer used in 2.1 but for 2.0 we want this QStringLiteral("secring.gpg"), // Secret keys (living under private-keys-v1.d/) QStringLiteral("*.key"), // Changes to the trustmodel / compliance mode might // affect validity so we check this, too. // Globbing for gpg.conf* here will trigger too often // as gpgconf creates files like gpg.conf.bak or // gpg.conf.tmp12312.gpgconf that should not trigger // a change. QStringLiteral("gpg.conf"), QStringLiteral("gpg.conf-?"), QStringLiteral("gpg.conf-?.?"), }; } QStringList Kleo::gnupgFolderWhitelist() { static const QDir gnupgHome{gnupgHomeDirectory()}; return { gnupgHome.path(), gnupgPrivateKeysDirectory(), }; } namespace { class Gpg4win { public: static const Gpg4win *instance() { // We use singleton to do the signature check only once. static Gpg4win *inst = nullptr; if (!inst) { inst = new Gpg4win(); } return inst; } private: QString mVersion; QString mDescription; QString mDescLong; QString mBrandingWindowTitle; QString mBrandingIcon; bool mSignedVersion; Gpg4win() : mVersion(QStringLiteral("Unknown Windows Version")) , mDescription(i18n("Certificate Manager and Unified Crypto GUI")) , mDescLong(QStringLiteral("Visit the Gpg4win homepage")) , mSignedVersion(false) { const QString instPath = Kleo::gpg4winInstallPath(); const QString verPath = instPath + QStringLiteral("/../VERSION"); QFile versionFile(verPath); // Open the file first to avoid a verify and then read issue where // "auditors" might say its an issue,... if (!versionFile.open(QIODevice::ReadOnly)) { return; } // Expect a three line format of three HTML strings. const auto versVersion = QString::fromUtf8(versionFile.readLine()).trimmed(); const auto versDescription = QString::fromUtf8(versionFile.readLine()).trimmed(); const auto versDescLong = QString::fromUtf8(versionFile.readLine()).trimmed(); // read optional two branding strings const auto brandingWindowTitle = QString::fromUtf8(versionFile.readLine()).trimmed(); const auto brandingIcon = QString::fromUtf8(versionFile.readLine()).trimmed(); const QString sigPath = verPath + QStringLiteral(".sig"); QFileInfo versionSig(instPath + QStringLiteral("/../VERSION.sig")); if (versionSig.exists()) { /* We have a signed version so let us check it against the GnuPG * release keys. */ QProcess gpgv; gpgv.setProgram(Kleo::gpgPath().replace(QStringLiteral("gpg.exe"), QStringLiteral("gpgv.exe"))); const QString keyringPath(QStringLiteral("%1/../share/gnupg/distsigkey.gpg").arg(Kleo::gnupgInstallPath())); gpgv.setArguments(QStringList() << QStringLiteral("--keyring") << keyringPath << QStringLiteral("--") << sigPath << verPath); gpgv.start(); gpgv.waitForFinished(); if (gpgv.exitStatus() == QProcess::NormalExit && !gpgv.exitCode()) { qCDebug(LIBKLEO_LOG) << "Valid Version: " << versVersion; mVersion = versVersion; mDescription = versDescription; mDescLong = versDescLong; mBrandingWindowTitle = brandingWindowTitle; mBrandingIcon = brandingIcon; mSignedVersion = true; } else { qCDebug(LIBKLEO_LOG) << "gpgv failed with stderr: " << gpgv.readAllStandardError(); qCDebug(LIBKLEO_LOG) << "gpgv stdout" << gpgv.readAllStandardOutput(); } } else { qCDebug(LIBKLEO_LOG) << "No signed VERSION file found."; } // Also take Version information from unsigned Versions. mVersion = versVersion; } public: const QString &version() const { return mVersion; } const QString &description() const { return mDescription; } const QString &longDescription() const { return mDescLong; } bool isSignedVersion() const { return mSignedVersion; } const QString &brandingWindowTitle() const { return mBrandingWindowTitle; } const QString &brandingIcon() const { return mBrandingIcon; } }; } // namespace bool Kleo::gpg4winSignedversion() { return Gpg4win::instance()->isSignedVersion(); } QString Kleo::gpg4winVersionNumber() { // extract the actual version number from the string returned by Gpg4win::version(); // we assume that Gpg4win::version() returns a version number (conforming to the semantic // versioning spec) optionally prefixed with some text followed by a dash, // e.g. "Gpg4win-3.1.15-beta15"; see https://dev.gnupg.org/T5663 static const QRegularExpression catchSemVerRegExp{QLatin1String{R"(-([0-9]+(?:\.[0-9]+)*(?:-[.0-9A-Za-z-]+)?(?:\+[.0-9a-zA-Z-]+)?)$)"}}; QString ret; const auto match = catchSemVerRegExp.match(gpg4winVersion()); if (match.hasMatch()) { ret = match.captured(1); } else { ret = gpg4winVersion(); } qCDebug(LIBKLEO_LOG) << __func__ << "returns" << ret; return ret; } QString Kleo::gpg4winVersion() { return Gpg4win::instance()->version(); } QString Kleo::gpg4winDescription() { return Gpg4win::instance()->description(); } QString Kleo::gpg4winLongDescription() { return Gpg4win::instance()->longDescription(); } QString Kleo::brandingWindowTitle() { return Gpg4win::instance()->brandingWindowTitle(); } QString Kleo::brandingIcon() { return Gpg4win::instance()->brandingIcon(); } QString Kleo::gpg4winInstallPath() { #ifdef Q_OS_WIN // QApplication::applicationDirPath is only used as a fallback // to support the case where Kleopatra is not installed from // Gpg4win but Gpg4win is also installed. char *instDir = read_w32_registry_string("HKEY_LOCAL_MACHINE", "Software\\GPG4Win", "Install Directory"); if (!instDir) { // Fallback to HKCU instDir = read_w32_registry_string("HKEY_CURRENT_USER", "Software\\GPG4Win", "Install Directory"); } if (instDir) { QString ret = QString::fromLocal8Bit(instDir) + QStringLiteral("/bin"); free(instDir); return ret; } qCDebug(LIBKLEO_LOG) << "Gpg4win not found. Falling back to Kleopatra instdir."; #endif return QCoreApplication::applicationDirPath(); } QString Kleo::gnupgInstallPath() { #ifdef Q_OS_WIN // QApplication::applicationDirPath is only used as a fallback // to support the case where Kleopatra is not installed from // Gpg4win but Gpg4win is also installed. char *instDir = read_w32_registry_string("HKEY_LOCAL_MACHINE", "Software\\GnuPG", "Install Directory"); if (!instDir) { // Fallback to HKCU instDir = read_w32_registry_string("HKEY_CURRENT_USER", "Software\\GnuPG", "Install Directory"); } if (instDir) { QString ret = QString::fromLocal8Bit(instDir) + QStringLiteral("/bin"); free(instDir); return ret; } qCDebug(LIBKLEO_LOG) << "GnuPG not found. Falling back to gpgconf list dir."; #endif return gpgConfListDir("bindir"); } QString Kleo::gpgConfListDir(const char *which) { if (!which || !*which) { return QString(); } const QString gpgConfPath = Kleo::gpgConfPath(); if (gpgConfPath.isEmpty()) { return QString(); } QProcess gpgConf; qCDebug(LIBKLEO_LOG) << "gpgConfListDir: starting " << qPrintable(gpgConfPath) << " --list-dirs"; gpgConf.start(gpgConfPath, QStringList() << QStringLiteral("--list-dirs")); if (!gpgConf.waitForFinished()) { qCDebug(LIBKLEO_LOG) << "gpgConfListDir(): failed to execute gpgconf: " << qPrintable(gpgConf.errorString()); qCDebug(LIBKLEO_LOG) << "output was:\n" << gpgConf.readAllStandardError().constData(); return QString(); } const QList lines = gpgConf.readAllStandardOutput().split('\n'); for (const QByteArray &line : lines) { if (line.startsWith(which) && line[qstrlen(which)] == ':') { const int begin = qstrlen(which) + 1; int end = line.size(); while (end && (line[end - 1] == '\n' || line[end - 1] == '\r')) { --end; } const QString result = QDir::fromNativeSeparators(QFile::decodeName(hexdecode(line.mid(begin, end - begin)))); qCDebug(LIBKLEO_LOG) << "gpgConfListDir: found " << qPrintable(result) << " for '" << which << "'entry"; return result; } } qCDebug(LIBKLEO_LOG) << "gpgConfListDir(): didn't find '" << which << "'" << "entry in output:\n" << gpgConf.readAllStandardError().constData(); return QString(); } static std::array getVersionFromString(const char *actual, bool &ok) { std::array ret; ok = false; if (!actual) { return ret; } QString versionString = QString::fromLatin1(actual); // Try to fix it up QRegExp rx(QLatin1String(R"((\d+)\.(\d+)\.(\d+)(?:-svn\d+)?.*)")); for (int i = 0; i < 3; i++) { if (!rx.exactMatch(versionString)) { versionString += QStringLiteral(".0"); } else { ok = true; break; } } if (!ok) { qCDebug(LIBKLEO_LOG) << "Can't parse version " << actual; return ret; } for (int i = 0; i < 3; ++i) { ret[i] = rx.cap(i + 1).toUInt(&ok); if (!ok) { return ret; } } ok = true; return ret; } bool Kleo::versionIsAtLeast(const char *minimum, const char *actual) { if (!minimum || !actual) { return false; } bool ok; const auto minimum_version = getVersionFromString(minimum, ok); if (!ok) { return false; } const auto actual_version = getVersionFromString(actual, ok); if (!ok) { return false; } return !std::lexicographical_compare(std::begin(actual_version), std::end(actual_version), std::begin(minimum_version), std::end(minimum_version)); } bool Kleo::engineIsVersion(int major, int minor, int patch, GpgME::Engine engine) { static QMap> cachedVersions; const int required_version[] = {major, minor, patch}; // Gpgconf means spawning processes which is expensive on windows. std::array actual_version; if (!cachedVersions.contains(engine)) { const Error err = checkEngine(engine); if (err.code() == GPG_ERR_INV_ENGINE) { qCDebug(LIBKLEO_LOG) << "isVersion: invalid engine. '"; return false; } const char *actual = GpgME::engineInfo(engine).version(); bool ok; actual_version = getVersionFromString(actual, ok); qCDebug(LIBKLEO_LOG) << "Parsed" << actual << "as: " << actual_version[0] << '.' << actual_version[1] << '.' << actual_version[2] << '.'; if (!ok) { return false; } cachedVersions.insert(engine, actual_version); } else { actual_version = cachedVersions.value(engine); } // return ! ( actual_version < required_version ) return !std::lexicographical_compare(std::begin(actual_version), std::end(actual_version), std::begin(required_version), std::end(required_version)); } const QString &Kleo::paperKeyInstallPath() { static const QString pkPath = (QStandardPaths::findExecutable(QStringLiteral("paperkey"), QStringList() << QCoreApplication::applicationDirPath()).isEmpty() ? QStandardPaths::findExecutable(QStringLiteral("paperkey")) : QStandardPaths::findExecutable(QStringLiteral("paperkey"), QStringList() << QCoreApplication::applicationDirPath())); return pkPath; } bool Kleo::haveKeyserverConfigured() { if (engineIsVersion(2, 4, 4) // || (engineIsVersion(2, 2, 42) && !engineIsVersion(2, 3, 0))) { return Kleo::keyserver() != QLatin1String{"none"}; } if (engineIsVersion(2, 1, 19)) { // since 2.1.19 there is a builtin keyserver return true; } return !Kleo::keyserver().isEmpty(); } QString Kleo::keyserver() { QString result = getCryptoConfigStringValue("gpg", "keyserver"); if (result.isEmpty()) { result = getCryptoConfigStringValue("dirmngr", "keyserver"); } if (result.endsWith(QLatin1String{"://none"})) { // map hkps://none, etc., to "none"; see https://dev.gnupg.org/T6708 result = QStringLiteral("none"); } return result; } bool Kleo::haveX509DirectoryServerConfigured() { return !getCryptoConfigUrlList("dirmngr", "ldapserver").empty() // || !getCryptoConfigUrlList("dirmngr", "LDAP Server").empty() // || !getCryptoConfigUrlList("gpgsm", "keyserver").empty(); } bool Kleo::gpgComplianceP(const char *mode) { const auto conf = QGpgME::cryptoConfig(); const auto entry = getCryptoConfigEntry(conf, "gpg", "compliance"); return entry && entry->stringValue() == QString::fromLatin1(mode); } bool Kleo::gnupgUsesDeVsCompliance() { return DeVSCompliance::isActive(); } bool Kleo::gnupgIsDeVsCompliant() { return DeVSCompliance::isCompliant(); } #ifdef Q_OS_WIN static unsigned int guessConsoleOutputCodePage() { /* Qt on Windows uses GetACP while GnuPG prefers * GetConsoleOutputCP. * * As we are not a console application GetConsoleOutputCP * usually returns 0. * From experience the closest thing that let's us guess * what GetConsoleOutputCP returns for a console application * it appears to be the OEMCP. */ unsigned int cpno = GetConsoleOutputCP(); if (!cpno) { cpno = GetOEMCP(); } if (!cpno) { cpno = GetACP(); } if (!cpno) { qCDebug(LIBKLEO_LOG) << __func__ << "Failed to find native codepage"; } qCDebug(LIBKLEO_LOG) << __func__ << "returns" << cpno; return cpno; } static QString fromEncoding(unsigned int src_encoding, const char *data) { if (!data || !*data) { return {}; } // returns necessary buffer size including the terminating null character int n = MultiByteToWideChar(src_encoding, 0, data, -1, NULL, 0); if (n <= 0) { qCDebug(LIBKLEO_LOG) << __func__ << "determining necessary buffer size failed with error code" << GetLastError(); return QString(); } wchar_t *result = (wchar_t *)malloc((n + 1) * sizeof *result); n = MultiByteToWideChar(src_encoding, 0, data, -1, result, n); if (n <= 0) { free(result); qCDebug(LIBKLEO_LOG) << __func__ << "conversion failed with error code" << GetLastError(); return QString(); } const auto ret = QString::fromWCharArray(result, n - 1); free(result); return ret; } static QString stringFromGpgOutput_legacy(const QByteArray &ba) { static const unsigned int cpno = guessConsoleOutputCodePage(); if (cpno) { qCDebug(LIBKLEO_LOG) << __func__ << "trying to decode" << ba << "using codepage" << cpno; const auto rawData = QByteArray{ba}.replace("\r\n", "\n"); const auto s = fromEncoding(cpno, rawData.constData()); if (!s.isEmpty() || ba.isEmpty()) { return s; } qCDebug(LIBKLEO_LOG) << __func__ << "decoding output failed; falling back to QString::fromLocal8Bit()"; } qCDebug(LIBKLEO_LOG) << __func__ << "decoding from local encoding:" << ba; return QString::fromLocal8Bit(ba); } #endif QString Kleo::stringFromGpgOutput(const QByteArray &ba) { #ifdef Q_OS_WIN // since 2.2.28, GnuPG always uses UTF-8 for console output (and input) if (Kleo::engineIsVersion(2, 2, 28, GpgME::GpgEngine)) { return QString::fromUtf8(ba); } else { return stringFromGpgOutput_legacy(ba); } #else return QString::fromLocal8Bit(ba); #endif } QStringList Kleo::backendVersionInfo() { QStringList versions; if (Kleo::engineIsVersion(2, 2, 24, GpgME::GpgConfEngine)) { QProcess p; qCDebug(LIBKLEO_LOG) << "Running gpgconf --show-versions ..."; p.start(Kleo::gpgConfPath(), {QStringLiteral("--show-versions")}); // wait at most 1 second if (!p.waitForFinished(1000)) { qCDebug(LIBKLEO_LOG) << "Running gpgconf --show-versions timed out after 1 second."; } else if (p.exitStatus() != QProcess::NormalExit || p.exitCode() != 0) { qCDebug(LIBKLEO_LOG) << "Running gpgconf --show-versions failed:" << p.errorString(); qCDebug(LIBKLEO_LOG) << "gpgconf stderr:" << p.readAllStandardError(); qCDebug(LIBKLEO_LOG) << "gpgconf stdout:" << p.readAllStandardOutput(); } else { const QByteArray output = p.readAllStandardOutput().replace("\r\n", "\n"); qCDebug(LIBKLEO_LOG) << "gpgconf stdout:" << p.readAllStandardOutput(); const auto lines = output.split('\n'); for (const auto &line : lines) { if (line.startsWith("* GnuPG") || line.startsWith("* Libgcrypt")) { const auto components = line.split(' '); versions.push_back(QString::fromLatin1(components.at(1) + ' ' + components.value(2))); } } } } return versions; } namespace { template auto startGpgConf(const QStringList &arguments, Function1 onSuccess, Function2 onFailure) { auto process = new QProcess; process->setProgram(Kleo::gpgConfPath()); process->setArguments(arguments); QObject::connect(process, &QProcess::started, [process]() { qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") was started successfully"; }); QObject::connect(process, &QProcess::errorOccurred, [process, onFailure](auto error) { qCDebug(LIBKLEO_LOG).nospace() << "Error while running gpgconf (" << process << "): " << error; process->deleteLater(); onFailure(); }); QObject::connect(process, &QProcess::readyReadStandardError, [process]() { for (const auto &line : process->readAllStandardError().trimmed().split('\n')) { qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") stderr: " << line; } }); QObject::connect(process, &QProcess::readyReadStandardOutput, [process]() { (void)process->readAllStandardOutput(); /* ignore stdout */ }); QObject::connect(process, qOverload(&QProcess::finished), [process, onSuccess, onFailure](int exitCode, QProcess::ExitStatus exitStatus) { if (exitStatus == QProcess::NormalExit) { qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") exited (exit code: " << exitCode << ")"; if (exitCode == 0) { onSuccess(); } else { onFailure(); } } else { qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") crashed (exit code: " << exitCode << ")"; onFailure(); } process->deleteLater(); }); qCDebug(LIBKLEO_LOG).nospace() << "Starting gpgconf (" << process << ") with arguments " << process->arguments().join(QLatin1Char(' ')) << " ..."; process->start(); return process; } - -static auto startGpgConf(const QStringList &arguments) -{ - return startGpgConf( - arguments, - []() {}, - []() {}); -} - } void Kleo::launchGpgAgent() { static QPointer process; static qint64 mSecsSinceEpochOfLastLaunch = 0; static int numberOfFailedLaunches = 0; if (Kleo::Assuan::agentIsRunning()) { qCDebug(LIBKLEO_LOG) << __func__ << ": gpg-agent is already running"; return; } if (process) { qCDebug(LIBKLEO_LOG) << __func__ << ": gpg-agent is already being launched"; return; } const auto now = QDateTime::currentMSecsSinceEpoch(); if (now - mSecsSinceEpochOfLastLaunch < 1000) { // reduce attempts to launch the agent to 1 attempt per second return; } mSecsSinceEpochOfLastLaunch = now; if (numberOfFailedLaunches > 5) { qCWarning(LIBKLEO_LOG) << __func__ << ": Launching gpg-agent failed" << numberOfFailedLaunches << "times in a row. Giving up."; return; } process = startGpgConf( {QStringLiteral("--launch"), QStringLiteral("gpg-agent")}, []() { numberOfFailedLaunches = 0; }, []() { numberOfFailedLaunches++; }); } -void Kleo::killDaemons() +void Kleo::restartGpgAgent() { static QPointer process; if (process) { - qCDebug(LIBKLEO_LOG) << __func__ << ": The daemons are already being shut down"; + qCDebug(LIBKLEO_LOG) << __func__ << ": gpg-agent is already being restarted"; return; } - process = startGpgConf({QStringLiteral("--kill"), QStringLiteral("all")}); + process = startGpgConf({QStringLiteral("--kill"), QStringLiteral("all")}, &Kleo::launchGpgAgent, &Kleo::launchGpgAgent); } const std::vector &Kleo::availableAlgorithms() { static const std::vector algos = { "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1", "curve25519", "curve448", "nistp256", "nistp384", "nistp521", "rsa2048", "rsa3072", "rsa4096", // "secp256k1", // Curve secp256k1 is explicitly ignored }; return algos; } const std::vector &Kleo::preferredAlgorithms() { static const std::vector algos = { "curve25519", "brainpoolP256r1", "rsa3072", "rsa2048", }; return algos; } const std::vector &Kleo::ignoredAlgorithms() { static const std::vector algos = { "secp256k1", // Curve secp256k1 is not useful }; return algos; } bool Kleo::gpgvVerify(const QString &filePath, const QString &sigPath, const QString &keyring, const QStringList &additionalSearchPaths) { const QFileInfo verifyFi(filePath); if (!verifyFi.isReadable()) { return false; } else { qCDebug(LIBKLEO_LOG) << "Verifying" << filePath; } const auto gpgvPath = QStandardPaths::findExecutable(QStringLiteral("gpgv"), additionalSearchPaths); if (gpgvPath.isEmpty()) { qCDebug(LIBKLEO_LOG) << "Could not find gpgv"; return false; } QFileInfo sigFi; if (!sigPath.isEmpty()) { sigFi.setFile(sigPath); } else { sigFi.setFile(filePath + QStringLiteral(".sig")); } if (!sigFi.isReadable()) { qCDebug(LIBKLEO_LOG) << "No signature found at" << sigFi.absoluteFilePath(); return false; } auto process = QProcess(); process.setProgram(gpgvPath); QStringList args; if (!keyring.isEmpty()) { args << QStringLiteral("--keyring") << keyring; } args << QStringLiteral("--") << sigFi.absoluteFilePath() << verifyFi.absoluteFilePath(); process.setArguments(args); qCDebug(LIBKLEO_LOG).nospace() << "Starting gpgv (" << gpgvPath << ") with arguments " << args.join(QLatin1Char(' ')) << " ..."; process.start(); if (!process.waitForFinished(-1)) { qCDebug(LIBKLEO_LOG) << "Failed to execute gpgv" << process.errorString(); } bool ret = (process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0); if (!ret) { qCDebug(LIBKLEO_LOG) << "Failed to verify file"; qCDebug(LIBKLEO_LOG) << "gpgv stdout:" << QString::fromUtf8(process.readAllStandardOutput()); qCDebug(LIBKLEO_LOG) << "gpgv stderr:" << QString::fromUtf8(process.readAllStandardError()); } return ret; } diff --git a/src/utils/gnupg.h b/src/utils/gnupg.h index c0fd0833e..cd1f227c0 100644 --- a/src/utils/gnupg.h +++ b/src/utils/gnupg.h @@ -1,156 +1,154 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/gnupg.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2020-2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "kleo_export.h" #include #include #include class QString; class QByteArray; namespace Kleo { KLEO_EXPORT QString gnupgHomeDirectory(); KLEO_EXPORT QString gnupgPrivateKeysDirectory(); KLEO_EXPORT QString gpgConfPath(); KLEO_EXPORT QString gpgSmPath(); KLEO_EXPORT QString gpgPath(); KLEO_EXPORT QString gpgConfListDir(const char *which); KLEO_EXPORT QString gpg4winInstallPath(); KLEO_EXPORT QString gnupgInstallPath(); KLEO_EXPORT const QString &paperKeyInstallPath(); /* Deprecated. Better read this from a settings file which * can optionally be verified with gpgvVerify. */ KLEO_DEPRECATED_EXPORT QString gpg4winVersionNumber(); KLEO_DEPRECATED_EXPORT QString gpg4winVersion(); KLEO_DEPRECATED_EXPORT bool gpg4winSignedversion(); KLEO_DEPRECATED_EXPORT QString gpg4winDescription(); KLEO_DEPRECATED_EXPORT QString gpg4winLongDescription(); KLEO_DEPRECATED_EXPORT QString brandingWindowTitle(); KLEO_DEPRECATED_EXPORT QString brandingIcon(); /** * Verify \p filePath using gpgv. If \p sigPath is provided it uses * this signature, otherwise it adds .sig to the \p filePath. If * \p keyring is provided that is the keyring where the signature is * checked against. Otherwise it uses the default of gpgv. * \p additionalSearchPaths can be used to specify where gpgv is * searched for first. * * Blocks until the verification is done which can be indefinetly to * allow for very large files. * * Returns true if the verification was successful, false if any problem * occured. */ KLEO_EXPORT bool gpgvVerify(const QString &filePath, const QString &sigPath = {}, const QString &keyring = {}, const QStringList &additionalSearchPaths = {}); /** * Returns a list of filename globs of files in one of the whitelisted folders * to watch for changes. * \sa gnupgFolderWhitelist, Kleo::FileSystemWatcher */ KLEO_EXPORT QStringList gnupgFileWhitelist(); /** * Returns a list of absolute paths of folders to watch for changes. * \sa gnupgFileWhitelist, Kleo::FileSystemWatcher */ KLEO_EXPORT QStringList gnupgFolderWhitelist(); KLEO_EXPORT int makeGnuPGError(int code); KLEO_EXPORT bool engineIsVersion(int major, int minor, int patch, GpgME::Engine = GpgME::GpgConfEngine); /** Returns true, if GnuPG knows which keyserver to use for keyserver * operations. * * Since GnuPG 2.2.42/2.4.4 dirmngr supports the special value "none" * to disable usage of the default keyserver. If this value is configured * and GnuPG is new enough then this function returns false. * Since version 2.1.19 GnuPG has a builtin default keyserver, so that this * function always returns true (unless the above applies). * For older versions of GnuPG it checks if a keyserver has been configured. */ KLEO_EXPORT bool haveKeyserverConfigured(); /** Returns the configured keyserver or an empty string if no keyserver is * configured. The special value "none" indicates that no keyserver shall * be used. * * Note: Since GnuPG 2.1.19 gpg/dirmngr uses a default keyserver if no * keyserver is configured. * Since GnuPG 2.2.42/2.4.4 dirmngr supports the special value "none" * to disable usage of the default keyserver. */ KLEO_EXPORT QString keyserver(); /** Returns true, if GnuPG knows which server to use for directory service * operations for X.509 certificates. */ KLEO_EXPORT bool haveX509DirectoryServerConfigured(); /* Use gnupgUsesDeVsCompliance() or gnupgIsDeVsCompliant() instead. */ KLEO_DEPRECATED_EXPORT bool gpgComplianceP(const char *mode); /** * Use Kleo::DeVSCompliance::isActive() instead. */ KLEO_DEPRECATED_EXPORT bool gnupgUsesDeVsCompliance(); /** * Use Kleo::DeVSCompliance::isCompliant() instead. */ KLEO_DEPRECATED_EXPORT bool gnupgIsDeVsCompliant(); /* Convert GnuPG output to a QString with proper encoding. * Takes Gpg Quirks into account and might handle future * changes in GnuPG Output. */ KLEO_EXPORT QString stringFromGpgOutput(const QByteArray &ba); /* Check if a minimum version is there. Strings should be in the format: * 1.2.3 */ KLEO_EXPORT bool versionIsAtLeast(const char *minimum, const char *actual); /** Returns a list of component names (e.g. GnuPG, libgcrypt) followed by * version numbers. This is meant for displaying in the About dialog. */ KLEO_EXPORT QStringList backendVersionInfo(); /** Launch the GnuPG agent if it is not already running. */ KLEO_EXPORT void launchGpgAgent(); -/** Shut down all GnuPG daemons. They will be restarted automatically when - * needed. - */ -KLEO_EXPORT void killDaemons(); +/** Shut down all GnuPG daemons and restart the GnuPG agent. */ +KLEO_EXPORT void restartGpgAgent(); /** * Returns a static list of the available algorithms. */ KLEO_EXPORT const std::vector &availableAlgorithms(); /** * Returns a static list of the preferred algorithms with decreasing preference. */ KLEO_EXPORT const std::vector &preferredAlgorithms(); /** * Returns a static list of algorithms that are explicitly not supported. */ KLEO_EXPORT const std::vector &ignoredAlgorithms(); }