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

View File

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

View File

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

View File

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