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
|
||||
|
||||
#include <string>
|
||||
#include <QCoreApplication>
|
||||
#include <QSettings>
|
||||
|
||||
using namespace deskflow::core;
|
||||
|
||||
|
|
@ -45,26 +46,40 @@ void DaemonApp::saveLogLevel(const QString &logLevel) const
|
|||
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");
|
||||
m_elevate = elevate;
|
||||
Settings::setValue(Settings::Daemon::Elevate, m_elevate);
|
||||
}
|
||||
|
||||
void DaemonApp::setCommand(const QString &command)
|
||||
{
|
||||
LOG_DEBUG("service command updated");
|
||||
Settings::setValue(Settings::Daemon::Command, command);
|
||||
m_command = command.toStdString();
|
||||
LOG_DEBUG("config file updated: %s", configFile.toUtf8().constData());
|
||||
m_configFile = configFile;
|
||||
Settings::setValue(Settings::Daemon::ConfigFile, configFile);
|
||||
}
|
||||
|
||||
void DaemonApp::applyWatchdogCommand() const
|
||||
{
|
||||
LOG_DEBUG("applying watchdog command");
|
||||
|
||||
#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
|
||||
LOG_ERR("applying watchdog command not implemented on this platform");
|
||||
#endif
|
||||
|
|
@ -74,8 +89,9 @@ void DaemonApp::clearWatchdogCommand()
|
|||
{
|
||||
LOG_DEBUG("clearing watchdog command");
|
||||
|
||||
// Clear the setting to prevent it from being next time the daemon starts.
|
||||
setCommand("");
|
||||
// Clear the persisted config path so the daemon does not auto-start the core on next boot.
|
||||
m_configFile.clear();
|
||||
Settings::setValue(Settings::Daemon::ConfigFile);
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
m_pWatchdog->setProcessConfig("", false);
|
||||
|
|
@ -84,11 +100,11 @@ void DaemonApp::clearWatchdogCommand()
|
|||
#endif
|
||||
}
|
||||
|
||||
void DaemonApp::clearSettings() const
|
||||
void DaemonApp::clearSettings()
|
||||
{
|
||||
LOG_INFO("clearing daemon settings");
|
||||
Settings::setValue(Settings::Daemon::Command);
|
||||
Settings::setValue(Settings::Daemon::Elevate);
|
||||
m_configFile.clear();
|
||||
Settings::setValue(Settings::Daemon::ConfigFile);
|
||||
Settings::setValue(Settings::Daemon::LogFile);
|
||||
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,
|
||||
// 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::elevateModeChanged, this, &DaemonApp::setElevate, Qt::DirectConnection);
|
||||
connect(ipcServer, &ipc::DaemonIpcServer::commandChanged, this, &DaemonApp::setCommand, Qt::DirectConnection);
|
||||
connect(ipcServer, &ipc::DaemonIpcServer::configFileChanged, this, &DaemonApp::setConfigFile, Qt::DirectConnection);
|
||||
connect(
|
||||
ipcServer, &ipc::DaemonIpcServer::startProcessRequested, this, &DaemonApp::applyWatchdogCommand,
|
||||
Qt::DirectConnection
|
||||
|
|
@ -139,11 +154,11 @@ void DaemonApp::run(QThread &daemonThread)
|
|||
#if defined(Q_OS_WIN)
|
||||
m_pWatchdog = std::make_unique<MSWindowsWatchdog>(m_foreground, *m_pFileLogOutputter);
|
||||
|
||||
auto command = Settings::value(Settings::Daemon::Command).toString().toStdString();
|
||||
bool elevate = Settings::value(Settings::Daemon::Elevate).toBool();
|
||||
if (!command.empty()) {
|
||||
LOG_DEBUG("using last known command: %s", command.c_str());
|
||||
m_pWatchdog->setProcessConfig(command, elevate);
|
||||
if (const auto persistedConfig = Settings::value(Settings::Daemon::ConfigFile).toString();
|
||||
!persistedConfig.isEmpty()) {
|
||||
LOG_DEBUG("using last known config file: %s", persistedConfig.toUtf8().constData());
|
||||
m_configFile = persistedConfig;
|
||||
applyWatchdogCommand();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
|
||||
|
|
@ -48,11 +46,10 @@ private:
|
|||
int mainLoop();
|
||||
int daemonLoop();
|
||||
void saveLogLevel(const QString &logLevel) const;
|
||||
void setElevate(bool elevate);
|
||||
void setCommand(const QString &command);
|
||||
void setConfigFile(const QString &configFile);
|
||||
void applyWatchdogCommand() const;
|
||||
void clearWatchdogCommand();
|
||||
void clearSettings() const;
|
||||
void clearSettings();
|
||||
|
||||
static void showConsole();
|
||||
|
||||
|
|
@ -63,7 +60,6 @@ private:
|
|||
IEventQueue &m_events;
|
||||
FileLogOutputter *m_pFileLogOutputter = nullptr;
|
||||
deskflow::core::ipc::DaemonIpcServer *m_ipcServer = nullptr;
|
||||
std::string m_command = "";
|
||||
bool m_elevate = false;
|
||||
QString m_configFile;
|
||||
bool m_foreground = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ public:
|
|||
};
|
||||
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 LogFile = QStringLiteral("daemon/logFile");
|
||||
inline static const auto LogLevel = QStringLiteral("daemon/logLevel");
|
||||
|
|
@ -225,7 +225,7 @@ private:
|
|||
, Settings::Core::UseHooks
|
||||
, Settings::Core::UseWlClipboard
|
||||
, Settings::Core::Language
|
||||
, Settings::Daemon::Command
|
||||
, Settings::Daemon::ConfigFile
|
||||
, Settings::Daemon::Elevate
|
||||
, Settings::Daemon::LogFile
|
||||
, Settings::Daemon::LogLevel
|
||||
|
|
|
|||
|
|
@ -27,10 +27,8 @@ void DaemonIpcServer::processCommand(QLocalSocket *clientSocket, const QString &
|
|||
{
|
||||
if (command == QStringLiteral("logLevel")) {
|
||||
processLogLevel(clientSocket, parts);
|
||||
} else if (command == QStringLiteral("elevate")) {
|
||||
processElevate(clientSocket, parts);
|
||||
} else if (command == QStringLiteral("command")) {
|
||||
processCommandMessage(clientSocket, parts);
|
||||
} else if (command == QStringLiteral("configFile")) {
|
||||
processConfigFile(clientSocket, parts);
|
||||
} else if (command == QStringLiteral("start")) {
|
||||
LOG_DEBUG("daemon ipc server got start message");
|
||||
Q_EMIT startProcessRequested();
|
||||
|
|
@ -71,43 +69,23 @@ void DaemonIpcServer::processLogLevel(QLocalSocket *&clientSocket, const QString
|
|||
writeToClientSocket(clientSocket, kAckMessage);
|
||||
}
|
||||
|
||||
void DaemonIpcServer::processElevate(QLocalSocket *&clientSocket, const QStringList &messageParts)
|
||||
void DaemonIpcServer::processConfigFile(QLocalSocket *&clientSocket, const QStringList &messageParts)
|
||||
{
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &elevate = messageParts.at(1);
|
||||
if (elevate != QStringLiteral("yes") && elevate != QStringLiteral("no")) {
|
||||
LOG_ERR("daemon ipc server got invalid elevate value: %s", elevate.toUtf8().constData());
|
||||
const auto &configFile = messageParts.at(1);
|
||||
if (configFile.isEmpty()) {
|
||||
LOG_ERR("daemon ipc server got empty config file path");
|
||||
writeToClientSocket(clientSocket, kErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG("daemon ipc server got new elevate value: %s", elevate.toUtf8().constData());
|
||||
Q_EMIT elevateModeChanged(elevate == QStringLiteral("yes"));
|
||||
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);
|
||||
LOG_DEBUG("daemon ipc server got config file: %s", configFile.toUtf8().constData());
|
||||
Q_EMIT configFileChanged(configFile);
|
||||
writeToClientSocket(clientSocket, kAckMessage);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,8 +25,7 @@ public:
|
|||
private:
|
||||
void processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts) override;
|
||||
void processLogLevel(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||
void processElevate(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||
void processCommandMessage(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||
void processConfigFile(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||
|
||||
private:
|
||||
const QString m_logFilename;
|
||||
|
|
|
|||
|
|
@ -27,8 +27,7 @@ public:
|
|||
|
||||
Q_SIGNALS:
|
||||
void logLevelChanged(const QString &logLevel);
|
||||
void elevateModeChanged(bool elevate);
|
||||
void commandChanged(const QString &command);
|
||||
void configFileChanged(const QString &configFile);
|
||||
void startProcessRequested();
|
||||
void stopProcessRequested();
|
||||
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) {
|
||||
qFatal("core process must be in starting state");
|
||||
}
|
||||
|
||||
QString commandQuoted = makeQuotedArgs(m_appPath, args);
|
||||
qInfo("running command: %s", qPrintable(commandQuoted));
|
||||
const auto configFile = Settings::settingsFile();
|
||||
qInfo("sending start to daemon (config file: %s)", qPrintable(configFile));
|
||||
|
||||
auto sendStart = [this, commandQuoted] {
|
||||
m_daemonIpcClient->sendStartProcess(commandQuoted, Settings::value(Settings::Daemon::Elevate).toBool());
|
||||
auto sendStart = [this, configFile] {
|
||||
m_daemonIpcClient->sendConfigFile(configFile);
|
||||
m_daemonIpcClient->sendStartProcess();
|
||||
setProcessState(ProcessState::Started);
|
||||
};
|
||||
|
||||
|
|
@ -412,8 +413,7 @@ void CoreProcess::start(std::optional<ProcessMode> processModeOption)
|
|||
if (processMode == ProcessMode::Desktop) {
|
||||
startForegroundProcess(args);
|
||||
} else if (processMode == ProcessMode::Service) {
|
||||
args.append({QStringLiteral("--settings"), Settings::settingsFile()});
|
||||
startProcessFromDaemon(args);
|
||||
startProcessFromDaemon();
|
||||
}
|
||||
|
||||
m_lastProcessMode = processMode;
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ private Q_SLOTS:
|
|||
|
||||
private:
|
||||
void startForegroundProcess(const QStringList &args);
|
||||
void startProcessFromDaemon(const QStringList &args);
|
||||
void startProcessFromDaemon();
|
||||
void stopForegroundProcess() const;
|
||||
void stopProcessFromDaemon();
|
||||
QPair<bool, QString> persistServerConfig() const;
|
||||
|
|
|
|||
|
|
@ -21,11 +21,13 @@ void DaemonIpcClient::sendLogLevel(const QString &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"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ class DaemonIpcClient : public IpcClient
|
|||
public:
|
||||
explicit DaemonIpcClient(QObject *parent = nullptr);
|
||||
void sendLogLevel(const QString &logLevel);
|
||||
void sendStartProcess(const QString &command, bool elevate);
|
||||
void sendConfigFile(const QString &configFile);
|
||||
void sendStartProcess();
|
||||
void sendStopProcess();
|
||||
void sendClearSettings();
|
||||
void requestLogPath();
|
||||
|
|
|
|||
Loading…
Reference in a new issue