WIP: port to polkit
This commit is contained in:
parent
08c1acb6b4
commit
edfabb90dc
|
@ -39,6 +39,7 @@ include(KDECompilerSettings NO_POLICY_SCOPE)
|
|||
include(FeatureSummary)
|
||||
include(GenerateExportHeader)
|
||||
include(ECMSetupVersion)
|
||||
include(ECMConfiguredInstall)
|
||||
|
||||
ecm_setup_version(${VERSION} VARIABLE_PREFIX KPMCORE
|
||||
VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kpmcore_version.h"
|
||||
|
|
|
@ -23,6 +23,9 @@ qt5_generate_dbus_interface(
|
|||
OPTIONS -a
|
||||
)
|
||||
|
||||
|
||||
find_package(PolkitQt5-1 REQUIRED)
|
||||
|
||||
qt5_add_dbus_interface(ApplicationInterface_SRCS ${CMAKE_CURRENT_BINARY_DIR}/${application_interface_xml} externalcommand_interface)
|
||||
qt5_add_dbus_interface(HelperInterface_SRCS ${CMAKE_CURRENT_BINARY_DIR}/${helper_interface_xml} externalcommandhelper_interface)
|
||||
|
||||
|
@ -57,11 +60,15 @@ target_link_libraries(kpmcore_externalcommand
|
|||
Qt5::DBus
|
||||
KF5::AuthCore
|
||||
KF5::I18n
|
||||
PolkitQt5-1::Core
|
||||
)
|
||||
|
||||
install(TARGETS kpmcore_externalcommand DESTINATION ${KAUTH_HELPER_INSTALL_DIR})
|
||||
install(TARGETS kpmcore_externalcommand DESTINATION ${KDE_INSTALL_LIBEXECDIR})
|
||||
install( FILES util/org.kde.kpmcore.helperinterface.conf DESTINATION ${KDE_INSTALL_DBUSDIR}/system.d )
|
||||
install( FILES util/org.kde.kpmcore.applicationinterface.conf DESTINATION ${KDE_INSTALL_DBUSDIR}/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)
|
||||
ecm_install_configured_files(
|
||||
INPUT util/org.kde.kpmcore.helperinterface.service.in
|
||||
DESTINATION ${KDE_INSTALL_DBUSDIR}/system-services
|
||||
)
|
||||
|
|
|
@ -49,7 +49,6 @@ struct ExternalCommandPrivate
|
|||
int m_ExitCode;
|
||||
QByteArray m_Output;
|
||||
QByteArray m_Input;
|
||||
DBusThread *m_thread;
|
||||
QProcess::ProcessChannelMode processChannelMode;
|
||||
};
|
||||
|
||||
|
@ -71,9 +70,9 @@ ExternalCommand::ExternalCommand(const QString& cmd, const QStringList& args, co
|
|||
d->m_ExitCode = -1;
|
||||
d->m_Output = QByteArray();
|
||||
|
||||
if (!helperStarted)
|
||||
if(!startHelper())
|
||||
Log(Log::Level::error) << xi18nc("@info:status", "Could not obtain administrator privileges.");
|
||||
// if (!helperStarted)
|
||||
// if(!startHelper())
|
||||
// Log(Log::Level::error) << xi18nc("@info:status", "Could not obtain administrator privileges.");
|
||||
|
||||
d->processChannelMode = processChannelMode;
|
||||
}
|
||||
|
@ -129,6 +128,8 @@ bool ExternalCommand::start(int timeout)
|
|||
if (cmd.isEmpty())
|
||||
cmd = QStandardPaths::findExecutable(command(), { QStringLiteral("/sbin/"), QStringLiteral("/usr/sbin/"), QStringLiteral("/usr/local/sbin/") });
|
||||
|
||||
qDebug() << "start";
|
||||
|
||||
auto interface = helperInterface();
|
||||
if (!interface)
|
||||
return false;
|
||||
|
@ -231,7 +232,7 @@ OrgKdeKpmcoreExternalcommandInterface* ExternalCommand::helperInterface()
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"),
|
||||
auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.helperinterface"),
|
||||
QStringLiteral("/Helper"), QDBusConnection::systemBus(), this);
|
||||
interface->setTimeout(10 * 24 * 3600 * 1000); // 10 days
|
||||
return interface;
|
||||
|
@ -355,46 +356,14 @@ bool ExternalCommand::startHelper()
|
|||
exit(0);
|
||||
}
|
||||
|
||||
d->m_thread = new DBusThread;
|
||||
d->m_thread->start();
|
||||
|
||||
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
|
||||
action.setParentWidget(parent);
|
||||
QVariantMap arguments;
|
||||
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;
|
||||
qDebug() <<"starting helper";
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExternalCommand::stopHelper()
|
||||
{
|
||||
auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.externalcommand"),
|
||||
auto *interface = new org::kde::kpmcore::externalcommand(QStringLiteral("org.kde.kpmcore.helperinterface"),
|
||||
QStringLiteral("/Helper"), QDBusConnection::systemBus());
|
||||
interface->exit();
|
||||
|
||||
}
|
||||
|
||||
void DBusThread::run()
|
||||
{
|
||||
if (!QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.applicationinterface")) ||
|
||||
!QDBusConnection::systemBus().registerObject(QStringLiteral("/Application"), this, QDBusConnection::ExportAllSlots)) {
|
||||
qWarning() << QDBusConnection::systemBus().lastError().message();
|
||||
return;
|
||||
}
|
||||
|
||||
QEventLoop loop;
|
||||
loop.exec();
|
||||
}
|
||||
|
|
|
@ -36,15 +36,6 @@ class OrgKdeKpmcoreExternalcommandInterface;
|
|||
|
||||
struct ExternalCommandPrivate;
|
||||
|
||||
class DBusThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
// We register on DBus so the helper can monitor us and terminate if we
|
||||
// terminate.
|
||||
Q_CLASSINFO("D-Bus Interface", "org.kde.kpmcore.applicationinterface")
|
||||
void run() override;
|
||||
};
|
||||
|
||||
/** An external command.
|
||||
|
||||
Runs an external command as a child process.
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "externalcommand_whitelist.h"
|
||||
|
||||
#include <QtDBus>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QElapsedTimer>
|
||||
|
@ -21,6 +22,10 @@
|
|||
#include <QVariant>
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <PolkitQt1/Authority>
|
||||
#include <PolkitQt1/Subject>
|
||||
|
||||
#include <polkitqt1-version.h>
|
||||
|
||||
/** Initialize ExternalCommandHelper Daemon and prepare DBus interface
|
||||
*
|
||||
|
@ -35,52 +40,34 @@
|
|||
* This helper also starts another DBus interface where it listens to
|
||||
* command execution requests from the application that started the helper.
|
||||
*
|
||||
*
|
||||
* DAVE - this all needs updating
|
||||
*
|
||||
*/
|
||||
ActionReply ExternalCommandHelper::init(const QVariantMap& args)
|
||||
|
||||
ExternalCommandHelper::ExternalCommandHelper()
|
||||
{
|
||||
Q_UNUSED(args)
|
||||
|
||||
ActionReply reply;
|
||||
|
||||
if (!QDBusConnection::systemBus().isConnected() || !QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.helperinterface")) ||
|
||||
!QDBusConnection::systemBus().registerObject(QStringLiteral("/Helper"), this, QDBusConnection::ExportAllSlots)) {
|
||||
qWarning() << QDBusConnection::systemBus().lastError().message();
|
||||
reply.addData(QStringLiteral("success"), false);
|
||||
|
||||
// Also end the application loop started by KAuth's main() code. Our loop
|
||||
// exits when our client disappears. Without client we have no reason to
|
||||
// live.
|
||||
qApp->quit();
|
||||
|
||||
return reply;
|
||||
if (!QDBusConnection::systemBus().registerObject(QStringLiteral("/Helper"), this, QDBusConnection::ExportAllSlots)) {
|
||||
::exit(-1);
|
||||
}
|
||||
|
||||
m_loop = std::make_unique<QEventLoop>();
|
||||
HelperSupport::progressStep(QVariantMap());
|
||||
|
||||
// End the loop and return only once the client is done using us.
|
||||
auto serviceWatcher =
|
||||
new QDBusServiceWatcher(QStringLiteral("org.kde.kpmcore.applicationinterface"),
|
||||
QDBusConnection::systemBus(),
|
||||
QDBusServiceWatcher::WatchForUnregistration,
|
||||
this);
|
||||
connect(serviceWatcher, &QDBusServiceWatcher::serviceUnregistered,
|
||||
[this]() {
|
||||
m_loop->exit();
|
||||
if (!QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.kpmcore.helperinterface"))) {
|
||||
::exit(-1);
|
||||
}
|
||||
|
||||
// we know this service must be registered already as DBus policy blocks calls from anyone else
|
||||
m_serviceWatcher = new QDBusServiceWatcher(this);
|
||||
m_serviceWatcher->setConnection(QDBusConnection ::systemBus());
|
||||
m_serviceWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration);
|
||||
|
||||
connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, qApp, [this](const QString &service) {
|
||||
m_serviceWatcher->removeWatchedService(service);
|
||||
if (m_serviceWatcher->watchedServices().isEmpty()) {
|
||||
qApp->quit();
|
||||
}
|
||||
});
|
||||
|
||||
m_loop->exec();
|
||||
reply.addData(QStringLiteral("success"), true);
|
||||
|
||||
// Also end the application loop started by KAuth's main() code. Our loop
|
||||
// exits when our client disappears. Without client we have no reason to
|
||||
// live.
|
||||
qApp->quit();
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
/** Reads the given number of bytes from the sourceDevice into the given buffer.
|
||||
@param sourceDevice device or file to read from
|
||||
@param buffer buffer to store the bytes read in
|
||||
|
@ -148,6 +135,9 @@ bool ExternalCommandHelper::writeData(const QString &targetDevice, const QByteAr
|
|||
*/
|
||||
bool ExternalCommandHelper::createFile(const QString &filePath, const QByteArray& fileContents)
|
||||
{
|
||||
if (!isCallerAuthorized()) {
|
||||
return false;
|
||||
}
|
||||
QFile device(filePath);
|
||||
|
||||
auto flags = QIODevice::WriteOnly | QIODevice::Unbuffered;
|
||||
|
@ -167,6 +157,9 @@ bool ExternalCommandHelper::createFile(const QString &filePath, const QByteArray
|
|||
// If targetDevice is empty then return QByteArray with data that was read from disk.
|
||||
QVariantMap ExternalCommandHelper::copyblocks(const QString& sourceDevice, const qint64 sourceFirstByte, const qint64 sourceLength, const QString& targetDevice, const qint64 targetFirstByte, const qint64 blockSize)
|
||||
{
|
||||
if (!isCallerAuthorized()) {
|
||||
return QVariantMap();
|
||||
}
|
||||
QVariantMap reply;
|
||||
reply[QStringLiteral("success")] = true;
|
||||
|
||||
|
@ -256,6 +249,9 @@ QVariantMap ExternalCommandHelper::copyblocks(const QString& sourceDevice, const
|
|||
|
||||
bool ExternalCommandHelper::writeData(const QByteArray& buffer, const QString& targetDevice, const qint64 targetFirstByte)
|
||||
{
|
||||
if (!isCallerAuthorized()) {
|
||||
return false;
|
||||
}
|
||||
// Do not allow using this helper for writing to arbitrary location
|
||||
if ( targetDevice.left(5) != QStringLiteral("/dev/") )
|
||||
return false;
|
||||
|
@ -265,6 +261,9 @@ bool ExternalCommandHelper::writeData(const QByteArray& buffer, const QString& t
|
|||
|
||||
bool ExternalCommandHelper::createFile(const QByteArray& fileContents, const QString& filePath)
|
||||
{
|
||||
if (!isCallerAuthorized()) {
|
||||
return false;
|
||||
}
|
||||
// Do not allow using this helper for writing to arbitrary location
|
||||
if ( !filePath.contains(QStringLiteral("/etc/fstab")) )
|
||||
return false;
|
||||
|
@ -274,6 +273,9 @@ bool ExternalCommandHelper::createFile(const QByteArray& fileContents, const QSt
|
|||
|
||||
QVariantMap ExternalCommandHelper::start(const QString& command, const QStringList& arguments, const QByteArray& input, const int processChannelMode)
|
||||
{
|
||||
if (!isCallerAuthorized()) {
|
||||
return QVariantMap();
|
||||
}
|
||||
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
|
||||
QVariantMap reply;
|
||||
reply[QStringLiteral("success")] = true;
|
||||
|
@ -287,7 +289,7 @@ QVariantMap ExternalCommandHelper::start(const QString& command, const QStringLi
|
|||
QString basename = command.mid(command.lastIndexOf(QLatin1Char('/')) + 1);
|
||||
if (std::find(std::begin(allowedCommands), std::end(allowedCommands), basename) == std::end(allowedCommands)) {
|
||||
qInfo() << command <<" command is not one of the whitelisted command";
|
||||
m_loop->exit();
|
||||
qApp->quit();
|
||||
reply[QStringLiteral("success")] = false;
|
||||
return reply;
|
||||
}
|
||||
|
@ -309,10 +311,10 @@ QVariantMap ExternalCommandHelper::start(const QString& command, const QStringLi
|
|||
|
||||
void ExternalCommandHelper::exit()
|
||||
{
|
||||
m_loop->exit();
|
||||
|
||||
QDBusConnection::systemBus().unregisterObject(QStringLiteral("/Helper"));
|
||||
QDBusConnection::systemBus().unregisterService(QStringLiteral("org.kde.kpmcore.helperinterface"));
|
||||
if (!isCallerAuthorized()) {
|
||||
return;
|
||||
}
|
||||
qApp->quit();
|
||||
}
|
||||
|
||||
void ExternalCommandHelper::onReadOutput()
|
||||
|
@ -331,4 +333,48 @@ void ExternalCommandHelper::onReadOutput()
|
|||
*report() << QString::fromLocal8Bit(s);*/
|
||||
}
|
||||
|
||||
KAUTH_HELPER_MAIN("org.kde.kpmcore.externalcommand", ExternalCommandHelper)
|
||||
bool ExternalCommandHelper::isCallerAuthorized()
|
||||
{
|
||||
if (!calledFromDBus()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// track who called into us so we can close when all callers have gone away
|
||||
// this has to happen before authorisation as anyone could have activated us
|
||||
m_serviceWatcher->addWatchedService(message().service());
|
||||
|
||||
PolkitQt1::SystemBusNameSubject subject(message().service());
|
||||
PolkitQt1::Authority *authority = PolkitQt1::Authority::instance();
|
||||
|
||||
PolkitQt1::Authority::Result result;
|
||||
QEventLoop e;
|
||||
connect(authority, &PolkitQt1::Authority::checkAuthorizationFinished, [&result, &e](PolkitQt1::Authority::Result _result) {
|
||||
result = _result;
|
||||
e.quit();
|
||||
});
|
||||
|
||||
authority->checkAuthorization(QStringLiteral("org.kde.kpmcore.externalcommand.init"), subject, PolkitQt1::Authority::AllowUserInteraction);
|
||||
e.exec();
|
||||
|
||||
if (authority->hasError()) {
|
||||
qDebug() << "Encountered error while checking authorization, error code:" << authority->lastError() << authority->errorDetails();
|
||||
authority->clearError();
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case PolkitQt1::Authority::Yes:
|
||||
return true;
|
||||
default:
|
||||
sendErrorReply(QDBusError::AccessDenied);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
ExternalCommandHelper helper;
|
||||
app.exec();
|
||||
}
|
||||
|
||||
#include "externalcommandhelper.moc"
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
#include <QEventLoop>
|
||||
#include <QString>
|
||||
#include <QProcess>
|
||||
#include <QDBusContext>
|
||||
|
||||
class QDBusServiceWatcher;
|
||||
|
||||
using namespace KAuth;
|
||||
|
||||
class ExternalCommandHelper : public QObject
|
||||
class ExternalCommandHelper : public QObject, public QDBusContext
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.kde.kpmcore.externalcommand")
|
||||
|
@ -31,12 +34,12 @@ Q_SIGNALS:
|
|||
void quit();
|
||||
|
||||
public:
|
||||
ExternalCommandHelper();
|
||||
bool readData(const QString& sourceDevice, QByteArray& buffer, const qint64 offset, const qint64 size);
|
||||
bool writeData(const QString& targetDevice, const QByteArray& buffer, const qint64 offset);
|
||||
bool createFile(const QString& filePath, const QByteArray& fileContents);
|
||||
|
||||
public Q_SLOTS:
|
||||
ActionReply init(const QVariantMap& args);
|
||||
Q_SCRIPTABLE QVariantMap start(const QString& command, const QStringList& arguments, const QByteArray& input, const int processChannelMode);
|
||||
Q_SCRIPTABLE QVariantMap copyblocks(const QString& sourceDevice, const qint64 sourceFirstByte, const qint64 sourceLength, const QString& targetDevice, const qint64 targetFirstByte, const qint64 blockSize);
|
||||
Q_SCRIPTABLE bool writeData(const QByteArray& buffer, const QString& targetDevice, const qint64 targetFirstByte);
|
||||
|
@ -44,11 +47,12 @@ public Q_SLOTS:
|
|||
Q_SCRIPTABLE void exit();
|
||||
|
||||
private:
|
||||
void onReadOutput();
|
||||
|
||||
std::unique_ptr<QEventLoop> m_loop;
|
||||
bool isCallerAuthorized();
|
||||
|
||||
void onReadOutput();
|
||||
QProcess m_cmd;
|
||||
// QByteArray output;
|
||||
QDBusServiceWatcher *m_serviceWatcher = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</policy>
|
||||
|
||||
<policy context="default">
|
||||
<allow send_destination="org.kde.kpmcore.externalcommand"
|
||||
<allow send_destination="org.kde.kpmcore.helperinterface"
|
||||
send_interface="org.kde.kpmcore.externalcommand"/>
|
||||
|
||||
</policy>
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
[D-BUS Service]
|
||||
Name=org.kde.kpmcore.helperinterface
|
||||
Exec=@KDE_INSTALL_FULL_LIBEXECDIR@/kpmcore_externalcommand
|
||||
User=root
|
Loading…
Reference in New Issue