From 51780a62975b7b14baceda091ca0e9e267846b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20=C5=A0tikonas?= Date: Sun, 15 Jul 2018 00:09:39 +0100 Subject: [PATCH] Stop KAuth helper if application crashes. This is implemented by pinging application from KAuth helper via DBus. If it is busy then KAuth helper also waits for the current job to finish. --- src/util/CMakeLists.txt | 1 + src/util/externalcommand.cpp | 28 +++++++++++- src/util/externalcommand.h | 13 +++++- src/util/externalcommandhelper.cpp | 43 +++++++++++++++++-- .../org.kde.kpmcore.applicationinterface.conf | 14 ++++++ 5 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 src/util/org.kde.kpmcore.applicationinterface.conf diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 6e3abb2..4d1fd5a 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -34,6 +34,7 @@ target_link_libraries(kpmcore_externalcommand install(TARGETS kpmcore_externalcommand DESTINATION ${KAUTH_HELPER_INSTALL_DIR}) install( FILES util/org.kde.kpmcore.helperinterface.conf DESTINATION ${SYSCONF_INSTALL_DIR}/dbus-1/system.d ) +install( FILES util/org.kde.kpmcore.applicationinterface.conf DESTINATION ${SYSCONF_INSTALL_DIR}/dbus-1/system.d ) kauth_install_helper_files(kpmcore_externalcommand org.kde.kpmcore.externalcommand root) kauth_install_actions(org.kde.kpmcore.externalcommand util/org.kde.kpmcore.externalcommand.actions) diff --git a/src/util/externalcommand.cpp b/src/util/externalcommand.cpp index a165913..716e43b 100644 --- a/src/util/externalcommand.cpp +++ b/src/util/externalcommand.cpp @@ -22,10 +22,12 @@ #include "core/copytarget.h" #include "core/copysourcedevice.h" #include "core/copytargetdevice.h" +#include "util/globallog.h" #include "util/externalcommand.h" #include "util/report.h" #include +#include #include #include #include @@ -51,6 +53,7 @@ struct ExternalCommandPrivate int m_ExitCode; QByteArray m_Output; QByteArray m_Input; + DBusThread *m_thread; }; unsigned int ExternalCommand::counter = 0; @@ -75,7 +78,8 @@ ExternalCommand::ExternalCommand(const QString& cmd, const QStringList& args, co d->m_Output = QByteArray(); if (!helperStarted) - startHelper(); + if(!startHelper()) + Log(Log::Level::error) << xi18nc("@info:status", "Could not obtain administrator privileges."); setup(processChannelMode); } @@ -346,6 +350,13 @@ void ExternalCommand::setExitCode(int i) bool ExternalCommand::startHelper() { + if (!QDBusConnection::systemBus().isConnected()) { + qWarning() << "Could not connect to DBus session bus"; + return false; + } + d->m_thread = new DBusThread; + d->m_thread->start(); + init = new QCA::Initializer; // Generate RSA key pair for signing external command requests if (!QCA::isSupported("pkey") || !QCA::PKey::supportedIOTypes().contains(QCA::PKey::RSA)) { @@ -402,3 +413,18 @@ void ExternalCommand::stopHelper() delete privateKey; delete init; } + +void DBusThread::run() +{ + if (!QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.applicationinterface"))) { + qWarning() << QDBusConnection::systemBus().lastError().message(); + return; + } + if (!QDBusConnection::systemBus().registerObject(QStringLiteral("/Application"), this, QDBusConnection::ExportAllSlots)) { + qWarning() << QDBusConnection::systemBus().lastError().message(); + return; + } + + QEventLoop loop; + loop.exec(); +} diff --git a/src/util/externalcommand.h b/src/util/externalcommand.h index b260b10..069640b 100644 --- a/src/util/externalcommand.h +++ b/src/util/externalcommand.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,16 @@ class CopySource; class CopyTarget; struct ExternalCommandPrivate; +class DBusThread : public QThread +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.kpmcore.ping") + void run() override; + +public Q_SLOTS: + Q_SCRIPTABLE void ping() {return;}; +}; + /** An external command. Runs an external command as a child process. @@ -94,7 +105,7 @@ public: // KAuth /**< start ExternalCommand Helper */ - static bool startHelper(); + bool startHelper(); /**< stop ExternalCommand Helper */ static void stopHelper(); diff --git a/src/util/externalcommandhelper.cpp b/src/util/externalcommandhelper.cpp index 123e300..61f0212 100644 --- a/src/util/externalcommandhelper.cpp +++ b/src/util/externalcommandhelper.cpp @@ -27,6 +27,18 @@ #include /** Initialize ExternalCommandHelper Daemon and prepare DBus interface + * + * KAuth helper runs in the background until application exits. + * To avoid forever running helper in case of application crash + * ExternalCommand class opens DBus interface that we ping. + * If helper is not busy than it exits when ping fails. Otherwise, + * we wait for the current job to finish before exiting, so even in case + * of main application crash, we do not leave partially moved data. + * + * This helper also starts another DBus interface where it listens to + * command execution requests from the application that started the helper. + * These requests are validated using public key cryptography, to prevent + * other unprivileged applications from gaining root privileges. */ ActionReply ExternalCommandHelper::init(const QVariantMap& args) { @@ -36,17 +48,42 @@ ActionReply ExternalCommandHelper::init(const QVariantMap& args) reply.addData(QStringLiteral("success"), false); return reply; } - m_publicKey = QCA::PublicKey::fromDER(args[QStringLiteral("pubkey")].toByteArray()); - m_Counter = 0; if (!QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.helperinterface"))) { qWarning() << QDBusConnection::systemBus().lastError().message(); reply.addData(QStringLiteral("success"), false); return reply; } - QDBusConnection::systemBus().registerObject(QStringLiteral("/Helper"), this, QDBusConnection::ExportAllSlots); + if (!QDBusConnection::systemBus().registerObject(QStringLiteral("/Helper"), this, QDBusConnection::ExportAllSlots)) { + qWarning() << QDBusConnection::systemBus().lastError().message(); + reply.addData(QStringLiteral("success"), false); + return reply; + } + + m_publicKey = QCA::PublicKey::fromDER(args[QStringLiteral("pubkey")].toByteArray()); + m_Counter = 0; HelperSupport::progressStep(QVariantMap()); + auto timeout = [this] () { + QDBusInterface iface(QStringLiteral("org.kde.kpmcore.applicationinterface"), + QStringLiteral("/Application"), + QStringLiteral("org.kde.kpmcore.ping"), + QDBusConnection::systemBus()); + iface.setTimeout(5000); // 5 seconds; + auto pcall = iface.asyncCall(QStringLiteral("ping")); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); + auto exitLoop = [&] (QDBusPendingCallWatcher *watcher) { + if (watcher->isError()) { + qWarning() << watcher->error(); + m_loop.exit(); + } + }; + connect(watcher, &QDBusPendingCallWatcher::finished, exitLoop); + }; + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, timeout); + timer->start(5000); // 5 seconds m_loop.exec(); reply.addData(QStringLiteral("success"), true); diff --git a/src/util/org.kde.kpmcore.applicationinterface.conf b/src/util/org.kde.kpmcore.applicationinterface.conf new file mode 100644 index 0000000..59113ff --- /dev/null +++ b/src/util/org.kde.kpmcore.applicationinterface.conf @@ -0,0 +1,14 @@ + + + + + + + + + + +