Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2906 lines
82 KiB

/*++
Copyright (c) 1999-2000 Microsoft Corporation
Module Name:
sessmgr.cpp
Abstract:
ATL wizard generated code.
Author:
HueiWang 2/17/2000
--*/
// Note: Proxy/Stub Information
// To build a separate proxy/stub DLL,
// run nmake -f sessmgrps.mk in the project directory.
#include "stdafx.h"
#include "resource.h"
#include <initguid.h>
#include <process.h>
#include <tchar.h>
#include "sessmgr.h"
#include "sessmgr_i.c"
#include <stdio.h>
//#include <new.h>
#include "global.h"
#include "HelpSess.h"
#include "HelpMgr.h"
#include "helper.h"
#include "helpacc.h"
#include <rdshost.h>
#include "policy.h"
#include "remotedesktoputils.h"
#include <SHlWapi.h>
#define SETUPLOGFILE_NAME _TEXT("sessmgr.setup.log")
#define SESSMGR_SERVICE 0
#define SESSMGR_REGSERVER 1
#define SESSMGR_UNREGSERVER 2
BEGIN_OBJECT_MAP(ObjectMap)
//OBJECT_ENTRY(CLSID_RemoteDesktopHelpSession, CRemoteDesktopHelpSession)
OBJECT_ENTRY(CLSID_RemoteDesktopHelpSessionMgr, CRemoteDesktopHelpSessionMgr)
END_OBJECT_MAP()
CServiceModule _Module;
HANDLE g_hTSCertificateChanged = NULL;
HANDLE g_hWaitTSCertificateChanged = NULL;
HKEY g_hTSCertificateRegKey = NULL;
DWORD
RestartFromSystemRestore();
VOID CALLBACK
TSCertChangeCallback(
PVOID pContext,
BOOLEAN bTimerOrWaitFired
)
/*++
Callback for TS certificate registry change from threadpool function.
--*/
{
MYASSERT( FALSE == bTimerOrWaitFired );
// Our wait is forever so can't be timeout.
if( FALSE == bTimerOrWaitFired )
{
PostThreadMessage(
_Module.dwThreadID,
WM_LOADTSPUBLICKEY,
0,
0
);
}
else
{
DebugPrintf(
_TEXT("TSCertChangeCallback does not expect timeout...\n") );
MYASSERT(FALSE);
}
}
DWORD
LoadTermSrvSecurityBlob()
/*++
Function to load TS machine specific identification blob, for now
we use TS public key.
--*/
{
DWORD dwStatus;
PBYTE pbTSPublicKey = NULL;
DWORD cbTSPublicKey = 0;
DWORD dwType;
DWORD cbData;
BOOL bSuccess;
BOOL bUsesX509PublicKey = FALSE;
if( NULL == g_hTSCertificateRegKey )
{
MYASSERT(FALSE);
dwStatus = ERROR_INTERNAL_ERROR;
goto CLEANUPANDEXIT;
}
//
// Make sure TS certificate is there before
// we directly load public key from LSA
//
dwStatus = RegQueryValueEx(
g_hTSCertificateRegKey,
REGVALUE_TSX509_CERT,
NULL,
&dwType,
NULL,
&cbData
);
if( ERROR_SUCCESS == dwStatus )
{
DebugPrintf(
_TEXT("TermSrv X509 certificate found, trying to load TS X509 public key\n")
);
cbTSPublicKey = 0;
//
// Current TLSAPI does not support retrival of
// X509 certificate public key and TS cert is in
// special format not standard x509 cert chain.
//
dwStatus = LsCsp_RetrieveSecret(
LSA_TSX509_CERT_PUBLIC_KEY_NAME,
NULL,
&cbTSPublicKey
);
if( LICENSE_STATUS_OK != dwStatus &&
LICENSE_STATUS_INSUFFICIENT_BUFFER != dwStatus )
{
MYASSERT( FALSE );
goto CLEANUPANDEXIT;
}
pbTSPublicKey = (PBYTE)LocalAlloc( LPTR, cbTSPublicKey );
if( NULL == pbTSPublicKey )
{
dwStatus = GetLastError();
goto CLEANUPANDEXIT;
}
dwStatus = LsCsp_RetrieveSecret(
LSA_TSX509_CERT_PUBLIC_KEY_NAME,
pbTSPublicKey,
&cbTSPublicKey
);
//
// Critical error, We have certificate in registry
// but don't have public key in LSA
//
MYASSERT( LICENSE_STATUS_OK == dwStatus );
if( LICENSE_STATUS_OK != dwStatus )
{
DebugPrintf(
_TEXT("TermSrv X509 certificate found but can't load X509 public key\n")
);
goto CLEANUPANDEXIT;
}
bUsesX509PublicKey = TRUE;
}
else
{
DebugPrintf(
_TEXT("TermSrv X509 certificate not found\n")
);
//
// Load pre-define TS public key
//
dwStatus = LsCsp_GetServerData(
LsCspInfo_PublicKey,
pbTSPublicKey,
&cbTSPublicKey
);
// expecting insufficient buffer
if( LICENSE_STATUS_INSUFFICIENT_BUFFER != dwStatus &&
LICENSE_STATUS_OK != dwStatus )
{
// invalid return code.
MYASSERT(FALSE);
goto CLEANUPANDEXIT;
}
MYASSERT( cbTSPublicKey > 0 );
pbTSPublicKey = (PBYTE)LocalAlloc( LPTR, cbTSPublicKey );
if( NULL == pbTSPublicKey )
{
dwStatus = GetLastError();
goto CLEANUPANDEXIT;
}
dwStatus = LsCsp_GetServerData(
LsCspInfo_PublicKey,
pbTSPublicKey,
&cbTSPublicKey
);
if( LICENSE_STATUS_OK != dwStatus )
{
MYASSERT(FALSE);
goto CLEANUPANDEXIT;
}
}
if( ERROR_SUCCESS == dwStatus )
{
//
// Lock access to g_TSSecurityBlob, this is global and
// other thread might be calling get_ConnectParm which access
// g_TSSecurityBlob.
//
CCriticalSectionLocker l(g_GlobalLock);
dwStatus = HashSecurityData(
pbTSPublicKey,
cbTSPublicKey,
g_TSSecurityBlob
);
MYASSERT( ERROR_SUCCESS == dwStatus );
MYASSERT( g_TSSecurityBlob.Length() > 0 );
DebugPrintf(
_TEXT("HashSecurityData() returns %d\n"), dwStatus
);
if( ERROR_SUCCESS != dwStatus )
{
goto CLEANUPANDEXIT;
}
}
//
// SRV, ADS, ... SKU uses seperate thread
// to register with license server, so we use
// different thread to receive certificate change notification.
// Since TermSrv cached certificate, no reason to queue
// notification once we successfully loaded tersrmv public key
//
if( !IsPersonalOrProMachine() && FALSE == bUsesX509PublicKey )
{
DebugPrintf(
_TEXT("Setting up registry notification...\n")
);
MYASSERT( NULL != g_hTSCertificateChanged );
ResetEvent(g_hTSCertificateChanged);
// register a registry change notification
// RegNotifyChangeKeyValue() only signal once.
dwStatus = RegNotifyChangeKeyValue(
g_hTSCertificateRegKey,
TRUE,
REG_NOTIFY_CHANGE_LAST_SET,
g_hTSCertificateChanged,
TRUE
);
if( ERROR_SUCCESS != dwStatus )
{
MYASSERT(FALSE);
DebugPrintf(
_TEXT("RegNotifyChangeKeyValue() returns %d\n"), dwStatus
);
goto CLEANUPANDEXIT;
}
if( NULL != g_hWaitTSCertificateChanged )
{
if( FALSE == UnregisterWait( g_hWaitTSCertificateChanged ) )
{
dwStatus = GetLastError();
DebugPrintf(
_TEXT("UnregisterWait() returns %d\n"),
dwStatus
);
MYASSERT(FALSE);
}
g_hWaitTSCertificateChanged = NULL;
}
//
// Queue notification to threadpool, we need to use WT_EXECUTEONLYONCE
// since we are registering manual reset event.
//
bSuccess = RegisterWaitForSingleObject(
&g_hWaitTSCertificateChanged,
g_hTSCertificateChanged,
(WAITORTIMERCALLBACK) TSCertChangeCallback,
NULL,
INFINITE,
WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE
);
if( FALSE == bSuccess )
{
dwStatus = GetLastError();
DebugPrintf(
_TEXT("RegisterWaitForSingleObject() returns %d\n"), dwStatus
);
}
}
CLEANUPANDEXIT:
if( ERROR_SUCCESS != dwStatus )
{
//
// Lock access to g_TSSecurityBlob, this is global and
// other thread might be calling get_ConnectParm which access
// g_TSSecurityBlob.
//
CCriticalSectionLocker l(g_GlobalLock);
//
// TS either update its public key or key has change
// and we failed to reload it, there is no reason to
// to continue create help ticket since public key already
/// mismatached, set service status and log error event
//
g_TSSecurityBlob.Empty();
}
if( NULL != pbTSPublicKey )
{
LocalFree(pbTSPublicKey);
}
return HRESULT_FROM_WIN32( dwStatus );
}
DWORD
LoadAndSetupTSCertChangeNotification()
{
DWORD dwStatus;
DWORD dwDisp;
BOOL bSuccess;
//
// Only setup registry change notification if we
// runs on higher SKU
//
g_hTSCertificateChanged = CreateEvent( NULL, TRUE, FALSE, NULL );
if( NULL == g_hTSCertificateChanged )
{
dwStatus = GetLastError();
goto CLEANUPANDEXIT;
}
//
// Open parameters key under TermServices if key isn't
// there, create it, this does not interfere with TermSrv
// since we only create the reg. key not updating values
// under it.
//
dwStatus = RegCreateKeyEx(
HKEY_LOCAL_MACHINE,
REGKEY_TSX509_CERT ,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_WRITE | KEY_READ,
NULL,
&g_hTSCertificateRegKey,
&dwDisp
);
if( ERROR_SUCCESS != dwStatus )
{
MYASSERT(FALSE);
DebugPrintf(
_TEXT("RegCreateKeyEx on %s failed with 0x%08x\n"),
REGKEY_TSX509_CERT,
dwStatus
);
goto CLEANUPANDEXIT;
}
//
// Load security blob from TS, currently, we use TS public key
// as security blob
//
dwStatus = LoadTermSrvSecurityBlob();
if( ERROR_SUCCESS != dwStatus )
{
MYASSERT(FALSE);
}
CLEANUPANDEXIT:
return dwStatus;
}
LPCTSTR FindOneOf(LPCTSTR p1, LPCTSTR p2)
{
while (p1 != NULL && *p1 != NULL)
{
LPCTSTR p = p2;
while (p != NULL && *p != NULL)
{
if (*p1 == *p)
return CharNext(p1);
p = CharNext(p);
}
p1 = CharNext(p1);
}
return NULL;
}
void
LogSetup(
IN FILE* pfd,
IN LPCTSTR format, ...
)
/*++
Routine Description:
sprintf() like wrapper around OutputDebugString().
Parameters:
hConsole : Handle to console.
format : format string.
Returns:
None.
Note:
To be replace by generic tracing code.
++*/
{
TCHAR buf[8096]; // max. error text
DWORD dump;
va_list marker;
va_start(marker, format);
try {
_vsntprintf(
buf,
sizeof(buf)/sizeof(buf[0])-1,
format,
marker
);
if( NULL == pfd )
{
OutputDebugString(buf);
}
else
{
_fputts( buf, pfd );
fflush( pfd );
}
}
catch(...) {
}
va_end(marker);
return;
}
#if DISABLESECURITYCHECKS
DWORD WINAPI
NotifySessionLogoff(
LPARAM pParm
)
/*++
Routine Description:
Routine to notified all currently loaded help that a user has
logoff/disconnect from session, routine is kick off via thread pools'
QueueUserWorkItem().
Parameters:
pContext : logoff or disconnected Session ID
Returns:
None.
Note :
We treat disconnect same as logoff since user might be actually
active on the other session logged in with same credential, so
we rely on resolver.
--*/
{
DebugPrintf(_TEXT("NotifySessionLogoff() started...\n"));
//
// Tell service don't shutdown, we are in process.
//
_Module.AddRef();
CRemoteDesktopHelpSessionMgr::NotifyHelpSesionLogoff( pParm );
_Module.Release();
return ERROR_SUCCESS;
}
#endif
/////////////////////////////////////////////////////////////////////////////
void
DeleteAccountFromFilterList(
LPCTSTR lpszAccountName
)
/*++
Routine Description:
Delete HelpAssistant account from account filter list, this is temporary
until we have long term solution.
Parameters:
lpszAccountName : Name of HelpAssistant account.
Returns:
None.
Note:
Account filter list is on
HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList
<name of SALEM account> REG_DWORD 0x00000000
--*/
{
HKEY hKey = NULL;
DWORD dwStatus;
DWORD dwValue = 0;
dwStatus = RegCreateKeyEx(
HKEY_LOCAL_MACHINE,
_TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts\\UserList"),
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
NULL
);
if( ERROR_SUCCESS == dwStatus )
{
dwStatus = RegDeleteValue(
hKey,
lpszAccountName
);
}
if( NULL != hKey )
{
RegCloseKey( hKey );
}
return;
}
void
AddAccountToFilterList(
LPCTSTR lpszAccountName
)
/*++
Routine Description:
Add HelpAssistant account into account filter list, this is temporary
until we have long term solution.
Parameters:
lpszAccountName : Name of HelpAssistant account.
Returns:
None.
Note:
Account filter list is on
HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList
<name of SALEM account> REG_DWORD 0x00000000
--*/
{
HKEY hKey = NULL;
DWORD dwStatus;
DWORD dwValue = 0;
dwStatus = RegCreateKeyEx(
HKEY_LOCAL_MACHINE,
_TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts\\UserList"),
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
NULL
);
if( ERROR_SUCCESS == dwStatus )
{
dwStatus = RegSetValueEx(
hKey,
lpszAccountName,
0,
REG_DWORD,
(LPBYTE) &dwValue,
sizeof(DWORD)
);
}
//MYASSERT( ERROR_SUCCESS == dwStatus );
if( NULL != hKey )
{
RegCloseKey( hKey );
}
return;
}
void
CServiceModule::LogSessmgrEventLog(
DWORD dwEventType,
DWORD dwEventCode,
CComBSTR& bstrNoviceDomain,
CComBSTR& bstrNoviceAccount,
CComBSTR& bstrRaType,
CComBSTR& bstrExpertIPFromClient,
CComBSTR& bstrExpertIPFromTS,
DWORD dwErrCode
)
/*++
Description:
Log a Salem specific event log, this includes all event log in sessmgr.
Parameters:
dwEventCode : Event code.
bstrNoviceDomain : Ticket owner's domain name.
bstrNoviceAccount : Ticket owner's user account name.
bstrExpertIPFromClient : Expert's IP address send from mstacax.
bstrExpertIPFromTS : Expert IP address we query from TermSrv.
dwErrCode : Error code.
Returns:
None.
Note:
Max. sessmgr specific log require at most 5 parameters but must
contain novice domain, account name and also expert IP address
send to mstscax and expert IP address we query from TermSrv.
--*/
{
TCHAR szErrCode[256];
LPTSTR eventString[6];
_stprintf( szErrCode, L"0x%x", dwErrCode );
eventString[0] = (LPTSTR)bstrNoviceDomain;
eventString[1] = (LPTSTR)bstrNoviceAccount;
eventString[2] = (LPTSTR)bstrRaType;
eventString[3] = (LPTSTR)bstrExpertIPFromClient;
eventString[4] = (LPTSTR)bstrExpertIPFromTS;
eventString[5] = szErrCode;
LogRemoteAssistanceEventString(
dwEventType,
dwEventCode,
sizeof(eventString)/sizeof(eventString[0]),
eventString
);
return;
}
/////////////////////////////////////////////////////////////////////////////
void
CServiceModule::LogEventWithStatusCode(
IN DWORD dwEventType,
IN DWORD dwEventId,
IN DWORD dwErrorCode
)
/*++
--*/
{
TCHAR szErrCode[256];
LPTSTR eventString[1];
eventString[0] = szErrCode;
_stprintf( szErrCode, L"0x%x", dwErrorCode );
LogRemoteAssistanceEventString(
dwEventType,
dwEventId,
1,
eventString
);
return;
}
inline HRESULT
CServiceModule::RemoveEventViewerSource(
IN FILE* pSetupLog
)
/*++
--*/
{
TCHAR szBuffer[MAX_PATH + 2];
DWORD dwStatus;
_stprintf(
szBuffer,
_TEXT("%s\\%s"),
REGKEY_SYSTEM_EVENTSOURCE,
m_szServiceDispName
);
dwStatus = SHDeleteKey( HKEY_LOCAL_MACHINE, szBuffer );
LogSetup(
pSetupLog,
L"Exiting RemoveEventViewerSource() with status code %d...\n",
dwStatus
);
return HRESULT_FROM_WIN32(dwStatus);
}
// Although some of these functions are big they are declared inline since they are only used once
inline HRESULT
CServiceModule::RegisterServer(FILE* pSetupLog, BOOL bRegTypeLib, BOOL bService)
{
CRegKey key;
HRESULT hr;
CRegKey keyAppID;
LONG lRes;
LogSetup(
pSetupLog,
L"\nEntering CServiceModule::RegisterServer %d, %d\n",
bRegTypeLib,
bService
);
hr = CoInitialize(NULL);
if (FAILED(hr))
{
LogSetup( pSetupLog, L"CoInitialize() failed with 0x%08x\n", hr );
goto CLEANUPANDEXIT;
}
// Remove any previous service since it may point to
// the incorrect file
//Uninstall();
// Add service entries
UpdateRegistryFromResource(IDR_Sessmgr, TRUE);
// Adjust the AppID for Local Server or Service
lRes = keyAppID.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_WRITE);
if (lRes != ERROR_SUCCESS)
{
LogSetup( pSetupLog, L"Open key AppID failed with %d\n", lRes );
hr = HRESULT_FROM_WIN32(lRes);
goto CLEANUPANDEXIT;
}
lRes = key.Open(keyAppID, _T("{038ABBA4-4138-4AC4-A492-4A3DF068BD8A}"), KEY_WRITE);
if (lRes != ERROR_SUCCESS)
{
LogSetup( pSetupLog, L"Open key 038ABBA4-4138-4AC4-A492-4A3DF068BD8A failed with %d\n", lRes );
hr = HRESULT_FROM_WIN32(lRes);
goto CLEANUPANDEXIT;
}
key.DeleteValue(_T("LocalService"));
if (bService)
{
LogSetup( pSetupLog, L"Installing service...\n" );
BOOL bInstallSuccess;
key.SetValue(m_szServiceName, _T("LocalService"));
key.SetValue(_T("-Service"), _T("ServiceParameters"));
if( IsInstalled(pSetupLog) )
{
// update service description.
bInstallSuccess = UpdateService( pSetupLog );
}
else
{
//
// Create service
//
bInstallSuccess = Install(pSetupLog);
}
if( FALSE == bInstallSuccess )
{
LogSetup( pSetupLog, L"Install or update service description failed %d\n", GetLastError() );
MYASSERT( FALSE );
hr = HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR);
}
else
{
LogSetup( pSetupLog, L"successfully installing service...\n" );
if( IsInstalled(pSetupLog) == FALSE )
{
LogSetup( pSetupLog, L"IsInstalled() return FALSE after Install()\n" );
MYASSERT(FALSE);
hr = HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR);
}
//
// Event is not log via racpldlg.dll, remove previous event source.
//
RemoveEventViewerSource(pSetupLog);
}
}
if( SUCCEEDED(hr) )
{
// Add object entries
hr = CComModule::RegisterServer(bRegTypeLib);
if( FAILED(hr) )
{
LogSetup( pSetupLog, L"CComModule::RegisterServer() on type library failed with 0x%08x\n", hr );
}
}
CoUninitialize();
CLEANUPANDEXIT:
LogSetup( pSetupLog, L"Leaving CServiceModule::RegisterServer 0x%08x\n", hr );
return hr;
}
inline HRESULT CServiceModule::UnregisterServer(FILE* pSetupLog)
{
LogSetup( pSetupLog, L"\nEntering CServiceModule::UnregisterServer\n" );
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
{
LogSetup( pSetupLog, L"CoInitialize() failed with 0x%08x\n", hr );
goto CLEANUPANDEXIT;
}
// Remove service entries
UpdateRegistryFromResource(IDR_Sessmgr, FALSE);
// Remove service
Uninstall(pSetupLog);
// Remove object entries
CComModule::UnregisterServer(TRUE);
CoUninitialize();
CLEANUPANDEXIT:
LogSetup( pSetupLog, L"Leaving CServiceModule::UnregisterServer() - 0x%08x\n", hr );
return S_OK;
}
inline void
CServiceModule::Init(
_ATL_OBJMAP_ENTRY* p,
HINSTANCE h,
UINT nServiceNameID,
UINT nServiceDispNameID,
UINT nServiceDescID,
const GUID* plibid
)
/*++
ATL Wizard generated code
--*/
{
CComModule::Init(p, h, plibid);
m_bService = TRUE;
m_dwServiceStartupStatus = ERROR_SUCCESS;
LoadString(h, nServiceNameID, m_szServiceName, sizeof(m_szServiceName) / sizeof(TCHAR));
LoadString(h, nServiceDescID, m_szServiceDesc, sizeof(m_szServiceDesc) / sizeof(TCHAR));
LoadString(h, nServiceDispNameID, m_szServiceDispName, sizeof(m_szServiceDispName)/sizeof(TCHAR));
// set up the initial service status
m_hServiceStatus = NULL;
m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
m_status.dwCurrentState = SERVICE_STOPPED;
m_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SESSIONCHANGE;
m_status.dwWin32ExitCode = 0;
m_status.dwServiceSpecificExitCode = 0;
m_status.dwCheckPoint = 0;
m_status.dwWaitHint = 0;
}
LONG CServiceModule::Unlock()
{
LONG l = CComModule::Unlock();
if (l == 0 && !m_bService)
PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
return l;
}
BOOL CServiceModule::IsInstalled(FILE* pSetupLog)
{
LogSetup( pSetupLog, L"\nEntering CServiceModule::IsInstalled()\n" );
BOOL bResult = FALSE;
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM != NULL)
{
SC_HANDLE hService = ::OpenService(hSCM, m_szServiceName, SERVICE_QUERY_CONFIG);
if (hService != NULL)
{
LogSetup( pSetupLog, L"OpenService() Succeeded\n" );
bResult = TRUE;
::CloseServiceHandle(hService);
}
else
{
LogSetup( pSetupLog, L"OpenService() failed with %d\n", GetLastError() );
}
::CloseServiceHandle(hSCM);
}
else
{
LogSetup( pSetupLog, L"OpenSCManager() failed with %d\n", GetLastError() );
}
LogSetup( pSetupLog, L"Leaving IsInstalled() - %d\n", bResult );
return bResult;
}
inline BOOL CServiceModule::UpdateService(FILE* pSetupLog)
{
DWORD dwStatus = ERROR_SUCCESS;
SERVICE_DESCRIPTION serviceDesc;
SC_HANDLE hSCM = NULL;
SC_HANDLE hService = NULL;
LogSetup( pSetupLog, L"\nEntering CServiceModule::UpdateServiceDescription()...\n" );
hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
dwStatus = GetLastError();
LogSetup( pSetupLog, L"OpenSCManager() failed with %d\n", dwStatus );
goto CLEANUPANDEXIT;
}
hService = ::OpenService( hSCM, m_szServiceName, SERVICE_CHANGE_CONFIG );
if (hService == NULL)
{
dwStatus = GetLastError();
LogSetup( pSetupLog, L"OpenService() failed with %d\n", dwStatus );
goto CLEANUPANDEXIT;
}
serviceDesc.lpDescription = (LPTSTR)m_szServiceDesc;
if( FALSE == ChangeServiceConfig2( hService, SERVICE_CONFIG_DESCRIPTION, (LPVOID)&serviceDesc ) )
{
dwStatus = GetLastError();
LogSetup( pSetupLog, L"ChangeServiceConfig2() failed with %d\n", dwStatus );
MYASSERT( ERROR_SUCCESS == dwStatus );
}
//
// Performance : Set service to be demand start for upgrade
//
if( FALSE == ChangeServiceConfig(
hService,
SERVICE_NO_CHANGE,
SERVICE_DEMAND_START,
SERVICE_NO_CHANGE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
m_szServiceDispName
) )
{
dwStatus = GetLastError();
LogSetup( pSetupLog, L"ChangeServiceConfig() failed with %d\n", dwStatus );
MYASSERT( ERROR_SUCCESS == dwStatus );
}
CLEANUPANDEXIT:
if( NULL != hService )
{
::CloseServiceHandle(hService);
}
if( NULL != hSCM )
{
::CloseServiceHandle(hSCM);
}
LogSetup( pSetupLog, L"Leaving UpdateServiceDescription::Install() - %d\n", dwStatus );
return dwStatus == ERROR_SUCCESS;
}
inline BOOL CServiceModule::Install(FILE* pSetupLog)
{
DWORD dwStatus = ERROR_SUCCESS;
SERVICE_DESCRIPTION serviceDesc;
SC_HANDLE hSCM;
TCHAR szFilePath[_MAX_PATH];
SC_HANDLE hService;
LogSetup( pSetupLog, L"\nEntering CServiceModule::Install()...\n" );
if (IsInstalled(pSetupLog))
{
LogSetup( pSetupLog, L"Service already installed\n" );
dwStatus = ERROR_SUCCESS;
goto CLEANUPANDEXIT;
}
hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
dwStatus = GetLastError();
LogSetup( pSetupLog, L"OpenSCManager() failed with %d\n", dwStatus );
goto CLEANUPANDEXIT;
}
// Get the executable file path
::GetModuleFileName(NULL, szFilePath, _MAX_PATH);
hService = ::CreateService(
hSCM,
m_szServiceName,
m_szServiceDispName,
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
szFilePath,
NULL,
NULL,
_T("RPCSS\0"),
NULL,
NULL
);
if (hService == NULL)
{
dwStatus = GetLastError();
LogSetup( pSetupLog, L"CreateService() failed with %d\n", dwStatus );
::CloseServiceHandle(hSCM);
goto CLEANUPANDEXIT;
}
serviceDesc.lpDescription = (LPTSTR)m_szServiceDesc;
if( FALSE == ChangeServiceConfig2( hService, SERVICE_CONFIG_DESCRIPTION, (LPVOID)&serviceDesc ) )
{
dwStatus = GetLastError();
LogSetup( pSetupLog, L"ChangeServiceConfig2() failed with %d\n", dwStatus );
MYASSERT( ERROR_SUCCESS == dwStatus );
}
::CloseServiceHandle(hService);
::CloseServiceHandle(hSCM);
CLEANUPANDEXIT:
LogSetup( pSetupLog, L"Leaving CServiceModule::Install() - %d\n", dwStatus );
return dwStatus == ERROR_SUCCESS;
}
inline BOOL CServiceModule::Uninstall(FILE* pSetupLog)
{
BOOL bStatus = TRUE;
SC_HANDLE hService;
SC_HANDLE hSCM;
SERVICE_STATUS status;
LogSetup( pSetupLog, L"\nEntering CServiceModule::Uninstall()...\n" );
if (!IsInstalled(pSetupLog))
{
LogSetup( pSetupLog, L"Service is not installed...\n" );
goto CLEANUPANDEXIT;
}
hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
LogSetup( pSetupLog, L"OpenSCManager() failed with %d\n", GetLastError() );
bStatus = FALSE;
goto CLEANUPANDEXIT;
}
hService = ::OpenService(hSCM, m_szServiceName, SERVICE_STOP | DELETE);
if (hService == NULL)
{
::CloseServiceHandle(hSCM);
LogSetup( pSetupLog, L"OpenService() failed with %d\n", GetLastError() );
bStatus = FALSE;
goto CLEANUPANDEXIT;
}
::ControlService(hService, SERVICE_CONTROL_STOP, &status);
bStatus = ::DeleteService(hService);
if( FALSE == bStatus )
{
LogSetup( pSetupLog, L"DeleteService() failed with %d\n", GetLastError() );
}
::CloseServiceHandle(hService);
::CloseServiceHandle(hSCM);
CLEANUPANDEXIT:
LogSetup( pSetupLog, L"Leaving CServiceModule::Uninstall()\n" );
return bStatus;
}
//////////////////////////////////////////////////////////////////////////////////////////////
// Service startup and registration
inline void CServiceModule::Start()
{
SERVICE_TABLE_ENTRY st[] =
{
{ m_szServiceName, _ServiceMain },
{ NULL, NULL }
};
if (m_bService && !::StartServiceCtrlDispatcher(st))
{
m_bService = FALSE;
}
if (m_bService == FALSE)
Run();
}
inline void CServiceModule::ServiceMain(DWORD /* dwArgc */, LPTSTR* /* lpszArgv */)
{
// Register the control request handler
m_status.dwCurrentState = SERVICE_START_PENDING;
m_hServiceStatus = RegisterServiceCtrlHandlerEx(m_szServiceName, HandlerEx, this);
if (m_hServiceStatus == NULL)
{
//LogEvent(_T("Handler not installed"));
return;
}
m_status.dwWin32ExitCode = S_OK;
m_status.dwCheckPoint = 0;
m_status.dwWaitHint = SERVICE_STARTUP_WAITHINT;
SetServiceStatus(SERVICE_START_PENDING);
// When the Run function returns, the service has stopped.
Run();
SetServiceStatus(SERVICE_STOPPED);
}
inline void CServiceModule::Handler(DWORD dwOpcode)
{
switch (dwOpcode)
{
case SERVICE_CONTROL_STOP:
SetServiceStatus(SERVICE_STOP_PENDING);
if( PostThreadMessage(dwThreadID, WM_QUIT, 0, 0) == FALSE )
{
DWORD dwStatus = GetLastError();
}
break;
case SERVICE_CONTROL_PAUSE:
break;
case SERVICE_CONTROL_CONTINUE:
break;
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_SHUTDOWN:
break;
//default:
// LogEvent(_T("Bad service request"));
}
}
inline DWORD WINAPI
CServiceModule::HandlerEx(
DWORD dwControl,
DWORD dwEventType,
LPVOID lpEventData,
LPVOID lpContext
)
/*++
--*/
{
DWORD dwRetCode;
switch (dwControl)
{
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_PAUSE:
case SERVICE_CONTROL_CONTINUE:
case SERVICE_CONTROL_INTERROGATE:
case SERVICE_CONTROL_SHUTDOWN:
dwRetCode = NO_ERROR;
_Handler(dwControl);
break;
#if DISABLESECURITYCHECKS
// this is require for Salem Unit test, we need to update
// user session status but for pcHealth, resolver will
// always popup invitation dialog so no need to track
// user session status.
case SERVICE_CONTROL_SESSIONCHANGE:
MYASSERT( NULL != lpEventData );
if( NULL != lpEventData )
{
switch( dwEventType )
{
case WTS_SESSION_LOGON:
DebugPrintf(
_TEXT("Session %d has log on...\n"),
((WTSSESSION_NOTIFICATION *)lpEventData)->dwSessionId
);
break;
case WTS_SESSION_LOGOFF:
case WTS_CONSOLE_DISCONNECT:
case WTS_REMOTE_DISCONNECT:
DebugPrintf(
_TEXT("Session %d has log off...\n"),
((WTSSESSION_NOTIFICATION *)lpEventData)->dwSessionId
);
//
// Deadlock if we use other thread to process logoff or
// disconnect.
//
// Notification thread lock pending help table and need
// to run Resolver in COM, COM is in the middle of
// dispatching create help ticket call which also need
// lock to pending help table, this causes deadlock
//
PostThreadMessage(
_Module.dwThreadID,
WM_SESSIONLOGOFFDISCONNECT,
0,
(LPARAM)((WTSSESSION_NOTIFICATION *)lpEventData)->dwSessionId
);
}
}
dwRetCode = NO_ERROR;
break;
#endif
default:
dwRetCode = ERROR_CALL_NOT_IMPLEMENTED;
}
return dwRetCode;
}
void WINAPI CServiceModule::_ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
{
_Module.ServiceMain(dwArgc, lpszArgv);
}
void WINAPI CServiceModule::_Handler(DWORD dwOpcode)
{
_Module.Handler(dwOpcode);
}
void CServiceModule::SetServiceStatus(DWORD dwState)
{
m_status.dwCurrentState = dwState;
::SetServiceStatus(m_hServiceStatus, &m_status);
}
HANDLE CServiceModule::gm_hIdle = NULL;
HANDLE CServiceModule::gm_hIdleMonitorThread = NULL;
ULONG
CServiceModule::AddRef()
{
CCriticalSectionLocker l( m_ModuleLock );
m_RefCount++;
if( m_RefCount > 0 )
{
ASSERT( NULL != gm_hIdle );
ResetEvent( gm_hIdle );
}
return m_RefCount;
}
ULONG
CServiceModule::Release()
{
CCriticalSectionLocker l( m_ModuleLock );
m_RefCount--;
if( m_RefCount <= 0 )
{
// Only signal idle when there is no more pending help
if( g_HelpSessTable.NumEntries() == 0 )
{
ASSERT( NULL != gm_hIdle );
SetEvent( gm_hIdle );
}
}
return m_RefCount;
}
unsigned int WINAPI
CServiceModule::GPMonitorThread( void* ptr )
{
DWORD dwStatus = ERROR_SUCCESS;
CServiceModule* pServiceModule = (CServiceModule *)ptr;
if( pServiceModule != NULL )
{
dwStatus = WaitForRAGPDisableNotification( g_hServiceShutdown );
ASSERT(ERROR_SUCCESS == dwStatus || ERROR_SHUTDOWN_IN_PROGRESS == dwStatus);
pServiceModule->Handler(SERVICE_CONTROL_STOP);
}
_endthreadex( dwStatus );
return dwStatus;
}
unsigned int WINAPI
CServiceModule::IdleMonitorThread( void* ptr )
{
DWORD dwStatus = ERROR_SUCCESS;
BOOL bIdleShutdown = FALSE;
CServiceModule* pServiceModule = (CServiceModule *)ptr;
// remove gm_hICSAlertEvent, this event will be removed from ICS lib.
HANDLE hWaitHandles[] = {g_hServiceShutdown, gm_hIdle};
CoInitialize(NULL);
if( pServiceModule != NULL )
{
while (TRUE)
{
dwStatus = WaitForMultipleObjects(
sizeof( hWaitHandles ) / sizeof( hWaitHandles[0] ),
hWaitHandles,
FALSE,
EXPIRE_HELPSESSION_PERIOD
);
if( WAIT_TIMEOUT == dwStatus )
{
// expire help ticket, refer to session logoff/disconnect
// comment above on why PostThreadMessage.
PostThreadMessage(
_Module.dwThreadID,
WM_EXPIREHELPSESSION,
0,
0
);
}
else if( WAIT_OBJECT_0 == dwStatus )
{
// Main thread signal shutdown.
dwStatus = ERROR_SUCCESS;
break;
}
else if( WAIT_OBJECT_0 + 1 == dwStatus )
{
// we have been idle for too long, time to try shutdown.
// idle event will only be signal when there is no
// pending help so we don't have to worry about address
// changes.
dwStatus = WaitForSingleObject( g_hServiceShutdown, IDLE_SHUTDOWN_PERIOD );
if( WAIT_TIMEOUT != dwStatus )
{
// Main thread either signnaled shutdown or wait failed due to error, baid out
break;
}
dwStatus = WaitForSingleObject( gm_hIdle, 0 );
if( WAIT_OBJECT_0 == dwStatus )
{
// no one holding object, time to shutdown
bIdleShutdown = TRUE;
dwStatus = ERROR_SUCCESS;
break;
}
}
else if( WAIT_FAILED == dwStatus )
{
// some bad thing happen, shutdown.
//MYASSERT(FALSE);
break;
}
}
// only need to stop service if shutdown is due to idle
if( bIdleShutdown )
{
pServiceModule->Handler(SERVICE_CONTROL_STOP);
}
}
CoUninitialize();
_endthreadex( dwStatus );
return dwStatus;
}
BOOL
CServiceModule::InitializeSessmgr()
{
CCriticalSectionLocker l( m_ModuleLock );
//
// Already initialize.
//
if( m_Initialized )
{
return TRUE;
}
//
// Service failed to startup, just return without initialize
// anything
//
if( !_Module.IsSuccessServiceStartup() )
{
return FALSE;
}
DWORD dwStatus;
unsigned int junk;
//
// Start ICSHELPER library, this library calls into some DLL that
// makes outgoing COM call which will trigger COM re-entrance, so
// InitializeSessmgr() must be invoke in helpsessionmgr object
// constructor instead of service startup time.
//
dwStatus = StartICSLib();
if( ERROR_SUCCESS != dwStatus )
{
// Log an error event, we still need to startup
// so that we can report error back to caller
LogEventWithStatusCode(
EVENTLOG_ERROR_TYPE,
SESSMGR_E_ICSHELPER,
dwStatus
);
_Module.m_dwServiceStartupStatus = SESSMGR_E_ICSHELPER;
}
else
{
//
// Go thru all pending tickets and re-punch ICS hole
//
CRemoteDesktopHelpSessionMgr::NotifyPendingHelpServiceStartup();
}
m_Initialized = TRUE;
return _Module.IsSuccessServiceStartup();
}
ISAFRemoteDesktopCallback* g_pIResolver = NULL;
// SERVICE_STARTUP_WAITHINT is 30 seconds, retry 6 time will give us
// 3 mins of wait time.
#define RA_ACCOUNT_CREATE_RETRYTIME 6
unsigned int WINAPI
StartupCreateAccountThread( void* ptr )
{
HRESULT hr = S_OK;
//
// BDC request pool of RID from DC and during that time, it
// will return error ERROR_DS_NO_RIDS_ALLOCATED, we wait and retry
// RA_ACCOUNT_CREATE_RETRYTIME times before we actually fail.
//
for(DWORD index=0; index < RA_ACCOUNT_CREATE_RETRYTIME; index++)
{
// Try re-create the account
hr = g_HelpAccount.CreateHelpAccount();
if( SUCCEEDED(hr) )
{
CComBSTR bstrHelpAccName;
hr = g_HelpAccount.GetHelpAccountNameEx( bstrHelpAccName );
MYASSERT( SUCCEEDED(hr) );
if( SUCCEEDED(hr) )
{
// Add HelpAssistantAccount into account filter list
AddAccountToFilterList( bstrHelpAccName );
}
break;
}
else if( hr != HRESULT_FROM_WIN32(ERROR_DS_NO_RIDS_ALLOCATED) )
{
break;
}
DebugPrintf(
_TEXT("CreateHelpAccount() return 0x%08x, retry again...\n"),
hr
);
// Wait one second before continue.
Sleep( 1000 );
}
_endthreadex( hr );
return hr;
}
void CServiceModule::Run()
{
//
// Mark we are not initialized yet...
//
m_Initialized = FALSE;
_Module.dwThreadID = GetCurrentThreadId();
DWORD dwStatus;
unsigned int dwJunk;
WSADATA wsData;
LPWSTR pszSysAccName = NULL;
DWORD cbSysAccName = 0;
LPWSTR pszSysDomainName = NULL;
DWORD cbSysDomainName = 0;
SID_NAME_USE SidType;
BOOL bReCreateRAAccount = FALSE;
HRESULT hr;
//
// make sure no other thread can access _Module until we fully
// startup.
//
m_ModuleLock.Lock();
//
// Initialize encryption library
//
dwStatus = TSHelpAssistantInitializeEncryptionLib();
if( ERROR_SUCCESS != dwStatus )
{
// Log an error event, we still need to startup
// so that we can report error back to caller
LogEventWithStatusCode(
EVENTLOG_ERROR_TYPE,
SESSMGR_E_INIT_ENCRYPTIONLIB,
dwStatus
);
_Module.m_dwServiceStartupStatus = SESSMGR_E_INIT_ENCRYPTIONLIB;
MYASSERT(FALSE);
}
else
{
//
// Check if we just started from system restore, if so, restore necessary
// LSA key.
//
RestartFromSystemRestore();
}
//
// Create an manual reset event for background thread to terminate
// service
//
gm_hIdle = CreateEvent(
NULL,
TRUE,
FALSE,
NULL
);
if( NULL == gm_hIdle )
{
LogEventWithStatusCode(
EVENTLOG_ERROR_TYPE,
SESSMGR_E_GENERALSTARTUP,
GetLastError()
);
_Module.m_dwServiceStartupStatus = SESSMGR_E_GENERALSTARTUP;
MYASSERT(FALSE);
}
//
// Create a service shutdown event for GP notification thread.
//
g_hServiceShutdown = CreateEvent(
NULL,
TRUE,
FALSE,
NULL
);
if( NULL == g_hServiceShutdown )
{
LogEventWithStatusCode(
EVENTLOG_ERROR_TYPE,
SESSMGR_E_GENERALSTARTUP,
GetLastError()
);
_Module.m_dwServiceStartupStatus = SESSMGR_E_GENERALSTARTUP;
MYASSERT(FALSE);
}
//
// **** DO NOT CHANGE SEQUENCE ****
//
// Refer to XP RAID 407457 for detail
//
// A thread named SessMgr!DpNatHlpThread is calling into dpnhupnp.dll,
// which is doing COM-related stuff, this is happening before the
// sessmgr!CServiceModule__Run method calls CoInitializeSecurity.
// When you do COM stuff before calling CoInitSec, COM do it for you,
// and you end up accepting the defaults
//
hr = g_HelpAccount.Initialize();
if( FAILED(hr) )
{
// use seperate thread to re-create RA account
//
// BDC request pool of RID from PDC, during this time, account
// creation will failed with ERROR_DS_NO_RIDS_ALLOCATED.
// since RA account is needed before we initialize our
// COM security, we will loop/retry a few time and duing this
// time, we still need to notify service control manager that
// we still pending startup.
//
HANDLE hCreateAcctThread = NULL;
bReCreateRAAccount = TRUE;
hr = S_OK;
hCreateAcctThread = (HANDLE)_beginthreadex(
NULL,
0,
StartupCreateAccountThread,
NULL,
0,
&dwJunk
);
if( NULL == hCreateAcctThread )
{
dwStatus = GetLastError();
hr = HRESULT_FROM_WIN32( dwStatus );
}
else
{
// wait for account creation thread to terminate, thread will retry to
// create account for number of time before it bail out.
while( WaitForSingleObject( hCreateAcctThread, SERVICE_STARTUP_WAITHINT ) == WAIT_TIMEOUT )
{
SetServiceStatus( SERVICE_START_PENDING );
continue;
}
if( FALSE == GetExitCodeThread( hCreateAcctThread, &dwStatus ) )
{
_Module.m_dwServiceStartupStatus = SESSMGR_E_HELPACCOUNT;
hr = SESSMGR_E_HELPACCOUNT;
}
else
{
_Module.m_dwServiceStartupStatus = dwStatus;
hr = HRESULT_FROM_WIN32( dwStatus );
}
CloseHandle( hCreateAcctThread );
}
if( FAILED(hr) )
{
dwStatus = SESSMGR_E_HELPACCOUNT;
LogEventWithStatusCode(
EVENTLOG_ERROR_TYPE,
SESSMGR_E_GENERALSTARTUP,
hr
);
_Module.m_dwServiceStartupStatus = SESSMGR_E_HELPACCOUNT;
}
}
hr = LoadLocalSystemSID();
if( FAILED(hr) )
{
LogEventWithStatusCode(
EVENTLOG_ERROR_TYPE,
SESSMGR_E_GENERALSTARTUP,
hr
);
_Module.m_dwServiceStartupStatus = SESSMGR_E_GENERALSTARTUP;
MYASSERT(FALSE);
}
//
// We always need to startup otherwise will cause caller to timeout
// or AV.
//
// hr = CoInitialize(NULL);
// If you are running on NT 4.0 or higher you can use the following call
// instead to make the EXE free threaded.
// This means that calls come in on a random RPC thread
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
_ASSERTE(SUCCEEDED(hr));
CSecurityDescriptor sd;
sd.InitializeFromThreadToken(); // get a default DACL
#ifndef DISABLESECURITYCHECKS
if( _Module.IsSuccessServiceStartup() )
{
BOOL bSuccess;
CComBSTR bstrHelpAccName;
//
// Retrieve System account name, might not be necessary since this
// pre-defined account shouldn't be localizable.
//
pszSysAccName = NULL;
cbSysAccName = 0;
pszSysDomainName = NULL;
cbSysDomainName = 0;
bSuccess = LookupAccountSid(
NULL,
g_pSidSystem,
pszSysAccName,
&cbSysAccName,
pszSysDomainName,
&cbSysDomainName,
&SidType
);
if( TRUE == bSuccess ||
ERROR_INSUFFICIENT_BUFFER == GetLastError() )
{
pszSysAccName = (LPWSTR) LocalAlloc( LPTR, (cbSysAccName + 1) * sizeof(WCHAR) );
pszSysDomainName = (LPWSTR) LocalAlloc( LPTR, (cbSysDomainName + 1) * sizeof(WCHAR) );
if( NULL != pszSysAccName && NULL != pszSysDomainName )
{
bSuccess = LookupAccountSid(
NULL,
g_pSidSystem,
pszSysAccName,
&cbSysAccName,
pszSysDomainName,
&cbSysDomainName,
&SidType
);
if( TRUE == bSuccess )
{
hr = sd.Allow( pszSysAccName, COM_RIGHTS_EXECUTE );
}
}
}
if( FALSE == bSuccess )
{
dwStatus = GetLastError();
hr = HRESULT_FROM_WIN32( dwStatus );
MYASSERT( SUCCEEDED(hr) );
}
//
// Add access permission to help assistant account
if( SUCCEEDED(hr) )
{
//
// Allow access to HelpAssistant account
//
hr = g_HelpAccount.GetHelpAccountNameEx( bstrHelpAccName );
if( SUCCEEDED(hr) )
{
hr = sd.Allow( (LPCTSTR)bstrHelpAccName, COM_RIGHTS_EXECUTE );
MYASSERT( SUCCEEDED(hr) );
}
}
//
// If we failed in setting DACL, we still need to startup but without
// full security, however, our interface will fail because service
// does not initialize correctly.
//
if( FAILED(hr) )
{
LogEventWithStatusCode(
EVENTLOG_ERROR_TYPE,
SESSMGR_E_RESTRICTACCESS,
hr
);
_Module.m_dwServiceStartupStatus = SESSMGR_E_RESTRICTACCESS;
}
}
#endif
//
// We still need to startup or client might behave weird; interface call
// will be block by checking service startup status.
//
hr = CoInitializeSecurity(
sd,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IDENTIFY,
NULL,
EOAC_NONE,
NULL
);
_ASSERTE(SUCCEEDED(hr));
hr = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
_ASSERTE(SUCCEEDED(hr));
//
// Load unknown string for event loggging
//
g_UnknownString.LoadString( IDS_UNKNOWN );
//
// Load RA and URA string for event log
//
g_RAString.LoadString( IDS_RA_STRING );
g_URAString.LoadString( IDS_URA_STRING );
if( _Module.IsSuccessServiceStartup() )
{
//
// Startup TLSAPI in order to get public key
//
dwStatus = TLSInit();
if( LICENSE_STATUS_OK != dwStatus )
{
LogEventWithStatusCode(
EVENTLOG_ERROR_TYPE,
SESSMGR_E_GENERALSTARTUP,
dwStatus
);
_Module.m_dwServiceStartupStatus = SESSMGR_E_GENERALSTARTUP;
MYASSERT(FALSE);
}
}
if( _Module.IsSuccessServiceStartup() )
{
//
// Load TermSrv public key, on PRO/PER we load public key from
// non-x509 certificate, on other SKU, we register a registry change
// notification and post ourself a message regarding public key
// change.
//
dwStatus = LoadAndSetupTSCertChangeNotification();
MYASSERT( ERROR_SUCCESS == dwStatus );
if( ERROR_SUCCESS != dwStatus )
{
// Log an error event, we still need to startup
// so that we can report error back to caller
LogEventWithStatusCode(
EVENTLOG_ERROR_TYPE,
SESSMGR_E_GENERALSTARTUP,
dwStatus
);
_Module.m_dwServiceStartupStatus = SESSMGR_E_GENERALSTARTUP;
}
}
if( _Module.IsSuccessServiceStartup() )
{
//
// startup WSA so we can invoke gethostname()
// critical error if we can startup WSA
if( WSAStartup(0x0101, &wsData) != 0 )
{
// Log an error event, we still need to startup
// so that we can report error back to caller
LogEventWithStatusCode(
EVENTLOG_ERROR_TYPE,
SESSMGR_E_WSASTARTUP,
GetLastError()
);
_Module.m_dwServiceStartupStatus = SESSMGR_E_WSASTARTUP;
}
}
if( _Module.IsSuccessServiceStartup() )
{
hr = g_HelpSessTable.OpenSessionTable(NULL);
if( FAILED(hr) )
{
LogEventWithStatusCode(
EVENTLOG_ERROR_TYPE,
SESSMGR_E_HELPSESSIONTABLE,
hr
);
_Module.m_dwServiceStartupStatus = SESSMGR_E_HELPSESSIONTABLE;
MYASSERT(FALSE);
}
}
if( _Module.IsSuccessServiceStartup() )
{
if( g_HelpSessTable.NumEntries() == 0)
{
// Immediately set event to signal state so idle monitor
// thread can start shutdown timer.
SetEvent( gm_hIdle );
g_HelpAccount.EnableHelpAssistantAccount(FALSE);
g_HelpAccount.EnableRemoteInteractiveRight(FALSE);
}
else
{
// outstanding ticket exists, set event to non-signal state
// and don't let idle monitor thread to start shutdown timer.
ResetEvent( gm_hIdle );
//
// make sure HelpAssistant account is enabled and can logon locally
//
g_HelpAccount.EnableHelpAssistantAccount(TRUE);
g_HelpAccount.EnableRemoteInteractiveRight(TRUE);
//
// demote BDC back to server in domain.
//
g_HelpAccount.SetupHelpAccountTSSettings( bReCreateRAAccount );
}
// Create nackground thread thread
gm_hIdleMonitorThread = (HANDLE)_beginthreadex(
NULL,
0,
IdleMonitorThread,
(HANDLE)this,
0,
&dwJunk
);
if( NULL == gm_hIdleMonitorThread )
{
_Module.m_dwServiceStartupStatus = SESSMGR_E_GENERALSTARTUP;
}
// Create background thread to monitor RA GP change.
// We have to use extra thread because
g_hGPMonitorThread = (HANDLE)_beginthreadex(
NULL,
0,
GPMonitorThread,
(HANDLE)this,
0,
&dwJunk
);
if( NULL == g_hGPMonitorThread )
{
_Module.m_dwServiceStartupStatus = SESSMGR_E_GENERALSTARTUP;
}
}
//LogEvent(_T("Service started"));
if (m_bService)
SetServiceStatus(SERVICE_RUNNING);
//
// Load resolver, this will put one ref. count on it
// so it won't got unload until we are done.
//
hr = CoCreateInstance(
SESSIONRESOLVERCLSID,
NULL,
CLSCTX_INPROC_SERVER | CLSCTX_DISABLE_AAA,
IID_ISAFRemoteDesktopCallback,
(void **)&g_pIResolver
);
MYASSERT( SUCCEEDED(hr) );
if( FAILED(hr) )
{
//
// Can't initialize session resolver,
// session resolver will not be able to
// do caching.
//
LogEventWithStatusCode(
EVENTLOG_WARNING_TYPE,
SESSMGR_E_SESSIONRESOLVER,
hr
);
_Module.m_dwServiceStartupStatus = SESSMGR_E_SESSIONRESOLVER;
}
m_ModuleLock.UnLock();
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
{
switch( msg.message )
{
case WM_EXPIREHELPSESSION:
DebugPrintf(_TEXT("Executing TimeoutHelpSesion()...\n"));
CRemoteDesktopHelpSessionMgr::TimeoutHelpSesion();
break;
#if DISABLESECURITYCHECKS
case WM_SESSIONLOGOFFDISCONNECT:
DebugPrintf(_TEXT("Executing NotifySessionLogoff() %d...\n"), msg.lParam);
NotifySessionLogoff( msg.lParam );
break;
#endif
case WM_LOADTSPUBLICKEY:
DebugPrintf( _TEXT("Executing LoadTermSrvSecurityBlob() ...\n") );
dwStatus = LoadTermSrvSecurityBlob();
if( ERROR_SUCCESS != dwStatus )
{
// Log an error event, we still need to startup
// so that we can report error back to caller
LogEventWithStatusCode(
EVENTLOG_ERROR_TYPE,
SESSMGR_E_GENERALSTARTUP,
dwStatus
);
_Module.m_dwServiceStartupStatus = SESSMGR_E_GENERALSTARTUP;
}
break;
case WM_HELPERRDSADDINEXIT:
DebugPrintf( _TEXT("WM_HELPERRDSADDINEXIT()...\n") );
CRemoteDesktopHelpSessionMgr::NotifyExpertLogoff( msg.wParam, (BSTR)msg.lParam );
break;
default:
DispatchMessage(&msg);
}
}
//
// Calling StopICSLib() while there is a call into ICS lib's OpenPort()
// will cause deadlock in this main thread, ICS lib's DpNatHlpThread()'s
// shutdown and ICS lib's OpenPort().
//
// First call is to lock access to InitializeSessmgr() which is call on FinalConstruct()
// of CRemoteDesktopHelpSessionMgr, second is lock calls into ICS lib to make sure
// no client is making call into ICS lib.
m_ModuleLock.Lock();
g_ICSLibLock.Lock();
if( g_hServiceShutdown )
{
// Signal we are shutting down
SetEvent(g_hServiceShutdown);
}
if( g_hGPMonitorThread )
{
// GPMonitor thread can stuck for DELAY_SHUTDOWN_SALEM_TIME
// waiting for policy change so we wait twice that time.
dwStatus = WaitForSingleObject(
g_hGPMonitorThread,
DELAY_SHUTDOWN_SALEM_TIME * 2
);
ASSERT( dwStatus == WAIT_OBJECT_0 );
}
if( gm_hIdleMonitorThread )
{
// Wait for IdleMonitor thread to shutdown
dwStatus = WaitForSingleObject(
gm_hIdleMonitorThread,
DELAY_SHUTDOWN_SALEM_TIME * 2
);
ASSERT( dwStatus == WAIT_OBJECT_0 );
}
CleanupMonitorExpertList();
if( g_hWaitTSCertificateChanged )
{
UnregisterWait( g_hWaitTSCertificateChanged );
g_hWaitTSCertificateChanged = NULL;
}
if( g_hTSCertificateChanged )
{
CloseHandle( g_hTSCertificateChanged );
g_hTSCertificateChanged = NULL;
}
if( g_hTSCertificateRegKey )
{
RegCloseKey( g_hTSCertificateRegKey );
g_hTSCertificateRegKey = NULL;
}
//
// If service is started manually, we won't be able to call
// StartICSLib() and will close invalid handle in ICS.
//
if( m_Initialized )
{
// Close all port including close firewall.
CloseAllOpenPorts();
// Stop ICS library, ignore error code.
StopICSLib();
}
g_ICSLibLock.UnLock();
m_ModuleLock.UnLock();
//
// sync. access to resolver.
//
{
CCriticalSectionLocker Lock(g_ResolverLock);
if( NULL != g_pIResolver )
{
g_pIResolver->Release();
g_pIResolver = NULL;
}
}
_Module.RevokeClassObjects();
CoUninitialize();
//
// No outstanding ticket, delete the account
//
if( g_HelpSessTable.NumEntries() == 0)
{
CComBSTR bstrHelpAccName;
hr = g_HelpAccount.GetHelpAccountNameEx( bstrHelpAccName );
MYASSERT( SUCCEEDED(hr) );
if( SUCCEEDED(hr) )
{
// Add HelpAssistantAccount into account filter list
DeleteAccountFromFilterList( bstrHelpAccName );
}
g_HelpAccount.DeleteHelpAccount();
}
else
{
// Extra security measure, at the time of shutdown,
// if there is outstanding ticket, we disable helpassistant
// account, on service startup, we will re-enable it again.
g_HelpAccount.EnableHelpAssistantAccount(FALSE);
g_HelpAccount.EnableRemoteInteractiveRight(FALSE);
}
if( NULL != gm_hIdle )
{
CloseHandle( gm_hIdle );
gm_hIdle = NULL;
}
if( WSACleanup() != 0 )
{
// shutting down, ignore WSA error
#if DBG
OutputDebugString( _TEXT("WSACleanup() failed...\n") );
#endif
}
#if DBG
OutputDebugString( _TEXT("Help Session Manager Exited...\n") );
#endif
// Close the help session table, help session table
// open by init. thread
g_HelpSessTable.CloseSessionTable();
TSHelpAssistantEndEncryptionLib();
if( NULL != pszSysAccName )
{
LocalFree( pszSysAccName );
}
if( NULL != pszSysDomainName )
{
LocalFree( pszSysDomainName );
}
if( NULL != gm_hIdleMonitorThread )
{
CloseHandle( gm_hIdleMonitorThread );
}
if( NULL != g_hGPMonitorThread )
{
CloseHandle( g_hGPMonitorThread );
}
if( NULL != g_hServiceShutdown )
{
CloseHandle( g_hServiceShutdown );
}
TLSShutdown();
}
#define OLD_SALEMHELPASSISTANTACCOUNT_PASSWORDKEY \
L"0083343a-f925-4ed7-b1d6-d95d17a0b57b-RemoteDesktopHelpAssistantAccount"
#define OLD_SALEMHELPASSISTANTACCOUNT_SIDKEY \
L"0083343a-f925-4ed7-b1d6-d95d17a0b57b-RemoteDesktopHelpAssistantSID"
#define OLD_SALEMHELPASSISTANTACCOUNT_ENCRYPTIONKEY \
L"c261dd33-c55b-4a37-924b-746bbf3569ad-RemoteDesktopHelpAssistantEncrypt"
#define OLD_HELPACCOUNTPROPERLYSETUP \
_TEXT("20ed87e2-3b82-4114-81f9-5e219ed4c481-SALEMHELPACCOUNT")
VOID
TransferLSASecretKey()
/*++
Routine Description:
Retrieve data we store in LSA secret key and re-store it with LSA key prefixed with L$
to make LSA secret value local to machine only.
Parameters:
None.
Returns:
None.
--*/
{
PBYTE pbData = NULL;
DWORD cbData = 0;
DWORD dwStatus;
dwStatus = RetrieveKeyFromLSA(
OLD_HELPACCOUNTPROPERLYSETUP,
(PBYTE *)&pbData,
&cbData
);
if( ERROR_SUCCESS == dwStatus )
{
//
// Old key exists, store it with new key and delete the old key.
//
dwStatus = StoreKeyWithLSA(
HELPACCOUNTPROPERLYSETUP,
pbData,
cbData
);
SecureZeroMemory( pbData, cbData );
LocalFree(pbData);
pbData = NULL;
cbData = 0;
}
dwStatus = RetrieveKeyFromLSA(
OLD_SALEMHELPASSISTANTACCOUNT_PASSWORDKEY,
(PBYTE *)&pbData,
&cbData
);
if( ERROR_SUCCESS == dwStatus )
{
//
// Old key exists, store it with new key and delete the old key.
//
dwStatus = StoreKeyWithLSA(
SALEMHELPASSISTANTACCOUNT_PASSWORDKEY,
pbData,
cbData
);
SecureZeroMemory( pbData, cbData );
LocalFree(pbData);
pbData = NULL;
cbData = 0;
}
dwStatus = RetrieveKeyFromLSA(
OLD_SALEMHELPASSISTANTACCOUNT_SIDKEY,
(PBYTE *)&pbData,
&cbData
);
if( ERROR_SUCCESS == dwStatus )
{
//
// Old key exists, store it with new key and delete the old key.
//
dwStatus = StoreKeyWithLSA(
SALEMHELPASSISTANTACCOUNT_SIDKEY,
pbData,
cbData
);
SecureZeroMemory( pbData, cbData );
LocalFree(pbData);
pbData = NULL;
cbData = 0;
}
dwStatus = RetrieveKeyFromLSA(
OLD_SALEMHELPASSISTANTACCOUNT_ENCRYPTIONKEY,
(PBYTE *)&pbData,
&cbData
);
if( ERROR_SUCCESS == dwStatus )
{
//
// Old key exists, store it with new key and delete the old key.
//
dwStatus = StoreKeyWithLSA(
SALEMHELPASSISTANTACCOUNT_ENCRYPTIONKEY,
pbData,
cbData
);
SecureZeroMemory( pbData, cbData );
LocalFree(pbData);
pbData = NULL;
cbData = 0;
}
//
// Delete the key and ignore the error.
//
StoreKeyWithLSA(
OLD_HELPACCOUNTPROPERLYSETUP,
NULL,
0
);
StoreKeyWithLSA(
OLD_SALEMHELPASSISTANTACCOUNT_PASSWORDKEY,
NULL,
0
);
StoreKeyWithLSA(
OLD_SALEMHELPASSISTANTACCOUNT_SIDKEY,
NULL,
0
);
StoreKeyWithLSA(
OLD_SALEMHELPASSISTANTACCOUNT_ENCRYPTIONKEY,
NULL,
0
);
return;
}
#define UNINSTALL_BEFORE_INSTALL _TEXT("UninstallBeforeInstall")
HRESULT
InstallUninstallSessmgr(
DWORD code
)
/*++
--*/
{
FILE* pSetupLog;
TCHAR LogFile[MAX_PATH+1];
HRESULT hRes = S_OK;
DWORD dwStatus = ERROR_SUCCESS;
HKEY hKey = NULL;
DWORD dwValue = 1;
DWORD dwType;
DWORD cbData = sizeof(dwValue);
GetWindowsDirectory( LogFile, MAX_PATH );
lstrcat( LogFile, L"\\" );
lstrcat( LogFile, SETUPLOGFILE_NAME );
pSetupLog = _tfopen( LogFile, L"a+t" );
MYASSERT( NULL != pSetupLog );
LogSetup( pSetupLog, L"\n\n********* Install/uninstall sessmgr service *********\n" );
//
// no checking on return, if failure, we just do OutputDebugString();
//
switch( code )
{
case SESSMGR_UNREGSERVER:
{
LogSetup( pSetupLog, L"Uninstalling sessmgr service\n" );
//
// Delete all pending help session.
//
dwStatus = RegDelKey(
HKEY_LOCAL_MACHINE,
REGKEYCONTROL_REMDSK _TEXT("\\") REGKEY_HELPSESSIONTABLE
);
LogSetup( pSetupLog, L"Delete pending table return %d\n", dwStatus );
//
// We might not be running in system context so deleting registry and
// cleanup LSA key will fail, write a key to our control location to
// mark such that delete everything before install
//
dwStatus = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
REGKEYCONTROL_REMDSK,
0,
KEY_ALL_ACCESS,
&hKey
);
if( ERROR_SUCCESS == dwStatus )
{
dwStatus = RegSetValueEx(
hKey,
UNINSTALL_BEFORE_INSTALL,
0,
REG_DWORD,
(BYTE *) &dwValue,
sizeof(dwValue)
);
if( ERROR_SUCCESS != dwStatus )
{
LogSetup( pSetupLog, L"Failed to set value, error code %d\n", dwStatus );
MYASSERT(FALSE);
}
RegCloseKey( hKey );
}
else
{
// This is OK since we havn't been install before.
LogSetup( pSetupLog, L"Failed to open control key, error code %d\n", dwStatus );
}
//
// Initialize to get help account name.
//
hRes = g_HelpAccount.Initialize();
LogSetup( pSetupLog, L"Initialize help account return 0x%08x\n", hRes );
//
// ignore error, try to delete the account
hRes = g_HelpAccount.DeleteHelpAccount();
LogSetup( pSetupLog, L"Delete help account return 0x%08x\n", hRes );
MYASSERT( SUCCEEDED(hRes) );
hRes = _Module.UnregisterServer(pSetupLog);
LogSetup( pSetupLog, L"UnregisterServer() returns 0x%08x\n", hRes );
if( ERROR_SUCCESS == StartICSLib() )
{
// Non-critical if we can't startup the lib since after we shutdown,
// we would have close all the port
CloseAllOpenPorts();
StopICSLib();
}
}
break;
case SESSMGR_REGSERVER:
{
LogSetup( pSetupLog, L"Installing as non-service\n" );
#if DBG
AddAccountToFilterList( HELPASSISTANTACCOUNT_NAME );
MYASSERT( ERROR_SUCCESS == g_HelpAccount.CreateHelpAccount() ) ;
hRes = _Module.RegisterServer(pSetupLog, TRUE, FALSE);
#else
hRes = E_INVALIDARG;
#endif
}
break;
//case SESSMGR_UPGRADE:
//
// TODO - ICS work, add upgrade special code.
//
case SESSMGR_SERVICE:
{
LogSetup( pSetupLog, L"Installing sessmgr service\n" );
hRes = S_OK;
//
// Clean up again, we might not be running in system
// context at the time of uninstall so clean up will failed.
//
dwStatus = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
REGKEYCONTROL_REMDSK,
0,
KEY_ALL_ACCESS,
&hKey
);
if( ERROR_SUCCESS == dwStatus )
{
//
// Check to see if previous uninstall failed,
// we only need to check value exists.
//
dwStatus = RegQueryValueEx(
hKey,
UNINSTALL_BEFORE_INSTALL,
0,
&dwType,
(BYTE *) &dwValue,
&cbData
);
if( ERROR_SUCCESS != dwStatus || REG_DWORD != dwType )
{
//
// No previous uninstall information, no need to delete anything
//
LogSetup( pSetupLog, L"UninstallBeforeInstall value not found or invalid, code %d\n", dwStatus );
}
else
{
LogSetup( pSetupLog, L"UninstallBeforeInstall exists, cleanup previous uninstall\n" );
//
// Previous uninstall failed, delete all pending help session,
// and clean up encryption key
//
dwStatus = RegDelKey(
HKEY_LOCAL_MACHINE,
REGKEYCONTROL_REMDSK _TEXT("\\") REGKEY_HELPSESSIONTABLE
);
//
// It's OK to fail here since we reset encryption key making existing
// ticket useless and will be deleted on expire.
//
LogSetup( pSetupLog, L"Delete pending table return %d\n", dwStatus );
dwStatus = TSHelpAssistantInitializeEncryptionLib();
if( ERROR_SUCCESS == dwStatus )
{
dwStatus = TSHelpAssisantEndEncryptionCycle();
if( ERROR_SUCCESS != dwStatus )
{
LogSetup( pSetupLog, L"TSHelpAssisantEndEncryptionCycle() returns 0x%08x\n", dwStatus );
LogSetup( pSetupLog, L"sessmgr setup can't continue\n" );
// Critical security error, existing ticket might still be valid
hRes = HRESULT_FROM_WIN32( dwStatus );
}
TSHelpAssistantEndEncryptionLib();
}
else
{
LogSetup( pSetupLog, L"TSHelpAssistantInitializeEncryptionLib return %d\n", dwStatus );
LogSetup( pSetupLog, L"sessmgr setup can't continue\n" );
// Critical security error, existing ticket might still be valid
hRes = HRESULT_FROM_WIN32( dwStatus );
}
}
if( SUCCEEDED(hRes) )
{
//
// Delete reg. value to uninstall before install only when successfully
// resetting encryption key
//
RegDeleteValue( hKey, UNINSTALL_BEFORE_INSTALL );
}
RegCloseKey( hKey );
}
if( SUCCEEDED(hRes) )
{
// SECURITY: prefix LSA key with L$ and delete old LSA key.
TransferLSASecretKey();
// Bug Fix : 590840, delay help assistant account creation until service start.
hRes = g_HelpAccount.Initialize();
if( SUCCEEDED(hRes) )
{
hRes = g_HelpAccount.DeleteHelpAccount();
if( FAILED(hRes) )
{
// None Critical Error.
LogSetup( pSetupLog, L"Failed to delete HelpAssistant account 0x%08x\n", hRes );
}
}
hRes = _Module.RegisterServer(pSetupLog, TRUE, TRUE);
if( FAILED(hRes) )
{
LogSetup( pSetupLog, L"Failed to register/installing service - 0x%08x\n", hRes );
}
}
if( SUCCEEDED(hRes) )
{
hRes = CHelpSessionTable::CreatePendingHelpTable();
if( FAILED(hRes) )
{
LogSetup(
pSetupLog,
L"CreatePendingHelpTable() failed - 0x%08x\n",
hRes
);
}
}
}
break;
default:
LogSetup( pSetupLog, L"Invalid setup operation %d\n", code );
hRes = E_UNEXPECTED;
}
LogSetup( pSetupLog, L"\n*** Finish Setup with Status 0x%08x ***\n", hRes );
if( pSetupLog )
{
fflush( pSetupLog );
fclose( pSetupLog);
}
return hRes;
}
/////////////////////////////////////////////////////////////////////////////
//
extern "C" int WINAPI _tWinMain(HINSTANCE hInstance,
HINSTANCE /*hPrevInstance*/, LPTSTR lpCmdLine, int /*nShowCmd*/)
{
HRESULT hRes;
CComBSTR bstrErrMsg;
CComBSTR bstrServiceDesc;
DWORD dwStatus;
lpCmdLine = GetCommandLine(); //this line necessary for _ATL_MIN_CRT
_Module.Init(ObjectMap, hInstance, IDS_SERVICENAME, IDS_SERVICEDISPLAYNAME, IDS_SERVICEDESC, &LIBID_RDSESSMGRLib);
_Module.m_bService = TRUE;
TCHAR szTokens[] = _T("-/");
//
// We don't do OS version checking as in Win9x case, some of our
// call uses API not exists on Win9x so will get unresolve
// reference when running on Win9x box.
//
bstrServiceDesc.LoadString( IDS_SERVICEDISPLAYNAME );
LPCTSTR lpszToken = FindOneOf(lpCmdLine, szTokens);
while (lpszToken != NULL)
{
if (lstrcmpi(lpszToken, _T("UnregServer"))==0)
{
return InstallUninstallSessmgr( SESSMGR_UNREGSERVER );
}
else if (lstrcmpi(lpszToken, _T("RegServer"))==0)
{
return InstallUninstallSessmgr( SESSMGR_REGSERVER );
}
else if (lstrcmpi(lpszToken, _T("Service"))==0)
{
return InstallUninstallSessmgr( SESSMGR_SERVICE );
}
lpszToken = FindOneOf(lpszToken, szTokens);
}
// Are we Service or Local Server
CRegKey keyAppID;
LONG lRes = keyAppID.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_READ);
if (lRes != ERROR_SUCCESS)
{
LogRemoteAssistanceEventString(
EVENTLOG_ERROR_TYPE,
SESSMGR_E_SETUP,
0,
NULL
);
return lRes;
}
CRegKey key;
lRes = key.Open(keyAppID, _T("{038ABBA4-4138-4AC4-A492-4A3DF068BD8A}"), KEY_READ);
if (lRes != ERROR_SUCCESS)
{
LogRemoteAssistanceEventString(
EVENTLOG_ERROR_TYPE,
SESSMGR_E_SETUP,
0,
NULL
);
return lRes;
}
TCHAR szValue[_MAX_PATH];
DWORD dwLen = _MAX_PATH;
lRes = key.QueryValue(szValue, _T("LocalService"), &dwLen);
_Module.m_bService = FALSE;
if (lRes == ERROR_SUCCESS)
_Module.m_bService = TRUE;
_Module.Start();
// When we get here, the service has been stopped
return _Module.m_status.dwWin32ExitCode;
}
DWORD
RestartFromSystemRestore()
{
DWORD dwStatus = ERROR_SUCCESS;
if( TSIsMachineInSystemRestore() )
{
dwStatus = TSSystemRestoreResetValues();
}
return dwStatus;
}