added process elevation support to the relauncher, very experimental, has some bugs.
This commit is contained in:
parent
cf5a7d297d
commit
268f3a99bb
|
@ -27,14 +27,14 @@
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
<layout class="QGridLayout">
|
<layout class="QGridLayout">
|
||||||
<item row="6" column="6">
|
<item row="6" column="7">
|
||||||
<widget class="QPushButton" name="m_pButtonToggleStart">
|
<widget class="QPushButton" name="m_pButtonToggleStart">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Start</string>
|
<string>&Start</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0" colspan="7">
|
<item row="1" column="0" colspan="8">
|
||||||
<widget class="QGroupBox" name="m_pGroupServer">
|
<widget class="QGroupBox" name="m_pGroupServer">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
@ -127,7 +127,7 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" colspan="7">
|
<item row="2" column="0" colspan="8">
|
||||||
<widget class="QGroupBox" name="m_pGroupClient">
|
<widget class="QGroupBox" name="m_pGroupClient">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
@ -168,7 +168,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" colspan="7">
|
<item row="3" column="0" colspan="8">
|
||||||
<widget class="QGroupBox" name="m_pGroupLog">
|
<widget class="QGroupBox" name="m_pGroupLog">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Log</string>
|
<string>Log</string>
|
||||||
|
@ -223,7 +223,7 @@
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="pixmap">
|
<property name="pixmap">
|
||||||
<pixmap resource="Synergy.qrc">:/res/icons/16x16/warning.png</pixmap>
|
<pixmap>:/res/icons/16x16/warning.png</pixmap>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -237,6 +237,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="6" column="6">
|
||||||
|
<widget class="QCheckBox" name="m_pElevateCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Elevate</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<action name="m_pActionAbout">
|
<action name="m_pActionAbout">
|
||||||
|
@ -323,9 +330,7 @@
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources/>
|
||||||
<include location="Synergy.qrc"/>
|
|
||||||
</resources>
|
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
<sender>m_pButtonToggleStart</sender>
|
<sender>m_pButtonToggleStart</sender>
|
||||||
|
|
|
@ -15,9 +15,11 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// this class is a duplicate of /src/lib/ipc/Ipc.cpp
|
||||||
|
|
||||||
#include "Ipc.h"
|
#include "Ipc.h"
|
||||||
|
|
||||||
const char* kIpcMsgHello = "IHEL%1i";
|
const char* kIpcMsgHello = "IHEL%1i";
|
||||||
const char* kIpcMsgLogLine = "ILOG%s";
|
const char* kIpcMsgLogLine = "ILOG%s";
|
||||||
const char* kIpcMsgCommand = "ICMD%s";
|
const char* kIpcMsgCommand = "ICMD%s%1i";
|
||||||
const char* kIpcMsgShutdown = "ISDN";
|
const char* kIpcMsgShutdown = "ISDN";
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// this class is a duplicate of /src/lib/ipc/Ipc.h
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define IPC_HOST "127.0.0.1"
|
#define IPC_HOST "127.0.0.1"
|
||||||
|
|
|
@ -43,7 +43,7 @@ void IpcClient::connected()
|
||||||
{
|
{
|
||||||
char typeBuf[1];
|
char typeBuf[1];
|
||||||
typeBuf[0] = kIpcClientGui;
|
typeBuf[0] = kIpcClientGui;
|
||||||
write(kIpcHello, 1, typeBuf);
|
sendHello();
|
||||||
|
|
||||||
infoMessage("connection established");
|
infoMessage("connection established");
|
||||||
}
|
}
|
||||||
|
@ -89,30 +89,34 @@ void IpcClient::retryConnect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IpcClient::write(int code, int length, const char* data)
|
void IpcClient::sendHello()
|
||||||
|
{
|
||||||
|
QDataStream stream(m_Socket);
|
||||||
|
stream.writeRawData(kIpcMsgHello, 4);
|
||||||
|
|
||||||
|
char typeBuf[1];
|
||||||
|
typeBuf[0] = kIpcClientGui;
|
||||||
|
stream.writeRawData(typeBuf, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpcClient::sendCommand(const QString& command, bool elevate)
|
||||||
{
|
{
|
||||||
QDataStream stream(m_Socket);
|
QDataStream stream(m_Socket);
|
||||||
|
|
||||||
switch (code) {
|
stream.writeRawData(kIpcMsgCommand, 4);
|
||||||
case kIpcHello:
|
|
||||||
stream.writeRawData(kIpcMsgHello, 4);
|
|
||||||
stream.writeRawData(data, 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kIpcCommand: {
|
std::string stdStringCommand = command.toStdString();
|
||||||
char lenBuf[4];
|
const char* charCommand = stdStringCommand.c_str();
|
||||||
intToBytes(length, lenBuf, 4);
|
int length = strlen(charCommand);
|
||||||
|
|
||||||
stream.writeRawData(kIpcMsgCommand, 4);
|
char lenBuf[4];
|
||||||
stream.writeRawData(lenBuf, 4);
|
intToBytes(length, lenBuf, 4);
|
||||||
stream.writeRawData(data, length);
|
stream.writeRawData(lenBuf, 4);
|
||||||
break;
|
stream.writeRawData(charCommand, length);
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
char elevateBuf[1];
|
||||||
std::cerr << "message type not supported: " << code << std::endl;
|
elevateBuf[0] = elevate ? 1 : 0;
|
||||||
break;
|
stream.writeRawData(elevateBuf, 1);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IpcClient::handleReadLogLine(const QString& text)
|
void IpcClient::handleReadLogLine(const QString& text)
|
||||||
|
|
|
@ -31,7 +31,8 @@ public:
|
||||||
IpcClient();
|
IpcClient();
|
||||||
virtual ~IpcClient();
|
virtual ~IpcClient();
|
||||||
|
|
||||||
void write(int code, int length, const char* data);
|
void sendHello();
|
||||||
|
void sendCommand(const QString& command, bool elevate);
|
||||||
void connectToHost();
|
void connectToHost();
|
||||||
void disconnectFromHost();
|
void disconnectFromHost();
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,9 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) :
|
||||||
m_pTrayIcon(NULL),
|
m_pTrayIcon(NULL),
|
||||||
m_pTrayIconMenu(NULL),
|
m_pTrayIconMenu(NULL),
|
||||||
m_alreadyHidden(false),
|
m_alreadyHidden(false),
|
||||||
m_SetupWizard(NULL)
|
m_SetupWizard(NULL),
|
||||||
|
m_ElevateProcess(false),
|
||||||
|
m_SuppressElevateWarning(false)
|
||||||
{
|
{
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
|
|
||||||
|
@ -85,6 +87,9 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) :
|
||||||
connect(&m_IpcClient, SIGNAL(errorMessage(const QString&)), this, SLOT(appendLogError(const QString&)));
|
connect(&m_IpcClient, SIGNAL(errorMessage(const QString&)), this, SLOT(appendLogError(const QString&)));
|
||||||
connect(&m_IpcClient, SIGNAL(infoMessage(const QString&)), this, SLOT(appendLogNote(const QString&)));
|
connect(&m_IpcClient, SIGNAL(infoMessage(const QString&)), this, SLOT(appendLogNote(const QString&)));
|
||||||
m_IpcClient.connectToHost();
|
m_IpcClient.connectToHost();
|
||||||
|
#else
|
||||||
|
// elevate checkbox is only useful on ms windows.
|
||||||
|
m_pElevateCheckBox->hide();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,13 +138,15 @@ void MainWindow::onModeChanged(bool firstRun, bool forceServiceApply)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// cause the service to stop creating processes.
|
// cause the service to stop creating processes.
|
||||||
sendDaemonCommand("", false);
|
m_IpcClient.sendCommand("", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((appConfig().processMode() == Desktop) && !firstRun && appConfig().autoConnect())
|
if ((appConfig().processMode() == Desktop) && !firstRun && appConfig().autoConnect())
|
||||||
{
|
{
|
||||||
startSynergy();
|
startSynergy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_pElevateCheckBox->setEnabled(appConfig().processMode() == Service);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::refreshStartButton()
|
void MainWindow::refreshStartButton()
|
||||||
|
@ -221,6 +228,10 @@ void MainWindow::loadSettings()
|
||||||
m_pLineEditConfigFile->setText(settings().value("configFile", QDir::homePath() + "/" + synergyConfigName).toString());
|
m_pLineEditConfigFile->setText(settings().value("configFile", QDir::homePath() + "/" + synergyConfigName).toString());
|
||||||
m_pGroupClient->setChecked(settings().value("groupClientChecked", true).toBool());
|
m_pGroupClient->setChecked(settings().value("groupClientChecked", true).toBool());
|
||||||
m_pLineEditHostname->setText(settings().value("serverHostname").toString());
|
m_pLineEditHostname->setText(settings().value("serverHostname").toString());
|
||||||
|
|
||||||
|
m_SuppressElevateWarning = true;
|
||||||
|
m_pElevateCheckBox->setChecked(settings().value("elevateChecked", false).toBool());
|
||||||
|
m_SuppressElevateWarning = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::initConnections()
|
void MainWindow::initConnections()
|
||||||
|
@ -452,7 +463,7 @@ void MainWindow::startSynergy()
|
||||||
if (serviceMode)
|
if (serviceMode)
|
||||||
{
|
{
|
||||||
QString command(app + " " + args.join(" "));
|
QString command(app + " " + args.join(" "));
|
||||||
sendDaemonCommand(command, true);
|
m_IpcClient.sendCommand(command, m_ElevateProcess);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -702,14 +713,29 @@ void MainWindow::on_m_pButtonConfigureServer_clicked()
|
||||||
dlg.exec();
|
dlg.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::sendDaemonCommand(const QString& command, bool showErrors)
|
|
||||||
{
|
|
||||||
std::string s = command.toStdString();
|
|
||||||
const char* data = s.c_str();
|
|
||||||
m_IpcClient.write(kIpcCommand, strlen(data), data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::on_m_pActionWizard_triggered()
|
void MainWindow::on_m_pActionWizard_triggered()
|
||||||
{
|
{
|
||||||
m_SetupWizard->show();
|
m_SetupWizard->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_m_pElevateCheckBox_toggled(bool checked)
|
||||||
|
{
|
||||||
|
if (checked && !m_SuppressElevateWarning) {
|
||||||
|
int r = QMessageBox::warning(
|
||||||
|
this, tr("Elevate Synergy"),
|
||||||
|
tr("Are you sure you want to elevate Synergy?\n\n"
|
||||||
|
"This allows Synergy to interact with elevated processes "
|
||||||
|
"and the UAC dialog, but can cause problems with non-elevated "
|
||||||
|
"processes. Elevate Synergy only if you really need to."),
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
|
if (r != QMessageBox::Yes) {
|
||||||
|
m_pElevateCheckBox->setChecked(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ElevateProcess = checked;
|
||||||
|
settings().setValue("elevateChecked", checked);
|
||||||
|
settings().sync();
|
||||||
|
}
|
||||||
|
|
|
@ -104,6 +104,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase
|
||||||
void on_m_pActionAbout_triggered();
|
void on_m_pActionAbout_triggered();
|
||||||
void on_m_pActionSettings_triggered();
|
void on_m_pActionSettings_triggered();
|
||||||
void on_m_pActionWizard_triggered();
|
void on_m_pActionWizard_triggered();
|
||||||
|
void on_m_pElevateCheckBox_toggled(bool checked);
|
||||||
void synergyFinished(int exitCode, QProcess::ExitStatus);
|
void synergyFinished(int exitCode, QProcess::ExitStatus);
|
||||||
void iconActivated(QSystemTrayIcon::ActivationReason reason);
|
void iconActivated(QSystemTrayIcon::ActivationReason reason);
|
||||||
void startSynergy();
|
void startSynergy();
|
||||||
|
@ -132,7 +133,6 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase
|
||||||
bool clientArgs(QStringList& args, QString& app);
|
bool clientArgs(QStringList& args, QString& app);
|
||||||
bool serverArgs(QStringList& args, QString& app);
|
bool serverArgs(QStringList& args, QString& app);
|
||||||
void setStatus(const QString& status);
|
void setStatus(const QString& status);
|
||||||
void sendDaemonCommand(const QString& command, bool showErrors);
|
|
||||||
void sendIpcMessage(qIpcMessageType type, const char* buffer, bool showErrors);
|
void sendIpcMessage(qIpcMessageType type, const char* buffer, bool showErrors);
|
||||||
void onModeChanged(bool firstRun, bool forceServiceApply);
|
void onModeChanged(bool firstRun, bool forceServiceApply);
|
||||||
|
|
||||||
|
@ -149,6 +149,8 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase
|
||||||
VersionChecker m_versionChecker;
|
VersionChecker m_versionChecker;
|
||||||
SetupWizard* m_SetupWizard;
|
SetupWizard* m_SetupWizard;
|
||||||
IpcClient m_IpcClient;
|
IpcClient m_IpcClient;
|
||||||
|
bool m_ElevateProcess;
|
||||||
|
bool m_SuppressElevateWarning;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -173,10 +173,11 @@ CIpcCommandMessage*
|
||||||
CIpcClientProxy::parseCommand()
|
CIpcClientProxy::parseCommand()
|
||||||
{
|
{
|
||||||
CString command;
|
CString command;
|
||||||
CProtocolUtil::readf(&m_stream, kIpcMsgCommand + 4, &command);
|
UInt8 elevate;
|
||||||
|
CProtocolUtil::readf(&m_stream, kIpcMsgCommand + 4, &command, &elevate);
|
||||||
|
|
||||||
// must be deleted by event handler.
|
// must be deleted by event handler.
|
||||||
return new CIpcCommandMessage(command);
|
return new CIpcCommandMessage(command, elevate != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -56,9 +56,10 @@ CIpcLogLineMessage::~CIpcLogLineMessage()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CIpcCommandMessage::CIpcCommandMessage(const CString& command) :
|
CIpcCommandMessage::CIpcCommandMessage(const CString& command, bool elevate) :
|
||||||
CIpcMessage(kIpcCommand),
|
CIpcMessage(kIpcCommand),
|
||||||
m_command(command)
|
m_command(command),
|
||||||
|
m_elevate(elevate)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,12 +69,16 @@ private:
|
||||||
|
|
||||||
class CIpcCommandMessage : public CIpcMessage {
|
class CIpcCommandMessage : public CIpcMessage {
|
||||||
public:
|
public:
|
||||||
CIpcCommandMessage(const CString& command);
|
CIpcCommandMessage(const CString& command, bool elevate);
|
||||||
virtual ~CIpcCommandMessage();
|
virtual ~CIpcCommandMessage();
|
||||||
|
|
||||||
//! Gets the command.
|
//! Gets the command.
|
||||||
CString command() const { return m_command; }
|
CString command() const { return m_command; }
|
||||||
|
|
||||||
|
//! Gets whether or not the process should be elevated on MS Windows.
|
||||||
|
bool elevate() const { return m_elevate; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CString m_command;
|
CString m_command;
|
||||||
|
bool m_elevate;
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,5 +19,5 @@
|
||||||
|
|
||||||
const char* kIpcMsgHello = "IHEL%1i";
|
const char* kIpcMsgHello = "IHEL%1i";
|
||||||
const char* kIpcMsgLogLine = "ILOG%s";
|
const char* kIpcMsgLogLine = "ILOG%s";
|
||||||
const char* kIpcMsgCommand = "ICMD%s";
|
const char* kIpcMsgCommand = "ICMD%s%1i";
|
||||||
const char* kIpcMsgShutdown = "ISDN";
|
const char* kIpcMsgShutdown = "ISDN";
|
||||||
|
|
|
@ -33,7 +33,19 @@ enum EIpcClientType {
|
||||||
kIpcClientNode,
|
kIpcClientNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// handshake: node/gui -> daemon
|
||||||
|
// $1 = type, the client identifies it's self as gui or node (synergyc/s).
|
||||||
extern const char* kIpcMsgHello;
|
extern const char* kIpcMsgHello;
|
||||||
|
|
||||||
|
// log line: daemon -> gui
|
||||||
|
// $1 = aggregate log lines collected from synergys/c or the daemon itself.
|
||||||
extern const char* kIpcMsgLogLine;
|
extern const char* kIpcMsgLogLine;
|
||||||
|
|
||||||
|
// command: gui -> daemon
|
||||||
|
// $1 = command; the command for the daemon to launch, typically the full
|
||||||
|
// path to synergys/c. $2 = true when process must be elevated on ms windows.
|
||||||
extern const char* kIpcMsgCommand;
|
extern const char* kIpcMsgCommand;
|
||||||
|
|
||||||
|
// shutdown: daemon -> node
|
||||||
|
// the daemon tells synergys/c to shut down gracefully.
|
||||||
extern const char* kIpcMsgShutdown;
|
extern const char* kIpcMsgShutdown;
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
#include <Tlhelp32.h>
|
#include <Tlhelp32.h>
|
||||||
#include <UserEnv.h>
|
#include <UserEnv.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <Wtsapi32.h>
|
||||||
|
#include <Shellapi.h>
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
kOutputBufferSize = 4096
|
kOutputBufferSize = 4096
|
||||||
|
@ -51,7 +53,8 @@ CMSWindowsRelauncher::CMSWindowsRelauncher(
|
||||||
m_stdOutWrite(NULL),
|
m_stdOutWrite(NULL),
|
||||||
m_stdOutRead(NULL),
|
m_stdOutRead(NULL),
|
||||||
m_ipcServer(ipcServer),
|
m_ipcServer(ipcServer),
|
||||||
m_ipcLogOutputter(ipcLogOutputter)
|
m_ipcLogOutputter(ipcLogOutputter),
|
||||||
|
m_elevateProcess(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +93,7 @@ CMSWindowsRelauncher::getSessionId()
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL
|
BOOL
|
||||||
CMSWindowsRelauncher::winlogonInSession(DWORD sessionId, PHANDLE process)
|
CMSWindowsRelauncher::isProcessInSession(const char* name, DWORD sessionId, PHANDLE process)
|
||||||
{
|
{
|
||||||
// first we need to take a snapshot of the running processes
|
// first we need to take a snapshot of the running processes
|
||||||
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
|
@ -138,9 +141,8 @@ CMSWindowsRelauncher::winlogonInSession(DWORD sessionId, PHANDLE process)
|
||||||
// store the names so we can record them for debug
|
// store the names so we can record them for debug
|
||||||
nameList.push_back(entry.szExeFile);
|
nameList.push_back(entry.szExeFile);
|
||||||
|
|
||||||
if (_stricmp(entry.szExeFile, "winlogon.exe") == 0) {
|
if (_stricmp(entry.szExeFile, name) == 0) {
|
||||||
pid = entry.th32ProcessID;
|
pid = entry.th32ProcessID;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,67 +169,97 @@ CMSWindowsRelauncher::winlogonInSession(DWORD sessionId, PHANDLE process)
|
||||||
nameListJoin.append(", ");
|
nameListJoin.append(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "checked processes while looking for winlogon.exe: %s",
|
LOG((CLOG_DEBUG "processes in session %d: %s",
|
||||||
nameListJoin.c_str()));
|
sessionId, nameListJoin.c_str()));
|
||||||
|
|
||||||
CloseHandle(snapshot);
|
CloseHandle(snapshot);
|
||||||
|
|
||||||
if (pid) {
|
if (pid) {
|
||||||
// now get the process so we can get the process, with which
|
// now get the process so we can get the process, with which
|
||||||
// we'll use to get the process token.
|
// we'll use to get the process token.
|
||||||
|
LOG((CLOG_DEBUG "found %s in session %i", name, sessionId));
|
||||||
*process = OpenProcess(MAXIMUM_ALLOWED, FALSE, pid);
|
*process = OpenProcess(MAXIMUM_ALLOWED, FALSE, pid);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG((CLOG_DEBUG "could not find winlogon.exe in session %i", sessionId));
|
LOG((CLOG_DEBUG "could not find %s in session %i", name, sessionId));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets the current user (so we can launch under their session)
|
HANDLE
|
||||||
HANDLE
|
CMSWindowsRelauncher::duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security)
|
||||||
CMSWindowsRelauncher::getCurrentUserToken(DWORD sessionId, LPSECURITY_ATTRIBUTES security)
|
|
||||||
{
|
{
|
||||||
HANDLE currentToken;
|
HANDLE sourceToken;
|
||||||
HANDLE winlogonProcess;
|
|
||||||
|
|
||||||
if (winlogonInSession(sessionId, &winlogonProcess)) {
|
BOOL tokenRet = OpenProcessToken(
|
||||||
|
process,
|
||||||
|
TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS,
|
||||||
|
&sourceToken);
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "session %i has winlogon.exe", sessionId));
|
if (!tokenRet) {
|
||||||
|
LOG((CLOG_ERR "could not open token, process handle: %d (error: %i)", process, GetLastError()));
|
||||||
// get the token, so we can re-launch with this token
|
return NULL;
|
||||||
// -- do we really need all these access bits?
|
|
||||||
BOOL tokenRet = OpenProcessToken(
|
|
||||||
winlogonProcess,
|
|
||||||
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY |
|
|
||||||
TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY |
|
|
||||||
TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
|
|
||||||
¤tToken);
|
|
||||||
|
|
||||||
if (!tokenRet) {
|
|
||||||
LOG((CLOG_ERR "could not open token (error: %i)", GetLastError()));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
|
LOG((CLOG_ERR "got token %i, duplicating", sourceToken));
|
||||||
|
|
||||||
LOG((CLOG_ERR "session %i does not have winlogon.exe "
|
HANDLE newToken;
|
||||||
"which is needed for re-launch", sessionId));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE primaryToken;
|
|
||||||
BOOL duplicateRet = DuplicateTokenEx(
|
BOOL duplicateRet = DuplicateTokenEx(
|
||||||
currentToken, MAXIMUM_ALLOWED, security,
|
sourceToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, security,
|
||||||
SecurityImpersonation, TokenPrimary, &primaryToken);
|
SecurityImpersonation, TokenPrimary, &newToken);
|
||||||
|
|
||||||
if (!duplicateRet) {
|
if (!duplicateRet) {
|
||||||
LOG((CLOG_ERR "could not duplicate token %i (error: %i)",
|
LOG((CLOG_ERR "could not duplicate token %i (error: %i)",
|
||||||
currentToken, GetLastError()));
|
sourceToken, GetLastError()));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG((CLOG_DEBUG "duplicated, new token: %i", newToken));
|
||||||
|
return newToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
// use either an elevated token (winlogon) or the user's session
|
||||||
|
// token (non-elevated). processes launched with a non-elevated token
|
||||||
|
// cannot interact with elevated processes.
|
||||||
|
HANDLE
|
||||||
|
CMSWindowsRelauncher::getUserToken(DWORD sessionId, LPSECURITY_ATTRIBUTES security)
|
||||||
|
{
|
||||||
|
if (m_elevateProcess) {
|
||||||
|
HANDLE process;
|
||||||
|
if (isProcessInSession("winlogon.exe", sessionId, &process)) {
|
||||||
|
return duplicateProcessToken(process, security);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOG((CLOG_ERR "could not find token in session %d", sessionId));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return getSessionToken(sessionId, security);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE
|
||||||
|
CMSWindowsRelauncher::getSessionToken(DWORD sessionId, LPSECURITY_ATTRIBUTES security)
|
||||||
|
{
|
||||||
|
HANDLE sourceToken;
|
||||||
|
if (!WTSQueryUserToken(sessionId, &sourceToken)) {
|
||||||
|
LOG((CLOG_ERR "could not get token from session %d (error: %i)", sessionId, GetLastError()));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HANDLE newToken;
|
||||||
|
if (!DuplicateTokenEx(
|
||||||
|
sourceToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, security,
|
||||||
|
SecurityImpersonation, TokenPrimary, &newToken)) {
|
||||||
|
|
||||||
return primaryToken;
|
LOG((CLOG_ERR "could not duplicate token (error: %i)", GetLastError()));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG((CLOG_DEBUG "duplicated, new token: %i", newToken));
|
||||||
|
return newToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -319,67 +351,64 @@ CMSWindowsRelauncher::mainLoop(void*)
|
||||||
SECURITY_ATTRIBUTES sa;
|
SECURITY_ATTRIBUTES sa;
|
||||||
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
|
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
|
||||||
|
|
||||||
// get the token for the user in active session, which is the
|
HANDLE userToken = getUserToken(sessionId, &sa);
|
||||||
// one receiving input from mouse and keyboard.
|
if (userToken == NULL) {
|
||||||
HANDLE userToken = getCurrentUserToken(sessionId, &sa);
|
// HACK: trigger retry mechanism.
|
||||||
|
launched = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (userToken != 0) {
|
std::string cmd = command();
|
||||||
LOG((CLOG_DEBUG "got user token to launch new process"));
|
if (cmd == "") {
|
||||||
|
// this appears on first launch when the user hasn't configured
|
||||||
|
// anything yet, so don't show it as a warning, only show it as
|
||||||
|
// debug to devs to let them know why nothing happened.
|
||||||
|
LOG((CLOG_DEBUG "nothing to launch, no command specified."));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
std::string cmd = command();
|
// in case reusing process info struct
|
||||||
if (cmd == "") {
|
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
|
||||||
// this appears on first launch when the user hasn't configured
|
|
||||||
// anything yet, so don't show it as a warning, only show it as
|
|
||||||
// debug to devs to let them know why nothing happened.
|
|
||||||
LOG((CLOG_DEBUG "nothing to launch, no command specified."));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// in case reusing process info struct
|
STARTUPINFO si;
|
||||||
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
|
ZeroMemory(&si, sizeof(STARTUPINFO));
|
||||||
|
si.cb = sizeof(STARTUPINFO);
|
||||||
|
si.lpDesktop = "winsta0\\Default"; // TODO: maybe this should be \winlogon if we have logonui.exe?
|
||||||
|
si.hStdError = m_stdOutWrite;
|
||||||
|
si.hStdOutput = m_stdOutWrite;
|
||||||
|
si.dwFlags |= STARTF_USESTDHANDLES;
|
||||||
|
|
||||||
STARTUPINFO si;
|
LPVOID environment;
|
||||||
ZeroMemory(&si, sizeof(STARTUPINFO));
|
BOOL blockRet = CreateEnvironmentBlock(&environment, userToken, FALSE);
|
||||||
si.cb = sizeof(STARTUPINFO);
|
if (!blockRet) {
|
||||||
si.lpDesktop = "winsta0\\default";
|
LOG((CLOG_ERR "could not create environment block (error: %i)",
|
||||||
si.hStdError = m_stdOutWrite;
|
GetLastError()));
|
||||||
si.hStdOutput = m_stdOutWrite;
|
continue;
|
||||||
si.dwFlags |= STARTF_USESTDHANDLES;
|
}
|
||||||
|
|
||||||
LPVOID environment;
|
DWORD creationFlags =
|
||||||
BOOL blockRet = CreateEnvironmentBlock(&environment, userToken, FALSE);
|
NORMAL_PRIORITY_CLASS |
|
||||||
if (!blockRet) {
|
CREATE_NO_WINDOW |
|
||||||
LOG((CLOG_ERR "could not create environment block (error: %i)",
|
CREATE_UNICODE_ENVIRONMENT;
|
||||||
GetLastError()));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
DWORD creationFlags =
|
// re-launch in current active user session
|
||||||
NORMAL_PRIORITY_CLASS |
|
LOG((CLOG_INFO "starting new process"));
|
||||||
CREATE_NO_WINDOW |
|
BOOL createRet = CreateProcessAsUser(
|
||||||
CREATE_UNICODE_ENVIRONMENT;
|
userToken, NULL, LPSTR(cmd.c_str()),
|
||||||
|
&sa, NULL, TRUE, creationFlags,
|
||||||
|
environment, NULL, &si, &pi);
|
||||||
|
|
||||||
// re-launch in current active user session
|
DestroyEnvironmentBlock(environment);
|
||||||
LOG((CLOG_INFO "starting new process"));
|
CloseHandle(userToken);
|
||||||
BOOL createRet = CreateProcessAsUser(
|
|
||||||
userToken, NULL, LPSTR(cmd.c_str()),
|
|
||||||
&sa, NULL, TRUE, creationFlags,
|
|
||||||
environment, NULL, &si, &pi);
|
|
||||||
|
|
||||||
DestroyEnvironmentBlock(environment);
|
if (!createRet) {
|
||||||
CloseHandle(userToken);
|
LOG((CLOG_ERR "could not launch (error: %i)", GetLastError()));
|
||||||
|
continue;
|
||||||
if (!createRet) {
|
}
|
||||||
LOG((CLOG_ERR "could not launch (error: %i)", GetLastError()));
|
else {
|
||||||
continue;
|
LOG((CLOG_DEBUG "launched in session %i (cmd: %s)",
|
||||||
}
|
sessionId, cmd.c_str()));
|
||||||
else {
|
launched = true;
|
||||||
LOG((CLOG_DEBUG "launched in session %i (cmd: %s)",
|
|
||||||
sessionId, cmd.c_str()));
|
|
||||||
launched = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,11 +435,11 @@ CMSWindowsRelauncher::mainLoop(void*)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CMSWindowsRelauncher::command(const std::string& command)
|
CMSWindowsRelauncher::command(const std::string& command, bool elevate)
|
||||||
{
|
{
|
||||||
LOG((CLOG_INFO "service command updated"));
|
LOG((CLOG_INFO "service command updated"));
|
||||||
LOG((CLOG_DEBUG "new command: %s", command.c_str()));
|
|
||||||
m_command = command;
|
m_command = command;
|
||||||
|
m_elevateProcess = elevate;
|
||||||
m_commandChanged = true;
|
m_commandChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,17 +35,19 @@ public:
|
||||||
virtual ~CMSWindowsRelauncher();
|
virtual ~CMSWindowsRelauncher();
|
||||||
void startAsync();
|
void startAsync();
|
||||||
std::string command() const;
|
std::string command() const;
|
||||||
void command(const std::string& command);
|
void command(const std::string& command, bool elevate);
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void mainLoop(void*);
|
void mainLoop(void*);
|
||||||
BOOL winlogonInSession(DWORD sessionId, PHANDLE process);
|
BOOL isProcessInSession(const char* name, DWORD sessionId, PHANDLE process);
|
||||||
DWORD getSessionId();
|
DWORD getSessionId();
|
||||||
HANDLE getCurrentUserToken(DWORD sessionId, LPSECURITY_ATTRIBUTES security);
|
|
||||||
void outputLoop(void*);
|
void outputLoop(void*);
|
||||||
void shutdownProcess(HANDLE handle, DWORD pid, int timeout);
|
void shutdownProcess(HANDLE handle, DWORD pid, int timeout);
|
||||||
void shutdownExistingProcesses();
|
void shutdownExistingProcesses();
|
||||||
|
HANDLE duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security);
|
||||||
|
HANDLE getSessionToken(DWORD sessionId, LPSECURITY_ATTRIBUTES security);
|
||||||
|
HANDLE getUserToken(DWORD sessionId, LPSECURITY_ATTRIBUTES security);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CThread* m_thread;
|
CThread* m_thread;
|
||||||
|
@ -58,4 +60,5 @@ private:
|
||||||
CThread* m_outputThread;
|
CThread* m_outputThread;
|
||||||
CIpcServer& m_ipcServer;
|
CIpcServer& m_ipcServer;
|
||||||
CIpcLogOutputter& m_ipcLogOutputter;
|
CIpcLogOutputter& m_ipcLogOutputter;
|
||||||
|
bool m_elevateProcess;
|
||||||
};
|
};
|
||||||
|
|
|
@ -222,11 +222,12 @@ CDaemonApp::mainLoop(bool logToFile)
|
||||||
CMSWindowsScreen::init(CArchMiscWindows::instanceWin32());
|
CMSWindowsScreen::init(CArchMiscWindows::instanceWin32());
|
||||||
CGameDeviceInfo gameDevice;
|
CGameDeviceInfo gameDevice;
|
||||||
CScreen dummyScreen(new CMSWindowsScreen(false, true, gameDevice));
|
CScreen dummyScreen(new CMSWindowsScreen(false, true, gameDevice));
|
||||||
|
|
||||||
string command = ARCH->setting("Command");
|
CString command = ARCH->setting("Command");
|
||||||
|
bool elevate = ARCH->setting("Elevate") == "1";
|
||||||
if (command != "") {
|
if (command != "") {
|
||||||
LOG((CLOG_INFO "using last known command: %s", command.c_str()));
|
LOG((CLOG_INFO "using last known command: %s", command.c_str()));
|
||||||
m_relauncher->command(command);
|
m_relauncher->command(command, elevate);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_relauncher->startAsync();
|
m_relauncher->startAsync();
|
||||||
|
@ -295,7 +296,7 @@ CDaemonApp::handleIpcMessage(const CEvent& e, void*)
|
||||||
case kIpcCommand: {
|
case kIpcCommand: {
|
||||||
CIpcCommandMessage* cm = static_cast<CIpcCommandMessage*>(m);
|
CIpcCommandMessage* cm = static_cast<CIpcCommandMessage*>(m);
|
||||||
CString command = cm->command();
|
CString command = cm->command();
|
||||||
LOG((CLOG_DEBUG "got new command: %s", command.c_str()));
|
LOG((CLOG_DEBUG "new command, elevate=%d command=%s", cm->elevate(), command.c_str()));
|
||||||
|
|
||||||
CString debugArg("--debug");
|
CString debugArg("--debug");
|
||||||
int debugArgPos = command.find(debugArg);
|
int debugArgPos = command.find(debugArg);
|
||||||
|
@ -319,16 +320,19 @@ CDaemonApp::handleIpcMessage(const CEvent& e, void*)
|
||||||
// store command in system settings. this is used when the daemon
|
// store command in system settings. this is used when the daemon
|
||||||
// next starts.
|
// next starts.
|
||||||
ARCH->setting("Command", command);
|
ARCH->setting("Command", command);
|
||||||
|
|
||||||
|
// TODO: it would be nice to store bools/ints...
|
||||||
|
ARCH->setting("Elevate", CString(cm->elevate() ? "1" : "0"));
|
||||||
}
|
}
|
||||||
catch (XArch& e) {
|
catch (XArch& e) {
|
||||||
LOG((CLOG_ERR "failed to save Command setting, %s", e.what().c_str()));
|
LOG((CLOG_ERR "failed to save settings, %s", e.what().c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SYSAPI_WIN32
|
#if SYSAPI_WIN32
|
||||||
// tell the relauncher about the new command. this causes the
|
// tell the relauncher about the new command. this causes the
|
||||||
// relauncher to stop the existing command and start the new
|
// relauncher to stop the existing command and start the new
|
||||||
// command.
|
// command.
|
||||||
m_relauncher->command(command);
|
m_relauncher->command(command, cm->elevate());
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,7 +181,7 @@ CIpcTests::connectToServer_handleMessageReceived(const CEvent& e, void*)
|
||||||
void
|
void
|
||||||
CIpcTests::sendMessageToServer_handleClientConnected(const CEvent& e, void*)
|
CIpcTests::sendMessageToServer_handleClientConnected(const CEvent& e, void*)
|
||||||
{
|
{
|
||||||
CIpcCommandMessage m("test");
|
CIpcCommandMessage m("test", true);
|
||||||
m_sendMessageToServer_client->send(m);
|
m_sendMessageToServer_client->send(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue