#include "precomp.h" #include #include #include #include #include "fconsend.h" // flags below 0x10000 are reserved for msg implementations. #define FWD_FLAG_NO_INDIRECT 0x00010000 // {DDE18466-D244-4a5b-B91F-93D17E13178D} static const GUID g_guidQueueType = {0xdde18466, 0xd244, 0x4a5b, {0xb9, 0x1f, 0x93, 0xd1, 0x7e, 0x13, 0x17, 0x8d}}; HRESULT IsTcpIpAddress( LPCWSTR wszTarget ) { HRESULT hr; WSADATA wsaData; WORD wVersionRequested; wVersionRequested = MAKEWORD( 2, 2 ); int err = WSAStartup( wVersionRequested, &wsaData ); if ( err == 0 ) { IN_ADDR ia; // // convert unicode addr to ansi .. // int cTarget = wcstombs( NULL, wszTarget, 0 ); LPSTR szTarget = new char[cTarget+1]; if ( szTarget != NULL ) { wcstombs( szTarget, wszTarget, cTarget+1 ); ULONG in_ad = inet_addr( szTarget ); hr = in_ad == INADDR_NONE ? WBEM_S_FALSE : WBEM_S_NO_ERROR; delete [] szTarget; } else { hr = WBEM_E_OUT_OF_MEMORY; } WSACleanup(); } else { hr = WMIMSG_E_REQSVCNOTAVAIL; } return hr; } HRESULT GetDnsName( LPCWSTR wszTarget, LPWSTR* pwszDnsName ) { HRESULT hr; *pwszDnsName = NULL; // // first make sure winsock is initialized. // WSADATA wsaData; WORD wVersionRequested; wVersionRequested = MAKEWORD( 2, 2 ); int err = WSAStartup( wVersionRequested, &wsaData ); if ( err == 0 ) { IN_ADDR ia; HOSTENT* pHost; // // convert unicode addr to ansi .. // int cTarget = wcstombs( NULL, wszTarget, 0 ); LPSTR szTarget = new char[cTarget+1]; if ( szTarget != NULL ) { wcstombs( szTarget, wszTarget, cTarget+1 ); // // see if its an ip address ... // ULONG in_ad = inet_addr( szTarget ); if ( in_ad == INADDR_NONE ) { pHost = gethostbyname( szTarget ); } else { pHost = gethostbyaddr( (char*)&in_ad, 4, PF_INET ); } if ( pHost != NULL ) { int cDnsTarget = MultiByteToWideChar( CP_ACP, 0, pHost->h_name, -1, NULL, 0 ); *pwszDnsName = new WCHAR[cDnsTarget+1]; if ( *pwszDnsName != NULL ) { MultiByteToWideChar( CP_ACP, 0, pHost->h_name, -1, *pwszDnsName, cDnsTarget+1 ); hr = WBEM_S_NO_ERROR; } else { hr = WBEM_E_OUT_OF_MEMORY; } } else { hr = WMIMSG_E_INVALIDADDRESS; } delete [] szTarget; } else { hr = WBEM_E_OUT_OF_MEMORY; } WSACleanup(); } else { hr = WMIMSG_E_REQSVCNOTAVAIL; } return hr; } HRESULT GetSpn( LPCWSTR wszTarget, LPWSTR* wszSpn ) { HRESULT hr; *wszSpn = NULL; LPWSTR wszNormTarget; hr = GetDnsName( wszTarget, &wszNormTarget ); if ( FAILED(hr) ) return hr; CVectorDeleteMe vdm( wszNormTarget ); DWORD cSpn = 0; DWORD dwRes = DsMakeSpn( L"HOST", wszNormTarget, NULL, 0, NULL, &cSpn, NULL); if ( dwRes == ERROR_MORE_DATA || dwRes == ERROR_BUFFER_OVERFLOW ) { *wszSpn = new WCHAR[cSpn]; if ( *wszSpn != NULL ) { dwRes = DsMakeSpn( L"HOST", wszNormTarget, NULL, 0, NULL, &cSpn, *wszSpn ); if ( dwRes == ERROR_SUCCESS ) { hr = WBEM_S_NO_ERROR; } else { delete [] *wszSpn; *wszSpn = NULL; hr = HRESULT_FROM_WIN32( dwRes ); } } else { hr = WBEM_E_OUT_OF_MEMORY; } } else { hr = HRESULT_FROM_WIN32( dwRes ); } return hr; } /******************************************************************** CFwdConsSend *********************************************************************/ HRESULT CFwdConsSend::AddMSMQSender( LPCWSTR wszName ) { HRESULT hr; // // need to construct the Ack address for this sender. // TCHAR atchComputer[MAX_COMPUTERNAME_LENGTH+1]; DWORD cComputer = MAX_COMPUTERNAME_LENGTH+1; GetComputerName( atchComputer, &cComputer ); WCHAR awchComputer[MAX_COMPUTERNAME_LENGTH+1]; cComputer = MAX_COMPUTERNAME_LENGTH+1; tsz2wsz( atchComputer, awchComputer, &cComputer ); WString wsAckQueueName = L"DIRECT=OS:"; wsAckQueueName += awchComputer; wsAckQueueName += L"\\private$\\WMIFwdAck"; // // create the sender object and add it to the multisender. // CWbemPtr pSender; hr = CoCreateInstance( CLSID_WmiMessageMsmqSender, NULL, CLSCTX_INPROC, IID_IWmiMessageSender, (void**)&pSender ); if ( FAILED(hr) ) { return hr; } CWbemPtr pSend; DWORD dwFlags = m_dwFlags | WMIMSG_FLAG_SNDR_LAZY_INIT | WMIMSG_FLAG_SNDR_PRIV_SIGN | WMIMSG_FLAG_SNDR_NACK_ONLY; hr = pSender->Open( wszName, dwFlags, NULL, wsAckQueueName, m_pTraceSink, &pSend ); if ( FAILED(hr) ) { return hr; } return m_pMultiSend->Add( WMIMSG_FLAG_MULTISEND_TERMINATING_SENDER, pSend); } HRESULT CFwdConsSend::AddAsyncSender( LPCWSTR wszMachine ) { HRESULT hr; // // derive the msmq address from the target and add the sender. // WString wsQueueName; if ( (m_dwFlags & WMIMSG_FLAG_SNDR_ENCRYPT) == 0 ) { // // we can use a direct format name to identify the target. This is // the most flexible type of address besides a private format name, // but those cannot be derived. ( public pathnames can only be used // when online ) // wsQueueName = L"DIRECT="; hr = IsTcpIpAddress( wszMachine ); if ( FAILED(hr) ) { return hr; } if ( hr == WBEM_S_NO_ERROR ) { wsQueueName += L"TCP:"; } else { wsQueueName += L"OS:"; } wsQueueName += wszMachine; wsQueueName += L"\\private$\\"; } else { // // we must use a public queue pathname to reference the queue. this is // because msmq will not accept direct format names for encryption. // encryption is supported by msmq only when the sender has access to // ds. This means when this machine is offline, we cannot encrypt // messages. // wsQueueName = L"wszMachine\\"; } DWORD dwQos = m_dwFlags & WMIMSG_MASK_QOS; _DBG_ASSERT( dwQos != WMIMSG_FLAG_QOS_SYNCHRONOUS ); if( dwQos == WMIMSG_FLAG_QOS_EXPRESS ) { wsQueueName += L"WMIFwdExpress"; } else if( dwQos == WMIMSG_FLAG_QOS_GUARANTEED ) { wsQueueName += L"WMIFwdGuaranteed"; } else if ( dwQos == WMIMSG_FLAG_QOS_XACT ) { wsQueueName += L"WMIFwdXact"; } if ( m_dwFlags & WMIMSG_FLAG_SNDR_ENCRYPT ) { wsQueueName += L"Encrypt"; } else if ( m_dwFlags & WMIMSG_FLAG_SNDR_AUTHENTICATE ) { wsQueueName += L"Auth"; } return AddMSMQSender(wsQueueName); } HRESULT CFwdConsSend::AddSyncSender( LPCWSTR wszMachine ) { HRESULT hr; CWbemPtr pSender; hr = CoCreateInstance( CLSID_WmiMessageRpcSender, NULL, CLSCTX_INPROC, IID_IWmiMessageSender, (void**)&pSender ); if ( FAILED(hr) ) { return hr; } // // construct target binding : OBJID@ncacn_ip_tcp:target // WString wsTarget = L"7879E40D-9FB5-450a-8A6D-00C89F349FCE@ncacn_ip_tcp:"; wsTarget += wszMachine; // // construct the target principal name - for kerberos. We expect that // has registered its SPN with AD. we only need to do this if we // are sending authenticated though ... // LPWSTR wszSpn = NULL; if ( m_dwFlags & WMIMSG_FLAG_SNDR_AUTHENTICATE ) { hr = GetSpn( wszMachine, &wszSpn ); if ( FAILED(hr) ) { DEBUGTRACE((LOG_ESS,"FC: Could not determine SPN for target %S. " "hr=0x%x. Will try to forward events using NTLM\n", wszMachine, hr )); } } CVectorDeleteMe vdm2( wszSpn ); WMIMSG_SNDR_AUTH_INFO AuthInfo; ZeroMemory( &AuthInfo, sizeof( WMIMSG_SNDR_AUTH_INFO ) ); AuthInfo.wszTargetPrincipal = wszSpn; // // open sender // CWbemPtr pSend; hr = pSender->Open( wsTarget, m_dwFlags | WMIMSG_FLAG_SNDR_LAZY_INIT, &AuthInfo, NULL, m_pTraceSink, &pSend ); if ( FAILED(hr) ) { return hr; } // // add to multi sender and return. // return m_pMultiSend->Add(WMIMSG_FLAG_MULTISEND_TERMINATING_SENDER, pSend ); } HRESULT CFwdConsSend::AddPhysicalSender( LPCWSTR wszMachine ) { HRESULT hr; #ifndef __WHISTLER_UNCUT if ( (m_dwFlags & WMIMSG_MASK_QOS) != WMIMSG_FLAG_QOS_SYNCHRONOUS ) { return WBEM_E_NOT_SUPPORTED; } return AddSyncSender( wszMachine ); #else // // here, we always add a sync sender first even if a qos // of async is specified. Later this type of service may change to // be its own QoS class. // hr = AddSyncSender( wszMachine ); if ( FAILED(hr) ) { return hr; } if ( (m_dwFlags & WMIMSG_MASK_QOS) == WMIMSG_FLAG_QOS_SYNCHRONOUS ) { return WBEM_S_NO_ERROR; } return AddAsyncSender( wszMachine ); #endif } HRESULT CFwdConsSend::AddLogicalSender( LPCWSTR wszTarget ) { HRESULT hr; CWbemPtr pSend; hr = Create( m_pControl, wszTarget, m_dwFlags | FWD_FLAG_NO_INDIRECT, NULL, m_pTraceSink, &pSend ); if ( FAILED(hr) ) { return hr; } return m_pMultiSend->Add( 0, pSend ); } HRESULT CFwdConsSend::AddLogicalSender( LPCWSTR wszObjpath, LPCWSTR wszProp ) { HRESULT hr; // // Check to make sure that indirect names are supported. // This flag is mostly used to prohibit recursive indirect // addesses. // if ( m_dwFlags & FWD_FLAG_NO_INDIRECT ) { return WBEM_E_NOT_SUPPORTED; } CWbemBSTR bsObjPath = wszObjpath; // // Resolve the address by obtaining the object and getting // the value of the specified property. // VARIANT var; CWbemPtr pObj; hr = m_pDefaultSvc->GetObject( bsObjPath, 0, NULL, &pObj, NULL ); if ( FAILED(hr) ) { return hr; } hr = pObj->Get( wszProp, 0, &var, NULL, NULL ); if ( FAILED(hr) ) { return hr; } CClearMe cmvar(&var); // // Add a new logical sender after resolving the address. // Before adding the new sender, make sure we disable indirect // addresses on it to prohibit recursive resolution. // DWORD dwFlags = m_dwFlags | FWD_FLAG_NO_INDIRECT; if ( V_VT(&var) == VT_BSTR ) { return AddLogicalSender( V_BSTR(&var) ); } else if ( V_VT(&var) == (VT_ARRAY | VT_BSTR) ) { BSTR* abstrNames; hr = SafeArrayAccessData( V_ARRAY(&var), (void**)&abstrNames ); if ( FAILED(hr) ) { return hr; } long lUbound; hr = SafeArrayGetUBound( V_ARRAY(&var), 0, &lUbound ); _DBG_ASSERT(SUCCEEDED(hr)); for( long i=0; i < lUbound+1; i++ ) { AddLogicalSender( V_BSTR(&var) ); } SafeArrayUnaccessData( V_ARRAY(&var) ); } else { return WBEM_E_TYPE_MISMATCH; } return hr; } HRESULT CFwdConsSend::EnsureSender() { HRESULT hr; CInCritSec ics(&m_cs); if ( m_bResolved ) { return S_OK; } WString wsTarget = m_wsTarget; LPWSTR wszToken = wcstok( wsTarget, L"!" ); LPWSTR wszToken2 = wcstok( NULL, L"!" ); if ( wszToken2 == NULL ) { hr = AddPhysicalSender( wszToken ); } #ifdef __WHISTLER_UNCUT else if ( wbem_wcsicmp( wszToken, L"msmq" ) == 0 ) { hr = AddMSMQSender( wszToken2 ); } else if ( wbem_wcsicmp( wszToken, L"wmi" ) == 0 ) { LPWSTR wszToken3 = wcstok( NULL, L"!" ); if ( wszToken3 == NULL ) { return WMIMSG_E_INVALIDADDRESS; } hr = AddLogicalSender( wszToken2, wszToken3 ); } #endif else { return WMIMSG_E_INVALIDADDRESS; } if ( FAILED(hr) ) { return hr; } m_bResolved = TRUE; return hr; } HRESULT CFwdConsSend::HandleTrace( HRESULT hr, IUnknown* pCtx ) { if ( m_pTraceSink == NULL ) { return WBEM_S_NO_ERROR; } return m_pTraceSink->Notify( hr, g_guidQueueType, m_wsTarget, pCtx ); } HRESULT CFwdConsSend::SendReceive( PBYTE pData, ULONG cData, PBYTE pAuxData, ULONG cAuxData, DWORD dwFlagStatus, IUnknown* pCtx ) { HRESULT hr; hr = EnsureSender(); if ( FAILED(hr) ) { HandleTrace( hr, pCtx ); return hr; } return m_pMultiSend->SendReceive( pData, cData, pAuxData, cAuxData, dwFlagStatus, pCtx ); } HRESULT CFwdConsSend::Create( CLifeControl* pCtl, LPCWSTR wszTarget, DWORD dwFlags, IWbemServices* pDefaultSvc, IWmiMessageTraceSink* pTraceSink, IWmiMessageSendReceive** ppSend ) { HRESULT hr; *ppSend = NULL; CWbemPtr pMultiSend; hr = CoCreateInstance( CLSID_WmiMessageMultiSendReceive, NULL, CLSCTX_INPROC, IID_IWmiMessageMultiSendReceive, (void**)&pMultiSend ); if ( FAILED(hr) ) { return hr; } CWbemPtr pSend = new CFwdConsSend( pCtl ); if ( pSend == NULL ) { return WBEM_E_OUT_OF_MEMORY; } if ( wszTarget != NULL && *wszTarget != 0 ) { pSend->m_wsTarget = wszTarget; } else { // // the default is to send to local computer. // TCHAR achComputer[MAX_COMPUTERNAME_LENGTH+1]; ULONG ulSize = MAX_COMPUTERNAME_LENGTH+1; GetComputerName( achComputer, &ulSize ); pSend->m_wsTarget = achComputer; } pSend->m_dwFlags = dwFlags; pSend->m_pDefaultSvc = pDefaultSvc; pSend->m_pTraceSink = pTraceSink; pSend->m_pMultiSend = pMultiSend; hr = pSend->QueryInterface( IID_IWmiMessageSendReceive, (void**)ppSend ); _DBG_ASSERT(SUCCEEDED(hr)); return WBEM_S_NO_ERROR; }