diff --git a/src/commands/changepincommand.cpp b/src/commands/changepincommand.cpp index 0e4fb1b6b..94ceb9ef2 100644 --- a/src/commands/changepincommand.cpp +++ b/src/commands/changepincommand.cpp @@ -1,189 +1,191 @@ /* commands/changepincommand.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "changepincommand.h" #include "cardcommand_p.h" #include "smartcard/openpgpcard.h" #include "smartcard/pivcard.h" #include "smartcard/readerstatus.h" #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::SmartCard; using namespace GpgME; class ChangePinCommand::Private : public CardCommand::Private { friend class ::Kleo::Commands::ChangePinCommand; ChangePinCommand *q_func() const { return static_cast(q); } public: explicit Private(ChangePinCommand *qq, const std::string &serialNumber, const std::string &appName, QWidget *p); ~Private(); void init(); private: void slotResult(const Error &err); private: void changePin(); private: std::string appName; std::string keyRef; ChangePinMode mode = NormalMode; }; ChangePinCommand::Private *ChangePinCommand::d_func() { return static_cast(d.get()); } const ChangePinCommand::Private *ChangePinCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() ChangePinCommand::Private::Private(ChangePinCommand *qq, const std::string &serialNumber, const std::string &appName_, QWidget *p) : CardCommand::Private(qq, serialNumber, p) , appName(appName_) { } ChangePinCommand::Private::~Private() { qCDebug(KLEOPATRA_LOG) << "ChangePinCommand::Private::~Private()"; } ChangePinCommand::ChangePinCommand(const std::string &serialNumber, const std::string &appName, QWidget *p) : CardCommand(new Private(this, serialNumber, appName, p)) { d->init(); } void ChangePinCommand::Private::init() { } ChangePinCommand::~ChangePinCommand() { qCDebug(KLEOPATRA_LOG) << "ChangePinCommand::~ChangePinCommand()"; } void ChangePinCommand::setKeyRef(const std::string &keyRef) { d->keyRef = keyRef; } void ChangePinCommand::setMode(ChangePinMode mode) { d->mode = mode; } void ChangePinCommand::doStart() { qCDebug(KLEOPATRA_LOG) << "ChangePinCommand::doStart()"; d->changePin(); } void ChangePinCommand::doCancel() { } void ChangePinCommand::Private::changePin() { qCDebug(KLEOPATRA_LOG) << "ChangePinCommand::changePin()"; const auto card = SmartCard::ReaderStatus::instance()->getCard(serialNumber(), appName); if (!card) { error(i18n("Failed to find the smartcard with the serial number: %1", QString::fromStdString(serialNumber()))); finished(); return; } QByteArrayList command; command << "SCD PASSWD"; if (mode == ResetMode) { command << "--reset"; + } else if (mode == NullPinMode) { + command << "--nullpin"; } command << QByteArray::fromStdString(keyRef); ReaderStatus::mutableInstance()->startSimpleTransaction(card, command.join(' '), q, "slotResult"); } namespace { static QString errorMessage(const std::string &keyRef, ChangePinCommand::ChangePinMode mode, const QString &errorText) { // see cmd_passwd() in gpg-card.c if (keyRef == PIVCard::pukKeyRef()) { return i18nc("@info", "Changing the PUK failed: %1", errorText); } if (keyRef == OpenPGPCard::resetCodeKeyRef()) { return i18nc("@info", "Unblocking the PIN failed: %1", errorText); } if (keyRef == OpenPGPCard::adminPinKeyRef()) { return i18nc("@info", "Changing the Admin PIN failed: %1", errorText); } if (keyRef == OpenPGPCard::resetCodeKeyRef() && mode == ChangePinCommand::ResetMode) { return i18nc("@info", "Changing the Reset Code failed: %1", errorText); } return i18nc("@info", "Changing the PIN failed: %1", errorText); } static QString successMessage(const std::string &keyRef, ChangePinCommand::ChangePinMode mode) { // see cmd_passwd() in gpg-card.c if (keyRef == PIVCard::pukKeyRef()) { return i18nc("@info", "PUK successfully changed."); } if (keyRef == OpenPGPCard::resetCodeKeyRef()) { return i18nc("@info", "Unblocked and set a new PIN successfully."); } if (keyRef == OpenPGPCard::adminPinKeyRef()) { return i18nc("@info", "Admin PIN changed successfully."); } if (keyRef == OpenPGPCard::resetCodeKeyRef() && mode == ChangePinCommand::ResetMode) { return i18nc("@info", "Reset Code changed successfully."); } return i18nc("@info", "PIN changed successfully."); } } void ChangePinCommand::Private::slotResult(const GpgME::Error& err) { qCDebug(KLEOPATRA_LOG) << "ChangePinCommand::slotResult():" << err.asString() << "(" << err.code() << ")"; if (err) { error(errorMessage(keyRef, mode, QString::fromLatin1(err.asString())), i18nc("@title", "Error")); } else if (!err.isCanceled()) { information(successMessage(keyRef, mode), i18nc("@title", "Success")); ReaderStatus::mutableInstance()->updateStatus(); } finished(); } #undef d #undef q #include "moc_changepincommand.cpp" diff --git a/src/commands/changepincommand.h b/src/commands/changepincommand.h index b31d88349..83090958a 100644 --- a/src/commands/changepincommand.h +++ b/src/commands/changepincommand.h @@ -1,54 +1,55 @@ /* commands/changepincommand.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef __KLEOPATRA_COMMMANDS_CHANGEPINCOMMAND_H__ #define __KLEOPATRA_COMMMANDS_CHANGEPINCOMMAND_H__ #include "cardcommand.h" namespace GpgME { class Error; } namespace Kleo { namespace Commands { class ChangePinCommand : public CardCommand { Q_OBJECT public: enum ChangePinMode { NormalMode = 0, - ResetMode = 1 + ResetMode = 1, + NullPinMode = 2 }; explicit ChangePinCommand(const std::string &serialNumber, const std::string &appName, QWidget *parent); ~ChangePinCommand() override; void setKeyRef(const std::string &keyRef); void setMode(ChangePinMode mode = NormalMode); private: void doStart() override; void doCancel() override; private: class Private; inline Private *d_func(); inline const Private *d_func() const; Q_PRIVATE_SLOT(d_func(), void slotResult(GpgME::Error)) }; } // namespace Commands } // namespace Kleo #endif // __KLEOPATRA_COMMMANDS_CHANGEPINCOMMAND_H__ diff --git a/src/view/netkeywidget.cpp b/src/view/netkeywidget.cpp index 87f473df1..347a35f3b 100644 --- a/src/view/netkeywidget.cpp +++ b/src/view/netkeywidget.cpp @@ -1,306 +1,317 @@ /* view/netkeywidget.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2017 Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include "netkeywidget.h" #include "nullpinwidget.h" #include "keytreeview.h" #include "kleopatraapplication.h" #include "systrayicon.h" #include "kleopatra_debug.h" #include "smartcard/netkeycard.h" #include "smartcard/readerstatus.h" #include "commands/changepincommand.h" #include "commands/createopenpgpkeyfromcardkeyscommand.h" #include "commands/createcsrforcardkeycommand.h" #include "commands/learncardkeyscommand.h" #include "commands/detailscommand.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::SmartCard; using namespace Kleo::Commands; NetKeyWidget::NetKeyWidget(QWidget *parent) : QWidget(parent), mSerialNumberLabel(new QLabel(this)), mVersionLabel(new QLabel(this)), mLearnKeysLabel(new QLabel(this)), mErrorLabel(new QLabel(this)), mNullPinWidget(new NullPinWidget(this)), mLearnKeysBtn(new QPushButton(this)), mChangeNKSPINBtn(new QPushButton(this)), mChangeSigGPINBtn(new QPushButton(this)), mTreeView(new KeyTreeView(this)), mArea(new QScrollArea) { auto vLay = new QVBoxLayout; // Set up the scroll are mArea->setFrameShape(QFrame::NoFrame); mArea->setWidgetResizable(true); auto mAreaWidget = new QWidget; mAreaWidget->setLayout(vLay); mArea->setWidget(mAreaWidget); auto scrollLay = new QVBoxLayout(this); scrollLay->setContentsMargins(0, 0, 0, 0); scrollLay->addWidget(mArea); // Add general widgets mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); vLay->addWidget(mVersionLabel, 0, Qt::AlignLeft); mSerialNumberLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); auto hLay1 = new QHBoxLayout; hLay1->addWidget(new QLabel(i18n("Serial number:"))); hLay1->addWidget(mSerialNumberLabel); hLay1->addStretch(1); vLay->addLayout(hLay1); vLay->addWidget(mNullPinWidget); auto line1 = new QFrame(); line1->setFrameShape(QFrame::HLine); vLay->addWidget(line1); vLay->addWidget(new QLabel(QStringLiteral("%1").arg(i18n("Certificates:"))), 0, Qt::AlignLeft); mLearnKeysLabel = new QLabel(i18n("There are unknown certificates on this card.")); mLearnKeysBtn->setText(i18nc("@action", "Load Certificates")); connect(mLearnKeysBtn, &QPushButton::clicked, this, [this] () { mLearnKeysBtn->setEnabled(false); auto cmd = new LearnCardKeysCommand(GpgME::CMS); cmd->setParentWidget(this); cmd->start(); auto icon = KleopatraApplication::instance()->sysTrayIcon(); if (icon) { icon->setLearningInProgress(true); } connect(cmd, &Command::finished, this, [icon] () { ReaderStatus::mutableInstance()->updateStatus(); icon->setLearningInProgress(false); }); }); auto hLay2 = new QHBoxLayout; hLay2->addWidget(mLearnKeysLabel); hLay2->addWidget(mLearnKeysBtn); hLay2->addStretch(1); vLay->addLayout(hLay2); mErrorLabel->setVisible(false); vLay->addWidget(mErrorLabel); // The certificate view mTreeView->setHierarchicalModel(AbstractKeyListModel::createHierarchicalKeyListModel(mTreeView)); mTreeView->setHierarchicalView(true); connect(mTreeView->view(), &QAbstractItemView::doubleClicked, this, [this] (const QModelIndex &idx) { const auto klm = dynamic_cast (mTreeView->view()->model()); if (!klm) { qCDebug(KLEOPATRA_LOG) << "Unhandled Model: " << mTreeView->view()->model()->metaObject()->className(); return; } auto cmd = new DetailsCommand(klm->key(idx), nullptr); cmd->setParentWidget(this); cmd->start(); }); vLay->addWidget(mTreeView); // The action area auto line2 = new QFrame(); line2->setFrameShape(QFrame::HLine); vLay->addWidget(line2); vLay->addWidget(new QLabel(QStringLiteral("%1").arg(i18n("Actions:"))), 0, Qt::AlignLeft); auto actionLayout = new QHBoxLayout(); if (CreateOpenPGPKeyFromCardKeysCommand::isSupported()) { mKeyForCardKeysButton = new QPushButton(this); mKeyForCardKeysButton->setText(i18nc("@action:button", "Create OpenPGP Key")); mKeyForCardKeysButton->setToolTip(i18nc("@info:tooltip", "Create an OpenPGP key for the keys stored on the card.")); actionLayout->addWidget(mKeyForCardKeysButton); connect(mKeyForCardKeysButton, &QPushButton::clicked, this, &NetKeyWidget::createKeyFromCardKeys); } if (!(engineInfo(GpgME::GpgSMEngine).engineVersion() < "2.2.26")) { // see https://dev.gnupg.org/T5184 mCreateCSRButton = new QPushButton(this); mCreateCSRButton->setText(i18nc("@action:button", "Create CSR")); mCreateCSRButton->setToolTip(i18nc("@info:tooltip", "Create a certificate signing request for a key stored on the card")); mCreateCSRButton->setEnabled(false); actionLayout->addWidget(mCreateCSRButton); connect(mCreateCSRButton, &QPushButton::clicked, this, [this] () { createCSR(); }); } mChangeNKSPINBtn->setText(i18nc("NKS is an identifier for a type of keys on a NetKey card", "Change NKS PIN")); mChangeSigGPINBtn->setText(i18nc("SigG is an identifier for a type of keys on a NetKey card", "Change SigG PIN")); connect(mChangeNKSPINBtn, &QPushButton::clicked, this, [this] () { doChangePin(NetKeyCard::nksPinKeyRef()); }); connect(mChangeSigGPINBtn, &QPushButton::clicked, this, [this] () { doChangePin(NetKeyCard::sigGPinKeyRef()); }); actionLayout->addWidget(mChangeNKSPINBtn); actionLayout->addWidget(mChangeSigGPINBtn); actionLayout->addStretch(1); vLay->addLayout(actionLayout); vLay->addStretch(1); } NetKeyWidget::~NetKeyWidget() { } namespace { std::vector getUsableSigningKeys(const NetKeyCard *netKeyCard) { std::vector keys; for (const auto &key : netKeyCard->keyInfos()) { if (key.canSign() && (((key.keyRef.substr(0, 9) == "NKS-NKS3.") && !netKeyCard->hasNKSNullPin()) || ((key.keyRef.substr(0, 9) == "NKS-SIGG.") && !netKeyCard->hasSigGNullPin()))) { keys.push_back(key); } } return keys; } } void NetKeyWidget::setCard(const NetKeyCard* card) { mSerialNumber = card->serialNumber(); mVersionLabel->setText(i18nc("1 is a Version number", "NetKey v%1 Card", card->appVersion())); mSerialNumberLabel->setText(QString::fromStdString(mSerialNumber)); mNullPinWidget->setSerialNumber(mSerialNumber); /* According to users of NetKey Cards it is fairly uncommon * to use SigG Certificates at all. So it should be optional to set the pins. */ mNullPinWidget->setVisible(card->hasNKSNullPin() /*|| card->hasSigGNullPin()*/); mNullPinWidget->setSigGVisible(false/*card->hasSigGNullPin()*/); mNullPinWidget->setNKSVisible(card->hasNKSNullPin()); mChangeNKSPINBtn->setEnabled(!card->hasNKSNullPin()); if (card->hasSigGNullPin()) { mChangeSigGPINBtn->setText(i18nc("SigG is an identifier for a type of keys on a NetKey card", "Set SigG PIN")); } else { mChangeSigGPINBtn->setText(i18nc("SigG is an identifier for a type of keys on a NetKey card", "Change SigG PIN")); } mLearnKeysBtn->setEnabled(true); mLearnKeysBtn->setVisible(card->canLearnKeys()); mTreeView->setVisible(!card->canLearnKeys()); mLearnKeysLabel->setVisible(card->canLearnKeys()); const auto errMsg = card->errorMsg(); if (!errMsg.isEmpty()) { mErrorLabel->setText(QStringLiteral("%1: %2").arg(i18n("Error"), errMsg)); mErrorLabel->setVisible(true); } else { mErrorLabel->setVisible(false); } const auto keys = card->keys(); mTreeView->setKeys(keys); if (mKeyForCardKeysButton) { mKeyForCardKeysButton->setEnabled(!card->hasNKSNullPin() && card->hasSigningKey() && card->hasEncryptionKey()); } if (mCreateCSRButton) { mCreateCSRButton->setEnabled(!getUsableSigningKeys(card).empty()); } } void NetKeyWidget::doChangePin(const std::string &keyRef) { + const auto netKeyCard = ReaderStatus::instance()->getCard(mSerialNumber); + if (!netKeyCard) { + KMessageBox::error(this, + i18n("Failed to find the smartcard with the serial number: %1", QString::fromStdString(mSerialNumber))); + return; + } + auto cmd = new ChangePinCommand(mSerialNumber, NetKeyCard::AppName, this); this->setEnabled(false); connect(cmd, &ChangePinCommand::finished, this, [this]() { this->setEnabled(true); }); cmd->setKeyRef(keyRef); + if ((keyRef == NetKeyCard::nksPinKeyRef() && netKeyCard->hasNKSNullPin()) + || (keyRef == NetKeyCard::sigGPinKeyRef() && netKeyCard->hasSigGNullPin())) { + cmd->setMode(ChangePinCommand::NullPinMode); + } cmd->start(); } void NetKeyWidget::createKeyFromCardKeys() { auto cmd = new CreateOpenPGPKeyFromCardKeysCommand(mSerialNumber, NetKeyCard::AppName, this); this->setEnabled(false); connect(cmd, &CreateOpenPGPKeyFromCardKeysCommand::finished, this, [this]() { this->setEnabled(true); }); cmd->start(); } namespace { std::string getKeyRef(const std::vector &keys, QWidget *parent) { QStringList options; for (const auto &key : keys) { options << QStringLiteral("%1 - %2").arg(QString::fromStdString(key.keyRef), QString::fromStdString(key.grip)); } bool ok; const QString choice = QInputDialog::getItem(parent, i18n("Select Key"), i18n("Please select the key you want to create a certificate signing request for:"), options, /* current= */ 0, /* editable= */ false, &ok); return ok ? keys[options.indexOf(choice)].keyRef : std::string(); } } void NetKeyWidget::createCSR() { const auto netKeyCard = ReaderStatus::instance()->getCard(mSerialNumber); if (!netKeyCard) { KMessageBox::error(this, i18n("Failed to find the smartcard with the serial number: %1", QString::fromStdString(mSerialNumber))); return; } const auto usableKeys = getUsableSigningKeys(netKeyCard.get()); if (usableKeys.empty()) { KMessageBox::error(this, i18n("Sorry! No keys suitable for creating a certificate signing request found on the smartcard.")); return; } const auto keyRef = getKeyRef(usableKeys, this); if (keyRef.empty()) { return; } auto cmd = new CreateCSRForCardKeyCommand(keyRef, mSerialNumber, NetKeyCard::AppName, this); this->setEnabled(false); connect(cmd, &CreateCSRForCardKeyCommand::finished, this, [this]() { this->setEnabled(true); }); cmd->start(); }