Move KAuth helper setup code to ExternalCommand class.

Delay helper startup until it is actually required.
This commit is contained in:
Andrius Štikonas 2018-04-14 23:55:05 +03:00
parent ed59aac2c8
commit 145f54f18c
7 changed files with 114 additions and 122 deletions

View File

@ -21,16 +21,10 @@
#include "backend/corebackend.h" #include "backend/corebackend.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QCryptographicHash>
#include <QDebug> #include <QDebug>
#include <QDBusInterface>
#include <QStringList>
#include <QString> #include <QString>
#include <QVector> #include <QVector>
#include <QtCrypto>
#include <KAuth>
#include <KLocalizedString> #include <KLocalizedString>
#include <KPluginFactory> #include <KPluginFactory>
#include <KPluginLoader> #include <KPluginLoader>
@ -38,18 +32,16 @@
struct CoreBackendManagerPrivate struct CoreBackendManagerPrivate
{ {
KAuth::ExecuteJob *m_job;
CoreBackend *m_Backend; CoreBackend *m_Backend;
QCA::Initializer init;
QCA::PrivateKey privateKey;
unsigned int counter = 0;
}; };
CoreBackendManager::CoreBackendManager() : CoreBackendManager::CoreBackendManager() :
d(std::make_unique<CoreBackendManagerPrivate>()) d(std::make_unique<CoreBackendManagerPrivate>())
{ {
startExternalCommandHelper(); }
CoreBackendManager::~CoreBackendManager()
{
} }
CoreBackendManager* CoreBackendManager::self() CoreBackendManager* CoreBackendManager::self()
@ -78,63 +70,6 @@ QVector<KPluginMetaData> CoreBackendManager::list() const
return KPluginLoader::findPlugins(QString(), filter); 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) bool CoreBackendManager::load(const QString& name)
{ {
if (backend()) if (backend())
@ -168,13 +103,3 @@ bool CoreBackendManager::load(const QString& name)
void CoreBackendManager::unload() void CoreBackendManager::unload()
{ {
} }
QCA::PrivateKey CoreBackendManager::privateKey()
{
return d->privateKey;
}
unsigned int& CoreBackendManager::counter()
{
return d->counter;
}

View File

@ -17,22 +17,18 @@
*************************************************************************/ *************************************************************************/
#ifndef KPMCORE_COREBACKENDMANAGER_H #ifndef KPMCORE_COREBACKENDMANAGER_H
#define KPMCORE_COREBACKENDMANAGER_H #define KPMCORE_COREBACKENDMANAGER_H
#include "util/libpartitionmanagerexport.h" #include "util/libpartitionmanagerexport.h"
#include <memory> #include <memory>
#include <QObject>
#include <QVector> #include <QVector>
class QString; class QString;
class QStringList; class QStringList;
class KPluginMetaData; class KPluginMetaData;
class CoreBackend; class CoreBackend;
namespace KAuth { class ExecuteJob; }
namespace QCA { class PrivateKey; }
struct CoreBackendManagerPrivate; struct CoreBackendManagerPrivate;
/** /**
@ -44,8 +40,8 @@ struct CoreBackendManagerPrivate;
*/ */
class LIBKPMCORE_EXPORT CoreBackendManager class LIBKPMCORE_EXPORT CoreBackendManager
{ {
private:
CoreBackendManager(); CoreBackendManager();
~CoreBackendManager();
public: public:
/** /**
@ -82,24 +78,6 @@ public:
*/ */
CoreBackend* backend(); 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: private:
std::unique_ptr<CoreBackendManagerPrivate> d; std::unique_ptr<CoreBackendManagerPrivate> d;
}; };

View File

@ -53,6 +53,12 @@ struct ExternalCommandPrivate
QByteArray m_Input; 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. /** Creates a new ExternalCommand instance without Report.
@param cmd the command to run @param cmd the command to run
@param args the arguments to pass to the command @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_ExitCode = -1;
d->m_Output = QByteArray(); d->m_Output = QByteArray();
if (!helperStarted)
startHelper();
setup(processChannelMode); setup(processChannelMode);
} }
@ -130,7 +139,7 @@ bool ExternalCommand::start(int timeout)
if (iface.isValid()) { if (iface.isValid()) {
QByteArray request; QByteArray request;
request.setNum(++CoreBackendManager::self()->counter()); request.setNum(++counter);
request.append(cmd.toUtf8()); request.append(cmd.toUtf8());
for (const auto &argument : qAsConst(d->m_Args)) for (const auto &argument : qAsConst(d->m_Args))
request.append(argument.toUtf8()); request.append(argument.toUtf8());
@ -139,7 +148,7 @@ bool ExternalCommand::start(int timeout)
QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512); QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512);
QDBusPendingCall pcall = iface.asyncCall(QStringLiteral("start"), QDBusPendingCall pcall = iface.asyncCall(QStringLiteral("start"),
CoreBackendManager::self()->privateKey().signMessage(hash, QCA::EMSA3_Raw), privateKey->signMessage(hash, QCA::EMSA3_Raw),
cmd, cmd,
args(), args(),
d->m_Input); d->m_Input);
@ -180,15 +189,15 @@ bool ExternalCommand::copyBlocks(CopySource& source, CopyTarget& target)
} }
// TODO KF6:Use new signal-slot syntax // TODO KF6:Use new signal-slot syntax
connect(CoreBackendManager::self()->job(), SIGNAL(percent(KJob*, unsigned long)), this, SLOT(emitProgress(KJob*, unsigned long))); connect(m_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, &KAuth::ExecuteJob::newData, this, &ExternalCommand::emitReport);
QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QDBusConnection::systemBus()); QDBusInterface iface(QStringLiteral("org.kde.kpmcore.helperinterface"), QStringLiteral("/Helper"), QStringLiteral("org.kde.kpmcore.externalcommand"), QDBusConnection::systemBus());
iface.setTimeout(10 * 24 * 3600 * 1000); // 10 days iface.setTimeout(10 * 24 * 3600 * 1000); // 10 days
if (iface.isValid()) { if (iface.isValid()) {
QByteArray request; QByteArray request;
request.setNum(++CoreBackendManager::self()->counter()); request.setNum(++counter);
request.append(source.path().toUtf8()); request.append(source.path().toUtf8());
request.append(QByteArray::number(source.firstByte())); request.append(QByteArray::number(source.firstByte()));
request.append(QByteArray::number(source.length())); 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 // Use asynchronous DBus calls, so that we can process reports and progress
QDBusPendingCall pcall= iface.asyncCall(QStringLiteral("copyblocks"), 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(), source.path(), source.firstByte(), source.length(),
target.path(), target.firstByte(), blockSize); target.path(), target.firstByte(), blockSize);
@ -332,3 +341,61 @@ void ExternalCommand::setExitCode(int i)
{ {
d->m_ExitCode = 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;
}

View File

@ -20,8 +20,6 @@
#define KPMCORE_EXTERNALCOMMAND_H #define KPMCORE_EXTERNALCOMMAND_H
#include "util/libpartitionmanagerexport.h" #include "util/libpartitionmanagerexport.h"
#include "core/copysourcedevice.h"
#include "core/copytargetfile.h"
#include <QDebug> #include <QDebug>
#include <QProcess> #include <QProcess>
@ -33,7 +31,11 @@
#include <memory> #include <memory>
class KJob; class KJob;
namespace KAuth { class ExecuteJob; }
namespace QCA { class PrivateKey; class Initializer; }
class Report; class Report;
class CopySource;
class CopyTarget;
struct ExternalCommandPrivate; struct ExternalCommandPrivate;
/** An external command. /** An external command.
@ -90,6 +92,13 @@ public:
void emitReport(const QVariantMap& report) { emit reportSignal(report); } void emitReport(const QVariantMap& report) { emit reportSignal(report); }
// KAuth
/**< start ExternalCommand Helper */
static bool startHelper();
/**< stop ExternalCommand Helper */
static void stopHelper();
Q_SIGNALS: Q_SIGNALS:
void progress(int); void progress(int);
void reportSignal(const QVariantMap&); void reportSignal(const QVariantMap&);
@ -97,7 +106,7 @@ Q_SIGNALS:
public Q_SLOTS: public Q_SLOTS:
void emitProgress(KJob*, unsigned long percent) { emit progress(percent); }; void emitProgress(KJob*, unsigned long percent) { emit progress(percent); };
protected: private:
void setExitCode(int i); void setExitCode(int i);
void setup(const QProcess::ProcessChannelMode processChannelMode); void setup(const QProcess::ProcessChannelMode processChannelMode);
@ -106,6 +115,13 @@ protected:
private: private:
std::unique_ptr<ExternalCommandPrivate> d; std::unique_ptr<ExternalCommandPrivate> d;
// KAuth stuff
static unsigned int counter;
static KAuth::ExecuteJob *m_job;
static QCA::Initializer *init;
static QCA::PrivateKey *privateKey;
static bool helperStarted;
}; };
#endif #endif

