File indexing completed on 2024-06-23 05:13:43
0001 /* commands/importperkeycommand.cpp 0002 0003 This file is part of Kleopatra, the KDE keymanager 0004 SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik 0005 SPDX-FileContributor: Intevation GmbH 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include <config-kleopatra.h> 0011 0012 #include "importpaperkeycommand.h" 0013 0014 #include <Libkleo/Formatting> 0015 #include <Libkleo/GnuPG> 0016 0017 #include <QGpgME/ExportJob> 0018 #include <QGpgME/ImportJob> 0019 #include <QGpgME/Protocol> 0020 #include <gpgme++/importresult.h> 0021 #include <gpgme++/key.h> 0022 0023 #include <Libkleo/KeyCache> 0024 0025 #include <KLocalizedString> 0026 #include <KMessageBox> 0027 0028 #include <QFileDialog> 0029 #include <QTextStream> 0030 0031 #include "command_p.h" 0032 #include "kleopatra_debug.h" 0033 0034 using namespace Kleo; 0035 using namespace Kleo::Commands; 0036 using namespace GpgME; 0037 0038 ImportPaperKeyCommand::ImportPaperKeyCommand(const GpgME::Key &k) 0039 : GnuPGProcessCommand(k) 0040 { 0041 } 0042 0043 QStringList ImportPaperKeyCommand::arguments() const 0044 { 0045 const Key key = d->key(); 0046 return { 0047 paperKeyInstallPath(), 0048 QStringLiteral("--pubring"), 0049 mTmpDir.path() + QStringLiteral("/pubkey.gpg"), 0050 QStringLiteral("--secrets"), 0051 mTmpDir.path() + QStringLiteral("/secrets.txt"), 0052 QStringLiteral("--output"), 0053 mTmpDir.path() + QStringLiteral("/seckey.gpg"), 0054 }; 0055 } 0056 0057 void ImportPaperKeyCommand::exportResult(const GpgME::Error &err, const QByteArray &data) 0058 { 0059 if (err) { 0060 d->error(Formatting::errorAsString(err), errorCaption()); 0061 d->finished(); 0062 return; 0063 } 0064 if (!mTmpDir.isValid()) { 0065 // Should not happen so no i18n 0066 d->error(QStringLiteral("Failed to get temporary directory"), errorCaption()); 0067 qCWarning(KLEOPATRA_LOG) << "Failed to get temporary dir"; 0068 d->finished(); 0069 return; 0070 } 0071 const QString fileName = mTmpDir.path() + QStringLiteral("/pubkey.gpg"); 0072 QFile f(fileName); 0073 if (!f.open(QIODevice::WriteOnly)) { 0074 d->error(QStringLiteral("Failed to create temporary file"), errorCaption()); 0075 qCWarning(KLEOPATRA_LOG) << "Failed to open tmp file"; 0076 d->finished(); 0077 return; 0078 } 0079 f.write(data); 0080 f.close(); 0081 0082 // Copy and sanitize input a bit 0083 QFile input(mFileName); 0084 0085 if (!input.open(QIODevice::ReadOnly)) { 0086 d->error(xi18n("Cannot open <filename>%1</filename> for reading.", mFileName), errorCaption()); 0087 d->finished(); 0088 return; 0089 } 0090 const QString outName = mTmpDir.path() + QStringLiteral("/secrets.txt"); 0091 QFile out(outName); 0092 if (!out.open(QIODevice::WriteOnly)) { 0093 // Should not happen 0094 d->error(QStringLiteral("Failed to create temporary file"), errorCaption()); 0095 qCWarning(KLEOPATRA_LOG) << "Failed to open tmp file for writing"; 0096 d->finished(); 0097 return; 0098 } 0099 0100 QTextStream in(&input); 0101 while (!in.atEnd()) { 0102 // Paperkey is picky, tabs may not be part. Neither may be empty lines. 0103 const QString line = in.readLine().trimmed().replace(QLatin1Char('\t'), QStringLiteral(" ")) + QLatin1Char('\n'); 0104 out.write(line.toUtf8()); 0105 } 0106 input.close(); 0107 out.close(); 0108 0109 GnuPGProcessCommand::doStart(); 0110 } 0111 0112 void ImportPaperKeyCommand::postSuccessHook(QWidget *) 0113 { 0114 qCDebug(KLEOPATRA_LOG) << "Paperkey secrets restore finished successfully."; 0115 0116 QFile secKey(mTmpDir.path() + QStringLiteral("/seckey.gpg")); 0117 if (!secKey.open(QIODevice::ReadOnly)) { 0118 d->error(QStringLiteral("Failed to open temporary secret"), errorCaption()); 0119 qCWarning(KLEOPATRA_LOG) << "Failed to open tmp file"; 0120 Q_EMIT finished(); 0121 return; 0122 } 0123 auto data = secKey.readAll(); 0124 secKey.close(); 0125 0126 auto importjob = QGpgME::openpgp()->importJob(); 0127 auto result = importjob->exec(data); 0128 delete importjob; 0129 if (result.error()) { 0130 d->error(Formatting::errorAsString(result.error()), errorCaption()); 0131 Q_EMIT finished(); 0132 return; 0133 } 0134 if (!result.numSecretKeysImported() || (result.numSecretKeysUnchanged() == result.numSecretKeysImported())) { 0135 d->error(i18n("Failed to restore any secret keys."), errorCaption()); 0136 Q_EMIT finished(); 0137 return; 0138 } 0139 0140 // Refresh the key after success 0141 KeyCache::mutableInstance()->reload(OpenPGP); 0142 Q_EMIT finished(); 0143 d->information(xi18nc("@info", "Successfully restored the secret key parts from <filename>%1</filename>", mFileName)); 0144 return; 0145 } 0146 0147 void ImportPaperKeyCommand::doStart() 0148 { 0149 if (paperKeyInstallPath().isNull()) { 0150 KMessageBox::error(d->parentWidgetOrView(), 0151 xi18nc("@info", 0152 "<para><application>Kleopatra</application> uses " 0153 "<application>PaperKey</application> to import your " 0154 "text backup.</para>" 0155 "<para>Please make sure it is installed.</para>"), 0156 i18nc("@title", "Failed to find PaperKey executable.")); 0157 return; 0158 } 0159 0160 mFileName = QFileDialog::getOpenFileName(d->parentWidgetOrView(), 0161 i18n("Select input file"), 0162 QString(), 0163 QStringLiteral("%1 (*.txt)").arg(i18n("Paper backup")) 0164 #ifdef Q_OS_WIN 0165 /* For whatever reason at least with Qt 5.6.1 the native file dialog crashes in 0166 * my (aheinecke) Windows 10 environment when invoked here. 0167 * In other places it works, with the same arguments as in other places (e.g. import) 0168 * it works. But not here. Maybe it's our (gpg4win) build? But why did it only 0169 * crash here? 0170 * 0171 * It does not crash immediately, the program flow continues for a while before it 0172 * crashes so this is hard to debug. 0173 * 0174 * There are some reports about this 0175 * QTBUG-33119 QTBUG-41416 where different people describe "bugs" but they 0176 * describe them differently also not really reproducible. 0177 * Anyway this works for now and for such an exotic feature its good enough for now. 0178 */ 0179 , 0180 nullptr, 0181 QFileDialog::DontUseNativeDialog 0182 #endif 0183 ); 0184 if (mFileName.isEmpty()) { 0185 d->finished(); 0186 return; 0187 } 0188 0189 auto exportJob = QGpgME::openpgp()->publicKeyExportJob(); 0190 connect(exportJob, &QGpgME::ExportJob::result, this, &ImportPaperKeyCommand::exportResult); 0191 exportJob->start(QStringList() << QLatin1StringView(d->key().primaryFingerprint())); 0192 } 0193 0194 QString ImportPaperKeyCommand::errorCaption() const 0195 { 0196 return i18nc("@title:window", "Error importing secret key"); 0197 } 0198 0199 QString ImportPaperKeyCommand::crashExitMessage(const QStringList &args) const 0200 { 0201 return xi18nc("@info", 0202 "<para>The GPG process that tried to restore the secret key " 0203 "ended prematurely because of an unexpected error.</para>" 0204 "<para>Please check the output of <icode>%1</icode> for details.</para>", 0205 args.join(QLatin1Char(' '))); 0206 } 0207 0208 QString ImportPaperKeyCommand::errorExitMessage(const QStringList &args) const 0209 { 0210 return xi18nc("@info", 0211 "<para>An error occurred while trying to restore the secret key.</para> " 0212 "<para>The output from <command>%1</command> was:</para>" 0213 "<para><message>%2</message></para>", 0214 args[0], 0215 errorString()); 0216 } 0217 0218 QString ImportPaperKeyCommand::successMessage(const QStringList &) const 0219 { 0220 return QString(); 0221 } 0222 0223 #include "moc_importpaperkeycommand.cpp"