First attempt at using RSA to sign requests to KAuth helper.

This commit is contained in:
Andrius Štikonas 2018-04-13 00:43:12 +03:00
parent 527734e9e8
commit 428ee5b6c8
8 changed files with 81 additions and 13 deletions

View File

@ -63,6 +63,8 @@ find_package(KF5 REQUIRED
WidgetsAddons
)
find_package(Qca-qt5 REQUIRED)
# use sane compile flags
add_definitions(
-DQT_USE_QSTRINGBUILDER

View File

@ -42,12 +42,13 @@ add_library(kpmcore SHARED ${kpmcore_SRCS})
target_link_libraries( kpmcore PUBLIC
Qt5::Core
PRIVATE
${BLKID_LIBRARIES}
Qt5::DBus
Qt5::Gui
qca-qt5
KF5::I18n
KF5::CoreAddons
KF5::WidgetsAddons
${BLKID_LIBRARIES}
KF5::Auth
)

View File

@ -28,6 +28,8 @@
#include <QVector>
#include <QUuid>
#include <QtCrypto>
#include <KAuth>
#include <KLocalizedString>
#include <KPluginFactory>
@ -40,6 +42,9 @@ struct CoreBackendManagerPrivate
CoreBackend *m_Backend;
QString m_Uuid;
QCA::Initializer init;
QCA::PrivateKey privateKey;
unsigned int counter = 0;
};
CoreBackendManager::CoreBackendManager() :
@ -74,14 +79,34 @@ QVector<KPluginMetaData> CoreBackendManager::list() const
return KPluginLoader::findPlugins(QString(), filter);
}
void CoreBackendManager::startExternalCommandHelper()
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;
d->m_Uuid = QUuid::createUuid().toString();
arguments.insert(QStringLiteral("callerUuid"), Uuid());
arguments.insert(QStringLiteral("pubkey"), pubkey.toDER());
action.setArguments(arguments);
d->m_job = action.execute();
job()->start();
@ -93,6 +118,8 @@ void CoreBackendManager::startExternalCommandHelper()
QObject::connect(job(), &KJob::finished, [=] () { if(d->m_job->error()) exitLoop(); } );
loop.exec();
QObject::disconnect(conn);
return true;
}
void CoreBackendManager::stopExternalCommandHelper()
@ -145,3 +172,13 @@ 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

@ -32,6 +32,7 @@ class QStringList;
class KPluginMetaData;
class CoreBackend;
namespace KAuth { class ExecuteJob; }
namespace QCA { class PrivateKey; }
struct CoreBackendManagerPrivate;
/**
@ -96,8 +97,13 @@ public:
*/
static void stopExternalCommandHelper();
/**< @return RSA private key used for signing External Command requests. */
QCA::PrivateKey privateKey();
unsigned int& counter();
private:
void startExternalCommandHelper();
bool startExternalCommandHelper();
private:
std::unique_ptr<CoreBackendManagerPrivate> d;

View File

