diff --git a/CMakeLists.txt b/CMakeLists.txt index ddb5423..caff811 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,8 @@ find_package(KF5 REQUIRED WidgetsAddons ) +find_package(Qca-qt5 REQUIRED) + # use sane compile flags add_definitions( -DQT_USE_QSTRINGBUILDER diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 52ee0b9..500b9a8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 ) diff --git a/src/backend/corebackendmanager.cpp b/src/backend/corebackendmanager.cpp index 62322f2..cd228be 100644 --- a/src/backend/corebackendmanager.cpp +++ b/src/backend/corebackendmanager.cpp @@ -28,6 +28,8 @@ #include #include +#include + #include #include #include @@ -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 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; +} diff --git a/src/backend/corebackendmanager.h b/src/backend/corebackendmanager.h index d623b53..70476d7 100644 --- a/src/backend/corebackendmanager.h +++ b/src/backend/corebackendmanager.h @@ -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 d; diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 22957f7..6e3abb2 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -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 diff --git a/src/util/externalcommand.cpp b/src/util/externalcommand.cpp index 8b67bb6..6b51017 100644 --- a/src/util/externalcommand.cpp +++ b/src/util/externalcommand.cpp @@ -25,6 +25,7 @@ #include "util/externalcommand.h" #include "util/report.h" +#include #include #include #include @@ -36,14 +37,14 @@ #include #include +#include + #include #include #include 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(&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, diff --git a/src/util/externalcommandhelper.cpp b/src/util/externalcommandhelper.cpp index 0601f23..998ffef 100644 --- a/src/util/externalcommandhelper.cpp +++ b/src/util/externalcommandhelper.cpp @@ -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; } diff --git a/src/util/externalcommandhelper.h b/src/util/externalcommandhelper.h index 299a27e..05d48c1 100644 --- a/src/util/externalcommandhelper.h +++ b/src/util/externalcommandhelper.h @@ -24,7 +24,7 @@ #include #include -class QTimer; +#include 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; };