Page MenuHome GnuPG

No OneTemporary

diff --git a/client/draft/draftmanager.cpp b/client/draft/draftmanager.cpp
index 0ccb4a2..f2a93bd 100644
--- a/client/draft/draftmanager.cpp
+++ b/client/draft/draftmanager.cpp
@@ -1,114 +1,114 @@
// SPDX-FileCopyrightText: 2023 g10 code GmbH
// SPDX-Contributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "draftmanager.h"
#include <QDir>
#include <QStandardPaths>
#include "editor_debug.h"
#include "../../common/paths.h"
DraftManager::DraftManager(bool testMode)
: m_testMode(testMode)
{
const QDir directory(draftDirectory(testMode));
const auto entries = directory.entryList(QDir::Files);
for (const QString &entry : entries) {
Draft draft(draftDirectory() + entry);
if (draft.isValid()) {
m_drafts << draft;
} else {
qFatal(EDITOR_LOG) << "File does not exist or is not readable" << entry;
}
}
}
QString DraftManager::draftDirectory(bool testMode)
{
if (testMode) {
- static const QString path = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QLatin1String("/gpgol-server/draft/");
+ static const QString path = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QLatin1String("/draft/");
return path;
} else {
- static const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/gpgol-server/draft/");
+ static const QString path = CommonPaths::writeablePath() + QLatin1String("/draft/");
return path;
}
}
QString DraftManager::autosaveDirectory(bool testMode)
{
if (testMode) {
- static const QString path = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QLatin1String("/gpgol-server/autosave/");
+ static const QString path = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QLatin1String("/autosave/");
return path;
} else {
- static const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/gpgol-server/autosave/");
+ static const QString path = CommonPaths::writeablePath() + QLatin1String("/autosave/");
return path;
}
}
DraftManager &DraftManager::self(bool testMode)
{
static DraftManager s_draftManager(testMode);
return s_draftManager;
}
QList<Draft> DraftManager::drafts() const
{
return m_drafts;
}
QList<Draft> DraftManager::autosaves() const
{
QList<Draft> autosaves;
const QDir directory(autosaveDirectory(false));
const auto entries = directory.entryList(QDir::Files);
for (const QString &entry : entries) {
Draft draft(autosaveDirectory() + entry);
if (draft.isValid()) {
autosaves << draft;
} else {
qFatal(EDITOR_LOG) << "File does not exist or is not readable" << entry;
}
}
return autosaves;
}
QJsonArray DraftManager::toJson() const
{
if (m_drafts.isEmpty()) {
return {};
}
QJsonArray array;
std::transform(m_drafts.cbegin(), m_drafts.cend(), std::back_inserter(array), [](const auto draft) {
return draft.toJson();
});
return array;
}
bool DraftManager::remove(const Draft &draft)
{
auto it = std::find(m_drafts.begin(), m_drafts.end(), draft);
if (it == m_drafts.end()) {
return false;
}
bool ok = it->remove();
if (ok) {
m_drafts.erase(it);
}
return ok;
}
void DraftManager::add(const QString &localFileName)
{
m_drafts.append(Draft(localFileName));
}
Draft DraftManager::draftById(const QByteArray &draftId)
{
return Draft(draftDirectory() + QString::fromUtf8(draftId));
}
diff --git a/client/rootcagenerator/controller.cpp b/client/rootcagenerator/controller.cpp
index f2b4b93..159ea34 100644
--- a/client/rootcagenerator/controller.cpp
+++ b/client/rootcagenerator/controller.cpp
@@ -1,399 +1,399 @@
// SPDX-FileCopyrightText: 2023 g10 code GmbH
// SPDX-Contributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "controller.h"
#include "truststore.h"
#include "../gpgol_client_debug.h"
#include "../../common/paths.h"
#include "../utils/formatter.h"
#include <QDate>
#include <QDir>
#include <QProcess>
#include <QSaveFile>
#include <QTemporaryDir>
#include <QGpgME/ExportJob>
#include <QGpgME/ImportJob>
#include <QGpgME/KeyGenerationJob>
#include <QGpgME/KeyListJob>
#include <QGpgME/Protocol>
#include <gpgme++/context.h>
#include <Libkleo/Formatting>
#include <Libkleo/KeyParameters>
#include <Libkleo/KeyUsage>
#include <KLocalizedString>
using namespace Qt::StringLiterals;
using namespace Kleo;
using namespace GpgME;
static KeyParameters createRootCaParms()
{
KeyParameters keyParameters(KeyParameters::CMS);
keyParameters.setKeyType(GpgME::Subkey::PubkeyAlgo::AlgoRSA);
keyParameters.setKeyUsage(KeyUsage{KeyUsage::Sign | KeyUsage::Certify});
keyParameters.setDN(u"CN=GnuPG Outlook Add-in Local Root CA"_s);
keyParameters.setEmail(u"localroot@gpgoljs.local"_s);
keyParameters.setKeyLength(3072);
keyParameters.setExpirationDate(QDate(2060, 10, 10));
keyParameters.setUseRandomSerial();
keyParameters.setControlStatements({u"%no-protection"_s});
return keyParameters;
}
static KeyParameters createTlsCertParms(QLatin1StringView keyGrip)
{
KeyParameters keyParameters(KeyParameters::CMS);
keyParameters.setKeyType(GpgME::Subkey::PubkeyAlgo::AlgoRSA);
keyParameters.setKeyUsage(KeyUsage{KeyUsage::Sign | KeyUsage::Encrypt});
keyParameters.setDN(u"CN=GnuPG Outlook Add-in Local Server Certificate"_s);
keyParameters.setEmail(u"local@gpgoljs.local"_s);
keyParameters.setKeyLength(3072);
keyParameters.setExpirationDate(QDate(2060, 10, 10));
keyParameters.setIssuerDN(u"CN=GnuPG Outlook Add-in Local Root CA"_s);
keyParameters.setSigningKey(keyGrip);
keyParameters.setUseRandomSerial();
keyParameters.addDomainName(u"localhost"_s);
keyParameters.setControlStatements({u"%no-protection"_s});
return keyParameters;
}
Controller::Controller(QObject *parent)
: KJob(parent)
{
}
Controller::~Controller()
{
if (m_tmpdir) {
QProcess p;
p.start(u"gpgconf"_s, {u"--homedir"_s, m_tmpdir->path(), u"--kill"_s, u"all"_s});
p.waitForFinished();
}
}
QString Controller::caUniqueName() const
{
return u"GPGOL2 CA "_s + QString::fromLatin1(m_ca.issuerSerial());
}
QByteArray Controller::caCert() const
{
return m_publicCA;
}
bool Controller::certificateAlreadyGenerated()
{
if (!(CommonPaths::locateFile(u"certificate.pem"_s).isEmpty())) {
return true;
}
// Copy from legacy path, if installed, there.
// TODO: remove this after grace period
// NOTE: copy certiface.pem last: Only if all three files were copied successfully, we shall return true below
const QStringList files{ u"certificate-key.pem"_s, u"root-ca.pem"_s, u"certificate.pem"_s };
const auto modern_path = CommonPaths::writeablePath();
QDir().mkpath(modern_path);
for (const auto & file : files) {
const auto legacy_path = CommonPaths::locateFileInLegacyPath(file);
if (!legacy_path.isEmpty() && QFile::exists(legacy_path)) {
qCWarning(GPGOL_CLIENT_LOG) << "Copying from legacy path:" << legacy_path;
if (!QFile::copy(legacy_path, QString(modern_path + u"/"_s + file))) {
qCWarning(GPGOL_CLIENT_LOG) << "Copying from legacy path failed";
return false;
}
}
}
return !(CommonPaths::locateFile(u"certificate.pem"_s).isEmpty());
}
void Controller::setHomeDirForJob(QGpgME::Job *job)
{
auto context = QGpgME::Job::context(job);
context->setEngineHomeDirectory(m_tmpdir->path().toUtf8().constData());
}
void Controller::start()
{
if (certificateAlreadyGenerated()) {
emitResult();
return;
}
m_tmpdir = std::make_unique<QTemporaryDir>();
auto keyGenerationJob = QGpgME::smime()->keyGenerationJob();
setHomeDirForJob(keyGenerationJob);
connect(keyGenerationJob, &QGpgME::KeyGenerationJob::result, this, &Controller::slotRootCaCreatedSlot);
keyGenerationJob->start(createRootCaParms().toString());
}
void Controller::slotRootCaCreatedSlot(const GpgME::KeyGenerationResult &result, const QByteArray &pubKeyData, const QString &auditLog)
{
Q_UNUSED(auditLog)
if (result.error().code()) {
setErrorText(result.error().isCanceled() ? i18n("Operation canceled.")
: i18n("Could not create key pair: %1", Formatting::errorAsString(result.error())));
setError(UserDefinedError);
emitResult();
return;
}
auto importJob = QGpgME::smime()->importJob();
setHomeDirForJob(importJob);
connect(importJob, &QGpgME::ImportJob::result, this, &Controller::slotRootCaImportedSlot);
importJob->start(pubKeyData);
}
void Controller::slotRootCaImportedSlot(const GpgME::ImportResult &result, const QString &auditLogAsHtml, const GpgME::Error &auditLogError)
{
Q_UNUSED(auditLogAsHtml)
Q_UNUSED(auditLogError)
Q_EMIT debutOutput(i18nc("Debug message", "Imported root CA"));
if (result.error().code()) {
setErrorText(result.error().isCanceled() ? i18n("Operation canceled.")
: i18n("Could not create key pair: %1", Formatting::errorAsString(result.error())));
setError(UserDefinedError);
emitResult();
return;
}
// Get the keygrip
auto keyListJob = QGpgME::smime()->keyListJob();
setHomeDirForJob(keyListJob);
connect(keyListJob, &QGpgME::KeyListJob::result, this, &Controller::slotKeyGripOptained);
keyListJob->start({u"GnuPG Outlook Add-in Local Root CA"_s}, true);
// Export public key
auto exportJob = QGpgME::smime()->publicKeyExportJob(true);
setHomeDirForJob(exportJob);
const auto imports = result.imports();
const auto fingerprint = imports[0].fingerprint();
m_fingerPrint = Formatter::formatX509Fingerprint(QByteArray(fingerprint));
Q_EMIT debutOutput(i18nc("Debug message, %1 is fingerprint", "Root CA created: %1", m_fingerPrint));
exportJob->start({QString::fromLatin1(fingerprint)});
connect(exportJob, &QGpgME::ExportJob::result, this, [this](const GpgME::Error &error, const QByteArray &keyData) {
if (error.code()) {
setErrorText(error.isCanceled() ? i18n("Operation canceled.") : i18n("Could not export public key: %1", Formatting::errorAsString(error)));
setError(UserDefinedError);
emitResult();
return;
}
m_publicCA = keyData;
checkFinished();
});
// Export private key
auto exportSecretJob = QGpgME::smime()->secretKeyExportJob(true);
setHomeDirForJob(exportSecretJob);
exportSecretJob->start({QString::fromLatin1(fingerprint)});
connect(exportSecretJob, &QGpgME::ExportJob::result, this, [this](const GpgME::Error &error, const QByteArray &keyData) {
if (error.code()) {
setErrorText(error.isCanceled() ? i18n("Operation canceled.") : i18n("Could not export secret key: %1", Formatting::errorAsString(error)));
setError(UserDefinedError);
emitResult();
return;
}
m_secretCA = keyData;
checkFinished();
});
}
void Controller::slotKeyGripOptained(const GpgME::KeyListResult &result,
const std::vector<GpgME::Key> &keys,
const QString &auditLogAsHtml,
const GpgME::Error &auditLogError)
{
Q_EMIT debutOutput(i18nc("Debug message", "Got the key grip of Root CA"));
Q_UNUSED(auditLogAsHtml)
Q_UNUSED(auditLogError)
if (result.error().code()) {
setErrorText(result.error().isCanceled() ? i18n("Operation canceled.") : i18n("Could not get keygrip : %1", Formatting::errorAsString(result.error())));
setError(UserDefinedError);
emitResult();
return;
}
if (keys.size() != 1) {
setErrorText(i18n("More than one root certificate found"));
setError(UserDefinedError);
emitResult();
return;
}
m_ca = keys[0];
auto keyGenerationJob = QGpgME::smime()->keyGenerationJob();
setHomeDirForJob(keyGenerationJob);
connect(keyGenerationJob, &QGpgME::KeyGenerationJob::result, this, &Controller::slotCertCreatedSlot);
keyGenerationJob->start(createTlsCertParms(QLatin1StringView(keys[0].subkey(0).keyGrip())).toString());
}
void Controller::slotCertCreatedSlot(const GpgME::KeyGenerationResult &result, const QByteArray &pubKeyData, const QString &auditLog)
{
Q_EMIT debutOutput(i18nc("Debug message", "TLS certificate created"));
Q_UNUSED(auditLog)
if (result.error().code()) {
setErrorText(result.error().isCanceled() ? i18n("Operation canceled.")
: i18n("Could not create key pair for cert: %1", Formatting::errorAsString(result.error())));
setError(UserDefinedError);
emitResult();
return;
}
auto importJob = QGpgME::smime()->importJob();
setHomeDirForJob(importJob);
connect(importJob, &QGpgME::ImportJob::result, this, &Controller::slotCertImportedSlot);
importJob->start(pubKeyData);
}
void Controller::slotCertImportedSlot(const GpgME::ImportResult &result, const QString &auditLogAsHtml, const GpgME::Error &auditLogError)
{
Q_UNUSED(auditLogAsHtml)
Q_UNUSED(auditLogError)
if (result.error().code()) {
setErrorText(result.error().isCanceled() ? i18n("Operation canceled.") : i18n("Could not import cert: %1", Formatting::errorAsString(result.error())));
setError(UserDefinedError);
emitResult();
return;
}
auto keyListJob = QGpgME::smime()->keyListJob();
setHomeDirForJob(keyListJob);
connect(keyListJob,
&QGpgME::KeyListJob::result,
this,
[this](const GpgME::KeyListResult &result, const std::vector<GpgME::Key> &keys, const QString &auditLogAsHtml, const GpgME::Error &auditLogError) {
Q_UNUSED(result);
Q_UNUSED(auditLogAsHtml);
Q_UNUSED(auditLogError);
m_tls = keys[0];
checkFinished();
});
keyListJob->start({u"GnuPG Outlook Add-in Local Root CA"_s}, true);
// Export public key
auto exportJob = QGpgME::smime()->publicKeyExportJob(true);
setHomeDirForJob(exportJob);
const auto imports = result.imports();
const auto fingerprint = imports[0].fingerprint();
exportJob->start({QString::fromLatin1(fingerprint)});
connect(exportJob, &QGpgME::ExportJob::result, this, [this](const GpgME::Error &error, const QByteArray &keyData) {
if (error.code()) {
setErrorText(error.isCanceled() ? i18n("Operation canceled.") : i18n("Could not export public key: %1", Formatting::errorAsString(error)));
setError(UserDefinedError);
emitResult();
return;
}
m_publicTLS = keyData;
checkFinished();
});
// Export private key
auto exportSecretJob = QGpgME::smime()->secretKeyExportJob(true);
setHomeDirForJob(exportSecretJob);
exportSecretJob->start({QString::fromLatin1(fingerprint)});
connect(exportSecretJob, &QGpgME::ExportJob::result, this, [this](const GpgME::Error &error, const QByteArray &keyData) {
if (error.code()) {
setErrorText(error.isCanceled() ? i18n("Operation canceled.") : i18n("Could not export secret key: %1", Formatting::errorAsString(error)));
setError(UserDefinedError);
emitResult();
return;
}
m_secretTLS = keyData;
checkFinished();
});
}
void Controller::checkFinished()
{
if (!m_secretCA.isEmpty() && !m_publicCA.isEmpty() && !m_publicTLS.isEmpty() && !m_secretTLS.isEmpty() && !m_ca.isNull() && !m_tls.isNull()) {
Q_EMIT generationDone();
}
}
void Controller::install()
{
- // Install for gpgol-client
+ // Install
{
auto certPath = CommonPaths::writeablePath();
Q_EMIT debutOutput(i18nc("Debug message, %1 is a path", "Installing certificate for gpgol-client in %1", certPath));
QDir dir;
if (!dir.mkpath(certPath)) {
Q_EMIT debutOutput(i18nc("Debug message, %1 is a path", "Unable to create the following path: ", certPath));
setError(UserDefinedError);
emitResult();
return;
}
QSaveFile localhostPub(certPath + u"/certificate.pem"_s);
if (localhostPub.open(QIODeviceBase::WriteOnly)) {
localhostPub.write(m_publicTLS);
localhostPub.commit();
} else {
Q_EMIT debutOutput(
i18nc("Debug message. %1 is a filename. %2 is a path", "No permission to write: %1 in %2", localhostPub.fileName(), dir.absolutePath()));
setError(UserDefinedError);
emitResult();
return;
}
QSaveFile rootCaPub(certPath + u"/root-ca.pem"_s);
if (rootCaPub.open(QIODeviceBase::WriteOnly)) {
rootCaPub.write(m_publicCA);
rootCaPub.commit();
} else {
Q_EMIT debutOutput(
i18nc("Debug message. %1 is a filename. %2 is a path", "No permission to write: %1 in %2", rootCaPub.fileName(), dir.absolutePath()));
setError(UserDefinedError);
emitResult();
return;
}
// This one needed for the server, only
{
QSaveFile localhostKey(certPath + u"/certificate-key.pem"_s);
if (localhostKey.open(QIODeviceBase::WriteOnly)) {
localhostKey.write(m_secretTLS);
localhostKey.commit();
} else {
Q_EMIT debutOutput(
i18nc("Debug message. %1 is a filename. %2 is a path.", "No permission to write: %1 in %2", localhostKey.fileName(), dir.absolutePath()));
setError(UserDefinedError);
emitResult();
return;
}
}
}
auto trustStore = TrustStore();
if (!trustStore.install(*this)) {
Q_EMIT debutOutput(i18nc("Debug message", "Installing certificate to browser failed"));
}
emitResult();
}
QString Controller::rootFingerprint() const
{
return m_fingerPrint;
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 26, 7:06 PM (19 h, 11 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
78/bc/89c522cfc8cf2fa87828366e0612

Event Timeline