fix(cve): remove ipc command that creates privilege escalation vulnerability
CVE-2026-41477
This commit is contained in:
parent
f0631f7a3c
commit
e7040a1f82
10 changed files with 74 additions and 84 deletions
|
|
@ -27,7 +27,8 @@
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <string>
|
#include <QCoreApplication>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
using namespace deskflow::core;
|
using namespace deskflow::core;
|
||||||
|
|
||||||
|
|
@ -45,26 +46,40 @@ void DaemonApp::saveLogLevel(const QString &logLevel) const
|
||||||
Settings::setValue(Settings::Daemon::LogLevel, logLevel);
|
Settings::setValue(Settings::Daemon::LogLevel, logLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DaemonApp::setElevate(bool elevate)
|
void DaemonApp::setConfigFile(const QString &configFile)
|
||||||
{
|
{
|
||||||
LOG_DEBUG("elevate value changed: %s", elevate ? "yes" : "no");
|
LOG_DEBUG("config file updated: %s", configFile.toUtf8().constData());
|
||||||
m_elevate = elevate;
|
m_configFile = configFile;
|
||||||
Settings::setValue(Settings::Daemon::Elevate, m_elevate);
|
Settings::setValue(Settings::Daemon::ConfigFile, configFile);
|
||||||
}
|
|
||||||
|
|
||||||
void DaemonApp::setCommand(const QString &command)
|
|
||||||
{
|
|
||||||
LOG_DEBUG("service command updated");
|
|
||||||
Settings::setValue(Settings::Daemon::Command, command);
|
|
||||||
m_command = command.toStdString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DaemonApp::applyWatchdogCommand() const
|
void DaemonApp::applyWatchdogCommand() const
|
||||||
{
|
{
|
||||||
LOG_DEBUG("applying watchdog command");
|
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
m_pWatchdog->setProcessConfig(m_command, m_elevate);
|
if (m_configFile.isEmpty()) {
|
||||||
|
LOG_ERR("cannot apply watchdog command: no config file set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSettings config(m_configFile, QSettings::IniFormat);
|
||||||
|
const auto coreMode = config.value(Settings::Core::CoreMode).toInt();
|
||||||
|
const auto elevate = config.value(Settings::Daemon::Elevate, !Settings::isPortableMode()).toBool();
|
||||||
|
|
||||||
|
QString modeArg;
|
||||||
|
if (coreMode == Settings::CoreMode::Server) {
|
||||||
|
modeArg = QStringLiteral("server");
|
||||||
|
} else if (coreMode == Settings::CoreMode::Client) {
|
||||||
|
modeArg = QStringLiteral("client");
|
||||||
|
} else {
|
||||||
|
LOG_ERR("cannot apply watchdog command: invalid core mode in config: %d", coreMode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto corePath = QStringLiteral("%1/%2").arg(QCoreApplication::applicationDirPath(), kCoreBinName);
|
||||||
|
const auto command = QStringLiteral("\"%1\" %2 --settings \"%3\"").arg(corePath, modeArg, m_configFile).toStdString();
|
||||||
|
|
||||||
|
LOG_DEBUG("applying watchdog command (elevate: %s)", elevate ? "yes" : "no");
|
||||||
|
m_pWatchdog->setProcessConfig(command, elevate);
|
||||||
#else
|
#else
|
||||||
LOG_ERR("applying watchdog command not implemented on this platform");
|
LOG_ERR("applying watchdog command not implemented on this platform");
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -74,8 +89,9 @@ void DaemonApp::clearWatchdogCommand()
|
||||||
{
|
{
|
||||||
LOG_DEBUG("clearing watchdog command");
|
LOG_DEBUG("clearing watchdog command");
|
||||||
|
|
||||||
// Clear the setting to prevent it from being next time the daemon starts.
|
// Clear the persisted config path so the daemon does not auto-start the core on next boot.
|
||||||
setCommand("");
|
m_configFile.clear();
|
||||||
|
Settings::setValue(Settings::Daemon::ConfigFile);
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
m_pWatchdog->setProcessConfig("", false);
|
m_pWatchdog->setProcessConfig("", false);
|
||||||
|
|
@ -84,11 +100,11 @@ void DaemonApp::clearWatchdogCommand()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void DaemonApp::clearSettings() const
|
void DaemonApp::clearSettings()
|
||||||
{
|
{
|
||||||
LOG_INFO("clearing daemon settings");
|
LOG_INFO("clearing daemon settings");
|
||||||
Settings::setValue(Settings::Daemon::Command);
|
m_configFile.clear();
|
||||||
Settings::setValue(Settings::Daemon::Elevate);
|
Settings::setValue(Settings::Daemon::ConfigFile);
|
||||||
Settings::setValue(Settings::Daemon::LogFile);
|
Settings::setValue(Settings::Daemon::LogFile);
|
||||||
Settings::setValue(Settings::Daemon::LogLevel);
|
Settings::setValue(Settings::Daemon::LogLevel);
|
||||||
}
|
}
|
||||||
|
|
@ -98,8 +114,7 @@ void DaemonApp::connectIpcServer(const ipc::DaemonIpcServer *ipcServer) const
|
||||||
// Use direct connection as this object is on it's own thread,
|
// Use direct connection as this object is on it's own thread,
|
||||||
// and so is on a different event loop to the main Qt loop.
|
// and so is on a different event loop to the main Qt loop.
|
||||||
connect(ipcServer, &ipc::DaemonIpcServer::logLevelChanged, this, &DaemonApp::saveLogLevel, Qt::DirectConnection);
|
connect(ipcServer, &ipc::DaemonIpcServer::logLevelChanged, this, &DaemonApp::saveLogLevel, Qt::DirectConnection);
|
||||||
connect(ipcServer, &ipc::DaemonIpcServer::elevateModeChanged, this, &DaemonApp::setElevate, Qt::DirectConnection);
|
connect(ipcServer, &ipc::DaemonIpcServer::configFileChanged, this, &DaemonApp::setConfigFile, Qt::DirectConnection);
|
||||||
connect(ipcServer, &ipc::DaemonIpcServer::commandChanged, this, &DaemonApp::setCommand, Qt::DirectConnection);
|
|
||||||
connect(
|
connect(
|
||||||
ipcServer, &ipc::DaemonIpcServer::startProcessRequested, this, &DaemonApp::applyWatchdogCommand,
|
ipcServer, &ipc::DaemonIpcServer::startProcessRequested, this, &DaemonApp::applyWatchdogCommand,
|
||||||
Qt::DirectConnection
|
Qt::DirectConnection
|
||||||
|
|
@ -139,11 +154,11 @@ void DaemonApp::run(QThread &daemonThread)
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
m_pWatchdog = std::make_unique<MSWindowsWatchdog>(m_foreground, *m_pFileLogOutputter);
|
m_pWatchdog = std::make_unique<MSWindowsWatchdog>(m_foreground, *m_pFileLogOutputter);
|
||||||
|
|
||||||
auto command = Settings::value(Settings::Daemon::Command).toString().toStdString();
|
if (const auto persistedConfig = Settings::value(Settings::Daemon::ConfigFile).toString();
|
||||||
bool elevate = Settings::value(Settings::Daemon::Elevate).toBool();
|
!persistedConfig.isEmpty()) {
|
||||||
if (!command.empty()) {
|
LOG_DEBUG("using last known config file: %s", persistedConfig.toUtf8().constData());
|
||||||
LOG_DEBUG("using last known command: %s", command.c_str());
|
m_configFile = persistedConfig;
|
||||||
m_pWatchdog->setProcessConfig(command, elevate);
|
applyWatchdogCommand();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
|
|
@ -48,11 +46,10 @@ private:
|
||||||
int mainLoop();
|
int mainLoop();
|
||||||
int daemonLoop();
|
int daemonLoop();
|
||||||
void saveLogLevel(const QString &logLevel) const;
|
void saveLogLevel(const QString &logLevel) const;
|
||||||
void setElevate(bool elevate);
|
void setConfigFile(const QString &configFile);
|
||||||
void setCommand(const QString &command);
|
|
||||||
void applyWatchdogCommand() const;
|
void applyWatchdogCommand() const;
|
||||||
void clearWatchdogCommand();
|
void clearWatchdogCommand();
|
||||||
void clearSettings() const;
|
void clearSettings();
|
||||||
|
|
||||||
static void showConsole();
|
static void showConsole();
|
||||||
|
|
||||||
|
|
@ -63,7 +60,6 @@ private:
|
||||||
IEventQueue &m_events;
|
IEventQueue &m_events;
|
||||||
FileLogOutputter *m_pFileLogOutputter = nullptr;
|
FileLogOutputter *m_pFileLogOutputter = nullptr;
|
||||||
deskflow::core::ipc::DaemonIpcServer *m_ipcServer = nullptr;
|
deskflow::core::ipc::DaemonIpcServer *m_ipcServer = nullptr;
|
||||||
std::string m_command = "";
|
QString m_configFile;
|
||||||
bool m_elevate = false;
|
|
||||||
bool m_foreground = false;
|
bool m_foreground = false;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ public:
|
||||||
};
|
};
|
||||||
struct Daemon
|
struct Daemon
|
||||||
{
|
{
|
||||||
inline static const auto Command = QStringLiteral("daemon/command");
|
inline static const auto ConfigFile = QStringLiteral("daemon/configFile");
|
||||||
inline static const auto Elevate = QStringLiteral("daemon/elevate");
|
inline static const auto Elevate = QStringLiteral("daemon/elevate");
|
||||||
inline static const auto LogFile = QStringLiteral("daemon/logFile");
|
inline static const auto LogFile = QStringLiteral("daemon/logFile");
|
||||||
inline static const auto LogLevel = QStringLiteral("daemon/logLevel");
|
inline static const auto LogLevel = QStringLiteral("daemon/logLevel");
|
||||||
|
|
@ -225,7 +225,7 @@ private:
|
||||||
, Settings::Core::UseHooks
|
, Settings::Core::UseHooks
|
||||||
, Settings::Core::UseWlClipboard
|
, Settings::Core::UseWlClipboard
|
||||||
, Settings::Core::Language
|
, Settings::Core::Language
|
||||||
, Settings::Daemon::Command
|
, Settings::Daemon::ConfigFile
|
||||||
, Settings::Daemon::Elevate
|
, Settings::Daemon::Elevate
|
||||||
, Settings::Daemon::LogFile
|
, Settings::Daemon::LogFile
|
||||||
, Settings::Daemon::LogLevel
|
, Settings::Daemon::LogLevel
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,8 @@ void DaemonIpcServer::processCommand(QLocalSocket *clientSocket, const QString &
|
||||||
{
|
{
|
||||||
if (command == QStringLiteral("logLevel")) {
|
if (command == QStringLiteral("logLevel")) {
|
||||||
processLogLevel(clientSocket, parts);
|
processLogLevel(clientSocket, parts);
|
||||||
} else if (command == QStringLiteral("elevate")) {
|
} else if (command == QStringLiteral("configFile")) {
|
||||||
processElevate(clientSocket, parts);
|
processConfigFile(clientSocket, parts);
|
||||||
} else if (command == QStringLiteral("command")) {
|
|
||||||
processCommandMessage(clientSocket, parts);
|
|
||||||
} else if (command == QStringLiteral("start")) {
|
} else if (command == QStringLiteral("start")) {
|
||||||
LOG_DEBUG("daemon ipc server got start message");
|
LOG_DEBUG("daemon ipc server got start message");
|
||||||
Q_EMIT startProcessRequested();
|
Q_EMIT startProcessRequested();
|
||||||
|
|
@ -71,43 +69,23 @@ void DaemonIpcServer::processLogLevel(QLocalSocket *&clientSocket, const QString
|
||||||
writeToClientSocket(clientSocket, kAckMessage);
|
writeToClientSocket(clientSocket, kAckMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DaemonIpcServer::processElevate(QLocalSocket *&clientSocket, const QStringList &messageParts)
|
void DaemonIpcServer::processConfigFile(QLocalSocket *&clientSocket, const QStringList &messageParts)
|
||||||
{
|
{
|
||||||
if (messageParts.size() < 2) {
|
if (messageParts.size() < 2) {
|
||||||
LOG_ERR("daemon ipc server got invalid elevate message");
|
LOG_ERR("daemon ipc server got invalid config file message");
|
||||||
writeToClientSocket(clientSocket, kErrorMessage);
|
writeToClientSocket(clientSocket, kErrorMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &elevate = messageParts.at(1);
|
const auto &configFile = messageParts.at(1);
|
||||||
if (elevate != QStringLiteral("yes") && elevate != QStringLiteral("no")) {
|
if (configFile.isEmpty()) {
|
||||||
LOG_ERR("daemon ipc server got invalid elevate value: %s", elevate.toUtf8().constData());
|
LOG_ERR("daemon ipc server got empty config file path");
|
||||||
writeToClientSocket(clientSocket, kErrorMessage);
|
writeToClientSocket(clientSocket, kErrorMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG("daemon ipc server got new elevate value: %s", elevate.toUtf8().constData());
|
LOG_DEBUG("daemon ipc server got config file: %s", configFile.toUtf8().constData());
|
||||||
Q_EMIT elevateModeChanged(elevate == QStringLiteral("yes"));
|
Q_EMIT configFileChanged(configFile);
|
||||||
writeToClientSocket(clientSocket, kAckMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DaemonIpcServer::processCommandMessage(QLocalSocket *&clientSocket, const QStringList &messageParts)
|
|
||||||
{
|
|
||||||
if (messageParts.size() < 2) {
|
|
||||||
LOG_ERR("daemon ipc server got invalid command message");
|
|
||||||
writeToClientSocket(clientSocket, kErrorMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &command = messageParts.at(1);
|
|
||||||
if (command.isEmpty()) {
|
|
||||||
LOG_ERR("daemon ipc server got empty command");
|
|
||||||
writeToClientSocket(clientSocket, kErrorMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_DEBUG("daemon ipc server got new command: %s", command.toUtf8().constData());
|
|
||||||
Q_EMIT commandChanged(command);
|
|
||||||
writeToClientSocket(clientSocket, kAckMessage);
|
writeToClientSocket(clientSocket, kAckMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,7 @@ public:
|
||||||
private:
|
private:
|
||||||
void processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts) override;
|
void processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts) override;
|
||||||
void processLogLevel(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
void processLogLevel(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||||
void processElevate(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
void processConfigFile(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||||
void processCommandMessage(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QString m_logFilename;
|
const QString m_logFilename;
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,7 @@ public:
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void logLevelChanged(const QString &logLevel);
|
void logLevelChanged(const QString &logLevel);
|
||||||
void elevateModeChanged(bool elevate);
|
void configFileChanged(const QString &configFile);
|
||||||
void commandChanged(const QString &command);
|
|
||||||
void startProcessRequested();
|
void startProcessRequested();
|
||||||
void stopProcessRequested();
|
void stopProcessRequested();
|
||||||
void clearSettingsRequested();
|
void clearSettingsRequested();
|
||||||
|
|
|
||||||
|
|
@ -214,17 +214,18 @@ void CoreProcess::startForegroundProcess(const QStringList &args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreProcess::startProcessFromDaemon(const QStringList &args)
|
void CoreProcess::startProcessFromDaemon()
|
||||||
{
|
{
|
||||||
if (m_processState != ProcessState::Starting) {
|
if (m_processState != ProcessState::Starting) {
|
||||||
qFatal("core process must be in starting state");
|
qFatal("core process must be in starting state");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString commandQuoted = makeQuotedArgs(m_appPath, args);
|
const auto configFile = Settings::settingsFile();
|
||||||
qInfo("running command: %s", qPrintable(commandQuoted));
|
qInfo("sending start to daemon (config file: %s)", qPrintable(configFile));
|
||||||
|
|
||||||
auto sendStart = [this, commandQuoted] {
|
auto sendStart = [this, configFile] {
|
||||||
m_daemonIpcClient->sendStartProcess(commandQuoted, Settings::value(Settings::Daemon::Elevate).toBool());
|
m_daemonIpcClient->sendConfigFile(configFile);
|
||||||
|
m_daemonIpcClient->sendStartProcess();
|
||||||
setProcessState(ProcessState::Started);
|
setProcessState(ProcessState::Started);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -412,8 +413,7 @@ void CoreProcess::start(std::optional<ProcessMode> processModeOption)
|
||||||
if (processMode == ProcessMode::Desktop) {
|
if (processMode == ProcessMode::Desktop) {
|
||||||
startForegroundProcess(args);
|
startForegroundProcess(args);
|
||||||
} else if (processMode == ProcessMode::Service) {
|
} else if (processMode == ProcessMode::Service) {
|
||||||
args.append({QStringLiteral("--settings"), Settings::settingsFile()});
|
startProcessFromDaemon();
|
||||||
startProcessFromDaemon(args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_lastProcessMode = processMode;
|
m_lastProcessMode = processMode;
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ private Q_SLOTS:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void startForegroundProcess(const QStringList &args);
|
void startForegroundProcess(const QStringList &args);
|
||||||
void startProcessFromDaemon(const QStringList &args);
|
void startProcessFromDaemon();
|
||||||
void stopForegroundProcess() const;
|
void stopForegroundProcess() const;
|
||||||
void stopProcessFromDaemon();
|
void stopProcessFromDaemon();
|
||||||
QPair<bool, QString> persistServerConfig() const;
|
QPair<bool, QString> persistServerConfig() const;
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,13 @@ void DaemonIpcClient::sendLogLevel(const QString &logLevel)
|
||||||
sendMessage(QStringLiteral("logLevel=%1").arg(logLevel));
|
sendMessage(QStringLiteral("logLevel=%1").arg(logLevel));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DaemonIpcClient::sendStartProcess(const QString &command, bool elevate)
|
void DaemonIpcClient::sendConfigFile(const QString &configFile)
|
||||||
|
{
|
||||||
|
sendMessage(QStringLiteral("configFile=%1").arg(configFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DaemonIpcClient::sendStartProcess()
|
||||||
{
|
{
|
||||||
const auto elevateStr = elevate ? QStringLiteral("yes") : QStringLiteral("no");
|
|
||||||
sendMessage(QStringLiteral("elevate=%1").arg(elevateStr));
|
|
||||||
sendMessage(QStringLiteral("command=%1").arg(command));
|
|
||||||
sendMessage(QStringLiteral("start"));
|
sendMessage(QStringLiteral("start"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@ class DaemonIpcClient : public IpcClient
|
||||||
public:
|
public:
|
||||||
explicit DaemonIpcClient(QObject *parent = nullptr);
|
explicit DaemonIpcClient(QObject *parent = nullptr);
|
||||||
void sendLogLevel(const QString &logLevel);
|
void sendLogLevel(const QString &logLevel);
|
||||||
void sendStartProcess(const QString &command, bool elevate);
|
void sendConfigFile(const QString &configFile);
|
||||||
|
void sendStartProcess();
|
||||||
void sendStopProcess();
|
void sendStopProcess();
|
||||||
void sendClearSettings();
|
void sendClearSettings();
|
||||||
void requestLogPath();
|
void requestLogPath();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue