diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 637876055..69aed69da 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,410 +1,411 @@ # SPDX-FileCopyrightText: none # SPDX-License-Identifier: BSD-3-Clause add_subdirectory(icons) add_subdirectory(mimetypes) include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) if (NOT DISABLE_KWATCHGNUPG) add_subdirectory(kwatchgnupg) endif() add_subdirectory(libkleopatraclient) add_subdirectory(conf) add_subdirectory(kconf_update) if(WIN32) set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_win.cpp) set(_kleopatra_extra_SRCS utils/gnupg-registry.c selftest/registrycheck.cpp utils/windowsprocessdevice.cpp utils/userinfo_win.cpp ) else() set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_unix.cpp) set(_kleopatra_extra_SRCS) endif() set(_kleopatra_uiserver_SRCS uiserver/sessiondata.cpp uiserver/uiserver.cpp ${_kleopatra_extra_uiserver_SRCS} uiserver/assuanserverconnection.cpp uiserver/echocommand.cpp uiserver/decryptverifycommandemailbase.cpp uiserver/decryptverifycommandfilesbase.cpp uiserver/signcommand.cpp uiserver/signencryptfilescommand.cpp uiserver/prepencryptcommand.cpp uiserver/prepsigncommand.cpp uiserver/encryptcommand.cpp uiserver/selectcertificatecommand.cpp uiserver/importfilescommand.cpp uiserver/createchecksumscommand.cpp uiserver/verifychecksumscommand.cpp selftest/uiservercheck.cpp ) if(ASSUAN2_FOUND) include_directories(${ASSUAN2_INCLUDES}) set(_kleopatra_uiserver_extra_libs ${ASSUAN2_LIBRARIES}) else() include_directories(${ASSUAN_INCLUDES}) if(WIN32) set(_kleopatra_uiserver_extra_libs ${ASSUAN_VANILLA_LIBRARIES}) else() set(_kleopatra_uiserver_extra_libs ${ASSUAN_PTHREAD_LIBRARIES}) endif() endif() if(HAVE_GPG_ERR_SOURCE_KLEO) add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO) add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO) else() add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1) add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1) endif() if(KF5IdentityManagement_FOUND AND KF5MailTransport_FOUND AND KF5MailTransportAkonadi_FOUND) set(_kleopatra_mail_libs KF5::IdentityManagement # Export OpenPGP keys using WKS KF5::MailTransport KF5::MailTransportAkonadi ) add_definitions(-DMAILAKONADI_ENABLED) endif() ki18n_wrap_ui(_kleopatra_uiserver_SRCS crypto/gui/signingcertificateselectionwidget.ui) set(_kleopatra_SRCS utils/accessibility.cpp utils/gui-helper.cpp utils/filedialog.cpp utils/kdpipeiodevice.cpp utils/headerview.cpp utils/scrollarea.cpp utils/dragqueen.cpp utils/multivalidator.cpp utils/systemtrayicon.cpp utils/path-helper.cpp utils/input.cpp utils/output.cpp utils/validation.cpp utils/wsastarter.cpp utils/iodevicelogger.cpp utils/log.cpp utils/action_data.cpp utils/types.cpp utils/archivedefinition.cpp utils/auditlog.cpp utils/clipboardmenu.cpp utils/kuniqueservice.cpp utils/tags.cpp utils/writecertassuantransaction.cpp utils/keyparameters.cpp utils/userinfo.cpp utils/keys.cpp selftest/selftest.cpp selftest/enginecheck.cpp selftest/gpgconfcheck.cpp selftest/gpgagentcheck.cpp selftest/libkleopatrarccheck.cpp selftest/compliancecheck.cpp ${_kleopatra_extra_SRCS} view/errorlabel.cpp view/formtextinput.cpp view/htmllabel.cpp view/keylistcontroller.cpp view/keytreeview.cpp view/searchbar.cpp view/smartcardwidget.cpp view/openpgpkeycardwidget.cpp view/padwidget.cpp view/pgpcardwidget.cpp view/pivcardwidget.cpp view/p15cardwidget.cpp view/netkeywidget.cpp view/nullpinwidget.cpp view/tabwidget.cpp view/keycacheoverlay.cpp view/urllabel.cpp view/waitwidget.cpp view/welcomewidget.cpp dialogs/certificateselectiondialog.cpp dialogs/certifywidget.cpp dialogs/expirydialog.cpp dialogs/lookupcertificatesdialog.cpp dialogs/ownertrustdialog.cpp dialogs/selftestdialog.cpp dialogs/certifycertificatedialog.cpp dialogs/revokecertificationwidget.cpp dialogs/revokecertificationdialog.cpp dialogs/adduseriddialog.cpp dialogs/deletecertificatesdialog.cpp dialogs/setinitialpindialog.cpp dialogs/certificatedetailsdialog.cpp dialogs/certificatedetailswidget.cpp dialogs/trustchainwidget.cpp dialogs/weboftrustwidget.cpp dialogs/weboftrustdialog.cpp dialogs/exportdialog.cpp dialogs/subkeyswidget.cpp dialogs/gencardkeydialog.cpp dialogs/updatenotification.cpp dialogs/pivcardapplicationadministrationkeyinputdialog.cpp dialogs/certificatedetailsinputwidget.cpp dialogs/createcsrforcardkeydialog.cpp dialogs/groupdetailsdialog.cpp dialogs/editgroupdialog.cpp dialogs/revokekeydialog.cpp crypto/controller.cpp crypto/certificateresolver.cpp crypto/sender.cpp crypto/recipient.cpp crypto/task.cpp crypto/taskcollection.cpp crypto/decryptverifytask.cpp crypto/decryptverifyemailcontroller.cpp crypto/decryptverifyfilescontroller.cpp crypto/autodecryptverifyfilescontroller.cpp crypto/encryptemailtask.cpp crypto/encryptemailcontroller.cpp crypto/newsignencryptemailcontroller.cpp crypto/signencrypttask.cpp crypto/signencryptfilescontroller.cpp crypto/signemailtask.cpp crypto/signemailcontroller.cpp crypto/createchecksumscontroller.cpp crypto/verifychecksumscontroller.cpp crypto/gui/wizard.cpp crypto/gui/wizardpage.cpp crypto/gui/certificateselectionline.cpp crypto/gui/certificatelineedit.cpp crypto/gui/signingcertificateselectionwidget.cpp crypto/gui/signingcertificateselectiondialog.cpp crypto/gui/resultitemwidget.cpp crypto/gui/resultlistwidget.cpp crypto/gui/resultpage.cpp crypto/gui/newresultpage.cpp crypto/gui/signencryptfileswizard.cpp crypto/gui/signencryptemailconflictdialog.cpp crypto/gui/decryptverifyoperationwidget.cpp crypto/gui/decryptverifyfileswizard.cpp crypto/gui/decryptverifyfilesdialog.cpp crypto/gui/objectspage.cpp crypto/gui/resolverecipientspage.cpp crypto/gui/signerresolvepage.cpp crypto/gui/encryptemailwizard.cpp crypto/gui/signemailwizard.cpp crypto/gui/signencryptwidget.cpp crypto/gui/signencryptwizard.cpp crypto/gui/unknownrecipientwidget.cpp crypto/gui/verifychecksumsdialog.cpp commands/command.cpp commands/gnupgprocesscommand.cpp commands/detailscommand.cpp commands/exportcertificatecommand.cpp commands/exportgroupscommand.cpp commands/importcertificatescommand.cpp commands/importcertificatefromfilecommand.cpp commands/importcertificatefromclipboardcommand.cpp commands/importcertificatefromdatacommand.cpp commands/importcertificatefromkeyservercommand.cpp commands/lookupcertificatescommand.cpp commands/reloadkeyscommand.cpp commands/refreshcertificatecommand.cpp commands/refreshx509certscommand.cpp commands/refreshopenpgpcertscommand.cpp commands/deletecertificatescommand.cpp commands/decryptverifyfilescommand.cpp commands/signencryptfilescommand.cpp commands/signencryptfoldercommand.cpp commands/encryptclipboardcommand.cpp commands/signclipboardcommand.cpp commands/decryptverifyclipboardcommand.cpp commands/clearcrlcachecommand.cpp commands/dumpcrlcachecommand.cpp commands/dumpcertificatecommand.cpp commands/importcrlcommand.cpp commands/changeexpirycommand.cpp commands/changeownertrustcommand.cpp commands/changeroottrustcommand.cpp commands/changepassphrasecommand.cpp commands/certifycertificatecommand.cpp commands/revokecertificationcommand.cpp commands/selftestcommand.cpp commands/exportsecretkeycommand.cpp commands/exportsecretkeycommand_old.cpp commands/exportsecretsubkeycommand.cpp commands/exportopenpgpcertstoservercommand.cpp commands/exportopenpgpcerttoprovidercommand.cpp commands/adduseridcommand.cpp commands/newcertificatecommand.cpp commands/setinitialpincommand.cpp commands/learncardkeyscommand.cpp commands/checksumcreatefilescommand.cpp commands/checksumverifyfilescommand.cpp commands/exportpaperkeycommand.cpp commands/importpaperkeycommand.cpp commands/genrevokecommand.cpp commands/keytocardcommand.cpp commands/cardcommand.cpp commands/pivgeneratecardkeycommand.cpp commands/changepincommand.cpp commands/authenticatepivcardapplicationcommand.cpp commands/setpivcardapplicationadministrationkeycommand.cpp commands/certificatetopivcardcommand.cpp commands/importcertificatefrompivcardcommand.cpp commands/createopenpgpkeyfromcardkeyscommand.cpp commands/createcsrforcardkeycommand.cpp commands/revokekeycommand.cpp commands/revokeuseridcommand.cpp ${_kleopatra_uiserver_files} conf/configuredialog.cpp conf/groupsconfigdialog.cpp conf/groupsconfigpage.cpp conf/groupsconfigwidget.cpp newcertificatewizard/advancedsettingsdialog.cpp newcertificatewizard/chooseprotocolpage.cpp newcertificatewizard/enterdetailspage.cpp newcertificatewizard/keyalgo.cpp + newcertificatewizard/keycreationpage.cpp newcertificatewizard/listwidget.cpp newcertificatewizard/newcertificatewizard.cpp newcertificatewizard/wizardpage.cpp smartcard/readerstatus.cpp smartcard/card.cpp smartcard/openpgpcard.cpp smartcard/netkeycard.cpp smartcard/pivcard.cpp smartcard/p15card.cpp smartcard/keypairinfo.cpp smartcard/utils.cpp smartcard/deviceinfowatcher.cpp accessibility/accessiblerichtextlabel.cpp accessibility/accessiblewidgetfactory.cpp aboutdata.cpp systrayicon.cpp kleopatraapplication.cpp mainwindow.cpp main.cpp kleopatra.qrc ) if(WIN32) configure_file (versioninfo.rc.in versioninfo.rc) set(_kleopatra_SRCS ${CMAKE_CURRENT_BINARY_DIR}/versioninfo.rc ${_kleopatra_SRCS}) endif() set (_kleopatra_SRCS conf/kleopageconfigdialog.cpp ${_kleopatra_SRCS}) ecm_qt_declare_logging_category(_kleopatra_SRCS HEADER kleopatra_debug.h IDENTIFIER KLEOPATRA_LOG CATEGORY_NAME org.kde.pim.kleopatra DESCRIPTION "kleopatra (kleopatra)" OLD_CATEGORY_NAMES log_kleopatra EXPORT KLEOPATRA ) if(KLEO_MODEL_TEST) add_definitions(-DKLEO_MODEL_TEST) set(_kleopatra_SRCS ${_kleopatra_SRCS} models/modeltest.cpp) endif() ki18n_wrap_ui(_kleopatra_SRCS dialogs/ownertrustdialog.ui dialogs/selectchecklevelwidget.ui dialogs/selftestdialog.ui dialogs/setinitialpindialog.ui dialogs/trustchainwidget.ui dialogs/subkeyswidget.ui newcertificatewizard/listwidget.ui newcertificatewizard/enterdetailspage.ui newcertificatewizard/keycreationpage.ui newcertificatewizard/resultpage.ui newcertificatewizard/advancedsettingsdialog.ui ) kconfig_add_kcfg_files(_kleopatra_SRCS kcfg/tooltippreferences.kcfgc kcfg/emailoperationspreferences.kcfgc kcfg/fileoperationspreferences.kcfgc kcfg/smimevalidationpreferences.kcfgc kcfg/tagspreferences.kcfgc kcfg/settings.kcfgc ) file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/*-apps-kleopatra.png") ecm_add_app_icon(_kleopatra_SRCS ICONS ${ICONS_SRCS}) add_executable(kleopatra_bin ${_kleopatra_SRCS} ${_kleopatra_uiserver_SRCS}) # For the ConfigureDialog & KCMs target_link_libraries(kleopatra_bin kcm_kleopatra_static) #if (COMPILE_WITH_UNITY_CMAKE_SUPPORT) # set_target_properties(kleopatra_bin PROPERTIES UNITY_BUILD ON) #endif() set_target_properties(kleopatra_bin PROPERTIES OUTPUT_NAME kleopatra) if (WIN32) set(_kleopatra_platform_libs "secur32") endif () target_link_libraries(kleopatra_bin Gpgmepp QGpgme ${_kleopatra_extra_libs} KF5::Libkleo KF5::Mime KF5::I18n KF5::XmlGui KF5::IconThemes KF5::WindowSystem KF5::CoreAddons KF5::ItemModels KF5::Crash ${_kleopatra_mail_libs} Qt${QT_MAJOR_VERSION}::Network Qt${QT_MAJOR_VERSION}::PrintSupport # Printing secret keys ${_kleopatra_uiserver_extra_libs} ${_kleopatra_dbusaddons_libs} kleopatraclientcore ${_kleopatra_platform_libs} ) install(TARGETS kleopatra_bin ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install( PROGRAMS data/org.kde.kleopatra.desktop data/kleopatra_import.desktop DESTINATION ${KDE_INSTALL_APPDIR} ) install(FILES data/org.kde.kleopatra.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install( PROGRAMS data/kleopatra_signencryptfiles.desktop data/kleopatra_signencryptfolders.desktop data/kleopatra_decryptverifyfiles.desktop data/kleopatra_decryptverifyfolders.desktop DESTINATION ${KDE_INSTALL_DATADIR}/kio/servicemenus ) diff --git a/src/newcertificatewizard/keycreationpage.cpp b/src/newcertificatewizard/keycreationpage.cpp new file mode 100644 index 000000000..5f7bd3b5d --- /dev/null +++ b/src/newcertificatewizard/keycreationpage.cpp @@ -0,0 +1,247 @@ +/* -*- mode: c++; c-basic-offset:4 -*- + newcertificatewizard/keycreationpage.cpp + + This file is part of Kleopatra, the KDE keymanager + SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB + SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik + SPDX-FileContributor: Intevation GmbH + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include + +#include "keycreationpage_p.h" + +#include "keyalgo_p.h" + +#include "kleopatraapplication.h" + +#include "utils/keyparameters.h" + +#include + +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include "kleopatra_debug.h" + +using namespace Kleo; +using namespace Kleo::NewCertificateUi; +using namespace GpgME; + +class KeyCreationPage::EmptyPassphraseProvider: public PassphraseProvider +{ +public: + char *getPassphrase(const char * /*useridHint*/, const char * /*description*/, + bool /*previousWasBad*/, bool &/*canceled*/) Q_DECL_OVERRIDE + { + return gpgrt_strdup (""); + } +}; + +KeyCreationPage::KeyCreationPage(QWidget *p) + : WizardPage{p} + , mEmptyPWProvider{new EmptyPassphraseProvider} + , ui{new Ui_KeyCreationPage} +{ + ui->setupUi(this); +} + +KeyCreationPage::~KeyCreationPage() = default; + +bool KeyCreationPage::isComplete() const +{ + return !job; +} + +void KeyCreationPage::initializePage() +{ + startJob(); +} + +void KeyCreationPage::startJob() +{ + const auto proto = pgp() ? QGpgME::openpgp() : QGpgME::smime(); + if (!proto) { + return; + } + QGpgME::KeyGenerationJob *const j = proto->keyGenerationJob(); + if (!j) { + return; + } + if (!protectedKey() && pgp()) { + auto ctx = QGpgME::Job::context(j); + ctx->setPassphraseProvider(mEmptyPWProvider.get()); + ctx->setPinentryMode(Context::PinentryLoopback); + } + connect(j, &QGpgME::KeyGenerationJob::result, + this, &KeyCreationPage::slotResult); + if (const Error err = j->start(createGnupgKeyParms())) + setField(QStringLiteral("error"), i18n("Could not start key pair creation: %1", + QString::fromLocal8Bit(err.asString()))); + else { + job = j; + } +} + +QStringList KeyCreationPage::keyUsages() const +{ + QStringList usages; + if (signingAllowed()) { + usages << QStringLiteral("sign"); + } + if (encryptionAllowed() && !is_ecdh(subkeyType()) && + !is_dsa(keyType()) && !is_rsa(subkeyType())) { + usages << QStringLiteral("encrypt"); + } + if (authenticationAllowed()) { + usages << QStringLiteral("auth"); + } + if (usages.empty() && certificationAllowed()) { + /* Empty usages cause an error so we need to + * add at least certify if nothing else is selected */ + usages << QStringLiteral("cert"); + } + return usages; +} + +QStringList KeyCreationPage::subkeyUsages() const +{ + QStringList usages; + if (encryptionAllowed() && (is_dsa(keyType()) || is_rsa(subkeyType()) || + is_ecdh(subkeyType()))) { + Q_ASSERT(subkeyType()); + usages << QStringLiteral("encrypt"); + } + return usages; +} + +QString KeyCreationPage::createGnupgKeyParms() const +{ + KeyParameters keyParameters(pgp() ? KeyParameters::OpenPGP : KeyParameters::CMS); + + keyParameters.setKeyType(keyType()); + if (is_ecdsa(keyType()) || is_eddsa(keyType())) { + keyParameters.setKeyCurve(keyCurve()); + } else if (const unsigned int strength = keyStrength()) { + keyParameters.setKeyLength(strength); + } + keyParameters.setKeyUsages(keyUsages()); + + if (subkeyType()) { + keyParameters.setSubkeyType(subkeyType()); + if (is_ecdh(subkeyType())) { + keyParameters.setSubkeyCurve(subkeyCurve()); + } else if (const unsigned int strength = subkeyStrength()) { + keyParameters.setSubkeyLength(strength); + } + keyParameters.setSubkeyUsages(subkeyUsages()); + } + + if (pgp()) { + if (expiryDate().isValid()) { + keyParameters.setExpirationDate(expiryDate()); + } + if (!name().isEmpty()) { + keyParameters.setName(name()); + } + if (!email().isEmpty()) { + keyParameters.setEmail(email()); + } + } else { + keyParameters.setDN(dn()); + keyParameters.setEmail(email()); + const auto addesses{additionalEMailAddresses()}; + for (const QString &email : addesses) { + keyParameters.addEmail(email); + } + const auto dnsN{dnsNames()}; + for (const QString &dns : dnsN) { + keyParameters.addDomainName(dns); + } + const auto urisList{uris()}; + for (const QString &uri : urisList) { + keyParameters.addURI(uri); + } + } + + const QString result = keyParameters.toString(); + qCDebug(KLEOPATRA_LOG) << '\n' << result; + return result; +} + +void KeyCreationPage::slotResult(const GpgME::KeyGenerationResult &result, const QByteArray &request, const QString &auditLog) +{ + Q_UNUSED(auditLog) + if (result.error().code() || (pgp() && !result.fingerprint())) { + setField(QStringLiteral("error"), result.error().isCanceled() + ? i18n("Operation canceled.") + : i18n("Could not create key pair: %1", + QString::fromLocal8Bit(result.error().asString()))); + setField(QStringLiteral("url"), QString()); + setField(QStringLiteral("result"), QString()); + } else if (pgp()) { + setField(QStringLiteral("error"), QString()); + setField(QStringLiteral("url"), QString()); + setField(QStringLiteral("result"), i18n("Key pair created successfully.\n" + "Fingerprint: %1", Formatting::prettyID(result.fingerprint()))); + } else { + QFile file(tmpDir().absoluteFilePath(QStringLiteral("request.p10"))); + + if (!file.open(QIODevice::WriteOnly)) { + setField(QStringLiteral("error"), i18n("Could not write output file %1: %2", + file.fileName(), file.errorString())); + setField(QStringLiteral("url"), QString()); + setField(QStringLiteral("result"), QString()); + } else { + file.write(request); + setField(QStringLiteral("error"), QString()); + setField(QStringLiteral("url"), QUrl::fromLocalFile(file.fileName()).toString()); + setField(QStringLiteral("result"), i18n("Key pair created successfully.")); + } + } + // Ensure that we have the key in the keycache + if (pgp() && !result.error().code() && result.fingerprint()) { + auto ctx = Context::createForProtocol(OpenPGP); + if (ctx) { + // Check is pretty useless something very buggy in that case. + Error e; + const auto key = ctx->key(result.fingerprint(), e, true); + if (!key.isNull()) { + KeyCache::mutableInstance()->insert(key); + } else { + qCDebug(KLEOPATRA_LOG) << "Failed to find newly generated key."; + } + delete ctx; + } + } + setField(QStringLiteral("fingerprint"), result.fingerprint() ? + QString::fromLatin1(result.fingerprint()) : QString()); + job = nullptr; + Q_EMIT completeChanged(); + const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard"); + if (config.readEntry("SkipResultPage", false)) { + if (result.fingerprint()) { + KleopatraApplication::instance()->slotActivateRequested(QStringList() << + QStringLiteral("kleopatra") << QStringLiteral("--query") << QLatin1String(result.fingerprint()), QString()); + QMetaObject::invokeMethod(wizard(), "close", Qt::QueuedConnection); + } else { + QMetaObject::invokeMethod(wizard(), "next", Qt::QueuedConnection); + } + } else { + QMetaObject::invokeMethod(wizard(), "next", Qt::QueuedConnection); + } +} diff --git a/src/newcertificatewizard/keycreationpage_p.h b/src/newcertificatewizard/keycreationpage_p.h new file mode 100644 index 000000000..d8e316fd8 --- /dev/null +++ b/src/newcertificatewizard/keycreationpage_p.h @@ -0,0 +1,54 @@ +/* -*- mode: c++; c-basic-offset:4 -*- + newcertificatewizard/keycreationpage_p.h + + This file is part of Kleopatra, the KDE keymanager + SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB + SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik + SPDX-FileContributor: Intevation GmbH + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include "wizardpage_p.h" + +namespace GpgME +{ +class KeyGenerationResult; +} +namespace QGpgME +{ +class KeyGenerationJob; +} +namespace Kleo::NewCertificateUi +{ +class Ui_KeyCreationPage; +} + +class KeyCreationPage : public Kleo::NewCertificateUi::WizardPage +{ + Q_OBJECT +public: + explicit KeyCreationPage(QWidget *p = nullptr); + ~KeyCreationPage() override; + + bool isComplete() const override; + + void initializePage() override; + +private: + void startJob(); + QStringList keyUsages() const; + QStringList subkeyUsages() const; + QString createGnupgKeyParms() const; + +private Q_SLOTS: + void slotResult(const GpgME::KeyGenerationResult &result, const QByteArray &request, const QString &auditLog); + +private: + class EmptyPassphraseProvider; + std::unique_ptr mEmptyPWProvider; + std::unique_ptr ui; + QPointer job; +}; diff --git a/src/newcertificatewizard/newcertificatewizard.cpp b/src/newcertificatewizard/newcertificatewizard.cpp index 4efb5f0b1..7df8c4958 100644 --- a/src/newcertificatewizard/newcertificatewizard.cpp +++ b/src/newcertificatewizard/newcertificatewizard.cpp @@ -1,693 +1,473 @@ /* -*- mode: c++; c-basic-offset:4 -*- newcertificatewizard/newcertificatewizard.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "newcertificatewizard.h" #include #include "chooseprotocolpage_p.h" #include "enterdetailspage_p.h" #include "keyalgo_p.h" -#include "ui_keycreationpage.h" +#include "keycreationpage_p.h" #include "ui_resultpage.h" #include "wizardpage_p.h" #ifdef QGPGME_SUPPORTS_SECRET_KEY_EXPORT # include "commands/exportsecretkeycommand.h" #else # include "commands/exportsecretkeycommand_old.h" #endif #include "commands/exportopenpgpcertstoservercommand.h" #include "commands/exportcertificatecommand.h" #include "kleopatraapplication.h" #include "utils/validation.h" #include "utils/filedialog.h" #include "utils/keyparameters.h" #include "utils/userinfo.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::NewCertificateUi; using namespace Kleo::Commands; using namespace GpgME; #ifndef QGPGME_SUPPORTS_SECRET_KEY_EXPORT using Kleo::Commands::Compat::ExportSecretKeyCommand; #endif namespace { -class EmptyPassphraseProvider: public PassphraseProvider -{ -public: - char *getPassphrase(const char * /*useridHint*/, const char * /*description*/, - bool /*previousWasBad*/, bool &/*canceled*/) Q_DECL_OVERRIDE - { - return gpgrt_strdup (""); - } -}; - -} - -namespace -{ - -class KeyCreationPage : public WizardPage -{ - Q_OBJECT -public: - explicit KeyCreationPage(QWidget *p = nullptr) - : WizardPage(p), - ui() - { - ui.setupUi(this); - } - - bool isComplete() const override - { - return !job; - } - - void initializePage() override { - startJob(); - } - -private: - void startJob() - { - const auto proto = pgp() ? QGpgME::openpgp() : QGpgME::smime(); - if (!proto) { - return; - } - QGpgME::KeyGenerationJob *const j = proto->keyGenerationJob(); - if (!j) { - return; - } - if (!protectedKey() && pgp()) { - auto ctx = QGpgME::Job::context(j); - ctx->setPassphraseProvider(&mEmptyPWProvider); - ctx->setPinentryMode(Context::PinentryLoopback); - } - connect(j, &QGpgME::KeyGenerationJob::result, - this, &KeyCreationPage::slotResult); - if (const Error err = j->start(createGnupgKeyParms())) - setField(QStringLiteral("error"), i18n("Could not start key pair creation: %1", - QString::fromLocal8Bit(err.asString()))); - else { - job = j; - } - } - QStringList keyUsages() const; - QStringList subkeyUsages() const; - QString createGnupgKeyParms() const; - EmptyPassphraseProvider mEmptyPWProvider; - -private Q_SLOTS: - void slotResult(const GpgME::KeyGenerationResult &result, const QByteArray &request, const QString &auditLog) - { - Q_UNUSED(auditLog) - if (result.error().code() || (pgp() && !result.fingerprint())) { - setField(QStringLiteral("error"), result.error().isCanceled() - ? i18n("Operation canceled.") - : i18n("Could not create key pair: %1", - QString::fromLocal8Bit(result.error().asString()))); - setField(QStringLiteral("url"), QString()); - setField(QStringLiteral("result"), QString()); - } else if (pgp()) { - setField(QStringLiteral("error"), QString()); - setField(QStringLiteral("url"), QString()); - setField(QStringLiteral("result"), i18n("Key pair created successfully.\n" - "Fingerprint: %1", Formatting::prettyID(result.fingerprint()))); - } else { - QFile file(tmpDir().absoluteFilePath(QStringLiteral("request.p10"))); - - if (!file.open(QIODevice::WriteOnly)) { - setField(QStringLiteral("error"), i18n("Could not write output file %1: %2", - file.fileName(), file.errorString())); - setField(QStringLiteral("url"), QString()); - setField(QStringLiteral("result"), QString()); - } else { - file.write(request); - setField(QStringLiteral("error"), QString()); - setField(QStringLiteral("url"), QUrl::fromLocalFile(file.fileName()).toString()); - setField(QStringLiteral("result"), i18n("Key pair created successfully.")); - } - } - // Ensure that we have the key in the keycache - if (pgp() && !result.error().code() && result.fingerprint()) { - auto ctx = Context::createForProtocol(OpenPGP); - if (ctx) { - // Check is pretty useless something very buggy in that case. - Error e; - const auto key = ctx->key(result.fingerprint(), e, true); - if (!key.isNull()) { - KeyCache::mutableInstance()->insert(key); - } else { - qCDebug(KLEOPATRA_LOG) << "Failed to find newly generated key."; - } - delete ctx; - } - } - setField(QStringLiteral("fingerprint"), result.fingerprint() ? - QString::fromLatin1(result.fingerprint()) : QString()); - job = nullptr; - Q_EMIT completeChanged(); - const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard"); - if (config.readEntry("SkipResultPage", false)) { - if (result.fingerprint()) { - KleopatraApplication::instance()->slotActivateRequested(QStringList() << - QStringLiteral("kleopatra") << QStringLiteral("--query") << QLatin1String(result.fingerprint()), QString()); - QMetaObject::invokeMethod(wizard(), "close", Qt::QueuedConnection); - } else { - QMetaObject::invokeMethod(wizard(), "next", Qt::QueuedConnection); - } - } else { - QMetaObject::invokeMethod(wizard(), "next", Qt::QueuedConnection); - } - } - -private: - QPointer job; - Ui_KeyCreationPage ui; -}; - class ResultPage : public WizardPage { Q_OBJECT public: explicit ResultPage(QWidget *p = nullptr) : WizardPage(p), initialized(false), successfullyCreatedSigningCertificate(false), successfullyCreatedEncryptionCertificate(false), ui() { ui.setupUi(this); ui.dragQueen->setPixmap(QIcon::fromTheme(QStringLiteral("kleopatra")).pixmap(64, 64)); registerField(QStringLiteral("error"), ui.errorTB, "plainText"); registerField(QStringLiteral("result"), ui.resultTB, "plainText"); registerField(QStringLiteral("url"), ui.dragQueen, "url"); // hidden field, since QWizard can't deal with non-widget-backed fields... auto le = new QLineEdit(this); le->hide(); registerField(QStringLiteral("fingerprint"), le); } void initializePage() override { const bool error = isError(); if (error) { setTitle(i18nc("@title", "Key Creation Failed")); setSubTitle(i18n("Key pair creation failed. Please find details about the failure below.")); } else { setTitle(i18nc("@title", "Key Pair Successfully Created")); setSubTitle(i18n("Your new key pair was created successfully. Please find details on the result and some suggested next steps below.")); } ui.resultTB ->setVisible(!error); ui.errorTB ->setVisible(error); ui.dragQueen ->setVisible(!error &&!pgp()); ui.restartWizardPB ->setVisible(error); ui.nextStepsGB ->setVisible(!error); ui.saveRequestToFilePB ->setVisible(!pgp()); ui.makeBackupPB ->setVisible(pgp()); ui.createRevocationRequestPB->setVisible(pgp() &&false); // not implemented ui.sendCertificateByEMailPB ->setVisible(pgp()); ui.sendRequestByEMailPB ->setVisible(!pgp()); ui.uploadToKeyserverPB ->setVisible(pgp()); if (!error && !pgp()) { if (signingAllowed() && !encryptionAllowed()) { successfullyCreatedSigningCertificate = true; } else if (!signingAllowed() && encryptionAllowed()) { successfullyCreatedEncryptionCertificate = true; } else { successfullyCreatedEncryptionCertificate = successfullyCreatedSigningCertificate = true; } } ui.createSigningCertificatePB->setVisible(successfullyCreatedEncryptionCertificate &&!successfullyCreatedSigningCertificate); ui.createEncryptionCertificatePB->setVisible(successfullyCreatedSigningCertificate &&!successfullyCreatedEncryptionCertificate); if (error) { wizard()->setOptions(wizard()->options() & ~QWizard::NoCancelButtonOnLastPage); } else { wizard()->setOptions(wizard()->options() | QWizard::NoCancelButtonOnLastPage); } if (!initialized) { connect(ui.restartWizardPB, &QAbstractButton::clicked, this, [this]() { restartAtEnterDetailsPage(); }); } initialized = true; } bool isError() const { return !ui.errorTB->document()->isEmpty(); } bool isComplete() const override { return !isError(); } private: Key key() const { return KeyCache::instance()->findByFingerprint(fingerprint().toLatin1().constData()); } private Q_SLOTS: void slotSaveRequestToFile() { QString fileName = FileDialog::getSaveFileName(this, i18nc("@title", "Save Request"), QStringLiteral("imp"), i18n("PKCS#10 Requests (*.p10)")); if (fileName.isEmpty()) { return; } if (!fileName.endsWith(QLatin1String(".p10"), Qt::CaseInsensitive)) { fileName += QLatin1String(".p10"); } QFile src(QUrl(url()).toLocalFile()); if (!src.copy(fileName)) KMessageBox::error(this, xi18nc("@info", "Could not copy temporary file %1 " "to file %2: %3", src.fileName(), fileName, src.errorString()), i18nc("@title", "Error Saving Request")); else KMessageBox::information(this, xi18nc("@info", "Successfully wrote request to %1." "You should now send the request to the Certification Authority (CA).", fileName), i18nc("@title", "Request Saved")); } void slotSendRequestByEMail() { if (pgp()) { return; } const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard"); invokeMailer(config.readEntry("CAEmailAddress"), // to i18n("Please process this certificate."), // subject i18n("Please process this certificate and inform the sender about the location to fetch the resulting certificate.\n\nThanks,\n"), // body QUrl(url()).toLocalFile()); // attachment } void slotSendCertificateByEMail() { if (!pgp() || exportCertificateCommand) { return; } auto cmd = new ExportCertificateCommand(key()); connect(cmd, &ExportCertificateCommand::finished, this, &ResultPage::slotSendCertificateByEMailContinuation); cmd->setOpenPGPFileName(tmpDir().absoluteFilePath(fingerprint() + QLatin1String(".asc"))); cmd->start(); exportCertificateCommand = cmd; } void slotSendCertificateByEMailContinuation() { if (!exportCertificateCommand) { return; } // ### better error handling? const QString fileName = exportCertificateCommand->openPGPFileName(); qCDebug(KLEOPATRA_LOG) << "fileName" << fileName; exportCertificateCommand = nullptr; if (fileName.isEmpty()) { return; } invokeMailer(QString(), // to i18n("My new public OpenPGP key"), // subject i18n("Please find attached my new public OpenPGP key."), // body fileName); } QByteArray ol_quote(QByteArray str) { #ifdef Q_OS_WIN return "\"\"" + str.replace('"', "\\\"") + "\"\""; //return '"' + str.replace( '"', "\\\"" ) + '"'; #else return str; #endif } void invokeMailer(const QString &to, const QString &subject, const QString &body, const QString &attachment) { qCDebug(KLEOPATRA_LOG) << "to:" << to << "subject:" << subject << "body:" << body << "attachment:" << attachment; // RFC 2368 says body's linebreaks need to be encoded as // "%0D%0A", so normalize body to CRLF: //body.replace(QLatin1Char('\n'), QStringLiteral("\r\n")).remove(QStringLiteral("\r\r")); QUrlQuery query; query.addQueryItem(QStringLiteral("subject"), subject); query.addQueryItem(QStringLiteral("body"), body); if (!attachment.isEmpty()) { query.addQueryItem(QStringLiteral("attach"), attachment); } QUrl url; url.setScheme(QStringLiteral("mailto")); url.setQuery(query); qCDebug(KLEOPATRA_LOG) << "openUrl" << url; QDesktopServices::openUrl(url); KMessageBox::information(this, xi18nc("@info", "Kleopatra tried to send a mail via your default mail client." "Some mail clients are known not to support attachments when invoked this way." "If your mail client does not have an attachment, then drag the Kleopatra icon and drop it on the message compose window of your mail client." "If that does not work, either, save the request to a file, and then attach that."), i18nc("@title", "Sending Mail"), QStringLiteral("newcertificatewizard-mailto-troubles")); } void slotUploadCertificateToDirectoryServer() { if (pgp()) { (new ExportOpenPGPCertsToServerCommand(key()))->start(); } } void slotBackupCertificate() { if (pgp()) { (new ExportSecretKeyCommand(key()))->start(); } } void slotCreateRevocationRequest() { } void slotCreateSigningCertificate() { if (successfullyCreatedSigningCertificate) { return; } toggleSignEncryptAndRestart(); } void slotCreateEncryptionCertificate() { if (successfullyCreatedEncryptionCertificate) { return; } toggleSignEncryptAndRestart(); } private: void toggleSignEncryptAndRestart() { if (!wizard()) { return; } if (KMessageBox::warningContinueCancel( this, i18nc("@info", "This operation will delete the certification request. " "Please make sure that you have sent or saved it before proceeding."), i18nc("@title", "Certification Request About To Be Deleted")) != KMessageBox::Continue) { return; } const bool sign = signingAllowed(); const bool encr = encryptionAllowed(); setField(QStringLiteral("signingAllowed"), !sign); setField(QStringLiteral("encryptionAllowed"), !encr); restartAtEnterDetailsPage(); } private: bool initialized : 1; bool successfullyCreatedSigningCertificate : 1; bool successfullyCreatedEncryptionCertificate : 1; QPointer exportCertificateCommand; Ui_ResultPage ui; }; } class NewCertificateWizard::Private { friend class ::Kleo::NewCertificateWizard; NewCertificateWizard *const q; public: explicit Private(NewCertificateWizard *qq) : q(qq), tmp(QDir::temp().absoluteFilePath(QStringLiteral("kleo-"))), ui(q) { q->setWindowTitle(i18nc("@title:window", "Key Pair Creation Wizard")); } private: GpgME::Protocol initialProtocol = GpgME::UnknownProtocol; QTemporaryDir tmp; struct Ui { ChooseProtocolPage chooseProtocolPage; EnterDetailsPage enterDetailsPage; KeyCreationPage keyCreationPage; ResultPage resultPage; explicit Ui(NewCertificateWizard *q) : chooseProtocolPage(q), enterDetailsPage(q), keyCreationPage(q), resultPage(q) { KDAB_SET_OBJECT_NAME(chooseProtocolPage); KDAB_SET_OBJECT_NAME(enterDetailsPage); KDAB_SET_OBJECT_NAME(keyCreationPage); KDAB_SET_OBJECT_NAME(resultPage); q->setOptions(NoBackButtonOnStartPage|DisabledBackButtonOnLastPage); q->setPage(ChooseProtocolPageId, &chooseProtocolPage); q->setPage(EnterDetailsPageId, &enterDetailsPage); q->setPage(KeyCreationPageId, &keyCreationPage); q->setPage(ResultPageId, &resultPage); q->setStartId(ChooseProtocolPageId); } } ui; }; NewCertificateWizard::NewCertificateWizard(QWidget *p) : QWizard(p), d(new Private(this)) { } NewCertificateWizard::~NewCertificateWizard() {} void NewCertificateWizard::showEvent(QShowEvent *event) { // set WA_KeyboardFocusChange attribute to force visual focus of the // focussed button when the wizard is shown (required for Breeze style // and some other styles) window()->setAttribute(Qt::WA_KeyboardFocusChange); QWizard::showEvent(event); } void NewCertificateWizard::setProtocol(Protocol proto) { d->initialProtocol = proto; d->ui.chooseProtocolPage.setProtocol(proto); setStartId(proto == UnknownProtocol ? ChooseProtocolPageId : EnterDetailsPageId); } Protocol NewCertificateWizard::protocol() const { return d->ui.chooseProtocolPage.protocol(); } void NewCertificateWizard::resetProtocol() { d->ui.chooseProtocolPage.setProtocol(d->initialProtocol); } void NewCertificateWizard::restartAtEnterDetailsPage() { const auto protocol = d->ui.chooseProtocolPage.protocol(); restart(); // resets the protocol to the initial protocol d->ui.chooseProtocolPage.setProtocol(protocol); while (currentId() != NewCertificateWizard::EnterDetailsPageId) { next(); } } QDir NewCertificateWizard::tmpDir() const { return QDir(d->tmp.path()); } -QStringList KeyCreationPage::keyUsages() const -{ - QStringList usages; - if (signingAllowed()) { - usages << QStringLiteral("sign"); - } - if (encryptionAllowed() && !is_ecdh(subkeyType()) && - !is_dsa(keyType()) && !is_rsa(subkeyType())) { - usages << QStringLiteral("encrypt"); - } - if (authenticationAllowed()) { - usages << QStringLiteral("auth"); - } - if (usages.empty() && certificationAllowed()) { - /* Empty usages cause an error so we need to - * add at least certify if nothing else is selected */ - usages << QStringLiteral("cert"); - } - return usages; -} - -QStringList KeyCreationPage::subkeyUsages() const -{ - QStringList usages; - if (encryptionAllowed() && (is_dsa(keyType()) || is_rsa(subkeyType()) || - is_ecdh(subkeyType()))) { - Q_ASSERT(subkeyType()); - usages << QStringLiteral("encrypt"); - } - return usages; -} - namespace { template struct Row { QString key; T value; Row(const QString &k, const T &v) : key(k), value(v) {} }; template QTextStream &operator<<(QTextStream &s, const Row &row) { if (row.key.isEmpty()) { return s; } else { return s << "" << row.key << "" << row.value << ""; } } } -QString KeyCreationPage::createGnupgKeyParms() const -{ - KeyParameters keyParameters(pgp() ? KeyParameters::OpenPGP : KeyParameters::CMS); - - keyParameters.setKeyType(keyType()); - if (is_ecdsa(keyType()) || is_eddsa(keyType())) { - keyParameters.setKeyCurve(keyCurve()); - } else if (const unsigned int strength = keyStrength()) { - keyParameters.setKeyLength(strength); - } - keyParameters.setKeyUsages(keyUsages()); - - if (subkeyType()) { - keyParameters.setSubkeyType(subkeyType()); - if (is_ecdh(subkeyType())) { - keyParameters.setSubkeyCurve(subkeyCurve()); - } else if (const unsigned int strength = subkeyStrength()) { - keyParameters.setSubkeyLength(strength); - } - keyParameters.setSubkeyUsages(subkeyUsages()); - } - - if (pgp()) { - if (expiryDate().isValid()) { - keyParameters.setExpirationDate(expiryDate()); - } - if (!name().isEmpty()) { - keyParameters.setName(name()); - } - if (!email().isEmpty()) { - keyParameters.setEmail(email()); - } - } else { - keyParameters.setDN(dn()); - keyParameters.setEmail(email()); - const auto addesses{additionalEMailAddresses()}; - for (const QString &email : addesses) { - keyParameters.addEmail(email); - } - const auto dnsN{dnsNames()}; - for (const QString &dns : dnsN) { - keyParameters.addDomainName(dns); - } - const auto urisList{uris()}; - for (const QString &uri : urisList) { - keyParameters.addURI(uri); - } - } - - const QString result = keyParameters.toString(); - qCDebug(KLEOPATRA_LOG) << '\n' << result; - return result; -} - #include "newcertificatewizard.moc"