|
|
/*++
Copyright (c) 1999-2000 Microsoft Corporation
Module Name:
HelpMgr.cpp
Abstract:
HelpMgr.cpp : Implementation of CRemoteDesktopHelpSessionMgr
Author:
HueiWang 2/17/2000
--*/ #include "stdafx.h"
#include "global.h"
#include "policy.h"
#include "RemoteDesktopUtils.h"
//
// CRemoteDesktopHelpSessionMgr Static member variable
//
#define DEFAULT_UNSOLICATED_HELP_TIMEOUT IDLE_SHUTDOWN_PERIOD
CCriticalSection CRemoteDesktopHelpSessionMgr::gm_AccRefCountCS;
// Help Session ID to help session instance cache map
IDToSessionMap CRemoteDesktopHelpSessionMgr::gm_HelpIdToHelpSession;
//
// Expert logoff monitor list, this is used for cleanup at
// the shutdown time so we don't have any opened handle.
//
EXPERTLOGOFFMONITORLIST g_ExpertLogoffMonitorList;
extern ISAFRemoteDesktopCallback* g_pIResolver;
VOID CALLBACK ExpertLogoffCallback( PVOID pContext, BOOLEAN bTimerOrWaitFired ) /*++
Routine Description:
This routine is invoked by thread pool when handle to rdsaddin is signal.
Parameters:
pContext : Pointer to user data. bTimerOrWaitFired : TRUE if wait timeout, FALSE otherwise.
Return:
None.
Note :
Refer to MSDN RegisterWaitForSingleObject() for function parameters.
--*/ { PEXPERTLOGOFFSTRUCT pExpertLogoffStruct = (PEXPERTLOGOFFSTRUCT)pContext; BSTR bstrHelpedTicketId = NULL;
WINSTATIONINFORMATION ExpertWinStation; DWORD ReturnLength;
DWORD dwStatus; BOOL bSuccess;
MYASSERT( FALSE == bTimerOrWaitFired ); MYASSERT( NULL != pContext );
DebugPrintf( _TEXT("ExpertLogoffCallback()...\n") );
// Our wait is forever so can't be timeout.
if( FALSE == bTimerOrWaitFired ) { if( NULL != pExpertLogoffStruct ) { DebugPrintf( _TEXT("Expert %d has logoff\n"), pExpertLogoffStruct->ExpertSessionId );
MYASSERT( NULL != pExpertLogoffStruct->hWaitObject ); MYASSERT( NULL != pExpertLogoffStruct->hWaitProcess ); MYASSERT( pExpertLogoffStruct->bstrHelpedTicketId.Length() > 0 ); MYASSERT( pExpertLogoffStruct->bstrWinStationName.Length() > 0 );
if( pExpertLogoffStruct->bstrWinStationName.Length() > 0 ) { //
// Reset the winstation asap since rdsaddin might get kill
// and termsrv stuck on waiting for winlogon to exit and
// shadow won't terminate until termsrv reset the winstation
//
ZeroMemory( &ExpertWinStation, sizeof(ExpertWinStation) );
bSuccess = WinStationQueryInformation( SERVERNAME_CURRENT, pExpertLogoffStruct->ExpertSessionId, WinStationInformation, (PVOID)&ExpertWinStation, sizeof(WINSTATIONINFORMATION), &ReturnLength );
if( TRUE == bSuccess || ERROR_CTX_CLOSE_PENDING == GetLastError() ) { //
// Cases:
// 1) Termsrv mark Helper session as close pending and
// function will return FALSE.
// 2) If somehow, session ID is re-use, session name
// will change then we compare cached name.
// Both cases, we will force a reset, however, only hope
// shadow ended and if mobsync still up, session will
// take a long time to terminate.
//
if( FALSE == bSuccess || pExpertLogoffStruct->bstrWinStationName == CComBSTR(ExpertWinStation.WinStationName) ) { DebugPrintf( _TEXT("Resetting winstation name %s, id %d\n"), pExpertLogoffStruct->bstrWinStationName, pExpertLogoffStruct->ExpertSessionId );
// don't wait for it to return, can't do much if this fail
WinStationReset( SERVERNAME_CURRENT, pExpertLogoffStruct->ExpertSessionId, FALSE );
DebugPrintf( _TEXT("WinStationReset return %d\n"), GetLastError() ); } } else { DebugPrintf( _TEXT("Expert logoff failed to get winstation name %d\n"), GetLastError() ); } }
if( pExpertLogoffStruct->bstrHelpedTicketId.Length() > 0 ) {
//
// detach pointer from CComBSTR, we will free it after handling
// WM_HELPERRDSADDINEXIT, purpose of this is not to duplicate
// string again.
//
bstrHelpedTicketId = pExpertLogoffStruct->bstrHelpedTicketId.Detach();
DebugPrintf( _TEXT("Posting WM_HELPERRDSADDINEXIT...\n") );
PostThreadMessage( _Module.dwThreadID, WM_HELPERRDSADDINEXIT, pExpertLogoffStruct->ExpertSessionId, (LPARAM) bstrHelpedTicketId ); }
//
// Remove from monitor list.
//
{ EXPERTLOGOFFMONITORLIST::LOCK_ITERATOR it = g_ExpertLogoffMonitorList.find(pExpertLogoffStruct);
if( it != g_ExpertLogoffMonitorList.end() ) { g_ExpertLogoffMonitorList.erase(it); } else { MYASSERT(FALSE); } }
// Destructor will take care of closing handle
delete pExpertLogoffStruct; } }
return; }
/////////////////////////////////////////////////////////////////////////////
DWORD MonitorExpertLogoff( IN LONG pidToWaitFor, IN LONG expertSessionId, IN BSTR bstrHelpedTicketId ) /*++
Routine Description:
Monitor expert logoff, specifically, we wait on rdsaddin process handle, once signal, we immediately notify resolver that expert has logoff.
Parameters:
pidToWaitFor : RDSADDIN PID expertSessionId : TS session ID that rdsaddin is running. bstrHelpedTickerId : Help ticket ID that expert is helping.
Returns:
ERROR_SUCCESS or error code.
--*/ { HANDLE hRdsaddin = NULL; DWORD dwStatus = ERROR_SUCCESS; BOOL bSuccess; PEXPERTLOGOFFSTRUCT pExpertLogoffStruct = NULL;
WINSTATIONINFORMATION ExpertWinStation; DWORD ReturnLength;
DebugPrintf( _TEXT("CServiceModule::RegisterWaitForExpertLogoff...\n") );
pExpertLogoffStruct = new EXPERTLOGOFFSTRUCT; if( NULL == pExpertLogoffStruct ) { dwStatus = GetLastError(); goto CLEANUPANDEXIT; }
ZeroMemory( &ExpertWinStation, sizeof(ExpertWinStation) );
bSuccess = WinStationQueryInformation( SERVERNAME_CURRENT, expertSessionId, WinStationInformation, (PVOID)&ExpertWinStation, sizeof(WINSTATIONINFORMATION), &ReturnLength );
if( FALSE == bSuccess ) { //
// what do we do, we still need to inform resolver of disconnect,
// but we will not be able to reset winstation
//
dwStatus = GetLastError(); DebugPrintf( _TEXT("WinStationQueryInformation() failed with %d...\n"), dwStatus );
} else { pExpertLogoffStruct->bstrWinStationName = ExpertWinStation.WinStationName; DebugPrintf( _TEXT("Helper winstation name %s...\n"), pExpertLogoffStruct->bstrWinStationName ); }
//
// Open rdsaddin.exe, if failed, bail out and don't continue
// help.
//
pExpertLogoffStruct->hWaitProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pidToWaitFor ); if( NULL == pExpertLogoffStruct->hWaitProcess ) { dwStatus = GetLastError(); DebugPrintf( _TEXT( "OpenProcess() on rdsaddin %d failed with %d\n"), pidToWaitFor, dwStatus );
goto CLEANUPANDEXIT; }
pExpertLogoffStruct->ExpertSessionId = expertSessionId; pExpertLogoffStruct->bstrHelpedTicketId = bstrHelpedTicketId;
//
// Register wait on rdsaddin process handle.
//
bSuccess = RegisterWaitForSingleObject( &(pExpertLogoffStruct->hWaitObject), pExpertLogoffStruct->hWaitProcess, (WAITORTIMERCALLBACK) ExpertLogoffCallback, pExpertLogoffStruct, INFINITE, WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE );
if( FALSE == bSuccess ) { dwStatus = GetLastError();
DebugPrintf( _TEXT("RegisterWaitForSingleObject() failed with %d\n"), dwStatus ); } else { // store this into monitor list
try { g_ExpertLogoffMonitorList[pExpertLogoffStruct] = pExpertLogoffStruct; } catch( CMAPException e ) { dwStatus = e.m_ErrorCode; } catch(...) { dwStatus = ERROR_INTERNAL_ERROR; } }
CLEANUPANDEXIT:
if( ERROR_SUCCESS != dwStatus ) { if( NULL != pExpertLogoffStruct ) { // destructor will take care of closing handle
delete pExpertLogoffStruct; } } DebugPrintf( _TEXT( "MonitorExpertLogoff() return %d\n"), dwStatus ); return dwStatus; }
VOID CleanupMonitorExpertList() /*++
Routine Description:
Routine to clean up all remaining expert logoff monitor list, this should be done right before we shutdown so we don't have any handle leak.
Parameters:
None.
Returns:
None.
--*/ { EXPERTLOGOFFMONITORLIST::LOCK_ITERATOR it = g_ExpertLogoffMonitorList.begin();
DebugPrintf( _TEXT("CleanupMonitorExpertList() has %d left\n"), g_ExpertLogoffMonitorList.size() );
for(; it != g_ExpertLogoffMonitorList.end(); it++ ) { if( NULL != (*it).second ) { // destructor will take care of closing handle
delete (*it).second; (*it).second = NULL; } }
g_ExpertLogoffMonitorList.erase_all();
return; }
HRESULT LoadSessionResolver( OUT ISAFRemoteDesktopCallback** ppResolver ) /*++
Routine Description:
Load resolver interface, Resolver has data that depends on single instance.
Parameters:
ppResolver : Pointer to ISAFRemoteDesktopCallback* to receive Resolver pointer
Returns:
S_OK or error code.
--*/ { // sync. access to resolver since there is only one instance.
CCriticalSectionLocker Lock(g_ResolverLock);
HRESULT hr;
if( NULL != g_pIResolver ) { hr = g_pIResolver->QueryInterface( IID_ISAFRemoteDesktopCallback, (void **)ppResolver ); } else { hr = HRESULT_FROM_WIN32( ERROR_INTERNAL_ERROR ); MYASSERT(FALSE); }
return hr; }
//-----------------------------------------------------------
HRESULT ImpersonateClient() /*
Routine Description:
Impersonate client
Parameter:
None.
Returns:
S_OK or return code from CoImpersonateClient
--*/ { HRESULT hRes;
#if __WIN9XBUILD__
// CoImpersonateClient() on Win9x is not supported.
hRes = S_OK;
#else
hRes = CoImpersonateClient();
#endif
return hRes; }
//-----------------------------------------------------------
void EndImpersonateClient() /*
Routine Description:
End impersonating client
Parameter:
None.
Returns:
S_OK or return code from CoRevertToSelf
--*/ { #if __WIN9XBUILD__
#else
HRESULT hRes;
hRes = CoRevertToSelf(); MYASSERT( SUCCEEDED(hRes) );
#endif
return; }
HRESULT CRemoteDesktopHelpSessionMgr::AddHelpSessionToCache( IN BSTR bstrHelpId, IN CComObject<CRemoteDesktopHelpSession>* pIHelpSession ) /*++
Routine Description:
Add help session object to global cache.
Parameters:
bstrHelpId : Help Session ID. pIHelpSession : Pointer to help session object.
Returns:
S_OK. E_UNEXPECTED HRESULT_FROM_WIN32( ERROR_FILE_EXITS )
--*/ { HRESULT hRes = S_OK; IDToSessionMap::LOCK_ITERATOR it = gm_HelpIdToHelpSession.find( bstrHelpId );
if( it == gm_HelpIdToHelpSession.end() ) { try {
DebugPrintf( _TEXT("Adding Help Session %s to cache\n"), bstrHelpId );
gm_HelpIdToHelpSession[ bstrHelpId ] = pIHelpSession; }
catch( CMAPException e ) { hRes = HRESULT_FROM_WIN32( e.m_ErrorCode ); } catch(...) { hRes = E_UNEXPECTED; MYASSERT( SUCCEEDED(hRes) ); throw; } } else { hRes = HRESULT_FROM_WIN32( ERROR_FILE_EXISTS ); }
return hRes; }
HRESULT CRemoteDesktopHelpSessionMgr::ExpireUserHelpSessionCallback( IN CComBSTR& bstrHelpId, IN HANDLE userData ) /*++
Routine Description:
Expire help session call back routine, refer to EnumHelpEntry()
Parameters:
bstrHelpId : ID of help session. userData : Handle to user data.
Returns:
S_OK.
--*/ { HRESULT hRes = S_OK;
DebugPrintf( _TEXT("ExpireUserHelpSessionCallback() on %s...\n"), (LPCTSTR)bstrHelpId );
// Load Help Entry.
RemoteDesktopHelpSessionObj* pObj = LoadHelpSessionObj( NULL, bstrHelpId );
if( NULL != pObj ) { //
// LoadHelpSessionObj() will release expired help session.
//
pObj->Release(); }
return hRes; }
#if DISABLESECURITYCHECKS
HRESULT CRemoteDesktopHelpSessionMgr::LogoffUserHelpSessionCallback( IN CComBSTR& bstrHelpId, IN HANDLE userData ) /*++
Routine Description:
Expire help session call back routine, refer to EnumHelpEntry()
Parameters:
bstrHelpId : ID of help session. userData : Handle to user data.
Returns:
S_OK.
--*/ { HRESULT hRes = S_OK;
DWORD dwLogoffSessionId = PtrToUlong(userData);
long lHelpSessionUserSessionId;
DebugPrintf( _TEXT("LogoffUserHelpSessionCallback() on %s %d...\n"), bstrHelpId, dwLogoffSessionId );
// Load Help Entry.
RemoteDesktopHelpSessionObj* pObj = LoadHelpSessionObj( NULL, bstrHelpId );
if( NULL != pObj ) { //
// LoadHelpSessionObj() will release expired help session.
//
hRes = pObj->get_UserLogonId( &lHelpSessionUserSessionId );
if( SUCCEEDED(hRes) && (DWORD)lHelpSessionUserSessionId == dwLogoffSessionId ) { DebugPrintf( _TEXT("User Session has log off...\n") );
// rely on helpassistant session logoff to notify
// resolver.
hRes = pObj->put_UserLogonId(UNKNOWN_LOGONID); } else if( pObj->GetHelperSessionId() == dwLogoffSessionId ) {
DebugPrintf( _TEXT("Helper has log off...\n") );
// Helper has logoff, invoke disconnect to clean up
// resolver state.
hRes = pObj->NotifyDisconnect(); }
DebugPrintf( _TEXT("hRes = 0x%08x, lHelpSessionUserSessionId=%d\n"), hRes, lHelpSessionUserSessionId );
pObj->Release(); }
// Always return success to continue on next help session
return S_OK; }
#endif
HRESULT CRemoteDesktopHelpSessionMgr::NotifyPendingHelpServiceStartCallback( IN CComBSTR& bstrHelpId, IN HANDLE userData ) /*++
Routine Description:
Call back for NotifyPendingHelpServiceStartup, refer to EnumHelpEntry()
Parameters:
bstrHelpId : ID of help session. userData : Handle to user data.
Returns:
S_OK.
-*/ { HRESULT hRes = S_OK;
// DeleteHelp() will try to close the port and since we just startup,
// port is either invalid or not open, so we need manually delete
// expired help
RemoteDesktopHelpSessionObj* pObj = LoadHelpSessionObj( NULL, bstrHelpId, TRUE ); if( NULL != pObj ) { if( TRUE == pObj->IsHelpSessionExpired() ) { pObj->put_ICSPort( 0 ); pObj->DeleteHelp(); ReleaseAssistantAccount(); } else { DWORD dwICSPort;
//
// Sync. calls into ICS library, OpenPort() might trigger
// address list change while other thread is in the middle
// of FetchAllAddress() call.
//
CCriticalSectionLocker ICSLock(g_ICSLibLock);
//
// re-open the port so connection can come in
//
dwICSPort = OpenPort( TERMSRV_TCPPORT ); pObj->put_ICSPort( dwICSPort );
// We don't close the port until we are deleted.
}
pObj->Release(); }
return hRes; }
void CRemoteDesktopHelpSessionMgr::NotifyPendingHelpServiceStartup() /*++
Description:
Go thru all pending help and notify pending help about service startup.
Parameters:
None.
Returns:
None
--*/ { //
// CreateHelpSession() call will lock IDToSessionMap then try to lock table/registry,
// ticket loop (walking thru outstanding ticket) always lock table/registry then
// IDToSessionMap, this causes deadlock situation so we lock IDToSessionMap first before
// enumerating outstanding ticket and since IDToSessionMap is guarded by critical section,
// so we don't have any problem here.
//
LockIDToSessionMapCache();
try { g_HelpSessTable.EnumHelpEntry( NotifyPendingHelpServiceStartCallback, NULL ); } catch(...) { UnlockIDToSessionMapCache(); MYASSERT(FALSE); throw; }
UnlockIDToSessionMapCache();
return; } void CRemoteDesktopHelpSessionMgr::TimeoutHelpSesion() /*++
Routine Description:
Expire help session that has exceed its valid period.
Parameters:
None.
Returns:
None.
--*/ { DebugPrintf( _TEXT("TimeoutHelpSesion()...\n") );
//
// CreateHelpSession() call will lock IDToSessionMap then try to lock table/registry,
// ticket loop (walking thru outstanding ticket) always lock table/registry then
// IDToSessionMap, this causes deadlock situation so we lock IDToSessionMap first before
// enumerating outstanding ticket and since IDToSessionMap is guarded by critical section,
// so we don't have any problem here.
//
LockIDToSessionMapCache();
try { g_HelpSessTable.EnumHelpEntry( ExpireUserHelpSessionCallback, (HANDLE)NULL ); } catch(...) { MYASSERT(FALSE); UnlockIDToSessionMapCache(); throw; } UnlockIDToSessionMapCache(); return; }
#if DISABLESECURITYCHECKS
void CRemoteDesktopHelpSessionMgr::NotifyHelpSesionLogoff( DWORD dwLogonId ) /*++
Routine Description:
Parameters:
Returns:
--*/ { DebugPrintf( _TEXT("NotifyHelpSesionLogoff() %d...\n"), dwLogonId );
try { g_HelpSessTable.EnumHelpEntry( LogoffUserHelpSessionCallback, UlongToPtr(dwLogonId) ); } catch(...) { MYASSERT(FALSE); throw; } return; } #endif
HRESULT CRemoteDesktopHelpSessionMgr::DeleteHelpSessionFromCache( IN BSTR bstrHelpId ) /*++
Routine Descritpion:
Delete help session from global cache.
Parameters:
bstrHelpId : Help session ID to be deleted.
Returns:
S_OK. HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) --*/ { HRESULT hRes = S_OK;
DebugPrintf( _TEXT("DeleteHelpSessionFromCache() - %s\n"), bstrHelpId );
IDToSessionMap::LOCK_ITERATOR it = gm_HelpIdToHelpSession.find( bstrHelpId );
if( it != gm_HelpIdToHelpSession.end() ) { gm_HelpIdToHelpSession.erase( it ); } else { hRes = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ); }
return hRes; }
RemoteDesktopHelpSessionObj* CRemoteDesktopHelpSessionMgr::LoadHelpSessionObj( IN CRemoteDesktopHelpSessionMgr* pMgr, IN BSTR bstrHelpSession, IN BOOL bLoadExpiredHelp /* = FALSE */ ) /*++
Routine Description:
Find a pending help entry, routine will load from DB if not yet loaded.
Parameters:
pMgr : Pointer to CRemoteDesktopHelpSessionMgr object that wants to load this help session. bstrHelpSession : Help entry ID interested.
Returns:
--*/ { HRESULT hRes = S_OK; PHELPENTRY pHelp = NULL; RemoteDesktopHelpSessionObj* pHelpSessionObj = NULL; IDToSessionMap::LOCK_ITERATOR it = gm_HelpIdToHelpSession.find( bstrHelpSession );
if( it != gm_HelpIdToHelpSession.end() ) { DebugPrintf( _TEXT("LoadHelpSessionObj() %s is in cache ...\n"), bstrHelpSession );
pHelpSessionObj = (*it).second;
// One more reference to this object.
pHelpSessionObj->AddRef(); } else { DebugPrintf( _TEXT("Loading Help Session %s\n"), bstrHelpSession );
// load from table
hRes = g_HelpSessTable.OpenHelpEntry( bstrHelpSession, &pHelp );
if( SUCCEEDED(hRes) ) { //
// Object return from CreateInstance() has ref. count of 1
//
hRes = CRemoteDesktopHelpSession::CreateInstance( pMgr, (pMgr) ? pMgr->m_bstrUserSid : NULL, pHelp, &pHelpSessionObj ); if( SUCCEEDED(hRes) ) { if( NULL != pHelpSessionObj ) { hRes = AddHelpSessionToCache( bstrHelpSession, pHelpSessionObj );
if( SUCCEEDED(hRes) ) { //m_HelpListByLocal.push_back( bstrHelpSession );
it = gm_HelpIdToHelpSession.find( bstrHelpSession );
MYASSERT( it != gm_HelpIdToHelpSession.end() );
if( it == gm_HelpIdToHelpSession.end() ) { hRes = E_UNEXPECTED; MYASSERT( FALSE ); } } if( FAILED(hRes) ) { // we have big problem here...
pHelpSessionObj->Release(); pHelpSessionObj = NULL; } else { // ignore error here, it is possible that owner account
// got deleted even session is still active, we will let
// resolver to fail.
pHelpSessionObj->ResolveTicketOwner(); } } else { MYASSERT(FALSE); hRes = E_UNEXPECTED; } }
if( FAILED(hRes) ) { MYASSERT( FALSE ); pHelp->Close(); } } } //
// If automatically delete expired help, check and delete expired help
//
if( FALSE == bLoadExpiredHelp && pHelpSessionObj && TRUE == pHelpSessionObj->IsHelpSessionExpired() ) { // If session is in help or pending user response,
// don't expire it, let next load to delete it.
if( UNKNOWN_LOGONID == pHelpSessionObj->GetHelperSessionId() ) { // Delete it from data base and in memory cache
pHelpSessionObj->DeleteHelp(); ReleaseAssistantAccount(); pHelpSessionObj->Release();
pHelpSessionObj = NULL; } }
return pHelpSessionObj; }
/////////////////////////////////////////////////////////////////////////////
//
// CRemoteDesktopHelpSessionMgr
//
STDMETHODIMP CRemoteDesktopHelpSessionMgr::DeleteHelpSession( IN BSTR HelpSessionID ) /*++
Routine Description:
Delete a user created Help Session from our cached list.
Parameter:
HelpSessionID : Help Session ID returned from CreateHelpSession() or CreateHelpSessionEx().
Returns:
S_OK Success. HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) Help ID not found. HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) Help does not belong to user
--*/ { HRESULT hRes = S_OK; BOOL bInCache;
if( FALSE == _Module.IsSuccessServiceStartup() ) { // service startup problem, return error code.
hRes = _Module.GetServiceStartupStatus(); DebugPrintf( _TEXT("Service startup failed with 0x%x\n"), hRes ); return hRes; } if( NULL == HelpSessionID ) { hRes = E_POINTER;
//
// Assert here is just to cache invalid input.
//
ASSERT( FALSE ); return hRes; }
DebugPrintf( _TEXT("Delete Help Session %s\n"), HelpSessionID );
hRes = LoadUserSid();
MYASSERT( SUCCEEDED(hRes) );
RemoteDesktopHelpSessionObj* pHelpObj;
pHelpObj = LoadHelpSessionObj( this, HelpSessionID ); if( NULL != pHelpObj ) { // Only original creator can delete his/her help session
//if( TRUE == pHelpObj->IsEqualSid(m_bstrUserSid) )
//{
// DeleteHelp will also delete entry in global cache.
pHelpObj->DeleteHelp(); ReleaseAssistantAccount(); //}
//else
//{
// hRes = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
//}
// LoadHelpSessionObj() always AddRef().
pHelpObj->Release();
} else { HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } return hRes; }
STDMETHODIMP CRemoteDesktopHelpSessionMgr::CreateHelpSession( IN BSTR bstrSessName, IN BSTR bstrSessPwd, IN BSTR bstrSessDesc, IN BSTR bstrSessBlob, OUT IRemoteDesktopHelpSession **ppIRemoteDesktopHelpSession ) /*++
--*/ { // No one is using this routine.
return E_NOTIMPL; }
HRESULT CRemoteDesktopHelpSessionMgr::CreateHelpSession( IN BOOL bCacheEntry, IN BSTR bstrSessName, IN BSTR bstrSessPwd, IN BSTR bstrSessDesc, IN BSTR bstrSessBlob, IN LONG UserLogonId, IN BSTR bstrClientSid, OUT RemoteDesktopHelpSessionObj **ppIRemoteDesktopHelpSession ) /*++
Routine Description:
Create an instantiation of IRemoteDesktopHelpSession object, each instantiation represent a RemoteDesktop Help Session.
Parameters:
bstrSessName : User defined Help Session Name, currently not used. bstrSessPwd : User defined Help Session password. bstrSessDesc : User defined Help Session Description, currently not used. ppIRemoteDesktopHelpSession : return an IRemoteDesktopHelpSession object representing a Help Session
Returns:
S_OK E_UNEXPECTED SESSMGR_E_GETHELPNOTALLOW User not allow to get help Other COM error.
Note:
Caller must check if client is allowed to get help
--*/ { HRESULT hRes = S_OK; DWORD dwStatus; PHELPENTRY pHelp = NULL; CComBSTR bstrHelpSessionId; DWORD dwICSPort; LONG MaxTicketExpiry;
UNREFERENCED_PARAMETER(bstrSessName); UNREFERENCED_PARAMETER(bstrSessDesc); UNREFERENCED_PARAMETER(bstrSessBlob);
CComObject<CRemoteDesktopHelpSession>* pInternalHelpSessionObj = NULL;
if( NULL == ppIRemoteDesktopHelpSession ) { hRes = E_POINTER; return hRes; }
hRes = GenerateHelpSessionId( bstrHelpSessionId ); if( FAILED(hRes) ) { return hRes; }
DebugPrintf( _TEXT("CreateHelpSession %s\n"), bstrHelpSessionId );
//
// Setup assistant account rights and encryption parameters.
//
hRes = AcquireAssistantAccount(); if( FAILED(hRes) ) { return hRes; }
hRes = g_HelpSessTable.CreateInMemoryHelpEntry( bstrHelpSessionId, &pHelp ); if( FAILED(hRes) ) { goto CLEANUPANDEXIT; }
MYASSERT( NULL != pHelp );
//
// CRemoteDesktopHelpSession::CreateInstance() will load
// TS session ID and default RDS settings.
//
hRes = CRemoteDesktopHelpSession::CreateInstance( this, CComBSTR(bstrClientSid), // client SID that open this instance
pHelp, &pInternalHelpSessionObj );
if( SUCCEEDED(hRes) ) { hRes = pInternalHelpSessionObj->BeginUpdate(); if( FAILED(hRes) ) { goto CLEANUPANDEXIT; }
//
// Get default timeout value from registry, not a critical
// error, if we failed, we just default to 30 days
//
hRes = PolicyGetMaxTicketExpiry( &MaxTicketExpiry ); if( SUCCEEDED(hRes) && MaxTicketExpiry > 0 ) { pInternalHelpSessionObj->put_TimeOut( MaxTicketExpiry ); }
//
// We delay opening port until get_ConnectParm(), initialize to 0
// so that so we don't close the port in case of any error.
//
hRes = pInternalHelpSessionObj->put_ICSPort( 0 ); if( SUCCEEDED(hRes) ) { hRes = pInternalHelpSessionObj->put_UserLogonId(UserLogonId); }
if( SUCCEEDED(hRes) ) { // user SID that created this help session
hRes = pInternalHelpSessionObj->put_UserSID(bstrClientSid); }
if( SUCCEEDED(hRes) ) { hRes = pInternalHelpSessionObj->put_HelpSessionCreateBlob( bstrSessBlob ); }
if( SUCCEEDED(hRes) ) { hRes = pInternalHelpSessionObj->CommitUpdate(); }
if( FAILED(hRes) ) { // ignore error and exit
(VOID)pInternalHelpSessionObj->AbortUpdate(); goto CLEANUPANDEXIT; }
//
// Ignore error, we will let resolver fail.
pInternalHelpSessionObj->ResolveTicketOwner();
//
// We are adding entry to table and also our global object
// cache, to prevent deadlock or timing problem, lock
// global cache and let MemEntryToStorageEntry() lock table.
//
LockIDToSessionMapCache(); try { if( bCacheEntry ) { // convert a in-memory help to persistant help
hRes = g_HelpSessTable.MemEntryToStorageEntry( pHelp ); }
if( SUCCEEDED(hRes) ) { // Add help session to global cache
hRes = AddHelpSessionToCache( bstrHelpSessionId, pInternalHelpSessionObj );
if( SUCCEEDED(hRes) ) { *ppIRemoteDesktopHelpSession = pInternalHelpSessionObj; } else { MYASSERT( hRes != HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) ); } } } catch(...) { hRes = E_UNEXPECTED; throw; } UnlockIDToSessionMapCache(); }
CLEANUPANDEXIT:
if( FAILED(hRes) ) { ReleaseAssistantAccount();
if( NULL != pInternalHelpSessionObj ) { pInternalHelpSessionObj->DeleteHelp(); // this will also release pHelp.
pInternalHelpSessionObj->Release(); } }
return hRes; }
BOOL CRemoteDesktopHelpSessionMgr::CheckAccessRights( CComObject<CRemoteDesktopHelpSession>* pIHelpSess ) /*++
--*/ { //
// NOTE: This function checks to make sure the caller is the user that
// created the Help Session. For Whistler, we enforce that Help
// Sessions only be created by apps running as SYSTEM. Once
// created, the creating app can pass the object to any other app
// running in any other context. This function will get in the
// way of this capability so it simply returns TRUE for now.
//
return TRUE;
BOOL bSuccess;
// only original creator or help assistant can
// access
bSuccess = pIHelpSess->IsEqualSid( m_bstrUserSid );
if( FALSE == bSuccess ) { bSuccess = g_HelpAccount.IsAccountHelpAccount( m_pbUserSid, m_cbUserSid );
if( FALSE == bSuccess ) { bSuccess = pIHelpSess->IsEqualSid( g_LocalSystemSID ); } }
#if DISABLESECURITYCHECKS
//
// This is for private testing without using pcHealth, flag is not define
// in build.
//
//
// For testing only, allow admin to invoke this call
//
if( FALSE == bSuccess ) { DWORD dump;
if( SUCCEEDED(ImpersonateClient()) ) { dump = IsUserAdmin(&bSuccess); if( ERROR_SUCCESS != dump ) { bSuccess = FALSE; }
EndImpersonateClient(); } }
#endif
return bSuccess; }
STDMETHODIMP CRemoteDesktopHelpSessionMgr::RetrieveHelpSession( IN BSTR HelpSessionID, OUT IRemoteDesktopHelpSession **ppIRemoteDesktopHelpSession ) /*++
Routine Description:
Retrieve a help session based on ID.
Parameters:
HelpSessionID : Help Session ID returned from CreateHelpSession(). ppIRemoteDesktopHelpSession : Return Help Session Object for the Help Session.
Paramters:
S_OK Success HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) Help Session not found HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) Access Denied E_POINTER Invalid argument
--*/ { HRESULT hRes = S_OK;
if( FALSE == _Module.IsSuccessServiceStartup() ) { // service startup problem, return error code.
hRes = _Module.GetServiceStartupStatus();
DebugPrintf( _TEXT("Service startup failed with 0x%x\n"), hRes );
return hRes; }
DebugPrintf( _TEXT("RetrieveHelpSession %s\n"), HelpSessionID );
if( NULL != ppIRemoteDesktopHelpSession ) { // only user sid when needed
hRes = LoadUserSid(); if( SUCCEEDED(hRes) ) { RemoteDesktopHelpSessionObj* pObj = LoadHelpSessionObj( this, HelpSessionID );
if( NULL != pObj && !pObj->IsHelpSessionExpired() ) { if( TRUE == CheckAccessRights(pObj) ) { // LoadHelpSessionObj() AddRef() to object
*ppIRemoteDesktopHelpSession = pObj; } else { hRes = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); // LoadHelpSessionObj() AddRef() to object
pObj->Release(); } } else { hRes = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } } } else { hRes = E_POINTER; }
DebugPrintf( _TEXT("RetrieveHelpSession %s returns 0x%08x\n"), HelpSessionID, hRes );
return hRes; }
STDMETHODIMP CRemoteDesktopHelpSessionMgr::VerifyUserHelpSession( IN BSTR HelpSessionId, IN BSTR bstrSessPwd, IN BSTR bstrResolverConnectBlob, IN BSTR bstrExpertBlob, IN LONG CallerProcessId, OUT ULONG_PTR* phHelpCtr, OUT LONG* pResolverErrCode, OUT long* plUserTSSession ) /*++
Routine Description:
Verify a user help session is valid and invoke resolver to find the correct user help session to provide help.
Parameters:
HelpSessionId : Help Session ID. bstrSessPwd : Password to be compare. bstrResolverConnectBlob : Optional parameter to be passed to resolver. bstrExpertBlob : Optional blob to be passed to resolver for security check. pResolverErrCode : Return code from resolver. plUserTSSession : Current logon session.
Returns:
S_OK
--*/ { HRESULT hRes; CComBSTR bstrUserSidString; BOOL bMatch; BOOL bInCache = FALSE;
if( FALSE == _Module.IsSuccessServiceStartup() ) { // service startup problem, return error code.
hRes = _Module.GetServiceStartupStatus();
DebugPrintf( _TEXT("Service startup failed with 0x%x\n"), hRes );
*plUserTSSession = SAFERROR_SESSMGRERRORNOTINIT; return hRes; }
DebugPrintf( _TEXT("VerifyUserHelpSession %s\n"), HelpSessionId );
if( NULL != plUserTSSession && NULL != pResolverErrCode && NULL != phHelpCtr ) { hRes = LoadUserSid(); if( SUCCEEDED(hRes) ) { RemoteDesktopHelpSessionObj* pObj = LoadHelpSessionObj( this, HelpSessionId );
if( NULL != pObj ) { // Make sure object is still valid, Whister Server B3, only
// depends on helpsession ID for security check, help session
// password is gone.
bMatch = pObj->VerifyUserSession( CComBSTR(), CComBSTR(bstrSessPwd) );
if( TRUE == bMatch ) { hRes = pObj->ResolveUserSession( bstrResolverConnectBlob, bstrExpertBlob, CallerProcessId, phHelpCtr, pResolverErrCode, plUserTSSession ); } else { hRes = HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD); *pResolverErrCode = SAFERROR_INVALIDPASSWORD; }
// LoadHelpSessionObj() AddRef() to object
pObj->Release(); } else { hRes = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); *pResolverErrCode = SAFERROR_HELPSESSIONNOTFOUND; } } else { *pResolverErrCode = SAFERROR_INTERNALERROR; } } else { hRes = E_POINTER; *pResolverErrCode = SAFERROR_INVALIDPARAMETERSTRING; }
return hRes; }
STDMETHODIMP CRemoteDesktopHelpSessionMgr::IsValidHelpSession( /*[in]*/ BSTR HelpSessionId, /*[in]*/ BSTR HelpSessionPwd ) /*++
Description:
Verify if a help session exists and password match.
Parameters:
HelpSessionId : Help session ID. HelpSessionPwd : Optional help session password
Returns:
Note:
Only allow system service and administrator to invoke this call.
--*/ { HRESULT hRes = S_OK; BOOL bPasswordMatch; RemoteDesktopHelpSessionObj* pObj;
if( FALSE == _Module.IsSuccessServiceStartup() ) { // service startup problem, return error code.
hRes = _Module.GetServiceStartupStatus();
DebugPrintf( _TEXT("Service startup failed with 0x%x\n"), hRes );
return hRes; }
DebugPrintf( _TEXT("IsValidHelpSession ID %s\n"), HelpSessionId );
hRes = LoadUserSid();
if( FAILED(hRes) ) { goto CLEANUPANDEXIT; }
hRes = ImpersonateClient(); if( FAILED(hRes) ) { goto CLEANUPANDEXIT; }
//
// Make sure only system service can invoke this call.
//
if( !g_pSidSystem || FALSE == IsCallerSystem(g_pSidSystem) ) { #if DISABLESECURITYCHECKS
DWORD dump; BOOL bStatus;
//
// For testing only, allow admin to invoke this call
//
dump = IsUserAdmin(&bStatus); hRes = HRESULT_FROM_WIN32( dump ); if( FAILED(hRes) || FALSE == bStatus ) { EndImpersonateClient();
hRes = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ); goto CLEANUPANDEXIT; }
#else
EndImpersonateClient();
hRes = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ); goto CLEANUPANDEXIT;
#endif
}
// No need to run as client.
EndImpersonateClient();
pObj = LoadHelpSessionObj( this, HelpSessionId ); if( NULL != pObj ) { // Whister Server B3, only depends on helpsession ID
// for security check
hRes = S_OK; pObj->Release(); } else { hRes = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); }
CLEANUPANDEXIT:
return hRes; }
/////////////////////////////////////////////////////////////////////////////
//
CRemoteDesktopHelpSessionMgr::CRemoteDesktopHelpSessionMgr() : //m_lAccountAcquiredByLocal(0),
m_pbUserSid(NULL), m_cbUserSid(0) /*++
CRemoteDesktopHelpSessMgr Constructor
--*/ { }
void CRemoteDesktopHelpSessionMgr::Cleanup() /*++
Routine Description: Cleanup resource allocated in CRemoteDesktopHelpSessionMgr
Parameters:
None.
Returns:
None. --*/ { if( m_pbUserSid ) { LocalFree(m_pbUserSid); m_pbUserSid = NULL; } }
//--------------------------------------------------------------
HRESULT CRemoteDesktopHelpSessionMgr::LoadUserSid() /*++
Routine Description:
Load client's SID onto class member variable m_pbUserSid, m_cbUserSid, and m_bstrUserSid. We can't load user SID at class constructor as COM still haven't retrieve information about client's credential yey.
Parameters:
None.
Returns:
S_OK error code from ImpersonateClient() error code from GetTextualSid()
Note:
On Win9x machine, user SID is 'hardcoded WIN9X_USER_SID
--*/ { #ifndef __WIN9XBUILD__
HRESULT hRes = S_OK;
// check if SID already loaded, if not continue
// on loading SID
if( NULL == m_pbUserSid || 0 == m_cbUserSid ) { DWORD dwStatus; BOOL bSuccess = TRUE; LPTSTR pszTextualSid = NULL; DWORD dwTextualSid = 0;
hRes = ImpersonateClient(); if( SUCCEEDED(hRes) ) { m_LogonId = GetUserTSLogonId();
// retrieve user SID.
dwStatus = GetUserSid( &m_pbUserSid, &m_cbUserSid ); if( ERROR_SUCCESS == dwStatus ) { m_bstrUserSid.Empty();
// convert SID to string
bSuccess = GetTextualSid( m_pbUserSid, NULL, &dwTextualSid );
if( FALSE == bSuccess && ERROR_INSUFFICIENT_BUFFER == GetLastError() ) { pszTextualSid = (LPTSTR)LocalAlloc( LPTR, (dwTextualSid + 1) * sizeof(TCHAR) );
if( NULL != pszTextualSid ) { bSuccess = GetTextualSid( m_pbUserSid, pszTextualSid, &dwTextualSid );
if( TRUE == bSuccess ) { m_bstrUserSid = pszTextualSid; } } }
if( 0 == m_bstrUserSid.Length() ) { hRes = HRESULT_FROM_WIN32(GetLastError()); } }
if( NULL != pszTextualSid ) { LocalFree(pszTextualSid); }
EndImpersonateClient(); } }
return hRes;
#else
m_pbUserSid = NULL; m_cbUserSid = 0; m_bstrUserSid = WIN9X_USER_SID;
return S_OK;
#endif
} //---------------------------------------------------------------
HRESULT CRemoteDesktopHelpSessionMgr::IsUserAllowToGetHelp( OUT BOOL* pbAllow ) /*++
Routine Description:
Check if connected user is allowed to GetHelp.
Parameters:
pbAllow : Return TRUE if user is allowed to GetHelp, FALSE otherwise.
Returns:
S_OK or error code.
Note:
GetHelp's priviledge is via group membership.
--*/ { HRESULT hRes;
hRes = ImpersonateClient();
if( SUCCEEDED(hRes) ) { *pbAllow = ::IsUserAllowToGetHelp( GetUserTSLogonId(), (LPCTSTR) m_bstrUserSid ); hRes = S_OK; } else { // can't get help if impersonate failed.
*pbAllow = FALSE; }
EndImpersonateClient();
return hRes; }
//---------------------------------------------------------
HRESULT CRemoteDesktopHelpSessionMgr::AcquireAssistantAccount() /*++
Routine Description:
"Acquire", increase the reference count of RemoteDesktop Assistant account. Routine creates a 'well-known' assistant account If is not exist or enables/change password if the account is disabled.
Help Account Manager will automatically release all reference count acquire by a particular session when user log off to prevent this account been 'locked'. Parameters:
pvarAccountName
Pointer to BSTR to receive RemoteDesktop Assistant account name.
pvarAccountPwd
Pointer to BSTR to receive RemoteDesktop Assistant account password.
Returns:
Success or error code.
Note:
This is also the conference name and conference password when NetMeeting is used to share user desktop.
--*/ { HRESULT hRes = S_OK; DWORD dwStatus;
CCriticalSectionLocker l( gm_AccRefCountCS );
#ifndef __WIN9xBUILD__
//
// Always enable interactive rights.
//
hRes = g_HelpAccount.EnableRemoteInteractiveRight(TRUE);
if( FAILED(hRes) ) { DebugPrintf( _TEXT("Failed in EnableRemoteInteractiveRight() - 0x%08x\n"), hRes ); goto CLEANUPANDEXIT; }
//
// Always enable the account in case user disable it.
//
hRes = g_HelpAccount.EnableHelpAssistantAccount( TRUE );
if( FAILED(hRes) ) { DebugPrintf( _TEXT("Can't enable help assistant account 0x%x\n"), hRes ); goto CLEANUPANDEXIT; }
if( g_HelpSessTable.NumEntries() == 0 ) { DebugPrintf( _TEXT("Setting encryption parameters...\n") );
dwStatus = TSHelpAssistantBeginEncryptionCycle(); hRes = HRESULT_FROM_WIN32( dwStatus ); MYASSERT( SUCCEEDED(hRes) );
//
// Setup account TS setting via WTSAPI
//
hRes = g_HelpAccount.SetupHelpAccountTSSettings(); if( SUCCEEDED(hRes) ) { DebugPrintf( _TEXT("SetupHelpAccountTSSettings return 0x%08x\n"), hRes ); } else { DebugPrintf( _TEXT("SetupHelpAccountTSSettings failed with 0x%08x\n"), hRes ); } }
#endif
CLEANUPANDEXIT:
return hRes; }
//----------------------------------------------------------
HRESULT CRemoteDesktopHelpSessionMgr::ReleaseAssistantAccount() /*++
Routine Description:
Release RemoteDesktop assistant account previously acquired with AcquireAssistantAccount(), account will be disabled if the account reference count is 0.
Help Account Manager will automatically release all reference count acquire by a particular session when user log off to prevent this account been 'locked'.
Parameters:
None
Returns:
Success or error code.
--*/ { HRESULT hRes = S_OK; DWORD dwStatus; CCriticalSectionLocker l( gm_AccRefCountCS );
#ifndef __WIN9XBUILD__
if( g_HelpSessTable.NumEntries() == 0 ) { // ignore error if we can't reset account password
(void)g_HelpAccount.ResetHelpAccountPassword();
dwStatus = TSHelpAssisantEndEncryptionCycle(); hRes = HRESULT_FROM_WIN32( dwStatus ); MYASSERT( SUCCEEDED(hRes) );
//
// diable HelpAssistant TS 'Connect' right.
//
g_HelpAccount.EnableRemoteInteractiveRight(FALSE);
hRes = g_HelpAccount.EnableHelpAssistantAccount( FALSE ); if( FAILED(hRes) ) { // not a critical error.
DebugPrintf( _TEXT("Can't disable help assistant account 0x%x\n"), hRes ); }
} #endif
return S_OK; }
STDMETHODIMP CRemoteDesktopHelpSessionMgr::GetUserSessionRdsSetting( OUT REMOTE_DESKTOP_SHARING_CLASS* rdsLevel ) /*++
--*/ { HRESULT hRes; DWORD dwStatus; REMOTE_DESKTOP_SHARING_CLASS userRdsDefault;
if( NULL != rdsLevel ) { hRes = ImpersonateClient(); if( SUCCEEDED(hRes) ) { hRes = LoadUserSid();
MYASSERT( SUCCEEDED(hRes) ); dwStatus = GetUserRDSLevel( m_LogonId, &userRdsDefault ); hRes = HRESULT_FROM_WIN32( dwStatus );
*rdsLevel = userRdsDefault;
EndImpersonateClient(); } } else { hRes = E_POINTER; }
return hRes; }
STDMETHODIMP CRemoteDesktopHelpSessionMgr::ResetHelpAssistantAccount( BOOL bForce ) /*++
Routine Description:
Reset help assistant account password.
Parameters:
bForce : TRUE if delete all pending help and reset the account password, FALSE if reset account password if there is no more pending help session.
Returns:
S_OK HRESULT_FROM_WIN32( ERROR_MORE_DATA )
--*/ { HRESULT hRes = S_OK;
hRes = LoadUserSid();
MYASSERT( SUCCEEDED(hRes) );
// Check any help stil pending
if( g_HelpSessTable.NumEntries() > 0 ) { if( FALSE == bForce ) { hRes = HRESULT_FROM_WIN32( ERROR_MORE_DATA ); } else { IDToSessionMap::LOCK_ITERATOR it = gm_HelpIdToHelpSession.begin();
//
// notify all in cached pending help session that it has been deleted.
// rest help entry will be deleted via DeleteSessionTable().
for( ;it != gm_HelpIdToHelpSession.end(); ) { RemoteDesktopHelpSessionObj* pObj = (*it).second;
// DeleteHelp() will wipe entry from cache.
it++;
// We can't not release this object since client might still
// holding pointer
pObj->DeleteHelp(); }
g_HelpSessTable.DeleteSessionTable(); } }
if(SUCCEEDED(hRes)) { hRes = g_HelpAccount.ResetHelpAccountPassword(); } return hRes; }
HRESULT CRemoteDesktopHelpSessionMgr::GenerateHelpSessionId( CComBSTR& bstrHelpSessionId ) /*++
Routine Description:
Create a unique Help Session ID.
Parameters:
bstrHelpSessionId : Reference to CComBSTR to receive HelpSessionId.
Returns:
S_OK HRESULT_FROM_WIN32( Status from RPC call UuidCreate() or UuidToString() ) --*/ { LPTSTR pszRandomString = NULL; DWORD dwStatus;
dwStatus = GenerateRandomString( 32, &pszRandomString ); if( ERROR_SUCCESS == dwStatus ) { bstrHelpSessionId = pszRandomString; LocalFree( pszRandomString ); }
return HRESULT_FROM_WIN32( dwStatus ); }
STDMETHODIMP CRemoteDesktopHelpSessionMgr::CreateHelpSessionEx( /*[in]*/ REMOTE_DESKTOP_SHARING_CLASS sharingClass, /*[in]*/ BOOL fEnableCallback, /*[in]*/ LONG timeOut, /*[in]*/ LONG userSessionId, /*[in]*/ BSTR userSid, /*[in]*/ BSTR bstrUserHelpCreateBlob, /*[out, retval]*/ IRemoteDesktopHelpSession** ppIRemoteDesktopHelpSession ) /*++
Routine Description:
Simimar to CreateHelpSession() except it allow caller to assoicate a help session to a specific user, caller must be running in system context.
Parameters:
sharingClass : Level of remote control (shadow setting) needed. fEnableCallback : TRUE to enable resolver callback, FALSE otherwise. timeOut : Help session timeout value. userSessionId : Logon user TS session ID. userSid : User SID that help session associated. bstrUserHelpCreateBlob : user specific create blob. parms: Return connect parm.
Returns:
--*/ { HRESULT hRes; RemoteDesktopHelpSessionObj* pRemoteDesktopHelpSessionObj = NULL;
if( NULL == ppIRemoteDesktopHelpSession ) { hRes = E_POINTER; } else if( timeOut <= 0 ) { // pcHealth request, no default timeout
hRes = E_INVALIDARG; } else { hRes = RemoteCreateHelpSessionEx( TRUE, // cache entry
fEnableCallback, // enable resolver ?
sharingClass, timeOut, userSessionId, userSid, bstrUserHelpCreateBlob, &pRemoteDesktopHelpSessionObj ); //
// 1) pcHealth resolver interprete salem connection parm, reset help session name to
// some default string.
// 2) When resolver invoke helpctr, script will truncate up to first space so
// our name can not contain space.
//
if( SUCCEEDED(hRes) && pRemoteDesktopHelpSessionObj ) { ULONG flag;
flag = pRemoteDesktopHelpSessionObj->GetHelpSessionFlag(); pRemoteDesktopHelpSessionObj->SetHelpSessionFlag( flag & ~HELPSESSIONFLAG_UNSOLICITEDHELP ); }
*ppIRemoteDesktopHelpSession = pRemoteDesktopHelpSessionObj; }
return hRes; }
STDMETHODIMP CRemoteDesktopHelpSessionMgr::RemoteCreateHelpSession( /*[in]*/ REMOTE_DESKTOP_SHARING_CLASS sharingClass, /*[in]*/ LONG timeOut, /*[in]*/ LONG userSessionId, /*[in]*/ BSTR userSid, /*[in]*/ BSTR bstrHelpCreateBlob, /*[out, retval]*/ BSTR* parms ) /*++
Description:
UNSOLICTED SUPPORT, only invoke by PCHEALTH, differ to CreateHelpSessionEx() are help session entry will not cached into registry and resolver callback is always enable.
Parameters:
Refer to CreateHelpSessionEx().
Returns:
--*/ { HRESULT hRes; RemoteDesktopHelpSessionObj* pIRemoteDesktopHelpSession = NULL;
if( timeOut <= 0 ) { // pcHealth request, no default timeout
hRes = E_INVALIDARG; } else { // if pcHealth pass unresolve session, cache the entry, set
// timeout to very short for security reason.
hRes = RemoteCreateHelpSessionEx( FALSE, // don't cache entry in registry.
TRUE, // force resolver call.
sharingClass, timeOut, userSessionId, userSid, bstrHelpCreateBlob, &pIRemoteDesktopHelpSession );
if( SUCCEEDED(hRes) && NULL != pIRemoteDesktopHelpSession ) { hRes = pIRemoteDesktopHelpSession->get_ConnectParms( parms ); } }
return hRes; }
HRESULT CRemoteDesktopHelpSessionMgr::RemoteCreateHelpSessionEx( /*[in]*/ BOOL bCacheEntry, /*[in]*/ BOOL bEnableResolver, /*[in]*/ REMOTE_DESKTOP_SHARING_CLASS sharingClass, /*[in]*/ LONG timeOut, /*[in]*/ LONG userSessionId, /*[in]*/ BSTR userSid, /*[in]*/ BSTR bstrHelpCreateBlob, /*[out, retval]*/ RemoteDesktopHelpSessionObj** ppIRemoteDesktopHelpSession ) /*++
Routine Description:
Create help ticket and return connection parameters.
Parameters:
bCacheEntry : Cache help session to registry. bEnableCallback : TRUE to enable resolver callback, FALSE otherwise. sharingClass : RDS setting requested. timeout : Help session expiry period. userSessionId : User TS session ID that help session associated with. userSid : SID of user on the TS session. bstrHelpCreateBlob : User specific help session create blob, meaningless if resolver is not enabled. ppIRemoteDesktopHelpSession : Help session created.
Returns:
S_OK S_FALSE sharingClass violate policy setting. other error code.
--*/ { HRESULT hRes = S_OK; BOOL bStatus; RemoteDesktopHelpSessionObj *pIHelpSession = NULL; BOOL bAllowGetHelp = FALSE; ULONG flag;
LPTSTR eventString[3]; BSTR pszNoviceDomain = NULL; BSTR pszNoviceName = NULL; TCHAR buffer[256]; int numChars;
#if DBG
long HelpSessLogonId; #endif
if( FALSE == _Module.IsSuccessServiceStartup() ) { // service startup problem, return error code.
hRes = _Module.GetServiceStartupStatus();
DebugPrintf( _TEXT("Service startup failed with 0x%x\n"), hRes );
goto CLEANUPANDEXIT; }
if( 0 >= timeOut ) { hRes = E_INVALIDARG; MYASSERT(FALSE); goto CLEANUPANDEXIT; }
hRes = LoadUserSid();
if( FAILED(hRes) ) { goto CLEANUPANDEXIT; }
// common routine in tsremdsk.lib
if( FALSE == TSIsMachinePolicyAllowHelp() ) { hRes = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); goto CLEANUPANDEXIT; }
hRes = ImpersonateClient(); if( FAILED(hRes) ) { goto CLEANUPANDEXIT; }
//
// Make sure only system service can invoke this call.
//
if( !g_pSidSystem || FALSE == IsCallerSystem(g_pSidSystem) ) {
#if DISABLESECURITYCHECKS
DWORD dump;
//
// For testing only, allow admin to invoke this call
//
dump = IsUserAdmin(&bStatus); hRes = HRESULT_FROM_WIN32( dump ); if( FAILED(hRes) || FALSE == bStatus ) { EndImpersonateClient();
hRes = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ); goto CLEANUPANDEXIT; }
#else
EndImpersonateClient();
hRes = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ); goto CLEANUPANDEXIT;
#endif
}
// No need to run as client.
EndImpersonateClient();
//
// Log the event indicate that ticket was deleted, non-critical
// since we can still continue to run.
//
hRes = ConvertSidToAccountName( CComBSTR(userSid), &pszNoviceDomain, &pszNoviceName );
if( FAILED(hRes) ) { //
// If we can't conver SID to name, SID is invalid so bail out
//
MYASSERT(FALSE); goto CLEANUPANDEXIT; }
//
// No ERROR checking on userSessionId and userSid, pcHealth
// will make sure all parameter is correct
//
//
// Create a Help Session.
//
hRes = CreateHelpSession( bCacheEntry, HELPSESSION_UNSOLICATED, CComBSTR(""), HELPSESSION_UNSOLICATED, bstrHelpCreateBlob, (userSessionId == -1) ? UNKNOWN_LOGONID : userSessionId, userSid, &pIHelpSession );
if( FAILED(hRes) ) { goto CLEANUPANDEXIT; }
if( NULL == pIHelpSession ) { MYASSERT( NULL != pIHelpSession ); hRes = E_UNEXPECTED; goto CLEANUPANDEXIT; }
#if DBG
hRes = pIHelpSession->get_UserLogonId( &HelpSessLogonId ); MYASSERT( SUCCEEDED(hRes) );
if( userSessionId != -1 ) { MYASSERT( HelpSessLogonId == userSessionId ); } else { MYASSERT( HelpSessLogonId == UNKNOWN_LOGONID ); } #endif
//
// setup help session parms.
//
hRes = pIHelpSession->put_EnableResolver(bEnableResolver); MYASSERT( SUCCEEDED(hRes) ); if( FAILED(hRes) ) { goto CLEANUPANDEXIT; }
hRes = pIHelpSession->put_TimeOut( timeOut ); if( FAILED(hRes) ) { DebugPrintf( _TEXT("put_TimeOut() failed with 0x%08x\n"), hRes );
goto CLEANUPANDEXIT; }
//
// We change default RDS value at the end so we can return error code or S_FALSE
// from this.
//
hRes = pIHelpSession->put_UserHelpSessionRemoteDesktopSharingSetting( sharingClass ); if( FAILED( hRes) ) { DebugPrintf( _TEXT("put_UserHelpSessionRemoteDesktopSharingSetting() failed with 0x%08x\n"), hRes ); }
flag = pIHelpSession->GetHelpSessionFlag();
pIHelpSession->SetHelpSessionFlag( flag | HELPSESSIONFLAG_UNSOLICITEDHELP );
numChars = _sntprintf( buffer, sizeof(buffer)/sizeof(buffer[0]), _TEXT("%.2f"), (double)timeOut/(double)3600.0 ); if( numChars <= 0 ) { // we should have enough buffer to convert, internal error.
MYASSERT(FALSE); hRes = E_UNEXPECTED; goto CLEANUPANDEXIT; }
eventString[0] = buffer; eventString[1] = pszNoviceDomain; eventString[2] = pszNoviceName;
LogRemoteAssistanceEventString( EVENTLOG_INFORMATION_TYPE, SESSMGR_I_REMOTEASSISTANCE_CREATETICKET, 3, eventString );
CLEANUPANDEXIT:
if( NULL != pszNoviceDomain ) { SysFreeString( pszNoviceDomain ); }
if( NULL != pszNoviceName ) { SysFreeString( pszNoviceName ); }
if( FAILED(hRes) ) { if( NULL != pIHelpSession ) { pIHelpSession->DeleteHelp(); pIHelpSession->Release(); } } else { MYASSERT( NULL != pIHelpSession ); *ppIRemoteDesktopHelpSession = pIHelpSession; }
return hRes; }
HRESULT LoadLocalSystemSID() /*
Routine Description:
Load service account as SID string.
Parameter:
None.
Returns:
S_OK or error code
--*/ { DWORD dwStatus; BOOL bSuccess = TRUE; LPTSTR pszTextualSid = NULL; DWORD dwTextualSid = 0;
dwStatus = CreateSystemSid( &g_pSidSystem ); if( ERROR_SUCCESS == dwStatus ) { // convert SID to string
bSuccess = GetTextualSid( g_pSidSystem, NULL, &dwTextualSid );
if( FALSE == bSuccess && ERROR_INSUFFICIENT_BUFFER == GetLastError() ) { pszTextualSid = (LPTSTR)LocalAlloc( LPTR, (dwTextualSid + 1) * sizeof(TCHAR) );
if( NULL != pszTextualSid ) { bSuccess = GetTextualSid( g_pSidSystem, pszTextualSid, &dwTextualSid );
if( TRUE == bSuccess ) { g_LocalSystemSID = pszTextualSid; } } } if( 0 == g_LocalSystemSID.Length() ) { dwStatus = GetLastError(); } }
if( NULL != pszTextualSid ) { LocalFree(pszTextualSid); }
return HRESULT_FROM_WIN32(dwStatus); }
HRESULT CRemoteDesktopHelpSessionMgr::LogSalemEvent( IN long ulEventType, IN long ulEventCode, IN long numStrings, IN LPTSTR* pszStrings ) /*++
Description:
Log a Salem related event, this is invoked by TermSrv and rdshost to log event related to help assistant connection.
Parameters:
Returns:
S_OK or error code.
--*/ { HRESULT hRes = S_OK;
switch( ulEventCode ) { case REMOTEASSISTANCE_EVENTLOG_TERMSRV_INVALID_TICKET:
if( numStrings >= 3 ) { //
// this event require three parameters.
//
// NOTE: This message is log from TermSrv, we could just
// proxy this event to RACPLDLG.DLL.
ulEventCode = SESSMGR_E_REMOTEASSISTANCE_CONNECTFAILED; LogRemoteAssistanceEventString( ulEventType, ulEventCode, numStrings, pszStrings ); } else { hRes = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); }
break;
case REMOTEASSISTANCE_EVENTLOG_TERMSRV_REVERSE_CONNECT: // need at least three parameters.
//
// This event is log from TermSrv, TermSrv does not have
// owner of ticket so we add those into event, we don't want
// publish ticket owner SID to prevent security leak
//
if( numStrings >= 3 ) { //
// String is in the order of
// expert IP address from client
// expert IP address from rdshost.exe
// Ticket ID.
//
LPTSTR pszLogStrings[4]; ulEventCode = SESSMGR_I_REMOTEASSISTANCE_CONNECTTOEXPERT; RemoteDesktopHelpSessionObj* pObj;
//
// Load expire help session in order to log event, we will let
// validation catch error
//
pObj = LoadHelpSessionObj( NULL, CComBSTR(pszStrings[2]), TRUE ); if( NULL != pObj ) { pszLogStrings[0] = (LPTSTR)pObj->m_EventLogInfo.bstrNoviceDomain; pszLogStrings[1] = (LPTSTR)pObj->m_EventLogInfo.bstrNoviceAccount; pszLogStrings[2] = pszStrings[0]; pszLogStrings[3] = pszStrings[1]; LogRemoteAssistanceEventString( ulEventType, ulEventCode, 4, pszLogStrings );
pObj->Release(); } else { hRes = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ); } } else { hRes = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
MYASSERT(FALSE); }
break;
default: MYASSERT(FALSE); hRes = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); }
return hRes; }
STDMETHODIMP CRemoteDesktopHelpSessionMgr::LogSalemEvent( /*[in]*/ long ulEventType, /*[in]*/ long ulEventCode, /*[in]*/ VARIANT* pEventStrings ) /*++
--*/ { HRESULT hRes = S_OK; BSTR* bstrArray = NULL; SAFEARRAY* psa = NULL; VARTYPE vt_type; DWORD dwNumStrings = 0;
hRes = ImpersonateClient(); if( FAILED(hRes) ) { goto CLEANUPANDEXIT; }
//
// Make sure only system service can invoke this call.
//
if( !g_pSidSystem || FALSE == IsCallerSystem(g_pSidSystem) ) { #if DISABLESECURITYCHECKS
DWORD dump; BOOL bStatus;
//
// For testing only, allow admin to invoke this call
//
dump = IsUserAdmin(&bStatus); hRes = HRESULT_FROM_WIN32( dump ); if( FAILED(hRes) || FALSE == bStatus ) { EndImpersonateClient();
hRes = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ); goto CLEANUPANDEXIT; }
#else
EndImpersonateClient();
hRes = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ); goto CLEANUPANDEXIT;
#endif
}
// No need to run as client.
EndImpersonateClient();
if( NULL == pEventStrings ) { hRes = LogSalemEvent( ulEventType, ulEventCode ); } else { //
// we only support BSTR data type.
if( !(pEventStrings->vt & VT_BSTR) ) { MYASSERT(FALSE); hRes = E_INVALIDARG; goto CLEANUPANDEXIT; }
//
// we are dealing with multiple BSTRs
if( pEventStrings->vt & VT_ARRAY ) { psa = pEventStrings->parray;
// only accept 1 dim.
if( 1 != SafeArrayGetDim(psa) ) { hRes = E_INVALIDARG; MYASSERT(FALSE); goto CLEANUPANDEXIT; }
// only accept BSTR as input type.
hRes = SafeArrayGetVartype( psa, &vt_type ); if( FAILED(hRes) ) { MYASSERT(FALSE); goto CLEANUPANDEXIT; }
if( VT_BSTR != vt_type ) { DebugPrintf( _TEXT("Unsupported type 0x%08x\n"), vt_type );
hRes = E_INVALIDARG; MYASSERT(FALSE); goto CLEANUPANDEXIT; }
hRes = SafeArrayAccessData(psa, (void **)&bstrArray); if( FAILED(hRes) ) { MYASSERT(FALSE); goto CLEANUPANDEXIT; }
hRes = LogSalemEvent( ulEventType, ulEventCode, psa->rgsabound->cElements, (LPTSTR *)bstrArray );
SafeArrayUnaccessData(psa); } else { hRes = LogSalemEvent( ulEventType, ulEventCode, 1, (LPTSTR *)&(pEventStrings->bstrVal) ); }
}
CLEANUPANDEXIT:
return hRes; }
void CRemoteDesktopHelpSessionMgr::NotifyExpertLogoff( LONG ExpertSessionId, BSTR HelpedTicketId ) /*++
Routine Description:
Notify help ticket that helping expert has logoff so ticket object can de-associate (mark is not been help) with a particular helper session.
Parameters:
ExpertSessionId : Expert logon session ID. HelpedTicketId : Ticket ID that expert was providing help.
Returns:
None.
--*/ { MYASSERT( NULL != HelpedTicketId );
if( NULL != HelpedTicketId ) { DebugPrintf( _TEXT("NotifyExpertLogoff() on %d %s...\n"), ExpertSessionId, HelpedTicketId );
//
// Load Help Entry, we need to inform resolver on disconnect so load
// expired ticket.
//
RemoteDesktopHelpSessionObj* pObj = LoadHelpSessionObj( NULL, HelpedTicketId, TRUE );
if( NULL != pObj ) { MYASSERT( ExpertSessionId == pObj->GetHelperSessionId() );
if( ExpertSessionId == pObj->GetHelperSessionId() ) { pObj->NotifyDisconnect(); }
pObj->Release(); }
//
// Free ticket ID
//
SysFreeString( HelpedTicketId ); }
return; }
STDMETHODIMP CRemoteDesktopHelpSessionMgr::PrepareSystemRestore() { DWORD dwStatus = ERROR_SUCCESS;
//
// no pending ticket, just return
//
if( TSIsMachineInHelpMode() ) { DebugPrintf( _TEXT("PrepareSystemRestore()...\n") ); dwStatus = TSSystemRestoreCacheValues(); }
return HRESULT_FROM_WIN32( dwStatus ); }
|