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.
3117 lines
77 KiB
3117 lines
77 KiB
/*++
|
|
|
|
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 );
|
|
}
|
|
|