|
|
//=================================================================
//
// ImpLogonUser.CPP -- Class to perform impersonation of logged on user.
//
// Copyright (c) 1997-2002 Microsoft Corporation, All Rights Reserved
//
// Revisions: 09/09/97 a-sanjes Created
//
//=================================================================
//#define _WIN32_DCOM // For CoImpersonateUser and CoRevertToSelf
//#include <objbase.h>
#include "precomp.h"
#ifdef NTONLY
#include <tchar.h>
#include <winerror.h>
#include <cominit.h>
#include <lockwrap.h>
#include "Sid.h"
#include "AccessEntry.h" // CAccessEntry class
#include "AccessEntryList.h"
#include "DACL.h" // CDACL class
#include "SACL.h"
#include "securitydescriptor.h"
#include "CToken.h"
#include "SecureKernelObj.h"
#include "implogonuser.h"
#include "cluidhelper.h"
static DWORD s_dwProcessID = 0; static CCritSec g_csImpersonate;
//////////////////////////////////////////////////////////////////////////////
//
// implogonuser.cpp - Class implementation of CImpersonateLoggedOnUser.
//
// This class is intended to provide a way for a process to identify the shell
// process on a Windows NT system, and using the access token of that process
// to attempt to impersonate the user logged onto the Interactive Desktop of
// a workstation.
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : CImpersonateLoggedOnUser::CImpersonateLoggedOnUser
//
// DESCRIPTION : Constructor
//
// INPUTS : NONE
//
// OUTPUTS : none
//
// RETURNS : nothing
//
// COMMENTS : Constructs empty instance to prepare for impersonation of user.
//
//////////////////////////////////////////////////////////////////////////////
CImpersonateLoggedOnUser::CImpersonateLoggedOnUser() : m_hShellProcess(NULL), m_hUserToken(NULL), m_fImpersonatingUser(FALSE) , m_hThreadToken ( INVALID_HANDLE_VALUE ) { }
//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : CImpersonateLoggedOnUser::~CImpersonateLoggedOnUser
//
// DESCRIPTION : Destructor
//
// INPUTS : none
//
// OUTPUTS : none
//
// RETURNS : nothing
//
// COMMENTS : Class destructor
//
//////////////////////////////////////////////////////////////////////////////
CImpersonateLoggedOnUser::~CImpersonateLoggedOnUser( void ) { // Stop any current impersonation
End(); }
//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : CImpersonateLoggedOnUser::Begin
//
// DESCRIPTION : Attempts to begin impersonation of user.
//
// INPUTS : none
//
// OUTPUTS : none
//
// RETURNS : BOOL TRUE/FALSE - Success/Failure
//
// COMMENTS : Uses helper functions to try to impersonate the
// currently logged on user. The process must have
// the proper level of access to perform the operation.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CImpersonateLoggedOnUser::Begin( void ) { BOOL fReturn = FALSE; TCHAR szShellProcessName[256]; LogMessage(_T("CImpersonateLoggedOnUser::Begin")); // Only continue if we are not already impersonating a user
if (!m_fImpersonatingUser ) { //Store the current thread token, assuming that the thread is impersonating somebody (DCOM client)
if ( !OpenThreadToken ( GetCurrentThread(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, TRUE, &m_hThreadToken ) ) { m_hThreadToken = INVALID_HANDLE_VALUE; }
// We will need a copy of PSAPI.DLL and a bunch of entry point
// addresses before we can continue, so let our base class take
// care of this.
// We need a handle for the Shell Process in order to
// successfully impersonate the user.
if ( NULL == m_hShellProcess ) { if ( LoadShellName( szShellProcessName, sizeof(szShellProcessName) ) ) FindShellProcess( szShellProcessName); else LogErrorMessage(_T("LoadShellName failed")); }
if ( NULL != m_hShellProcess ) { fReturn = ImpersonateUser(); } else { // We didn't find the Shell Process Name that we extracted from the
// registry. We saw this happening on Alphas that seem to get "fx32strt.exe"
// dumped in the shell. In these cases, it seems to cause explorer to run.
// So with that in mind, if we drop down in this branch of code, we're going
// to retry the locate shell process operation using Explorer.Exe.
if ( IsErrorLoggingEnabled() ) { CHString sTemp; sTemp.Format(_T("Shell Name %s in Registry not found in process list."), szShellProcessName); LogErrorMessage(sTemp); }
FindShellProcess( IDS_WINNT_SHELLNAME_EXPLORER ) ;
// m_hShellProcess will be non-NULL if and only if we got one.
if ( NULL != m_hShellProcess ) { fReturn = ImpersonateUser(); } else { LogErrorMessage(_T("Unable to locate Shell Process, Impersonation failed.")); SetLastError(0); } } } else { LogMessage(_T("CImpersonateLoggedOnUser::Begin - Already impersonated")); fReturn = TRUE; // Already initialized
}
// We don't yet have a way to know whether explorer is really alive
// because we're impersonating someone and I can't find a way to
// revert back to LocalSystem. So, for now just set it to 0.
SetLastError(0);
return fReturn; }
//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : CImpersonateLoggedOnUser::End
//
// DESCRIPTION : Ends impersonation of logged on user
//
// INPUTS : none
//
// OUTPUTS : none
//
// RETURNS : BOOL TRUE/FALSE - Success/Failure
//
// COMMENTS : Ends impersonation of logged on user. Clears all elements
// of class as a byproduct.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CImpersonateLoggedOnUser::End( void ) { BOOL fReturn = FALSE;
// Only initiate a Revert if we are impersonating the user.
if ( m_fImpersonatingUser ) { LogMessage(_T("CImpersonateLoggedOnUser::End")); fReturn = Revert(); } else { fReturn = TRUE; }
// Clear the handles out
if ( NULL != m_hUserToken ) { CloseHandle( m_hUserToken ); m_hUserToken = NULL; }
if ( NULL != m_hShellProcess ) { CloseHandle( m_hShellProcess ); m_hShellProcess = NULL; } if ( m_hThreadToken != INVALID_HANDLE_VALUE ) { CloseHandle( m_hThreadToken ); m_hThreadToken = INVALID_HANDLE_VALUE ; } return fReturn;
}
//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : CImpersonateLoggedOnUser::LoadShellName
//
// DESCRIPTION : Loads Windows NT Shell name from registry
//
// INPUTS : DWORD cbShellNameBuffer - Shell Name Buffer Size (in bytes)
//
// OUTPUTS : LPTSTR pszShellName - Buffer to contain shell name.
//
// RETURNS : BOOL TRUE/FALSE - Success/Failure
//
// COMMENTS : Jumps into Windows Registry and attempts to determine the
// NT Shell Name.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CImpersonateLoggedOnUser::LoadShellName( LPTSTR pszShellName, DWORD cbShellNameBuffer ) { BOOL fReturn = FALSE; LONG lErrReturn = ERROR_SUCCESS;
// Only continue if we have a buffer to work with first
if ( NULL != pszShellName ) { HKEY hReg = NULL;
// Open the key in HKEY_LOCAL_MACHINE, if that succeeds, get the
// value associated with "Shell".
if ( ( lErrReturn = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WINNT_WINLOGON_KEY, 0, KEY_READ, &hReg ) ) == ERROR_SUCCESS ) { try {
DWORD dwType = REG_SZ;
if ( ( lErrReturn = RegQueryValueEx( hReg, WINNT_SHELL_VALUE, 0, &dwType, (LPBYTE) pszShellName, &cbShellNameBuffer ) ) == ERROR_SUCCESS ) { fReturn = TRUE; } else { LogErrorMessage(_T("RegQueryValueEx FAILED")); } } catch ( ... ) { RegCloseKey( hReg ); throw ; }
RegCloseKey( hReg );
} // RegOpenKeyEx
else { LogErrorMessage(_T("RegOpenKeyEx FAILED")); }
} // NULL != pszShellName
return fReturn; }
//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : CImpersonateLoggedOnUser::FindShellProcess
//
// DESCRIPTION : Enumerates the processes to locate the Shell Process.
//
// INPUTS : LPCTSTR pszShellName - Name of the process to locate.
//
// OUTPUTS : None.
//
// RETURNS : BOOL TRUE/FALSE - Success/Failure
//
// COMMENTS : Enumerates the processes on the local system using PSAPI.DLL
// functions, attempting to locate the one that corresponds to
// the WINNT Shell passed into this function.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CImpersonateLoggedOnUser::FindShellProcess( LPCTSTR pszShellName ) { BOOL fReturn = FALSE; HANDLE hProcess = NULL; HMODULE *phModules = NULL; DWORD dwModuleArraySize = 0; DWORD* pdwProcessIds = NULL;
if ( NULL != pszShellName ) { CPSAPI *t_psapi = (CPSAPI*) CResourceManager::sm_TheResourceManager.GetResource ( guidPSAPI, NULL ) ; if ( t_psapi ) { try { CLuidHelper luid ; LUID processLUID ;
// This locks access to the s_dwProcessID value. WATCH THE SCOPING HERE!
CLockWrapper t_lockImp(g_csImpersonate);
// First check to see if we have a cached value. If so, check to see if it's still valid.
if (s_dwProcessID != 0) { if ( ( hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, s_dwProcessID ) ) != NULL ) { try { // Now search the process modules for a match to the supplied
// shell name.
fReturn = FindShellModuleInProcess( pszShellName, hProcess, phModules, dwModuleArraySize, t_psapi );
if ( TRUE == fReturn ) { if ( STATUS_SUCCESS == luid.GetLUIDFromProcess ( hProcess, &processLUID ) ) { if ( ! luid.IsInteractiveSession ( &processLUID ) ) { fReturn = FALSE ; } } else { fReturn = FALSE ; } } } catch ( ... ) { CloseHandle (hProcess); throw ; }
// Close the process handle if it's not the shell (in which
// case we'll save the value and close it as part of the
// Clear() function.
if ( !fReturn ) { CloseHandle( hProcess ); hProcess = NULL;
// Not valid anymore
s_dwProcessID = 0; } else { m_hShellProcess = hProcess; LogMessage(L"Using cached handle for impersonation");
hProcess = NULL; }
} // if OpenProcess
else { // We didn't open the process, so we need to set the value to zero so that
// we will look for a new process below.
s_dwProcessID = 0; } }
// Did we find a cached value?
if (s_dwProcessID == 0) { // Nope. Scan all processes to see if we can find the explorer
DWORD dwProcessIdArraySize = 0, dwNumProcesses = 0, cbDataReturned = 0; BOOL fEnumSucceeded = FALSE;
// Perform initial allocations of our arrays. Since
// pointers and values are 0, this will just fill out
// said values.
do { ReallocProcessIdArray( pdwProcessIds, dwProcessIdArraySize );
fEnumSucceeded = t_psapi->EnumProcesses( pdwProcessIds, dwProcessIdArraySize, &cbDataReturned );
} while ( (dwProcessIdArraySize == cbDataReturned) && fEnumSucceeded);
// Only walk the array if we sucessfully populated it
if ( fEnumSucceeded ) { // Count of Bytes returned / sizeof(DWORD) tells us how many
// processes are out in the world.
dwNumProcesses = cbDataReturned / sizeof(DWORD);
DWORD dwId = 0;
// Enum processes until we obtain a shell process or run out
// of processes to query.
while ( dwId < dwNumProcesses && !fReturn ) { if ( ( hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pdwProcessIds[dwId] ) ) != NULL ) { try { // Now search the process modules for a match to the supplied
// shell name.
fReturn = FindShellModuleInProcess( pszShellName, hProcess, phModules, dwModuleArraySize, t_psapi );
if ( TRUE == fReturn ) { if ( STATUS_SUCCESS == luid.GetLUIDFromProcess ( hProcess, &processLUID ) ) { if ( ! luid.IsInteractiveSession ( &processLUID ) ) { fReturn = FALSE ; } } else { fReturn = FALSE ; } } } catch ( ... ) { CloseHandle (hProcess); throw ; }
// Close the process handle if it's not the shell (in which
// case we'll save the value and close it as part of the
// Clear() function.
if ( !fReturn ) { CloseHandle( hProcess ); hProcess = NULL; } else { m_hShellProcess = hProcess; s_dwProcessID = pdwProcessIds[dwId]; hProcess = NULL; }
} // if OpenProcess
// Increment the Id counter
++dwId;
} // While OpenProcesses
} // If !fRetryEnumProcesses
} } catch ( ... ) { if (phModules) { delete [] phModules; }
if (pdwProcessIds) { delete [] pdwProcessIds; }
if ( t_psapi ) { CResourceManager::sm_TheResourceManager.ReleaseResource ( guidPSAPI, t_psapi ) ; t_psapi = NULL ; } throw ; } }
if (pdwProcessIds) { delete [] pdwProcessIds; }
if (phModules) { delete [] phModules; }
if ( t_psapi ) { CResourceManager::sm_TheResourceManager.ReleaseResource ( guidPSAPI, t_psapi ) ; // t_psapi = NULL ;
} }
return fReturn; }
//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : CImpersonateLoggedOnUser::FindShellModuleInProcess
//
// DESCRIPTION : Enumerates the modules in a process to find our
// shell.
//
// INPUTS : LPCTSTR pszShellName - Name of the process to locate.
// HANDLE hProcess - Process we are enumerating modules in.
//
// OUTPUTS : HMODULE*& phModules - Array of module handle pointers.
// DWORD& dwModuleArraySize - Size of Module Array (in bytes)
//
// RETURNS : BOOL TRUE/FALSE - Success/Failure
//
// COMMENTS : Enumerates the modules specified by a process identifier and
// attemptsto locate the one that corresponds to the WINNT Shell
// passed into this function.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CImpersonateLoggedOnUser::FindShellModuleInProcess( LPCTSTR pszShellName, HANDLE hProcess, HMODULE*& phModules, DWORD& dwModuleArraySize, CPSAPI *a_psapi ) { BOOL fReturn = FALSE, fRetryEnumModules = FALSE; DWORD cbDataReturned = 0;
TCHAR szModuleName[MAX_PATH];
if (dwModuleArraySize == 0) { ReallocModuleHandleArray( phModules, dwModuleArraySize ); }
do { // Get a list of the process HMODULEs and for each HMODULE, get
// the base file name.
if ( a_psapi->EnumProcessModules( hProcess, phModules, dwModuleArraySize, &cbDataReturned ) ) {
// Because m_pfnEnumProcessModules will NOT fail if there are more process
// modules than bytes available in the array, if the amount returned is
// the same size as the array, realloc the array and retry the Enum.
if ( dwModuleArraySize == cbDataReturned ) { fRetryEnumModules = ReallocModuleHandleArray( phModules, dwModuleArraySize ); } else { fRetryEnumModules = FALSE; }
// Only walk the array if we don't need to retry the enum
if ( !fRetryEnumModules ) { DWORD dwModuleCtr = 0;
// Executable name always returned in entry 0
if ( a_psapi->GetModuleBaseName( hProcess, phModules[dwModuleCtr], szModuleName, sizeof(szModuleName) ) ) { fReturn = ( lstrcmpi( pszShellName, szModuleName ) == 0 ); }
} // If !fRetryEnumModules
} // if EnumProcessModules
} while ( fRetryEnumModules );
return fReturn; }
DWORD CImpersonateLoggedOnUser::AdjustSecurityDescriptorOfImpersonatedToken( CSid& csidSidOfCurrentProcess ) { DWORD dwRet = E_FAIL; // Get the thread token...
CThreadToken ctt; if ( ctt.IsValidToken () ) { // Obtain access to its security descriptor...
CSecureKernelObj sko(ctt.GetTokenHandle(), FALSE); // Modify the security descriptor...
if(sko.AddDACLEntry( csidSidOfCurrentProcess, ENUM_ACCESS_ALLOWED_ACE_TYPE, TOKEN_ALL_ACCESS, 0, NULL, NULL)) { dwRet = sko.ApplySecurity( DACL_SECURITY_INFORMATION); } }
return dwRet ; }
//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : CImpersonateLoggedOnUser::ImpersonateUser
//
// DESCRIPTION : Attempts to impersonate the user.
//
// INPUTS : None.
//
// OUTPUTS : None.
//
// RETURNS : BOOL TRUE/FALSE - Success/Failure
//
// COMMENTS : Opens the security token of the Shell Process and
// uses it to try to impersonate the user.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CImpersonateLoggedOnUser::ImpersonateUser( void ) { BOOL fRet = FALSE; // Make sure we have a shell process
if (m_hShellProcess) { CSid csidCurrentProcess; if(GetCurrentProcessSid(csidCurrentProcess)) { // Get the Process User token if we don't have one (token of the explorer process).
//Removed the TOKEN_ALL_ACCESS desired access mask to this call as Winmgmt(Local system) can't open the token of the
//shell process (with all access rights)if the logged-in user is an Admin. So open the token with 'desired access' sufficient
//enough to use it for impersonation only.
if (m_hUserToken || OpenProcessToken(m_hShellProcess, TOKEN_READ | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, &m_hUserToken)) { // Now we should have what we need. Impersonate the user.
HANDLE hCurThread = ::GetCurrentThread () ;
CProcessToken cpt ( m_hUserToken ); if ( cpt.IsValidToken () ) { TOKEN_TYPE type; if ( cpt.GetTokenType ( type ) ) { if ( TokenPrimary == type ) { CToken ct; if ( ct.Duplicate ( cpt, FALSE ) ) { if( ::SetThreadToken ( &hCurThread, ct.GetTokenHandle () ) ) { m_fImpersonatingUser = TRUE ; } } } else { if( ::SetThreadToken ( &hCurThread, cpt.GetTokenHandle () ) ) { m_fImpersonatingUser = TRUE ; } }
if ( m_fImpersonatingUser ) { if(AdjustSecurityDescriptorOfImpersonatedToken ( csidCurrentProcess ) == ERROR_SUCCESS) { fRet = TRUE; } else { Revert () ; } } } } } } } return (m_fImpersonatingUser = fRet); }
//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : CImpersonateLoggedOnUser::Revert
//
// DESCRIPTION : Attempts to revert to self.
//
// INPUTS : None.
//
// OUTPUTS : None.
//
// RETURNS : BOOL TRUE/FALSE - Success/Failure
//
// COMMENTS : If we're impersonating a user, we now revert to
// ourselves.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CImpersonateLoggedOnUser::Revert( void ) { HRESULT hRes = WBEM_E_FAILED ; BOOL bRet = FALSE ; // See if we're currently impersonating prior to reverting.
if (m_fImpersonatingUser) { // Now get back to to the previous impersonation or impersonate the DCOM client.
if ( m_hThreadToken != INVALID_HANDLE_VALUE ) { HANDLE hCurThread = ::GetCurrentThread () ;
CThreadToken cpt ( m_hThreadToken ); if ( cpt.IsValidToken () ) { TOKEN_TYPE type; if ( cpt.GetTokenType ( type ) ) { if ( TokenPrimary == type ) { CToken ct; if ( ct.Duplicate ( cpt, FALSE ) ) { bRet = ::SetThreadToken ( &hCurThread, ct.GetTokenHandle () ); } } else { bRet = ::SetThreadToken ( &hCurThread, cpt.GetTokenHandle () ) ; }
if (!bRet) { throw CFramework_Exception(L"SetThreadToken failed", GetLastError()); } } } } else { hRes = WbemCoImpersonateClient();
if (FAILED(hRes)) { throw CFramework_Exception(L"WbemCoImpersonateClient failed", hRes); } }
if (SUCCEEDED(hRes) || hRes == E_NOTIMPL || bRet ) { m_fImpersonatingUser = FALSE; } } return ( !m_fImpersonatingUser ); }
//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : CImpersonateLoggedOnUser::ReallocProcessIdArray
//
// DESCRIPTION : Helper function to alloc a process id array.
//
// INPUTS : None.
//
// OUTPUTS : PDWORD& pdwProcessIds - Process Id Array pointer
// DWORD& dwArraySize - Size of array in bytes
//
// RETURNS : BOOL TRUE/FALSE - Success/Failure
//
// COMMENTS : Call when we need to realloc our process id array.
// This will grow the array by a fixed size, but not
// preserve values.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CImpersonateLoggedOnUser::ReallocProcessIdArray( PDWORD& pdwProcessIds, DWORD& dwArraySize ) { DWORD dwNewArraySize = dwArraySize + ( PROCESSID_ARRAY_BLOCKSIZE * sizeof(DWORD) ); PDWORD pdwNewArray = new DWORD[dwNewArraySize];
// Make sure the allocation succeeded before overwriting any existing values.
if ( NULL != pdwNewArray ) { if ( NULL != pdwProcessIds ) { delete [] pdwProcessIds; }
pdwProcessIds = pdwNewArray; dwArraySize = dwNewArraySize; } else { throw CHeap_Exception ( CHeap_Exception :: E_ALLOCATION_ERROR ) ; }
return ( NULL != pdwNewArray ); }
//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : CImpersonateLoggedOnUser::ReallocModuleHandleArray
//
// DESCRIPTION : Helper function to alloc a module handle array.
//
// INPUTS : None.
//
// OUTPUTS : HMODULE*& phModules - Module Handle Array pointer
// DWORD& dwArraySize - size of array in bytes
//
// RETURNS : BOOL TRUE/FALSE - Success/Failure
//
// COMMENTS : Call when we need to realloc our module handle array.
// This will grow the array by a fixed size, but not
// preserve values.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CImpersonateLoggedOnUser::ReallocModuleHandleArray( HMODULE*& phModules, DWORD& dwArraySize ) { DWORD dwNewArraySize = dwArraySize + ( HMODULE_ARRAY_BLOCKSIZE * sizeof(HMODULE) ); HMODULE* phNewArray = new HMODULE[dwNewArraySize];
// Make sure the allocation succeeded before overwriting any existing values.
if ( NULL != phNewArray ) { if ( NULL != phModules ) { delete [] phModules; }
phModules = phNewArray; dwArraySize = dwNewArraySize; } else { throw CHeap_Exception ( CHeap_Exception :: E_ALLOCATION_ERROR ) ; }
return ( NULL != phNewArray ); }
bool CImpersonateLoggedOnUser::GetCurrentProcessSid(CSid& sidCurrentProcess) { bool fRet = false;
PBYTE pbuff = NULL;
// I am going to revert here in order to access the process's
// sid. This is not privileged information, so this doesn't
// present a security breach.
WbemCoRevertToSelf();
try { CProcessToken cpt(NULL, true, TOKEN_QUERY);
DWORD dwLen = 0; if(!::GetTokenInformation( cpt.GetTokenHandle(), // the PR0CESS token
TokenUser, NULL, 0L, &dwLen) && (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { pbuff = new BYTE[dwLen]; if(pbuff) { if(::GetTokenInformation( cpt.GetTokenHandle(), TokenUser, pbuff, dwLen, &dwLen)) { PTOKEN_USER ptu = (PTOKEN_USER)pbuff; CSid sidTemp(ptu->User.Sid); if(sidTemp.IsOK() && sidTemp.IsValid()) { sidCurrentProcess = sidTemp; fRet = true; } } delete pbuff; pbuff = NULL; } } } catch(...) { //on our way out not returning anything to user except failure
//can't do anything on this impersonation failure...
WbemCoImpersonateClient(); delete pbuff; pbuff = NULL; throw; }
HRESULT hr = WbemCoImpersonateClient() ;
if (FAILED(hr)) { throw CFramework_Exception(L"WbemCoImpersonateClient failed", hr); }
return fRet; }
#endif
|