//============================================================================= // session.cpp -- implementation of session collection class. // // Copyright (c) 1998-2002 Microsoft Corporation, All Rights Reserved //============================================================================= #include #include #include #include #define _WINNT_ // have what is needed from above #pragma warning (disable: 4786) #include "precomp.h" #include #include #include #include "chstring.h" #include "session.h" #include #include #include #include #ifdef _WIN32_WINNT #define SECURITY_WIN32 #else #define SECURITY_WIN16 #endif #include #include "ctoken.h" #include #include 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 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& 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; } } }