You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1495 lines
39 KiB
1495 lines
39 KiB
//=============================================================================
|
|
// session.cpp -- implementation of session collection class.
|
|
//
|
|
// Copyright (c) 1998-2002 Microsoft Corporation, All Rights Reserved
|
|
//=============================================================================
|
|
|
|
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <ntobapi.h>
|
|
|
|
#define _WINNT_ // have what is needed from above
|
|
|
|
#pragma warning (disable: 4786)
|
|
|
|
|
|
#include "precomp.h"
|
|
#include <map>
|
|
#include <vector>
|
|
#include <comdef.h>
|
|
#include "chstring.h"
|
|
#include "session.h"
|
|
#include <ProvExce.h>
|
|
#include <AssertBreak.h>
|
|
#include <wbemcli.h>
|
|
#include <ntsecapi.h>
|
|
|
|
#ifdef _WIN32_WINNT
|
|
#define SECURITY_WIN32
|
|
#else
|
|
#define SECURITY_WIN16
|
|
#endif
|
|
|
|
#include <sspi.h>
|
|
|
|
#include "ctoken.h"
|
|
|
|
#include <autoptr.h>
|
|
#include <ScopeGuard.h>
|
|
|
|
typedef SECURITY_STATUS (SEC_ENTRY *PFN_LSA_ENUMERATE_LOGON_SESSIONS)
|
|
(
|
|
OUT PULONG LogonSessionCount,
|
|
OUT PLUID* LogonSessionList
|
|
);
|
|
|
|
|
|
typedef SECURITY_STATUS (SEC_ENTRY *PFN_LSA_GET_LOGON_SESSION_DATA)
|
|
(
|
|
IN PLUID LogonId,
|
|
OUT PSECURITY_LOGON_SESSION_DATA* ppLogonSessionData
|
|
);
|
|
|
|
|
|
typedef NTSTATUS (*PFN_LSA_FREE_RETURN_BUFFER)
|
|
(
|
|
IN PVOID Buffer
|
|
);
|
|
|
|
|
|
|
|
//*****************************************************************************
|
|
// CUserSessionCollection functions
|
|
//*****************************************************************************
|
|
|
|
CUserSessionCollection::CUserSessionCollection()
|
|
{
|
|
Refresh();
|
|
}
|
|
|
|
|
|
CUserSessionCollection::CUserSessionCollection(
|
|
const CUserSessionCollection& sescol)
|
|
{
|
|
m_usr2ses.clear();
|
|
|
|
USER_SESSION_ITERATOR sourceIter;
|
|
|
|
for(sourceIter = sescol.m_usr2ses.begin();
|
|
sourceIter != sescol.m_usr2ses.end();
|
|
sourceIter++)
|
|
{
|
|
m_usr2ses.insert(
|
|
USER_SESSION_MAP::value_type(
|
|
sourceIter->first,
|
|
sourceIter->second));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD CUserSessionCollection::Refresh()
|
|
{
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
|
|
// Empty out previous contents...
|
|
m_usr2ses.clear();
|
|
|
|
dwRet = CollectSessions();
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
|
|
DWORD CUserSessionCollection::CollectSessions()
|
|
{
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
std::vector<CProcess> vecProcesses;
|
|
SmartCloseHandle hProcess;
|
|
SmartCloseHandle hToken;
|
|
TOKEN_STATISTICS tokstats;
|
|
PTOKEN_USER ptokusr = NULL;
|
|
DWORD dwRetSize = 0L;
|
|
PSID psidUsr = NULL;
|
|
CHString chstrUsr;
|
|
LUID luidSes;
|
|
|
|
|
|
// Enable the debug privilege...
|
|
EnablePrivilegeOnCurrentThread(SE_DEBUG_NAME);
|
|
|
|
// Get a list of all running processes...
|
|
dwRet = GetProcessList(vecProcesses);
|
|
|
|
if(dwRet == ERROR_SUCCESS)
|
|
{
|
|
// For each member of the process list...
|
|
for(long m = 0L;
|
|
m < vecProcesses.size();
|
|
m++)
|
|
{
|
|
// open the process...
|
|
::SetLastError(ERROR_SUCCESS);
|
|
dwRet = ERROR_SUCCESS;
|
|
|
|
hProcess = ::OpenProcess(
|
|
PROCESS_QUERY_INFORMATION,
|
|
FALSE,
|
|
vecProcesses[m].GetPID());
|
|
|
|
if(hProcess == NULL)
|
|
{
|
|
dwRet = ::GetLastError();
|
|
}
|
|
|
|
// get the process token...
|
|
if(hProcess != NULL &&
|
|
dwRet == ERROR_SUCCESS)
|
|
{
|
|
::SetLastError(ERROR_SUCCESS);
|
|
dwRet = ERROR_SUCCESS;
|
|
|
|
if(!::OpenProcessToken(
|
|
hProcess,
|
|
TOKEN_QUERY,
|
|
&hToken))
|
|
{
|
|
dwRet = ::GetLastError();
|
|
}
|
|
}
|
|
|
|
// get the token statistics...
|
|
if(hToken != NULL &&
|
|
dwRet == ERROR_SUCCESS)
|
|
{
|
|
::SetLastError(ERROR_SUCCESS);
|
|
dwRet = ERROR_SUCCESS;
|
|
if(!::GetTokenInformation(
|
|
hToken,
|
|
TokenStatistics,
|
|
&tokstats,
|
|
sizeof(TOKEN_STATISTICS),
|
|
&dwRetSize))
|
|
{
|
|
dwRet = ::GetLastError();
|
|
}
|
|
}
|
|
|
|
//
|
|
// smart token user
|
|
//
|
|
wmilib::auto_buffer < BYTE > smartptokusr;
|
|
|
|
// get the token user sid...
|
|
if(dwRet == ERROR_SUCCESS)
|
|
{
|
|
// the token user struct varries
|
|
// in size depending on the size
|
|
// of the sid in the SID_AND_ATTRIBUTES
|
|
// structure, so need to allocate
|
|
// it dynamically.
|
|
if(!::GetTokenInformation(
|
|
hToken,
|
|
TokenUser,
|
|
NULL,
|
|
0L,
|
|
&dwRetSize))
|
|
{
|
|
dwRet = ::GetLastError();
|
|
}
|
|
if(dwRet == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
smartptokusr.reset ( new BYTE [ dwRetSize ] ) ;
|
|
ptokusr = (PTOKEN_USER) smartptokusr.get () ;
|
|
DWORD dwTmp = dwRetSize;
|
|
|
|
if(!::GetTokenInformation(
|
|
hToken,
|
|
TokenUser,
|
|
ptokusr,
|
|
dwTmp,
|
|
&dwRetSize))
|
|
{
|
|
dwRet = ::GetLastError();
|
|
}
|
|
else
|
|
{
|
|
dwRet = ERROR_SUCCESS ;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(ptokusr != NULL)
|
|
{
|
|
if(dwRet == ERROR_SUCCESS)
|
|
{
|
|
psidUsr = (ptokusr->User).Sid;
|
|
|
|
// from the token statistics, get
|
|
// the TokenID LUID of the session...
|
|
luidSes.LowPart = tokstats.AuthenticationId.LowPart;
|
|
luidSes.HighPart = tokstats.AuthenticationId.HighPart;
|
|
|
|
// try to find the session of the
|
|
// process in the multimap...
|
|
USER_SESSION_ITERATOR usiter;
|
|
|
|
if(FindSessionInternal(
|
|
luidSes,
|
|
usiter))
|
|
{
|
|
// try to find the process id in the
|
|
// session's process vector...
|
|
CSession sesTmp(usiter->second);
|
|
CProcess* procTmp = NULL;
|
|
bool fFoundIt = false;
|
|
|
|
for(long z = 0L;
|
|
z < sesTmp.m_vecProcesses.size() && !fFoundIt;
|
|
z++)
|
|
{
|
|
if((DWORD)(sesTmp.m_vecProcesses[z].GetPID()) ==
|
|
vecProcesses[m].GetPID())
|
|
{
|
|
fFoundIt = true;
|
|
}
|
|
}
|
|
|
|
// If we didn't find the process in the
|
|
// session's list of processes, add it in...
|
|
if(!fFoundIt)
|
|
{
|
|
(usiter->second).m_vecProcesses.push_back(
|
|
CProcess(vecProcesses[m]));
|
|
}
|
|
}
|
|
else // no such session in the map, so add an entry
|
|
{
|
|
// Create new CSession(tokenid LUID), and
|
|
// add process to the session's process vector...
|
|
CSession sesNew(luidSes);
|
|
sesNew.m_vecProcesses.push_back(
|
|
vecProcesses[m]);
|
|
|
|
// add CUser(user sid) to map.first and the
|
|
// CSession just created to map.second...
|
|
CUser cuTmp(psidUsr);
|
|
if(cuTmp.IsValid())
|
|
{
|
|
m_usr2ses.insert(
|
|
USER_SESSION_MAP::value_type(
|
|
cuTmp,
|
|
sesNew));
|
|
}
|
|
else
|
|
{
|
|
LogErrorMessage2(
|
|
L"Token of process %d contains an invalid sid",
|
|
vecProcesses[m].GetPID());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // next process
|
|
}
|
|
|
|
// There may have been sessions not associated
|
|
// with any processes. To get these, we will
|
|
// use LSA.
|
|
CollectNoProcessesSessions();
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
void CUserSessionCollection::Copy(
|
|
CUserSessionCollection& out) const
|
|
{
|
|
out.m_usr2ses.clear();
|
|
|
|
USER_SESSION_ITERATOR meIter;
|
|
|
|
for(meIter = m_usr2ses.begin();
|
|
meIter != m_usr2ses.end();
|
|
meIter++)
|
|
{
|
|
out.m_usr2ses.insert(
|
|
USER_SESSION_MAP::value_type(
|
|
meIter->first,
|
|
meIter->second));
|
|
}
|
|
}
|
|
|
|
|
|
// Support enumeration of users. Returns
|
|
// a newly allocated copy of what was in
|
|
// the map (caller must free).
|
|
CUser* CUserSessionCollection::GetFirstUser(
|
|
USER_SESSION_ITERATOR& pos)
|
|
{
|
|
CUser* cusrRet = NULL;
|
|
|
|
if(!m_usr2ses.empty())
|
|
{
|
|
pos = m_usr2ses.begin();
|
|
cusrRet = new CUser(pos->first);
|
|
}
|
|
|
|
return cusrRet;
|
|
}
|
|
|
|
// Returns a newly allocated CUser*, which
|
|
// the caller must free.
|
|
CUser* CUserSessionCollection::GetNextUser(
|
|
USER_SESSION_ITERATOR& pos)
|
|
{
|
|
// Users are the non-unique part of
|
|
// the map, so we need to go through
|
|
// the map until the next user entry
|
|
// comes up.
|
|
CUser* usrRet = NULL;
|
|
|
|
while(pos != m_usr2ses.end())
|
|
{
|
|
CHString chstrSidCur;
|
|
pos->first.GetSidString(chstrSidCur);
|
|
|
|
pos++;
|
|
|
|
if(pos != m_usr2ses.end())
|
|
{
|
|
CHString chstrSidNext;
|
|
pos->first.GetSidString(chstrSidNext);
|
|
|
|
// Return the first instance where
|
|
// the next user is different from
|
|
// the current one.
|
|
if(chstrSidNext.CompareNoCase(chstrSidCur) != 0)
|
|
{
|
|
usrRet = new CUser(pos->first);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return usrRet;
|
|
}
|
|
|
|
|
|
// Support enumeration of sessions
|
|
// belonging to a particular user.
|
|
CSession* CUserSessionCollection::GetFirstSessionOfUser(
|
|
CUser& usr,
|
|
USER_SESSION_ITERATOR& pos)
|
|
{
|
|
CSession* csesRet = NULL;
|
|
|
|
if(!m_usr2ses.empty())
|
|
{
|
|
pos = m_usr2ses.find(usr);
|
|
if(pos != m_usr2ses.end())
|
|
{
|
|
csesRet = new CSession(pos->second);
|
|
}
|
|
}
|
|
return csesRet;
|
|
}
|
|
|
|
|
|
CSession* CUserSessionCollection::GetNextSessionOfUser(
|
|
USER_SESSION_ITERATOR& pos)
|
|
{
|
|
// Sessions are the unique part of
|
|
// the map, so we just need to get
|
|
// the next one as long as pos.first
|
|
// matches usr...
|
|
CSession* sesRet = NULL;
|
|
|
|
if(pos != m_usr2ses.end())
|
|
{
|
|
CHString chstrUsr1;
|
|
CHString chstrUsr2;
|
|
|
|
(pos->first).GetSidString(chstrUsr1);
|
|
|
|
pos++;
|
|
|
|
if(pos != m_usr2ses.end())
|
|
{
|
|
(pos->first).GetSidString(chstrUsr2);
|
|
if(chstrUsr1.CompareNoCase(chstrUsr2) == 0)
|
|
{
|
|
sesRet = new CSession(pos->second);
|
|
}
|
|
}
|
|
}
|
|
|
|
return sesRet;
|
|
}
|
|
|
|
|
|
|
|
// Support enumeration of all sessions. Returns a
|
|
// newly allocated CSession*, which the caller
|
|
// must free.
|
|
CSession* CUserSessionCollection::GetFirstSession(
|
|
USER_SESSION_ITERATOR& pos)
|
|
{
|
|
CSession* csesRet = NULL;
|
|
|
|
if(!m_usr2ses.empty())
|
|
{
|
|
pos = m_usr2ses.begin();
|
|
csesRet = new CSession(pos->second);
|
|
}
|
|
return csesRet;
|
|
}
|
|
|
|
// Returns a newly allocated CSession* that the
|
|
// caller must free.
|
|
CSession* CUserSessionCollection::GetNextSession(
|
|
USER_SESSION_ITERATOR& pos)
|
|
{
|
|
// Sessions are the unique part of
|
|
// the map, so we just need to get
|
|
// the next one...
|
|
CSession* sesRet = NULL;
|
|
|
|
if(pos != m_usr2ses.end())
|
|
{
|
|
pos++;
|
|
if(pos != m_usr2ses.end())
|
|
{
|
|
sesRet = new CSession(pos->second);
|
|
}
|
|
}
|
|
|
|
return sesRet;
|
|
}
|
|
|
|
|
|
// Support finding a particular session.
|
|
// This internal version hands back an iterator
|
|
// on our member map that points to the found
|
|
// instance if found (when the function returns
|
|
// true. If the function returns
|
|
// false, the iterator points to the end of our
|
|
// map.
|
|
bool CUserSessionCollection::FindSessionInternal(
|
|
LUID& luidSes,
|
|
USER_SESSION_ITERATOR& usiOut)
|
|
{
|
|
bool fFoundIt = false;
|
|
|
|
for(usiOut = m_usr2ses.begin();
|
|
usiOut != m_usr2ses.end();
|
|
usiOut++)
|
|
{
|
|
LUID luidTmp = (usiOut->second).GetLUID();
|
|
if(luidTmp.HighPart == luidSes.HighPart &&
|
|
luidTmp.LowPart == luidSes.LowPart)
|
|
{
|
|
fFoundIt = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return fFoundIt;
|
|
}
|
|
|
|
|
|
// Support finding a particular session - external
|
|
// callers can call this one, and are given a new
|
|
// CSession* they can play with.
|
|
CSession* CUserSessionCollection::FindSession(
|
|
LUID& luidSes)
|
|
{
|
|
CSession* psesRet = NULL;
|
|
USER_SESSION_ITERATOR pos;
|
|
|
|
if(FindSessionInternal(
|
|
luidSes,
|
|
pos))
|
|
{
|
|
psesRet = new CSession(pos->second);
|
|
}
|
|
|
|
return psesRet;
|
|
}
|
|
|
|
CSession* CUserSessionCollection::FindSession(
|
|
__int64 i64luidSes)
|
|
{
|
|
LUID luidSes = *((LUID*)(&i64luidSes));
|
|
return FindSession(luidSes);
|
|
}
|
|
|
|
|
|
// Support enumeration of processes
|
|
// belonging to a particular user. Returns
|
|
// newly allocated CProcess* which the caller
|
|
// must free.
|
|
CProcess* CUserSessionCollection::GetFirstProcessOfUser(
|
|
CUser& usr,
|
|
USER_SESSION_PROCESS_ITERATOR& pos)
|
|
{
|
|
CProcess* cprocRet = NULL;
|
|
CHString chstrUsrSidStr;
|
|
CHString chstrTmp;
|
|
|
|
if(!m_usr2ses.empty())
|
|
{
|
|
usr.GetSidString(chstrUsrSidStr);
|
|
pos.usIter = m_usr2ses.find(usr);
|
|
while(pos.usIter != m_usr2ses.end())
|
|
{
|
|
// Get the sid string of the user we
|
|
// are at and see whether the strings
|
|
// are the same (e.g., whether this is a
|
|
// session associated with the specified
|
|
// user)...
|
|
(pos.usIter)->first.GetSidString(chstrTmp);
|
|
if(chstrUsrSidStr.CompareNoCase(chstrTmp) == 0)
|
|
{
|
|
// Now check that the session of the user
|
|
// we are on has processes...
|
|
if(!(((pos.usIter)->second).m_vecProcesses.empty()))
|
|
{
|
|
pos.procIter =
|
|
((pos.usIter)->second).m_vecProcesses.begin();
|
|
cprocRet = new CProcess(*(pos.procIter));
|
|
}
|
|
else
|
|
{
|
|
// the session for this user has
|
|
// no processes, so go to the next
|
|
// session...
|
|
(pos.usIter)++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return cprocRet;
|
|
}
|
|
|
|
|
|
// Returns a newly allocated CProcess* that the
|
|
// caller must free.
|
|
CProcess* CUserSessionCollection::GetNextProcessOfUser(
|
|
USER_SESSION_PROCESS_ITERATOR& pos)
|
|
{
|
|
CProcess* cprocRet = NULL;
|
|
CHString chstrCurUsr;
|
|
CHString chstrNxtSesUsr;
|
|
|
|
if(pos.usIter != m_usr2ses.end())
|
|
{
|
|
(pos.usIter)->first.GetSidString(chstrCurUsr);
|
|
|
|
while(pos.usIter != m_usr2ses.end())
|
|
{
|
|
// First try to get the next process
|
|
// within the current session. If we
|
|
// were at the end of the list of processes
|
|
// for the current session, go to the
|
|
// next session...
|
|
(pos.procIter)++;
|
|
|
|
// Of course, if we have moved on
|
|
// to a different user, then stop.
|
|
(pos.usIter)->first.GetSidString(chstrNxtSesUsr);
|
|
if(chstrCurUsr.CompareNoCase(chstrNxtSesUsr) == 0)
|
|
{
|
|
if(pos.procIter ==
|
|
((pos.usIter)->second).m_vecProcesses.end())
|
|
{
|
|
(pos.usIter)++;
|
|
}
|
|
else
|
|
{
|
|
cprocRet = new CProcess(*(pos.procIter));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return cprocRet;
|
|
}
|
|
|
|
|
|
|
|
// Support enumeration of all processes. Returns
|
|
// newly allocated CProcess* which the caller
|
|
// must free.
|
|
CProcess* CUserSessionCollection::GetFirstProcess(
|
|
USER_SESSION_PROCESS_ITERATOR& pos)
|
|
{
|
|
CProcess* cprocRet = NULL;
|
|
|
|
if(!m_usr2ses.empty())
|
|
{
|
|
pos.usIter = m_usr2ses.begin();
|
|
while(pos.usIter != m_usr2ses.end())
|
|
{
|
|
if(!(((pos.usIter)->second).m_vecProcesses.empty()))
|
|
{
|
|
pos.procIter =
|
|
((pos.usIter)->second).m_vecProcesses.begin();
|
|
cprocRet = new CProcess(*(pos.procIter));
|
|
}
|
|
else
|
|
{
|
|
(pos.usIter)++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return cprocRet;
|
|
}
|
|
|
|
|
|
// Returns a newly allocated CProcess* that the
|
|
// caller must free.
|
|
CProcess* CUserSessionCollection::GetNextProcess(
|
|
USER_SESSION_PROCESS_ITERATOR& pos)
|
|
{
|
|
CProcess* cprocRet = NULL;
|
|
|
|
while(pos.usIter != m_usr2ses.end())
|
|
{
|
|
// First try to get the next process
|
|
// within the current session. If we
|
|
// were at the end of the list of processes
|
|
// for the current session, go to the
|
|
// next session...
|
|
(pos.procIter)++;
|
|
if(pos.procIter ==
|
|
((pos.usIter)->second).m_vecProcesses.end())
|
|
{
|
|
(pos.usIter)++;
|
|
}
|
|
else
|
|
{
|
|
cprocRet = new CProcess(*(pos.procIter));
|
|
}
|
|
}
|
|
|
|
return cprocRet;
|
|
}
|
|
|
|
|
|
// This helper enumerates the current set of processes
|
|
// and ads each process id as a DWORD in the vector.
|
|
DWORD CUserSessionCollection::GetProcessList( std::vector<CProcess>& vecProcesses ) const
|
|
{
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
|
|
// First, load up ntdll...
|
|
HMODULE hLib = NULL;
|
|
PFN_NT_QUERY_SYSTEM_INFORMATION pfnNtQuerySystemInformation = NULL;
|
|
|
|
hLib = LoadLibraryW(L"NTDLL.DLL");
|
|
if(hLib != NULL)
|
|
{
|
|
//
|
|
// auto FreeLibrary
|
|
//
|
|
ON_BLOCK_EXIT ( FreeLibrary, hLib ) ;
|
|
|
|
// Get proc address of NtQuerySystemInformation...
|
|
pfnNtQuerySystemInformation = (PFN_NT_QUERY_SYSTEM_INFORMATION)
|
|
GetProcAddress(
|
|
hLib,
|
|
"NtQuerySystemInformation");
|
|
|
|
if(pfnNtQuerySystemInformation != NULL)
|
|
{
|
|
// Ready to rock. Enable debug priv...
|
|
EnablePrivilegeOnCurrentThread(SE_DEBUG_NAME);
|
|
|
|
DWORD dwProcessInformationSize = 0;
|
|
SYSTEM_PROCESS_INFORMATION* ProcessInformation = NULL;
|
|
|
|
//
|
|
// smart ProcessInformation
|
|
//
|
|
wmilib::auto_buffer < BYTE > SmartProcessInformation;
|
|
|
|
// Get the process information...
|
|
|
|
BOOL fRetry = TRUE;
|
|
while(fRetry)
|
|
{
|
|
dwRet = pfnNtQuerySystemInformation(
|
|
SystemProcessInformation,
|
|
ProcessInformation,
|
|
dwProcessInformationSize,
|
|
NULL);
|
|
|
|
if(dwRet == STATUS_INFO_LENGTH_MISMATCH)
|
|
{
|
|
dwProcessInformationSize += 32768;
|
|
|
|
SmartProcessInformation.reset ( new BYTE [ dwProcessInformationSize ] );
|
|
ProcessInformation = (SYSTEM_PROCESS_INFORMATION*)SmartProcessInformation.get () ;
|
|
}
|
|
else
|
|
{
|
|
fRetry = FALSE;
|
|
}
|
|
}
|
|
|
|
// If we got the process information, process it...
|
|
if(ProcessInformation != NULL &&
|
|
dwRet == ERROR_SUCCESS)
|
|
{
|
|
SYSTEM_PROCESS_INFORMATION* CurrentInformation = NULL;
|
|
DWORD dwNextOffset;
|
|
CurrentInformation = ProcessInformation;
|
|
bool fContinue = true;
|
|
while(CurrentInformation != NULL &&
|
|
fContinue)
|
|
{
|
|
{
|
|
CProcess cptmp(
|
|
HandleToUlong(CurrentInformation->UniqueProcessId),
|
|
(CurrentInformation->ImageName).Buffer);
|
|
|
|
vecProcesses.push_back(cptmp);
|
|
}
|
|
|
|
dwNextOffset = CurrentInformation->NextEntryOffset;
|
|
if(dwNextOffset)
|
|
{
|
|
CurrentInformation = (SYSTEM_PROCESS_INFORMATION*)
|
|
(((BYTE*) CurrentInformation) + dwNextOffset);
|
|
}
|
|
else
|
|
{
|
|
fContinue = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogErrorMessage(L"Failed to load library ntdll.dll");
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
// Implementation lifted from dllutils.cpp.
|
|
DWORD CUserSessionCollection::EnablePrivilegeOnCurrentThread(
|
|
LPCTSTR szPriv) const
|
|
{
|
|
SmartCloseHandle hToken = NULL;
|
|
TOKEN_PRIVILEGES tkp;
|
|
BOOL bLookup = FALSE;
|
|
DWORD dwLastError = ERROR_SUCCESS;
|
|
|
|
// Try to open the thread token.
|
|
if (::OpenThreadToken(
|
|
GetCurrentThread(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
FALSE,
|
|
&hToken))
|
|
{
|
|
|
|
{
|
|
bLookup = ::LookupPrivilegeValue(
|
|
NULL,
|
|
szPriv,
|
|
&tkp.Privileges[0].Luid);
|
|
}
|
|
if (bLookup)
|
|
{
|
|
tkp.PrivilegeCount = 1;
|
|
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
|
|
// Clear the last error.
|
|
SetLastError(0);
|
|
|
|
// Turn it on
|
|
::AdjustTokenPrivileges(
|
|
hToken,
|
|
FALSE,
|
|
&tkp,
|
|
0,
|
|
(PTOKEN_PRIVILEGES) NULL,
|
|
0);
|
|
|
|
dwLastError = GetLastError();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwLastError = ::GetLastError();
|
|
}
|
|
|
|
// We have to check GetLastError() because
|
|
// AdjustTokenPrivileges lies about
|
|
// its success but GetLastError() doesn't.
|
|
return dwLastError;
|
|
}
|
|
|
|
|
|
|
|
bool CUserSessionCollection::IsSessionMapped(
|
|
LUID& luidSes)
|
|
{
|
|
bool fRet = false;
|
|
|
|
USER_SESSION_ITERATOR usiter;
|
|
usiter = m_usr2ses.begin();
|
|
for(usiter = m_usr2ses.begin();
|
|
usiter != m_usr2ses.end() && !fRet;
|
|
usiter++)
|
|
{
|
|
LUID luidTmp = (usiter->second).GetLUID();
|
|
if(luidTmp.HighPart == luidSes.HighPart &&
|
|
luidTmp.LowPart == luidSes.LowPart)
|
|
{
|
|
fRet = true;
|
|
}
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
bool CUserSessionCollection::IsSessionMapped(
|
|
__int64 i64luidSes)
|
|
{
|
|
LUID luidSes = *((LUID*)(&i64luidSes));
|
|
return IsSessionMapped(luidSes);
|
|
}
|
|
|
|
|
|
// Collects sessions that have no associated
|
|
// process. Uses LSA to enumerate sessions,
|
|
// then checks to see if we have each session
|
|
// already. If we don't, adds it to our map.
|
|
|
|
DWORD CUserSessionCollection::CollectNoProcessesSessions()
|
|
{
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
ULONG ulLogonSessionCount = 0L;
|
|
PLUID pluidLogonSessions = NULL;
|
|
HMODULE hLib = NULL;
|
|
PFN_LSA_ENUMERATE_LOGON_SESSIONS pfnEnumLogonSessions = NULL;
|
|
PFN_LSA_GET_LOGON_SESSION_DATA pfnGetLogonSessionData = NULL;
|
|
PFN_LSA_FREE_RETURN_BUFFER pfnLsaFreeReturnBuffer = NULL;
|
|
|
|
// Doing a load library here rather than using the
|
|
// resource manager, as SECURITYAPI.CPP defines us
|
|
// to point to SECURITY.DLL, not SECUR32.DLL for the
|
|
// W2K case.
|
|
|
|
hLib = ::LoadLibraryW(L"SECUR32.DLL");
|
|
if(hLib)
|
|
{
|
|
//
|
|
// auto FreeLibrary
|
|
//
|
|
ON_BLOCK_EXIT ( FreeLibrary, hLib ) ;
|
|
|
|
pfnEnumLogonSessions =
|
|
(PFN_LSA_ENUMERATE_LOGON_SESSIONS) ::GetProcAddress(
|
|
hLib,
|
|
"LsaEnumerateLogonSessions");
|
|
|
|
pfnGetLogonSessionData =
|
|
(PFN_LSA_GET_LOGON_SESSION_DATA) ::GetProcAddress(
|
|
hLib,
|
|
"LsaGetLogonSessionData");
|
|
|
|
pfnLsaFreeReturnBuffer =
|
|
(PFN_LSA_FREE_RETURN_BUFFER) ::GetProcAddress(
|
|
hLib,
|
|
"LsaFreeReturnBuffer");
|
|
|
|
if(pfnEnumLogonSessions &&
|
|
pfnGetLogonSessionData &&
|
|
pfnLsaFreeReturnBuffer)
|
|
{
|
|
dwRet = pfnEnumLogonSessions(
|
|
&ulLogonSessionCount,
|
|
&pluidLogonSessions);
|
|
|
|
if(dwRet == ERROR_SUCCESS &&
|
|
pluidLogonSessions)
|
|
{
|
|
//
|
|
// auto destructor for logon session
|
|
//
|
|
ON_BLOCK_EXIT ( pfnLsaFreeReturnBuffer, pluidLogonSessions ) ;
|
|
|
|
for(ULONG u = 0L;
|
|
u < ulLogonSessionCount && dwRet == ERROR_SUCCESS;
|
|
u++)
|
|
{
|
|
PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
|
|
dwRet = pfnGetLogonSessionData(
|
|
&pluidLogonSessions[u],
|
|
&pSessionData);
|
|
|
|
if(dwRet == ERROR_SUCCESS &&
|
|
pSessionData)
|
|
{
|
|
//
|
|
// smart session data
|
|
//
|
|
ON_BLOCK_EXIT ( pfnLsaFreeReturnBuffer, pSessionData ) ;
|
|
|
|
// See if we have the session already...
|
|
if(!IsSessionMapped(pSessionData->LogonId))
|
|
{
|
|
// and if not, add it to the map.
|
|
CSession sesNew(pSessionData->LogonId);
|
|
CUser cuTmp(pSessionData->Sid);
|
|
CHString chstrTmp;
|
|
|
|
if(cuTmp.IsValid())
|
|
{
|
|
cuTmp.GetSidString(chstrTmp);
|
|
|
|
m_usr2ses.insert(
|
|
USER_SESSION_MAP::value_type(
|
|
cuTmp,
|
|
sesNew));
|
|
}
|
|
else
|
|
{
|
|
LUID luidTmp = sesNew.GetLUID();
|
|
LogMessage3(
|
|
L"GetLogonSessionData returned logon data for session "
|
|
L"luid %d (highpart) %u (lowpart) containing an invalid SID",
|
|
luidTmp.HighPart,
|
|
luidTmp.LowPart);
|
|
}
|
|
}
|
|
|
|
// While we are here, add in various
|
|
// session properties lsa has been kind
|
|
// enough to provide for us.
|
|
USER_SESSION_ITERATOR usiter;
|
|
usiter = m_usr2ses.begin();
|
|
bool fFound = false;
|
|
while(usiter != m_usr2ses.end() &&
|
|
!fFound)
|
|
{
|
|
LUID luidTmp = pSessionData->LogonId;
|
|
__int64 i64Tmp = *((__int64*)(&luidTmp));
|
|
|
|
if((usiter->second).GetLUIDint64() ==
|
|
i64Tmp)
|
|
{
|
|
fFound = true;
|
|
}
|
|
else
|
|
{
|
|
usiter++;
|
|
}
|
|
}
|
|
if(fFound)
|
|
{
|
|
WCHAR wstrTmp[_MAX_PATH] = { '\0' };
|
|
if((pSessionData->AuthenticationPackage).Length < (_MAX_PATH - 1))
|
|
{
|
|
wcsncpy(
|
|
wstrTmp,
|
|
(pSessionData->AuthenticationPackage).Buffer,
|
|
(pSessionData->AuthenticationPackage).Length);
|
|
|
|
(usiter->second).m_chstrAuthPkg = wstrTmp;
|
|
}
|
|
|
|
(usiter->second).m_ulLogonType =
|
|
pSessionData->LogonType;
|
|
|
|
(usiter->second).i64LogonTime =
|
|
*((__int64*)(&(pSessionData->LogonTime)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogErrorMessage(L"Failed to load library SECUR32.dll");
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
// CSession functions
|
|
//*****************************************************************************
|
|
|
|
CSession::CSession(
|
|
const LUID& luidSessionID)
|
|
{
|
|
m_luid.LowPart = luidSessionID.LowPart;
|
|
m_luid.HighPart = luidSessionID.HighPart;
|
|
m_ulLogonType = 0;
|
|
i64LogonTime = 0;
|
|
}
|
|
|
|
CSession::CSession(
|
|
const CSession& ses)
|
|
{
|
|
m_luid.LowPart = ses.m_luid.LowPart;
|
|
m_luid.HighPart = ses.m_luid.HighPart;
|
|
m_chstrAuthPkg = ses.m_chstrAuthPkg;
|
|
m_ulLogonType = ses.m_ulLogonType;
|
|
i64LogonTime = ses.i64LogonTime;
|
|
|
|
m_vecProcesses.clear();
|
|
for(long lPos = 0;
|
|
lPos < ses.m_vecProcesses.size();
|
|
lPos++)
|
|
{
|
|
m_vecProcesses.push_back(
|
|
ses.m_vecProcesses[lPos]);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
LUID CSession::GetLUID() const
|
|
{
|
|
return m_luid;
|
|
}
|
|
|
|
__int64 CSession::GetLUIDint64() const
|
|
{
|
|
__int64 i64LuidSes = *((__int64*)(&m_luid));
|
|
return i64LuidSes;
|
|
}
|
|
|
|
CHString CSession::GetAuthenticationPkg() const
|
|
{
|
|
return m_chstrAuthPkg;
|
|
}
|
|
|
|
|
|
ULONG CSession::GetLogonType() const
|
|
{
|
|
return m_ulLogonType;
|
|
}
|
|
|
|
|
|
__int64 CSession::GetLogonTime() const
|
|
{
|
|
return i64LogonTime;
|
|
}
|
|
|
|
|
|
|
|
|
|
// Functions to support enumeration of
|
|
// processes associated with this session.
|
|
// Returns a newly allocated CProcess* that
|
|
// the caller must free.
|
|
CProcess* CSession::GetFirstProcess(
|
|
PROCESS_ITERATOR& pos)
|
|
{
|
|
CProcess* procRet = NULL;
|
|
if(!m_vecProcesses.empty())
|
|
{
|
|
pos = m_vecProcesses.begin();
|
|
procRet = new CProcess(*pos);
|
|
}
|
|
return procRet;
|
|
}
|
|
|
|
|
|
// Returns a newly allocated CProcess* that
|
|
// the caller must free.
|
|
CProcess* CSession::GetNextProcess(
|
|
PROCESS_ITERATOR& pos)
|
|
{
|
|
CProcess* procRet = NULL;
|
|
|
|
if(pos >= m_vecProcesses.begin() &&
|
|
pos < m_vecProcesses.end())
|
|
{
|
|
pos++;
|
|
if(pos != m_vecProcesses.end())
|
|
{
|
|
procRet = new CProcess(*pos);
|
|
}
|
|
}
|
|
|
|
return procRet;
|
|
}
|
|
|
|
|
|
|
|
void CSession::Copy(
|
|
CSession& sesCopy) const
|
|
{
|
|
sesCopy.m_luid.LowPart = m_luid.LowPart;
|
|
sesCopy.m_luid.HighPart = m_luid.HighPart;
|
|
sesCopy.m_chstrAuthPkg = m_chstrAuthPkg;
|
|
sesCopy.m_ulLogonType = m_ulLogonType;
|
|
sesCopy.i64LogonTime = i64LogonTime;
|
|
|
|
sesCopy.m_vecProcesses.clear();
|
|
for(long lPos = 0;
|
|
lPos < m_vecProcesses.size();
|
|
lPos++)
|
|
{
|
|
sesCopy.m_vecProcesses.push_back(
|
|
m_vecProcesses[lPos]);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// This function impersonates the
|
|
// explorer process in the session's
|
|
// process array, if it is present.
|
|
// (If it isn't, impersonates the
|
|
// first process in the process array.)
|
|
// Returns the handle of token of the
|
|
// thread we started from for easy
|
|
// reversion, orINVALID_HANDLE_VALUE if
|
|
// we couldn't impersonate. The caller
|
|
// must close that handle.
|
|
HANDLE CSession::Impersonate()
|
|
{
|
|
HANDLE hCurToken = INVALID_HANDLE_VALUE;
|
|
|
|
// Find the explorer process...
|
|
DWORD dwImpProcPID = GetImpProcPID();
|
|
if(dwImpProcPID != -1L)
|
|
{
|
|
//
|
|
// smart CloseHandle
|
|
//
|
|
ScopeGuard SmartCloseHandleFnc = MakeGuard ( CloseHandle, hCurToken ) ;
|
|
|
|
bool fOK = false;
|
|
|
|
if(::OpenThreadToken(
|
|
::GetCurrentThread(),
|
|
TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE ,
|
|
TRUE,
|
|
&hCurToken))
|
|
{
|
|
SmartCloseHandle hProcess;
|
|
hProcess = ::OpenProcess(
|
|
PROCESS_QUERY_INFORMATION,
|
|
FALSE,
|
|
dwImpProcPID);
|
|
|
|
if(hProcess)
|
|
{
|
|
// now open its token...
|
|
SmartCloseHandle hExplorerToken;
|
|
if(::OpenProcessToken(
|
|
hProcess,
|
|
TOKEN_READ | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE,
|
|
&hExplorerToken))
|
|
{
|
|
CProcessToken cpt ( hExplorerToken );
|
|
if ( cpt.IsValidToken () )
|
|
{
|
|
TOKEN_TYPE type;
|
|
if ( cpt.GetTokenType ( type ) )
|
|
{
|
|
if ( TokenPrimary == type )
|
|
{
|
|
CToken ct;
|
|
if ( ct.Duplicate ( cpt, FALSE ) )
|
|
{
|
|
// Set the thread token...
|
|
if(::SetThreadToken(NULL, ct.GetTokenHandle ()))
|
|
{
|
|
fOK = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Set the thread token...
|
|
if(::SetThreadToken(NULL, cpt.GetTokenHandle ()))
|
|
{
|
|
fOK = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SmartCloseHandleFnc.Dismiss () ;
|
|
if (!fOK)
|
|
{
|
|
if(hCurToken != INVALID_HANDLE_VALUE)
|
|
{
|
|
::CloseHandle(hCurToken);
|
|
hCurToken = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hCurToken;
|
|
}
|
|
|
|
|
|
DWORD CSession::GetImpProcPID()
|
|
{
|
|
DWORD dwRet = -1L;
|
|
|
|
if(!m_vecProcesses.empty())
|
|
{
|
|
bool fFoundExplorerExe = false;
|
|
|
|
for(long m = 0;
|
|
m < m_vecProcesses.size() &&
|
|
!fFoundExplorerExe;)
|
|
{
|
|
if(m_vecProcesses[m].GetImageName().CompareNoCase(
|
|
L"explorer.exe") == 0)
|
|
{
|
|
fFoundExplorerExe = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
m++;
|
|
}
|
|
}
|
|
|
|
if(!fFoundExplorerExe)
|
|
{
|
|
m = 0;
|
|
}
|
|
|
|
dwRet = m_vecProcesses[m].GetPID();
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
|
|
bool CSession::IsSessionIDValid(
|
|
LPCWSTR wstrSessionID)
|
|
{
|
|
bool fRet = true;
|
|
|
|
if(wstrSessionID != NULL &&
|
|
*wstrSessionID != L'\0')
|
|
{
|
|
for(const WCHAR* pwc = wstrSessionID;
|
|
*pwc != NULL && fRet;
|
|
pwc++)
|
|
{
|
|
fRet = iswdigit(*pwc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fRet = false;
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
// CProcess functions
|
|
//*****************************************************************************
|
|
|
|
CProcess::CProcess()
|
|
: m_dwPID(0)
|
|
{
|
|
}
|
|
|
|
|
|
CProcess::CProcess(
|
|
DWORD dwPID,
|
|
LPCWSTR wstrImageName)
|
|
: m_dwPID(dwPID)
|
|
{
|
|
m_chstrImageName = wstrImageName;
|
|
}
|
|
|
|
|
|
CProcess::CProcess(
|
|
const CProcess& process)
|
|
{
|
|
m_dwPID = process.m_dwPID;
|
|
m_chstrImageName = process.m_chstrImageName;
|
|
}
|
|
|
|
CProcess::~CProcess()
|
|
{
|
|
}
|
|
|
|
|
|
DWORD CProcess::GetPID() const
|
|
{
|
|
return m_dwPID;
|
|
}
|
|
|
|
CHString CProcess::GetImageName() const
|
|
{
|
|
return m_chstrImageName;
|
|
}
|
|
|
|
|
|
void CProcess::Copy(
|
|
CProcess& out) const
|
|
{
|
|
out.m_dwPID = m_dwPID;
|
|
out.m_chstrImageName = m_chstrImageName;
|
|
}
|
|
|
|
|
|
|
|
|
|
//*****************************************************************************
|
|
// CUser functions
|
|
//*****************************************************************************
|
|
|
|
|
|
CUser::CUser(
|
|
PSID pSid)
|
|
: m_sidUser(NULL),
|
|
m_fValid(false)
|
|
{
|
|
if(::IsValidSid(pSid))
|
|
{
|
|
DWORD dwSize = ::GetLengthSid(pSid);
|
|
m_sidUser = NULL;
|
|
m_sidUser = malloc(dwSize);
|
|
if(m_sidUser == NULL)
|
|
{
|
|
throw CHeap_Exception(
|
|
CHeap_Exception::E_ALLOCATION_ERROR);
|
|
}
|
|
else
|
|
{
|
|
::CopySid(
|
|
dwSize,
|
|
m_sidUser,
|
|
pSid);
|
|
|
|
m_fValid = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
CUser::CUser(
|
|
const CUser& user)
|
|
{
|
|
DWORD dwSize = ::GetLengthSid(user.m_sidUser);
|
|
m_sidUser = malloc(dwSize);
|
|
|
|
if(m_sidUser == NULL)
|
|
{
|
|
throw CHeap_Exception(
|
|
CHeap_Exception::E_ALLOCATION_ERROR);
|
|
}
|
|
|
|
::CopySid(
|
|
dwSize,
|
|
m_sidUser,
|
|
user.m_sidUser);
|
|
|
|
m_fValid = user.m_fValid;
|
|
|
|
}
|
|
|
|
|
|
|
|
CUser::~CUser()
|
|
{
|
|
if(m_sidUser)
|
|
{
|
|
free(m_sidUser);
|
|
m_sidUser = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
bool CUser::IsValid()
|
|
{
|
|
return m_fValid;
|
|
}
|
|
|
|
|
|
void CUser::Copy(
|
|
CUser& out) const
|
|
{
|
|
if(out.m_sidUser)
|
|
{
|
|
free(out.m_sidUser);
|
|
out.m_sidUser = NULL;
|
|
}
|
|
|
|
DWORD dwSize = ::GetLengthSid(m_sidUser);
|
|
out.m_sidUser = malloc(dwSize);
|
|
|
|
if(out.m_sidUser == NULL)
|
|
{
|
|
throw CHeap_Exception(
|
|
CHeap_Exception::E_ALLOCATION_ERROR);
|
|
}
|
|
|
|
::CopySid(
|
|
dwSize,
|
|
out.m_sidUser,
|
|
m_sidUser);
|
|
|
|
out.m_fValid = m_fValid;
|
|
}
|
|
|
|
|
|
// Implementation lifted from sid.cpp.
|
|
void CUser::GetSidString(CHString& str) const
|
|
{
|
|
ASSERT_BREAK(m_fValid);
|
|
|
|
if(m_fValid)
|
|
{
|
|
// Initialize m_strSid - human readable form of our SID
|
|
SID_IDENTIFIER_AUTHORITY *psia = NULL;
|
|
psia = ::GetSidIdentifierAuthority( m_sidUser );
|
|
|
|
// We assume that only last byte is used (authorities between 0 and 15).
|
|
// Correct this if needed.
|
|
ASSERT_BREAK( psia->Value[0] == psia->Value[1] ==
|
|
psia->Value[2] == psia->Value[3] ==
|
|
psia->Value[4] == 0 );
|
|
|
|
DWORD dwTopAuthority = psia->Value[5];
|
|
|
|
str.Format( L"S-1-%u", dwTopAuthority );
|
|
CHString strSubAuthority;
|
|
int iSubAuthorityCount = *( GetSidSubAuthorityCount( m_sidUser ) );
|
|
for ( int i = 0; i < iSubAuthorityCount; i++ ) {
|
|
|
|
DWORD dwSubAuthority = *( GetSidSubAuthority( m_sidUser, i ) );
|
|
strSubAuthority.Format( L"%u", dwSubAuthority );
|
|
str += _T("-") + strSubAuthority;
|
|
}
|
|
}
|
|
}
|