Leaked source code of windows server 2003
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

#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;
}