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 <QCoreApplication>
#include <QCryptographicHash>
#include <QDebug>
#include <QDBusInterface>
#include <QStringList>
#include <QString>
#include <QVector>
#include <QtCrypto>
#include <KAuth>
#include <KLocalizedString>
#include <KPluginFactory>
#include <KPluginLoader>
@ -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<CoreBackendManagerPrivate>())
{
startExternalCommandHelper();
}
CoreBackendManager::~CoreBackendManager()
{
}
CoreBackendManager* CoreBackendManager::self()
@ -78,63 +70,6 @@ QVector<KPluginMetaData> 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;
}

View File

@ -17,22 +17,18 @@
*************************************************************************/
#ifndef KPMCORE_COREBACKENDMANAGER_H
#define KPMCORE_COREBACKENDMANAGER_H
#include "util/libpartitionmanagerexport.h"
#include <memory>
#include <QObject>
#include <QVector>
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<CoreBackendManagerPrivate> d;
};

View File

@ -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;
}

View File

@ -20,8 +20,6 @@
#define KPMCORE_EXTERNALCOMMAND_H
#include "util/libpartitionmanagerexport.h"
#include "core/copysourcedevice.h"
#include "core/copytargetfile.h"
#include <QDebug>
#include <QProcess>
@ -33,7 +31,11 @@
#include <memory>
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<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

View File

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

View File

@ -34,6 +34,8 @@
#include <QDebug>
#include <QList>
#include <memory>
using PartitionList = QList<Partition *>;
// 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<KPMCoreInitializer> i;
if (argc != 2)
{
KPMCoreInitializer i;
if (!i.isValid())
i = std::make_unique<KPMCoreInitializer>();
if (!i->isValid())
return 1;
}
else
{
KPMCoreInitializer i( argv[1] );
if (!i.isValid())
i = std::make_unique<KPMCoreInitializer>( argv[1] );
if (!i->isValid())
return 1;
}
@ -112,6 +116,5 @@ int main( int argc, char **argv )
}
return 0;
}

View File

@ -31,6 +31,8 @@
#include <QDebug>
#include <QList>
#include <memory>
using PartitionList = QList<Partition *>;
// 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<KPMCoreInitializer> i;
if (argc != 2)
{
KPMCoreInitializer i;
if (!i.isValid())
i = std::make_unique<KPMCoreInitializer>();
if (!i->isValid())
return 1;
}
else
{
KPMCoreInitializer i( argv[1] );
if (!i.isValid())
i = std::make_unique<KPMCoreInitializer>( argv[1] );
if (!i->isValid())
return 1;
}
@ -105,6 +108,5 @@ int main( int argc, char **argv )
}
return 0;
}