473 lines
11 KiB
C++
473 lines
11 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 2000
|
|
//
|
|
// File: ausessions.cpp
|
|
//
|
|
// History: 10/19/2001 annah
|
|
// transformed struct in class, added constructors
|
|
// destructors, and also proctected write operations
|
|
// with a critical session (on win2k the code can
|
|
// have race conditions). Also moved some functions
|
|
// from service.cpp to the class.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
#include "pch.h"
|
|
#include "tscompat.h"
|
|
#include "service.h"
|
|
|
|
#pragma hdrstop
|
|
|
|
//SESSION_STATUS Functions
|
|
SESSION_STATUS::SESSION_STATUS (void)
|
|
{
|
|
m_fInitCS = FALSE;
|
|
}
|
|
|
|
SESSION_STATUS::~SESSION_STATUS()
|
|
{
|
|
if (m_fInitCS)
|
|
{
|
|
DeleteCriticalSection(&m_csWrite);
|
|
}
|
|
}
|
|
|
|
BOOL SESSION_STATUS::Initialize(BOOL fUseCriticalSection, BOOL fAllActiveUsers)
|
|
{
|
|
m_fAllActiveUsers = fAllActiveUsers;
|
|
m_pSessionStateInfo = NULL;
|
|
m_iLastSession = -1;
|
|
m_cAllocBufSessions = 0;
|
|
m_iCurSession = CDWNO_SESSION;
|
|
//
|
|
// The code will only execute concurrent paths on win2K
|
|
// It won't be expensive to use th critical session
|
|
// for all platforms, however
|
|
//
|
|
if (fUseCriticalSection)
|
|
{
|
|
if (!m_fInitCS)
|
|
{
|
|
m_fInitCS = SafeInitializeCriticalSection(&m_csWrite);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AUASSERT(!m_fInitCS);
|
|
}
|
|
return (m_fInitCS == fUseCriticalSection);
|
|
}
|
|
|
|
void SESSION_STATUS::Clear(void)
|
|
{
|
|
if (NULL != m_pSessionStateInfo)
|
|
{
|
|
free(m_pSessionStateInfo);
|
|
}
|
|
}
|
|
|
|
// Method used when we want to rebuild the array from scratch
|
|
void SESSION_STATUS::m_EraseAll()
|
|
{
|
|
if (m_fInitCS)
|
|
{
|
|
EnterCriticalSection(&m_csWrite);
|
|
}
|
|
|
|
Clear();
|
|
BOOL fRet = Initialize(m_fInitCS, m_fAllActiveUsers);
|
|
AUASSERT(fRet);
|
|
|
|
if (m_fInitCS)
|
|
{
|
|
LeaveCriticalSection(&m_csWrite);
|
|
}
|
|
}
|
|
|
|
void SESSION_STATUS::RebuildSessionCache()
|
|
{
|
|
if (m_fInitCS)
|
|
{
|
|
EnterCriticalSection(&m_csWrite);
|
|
}
|
|
|
|
m_EraseAll();
|
|
CacheExistingSessions();
|
|
|
|
if (m_fInitCS)
|
|
{
|
|
LeaveCriticalSection(&m_csWrite);
|
|
}
|
|
}
|
|
|
|
BOOL SESSION_STATUS::m_FAddSession(DWORD dwSessionId, SESSION_STATE *pSesState)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
|
|
if (m_fInitCS)
|
|
{
|
|
EnterCriticalSection(&m_csWrite);
|
|
}
|
|
|
|
//
|
|
// Fix for bug 498256 -- annah
|
|
// It can happen that a user logs in right when the service is starting, so
|
|
// we would add the session though the TS enumeration code AND
|
|
// then possibly again through the code that receives SCM logon notifications.
|
|
// The fix will be to test if the session is already stored, and skip
|
|
// adding entries that for session ids that are already there.
|
|
//
|
|
if (m_iFindSession(dwSessionId) != CDWNO_SESSION)
|
|
{
|
|
// do nothing -- don't add duplicate entries if the session id
|
|
// is already there.
|
|
fRet = FALSE;
|
|
goto Done;
|
|
}
|
|
|
|
if (NULL == m_pSessionStateInfo || m_iLastSession >= m_cAllocBufSessions - 1)
|
|
{
|
|
int cSessions;
|
|
|
|
if (m_cAllocBufSessions == 0)
|
|
{
|
|
cSessions = CMIN_SESSIONS;
|
|
m_iCurSession = 0;
|
|
}
|
|
else
|
|
{
|
|
cSessions = m_cAllocBufSessions * 2;
|
|
}
|
|
if (!m_FChangeBufSession(cSessions))
|
|
{
|
|
fRet = FALSE;
|
|
goto Done;
|
|
}
|
|
}
|
|
m_iLastSession++;
|
|
|
|
m_pSessionStateInfo[m_iLastSession].dwSessionId = dwSessionId;
|
|
m_pSessionStateInfo[m_iLastSession].SessionState = *pSesState;
|
|
|
|
Done:
|
|
if (m_fInitCS)
|
|
{
|
|
LeaveCriticalSection(&m_csWrite);
|
|
}
|
|
#ifdef DBG
|
|
DEBUGMSG("After AddSession");
|
|
m_DumpSessions();
|
|
#endif
|
|
return fRet;
|
|
}
|
|
|
|
//determine whether or not session dwSessionId is a cached AU session
|
|
BOOL SESSION_STATUS::m_FGetSessionState(DWORD dwSessionId, SESSION_STATE **pSesState )
|
|
{
|
|
int iSession = m_iFindSession(dwSessionId);
|
|
|
|
BOOL fRet = (CDWNO_SESSION != iSession);
|
|
|
|
if (fRet && (NULL != pSesState))
|
|
{
|
|
*pSesState = &m_pSessionStateInfo[iSession].SessionState;
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
BOOL SESSION_STATUS::m_FDeleteSession(DWORD dwSessionId)
|
|
{
|
|
BOOL fRet= FALSE;
|
|
|
|
if (m_fInitCS)
|
|
{
|
|
EnterCriticalSection(&m_csWrite);
|
|
}
|
|
|
|
int iSession = m_iFindSession(dwSessionId);
|
|
if (CDWNO_SESSION == iSession)
|
|
{
|
|
goto Done;
|
|
}
|
|
if (iSession != m_iLastSession)
|
|
{
|
|
memmove(m_pSessionStateInfo + iSession,
|
|
m_pSessionStateInfo + iSession + 1,
|
|
sizeof(SESSION_STATE_INFO) * (m_iLastSession - iSession));
|
|
}
|
|
if (m_iCurSession > iSession)
|
|
{
|
|
m_iCurSession--;
|
|
}
|
|
//fixcode m_iCurSession should point to the previous session
|
|
if (m_iCurSession == m_iLastSession)
|
|
{
|
|
m_iCurSession = 0;
|
|
}
|
|
m_iLastSession--;
|
|
fRet = TRUE;
|
|
|
|
Done:
|
|
if (m_fInitCS)
|
|
{
|
|
LeaveCriticalSection(&m_csWrite);
|
|
}
|
|
#ifdef DBG
|
|
DEBUGMSG("After DeleteSession");
|
|
m_DumpSessions();
|
|
#endif
|
|
return fRet;
|
|
}
|
|
|
|
BOOL SESSION_STATUS::m_FGetCurrentSession(DWORD *pdwSessionId)
|
|
{
|
|
if (0 == CSessions())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*pdwSessionId = m_pSessionStateInfo[m_iCurSession].dwSessionId;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SESSION_STATUS::m_FGetNextSession(DWORD *pdwSessionId)
|
|
{
|
|
if (0 == CSessions())
|
|
{
|
|
return FALSE;
|
|
}
|
|
m_iCurSession = (m_iCurSession + 1 ) % CSessions();
|
|
*pdwSessionId = m_pSessionStateInfo[m_iCurSession].dwSessionId;
|
|
|
|
return TRUE;
|
|
}
|
|
int SESSION_STATUS::m_iFindSession(DWORD dwSessionId)
|
|
{
|
|
|
|
for (int iSession = 0; iSession < CSessions(); iSession++)
|
|
{
|
|
if (dwSessionId == m_pSessionStateInfo[iSession].dwSessionId)
|
|
{
|
|
return iSession;
|
|
}
|
|
}
|
|
return CDWNO_SESSION;
|
|
}
|
|
|
|
// this functions lets someone traverse the content of
|
|
// the array sequencially.
|
|
int SESSION_STATUS::m_iGetSessionIdAtIndex(int iIndex)
|
|
{
|
|
if (iIndex < 0 || iIndex >= CSessions())
|
|
{
|
|
// Out of bound!!
|
|
return -1;
|
|
}
|
|
|
|
return m_pSessionStateInfo[iIndex].dwSessionId;
|
|
}
|
|
|
|
BOOL SESSION_STATUS::m_FChangeBufSession(int cSessions)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
SESSION_STATE_INFO *pSessionStateInfo = (SESSION_STATE_INFO *)realloc(m_pSessionStateInfo, sizeof(SESSION_STATE_INFO) * cSessions);
|
|
if (NULL != pSessionStateInfo)
|
|
{
|
|
m_pSessionStateInfo = pSessionStateInfo;
|
|
}
|
|
else
|
|
{
|
|
goto Done;
|
|
}
|
|
m_cAllocBufSessions = cSessions;
|
|
fRet = TRUE;
|
|
Done:
|
|
return fRet;
|
|
}
|
|
|
|
// Function for debugging
|
|
VOID SESSION_STATUS::m_DumpSessions()
|
|
{
|
|
DEBUGMSG(">>>>>>> DUMPING cached sessions content ");
|
|
|
|
for (int iSession = 0; iSession < CSessions(); iSession++)
|
|
{
|
|
DEBUGMSG(">>> position %d: %lu", iSession, m_pSessionStateInfo[iSession].dwSessionId);
|
|
}
|
|
|
|
DEBUGMSG(">>>>>>> END DUMPING cached sessions content ");
|
|
}
|
|
|
|
/**
|
|
CacheExistingSessions()
|
|
Enumerates existent sessions and persists the admin sessions for future reference
|
|
**/
|
|
VOID SESSION_STATUS::CacheExistingSessions()
|
|
{
|
|
PWTS_SESSION_INFO pSessionInfo = NULL;
|
|
DWORD dwCount = 0;
|
|
|
|
//
|
|
// Check if TS is enabled and try to enumerate existing
|
|
// sessions. If TS is not running, query only session 0.
|
|
//
|
|
if (_IsTerminalServiceRunning())
|
|
{
|
|
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwCount))
|
|
{
|
|
DEBUGMSG("WUAUENG WTSEnumerateSessions dwCount= %lu",dwCount);
|
|
|
|
for (DWORD dwSession = 0; dwSession < dwCount; dwSession++)
|
|
{
|
|
WTS_SESSION_INFO SessionInfo = pSessionInfo[dwSession];
|
|
|
|
DEBUGMSG("WUAUENG CacheExistingSessions, enumerating SessionId =%lu, State =%d",SessionInfo.SessionId, SessionInfo.State);
|
|
|
|
if (m_fAllActiveUsers)
|
|
{
|
|
if (WTSActive != SessionInfo.State)
|
|
{
|
|
continue;
|
|
}
|
|
HANDLE hImpersonationToken;
|
|
if (AUGetUserToken(SessionInfo.SessionId, &hImpersonationToken))
|
|
{
|
|
SESSION_STATE SessionState;
|
|
SessionState.fFoundEnumerating = TRUE;
|
|
m_FAddSession(SessionInfo.SessionId, &SessionState);
|
|
CloseHandle(hImpersonationToken);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((WTSActive != SessionInfo.State) && ( WTSConnected != SessionInfo.State) ||
|
|
(m_iFindSession(SessionInfo.SessionId) != CDWNO_SESSION))
|
|
{
|
|
//We only care about Active and Connected sessions that existed before
|
|
//the service was registered, this means that if fAdminSessionLoggedOn is turned on
|
|
//the logon notification was received already and we must no check anything
|
|
continue;
|
|
}
|
|
if (CacheSessionIfAUEnabledAdmin(SessionInfo.SessionId, TRUE))
|
|
{
|
|
DEBUGMSG("WUAUENG Existent Admin Session = %lu",SessionInfo.SessionId);
|
|
}
|
|
}
|
|
}
|
|
|
|
WTSFreeMemory(pSessionInfo);
|
|
}
|
|
else
|
|
{
|
|
DWORD dwRet = GetLastError();
|
|
DEBUGMSG("WUAUENG WTSEnumerateSessions failed dwRet = %lu", dwRet);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_fAllActiveUsers)
|
|
{
|
|
HANDLE hImpersonationToken;
|
|
if (AUGetUserToken(0, &hImpersonationToken))
|
|
{
|
|
SESSION_STATE SessionState;
|
|
SessionState.fFoundEnumerating = TRUE;
|
|
m_FAddSession(0, &SessionState);
|
|
CloseHandle(hImpersonationToken);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CacheSessionIfAUEnabledAdmin(0, TRUE)) //Check Session 0 because Terminal Services are disabled
|
|
{
|
|
DEBUGMSG("WUAUENG Existent Admin Session = %d",0);
|
|
}
|
|
}
|
|
}
|
|
#ifdef DBG
|
|
DEBUGMSG("After CacheExistingSessions");
|
|
m_DumpSessions();
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
CacheSessionIfAUEnabledAdmin
|
|
Cache session in internal data structure if session has administrator logged on and
|
|
has AU group policy allowing update
|
|
Also store this admin session's origin (logon notification or via Enumeration)
|
|
**/
|
|
BOOL SESSION_STATUS::CacheSessionIfAUEnabledAdmin(DWORD dwSessionId, BOOL fFoundEnumerating)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
|
|
if (IsUserAUEnabledAdmin(dwSessionId))
|
|
{
|
|
SESSION_STATE SessionState;
|
|
|
|
SessionState.fFoundEnumerating = fFoundEnumerating;
|
|
fRet = m_FAddSession(dwSessionId, &SessionState);
|
|
}
|
|
else
|
|
{
|
|
fRet = FALSE;
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
void SESSION_STATUS::ValidateCachedSessions()
|
|
{
|
|
DWORD *rgMarkedForDelete = NULL;
|
|
int cSession = 0;
|
|
int cMarkedForDelete = 0;
|
|
|
|
//m_DumpSessions();
|
|
|
|
cSession = CSessions();
|
|
|
|
rgMarkedForDelete = new DWORD[cSession];
|
|
if (!rgMarkedForDelete)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
if (m_fInitCS)
|
|
{
|
|
EnterCriticalSection(&m_csWrite);
|
|
}
|
|
for (int i = 0; i < cSession; i++)
|
|
{
|
|
DWORD dwAdminSession = m_iGetSessionIdAtIndex(i);
|
|
if (!IsAUValidSession(dwAdminSession))
|
|
{
|
|
// store the sessions id to be deleted and deleted after we exit the loop
|
|
rgMarkedForDelete[cMarkedForDelete] = dwAdminSession;
|
|
cMarkedForDelete++;
|
|
}
|
|
}
|
|
|
|
// delete the pending sessions that are now invalid
|
|
for (int i=0; i < cMarkedForDelete; i++)
|
|
{
|
|
DEBUGMSG("WUAUENG Found cached admin session that is not valid anymore. Deleting entry for session %lu", rgMarkedForDelete[i]);
|
|
m_FDeleteSession(rgMarkedForDelete[i]);
|
|
}
|
|
if (m_fInitCS)
|
|
{
|
|
LeaveCriticalSection(&m_csWrite);
|
|
}
|
|
|
|
//m_DumpSessions();
|
|
|
|
cleanup:
|
|
|
|
if (rgMarkedForDelete)
|
|
{
|
|
delete [] rgMarkedForDelete;
|
|
}
|
|
}
|
|
|