KasmVNC/win/winvnc/VNCServerService.cxx
2020-09-20 12:16:44 +00:00

184 lines
5.8 KiB
C++

/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software 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 software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
// -=- WinVNC Version 4.0 Service-Mode implementation
#include <winvnc/VNCServerService.h>
#include <rfb_win32/TsSessions.h>
#include <rfb_win32/ModuleFileName.h>
#include <windows.h>
#include <wtsapi32.h>
#include <tlhelp32.h>
using namespace winvnc;
using namespace rfb;
using namespace win32;
const TCHAR* winvnc::VNCServerService::Name = _T("KasmVNC");
// SendSAS is not available until Windows 7, and missing from MinGW
static HMODULE sasLibrary = NULL;
typedef void WINAPI (*SendSAS_proto)(BOOL AsUser);
static SendSAS_proto _SendSAS = NULL;
VNCServerService::VNCServerService()
: Service(Name)
, stopServiceEvent(CreateEvent(0, FALSE, FALSE, 0))
, sessionEvent(CreateEvent(0, FALSE, FALSE, "Global\\SessionEventKasmVNC"))
, sessionEventCad(CreateEvent(0, FALSE, FALSE, "Global\\SessionEventKasmVNCCad")) {
if (sasLibrary == NULL) {
sasLibrary = LoadLibrary("sas.dll");
if (sasLibrary != NULL)
_SendSAS = (SendSAS_proto)GetProcAddress(sasLibrary, "SendSAS");
}
// - Set the service-mode logging defaults
// These will be overridden by the Log option in the
// registry, if present.
logParams.setParam("*:EventLog:0,Connections:EventLog:100");
}
//////////////////////////////////////////////////////////////////////////////
DWORD GetLogonPid(DWORD dwSessionId)
{
DWORD dwLogonPid = 0;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32 procEntry;
procEntry.dwSize = sizeof procEntry;
if (Process32First(hSnap, &procEntry)) do
{
DWORD dwLogonSessionId = 0;
if (_stricmp(procEntry.szExeFile, "winlogon.exe") == 0 &&
ProcessIdToSessionId(procEntry.th32ProcessID, &dwLogonSessionId) &&
dwLogonSessionId == dwSessionId)
{
dwLogonPid = procEntry.th32ProcessID;
break;
}
} while (Process32Next(hSnap, &procEntry));
CloseHandle(hSnap);
}
return dwLogonPid;
}
//////////////////////////////////////////////////////////////////////////////
BOOL GetSessionUserTokenWin(OUT LPHANDLE lphUserToken)
{
BOOL bResult = FALSE;
ConsoleSessionId ID_session;
DWORD Id = GetLogonPid(ID_session.id);
if (HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Id))
{
bResult = OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, lphUserToken);
CloseHandle(hProcess);
}
return bResult;
}
//////////////////////////////////////////////////////////////////////////////
// START the app as system
HANDLE LaunchProcessWin(DWORD dwSessionId)
{
HANDLE hProcess = NULL;
HANDLE hToken = NULL;
if (GetSessionUserTokenWin(&hToken))
{
ModuleFileName filename;
TCharArray cmdLine;
cmdLine.format("\"%s\" -noconsole -service_run", filename.buf);
STARTUPINFO si;
ZeroMemory(&si, sizeof si);
si.cb = sizeof si;
si.dwFlags = STARTF_USESHOWWINDOW;
PROCESS_INFORMATION pi;
if (CreateProcessAsUser(hToken, NULL, cmdLine.buf, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi))
{
CloseHandle(pi.hThread);
hProcess = pi.hProcess;
}
CloseHandle(hToken);
}
return hProcess;
}
DWORD VNCServerService::serviceMain(int argc, TCHAR* argv[])
{
ConsoleSessionId OlddwSessionId;
HANDLE hProcess = NULL;
//We use this event to notify the program that the session has changed
//The program need to end so the service can restart the program in the correct session
//wait_for_existing_process();
HANDLE testevent[2] = { stopServiceEvent, sessionEventCad };
setStatus(SERVICE_RUNNING);
while (status.dwCurrentState == SERVICE_RUNNING)
{
DWORD dwEvent = WaitForMultipleObjects(2, testevent, FALSE, 1000);
switch (dwEvent)
{
//stopServiceEvent, exit while loop
case WAIT_OBJECT_0 + 0:
setStatus(SERVICE_STOP_PENDING);
break;
//cad request
case WAIT_OBJECT_0 + 1:
if (_SendSAS != NULL)
_SendSAS(FALSE);
break;
case WAIT_TIMEOUT:
{
ConsoleSessionId dwSessionId;
if (OlddwSessionId.id != dwSessionId.id)
{
OlddwSessionId.id = dwSessionId.id;
SetEvent(sessionEvent);
}
DWORD dwExitCode = 0;
if (hProcess == NULL ||
(GetExitCodeProcess(hProcess, &dwExitCode) &&
dwExitCode != STILL_ACTIVE &&
CloseHandle(hProcess)))
{
hProcess = LaunchProcessWin(dwSessionId.id);
}
}
break;
}
}
SetEvent(sessionEvent);
if (hProcess)
{
WaitForSingleObject(hProcess, 15000);
CloseHandle(hProcess);
}
return 0;
}
void VNCServerService::stop() {
SetEvent(stopServiceEvent);
SetEvent(sessionEvent);
}