diff --git a/src/lib/platform/CMSWindowsRelauncher.cpp b/src/lib/platform/CMSWindowsRelauncher.cpp index d7b563b0..075b2ff6 100644 --- a/src/lib/platform/CMSWindowsRelauncher.cpp +++ b/src/lib/platform/CMSWindowsRelauncher.cpp @@ -31,10 +31,8 @@ #include "CIpcMessage.h" #include "Ipc.h" -#include #include #include -#include #include enum { @@ -85,110 +83,6 @@ CMSWindowsRelauncher::stop() delete m_outputThread; } -// this still gets the physical session (the one the keyboard and -// mouse is connected to), sometimes this returns -1 but not sure why -DWORD -CMSWindowsRelauncher::getSessionId() -{ - return WTSGetActiveConsoleSessionId(); -} - -BOOL -CMSWindowsRelauncher::isProcessInSession(const char* name, DWORD sessionId, PHANDLE process = NULL) -{ - // first we need to take a snapshot of the running processes - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (snapshot == INVALID_HANDLE_VALUE) { - LOG((CLOG_ERR "could not get process snapshot (error: %i)", - GetLastError())); - return 0; - } - - PROCESSENTRY32 entry; - entry.dwSize = sizeof(PROCESSENTRY32); - - // get the first process, and if we can't do that then it's - // unlikely we can go any further - BOOL gotEntry = Process32First(snapshot, &entry); - if (!gotEntry) { - LOG((CLOG_ERR "could not get first process entry (error: %i)", - GetLastError())); - return 0; - } - - // used to record process names for debug info - std::list nameList; - - // now just iterate until we can find winlogon.exe pid - DWORD pid = 0; - while(gotEntry) { - - // make sure we're not checking the system process - if (entry.th32ProcessID != 0) { - - DWORD processSessionId; - BOOL pidToSidRet = ProcessIdToSessionId( - entry.th32ProcessID, &processSessionId); - - if (!pidToSidRet) { - LOG((CLOG_ERR "could not get session id for process id %i (error: %i)", - entry.th32ProcessID, GetLastError())); - return 0; - } - - // only pay attention to processes in the active session - if (processSessionId == sessionId) { - - // store the names so we can record them for debug - nameList.push_back(entry.szExeFile); - - if (_stricmp(entry.szExeFile, name) == 0) { - pid = entry.th32ProcessID; - } - } - } - - // now move on to the next entry (if we're not at the end) - gotEntry = Process32Next(snapshot, &entry); - if (!gotEntry) { - - DWORD err = GetLastError(); - if (err != ERROR_NO_MORE_FILES) { - - // only worry about error if it's not the end of the snapshot - LOG((CLOG_ERR "could not get subsiquent process entry (error: %i)", - GetLastError())); - return 0; - } - } - } - - std::string nameListJoin; - for(std::list::iterator it = nameList.begin(); - it != nameList.end(); it++) { - nameListJoin.append(*it); - nameListJoin.append(", "); - } - - LOG((CLOG_DEBUG "processes in session %d: %s", - sessionId, nameListJoin.c_str())); - - CloseHandle(snapshot); - - if (pid) { - if (process != NULL) { - // now get the process so we can get the process, with which - // we'll use to get the process token. - LOG((CLOG_DEBUG "found %s in session %i", name, sessionId)); - *process = OpenProcess(MAXIMUM_ALLOWED, FALSE, pid); - } - return true; - } - else { - return false; - } -} - HANDLE CMSWindowsRelauncher::duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security) { @@ -222,54 +116,31 @@ CMSWindowsRelauncher::duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTE } HANDLE -CMSWindowsRelauncher::getUserToken(DWORD sessionId, LPSECURITY_ATTRIBUTES security) +CMSWindowsRelauncher::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 || isProcessInSession("logonui.exe", sessionId)) { + if (m_elevateProcess || m_session.isProcessInSession("logonui.exe", NULL)) { LOG((CLOG_DEBUG "getting elevated token, %s", (m_elevateProcess ? "elevation required" : "at login screen"))); HANDLE process; - if (isProcessInSession("winlogon.exe", sessionId, &process)) { + if (m_session.isProcessInSession("winlogon.exe", &process)) { return duplicateProcessToken(process, security); } else { - LOG((CLOG_ERR "could not find winlogon in session %i", sessionId)); return NULL; } } else { LOG((CLOG_DEBUG "getting non-elevated token")); - return getSessionToken(sessionId, security); + return m_session.getUserToken(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; - } - - HANDLE newToken; - if (!DuplicateTokenEx( - sourceToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, security, - SecurityImpersonation, TokenPrimary, &newToken)) { - - LOG((CLOG_ERR "could not duplicate token (error: %i)", GetLastError())); - return 0; - } - - LOG((CLOG_DEBUG "duplicated, new token: %i", newToken)); - return newToken; -} - void CMSWindowsRelauncher::mainLoop(void*) { @@ -282,7 +153,6 @@ CMSWindowsRelauncher::mainLoop(void*) sendSasFunc = (SendSas)GetProcAddress(sasLib, "SendSAS"); } - DWORD sessionId = -1; bool launched = false; SECURITY_ATTRIBUTES saAttr; @@ -308,7 +178,7 @@ CMSWindowsRelauncher::mainLoop(void*) sendSasEvent = CreateEvent(NULL, FALSE, FALSE, "Global\\SendSAS"); } - DWORD newSessionId = getSessionId(); + m_session.updateNewSessionId(); bool running = false; if (launched) { @@ -336,14 +206,12 @@ CMSWindowsRelauncher::mainLoop(void*) } } - // only enter here when id changes, and the session isn't -1, which - // may mean that there is no active session. - bool sessionChanged = ((newSessionId != sessionId) && (newSessionId != -1)); + // relaunch if it was running but has stopped unexpectedly. bool stoppedRunning = (launched && !running); - if (stoppedRunning || sessionChanged || m_commandChanged) { + if (stoppedRunning || m_session.hasChanged() || m_commandChanged) { m_commandChanged = false; @@ -354,12 +222,12 @@ CMSWindowsRelauncher::mainLoop(void*) } // ok, this is now the active session (forget the old one if any) - sessionId = newSessionId; + m_session.updateActiveSession(); SECURITY_ATTRIBUTES sa; ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); - HANDLE userToken = getUserToken(sessionId, &sa); + HANDLE userToken = getUserToken(&sa); if (userToken == NULL) { // HACK: trigger retry mechanism. launched = true; @@ -415,7 +283,7 @@ CMSWindowsRelauncher::mainLoop(void*) } else { LOG((CLOG_DEBUG "launched in session %i (cmd: %s)", - sessionId, cmd.c_str())); + m_session.getActiveSessionId(), cmd.c_str())); launched = true; } } diff --git a/src/lib/platform/CMSWindowsRelauncher.h b/src/lib/platform/CMSWindowsRelauncher.h index 435df275..b22743d4 100644 --- a/src/lib/platform/CMSWindowsRelauncher.h +++ b/src/lib/platform/CMSWindowsRelauncher.h @@ -19,6 +19,7 @@ #pragma once #define WIN32_LEAN_AND_MEAN +#include "CMSWindowsSession.h" #include #include #include @@ -34,32 +35,31 @@ public: CIpcServer& ipcServer, CIpcLogOutputter& ipcLogOutputter); virtual ~CMSWindowsRelauncher(); - void startAsync(); - std::string command() const; - void command(const std::string& command, bool elevate); - void stop(); + + void startAsync(); + std::string command() const; + void command(const std::string& command, bool elevate); + void stop(); private: - void mainLoop(void*); - BOOL isProcessInSession(const char* name, DWORD sessionId, PHANDLE process); - DWORD getSessionId(); - void outputLoop(void*); - void shutdownProcess(HANDLE handle, DWORD pid, int timeout); - void shutdownExistingProcesses(); + void mainLoop(void*); + void outputLoop(void*); + void shutdownProcess(HANDLE handle, DWORD pid, int timeout); + void shutdownExistingProcesses(); HANDLE duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security); - HANDLE getSessionToken(DWORD sessionId, LPSECURITY_ATTRIBUTES security); - HANDLE getUserToken(DWORD sessionId, LPSECURITY_ATTRIBUTES security); + HANDLE getUserToken(LPSECURITY_ATTRIBUTES security); private: - CThread* m_thread; - bool m_autoDetectCommand; - std::string m_command; - bool m_running; - bool m_commandChanged; - HANDLE m_stdOutWrite; - HANDLE m_stdOutRead; - CThread* m_outputThread; + CThread* m_thread; + bool m_autoDetectCommand; + std::string m_command; + bool m_running; + bool m_commandChanged; + HANDLE m_stdOutWrite; + HANDLE m_stdOutRead; + CThread* m_outputThread; CIpcServer& m_ipcServer; CIpcLogOutputter& m_ipcLogOutputter; bool m_elevateProcess; + CMSWindowsSession m_session; }; diff --git a/src/lib/platform/CMSWindowsSession.cpp b/src/lib/platform/CMSWindowsSession.cpp new file mode 100644 index 00000000..783f360c --- /dev/null +++ b/src/lib/platform/CMSWindowsSession.cpp @@ -0,0 +1,182 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 Bolton Software Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "CMSWindowsSession.h" +#include "CLog.h" +#include +#include + +CMSWindowsSession::CMSWindowsSession() : + m_sessionId(-1), + m_newSessionId(-1) +{ +} + +CMSWindowsSession::~CMSWindowsSession() +{ +} + +DWORD +CMSWindowsSession::getSessionId() +{ + return WTSGetActiveConsoleSessionId(); +} + +BOOL +CMSWindowsSession::isProcessInSession(const char* name, PHANDLE process = NULL) +{ + BOOL result = isProcessInSession_(name, process); + if (!result) { + LOG((CLOG_ERR "could not find winlogon in session %i", m_sessionId)); + } + return result; +} + +BOOL +CMSWindowsSession::isProcessInSession_(const char* name, PHANDLE process = NULL) +{ + // first we need to take a snapshot of the running processes + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot == INVALID_HANDLE_VALUE) { + LOG((CLOG_ERR "could not get process snapshot (error: %i)", + GetLastError())); + return 0; + } + + PROCESSENTRY32 entry; + entry.dwSize = sizeof(PROCESSENTRY32); + + // get the first process, and if we can't do that then it's + // unlikely we can go any further + BOOL gotEntry = Process32First(snapshot, &entry); + if (!gotEntry) { + LOG((CLOG_ERR "could not get first process entry (error: %i)", + GetLastError())); + return 0; + } + + // used to record process names for debug info + std::list nameList; + + // now just iterate until we can find winlogon.exe pid + DWORD pid = 0; + while(gotEntry) { + + // make sure we're not checking the system process + if (entry.th32ProcessID != 0) { + + DWORD processSessionId; + BOOL pidToSidRet = ProcessIdToSessionId( + entry.th32ProcessID, &processSessionId); + + if (!pidToSidRet) { + LOG((CLOG_ERR "could not get session id for process id %i (error: %i)", + entry.th32ProcessID, GetLastError())); + return 0; + } + + // only pay attention to processes in the active session + if (processSessionId == m_sessionId) { + + // store the names so we can record them for debug + nameList.push_back(entry.szExeFile); + + if (_stricmp(entry.szExeFile, name) == 0) { + pid = entry.th32ProcessID; + } + } + } + + // now move on to the next entry (if we're not at the end) + gotEntry = Process32Next(snapshot, &entry); + if (!gotEntry) { + + DWORD err = GetLastError(); + if (err != ERROR_NO_MORE_FILES) { + + // only worry about error if it's not the end of the snapshot + LOG((CLOG_ERR "could not get subsiquent process entry (error: %i)", + GetLastError())); + return 0; + } + } + } + + std::string nameListJoin; + for(std::list::iterator it = nameList.begin(); + it != nameList.end(); it++) { + nameListJoin.append(*it); + nameListJoin.append(", "); + } + + LOG((CLOG_DEBUG "processes in session %d: %s", + m_sessionId, nameListJoin.c_str())); + + CloseHandle(snapshot); + + if (pid) { + if (process != NULL) { + // now get the process, which we'll use to get the process token. + LOG((CLOG_DEBUG "found %s in session %i", name, m_sessionId)); + *process = OpenProcess(MAXIMUM_ALLOWED, FALSE, pid); + } + return true; + } + else { + return false; + } +} + +HANDLE +CMSWindowsSession::getUserToken(LPSECURITY_ATTRIBUTES security) +{ + HANDLE sourceToken; + if (!WTSQueryUserToken(m_sessionId, &sourceToken)) { + LOG((CLOG_ERR "could not get token from session %d (error: %i)", m_sessionId, GetLastError())); + return 0; + } + + HANDLE newToken; + if (!DuplicateTokenEx( + sourceToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, security, + SecurityImpersonation, TokenPrimary, &newToken)) { + + LOG((CLOG_ERR "could not duplicate token (error: %i)", GetLastError())); + return 0; + } + + LOG((CLOG_DEBUG "duplicated, new token: %i", newToken)); + return newToken; +} + +BOOL +CMSWindowsSession::hasChanged() +{ + return ((m_newSessionId != m_sessionId) && (m_newSessionId != -1)); +} + +void +CMSWindowsSession::updateNewSessionId() +{ + m_newSessionId = getSessionId(); +} + +void +CMSWindowsSession::updateActiveSession() +{ + m_sessionId = m_newSessionId; +} \ No newline at end of file diff --git a/src/lib/platform/CMSWindowsSession.h b/src/lib/platform/CMSWindowsSession.h new file mode 100644 index 00000000..2fae9cdd --- /dev/null +++ b/src/lib/platform/CMSWindowsSession.h @@ -0,0 +1,55 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 Bolton Software Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include + +class CMSWindowsSession { +public: + CMSWindowsSession(); + ~CMSWindowsSession(); + + //! Get session ID from Windows + /*! + This gets the physical session (the one the keyboard and + mouse is connected to), sometimes this returns -1. + */ + DWORD getSessionId(); + + BOOL isProcessInSession(const char* name, PHANDLE process); + HANDLE getUserToken(LPSECURITY_ATTRIBUTES security); + DWORD getActiveSessionId() { return m_sessionId; } + + //! + /*! + only enter here when id changes, and the session isn't -1, which + may mean that there is no active session. + */ + BOOL hasChanged(); + + void updateNewSessionId(); + void updateActiveSession(); + +private: + BOOL isProcessInSession_(const char* name, PHANDLE process); + +private: + DWORD m_sessionId; + DWORD m_newSessionId; +}; diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt index 12de3f30..89b8f38c 100644 --- a/src/lib/platform/CMakeLists.txt +++ b/src/lib/platform/CMakeLists.txt @@ -33,6 +33,7 @@ if (WIN32) CMSWindowsHookLibraryLoader.h IMSWindowsClipboardFacade.h CMSWindowsDebugOutputter.h + CMSWindowsSession.h ) set(src @@ -52,6 +53,7 @@ if (WIN32) CMSWindowsRelauncher.cpp CMSWindowsHookLibraryLoader.cpp CMSWindowsDebugOutputter.cpp + CMSWindowsSession.cpp ) list(APPEND src