From 145f54f18ce2100320a51e422c64af4240de8b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20=C5=A0tikonas?= Date: Sat, 14 Apr 2018 23:55:05 +0300 Subject: [PATCH] Move KAuth helper setup code to ExternalCommand class. Delay helper startup until it is actually required. --- src/backend/corebackendmanager.cpp | 83 ++---------------------------- src/backend/corebackendmanager.h | 24 +-------- src/util/externalcommand.cpp | 79 +++++++++++++++++++++++++--- src/util/externalcommand.h | 22 ++++++-- test/helpers.cpp | 3 +- test/testdevicescanner.cpp | 13 +++-- test/testlist.cpp | 12 +++-- 7 files changed, 114 insertions(+), 122 deletions(-) diff --git a/src/backend/corebackendmanager.cpp b/src/backend/corebackendmanager.cpp index 23c4060..bb1fdd2 100644 --- a/src/backend/corebackendmanager.cpp +++ b/src/backend/corebackendmanager.cpp @@ -21,16 +21,10 @@ #include "backend/corebackend.h" #include -#include #include -#include -#include #include #include -#include - -#include #include #include #include @@ -38,18 +32,16 @@ struct CoreBackendManagerPrivate { - KAuth::ExecuteJob *m_job; CoreBackend *m_Backend; - - QCA::Initializer init; - QCA::PrivateKey privateKey; - unsigned int counter = 0; }; CoreBackendManager::CoreBackendManager() : d(std::make_unique()) { - startExternalCommandHelper(); +} + +CoreBackendManager::~CoreBackendManager() +{ } CoreBackendManager* CoreBackendManager::self() @@ -78,63 +70,6 @@ QVector CoreBackendManager::list() const return KPluginLoader::findPlugins(QString(), filter); } -bool CoreBackendManager::startExternalCommandHelper() -{ - // Generate RSA key pair for signing external command requests - if (!QCA::isSupported("pkey") || !QCA::PKey::supportedIOTypes().contains(QCA::PKey::RSA)) { - qCritical() << xi18n("QCA does not support RSA."); - return false; - } - - d->privateKey = QCA::KeyGenerator().createRSA(4096); - if(d->privateKey.isNull()) { - qCritical() << xi18n("Failed to make private RSA key."); - return false; - } - - if (!d->privateKey.canSign()) { - qCritical() << xi18n("Generated key cannot be used for signatures."); - return false; - } - - QCA::PublicKey pubkey = d->privateKey.toPublicKey(); - - KAuth::Action action = KAuth::Action(QStringLiteral("org.kde.kpmcore.externalcommand.init")); - action.setHelperId(QStringLiteral("org.kde.kpmcore.externalcommand")); - action.setTimeout(10 * 24 * 3600 * 1000); // 10 days - QVariantMap arguments; - arguments.insert(QStringLiteral("pubkey"), pubkey.toDER()); - action.setArguments(arguments); - d->m_job = action.execute(); - job()->start(); - - // Wait until ExternalCommand Helper is ready (helper sends newData signal just before it enters event loop) - QEventLoop loop; - auto exitLoop = [&] () { loop.exit(); }; - auto conn = QObject::connect(job(), &KAuth::ExecuteJob::newData, exitLoop); - QObject::connect(job(), &KJob::finished, [=] () { if(d->m_job->error()) exitLoop(); } ); - loop.exec(); - QObject::disconnect(conn); - - return true; -} - -void CoreBackendManager::stopExternalCommandHelper() -{ - QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QDBusConnection::systemBus()); - if (iface.isValid()) { - QByteArray request; - request.setNum(++CoreBackendManager::self()->counter()); - QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); - iface.call(QStringLiteral("exit"), d->privateKey.signMessage(hash, QCA::EMSA3_Raw)); - } -} - -KAuth::ExecuteJob* CoreBackendManager::job() -{ - return d->m_job; -} - bool CoreBackendManager::load(const QString& name) { if (backend()) @@ -168,13 +103,3 @@ bool CoreBackendManager::load(const QString& name) void CoreBackendManager::unload() { } - -QCA::PrivateKey CoreBackendManager::privateKey() -{ - return d->privateKey; -} - -unsigned int& CoreBackendManager::counter() -{ - return d->counter; -} diff --git a/src/backend/corebackendmanager.h b/src/backend/corebackendmanager.h index 07c56eb..45ceaae 100644 --- a/src/backend/corebackendmanager.h +++ b/src/backend/corebackendmanager.h @@ -17,22 +17,18 @@ *************************************************************************/ #ifndef KPMCORE_COREBACKENDMANAGER_H - #define KPMCORE_COREBACKENDMANAGER_H #include "util/libpartitionmanagerexport.h" #include -#include #include class QString; class QStringList; class KPluginMetaData; class CoreBackend; -namespace KAuth { class ExecuteJob; } -namespace QCA { class PrivateKey; } struct CoreBackendManagerPrivate; /** @@ -44,8 +40,8 @@ struct CoreBackendManagerPrivate; */ class LIBKPMCORE_EXPORT CoreBackendManager { -private: CoreBackendManager(); + ~CoreBackendManager(); public: /** @@ -82,24 +78,6 @@ public: */ CoreBackend* backend(); - /** - * @return a pointer to the KAuth job - */ - KAuth::ExecuteJob* job(); - - /** - * stop ExternalCommand Helper - */ - void stopExternalCommandHelper(); - - /**< @return RSA private key used for signing External Command requests. */ - QCA::PrivateKey privateKey(); - - unsigned int& counter(); - -private: - bool startExternalCommandHelper(); - private: std::unique_ptr d; }; diff --git a/src/util/externalcommand.cpp b/src/util/externalcommand.cpp index a560b30..c62cc15 100644 --- a/src/util/externalcommand.cpp +++ b/src/util/externalcommand.cpp @@ -53,6 +53,12 @@ struct ExternalCommandPrivate QByteArray m_Input; }; +unsigned int ExternalCommand::counter = 0; +KAuth::ExecuteJob* ExternalCommand::m_job; +QCA::PrivateKey* ExternalCommand::privateKey; +QCA::Initializer* ExternalCommand::init; +bool ExternalCommand::helperStarted = false; + /** Creates a new ExternalCommand instance without Report. @param cmd the command to run @param args the arguments to pass to the command @@ -66,6 +72,9 @@ ExternalCommand::ExternalCommand(const QString& cmd, const QStringList& args, co d->m_ExitCode = -1; d->m_Output = QByteArray(); + if (!helperStarted) + startHelper(); + setup(processChannelMode); } @@ -130,7 +139,7 @@ bool ExternalCommand::start(int timeout) if (iface.isValid()) { QByteArray request; - request.setNum(++CoreBackendManager::self()->counter()); + request.setNum(++counter); request.append(cmd.toUtf8()); for (const auto &argument : qAsConst(d->m_Args)) request.append(argument.toUtf8()); @@ -139,7 +148,7 @@ bool ExternalCommand::start(int timeout) QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); QDBusPendingCall pcall = iface.asyncCall(QStringLiteral("start"), - CoreBackendManager::self()->privateKey().signMessage(hash, QCA::EMSA3_Raw), + privateKey->signMessage(hash, QCA::EMSA3_Raw), cmd, args(), d->m_Input); @@ -180,15 +189,15 @@ bool ExternalCommand::copyBlocks(CopySource& source, CopyTarget& target) } // TODO KF6:Use new signal-slot syntax - connect(CoreBackendManager::self()->job(), SIGNAL(percent(KJob*, unsigned long)), this, SLOT(emitProgress(KJob*, unsigned long))); - connect(CoreBackendManager::self()->job(), &KAuth::ExecuteJob::newData, this, &ExternalCommand::emitReport); + connect(m_job, SIGNAL(percent(KJob*, unsigned long)), this, SLOT(emitProgress(KJob*, unsigned long))); + connect(m_job, &KAuth::ExecuteJob::newData, this, &ExternalCommand::emitReport); QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QDBusConnection::systemBus()); iface.setTimeout(10 * 24 * 3600 * 1000); // 10 days if (iface.isValid()) { QByteArray request; - request.setNum(++CoreBackendManager::self()->counter()); + request.setNum(++counter); request.append(source.path().toUtf8()); request.append(QByteArray::number(source.firstByte())); request.append(QByteArray::number(source.length())); @@ -200,7 +209,7 @@ bool ExternalCommand::copyBlocks(CopySource& source, CopyTarget& target) // Use asynchronous DBus calls, so that we can process reports and progress QDBusPendingCall pcall= iface.asyncCall(QStringLiteral("copyblocks"), - CoreBackendManager::self()->privateKey().signMessage(hash, QCA::EMSA3_Raw), + privateKey->signMessage(hash, QCA::EMSA3_Raw), source.path(), source.firstByte(), source.length(), target.path(), target.firstByte(), blockSize); @@ -332,3 +341,61 @@ void ExternalCommand::setExitCode(int i) { d->m_ExitCode = i; } + +bool ExternalCommand::startHelper() +{ + init = new QCA::Initializer; + // Generate RSA key pair for signing external command requests + if (!QCA::isSupported("pkey") || !QCA::PKey::supportedIOTypes().contains(QCA::PKey::RSA)) { + qCritical() << xi18n("QCA does not support RSA."); + return false; + } + + privateKey = new QCA::PrivateKey; + *privateKey = QCA::KeyGenerator().createRSA(4096); + if(privateKey->isNull()) { + qCritical() << xi18n("Failed to make private RSA key."); + return false; + } + + if (!privateKey->canSign()) { + qCritical() << xi18n("Generated key cannot be used for signatures."); + return false; + } + + QCA::PublicKey pubkey = privateKey->toPublicKey(); + + KAuth::Action action = KAuth::Action(QStringLiteral("org.kde.kpmcore.externalcommand.init")); + action.setHelperId(QStringLiteral("org.kde.kpmcore.externalcommand")); + action.setTimeout(10 * 24 * 3600 * 1000); // 10 days + QVariantMap arguments; + arguments.insert(QStringLiteral("pubkey"), pubkey.toDER()); + action.setArguments(arguments); + m_job = action.execute(); + m_job->start(); + + // Wait until ExternalCommand Helper is ready (helper sends newData signal just before it enters event loop) + QEventLoop loop; + auto exitLoop = [&] () { loop.exit(); }; + auto conn = QObject::connect(m_job, &KAuth::ExecuteJob::newData, exitLoop); + QObject::connect(m_job, &KJob::finished, [=] () { if(m_job->error()) exitLoop(); } ); + loop.exec(); + QObject::disconnect(conn); + + helperStarted = true; + return true; +} + +void ExternalCommand::stopHelper() +{ + QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QDBusConnection::systemBus()); + if (iface.isValid()) { + QByteArray request; + request.setNum(++counter); + QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); + iface.call(QStringLiteral("exit"), privateKey->signMessage(hash, QCA::EMSA3_Raw)); + } + + delete privateKey; + delete init; +} diff --git a/src/util/externalcommand.h b/src/util/externalcommand.h index f045f4e..c56d84e 100644 --- a/src/util/externalcommand.h +++ b/src/util/externalcommand.h @@ -20,8 +20,6 @@ #define KPMCORE_EXTERNALCOMMAND_H #include "util/libpartitionmanagerexport.h" -#include "core/copysourcedevice.h" -#include "core/copytargetfile.h" #include #include @@ -33,7 +31,11 @@ #include class KJob; +namespace KAuth { class ExecuteJob; } +namespace QCA { class PrivateKey; class Initializer; } class Report; +class CopySource; +class CopyTarget; struct ExternalCommandPrivate; /** An external command. @@ -90,6 +92,13 @@ public: void emitReport(const QVariantMap& report) { emit reportSignal(report); } + // KAuth + /**< start ExternalCommand Helper */ + static bool startHelper(); + + /**< stop ExternalCommand Helper */ + static void stopHelper(); + Q_SIGNALS: void progress(int); void reportSignal(const QVariantMap&); @@ -97,7 +106,7 @@ Q_SIGNALS: public Q_SLOTS: void emitProgress(KJob*, unsigned long percent) { emit progress(percent); }; -protected: +private: void setExitCode(int i); void setup(const QProcess::ProcessChannelMode processChannelMode); @@ -106,6 +115,13 @@ protected: private: std::unique_ptr d; + + // KAuth stuff + static unsigned int counter; + static KAuth::ExecuteJob *m_job; + static QCA::Initializer *init; + static QCA::PrivateKey *privateKey; + static bool helperStarted; }; #endif diff --git a/test/helpers.cpp b/test/helpers.cpp index b89bdd8..9ec2703 100644 --- a/test/helpers.cpp +++ b/test/helpers.cpp @@ -20,6 +20,7 @@ #include "helpers.h" #include "backend/corebackendmanager.h" +#include "util/externalcommand.h" #include #include @@ -59,5 +60,5 @@ KPMCoreInitializer::KPMCoreInitializer( const char* backend ) : KPMCoreInitializ KPMCoreInitializer::~KPMCoreInitializer() { - CoreBackendManager::self()->stopExternalCommandHelper(); + ExternalCommand::stopHelper(); } diff --git a/test/testdevicescanner.cpp b/test/testdevicescanner.cpp index d3a39b6..0629e6d 100644 --- a/test/testdevicescanner.cpp +++ b/test/testdevicescanner.cpp @@ -34,6 +34,8 @@ #include #include +#include + using PartitionList = QList; // Recursive helper for flatten(), adds partitions that @@ -65,16 +67,18 @@ PartitionList flatten(PartitionTable *table) int main( int argc, char **argv ) { QCoreApplication app(argc, argv); + std::unique_ptr i; + if (argc != 2) { - KPMCoreInitializer i; - if (!i.isValid()) + i = std::make_unique(); + if (!i->isValid()) return 1; } else { - KPMCoreInitializer i( argv[1] ); - if (!i.isValid()) + i = std::make_unique( argv[1] ); + if (!i->isValid()) return 1; } @@ -112,6 +116,5 @@ int main( int argc, char **argv ) } return 0; - } diff --git a/test/testlist.cpp b/test/testlist.cpp index 6b1c9eb..1960e00 100644 --- a/test/testlist.cpp +++ b/test/testlist.cpp @@ -31,6 +31,8 @@ #include #include +#include + using PartitionList = QList; // Recursive helper for flatten(), adds partitions that @@ -62,17 +64,18 @@ PartitionList flatten(PartitionTable *table) int main( int argc, char **argv ) { QCoreApplication app(argc, argv); + std::unique_ptr i; if (argc != 2) { - KPMCoreInitializer i; - if (!i.isValid()) + i = std::make_unique(); + if (!i->isValid()) return 1; } else { - KPMCoreInitializer i( argv[1] ); - if (!i.isValid()) + i = std::make_unique( argv[1] ); + if (!i->isValid()) return 1; } @@ -105,6 +108,5 @@ int main( int argc, char **argv ) } return 0; - }