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.
613 lines
17 KiB
613 lines
17 KiB
|
|
#include "precomp.h"
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <buffer.h>
|
|
#include <wbemutil.h>
|
|
#include <sddl.h>
|
|
#include "fwdhdr.h"
|
|
#include "fconsink.h"
|
|
#include "fconsend.h"
|
|
|
|
LPCWSTR g_wszQos = L"ForwardingQos";
|
|
LPCWSTR g_wszAuth = L"Authenticate";
|
|
LPCWSTR g_wszEncrypt = L"Encryption";
|
|
LPCWSTR g_wszTargets = L"Targets";
|
|
LPCWSTR g_wszName = L"Name";
|
|
LPCWSTR g_wszTargetSD = L"TargetSD";
|
|
LPCWSTR g_wszSendSchema = L"IncludeSchema";
|
|
|
|
typedef BOOL (APIENTRY*PStringSDToSD)(
|
|
LPCWSTR StringSecurityDescriptor,
|
|
DWORD StringSDRevision,
|
|
PSECURITY_DESCRIPTOR *SecurityDescriptor,
|
|
PULONG SecurityDescriptorSize );
|
|
|
|
#define OPTIMAL_MESSAGE_SIZE 0x4000
|
|
|
|
class CTraceSink
|
|
: public CUnkBase< IWmiMessageTraceSink, &IID_IWmiMessageTraceSink >
|
|
{
|
|
CFwdConsSink* m_pOwner;
|
|
|
|
public:
|
|
|
|
CTraceSink( CFwdConsSink* pOwner ) : m_pOwner( pOwner ) { }
|
|
|
|
STDMETHOD(Notify)( HRESULT hRes,
|
|
GUID guidSource,
|
|
LPCWSTR wszTrace,
|
|
IUnknown* pContext )
|
|
{
|
|
return m_pOwner->Notify( hRes, guidSource, wszTrace, pContext );
|
|
}
|
|
};
|
|
|
|
/****************************************************************************
|
|
CFwdConsSink
|
|
*****************************************************************************/
|
|
|
|
CFwdConsSink::~CFwdConsSink()
|
|
{
|
|
if ( m_pTargetSD != NULL )
|
|
{
|
|
LocalFree( m_pTargetSD );
|
|
}
|
|
}
|
|
|
|
HRESULT CFwdConsSink::Initialize( CFwdConsNamespace* pNspc,
|
|
IWbemClassObject* pCons )
|
|
{
|
|
HRESULT hr;
|
|
CPropVar vQos, vAuth, vEncrypt, vTargets, vName, vSendSchema, vTargetSD;
|
|
|
|
m_pNamespace = pNspc;
|
|
|
|
//
|
|
// initialize multi sender. each forwarding consumer can
|
|
// contain multiple targets.
|
|
//
|
|
|
|
hr = CoCreateInstance( CLSID_WmiMessageMultiSendReceive,
|
|
NULL,
|
|
CLSCTX_INPROC,
|
|
IID_IWmiMessageMultiSendReceive,
|
|
(void**)&m_pMultiSend );
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
|
|
hr = CoCreateInstance( CLSID_WmiSmartObjectMarshal,
|
|
NULL,
|
|
CLSCTX_INPROC,
|
|
IID_IWmiObjectMarshal,
|
|
(void**)&m_pMrsh );
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// initialize internal props from forwarding consumer props.
|
|
//
|
|
|
|
hr = pCons->Get( g_wszQos, 0, &vQos, NULL, NULL );
|
|
|
|
if ( FAILED(hr) || FAILED( hr=vQos.SetType(VT_UI4) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if ( V_UI4(&vQos) != WMIMSG_FLAG_QOS_SYNCHRONOUS )
|
|
{
|
|
return WBEM_E_VALUE_OUT_OF_RANGE;
|
|
}
|
|
|
|
hr = pCons->Get( g_wszName, 0, &vName, NULL, NULL );
|
|
|
|
if ( FAILED(hr) || FAILED( hr=vName.CheckType(VT_BSTR) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
m_wsName = V_BSTR(&vName);
|
|
|
|
hr = pCons->Get( g_wszTargetSD, 0, &vTargetSD, NULL, NULL );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if ( V_VT(&vTargetSD) != VT_NULL )
|
|
{
|
|
if ( V_VT(&vTargetSD) != VT_BSTR )
|
|
{
|
|
return WBEM_E_INVALID_OBJECT;
|
|
}
|
|
|
|
//
|
|
// convert the SD string to a relative SD. The function to do this
|
|
// needs to be dynamically loaded because its w2k+ only.
|
|
//
|
|
|
|
HMODULE hMod = LoadLibrary( TEXT("advapi32") );
|
|
|
|
if ( hMod != NULL )
|
|
{
|
|
PStringSDToSD fpTextToSD;
|
|
|
|
fpTextToSD = (PStringSDToSD)GetProcAddress( hMod,
|
|
"ConvertStringSecurityDescriptorToSecurityDescriptorW" );
|
|
|
|
if ( fpTextToSD != NULL )
|
|
{
|
|
if ( (*fpTextToSD)( V_BSTR(&vTargetSD),
|
|
SDDL_REVISION_1,
|
|
&m_pTargetSD,
|
|
&m_cTargetSD ) )
|
|
{
|
|
hr = WBEM_S_NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = WBEM_E_NOT_SUPPORTED;
|
|
}
|
|
|
|
FreeLibrary( hMod );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return WBEM_E_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
hr = pCons->Get( g_wszAuth, 0, &vAuth, NULL, NULL );
|
|
|
|
if ( FAILED(hr) || FAILED( hr=vAuth.SetType(VT_BOOL) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = pCons->Get( g_wszEncrypt, 0, &vEncrypt, NULL, NULL );
|
|
|
|
if ( FAILED(hr) || FAILED( hr=vEncrypt.SetType(VT_BOOL) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = pCons->Get( g_wszSendSchema, 0, &vSendSchema, NULL, NULL );
|
|
|
|
if ( FAILED(hr) || FAILED( hr=vSendSchema.SetType(VT_BOOL) ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
m_dwFlags = V_UI4(&vQos);
|
|
m_dwFlags |= V_BOOL(&vAuth)==VARIANT_TRUE ?WMIMSG_FLAG_SNDR_AUTHENTICATE:0;
|
|
m_dwFlags |= V_BOOL(&vEncrypt)==VARIANT_TRUE ? WMIMSG_FLAG_SNDR_ENCRYPT:0;
|
|
|
|
m_dwCurrentMrshFlags = WMIMSG_FLAG_MRSH_FULL_ONCE;
|
|
m_dwDisconnectedMrshFlags = V_BOOL(&vSendSchema) == VARIANT_TRUE ?
|
|
WMIMSG_FLAG_MRSH_FULL : WMIMSG_FLAG_MRSH_PARTIAL;
|
|
|
|
//
|
|
// create a trace sink for receiving callbacks from wmimsg. Note that
|
|
// this sink's lifetime must be decoupled from this objects, else we'd
|
|
// end up with a circular ref.
|
|
//
|
|
CWbemPtr<CTraceSink> pInternalTraceSink = new CTraceSink( this );
|
|
|
|
if ( pInternalTraceSink == NULL )
|
|
{
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
}
|
|
|
|
CWbemPtr<IWmiMessageTraceSink> pTraceSink;
|
|
hr = pInternalTraceSink->QueryInterface( IID_IWmiMessageTraceSink,
|
|
(void**)&pTraceSink );
|
|
_DBG_ASSERT( SUCCEEDED(hr) );
|
|
|
|
//
|
|
// targets array can be null, in which case we treat it as if the array
|
|
// had one element, the empty string.
|
|
//
|
|
|
|
hr = pCons->Get( g_wszTargets, 0, &vTargets, NULL, NULL );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if ( V_VT(&vTargets) != VT_NULL )
|
|
{
|
|
if ( FAILED(hr=vTargets.CheckType(VT_ARRAY|VT_BSTR) ) )
|
|
{
|
|
return WBEM_E_INVALID_OBJECT;
|
|
}
|
|
|
|
CPropSafeArray<BSTR> aTargets( V_ARRAY(&vTargets) );
|
|
|
|
//
|
|
// create all the fwd cons senders for the targets.
|
|
//
|
|
|
|
for( ULONG i=0; i < aTargets.Length(); i++ )
|
|
{
|
|
CWbemPtr<IWmiMessageSendReceive> pSend;
|
|
|
|
hr = CFwdConsSend::Create( m_pControl,
|
|
aTargets[i],
|
|
m_dwFlags,
|
|
m_pNamespace->GetSvc(),
|
|
pTraceSink,
|
|
&pSend );
|
|
if ( FAILED(hr) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = m_pMultiSend->Add( 0, pSend );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CWbemPtr<IWmiMessageSendReceive> pSend;
|
|
|
|
hr = CFwdConsSend::Create( m_pControl,
|
|
L"",
|
|
m_dwFlags,
|
|
m_pNamespace->GetSvc(),
|
|
pTraceSink,
|
|
&pSend );
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = m_pMultiSend->Add( 0, pSend );
|
|
}
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return WBEM_S_NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// this method handles all sending/marshaling errors internally and will
|
|
// return either S_OK when all objects are processed or S_FALSE
|
|
// if only some are processed.
|
|
//
|
|
|
|
HRESULT CFwdConsSink::IndicateSome( IWbemClassObject* pConsumer,
|
|
long cObjs,
|
|
IWbemClassObject** ppObjs,
|
|
long* pcProcessed )
|
|
{
|
|
HRESULT hr;
|
|
|
|
_DBG_ASSERT( cObjs > 0 );
|
|
|
|
//
|
|
// create an execution id for this indicate.
|
|
//
|
|
|
|
GUID guidExecution;
|
|
CoCreateGuid( &guidExecution );
|
|
|
|
//
|
|
// marshal the events. we will stop marshaling them when the buffer
|
|
// gets bigger than it should for an optimally sized message.
|
|
//
|
|
|
|
BYTE achData[512];
|
|
BYTE achHdr[256];
|
|
|
|
CBuffer DataStrm( achData, 512, FALSE );
|
|
CBuffer HdrStrm( achHdr, 256, FALSE );
|
|
|
|
//
|
|
// we remembered our last buffer size, so set to that in the hopes that
|
|
// we can avoid a retry on the packing.
|
|
//
|
|
|
|
hr = DataStrm.SetSize( m_ulLastDataSize );
|
|
m_ulLastDataSize = 0;
|
|
|
|
ULONG i;
|
|
|
|
for( i = 0; i < cObjs && SUCCEEDED(hr); i++ )
|
|
{
|
|
ULONG cUsed;
|
|
PBYTE pData = DataStrm.GetRawData();
|
|
ULONG cData = DataStrm.GetSize();
|
|
ULONG iData = DataStrm.GetIndex();
|
|
|
|
if ( iData < OPTIMAL_MESSAGE_SIZE )
|
|
{
|
|
hr = m_pMrsh->Pack( ppObjs[i],
|
|
m_pNamespace->GetName(),
|
|
m_dwCurrentMrshFlags,
|
|
cData-iData,
|
|
pData+iData,
|
|
&cUsed );
|
|
|
|
if ( hr == WBEM_E_BUFFER_TOO_SMALL )
|
|
{
|
|
hr = DataStrm.SetSize( iData + cUsed );
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
pData = DataStrm.GetRawData();
|
|
cData = DataStrm.GetSize();
|
|
|
|
hr = m_pMrsh->Pack( ppObjs[i],
|
|
m_pNamespace->GetName(),
|
|
m_dwCurrentMrshFlags,
|
|
cData-iData,
|
|
pData+iData,
|
|
&cUsed);
|
|
}
|
|
}
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
DataStrm.Advance( cUsed );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// at this point, we know how many events we've actually processed
|
|
// i will always be the number of objects successfully processed.
|
|
// we want to try to separate out the events that fail to be packed
|
|
// from ones that are packed. For this reason, pretend we didn't event
|
|
// process the one that failed, unless it is the first one.
|
|
//
|
|
|
|
*pcProcessed = i > 0 ? i : 1;
|
|
|
|
//
|
|
// create a context object for this indicate. This is used to
|
|
// thread information through to the trace functions
|
|
// which are invoked by the senders.
|
|
//
|
|
|
|
CFwdContext Ctx( guidExecution, pConsumer, *pcProcessed, ppObjs );
|
|
|
|
if ( i > 0 ) // at least some were successfully processed.
|
|
{
|
|
m_ulLastDataSize = DataStrm.GetIndex();
|
|
|
|
//
|
|
// create and stream the msg header
|
|
//
|
|
|
|
CFwdMsgHeader Hdr( *pcProcessed,
|
|
m_dwFlags & WMIMSG_MASK_QOS,
|
|
m_dwFlags & WMIMSG_FLAG_SNDR_AUTHENTICATE,
|
|
m_dwFlags & WMIMSG_FLAG_SNDR_ENCRYPT,
|
|
guidExecution,
|
|
m_wsName,
|
|
m_pNamespace->GetName(),
|
|
PBYTE(m_pTargetSD),
|
|
m_cTargetSD );
|
|
|
|
hr = Hdr.Persist( HdrStrm );
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// send it and notify the tracing sink of the result. Always try
|
|
// once with return immediately set. This will try all the
|
|
// primary senders first.
|
|
//
|
|
|
|
hr = m_pMultiSend->SendReceive( DataStrm.GetRawData(),
|
|
DataStrm.GetIndex(),
|
|
HdrStrm.GetRawData(),
|
|
HdrStrm.GetIndex(),
|
|
WMIMSG_FLAG_MULTISEND_RETURN_IMMEDIATELY,
|
|
&Ctx );
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// o.k so all the primary ones failed, so now lets try all the
|
|
// senders.
|
|
//
|
|
|
|
hr = m_pMultiSend->SendReceive( DataStrm.GetRawData(),
|
|
DataStrm.GetIndex(),
|
|
HdrStrm.GetRawData(),
|
|
HdrStrm.GetIndex(),
|
|
0,
|
|
&Ctx );
|
|
}
|
|
}
|
|
}
|
|
|
|
m_pNamespace->HandleTrace( hr, &Ctx );
|
|
|
|
return *pcProcessed == cObjs ? S_OK : S_FALSE;
|
|
}
|
|
|
|
//
|
|
// this is where we get notified of every target send event. here, we look
|
|
// at the information and adjust our marshalers accordingly. we then pass the
|
|
// event onto the namespace sink for tracing purposes. NOTE: This solution of
|
|
// adjusting our marshalers on callbacks means that we're assuming a couple
|
|
// things about the send implementation .. 1 ) the notification of the send
|
|
// must be on the same control path as the send call. 2 ) the sender will
|
|
// use the same target when it has successfully sent to it previously (e.g it
|
|
// will not notify us that it sent to an rpc target, we then optimize our
|
|
// marshalers for it, then it chooses to send to an msmq target ).
|
|
//
|
|
|
|
HRESULT CFwdConsSink::Notify( HRESULT hRes,
|
|
GUID guidSource,
|
|
LPCWSTR wszTrace,
|
|
IUnknown* pContext )
|
|
{
|
|
HRESULT hr;
|
|
|
|
ENTER_API_CALL
|
|
|
|
if ( FAILED(hRes) )
|
|
{
|
|
//
|
|
// we failed sending to a target, flush any state the marshaler
|
|
// was keeping.
|
|
//
|
|
m_pMrsh->Flush();
|
|
}
|
|
|
|
//
|
|
// check that current marshaling flags against the type of sender that
|
|
// was used.
|
|
//
|
|
|
|
if ( guidSource == CLSID_WmiMessageRpcSender )
|
|
{
|
|
if ( SUCCEEDED(hRes) &&
|
|
m_dwCurrentMrshFlags != WMIMSG_FLAG_MRSH_FULL_ONCE )
|
|
{
|
|
//
|
|
// lets give schema once-only a whirl..
|
|
//
|
|
m_dwCurrentMrshFlags = WMIMSG_FLAG_MRSH_FULL_ONCE;
|
|
}
|
|
}
|
|
else // must be queueing
|
|
{
|
|
if ( m_dwCurrentMrshFlags == WMIMSG_FLAG_MRSH_FULL_ONCE )
|
|
{
|
|
//
|
|
// once only is not for messaging !! Its o.k. though
|
|
// because we are sure that we've only used it once
|
|
// and it did send the schema. Just don't use it again.
|
|
//
|
|
m_dwCurrentMrshFlags = m_dwDisconnectedMrshFlags;
|
|
}
|
|
}
|
|
|
|
//
|
|
// pass the call onto the namespace sink for tracing.
|
|
//
|
|
|
|
hr = m_pNamespace->Notify( hRes, guidSource, wszTrace, pContext );
|
|
|
|
EXIT_API_CALL
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFwdConsSink::IndicateToConsumer( IWbemClassObject* pConsumer,
|
|
long cObjs,
|
|
IWbemClassObject** ppObjs )
|
|
{
|
|
HRESULT hr;
|
|
|
|
ENTER_API_CALL
|
|
|
|
//
|
|
// If the security context of the event provider is maintained then
|
|
// we will use it to send the forwarded event.
|
|
//
|
|
CWbemPtr<IServerSecurity> pSec;
|
|
hr = CoGetCallContext( IID_IServerSecurity, (void**)&pSec );
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hr = pSec->ImpersonateClient();
|
|
|
|
if ( FAILED(hr) )
|
|
return hr;
|
|
}
|
|
|
|
long cProcessed = 0;
|
|
|
|
//
|
|
// IndicateSome() may send only a subset of the total indicated events.
|
|
// This is to avoid sending potentially huge messages. So we'll keep
|
|
// calling IndicateSome() until all messages are sent or there's an error.
|
|
//
|
|
|
|
do
|
|
{
|
|
cObjs -= cProcessed;
|
|
ppObjs += cProcessed;
|
|
|
|
hr = IndicateSome( pConsumer, cObjs, ppObjs, &cProcessed );
|
|
|
|
_DBG_ASSERT( FAILED(hr) || (SUCCEEDED(hr) && cProcessed > 0 ));
|
|
|
|
} while ( SUCCEEDED(hr) && cProcessed < cObjs );
|
|
|
|
if ( pSec != NULL )
|
|
pSec->RevertToSelf();
|
|
|
|
EXIT_API_CALL
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFwdConsSink::Create( CLifeControl* pCtl,
|
|
CFwdConsNamespace* pNspc,
|
|
IWbemClassObject* pCons,
|
|
IWbemUnboundObjectSink** ppSink )
|
|
{
|
|
HRESULT hr;
|
|
|
|
*ppSink = NULL;
|
|
|
|
CWbemPtr<CFwdConsSink> pSink = new CFwdConsSink( pCtl );
|
|
|
|
if ( pSink == NULL )
|
|
{
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
}
|
|
|
|
hr = pSink->Initialize( pNspc, pCons );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = pSink->QueryInterface( IID_IWbemUnboundObjectSink, (void**)ppSink );
|
|
|
|
assert( SUCCEEDED(hr) );
|
|
|
|
return WBEM_S_NO_ERROR;
|
|
}
|
|
|