Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

540 lines
13 KiB

/*++
Copyright (C) 1996-2001 Microsoft Corporation
Module Name:
Abstract:
History:
--*/
#include "precomp.h"
#include <assert.h>
#include <arrtempl.h>
#include <comutl.h>
#include <wbemcli.h>
#include <buffer.h>
#include "msmqhdr.h"
#include "msmqsend.h"
#include "msmqcomn.h"
#define MAXPROPS 10
#define MAXHASHSIZE 64
struct MsmqOutgoingMessage : MQMSGPROPS
{
MSGPROPID m_aPropID[MAXPROPS];
MQPROPVARIANT m_aPropVar[MAXPROPS];
MsmqOutgoingMessage( DWORD dwFlags, DWORD dwStatus,
LPBYTE pData, ULONG cData,
LPBYTE pHdr, ULONG cHdr,
HANDLE hSecCtx, LPCWSTR wszAckFormatName );
};
/*****************************************************************
CMsgMsmqSender
******************************************************************/
HRESULT CMsgMsmqSender::Open( LPCWSTR wszTarget,
DWORD dwFlags,
WMIMSG_SNDR_AUTH_INFOP pAuthInfo,
LPCWSTR wszResponse,
IWmiMessageTraceSink* pTraceSink,
IWmiMessageSendReceive** ppSend )
{
HRESULT hr;
*ppSend = NULL;
ENTER_API_CALL
if ( (dwFlags & WMIMSG_MASK_QOS) != WMIMSG_FLAG_QOS_EXPRESS &&
(dwFlags & WMIMSG_MASK_QOS) != WMIMSG_FLAG_QOS_GUARANTEED )
{
return WBEM_E_NOT_SUPPORTED;
}
CWbemPtr<CMsgMsmqSend> pSend;
pSend = new CMsgMsmqSend( m_pControl,
wszTarget,
dwFlags,
wszResponse,
pTraceSink );
if ( pSend == NULL )
{
return WBEM_E_OUT_OF_MEMORY;
}
if ( (dwFlags & WMIMSG_FLAG_SNDR_LAZY_INIT) == 0 )
{
hr = pSend->EnsureSender();
if ( FAILED(hr) )
{
return hr;
}
}
return pSend->QueryInterface(IID_IWmiMessageSendReceive, (void**)ppSend);
EXIT_API_CALL
}
/*****************************************************************
CMsgMsmqSend
******************************************************************/
#define CALLFUNC(FUNC) (*m_Api.m_fp ## FUNC )
CMsgMsmqSend::CMsgMsmqSend( CLifeControl* pCtl,
LPCWSTR wszTarget,
DWORD dwFlags,
LPCWSTR wszResponse,
IWmiMessageTraceSink* pTraceSink )
: CUnkBase<IWmiMessageSendReceive,&IID_IWmiMessageSendReceive>(pCtl),
m_bInit(FALSE), m_hQueue(NULL), m_dwFlags(dwFlags),
m_wsResponse( wszResponse ), m_hSecCtx( NULL ), m_pTraceSink( pTraceSink )
{
//
// save our computer name.
//
TCHAR achComputer[MAX_COMPUTERNAME_LENGTH+1];
ULONG ulSize = MAX_COMPUTERNAME_LENGTH+1;
GetComputerName( achComputer, &ulSize );
m_wsComputer = achComputer;
//
// if the target is NULL, then we use our computer name as the target.
//
if ( wszTarget != NULL && *wszTarget != '\0' )
{
m_wsTarget = wszTarget;
}
else
{
m_wsTarget = m_wsComputer;
}
}
CMsgMsmqSend::~CMsgMsmqSend()
{
Clear();
}
HRESULT CMsgMsmqSend::HandleTrace( HRESULT hr, IUnknown* pCtx )
{
if ( m_pTraceSink != NULL )
{
return m_pTraceSink->Notify( hr,
CLSID_WmiMessageMsmqSender,
m_wsTarget,
pCtx );
}
return WBEM_S_NO_ERROR;
}
void CMsgMsmqSend::Clear()
{
m_bInit = FALSE;
if ( m_hSecCtx != NULL )
{
CALLFUNC(MQFreeSecurityContext)( m_hSecCtx );
m_hSecCtx = NULL;
}
if ( m_hQueue != NULL )
{
CALLFUNC(MQCloseQueue)( m_hQueue );
m_hQueue = NULL;
}
}
HRESULT CMsgMsmqSend::EnsureSender()
{
HRESULT hr;
CInCritSec ics(&m_cs);
if ( m_bInit )
{
return WBEM_S_NO_ERROR;
}
hr = m_Api.Initialize();
if ( FAILED(hr) )
{
return hr; // MSMQ probably isn't installed.
}
hr = EnsureMsmqService( m_Api );
if ( FAILED(hr) )
{
return hr;
}
Clear();
WString wsFormatName;
hr = NormalizeQueueName( m_Api, m_wsTarget, wsFormatName );
if ( FAILED(hr) )
{
return MqResToWmiRes( hr, WMIMSG_E_INVALIDADDRESS );
}
hr = CALLFUNC(MQOpenQueue)( wsFormatName,
MQ_SEND_ACCESS,
MQ_DENY_NONE,
&m_hQueue );
if ( FAILED(hr) )
{
return MqResToWmiRes( hr, WMIMSG_E_TARGETNOTFOUND );
}
if ( m_dwFlags & WMIMSG_FLAG_SNDR_AUTHENTICATE )
{
//
// get security context for process account.
//
hr = CALLFUNC(MQGetSecurityContext)( NULL, 0, &m_hSecCtx );
if ( FAILED(hr) )
{
return MqResToWmiRes( hr, WMIMSG_E_AUTHFAILURE );
}
}
//
// this will be used to sign our hdr so that a local receiver ( such as
// an ack receiver can verify this machine sent it ).
//
hr = CSignMessage::Create( L"WMIMSG", &m_pSign );
if ( FAILED(hr) )
{
return hr;
}
m_bInit = TRUE;
return hr;
}
MsmqOutgoingMessage::MsmqOutgoingMessage( DWORD dwFlags, DWORD dwStatus,
LPBYTE pData, ULONG cData,
LPBYTE pHdr, ULONG cHdr,
HANDLE hSecCtx,
LPCWSTR wszAckFormatName )
{
cProp = 0;
aPropID = m_aPropID;
aPropVar = m_aPropVar;
aStatus= NULL;
m_aPropID[cProp] = PROPID_M_BODY;
m_aPropVar[cProp].vt = VT_VECTOR | VT_UI1;
m_aPropVar[cProp].caub.cElems = cData;
m_aPropVar[cProp].caub.pElems = pData;
cProp++;
m_aPropID[cProp] = PROPID_M_EXTENSION;
m_aPropVar[cProp].vt = VT_VECTOR | VT_UI1;
m_aPropVar[cProp].caub.cElems = cHdr;
m_aPropVar[cProp].caub.pElems = pHdr;
cProp++;
m_aPropID[cProp] = PROPID_M_APPSPECIFIC;
m_aPropVar[cProp].vt = VT_UI4;
m_aPropVar[cProp].ulVal = dwStatus;
cProp++;
if ( wszAckFormatName != NULL && *wszAckFormatName != '\0')
{
m_aPropID[cProp] = PROPID_M_ACKNOWLEDGE;
m_aPropVar[cProp].vt = VT_UI1;
m_aPropVar[cProp].bVal = MQMSG_ACKNOWLEDGMENT_NACK_RECEIVE;
cProp++;
m_aPropID[cProp] = PROPID_M_ADMIN_QUEUE;
m_aPropVar[cProp].vt = VT_LPWSTR;
m_aPropVar[cProp].pwszVal = LPWSTR(wszAckFormatName);
cProp++;
}
if ( (dwFlags & WMIMSG_MASK_QOS) != WMIMSG_FLAG_QOS_EXPRESS )
{
m_aPropID[cProp] = PROPID_M_DELIVERY;
m_aPropVar[cProp].vt = VT_UI1;
m_aPropVar[cProp].bVal = MQMSG_DELIVERY_RECOVERABLE;
cProp++;
}
if ( dwFlags & WMIMSG_FLAG_SNDR_AUTHENTICATE )
{
m_aPropID[cProp] = PROPID_M_AUTH_LEVEL;
m_aPropVar[cProp].vt = VT_UI4;
m_aPropVar[cProp].ulVal = MQMSG_AUTH_LEVEL_ALWAYS;
cProp++;
#ifndef _WIN64
m_aPropID[cProp] = PROPID_M_SECURITY_CONTEXT;
m_aPropVar[cProp].vt = VT_UI4;
m_aPropVar[cProp].ulVal = ULONG(hSecCtx);
cProp++;
#endif
}
if ( dwFlags & WMIMSG_FLAG_SNDR_ENCRYPT )
{
m_aPropID[cProp] = PROPID_M_PRIV_LEVEL;
m_aPropVar[cProp].vt = VT_UI4;
m_aPropVar[cProp].ulVal = MQMSG_PRIV_LEVEL_BODY;
cProp++;
}
}
HRESULT CMsgMsmqSend::SendReceive( PBYTE pData,
ULONG cData,
PBYTE pAuxData,
ULONG cAuxData,
DWORD dwFlagStatus,
IUnknown* pCtx )
{
ENTER_API_CALL
HRESULT hr;
hr = Send( pData, cData, pAuxData, cAuxData, dwFlagStatus, pCtx );
if ( FAILED(hr) )
{
HandleTrace( hr, pCtx );
return hr;
}
return HandleTrace( hr, pCtx );
EXIT_API_CALL
}
HRESULT CMsgMsmqSend::Send( PBYTE pData,
ULONG cData,
PBYTE pAuxData,
ULONG cAuxData,
DWORD dwFlagStatus,
IUnknown* pCtx )
{
HRESULT hr;
hr = EnsureSender();
if ( FAILED(hr) )
{
return hr;
}
DWORD dwStatus = 0;
ULONG cHash = MAXHASHSIZE;
BYTE achHash[MAXHASHSIZE];
if ( m_dwFlags & WMIMSG_FLAG_SNDR_PRIV_SIGN )
{
//
// create a 'private' hash on the data. this will be used by local
// receivers to verify that it sent the message and that it has not
// been tampered with.
//
hr = m_pSign->Sign( pData, cData, achHash, cHash );
if ( FAILED(hr) )
{
return hr;
}
}
else
{
cHash = 0;
}
//
// now we can create our msmq msg header. this header will be prepended
// to the users header in auxdata. We do not create our own header if
// this is an ack sender because AuxData already contains an msmq msg hdr.
// For nacks, msmq will return data as sent, so we want to remain
// consistent with this for our 'application' level acks.
//
BYTE achHdr[512];
CBuffer HdrStrm( achHdr, 512, FALSE );
CMsgMsmqHdr MsmqHdr( m_wsTarget, m_wsComputer, achHash, cHash, cAuxData );
if ( (m_dwFlags & WMIMSG_FLAG_SNDR_ACK) == 0 )
{
hr = MsmqHdr.Persist( HdrStrm );
if ( FAILED(hr) )
{
return hr;
}
}
else
{
//
// the flagstatus param contains the status.
//
dwStatus = dwFlagStatus;
}
hr = HdrStrm.Write( pAuxData, cAuxData, NULL );
if ( FAILED(hr) )
{
return hr;
}
if ( m_dwFlags & WMIMSG_FLAG_SNDR_PRIV_SIGN )
{
//
// hash the entire header and store it at the end of the header.
//
hr = m_pSign->Sign( HdrStrm.GetRawData(),
HdrStrm.GetIndex(),
achHash,
cHash );
if ( FAILED(hr) )
{
return hr;
}
}
else
{
cHash = 0;
}
hr = HdrStrm.Write( &cHash, sizeof(DWORD), NULL );
if ( FAILED(hr) )
{
return hr;
}
hr = HdrStrm.Write( achHash, cHash, NULL );
if ( FAILED(hr) )
{
return hr;
}
//
// Obtain the correct ITransaction ptr. If the user did not specify
// a txn and we are sending using xact qos, then we need to use the
// single message txn.
//
CWbemPtr<ITransaction> pTxn;
if ( pCtx != NULL )
{
pCtx->QueryInterface( IID_ITransaction, (void**)&pTxn );
}
DWORD dwQos = m_dwFlags & WMIMSG_MASK_QOS;
if ( pTxn == NULL && dwQos == WMIMSG_FLAG_QOS_XACT )
{
pTxn = MQ_SINGLE_MESSAGE;
}
HANDLE hSecCtx = m_hSecCtx;
if ( m_dwFlags & WMIMSG_FLAG_SNDR_AUTHENTICATE )
{
//
// Check to see if we're impersonating. If so, then we need to
// obtain the MSMQ security context and use it when sending the
// message.
//
HANDLE hToken;
if ( OpenThreadToken( GetCurrentThread(),
TOKEN_QUERY,
TRUE,
&hToken ) )
{
CloseHandle( &hToken );
//
// we are forgiving when encountering errors with authentication
// on the send side. This is because the receiving end might not
// even care about authentication ( and msmq doesn't have mutual
// auth ). If the target cares about auth, then we'll be sure to
// find out via a nack msg.
//
hr = CALLFUNC(MQGetSecurityContext)( NULL, 0, &hSecCtx );
if ( FAILED(hr) )
{
return MqResToWmiRes( hr, WMIMSG_E_AUTHFAILURE );
}
}
}
MsmqOutgoingMessage Msg( m_dwFlags,
dwStatus,
pData,
cData,
HdrStrm.GetRawData(),
HdrStrm.GetIndex(),
hSecCtx,
m_wsResponse );
hr = CALLFUNC(MQSendMessage)( m_hQueue, &Msg, pTxn );
if( hSecCtx != m_hSecCtx && hSecCtx != NULL )
{
CALLFUNC(MQFreeSecurityContext)( hSecCtx );
}
if ( FAILED(hr) )
{
//
// this is so the next call will reset us.
//
Clear();
return MqResToWmiRes( hr );
}
return hr;
}