mirror of https://github.com/UMSKT/xpmgr.git
264 lines
9.8 KiB
C++
264 lines
9.8 KiB
C++
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
|
|
#pragma once
|
|
|
|
class CFDProviderHelper
|
|
{
|
|
public:
|
|
CFDProviderHelper() : m_hToken(NULL), m_dwSessionId((DWORD) -1)
|
|
{
|
|
InitializeSRWLock(&srwTokenLock);
|
|
}
|
|
~CFDProviderHelper()
|
|
{
|
|
if ( NULL != m_hToken )
|
|
CloseHandle( m_hToken );
|
|
}
|
|
|
|
// Implementation
|
|
public:
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CoSetProxyBlanket
|
|
//
|
|
// Purpose:
|
|
// If the provider is running in a process that doesn't have permissions
|
|
// to call back into the client process by default then CoSetProxyBlanket
|
|
// must be called to enable impersonation.
|
|
// This function wraps the necessary calls and provides the correct CoSetProxyBlanket
|
|
// parameters in a single function.
|
|
//
|
|
// Arguments:
|
|
// pIUnk: Interface proxy to enable callbacks on
|
|
//
|
|
// Returns: S_OK on success, appropriate HRESULT otherwise
|
|
static HRESULT CoSetProxyBlanket( IUnknown *pIUnk )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IUnknown* pRealUnknown;
|
|
|
|
// Try to see if it's a COM proxy. If so, see if it's local to the process or a remote COM proxy.
|
|
// If it's remote, set the proxy blanket
|
|
ULONG_PTR ulRpcOptions = GetRpcOptions( pIUnk );
|
|
|
|
if ( SERVER_LOCALITY_PROCESS_LOCAL != ulRpcOptions )
|
|
{
|
|
hr = CoImpersonateClient();
|
|
if ( S_OK == hr )
|
|
{
|
|
hr = pIUnk->QueryInterface(IID_IUnknown, (LPVOID*)&pRealUnknown );
|
|
if ( S_OK == hr )
|
|
{
|
|
hr = ::CoSetProxyBlanket( pRealUnknown, RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_NONE, COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_STATIC_CLOAKING );
|
|
if ( S_OK == hr )
|
|
{
|
|
hr = ::CoSetProxyBlanket( pIUnk, RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_NONE, COLE_DEFAULT_PRINCIPAL,
|
|
RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_STATIC_CLOAKING );
|
|
}
|
|
if ( E_NOINTERFACE == hr )
|
|
hr = S_OK; // Provider can end up being inproc if we are in the fdPHost process. Ignore error and try to proceed.
|
|
|
|
pRealUnknown->Release();
|
|
}
|
|
if ( S_OK == hr )
|
|
hr = CoRevertToSelf();
|
|
else
|
|
CoRevertToSelf(); // Don't want the return code
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CoSetProxyBlanketWithThreadToken
|
|
//
|
|
// Purpose:
|
|
// If the provider is running in a process that doesn't have permissions
|
|
// to call back into the client process by default then CoSetProxyBlanket
|
|
// must be called to enable impersonation.
|
|
// This function wraps the necessary calls to CoSetProxyBlanket if the
|
|
// provider thread is from a thread pool instead of a thread called directly
|
|
// by FD.
|
|
// If you want to use this method you must call Initialize on the class instance.
|
|
// This is best done in IFunctionDiscoveryProvider::Initialize.
|
|
// Initialize must be called from one of the methods called by the client process.
|
|
//
|
|
// Arguments:
|
|
// pIUnk: Interface proxy to enable callbacks on
|
|
//
|
|
// Returns: S_OK on success, appropriate HRESULT otherwise
|
|
HRESULT CoSetProxyBlanketWithThreadToken( IUnknown *pIUnk )
|
|
{
|
|
if ( NULL == m_hToken )
|
|
return S_OK;
|
|
IUnknown* pRealUnknown;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
AcquireSRWLockShared(&srwTokenLock);
|
|
if ( !SetThreadToken( NULL, m_hToken ))
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
ReleaseSRWLockShared(&srwTokenLock);
|
|
|
|
if ( S_OK == hr )
|
|
{
|
|
hr = pIUnk->QueryInterface(IID_IUnknown, (LPVOID*)&pRealUnknown );
|
|
if ( S_OK == hr )
|
|
{
|
|
hr = ::CoSetProxyBlanket( pRealUnknown, RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_NONE, COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_STATIC_CLOAKING );
|
|
if (S_OK == hr)
|
|
hr = ::CoSetProxyBlanket( pIUnk, RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_NONE, COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_STATIC_CLOAKING );
|
|
if ( E_NOINTERFACE == hr )
|
|
hr = S_OK; // Provider can end up being inproc if we are in the fdPHost process. Ignore error and try to proceed.
|
|
|
|
pRealUnknown->Release();
|
|
}
|
|
if ( !SetThreadToken( NULL, NULL ))
|
|
{
|
|
if ( S_OK == hr )
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Initialize
|
|
//
|
|
// Purpose:
|
|
// If the provider is running in a process that doesn't have permissions
|
|
// to call back into the client process by default then CoSetProxyBlanket
|
|
// must be called to enable impersonation.
|
|
// CoSetProxyBlanketWithThreadToken wraps the calls necessary to do this.
|
|
// Before using that function you must call Initialize on the class instance.
|
|
// This is best done in IFunctionDiscoveryProvider::Initialize.
|
|
// Initialize must be called from one of the methods called by the client process.
|
|
//
|
|
// Note: There are three cases we we will not impersonate
|
|
// 1. If the caller is Local System we will not impersonate and subsequent
|
|
// calls to CoSetProxyBlanketWithThreadToken will effectively noop
|
|
// 2. Similary if the provider is actually running in-proc no proxy blanket
|
|
// is required and CoImpersonateClient will fail with:
|
|
// Code=0x80010117 (2147549463): RPC_E_CALL_COMPLETE: "Call context cannot be accessed after call completed."
|
|
// Severity=FAILURE; Code=279 (0x117); Facility=1 (0x1) (FACILITY_RPC)
|
|
// Handle this case gracefully and effectively noop on the
|
|
// CoSetProxyBlanketWithThreadToken as well.
|
|
// 3. If we get an inproc proxy to the provider we detect that and subsequent
|
|
// calls to CoSetProxyBlanketWithThreadToken will effectively noop.
|
|
//
|
|
// Returns: S_OK on success, appropriate HRESULT otherwise
|
|
HRESULT Initialize( IUnknown *pIUnk )
|
|
{
|
|
if ( NULL != m_hToken )
|
|
return S_FALSE;
|
|
|
|
const SID LOCAL_SYSTEM_SID = { SID_REVISION, 1, {0,0,0,0,0,5}, SECURITY_LOCAL_SYSTEM_RID };
|
|
|
|
HRESULT hr = CoImpersonateClient();
|
|
|
|
if ( S_OK == hr )
|
|
{
|
|
BOOL isLocalSystem = FALSE;
|
|
BOOL isLocalCall = (GetRpcOptions( pIUnk ) == SERVER_LOCALITY_PROCESS_LOCAL);
|
|
|
|
if (!isLocalCall)
|
|
{
|
|
if ( !CheckTokenMembership( NULL, (PSID)&LOCAL_SYSTEM_SID, &isLocalSystem ))
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
}
|
|
|
|
// Only save the impersonation info if
|
|
// this is not a local call, and the caller is not System.
|
|
if ( (S_OK == hr) && (!isLocalCall) && (!isLocalSystem) )
|
|
{
|
|
AcquireSRWLockExclusive(&srwTokenLock);
|
|
if ( !OpenThreadToken( GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_QUERY, FALSE, &m_hToken ))
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
if ( S_OK == hr )
|
|
{
|
|
DWORD dwSizeReturned;
|
|
if ( !GetTokenInformation(m_hToken, TokenSessionId, &m_dwSessionId, sizeof(m_dwSessionId), &dwSizeReturned ))
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
ReleaseSRWLockExclusive(&srwTokenLock);
|
|
}
|
|
if ( S_OK == hr )
|
|
hr = CoRevertToSelf();
|
|
else
|
|
CoRevertToSelf(); // Don't want the return code
|
|
}
|
|
else if ( RPC_E_CALL_COMPLETE == hr )
|
|
{
|
|
hr = S_OK; // InProc
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOL ReleaseToken(DWORD dwSessionId)
|
|
{
|
|
BOOL bMatch = FALSE;
|
|
|
|
AcquireSRWLockExclusive( &srwTokenLock );
|
|
if ( NULL != m_hToken && dwSessionId == m_dwSessionId )
|
|
{
|
|
CloseHandle( m_hToken );
|
|
m_hToken = NULL;
|
|
bMatch = TRUE;
|
|
}
|
|
ReleaseSRWLockExclusive( &srwTokenLock );
|
|
|
|
return bMatch;
|
|
}
|
|
VOID ReleaseToken()
|
|
{
|
|
AcquireSRWLockExclusive( &srwTokenLock );
|
|
if ( NULL != m_hToken)
|
|
{
|
|
CloseHandle( m_hToken );
|
|
m_hToken = NULL;
|
|
m_dwSessionId = (DWORD) -1;
|
|
}
|
|
ReleaseSRWLockExclusive( &srwTokenLock );
|
|
}
|
|
|
|
// Implementation
|
|
protected:
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// GetRpcOptions
|
|
//
|
|
// Purpose:
|
|
// Get the rpc options if this is a proxy.
|
|
//
|
|
// Arguments:
|
|
// pIUnk: Interface proxy.
|
|
//
|
|
// Returns: ULONG_PTR rpc options on success, assume it's inproc otherwise (SERVER_LOCALITY_PROCESS_LOCAL)
|
|
static ULONG_PTR GetRpcOptions( IUnknown *pIUnk )
|
|
{
|
|
ULONG_PTR ulOptions = SERVER_LOCALITY_PROCESS_LOCAL;
|
|
IRpcOptions* pRpcOptions;
|
|
|
|
HRESULT hr = pIUnk->QueryInterface( __uuidof( IRpcOptions ), reinterpret_cast<LPVOID*>( &pRpcOptions ) );
|
|
if ( S_OK == hr )
|
|
{
|
|
hr = pRpcOptions->Query( pIUnk, COMBND_SERVER_LOCALITY, &ulOptions );
|
|
pRpcOptions->Release();
|
|
}
|
|
return ulOptions;
|
|
}
|
|
|
|
// Attributes
|
|
protected:
|
|
DWORD m_dwSessionId;
|
|
HANDLE m_hToken;
|
|
SRWLOCK srwTokenLock;
|
|
};
|
|
|
|
|