#include "precomp.h" #include #include #include #include #include "evtlog.h" #include #include #include #undef _ASSERT #include #define EVENTLOG_PROPNAME_SERVER L"UNCServerName" #define EVENTLOG_PROPNAME_SOURCE L"SourceName" #define EVENTLOG_PROPNAME_EVENTID L"EventID" #define EVENTLOG_PROPNAME_TYPE L"EventType" #define EVENTLOG_PROPNAME_CATEGORY L"Category" #define EVENTLOG_PROPNAME_NUMSTRINGS L"NumberOfInsertionStrings" #define EVENTLOG_PROPNAME_STRINGS L"InsertionStringTemplates" #define EVENTLOG_PROPNAME_CREATORSID L"CreatorSid" #define EVENTLOG_PROPNAME_DATANAME L"NameOfRawDataProperty" #define EVENTLOG_PROPNAME_SIDNAME L"NameOfUserSIDProperty" HRESULT STDMETHODCALLTYPE CEventLogConsumer::XProvider::FindConsumer( IWbemClassObject* pLogicalConsumer, IWbemUnboundObjectSink** ppConsumer) { CEventLogSink* pSink = new CEventLogSink(m_pObject->m_pControl); if(pSink == NULL) return WBEM_E_OUT_OF_MEMORY; HRESULT hres = pSink->Initialize(pLogicalConsumer); if(FAILED(hres)) { delete pSink; *ppConsumer = NULL; return hres; } else return pSink->QueryInterface(IID_IWbemUnboundObjectSink, (void**)ppConsumer); } void* CEventLogConsumer::GetInterface(REFIID riid) { if(riid == IID_IWbemEventConsumerProvider) return &m_XProvider; else return NULL; } CEventLogSink::~CEventLogSink() { if(m_hEventLog) DeregisterEventSource(m_hEventLog); if(m_aTemplates) delete [] m_aTemplates; if(m_pSidCreator) delete [] m_pSidCreator; } HRESULT CEventLogSink::Initialize(IWbemClassObject* pLogicalConsumer) { // Get the information // =================== HRESULT hres = WBEM_S_NO_ERROR; CComVariant v; // Get the server and source // ========================= WString wsServer; hres = pLogicalConsumer->Get(EVENTLOG_PROPNAME_SERVER, 0, &v, NULL, NULL); if(FAILED(hres)) return hres; if(V_VT(&v) == VT_BSTR) wsServer = V_BSTR(&v); VariantClear(&v); WString wsSource; hres = pLogicalConsumer->Get(EVENTLOG_PROPNAME_SOURCE, 0, &v, NULL, NULL); if(SUCCEEDED(hres) && (V_VT(&v) == VT_BSTR)) wsSource = V_BSTR(&v); VariantClear(&v); m_hEventLog = RegisterEventSourceW( ( (wsServer.Length() == 0) ? NULL : (LPCWSTR)wsServer), wsSource); if(m_hEventLog == NULL) { ERRORTRACE((LOG_ESS, "Unable to register event source '%S' on server " "'%S'. Error code: %X\n", (LPCWSTR)wsSource, (LPCWSTR)wsServer, GetLastError())); return WBEM_E_FAILED; } // Get event parameters // ==================== hres = pLogicalConsumer->Get(EVENTLOG_PROPNAME_EVENTID, 0, &v, NULL, NULL); if(SUCCEEDED(hres) && (V_VT(&v) == VT_I4)) m_dwEventId = V_I4(&v); else // This will mean we need to try to get the event information off of each // event class as it arrives. m_dwEventId = 0; hres = pLogicalConsumer->Get(EVENTLOG_PROPNAME_TYPE, 0, &v, NULL, NULL); if(FAILED(hres) || (V_VT(&v) != VT_I4)) return WBEM_E_INVALID_PARAMETER; m_dwType = V_I4(&v); hres = pLogicalConsumer->Get(EVENTLOG_PROPNAME_CATEGORY, 0, &v, NULL, NULL); if(FAILED(hres) || (V_VT(&v) != VT_I4)) m_dwCategory = 0; else m_dwCategory = V_I4(&v); if (m_dwCategory > 0xFFFF) return WBEM_E_INVALID_PARAMETER; // Get insertion strings // ===================== // Only get this stuff if the logical consumer has an event id. if (m_dwEventId) { hres = pLogicalConsumer->Get(EVENTLOG_PROPNAME_NUMSTRINGS, 0, &v, NULL, NULL); if(FAILED(hres) || (V_VT(&v) != VT_I4)) return WBEM_E_INVALID_PARAMETER; m_dwNumTemplates = V_I4(&v); hres = pLogicalConsumer->Get(EVENTLOG_PROPNAME_STRINGS, 0, &v, NULL, NULL); if(FAILED(hres)) return WBEM_E_INVALID_PARAMETER; // array of bstrs or null, else bail if ((V_VT(&v) != (VT_BSTR | VT_ARRAY)) && (V_VT(&v) != VT_NULL)) { VariantClear(&v); return WBEM_E_INVALID_PARAMETER; } if ((V_VT(&v) == VT_NULL) && (m_dwNumTemplates > 0)) return WBEM_E_INVALID_PARAMETER; if (m_dwNumTemplates > 0) { CVarVector vv(VT_BSTR, V_ARRAY(&v)); VariantClear(&v); if (vv.Size() < m_dwNumTemplates) return WBEM_E_INVALID_PARAMETER; m_aTemplates = new CTextTemplate[m_dwNumTemplates]; if(m_aTemplates == NULL) return WBEM_E_OUT_OF_MEMORY; for(DWORD i = 0; i < m_dwNumTemplates; i++) { m_aTemplates[i].SetTemplate(vv.GetAt(i).GetLPWSTR()); } } } hres = pLogicalConsumer->Get(EVENTLOG_PROPNAME_DATANAME, 0, &v, NULL, NULL); if (SUCCEEDED(hres) && (v.vt == VT_BSTR) && (v.bstrVal != NULL)) { m_dataName = v.bstrVal; } VariantClear(&v); hres = pLogicalConsumer->Get(EVENTLOG_PROPNAME_SIDNAME, 0, &v, NULL, NULL); if (SUCCEEDED(hres) && (v.vt == VT_BSTR) && (v.bstrVal != NULL)) { m_sidName = v.bstrVal; } VariantClear(&v); hres = pLogicalConsumer->Get(EVENTLOG_PROPNAME_CREATORSID, 0, &v, NULL, NULL); if (SUCCEEDED(hres) && (v.vt != VT_NULL)) { if((v.vt != (VT_ARRAY | VT_UI1))) { VariantClear(&v); return WBEM_E_INVALID_OBJECT; } long ubound; if(SUCCEEDED(hres = SafeArrayGetUBound(V_ARRAY(&v), 1, &ubound))) { PVOID pVoid; if(SUCCEEDED(hres = SafeArrayAccessData(V_ARRAY(&v), &pVoid))) { m_pSidCreator = new BYTE[ubound +1]; if(m_pSidCreator == NULL) hres = WBEM_E_OUT_OF_MEMORY; else memcpy(m_pSidCreator, pVoid, ubound + 1); SafeArrayUnaccessData(V_ARRAY(&v)); } } } VariantClear(&v); return hres; } HRESULT CEventLogSink::XSink::GetDatEmbeddedObjectOut(IWbemClassObject* pObject, WCHAR* objectName, IWbemClassObject*& pEmbeddedObject) { HRESULT hr; VARIANT vObject; VariantInit(&vObject); hr = pObject->Get(objectName, 0, &vObject, NULL, NULL); if (FAILED(hr)) { ERRORTRACE((LOG_ESS, "NT Event Log Consumer: could not retrieve %S, 0x%08X\n", objectName, hr)); } else if ((vObject.vt != VT_UNKNOWN) || (vObject.punkVal == NULL) || FAILED(vObject.punkVal->QueryInterface(IID_IWbemClassObject, (void**)&pEmbeddedObject))) { ERRORTRACE((LOG_ESS, "NT Event Log Consumer: %S is not an embedded object\n", objectName)); hr = WBEM_E_INVALID_PARAMETER; } VariantClear(&vObject); return hr; } HRESULT CEventLogSink::XSink::GetDatDataVariant(IWbemClassObject* pEventObj, WCHAR* dataName, VARIANT& vData) { WCHAR* propName = NULL; IWbemClassObject* pDataObj = NULL; HRESULT hr = WBEM_S_NO_ERROR; // parse out data name WCHAR* pDot; if (pDot = wcschr(dataName, L'.')) { // found a dot, we're dealing with an embedded object // mask out dot to make our life easier *pDot = L'\0'; WCHAR* pNextDot; pNextDot = wcschr(pDot+1, L'.'); if (pNextDot) // we have a doubly embedded object, that's as deep as we support { // we now have three prop names with nulls between *pNextDot = '\0'; IWbemClassObject* pIntermediateObj = NULL; if (SUCCEEDED(hr = GetDatEmbeddedObjectOut(pEventObj, dataName, pIntermediateObj))) { hr = GetDatEmbeddedObjectOut(pIntermediateObj, pDot +1, pDataObj); pIntermediateObj->Release(); } propName = pNextDot +1; // put dot back *pDot = L'.'; // put dot dot back back *pNextDot = L'.'; } else // we have a singly embedded object. cool. { hr = GetDatEmbeddedObjectOut(pEventObj, dataName, pDataObj); // put dot back *pDot = L'.'; propName = pDot +1; } } else { // not an embedded object pDataObj = pEventObj; pDataObj->AddRef(); propName = dataName; } if (SUCCEEDED(hr) && pDataObj) { if (FAILED(hr = pDataObj->Get(propName, 0, &vData, NULL, NULL))) DEBUGTRACE((LOG_ESS, "NT Event Log Consumer: could not retrieve property '%S' 0x%08X\n", dataName, hr)); } if (pDataObj) pDataObj->Release(); return hr; } // assumes that dataName is a valid string // retrieves data from event object // upon return pData points at data contained in variant // calls responsibility to clear variant (don't delete pData) // void return, any errors are logged - we don't want to block an event log if we can avoid it void CEventLogSink::XSink::GetDatData(IWbemClassObject* pEventObj, WCHAR* dataName, VARIANT& vData, BYTE*& pData, DWORD& dataSize) { pData = NULL; dataSize = 0; HRESULT hr; if (SUCCEEDED(GetDatDataVariant(pEventObj, dataName, vData))) { hr = VariantChangeType(&vData, &vData, 0, (VT_UI1 | VT_ARRAY)); if (FAILED(hr) || (vData.vt != (VT_UI1 | VT_ARRAY))) { ERRORTRACE((LOG_ESS, "NT Event Log Consumer: %S cannot be converted to a byte array (0x%08X)\n", dataName, hr)); VariantClear(&vData); } else // should be good to go! { if (FAILED(hr = SafeArrayAccessData(vData.parray, (void**)&pData))) { ERRORTRACE((LOG_ESS, "NT Event Log Consumer: failed to access %S, 0x%08X\n", dataName, hr)); VariantClear(&vData); } long lDataSize; SafeArrayGetUBound(vData.parray, 1, &lDataSize); dataSize = (DWORD)lDataSize + 1; } } } // assumes that dataName is a valid string // retrieves data from event object // void return, any errors are logged - we don't want to block an event log if we can avoid it void CEventLogSink::XSink::GetDatSID(IWbemClassObject* pEventObj, WCHAR* dataName, PSID& pSid) { HRESULT hr; VARIANT vData; VariantInit(&vData); pSid = NULL; if (SUCCEEDED(hr = GetDatDataVariant(pEventObj, dataName, vData))) { if (vData.vt == (VT_UI1 | VT_ARRAY)) { BYTE* pData; // this should be a binary SID if (FAILED(hr = SafeArrayAccessData(vData.parray, (void**)&pData))) ERRORTRACE((LOG_ESS, "NT Event Log Consumer: failed to access %S, 0x%08X\n", dataName, hr)); else { if (IsValidSid((PSID)pData)) { DWORD l = GetLengthSid((PSID)pData); if (pSid = new BYTE[l]) CopySid(l, pSid, (PSID)pData); } } } else if ((vData.vt == VT_BSTR) && (vData.bstrVal != NULL)) { PSID pLocalSid; if (!ConvertStringSidToSid(vData.bstrVal, &pLocalSid)) ERRORTRACE((LOG_ESS, "NT Event Log Consumer: cannot convert %S to a SID\n", vData.bstrVal)); else { DWORD l = GetLengthSid(pLocalSid); if (pSid = new BYTE[l]) CopySid(l, pSid, pLocalSid); FreeSid(pLocalSid); } } else ERRORTRACE((LOG_ESS, "NT Event Log Consumer: %S is not a SID\n", dataName)); VariantClear(&vData); } } HRESULT STDMETHODCALLTYPE CEventLogSink::XSink::IndicateToConsumer( IWbemClassObject* pLogicalConsumer, long lNumObjects, IWbemClassObject** apObjects) { HRESULT hr = WBEM_S_NO_ERROR; if (IsNT()) { PSID pSidSystem; SID_IDENTIFIER_AUTHORITY id = SECURITY_NT_AUTHORITY; if (AllocateAndInitializeSid(&id, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0,0,0,0,0,0,&pSidSystem)) { // guilty until proven innocent hr = WBEM_E_ACCESS_DENIED; // check to see if sid is either Local System or an admin of some sort... if ((EqualSid(pSidSystem, m_pObject->m_pSidCreator)) || (S_OK == IsUserAdministrator(m_pObject->m_pSidCreator))) hr = WBEM_S_NO_ERROR; // We're done with this FreeSid(pSidSystem); if (FAILED(hr)) return hr; } else return WBEM_E_OUT_OF_MEMORY; } for(int i = 0; i < lNumObjects; i++) { int j; BOOL bRes = FALSE; // Do all events use the same ID? if (m_pObject->m_dwEventId) { BSTR* astrStrings = new BSTR[m_pObject->m_dwNumTemplates]; if(astrStrings == NULL) return WBEM_E_OUT_OF_MEMORY; for(j = 0; j < m_pObject->m_dwNumTemplates; j++) { BSTR strText = m_pObject->m_aTemplates[j].Apply(apObjects[i]); if(strText == NULL) { strText = SysAllocString(L"invalid log entry"); if(strText == NULL) { delete [] astrStrings; return WBEM_E_OUT_OF_MEMORY; } } astrStrings[j] = strText; } DWORD dataSize = NULL; // data is actually held in the variant // pData just makes access easier (clear the variant, don't delete pData!) VARIANT vData; VariantInit(&vData); BYTE *pData = NULL; PSID pSid = NULL; if (m_pObject->m_dataName.Length() > 0) GetDatData(apObjects[i], m_pObject->m_dataName, vData, pData, dataSize); if (m_pObject->m_sidName.Length() > 0) GetDatSID(apObjects[i], m_pObject->m_sidName, pSid); bRes = ReportEventW(m_pObject->m_hEventLog, m_pObject->m_dwType, m_pObject->m_dwCategory, m_pObject->m_dwEventId, pSid, m_pObject->m_dwNumTemplates, dataSize, (LPCWSTR*)astrStrings, pData); // sid was allocated as an array of BYTE, not via AllocateAndInitializeSid if (pSid) delete[] pSid; if (vData.vt == (VT_UI1 | VT_ARRAY)) SafeArrayUnaccessData(vData.parray); VariantClear(&vData); pData = NULL; if(!bRes) { ERRORTRACE((LOG_ESS, "Failed to log an event: %X\n", GetLastError())); hr = WBEM_E_FAILED; } for(j = 0; j < m_pObject->m_dwNumTemplates; j++) { SysFreeString(astrStrings[j]); } delete [] astrStrings; } // If each event supplies its own ID, we have some work to do. else { IWbemQualifierSet *pQuals = NULL; if (SUCCEEDED(apObjects[i]->GetQualifierSet(&pQuals))) { _variant_t vMsgID; if (SUCCEEDED(pQuals->Get(EVENTLOG_PROPNAME_EVENTID, 0, &vMsgID, NULL)) && ((vMsgID.vt == VT_BSTR) || (vMsgID.vt == VT_I4))) { _variant_t vTemplates; BSTR *pstrInsertionStrings = NULL; DWORD nStrings = 0; if (SUCCEEDED(pQuals->Get( EVENTLOG_PROPNAME_STRINGS, 0, &vTemplates, NULL)) && vTemplates.vt == (VT_ARRAY | VT_BSTR) && vTemplates.parray->rgsabound[0].cElements > 0) { CTextTemplate *pTemplates; nStrings = vTemplates.parray->rgsabound[0].cElements; pTemplates = new CTextTemplate[nStrings]; if(pTemplates == NULL) return WBEM_E_OUT_OF_MEMORY; pstrInsertionStrings = new BSTR[nStrings]; if(pstrInsertionStrings == NULL) { delete [] pTemplates; return WBEM_E_OUT_OF_MEMORY; } if (pTemplates && pstrInsertionStrings) { BSTR *pTemplateStrings = (BSTR*) vTemplates.parray->pvData; for (j = 0; j < nStrings; j++) { pTemplates[j].SetTemplate(pTemplateStrings[j]); pstrInsertionStrings[j] = pTemplates[j].Apply(apObjects[i]); } } else nStrings = 0; if (pTemplates) delete [] pTemplates; } DWORD dwEventID, dwType, dwCategory; _variant_t vTemp; WCHAR *szBad; if (vMsgID.vt == VT_BSTR) dwEventID = wcstoul(V_BSTR(&vMsgID), &szBad, 10); else if (vMsgID.vt == VT_I4) dwEventID = V_I4(&vMsgID); if ((SUCCEEDED(pQuals->Get(EVENTLOG_PROPNAME_TYPE, 0, &vTemp, NULL))) && (V_VT(&vTemp) == VT_I4)) dwType = V_I4(&vTemp); else dwType = m_pObject->m_dwType; if (SUCCEEDED(pQuals->Get( EVENTLOG_PROPNAME_CATEGORY, 0, &vTemp, NULL))) dwCategory = V_I4(&vTemp); else dwCategory = m_pObject->m_dwCategory; DWORD dataSize = NULL; // data is actually held in the variant // pData just makes access easier (clear the variant, don't delete pData!) VARIANT vData; VariantInit(&vData); BYTE *pData = NULL; PSID pSid = NULL; if (m_pObject->m_dataName.Length() > 0) GetDatData(apObjects[i], m_pObject->m_dataName, vData, pData, dataSize); if (m_pObject->m_sidName.Length() > 0) GetDatSID(apObjects[i], m_pObject->m_sidName, pSid); bRes = ReportEventW( m_pObject->m_hEventLog, dwType, dwCategory, dwEventID, pSid, nStrings, dataSize, (LPCWSTR*) pstrInsertionStrings, pData); // sid was allocated as an array of BYTE, not via AllocateAndInitializeSid if (pSid) delete[] pSid; if (vData.vt == (VT_UI1 | VT_ARRAY)) SafeArrayUnaccessData(vData.parray); VariantClear(&vData); pData = NULL; if (!bRes) { ERRORTRACE((LOG_ESS, "Failed to log an event: %X\n", GetLastError())); hr = WBEM_E_FAILED; } for (j = 0; j < nStrings; j++) SysFreeString(pstrInsertionStrings[j]); delete [] pstrInsertionStrings; } // SUCCEEDED(Get) pQuals->Release(); } // SUCCEEDED(GetQualifierSet) } } return hr; } void* CEventLogSink::GetInterface(REFIID riid) { if(riid == IID_IWbemUnboundObjectSink) return &m_XSink; else return NULL; }