//*************************************************************************** // // NTEVTLOGR.CPP // // Module: WBEM NT EVENT PROVIDER // // Purpose: Contains the Eventlog record classes // // Copyright (c) 1996-2002 Microsoft Corporation, All Rights Reserved // //*************************************************************************** #include "precomp.h" #include #include #include #include #include #include #define MAX_INSERT_OPS 100 CEventlogRecord::CEventlogRecord(const wchar_t* logfile, const EVENTLOGRECORD* pEvt, IWbemServices* ns, IWbemClassObject* pClass, IWbemClassObject* pAClass) : m_nspace(NULL), m_pClass(NULL), m_pAClass(NULL) { m_EvtType = 0; m_Data = NULL; m_Obj = NULL; m_NumStrs = 0; m_DataLen = 0; m_nspace = ns; if (m_nspace != NULL) { m_nspace->AddRef(); } else { m_pClass = pClass; if (m_pClass != NULL) { m_pClass->AddRef(); } m_pAClass = pAClass; if (m_pAClass != NULL) { m_pAClass->AddRef(); } } if ((NULL == logfile) || ((m_pClass == NULL) && (m_nspace == NULL))) { m_Valid = FALSE; DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::CEventlogRecord:Created INVALID Record\r\n" ) ; ) } else { m_Logfile = logfile; m_Valid = Init(pEvt); } } CEventlogRecord::~CEventlogRecord() { if (m_pClass != NULL) { m_pClass->Release(); } if (m_pAClass != NULL) { m_pAClass->Release(); } if (m_nspace != NULL) { m_nspace->Release(); } for (LONG x = 0; x < m_NumStrs; x++) { delete [] m_InsStrs[x]; } if (m_Data != NULL) { delete [] m_Data; } if (m_Obj != NULL) { m_Obj->Release(); } } BOOL CEventlogRecord::Init(const EVENTLOGRECORD* pEvt) { if (NULL == pEvt) { DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::Init:No DATA return FALSE\r\n" ) ; ) return FALSE; } if (!GetInstance()) { return FALSE; } m_Record = pEvt->RecordNumber; m_EvtID = pEvt->EventID; m_SourceName = (const wchar_t*)((UCHAR*)pEvt + sizeof(EVENTLOGRECORD)); m_CompName = (const wchar_t*)((UCHAR*)pEvt + sizeof(EVENTLOGRECORD)) + wcslen(m_SourceName) + 1; SetType(pEvt->EventType); m_Category = pEvt->EventCategory; SetTimeStr(m_TimeGen, pEvt->TimeGenerated); SetTimeStr(m_TimeWritten, pEvt->TimeWritten); if (pEvt->UserSidLength > 0) { PSID pSid = NULL; pSid = (PSID)((UCHAR*)pEvt + pEvt->UserSidOffset); if ( pSid && IsValidSid ( pSid ) ) { SetUser( pSid ); } } if (pEvt->NumStrings) { //Must have an element for every expected insertion string //don't know how many that is so create max size array and //intitialize all to NULL memset(m_InsStrs, 0, MAX_NUM_OF_INS_STRS * sizeof(wchar_t*)); const wchar_t* pstr = (const wchar_t*)((UCHAR*)pEvt + pEvt->StringOffset); for (WORD x = 0; x < pEvt->NumStrings; x++) { LONG len = wcslen(pstr) + 1; m_InsStrs[x] = new wchar_t[len]; m_NumStrs++; StringCchCopyW ( m_InsStrs[x], len, pstr ); pstr += len; } } SetMessage(); if (pEvt->DataLength) { m_Data = new UCHAR[pEvt->DataLength]; m_DataLen = pEvt->DataLength; memcpy((void*)m_Data, (void*)((UCHAR*)pEvt + pEvt->DataOffset), pEvt->DataLength); } return TRUE; } BOOL CEventlogRecord::GenerateInstance(IWbemClassObject** ppInst) { if (ppInst == NULL) { DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::GenerateInstance:Invalid parameter\r\n" ) ; ) return FALSE; } *ppInst = NULL; if (!m_Valid) { DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::GenerateInstance:Invalid record\r\n" ) ; ) return FALSE; } if (!SetProperty(LOGFILE_PROP, m_Logfile)) { m_Valid = FALSE; DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::GenerateInstance:Failed to set key\r\n" ) ; ) return FALSE; } else { DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::GenerateInstance:Log: %s\r\n", m_Logfile ) ; ) } if (!SetProperty(RECORD_PROP, m_Record)) { m_Valid = FALSE; DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::GenerateInstance:Failed to set key\r\n" ) ; ) return FALSE; } else { DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::GenerateInstance:Record: %d\r\n", m_Record ) ; ) } SetProperty(TYPE_PROP, m_Type); SetProperty(EVTTYPE_PROP, (DWORD)m_EvtType); if (!m_SourceName.IsEmpty()) { SetProperty(SOURCE_PROP, m_SourceName); DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::GenerateInstance:Source: %s\r\n", m_SourceName ) ; ) } SetProperty(EVTID_PROP, m_EvtID); SetProperty(EVTID2_PROP, (m_EvtID & 0xFFFF)); if (!m_TimeGen.IsEmpty()) { DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::GenerateInstance:TimeGenerated: %s\r\n", m_TimeGen ) ; ) SetProperty(GENERATED_PROP, m_TimeGen); } if (!m_TimeWritten.IsEmpty()) { SetProperty(WRITTEN_PROP, m_TimeWritten); } if (!m_CompName.IsEmpty()) { SetProperty(COMPUTER_PROP, m_CompName); } if (!m_User.IsEmpty()) { SetProperty(USER_PROP, m_User); } if (!m_Message.IsEmpty()) { SetProperty(MESSAGE_PROP, m_Message); } if (!m_CategoryString.IsEmpty()) { SetProperty(CATSTR_PROP, m_CategoryString); } SetProperty(CATEGORY_PROP, (DWORD)m_Category); VARIANT v; if (m_Data != NULL) { SAFEARRAYBOUND rgsabound[1]; SAFEARRAY* psa = NULL; UCHAR* pdata = NULL; rgsabound[0].lLbound = 0; VariantInit(&v); rgsabound[0].cElements = m_DataLen; psa = SafeArrayCreate(VT_UI1, 1, rgsabound); if (NULL != psa) { v.vt = VT_ARRAY|VT_UI1; v.parray = psa; if (SUCCEEDED(SafeArrayAccessData(psa, (void **)&pdata))) { memcpy((void *)pdata, (void *)m_Data, m_DataLen); SafeArrayUnaccessData(psa); m_Obj->Put(DATA_PROP, 0, &v, 0); } else { VariantClear (&v) ; return FALSE ; } } else { VariantClear(&v); return FALSE ; } VariantClear(&v); } if (0 != m_NumStrs) { SAFEARRAYBOUND rgsabound[1]; SAFEARRAY* psa = NULL; BSTR* pBstr = NULL; rgsabound[0].lLbound = 0; VariantInit(&v); rgsabound[0].cElements = m_NumStrs; psa = SafeArrayCreate(VT_BSTR, 1, rgsabound); if (NULL != psa) { v.vt = VT_ARRAY|VT_BSTR; v.parray = psa; if (SUCCEEDED(SafeArrayAccessData(psa, (void **)&pBstr))) { for (LONG x = 0; x < m_NumStrs; x++) { pBstr[x] = SysAllocString(m_InsStrs[x]); if ( NULL == pBstr[x] ) { SafeArrayUnaccessData(psa); VariantClear (&v) ; return FALSE ; } } SafeArrayUnaccessData(psa); m_Obj->Put(INSSTRS_PROP, 0, &v, 0); } else { VariantClear (&v) ; return FALSE ; } } else { VariantClear(&v); return FALSE ; } VariantClear(&v); } *ppInst = m_Obj; m_Obj->AddRef(); return TRUE; } BOOL CEventlogRecord::SetProperty(wchar_t* prop, CStringW val) { VARIANT v; VariantInit(&v); v.vt = VT_BSTR; v.bstrVal = val.AllocSysString(); HRESULT hr = m_Obj->Put(prop, 0, &v, 0); VariantClear(&v); if (FAILED(hr)) { DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::SetProperty:Failed to set %s with %s\r\n", prop, val ) ; ) return FALSE; } return TRUE; } BOOL CEventlogRecord::SetProperty(wchar_t* prop, DWORD val) { VARIANT v; VariantInit(&v); v.vt = VT_I4; v.lVal = val; HRESULT hr = m_Obj->Put(prop, 0, &v, 0); VariantClear(&v); if (FAILED(hr)) { DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::SetProperty:Failed to set %s with %lx\r\n", prop, val ) ; ) return FALSE; } return TRUE; } void CEventlogRecord::SetUser(PSID psidUserSid) { m_User = GetUser(psidUserSid); } ULONG CEventlogRecord::CheckInsertionStrings(HKEY hk, HKEY hkPrimary) { // // If the message doesn't have any percent signs, it can't have any // insertions. // if (!m_Message.IsEmpty() && !wcschr(m_Message, L'%')) { return 0; } HINSTANCE hParamModule = NULL; CStringW paramModule = CEventLogFile::GetFileName(hk, PARAM_MODULE); if (paramModule.IsEmpty()) { if ( hkPrimary ) { paramModule = CEventLogFile::GetFileName(hkPrimary, PARAM_MODULE); } } if (!paramModule.IsEmpty()) { hParamModule = GetDll(paramModule); } ULONG size = 0; LPWSTR Message = NULL; Message = new WCHAR [ ( m_Message.GetLength() + 1 ) ]; StringCchCopy ( Message, m_Message.GetLength() + 1, m_Message ); wchar_t* lpszString = Message; // set initial pointer UINT nInsertions = 0; // limit number of recursions while ( lpszString && *lpszString ) { wchar_t* lpStartDigit = wcschr(lpszString, L'%'); // // If there are no more insertion markers in the source string, // we're done. // if (lpStartDigit == NULL) { break; } // // get the offset of %string from the beggining of buffer for future replacement // UINT nOffset = (DWORD) ( lpStartDigit - Message ); UINT nStrSize = wcslen ( lpStartDigit ); // // Found a possible insertion marker. If it's followed by a // number, it's an insert string. If it's followed by another // percent, it could be a parameter insert. // if ( nStrSize > 1 && lpStartDigit[1] >= L'0' && lpStartDigit[1] <= L'9' ) { // Object with percent-sign in name messes up object access audit // This might fail because an inserted string itself contained // text which looks like an insertion parameter, such as "%20". // Ignore the return value and continue with further replacements. (void) ReplaceStringInsert ( &Message, nOffset, &lpStartDigit, &size ); // set pointer to the beginning of replacement lpszString = lpStartDigit; // // If we've reached the limit of insertion operations, quit. // This shouldn't normally happen and could indicate that // the insert strings or parameter strings are self referencing // and would create an infinite loop. // if (++nInsertions >= MAX_INSERT_OPS) { break; } } else if ( nStrSize > 2 && lpStartDigit[1] == '%' ) { // // Found %%. If that is followed by a digit, it's a parameter string. // if (lpStartDigit[2] >= L'0' && lpStartDigit[2] <= L'9') { if ( SUCCEEDED ( ReplaceParameterInsert ( hParamModule, paramModule, &Message, nOffset, &lpStartDigit, &size ) ) ) { // set pointer to the beginning of replacement lpszString = lpStartDigit; // // If we've reached the limit of insertion operations, quit. // This shouldn't normally happen and could indicate that // the insert strings or parameter strings are self referencing // and would create an infinite loop. // if (++nInsertions >= MAX_INSERT_OPS) { break; } } else { // // unable to replace (error). Just keep moving. // lpszString++; } } else if ( nStrSize > 3 && lpStartDigit[2] == '%' ) { // // Found %%%. If that is followed by a digit, it's a insertion string. // if (lpStartDigit[3] >= L'0' && lpStartDigit[3] <= L'9') { // // Got %%%n, where n is a number. For compatibility with // old event viewer, must replace this with %%x, where x // is insertion string n. If insertion string n is itself // a number m, this becomes %%m, which is treated as parameter // message number m. // lpStartDigit += 2; // point at %n // // nOffset shows offset from the beginning of the buffer where // replacement is going to happen to first % character lpStartDigit // // as we are chaging %%%n to be be %%x where x = %n, implementation // needs to move offset to point to the x here to get correct replacement // if ( SUCCEEDED ( ReplaceStringInsert ( &Message, nOffset+2, &lpStartDigit, &size ) ) ) { // // set pointer to the beginning of %%x (x=%n) // // this operation is done by substract as lpStartDigit pointer could // possibly change when original buffer gets reallocated // lpszString = lpStartDigit-2; // // If we've reached the limit of insertion operations, quit. // This shouldn't normally happen and could indicate that // the insert strings or parameter strings are self referencing // and would create an infinite loop. // if (++nInsertions >= MAX_INSERT_OPS) { break; } } else { // // unable to replace (error). Just keep moving. // lpszString++; } } else { // // Got %%%x, where x is non-digit. skip first percent; // maybe x is % and is followed by digit. // lpszString++; } } else { // // Got %%x, where x is non-digit. skip first percent; // maybe x is % and is followed by digit. // lpszString++; } } else if (nStrSize >= 3 && (lpStartDigit[1] == L'{') && (lpStartDigit[2] != L'S')) { // Parameters of form %{guid}, where {guid} is a string of // hex digits in the form returned by ::StringFromGUID2 (e.g. // {c200e360-38c5-11ce-ae62-08002b2b79ef}), and represents a // unique object in the Active Directory. // // These parameters are only found in the security event logs // of NT5 domain controllers. We will attempt to map the guid // to the human-legible name of the DS object. Failing to find // a mapping, we will leave the parameter untouched. // look for closing } wchar_t *strEnd = wcschr(lpStartDigit + 2, L'}'); if (!strEnd) { //ignore this %{? lpszString++; } else { //guid string braces but no percent sign... CStringW strGUID((LPWSTR)(lpStartDigit+1), (int)(strEnd - lpStartDigit)); strEnd++; // now points past '}' wchar_t t_csbuf[MAX_COMPUTERNAME_LENGTH + 1]; DWORD t_csbuflen = MAX_COMPUTERNAME_LENGTH + 1; if (GetComputerName(t_csbuf, &t_csbuflen)) { CStringW temp = GetMappedGUID(t_csbuf, strGUID); if (temp.GetLength()) { DWORD nParmSize = strEnd - lpStartDigit; if ( SUCCEEDED ( ReplaceSubStr ( temp, &Message, nOffset, nParmSize, &lpStartDigit, &size ) ) ) { // set pointer to the beginning of replacement lpszString = lpStartDigit; // // If we've reached the limit of insertion operations, quit. // This shouldn't normally happen and could indicate that // the insert strings or parameter strings are self referencing // and would create an infinite loop. // if (++nInsertions >= MAX_INSERT_OPS) { break; } } else { // // unable to replace (error). Just keep moving. // lpszString = strEnd; } } else { // couldn't get a replacement, so skip it. lpszString = strEnd; } } else { // couldn't get a replacement, so skip it. lpszString = strEnd; } } } else if (nStrSize >= 3 && (lpStartDigit[1] == L'{') && (lpStartDigit[2] == L'S')) { // // Parameters of form %{S}, where S is a string-ized SID returned // by ConvertSidToStringSid, are converted to an object name if // possible. // // look for closing } wchar_t *strEnd = wcschr(lpStartDigit + 2, L'}'); if (!strEnd) { //ignore this %{? lpszString++; } else { //sid string no braces or percent sign... CStringW strSID((LPWSTR)(lpStartDigit+2), (int)(strEnd - lpStartDigit - 2)); strEnd++; // now points past '}' PSID t_pSid = NULL; if (ConvertStringSidToSid((LPCWSTR) strSID, &t_pSid)) { CStringW temp = GetUser(t_pSid); LocalFree(t_pSid); if (temp.GetLength()) { DWORD nParmSize = strEnd - lpStartDigit; if ( SUCCEEDED ( ReplaceSubStr ( temp, &Message, nOffset, nParmSize, &lpStartDigit, &size ) ) ) { // set pointer to the beginning of replacement lpszString = lpStartDigit; // // If we've reached the limit of insertion operations, quit. // This shouldn't normally happen and could indicate that // the insert strings or parameter strings are self referencing // and would create an infinite loop. // if (++nInsertions >= MAX_INSERT_OPS) { break; } } else { // // unable to replace (error). Just keep moving. // lpszString = strEnd; } } else { // couldn't get a replacement, so skip it. lpszString = strEnd; } } else { // couldn't get a replacement, so skip it. lpszString = strEnd; } } } else { // // Found %x where x is neither a % nor a digit. Just keep moving. // lpszString++; } } m_Message.Empty(); m_Message = Message; delete [] Message; Message = NULL; return size; } //+-------------------------------------------------------------------------- // // Function: ReplaceStringInsert // // Synopsis: Replace the string insert (%n, where n is a number) at // [Message + nOffset] with insert string number n from the // event log record [lpParmBuffer]. // // Modifies: lpStartDigit to point to replacement // //--------------------------------------------------------------------------- HRESULT CEventlogRecord::ReplaceStringInsert ( LPWSTR* ppwszBuf, ULONG nOffset, LPWSTR* ppwszReplacement, ULONG* pulSize ) { HRESULT hr = E_INVALIDARG; *ppwszReplacement += 1; // point to start of potential digit if (**ppwszReplacement != 0) // check to see there is { LPWSTR pwszEnd = NULL; ULONG idxInsertStr = wcstoul(*ppwszReplacement, &pwszEnd, 10); if ( idxInsertStr && idxInsertStr <= m_NumStrs ) { DWORD nParmSize = pwszEnd - *ppwszReplacement + 1; hr = ReplaceSubStr ( m_InsStrs [ idxInsertStr-1 ], ppwszBuf, nOffset, nParmSize, ppwszReplacement, pulSize ); } // // else // { // we fail as we didn't recognize replacement // and/or insertion string // // see comment in CheckInsertionString // } } return hr; } //+-------------------------------------------------------------------------- // // Function: ReplaceParameterInsert // // Synopsis: Replace the parameter insert (double percent sign number) at // [ppwszReplacement] with a string loaded from a parameter message // file module. // //--------------------------------------------------------------------------- HRESULT CEventlogRecord::ReplaceParameterInsert ( HINSTANCE& hParamModule, CStringW& paramModule, LPWSTR* ppwszBuf, ULONG nOffset, LPWSTR* ppwszReplacement, ULONG* pulSize ) { DWORD nChars = 0; wchar_t* lpParmBuffer = NULL; ULONG nParmSize = 0; ULONG flFmtMsgFlags = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK; LPWSTR pwszEnd = NULL; ULONG idxParameterStr = wcstoul(*ppwszReplacement + 2, &pwszEnd, 10); HRESULT hr = E_FAIL; // Allow "%%0" if ( idxParameterStr || (L'0' == *(*ppwszReplacement + 2))) { if (hParamModule != NULL) { nChars = FormatMessage ( flFmtMsgFlags | FORMAT_MESSAGE_FROM_HMODULE, // look thru message DLL (LPVOID) hParamModule, // use parameter file idxParameterStr, // parameter number to get (ULONG) NULL, // specify no language (LPWSTR) &lpParmBuffer, // address for buffer pointer 256, // minimum space to allocate NULL // no inserted strings ); } if (nChars == 0) { if (hParamModule != NULL) { LocalFree(lpParmBuffer); lpParmBuffer = NULL; } // // It is common practice to write events with an insertion string whose // value is %%n, where n is a win32 error code, and to specify a // parameter message file of kernel32.dll. Unfortunately, kernel32.dll // doesn't contain messages for all win32 error codes. // // So if the parameter wasn't found, and the parameter message file was // kernel32.dll, attempt a format message from system. // paramModule.MakeLower(); if ( wcsstr( paramModule, L"kernel32.dll") ) { nChars = FormatMessage ( flFmtMsgFlags | FORMAT_MESSAGE_FROM_SYSTEM, // look thru system NULL, // no module idxParameterStr, // parameter number to get (ULONG) NULL, // specify no language (LPWSTR) &lpParmBuffer, // address for buffer pointer 256, // minimum space to allocate NULL // no inserted strings ); if (nChars == 0) { LocalFree(lpParmBuffer); lpParmBuffer = NULL; } } } if ( lpParmBuffer ) { try { DWORD nParmSize = pwszEnd - *ppwszReplacement; hr = ReplaceSubStr ( lpParmBuffer, ppwszBuf, nOffset, nParmSize, ppwszReplacement, pulSize ); } catch ( ... ) { if ( lpParmBuffer ) { LocalFree(lpParmBuffer); lpParmBuffer = NULL; } throw; } LocalFree(lpParmBuffer); lpParmBuffer = NULL; } else { hr = E_INVALIDARG; // move past whole parameter *ppwszReplacement = pwszEnd; } } return hr; } //+-------------------------------------------------------------------------- // // Function: ReplaceSubStr // // Synopsis: Replace the characters from *[ppwszInsertPoint] to just // before [pwszSubStrEnd] with the string [pwszToInsert]. // // Arguments: [pwszToInsert] - string to insert; may be L"" but not NULL. // [ppwszBuf] - buffer in which insertion occurs // [ulOffset] - point in *[ppwszBuf] to insert // [ulCharsOld] - number of chars to replace // [pulSize] - number of chars replaced // // Returns: S_OK // E_INVALIDARG // // Modifies: [ppwszBuf], [pptrReplacement] // // Notes: The substring to be replaced must be > 0 chars in length. // // The replacement string can be >= 0 chars. // // Therefore if the substring to replace is "%%12" and the // string to insert is "C:", on exit *[pcchRemain] will have // been incremented by 2. // // If there are insufficient characters remaining to replace // the substring with the insert string, reallocates the // buffer. // //--------------------------------------------------------------------------- HRESULT CEventlogRecord::ReplaceSubStr ( LPCWSTR pwszToInsert, LPWSTR *ppwszBuf, ULONG nOffset, ULONG nCharsOld, LPWSTR *pptrReplacement, ULONG *pulSize ) { HRESULT hr = E_INVALIDARG; try { if ( pwszToInsert ) { ULONG nChars = wcslen(pwszToInsert); UINT nStrSize = wcslen(*ppwszBuf)+1; // calculate original length UINT nNewSize = nStrSize+nChars-nCharsOld; // calculate new length wchar_t* tmp = *ppwszBuf; // // do we need to reallocate? // if (nNewSize > nStrSize) { tmp = new wchar_t[nNewSize]; // allocate new buffer if ( tmp == NULL ) // there is exception raisen in current implementation { hr = E_OUTOFMEMORY; *pptrReplacement -= 1; // get back to % as memory could get back } else { StringCchCopyW ( tmp, nNewSize, *ppwszBuf ); delete [] *ppwszBuf; *ppwszBuf = tmp; hr = S_FALSE; } } else { hr = S_FALSE; } if ( SUCCEEDED ( hr ) ) { *pptrReplacement = *ppwszBuf + nOffset; // point to start of current % (we are replacing) nStrSize = wcslen(*pptrReplacement)-nCharsOld+1; // calculate length of remainder of string // // perform move // memmove((void *)(*pptrReplacement+nChars), // destination address (void *)(*pptrReplacement+nCharsOld), // source address nStrSize*sizeof(wchar_t)); // amount of data to move memmove((void *)*pptrReplacement, // destination address (void *)pwszToInsert, // source address nChars*sizeof(wchar_t)); // amount of data to move *pulSize += ( nChars + 1 ); hr = S_OK; } } } catch ( ... ) { if (*ppwszBuf) { delete [] *ppwszBuf; *ppwszBuf = NULL; } throw; } return hr; } CStringW CEventlogRecord::GetUser(PSID userSid) { CStringW retVal; BOOL bFound = FALSE; MyPSID usrSID(userSid); { ScopeLock sl(sm_usersMap); if (!sm_usersMap.IsEmpty() && sm_usersMap.Lookup(usrSID, retVal)) { bFound = TRUE; } } if (!bFound) { DWORD dwVersion = GetVersion(); if ( (4 < (DWORD)(LOBYTE(LOWORD(dwVersion)))) || ObtainedSerialAccess(CNTEventProvider::g_secMutex) ) { wchar_t szDomBuff[MAX_PATH]; wchar_t szUsrBuff[MAX_PATH]; DWORD domBuffLen = MAX_PATH; DWORD usrBuffLen = MAX_PATH; SID_NAME_USE snu; if (LookupAccountSid( // lookup account name NULL, // system to lookup account on userSid, // pointer to SID for this account szUsrBuff, // return account name in this buffer &usrBuffLen, // pointer to size of account name returned szDomBuff, // domain where account was found &domBuffLen, //pointer to size of domain name &snu)) // sid name use field pointer { retVal = szDomBuff; retVal += L'\\'; retVal += szUsrBuff; } else { LONG lasterr = GetLastError(); DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::GetUser:API (LookupAccountSid) failed with %lx\r\n", lasterr ) ; ) } if ( 5 > (DWORD)(LOBYTE(LOWORD(dwVersion))) ) { ReleaseSerialAccess(CNTEventProvider::g_secMutex); } } else { DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::GetUser:Failed to get serial access to security APIs\r\n" ) ; ) } //regardless of error enter this into map so we //don't look up this PSID again { ScopeLock sl(sm_usersMap); CStringW LookretVal; if (!sm_usersMap.IsEmpty() && sm_usersMap.Lookup(usrSID, LookretVal)) { return LookretVal; } else { DWORD sidlen = GetLengthSid(userSid); MyPSID key; key.m_SID = (PSID) new UCHAR[sidlen]; CopySid(sidlen, key.m_SID, userSid); sm_usersMap[key] = retVal; } } } return retVal; } void CEventlogRecord::EmptyUsersMap() { if (sm_usersMap.Lock()) { sm_usersMap.RemoveAll(); sm_usersMap.Unlock(); } } HINSTANCE CEventlogRecord::GetDll(CStringW path) { HINSTANCE retVal = NULL; CStringW key(path); key.MakeUpper(); BOOL bFound = FALSE; { ScopeLock sl(sm_dllMap); if (!sm_dllMap.IsEmpty() && sm_dllMap.Lookup(key, retVal)) { bFound = TRUE; } } if (!bFound) { retVal = LoadLibraryEx(path, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); if (retVal == NULL) { DebugOut( DWORD lasterr = GetLastError(); CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::GetDll:API (LoadLibraryEx) failed with %lx for %s\r\n", lasterr, path ) ; ) } else { HINSTANCE LookretVal = NULL; ScopeLock sl(sm_dllMap); if (!sm_dllMap.IsEmpty() && sm_dllMap.Lookup(key, LookretVal)) { FreeLibrary(retVal); //release the ref count as we increased it by one as above. return LookretVal; } else { sm_dllMap[key] = retVal; } } } return retVal; } void CEventlogRecord::EmptyDllMap() { if (sm_dllMap.Lock()) { sm_dllMap.RemoveAll(); sm_dllMap.Unlock(); } } void CEventlogRecord::SetMessage() { HINSTANCE hMsgModule; wchar_t* lpBuffer = NULL; CStringW log(EVENTLOG_BASE); log += L"\\"; log += m_Logfile; HKEY hkResult; LONG status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, log, 0, KEY_READ, &hkResult); if (status != ERROR_SUCCESS) { DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::SetMessage:API (RegOpenKeyEx) failed with %lx for %s\r\n", status, log ) ; ) return; } ON_BLOCK_EXIT ( RegCloseKey, hkResult ) ; DWORD dwType; wchar_t* prim = NULL; prim = new wchar_t[MAX_PATH]; DWORD datalen = MAX_PATH * sizeof(wchar_t); status = RegQueryValueEx(hkResult, PRIM_MODULE, 0, &dwType, (LPBYTE)prim, &datalen); if (status != ERROR_SUCCESS) { if (status == ERROR_MORE_DATA) { delete [] prim; prim = new wchar_t[datalen]; status = RegQueryValueEx(hkResult, PRIM_MODULE, 0, &dwType, (LPBYTE)prim, &datalen); } } HKEY hkPrimary = NULL; HKEY hkSource = NULL; wmilib::auto_buffer < wchar_t > Smartprim ( prim ) ; if ( ERROR_SUCCESS == status && dwType == REG_SZ ) { // this is path to primary log CStringW primLog = log + L"\\"; primLog += prim; // open a registry for primary event log key if ((_wcsicmp(m_SourceName, prim)) != 0) { status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, primLog, 0, KEY_READ, &hkPrimary); } } ON_BLOCK_EXIT ( RegCloseKey, hkPrimary ) ; // this is path to source log CStringW sourceLog = log + L"\\"; sourceLog += m_SourceName; // check to see there is a registry for source status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, sourceLog, 0, KEY_READ, &hkSource); if (status != ERROR_SUCCESS) { DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::SetMessage:API (RegOpenKeyEx) failed with %lx for %s\r\n", status, log ) ; ) return; } ON_BLOCK_EXIT ( RegCloseKey, hkSource ) ; // get category file CStringW cat_modname = CEventLogFile::GetFileName(hkSource, CAT_MODULE); if (cat_modname.IsEmpty()) { if ( hkPrimary ) { // try primary event log key as source doesn't have category cat_modname = CEventLogFile::GetFileName(hkPrimary, CAT_MODULE); } } // workout category and category string if possible if (!cat_modname.IsEmpty()) { hMsgModule = GetDll(cat_modname); if (hMsgModule != NULL) { if (0 != FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | // let api build buffer FORMAT_MESSAGE_IGNORE_INSERTS | // indicate no string inserts FORMAT_MESSAGE_FROM_HMODULE | // look thru message DLL FORMAT_MESSAGE_MAX_WIDTH_MASK , (LPVOID) hMsgModule, // handle to message module m_Category, // message number to get (ULONG) NULL, // specify no language (LPWSTR) &lpBuffer, // address for buffer pointer 80, // minimum space to allocate NULL)) { m_CategoryString = lpBuffer; m_CategoryString.TrimRight(); LocalFree(lpBuffer); } else { DWORD lasterr = GetLastError(); DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::SetMessage:API (FormatMessage) failed with %lx\r\n", lasterr ) ; ) } } } // get event message file CStringW* names; DWORD count = CEventLogFile::GetFileNames(hkSource, &names); if ( !count ) { if ( hkPrimary ) { // try primary event log key as source doesn't have event message file count = CEventLogFile::GetFileNames(hkPrimary, &names); } } // work out event messages if (count != 0) { for (int x = 0; x < count; x++) { hMsgModule = GetDll(names[x]); if (hMsgModule != NULL) { if (0 != FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | // let api build buffer FORMAT_MESSAGE_ARGUMENT_ARRAY | // indicate an array of string inserts FORMAT_MESSAGE_IGNORE_INSERTS | // indicate no string inserts FORMAT_MESSAGE_FROM_HMODULE, // look thru message DLL (LPVOID) hMsgModule, // handle to message module m_EvtID, // message number to get (ULONG) NULL, // specify no language (LPWSTR) &lpBuffer, // address for buffer pointer 80, // minimum space to allocate NULL)) { m_Message = lpBuffer; LocalFree(lpBuffer); break; } else { DWORD lasterr = GetLastError(); DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::SetMessage:API (FormatMessage) failed with %lx\r\n", lasterr ) ; ) } } } delete [] names; } if (m_NumStrs != 0) { CheckInsertionStrings(hkSource, hkPrimary); } } void CEventlogRecord::SetTimeStr(CStringW& str, DWORD timeVal) { WBEMTime tmpTime((time_t)timeVal); BSTR tStr = tmpTime.GetDMTF(TRUE); str = tStr; SysFreeString(tStr); } void CEventlogRecord::SetType(WORD type) { switch (type) { case 0: { m_Type = m_TypeArray[0]; m_EvtType = 0; break; } case 1: { m_Type = m_TypeArray[1]; m_EvtType = 1; break; } case 2: { m_Type = m_TypeArray[2]; m_EvtType = 2; break; } case 4: { m_Type = m_TypeArray[3]; m_EvtType = 3; break; } case 8: { m_Type = m_TypeArray[4]; m_EvtType = 4; break; } case 16: { m_Type = m_TypeArray[5]; m_EvtType = 5; break; } default: { DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::SetType:Unknown type %lx\r\n", (long)type ) ; ) break; } } #if 0 if (m_Type.IsEmpty()) { wchar_t* buff = m_Type.GetBuffer(20); _ultow((ULONG)type, buff, 10); m_Type.ReleaseBuffer(); } #endif } ULONG CEventlogRecord::GetIndex(wchar_t* indexStr, BOOL* bError) { int val = _wtoi(indexStr); *bError = FALSE; ULONG index = 0; switch (val) { case EVENTLOG_SUCCESS: //0 { index = 0; break; } case EVENTLOG_ERROR_TYPE: //1 { index = 1; break; } case EVENTLOG_WARNING_TYPE: //2 { index = 2; break; } case EVENTLOG_INFORMATION_TYPE: //4 { index = 3; break; } case EVENTLOG_AUDIT_SUCCESS: //8 { index = 4; break; } case EVENTLOG_AUDIT_FAILURE: //16 { index = 5; break; } default: { *bError = TRUE; DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::Index:Unknown index %lx\r\n", val ) ; ) } } return index; } BOOL CEventlogRecord::SetEnumArray(IWbemClassObject* pClass, wchar_t* propname, CStringW* strArray, ULONG strArrayLen, GetIndexFunc IndexFunc) { BOOL retVal = FALSE; IWbemQualifierSet* pQuals = NULL; if (SUCCEEDED(pClass->GetPropertyQualifierSet(propname, &pQuals))) { VARIANT vVals; if (SUCCEEDED(pQuals->Get(EVT_ENUM_QUAL, 0, &vVals, NULL))) { VARIANT vInds; if (SUCCEEDED(pQuals->Get(EVT_MAP_QUAL, 0, &vInds, NULL))) { if ((vInds.vt == vVals.vt) && (vInds.vt == (VT_BSTR | VT_ARRAY)) && (SafeArrayGetDim(vInds.parray) == SafeArrayGetDim(vVals.parray)) && (SafeArrayGetDim(vVals.parray) == 1) && (vInds.parray->rgsabound[0].cElements == strArrayLen) && (vInds.parray->rgsabound[0].cElements == vVals.parray->rgsabound[0].cElements) ) { BSTR *strInds = NULL; if (SUCCEEDED(SafeArrayAccessData(vInds.parray, (void **)&strInds)) ) { BSTR *strVals = NULL; if (SUCCEEDED(SafeArrayAccessData(vVals.parray, (void **)&strVals)) ) { BOOL bErr = FALSE; retVal = TRUE; for (ULONG x = 0; x < strArrayLen; x++) { ULONG index = IndexFunc(strInds[x], &bErr); if (!bErr) { if (strArray[index].IsEmpty()) { strArray[index] = strVals[x]; } } else { retVal = FALSE; break; } } SafeArrayUnaccessData(vVals.parray); } SafeArrayUnaccessData(vInds.parray); } } VariantClear(&vInds); } VariantClear(&vVals); } else { DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::SetEnumArray:Failed to get enumeration qualifier.\r\n" ) ; ) } pQuals->Release(); } else { DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::SetEnumArray:Failed to get qualifier set for enumeration.\r\n" ) ; ) } return retVal; } BOOL CEventlogRecord::GetInstance() { BSTR path = SysAllocString(NTEVT_CLASS); if ( NULL == path ) { return FALSE ; } if (m_nspace != NULL) { if (!WbemTaskObject::GetClassObject(path, FALSE, m_nspace, NULL, &m_pClass )) { m_pClass = NULL; DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::GetInstance:Failed to get Class object\r\n" ) ; ) } if (!WbemTaskObject::GetClassObject(path, TRUE, m_nspace, NULL, &m_pAClass )) { m_pAClass = NULL; DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::GetInstance:Failed to get Amended Class object\r\n" ) ; ) } m_nspace->Release(); m_nspace = NULL; } if (m_pClass != NULL) { m_pClass->SpawnInstance(0, &m_Obj); if (m_pAClass) { SetEnumArray(m_pAClass, TYPE_PROP,(CStringW*)m_TypeArray, TYPE_ARRAY_LEN, (GetIndexFunc)GetIndex); m_pAClass->Release(); m_pAClass = NULL; } m_pClass->Release(); m_pClass = NULL; } SysFreeString(path); if (m_Obj != NULL) { return TRUE; } DebugOut( CNTEventProvider::g_NTEvtDebugLog->WriteFileAndLine ( _T(__FILE__),__LINE__, L"CEventlogRecord::GetInstance:Failed to spawn instance\r\n" ) ; ) return FALSE; } class CDsBindingHandle { public: // initally unbound CDsBindingHandle() : m_hDS(0) { } ~CDsBindingHandle() { if ( m_hDS ) { DsUnBind(&m_hDS); } } // only re-binds if the dc name differs... DWORD Bind(LPCWSTR strDcName); // don't call DsUnBind on an instance of this class: you'll only regret // it later. Let the dtor do the unbind. operator HANDLE() { return m_hDS; } DWORD CrackGuid(LPCWSTR pwzGuid, CStringW &strResult); private: HANDLE m_hDS; }; DWORD CDsBindingHandle::Bind(LPCWSTR strDcName) { DWORD err = NO_ERROR; if (m_hDS) { DsUnBind(&m_hDS); m_hDS = NULL; } // // NULL is used for connecting GC // LPCWSTR szDcName = NULL ; if ( NULL != *strDcName ) { szDcName = strDcName ; } err = DsBind ( szDcName, 0, &m_hDS ) ; if (err != NO_ERROR) { m_hDS = NULL; } return err; } DWORD CDsBindingHandle::CrackGuid(LPCWSTR pwzGuid, CStringW &strResult) { DWORD err = ERROR; if( NULL == m_hDS ) return err; strResult.Empty(); DS_NAME_RESULT* name_result = 0; err = DsCrackNames( m_hDS, DS_NAME_NO_FLAGS, DS_UNIQUE_ID_NAME, DS_FQDN_1779_NAME, 1, // only 1 name to crack &pwzGuid, &name_result); if (err == NO_ERROR && name_result) { DS_NAME_RESULT_ITEM* item = name_result->rItems; if (item) { // the API may return success, but each cracked name also carries // an error code, which we effectively check by checking the name // field for a value. if (item->pName) { strResult = item->pName; } } DsFreeNameResult(name_result); } return err; } CStringW CEventlogRecord::GetMappedGUID(LPCWSTR strDcName, LPCWSTR strGuid) { GUID guid; CDsBindingHandle s_hDS; if (RPC_S_OK == UuidFromString((LPWSTR)strGuid, &guid)) { return CStringW(); } CStringW strResult; ULONG ulError = NO_ERROR; do { ulError = s_hDS.Bind(strDcName); if (ulError != NO_ERROR) { break; } DS_SCHEMA_GUID_MAP* guidmap = 0; ulError = DsMapSchemaGuids(s_hDS, 1, &guid, &guidmap); if (ulError != NO_ERROR) { break; } if (guidmap->pName) { strResult = guidmap->pName; } DsFreeSchemaGuidMap(guidmap); if (strResult.GetLength()) { // the guid mapped as a schema guid: we're done break; } // the guid is not a schema guid. Proabably an object guid. ulError = s_hDS.CrackGuid(strGuid, strResult); } while (0); do { // // If we've got a string from the guid already, we're done. // if (strResult.GetLength()) { break; } // // one last try. in this case, we bind to a GC to try to crack the // name. // empty string implies GC if (s_hDS.Bind(L"") != NO_ERROR) { break; } ulError = s_hDS.CrackGuid(strGuid, strResult); } while (0); return strResult; }