View File

@ -20,6 +20,7 @@
#include "helpers.h" #include "helpers.h"
#include "backend/corebackendmanager.h" #include "backend/corebackendmanager.h"
#include "util/externalcommand.h"
#include <QDebug> #include <QDebug>
#include <QString> #include <QString>
@ -59,5 +60,5 @@ KPMCoreInitializer::KPMCoreInitializer( const char* backend ) : KPMCoreInitializ
KPMCoreInitializer::~KPMCoreInitializer() KPMCoreInitializer::~KPMCoreInitializer()
{ {
CoreBackendManager::self()->stopExternalCommandHelper(); ExternalCommand::stopHelper();
} }

View File

@ -34,6 +34,8 @@
#include <QDebug> #include <QDebug>
#include <QList> #include <QList>
#include <memory>
using PartitionList = QList<Partition *>; using PartitionList = QList<Partition *>;
// Recursive helper for flatten(), adds partitions that // Recursive helper for flatten(), adds partitions that
@ -65,16 +67,18 @@ PartitionList flatten(PartitionTable *table)
int main( int argc, char **argv ) int main( int argc, char **argv )
{ {
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
std::unique_ptr<KPMCoreInitializer> i;
if (argc != 2) if (argc != 2)
{ {
KPMCoreInitializer i; i = std::make_unique<KPMCoreInitializer>();
if (!i.isValid()) if (!i->isValid())
return 1; return 1;
} }
else else
{ {
KPMCoreInitializer i( argv[1] ); i = std::make_unique<KPMCoreInitializer>( argv[1] );
if (!i.isValid()) if (!i->isValid())
return 1; return 1;
} }
@ -112,6 +116,5 @@ int main( int argc, char **argv )
} }
return 0; return 0;
} }

View File

@ -31,6 +31,8 @@
#include <QDebug> #include <QDebug>
#include <QList> #include <QList>
#include <memory>
using PartitionList = QList<Partition *>; using PartitionList = QList<Partition *>;
// Recursive helper for flatten(), adds partitions that // Recursive helper for flatten(), adds partitions that
@ -62,17 +64,18 @@ PartitionList flatten(PartitionTable *table)
int main( int argc, char **argv ) int main( int argc, char **argv )
{ {
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
std::unique_ptr<KPMCoreInitializer> i;
if (argc != 2) if (argc != 2)
{ {
KPMCoreInitializer i; i = std::make_unique<KPMCoreInitializer>();
if (!i.isValid()) if (!i->isValid())
return 1; return 1;
} }
else else
{ {
KPMCoreInitializer i( argv[1] ); i = std::make_unique<KPMCoreInitializer>( argv[1] );
if (!i.isValid()) if (!i->isValid())
return 1; return 1;
} }
@ -105,6 +108,5 @@ int main( int argc, char **argv )
} }
return 0; return 0;
} }