File indexing completed on 2024-06-23 05:13:36

0001 /* commands/certificatetopivcardcommand.cpp
0002 
0003     This file is part of Kleopatra, the KDE keymanager
0004     SPDX-FileCopyrightText: 2020 g10 Code GmbH
0005     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include <config-kleopatra.h>
0011 
0012 #include "certificatetopivcardcommand.h"
0013 
0014 #include "cardcommand_p.h"
0015 
0016 #include "commands/authenticatepivcardapplicationcommand.h"
0017 
0018 #include "smartcard/pivcard.h"
0019 #include "smartcard/readerstatus.h"
0020 
0021 #include "utils/writecertassuantransaction.h"
0022 
0023 #include <Libkleo/Compat>
0024 #include <Libkleo/Dn>
0025 #include <Libkleo/Formatting>
0026 #include <Libkleo/KeyCache>
0027 
0028 #include <KLocalizedString>
0029 
0030 #include <qgpgme/dataprovider.h>
0031 
0032 #include <gpgme++/context.h>
0033 
0034 #include <gpg-error.h>
0035 #if GPG_ERROR_VERSION_NUMBER >= 0x12400 // 1.36
0036 #define GPG_ERROR_HAS_NO_AUTH
0037 #endif
0038 
0039 #include "kleopatra_debug.h"
0040 
0041 using namespace Kleo;
0042 using namespace Kleo::Commands;
0043 using namespace Kleo::SmartCard;
0044 using namespace GpgME;
0045 
0046 class CertificateToPIVCardCommand::Private : public CardCommand::Private
0047 {
0048     friend class ::Kleo::Commands::CertificateToPIVCardCommand;
0049     CertificateToPIVCardCommand *q_func() const
0050     {
0051         return static_cast<CertificateToPIVCardCommand *>(q);
0052     }
0053 
0054 public:
0055     explicit Private(CertificateToPIVCardCommand *qq, const std::string &slot, const std::string &serialno);
0056     ~Private() override;
0057 
0058 private:
0059     void start();
0060     void startCertificateToPIVCard();
0061 
0062     void authenticate();
0063     void authenticationFinished();
0064     void authenticationCanceled();
0065 
0066 private:
0067     std::string cardSlot;
0068     Key certificate;
0069     bool hasBeenCanceled = false;
0070 };
0071 
0072 CertificateToPIVCardCommand::Private *CertificateToPIVCardCommand::d_func()
0073 {
0074     return static_cast<Private *>(d.get());
0075 }
0076 const CertificateToPIVCardCommand::Private *CertificateToPIVCardCommand::d_func() const
0077 {
0078     return static_cast<const Private *>(d.get());
0079 }
0080 
0081 #define q q_func()
0082 #define d d_func()
0083 
0084 CertificateToPIVCardCommand::Private::Private(CertificateToPIVCardCommand *qq, const std::string &slot, const std::string &serialno)
0085     : CardCommand::Private(qq, serialno, nullptr)
0086     , cardSlot(slot)
0087 {
0088 }
0089 
0090 CertificateToPIVCardCommand::Private::~Private()
0091 {
0092 }
0093 
0094 namespace
0095 {
0096 static Key getCertificateToWriteToPIVCard(const std::string &cardSlot, const std::shared_ptr<PIVCard> &card)
0097 {
0098     if (!cardSlot.empty()) {
0099         const std::string cardKeygrip = card->keyInfo(cardSlot).grip;
0100         const auto certificate = KeyCache::instance()->findSubkeyByKeyGrip(cardKeygrip).parent();
0101         if (certificate.isNull() || certificate.protocol() != GpgME::CMS) {
0102             return Key();
0103         }
0104         if ((cardSlot == PIVCard::pivAuthenticationKeyRef() && Kleo::keyHasSign(certificate))
0105             || (cardSlot == PIVCard::cardAuthenticationKeyRef() && Kleo::keyHasSign(certificate))
0106             || (cardSlot == PIVCard::digitalSignatureKeyRef() && Kleo::keyHasSign(certificate))
0107             || (cardSlot == PIVCard::keyManagementKeyRef() && Kleo::keyHasEncrypt(certificate))) {
0108             return certificate;
0109         }
0110     }
0111 
0112     return Key();
0113 }
0114 }
0115 
0116 void CertificateToPIVCardCommand::Private::start()
0117 {
0118     qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::Private::start()";
0119 
0120     const auto pivCard = SmartCard::ReaderStatus::instance()->getCard<PIVCard>(serialNumber());
0121     if (!pivCard) {
0122         error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(serialNumber())));
0123         finished();
0124         return;
0125     }
0126 
0127     certificate = getCertificateToWriteToPIVCard(cardSlot, pivCard);
0128     if (certificate.isNull()) {
0129         error(i18n("Sorry! No suitable certificate to write to this card slot was found."));
0130         finished();
0131         return;
0132     }
0133 
0134     const QString certificateInfo = i18nc("X.509 certificate DN (validity, created: date)",
0135                                           "%1 (%2, created: %3)",
0136                                           DN(certificate.userID(0).id()).prettyDN(),
0137                                           Formatting::complianceStringShort(certificate),
0138                                           Formatting::creationDateString(certificate));
0139     const QString message = i18nc("@info %1 name of card slot, %2 serial number of card",
0140                                   "<p>Please confirm that you want to write the following certificate to the %1 slot of card %2:</p>"
0141                                   "<center>%3</center>",
0142                                   PIVCard::keyDisplayName(cardSlot),
0143                                   QString::fromStdString(serialNumber()),
0144                                   certificateInfo);
0145     auto confirmButton = KStandardGuiItem::ok();
0146     confirmButton.setText(i18nc("@action:button", "Write certificate"));
0147     confirmButton.setToolTip(QString());
0148     const auto choice = KMessageBox::questionTwoActions(parentWidgetOrView(),
0149                                                         message,
0150                                                         i18nc("@title:window", "Write certificate to card"),
0151                                                         confirmButton,
0152                                                         KStandardGuiItem::cancel(),
0153                                                         QString(),
0154                                                         KMessageBox::Notify | KMessageBox::WindowModal);
0155     if (choice != KMessageBox::ButtonCode::PrimaryAction) {
0156         finished();
0157         return;
0158     }
0159 
0160     startCertificateToPIVCard();
0161 }
0162 
0163 void CertificateToPIVCardCommand::Private::startCertificateToPIVCard()
0164 {
0165     qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::Private::startCertificateToPIVCard()";
0166 
0167     auto ctx = Context::createForProtocol(GpgME::CMS);
0168     QGpgME::QByteArrayDataProvider dp;
0169     Data data(&dp);
0170     const Error err = ctx->exportPublicKeys(certificate.primaryFingerprint(), data);
0171     if (err) {
0172         error(i18nc("@info", "Exporting the certificate failed: %1", Formatting::errorAsString(err)));
0173         finished();
0174         return;
0175     }
0176     const QByteArray certificateData = dp.data();
0177 
0178     const auto pivCard = SmartCard::ReaderStatus::instance()->getCard<PIVCard>(serialNumber());
0179     if (!pivCard) {
0180         error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(serialNumber())));
0181         finished();
0182         return;
0183     }
0184 
0185     const QByteArray command = QByteArrayLiteral("SCD WRITECERT ") + QByteArray::fromStdString(cardSlot);
0186     auto transaction = std::unique_ptr<AssuanTransaction>(new WriteCertAssuanTransaction(certificateData));
0187     ReaderStatus::mutableInstance()->startTransaction(
0188         pivCard,
0189         command,
0190         q_func(),
0191         [this](const GpgME::Error &err) {
0192             q->certificateToPIVCardDone(err);
0193         },
0194         std::move(transaction));
0195 }
0196 
0197 void CertificateToPIVCardCommand::Private::authenticate()
0198 {
0199     qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::authenticate()";
0200 
0201     auto cmd = new AuthenticatePIVCardApplicationCommand(serialNumber(), parentWidgetOrView());
0202     cmd->setAutoResetCardToOpenPGP(false);
0203     connect(cmd, &AuthenticatePIVCardApplicationCommand::finished, q, [this]() {
0204         authenticationFinished();
0205     });
0206     connect(cmd, &AuthenticatePIVCardApplicationCommand::canceled, q, [this]() {
0207         authenticationCanceled();
0208     });
0209     cmd->start();
0210 }
0211 
0212 void CertificateToPIVCardCommand::Private::authenticationFinished()
0213 {
0214     qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::authenticationFinished()";
0215     if (!hasBeenCanceled) {
0216         startCertificateToPIVCard();
0217     }
0218 }
0219 
0220 void CertificateToPIVCardCommand::Private::authenticationCanceled()
0221 {
0222     qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::authenticationCanceled()";
0223     hasBeenCanceled = true;
0224     canceled();
0225 }
0226 
0227 CertificateToPIVCardCommand::CertificateToPIVCardCommand(const std::string &cardSlot, const std::string &serialno)
0228     : CardCommand(new Private(this, cardSlot, serialno))
0229 {
0230 }
0231 
0232 CertificateToPIVCardCommand::~CertificateToPIVCardCommand()
0233 {
0234     qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::~CertificateToPIVCardCommand()";
0235 }
0236 
0237 void CertificateToPIVCardCommand::certificateToPIVCardDone(const Error &err)
0238 {
0239     qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::certificateToPIVCardDone():" << Formatting::errorAsString(err) << "(" << err.code() << ")";
0240     if (err) {
0241 #ifdef GPG_ERROR_HAS_NO_AUTH
0242         // gpgme 1.13 reports "BAD PIN" instead of "NO AUTH"
0243         if (err.code() == GPG_ERR_NO_AUTH || err.code() == GPG_ERR_BAD_PIN) {
0244             d->authenticate();
0245             return;
0246         }
0247 #endif
0248 
0249         d->error(i18nc("@info", "Writing the certificate to the card failed: %1", Formatting::errorAsString(err)));
0250     } else if (!err.isCanceled()) {
0251         d->success(i18nc("@info", "Writing the certificate to the card succeeded."));
0252         ReaderStatus::mutableInstance()->updateStatus();
0253     }
0254 
0255     d->finished();
0256 }
0257 
0258 void CertificateToPIVCardCommand::doStart()
0259 {
0260     qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::doStart()";
0261 
0262     d->start();
0263 }
0264 
0265 void CertificateToPIVCardCommand::doCancel()
0266 {
0267 }
0268 
0269 #undef q_func
0270 #undef d_func
0271 
0272 #include "moc_certificatetopivcardcommand.cpp"