@ -25,6 +25,7 @@ qt5_generate_dbus_interface(
add_executable(kpmcore_externalcommand util/externalcommandhelper.cpp)
target_link_libraries(kpmcore_externalcommand
qca-qt5
Qt5::Core
Qt5::DBus
KF5::Auth

View File

@ -25,6 +25,7 @@
#include "util/externalcommand.h"
#include "util/report.h"
#include <QCryptographicHash>
#include <QDBusInterface>
#include <QDBusReply>
#include <QEventLoop>
@ -36,14 +37,14 @@
#include <QThread>
#include <QVariant>
#include <QtCrypto>
#include <KAuth>
#include <KJob>
#include <KLocalizedString>
struct ExternalCommandPrivate
{
QVariantMap arguments;
Report *m_Report;
QString m_Command;
QStringList m_Args;
@ -91,8 +92,7 @@ ExternalCommand::~ExternalCommand()
void ExternalCommand::setup(const QProcess::ProcessChannelMode processChannelMode)
{
d->arguments.insert(QStringLiteral("environment"), QStringList() << QStringLiteral("LC_ALL=C") << QStringLiteral("LVM_SUPPRESS_FD_WARNINGS=1"));
d->arguments.insert(QStringLiteral("processChannelMode"), processChannelMode);
// d->arguments.insert(QStringLiteral("processChannelMode"), processChannelMode); // FIXME
// connect(this, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this, &ExternalCommand::onFinished);
// connect(this, &ExternalCommand::readyReadStandardOutput, this, &ExternalCommand::onReadOutput);
@ -128,8 +128,17 @@ bool ExternalCommand::start(int timeout)
bool rval = false;
if (iface.isValid()) {
QByteArray request;
request.setNum(++CoreBackendManager::self()->counter());
request.append(cmd.toUtf8());
for (const auto &argument : qAsConst(d->m_Args))
request.append(argument.toLocal8Bit());
request.append(d->m_Input);
QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512);
QDBusPendingCall pcall = iface.asyncCall(QStringLiteral("start"),
CoreBackendManager::self()->Uuid(),
CoreBackendManager::self()->privateKey().signMessage(hash, QCA::EMSA3_Raw),
cmd,
args(),
d->m_Input,

View File

@ -40,6 +40,7 @@ ActionReply ExternalCommandHelper::init(const QVariantMap& args)
return reply;
}
m_callerUuid = args[QStringLiteral("callerUuid")].toString();
m_publicKey = QCA::PublicKey::fromDER(args[QStringLiteral("pubkey")].toByteArray());
if (!QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.helperinterface"))) {
qWarning() << QDBusConnection::systemBus().lastError().message();
@ -196,10 +197,19 @@ bool ExternalCommandHelper::copyblocks(const QString& Uuid, const QString& sourc
return rval;
}
QVariantMap ExternalCommandHelper::start(const QString& Uuid, const QString& command, const QStringList& arguments, const QByteArray& input, const QStringList& environment)
QVariantMap ExternalCommandHelper::start(const QByteArray& signature, const QString& command, const QStringList& arguments, const QByteArray& input, const QStringList& environment)
{
QVariantMap reply;
if (!isCallerAuthorized(Uuid)) {
QByteArray request;
request.setNum(++m_Counter);
request.append(command.toLocal8Bit());
for (const auto &argument : arguments)
request.append(argument.toUtf8());
request.append(input);
QByteArray hash = QCryptographicHash::hash(request, QCryptographicHash::Sha512);
if (!m_publicKey.verifyMessage(hash, signature, QCA::EMSA3_Raw)) {
qCritical() << xi18n("Invalid cryptographic signature");
reply[QStringLiteral("success")] = false;
return reply;
}

View File

@ -24,7 +24,7 @@
#include <QString>
#include <QProcess>
class QTimer;
#include <QtCrypto>
using namespace KAuth;
@ -43,7 +43,7 @@ public:
public Q_SLOTS:
ActionReply init(const QVariantMap& args);
Q_SCRIPTABLE QVariantMap start(const QString& Uuid, const QString& command, const QStringList& arguments, const QByteArray& input, const QStringList& environment);
Q_SCRIPTABLE QVariantMap start(const QByteArray& signature, const QString& command, const QStringList& arguments, const QByteArray& input, const QStringList& environment);
Q_SCRIPTABLE bool copyblocks(const QString& Uuid, const QString& sourceDevice, const qint64 sourceFirstByte, const qint64 sourceLength, const QString& targetDevice, const qint64 targetFirstByte, const qint64 blockSize);
Q_SCRIPTABLE void exit(const QString& Uuid);
@ -53,11 +53,13 @@ private:
QEventLoop m_loop;
QString m_callerUuid;
QCA::Initializer initializer;
QCA::PublicKey m_publicKey;
unsigned int m_Counter;
QString m_command;
QString m_sourceDevice;
QProcess m_cmd;
QTimer *timer;
// QByteArray output;
};