|
|
/*++
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"
#define SETUPLOGFILE_NAME _TEXT("sessmgr.setup.log")
#define SESSMGR_SERVICE 0
#define SESSMGR_REGSERVER 1
#define SESSMGR_UNREGSERVER 2
#define SESSMGR_SP1UPGRADE 3
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;
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 ) { 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 ) { //
// 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; }
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; }
/////////////////////////////////////////////////////////////////////////////
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]; LPCTSTR eventString[6]; _stprintf( szErrCode, L"0x%x", dwErrCode ); eventString[0] = (LPCTSTR)bstrNoviceDomain; eventString[1] = (LPCTSTR)bstrNoviceAccount; eventString[2] = (LPCTSTR)bstrRaType; eventString[3] = (LPCTSTR)bstrExpertIPFromClient; eventString[4] = (LPCTSTR)bstrExpertIPFromTS; eventString[5] = szErrCode;
LogEventString( dwEventType, dwEventCode, sizeof(eventString)/sizeof(eventString[0]), eventString, sizeof(dwErrCode), &dwErrCode );
return; }
/////////////////////////////////////////////////////////////////////////////
void CServiceModule::LogEventWithStatusCode( IN DWORD dwEventType, IN DWORD dwEventId, IN DWORD dwErrorCode ) /*++
--*/ { TCHAR szErrCode[256]; LPCTSTR eventString[1];
eventString[0] = szErrCode;
_stprintf( szErrCode, L"0x%x", dwErrorCode ); LogEventString( dwEventType, dwEventId, 1, eventString );
return; }
/////////////////////////////////////////////////////////////////////////////
void CServiceModule::LogInfoEvent( IN DWORD code ) /*++
--*/ { LogEventString( EVENTLOG_INFORMATION_TYPE, code, 0, NULL ); }
/////////////////////////////////////////////////////////////////////////////
void CServiceModule::LogWarningEvent( IN DWORD code ) /*++
--*/ {
LogEventString( EVENTLOG_WARNING_TYPE, code, 0, NULL );
}
/////////////////////////////////////////////////////////////////////////////
void CServiceModule::LogErrorEvent( IN DWORD errCode ) /*++
--*/ {
LogEventString( EVENTLOG_ERROR_TYPE, errCode, 0, NULL );
}
/////////////////////////////////////////////////////////////////////////////
void CServiceModule::LogEventString( IN DWORD dwEventType, IN DWORD dwEventId, IN DWORD wNumString, IN LPCTSTR* lpStrings, DWORD dwBinaryDataSize, /* 0 */ LPVOID lpRawData /* NULL */ ) /*++
--*/ {
HANDLE hAppLog=NULL; BOOL bSuccess=FALSE; WORD wElogType = (WORD) dwEventType;
try { if(hAppLog=RegisterEventSource(NULL, m_szServiceDispName)) { bSuccess = ReportEvent( hAppLog, wElogType, 0, dwEventId, NULL, (WORD)wNumString, dwBinaryDataSize, (const TCHAR **) lpStrings, lpRawData );
DeregisterEventSource(hAppLog); } } catch(...) { }
return; }
inline HRESULT CServiceModule::SetupEventViewerSource( IN FILE* pSetupLog ) /*++
--*/ { HKEY hKey = NULL; DWORD dwData; TCHAR szBuffer[MAX_PATH + 2]; DWORD dwStatus;
LogSetup( pSetupLog, L"Entering SetupEventViewerSource()...\n" );
_stprintf( szBuffer, _TEXT("%s\\%s"), REGKEY_SYSTEM_EVENTSOURCE, m_szServiceDispName );
// Add your source name as a subkey under the Application
// key in the EventLog registry key.
dwStatus = RegCreateKey( HKEY_LOCAL_MACHINE, szBuffer, &hKey ); if( ERROR_SUCCESS != dwStatus ) { LogSetup( pSetupLog, L"RegCreateKey() fail to create key %s with %d\n", szBuffer, dwStatus );
goto CLEANUPANDEXIT; }
dwStatus = GetModuleFileName( NULL, szBuffer, MAX_PATH+1 );
if( 0 == dwStatus ) { LogSetup( pSetupLog, L"GetModuleFileName() failed with error code %s\n", dwStatus = GetLastError() ); goto CLEANUPANDEXIT; }
// Add the name to the EventMessageFile subkey.
dwStatus = RegSetValueEx( hKey, L"EventMessageFile", 0, REG_SZ, (LPBYTE) szBuffer, (_tcslen(szBuffer)+1)*sizeof(TCHAR) );
if( ERROR_SUCCESS != dwStatus ) { LogSetup( pSetupLog, L"Failed to create EventMessageFile value, error code %d\n", dwStatus ); goto CLEANUPANDEXIT; }
// Set the supported event types in the TypesSupported subkey.
dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
dwStatus = RegSetValueEx( hKey, L"TypesSupported", 0, REG_DWORD, (LPBYTE) &dwData, sizeof(DWORD) );
if( ERROR_SUCCESS != dwStatus ) { LogSetup( pSetupLog, L"Failed to create TypesSupported value, error code %d\n", dwStatus ); }
CLEANUPANDEXIT:
if( NULL != hKey ) { RegCloseKey(hKey); }
LogSetup( pSetupLog, L"Exiting SetupEventViewerSource() 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); }
SetupEventViewerSource(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; HANDLE CServiceModule::gm_hICSAlertEvent = NULL;
unsigned __stdcall StartupInitThread( void* ptr ) { HRESULT hRes = S_OK; DWORD dwErrCode = ERROR_SUCCESS;
hRes = g_HelpSessTable.OpenSessionTable(NULL);
if( SUCCEEDED(hRes) ) { CRemoteDesktopHelpSessionMgr::NotifyPendingHelpServiceStartup(); } else { dwErrCode = SESSMGR_E_HELPSESSIONTABLE; }
if( FAILED(hRes) ) { _Module.LogEventWithStatusCode( EVENTLOG_ERROR_TYPE, dwErrCode, hRes );
hRes = dwErrCode; }
_endthreadex( hRes ); return hRes; }
ULONG CServiceModule::AddRef() { CCriticalSectionLocker l( m_Locks ); m_RefCount++;
if( m_RefCount > 0 ) { ResetEvent( gm_hIdle ); }
return m_RefCount; }
ULONG CServiceModule::Release() { CCriticalSectionLocker l( m_Locks ); m_RefCount--;
if( m_RefCount <= 0 ) { // Only signal idle when there is no more pending help
if( g_HelpSessTable.NumEntries() == 0 ) { SetEvent( gm_hIdle ); } }
return m_RefCount; }
unsigned int WINAPI CServiceModule::IdleMonitorThread( void* ptr ) { DWORD dwStatus = ERROR_SUCCESS; CServiceModule* pServiceModule = (CServiceModule *)ptr; HANDLE hWaitHandles[] = {gm_hICSAlertEvent, 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 ) { // ICS has signal us something has changed.
ResetEvent( gm_hICSAlertEvent ); } else if( WAIT_OBJECT_0 + 1 == dwStatus ) { // we have been idel 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.
Sleep( IDLE_SHUTDOWN_PERIOD ); dwStatus = WaitForSingleObject( gm_hIdle, 0 ); if( WAIT_OBJECT_0 == dwStatus ) { // no one holding object, time to shutdown
dwStatus = ERROR_SUCCESS; break; } } else if( WAIT_FAILED == dwStatus ) { // some bad thing happen, shutdown.
//MYASSERT(FALSE);
break; }
} }
// TOBE enable with ICS work
//pServiceModule->LogInfoEvent( SESSMGR_I_SERVICE_IDLESTOP );
pServiceModule->Handler(SERVICE_CONTROL_STOP);
CoUninitialize(); _endthreadex( dwStatus ); return dwStatus; }
BOOL CServiceModule::InitializeSessmgr() { CCriticalSectionLocker l( m_Locks );
//
// 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.
//
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 { CRemoteDesktopHelpSessionMgr::NotifyPendingHelpServiceStartup(); }
m_Initialized = TRUE; return _Module.IsSuccessServiceStartup(); }
void CServiceModule::Run() { //
// Mark we are not initialized yet...
//
m_Initialized = FALSE; _Module.dwThreadID = GetCurrentThreadId();
DWORD dwStatus; unsigned int dwJunk; WSADATA wsData; ISAFRemoteDesktopCallback* pIResolver = NULL;
LPWSTR pszSysAccName = NULL; DWORD cbSysAccName = 0; LPWSTR pszSysDomainName = NULL; DWORD cbSysDomainName = 0; SID_NAME_USE SidType;
HRESULT hr;
//
// **** 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) ) { dwStatus = SESSMGR_E_HELPACCOUNT;
LogEventWithStatusCode( EVENTLOG_ERROR_TYPE, SESSMGR_E_GENERALSTARTUP, hr );
_Module.m_dwServiceStartupStatus = SESSMGR_E_HELPACCOUNT; MYASSERT(FALSE); }
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
// HRESULT 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, RPC_C_IMP_LEVEL_IMPERSONATE, 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() ) { //
// Create an event for ICS to signal us
// any address change
//
gm_hICSAlertEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
if( NULL == gm_hICSAlertEvent ) { LogEventWithStatusCode( EVENTLOG_ERROR_TYPE, SESSMGR_E_GENERALSTARTUP, GetLastError() );
_Module.m_dwServiceStartupStatus = SESSMGR_E_GENERALSTARTUP; MYASSERT(FALSE); } }
if( _Module.IsSuccessServiceStartup() ) { //
// 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); } }
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() ) { //
// Service might startup from system restore, re-setup HelpAssistant account
// settings
if( g_HelpSessTable.NumEntries() == 0) { g_HelpAccount.EnableHelpAssistantAccount(FALSE); g_HelpAccount.EnableRemoteInteractiveRight(FALSE); } else { //
// make sure HelpAssistant account is enabled and can logon locally
//
g_HelpAccount.EnableHelpAssistantAccount(TRUE); g_HelpAccount.EnableRemoteInteractiveRight(TRUE); g_HelpAccount.SetupHelpAccountTSSettings(); }
//
// Create an manual reset event for background thread to terminate
// service
//
gm_hIdle = CreateEvent( NULL, TRUE, (g_HelpSessTable.NumEntries() == 0) ? TRUE : FALSE, NULL );
if( NULL == gm_hIdle ) { LogEventWithStatusCode( EVENTLOG_ERROR_TYPE, SESSMGR_E_GENERALSTARTUP, GetLastError() );
_Module.m_dwServiceStartupStatus = SESSMGR_E_GENERALSTARTUP; MYASSERT(FALSE); }
// 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; } }
//LogEvent(_T("Service started"));
if (m_bService) SetServiceStatus(SERVICE_RUNNING);
// TOBE enable with ICS work
//LogInfoEvent(SESSMGR_I_SERVICE_START);
//
// 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_SERVER, IID_ISAFRemoteDesktopCallback, (void **)&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; } else { //
// Create global interface table
//
hr = InitializeGlobalInterfaceTable();
if( SUCCEEDED(hr) ) { //
// register resolver interface with GIT, resolver has some
// data structure that depends on single instance.
//
hr = RegisterResolverWithGIT(pIResolver); }
if( FAILED(hr) ) { //
// Not critical, Resolver function is disabled.
LogEventWithStatusCode( EVENTLOG_WARNING_TYPE, SESSMGR_E_REGISTERSESSIONRESOLVER, hr );
_Module.m_dwServiceStartupStatus = SESSMGR_E_REGISTERSESSIONRESOLVER; } }
MSG msg; while (GetMessage(&msg, 0, 0, 0)) { switch( msg.message ) { case WM_EXPIREHELPSESSION: DebugPrintf(_TEXT("Executing TimeoutHelpSesion()...\n")); CRemoteDesktopHelpSessionMgr::TimeoutHelpSesion(); break;
case WM_SESSIONLOGOFFDISCONNECT: DebugPrintf(_TEXT("Executing NotifySessionLogoff() %d...\n"), msg.lParam); NotifySessionLogoff( msg.lParam ); break;
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); } }
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( NULL != pIResolver ) { pIResolver->Release(); }
_Module.RevokeClassObjects();
// TOBE enable with ICS work
//LogInfoEvent(SESSMGR_I_SERVICE_STOP);
CoUninitialize();
if( NULL != gm_hIdle ) { CloseHandle( gm_hIdle ); }
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();
// Stop ICS library, ignore error code.
StopICSLib(); TSHelpAssistantEndEncryptionLib();
UnInitializeGlobalInterfaceTable();
if( NULL != pszSysAccName ) { LocalFree( pszSysAccName ); }
if( NULL != pszSysDomainName ) { LocalFree( pszSysDomainName ); }
TLSShutdown();
}
HRESULT CreatePendingHelpTable( FILE* pSetupLog ) /*++
Routine to create pending help table registry key, if registry key already exist, set the DACL to system context only.
--*/ { PACL pAcl=NULL; DWORD dwStatus = ERROR_SUCCESS; PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
DWORD cbAcl = 0; PSID pSidSystem = NULL; HKEY hKey = NULL;
LogSetup( pSetupLog, L"\nEntering CreatePendingHelpTable\n" );
pSecurityDescriptor = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, sizeof(SECURITY_DESCRIPTOR)); if( NULL == pSecurityDescriptor ) { LogSetup( pSetupLog, L"Can't alloc memory for SECURITY_DESCRIPTOR\n" ); dwStatus = GetLastError(); goto CLEANUPANDEXIT; }
//
// Initialize the security descriptor.
//
if (!InitializeSecurityDescriptor( pSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION )) { dwStatus = GetLastError(); LogSetup( pSetupLog, L"InitializeSecurityDescriptor() failed with %d\n", dwStatus ); goto CLEANUPANDEXIT; }
dwStatus = CreateSystemSid( &pSidSystem ); if( ERROR_SUCCESS != dwStatus ) { LogSetup( pSetupLog, L"CreateSystemSid() failed with %d\n", dwStatus ); goto CLEANUPANDEXIT; }
cbAcl = GetLengthSid( pSidSystem ) + sizeof(ACL) + (2 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD))); pAcl = (PACL) LocalAlloc( LPTR, cbAcl ); if( NULL == pAcl ) { LogSetup( pSetupLog, L"Can't alloc memory for ACL\n" ); dwStatus = GetLastError(); goto CLEANUPANDEXIT; }
//
// Initialize the ACL.
//
if (!InitializeAcl(pAcl, cbAcl, ACL_REVISION)) { dwStatus = GetLastError(); LogSetup( pSetupLog, L"InitializeAcl() failed with error code %d\n", dwStatus ); goto CLEANUPANDEXIT; }
//
if (!AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_READ | GENERIC_WRITE | GENERIC_ALL, pSidSystem )) { dwStatus = GetLastError(); LogSetup( pSetupLog, L"AddAccessAllowedAce() failed with error code %d\n", dwStatus ); goto CLEANUPANDEXIT; }
if (!SetSecurityDescriptorDacl(pSecurityDescriptor, TRUE, pAcl, FALSE)) { dwStatus = GetLastError(); LogSetup( pSetupLog, L"SetSecurityDescriptorDacl() failed with error code %d\n", dwStatus ); goto CLEANUPANDEXIT; }
//
// Create/open the pending table registry key
//
dwStatus = RegCreateKeyEx( HKEY_LOCAL_MACHINE, REGKEYCONTROL_REMDSK _TEXT("\\") REGKEY_HELPSESSIONTABLE, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL );
if( ERROR_SUCCESS != dwStatus ) { LogSetup( pSetupLog, L"RegCreateKeyEx() failed with error code %d\n", dwStatus ); goto CLEANUPANDEXIT; }
//
// Set table (registry) DACL
//
dwStatus = RegSetKeySecurity( hKey, DACL_SECURITY_INFORMATION, pSecurityDescriptor );
if( ERROR_SUCCESS != dwStatus ) { LogSetup( pSetupLog, L"RegSetKeySecurity() failed with error code %d\n", dwStatus ); }
CLEANUPANDEXIT:
if( NULL != hKey ) { RegCloseKey(hKey); }
if( pAcl != NULL ) { LocalFree(pAcl); }
if( pSecurityDescriptor != NULL ) { LocalFree( pSecurityDescriptor ); }
if( pSidSystem != NULL ) { FreeSid( pSidSystem ); }
LogSetup( pSetupLog, L"CreatePendingHelpTable() return %d\n", dwStatus );
return HRESULT_FROM_WIN32(dwStatus); }
void UpgradeSessmgr( FILE* pSetupLog ) /*++
Routine to upgrade session manager
--*/ { LogSetup( pSetupLog, L"\nEntering UpgradeSessmgr\n" );
//
// Migrate fAllowToGetHelp policy in HKLM\Software\Microsoft\Remote Desktop\Policies
// to termsrv so listening thread can look at single place.
//
DWORD dwValue; DWORD dwStatus; DWORD dwSize; DWORD dwType; HKEY hKey; BOOL bUpgradeFromB1 = FALSE;
LogSetup( pSetupLog, L"Setting policy registry key\n" );
//
// Open TS registry key under HKLM\System\CurrentControlSet\Control\Terminal Serv...
//
dwStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_CONTROL_GETHELP, 0, KEY_READ | KEY_WRITE, &hKey );
if( ERROR_SUCCESS == dwStatus ) { dwStatus = GetPolicyAllowGetHelpSetting( HKEY_LOCAL_MACHINE, OLD_REG_CONTROL_GETHELP, RDS_ALLOWGETHELP_VALUENAME, &dwValue );
if( ERROR_SUCCESS == dwStatus ) { LogSetup( pSetupLog, L"B1 policy value %d\n", dwValue );
//
// Delete the old policy key, ignore error.
//
RegDelKey( HKEY_LOCAL_MACHINE, OLD_REG_CONTROL_GETHELP );
//
// This value exists in old Salem policy key, system must be
// upgrading from B1.
//
dwStatus = RegSetValueEx( hKey, RDS_ALLOWGETHELP_VALUENAME, 0, REG_DWORD, (PBYTE)&dwValue, sizeof(dwValue) );
bUpgradeFromB1 = TRUE; } else { LogSetup( pSetupLog, L"B1 policy key does not exist\n" );
dwSize = sizeof(dwValue);
//
// Upgrade from B2 and beyond, check to see if fAllowToGetHelp exist
// if not, set it to 1.
//
dwStatus = RegQueryValueEx( hKey, RDS_ALLOWGETHELP_VALUENAME, 0, &dwType, (PBYTE)&dwValue, &dwSize );
if( ERROR_FILE_NOT_FOUND == dwStatus || REG_DWORD != dwType ) { //
// default is not allow to get help if
// value does not exist.
//
dwValue = 1; dwStatus = RegSetValueEx( hKey, RDS_ALLOWGETHELP_VALUENAME, 0, REG_DWORD, (PBYTE)&dwValue, sizeof(dwValue) ); } else { LogSetup( pSetupLog, L"Existing fAllowToGetHelp Policy value %d\n", dwValue ); } }
LogSetup( pSetupLog, L"Setting fAllowGetHelp policy key result - %d\n", dwStatus ); RegCloseKey( hKey ); } else { LogSetup( pSetupLog, L"Can't open registry key %s, error code %d\n", REG_CONTROL_GETHELP, dwStatus ); }
if( TRUE == bUpgradeFromB1 ) { //
// B1, help assistant account password is clear text, this will fix the
// security leak.
//
LogSetup( pSetupLog, L"Upgrade from B1, delete all pending help and reset account password\n" );
RegDelKey( HKEY_LOCAL_MACHINE, REGKEYCONTROL_REMDSK _TEXT("\\") REGKEY_HELPSESSIONTABLE );
g_HelpAccount.ResetHelpAccountPassword(); }
LogSetup( pSetupLog, L"Leaving UpgradeSessmgr\n" ); 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) ) { hRes = g_HelpAccount.CreateHelpAccount(); if( SUCCEEDED(hRes) ) { CComBSTR bstrHelpAccName;
hRes = g_HelpAccount.GetHelpAccountNameEx( bstrHelpAccName ); MYASSERT( SUCCEEDED(hRes) );
if( FAILED(hRes) ) { // not a critical error, we can still use help
LogSetup( pSetupLog, L"Failed to retrieve HelpAssistantaccount name - 0x%08x\n", hRes ); } else { LogSetup( pSetupLog, L"HelpAssistant account name - %s\n", bstrHelpAccName );
// Add HelpAssistantAccount into account filter list
AddAccountToFilterList( bstrHelpAccName ); }
//
// Two type of SP install, one thru SP1 installer and the other is
// normal NT setup including all SP fixes.
// We disable account on setup and let service startup to re-enable it
//
g_HelpAccount.EnableHelpAssistantAccount(FALSE);
//
// Upgrade session manager
//
UpgradeSessmgr(pSetupLog);
LogSetup( pSetupLog, L"Successfully setup account\n" ); hRes = _Module.RegisterServer(pSetupLog, TRUE, TRUE);
if( FAILED(hRes) ) { LogSetup( pSetupLog, L"Failed to register/installing service - 0x%08x\n", hRes ); } } else { LogSetup( pSetupLog, L"Creating Help Assistant account failed with 0x%08x\n", hRes );
(void) g_HelpAccount.DeleteHelpAccount(); hRes = E_FAIL; } }
if( SUCCEEDED(hRes) ) { hRes = CreatePendingHelpTable(pSetupLog); 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 ); } else if ( lstrcmpi(lpszToken, _T("SP1Upgrade"))==0) { // BUGL 602653, HelpAssistant account is not disable at clean install
// disable account here and let service start up to re-enable.
return EnableLocalAccount( HELPASSISTANTACCOUNT_NAME, FALSE ); }
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) { _Module.LogErrorEvent( SESSMGR_E_SETUP ); return lRes; }
CRegKey key; lRes = key.Open(keyAppID, _T("{038ABBA4-4138-4AC4-A492-4A3DF068BD8A}"), KEY_READ); if (lRes != ERROR_SUCCESS) { _Module.LogErrorEvent( SESSMGR_E_SETUP ); 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; }
|