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
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;
|
|
}
|