windows-nt/Source/XPSP1/NT/termsrv/remdsk/server/sessmgr/helpsess.cpp
2020-09-26 16:20:57 +08:00

2828 lines
61 KiB
C++

/*++
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>
/////////////////////////////////////////////////////////////////////////////
HRESULT
ConvertSidToAccountName(
IN CComBSTR& SidString,
IN BSTR* ppszDomain,
IN BSTR* ppszUserAcc
//IN LPTSTR* ppszDomain,
//IN LPTSTR* ppszUserAcc
)
/*++
Description:
Convert a string SID to domain\account.
Parameters:
ownerSidString : SID in string form to be converted.
ppszDomain : Pointer to string pointer to receive domain name
UserAcc : Pointer to string pointer to receive user name
Returns:
S_OK or error code.
Note:
Routine uses LocalAlloc() to allocate memory for ppszDomain
and ppszUserAcc.
--*/
{
DWORD dwStatus = ERROR_SUCCESS;
PSID pOwnerSid = NULL;
//LPTSTR pszAccName = NULL;
BSTR pszAccName = NULL;
DWORD cbAccName = 0;
//LPTSTR pszDomainName = NULL;
BSTR pszDomainName = NULL;
DWORD cbDomainName = 0;
SID_NAME_USE SidType;
BOOL bSuccess;
//
// Convert string form SID to PSID
//
if( FALSE == ConvertStringSidToSid( (LPCTSTR)SidString, &pOwnerSid ) )
{
// this might also fail if system is in shutdown state.
dwStatus = GetLastError();
goto CLEANUPANDEXIT;
}
if( NULL == ppszDomain || NULL == ppszUserAcc )
{
dwStatus = ERROR_INVALID_PARAMETER;
MYASSERT( FALSE );
goto CLEANUPANDEXIT;
}
//
// Lookup user account for this SID
//
bSuccess = LookupAccountSid(
NULL,
pOwnerSid,
pszAccName,
&cbAccName,
pszDomainName,
&cbDomainName,
&SidType
);
if( TRUE == bSuccess || ERROR_INSUFFICIENT_BUFFER == GetLastError() )
{
//pszAccName = (LPWSTR) LocalAlloc( LPTR, (cbAccName + 1) * sizeof(WCHAR) );
//pszDomainName = (LPWSTR) LocalAlloc( LPTR, (cbDomainName + 1)* sizeof(WCHAR) );
pszAccName = ::SysAllocStringLen( NULL, (cbAccName + 1) );
pszDomainName = ::SysAllocStringLen( NULL, (cbDomainName + 1) );
if( NULL != pszAccName && NULL != pszDomainName )
{
bSuccess = LookupAccountSid(
NULL,
pOwnerSid,
pszAccName,
&cbAccName,
pszDomainName,
&cbDomainName,
&SidType
);
}
else
{
SetLastError(ERROR_OUTOFMEMORY);
bSuccess = FALSE;
}
}
if( FALSE == bSuccess )
{
dwStatus = GetLastError();
}
else
{
*ppszDomain = pszDomainName;
*ppszUserAcc = pszAccName;
pszDomainName = NULL;
pszAccName = NULL;
}
CLEANUPANDEXIT:
if( NULL != pOwnerSid )
{
LocalFree( pOwnerSid );
}
if( NULL != pszAccName )
{
//LocalFree( pszAccName );
::SysFreeString( pszAccName );
}
if( NULL != pszDomainName )
{
// LocalFree( pszDomainName );
::SysFreeString( pszAccName );
}
return HRESULT_FROM_WIN32(dwStatus);
}
/////////////////////////////////////////////////////////////////////////////
//
// 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 )
{
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;
CCriticalSectionLocker l(m_HelpSessionLock);
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;
CCriticalSectionLocker l(m_HelpSessionLock);
if( FALSE == IsSessionValid() )
{
hRes = E_HANDLE;
goto CLEANUPANDEXIT;
}
//
// 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 );
MYASSERT( dwNumChars <= dwBufferRequire );
if( dwNumChars <= dwBufferRequire )
{
break;
}
}
if( NULL == pszAddress )
{
hRes = E_UNEXPECTED;
goto CLEANUPANDEXIT;
}
//
// Store the IP address
//
m_pHelpSession->m_IpAddress = pszAddress;
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;
}
MYASSERT( g_TSSecurityBlob.Length() > 0 );
*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
);
#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;
CCriticalSectionLocker l(m_HelpSessionLock);
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;
if((*pTimeout = mktime(&gmTime)) == (time_t)-1)
{
*pTimeout = INT_MAX;
}
}
}
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;
CCriticalSectionLocker l(m_HelpSessionLock);
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;
CCriticalSectionLocker l(m_HelpSessionLock);
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;
CCriticalSectionLocker l(m_HelpSessionLock);
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_HelpSessionName(
OUT BSTR *pVal
)
/*++
Routine Description:
Get help session name associated with this
help session.
Parameters:
pVal : Return help session name associated
with this help session.
Returns:
S_OK
S_FALSE No Help Session Name
E_OUTOFMEMORY Out of memory
E_POINTER Invalid parameter
HRESULT_FROM_WIN32( ERROR_LOCK_VIOLATION );
--*/
{
HRESULT hRes = S_OK;
CCriticalSectionLocker l(m_HelpSessionLock);
if(FALSE == IsSessionValid())
{
hRes = E_HANDLE;
}
else if( NULL == pVal )
{
hRes = E_POINTER;
}
else if( NULL != m_pHelpSession )
{
if( m_pHelpSession->m_SessionName->Length() > 0 )
{
*pVal = m_pHelpSession->m_SessionName->Copy();
if( NULL == *pVal )
{
hRes = E_OUTOFMEMORY;
}
}
else
{
hRes = S_FALSE;
}
}
else
{
hRes = E_UNEXPECTED;
MYASSERT(FALSE);
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::put_HelpSessionName(
IN BSTR newVal
)
/*++
Routine Description:
Set help session name associated with this
help session.
Parameters:
newVal : New help session name.
Returns:
S_OK
E_OUTOFMEMORY
--*/
{
HRESULT hRes = S_OK;
CCriticalSectionLocker l(m_HelpSessionLock);
if(FALSE == IsSessionValid())
{
hRes = E_HANDLE;
}
else if( FALSE == IsClientSessionCreator() )
{
hRes = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
}
else if( m_pHelpSession != NULL )
{
//
// NULL will reset help session name
//
//
// operator =() update registry immediately
//
m_pHelpSession->m_SessionName = newVal;
if( !((CComBSTR)m_pHelpSession->m_SessionName == newVal) )
{
hRes = E_OUTOFMEMORY;
}
}
else
{
MYASSERT(FALSE);
hRes = E_UNEXPECTED;
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::put_HelpSessionPassword(
IN BSTR newVal
)
/*++
Routine Description:
Change help session password associated with this
help session.
Parameters:
newVal : New help session password.
Returns:
S_OK
E_OUTOFMEMORY Out of memory
--*/
{
HRESULT hRes = S_OK;
CCriticalSectionLocker l(m_HelpSessionLock);
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 password
//
//
// operator =() update registry immediately
//
m_pHelpSession->m_SessionPwd = newVal;
if( !((CComBSTR)m_pHelpSession->m_SessionPwd == newVal) )
{
hRes = E_OUTOFMEMORY;
}
}
else
{
hRes = E_UNEXPECTED;
MYASSERT( FALSE );
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::get_HelpSessionDescription(
OUT BSTR *pVal
)
/*++
Routine Description:
Get help session description associated with this
help session.
Parameters:
pVal : Return help session description associated
with this help session.
Returns:
S_OK
S_FALSE No Description
E_OUTOFMEMORY Out of memory
E_POINTER Invalid argument
--*/
{
HRESULT hRes = S_OK;
CCriticalSectionLocker l(m_HelpSessionLock);
if( NULL == pVal )
{
hRes = E_POINTER;
}
else if( FALSE == IsSessionValid() )
{
hRes = E_HANDLE;
}
else if( m_pHelpSession != NULL )
{
if( m_pHelpSession->m_SessionDesc->Length() > 0 )
{
*pVal = m_pHelpSession->m_SessionDesc->Copy();
if( NULL == *pVal )
{
hRes = E_OUTOFMEMORY;
}
}
else
{
hRes = S_FALSE;
}
}
else
{
hRes = E_UNEXPECTED;
MYASSERT(FALSE);
}
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::put_HelpSessionDescription(
IN BSTR newVal
)
/*++
Routine Description:
Change help session description associated with this
help session.
Parameters:
newVal : new help session description associated
with this help session.
Returns:
S_OK
Note:
Help Session name and description is reserved for future use
--*/
{
HRESULT hRes = S_OK;
CCriticalSectionLocker l(m_HelpSessionLock);
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 description
//
//
// operator =() update registry immediately
//
m_pHelpSession->m_SessionDesc = newVal;
if( !((CComBSTR)m_pHelpSession->m_SessionDesc == newVal) )
{
hRes = E_OUTOFMEMORY;
}
}
else
{
hRes = E_UNEXPECTED;
MYASSERT(FALSE);
}
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;
CCriticalSectionLocker l(m_HelpSessionLock);
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;
CCriticalSectionLocker l(m_HelpSessionLock);
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;
CCriticalSectionLocker l(m_HelpSessionLock);
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;
CCriticalSectionLocker l(m_HelpSessionLock);
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;
CCriticalSectionLocker l(m_HelpSessionLock);
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;
CCriticalSectionLocker l(m_HelpSessionLock);
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;
CCriticalSectionLocker l(m_HelpSessionLock);
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;
CCriticalSectionLocker l(m_HelpSessionLock);
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::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;
CCriticalSectionLocker l(m_HelpSessionLock);
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,
(LPCTSTR) CComBSTRtoLPTSTR( (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;
CRemoteDesktopHelpSessionMgr::LockIDToSessionMapCache();
CCriticalSectionLocker l(m_HelpSessionLock);
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();
//
// 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 )
{
//
// 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();
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;
CCriticalSectionLocker l(m_HelpSessionLock);
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;
hRes = CoInitialize( NULL );
if( FAILED(hRes) )
{
// failed to coinitialize,
*pResolverErrCode = SAFERROR_INTERNALERROR;
goto CLEANUPANDEXIT;
}
//
// Load resolver
hRes = LoadResolverFromGIT( &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;
}
hRes = pIResolver->ResolveUserSessionID(
m_ResolverConnectBlob,
(CComBSTR)m_pHelpSession->m_UserSID,
expertBlob,
(CComBSTR)m_pHelpSession->m_SessionCreateBlob,
&sessionId,
CallerProcessId,
phHelpCtr,
&resolverRetCode
);
*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;
}
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;
}
CoUninitialize();
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
);
CCriticalSectionLocker l(m_HelpSessionLock);
//
// 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;
}
hRes = CoInitialize( NULL );
if( FAILED(hRes) )
{
goto CLEANUPANDEXIT;
}
//
// Load resolver
hRes = LoadResolverFromGIT( &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
);
}
CoUninitialize();
}
CLEANUPANDEXIT:
return hRes;
}
STDMETHODIMP
CRemoteDesktopHelpSession::EnableUserSessionRdsSetting(
IN BOOL bEnable
)
/*++
Routine Description:
Enable/restore user session shadow setting.
--*/
{
HRESULT hRes = S_OK;
CCriticalSectionLocker l(m_HelpSessionLock);
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;
}
//
// Retrieve current user session's shadow setting.
//
dwStatus = GetUserRDSLevel( m_ulLogonId, &userRDSDefault );
if( ERROR_SUCCESS != dwStatus )
{
hRes = HRESULT_FROM_WIN32( dwStatus );
goto CLEANUPANDEXIT;
}
if( NO_DESKTOP_SHARING != userRDSDefault )
{
//
// Force reset on user session shadow setting only if
// user session is allowed remote control.
//
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
);
}
else
{
// return S_FALSE and let shadow fail
hRes = S_FALSE;
DebugPrintf( _TEXT("TS User session does not remote control\n") );
}
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;
CCriticalSectionLocker l(m_HelpSessionLock);
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
--*/
{
CCriticalSectionLocker l(m_HelpSessionLock);
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
--*/
{
DWORD dwStatus;
BOOL bReturn = FALSE;
CCriticalSectionLocker l(m_HelpSessionLock);
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
if( FALSE == IsSessionValid() )
{
// Help Session is invalid
goto CLEANUPANDEXIT;
}
bReturn = TRUE;
CLEANUPANDEXIT:
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;
hRes = RemoteDesktopHelpSessionObj::CreateInstance( &p );
if( SUCCEEDED(hRes) )
{
hRes = p->InitInstance(
pMgr,
bstrClientSid,
pHelpEntry
);
if( SUCCEEDED(hRes) )
{
p->AddRef();
*ppRemoteDesktopHelpSession = p;
}
else
{
p->Release();
}
}
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;
}