/*++ 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 #include #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 #include "RemoteDesktopUtils.h" #include "RemoteDesktop.h" #include ///////////////////////////////////////////////////////////////////////////// // // 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; }