/*++ Copyright (C) 1996-2001 Microsoft Corporation Module Name: Abstract: History: --*/ #include "precomp.h" #include #include #include #include #include #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 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(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 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; }