Leaked source code of windows server 2003
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.
 
 
 
 
 
 

3333 lines
98 KiB

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1997.
//
// File: desc.cxx
//
// Contents:
//
// History: 2-25-1997 DavidMun Created
//
//---------------------------------------------------------------------------
#include "headers.hxx"
#pragma hdrstop
#include <malloc.h>
#define TEMP_STR_BUF MAX_PATH
#define EVENT_MSGFILE_DELIMETERS L";,"
#define PARAM_MSG_FILE_VALUE L"ParameterMessageFile"
#define MAX_INSERT_OPS 100
#define FMT_MSG_MIN_ALLOC 256
#define KERNEL32_DLL L"kernel32.dll"
//
// Types
//
// PSK - used to track the use of the Primary Source Key. This allows the
// code to initialize it on demand.
//
// SDescriptionStr - holds string and information about string as it is
// being built up.
//
// MODULE_LOAD_STATUS - describes whether a module has been loaded or
// if the attempt failed.
//
// MODULE_TYPE - identifies the type of message file module being used
//
// SParamModule - used to track handle to and status of a message file
// module.
//
enum PSK
{
PSK_NOT_EXAMINED,
PSK_NONE,
PSK_IS_LOCAL_SOURCE,
PSK_OPEN_FAILED,
PSK_VALID
};
struct SDescriptionStr
{
LPWSTR pwszExpanded;
LPWSTR pwszCur;
ULONG cchBuf;
ULONG cchRemain;
};
enum MODULE_LOAD_STATUS
{
LOAD_NOT_ATTEMPTED,
LOAD_SUCCEEDED,
LOAD_FAILED
};
enum MODULE_TYPE
{
REMOTE,
LOCAL,
PRIMARY
};
struct SParamModule
{
MODULE_LOAD_STATUS mls;
MODULE_TYPE mt;
CSafeReg *pshkSource;
CSafeCacheHandle schModule;
};
//
// Forward references
//
HRESULT
GetRemoteSystemRoot(
HKEY hkRemoteHKLM,
LPWSTR wszRemoteSystemRoot,
ULONG cch);
HRESULT
GetEventMessageFileList(
CSafeReg *pshkSource,
LPCWSTR pwszServerName,
LPWSTR wszRemoteSystemRoot,
LPWSTR **apwszEventMessageFiles);
MODULE_LOAD_STATUS
LoadRemoteParamModule(
LPCWSTR wszServerName,
CSafeReg *pshkRemote,
LPCWSTR wszRemoteSystemRoot,
LPWSTR wszRemoteParamMsgFilePath,
ULONG cchRemoteParamMsgFilePath,
CSafeCacheHandle *pschRemoteModule);
PSK
OpenPrimarySourceKey(
LPCWSTR pwszPrimarySourceName,
LPCWSTR pwszLocalSourceName,
LPCWSTR pwszLogName,
CSafeReg *pshkPrimarySource);
VOID
ReplaceAllInserts(
EVENTLOGRECORD *pelr,
CLogInfo *pli,
LPWSTR wszRemoteSystemRoot,
CSafeReg *pshkRemoteSource,
CSafeReg *pshkLocalSource,
CSafeReg *pshkPrimarySource,
PSK psk,
LPWSTR *ppwszMsg);
HRESULT
ReplaceStringInsert(
SDescriptionStr *pds,
EVENTLOGRECORD *pelr);
VOID
ReplaceParameterInsert(
SDescriptionStr *pds,
EVENTLOGRECORD *pelr,
CLogInfo *pli,
LPWSTR wszRemoteSystemRoot,
SParamModule *ppmRemote,
SParamModule *ppmLocal,
SParamModule *ppmPrimary,
PSK *pPSK);
HRESULT
ReplaceSubStr(
LPCWSTR pwszToInsert,
LPWSTR *ppwszBuf,
LPWSTR *ppwszInsertPoint,
LPCWSTR pwszSubStrEnd,
ULONG *pcchBuf,
ULONG *pcchRemain);
ULONG
TerminatePathStrings(
LPWSTR pwszUnexpanded);
IDirectorySearch *
DeriveGcFromServer(
PCWSTR pwzMachine);
wstring
DoLdapQueryForSid(
IDirectorySearch *pDirSearch,
PSID psid);
//+--------------------------------------------------------------------------
//
// Function: AppendRedirectionURLText
//
// Synopsis: Uses the g_IsMicrosoftDllCache to find out if Microsoft
// created pwszMessageFile. If so, reallocates *ppwszMsg and
// appends the redirection URL text that instructs the user to
// to to the Microsoft web site for more information.
//
// Arguments: [pwszMessageFile] - module filename
// [ppwszMsg] - holds result
//
// Modifies: *[ppwszMsg], accesses "is Microsoft" dll cache
//
// History: 9-16-2000 Created
//
//---------------------------------------------------------------------------
static VOID
AppendRedirectionURLText(
LPCWSTR pwszMessageFile,
LPWSTR *ppwszMsg)
{
TRACE_FUNCTION(AppendRedirectionURLText);
//
// If we previously failed to load the redirection URL text, just return.
//
if (g_wszRedirectionTextToAppend[0] == 0)
return;
//
// Use the cache to determine if the DLL was created by Microsoft. If not
// just return.
//
BOOL fIsMicrosoftDll = FALSE;
(void) g_IsMicrosoftDllCache.Fetch(pwszMessageFile, &fIsMicrosoftDll);
if (!fIsMicrosoftDll)
return;
// 714804-2002/10/04-JonN Suppress FWLINK if Exchange's
// CONTENTREDIRECT link is present
// Note that this test could in theory trigger if the CONTENTREDIRECT
// string is present as a substring which is not linked, e.g.
// "foobar\http://www.microsoft.com...". This would cause the FWLINK
// to be suppressed incorrectly. This is extremely unlikely however,
// especially since *ppwszMsg at this point comes directly from the
// EventMessageFile and has not yet been expanded.
if (NULL != wcsistr(*ppwszMsg,
L"http://www.microsoft.com/contentredirect.asp"))
return;
//
// Allocate a new buffer large enough to hold the original description
// plus the redirection URL text.
//
LPWSTR pwszNewMsg = (LPWSTR) LocalAlloc(0, (wcslen(*ppwszMsg) + wcslen(g_wszRedirectionTextToAppend) + 1) * sizeof(WCHAR));
if (pwszNewMsg == NULL)
return;
//
// Some applications such as IIS already hard-code the redirection URL in
// their event descriptions. If we find the URL in the original
// description, just return.
//
//This modification is not necessary in W2K code. #256032.
WCHAR wszUpperCaseURL[MAX_PATH];
wcscpy(wszUpperCaseURL, g_wszRedirectionURL);
_wcsupr(wszUpperCaseURL);
wcscpy(pwszNewMsg, *ppwszMsg);
_wcsupr(pwszNewMsg);
if (wcsstr(pwszNewMsg, wszUpperCaseURL) != NULL) {
LocalFree(pwszNewMsg);
return;
}
//
// Fill the new buffer with the original text and append the URL
// redirection text. Deallocate the original buffer and return the new
// one to the caller.
//
wcscpy(pwszNewMsg, *ppwszMsg);
wcscat(pwszNewMsg, g_wszRedirectionTextToAppend);
LocalFree(*ppwszMsg);
*ppwszMsg = pwszNewMsg;
}
//+--------------------------------------------------------------------------
//
// Function: AttemptFormatMessage
//
// Synopsis: Perform a FormatMessage against message id [ulEventID] in
// module [pwszMessageFile], putting the LocalAlloced result
// in [ppwszMsg].
//
// Arguments: [ulEventID] - message id
// [pwszMessageFile] - module filename
// [ppwszMsg] - holds result
// [fAppendURL] - TRUE if formatting and event descr.
//
// Returns: TRUE - *[ppwszMsg] valid
// FALSE - *[ppwszMsg] is NULL
//
// Modifies: *[ppwszMsg], accesses dll cache
//
// History: 2-24-1997 DavidMun Created
//
//---------------------------------------------------------------------------
BOOL
AttemptFormatMessage(
ULONG ulEventID,
LPCWSTR pwszMessageFile,
LPWSTR *ppwszMsg,
BOOL fAppendURL)
{
TRACE_FUNCTION(AttemptFormatMessage);
BOOL fOk = FALSE; // init for failure
CSafeCacheHandle schMsgFile;
*ppwszMsg = NULL;
(void) g_DllCache.Fetch(pwszMessageFile, &schMsgFile);
if ((HINSTANCE) schMsgFile)
{
ULONG cch = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
(LPCVOID) (HINSTANCE) schMsgFile,
ulEventID,
0,
(LPWSTR) ppwszMsg,
FMT_MSG_MIN_ALLOC,
NULL);
if (cch)
{
// successfully got event message string
fOk = TRUE;
// JonN 11/21/00 213436
// Event Viewer: Trailing spaces in strings within results pane
if (*ppwszMsg && (L' ' == (*ppwszMsg)[cch-1]))
(*ppwszMsg)[cch-1] = TEXT('\0');
if (fAppendURL)
AppendRedirectionURLText(pwszMessageFile, ppwszMsg);
}
else
{
Dbg(DEB_ITRACE,
"AttemptFormatMessage: error %uL for eventid %uL\n",
GetLastError(),
ulEventID);
}
}
return fOk;
}
#define CCH_PUNCTUATION_MAX 10
//+--------------------------------------------------------------------------
//
// Function: CreateFallbackMessage
//
// Synopsis: Create a string that lists all inserts found in [pelr].
//
// Arguments: [pelr] - event log record for which to construct string
// [ppwszMsg] - filled with LocalAlloced message or NULL
//
// Modifies: *[ppwszMsg]
//
// History: 2-24-1997 DavidMun Created
// 2-01-2001 JonN/YangGao 256032 buffer overrun cleanup
// 3-01-2002 JonN Added pstrUnexpandedDescriptionStr
// parameter (539485)
//
//---------------------------------------------------------------------------
VOID
CreateFallbackMessage(
EVENTLOGRECORD *pelr,
LPWSTR *ppwszMsg,
LPWSTR *ppstrUnexpandedDescriptionStr)
{
TRACE_FUNCTION(CreateFallbackMessage);
*ppwszMsg = NULL; // init for failure
LPWSTR pwszSrcName = GetSourceStr(pelr);
wstring wstrNotFound = FormatString(IDS_DESCR_NOTFOUND, (WORD)pelr->EventID, pwszSrcName);
if(wstrNotFound.empty())
{
return;
}
// 2002/03/12-JonN 539485
if (NULL != ppstrUnexpandedDescriptionStr)
{
*ppstrUnexpandedDescriptionStr = (WCHAR*)
LocalAlloc(LMEM_FIXED, (wcslen(wstrNotFound.c_str())+1)
* sizeof(WCHAR));
ASSERT( NULL != *ppstrUnexpandedDescriptionStr );
if (*ppstrUnexpandedDescriptionStr)
{
lstrcpy( *ppstrUnexpandedDescriptionStr, wstrNotFound.c_str() );
}
}
// +6 for extra room
size_t cchRequired = wcslen(wstrNotFound.c_str()) + 6;
wstring strErr;
LPWSTR pwszCurInsert = GetFirstInsertString(pelr);
if (IsBadStringPtr(pwszCurInsert, (UINT_PTR)-1))
{
// NTRAID#NTBUG9-546298-2002/05/04-ericb
strErr = load_wstring(IDS_EVENTLOG_FILE_CORRUPT);
pwszCurInsert = const_cast<LPWSTR>(strErr.c_str());
pelr->NumStrings = 1;
}
USHORT i;
//
// Get the localized strings for separating a list and ending a sentence.
//
WCHAR wszListSeparator[CCH_PUNCTUATION_MAX];
ULONG cchSeparator;
WCHAR wszTerminator[CCH_PUNCTUATION_MAX];
ULONG cchTerminator;
GetLocaleStr(LOCALE_SLIST,
wszListSeparator,
ARRAYLEN(wszListSeparator),
L",");
cchSeparator = lstrlen(wszListSeparator);
LoadStr(IDS_FALLBACK_DESCR_TERMINATOR,
wszTerminator,
ARRAYLEN(wszTerminator),
L".");
cchTerminator = lstrlen(wszTerminator);
if (!pelr->NumStrings)
{
cchRequired++; // for space before terminator
}
else
{
for (i = 0; i < pelr->NumStrings; i++)
{
//
// each insert string is preceeded by a space
// & followed by a list separator.
//
if (IsBadStringPtr(pwszCurInsert, (UINT_PTR)-1))
{
ASSERT(false && "bad insert string pointer!");
pelr->NumStrings = i - 1;
break;
}
size_t cchCurInsert = wcslen(pwszCurInsert);
cchRequired += 1 + cchCurInsert + cchSeparator;
pwszCurInsert += cchCurInsert + 1;
}
}
cchRequired += cchTerminator;
*ppwszMsg = (WCHAR*)
LocalAlloc(LMEM_FIXED, cchRequired * sizeof(WCHAR));
if (!*ppwszMsg)
{
DBG_OUT_HRESULT(E_OUTOFMEMORY);
return;
}
wcscpy( *ppwszMsg, wstrNotFound.c_str() );
//
// If there are no inserts, terminate the sentence and leave.
//
if (!pelr->NumStrings)
{
wcscat(*ppwszMsg, L" ");
wcscat(*ppwszMsg, wszTerminator);
return;
}
pwszCurInsert = GetFirstInsertString(pelr);
if (IsBadStringPtr(pwszCurInsert, (UINT_PTR)-1))
{
// NTRAID#NTBUG9-546298-2002/05/04-ericb
if (strErr.empty()) strErr = load_wstring(IDS_EVENTLOG_FILE_CORRUPT);
pwszCurInsert = const_cast<LPWSTR>(strErr.c_str());
pelr->NumStrings = 1;
}
for (i = 0; i < pelr->NumStrings; i++)
{
//
// for each insert string: preceed it by a space
// & follow it by a comma/period.
//
wcscat(*ppwszMsg, L" ");
wcscat(*ppwszMsg, pwszCurInsert);
if (i == (pelr->NumStrings - 1))
{
wcscat(*ppwszMsg, wszTerminator);
}
else
{
wcscat(*ppwszMsg, wszListSeparator);
}
pwszCurInsert += wcslen(pwszCurInsert) + 1;
}
}
//+--------------------------------------------------------------------------
//
// Function: ExpandStringArray
//
// Synopsis: Convert the multi-sz string [pwszUnexpanded] containing
// path names into an array of paths or UNCs with expanded
// environment variables.
//
// Arguments: [pwszUnexpanded] - multi-sz string
// [cPaths] - number of non-empty paths in
// [pwszUnexpanded]
// [pwszServerName] - NULL or name of server if paths
// are on remote machine
// [wszRemoteSystemRoot] - value of remote systemroot
// [ppwszExpanded] - array of [cPaths] pointers,
// all NULL on entry
//
// Returns: HRESULT
//
// Modifies: *[ppwszExpanded]
//
// History: 3-04-1997 DavidMun Created
//
//---------------------------------------------------------------------------
HRESULT
ExpandStringArray(
LPWSTR pwszUnexpanded,
ULONG cPaths,
LPCWSTR pwszServerName,
LPWSTR wszRemoteSystemRoot,
LPWSTR *ppwszExpanded)
{
TRACE_FUNCTION(ExpandStringArray);
HRESULT hr = S_OK;
ULONG i;
LPWSTR pwszCurUnexpanded = pwszUnexpanded;
for (i = 0; i < cPaths; i++)
{
//
// skip multiple terminators. If the value is "c:\foo;;d:\bar"
// TerminatePathStrings will return 2 and leave the string as
// "c:\foo\0\0d:\bar".
//
while (!*pwszCurUnexpanded)
{
pwszCurUnexpanded++;
}
//
// Allocate enough space to hold a max length UNC name. This
// wastes a small amount of space for the local machine, but
// this array only exists temporarily.
//
ppwszExpanded[i] = new WCHAR[MAX_PATH];
if (!ppwszExpanded[i])
{
hr = E_OUTOFMEMORY;
DBG_OUT_HRESULT(hr);
break;
}
if (pwszServerName)
{
hr = ExpandRemoteSystemRoot(pwszCurUnexpanded,
wszRemoteSystemRoot,
ppwszExpanded[i],
MAX_PATH);
BREAK_ON_FAIL_HRESULT(hr);
hr = ConvertPathToUNC(pwszServerName,
ppwszExpanded[i],
MAX_PATH);
BREAK_ON_FAIL_HRESULT(hr);
}
else
{
ULONG cch = ExpandEnvironmentStrings(pwszCurUnexpanded,
ppwszExpanded[i],
MAX_PATH);
if (!cch)
{
hr = HRESULT_FROM_LASTERROR;
DBG_OUT_LASTERROR;
break;
}
if (cch > MAX_PATH)
{
hr = E_FAIL;
Dbg(DEB_ERROR,
"ExpandEnvironmentStrings of '%s' requires %u chars\n",
pwszCurUnexpanded,
cch);
break;
}
}
//
// Advance within the multi-terminated string to the next
// unexpanded path.
//
pwszCurUnexpanded += wcslen(pwszCurUnexpanded) + 1;
}
return hr;
}
//+--------------------------------------------------------------------------
//
// Function: FreeStringArray
//
// Synopsis: Delete the strings in [apwsz], then delete [apwsz] itself.
//
// Arguments: [apwsz] - array of pointers to strings, last element is
// NULL.
//
// History: 2-24-1997 DavidMun Created
//
//---------------------------------------------------------------------------
VOID
FreeStringArray(LPWSTR *apwsz)
{
TRACE_FUNCTION(FreeStringArray);
if (!apwsz)
{
return;
}
LPWSTR *ppwszCur = apwsz;
while (*ppwszCur)
{
delete [] *ppwszCur;
ppwszCur++;
}
delete [] apwsz;
}
//+--------------------------------------------------------------------------
//
// Function: GetDescriptionStr
//
// Synopsis: Return a LocalAlloced description string for record [pelr]
// read from log described by [pli].
//
// Arguments: [pli] - info about log containing [pelr]
// [pelr] - event containing description to expand
//
// Returns: String allocated with LocalAlloc, or NULL on error
//
// Modifies: Uses dll cache.
//
// History: 2-24-1997 DavidMun Created
// 4-17-2001 JonN Added fAppendURL parameter
// 3-01-2002 JonN Added pstrUnexpandedDescriptionStr
// parameter (539485)
//
// Notes: Caller must LocalFree returned string.
//
//---------------------------------------------------------------------------
LPWSTR
GetDescriptionStr(
CLogInfo *pli,
EVENTLOGRECORD *pelr,
wstring *pstrMessageFile,
BOOL fAppendURL,
LPWSTR *ppstrUnexpandedDescriptionStr)
{
TRACE_FUNCTION(GetDescriptionStr);
// JonN 3/21/01 350614
// Use message dlls and DS, FRS and DNS log types from specified computer
LPWSTR pwszRemoteMessageServer = pli->GetLogServerName();
if ( pli->IsBackupLog() && *g_wszAuxMessageSource )
{
pwszRemoteMessageServer = g_wszAuxMessageSource;
}
HRESULT hr = S_OK;
BOOL fRemote = pwszRemoteMessageServer != NULL;
BOOL fRemoteMessageFilesToTry = fRemote;
BOOL fLocalMessageFilesToTry = TRUE;
BOOL fFoundMsg = FALSE;
LPWSTR *apwszRemoteEventMessageFiles = NULL;
LPWSTR *ppwszCurRemote = NULL;
LPWSTR *apwszLocalEventMessageFiles = NULL;
LPWSTR *ppwszCurLocal = NULL;
LPWSTR pwszMsg = NULL;
WCHAR wszRemoteSystemRoot[MAX_PATH+1] = L"";
CSafeReg shkRemoteHKLM;
CSafeReg shkLocalSource;
CSafeReg shkRemoteSource;
if (pstrMessageFile)
{
pstrMessageFile->erase();
}
//
// Build the name of the source registry key so we can read the parameter
// message file and event message file names.
//
// JonN 12/18/00 256032: Buffer overrun in Event Viewer
LPCWSTR pwszLogName = pli->GetLogName();
LPCWSTR pwszSourceStr = GetSourceStr(pelr);
if (!pwszLogName || !pwszSourceStr)
{
DBG_OUT_LRESULT(E_UNEXPECTED);
CreateFallbackMessage(pelr, &pwszMsg, ppstrUnexpandedDescriptionStr);
return pwszMsg;
}
// NTRAID#NTBUG9-2002/05/17-623723-ericb: prefast
WCHAR* wszSourceKeyName = (WCHAR*)malloc(
(wcslen(EVENTLOG_KEY)+wcslen(pwszLogName)+wcslen(pwszSourceStr)+3)
* sizeof(WCHAR) );
if (!wszSourceKeyName)
{
DBG_OUT_LRESULT(E_OUTOFMEMORY);
CreateFallbackMessage(pelr, &pwszMsg, ppstrUnexpandedDescriptionStr);
return pwszMsg;
}
wsprintf(wszSourceKeyName,
L"%s\\%s\\%s",
EVENTLOG_KEY,
pwszLogName,
pwszSourceStr);
//
// Attempt to open the key on the local machine. It will be used if
// the log is local, or if the log is remote and there is a problem
// loading the remote message files.
//
hr = shkLocalSource.Open(HKEY_LOCAL_MACHINE,
wszSourceKeyName,
KEY_QUERY_VALUE);
if (FAILED(hr))
{
fLocalMessageFilesToTry = FALSE;
Dbg(DEB_ITRACE,
"Error 0x%x attempting to open local key '%s'\n",
hr,
wszSourceKeyName);
hr = S_OK; // this error has been processed, reset hr
}
//
// If the log lies on a remote machine, get the information needed to
// read event message files and parameter message files from the machine:
// a connection to its HKEY_LOCAL_MACHINE, the value of its SystemRoot
// environment variable, and a list of event message files for the source
// in pelr.
//
// If any of these fail, use only the local machine.
//
do
{
if (!fRemoteMessageFilesToTry) // JonN 3/20/01 350614
{
break;
}
hr = shkRemoteHKLM.Connect(pwszRemoteMessageServer, HKEY_LOCAL_MACHINE);
BREAK_ON_FAIL_HRESULT(hr);
hr = GetRemoteSystemRoot(shkRemoteHKLM,
wszRemoteSystemRoot,
ARRAYLEN(wszRemoteSystemRoot));
BREAK_ON_FAIL_HRESULT(hr);
hr = shkRemoteSource.Open(shkRemoteHKLM,
wszSourceKeyName,
KEY_QUERY_VALUE);
if (FAILED(hr))
{
Dbg(DEB_ITRACE,
"Error 0x%x attempting to open remote source key '%s'\n",
hr,
wszSourceKeyName);
break;
}
Dbg(DEB_ITRACE, "Opened remote source key '%s'\n", wszSourceKeyName);
hr = GetEventMessageFileList(&shkRemoteSource,
pwszRemoteMessageServer,
wszRemoteSystemRoot,
&apwszRemoteEventMessageFiles);
BREAK_ON_FAIL_HRESULT(hr);
ppwszCurRemote = apwszRemoteEventMessageFiles;
} while (0);
free(wszSourceKeyName);
if (FAILED(hr))
{
fRemoteMessageFilesToTry = FALSE;
}
while (!fFoundMsg &&
(fRemoteMessageFilesToTry || fLocalMessageFilesToTry))
{
//
// If we're looking at a log on a remote machine, were able to get
// a list of the event message files for pelr's source, and haven't
// exhausted that list yet, see if a string for pelr->EventID is in
// the next remote machine module.
//
if (fRemoteMessageFilesToTry)
{
if (*ppwszCurRemote)
{
fFoundMsg = AttemptFormatMessage(pelr->EventID,
*ppwszCurRemote,
&pwszMsg,
fAppendURL);
if (fFoundMsg)
{
Dbg(DEB_ITRACE,
"Found event id 0x%x message '%s' in remote file '%s'\n",
pelr->EventID,
pwszMsg,
*ppwszCurRemote);
//
// If caller wants message file used to get message,
// fill it in.
//
if (pstrMessageFile)
{
*pstrMessageFile = *ppwszCurRemote;
}
}
ppwszCurRemote++;
}
else
{
fRemoteMessageFilesToTry = FALSE;
}
}
//
// If the log is remote and we didn't just get the message string from
// the remote machine, or if the log is local, try the local machine,
// assuming we haven't already and failed to get the local event file
// message list.
//
if (!fFoundMsg && fLocalMessageFilesToTry)
{
if (!ppwszCurLocal)
{
hr = GetEventMessageFileList(&shkLocalSource,
NULL,
L"",
&apwszLocalEventMessageFiles);
if (FAILED(hr))
{
fLocalMessageFilesToTry = FALSE;
}
else
{
ppwszCurLocal = apwszLocalEventMessageFiles;
}
}
if (fLocalMessageFilesToTry && *ppwszCurLocal)
{
fFoundMsg = AttemptFormatMessage(pelr->EventID,
*ppwszCurLocal,
&pwszMsg,
fAppendURL);
if (fFoundMsg)
{
Dbg(DEB_ITRACE,
"Found event id 0x%x message '%s' in local file '%s'\n",
pelr->EventID,
pwszMsg,
*ppwszCurLocal);
// JonN 3/20/01 CODEWORK I don't understand why only if !fRemote
if (!fRemote && pstrMessageFile)
{
*pstrMessageFile = *ppwszCurLocal;
}
}
ppwszCurLocal++;
}
else
{
fLocalMessageFilesToTry = FALSE;
}
}
}
FreeStringArray(apwszRemoteEventMessageFiles);
FreeStringArray(apwszLocalEventMessageFiles);
//
// If we couldn't find it on the remote or local machines using the
// source named in the eventlog record itself, look in the primary
// source on the local machine, if it exists.
//
PSK PrimarySourceKey = PSK_NOT_EXAMINED;
LPWSTR *apwszPrimaryEventMessageFiles = NULL;
LPWSTR *ppwszCurPrimary = NULL;
BOOL fPrimaryMessageFilesToTry = FALSE;
CSafeReg shkPrimarySource;
do
{
//
// Don't bother with primary source if we've already found the
// message.
//
if (fFoundMsg)
{
break;
}
PrimarySourceKey = OpenPrimarySourceKey(pli->GetPrimarySourceStr(),
pwszSourceStr,
pwszLogName,
&shkPrimarySource);
if (PrimarySourceKey != PSK_VALID)
{
break;
}
//
// Get the primary source's message file list
//
hr = GetEventMessageFileList(&shkPrimarySource,
NULL,
L"",
&apwszPrimaryEventMessageFiles);
BREAK_ON_FAIL_HRESULT(hr);
ppwszCurPrimary = apwszPrimaryEventMessageFiles;
fPrimaryMessageFilesToTry = TRUE;
} while (0);
while (!fFoundMsg && ppwszCurPrimary && *ppwszCurPrimary)
{
fFoundMsg = AttemptFormatMessage(pelr->EventID,
*ppwszCurPrimary,
&pwszMsg,
fAppendURL);
#if (DBG == 1)
if (fFoundMsg)
{
Dbg(DEB_ITRACE,
"Found event id 0x%x message '%s' in primary file '%s'\n",
pelr->EventID,
pwszMsg,
*ppwszCurPrimary);
}
#endif // (DBG == 1)
ppwszCurPrimary++;
}
FreeStringArray(apwszPrimaryEventMessageFiles);
//
// If we still didn't find it use the fallback message (localized
// string like "couldn't find message string for event id %u")
//
if (!fFoundMsg)
{
CreateFallbackMessage(pelr, &pwszMsg, ppstrUnexpandedDescriptionStr);
}
//
// Replace the strings inserts (they are of the form %n) and the
// parameter inserts (they are of the form %%n). Note that
// ReplaceAllInserts will realloc pwszMsg if it finds any inserts.
//
if (pwszMsg)
{
// 2002/03/12-JonN 539485
if (fFoundMsg && NULL != ppstrUnexpandedDescriptionStr)
{
*ppstrUnexpandedDescriptionStr = (WCHAR*)
LocalAlloc(LMEM_FIXED, (wcslen(pwszMsg)+1) * sizeof(WCHAR));
ASSERT( NULL != *ppstrUnexpandedDescriptionStr );
if (*ppstrUnexpandedDescriptionStr)
{
lstrcpy( *ppstrUnexpandedDescriptionStr, pwszMsg );
}
}
ReplaceAllInserts(pelr,
pli,
wszRemoteSystemRoot,
&shkRemoteSource,
&shkLocalSource,
&shkPrimarySource,
PrimarySourceKey,
&pwszMsg);
}
return pwszMsg;
} // GetDescriptionStr
//+--------------------------------------------------------------------------
//
// Function: GetEventMessageFileList
//
// Synopsis: Create an array of paths to the event message files
// specified by the arguments; for a remote machine, the paths
// will be UNCs.
//
// Arguments: [pshkSource] - source reg key (may be remote)
// [pwszServerName] - server name or NULL for local
// [hkRemoteHKLM] - connection to remote HKLM
// [wszRemoteSystemRoot] - value for remote %SystemRoot%
// [apwszEventMessageFiles] - filled with array of msg files
// or NULL on error
//
// Returns: HRESULT
//
// Modifies: *[apwszEventMessageFiles]
//
// History: 2-24-1997 DavidMun Created
//
// Notes: Caller must free with FreeStringArray.
//
//---------------------------------------------------------------------------
HRESULT
GetEventMessageFileList(
CSafeReg *pshkSource,
LPCWSTR pwszServerName,
LPWSTR wszRemoteSystemRoot,
LPWSTR **apwszEventMessageFiles)
{
TRACE_FUNCTION(GetEventMessageFileList);
HRESULT hr = S_OK;
LPWSTR *ppwszExpanded = NULL;
LPWSTR pwszUnexpanded = NULL;
do
{
//
// Read the EventMessageFile value, which is a REG_EXPAND_SZ string
// containing multiple comma or semicolon separated filenames.
//
ULONG cbUnexpanded;
hr = pshkSource->QueryBufSize(MESSAGEFILE_VALUE, &cbUnexpanded);
BREAK_ON_FAIL_HRESULT(hr);
ULONG cchUnexpanded = cbUnexpanded / sizeof(WCHAR) + 1;
pwszUnexpanded = new WCHAR[cchUnexpanded];
if (!pwszUnexpanded)
{
hr = E_OUTOFMEMORY;
DBG_OUT_HRESULT(hr);
break;
}
hr = pshkSource->QueryPath(MESSAGEFILE_VALUE,
pwszUnexpanded,
cchUnexpanded,
FALSE);
BREAK_ON_FAIL_HRESULT(hr);
//
// Count the number of paths so we can allocate the array and
// each of the individual path strings, and change the delimiters
// to null chars.
//
ULONG cPaths = TerminatePathStrings(pwszUnexpanded);
if (!cPaths)
{
hr = E_FAIL;
Dbg(DEB_IWARN, "Messagefile value has no paths\n");
DBG_OUT_HRESULT(hr);
break;
}
ppwszExpanded = new LPWSTR[cPaths + 1]; // +1 for sentinel
if (!ppwszExpanded)
{
hr = E_OUTOFMEMORY;
DBG_OUT_HRESULT(hr);
break;
}
ZeroMemory(ppwszExpanded, sizeof(LPWSTR) * (cPaths + 1));
hr = ExpandStringArray(pwszUnexpanded,
cPaths,
pwszServerName,
wszRemoteSystemRoot,
ppwszExpanded);
if (FAILED(hr))
{
DBG_OUT_HRESULT(hr);
FreeStringArray(ppwszExpanded);
ppwszExpanded = NULL;
break;
}
} while (0);
delete [] pwszUnexpanded;
*apwszEventMessageFiles = ppwszExpanded;
return hr;
}
//+--------------------------------------------------------------------------
//
// Function: GetInsertStr
//
// Synopsis: Return a pointer to the specified insert string within
// [pelr].
//
// Arguments: [pelr] - points to event log record
// [idxString] - 0-based index of string to get
//
// Returns: Pointer to requested string
//
// History: 3-04-1997 DavidMun Created
//
//---------------------------------------------------------------------------
LPWSTR
GetInsertStr(
EVENTLOGRECORD *pelr,
ULONG idxString)
{
TRACE_FUNCTION(GetInsertStr);
ASSERT(pelr);
ASSERT(pelr->NumStrings > idxString);
LPWSTR pwszCur = GetFirstInsertString(pelr);
if (!pwszCur)
{
Dbg(DEB_ERROR, "First insert string not found\n");
return L"";
}
ULONG cToSkip;
for (cToSkip = idxString; cToSkip; cToSkip--)
{
pwszCur += lstrlen(pwszCur) + 1;
}
return pwszCur;
}
//+--------------------------------------------------------------------------
//
// Function: LoadRemoteParamModule
//
// Synopsis: Load the module containing parameter inserts on remote
// machine [wszServerName].
//
// Arguments: [wszServerName] - remote machine name (less \\)
// [pshkRemoteSource] - source reg key on [wszServerName]
// [wszRemoteSystemRoot] - %SystemRoot% on [wszServerName]
// [wszRemoteParamMsgFilePath] - filled with remote source key's
// parameter msg file value
// [cchRemoteParamMsgFilePath] - size of buffer in characters
// [pschRemoteModule] - filled with module handle
//
// Returns: LOAD_SUCCEEDED or LOAD_FAILED
//
// Modifies: [pschRemoteModule]
//
// History: 3-06-1997 DavidMun Created
//
//---------------------------------------------------------------------------
MODULE_LOAD_STATUS
LoadRemoteParamModule(
LPCWSTR wszServerName,
CSafeReg *pshkRemoteSource,
LPCWSTR wszRemoteSystemRoot,
LPWSTR wszRemoteParamMsgFilePath,
ULONG cchRemoteParamMsgFilePath,
CSafeCacheHandle *pschRemoteModule)
{
HRESULT hr = S_OK;
WCHAR wszTemp[MAX_PATH + 1] = L"";
do
{
//
// If the handle to the remote system's source reg key couldn't
// be opened we won't be able to query it for the parameter
// message file value, so bail.
//
if (!(HKEY)*pshkRemoteSource)
{
hr = E_FAIL;
break;
}
ASSERT(*wszRemoteSystemRoot);
//
// Determine the file name of the remote parameter file
//
hr = pshkRemoteSource->QueryPath(PARAM_MSG_FILE_VALUE,
wszTemp,
ARRAYLEN(wszTemp),
FALSE);
if (FAILED(hr))
{
//
// Since the ParameterMessageFile value is optional, this
// is an acceptable "error."
//
Dbg(DEB_ITRACE,
"Result 0x%x attempting to get remote parameter file\n",
hr);
break;
}
//
// Expand the filename into a UNC and try to load the module
// via the dll cache.
//
hr = ExpandRemoteSystemRoot(wszTemp,
wszRemoteSystemRoot,
wszRemoteParamMsgFilePath,
cchRemoteParamMsgFilePath);
BREAK_ON_FAIL_HRESULT(hr);
hr = ConvertPathToUNC(wszServerName,
wszRemoteParamMsgFilePath,
cchRemoteParamMsgFilePath);
Dbg(DEB_ITRACE,
"Expanded remote parameter file is '%s'\n",
wszRemoteParamMsgFilePath);
//
// Ignore the return value except for a debug out, since
// schRemoteParam will either contain a valid module handle
// or NULL.
//
hr = g_DllCache.Fetch(wszRemoteParamMsgFilePath, pschRemoteModule);
CHECK_HRESULT(hr);
} while (0);
return FAILED(hr) ? LOAD_FAILED : LOAD_SUCCEEDED;
}
//+--------------------------------------------------------------------------
//
// Function: OpenPrimarySourceKey
//
// Synopsis: Attempt to open the registry key for the primary source
// named [pwszPrimarySourceName] under HKLM, but only if
// there is a primary source and it differs from the local
// source key.
//
// Arguments: [pwszPrimarySourceName] - name of primary source, may be NULL
// [pwszLocalSourceName] - name of local source
// [pwszLogName] - name of log sources live in
// [pshkPrimarySource] - valid unopened CSafeReg object
//
// Returns: Enumeration value describing resulting status of
// *[pshkPrimarySource].
//
// History: 2-27-1997 DavidMun Created
//
//---------------------------------------------------------------------------
PSK
OpenPrimarySourceKey(
LPCWSTR pwszPrimarySourceName,
LPCWSTR pwszLocalSourceName,
LPCWSTR pwszLogName,
CSafeReg *pshkPrimarySource)
{
TRACE_FUNCTION(OpenPrimarySourceKey);
PSK PrimarySourceKey = PSK_NONE;
wstring wstrPrimarySourceKeyName;
do
{
//
// If this record's log doesn't have a primary source, quit.
//
if (!pwszPrimarySourceName)
{
break;
}
//
// Quit if the source we have already looked under is the same
// as the primary source.
//
// NTRAID#NTBUG9-2002/05/17-623723-ericb: prefast, don't use lstrcmpi
if (!_wcsicmp(pwszLocalSourceName, pwszPrimarySourceName))
{
PrimarySourceKey = PSK_IS_LOCAL_SOURCE;
break;
}
//
// Open the primary source's reg key
//
wstrPrimarySourceKeyName = EVENTLOG_KEY;
wstrPrimarySourceKeyName += L"\\";
wstrPrimarySourceKeyName += pwszLogName;
wstrPrimarySourceKeyName += L"\\";
wstrPrimarySourceKeyName += pwszPrimarySourceName;
HRESULT hr;
hr = pshkPrimarySource->Open(HKEY_LOCAL_MACHINE,
(LPCTSTR)(wstrPrimarySourceKeyName.c_str()),
KEY_QUERY_VALUE);
if (FAILED(hr))
{
PrimarySourceKey = PSK_OPEN_FAILED;
}
else
{
PrimarySourceKey = PSK_VALID;
}
} while (0);
return PrimarySourceKey;
}
// convert a string of the form returned by StringFromGUID2 (e.g.
// {c200e360-38c5-11ce-ae62-08002b2b79ef} ) to a GUID.
//
// Returns true on success, false on failure.
bool
wstringToGUID(const wstring& guidString, GUID& guid)
{
ASSERT(!guidString.empty());
memset(&guid, 0, sizeof(guid));
// you'd think there'd be a GUIDFromString, but nooooo....
HRESULT hr =
::IIDFromString(
const_cast<wstring::value_type*>(guidString.c_str()),
&guid);
return SUCCEEDED(hr) ? true : false;
}
// class wrapping a handle bound to the directory service on a given domain
// controller. Used to give the binding nice lifetime semantics such that
// when the wrapper instance is destroyed, the handle is unbound.
class CDsBindingHandle
{
public:
// initally unbound
CDsBindingHandle()
:
m_hDS(0)
{
}
~CDsBindingHandle()
{
::DsUnBind(&m_hDS);
}
// only re-binds if the dc name differs...
DWORD
Bind(const wstring& 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;
}
private:
HANDLE m_hDS;
wstring m_strDcName;
};
DWORD
CDsBindingHandle::Bind(const wstring &strDcName)
{
TRACE_METHOD(CDsBindingHandle, Bind);
if (m_strDcName != strDcName || !m_hDS)
{
if (m_hDS)
{
::DsUnBind(&m_hDS);
m_hDS = NULL;
}
PWSTR pwzDcName = (PWSTR)strDcName.c_str();
if (!*pwzDcName)
{
Dbg(DEB_TRACE, "binding with NULL\n");
pwzDcName = NULL;
}
else
{
Dbg(DEB_TRACE, "binding to %s\n", pwzDcName);
}
DWORD err = ::DsBind(pwzDcName, 0, &m_hDS);
if (err == NO_ERROR)
{
m_strDcName = strDcName;
}
else
{
DBG_OUT_LRESULT(err);
ASSERT(!m_hDS);
m_hDS = NULL;
}
return err;
}
return NO_ERROR;
}
// Call DsCrackNames to attempt to resolve a guid to its DN. Returns the
// error code returned by DsCrackNames, and is successful, sets the result
// parameter to the DN of the object represented by the guid.
//
// handle - the handle to use, must already be opened by a prior call to
// DsBind.
//
// pwzGuid - the stringized guid
//
// strResult - receives the resulting DN upon success, cleared otherwise.
DWORD
CrackGuid(
HANDLE handle,
PWSTR pwzGuid,
wstring *pstrResult)
{
TRACE_FUNCTION(CrackGuid);
ASSERT(pwzGuid);
ASSERT(pstrResult);
pstrResult->erase();
DS_NAME_RESULT* name_result = 0;
DWORD err = ::DsCrackNames(
handle,
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)
{
*pstrResult = item->pName;
}
else
{
Dbg(DEB_ERROR,
"DsCrackNames status %#x\n",
name_result->rItems[0].status);
}
}
else
{
Dbg(DEB_ERROR, "DsCrackNames returned no items\n");
}
::DsFreeNameResult(name_result);
}
else if (err != NO_ERROR)
{
Dbg(DEB_ERROR, "DsCrackNames of '%s' %u\n", pwzGuid, err);
}
return err;
}
//+--------------------------------------------------------------------------
//
// Function: GetMappedGUID
//
// Synopsis: Map a GUID to the name of the directory object the GUID
// represents by appealing to the given domain controller to
// provide this information.
//
// Arguments: [strDcName] - domain controller from the domain where the
// object represented by the GUID exists.
// [strGuid] - GUID of the object as a string.
//
// Returns: String containing mapped name
//
// History: ??-??-199? SBurns Created
//
//---------------------------------------------------------------------------
wstring
GetMappedGUID(const wstring& strDcName, const wstring& strGuid)
{
TRACE_FUNCTION(GetMappedGUID);
ASSERT(!strGuid.empty());
GUID guid;
if (!wstringToGUID(strGuid, guid))
{
return wstring();
}
wstring strResult;
static CDsBindingHandle s_hDS;
ULONG ulError = NO_ERROR;
PWSTR pwzGuid = const_cast<wstring::value_type*>(strGuid.c_str());
do
{
Dbg(DEB_ITRACE, "attempting to map the guid\n");
ulError = s_hDS.Bind(strDcName);
if (ulError != NO_ERROR)
{
Dbg(DEB_IWARN,
"Binding to %s gave error %u\n",
strDcName.c_str(),
ulError);
break;
}
DS_SCHEMA_GUID_MAP* guidmap = 0;
ulError = ::DsMapSchemaGuids(s_hDS, 1, &guid, &guidmap);
if (ulError != NO_ERROR)
{
Dbg(DEB_IWARN, "DsMapSchemaGuids failed with 0x%X\n", ulError);
break;
}
if (guidmap->pName)
{
strResult = guidmap->pName;
}
::DsFreeSchemaGuidMap(guidmap);
if (!strResult.empty())
{
// the guid mapped as a schema guid: we're done
break;
}
// the guid is not a schema guid. Proabably an object guid.
Dbg(DEB_ITRACE, "attempting to crack the guid\n");
ulError = CrackGuid(s_hDS, pwzGuid, &strResult);
}
while (0);
do
{
//
// If we've got a string from the guid already, we're done.
//
if (!strResult.empty())
{
break;
}
//
// one last try. in this case, we bind to a GC to try to crack the
// name.
Dbg(DEB_ITRACE, "Attempting to crack the guid using GC\n");
static CDsBindingHandle s_hGC;
// empty string implies GC
if (s_hGC.Bind(L"") != NO_ERROR)
{
Dbg(DEB_IWARN, "Unable to bind to GC\n");
break;
}
Dbg(DEB_TRACE, "s_hGC = %#x\n", (HANDLE)s_hGC);
ulError = CrackGuid(s_hGC, pwzGuid, &strResult);
if (ulError != NO_ERROR)
{
DBG_OUT_LRESULT(ulError);
}
}
while (0);
return strResult;
}
//+--------------------------------------------------------------------------
//
// Function: GetMappedSID
//
// Synopsis: Return a friendly name for sid [psid].
//
// Arguments: [pwzServer1] - first server to base search on, NULL==local
// [pwzServer2] - backup server to base search on, NULL==local
// [psid] - points to SID to look up
//
// Returns: Possibly empty string
//
// History: 08-09-1999 davidmun Created
//
// Notes: If [pwzServer1] and [pwzServer2] represent the same machine,
// only one search will be done.
//
//---------------------------------------------------------------------------
wstring
GetMappedSID(
PCWSTR pwzServer1,
PCWSTR pwzServer2,
const PSID psid)
{
TRACE_FUNCTION(GetMappedSID);
HRESULT hr = S_OK;
wstring strResult;
BOOL fDirSearchInCache1 = FALSE;
BOOL fDirSearchInCache2 = FALSE;
do
{
CWaitCursor Hourglass;
//
// See if there's a name for this sid already in the cache
//
WCHAR wzName[DNLEN + 1 + UNLEN];
hr = g_SidCache.Fetch(psid, wzName, ARRAYLEN(wzName));
if (hr == S_OK)
{
strResult = wzName;
break;
}
//
// Figure out whether Server1 and Server2 are different
// machines.
//
BOOL fServersDiffer = FALSE;
BOOL fServer1IsLocal = FALSE;
BOOL fServer2IsLocal = FALSE;
wstring wstrThisComputer = GetComputerNameAsString();
if (!pwzServer1)
{
fServer1IsLocal = TRUE;
}
// This is an insufficient comparison; see the many forms of computer
// names provided by GetComputerNameEx. However, the cost of missing a
// valid match is just a redundant lookup.
else if (!_wcsicmp(pwzServer1, wstrThisComputer.c_str()))
{
fServer1IsLocal = TRUE;
}
if (!pwzServer2)
{
fServer2IsLocal = TRUE;
}
else if (!_wcsicmp(pwzServer2, wstrThisComputer.c_str()))
{
fServer2IsLocal = TRUE;
}
fServersDiffer = (fServer1IsLocal && !fServer2IsLocal) ||
(!fServer1IsLocal && fServer2IsLocal) ||
(!fServer1IsLocal && !fServer2IsLocal &&
_wcsicmp(pwzServer1, pwzServer2));
//
// Try to obtain it with a simple lookup.
//
WCHAR wszName[MAX_PATH];
ULONG cchName = ARRAYLEN(wszName);
WCHAR wszDomain[MAX_PATH];
ULONG cchDomain = ARRAYLEN(wszDomain);
SID_NAME_USE snuUnused;
BOOL fOk;
fOk = LookupAccountSid(pwzServer1,
psid,
&wszName[0],
&cchName,
&wszDomain[0],
&cchDomain,
&snuUnused);
if (fOk)
{
if (*wszDomain)
{
strResult = wszDomain;
strResult += L"\\";
}
strResult += wszName;
break;
}
//
// If server2 != server1, retry the lookup using server2.
//
if (fServersDiffer)
{
fOk = LookupAccountSid(pwzServer2,
psid,
&wszName[0],
&cchName,
&wszDomain[0],
&cchDomain,
&snuUnused);
if (fOk)
{
if (*wszDomain)
{
strResult = wszDomain;
strResult += L"\\";
}
strResult += wszName;
break;
}
}
//
// Lookups failed. Have to try searching a GC.
//
// If there's a cached IDirectorySearch instance for either
// server name, use those to do a search (checking to ensure that
// if both have the same cached interface, we don't search it
// twice).
//
// Note that failure to obtain an IDirectorySearch interface instance
// is cached because attempting to get one is an expensive operation and we
// don't want to retry it. Therefore, Fetch can return
// TRUE with a NULL interface pointer, meaning the failure was cached.
//
// TODO: it would be better if doing a Refresh command in the UI
// would clear all cached failures.
//
IDirectorySearch *pDirSearch1 = NULL;
IDirectorySearch *pDirSearch2 = NULL;
fDirSearchInCache1 = g_DirSearchCache.Fetch(pwzServer1 ?
pwzServer1 :
wstrThisComputer.c_str(),
&pDirSearch1);
if (pDirSearch1)
{
strResult = DoLdapQueryForSid(pDirSearch1, psid);
if (!strResult.empty())
{
break;
}
}
if (fServersDiffer)
{
fDirSearchInCache2 = g_DirSearchCache.Fetch(pwzServer2 ?
pwzServer2 :
wstrThisComputer.c_str(),
&pDirSearch2);
if (pDirSearch2)
{
//
// If both server names are supported by the same GC, then
// don't search it again.
//
if (pDirSearch1 == pDirSearch2)
{
break;
}
strResult = DoLdapQueryForSid(pDirSearch2, psid);
//
// If the search succeeded, we're done.
//
if (!strResult.empty())
{
break;
}
//
// If we just searched both GCs, there's nothing more to do.
//
if (fDirSearchInCache1)
{
break;
}
}
}
//
// Calling LookupAccountSid failed using both servers, and we have
// searched either zero or one GC.
//
// Search the GCs associated with the two servers, making sure that
// we don't search the same GC twice.
//
ASSERT(!fDirSearchInCache1 || !fDirSearchInCache2);
if (!fDirSearchInCache1)
{
ASSERT(!pDirSearch1);
pDirSearch1 = DeriveGcFromServer(pwzServer1);
g_DirSearchCache.Add(pwzServer1 ? pwzServer1 : wstrThisComputer.c_str(),
pDirSearch1);
if (pDirSearch1)
{
strResult = DoLdapQueryForSid(pDirSearch1, psid);
if (!strResult.empty())
{
break;
}
}
}
if (!fDirSearchInCache2)
{
ASSERT(!pDirSearch2);
pDirSearch2 = DeriveGcFromServer(pwzServer2);
g_DirSearchCache.Add(pwzServer2 ? pwzServer2 : wstrThisComputer.c_str(),
pDirSearch2);
if (pDirSearch2)
{
strResult = DoLdapQueryForSid(pDirSearch2, psid);
}
}
} while (0);
//
// Add the resulting string to the cache. Even if we failed and
// ended up with an empty string, we want it in the cache to
// prevent further attempts to translate this sid to a name,
// because they are expensive.
//
g_SidCache.Add(psid, strResult.c_str());
return strResult;
}
//+--------------------------------------------------------------------------
//
// Function: DeriveGcFromServer
//
// Synopsis: Return an IDirectorySearch instance bound to the GC which
// represents the enterprise to which [pwzMachine] belongs,
// or NULL on error.
//
// Arguments: [pwzMachine] - machine used to determine GC
//
// Returns: NULL or interface which caller must release
//
// History: 08-17-1999 davidmun Created
//
//---------------------------------------------------------------------------
IDirectorySearch *
DeriveGcFromServer(
PCWSTR pwzMachine)
{
HRESULT hr = S_OK;
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDsRole = NULL;
PDOMAIN_CONTROLLER_INFO pdci = NULL;
ULONG ulResult;
IDirectorySearch *pDirSearch = NULL;
do
{
ulResult = DsRoleGetPrimaryDomainInformation(
pwzMachine,
DsRolePrimaryDomainInfoBasic,
(PBYTE *)&pDsRole);
if (ulResult != NO_ERROR)
{
DBG_OUT_LRESULT(ulResult);
hr = HRESULT_FROM_WIN32(ulResult);
break;
}
//
// If machine is in a workgroup, we're done.
//
if (pDsRole->MachineRole == DsRole_RoleStandaloneWorkstation ||
pDsRole->MachineRole == DsRole_RoleStandaloneServer)
{
Dbg(DEB_TRACE, "Target machine is not joined to a domain\n");
break;
}
//
// Target machine is joined to a domain. Find out if it's joined
// to an NT4 or an NT5 domain by getting the name of a DC, and
// requesting that we get one which supports DS.
//
PWSTR pwzDomainNameForDsGetDc;
ULONG flDsGetDc = DS_DIRECTORY_SERVICE_REQUIRED;
if (pDsRole->DomainNameDns)
{
pwzDomainNameForDsGetDc = pDsRole->DomainNameDns;
flDsGetDc |= DS_IS_DNS_NAME;
}
else
{
pwzDomainNameForDsGetDc = pDsRole->DomainNameFlat;
flDsGetDc |= DS_IS_FLAT_NAME;
}
ulResult = DsGetDcName(NULL,
pwzDomainNameForDsGetDc,
NULL,
NULL,
flDsGetDc,
&pdci);
if (ulResult == ERROR_NO_SUCH_DOMAIN)
{
Dbg(DEB_ERROR,
"DsGetDcName for domain %ws returned ERROR_NO_SUCH_DOMAIN, treating target machine as no-net\n",
pwzDomainNameForDsGetDc);
break;
}
if (ulResult != NO_ERROR)
{
Dbg(DEB_ERROR,
"DsGetDcName for domain %ws returned %uL\n",
pwzDomainNameForDsGetDc,
ulResult);
break;
}
if (pdci->Flags & DS_DS_FLAG)
{
PDOMAIN_CONTROLLER_INFO pdci2 = NULL;
ulResult = DsGetDcName(NULL,
pwzDomainNameForDsGetDc,
NULL,
NULL,
flDsGetDc | DS_RETURN_DNS_NAME,
&pdci2);
if (ulResult == NO_ERROR)
{
NetApiBufferFree(pdci);
pdci = pdci2;
}
else
{
ASSERT(!pdci2);
}
}
if (ulResult != NO_ERROR)
{
Dbg(DEB_ERROR,
"DsGetDcName for domain %ws (second call) returned %uL\n",
pwzDomainNameForDsGetDc,
ulResult);
break;
}
ASSERT(pdci->Flags & DS_DS_FLAG);
wstring strTargetDomainDns;
wstring strTargetForest;
if (pDsRole->DomainNameDns)
{
strTargetDomainDns = pDsRole->DomainNameDns;
}
else if (pdci->DomainName)
{
strTargetDomainDns = pdci->DomainName;
}
while (!strTargetDomainDns.empty() &&
strTargetDomainDns.end() && // 655675-2002/07/08-JonN PREFIX
*(strTargetDomainDns.end() - 1) == L'.')
{
strTargetDomainDns.erase(strTargetDomainDns.end() - 1);
}
strTargetForest = pdci->DnsForestName;
while (!strTargetForest.empty() && *(strTargetForest.end() - 1) == L'.')
{
strTargetForest.erase(strTargetForest.end() - 1);
}
// reuse this for call to find GC server
NetApiBufferFree(pdci);
pdci = NULL;
PCWSTR pwzTargetDomain = strTargetForest.c_str();
if (!pwzTargetDomain[0])
{
pwzTargetDomain = NULL;
}
wstring strGcPath;
DWORD dwResult;
Dbg(DEB_TRACE,
"DsGetDcName with DomainName=%ws\n",
pwzTargetDomain ? pwzTargetDomain : L"<NULL>");
dwResult = DsGetDcName(NULL,
pwzTargetDomain,
NULL,
NULL,
DS_GC_SERVER_REQUIRED,
&pdci);
if (dwResult != NO_ERROR)
{
DBG_OUT_LRESULT(dwResult);
break;
}
Dbg(DEB_TRACE,
"Domain '%s' DC hosting GC is '%s'\n",
pdci->DomainName,
pdci->DomainControllerName);
strGcPath = L"GC://";
PWSTR pwzDomainName = pdci->DomainName;
while (*pwzDomainName == L'\\')
{
pwzDomainName++;
}
strGcPath += pwzDomainName;
size_t idxLastDot = strGcPath.rfind(L'.');
if (idxLastDot == strGcPath.length() - 1)
{
strGcPath.erase(strGcPath.length() - 1, 1);
}
NetApiBufferFree(pdci);
pdci = NULL;
WCHAR wzPort[20];
wsprintf(wzPort, L":%u", LDAP_GC_PORT);
strGcPath += wzPort;
//
// Now bind to the GC for a search interface
//
hr = ADsOpenObject((PWSTR)strGcPath.c_str(),
NULL,
NULL,
ADS_SECURE_AUTHENTICATION,
IID_IDirectorySearch,
(void**)&pDirSearch);
CHECK_HRESULT(hr);
} while (0);
if (pdci)
{
NetApiBufferFree(pdci);
}
if (pDsRole)
{
DsRoleFreeMemory(pDsRole);
}
return pDirSearch;
}
//+--------------------------------------------------------------------------
//
// Function: DoLdapQueryForSid
//
// Synopsis: Locate the GC for the enterprise to which [strServer] belongs
// and perform a query among the deleted (and active) objects
// for one with a sid of [psid], asking for the object's name.
//
// Arguments: [strServer] - target machine
// [psid] - SID for which to search
//
// Returns: Name of object having sid [psid].
//
// History: 08-03-1999 davidmun Created
//
//---------------------------------------------------------------------------
wstring
DoLdapQueryForSid(
IDirectorySearch *pDirSearch,
PSID psid)
{
HRESULT hr = S_OK;
PWSTR pwzSidBlob = NULL;
ADS_SEARCH_HANDLE hSearch = NULL;
wstring strResult;
ASSERT(pDirSearch);
ASSERT(psid && IsValidSid(psid));
do
{
//
// Set up the preferences for the search
//
ADS_SEARCHPREF_INFO aSearchPrefs[3];
ZeroMemory(aSearchPrefs, sizeof aSearchPrefs);
//
// Always follow aliases
//
aSearchPrefs[0].dwSearchPref = ADS_SEARCHPREF_DEREF_ALIASES;
aSearchPrefs[0].vValue.dwType = ADSTYPE_INTEGER;
aSearchPrefs[0].vValue.Integer = ADS_DEREF_ALWAYS;
//
// Search down the subtree
//
aSearchPrefs[1].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
aSearchPrefs[1].vValue.dwType = ADSTYPE_INTEGER;
aSearchPrefs[1].vValue.Integer = ADS_SCOPE_SUBTREE;
//
// Look through tombstone data
//
aSearchPrefs[2].dwSearchPref = ADS_SEARCHPREF_TOMBSTONE;
aSearchPrefs[2].vValue.dwType = ADSTYPE_BOOLEAN;
aSearchPrefs[2].vValue.Integer = TRUE;
hr = pDirSearch->SetSearchPreference(aSearchPrefs,
ARRAYLEN(aSearchPrefs));
BREAK_ON_FAIL_HRESULT(hr);
//
// Build the search filter
//
wstring strFilter;
hr = ADsEncodeBinaryData((PBYTE)psid, GetLengthSid(psid), &pwzSidBlob);
BREAK_ON_FAIL_HRESULT(hr);
strFilter = L"(objectSid=";
strFilter += pwzSidBlob;
strFilter += L")";
PWSTR apwzAttrs[] = { L"name" };
Dbg(DEB_TRACE, "searching for '%ws'\n", strFilter.c_str());
hr = pDirSearch->ExecuteSearch((PWSTR)strFilter.c_str(),
apwzAttrs,
ARRAYLEN(apwzAttrs),
&hSearch);
BREAK_ON_FAIL_HRESULT(hr);
hr = pDirSearch->GetFirstRow(hSearch);
BREAK_ON_FAIL_HRESULT(hr);
if (hr == S_ADS_NOMORE_ROWS)
{
Dbg(DEB_TRACE, "No matches\n");
break;
}
ADS_SEARCH_COLUMN Column;
hr = pDirSearch->GetColumn(hSearch, apwzAttrs[0], &Column);
BREAK_ON_FAIL_HRESULT(hr);
strResult = Column.pADsValues[0].CaseIgnoreString;
hr = pDirSearch->FreeColumn(&Column);
CHECK_HRESULT(hr);
} while (0);
if (hSearch)
{
pDirSearch->CloseSearchHandle(hSearch);
}
if (pwzSidBlob)
{
FreeADsMem(pwzSidBlob);
}
return strResult;
}
//+--------------------------------------------------------------------------
//
// Function: ReplaceAllInserts
//
// Synopsis: Replace all inserts of the form %n with insert strings in
// the event itself and of the form %%n with insert strings
// in the parameter message file associated with the source in
// the event.
//
// Arguments: [pelr] - record containing insert strings
// [pli] - decribes log containing record
// [wszRemoteSystemRoot] - value of SystemRoot environment var
// on remote machine
// [pshkRemoteSource] - hkey to remote source key
// [pshkLocalSource] - hkey to local source key
// [pshkPrimarySource] - hkey to primary source key
// [psk] - status of primary source key
// [ppwszMsg] - points to description string to use
//
// Modifies: Frees *[ppwszMsg] (using LocalFree) and replaces it with a
// new copy (allocated with LocalAlloc) if the insertion
// string replacement is completed without error. In case
// of error, or if no insertion markers are present in the
// description, *[ppwszMsg] is untouched.
//
// History: 2-25-1997 DavidMun Created
//
// Notes: The source hkeys may be NULL.
//
// The original log viewer arbitrarily limits the number of
// insertion strings it will handle to 99. This code has no
// limit on the number of insert strings, but does limit the
// number of insertion operations. This allows it to safely
// process insert strings which reference parameter strings or
// other insert strings, and parameter strings which reference
// other parameter strings.
//
// If the creator of the message file or insert strings makes a
// circular reference, this code will follow that reference
// until it hits the limit of insertion operations, then
// stop following that trail of insertions.
//
// Insertion string and parameter values start at 1. Zero
// values will not be transferred to the final string.
//
//---------------------------------------------------------------------------
VOID
ReplaceAllInserts(
EVENTLOGRECORD *pelr,
CLogInfo *pli,
LPWSTR wszRemoteSystemRoot,
CSafeReg *pshkRemoteSource,
CSafeReg *pshkLocalSource,
CSafeReg *pshkPrimarySource,
PSK psk,
LPWSTR *ppwszMsg)
{
TRACE_FUNCTION(ReplaceAllInserts);
// JonN 3/21/01 350614
// Use message dlls and DS, FRS and DNS log types from specified computer
LPWSTR pwszRemoteMessageServer = pli->GetLogServerName();
if ( pli->IsBackupLog() && *g_wszAuxMessageSource )
{
pwszRemoteMessageServer = g_wszAuxMessageSource;
}
HRESULT hr = S_OK;
SDescriptionStr ds;
SParamModule pmRemote;
SParamModule pmLocal;
SParamModule pmPrimary;
pmRemote.mls = LOAD_NOT_ATTEMPTED;
pmRemote.mt = REMOTE;
pmRemote.pshkSource = pshkRemoteSource;
pmLocal.mls = LOAD_NOT_ATTEMPTED;
pmLocal.mt = LOCAL;
pmLocal.pshkSource = pshkLocalSource;
pmPrimary.mls = LOAD_NOT_ATTEMPTED;
pmPrimary.mt = PRIMARY;
pmPrimary.pshkSource = pshkPrimarySource;
//
// If the message doesn't have any percent signs, it can't have any
// insertions.
//
if (!wcschr(*ppwszMsg, L'%'))
{
return;
}
//
// Give the insert strings some room initially. Strike a compromise
// between wasting memory and doing more reallocs than necessary.
//
ds.cchBuf = lstrlen(*ppwszMsg) + 1;
ds.cchRemain = ds.cchBuf / 2; // amount extra we'll start with
ds.cchBuf += ds.cchRemain;
//
// Allocate the initial buffer for the expanded string. We'll grow it
// with LocalReAlloc as necessary.
//
ds.pwszExpanded = (LPWSTR) LocalAlloc(LPTR, ds.cchBuf * sizeof(WCHAR));
if (!ds.pwszExpanded)
{
DBG_OUT_HRESULT(E_OUTOFMEMORY);
return;
}
lstrcpy(ds.pwszExpanded, *ppwszMsg);
ds.pwszCur = ds.pwszExpanded;
ULONG cInsertOps = 0;
while (ds.pwszCur && *ds.pwszCur)
{
ds.pwszCur = wcschr(ds.pwszCur, L'%');
//
// If there are no more insertion markers in the source string,
// we're done.
//
if (!ds.pwszCur)
{
break;
}
//
// 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 (IsDigit(ds.pwszCur[1]))
{
// JonN 7/17/01 435941
// 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(&ds, pelr);
//
// 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 (++cInsertOps >= MAX_INSERT_OPS)
{
Dbg(DEB_IWARN,
"ReplaceAllInserts: hit max replacements\n");
break;
}
//
// Note ds.pwszCur has not moved (unless there was an error), so
// we will process the contents of the insertion string.
//
}
else if (ds.pwszCur[1] == L'%')
{
//
// Found %%. If that is followed by a digit, it's a parameter string.
//
if (IsDigit(ds.pwszCur[2]))
{
ReplaceParameterInsert(&ds,
pelr,
pli,
wszRemoteSystemRoot,
&pmRemote,
&pmLocal,
&pmPrimary,
&psk);
if (++cInsertOps >= MAX_INSERT_OPS)
{
break;
}
}
else if (ds.pwszCur[2] == L'%' && IsDigit(ds.pwszCur[3]))
{
//
// 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.
//
//
// JonN 1/22/02 524570
// DavidMun made a terrible goof here! You can't just store
// ds.pwszCur and restore it later; ReplaceStringInsert
// is liable to reallocate the string somewhere else!
//
// PWSTR pwszStartOfTriplePct = ds.pwszCur;
int cch = static_cast<int>(ds.pwszCur - ds.pwszExpanded);
ds.pwszCur += 2; // point at %n
hr = ReplaceStringInsert(&ds, pelr);
if (SUCCEEDED(hr))
{
// ds.pwszCur = pwszStartOfTriplePct;
ds.pwszCur = ds.pwszExpanded + cch;
if (++cInsertOps >= MAX_INSERT_OPS)
{
break;
}
}
}
else
{
//
// Got %%x, where x is non-digit. skip first percent;
// maybe x is % and is followed by digit.
//
ds.pwszCur++;
}
}
else if (ds.pwszCur[1] == L'{' && ds.pwszCur[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 }
PWSTR pwszEnd = wcschr(ds.pwszCur + 2, L'}');
if (!pwszEnd)
{
Dbg(DEB_ERROR,
"ReplaceAllInserts: ignoring invalid guid insert string %s\n",
ds.pwszCur);
ds.pwszCur++;
}
else
{
wstring guid(ds.pwszCur + 1, pwszEnd - (ds.pwszCur));
// JonN 3/21/01 350614
// Use message dlls and DS, FRS and DNS log types
// from specified computer
PWSTR pwszServer = pwszRemoteMessageServer;
wstring strReplacement =
GetMappedGUID(
pwszServer ? pwszServer : GetComputerNameAsString(),
guid);
if (!strReplacement.empty())
{
pwszEnd++; // now points past '}'
hr = ReplaceSubStr(
strReplacement.c_str(),
&ds.pwszExpanded,
&ds.pwszCur,
pwszEnd,
&ds.cchBuf,
&ds.cchRemain);
BREAK_ON_FAIL_HRESULT(hr);
}
else
{
// couldn't get a replacement, so skip it.
ds.pwszCur = pwszEnd;
}
}
}
else if (ds.pwszCur[1] == L'{' && ds.pwszCur[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 }
PWSTR pwszEnd = wcschr(ds.pwszCur + 2, L'}');
if (!pwszEnd)
{
Dbg(DEB_ERROR,
"ReplaceAllInserts: ignoring invalid SID insert string %s. See Kumar.\n",
ds.pwszCur);
ds.pwszCur++;
}
else
{
wstring strSID(ds.pwszCur + 2, pwszEnd - (ds.pwszCur) - 2);
wstring strReplacement;
PSID psid = NULL;
BOOL fOk = ConvertStringSidToSid(strSID.c_str(), &psid);
if (!fOk)
{
DBG_OUT_LASTERROR;
ds.pwszCur = pwszEnd;
continue;
}
// JonN 3/21/01 350614
// Use message dlls and DS, FRS and DNS log types
// from specified computer
strReplacement = GetMappedSID(GetComputerStr(pelr),
pwszRemoteMessageServer,
psid);
LocalFree(psid);
psid = NULL;
if (!strReplacement.empty())
{
pwszEnd++; // now points past '}'
hr = ReplaceSubStr(
strReplacement.c_str(),
&ds.pwszExpanded,
&ds.pwszCur,
pwszEnd,
&ds.cchBuf,
&ds.cchRemain);
BREAK_ON_FAIL_HRESULT(hr);
}
else
{
// couldn't get a replacement, so skip it.
ds.pwszCur = pwszEnd;
}
}
}
else
{
//
// Found %x where x is neither a % nor a digit. Just keep moving.
//
ds.pwszCur++;
}
}
if (ds.pwszExpanded)
{
ASSERT(wcslen(ds.pwszExpanded) < ds.cchBuf);
ASSERT((LONG)ds.cchRemain >= 0L);
LocalFree(*ppwszMsg);
*ppwszMsg = ds.pwszExpanded;
}
}
//+--------------------------------------------------------------------------
//
// Function: ReplaceStringInsert
//
// Synopsis: Replace the string insert (%n, where n is a number) at
// [pds.pwszCur] with insert string number n from the event
// log record [pelr].
//
// Arguments: [pds] - contains information about string with insert
// escape
// [pelr] - event record that contains insert strings
//
// Modifies: Values in [pds]
//
// History: 01-20-1999 DavidMun Created
//
//---------------------------------------------------------------------------
HRESULT
ReplaceStringInsert(
SDescriptionStr *pds,
EVENTLOGRECORD *pelr)
{
HRESULT hr = S_OK;
LPWSTR pwszEnd = NULL;
ULONG idxInsertStr = wcstoul(pds->pwszCur + 1, &pwszEnd, 10);
ASSERT(pwszEnd);
if (idxInsertStr && idxInsertStr <= pelr->NumStrings)
{
LPWSTR pwszInsertStr = GetInsertStr(pelr, idxInsertStr - 1);
Dbg(DEB_ITRACE,
"Replacing %%%u with %u length string\n",
idxInsertStr,
lstrlen(pwszInsertStr));
hr = ReplaceSubStr(pwszInsertStr,
&pds->pwszExpanded,
&pds->pwszCur,
pwszEnd,
&pds->cchBuf,
&pds->cchRemain);
CHECK_HRESULT(hr);
}
else
{
Dbg(DEB_ERROR,
"ignoring invalid insert string index %u\n",
idxInsertStr);
pds->pwszCur++; // move past %
hr = E_INVALIDARG;
}
return hr;
}
//+--------------------------------------------------------------------------
//
// Function: ReplaceParameterInsert
//
// Synopsis: Replace the parameter insert (double percent sign number) at
// [pds.pwszCur] with a string loaded from a parameter message
// file module.
//
// Arguments: [pds] - contains information about string
// with parameter insert
// [pelr] - event record that the description
// string is being built for
// [pli] - describes log in which [pelr] lives
// [wszRemoteSystemRoot] - for remote log, %SystemRoot% value
// of its machine
// [ppmRemote] - describes remote parameter module
// [ppmLocal] - describes local parameter module
// [ppmPrimary] - describes primary parameter module
// [pPSK] - describes [ppmPrimary->pshkSource]
//
// Modifies: Values in [pds], [ppmRemote], [ppmLocal], and [ppmPrimary]
//
// History: 3-06-1997 DavidMun Created
//
//---------------------------------------------------------------------------
VOID
ReplaceParameterInsert(
SDescriptionStr *pds,
EVENTLOGRECORD *pelr,
CLogInfo *pli,
LPWSTR wszRemoteSystemRoot,
SParamModule *ppmRemote,
SParamModule *ppmLocal,
SParamModule *ppmPrimary,
PSK *pPSK)
{
LPWSTR pwszEnd;
ULONG idxParameterStr = wcstoul(pds->pwszCur + 2, &pwszEnd, 10);
ASSERT(pwszEnd);
// JonN 3/21/01 350614
// Use message dlls and DS, FRS and DNS log types from specified computer
LPWSTR pwszRemoteMessageServer = pli->GetLogServerName();
if ( pli->IsBackupLog() && *g_wszAuxMessageSource )
{
pwszRemoteMessageServer = g_wszAuxMessageSource;
}
BOOL fRemote = pwszRemoteMessageServer != NULL;
// JonN 10/12/01 Allow "%%0"
if (!idxParameterStr && (L'0' != *(pds->pwszCur + 2)))
{
Dbg(DEB_ERROR,
"ReplaceParameterInsert: Skipping invalid parameter index 0\n");
pds->pwszCur++;
return;
}
LPWSTR pwszParameter = NULL;
ULONG flFmtMsgFlags = FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_MAX_WIDTH_MASK;
//
// Demand load the parameter message file modules in this order: remote,
// local, primary.
//
WCHAR wszRemoteParamMsgFilePath[MAX_PATH + 1] = L"";
if (fRemote && ppmRemote->mls == LOAD_NOT_ATTEMPTED)
{
ASSERT(!(HINSTANCE)ppmRemote->schModule);
//
// If caller was unable to determine the value of the remote system's
// SystemRoot environment variable, or if it was unable to open the
// registry key on the remote machine for the event source used by
// pelr, then it will not be possible to determine the UNC of the
// parameter message file module, so consider it a failed load.
//
// Otherwise attempt to get the UNC and do a LoadLibrary on it.
//
if (!*wszRemoteSystemRoot || !(HKEY)*ppmRemote->pshkSource)
{
ppmRemote->mls = LOAD_FAILED;
}
else
{
ppmRemote->mls = LoadRemoteParamModule(pwszRemoteMessageServer,
ppmRemote->pshkSource,
wszRemoteSystemRoot,
wszRemoteParamMsgFilePath,
ARRAYLEN(wszRemoteParamMsgFilePath),
&ppmRemote->schModule);
}
}
//
// If the log is remote and we loaded the remote parameter message file,
// see if we can extract the parameter value from it.
//
if (fRemote && ppmRemote->mls == LOAD_SUCCEEDED)
{
FormatMessage(flFmtMsgFlags,
(LPCVOID) (HINSTANCE) ppmRemote->schModule,
idxParameterStr,
0,
(LPWSTR) &pwszParameter,
FMT_MSG_MIN_ALLOC,
NULL);
if (!pwszParameter)
{
//
// Parameter value not in remote file; we'll continue to try
// to get a value.
//
DBG_OUT_LASTERROR;
}
else
{
Dbg(DEB_ITRACE,
"Replacing %%%%%u with remote parameter '%s'\n",
idxParameterStr,
pwszParameter);
}
}
//
// If the parameter value hasn't been found yet (remote load failed or
// not remote or message not in remote file) try the local. If its
// module hasn't been loaded yet, attempt to do so.
//
HRESULT hr;
WCHAR wszLocalParamMsgFilePath[MAX_PATH + 1] = L"";
if (!pwszParameter && ppmLocal->mls == LOAD_NOT_ATTEMPTED)
{
if ((HKEY)*ppmLocal->pshkSource)
{
hr = ppmLocal->pshkSource->QueryPath(PARAM_MSG_FILE_VALUE,
wszLocalParamMsgFilePath,
ARRAYLEN(wszLocalParamMsgFilePath),
TRUE);
if (SUCCEEDED(hr))
{
hr = g_DllCache.Fetch(wszLocalParamMsgFilePath, &ppmLocal->schModule);
if (SUCCEEDED(hr))
{
ppmLocal->mls = LOAD_SUCCEEDED;
}
else
{
ppmLocal->mls = LOAD_FAILED;
}
}
else
{
ppmLocal->mls = LOAD_FAILED;
}
}
else
{
ppmLocal->mls = LOAD_FAILED;
}
}
if (!pwszParameter && ppmLocal->mls == LOAD_SUCCEEDED)
{
FormatMessage(flFmtMsgFlags,
(LPCVOID) (HINSTANCE) ppmLocal->schModule,
idxParameterStr,
0,
(LPWSTR) &pwszParameter,
FMT_MSG_MIN_ALLOC,
NULL);
if (!pwszParameter)
{
DBG_OUT_LASTERROR;
}
else
{
Dbg(DEB_ITRACE,
"Replacing %%%%%u with local parameter '%s'\n",
idxParameterStr,
pwszParameter);
}
}
//
// If the parameter wasn't obtained from a remote or local message file,
// see if there is a primary module to load from.
//
WCHAR wszPrimaryParamMsgFilePath[MAX_PATH + 1] = L"";
if (!pwszParameter && ppmPrimary->mls == LOAD_NOT_ATTEMPTED)
{
//
// The primary module has a little twist: unlike the remote and
// local cases, if the primary source key is null it can be
// because the primary source was never used.
//
// If this is so first try and open the primary source key.
//
if (*pPSK == PSK_NOT_EXAMINED)
{
*pPSK = OpenPrimarySourceKey(pli->GetPrimarySourceStr(),
GetSourceStr(pelr),
pli->GetLogName(),
ppmPrimary->pshkSource);
}
//
// If ppmPrimary->pshkSource is valid, we can now attempt to
// load the primary module.
//
if (*pPSK == PSK_VALID)
{
hr = ppmPrimary->pshkSource->QueryPath(PARAM_MSG_FILE_VALUE,
wszPrimaryParamMsgFilePath,
ARRAYLEN(wszPrimaryParamMsgFilePath),
TRUE);
if (SUCCEEDED(hr))
{
hr = g_DllCache.Fetch(wszPrimaryParamMsgFilePath,
&ppmPrimary->schModule);
if (SUCCEEDED(hr))
{
ppmPrimary->mls = LOAD_SUCCEEDED;
}
else
{
ppmPrimary->mls = LOAD_FAILED;
}
}
else
{
ppmPrimary->mls = LOAD_FAILED;
}
}
else
{
ppmPrimary->mls = LOAD_FAILED;
}
}
if (!pwszParameter && ppmPrimary->mls == LOAD_SUCCEEDED)
{
FormatMessage(flFmtMsgFlags,
(LPCVOID) (HINSTANCE) ppmPrimary->schModule,
idxParameterStr,
0,
(LPWSTR) &pwszParameter,
FMT_MSG_MIN_ALLOC,
NULL);
if (!pwszParameter)
{
DBG_OUT_LASTERROR;
}
else
{
Dbg(DEB_ITRACE,
"Replacing %%%%%u with primary parameter '%s'\n",
idxParameterStr,
pwszParameter);
}
}
//
// 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.
//
if (!pwszParameter &&
(wcsistr(wszRemoteParamMsgFilePath, KERNEL32_DLL) ||
wcsistr(wszLocalParamMsgFilePath, KERNEL32_DLL) ||
wcsistr(wszPrimaryParamMsgFilePath, KERNEL32_DLL)))
{
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL,
idxParameterStr,
0,
(LPWSTR) &pwszParameter,
FMT_MSG_MIN_ALLOC,
NULL);
if (!pwszParameter)
{
DBG_OUT_LASTERROR;
}
else
{
Dbg(DEB_ITRACE,
"Replacing %%%%%u with system string '%s'\n",
idxParameterStr,
pwszParameter);
}
}
if (pwszParameter)
{
ReplaceSubStr(pwszParameter,
&pds->pwszExpanded,
&pds->pwszCur,
pwszEnd,
&pds->cchBuf,
&pds->cchRemain);
LocalFree(pwszParameter);
}
else
{
pds->pwszCur = pwszEnd; // move past whole parameter
}
}
//+--------------------------------------------------------------------------
//
// 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
// [ppwszInsertPoint] - point in *[ppwszBuf] to insert
// [pwszSubStrEnd] - first character beyond
// *[ppwszInsertPoint] which should not
// be deleted.
// [pcchBuf] - total size, in WCHARs, of *[ppwszBuf]
// [pcchRemain] - unused space, in WCHARs, in *[ppwszBuf]
//
// Returns: S_OK
// E_OUTOFMEMORY
//
// Modifies: [ppwszBuf], [ppwszInsertPoint], [pcchBuf], [pcchRemain]
//
// History: 2-27-1997 DavidMun Created
//
// 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
ReplaceSubStr(
LPCWSTR pwszToInsert,
LPWSTR *ppwszBuf,
LPWSTR *ppwszInsertPoint,
LPCWSTR pwszSubStrEnd,
ULONG *pcchBuf,
ULONG *pcchRemain)
{
TRACE_FUNCTION(ReplaceSubStr);
ASSERT(pwszSubStrEnd > *ppwszInsertPoint);
ASSERT(pwszSubStrEnd < *ppwszBuf + *pcchBuf);
size_t cchToDelete = pwszSubStrEnd - *ppwszInsertPoint;
ULONG cchToInsert = lstrlen(pwszToInsert);
Dbg(DEB_ITRACE, "cchToDelete = %u\n", cchToDelete);
Dbg(DEB_ITRACE, "cchToInsert = %u\n", cchToInsert);
Dbg(DEB_ITRACE, "cchBuf = %u\n", *pcchBuf);
Dbg(DEB_ITRACE, "cchRemain = %u\n", *pcchRemain);
//
// Grow the string buffer if necessary.
//
if (cchToInsert > *pcchRemain + cchToDelete)
{
LPWSTR pwszNew;
Dbg(DEB_ITRACE,
"Allocating %u wchars for new buffer\n",
(*pcchBuf + 2 * (cchToInsert - cchToDelete)));
pwszNew = (LPWSTR) LocalAlloc(LPTR,
sizeof(WCHAR) *
(*pcchBuf +
2 *
(cchToInsert - cchToDelete)));
if (!pwszNew)
{
Dbg(DEB_ERROR, "Can't alloc %uL bytes\n",
sizeof(WCHAR) * (*pcchBuf + 2 * (cchToInsert - cchToDelete)));
DBG_OUT_HRESULT(E_OUTOFMEMORY);
return E_OUTOFMEMORY;
}
*ppwszInsertPoint = pwszNew + (*ppwszInsertPoint - *ppwszBuf);
pwszSubStrEnd = pwszNew + (pwszSubStrEnd - *ppwszBuf);
lstrcpy(pwszNew, *ppwszBuf);
LocalFree(*ppwszBuf);
*ppwszBuf = pwszNew;
*pcchBuf += static_cast<ULONG>(2 * (cchToInsert - cchToDelete));
*pcchRemain += static_cast<ULONG>(2 * (cchToInsert - cchToDelete));
}
//
// Adjust the space between the insert point and the start of the
// remainder of the string after the substring to delete so that
// it exactly matches the length of the string to insert.
//
// Note that this may mean moving left or right, or if the length of
// the string to insert is the same as the length of the substring,
// not at all.
//
// Notice also the null terminator is included in the move.
//
ASSERT(*ppwszInsertPoint + cchToInsert + lstrlen(pwszSubStrEnd) + 1 <=
*ppwszBuf + *pcchBuf);
if (*ppwszInsertPoint + cchToInsert != pwszSubStrEnd)
{
MoveMemory(*ppwszInsertPoint + cchToInsert,
pwszSubStrEnd,
sizeof(WCHAR) * (lstrlen(pwszSubStrEnd) + 1));
}
ASSERT((ULONG) lstrlen(*ppwszBuf) < *pcchBuf);
//
// Overwrite the memory at the insertion point with the string to
// insert, less its null terminator.
//
if (cchToInsert)
{
CopyMemory(*ppwszInsertPoint,
pwszToInsert,
sizeof(WCHAR) * cchToInsert);
}
//
// Keep track of the space consumed (or made available) in the buffer.
//
*pcchRemain = static_cast<ULONG>(*pcchRemain - cchToInsert + cchToDelete);
ASSERT((LONG) *pcchRemain >= 0);
return S_OK;
}
//+--------------------------------------------------------------------------
//
// Function: TerminatePathStrings
//
// Synopsis: Change delimiters into null characters and return a count
// of the number of resulting non-empty strings.
//
// Arguments: [pwszUnexpanded] -
//
// Returns: Count of strings ("a;b;c" and "a;;b;c" both give 3)
//
// Modifies: *[pwszUnexpanded]
//
// History: 2-24-1997 DavidMun Created
//
//---------------------------------------------------------------------------
ULONG
TerminatePathStrings(
LPWSTR pwszUnexpanded)
{
TRACE_FUNCTION(TerminatePathStrings);
//
// If the multi-sz string is empty, just return quickly
//
if (!*pwszUnexpanded)
{
return 0;
}
//
// Since the string isn't empty, it is considered (by this routine) to
// have at least one path.
//
LPWSTR pwszCurDelim;
ULONG cPaths = 1;
for (pwszCurDelim = wcspbrk(pwszUnexpanded, EVENT_MSGFILE_DELIMETERS);
pwszCurDelim;
pwszCurDelim = wcspbrk(pwszCurDelim, EVENT_MSGFILE_DELIMETERS))
{
//
// As long as this is not a leading delimiter, increment the count
// of paths found.
//
if (pwszCurDelim > pwszUnexpanded)
{
cPaths++;
}
//
// Null terminate at the current and all trailing delimiters for
// the current path.
//
while (wcschr(EVENT_MSGFILE_DELIMETERS, *pwszCurDelim))
{
*pwszCurDelim++ = L'\0';
}
}
return cPaths;
}