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

2650 lines
65 KiB

/*++
Copyright (c) 1999-2000 Microsoft Corporation
Module Name:
HelpSess.cpp
Abstract:
HelpSess.cpp : Implementation of CRemoteDesktopHelpSession
Author:
HueiWang 2/17/2000
--*/
#include "stdafx.h"
#include <time.h>
#include <Sddl.h>
#include "global.h"
#include "Sessmgr.h"
#include "rdshost.h"
#include "HelpTab.h"
#include "policy.h"
#include "HelpAcc.h"
#include "HelpMgr.h"
#include "HelpSess.h"
#include <rdshost_i.c>
#include "RemoteDesktopUtils.h"
#include "RemoteDesktop.h"
#include <safsessionresolver_i.c>
/////////////////////////////////////////////////////////////////////////////
//
// CRemoteDesktopHelpSession
//
//
CRemoteDesktopHelpSession::CRemoteDesktopHelpSession() :
m_ulLogonId(UNKNOWN_LOGONID),
m_ulHelperSessionId(UNKNOWN_LOGONID),
m_ulHelpSessionFlag(0)
{
}
CRemoteDesktopHelpSession::~CRemoteDesktopHelpSession()
{
}
void
CRemoteDesktopHelpSession::FinalRelease()
{
// Releas help entry
if( NULL != m_pHelpSession )
{
//
// DeleteHelp() will free m_pHelpSession but in the case that RA is in
// progress, it only set m_bDeleted to TRUE so we assert here if
// ticket is deleted.
//
MYASSERT( FALSE == m_bDeleted );
DebugPrintf(
_TEXT("FinalRelease %s on %s\n"),
(IsUnsolicitedHelp()) ? L"Unsolicted Help" : L"Solicited Help",
m_bstrHelpSessionId
);
// Notify disconnect will check if session is in help and bail out if necessary.
// there is a timing issue that our SCM notification might came after caller close
// all reference counter to our session object, in this case, SCM notification will
// trigger reload from database which does not have helper session ID and so will
// not notify resolver causing helpee been helped message.
// We also has AddRef() manually in ResolveXXX call and Release() in
// NotifyDisconnect(), this will hold the object in memory until SCM
// notification comes in.
NotifyDisconnect();
CRemoteDesktopHelpSessionMgr::DeleteHelpSessionFromCache( m_bstrHelpSessionId );
m_pHelpSession->Close();
m_pHelpSession = NULL;
}
ULONG count = _Module.Release();
DebugPrintf(
_TEXT("Module Release by CRemoteDesktopHelpSession() %p %d...\n"),
this,
count
);
}
HRESULT
CRemoteDesktopHelpSession::put_ICSPort(
IN DWORD newVal
)
/*++
Description:
Associate ICS port number with this help session.
Parameters:
newVal : ICS port number.
Returns:
--*/
{
HRESULT hRes = S_OK;
//
// Exclusive lock on helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_EXCLUSIVE);
if( FALSE == IsSessionValid() )
{
MYASSERT(FALSE);
hRes = E_UNEXPECTED;
return hRes;
}
//
// Need to immediately update the value...
//
m_pHelpSession->m_ICSPort.EnableImmediateUpdate(TRUE);
m_pHelpSession->m_ICSPort = newVal;
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::get_ConnectParms(
OUT BSTR* bstrConnectParms
)
/*++
Description:
Retrieve connection parameter for help session.
Parameters:
bstrConnectParms : Pointer to BSTR to receive connect parms.
Returns:
--*/
{
HRESULT hRes = S_OK;
LPTSTR pszAddress = NULL;
int BufSize;
DWORD dwBufferRequire;
DWORD dwNumChars;
DWORD dwRetry;
CComBSTR bstrSessId;
DWORD dwICSPort = 0;
//
// Acquire share access to helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker ObjLock(m_HelpSessionLock, RESOURCELOCK_SHARE);
if( FALSE == IsSessionValid() )
{
hRes = E_HANDLE;
goto CLEANUPANDEXIT;
}
//
// Sync. access from OpenPort() to FetchAllAddress().
//
{
CCriticalSectionLocker ICSLock(g_ICSLibLock);
//
// Open ICS port.
//
dwICSPort = OpenPort( TERMSRV_TCPPORT );
//
// Address might have change which might require bigger buffer, retry
//
//
for(dwRetry=0; dwRetry < MAX_FETCHIPADDRESSRETRY; dwRetry++)
{
if( NULL != pszAddress )
{
LocalFree( pszAddress );
}
//
// Fetch all address on local machine.
//
dwBufferRequire = FetchAllAddresses( NULL, 0 );
if( 0 == dwBufferRequire )
{
hRes = E_UNEXPECTED;
MYASSERT(FALSE);
goto CLEANUPANDEXIT;
}
pszAddress = (LPTSTR) LocalAlloc( LPTR, sizeof(TCHAR)*(dwBufferRequire+1) );
if( NULL == pszAddress )
{
hRes = E_OUTOFMEMORY;
goto CLEANUPANDEXIT;
}
dwNumChars = FetchAllAddresses( pszAddress, dwBufferRequire );
if( dwNumChars <= dwBufferRequire )
{
break;
}
}
}
if( NULL == pszAddress || dwRetry >= MAX_FETCHIPADDRESSRETRY )
{
hRes = E_UNEXPECTED;
MYASSERT( FALSE );
goto CLEANUPANDEXIT;
}
//
// Get an exclusive lock
//
ObjLock.ConvertToExclusiveLock();
//
// We hold the exclusive lock and if we call put_ICSPort(), it will
// deadlock since put_ICSPort() try to get an exclusive lock again.
//
m_pHelpSession->m_ICSPort.EnableImmediateUpdate(TRUE);
m_pHelpSession->m_ICSPort = dwICSPort;
//
// Store the IP address
//
m_pHelpSession->m_IpAddress.EnableImmediateUpdate(TRUE);
m_pHelpSession->m_IpAddress = pszAddress;
ObjLock.ConvertToShareLock();
MYASSERT( ((CComBSTR)m_pHelpSession->m_IpAddress).Length() > 0 );
DebugPrintf(
_TEXT("IP Address %s\n"),
(LPTSTR)(CComBSTR)m_pHelpSession->m_IpAddress
);
//
// Create connection parameters
//
hRes = get_HelpSessionId( &bstrSessId );
if( FAILED(hRes) )
{
MYASSERT(FALSE);
goto CLEANUPANDEXIT;
}
{
// MTA so we need to lock g_TSSecurityBlob.
CCriticalSectionLocker l(g_GlobalLock);
MYASSERT( g_TSSecurityBlob.Length() > 0 );
#ifndef USE_WEBLINK_PARMSTRING_FORMAT
*bstrConnectParms = CreateConnectParmsString(
REMOTEDESKTOP_TSRDP_PROTOCOL,
CComBSTR(pszAddress),
CComBSTR(SALEM_CONNECTPARM_UNUSEFILED_SUBSTITUTE),
CComBSTR(SALEM_CONNECTPARM_UNUSEFILED_SUBSTITUTE),
bstrSessId,
CComBSTR(SALEM_CONNECTPARM_UNUSEFILED_SUBSTITUTE),
CComBSTR(SALEM_CONNECTPARM_UNUSEFILED_SUBSTITUTE),
g_TSSecurityBlob
);
#else
*bstrConnectParms = CreateConnectParmsString(
REMOTEDESKTOP_TSRDP_PROTOCOL,
CComBSTR(pszAddress),
bstrSessId,
g_TSSecurityBlob
);
#endif
}
#if DBG
if( NULL != *bstrConnectParms )
{
DebugPrintf(
_TEXT("Connect Parms %s\n"),
*bstrConnectParms
);
}
#endif
CLEANUPANDEXIT:
if( NULL != pszAddress )
{
LocalFree( pszAddress );
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::get_TimeOut(
/*[out, retval]*/ DWORD* pTimeout
)
/*++
--*/
{
HRESULT hRes = S_OK;
BOOL bSuccess;
//
// Acquire share access to helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_SHARE);
if( NULL == pTimeout )
{
hRes = E_POINTER;
}
else if( FALSE == IsSessionValid() )
{
hRes = E_HANDLE;
}
else if( NULL != m_pHelpSession )
{
FILETIME ft;
SYSTEMTIME sysTime;
ft = m_pHelpSession->m_ExpirationTime;
bSuccess = FileTimeToSystemTime(&ft, &sysTime);
if( TRUE == bSuccess )
{
if( sysTime.wYear >= 2038 )
{
*pTimeout = INT_MAX;
}
else
{
struct tm gmTime;
memset(&gmTime, 0, sizeof(gmTime));
gmTime.tm_sec = sysTime.wSecond;
gmTime.tm_min = sysTime.wMinute;
gmTime.tm_hour = sysTime.wHour;
gmTime.tm_year = sysTime.wYear - 1900;
gmTime.tm_mon = sysTime.wMonth - 1;
gmTime.tm_mday = sysTime.wDay;
//
// mktime() return values in local time not UTC time
// all time uses in sessmgr is UTC time, time(), so
// return in UTC time, note that no XP client bug since
// this property is not exposed.
//
if((*pTimeout = mktime(&gmTime)) == (time_t)-1)
{
*pTimeout = INT_MAX;
}
else
{
// we don't need to substract daylight saving time (dst)
// since when calling mktime, we set isdst to 0 in
// gmTime.
struct _timeb timebuffer;
_ftime( &timebuffer );
(*pTimeout) -= (timebuffer.timezone * 60);
}
}
}
else
{
hRes = HRESULT_FROM_WIN32( GetLastError() );
}
}
else
{
hRes = E_UNEXPECTED;
MYASSERT( FALSE );
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::put_TimeOut(
/*[in]*/ DWORD Timeout
)
/*++
--*/
{
HRESULT hRes = S_OK;
//
// Exclusive lock on helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_EXCLUSIVE);
LONG MaxTicketExpiry;
//
// Get default timeout value from registry, not a critical
// error, if we failed, we just default to 30 days
//
hRes = PolicyGetMaxTicketExpiry( &MaxTicketExpiry );
if( FAILED(hRes) || 0 == MaxTicketExpiry )
{
MaxTicketExpiry = DEFAULT_MAXTICKET_EXPIRY;
}
if( Timeout > MaxTicketExpiry )
{
hRes = S_FALSE;
Timeout = MaxTicketExpiry;
}
time_t curTime;
FILETIME ftTimeOut;
// Get the current time.
time(&curTime);
// time out in seconds
curTime += Timeout;
// Convert to FILETIME
UnixTimeToFileTime( curTime, &ftTimeOut );
if( FALSE == IsSessionValid() )
{
hRes = E_HANDLE;
}
else if( NULL != m_pHelpSession )
{
if( FALSE == m_pHelpSession->IsEntryExpired() )
{
//
// operator =() update registry immediately
//
m_pHelpSession->m_ExpirationTime = ftTimeOut;
}
}
else
{
hRes = E_UNEXPECTED;
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::get_HelpSessionId(
OUT BSTR *pVal
)
/*++
Routine Description:
Get help session ID.
Parameters:
pVal : return Help session ID of this help session instance.
Returns:
S_OK
E_OUTOFMEMORY
E_UNEXPECTED
--*/
{
HRESULT hRes = S_OK;
//
// Acquire share access to helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_SHARE);
if( NULL == pVal )
{
hRes = E_POINTER;
}
else if( FALSE == IsSessionValid() )
{
hRes = E_HANDLE;
}
else if( NULL != m_pHelpSession )
{
DebugPrintf(
_TEXT("get_HelpSessionId() on %s\n"),
m_bstrHelpSessionId
);
MYASSERT( m_pHelpSession->m_SessionId->Length() > 0 );
if( m_pHelpSession->m_SessionId->Length() > 0 )
{
*pVal = m_pHelpSession->m_SessionId->Copy();
if( NULL == *pVal )
{
hRes = E_OUTOFMEMORY;
}
}
else
{
hRes = E_UNEXPECTED;
}
}
else
{
hRes = E_UNEXPECTED;
MYASSERT( FALSE );
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::get_UserLogonId(
OUT long *pVal
)
/*++
Routine Description:
Get user's TS session ID, note, non-ts session or Win9x always
has 0 as user logon id.
Parameters:
pVal : Return user logon ID.
Returns:
S_OK
--*/
{
HRESULT hRes = S_OK;
//
// Acquire share access to helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_SHARE);
if( NULL == pVal )
{
hRes = E_POINTER;
}
else if(FALSE == IsSessionValid())
{
hRes = E_HANDLE;
}
else if( NULL != m_pHelpSession )
{
*pVal = m_ulLogonId;
if( UNKNOWN_LOGONID == m_ulLogonId )
{
hRes = S_FALSE;
}
}
else
{
MYASSERT( FALSE );
hRes = E_UNEXPECTED;
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::get_AssistantAccountName(
OUT BSTR *pVal
)
/*++
Routine Description:
Get help assistant account name associated with this
help session.
Parameters:
pVal : Return help assistant account name associated
with this help session.
Returns:
S_OK
E_OUTOFMEMORY
--*/
{
HRESULT hRes = S_OK;
// Don't need a lock here.
if( NULL != pVal )
{
CComBSTR accName;
hRes = g_HelpAccount.GetHelpAccountNameEx( accName );
if( SUCCEEDED(hRes) )
{
*pVal = accName.Copy();
if( NULL == *pVal )
{
hRes = E_OUTOFMEMORY;
}
}
}
else
{
hRes = E_POINTER;
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::get_EnableResolver(
OUT BOOL* pVal
)
/*++
Routine Description:
Return Session Resolver's CLSID for this help session.
Parameters:
pVal : Pointer to BSTR to receive pointer to Resolver's CLSID.
Returns:
S_OK
E_POINTER Invalid argument
--*/
{
HRESULT hRes = S_OK;
//
// Acquire share access to helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_SHARE);
if( NULL == pVal )
{
hRes = E_POINTER;
}
else if(FALSE == IsSessionValid())
{
hRes = E_HANDLE;
}
else if( NULL != m_pHelpSession )
{
*pVal = ((long)m_pHelpSession->m_EnableResolver > 0) ? TRUE : FALSE;
}
else
{
hRes = E_UNEXPECTED;
MYASSERT(FALSE);
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::put_EnableResolver(
IN BOOL newVal
)
/*++
Routine Description:
Set Session Resolver's CLSID, if input is NULL or empty
string, Help Session Manager will not invoke resolver.
Parameters:
Val : Resolver's CLSID
Returns:
S_OK
E_OUTOFMEMORY
--*/
{
HRESULT hRes = S_OK;
//
// Exclusive lock on helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_EXCLUSIVE);
if( FALSE == IsSessionValid() )
{
hRes = E_HANDLE;
}
else if( FALSE == IsClientSessionCreator() )
{
hRes = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
}
else if( NULL != m_pHelpSession )
{
//
// NULL will reset resolver's ID for this session
//
//
// operator =() update registry immediately
//
m_pHelpSession->m_EnableResolver = newVal;
}
else
{
hRes = E_UNEXPECTED;
MYASSERT(FALSE);
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::get_ResolverBlob(
OUT BSTR* pVal
)
/*++
Routine Description:
Return blob for session resolver to map help session
to user session/
Parameters:
pVal : Pointer to BSTR to receive blob.
Returns:
S_OK
S_FALSE No blob
E_OUTOFMEMORY
E_POINTER
--*/
{
HRESULT hRes = S_OK;
//
// Acquire share access to helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_SHARE);
if( NULL == pVal )
{
hRes = E_POINTER;
}
else if(FALSE == IsSessionValid())
{
hRes = E_HANDLE;
}
else if( NULL != m_pHelpSession )
{
if( m_pHelpSession->m_SessResolverBlob->Length() > 0 )
{
*pVal = m_pHelpSession->m_SessResolverBlob->Copy();
if( NULL == *pVal )
{
hRes = E_OUTOFMEMORY;
}
}
else
{
hRes = S_FALSE;
}
}
else
{
hRes = E_UNEXPECTED;
MYASSERT(FALSE);
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::put_ResolverBlob(
IN BSTR newVal
)
/*++
Routine Description:
Add/change blob which will be passed to session
resolver to map/find user session associated with this
help session, Help Session Manager does not interpret this
blob.
Parameters:
newVal : Pointer to new blob.
Returns:
S_OK
E_OUTOFMEMORY Out of memory
--*/
{
HRESULT hRes = S_OK;
//
// Exclusive lock on helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_EXCLUSIVE);
if( FALSE == IsSessionValid() )
{
hRes = E_HANDLE;
}
else if( FALSE == IsClientSessionCreator() )
{
hRes = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
}
else if( NULL != m_pHelpSession )
{
//
// NULL will reset resolver's ID for this session
//
//
// operator =() update registry immediately
//
m_pHelpSession->m_SessResolverBlob = newVal;
if( !((CComBSTR)m_pHelpSession->m_SessResolverBlob == newVal) )
{
hRes = E_OUTOFMEMORY;
}
}
else
{
hRes = E_UNEXPECTED;
MYASSERT(FALSE);
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::get_HelpSessionCreateBlob(
OUT BSTR* pVal
)
/*++
Routine Description:
Return blob for session resolver to map help session
to user session/
Parameters:
pVal : Pointer to BSTR to receive blob.
Returns:
S_OK
S_FALSE No blob
E_OUTOFMEMORY
E_POINTER
--*/
{
HRESULT hRes = S_OK;
//
// Acquire share access to helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_SHARE);
if( NULL == pVal )
{
hRes = E_POINTER;
}
else if(FALSE == IsSessionValid())
{
hRes = E_HANDLE;
}
else if( NULL != m_pHelpSession )
{
if( m_pHelpSession->m_SessionCreateBlob->Length() > 0 )
{
*pVal = m_pHelpSession->m_SessionCreateBlob->Copy();
if( NULL == *pVal )
{
hRes = E_OUTOFMEMORY;
}
}
else
{
hRes = S_FALSE;
}
}
else
{
hRes = E_UNEXPECTED;
MYASSERT(FALSE);
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::put_HelpSessionCreateBlob(
IN BSTR newVal
)
/*++
Routine Description:
Add/change blob which will be passed to session
resolver to map/find user session associated with this
help session, Help Session Manager does not interpret this
blob.
Parameters:
newVal : Pointer to new blob.
Returns:
S_OK
E_OUTOFMEMORY Out of memory
--*/
{
HRESULT hRes = S_OK;
//
// Exclusive lock on helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_EXCLUSIVE);
if( FALSE == IsSessionValid() )
{
hRes = E_HANDLE;
}
else if( FALSE == IsClientSessionCreator() )
{
hRes = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
}
else if( NULL != m_pHelpSession )
{
//
// operator =() update registry immediately
//
m_pHelpSession->m_SessionCreateBlob = newVal;
if( !((CComBSTR)m_pHelpSession->m_SessionCreateBlob == newVal) )
{
hRes = E_OUTOFMEMORY;
}
}
else
{
hRes = E_UNEXPECTED;
MYASSERT(FALSE);
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::get_UserHelpSessionRemoteDesktopSharingSetting(
/*[out, retval]*/ REMOTE_DESKTOP_SHARING_CLASS* pSetting
)
/*++
Routine Description:
Return help session's RDS setting.
Parameters:
pSetting : Pointer to REMOTE_DESKTOP_SHARING_CLASS to
receive session's RDS setting.
Returns:
S_OK
E_POINTER Invalid argument.
--*/
{
HRESULT hRes = S_OK;
//
// Acquire share access to helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_SHARE);
if( NULL == pSetting )
{
hRes = E_POINTER;
}
else if(FALSE == IsSessionValid())
{
hRes = E_HANDLE;
}
else if( NULL != m_pHelpSession )
{
*pSetting = (REMOTE_DESKTOP_SHARING_CLASS)(long)m_pHelpSession->m_SessionRdsSetting;
}
else
{
hRes = E_UNEXPECTED;
MYASSERT(FALSE);
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::put_UserHelpSessionRemoteDesktopSharingSetting(
/*[in]*/ REMOTE_DESKTOP_SHARING_CLASS Setting
)
/*++
Routine Description:
Set help session's RDS setting.
Parameters:
Setting : New RDS setting.
Returns:
S_OK
S_FALSE New setting is overrided with
policy setting.
HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ) User not allow to get help.
HRESULT_FROM_WIN32( ERROR_SHARING_VIOLATION ) Other help session already has this set
HRESULT_FROM_WIN32( ERROR_VC_DISCONNECTED ) Session is not connected
HRESULT_FROM_WIN32( WinStationQueryInformation() );
E_OUTOFMEMORY
Note:
Only one help session can change the RDS setting, all other help session
will get HRESULT_FROM_WIN32( ERROR_SHARING_VIOLATION ) error return.
REMOTE_DESKTOP_SHARING_CLASS also define priviledge
level, that is user with NO_DESKTOP_SHARING can't
adjust his/her sharing class, user with CONTROLDESKTOP_PERMISSION_REQUIRE
can't adjust his/her sharing class to CONTROLDESKTOP_PERMISSION_NOT_REQUIRE
however, he/she can reset to NO_DESKTOP_SHARING, VIEWDESKTOP_PERMISSION_REQUIRE
--*/
{
HRESULT hRes = S_OK;
//
// Exclusive lock on helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_EXCLUSIVE);
if( FALSE == IsSessionValid() )
{
hRes = E_HANDLE;
}
else if( FALSE == IsClientSessionCreator() )
{
hRes = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
}
else if( NULL != m_pHelpSession )
{
//
// operator =() update registry immediately
//
m_pHelpSession->m_SessionRdsSetting = Setting;
}
else
{
hRes = E_UNEXPECTED;
MYASSERT(FALSE);
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::IsUserOwnerOfTicket(
/*[in]*/ BSTR bstrSID,
/* [out, retval] */ VARIANT_BOOL* pbUserIsOwner
)
/*++
Description:
Check if user is the owner of this ticket
Parameter:
SID : User sid to verify.
pbUserIsOwner : VARIANT_TRUE if user is the owner, VARIANT_FALSE otherwise
Return:
S_OK or error code
--*/
{
HRESULT hr = S_OK;
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_SHARE);
if( pbUserIsOwner == NULL )
{
hr = E_POINTER;
goto CLEANUPANDEXIT;
}
if( bstrSID == NULL || 0 == SysStringLen(bstrSID) )
{
hr = E_INVALIDARG;
goto CLEANUPANDEXIT;
}
*pbUserIsOwner = ( IsEqualSid(CComBSTR(bstrSID)) ) ? VARIANT_TRUE : VARIANT_FALSE;
CLEANUPANDEXIT:
return hr;
}
STDMETHODIMP
CRemoteDesktopHelpSession::get_AllowToGetHelp(
/*[out, retval]*/ BOOL* pVal
)
/*++
Routine Description:
Determine if user created this help session is
allowed to get help or not, this is possible that policy change
after user re-logon.
Parameters:
pVal : Pointer to BOOL to receive result.
Returns:
S_OK
HRESULT_FROM_WIN32( ERROR_VC_DISCONNECTED ) User is not connected any more.
E_UNEXPECTED; Internal error.
--*/
{
HRESULT hRes = S_OK;
//
// Acquire share access to helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_SHARE);
if( NULL == pVal )
{
hRes = E_POINTER;
}
else if( FALSE == IsSessionValid() )
{
hRes = E_HANDLE;
}
else if( NULL != m_pHelpSession )
{
if( UNKNOWN_LOGONID == m_ulLogonId )
{
hRes = HRESULT_FROM_WIN32( ERROR_VC_DISCONNECTED );
}
else if( m_pHelpSession->m_UserSID->Length() == 0 )
{
hRes = E_UNEXPECTED;
MYASSERT(FALSE);
}
else
{
*pVal = IsUserAllowToGetHelp(
m_ulLogonId,
(CComBSTR)m_pHelpSession->m_UserSID
);
}
}
else
{
hRes = E_UNEXPECTED;
MYASSERT(FALSE);
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::DeleteHelp()
/*++
Routine Description:
Delete this help session
Parameters:
None.
Returns:
S_OK or error code from
Help Session Manager's DeleteHelpSession().
--*/
{
HRESULT hRes = S_OK;
LPTSTR eventString[2];
BSTR pszNoviceDomain = NULL;
BSTR pszNoviceName = NULL;
HRESULT hr;
CRemoteDesktopHelpSessionMgr::LockIDToSessionMapCache();
//
// Exclusive lock on helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_EXCLUSIVE);
if( FALSE == IsSessionValid() )
{
hRes = E_HANDLE;
}
else if( NULL != m_pHelpSession )
{
DebugPrintf(
_TEXT("CRemoteDesktopHelpSession::DeleteHelp() %s...\n"),
m_bstrHelpSessionId
);
//
// BUGBUG : Refer to timing bug, no notification about terminating
// shadow, and we can't reset session's shadow class. force a
// reset here for now.
//
ResetSessionRDSSetting();
//
// Log the event indicate that ticket was deleted, non-critical
// since we can still continue to run.
//
hr = ConvertSidToAccountName(
(CComBSTR) m_pHelpSession->m_UserSID,
&pszNoviceDomain,
&pszNoviceName
);
if( SUCCEEDED(hr) )
{
eventString[0] = pszNoviceDomain;
eventString[1] = pszNoviceName;
LogRemoteAssistanceEventString(
EVENTLOG_INFORMATION_TYPE,
SESSMGR_I_REMOTEASSISTANCE_DELETEDTICKET,
2,
eventString
);
}
//
// if we are not been help, just delete it, if we are in the middle
// of help, deleting it from cache and database entry will cause
// Resolver's OnDisconnect() never got call resulting in user
// lock in resolver never got release so we update the expiration
// date to current, expiration thread or next load will trigger
// actual delete.
//
if(GetHelperSessionId() == UNKNOWN_LOGONID)
{
CRemoteDesktopHelpSessionMgr::DeleteHelpSessionFromCache( (CComBSTR) m_pHelpSession->m_SessionId );
if( (DWORD)(long)m_pHelpSession->m_ICSPort > 0 )
{
CCriticalSectionLocker ICSLock(g_ICSLibLock);
//
// Destructor does not close ICS port, we only close
// ICS port when help is deleted.
//
ClosePort( (DWORD)(long)m_pHelpSession->m_ICSPort );
}
// Delete will release the entry ref. count
hRes = m_pHelpSession->Delete();
m_pHelpSession = NULL;
m_bDeleted = TRUE;
}
else
{
put_TimeOut(0);
}
}
else
{
hRes = E_UNEXPECTED;
MYASSERT(FALSE);
}
CRemoteDesktopHelpSessionMgr::UnlockIDToSessionMapCache();
if( pszNoviceDomain )
{
SysFreeString( pszNoviceDomain );
}
if( pszNoviceName )
{
SysFreeString( pszNoviceName );
}
return hRes;
}
void
CRemoteDesktopHelpSession::ResolveTicketOwner()
/*++
Description:
Convert ticket owner SID to domain\account.
Parameters:
None.
Returns:
--*/
{
//LPTSTR pszNoviceDomain = NULL;
//LPTSTR pszNoviceName = NULL;
BSTR pszNoviceDomain = NULL;
BSTR pszNoviceName = NULL;
HRESULT hRes = S_OK;
MYASSERT( IsSessionValid() );
if( IsSessionValid() )
{
hRes = ConvertSidToAccountName(
(CComBSTR)m_pHelpSession->m_UserSID,
&pszNoviceDomain,
&pszNoviceName
);
}
else
{
// help session ticket is deleted
hRes = E_HANDLE;
}
//
// NO ASSERT, ConvertSidToAccountName() already assert.
//
if( SUCCEEDED(hRes) )
{
//
// DO NOT FREE the memory, once string is attached to CComBSTR,
// CComBSTR will free it at destructor
//
m_EventLogInfo.bstrNoviceDomain.Attach(pszNoviceDomain);
m_EventLogInfo.bstrNoviceAccount.Attach(pszNoviceName);
//m_EventLogInfo.bstrNoviceDomain = pszNoviceDomain;
//m_EventLogInfo.bstrNoviceAccount = pszNoviceName;
//LocalFree(pszNoviceDomain);
//LocalFree(pszNoviceName);
}
else
{
m_EventLogInfo.bstrNoviceDomain = g_UnknownString;
m_EventLogInfo.bstrNoviceAccount = (CComBSTR)m_pHelpSession->m_UserSID;
}
return;
}
void
CRemoteDesktopHelpSession::ResolveHelperInformation(
IN ULONG HelperSessionId,
OUT CComBSTR& bstrExpertIpAddressFromClient,
OUT CComBSTR& bstrExpertIpAddressFromServer
)
/*++
Description:
Retrieve from TermSrv regarding HelpAssistant session's IP address send from
expert (mstscax send this) and IP address of client machine retrive from TCPIP
Parameters:
HelperSessionId : TS session ID of help assistant session.
bstrExpertIpAddressFromClient : IP address send from mstscax.
bstrExpertIpAddressFromServer : IP address that TS retrieve from tcpip stack.
Returns:
--*/
{
HRESULT hRes = S_OK;
WINSTATIONCLIENT winstationClient;
WINSTATIONREMOTEADDRESS winstationRemoteAddress;
ULONG winstationInfoLen;
DWORD dwLength = 0;
DWORD dwStatus = ERROR_SUCCESS;
//
// Retrieve client IP address passed from client
winstationInfoLen = 0;
ZeroMemory( &winstationClient, sizeof(winstationClient) );
if(!WinStationQueryInformation(
SERVERNAME_CURRENT,
HelperSessionId,
WinStationClient,
(PVOID)&winstationClient,
sizeof(winstationClient),
&winstationInfoLen
))
{
dwStatus = GetLastError();
DebugPrintf(
_TEXT("WinStationQueryInformation() query WinStationClient return %d\n"), dwStatus
);
// Critical error?, fro now, log as 'unknown'.
bstrExpertIpAddressFromClient = g_UnknownString;
}
else
{
bstrExpertIpAddressFromClient = winstationClient.ClientAddress;
}
//
// Retrieve client IP address retrieve from server TCPIP
winstationInfoLen = 0;
ZeroMemory( &winstationRemoteAddress, sizeof(winstationRemoteAddress) );
if(!WinStationQueryInformation(
SERVERNAME_CURRENT,
HelperSessionId,
WinStationRemoteAddress,
(PVOID)&winstationRemoteAddress,
sizeof(winstationRemoteAddress),
&winstationInfoLen
))
{
dwStatus = GetLastError();
DebugPrintf(
_TEXT("WinStationQueryInformation() query WinStationRemoteAddress return %d %d %d\n"),
dwStatus,
sizeof(winstationRemoteAddress),
winstationInfoLen
);
// Critical error?, for now, log as 'unknown'.
bstrExpertIpAddressFromServer = g_UnknownString;
}
else
{
if( AF_INET == winstationRemoteAddress.sin_family )
{
// refer to in_addr structure.
struct in_addr S;
S.S_un.S_addr = winstationRemoteAddress.ipv4.in_addr;
bstrExpertIpAddressFromServer = inet_ntoa(S);
if(bstrExpertIpAddressFromServer.Length() == 0 )
{
MYASSERT(FALSE);
bstrExpertIpAddressFromServer = g_UnknownString;
}
}
else
{
// we are not yet support IPV6 address, calling WSAAddressToString() will fail with error.
bstrExpertIpAddressFromServer = g_UnknownString;
}
}
CLEANUPANDEXIT:
return;
}
STDMETHODIMP
CRemoteDesktopHelpSession::ResolveUserSession(
IN BSTR resolverBlob,
IN BSTR expertBlob,
LONG CallerProcessId,
OUT ULONG_PTR* phHelpCtr,
OUT LONG* pResolverErrCode,
OUT long* plUserSession
)
/*++
Routine Description:
Resolve a user help session to user TS session.
Parameters:
plUserSession : Pointer to long to receive user TS session.
Returns:
S_OK
HRESULT_FROM_WIN32( ERROR_NO_ASSOCIATION ) No resolver for this help session
HRESULT_FROM_WIN32( ERROR_INVALID_DATA ) Can't convert
result from CoCreateInstance() or IRDSCallback
-*/
{
HRESULT hRes = S_OK;
UUID ResolverUuid;
RPC_STATUS rpcStatus;
ISAFRemoteDesktopCallback* pIResolver;
long sessionId;
long HelperSessionId;
int resolverRetCode;
WINSTATIONINFORMATION HelperWinstationInfo;
DWORD dwStatus;
ULONG winstationInfoLen;
CComBSTR bstrExpertAddressFromClient;
CComBSTR bstrExpertAddressFromTSServer;
//
// Exclusive lock on helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker lock(m_HelpSessionLock, RESOURCELOCK_EXCLUSIVE);
DWORD dwEventLogCode;
if( FALSE == IsSessionValid() )
{
hRes = E_HANDLE;
*pResolverErrCode = SAFERROR_HELPSESSIONEXPIRED;
return hRes;
}
if( NULL == m_pHelpSession || NULL == pResolverErrCode )
{
hRes = E_POINTER;
*pResolverErrCode = SAFERROR_INVALIDPARAMETERSTRING;
MYASSERT(FALSE);
return hRes;
}
if( m_pHelpSession->m_UserSID->Length() == 0 )
{
hRes = E_UNEXPECTED;
*pResolverErrCode = SAFERROR_UNKNOWNSESSMGRERROR;
goto CLEANUPANDEXIT;
}
//
// must have user logon ID if we are not using resolver,
// in pure SALEM SDK, multiple expert can connect
// using same help ticket, only one can shadow.
//
*pResolverErrCode = SAFERROR_NOERROR;
if( (long)m_pHelpSession->m_EnableResolver == 0 )
{
if( UNKNOWN_LOGONID != m_ulLogonId )
{
*plUserSession = (long) m_ulLogonId;
}
else
{
// no resolver for this help session
hRes = HRESULT_FROM_WIN32( ERROR_NO_ASSOCIATION );
// user already logoff
*pResolverErrCode = SAFERROR_USERNOTFOUND;
}
//
// We are not using resolver, bail out.
//
goto CLEANUPANDEXIT;
}
//
// Retrieve Caller's TS session ID
//
hRes = ImpersonateClient();
if( FAILED(hRes) )
{
*pResolverErrCode = SAFERROR_UNKNOWNSESSMGRERROR;
return hRes;
}
HelperSessionId = GetUserTSLogonId();
EndImpersonateClient();
ResolveHelperInformation(
HelperSessionId,
bstrExpertAddressFromClient,
bstrExpertAddressFromTSServer
);
DebugPrintf(
_TEXT("Expert Session ID %d, Expert Address %s %s\n"),
HelperSessionId,
bstrExpertAddressFromClient,
bstrExpertAddressFromTSServer
);
DebugPrintf(
_TEXT("Novice %s %s\n"),
m_EventLogInfo.bstrNoviceDomain,
m_EventLogInfo.bstrNoviceAccount
);
//
// Check if helper session is still active, under stress, we might
// get this call after help assistant session is gone.
//
ZeroMemory( &HelperWinstationInfo, sizeof(HelperWinstationInfo) );
winstationInfoLen = 0;
if(!WinStationQueryInformation(
SERVERNAME_CURRENT,
HelperSessionId,
WinStationInformation,
(PVOID)&HelperWinstationInfo,
sizeof(HelperWinstationInfo),
&winstationInfoLen
))
{
dwStatus = GetLastError();
DebugPrintf(
_TEXT("WinStationQueryInformation() return %d\n"), dwStatus
);
hRes = HRESULT_FROM_WIN32( dwStatus );
*pResolverErrCode = SAFERROR_SESSIONNOTCONNECTED;
goto CLEANUPANDEXIT;
}
if( HelperWinstationInfo.ConnectState != State_Active )
{
DebugPrintf(
_TEXT("Helper session is %d"),
HelperWinstationInfo.ConnectState
);
// Helper Session is not active, can't provide help
hRes = HRESULT_FROM_WIN32( ERROR_NO_ASSOCIATION );
*pResolverErrCode = SAFERROR_SESSIONNOTCONNECTED;
goto CLEANUPANDEXIT;
}
//
// Either resolver is pending or already in progress,
// we have exclusive lock so we are safe to reference
// m_hExpertDisconnect.
//
if( UNKNOWN_LOGONID != m_ulHelperSessionId )
{
//
// LOGEVENT : SESSMGR_I_REMOTEASSISTANCE_USERALREADYHELP
//
_Module.LogSessmgrEventLog(
EVENTLOG_INFORMATION_TYPE,
SESSMGR_I_REMOTEASSISTANCE_USERALREADYHELP,
m_EventLogInfo.bstrNoviceDomain,
m_EventLogInfo.bstrNoviceAccount,
(IsUnsolicitedHelp())? g_URAString : g_RAString,
bstrExpertAddressFromClient,
bstrExpertAddressFromTSServer,
SAFERROR_HELPEEALREADYBEINGHELPED
);
*pResolverErrCode = SAFERROR_HELPEEALREADYBEINGHELPED;
hRes = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
goto CLEANUPANDEXIT;
}
//
// We assume User is going to accept help
// 1) When expert disconnect before user accept/deny request,
// our service logoff notification can find this object and invoke
// OnDisconnect() into resolver.
// 2) If another expert connect with same ticket and resolver still
// pending response from user, we can bail out right away.
//
InterlockedExchange( (LPLONG)&m_ulHelperSessionId, (LONG)HelperSessionId );
//
// Cache the SID in object
//
m_HelpSessionOwnerSid = (CComBSTR)m_pHelpSession->m_UserSID;
//
// Load resolver
hRes = LoadSessionResolver( &pIResolver );
if( SUCCEEDED(hRes) )
{
CComBSTR bstrResolverBlob;
bstrResolverBlob.Attach(resolverBlob);
sessionId = (long)m_ulLogonId;
DebugPrintf(
_TEXT("User Session ID %d\n"),
m_ulLogonId
);
//
// keep a copy of blob, we need this to send to resolver on
// disconnect, note, caller can pass its blob so we need to
// keep a copy of it.
//
if( bstrResolverBlob.Length() == 0 )
{
m_ResolverConnectBlob = (CComBSTR)m_pHelpSession->m_SessResolverBlob;
}
else
{
m_ResolverConnectBlob = bstrResolverBlob;
}
//
// We don't need to hold exclusive lock while waiting for resolver to
// return
//
lock.ConvertToShareLock();
hRes = pIResolver->ResolveUserSessionID(
m_ResolverConnectBlob,
(CComBSTR)m_pHelpSession->m_UserSID,
expertBlob,
(CComBSTR)m_pHelpSession->m_SessionCreateBlob,
(ULONG_PTR)g_hServiceShutdown,
&sessionId,
CallerProcessId,
phHelpCtr,
&resolverRetCode
);
//
// Get an exclusive lock since we are modifying internal data
//
lock.ConvertToExclusiveLock();
*pResolverErrCode = resolverRetCode;
bstrResolverBlob.Detach();
pIResolver->Release();
DebugPrintf(
_TEXT("Resolver returns 0x%08x\n"),
hRes
);
if( SUCCEEDED(hRes) )
{
*plUserSession = sessionId;
//
// Update session ID, take the value return from Resolver.
//
m_ulLogonId = sessionId;
//
// Add this expert to logoff monitor list, when expert session's
// rdsaddin terminates, we will inform resolver, reason for this
// is TS might not notify us of expert session disconnect because
// some system component popup a dialog in help assistant session
// and termsrv has no other way but to terminate entire session.
//
dwStatus = MonitorExpertLogoff(
CallerProcessId,
HelperSessionId,
m_bstrHelpSessionId
);
if( ERROR_SUCCESS != dwStatus )
{
//
// If we can't add to resolver list, we immediate notify
// resolver and return error or we will run into 'helpee
// already been help problem
//
DebugPrintf(
_TEXT("MonitorExpertLogoff() failed with %d\n"), dwStatus
);
// directly invoke resolver here.
hRes = pIResolver->OnDisconnect(
m_ResolverConnectBlob,
m_HelpSessionOwnerSid,
m_ulLogonId
);
MYASSERT( SUCCEEDED(hRes) );
resolverRetCode = SAFERROR_UNKNOWNSESSMGRERROR;
// SECURITY: Return error code or RA connection will continue on.
*pResolverErrCode = resolverRetCode;
InterlockedExchange( (LPLONG)&m_ulHelperSessionId, (LONG)UNKNOWN_LOGONID );
}
else
{
//
// It is possible for caller to close all reference counter to our object
// and cause a release of our object, if SCM notification comes in after
// our object is deleted from cache, SCM will reload from database entry
// and that does not have helper session ID and will not invoke NotifyDisconnect().
//
AddRef();
}
}
else
{
//
// User does not accept help from this helpassistant session,
// reset HelpAssistant session ID
//
InterlockedExchange( (LPLONG)&m_ulHelperSessionId, (LONG)UNKNOWN_LOGONID );
}
switch( resolverRetCode )
{
case SAFERROR_NOERROR :
// LOGEVENT : SESSMGR_I_REMOTEASSISTANCE_BEGIN
//
// Cache event log info so we don't have to retrieve it again.
//
m_EventLogInfo.bstrExpertIpAddressFromClient = bstrExpertAddressFromClient;
m_EventLogInfo.bstrExpertIpAddressFromServer = bstrExpertAddressFromTSServer;
dwEventLogCode = SESSMGR_I_REMOTEASSISTANCE_BEGIN;
break;
case SAFERROR_HELPEECONSIDERINGHELP:
case SAFERROR_HELPEEALREADYBEINGHELPED:
// LOGEVENT : SESSMGR_I_REMOTEASSISTANCE_USERALREADYHELP
dwEventLogCode = SESSMGR_I_REMOTEASSISTANCE_USERALREADYHELP;
break;
case SAFERROR_HELPEENOTFOUND:
// LOGEVENT : SESSMGR_I_REMOTEASSISTANCE_INACTIVEUSER
dwEventLogCode = SESSMGR_I_REMOTEASSISTANCE_INACTIVEUSER;
break;
case SAFERROR_HELPEENEVERRESPONDED:
// LOGEVENT : SESSMGR_I_REMOTEASSISTANCE_TIMEOUT
dwEventLogCode = SESSMGR_I_REMOTEASSISTANCE_TIMEOUT;
break;
case SAFERROR_HELPEESAIDNO:
// LOGEVENT : SESSMGR_I_REMOTEASSISTANCE_USERREJECT
dwEventLogCode = SESSMGR_I_REMOTEASSISTANCE_USERREJECT;
break;
default:
// LOGEVENT : SESSMGR_I_REMOTEASSISTANCE_UNKNOWNRESOLVERERRORCODE
dwEventLogCode = SESSMGR_I_REMOTEASSISTANCE_UNKNOWNRESOLVERERRORCODE;
break;
}
_Module.LogSessmgrEventLog(
EVENTLOG_INFORMATION_TYPE,
dwEventLogCode,
m_EventLogInfo.bstrNoviceDomain,
m_EventLogInfo.bstrNoviceAccount,
(IsUnsolicitedHelp())? g_URAString : g_RAString,
bstrExpertAddressFromClient,
bstrExpertAddressFromTSServer,
resolverRetCode
);
}
else
{
*pResolverErrCode = SAFERROR_CANTOPENRESOLVER;
//
// We havn't inform resolver yet so mark ticket not been help.
//
InterlockedExchange( (LPLONG)&m_ulHelperSessionId, (LONG)UNKNOWN_LOGONID );
}
CLEANUPANDEXIT:
DebugPrintf(
_TEXT("ResolverUserSession returns 0x%08x\n"),
hRes
);
return hRes;
}
HRESULT
CRemoteDesktopHelpSession::NotifyDisconnect()
/*++
Routine Description:
Notify Session Resolver that client is dis-connecting to help session.
Parameters:
bstrBlob : Blob to be passed to resolver, NULL if
use ResolverBlob property.
Returns:
E_HANDLE Invalid session, database entry has been deleted but refcount > 0
E_UNEXPECTED Internal error
HRESULT_FROM_WIN32( ERROR_VC_DISCONNECTED ) Client disconnected
HRESULT_FROM_WIN32( ERROR_NO_ASSOCIATION ) No Resolver
S_FALSE No Resolver
HRESULT_FROM_WIN32( ERROR_INVALID_DATA ) Invalid Resolver ID
Error code from CoCreateInstance() and resolver's OnConnect() method.
--*/
{
HRESULT hRes = S_OK;
ISAFRemoteDesktopCallback* pIResolver;
DebugPrintf(
_TEXT("OnDisconnect() - Helper Session ID %d\n"),
m_ulHelperSessionId
);
//
// Exclusive lock on helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker lock(m_HelpSessionLock, RESOURCELOCK_EXCLUSIVE);
//
// If we are not been help, just bail out.
//
if( UNKNOWN_LOGONID != m_ulHelperSessionId )
{
//
// LOGEVENT : SESSMGR_I_REMOTEASSISTANCE_END
//
//
// always cache help session creator at ResolveUserSession()
// so this value can't be empty.
//
MYASSERT( m_HelpSessionOwnerSid.Length() > 0 );
MYASSERT( m_ResolverConnectBlob.Length() > 0 );
if( m_HelpSessionOwnerSid.Length() == 0 ||
m_ResolverConnectBlob.Length() == 0 )
{
MYASSERT(FALSE);
hRes = E_UNEXPECTED;
goto CLEANUPANDEXIT;
}
//
// Load resolver
hRes = LoadSessionResolver( &pIResolver );
MYASSERT( SUCCEEDED(hRes) );
if( SUCCEEDED(hRes) )
{
DebugPrintf(
_TEXT("OnDisconnect() - Notify Resolver, %s\n%s\n%d\n"),
m_ResolverConnectBlob,
m_HelpSessionOwnerSid,
m_ulLogonId
);
hRes = pIResolver->OnDisconnect(
m_ResolverConnectBlob,
m_HelpSessionOwnerSid,
m_ulLogonId
);
pIResolver->Release();
m_ResolverConnectBlob.Empty();
m_HelpSessionOwnerSid.Empty();
InterlockedExchange( (LPLONG)&m_ulHelperSessionId, (LONG)UNKNOWN_LOGONID );
//
// It is possible for caller to close all reference counter to our object
// and cause a release of our object, if SCM notification comes in after
// our object is deleted from cache, SCM will reload from database entry
// and that does not have helper session ID and will not invoke NotifyDisconnect().
//
Release();
_Module.LogSessmgrEventLog(
EVENTLOG_INFORMATION_TYPE,
SESSMGR_I_REMOTEASSISTANCE_END,
m_EventLogInfo.bstrNoviceDomain,
m_EventLogInfo.bstrNoviceAccount,
(IsUnsolicitedHelp())? g_URAString : g_RAString,
m_EventLogInfo.bstrExpertIpAddressFromClient,
m_EventLogInfo.bstrExpertIpAddressFromServer,
ERROR_SUCCESS
);
}
}
CLEANUPANDEXIT:
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::EnableUserSessionRdsSetting(
IN BOOL bEnable
)
/*++
Routine Description:
Enable/restore user session shadow setting.
--*/
{
HRESULT hRes = S_OK;
//
// Exclusive lock on helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_EXCLUSIVE);
if( FALSE == IsSessionValid() )
{
hRes = E_HANDLE;
}
else
{
if( TRUE == bEnable )
{
hRes = ActivateSessionRDSSetting();
}
else
{
hRes = ResetSessionRDSSetting();
}
}
return hRes;
}
HRESULT
CRemoteDesktopHelpSession::ActivateSessionRDSSetting()
{
HRESULT hRes = S_OK;
DWORD dwStatus;
REMOTE_DESKTOP_SHARING_CLASS userRDSDefault;
BOOL bAllowToGetHelp;
MYASSERT( TRUE == IsSessionValid() );
//
// check if help session user is logon
//
if( UNKNOWN_LOGONID == m_ulLogonId )
{
hRes = HRESULT_FROM_WIN32( ERROR_VC_DISCONNECTED );
goto CLEANUPANDEXIT;
}
//
// Make sure user can get help, this is possible since
// policy might change after user re-logon to help session
//
hRes = get_AllowToGetHelp( &bAllowToGetHelp );
if( FAILED(hRes) )
{
goto CLEANUPANDEXIT;
}
if( FALSE == bAllowToGetHelp )
{
hRes = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
goto CLEANUPANDEXIT;
}
//
// DCR 681451 - Shadow is disabled by default on console session.
// Change functionality, if we enforce no shadow, RA won't work on console session.
//
dwStatus = ConfigUserSessionRDSLevel( m_ulLogonId, m_pHelpSession->m_SessionRdsSetting );
hRes = HRESULT_FROM_WIN32( dwStatus );
DebugPrintf(
_TEXT("ConfigUserSessionRDSLevel to %d returns 0x%08x\n"),
(DWORD)m_pHelpSession->m_SessionRdsSetting,
hRes
);
CLEANUPANDEXIT:
return hRes;
}
HRESULT
CRemoteDesktopHelpSession::ResetSessionRDSSetting()
{
HRESULT hRes = S_OK;
MYASSERT( TRUE == IsSessionValid() );
//
// check if user is log on
//
if( UNKNOWN_LOGONID == m_ulLogonId )
{
hRes = HRESULT_FROM_WIN32( ERROR_VC_DISCONNECTED );
}
//
// We don't do anything since TermSrv will reset shadow
// config back to original value if shadower is help
// assistant.
//
CLEANUPANDEXIT:
return hRes;
}
///////////////////////////////////////////////////////////////
//
// Private Function
//
HRESULT
CRemoteDesktopHelpSession::put_UserLogonId(
IN long newVal
)
/*++
Routine Description:
Set user TS session for current Help Session
Parameters:
newVal : New TS user session
Returns:
S_OK
--*/
{
HRESULT hRes = S_OK;
//
// Exclusive lock on helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker lock(m_HelpSessionLock, RESOURCELOCK_EXCLUSIVE);
if( FALSE == IsSessionValid() )
{
hRes = E_HANDLE;
}
else if( NULL != m_pHelpSession )
{
//MYASSERT( UNKNOWN_LOGONID == m_ulLogonId );
//
// User TS session ID is not persisted to registry
//
m_ulLogonId = newVal;
}
else
{
hRes = E_UNEXPECTED;
}
// private routine, assert if failed
MYASSERT( SUCCEEDED(hRes) );
return hRes;
}
BOOL
CRemoteDesktopHelpSession::IsEqualSid(
IN const CComBSTR& bstrSid
)
/*++
Routine Description:
Compare user's SID.
Parameters:
bstrSid : SID to be compared.
Returns:
TRUE/FALSE
--*/
{
//
// Acquire share access to helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_SHARE);
if( NULL == m_pHelpSession )
{
MYASSERT(FALSE);
return FALSE;
}
return (TRUE == IsSessionValid()) ? ((CComBSTR)m_pHelpSession->m_UserSID == bstrSid) : FALSE;
}
BOOL
CRemoteDesktopHelpSession::VerifyUserSession(
IN const CComBSTR& bstrUserSid,
IN const CComBSTR& bstrSessPwd
)
/*++
Routine Description:
Verify user help session password.
Parameters:
bstrUserSid : calling client's user SID.
bstrSessName : Help session name, not use currently.
bstrSessPwd : Help Session Password to be verified.
Returns:
TRUE/FALSE
--*/
{
LPWSTR bstrDecodePwd = NULL;
DWORD dwStatus;
BOOL bReturn = FALSE;
//
// Acquire share access to helpsession object, CResourceLocker constructor
// will wait indefinately for the resource and will release resource
// at destructor time.
//
CResourceLocker l(m_HelpSessionLock, RESOURCELOCK_SHARE);
if( NULL == m_pHelpSession )
{
MYASSERT(FALSE);
goto CLEANUPANDEXIT;
}
#if DISABLESECURITYCHECKS
if( (CComBSTR)m_pHelpSession->m_SessionName == HELPSESSION_UNSOLICATED )
{
// use console session
m_ulLogonId = 0;
}
#endif
//
// Object can only exists when helpsessionmgr object can create us
// via loading from registry or create a new one, so we only need to
// verify if ticket has been deleted.
//
//
// Whistler server B3, no more checking on help session password,
// help session ID is the only security check.
//
if( FALSE == IsSessionValid() )
{
// Help Session is invalid
goto CLEANUPANDEXIT;
}
bReturn = TRUE;
CLEANUPANDEXIT:
if( NULL != bstrDecodePwd )
{
LocalFree( bstrDecodePwd );
}
return bReturn;
}
HRESULT
CRemoteDesktopHelpSession::InitInstance(
IN CRemoteDesktopHelpSessionMgr* pMgr,
IN CComBSTR& bstrClientSid,
IN PHELPENTRY pHelpEntry
)
/*++
Routine Description:
Initialize a CRemoteDesktopHelpSession object.
Parameters:
Returns:
S_OK
--*/
{
HRESULT hRes = S_OK;
if( NULL != pHelpEntry )
{
m_pSessMgr = pMgr;
m_pHelpSession = pHelpEntry;
m_bstrClientSid = bstrClientSid;
m_bstrHelpSessionId = pHelpEntry->m_SessionId;
}
else
{
hRes = HRESULT_FROM_WIN32( ERROR_INTERNAL_ERROR );
MYASSERT( SUCCEEDED(hRes) );
}
return hRes;
}
HRESULT
CRemoteDesktopHelpSession::CreateInstance(
IN CRemoteDesktopHelpSessionMgr* pMgr,
IN CComBSTR& bstrClientSid,
IN PHELPENTRY pHelpEntry,
OUT RemoteDesktopHelpSessionObj** ppRemoteDesktopHelpSession
)
/*++
Routine Description:
Create an instance of help session.
Parameters:
pMgr : Pointer to help session manager object.
ppRemoteDesktopHelpSession : Return a pointer to help session instance.
Returns:
S_OK
E_OUTOFMEMORY
Error code in impersonating client
--*/
{
HRESULT hRes = S_OK;
RemoteDesktopHelpSessionObj* p = NULL;
try
{
hRes = RemoteDesktopHelpSessionObj::CreateInstance( &p );
if( SUCCEEDED(hRes) )
{
hRes = p->InitInstance(
pMgr,
bstrClientSid,
pHelpEntry
);
if( SUCCEEDED(hRes) )
{
p->AddRef();
*ppRemoteDesktopHelpSession = p;
}
else
{
p->Release();
}
}
}
catch( ... )
{
hRes = E_OUTOFMEMORY;
}
return hRes;
}
HRESULT
CRemoteDesktopHelpSession::BeginUpdate()
{
HRESULT hRes;
MYASSERT( NULL != m_pHelpSession );
if( NULL != m_pHelpSession )
{
hRes = m_pHelpSession->BeginUpdate();
}
else
{
hRes = E_UNEXPECTED;
}
return hRes;
}
HRESULT
CRemoteDesktopHelpSession::CommitUpdate()
{
HRESULT hRes;
//
// Update all entries.
//
MYASSERT( NULL != m_pHelpSession );
if( NULL != m_pHelpSession )
{
hRes = m_pHelpSession->CommitUpdate();
}
else
{
hRes = E_UNEXPECTED;
}
return hRes;
}
HRESULT
CRemoteDesktopHelpSession::AbortUpdate()
{
HRESULT hRes;
//
// Update all entries.
//
MYASSERT( NULL != m_pHelpSession );
if( NULL != m_pHelpSession )
{
hRes = m_pHelpSession->AbortUpdate();
}
else
{
hRes = E_UNEXPECTED;
}
return hRes;
}
BOOL
CRemoteDesktopHelpSession::IsHelpSessionExpired()
{
MYASSERT( NULL != m_pHelpSession );
return (NULL != m_pHelpSession) ? m_pHelpSession->IsEntryExpired() : TRUE;
}
BOOL
CRemoteDesktopHelpSession::IsClientSessionCreator()
{
BOOL bStatus;
//
// 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;
if( m_pHelpSession )
{
bStatus = (/* (CComBSTR) */m_pHelpSession->m_UserSID == m_bstrClientSid);
if( FALSE == bStatus )
{
bStatus = (m_pHelpSession->m_UserSID == g_LocalSystemSID);
}
}
else
{
bStatus = FALSE;
}
return bStatus;
}