Commit 77984dc4 authored by Felix Tiede's avatar Felix Tiede Committed by Ingo Klöcker
Browse files

Added WKS publishing support.

Some mail providers offer GnuPG WebKey Service, publishing a user's
public PGP key on the provider's WebKey Directory using a well-defined
string of mails to verify the submitter is actually the owner of the key
and approves of the submission, rectifying some of the shortcomings of
public key servers.

The process is defined in
https://www.ietf.org/archive/id/draft-koch-openpgp-webkey-service-13.html

Justification: Thunderbird's engimail extension already supports
publishing public PGP keys using the same process and KMail is already
capable of handling the authorization request and response mails from
the provider, at which point Kleopatra is no longer involved, Kleopatra
just needs to create and send the initial publication request mail with
the key's user id as the sender address, which is a feature this patch
adds.

Functional description: This patch adds a context menu entry for PGP
keys for which a secret key is in the keyring to "Publish at mail
provider". Calling thi...
parent 14fdc99f
......@@ -7,6 +7,8 @@ Dependencies:
'frameworks/extra-cmake-modules': '@latest'
'pim/kmime' : '@same'
'pim/libkleo' : '@same'
'pim/kidentitymanagement' : '@same'
'pim/kmailtransport' : '@same'
Options:
require-passing-tests-on: [ 'Linux', 'FreeBSD' ]
......@@ -31,6 +31,8 @@ option(DISABLE_KWATCHGNUPG "Don't build the kwatchgnupg tool [default=OFF]" OFF)
# Standalone build. Find / include everything necessary.
set(KF5_MIN_VERSION "5.94.0")
set(KIDENTITYMANAGEMENT_VERSION "5.20.40")
set(KMAILTRANSPORT_VERSION "5.20.40")
set(KMIME_VERSION "5.20.40")
set(LIBKLEO_VERSION "5.20.42")
set(QT_REQUIRED_VERSION "5.15.2")
......@@ -118,6 +120,9 @@ endif()
# Kdepimlibs packages
find_package(KF5Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED)
find_package(KF5Mime ${KMIME_WANT_VERSION} CONFIG REQUIRED)
find_package(KF5IdentityManagement ${KIDENTITYMANAGEMENT_VERSION} CONFIG REQUIRED)
find_package(KF5MailTransport ${KMAILTRANSPORT_VERSION} CONFIG REQUIRED)
find_package(KF5MailTransportAkonadi ${KMAILTRANSPORT_VERSION} CONFIG REQUIRED)
find_package(Qt${QT_MAJOR_VERSION} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Test Network PrintSupport)
......
......@@ -245,6 +245,7 @@ set(_kleopatra_SRCS
commands/exportsecretkeycommand_old.cpp
commands/exportsecretsubkeycommand.cpp
commands/exportopenpgpcertstoservercommand.cpp
commands/exportopenpgpcerttoprovidercommand.cpp
commands/adduseridcommand.cpp
commands/newcertificatecommand.cpp
commands/setinitialpincommand.cpp
......@@ -375,6 +376,9 @@ target_link_libraries(kleopatra_bin
KF5::CoreAddons
KF5::ItemModels
KF5::Crash
KF5::IdentityManagement # Export OpenPGP keys using WKS
KF5::MailTransport
KF5::MailTransportAkonadi
Qt${QT_MAJOR_VERSION}::Network
Qt${QT_MAJOR_VERSION}::PrintSupport # Printing secret keys
${_kleopatra_uiserver_extra_libs}
......
......@@ -42,7 +42,7 @@ ExportOpenPGPCertsToServerCommand::ExportOpenPGPCertsToServerCommand(const Key &
}
ExportOpenPGPCertsToServerCommand::~ExportOpenPGPCertsToServerCommand() {}
ExportOpenPGPCertsToServerCommand::~ExportOpenPGPCertsToServerCommand() = default;
bool ExportOpenPGPCertsToServerCommand::preStartHook(QWidget *parent) const
{
......
/* -*- mode: c++; c-basic-offset:4 -*-
commands/exportopenpgpcerttoprovidercommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2019-2022 Felix Tiede
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "exportopenpgpcerttoprovidercommand.h"
#include "command_p.h"
#include <Libkleo/GnuPG>
#include <gpgme++/key.h>
#include <kidentitymanagement/identity.h>
#include <MailTransport/TransportManager>
#include <MailTransportAkonadi/MessageQueueJob>
#include <KLocalizedString>
#include <KMessageBox>
#include <QString>
using namespace Kleo;
using namespace Kleo::Commands;
using namespace GpgME;
const KIdentityManagement::IdentityManager *ExportOpenPGPCertToProviderCommand::mailIdManager = new KIdentityManagement::IdentityManager(true);
ExportOpenPGPCertToProviderCommand::ExportOpenPGPCertToProviderCommand(QAbstractItemView *v, KeyListController *c)
: GnuPGProcessCommand(v, c)
{
wksMail.open();
wksMail.close();
}
ExportOpenPGPCertToProviderCommand::ExportOpenPGPCertToProviderCommand(const UserID &uid)
: GnuPGProcessCommand(uid.parent()),
uid(uid)
{
wksMail.open();
wksMail.close();
}
ExportOpenPGPCertToProviderCommand::~ExportOpenPGPCertToProviderCommand() {}
bool ExportOpenPGPCertToProviderCommand::preStartHook(QWidget *parent) const
{
QString sender;
if (uid.isNull())
sender = QString::fromLatin1(d->keys().at(0).userID(0).addrSpec().data());
else
sender = QString::fromLatin1(uid.addrSpec().data());
KIdentityManagement::Identity identity = ExportOpenPGPCertToProviderCommand::mailIdManager->identityForAddress(sender);
if (identity.isNull())
identity = ExportOpenPGPCertToProviderCommand::mailIdManager->defaultIdentity();
if (identity.transport().isEmpty()) {
KMessageBox::error(parent,
xi18nc("@warning",
"<para><email>%1</email> has no usable transport for mailing a key available, "
"WKS upload not possible.</para>", sender),
i18nc("@title:window", "OpenPGP Certificate Export"));
return false;
}
return KMessageBox::warningContinueCancel(parent,
xi18nc("@info",
"<para>Not every mail provider supports WKS, so any key being "
"exported this way may fail individually.</para><para>If exported, "
"a confirmation request mail will be sent to <email>%1</email> "
"which needs to be acknowledged with a mail program to complete the "
"export process.</para><para><application>KMail</application> "
"can handle these mails, but not all mail programs can.</para>"
"<para>Once exported, the standard does not (yet) allow for "
"automated removal of a published key.</para>"
"<para>Are you sure you want to continue?</para>", sender),
i18nc("@title:window", "OpenPGP Certificate Export"),
KStandardGuiItem::cont(), KStandardGuiItem::cancel(),
QStringLiteral("warn-export-openpgp-wks-unsupported"))
== KMessageBox::Continue;
}
void ExportOpenPGPCertToProviderCommand::postSuccessHook(QWidget *parent)
{
QString sender;
if (uid.isNull())
sender = QString::fromLatin1(d->keys().at(0).userID(0).addrSpec().data());
else
sender = QString::fromLatin1(uid.addrSpec().data());
KIdentityManagement::Identity identity = ExportOpenPGPCertToProviderCommand::mailIdManager->identityForAddress(sender);
if (identity.isNull())
identity = ExportOpenPGPCertToProviderCommand::mailIdManager->defaultIdentity();
MailTransport::Transport *transport = MailTransport::TransportManager::self()->transportByName(
identity.transport());
if (!transport)
return;
wksMail.open();
KMime::Message *msg = new KMime::Message();
msg->setContent(KMime::CRLFtoLF(wksMail.readAll()));
msg->parse();
wksMail.close();
MailTransport::MessageQueueJob *job = new MailTransport::MessageQueueJob(parent);
job->transportAttribute().setTransportId(transport->id());
job->addressAttribute().setFrom(msg->from()->asUnicodeString());
job->addressAttribute().setTo(msg->to()->displayNames());
job->setMessage(KMime::Message::Ptr(msg));
job->start();
}
QStringList ExportOpenPGPCertToProviderCommand::arguments() const
{
QStringList result;
result << gpgWksClientPath();
result << QStringLiteral("--output") << wksMail.fileName();
result << QStringLiteral("--create");
Q_FOREACH (const Key &key, d->keys()) {
result << QLatin1String(key.primaryFingerprint());
if (!uid.isNull()) {
result << QLatin1String(uid.addrSpec().data());
}
else {
result << QLatin1String(key.userID(0).email());
}
}
return result;
}
QString ExportOpenPGPCertToProviderCommand::errorCaption() const
{
return i18nc("@title:window", "OpenPGP Certificate Export Error");
}
QString ExportOpenPGPCertToProviderCommand::successCaption() const
{
return i18nc("@title:window", "OpenPGP Certificate Export Finished");
}
QString ExportOpenPGPCertToProviderCommand::crashExitMessage(const QStringList &args) const
{
return xi18nc("@info",
"<para>The GPG process that tried to export OpenPGP certificates "
"ended prematurely because of an unexpected error.</para>"
"<para>Please check the output of <icode>%1</icode> for details.</para>", args.join(QLatin1Char(' ')));
}
QString ExportOpenPGPCertToProviderCommand::errorExitMessage(const QStringList &args) const
{
return xi18nc("@info",
"<para>An error occurred while trying to export OpenPGP certificates.</para> "
"<para>The output from <command>%1</command> was: <message>%2</message></para>",
args[0], errorString());
}
QString ExportOpenPGPCertToProviderCommand::successMessage(const QStringList&) const
{
return i18nc("@info", "OpenPGP certificates exported successfully.");
}
/* -*- mode: c++; c-basic-offset:4 -*-
commands/exportopenpgpcertstoservercommand.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2019 Felix Tiede
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <commands/gnupgprocesscommand.h>
#include <kidentitymanagement/identitymanager.h>
#include <QtCore/QTemporaryFile>
#include <gpgme++/key.h>
namespace Kleo
{
namespace Commands
{
class ExportOpenPGPCertToProviderCommand : public GnuPGProcessCommand
{
Q_OBJECT
public:
explicit ExportOpenPGPCertToProviderCommand(QAbstractItemView *view, KeyListController *parent);
explicit ExportOpenPGPCertToProviderCommand(const GpgME::UserID &uid);
~ExportOpenPGPCertToProviderCommand() override;
static Restrictions restrictions()
{
return OnlyOneKey | NeedSecretKey | MustBeOpenPGP;
}
private:
bool preStartHook(QWidget *) const override;
void postSuccessHook(QWidget *) override;
QStringList arguments() const override;
QString errorCaption() const override;
QString successCaption() const override;
QString crashExitMessage(const QStringList &) const override;
QString errorExitMessage(const QStringList &) const override;
QString successMessage(const QStringList &) const override;
GpgME::UserID uid;
QTemporaryFile wksMail;
static const KIdentityManagement::IdentityManager *mailIdManager;
};
}
}
......@@ -6,6 +6,7 @@
SPDX-FileCopyrightText: 2017 Intevation GmbH
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-FileCopyrightText: 2022 Felix Tiede
SPDX-License-Identifier: GPL-2.0-or-later
*/
......@@ -23,6 +24,7 @@
#include "commands/changepassphrasecommand.h"
#include "commands/changeexpirycommand.h"
#include "commands/certifycertificatecommand.h"
#include "commands/exportopenpgpcerttoprovidercommand.h"
#include "commands/refreshcertificatecommand.h"
#include "commands/revokecertificationcommand.h"
#include "commands/revokeuseridcommand.h"
......@@ -95,6 +97,7 @@ public:
void webOfTrustClicked();
void exportClicked();
void addUserID();
void exportUserIDToProvider();
void changePassphrase();
void changeExpiration();
void keysMayHaveChanged();
......@@ -644,6 +647,16 @@ void CertificateDetailsWidget::Private::addUserID()
cmd->start();
}
void CertificateDetailsWidget::Private::exportUserIDToProvider()
{
auto userID = key.userID(0);
auto item = ui.userIDTable->currentItem();
if (item) {
userID = item->data(0, Qt::UserRole).value<GpgME::UserID>();
}
}
namespace
{
void ensureThatKeyDetailsAreLoaded(GpgME::Key &key)
......@@ -745,6 +758,17 @@ void CertificateDetailsWidget::Private::userIDTableContextMenuRequested(const QP
cmd->start();
});
}
if (key.hasSecret() && key.protocol() == GpgME::OpenPGP) {
menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-export-provider")),
i18n("Publish at mail provider ..."),
q, [this, userID]() {
auto cmd = new Kleo::Commands::ExportOpenPGPCertToProviderCommand(userID);
ui.userIDTable->setEnabled(false);
connect(cmd, &Kleo::Commands::ExportOpenPGPCertToProviderCommand::finished,
q, [this]() { ui.userIDTable->setEnabled(true); });
cmd->start();
});
}
connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater);
menu->popup(ui.userIDTable->viewport()->mapToGlobal(p));
}
......
<!DOCTYPE gui >
<gui name="kleopatra" version="508" >
<gui name="kleopatra" version="509" >
<MenuBar>
<Menu name="file">
<text>&amp;File</text>
......@@ -12,6 +12,7 @@
<Action name="file_export_secret_keys"/>
<Action name="file_export_paper_key"/>
<Action name="file_export_certificates_to_server"/>
<Action name="file_export_certificate_to_provider"/>
<Separator/>
<Action name="file_decrypt_verify_files"/>
<Action name="file_sign_encrypt_files"/>
......@@ -132,6 +133,7 @@
<Action name="file_export_secret_keys"/>
<Action name="file_export_paper_key"/>
<Action name="file_export_certificates_to_server"/>
<Action name="file_export_certificate_to_provider"/>
<Separator/>
<Action name="view_certificate_details"/>
</Menu>
......
......@@ -3,6 +3,7 @@
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2019-2022 Felix Tiede
SPDX-License-Identifier: GPL-2.0-or-later
*/
......@@ -21,6 +22,7 @@
#include "kleopatra_debug.h"
#include "commands/exportcertificatecommand.h"
#include "commands/exportopenpgpcertstoservercommand.h"
#include "commands/exportopenpgpcerttoprovidercommand.h"
#ifdef QGPGME_SUPPORTS_SECRET_KEY_EXPORT
# include "commands/exportsecretkeycommand.h"
#else
......@@ -368,6 +370,10 @@ void KeyListController::createActions(KActionCollection *coll)
"file_export_certificates_to_server", i18n("Publish on Server..."), i18n("Publish the selected certificate (public key) on a public keyserver"),
"view-certificate-export-server", nullptr, nullptr, QStringLiteral("Ctrl+Shift+E"), false, true
},
{
"file_export_certificate_to_provider", i18n("Publish at Mailprovider..."), i18n("Publish the selected certificate (public key) at mail provider's Web Key Directory if offered"),
"view-certificate-export-provider", nullptr, nullptr, QString(), false, true
},
{
"file_export_secret_keys", i18n("Backup Secret Keys..."), QString(),
"view-certificate-export-secret", nullptr, nullptr, QString(), false, true
......@@ -520,6 +526,7 @@ void KeyListController::createActions(KActionCollection *coll)
registerActionForCommand<ExportSecretKeyCommand>(coll->action(QStringLiteral("file_export_secret_keys")));
registerActionForCommand<ExportPaperKeyCommand>(coll->action(QStringLiteral("file_export_paper_key")));
registerActionForCommand<ExportOpenPGPCertsToServerCommand>(coll->action(QStringLiteral("file_export_certificates_to_server")));
registerActionForCommand<ExportOpenPGPCertToProviderCommand>(coll->action(QStringLiteral("file_export_certificate_to_provider")));
//---
registerActionForCommand<DecryptVerifyFilesCommand>(coll->action(QStringLiteral("file_decrypt_verify_files")));
registerActionForCommand<SignEncryptFilesCommand>(coll->action(QStringLiteral("file_sign_encrypt_files")));
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment