#6383 Use CreateProcess when in foreground mode

This commit is contained in:
Nick Bolton 2018-08-02 16:25:10 +01:00
parent 568a008037
commit 026b1f0de1
4 changed files with 142 additions and 102 deletions

View File

@ -49,7 +49,8 @@ const char g_activeDesktop[] = {"activeDesktop:"};
MSWindowsWatchdog::MSWindowsWatchdog(
bool autoDetectCommand,
IpcServer& ipcServer,
IpcLogOutputter& ipcLogOutputter) :
IpcLogOutputter& ipcLogOutputter,
bool foreground) :
m_thread(NULL),
m_autoDetectCommand(autoDetectCommand),
m_monitoring(true),
@ -63,7 +64,8 @@ MSWindowsWatchdog::MSWindowsWatchdog(
m_processRunning(false),
m_fileLogOutputter(NULL),
m_autoElevated(false),
m_ready(false)
m_ready(false),
m_foreground(foreground)
{
m_mutex = ARCH->newMutex();
m_condVar = ARCH->newCondVar();
@ -105,104 +107,118 @@ MSWindowsWatchdog::stop()
HANDLE
MSWindowsWatchdog::duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security)
{
HANDLE sourceToken;
HANDLE sourceToken;
BOOL tokenRet = OpenProcessToken(
process,
TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS,
&sourceToken);
BOOL tokenRet = OpenProcessToken(
process,
TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS,
&sourceToken);
if (!tokenRet) {
LOG((CLOG_ERR "could not open token, process handle: %d", process));
throw XArch(new XArchEvalWindows());
}
LOG((CLOG_DEBUG "got token %i, duplicating", sourceToken));
if (!tokenRet) {
LOG((CLOG_ERR "could not open token, process handle: %d", process));
throw XArch(new XArchEvalWindows());
}
HANDLE newToken;
BOOL duplicateRet = DuplicateTokenEx(
sourceToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, security,
SecurityImpersonation, TokenPrimary, &newToken);
LOG((CLOG_DEBUG "got token %i, duplicating", sourceToken));
if (!duplicateRet) {
LOG((CLOG_ERR "could not duplicate token %i", sourceToken));
throw XArch(new XArchEvalWindows());
}
LOG((CLOG_DEBUG "duplicated, new token: %i", newToken));
return newToken;
HANDLE newToken;
BOOL duplicateRet = DuplicateTokenEx(
sourceToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, security,
SecurityImpersonation, TokenPrimary, &newToken);
if (!duplicateRet) {
LOG((CLOG_ERR "could not duplicate token %i", sourceToken));
throw XArch(new XArchEvalWindows());
}
LOG((CLOG_DEBUG "duplicated, new token: %i", newToken));
return newToken;
}
HANDLE
HANDLE
MSWindowsWatchdog::getUserToken(LPSECURITY_ATTRIBUTES security)
{
// always elevate if we are at the vista/7 login screen. we could also
// elevate for the uac dialog (consent.exe) but this would be pointless,
// since synergy would re-launch as non-elevated after the desk switch,
// and so would be unusable with the new elevated process taking focus.
if (m_elevateProcess
|| m_autoElevated
|| m_session.isProcessInSession("logonui.exe", NULL)) {
LOG((CLOG_DEBUG "getting elevated token, %s",
(m_elevateProcess ? "elevation required" : "at login screen")));
HANDLE process;
if (!m_session.isProcessInSession("winlogon.exe", &process)) {
throw XMSWindowsWatchdogError("cannot get user token without winlogon.exe");
}
// always elevate if we are at the vista/7 login screen. we could also
// elevate for the uac dialog (consent.exe) but this would be pointless,
// since synergy would re-launch as non-elevated after the desk switch,
// and so would be unusable with the new elevated process taking focus.
if (m_elevateProcess
|| m_autoElevated
|| m_session.isProcessInSession("logonui.exe", NULL)) {
return duplicateProcessToken(process, security);
}
else {
LOG((CLOG_DEBUG "getting non-elevated token"));
return m_session.getUserToken(security);
}
LOG((CLOG_DEBUG "getting elevated token, %s",
(m_elevateProcess ? "elevation required" : "at login screen")));
HANDLE process;
if (!m_session.isProcessInSession("winlogon.exe", &process)) {
throw XMSWindowsWatchdogError("cannot get user token without winlogon.exe");
}
return duplicateProcessToken(process, security);
}
else {
LOG((CLOG_DEBUG "getting non-elevated token"));
return m_session.getUserToken(security);
}
}
void
MSWindowsWatchdog::mainLoop(void*)
{
shutdownExistingProcesses();
shutdownExistingProcesses();
SendSas sendSasFunc = NULL;
HINSTANCE sasLib = LoadLibrary("sas.dll");
if (sasLib) {
LOG((CLOG_DEBUG "found sas.dll"));
sendSasFunc = (SendSas)GetProcAddress(sasLib, "SendSAS");
}
SendSas sendSasFunc = NULL;
HINSTANCE sasLib = LoadLibrary("sas.dll");
if (sasLib) {
LOG((CLOG_DEBUG "found sas.dll"));
sendSasFunc = (SendSas)GetProcAddress(sasLib, "SendSAS");
}
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&m_stdOutRead, &m_stdOutWrite, &saAttr, 0)) {
throw XArch(new XArchEvalWindows());
}
if (!CreatePipe(&m_stdOutRead, &m_stdOutWrite, &saAttr, 0)) {
throw XArch(new XArchEvalWindows());
}
ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION));
while (m_monitoring) {
try {
while (m_monitoring) {
try {
if (m_processRunning && getCommand().empty()) {
LOG((CLOG_INFO "process started but command is empty, shutting down"));
shutdownExistingProcesses();
m_processRunning = false;
continue;
}
if (m_processRunning && getCommand().empty()) {
LOG((CLOG_INFO "process started but command is empty, shutting down"));
shutdownExistingProcesses();
m_processRunning = false;
continue;
}
if (m_processFailures != 0) {
// increasing backoff period, maximum of 10 seconds.
int timeout = (m_processFailures * 2) < 10 ? (m_processFailures * 2) : 10;
LOG((CLOG_INFO "backing off, wait=%ds, failures=%d", timeout, m_processFailures));
ARCH->sleep(timeout);
}
if (!getCommand().empty() && ((m_processFailures != 0) || m_session.hasChanged() || m_commandChanged)) {
startProcess();
}
if (m_processFailures != 0) {
// increasing backoff period, maximum of 10 seconds.
int timeout = (m_processFailures * 2) < 10 ? (m_processFailures * 2) : 10;
LOG((CLOG_INFO "backing off, wait=%ds, failures=%d", timeout, m_processFailures));
ARCH->sleep(timeout);
}
if (!getCommand().empty()) {
bool startNeeded = false;
if (m_processFailures != 0) {
startNeeded = true;
}
else if (!m_foreground && m_session.hasChanged()) {
startNeeded = true;
}
else if (m_commandChanged) {
startNeeded = true;
}
if (startNeeded) {
startProcess();
}
}
if (m_processRunning && !isProcessActive()) {
@ -284,28 +300,35 @@ MSWindowsWatchdog::startProcess()
m_processRunning = false;
}
m_session.updateActiveSession();
BOOL createRet;
if (m_foreground) {
LOG((CLOG_DEBUG "starting command in foreground"));
createRet = startProcessInForeground(m_command);
}
else {
LOG((CLOG_DEBUG "starting command as session user"));
m_session.updateActiveSession();
SECURITY_ATTRIBUTES sa;
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
SECURITY_ATTRIBUTES sa;
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
getActiveDesktop(&sa);
getActiveDesktop(&sa);
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
HANDLE userToken = getUserToken(&sa);
m_elevateProcess = m_autoElevated ? m_autoElevated : m_elevateProcess;
m_autoElevated = false;
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
HANDLE userToken = getUserToken(&sa);
m_elevateProcess = m_autoElevated ? m_autoElevated : m_elevateProcess;
m_autoElevated = false;
// patch by Jack Zhou and Henry Tung
// set UIAccess to fix Windows 8 GUI interaction
// http://symless.com/spit/issues/details/3338/#c70
DWORD uiAccess = 1;
SetTokenInformation(userToken, TokenUIAccess, &uiAccess, sizeof(DWORD));
// patch by Jack Zhou and Henry Tung
// set UIAccess to fix Windows 8 GUI interaction
DWORD uiAccess = 1;
SetTokenInformation(userToken, TokenUIAccess, &uiAccess, sizeof(DWORD));
BOOL createRet = doStartProcess(m_command, userToken, &sa);
createRet = startProcessAsUser(m_command, userToken, &sa);
}
if (!createRet) {
LOG((CLOG_ERR "could not launch"));
LOG((CLOG_ERR "could not launch command"));
DWORD exitCode = 0;
GetExitCodeProcess(m_processInfo.hProcess, &exitCode);
LOG((CLOG_ERR "exit code: %d", exitCode));
@ -329,7 +352,21 @@ MSWindowsWatchdog::startProcess()
}
BOOL
MSWindowsWatchdog::doStartProcess(String& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa)
MSWindowsWatchdog::startProcessInForeground(String& command)
{
// clear, as we're reusing process info struct
ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION));
STARTUPINFO startupInfo;
ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
return CreateProcess(
NULL, LPSTR(command.c_str()), NULL, NULL,
FALSE, 0, NULL, NULL, &startupInfo, &m_processInfo);
}
BOOL
MSWindowsWatchdog::startProcessAsUser(String& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa)
{
// clear, as we're reusing process info struct
ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION));
@ -542,7 +579,7 @@ MSWindowsWatchdog::getActiveDesktop(LPSECURITY_ATTRIBUTES security)
HANDLE userToken = getUserToken(security);
m_elevateProcess = elevateProcess;
BOOL createRet = doStartProcess(syntoolCommand, userToken, security);
BOOL createRet = startProcessAsUser(syntoolCommand, userToken, security);
if (!createRet) {
DWORD rc = GetLastError();

View File

@ -37,7 +37,8 @@ public:
MSWindowsWatchdog(
bool autoDetectCommand,
IpcServer& ipcServer,
IpcLogOutputter& ipcLogOutputter);
IpcLogOutputter& ipcLogOutputter,
bool foreground);
virtual ~MSWindowsWatchdog();
void startAsync();
@ -55,7 +56,8 @@ private:
HANDLE duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security);
HANDLE getUserToken(LPSECURITY_ATTRIBUTES security);
void startProcess();
BOOL doStartProcess(String& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa);
BOOL startProcessAsUser(String& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa);
BOOL startProcessInForeground(String& command);
void sendSas();
void getActiveDesktop(LPSECURITY_ATTRIBUTES security);
void testOutput(String buffer);
@ -81,6 +83,7 @@ private:
ArchMutex m_mutex;
ArchCond m_condVar;
bool m_ready;
bool m_foreground;
};
//! Relauncher error

View File

@ -155,7 +155,7 @@ DaemonApp::run(int argc, char** argv)
if (foreground) {
// run process in foreground instead of daemonizing.
// useful for debugging.
mainLoop(false);
mainLoop(false, foreground);
}
else {
#if SYSAPI_WIN32
@ -192,7 +192,7 @@ DaemonApp::run(int argc, char** argv)
}
void
DaemonApp::mainLoop(bool logToFile)
DaemonApp::mainLoop(bool logToFile, bool foreground)
{
try
{
@ -215,7 +215,7 @@ DaemonApp::mainLoop(bool logToFile)
CLOG->insert(m_ipcLogOutputter);
#if SYSAPI_WIN32
m_watchdog = new MSWindowsWatchdog(false, *m_ipcServer, *m_ipcLogOutputter);
m_watchdog = new MSWindowsWatchdog(false, *m_ipcServer, *m_ipcLogOutputter, foreground);
m_watchdog->setFileLogOutputter(m_fileLogOutputter);
#endif

View File

@ -37,7 +37,7 @@ public:
DaemonApp();
virtual ~DaemonApp();
int run(int argc, char** argv);
void mainLoop(bool logToFile);
void mainLoop(bool logToFile, bool foreground = false);
private:
void daemonize();