File indexing completed on 2024-06-16 04:56:16

0001 /*  view/p15cardwiget.cpp
0002 
0003     This file is part of Kleopatra, the KDE keymanager
0004     SPDX-FileCopyrightText: 2021 g10 Code GmbH
0005     SPDX-FileContributor: Andre Heinecke <aheinecke@g10code.com>
0006     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "p15cardwidget.h"
0012 
0013 #include "openpgpkeycardwidget.h"
0014 
0015 #include "settings.h"
0016 
0017 #include "smartcard/openpgpcard.h"
0018 #include "smartcard/p15card.h"
0019 #include "smartcard/readerstatus.h"
0020 
0021 #include "commands/learncardkeyscommand.h"
0022 
0023 #include <QGridLayout>
0024 #include <QLabel>
0025 #include <QPushButton>
0026 #include <QScrollArea>
0027 #include <QStringList>
0028 #include <QVBoxLayout>
0029 
0030 #include <KLocalizedString>
0031 #include <KSeparator>
0032 
0033 #include <Libkleo/Compat>
0034 #include <Libkleo/Formatting>
0035 #include <Libkleo/GnuPG>
0036 #include <Libkleo/KeyCache>
0037 
0038 #include <QGpgME/CryptoConfig>
0039 #include <QGpgME/ImportFromKeyserverJob>
0040 #include <QGpgME/KeyListJob>
0041 #include <QGpgME/Protocol>
0042 #include <gpgme++/importresult.h>
0043 #include <gpgme++/keylistresult.h>
0044 
0045 #include "kleopatra_debug.h"
0046 
0047 using namespace Kleo;
0048 using namespace Kleo::SmartCard;
0049 using namespace Kleo::Commands;
0050 
0051 P15CardWidget::P15CardWidget(QWidget *parent)
0052     : QWidget{parent}
0053     , mVersionLabel{new QLabel{this}}
0054     , mSerialNumber{new QLabel{this}}
0055     , mStatusLabel{new QLabel{this}}
0056     , mOpenPGPKeysSection{new QWidget{this}}
0057     , mOpenPGPKeysWidget{new OpenPGPKeyCardWidget{this}}
0058 {
0059     // Set up the scroll area
0060     auto myLayout = new QVBoxLayout(this);
0061     myLayout->setContentsMargins(0, 0, 0, 0);
0062 
0063     auto area = new QScrollArea;
0064     area->setFrameShape(QFrame::NoFrame);
0065     area->setWidgetResizable(true);
0066     myLayout->addWidget(area);
0067 
0068     auto areaWidget = new QWidget;
0069     area->setWidget(areaWidget);
0070 
0071     auto areaVLay = new QVBoxLayout(areaWidget);
0072 
0073     auto cardInfoGrid = new QGridLayout;
0074     {
0075         int row = 0;
0076 
0077         // Version and Serialnumber
0078         cardInfoGrid->addWidget(mVersionLabel, row++, 0, 1, 2);
0079         mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::TextSelectableByKeyboard);
0080 
0081         cardInfoGrid->addWidget(new QLabel(i18n("Serial number:")), row, 0);
0082         cardInfoGrid->addWidget(mSerialNumber, row++, 1);
0083         mSerialNumber->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::TextSelectableByKeyboard);
0084 
0085         cardInfoGrid->setColumnStretch(cardInfoGrid->columnCount(), 1);
0086     }
0087     areaVLay->addLayout(cardInfoGrid);
0088     areaVLay->addWidget(mStatusLabel);
0089     mStatusLabel->setVisible(false);
0090 
0091     areaVLay->addWidget(new KSeparator(Qt::Horizontal));
0092 
0093     {
0094         auto l = new QVBoxLayout{mOpenPGPKeysSection};
0095         l->setContentsMargins(0, 0, 0, 0);
0096         l->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("OpenPGP keys:"))));
0097         mOpenPGPKeysWidget->setAllowedActions(OpenPGPKeyCardWidget::NoAction);
0098         l->addWidget(mOpenPGPKeysWidget);
0099         l->addWidget(new KSeparator(Qt::Horizontal));
0100     }
0101     mOpenPGPKeysSection->setVisible(false);
0102     areaVLay->addWidget(mOpenPGPKeysSection);
0103 
0104     areaVLay->addStretch(1);
0105 }
0106 
0107 P15CardWidget::~P15CardWidget() = default;
0108 
0109 void P15CardWidget::searchPGPFpr(const std::string &fpr)
0110 {
0111     /* Only do auto import from LDAP */
0112     auto conf = QGpgME::cryptoConfig();
0113     Q_ASSERT(conf);
0114     if (!Settings().alwaysSearchCardOnKeyserver() && !Kleo::keyserver().startsWith(QLatin1StringView{"ldap"})) {
0115         return;
0116     }
0117     mStatusLabel->setText(i18n("Searching in directory service..."));
0118     mStatusLabel->setVisible(true);
0119     qCDebug(KLEOPATRA_LOG) << "Looking for:" << fpr.c_str() << "on ldap server";
0120     QGpgME::KeyListJob *job = QGpgME::openpgp()->keyListJob(true);
0121     connect(KeyCache::instance().get(), &KeyCache::keysMayHaveChanged, this, [this, fpr]() {
0122         qCDebug(KLEOPATRA_LOG) << "Updating key info after changes";
0123         ReaderStatus::mutableInstance()->updateStatus();
0124         mOpenPGPKeysWidget->update(nullptr);
0125     });
0126     connect(job, &QGpgME::KeyListJob::result, job, [this](GpgME::KeyListResult, std::vector<GpgME::Key> keys, QString, GpgME::Error) {
0127         if (keys.size() == 1) {
0128             auto importJob = QGpgME::openpgp()->importFromKeyserverJob();
0129             qCDebug(KLEOPATRA_LOG) << "Importing: " << keys[0].primaryFingerprint();
0130             connect(importJob, &QGpgME::ImportFromKeyserverJob::result, importJob, [this](GpgME::ImportResult, QString, GpgME::Error) {
0131                 qCDebug(KLEOPATRA_LOG) << "import job done";
0132                 mStatusLabel->setText(i18n("Automatic import finished."));
0133             });
0134             importJob->start(keys);
0135         } else if (keys.size() > 1) {
0136             qCDebug(KLEOPATRA_LOG) << "Multiple keys found on server";
0137             mStatusLabel->setText(i18n("Error multiple keys found on server."));
0138         } else {
0139             qCDebug(KLEOPATRA_LOG) << "No key found";
0140             mStatusLabel->setText(i18n("Key not found in directory service."));
0141         }
0142     });
0143     job->start(QStringList() << QString::fromStdString(fpr));
0144 }
0145 
0146 void P15CardWidget::setCard(const P15Card *card)
0147 {
0148     mCardSerialNumber = card->serialNumber();
0149     mVersionLabel->setText(i18nc("%1 is a smartcard manufacturer", "%1 PKCS#15 card", QString::fromStdString(card->manufacturer())));
0150     mSerialNumber->setText(card->displaySerialNumber());
0151     mSerialNumber->setToolTip(QString::fromStdString(card->serialNumber()));
0152 
0153     const auto sigInfo = card->keyInfo(card->signingKeyRef());
0154     if (!sigInfo.grip.empty()) {
0155         const auto key = KeyCache::instance()->findSubkeyByKeyGrip(sigInfo.grip, GpgME::OpenPGP).parent();
0156         if (key.isNull()) {
0157             qCDebug(KLEOPATRA_LOG) << "Failed to find key for grip:" << sigInfo.grip.c_str();
0158             const auto pgpSigFpr = card->keyFingerprint(OpenPGPCard::pgpSigKeyRef());
0159             if (!pgpSigFpr.empty()) {
0160                 qCDebug(KLEOPATRA_LOG) << "Should be pgp key:" << pgpSigFpr.c_str();
0161                 searchPGPFpr(pgpSigFpr);
0162             }
0163         } else {
0164             mStatusLabel->setVisible(false);
0165         }
0166     }
0167 
0168     const bool cardHasOpenPGPKeys = (!card->keyFingerprint(OpenPGPCard::pgpSigKeyRef()).empty() //
0169                                      || !card->keyFingerprint(OpenPGPCard::pgpEncKeyRef()).empty());
0170     mOpenPGPKeysSection->setVisible(cardHasOpenPGPKeys);
0171     if (cardHasOpenPGPKeys) {
0172         mOpenPGPKeysWidget->update(card);
0173     }
0174 
0175     /* Check if additional keys could be available */
0176     if (!Settings().autoLoadP15Certs()) {
0177         return;
0178     }
0179     for (const auto &info : card->keyInfos()) {
0180         const auto key = KeyCache::instance()->findSubkeyByKeyGrip(info.grip);
0181         if (key.isNull()) {
0182             auto cmd = new LearnCardKeysCommand(GpgME::CMS);
0183             cmd->setParentWidget(this);
0184             cmd->setShowsOutputWindow(false);
0185             qCDebug(KLEOPATRA_LOG) << "Did not find:" << info.grip.c_str() << "Starting gpgsm --learn.";
0186             cmd->start();
0187             connect(cmd, &Command::finished, this, []() {
0188                 qCDebug(KLEOPATRA_LOG) << "Learn command finished.";
0189             });
0190             return;
0191         }
0192     }
0193     qCDebug(KLEOPATRA_LOG) << "All certificates from card cached - Not learning.";
0194 }
0195 
0196 #include "moc_p15cardwidget.cpp"