summaryrefslogtreecommitdiffstats
path: root/private/eventlog/server
diff options
context:
space:
mode:
Diffstat (limited to 'private/eventlog/server')
-rw-r--r--private/eventlog/server/alert.c147
-rw-r--r--private/eventlog/server/cairo.c471
-rw-r--r--private/eventlog/server/config.c1007
-rw-r--r--private/eventlog/server/control.c699
-rw-r--r--private/eventlog/server/copy.c196
-rw-r--r--private/eventlog/server/daytona/makefile6
-rw-r--r--private/eventlog/server/daytona/sources69
-rw-r--r--private/eventlog/server/dirs25
-rw-r--r--private/eventlog/server/elfapi.c2235
-rw-r--r--private/eventlog/server/elfcfg.h66
-rw-r--r--private/eventlog/server/elfdata.c218
-rw-r--r--private/eventlog/server/elfdef.h478
-rw-r--r--private/eventlog/server/elfextrn.h99
-rw-r--r--private/eventlog/server/elflpc.c774
-rw-r--r--private/eventlog/server/elflpc.h54
-rw-r--r--private/eventlog/server/elfproto.h384
-rw-r--r--private/eventlog/server/elfrpc.c216
-rw-r--r--private/eventlog/server/elfsec.c920
-rw-r--r--private/eventlog/server/elfutil.c1737
-rw-r--r--private/eventlog/server/eventlog.c1510
-rw-r--r--private/eventlog/server/eventlog.def6
-rw-r--r--private/eventlog/server/eventlog.rc11
-rw-r--r--private/eventlog/server/eventp.h48
-rw-r--r--private/eventlog/server/file.c1088
-rw-r--r--private/eventlog/server/logclear.c621
-rw-r--r--private/eventlog/server/memory.c149
-rw-r--r--private/eventlog/server/operate.c3117
-rw-r--r--private/eventlog/server/terminat.c267
28 files changed, 16618 insertions, 0 deletions
diff --git a/private/eventlog/server/alert.c b/private/eventlog/server/alert.c
new file mode 100644
index 000000000..3751b91b2
--- /dev/null
+++ b/private/eventlog/server/alert.c
@@ -0,0 +1,147 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ ALERT.C
+
+Abstract:
+
+ This file contains the routine that sends the alert to the admin.
+
+Author:
+
+ Rajen Shah (rajens) 28-Aug-1991
+
+
+Revision History:
+
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <string.h>
+#include <lmalert.h> // LAN Manager alert structures
+
+
+
+BOOL
+SendAdminAlert (
+ ULONG MessageID,
+ ULONG NumStrings,
+ UNICODE_STRING *pStrings
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine raises an ADMIN alert with the message specified.
+
+
+Arguments:
+
+ MessageID - Message ID.
+ NumStrings - Number of replacement strings.
+ pStrings - Array of UNICODE_STRING Replacement strings.
+
+Return Value:
+
+ NONE
+
+Note:
+
+ This routine dynamically links to netapi32.dll to avoid a build
+ dependency.
+
+--*/
+{
+
+ typedef DWORD (*ALERT_FNC_PTR)(
+ IN LPTSTR AlertEventName,
+ IN LPVOID VariableInfo,
+ IN DWORD VariableInfoSize,
+ IN LPTSTR ServiceName
+ );
+
+
+ NET_API_STATUS NetStatus;
+
+ BYTE AlertBuffer[ELF_ADMIN_ALERT_BUFFER_SIZE + sizeof(ADMIN_OTHER_INFO)];
+ ADMIN_OTHER_INFO UNALIGNED *VariableInfo = (PADMIN_OTHER_INFO) AlertBuffer;
+
+ DWORD DataSize;
+ DWORD i;
+ LPWSTR pReplaceString;
+
+ HANDLE dllHandle;
+ ALERT_FNC_PTR AlertFunction;
+
+ //
+ // Dynamically link to netapi.dll. If it's not there just return. Return
+ // TRUE so we won't try to send this alert again.
+ //
+
+ dllHandle = LoadLibraryA("NetApi32.Dll");
+ if ( dllHandle == INVALID_HANDLE_VALUE ) {
+ ElfDbgPrint(("[ELF] Failed to load DLL NetApi32.Dll: %ld\n",
+ GetLastError()));
+ return(TRUE);
+ }
+
+ //
+ // Get the address of the service's main entry point. This
+ // entry point has a well-known name.
+ //
+
+ AlertFunction = (ALERT_FNC_PTR)GetProcAddress(
+ dllHandle,
+ "NetAlertRaiseEx"
+ );
+ if (AlertFunction == NULL ) {
+ ElfDbgPrint(("[ELF] Can't find entry NetAlertRaiseEx in NetApi32.Dll: %ld\n",
+ GetLastError()));
+ return(TRUE);
+
+ }
+
+ VariableInfo->alrtad_errcode = MessageID;
+ VariableInfo->alrtad_numstrings = NumStrings;
+
+ pReplaceString = (LPWSTR)(AlertBuffer + sizeof(ADMIN_OTHER_INFO));
+
+ //
+ // Copy over the replacement strings
+ //
+
+ for (i = 0; i < NumStrings; i++) {
+ RtlMoveMemory(pReplaceString, pStrings[i].Buffer,
+ pStrings[i].MaximumLength);
+ pReplaceString = (LPWSTR) ((PBYTE) pReplaceString +
+ pStrings[i].MaximumLength);
+ }
+
+ DataSize = (PBYTE) pReplaceString - (PBYTE) AlertBuffer;
+
+ NetStatus = (*AlertFunction)(ALERT_ADMIN_EVENT, AlertBuffer, DataSize,
+ wname_Eventlogsvc);
+
+ if (NetStatus != NERR_Success) {
+ ElfDbgPrint(("[ELF] NetAlertRaiseEx failed with %d\n", NetStatus));
+ ElfDbgPrint(("[ELF] Attempted to raise alert %d\n", MessageID));
+
+ //
+ // Probably just not started yet, try again later
+ //
+
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
diff --git a/private/eventlog/server/cairo.c b/private/eventlog/server/cairo.c
new file mode 100644
index 000000000..200049be1
--- /dev/null
+++ b/private/eventlog/server/cairo.c
@@ -0,0 +1,471 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ CAIRO.C
+
+Abstract:
+
+ This file contains Cairo alert extensions to the NT event log.
+
+Author:
+
+ Mark Blanford (markbl) 02-Nov-1994 New routines:
+
+ GetSourceAlertValuesFromRegistry
+ RaiseCairoAlert
+ TestSeverityFilter
+
+ With this check-in, it is possible for an NT event written to the 'System'
+ log to be raised as a Cairo alert. Further, it is possible for any
+ 'System' log source to have the NT events it writes be raised as Cairo
+ alerts (subject to a filter) by simply supplying two key values:
+ 'AlertCategory' & 'SeverityFilter' with their source name key under the
+ ...\EventLog\System portion of the registry.
+
+ The 'AlertCategory' key value specifies the alert category value used in
+ the alerts created from the source's NT events.
+
+ The 'SeverityFilter' key value is a bit mask specifying the severity of
+ NT events to be converted to Cairo alerts. Note: alert severity values are
+ a superset of the NT event types information, warning & error. For
+ example: assume the "Srv" source has specified alert category and severity
+ key values, and the severity value, a bit mask, is set to warning | error.
+ The NT events "Srv" subsequently writes to the 'System' log of NT event
+ type warning or error will be distributed as Cairo alerts. "Srv" NT events
+ with NT event types other than warning or error will not be raised as
+ Cairo alerts.
+
+ All Cairo alerts generated as a result of this conversion, are raised to
+ the local computer distributor.
+
+[Environment:]
+
+ User Mode - Win32, except for NTSTATUS returned by some functions.
+
+Revision History:
+
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <ole2.h>
+#include <oleext.h>
+#include <sysmgmt.h>
+#include <alertapi.h>
+
+GUID CLSID_CNTEventReport = {
+ 0x43750e20,
+ 0xfa68,
+ 0x11cd,
+ { 0x84, 0xb7, 0x00, 0xaa, 0x00, 0x4a, 0x56, 0xe1 }
+};
+
+static WCHAR wszAlertCategory[] = L"AlertCategory";
+static WCHAR wszSeverityFilter[] = L"SeverityFilter";
+
+BOOL IsValidSeverityFilter(SHORT sSeverity);
+
+
+/*++
+
+Routine Description:
+
+ Fetch "...\EventLog\System\<SourceName>" 'AlertCategory' and
+ 'SeverityFilter' registry values, if they exist.
+
+Arguments:
+
+ hKeyLogFile - Registry handle to '...\EventLog\<LogName>' where
+ <LogName> is likely to be 'System'.
+ pswszSourceName - Source name registered to report events to the log
+ indicated above.
+ psAlertCategory - Returned category value.
+ psSeverityFilter - Returned severity value.
+
+Return Value:
+
+ BOOL - TRUE if the category,severity filter was successfully read;
+ FALSE otherwise.
+
+Note:
+
+--*/
+
+BOOL
+GetSourceAlertFilterFromRegistry(
+ HANDLE hKeyLogFile,
+ UNICODE_STRING * pswszSourceName,
+ SHORT * psAlertCategory,
+ SHORT * psSeverityFilter)
+{
+#define KEY_VALUE_FULL_INFO_SIZE 1024 // BUGBUG : need proper size
+
+ BOOL fRet = FALSE;
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING SubKeyName;
+ HANDLE hKeySource;
+ BYTE Buffer[KEY_VALUE_FULL_INFO_SIZE];
+ PKEY_VALUE_FULL_INFORMATION KeyValue = (PKEY_VALUE_FULL_INFORMATION)Buffer;
+ ULONG Index = 0;
+ ULONG ActualSize;
+ SHORT sAlertCategory, sSeverityFilter;
+
+ ASSERT(pswszSourceName != NULL);
+ ASSERT(psAlertCategory != NULL);
+ ASSERT(psSeverityFilter != NULL);
+
+
+ InitializeObjectAttributes(&ObjectAttributes,
+ pswszSourceName,
+ OBJ_CASE_INSENSITIVE,
+ hKeyLogFile,
+ NULL);
+
+ //
+ // Read category,severity filter values, if they exist, for this source.
+ //
+
+ Status = NtOpenKey(&hKeySource, KEY_READ, &ObjectAttributes);
+
+ if (NT_SUCCESS(Status))
+ {
+ //
+ // Fetch alert category value, if it exists.
+ //
+
+ RtlInitUnicodeString(&SubKeyName, wszAlertCategory);
+
+ Status = NtQueryValueKey(hKeySource,
+ &SubKeyName,
+ KeyValueFullInformation,
+ KeyValue,
+ KEY_VALUE_FULL_INFO_SIZE,
+ &ActualSize);
+
+ if (NT_SUCCESS(Status))
+ {
+ sAlertCategory = *((PSHORT)(Buffer + KeyValue->DataOffset));
+
+ //
+ // Fetch severity filter value, if it exists.
+ //
+
+ RtlInitUnicodeString(&SubKeyName, wszSeverityFilter);
+
+ Status = NtQueryValueKey(hKeySource,
+ &SubKeyName,
+ KeyValueFullInformation,
+ KeyValue,
+ KEY_VALUE_FULL_INFO_SIZE,
+ &ActualSize);
+
+ if (NT_SUCCESS(Status))
+ {
+ sSeverityFilter = *((PSHORT)(Buffer + KeyValue->DataOffset));
+
+ //
+ // Is the severity valid?
+ //
+
+ if (!IsValidSeverityFilter(sSeverityFilter))
+ {
+ ElfDbgPrintNC((
+ "[ELF] Invalid severity filter (%02x) for " \
+ "source \"%ws\"\n",
+ sSeverityFilter,
+ pswszSourceName->Buffer));
+ return(FALSE);
+ }
+ }
+ }
+
+ if (NT_SUCCESS(Status))
+ {
+ *psAlertCategory = sAlertCategory;
+ *psSeverityFilter = sSeverityFilter;
+ fRet = TRUE;
+ }
+
+ Status = NtClose(hKeySource);
+ ASSERT(NT_SUCCESS(Status));
+ }
+ else
+ {
+ ElfDbgPrintNC((
+ "[ELF] Unable to open source key %ws to fetch alert " \
+ "filter (%x)\n",
+ pswszSourceName->Buffer,
+ Status));
+ }
+
+ return(fRet);
+}
+
+
+/*++
+
+Routine Description:
+
+ Bit-wise compare the passed NT event type & alert severity.
+
+Arguments:
+
+ NTEventType - NT event type.
+ sSeverityFilter - Alert severity.
+
+Return Value:
+
+ BOOL - TRUE if NTEventType has a bit in common with sSeverityFilter;
+ FALSE otherwise.
+
+Note:
+
+--*/
+
+BOOL
+TestFilter(WORD NTEventType, SHORT sSeverityFilter)
+{
+ //
+ // Mask off all bits but the alert severities.
+ //
+
+ if (((ALERTSEVERITY_FATAL_ERROR |
+ ALERTSEVERITY_SEVERE_ERROR |
+ ALERTSEVERITY_ERROR |
+ ALERTSEVERITY_WARNING |
+ ALERTSEVERITY_INFORMATION) & sSeverityFilter) & NTEventType)
+ {
+ return(TRUE);
+ }
+
+ return(FALSE);
+}
+
+/*++
+
+Routine Description:
+
+ Test the severity passed for validity. No bits other than the severity
+ mask must be set and the severity must be non-zero.
+
+Arguments:
+
+ sSeverity - Tested severity.
+
+Return Value:
+
+ BOOL - TRUE if the test succeeds;
+ FALSE otherwise.
+
+Note:
+
+--*/
+
+BOOL
+IsValidSeverityFilter(SHORT sSeverity)
+{
+ //
+ // Insure the only bits set are the severity mask.
+ //
+
+ if (!(sSeverity & ~(ALERTSEVERITY_FATAL_ERROR |
+ ALERTSEVERITY_SEVERE_ERROR |
+ ALERTSEVERITY_ERROR |
+ ALERTSEVERITY_WARNING |
+ ALERTSEVERITY_INFORMATION)))
+ {
+ //
+ // Is a severity specified at all?
+ //
+
+ return(sSeverity ? TRUE : FALSE);
+ }
+
+ return(FALSE);
+}
+
+
+/*++
+
+Routine Description:
+
+ Convert & raise the NT event passed as a Cairo alert to the local
+ computer distributor.
+
+Arguments:
+
+ pLogModule - Specific log module. This structure supplies the alert
+ category value & the log module name.
+ pEventLogRecord - The NT event to be converted & raised as an alert.
+
+Return Value:
+
+ HRESULT -
+
+Note:
+
+--*/
+
+HRESULT
+RaiseCairoAlert(PLOGMODULE pLogModule, EVENTLOGRECORD * pEventLogRecord)
+{
+ HRESULT hr = S_OK;
+ BSTR bstrLogName = NULL;
+ WCHAR * pwszTmp;
+ int i;
+ ALERTREPORTRECORD alertreport;
+ VARIANT avar[2];
+ DISPID aid[2];
+ WCHAR ** apwszInserts = NULL;
+ HINSTANCE hInstance;
+
+
+ do
+ {
+ //
+ // ALERTSYS.DLL & the ReportAlert entry point is linked to by hand
+ // in the eventlog initialization code (eventlog.c). If this global
+ // handle is NULL, the link attempt failed.
+ //
+
+ if (ghAlertSysDll == NULL)
+ {
+ //
+ // BUGBUG : Log an error?
+ //
+
+ return(E_FAIL);
+ }
+
+ //
+ // Initialize mandatory properties.
+ //
+
+ INITIALIZE_ALERTREPORTRECORD(alertreport,
+ pLogModule->AlertCategory, // Alert category
+ pEventLogRecord->EventType, // Alert severity
+ -1, // No title message no.
+ pEventLogRecord->EventID, // Description message no.
+ NULL, // No component ID
+ (WCHAR *)((BYTE *)pEventLogRecord + // Source name
+ sizeof(EVENTLOGRECORD)));
+
+ //
+ // Alert report class
+ //
+
+ alertreport.ReportClassID = &CLSID_CNTEventReport;
+
+ //
+ // Properties specific to NT event reports...
+ //
+ // Event category
+ //
+
+ aid[0] = DISPID_NTEventReport_EventCategory;
+ VariantInit(&avar[0]);
+ avar[0].vt = VT_I2;
+ avar[0].iVal = pEventLogRecord->EventCategory;
+
+ //
+ // Log module name
+ //
+
+ bstrLogName = SysAllocString(pLogModule->LogFile->
+ LogModuleName->Buffer);
+
+ if (bstrLogName == NULL)
+ {
+ ElfDbgPrintNC((
+ "[ELF RaiseCairoAlert] failed to allocate log name bstr\n"));
+ hr = E_OUTOFMEMORY;
+ break;
+ }
+
+ aid[1] = DISPID_NTEventReport_LogFile;
+ VariantInit(&avar[1]);
+ avar[1].vt = VT_BSTR;
+ avar[1].bstrVal = bstrLogName;
+
+ //
+ // Description inserts; optional.
+ //
+
+ if (pEventLogRecord->NumStrings > 0)
+ {
+ apwszInserts = (WCHAR **)ElfpAllocateBuffer(sizeof(WCHAR *) *
+ pEventLogRecord->NumStrings);
+
+ if (apwszInserts == NULL)
+ {
+ ElfDbgPrintNC((
+ "[ELF RaiseCairoAlert] failed to allocate insert " \
+ "string\n"));
+ hr = E_OUTOFMEMORY;
+ break;
+ }
+
+ for (i = 0, pwszTmp = (WCHAR *)((BYTE *)pEventLogRecord +
+ pEventLogRecord->StringOffset);
+ (pwszTmp != NULL) && (i < pEventLogRecord->NumStrings); i++)
+ {
+ apwszInserts[i] = pwszTmp;
+ pwszTmp += wcslen(pwszTmp) + 1;
+ }
+
+ alertreport.cDescrMessageInserts = i;
+ alertreport.DescrMessageInserts = apwszInserts;
+ }
+
+ alertreport.cAdditionalArguments = 2;
+ alertreport.AdditionalArgumentNames = aid;
+ alertreport.AdditionalArguments = avar;
+
+ //
+ // Alert data; optional, as well.
+ //
+
+ if (pEventLogRecord->DataLength > 0)
+ {
+ alertreport.cBytesAlertData = pEventLogRecord->DataLength;
+ alertreport.AlertData = (BYTE *)pEventLogRecord +
+ pEventLogRecord->DataOffset;
+ }
+
+ //
+ // Finally, raise the alert.
+ //
+
+ hr = gpfReportAlert(&alertreport, RA_REPORT);
+
+ if (FAILED(hr))
+ {
+ ElfDbgPrintNC((
+ "[ELF RaiseCairoAlert] ReportAlert failed HRESULT(%x)\n",
+ hr));
+ break;
+ }
+
+ } while (0);
+
+ // Cleanup
+
+ if (bstrLogName != NULL)
+ {
+ SysFreeString(bstrLogName);
+ }
+ if (apwszInserts != NULL)
+ {
+ ElfpFreeBuffer(apwszInserts);
+ }
+
+ return(hr);
+}
diff --git a/private/eventlog/server/config.c b/private/eventlog/server/config.c
new file mode 100644
index 000000000..ebf45b8b0
--- /dev/null
+++ b/private/eventlog/server/config.c
@@ -0,0 +1,1007 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ CONFIG.C
+
+Abstract:
+
+ This file contains the routines that walk the configuration registry.
+
+Author:
+
+ Rajen Shah (rajens) 1-Jul-1991
+
+
+Revision History:
+
+ 29-Aug-1994 Danl
+ We no longer grow log files in place. Therefore, the MaxSize value
+ in the registery ends up being advisory only. We don't try to reserve
+ that much memory at init time. So it could happen that when we need
+ a larger file size that we may not have enough memory to allocate
+ MaxSize bytes.
+ 28-Mar-1994 Danl
+ ReadRegistryInfo: LogFileInfo->LogFileName wasn't getting updated
+ when using the default (generated) LogFileName.
+ 16-Mar-1994 Danl
+ Fixed Memory Leaks in ReadRegistryInfo(). Call to
+ RtlDosPathNameToNtPathName allocates memory that wasn't being free'd.
+ 03-Mar-1995 MarkBl
+ Added GuestAccessRestriction flag initialization in ReadRegistryInfo.
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <elfcfg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <memory.h>
+
+//
+// STRUCTURES
+//
+
+//
+// This structure contains all the information used to setup and
+// for listening to registry changes in the eventlog tree.
+//
+typedef struct _REG_MONITOR_INFO {
+ HANDLE NotifyEventHandle;
+ DWORD Timeout;
+ HANDLE WorkItemHandle;
+} REG_MONITOR_INFO, *LPREG_MONITOR_INFO;
+
+//
+// GLOBALS
+//
+ REG_MONITOR_INFO GlRegMonitorInfo;
+
+//
+// LOCAL FUNCTIONS
+//
+DWORD
+ElfRegistryMonitor (
+ LPVOID pParms,
+ DWORD dwWaitStatus
+ );
+
+BOOL
+ElfSetupMonitor(
+ LPREG_MONITOR_INFO pMonitorInfo
+ );
+
+
+
+
+VOID
+ProcessChange (
+ HANDLE hLogFile,
+ PUNICODE_STRING ModuleName,
+ PUNICODE_STRING LogFileName,
+ ULONG MaxSize,
+ ULONG Retention,
+ ULONG GuestAccessRestriction
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by ProcessRegistryChanges for each module.
+
+Arguments:
+
+
+
+Return Value:
+
+ None
+
+Note:
+
+
+--*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLOGMODULE pModule;
+ PLOGFILE pLogFile;
+ ULONG Size;
+ PVOID BaseAddress;
+ PUNICODE_STRING pFileNameString;
+ LPWSTR FileName;
+ PVOID FreeAddress;
+
+
+ pModule = GetModuleStruc (ModuleName);
+
+ //
+ // If this module didn't exist, this was a brand new log file and
+ // we need to create all the structures
+ //
+
+ if (pModule == ElfDefaultLogModule &&
+ wcscmp(ModuleName->Buffer, ELF_DEFAULT_MODULE_NAME)) {
+ Status = SetUpDataStruct(LogFileName, MaxSize, Retention,
+ GuestAccessRestriction, ModuleName, hLogFile, ElfNormalLog);
+ return;
+ }
+
+ //
+ // Update values
+ //
+
+ pLogFile = pModule->LogFile;
+ pLogFile->Retention = Retention;
+
+ //
+ // Check to see if the name has changed. If it has, and the log
+ // hasn't been used yet, then use the new name. Be sure to free
+ // memory that was used for the old name.
+ //
+
+ if ((wcscmp(pLogFile->LogFileName->Buffer, LogFileName->Buffer) != 0) &&
+ (pLogFile->BeginRecord == pLogFile->EndRecord)) {
+
+ pFileNameString = ElfpAllocateBuffer(
+ sizeof(UNICODE_STRING) +
+ LogFileName->MaximumLength);
+
+ if (pFileNameString != NULL) {
+ FileName = (LPWSTR)(pFileNameString+1);
+ wcscpy(FileName, LogFileName->Buffer);
+ RtlInitUnicodeString(pFileNameString, FileName);
+
+ ElfpFreeBuffer(pLogFile->LogFileName);
+ pLogFile->LogFileName = pFileNameString;
+ }
+ }
+
+ //
+ // The log file can only be grown dynamically. To shrink it,
+ // it has to be cleared.
+ //
+
+
+ if (pLogFile->ConfigMaxFileSize < ELFFILESIZE(MaxSize)) {
+
+ /*
+ Description of recent changes. Problem and Solution:
+ A couple of problems exist. (1) There is no error
+ checking if memory can't be allocated or mapped, and
+ therefore, no error paths exist for handling these
+ situations. (2) Now that the eventlog is in services.exe
+ there isn't a good way to synchronize memory allocations.
+
+ Solution:
+ I considered having some utility routines for managing
+ memory in the eventlog. These would attempt to
+ extend a reserved block, or get a new reserved block.
+ However, there are so many places where that could fail,
+ it seemed very cumbersome to support the reserved blocks.
+ So the current design only deals with mapped views.
+ The ConfigMaxFileSize is only used to limit the size of
+ the mapped view, and doesn't reserve anything. This
+ means you are not guaranteed to be operating with a file as
+ large as the MaxSize specified in the registry. But then,
+ you weren't guarenteed that it would even work with the
+ original design.
+ */
+
+ pLogFile->ConfigMaxFileSize = ELFFILESIZE(MaxSize);
+ pLogFile->NextClearMaxFileSize = ELFFILESIZE(MaxSize);
+
+ }
+ else if (pLogFile->ConfigMaxFileSize > ELFFILESIZE(MaxSize)) {
+
+ //
+ // They're shrinking the size of the log file.
+ // Next time we clear the log file, we'll use the new size
+ // and new retention.
+ //
+
+ pLogFile->NextClearMaxFileSize = ELFFILESIZE(MaxSize);
+
+ }
+
+ //
+ // Now see if they've added any new modules for this log file
+ //
+
+ SetUpModules(hLogFile, pLogFile, TRUE);
+
+ return;
+}
+
+
+
+VOID
+ProcessRegistryChanges (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine processes that changes that have occurred in the
+ eventlog node. It does this by rescanning the whole Eventlog node
+ and then comparing with what it has as the current configuration.
+
+Arguments:
+
+ NONE.
+
+Return Value:
+
+ NONE
+
+--*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hLogFile;
+ UNICODE_STRING SubKeyName;
+ ULONG Index = 0;
+ BYTE Buffer[ELF_MAX_REG_KEY_INFO_SIZE];
+ PKEY_NODE_INFORMATION KeyBuffer = (PKEY_NODE_INFORMATION) Buffer;
+ ULONG ActualSize;
+ LOG_FILE_INFO LogFileInfo;
+ PWCHAR SubKeyString;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ ElfDbgPrint(("[ELF] ProcessRegistryChanges\n"));
+
+ //
+ // Take the global resource so that nobody is making changes or
+ // using the existing configured information.
+ //
+
+ GetGlobalResource (ELF_GLOBAL_SHARED);
+
+ //
+ // See if the Debug flag changed
+ //
+
+ RtlInitUnicodeString(&SubKeyName, VALUE_DEBUG);
+
+ NtQueryValueKey(hEventLogNode, &SubKeyName,
+ KeyValueFullInformation, KeyBuffer,
+ ELF_MAX_REG_KEY_INFO_SIZE, & ElfDebug);
+
+
+ //
+ // Loop thru the subkeys under Eventlog and set up each logfile
+ //
+
+ while (NT_SUCCESS(Status)) {
+
+ Status = NtEnumerateKey(hEventLogNode, Index++, KeyNodeInformation,
+ KeyBuffer, ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // It turns out the Name isn't null terminated, so we need
+ // to copy it somewhere and null terminate it before we use it
+ //
+
+ SubKeyString = ElfpAllocateBuffer(KeyBuffer->NameLength +
+ sizeof (WCHAR));
+ if (!SubKeyString) {
+
+ //
+ // No one to notify, just give up till next time.
+ //
+
+ ReleaseGlobalResource();
+ return;
+ }
+
+ memcpy(SubKeyString, KeyBuffer->Name, KeyBuffer->NameLength);
+ SubKeyString[KeyBuffer->NameLength / sizeof(WCHAR)] = L'\0' ;
+
+ //
+ // Open the node for this logfile and extract the information
+ // required by SetupDataStruct, and then call it.
+ //
+
+ RtlInitUnicodeString(&SubKeyName, SubKeyString);
+
+ InitializeObjectAttributes(&ObjectAttributes,
+ &SubKeyName,
+ OBJ_CASE_INSENSITIVE,
+ hEventLogNode,
+ NULL
+ );
+
+ Status = NtOpenKey(&hLogFile, KEY_READ | KEY_SET_VALUE,
+ &ObjectAttributes);
+
+ //
+ // Should always succeed since I just enum'ed it, but if it
+ // doesn't, just skip it
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfpFreeBuffer(SubKeyString);
+ Status = STATUS_SUCCESS; // to keep the enum going
+ continue;
+
+ }
+
+ Status = ReadRegistryInfo(hLogFile, &SubKeyName, & LogFileInfo);
+
+ if (NT_SUCCESS(Status)) {
+ //
+ // Now process any changes. Any errors are dealt with
+ // in ProcessChange
+ //
+
+ ProcessChange (
+ hLogFile,
+ &SubKeyName,
+ LogFileInfo.LogFileName,
+ LogFileInfo.MaxFileSize,
+ LogFileInfo.Retention,
+ LogFileInfo.GuestAccessRestriction
+ );
+
+ //
+ // Free the buffer that was allocated in ReadRegistryInfo.
+ //
+ ElfpFreeBuffer(LogFileInfo.LogFileName);
+ }
+
+ ElfpFreeBuffer(SubKeyString);
+ NtClose(hLogFile);
+ }
+ }
+
+ //
+ // Release the global resource.
+ //
+
+ ReleaseGlobalResource();
+
+
+} // ProcessRegistryChanges
+
+
+
+
+DWORD
+ElfRegistryMonitor (
+ LPVOID pParms,
+ DWORD dwWaitStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This is the entry point for the thread that will monitor changes in
+ the registry. If anything changes, it will have to scan the change
+ and then make the appropriate changes to the data structures in the
+ service to reflect the new information.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ LPREG_MONITOR_INFO pMonitorInfo=(LPREG_MONITOR_INFO)pParms;
+
+ ElfDbgPrint(("[ELF] Inside registry monitor thread\n"));
+
+
+ if (GetElState() == STOPPING) {
+ //
+ // If the eventlog is shutting down, then we need
+ // to terminate this thread.
+ //
+ ElfDbgPrint(("[ELF] ElfRegistryMonitor - Shutdown\n"));
+
+ //
+ // Close the registry handle and registry event handle.
+ //
+ NtClose( hEventLogNode);
+ CloseHandle(pMonitorInfo->NotifyEventHandle);
+
+ //===============================================================
+ // When we return from this call, the service controller will
+ // decrement our reference count to 0 and unload the DLL.
+ // So we can't exit until all the rest of the eventlog has shutdown.
+ // This thread will perform the final cleanup for the eventlog.
+ //
+ ElfpCleanUp(EventFlags);
+
+ //
+ // We should actually return here so that the DLL gets unloaded.
+ // However, RPC has a problem in that it might still call our
+ // context rundown routine even though we unregistered our interface.
+ // So we exit thread instead. This keeps our Dll loaded.
+ //
+ ExitThread(0);
+ }
+ if (dwWaitStatus == STATUS_TIMEOUT) {
+
+ ElfDbgPrint(("[ELF] Timer popped, running queued list\n"));
+
+ //
+ // Timer popped, try running the list
+ //
+
+ if (!IsListEmpty(&QueuedEventListHead)) {
+
+ //
+ // There are things queued up to write, do it
+ //
+
+ WriteQueuedEvents();
+
+ }
+
+ //
+ // Don't wait again
+ //
+
+ pMonitorInfo->Timeout = INFINITE;
+
+ }
+ else if (dwWaitStatus == WAIT_OBJECT_0) {
+ ElfDbgPrint(("[ELF] ElfRegistryMonitor - Notification\n"));
+ ProcessRegistryChanges ();
+ }
+ else {
+ ElfDbgPrint(("[ELF] WorkItemCallback returned %d\n",
+ dwWaitStatus));
+ }
+
+ if (!ElfSetupMonitor(pMonitorInfo)) {
+ ElfDbgPrint(("[ELF] ElfSetupMonitor Failed! Can't listen for Reg Changes\n"));
+ }
+
+ ElfDbgPrint(("[ELF] ElfRegistryMonitor - returning\n"));
+
+ return(0);
+
+} // ElfRegistryMonitor
+
+
+
+DWORD
+InitNotify(
+ PVOID pData,
+ DWORD dwWaitStatus
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ NTSTATUS NtStatus = STATUS_SUCCESS;
+ DWORD status = NO_ERROR;
+ DWORD Buffer;
+ PVOID pBuffer = & Buffer;
+ LPREG_MONITOR_INFO pMonitorInfo;
+
+ static IO_STATUS_BLOCK IoStatusBlock;
+
+ UNREFERENCED_PARAMETER(dwWaitStatus);
+
+ ElfDbgPrint(("[ELF]In InitNotify Routine\n"));
+
+ pMonitorInfo = (LPREG_MONITOR_INFO)pData;
+
+ NtStatus = NtNotifyChangeKey (
+ hEventLogNode,
+ pMonitorInfo->NotifyEventHandle,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ REG_NOTIFY_CHANGE_LAST_SET |
+ REG_NOTIFY_CHANGE_NAME,
+ TRUE,
+ pBuffer,
+ 1,
+ TRUE // return and wait on event
+ );
+
+ if (!NT_SUCCESS(NtStatus)) {
+ status = RtlNtStatusToDosError(NtStatus);
+ }
+ ElfDbgPrint(("[ELF]NtNotifyChangeKey Status = 0x%lx\n",NtStatus));
+ return(status);
+
+} // InitNotify
+
+
+BOOL
+ElfSetupMonitor(
+ LPREG_MONITOR_INFO pMonitorInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This function submits a request for a registry NotifyChangeKey
+ and then submits a work item to the service controller thread
+ management system to wait for the Notification handle to become
+ signaled.
+
+Arguments:
+
+ pMonitorInfo - This is a pointer to a MONITOR_INFO structure. This
+ function fills in the WorkItemHandle member of that structure
+ if successfully adds a new work item.
+
+Return Value:
+
+ TRUE - if successful in setting up.
+ FALSE - if unsuccessful. A work item hasn't been submitted, and
+ we won't be listening for registry changes.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // Call NtNotifyChange Key indirectly via the service controller
+ // Watcher thread. This way the thread that created the I/O
+ // request will always be around.
+ //
+ pMonitorInfo->WorkItemHandle = ElfGlobalData->SvcsAddWorkItem(
+ NULL,
+ InitNotify,
+ (PVOID)pMonitorInfo,
+ SVC_IMMEDIATE_CALLBACK,
+ INFINITE,
+ NULL);
+
+ if (pMonitorInfo->WorkItemHandle == NULL) {
+ ElfDbgPrint(("[ELF]Couldn't Initialize Registry Notify %d\n",GetLastError()));
+ return(FALSE);
+ }
+ //
+ // Add the work item that is to be called when the
+ // NotifyEventHandle is signalled.
+ //
+ pMonitorInfo->WorkItemHandle = ElfGlobalData->SvcsAddWorkItem(
+ pMonitorInfo->NotifyEventHandle,
+ ElfRegistryMonitor,
+ (PVOID)pMonitorInfo,
+ SVC_QUEUE_WORK_ITEM,
+ pMonitorInfo->Timeout,
+ ElfGlobalSvcRefHandle);
+
+ if (pMonitorInfo->WorkItemHandle == NULL) {
+ ElfDbgPrint(("[ELF]Couldn't add Reg Monitor work item\n"));
+ return(FALSE);
+ }
+ return(TRUE);
+
+} // ElfSetupMonitor
+
+
+BOOL
+ElfStartRegistryMonitor()
+
+/*++
+
+Routine Description:
+
+ This routine starts up the thread that monitors changes in the registry.
+
+ This function calls ElfSetupMonitor() to register for the change
+ notification and to submit a work item to wait for the registry
+ change event to get signaled. When signalled, the ElfRegistryMonitor()
+ callback function is called by a thread from the services thread pool.
+ This callback function services the notification.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ TRUE if thread creation succeeded, FALSE otherwise.
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status;
+ HANDLE hNotifyEvent=NULL;
+
+ ElfDbgPrint(("[ELF] Starting registry monitor\n"));
+
+ if (hEventLogNode == NULL) {
+ ElfDbgPrint(("[ELF]No EventLog node in registry - Exit Monitor"));
+ return(FALSE);
+ }
+
+ //
+ // Create an event to wait on
+ //
+
+ Status = NtCreateEvent (&hNotifyEvent, EVENT_ALL_ACCESS,
+ NULL, NotificationEvent, FALSE);
+
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF]Couldn't create event for registry monitor"));
+ return(FALSE);
+ }
+
+ //
+ // Fill in the Monitor info structure with the event handle
+ // and a 5 minute timeout.
+ //
+
+ GlRegMonitorInfo.NotifyEventHandle = hNotifyEvent;
+ GlRegMonitorInfo.Timeout = 5 * 60 * 1000;
+ GlRegMonitorInfo.WorkItemHandle = NULL;
+
+ //
+ // Setup for the change notify and
+ // submit the work item to the service controller.
+ //
+
+ if (!ElfSetupMonitor(&GlRegMonitorInfo)) {
+ return(FALSE);
+ }
+ return(TRUE);
+
+} // ElfStartRegistryMonitor
+
+
+VOID
+StopRegistryMonitor ()
+
+/*++
+
+Routine Description:
+
+ This routine wakes up the work item that has been submitted for the
+ purpose of monitoring registry eventlog changes. The thread created
+ to service that work item will actually do the clean-up of the monitor
+ thread.
+
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+--*/
+
+{
+ ElfDbgPrint (("[ELF] Stopping registry monitor\n"));
+
+ //
+ // Wake up the RegistryMonitorThread.
+ //
+ if (GlRegMonitorInfo.NotifyEventHandle != NULL ) {
+ SetEvent(GlRegMonitorInfo.NotifyEventHandle);
+ }
+
+ return;
+
+} // StopRegistryMonitor
+
+
+NTSTATUS
+ReadRegistryInfo (
+ HANDLE hLogFile,
+ PUNICODE_STRING SubKeyName,
+ PLOG_FILE_INFO LogFileInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reads in the information from the node pointed to by
+ hLogFile and stores it in the a structure so that the
+ necessary data structures can be set up for the service.
+
+ ALLOCATIONS: If successful, this function allocates memory for
+ LogFileInfo->LogFileName. It is the responsiblilty of the caller
+ to free this memory.
+
+Arguments:
+
+ hLogFile - A handle to the Eventlog\<somelogfile> node in the registry
+ KeyName - The subkey for this logfile to open
+ LogFileInfo - The structure to fill in with the data
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+
+#define EXPAND_BUFFER_SIZE 64
+
+ NTSTATUS Status;
+ BOOLEAN RegistryCorrupt = FALSE;
+ BYTE Buffer[ELF_MAX_REG_KEY_INFO_SIZE];
+ ULONG ActualSize;
+ UNICODE_STRING ValueName;
+ UNICODE_STRING UnexpandedName;
+ UNICODE_STRING ExpandedName;
+ ULONG NumberOfBytes = 0;
+ BYTE ExpandNameBuffer[EXPAND_BUFFER_SIZE];
+ PUNICODE_STRING FileNameString;
+ LPWSTR FileName;
+ BOOL ExpandedBufferWasAllocated=FALSE;
+ PKEY_VALUE_FULL_INFORMATION ValueBuffer =
+ (PKEY_VALUE_FULL_INFORMATION) Buffer;
+
+ ASSERT(hLogFile);
+
+ // MaxSize
+
+ RtlInitUnicodeString(&ValueName, VALUE_MAXSIZE);
+
+ Status = NtQueryValueKey(hLogFile, &ValueName,
+ KeyValueFullInformation, ValueBuffer,
+ ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] - Logfile %ws Maxsize doesn't exist\n",
+ SubKeyName->Buffer));
+ LogFileInfo->MaxFileSize = ELF_DEFAULT_MAX_FILE_SIZE;
+ RegistryCorrupt = TRUE;
+ }
+ else {
+ LogFileInfo->MaxFileSize = *((PULONG)(Buffer +
+ ValueBuffer->DataOffset));
+ }
+
+
+ // Retention period
+
+ RtlInitUnicodeString(&ValueName, VALUE_RETENTION);
+
+ Status = NtQueryValueKey(hLogFile, &ValueName,
+ KeyValueFullInformation, ValueBuffer,
+ ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] - Logfile %ws Retention doesn't exist\n",
+ SubKeyName->Buffer));
+ LogFileInfo->Retention = ELF_DEFAULT_RETENTION_PERIOD;
+ RegistryCorrupt = TRUE;
+ }
+ else {
+ LogFileInfo->Retention = *((PULONG)(Buffer +
+ ValueBuffer->DataOffset));
+ }
+
+
+ // RestrictGuestAccess
+
+ RtlInitUnicodeString(&ValueName, VALUE_RESTRICT_GUEST_ACCESS);
+
+ Status = NtQueryValueKey(hLogFile, &ValueName,
+ KeyValueFullInformation, ValueBuffer,
+ ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
+ if (!NT_SUCCESS(Status)) {
+ LogFileInfo->GuestAccessRestriction = ELF_GUEST_ACCESS_UNRESTRICTED;
+ }
+ else {
+ if (*((PULONG)(Buffer + ValueBuffer->DataOffset)) == 1) {
+ LogFileInfo->GuestAccessRestriction = ELF_GUEST_ACCESS_RESTRICTED;
+ }
+ else {
+ LogFileInfo->GuestAccessRestriction =
+ ELF_GUEST_ACCESS_UNRESTRICTED;
+ }
+ }
+
+
+ // Filename
+
+ RtlInitUnicodeString(&ValueName, VALUE_FILENAME);
+
+ Status = NtQueryValueKey(hLogFile, &ValueName,
+ KeyValueFullInformation, ValueBuffer,
+ ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // Allocate the buffer for the UNICODE_STRING for the filename and
+ // initialize it. (41 = \Systemroot\system32\config\xxxxxxxx.evt)
+ //
+
+ FileNameString = ElfpAllocateBuffer(41 * sizeof(WCHAR) +
+ sizeof(UNICODE_STRING));
+ if (!FileNameString) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ LogFileInfo->LogFileName = FileNameString;
+ FileName = (LPWSTR)(FileNameString + 1);
+ wcscpy(FileName, L"\\Systemroot\\System32\\Config\\");
+ wcsncat(FileName, SubKeyName->Buffer, 8);
+ wcscat(FileName, L".evt");
+ RtlInitUnicodeString(FileNameString, FileName);
+
+ RegistryCorrupt = TRUE;
+
+ }
+ else {
+
+ //
+ // If it's a REG_EXPAND_SZ expand it
+ //
+
+ if (ValueBuffer->Type == REG_EXPAND_SZ) {
+
+ //
+ // Initialize the UNICODE_STRING, when the string isn't null
+ // terminated
+ //
+
+ UnexpandedName.MaximumLength = UnexpandedName.Length =
+ (USHORT) ValueBuffer->DataLength;
+ UnexpandedName.Buffer = (PWSTR) ((PBYTE) ValueBuffer +
+ ValueBuffer->DataOffset);
+
+ //
+ // Call the magic expand-o api
+ //
+
+ ExpandedName.Length = ExpandedName.MaximumLength =
+ EXPAND_BUFFER_SIZE;
+ ExpandedName.Buffer = (LPWSTR) ExpandNameBuffer;
+ Status = RtlExpandEnvironmentStrings_U(NULL, &UnexpandedName,
+ &ExpandedName, &NumberOfBytes);
+
+ if (NumberOfBytes > EXPAND_BUFFER_SIZE) {
+
+ //
+ // The default buffer wasn't big enough. Allocate a
+ // bigger one and try again
+ //
+
+ ExpandedName.Length = ExpandedName.MaximumLength =
+ (USHORT) NumberOfBytes;
+ ExpandedName.Buffer = ElfpAllocateBuffer(ExpandedName.Length);
+ if (!ExpandedName.Buffer) {
+ return(STATUS_NO_MEMORY);
+ }
+ ExpandedBufferWasAllocated = TRUE;
+
+ Status = RtlExpandEnvironmentStrings_U(NULL, &UnexpandedName,
+ &ExpandedName, &NumberOfBytes);
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ if (ExpandedBufferWasAllocated) {
+ ElfpFreeBuffer(ExpandedName.Buffer);
+ }
+ return(Status);
+ }
+ }
+ else {
+
+ //
+ // It doesn't need to be expanded, just set up the UNICODE_STRING
+ // for the conversion to an NT pathname
+ //
+
+ ExpandedName.MaximumLength = ExpandedName.Length =
+ (USHORT) ValueBuffer->DataLength;
+ ExpandedName.Buffer = (PWSTR) ((PBYTE) ValueBuffer +
+ ValueBuffer->DataOffset);
+ }
+
+ //
+ // Now convert from a DOS pathname to an NT pathname
+ //
+ // Need to allocate this since it needs to stay around
+ //
+
+ //
+ // Translate to NtPathName.
+ // NOTE: this allocates a buffer for ValueName.Buffer.
+ //
+ if (!RtlDosPathNameToNtPathName_U(ExpandedName.Buffer,
+ &ValueName, NULL, NULL)) {
+
+ if (ExpandedBufferWasAllocated) {
+ ElfpFreeBuffer(ExpandedName.Buffer);
+ }
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+ //
+ // Allocate memory for the unicode string structure and the buffer
+ // so that it can be free'd with a single call.
+ //
+ FileNameString = ElfpAllocateBuffer(
+ sizeof(UNICODE_STRING) +
+ ((ValueName.Length + 1) * sizeof(WCHAR)));
+
+ if (!FileNameString) {
+ if (ExpandedBufferWasAllocated) {
+ ElfpFreeBuffer(ExpandedName.Buffer);
+ }
+ RtlFreeHeap(RtlProcessHeap(),0,ValueName.Buffer);
+ return(STATUS_NO_MEMORY);
+ }
+
+ //
+ // Copy the NtPathName string into the new buffer, and initialize
+ // the unicode string.
+ //
+ FileName = (LPWSTR)(FileNameString + 1);
+ wcsncpy(FileName, ValueName.Buffer, ValueName.Length);
+ *(FileName+ValueName.Length) = L'\0';
+ RtlInitUnicodeString(FileNameString, FileName);
+
+ //
+ // Free memory allocated by RtlDosPathNAmeToNtPathName.
+ //
+ RtlFreeHeap(RtlProcessHeap(),0,ValueName.Buffer);
+
+ //
+ // Clean up if I had to allocate a bigger buffer than the default
+ //
+
+ if (ExpandedBufferWasAllocated) {
+ ElfpFreeBuffer(ExpandedName.Buffer);
+ }
+
+ }
+
+ //
+ // Add the LogFileName to the LogFileInfo structure.
+ //
+ LogFileInfo->LogFileName = FileNameString;
+
+ //
+ // If we didn't find all the required values, tell someone
+ //
+
+ if (RegistryCorrupt) {
+ ElfDbgPrintNC(("[ELF] Registry information for %ws invalid\n",
+ SubKeyName->Buffer));
+ }
+
+ return(STATUS_SUCCESS);
+
+}
+
diff --git a/private/eventlog/server/control.c b/private/eventlog/server/control.c
new file mode 100644
index 000000000..6f937f510
--- /dev/null
+++ b/private/eventlog/server/control.c
@@ -0,0 +1,699 @@
+
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ CONTROL.C
+
+Abstract:
+
+ This file contains the control handler for the eventlog service.
+
+Author:
+
+ Rajen Shah (rajens) 16-Jul-1991
+
+Revision History:
+
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+
+
+//
+// GLOBALS
+//
+
+ CRITICAL_SECTION StatusCriticalSection={0};
+ SERVICE_STATUS ElStatus={0};
+ DWORD HintCount=0;
+ DWORD ElUninstallCode=0; // reason for uninstalling
+ DWORD ElSpecificCode=0;
+ DWORD ElState=STARTING;
+
+
+VOID
+ElfPrepareForPause()
+
+/*++
+
+Routine Description:
+
+ This routine prepares the eventlog service for pausing. Since the
+ caller took the global resource for exclusive access, we know that
+ there is no activity on any of the files.
+
+ We just have to note somewhere that the service is paused
+ so that any threads that start running will know that and will not
+ perform any further operations until the service is CONTINUEd.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ ElfDbgPrint(("[ELF] Control: Prepare for service pause\n"));
+
+ //
+ // Flush all files to disk.
+ //
+ Status = ElfpFlushFiles ();
+
+ return;
+}
+
+
+
+VOID
+ElfPrepareForContinue()
+
+/*++
+
+Routine Description:
+
+ This routine restarts the eventlog service after a pause operation.
+ It will signal the relevant event(s) for all the threads to start
+ running.
+
+ The caller ensures that it has exclusive access to the global resource
+ so that there is no thread inside the service that is doing operations
+ on the log file(s) or the data structures while the control request
+ is being handled.
+
+Arguments:
+
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ ElfDbgPrint(("[ELF] Control: Prepare for service continue\n"));
+
+ //
+ // Signal the "continue" event.
+ //
+
+
+ return;
+}
+
+
+
+VOID
+ElfControlResponse(
+ DWORD opCode
+ )
+
+{
+ DWORD state;
+
+ ElfDbgPrint(("[ELF] Inside control handler. Control = %ld\n", opCode));
+
+ //
+ // Determine the type of service control message and modify the
+ // service status, if necessary.
+ //
+ switch(opCode) {
+
+ case SERVICE_CONTROL_SHUTDOWN:
+ case SERVICE_CONTROL_STOP:
+
+
+ //
+ // If the service is installed, shut it down and exit.
+ //
+
+ ElfStatusUpdate(STOPPING);
+
+ GetGlobalResource (ELF_GLOBAL_EXCLUSIVE);
+
+ //
+ // Log an event that says we're stopping
+ //
+
+#if DBG
+ ElfpCreateElfEvent(EVENT_EventlogStopped,
+ EVENTLOG_INFORMATION_TYPE,
+ 0, // EventCategory
+ 0, // NumberOfStrings
+ NULL, // Strings
+ NULL, // Data
+ 0, // Datalength
+ 0 // flags
+ );
+
+ //
+ // Now force it to be written before we shut down
+ //
+
+ WriteQueuedEvents();
+#endif
+ ReleaseGlobalResource();
+ //
+ // If the RegistryMonitor is started, wakeup that
+ // worker thread and have it handle the rest of the
+ // shutdown.
+ //
+ // Otherwise The main thread should pick up the
+ // fact that a shutdown during startup is occuring.
+ //
+ if (EventFlags & ELF_STARTED_REGISTRY_MONITOR) {
+ StopRegistryMonitor();
+ }
+
+ break ;
+
+ case SERVICE_CONTROL_PAUSE:
+
+ //
+ // If the service is not already paused, pause it.
+ //
+ state = GetElState();
+ if ((state != PAUSED) && (state != PAUSING)) {
+
+ GetGlobalResource (ELF_GLOBAL_EXCLUSIVE);
+
+ // NOTE: If there was any service-related pause cleanup, we'd
+ // set the status to PAUSE_PENDING, announce it, and
+ // then do the cleanup.
+ //
+
+ // Announce that the service is about to be paused
+ ElfStatusUpdate(PAUSING);
+
+ // Get into a decent state to pause the service.
+
+ ElfPrepareForPause();
+
+
+ // Set the status and announce that the service is paused
+ ElfStatusUpdate(PAUSED);
+
+ ReleaseGlobalResource();
+ }
+
+ break ;
+
+ case SERVICE_CONTROL_CONTINUE:
+
+ //
+ // If the service is not already running, un-pause it.
+ //
+
+ state = GetElState();
+ if ((state != RUNNING) && (state != CONTINUING)) {
+
+ GetGlobalResource (ELF_GLOBAL_EXCLUSIVE);
+
+ // NOTE: If there was any service-related continue cleanup, we'd
+ // set the status to CONTINUE_PENDING, announce it, and
+ // then do the cleanup.
+ //
+
+ // Announce that the service is about to be continued
+
+ ElfStatusUpdate(CONTINUING);
+
+ // Start up the service.
+
+ ElfPrepareForContinue();
+
+
+ // Set the status and announce that the service is no longer
+ // paused
+ //
+ ElfStatusUpdate(RUNNING);
+
+ ReleaseGlobalResource();
+ }
+
+ break ;
+
+ case SERVICE_CONTROL_INTERROGATE:
+
+ // Do nothing; the status gets announced below
+
+ default:
+ // WARNING: This should never happen.
+ ElfStatusUpdate(UPDATE_ONLY);
+ break ;
+ }
+
+ return ;
+
+}
+
+DWORD
+ElfBeginForcedShutdown(
+ IN BOOL PendingCode,
+ IN DWORD ExitCode,
+ IN DWORD ServiceSpecificCode
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ DWORD status;
+
+ EnterCriticalSection(&StatusCriticalSection);
+
+ ElfDbgPrint(("BeginForcedShutdown: Errors - %d 0x%lx\n",
+ ExitCode, ServiceSpecificCode));
+ //
+ // See if the eventlog is already stopping for some reason.
+ // It could be that the ControlHandler thread received a control to
+ // stop the eventlog just as we decided to stop ourselves.
+ //
+ if ((ElState != STOPPING) || (ElState != STOPPED)) {
+ if (PendingCode == PENDING) {
+ ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ ElState = STOPPING;
+ }
+ else {
+ //
+ // The shutdown is to take immediate effect.
+ //
+ ElStatus.dwCurrentState = SERVICE_STOPPED;
+ ElStatus.dwControlsAccepted = 0;
+ ElStatus.dwCheckPoint = 0;
+ ElStatus.dwWaitHint = 0;
+ ElState = STOPPED;
+ }
+
+ ElUninstallCode = ExitCode;
+ ElSpecificCode = ServiceSpecificCode;
+
+ ElStatus.dwWin32ExitCode = ExitCode;
+ ElStatus.dwServiceSpecificExitCode = ServiceSpecificCode;
+ }
+
+ //
+ // Send the new status to the service controller.
+ //
+ if (ElfServiceStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
+ ElfDbgPrint(("ElfBeginForcedShutdown, no handle to call SetServiceStatus\n"));
+
+ }
+ else if (! SetServiceStatus( ElfServiceStatusHandle, &ElStatus )) {
+
+ status = GetLastError();
+
+ if (status != NERR_Success) {
+ ElfDbgPrint(("ElfBeginForcedShutdown,SetServiceStatus Failed %X\n",
+ status));
+ }
+ }
+
+ status = ElState;
+ LeaveCriticalSection(&StatusCriticalSection);
+ return(status);
+}
+
+
+DWORD
+ElfStatusUpdate(
+ IN DWORD NewState
+ )
+
+/*++
+
+Routine Description:
+
+ Sends a status to the Service Controller via SetServiceStatus.
+
+ The contents of the status message is controlled by this routine.
+ The caller simply passes in the desired state, and this routine does
+ the rest. For instance, if the Eventlog passes in a STARTING state,
+ This routine will update the hint count that it maintains, and send
+ the appropriate information in the SetServiceStatus call.
+
+ This routine uses transitions in state to send determine which status
+ to send. For instance if the status was STARTING, and has changed
+ to RUNNING, this routine sends out an INSTALLED to the Service
+ Controller.
+
+Arguments:
+
+ NewState - Can be any of the state flags:
+ UPDATE_ONLY - Simply send out the current status
+ STARTING - The Eventlog is in the process of initializing
+ RUNNING - The Eventlog has finished with initialization
+ STOPPING - The Eventlog is in the process of shutting down
+ STOPPED - The Eventlog has completed the shutdown.
+ PAUSING -
+ CONTINUING -
+ PAUSED -
+
+
+Return Value:
+
+ CurrentState - This may not be the same as the NewState that was
+ passed in. It could be that the main thread is sending in a new
+ install state just after the Control Handler set the state to
+ STOPPING. In this case, the STOPPING state will be returned so as
+ to inform the main thread that a shut-down is in process.
+
+Note:
+
+
+--*/
+
+{
+ DWORD status;
+ BOOL inhibit = FALSE; // Used to inhibit sending the status
+ // to the service controller.
+
+ EnterCriticalSection(&StatusCriticalSection);
+
+ ElfDbgPrint(("ElfStatusUpdate (entry) NewState = %d, OldState = %d\n",
+ NewState,ElState));
+
+ if (NewState == STOPPED) {
+ if (ElState == STOPPED) {
+ //
+ // It was already stopped, don't send another SetServiceStatus.
+ //
+ inhibit = TRUE;
+ }
+ else {
+ //
+ // The shut down is complete, indicate that the eventlog
+ // has stopped.
+ //
+ ElStatus.dwCurrentState = SERVICE_STOPPED;
+ ElStatus.dwControlsAccepted = 0;
+ ElStatus.dwCheckPoint = 0;
+ ElStatus.dwWaitHint = 0;
+
+ ElStatus.dwWin32ExitCode = ElUninstallCode;
+ ElStatus.dwServiceSpecificExitCode = ElSpecificCode;
+
+ }
+ ElState = NewState;
+ }
+ else if (NewState == UPDATE_ONLY) {
+ inhibit = FALSE;
+ }
+ else {
+ //
+ // We are not being asked to change to the STOPPED state.
+ //
+ switch(ElState) {
+
+ case STARTING:
+ if (NewState == STOPPING) {
+
+ ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ ElStatus.dwControlsAccepted = 0;
+ ElStatus.dwCheckPoint = HintCount++;
+ ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
+ ElState = NewState;
+
+ EventlogShutdown = TRUE;
+ }
+
+ else if (NewState == RUNNING) {
+
+ //
+ // The Eventlog Service has completed installation.
+ //
+ ElStatus.dwCurrentState = SERVICE_RUNNING;
+ ElStatus.dwCheckPoint = 0;
+ ElStatus.dwWaitHint = 0;
+ //
+ // Only stoppable/pausable if developer's build.
+ //
+#if DBG
+ ElStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE |
+ SERVICE_ACCEPT_SHUTDOWN;
+#else
+ ElStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN;
+#endif
+ ElState = NewState;
+ }
+
+ else {
+ //
+ // The NewState must be STARTING. So update the pending
+ // count
+ //
+
+ ElStatus.dwCurrentState = SERVICE_START_PENDING;
+ ElStatus.dwControlsAccepted = 0;
+ ElStatus.dwCheckPoint = HintCount++;
+ ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
+ }
+ break;
+
+ case RUNNING:
+ if (NewState == STOPPING) {
+
+ ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ ElStatus.dwControlsAccepted = 0;
+
+ EventlogShutdown = TRUE;
+ }
+ else if (NewState == PAUSING) {
+ ElStatus.dwCurrentState = SERVICE_PAUSE_PENDING;
+ }
+ else if (NewState == PAUSED) {
+ ElStatus.dwCurrentState = SERVICE_PAUSED;
+ }
+ ElStatus.dwCheckPoint = HintCount++;
+ ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
+ ElState = NewState;
+
+ break;
+
+ case STOPPING:
+ //
+ // No matter what else was passed in, force the status to
+ // indicate that a shutdown is pending.
+ //
+ ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ ElStatus.dwControlsAccepted = 0;
+ ElStatus.dwCheckPoint = HintCount++;
+ ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
+ EventlogShutdown = TRUE;
+
+ break;
+
+ case STOPPED:
+ //
+ // We're already stopped. Therefore, an uninstalled status
+ // has already been sent. Do nothing.
+ //
+ inhibit = TRUE;
+ break;
+ case PAUSING:
+ if (NewState == PAUSED) {
+ ElStatus.dwCurrentState = SERVICE_PAUSED;
+ ElStatus.dwCheckPoint = 0;
+ ElStatus.dwWaitHint = 0;
+ }
+ else {
+ ElStatus.dwCheckPoint = HintCount++;
+ ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
+ }
+ ElState = NewState;
+ break;
+ case CONTINUING:
+ if (NewState == RUNNING) {
+ //
+ // The Eventlog Service has completed installation.
+ //
+ ElStatus.dwCurrentState = SERVICE_RUNNING;
+ ElStatus.dwCheckPoint = 0;
+ ElStatus.dwWaitHint = 0;
+ //
+ // Only stoppable/pausable if developer's build.
+ //
+#if DBG
+ ElStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE |
+ SERVICE_ACCEPT_SHUTDOWN;
+#else
+ ElStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN;
+#endif
+ ElState = NewState;
+ }
+ else {
+ ElStatus.dwCheckPoint = HintCount++;
+ }
+ break;
+ case PAUSED:
+ if (NewState == CONTINUING) {
+ ElStatus.dwCheckPoint = HintCount++;
+ ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
+ ElStatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
+ }
+ else if (NewState == RUNNING) {
+ //
+ // The Eventlog Service has completed installation.
+ //
+ ElStatus.dwCurrentState = SERVICE_RUNNING;
+ ElStatus.dwCheckPoint = 0;
+ ElStatus.dwWaitHint = 0;
+ //
+ // Only stoppable/pausable if developer's build.
+ //
+#if DBG
+ ElStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE |
+ SERVICE_ACCEPT_SHUTDOWN;
+#else
+ ElStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN;
+#endif
+ ElState = NewState;
+ }
+ break;
+ }
+ }
+
+ if (!inhibit) {
+ if (ElfServiceStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
+ ElfDbgPrint(("ElfStatusUpdate, no handle to call SetServiceStatus\n"));
+
+ }
+ else if (! SetServiceStatus( ElfServiceStatusHandle, &ElStatus )) {
+
+ status = GetLastError();
+
+ if (status != NERR_Success) {
+ ElfDbgPrint(("ElfStatusUpdate, SetServiceStatus Failed %d\n",
+ status));
+ }
+ }
+ }
+
+ ElfDbgPrint(("ElfStatusUpdate (exit) State = %d\n",ElState));
+ status = ElState;
+ LeaveCriticalSection(&StatusCriticalSection);
+ return(status);
+}
+
+
+DWORD
+GetElState (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Obtains the state of the Eventlog Service. This state information
+ is protected as a critical section such that only one thread can
+ modify or read it at a time.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ The Eventlog State is returned as the return value.
+
+--*/
+{
+ DWORD status;
+
+ EnterCriticalSection(&StatusCriticalSection);
+ status = ElState;
+ LeaveCriticalSection(&StatusCriticalSection);
+
+ return(status);
+}
+
+
+VOID
+ElInitStatus(VOID)
+
+/*++
+
+Routine Description:
+
+ Initializes the critical section that is used to guard access to the
+ status database.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+Note:
+
+
+--*/
+{
+ InitializeCriticalSection(&StatusCriticalSection);
+
+ ElStatus.dwCurrentState = SERVICE_START_PENDING;
+ ElStatus.dwServiceType = SERVICE_WIN32;
+
+}
+
+
+VOID
+ElCleanupStatus(VOID)
+
+/*++
+
+Routine Description:
+
+ Deletes the critical section used to control access to the thread and
+ status database.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+Note:
+
+
+--*/
+{
+ DeleteCriticalSection(&StatusCriticalSection);
+}
+
+
diff --git a/private/eventlog/server/copy.c b/private/eventlog/server/copy.c
new file mode 100644
index 000000000..3344bc3e4
--- /dev/null
+++ b/private/eventlog/server/copy.c
@@ -0,0 +1,196 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ copy.c
+
+Abstract:
+
+ This module contains the routine to copy a file.
+
+Author:
+
+ Dan Hinsley (DanHi) 24-Feb-1991
+
+Revision History:
+
+ 02-Feb-1994 Danl
+ Fixed memory leak where ioBuffer wasn't getting free'd when doing
+ an error exit from ElfpCopyFile.
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+
+
+NTSTATUS
+ElfpCopyFile (
+ IN HANDLE SourceHandle,
+ IN PUNICODE_STRING TargetFileName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies or appends from the source file to the target file.
+ If the target file already exists, the copy fails.
+
+Arguments:
+
+ SourceHandle - An open handle to the source file.
+
+ TargetFileName - The name of the file to copy to.
+
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS or error.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+
+ IO_STATUS_BLOCK IoStatusBlock;
+ FILE_STANDARD_INFORMATION sourceStandardInfo;
+
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE TargetHandle;
+
+ PCHAR ioBuffer;
+ ULONG ioBufferSize;
+ ULONG bytesRead;
+
+ //
+ // Get the size of the file so we can set the attributes of the target
+ // file.
+ //
+
+ Status = NtQueryInformationFile(
+ SourceHandle,
+ &IoStatusBlock,
+ &sourceStandardInfo,
+ sizeof(sourceStandardInfo),
+ FileStandardInformation
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ return(Status);
+ }
+
+ //
+ // Open the target file, fail if the file already exists.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ TargetFileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ Status = NtCreateFile(&TargetHandle,
+ GENERIC_WRITE | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ &(sourceStandardInfo.EndOfFile),
+ FILE_ATTRIBUTE_NORMAL,
+ 0, // Share access
+ FILE_CREATE,
+ FILE_SYNCHRONOUS_IO_ALERT | FILE_SEQUENTIAL_ONLY,
+ NULL, // EA buffer
+ 0 // EA length
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ //
+ // Allocate a buffer to use for the data copy.
+ //
+
+ ioBufferSize = 4096;
+
+ ioBuffer = ElfpAllocateBuffer ( ioBufferSize);
+
+ if ( ioBuffer == NULL ) {
+
+ return (STATUS_NO_MEMORY);
+ }
+
+ //
+ // Copy data--read from source, write to target. Do this until
+ // all the data is written or an error occurs.
+ //
+
+ while ( TRUE ) {
+
+ Status = NtReadFile(
+ SourceHandle,
+ NULL, // Event
+ NULL, // ApcRoutine
+ NULL, // ApcContext
+ &IoStatusBlock,
+ ioBuffer,
+ ioBufferSize,
+ NULL, // ByteOffset
+ NULL // Key
+ );
+
+ if ( !NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE ) {
+
+ ElfDbgPrint(("[ELF] Copy failed reading source file - %X\n",
+ Status));
+ ElfpFreeBuffer(ioBuffer);
+ return (Status);
+
+ }
+
+ if ( IoStatusBlock.Information == 0 ||
+ Status == STATUS_END_OF_FILE ) {
+ break;
+ }
+
+ bytesRead = IoStatusBlock.Information;
+
+ Status = NtWriteFile(
+ TargetHandle,
+ NULL, // Event
+ NULL, // ApcRoutine
+ NULL, // ApcContext
+ &IoStatusBlock,
+ ioBuffer,
+ bytesRead,
+ NULL, // ByteOffset
+ NULL // Key
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ ElfDbgPrint(("[ELF] Copy failed writing target file - %X\n",
+ Status));
+ ElfpFreeBuffer(ioBuffer);
+ return (Status);
+ }
+ }
+
+ ElfpFreeBuffer ( ioBuffer );
+
+ Status = NtClose(TargetHandle);
+
+ ASSERT(NT_SUCCESS(Status));
+
+ return STATUS_SUCCESS;
+
+} // ElfpCopyFile
+
diff --git a/private/eventlog/server/daytona/makefile b/private/eventlog/server/daytona/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/eventlog/server/daytona/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/eventlog/server/daytona/sources b/private/eventlog/server/daytona/sources
new file mode 100644
index 000000000..c27140453
--- /dev/null
+++ b/private/eventlog/server/daytona/sources
@@ -0,0 +1,69 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Rajen Shah (rajens) 2-Jul-1991
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = eventlog
+MINORCOMP = server
+
+TARGETPATH=obj
+TARGETNAME= eventlog
+TARGETTYPE=DYNLINK
+
+DLLDEF=..\eventlog.def
+
+TARGETLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib
+
+INCLUDES=..\;..\..;$(BASEDIR)\public\sdk\inc;$(BASEDIR)\private\inc
+
+SOURCES= \
+ ..\eventlog.rc \
+ ..\eventlog.c \
+ ..\alert.c \
+ ..\config.c \
+ ..\control.c \
+ ..\copy.c \
+ ..\elfapi.c \
+ ..\elfdata.c \
+ ..\elflpc.c \
+ ..\elfrpc.c \
+ ..\elfsec.c \
+ ..\elfutil.c \
+ ..\elf_s.c \
+ ..\file.c \
+ ..\memory.c \
+ ..\operate.c \
+ ..\logclear.c \
+ ..\terminat.c
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H
+
+USE_CRTDLL=1
+
+UMTYPE=windows
diff --git a/private/eventlog/server/dirs b/private/eventlog/server/dirs
new file mode 100644
index 000000000..945735ae2
--- /dev/null
+++ b/private/eventlog/server/dirs
@@ -0,0 +1,25 @@
+!IF 0
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles. This was necessitated by the need to
+ build a and nt rdr.
+
+Author:
+
+ Milan Shah (April 20, 1994)
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=daytona
+
+OPTIONAL_DIRS=
diff --git a/private/eventlog/server/elfapi.c b/private/eventlog/server/elfapi.c
new file mode 100644
index 000000000..5ffa2a2fd
--- /dev/null
+++ b/private/eventlog/server/elfapi.c
@@ -0,0 +1,2235 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ELFAPI.C
+
+Abstract:
+
+ This module contains the server end of the Elf APIs.
+
+
+Author:
+
+ Rajen Shah (rajens) 29-Jul-1991
+
+
+Revision History:
+ 30-Jan-1995 MarkBl
+ Backup operators are allowed to _open the security log, but are only
+ allowed to perform backup operations on it; all other operations are
+ disallowed.
+
+ 13-Oct-1993 Danl
+ ElfrOpenELA: Fixed Memory Leak bug where it was not calling
+ RtlFreeUnicodeString for pRegModuleNameU and PModuleNameU.
+
+ 29-Jul-1991 RajenS
+ Created
+
+--*/
+
+
+//#include <rpcutil.h>
+
+#include <eventp.h>
+#include <elfcfg.h>
+#include <stdio.h> // sprintf
+#include <stdlib.h>
+#include <memory.h>
+
+//
+// PROTOTYPES
+//
+NTSTATUS
+ElfpOpenELW (
+ IN EVENTLOG_HANDLE_W UNCServerName,
+ IN PRPC_UNICODE_STRING ModuleName,
+ IN PRPC_UNICODE_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle,
+ IN ULONG DesiredAccess
+ );
+
+NTSTATUS
+ElfpOpenELA (
+ IN EVENTLOG_HANDLE_A UNCServerName,
+ IN PRPC_STRING ModuleName,
+ IN PRPC_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle,
+ IN ULONG DesiredAccess
+ );
+
+VOID
+FreePUStringArray (
+ IN PUNICODE_STRING * pUStringArray,
+ IN USHORT NumStrings
+ );
+
+
+//
+// These APIs only have one interface, since they don't take or return strings
+//
+
+NTSTATUS
+ElfrNumberOfRecords(
+ IN IELF_HANDLE LogHandle,
+ OUT PULONG NumberOfRecords
+ )
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrCurrentRecord API.
+
+Arguments:
+
+ LogHandle - The context-handle for this module's call.
+
+ NumberOfRecords - Where to return the total number of records in the
+ log file.
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+
+ PLOGMODULE Module;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // This condition is TRUE iff a backup operator has opened the security
+ // log. In this case deny access, since backup operators are allowed
+ // only backup operation on the security log.
+ //
+
+ if (LogHandle->GrantedAccess & ELF_LOGFILE_BACKUP)
+ {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ //
+ // If the OldestRecordNumber is 0, that means we have an empty
+ // file, else we calculate the difference between the oldest
+ // and next record numbers
+ //
+
+ Module = FindModuleStrucFromAtom (LogHandle->Atom);
+ if (Module != NULL) {
+ *NumberOfRecords = Module->LogFile->OldestRecordNumber == 0 ? 0 :
+ Module->LogFile->CurrentRecordNumber -
+ Module->LogFile->OldestRecordNumber;
+ }
+ else {
+ Status = STATUS_INVALID_HANDLE;
+ }
+
+ return (Status);
+
+}
+
+
+NTSTATUS
+ElfrOldestRecord(
+ IN IELF_HANDLE LogHandle,
+ OUT PULONG OldestRecordNumber
+ )
+{
+ PLOGMODULE Module;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // This condition is TRUE iff a backup operator has opened the security
+ // log. In this case deny access, since backup operators are allowed
+ // only backup operation on the security log.
+ //
+
+ if (LogHandle->GrantedAccess & ELF_LOGFILE_BACKUP)
+ {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ Module = FindModuleStrucFromAtom (LogHandle->Atom);
+ if (Module != NULL) {
+ *OldestRecordNumber = Module->LogFile->OldestRecordNumber;
+ }
+ else {
+ Status = STATUS_INVALID_HANDLE;
+ }
+
+ return (Status);
+}
+
+NTSTATUS
+ElfrChangeNotify(
+ IN IELF_HANDLE LogHandle,
+ IN RPC_CLIENT_ID ClientId,
+ IN ULONG Event
+ )
+{
+
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE ProcessHandle = NULL;
+ HANDLE EventHandle;
+ PLOGMODULE Module;
+ PNOTIFIEE Notifiee;
+
+ //
+ // First make sure that this is a local call and that it is not a
+ // handle that was created for a backup log file
+ //
+
+ if (LogHandle->Flags & ELF_LOG_HANDLE_REMOTE_HANDLE ||
+ LogHandle->Flags & ELF_LOG_HANDLE_BACKUP_LOG) {
+ return(STATUS_INVALID_HANDLE);
+ }
+
+ //
+ // This condition is TRUE iff a backup operator has opened the security
+ // log. In this case deny access, since backup operators are allowed
+ // only backup operation on the security log.
+ //
+
+ if (LogHandle->GrantedAccess & ELF_LOGFILE_BACKUP)
+ {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ //
+ // First get a handle to the process using the passed in ClientId
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ NULL, // UNICODE string
+ 0, // Attributes
+ NULL, // Root directory
+ NULL); // Security descriptor
+
+ Status = NtOpenProcess(
+ &ProcessHandle,
+ PROCESS_DUP_HANDLE,
+ &ObjectAttributes,
+ (PCLIENT_ID) &ClientId);
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Now dupe the handle they passed in for the event
+ //
+
+ Status = NtDuplicateObject(
+ ProcessHandle,
+ (HANDLE) Event,
+ NtCurrentProcess(),
+ &EventHandle,
+ 0,
+ 0,
+ DUPLICATE_SAME_ACCESS);
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Create a new NOTIFIEE control block to link in
+ //
+
+ Notifiee = ElfpAllocateBuffer(sizeof(NOTIFIEE));
+ if (Notifiee) {
+
+ //
+ // Fill in the fields
+ //
+
+ Notifiee->Handle = LogHandle;
+ Notifiee->Event = EventHandle;
+
+ //
+ // Find the LOGFILE associated with this handle
+ //
+
+ Module = FindModuleStrucFromAtom (LogHandle->Atom);
+ ASSERT(Module);
+
+ //
+ // Get exclusive access to the log file. This will ensure
+ // no one else is accessing the file.
+ //
+
+ RtlAcquireResourceExclusive (
+ &Module->LogFile->Resource,
+ TRUE // Wait until available
+ );
+
+ InsertHeadList(&Module->LogFile->Notifiees,
+ &Notifiee->Next);
+
+ //
+ // Free the resource
+ //
+
+ RtlReleaseResource ( &Module->LogFile->Resource );
+ }
+ else {
+ Status = STATUS_NO_MEMORY;
+ }
+ }
+ }
+ else {
+ if (Status == STATUS_INVALID_CID) {
+ Status = STATUS_INVALID_HANDLE;
+ }
+ }
+
+ if (ProcessHandle) {
+ NtClose(ProcessHandle);
+ }
+
+ return (Status);
+}
+
+
+//
+// UNICODE APIs
+//
+
+
+
+NTSTATUS
+ElfrClearELFW (
+ IN IELF_HANDLE LogHandle,
+ IN PRPC_UNICODE_STRING BackupFileName
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrClearELFW API.
+
+Arguments:
+
+ LogHandle - The context-handle for this module's call. This must
+ not have been returned from OpenBackupEventlog, or
+ this call will fail with invalid handle.
+
+ BackupFileName - Name of the file to back up the current log file.
+ NULL implies not to back up the file.
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status=STATUS_SUCCESS;
+ PLOGMODULE Module;
+ ELF_REQUEST_RECORD Request;
+ CLEAR_PKT ClearPkt;
+ DWORD status=NO_ERROR;
+
+ //
+ // Can't clear a backup log
+ //
+
+ if (LogHandle->Flags & ELF_LOG_HANDLE_BACKUP_LOG) {
+ return(STATUS_INVALID_HANDLE);
+ }
+
+ //
+ // This condition is TRUE iff a backup operator has opened the security
+ // log. In this case deny access, since backup operators are allowed
+ // only backup operation on the security log.
+ //
+
+ if (LogHandle->GrantedAccess & ELF_LOGFILE_BACKUP)
+ {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ //
+ // Find the matching module structure
+ //
+
+ Module = FindModuleStrucFromAtom (LogHandle->Atom);
+
+ Request.Pkt.ClearPkt = &ClearPkt;
+ Request.Flags = 0;
+
+ if (Module != NULL) {
+
+ //
+ // Verify that the caller has clear access to this logfile
+ //
+
+ if (! RtlAreAllAccessesGranted (
+ LogHandle->GrantedAccess,
+ ELF_LOGFILE_CLEAR)) {
+
+ Status = STATUS_ACCESS_DENIED;
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Fill in the request packet
+ //
+
+ Request.Module = Module;
+ Request.LogFile = Module->LogFile;
+ Request.Command = ELF_COMMAND_CLEAR;
+ Request.Status = STATUS_SUCCESS;
+ Request.Pkt.ClearPkt->BackupFileName =
+ (PUNICODE_STRING)BackupFileName;
+
+ //
+ // Call the worker routine to do the operation.
+ //
+
+ ElfPerformRequest (&Request);
+
+ //
+ // Extract status of operation from the request packet
+ //
+
+ Status = Request.Status;
+
+ //
+ // If this was the Security Logfile, and the clear was
+ // successful, then generate an audit.
+ //
+ if ((NT_SUCCESS(Status) &&
+ (_wcsicmp(ELF_SECURITY_MODULE_NAME, Module->LogFile->LogModuleName->Buffer) == 0))) {
+
+ //
+ // We just cleared the security log. Now we want to add
+ // a new event to that log to indicate who did it.
+ //
+ status = RpcImpersonateClient(NULL);
+ if (status != RPC_S_OK) {
+ ElfDbgPrint(("RPC IMPERSONATION FAILED %d\n",status));
+ }
+ else {
+ ElfpGenerateLogClearedEvent(LogHandle);
+ status = RpcRevertToSelf();
+ if (status != RPC_S_OK) {
+ DbgPrint("RPC REVERT TO SELF FAILED %d\n",status);
+ }
+ }
+ }
+
+ }
+ } else {
+ Status = STATUS_INVALID_HANDLE;
+ }
+
+
+ return (Status);
+
+}
+
+
+NTSTATUS
+ElfrBackupELFW (
+ IN IELF_HANDLE LogHandle,
+ IN PRPC_UNICODE_STRING BackupFileName
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrBackupELFW API.
+
+Arguments:
+
+ LogHandle - The context-handle for this module's call.
+
+ BackupFileName - Name of the file to back up the current log file.
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status;
+ PLOGMODULE Module;
+ ELF_REQUEST_RECORD Request;
+ BACKUP_PKT BackupPkt;
+
+ if (BackupFileName->Length == 0) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ Request.Pkt.BackupPkt = &BackupPkt;
+ Request.Flags = 0;
+
+ //
+ // Find the matching module structure
+ //
+
+ Module = FindModuleStrucFromAtom (LogHandle->Atom);
+
+ if (Module != NULL) {
+
+ //
+ // Fill in the request packet
+
+ Request.Module = Module;
+ Request.LogFile = Module->LogFile;
+ Request.Command = ELF_COMMAND_BACKUP;
+ Request.Status = STATUS_SUCCESS;
+ Request.Pkt.BackupPkt->BackupFileName =
+ (PUNICODE_STRING)BackupFileName;
+
+ //
+ // Call the worker routine to do the operation.
+ //
+
+ ElfPerformRequest (&Request);
+
+ //
+ // Extract status of operation from the request packet
+ //
+
+ Status = Request.Status;
+
+ } else {
+ Status = STATUS_INVALID_HANDLE;
+ }
+
+ return (Status);
+
+}
+
+
+NTSTATUS
+ElfrCloseEL (
+ IN OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrCloseEL API.
+
+Arguments:
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // Call the rundown routine to do all the work
+ //
+
+ IELF_HANDLE_rundown(*LogHandle);
+
+ *LogHandle = NULL; // so RPC knows it's closed
+
+ return (STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+ElfrDeregisterEventSource (
+ IN OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrDeregisterEventSource API.
+
+Arguments:
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // This condition is TRUE iff a backup operator has opened the security
+ // log. In this case deny access, since backup operators are allowed
+ // only backup operation on the security log.
+ //
+
+ if ((*LogHandle)->GrantedAccess & ELF_LOGFILE_BACKUP)
+ {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ //
+ // Call the rundown routine to do all the work
+ //
+
+ IELF_HANDLE_rundown(*LogHandle);
+
+ *LogHandle = NULL; // so RPC knows it's closed
+
+ return (STATUS_SUCCESS);
+}
+
+
+
+
+NTSTATUS
+ElfrOpenBELW (
+ IN EVENTLOG_HANDLE_W UNCServerName,
+ IN PRPC_UNICODE_STRING BackupFileName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrOpenBELW API. It creates
+ a module structure $BACKUPnnn where nnn is a unique number for every backup
+ log that is opened. It then calls ElfpOpenELW to actually open the file.
+
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ BackupFileName - Name of the backup log file.
+
+ MajorVersion/MinorVersion - The version of the client.
+
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+
+--*/
+{
+
+ NTSTATUS Status;
+ UNICODE_STRING BackupStringW;
+ LPWSTR BackupModuleName;
+ PLOGMODULE pModule;
+ DWORD dwModuleNumber;
+
+//
+// Size of buffer (in bytes) required for a UNICODE string of $BACKUPnnn
+//
+
+#define SIZEOF_BACKUP_MODULE_NAME 64
+
+ UNREFERENCED_PARAMETER(UNCServerName);
+
+ //
+ // Create a unique module name by incrementing a global value
+ //
+
+ BackupModuleName = ElfpAllocateBuffer(SIZEOF_BACKUP_MODULE_NAME);
+ if (BackupModuleName == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ //
+ // Serialize read, increment of the global backup module number.
+ // Note: double-timing the log file list critical section so as to not
+ // require another critical section specifically dedicated to this
+ // operation.
+ //
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&LogFileCritSec);
+
+ dwModuleNumber = BackupModuleNumber++;
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&LogFileCritSec);
+
+ swprintf(BackupModuleName, L"$BACKUP%06d", dwModuleNumber);
+ RtlInitUnicodeString(&BackupStringW, BackupModuleName);
+
+ //
+ // Call SetupDataStruct to build the module and log data structures
+ // and actually open the file.
+ //
+ // NOTE: If this call is successful, the Unicode String Buffer for
+ // BackupStringW (otherwise known as BackupModuleName) will be attached
+ // to the LogModule structure, and should not be free'd.
+ //
+ //
+
+ Status = SetUpDataStruct(
+ BackupFileName, // Filename
+ 0, // Max size, it will use actual
+ 0, // retention period, not used for bkup
+ ELF_GUEST_ACCESS_UNRESTRICTED, // restrict guest
+ // access flag, inapplicable for bkup
+ &BackupStringW, // Module name
+ NULL, // Handle to registry, not used
+ ElfBackupLog // Log type
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ ElfpFreeBuffer(BackupModuleName);
+ return(Status);
+ }
+
+ //
+ // Call ElfOpenELW to actually open the log file and get a handle.
+ //
+ if (NT_SUCCESS(Status)) {
+
+ Status = ElfpOpenELW(NULL,
+ (PRPC_UNICODE_STRING) & BackupStringW,
+ NULL,
+ MajorVersion,
+ MinorVersion,
+ LogHandle,
+ ELF_LOGFILE_READ);
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Mark this as a handle for a backup log, so we can clean up
+ // differently when it's closed, as well as disallow clear, backup
+ // and write operations.
+ //
+
+ (*LogHandle)->Flags |= ELF_LOG_HANDLE_BACKUP_LOG;
+
+ }
+ else {
+ //
+ // If we couldn't open the log file, then we need to tear down
+ // the DataStruct we set up with SetUpDataStruct.
+ //
+ pModule = GetModuleStruc ((PUNICODE_STRING)BackupFileName);
+
+ Status = ElfpCloseLogFile (pModule->LogFile, ELF_LOG_CLOSE_BACKUP);
+
+ UnlinkLogModule(pModule);
+ pModule->LogFile->RefCount--;
+ if (pModule->LogFile->RefCount == 0) {
+ UnlinkLogFile(pModule->LogFile); // Unlink the structure
+ RtlDeleteResource ( &pModule->LogFile->Resource );
+ RtlDeleteSecurityObject(&pModule->LogFile->Sd);
+ ElfpFreeBuffer (pModule->LogFile->LogFileName);
+ ElfpFreeBuffer (pModule->LogFile);
+ }
+ ElfpFreeBuffer(pModule->ModuleName);
+ ElfpFreeBuffer(pModule);
+
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+ElfrRegisterEventSourceW (
+ IN EVENTLOG_HANDLE_W UNCServerName,
+ IN PRPC_UNICODE_STRING ModuleName,
+ IN PRPC_UNICODE_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrRegisterEventSourceW API.
+ This routine allocates a structure for the context handle, finds
+ the matching module name and fills in the data. It returns the
+ pointer to the handle structure.
+
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ ModuleName - Name of the module that is making this call.
+
+ RegModuleName - Not used.
+
+ MajorVersion/MinorVersion - The version of the client.
+
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+Note:
+
+ For now, just call ElfpOpenELW.
+
+
+--*/
+{
+ PLOGMODULE Module;
+
+ Module = GetModuleStruc ((PUNICODE_STRING)ModuleName);
+
+ return(ElfpOpenELW(UNCServerName, ModuleName, RegModuleName,
+ MajorVersion, MinorVersion, LogHandle, ELF_LOGFILE_WRITE));
+}
+
+NTSTATUS
+ElfrOpenELW (
+ IN EVENTLOG_HANDLE_W UNCServerName,
+ IN PRPC_UNICODE_STRING ModuleName,
+ IN PRPC_UNICODE_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrOpenELW API.
+ This routine allocates a structure for the context handle, finds
+ the matching module name and fills in the data. It returns the
+ pointer to the handle structure.
+
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ ModuleName - Name of the module that is making this call.
+
+ RegModuleName - Not used.
+
+ MajorVersion/MinorVersion - The version of the client.
+
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+
+--*/
+{
+ return( ElfpOpenELW (
+ UNCServerName,
+ ModuleName,
+ RegModuleName,
+ MajorVersion,
+ MinorVersion,
+ LogHandle,
+ ELF_LOGFILE_READ));
+}
+
+NTSTATUS
+ElfpOpenELW (
+ IN EVENTLOG_HANDLE_W UNCServerName,
+ IN PRPC_UNICODE_STRING ModuleName,
+ IN PRPC_UNICODE_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle,
+ IN ULONG DesiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ Looks alot like ElfrOpenELW but also gets passed a DesiredAccess.
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ ModuleName - Name of the module that is making this call.
+
+ RegModuleName - Not used.
+
+ MajorVersion/MinorVersion - The version of the client.
+
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+ DesiredAccess - Indicates the access desired for this logfile.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLOGMODULE Module;
+ IELF_HANDLE LogIHandle;
+ BOOL ForSecurityLog = FALSE;
+
+ //
+ // Allocate a new structure for the context handle
+ //
+
+ LogIHandle = (IELF_HANDLE) ElfpAllocateBuffer (
+ sizeof (*LogIHandle)
+ + ModuleName->Length
+ + sizeof (WCHAR)
+ );
+
+ if (LogIHandle) {
+
+ //
+ // Find the module structure in order to pull out the Atom.
+ //
+ // GetModuleStruc *always* succeeds! (returns default if module
+ // not found).
+ //
+
+ Module = GetModuleStruc ((PUNICODE_STRING)ModuleName);
+
+ //
+ // Validate the caller has appropriate access to this logfile.
+ // If this is the security log, then check privilege instead.
+ //
+ if (_wcsicmp(ELF_SECURITY_MODULE_NAME, Module->LogFile->LogModuleName->Buffer) == 0) {
+ ForSecurityLog = TRUE;
+ }
+ Status = ElfpAccessCheckAndAudit(
+ L"EventLog", // SubSystemName
+ L"LogFile", // ObjectTypeName
+ Module->ModuleName, // ObjectName
+ LogIHandle, // Context handle - required?
+ Module->LogFile->Sd, // Security Descriptor
+ DesiredAccess, // Requested Access
+ NULL, // GENERIC_MAPPING
+ ForSecurityLog // Indicates the check is for security log
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ LogIHandle->Atom = Module->ModuleAtom;
+
+ LogIHandle->NameLength = ModuleName->Length + sizeof(WCHAR);
+ RtlMoveMemory( LogIHandle->Name,
+ ModuleName->Buffer,
+ ModuleName->Length
+ );
+
+ LogIHandle->Name[ModuleName->Length / sizeof(WCHAR)] = L'\0';
+
+ LogIHandle->MajorVersion = MajorVersion; // Store the version
+ LogIHandle->MinorVersion = MinorVersion; // of the client
+
+ //
+ // Initialize seek positions and flags to zero.
+ //
+
+ LogIHandle->SeekRecordPos = 0;
+ LogIHandle->SeekBytePos = 0;
+ LogIHandle->Flags = 0;
+
+ //
+ // Link in this structure to the list of context handles
+ //
+
+ LogIHandle->Signature = ELF_CONTEXTHANDLE_SIGN; // DEBUG
+ LinkContextHandle (LogIHandle);
+
+ *LogHandle = LogIHandle; // Set return handle
+ Status = STATUS_SUCCESS; // Set return status
+ }
+ else {
+ ElfpFreeBuffer(LogIHandle);
+ }
+
+ } else {
+
+ Status = STATUS_NO_MEMORY;
+ }
+
+ return (Status);
+
+ UNREFERENCED_PARAMETER(UNCServerName);
+ UNREFERENCED_PARAMETER(RegModuleName);
+}
+
+
+
+NTSTATUS
+w_ElfrReadEL (
+ IN ULONG Flags, // ANSI or UNICODE
+ IN IELF_HANDLE LogHandle,
+ IN ULONG ReadFlags,
+ IN ULONG RecordNumber,
+ IN ULONG NumberOfBytesToRead,
+ IN PBYTE Buffer,
+ OUT PULONG NumberOfBytesRead,
+ OUT PULONG MinNumberOfBytesNeeded
+ )
+
+/*++
+
+Routine Description:
+
+ This is the worker for the ElfrReadEL APIs.
+
+Arguments:
+
+ Same as ElfrReadELW API except that Flags contains an indication
+ of whether this is ANSI or UNICODE.
+
+Return Value:
+
+ Same as the main API.
+
+NOTES:
+
+ We assume that the client-side has validated the flags to ensure that
+ only one type of each bit is set. No checking is done at the server end.
+
+
+--*/
+{
+ NTSTATUS Status;
+ PLOGMODULE Module;
+ ELF_REQUEST_RECORD Request;
+ READ_PKT ReadPkt;
+
+ //
+ // The ELF_HANDLE_INVALID_FOR_READ flag bit would be set if the
+ // file changed underneath this handle.
+ //
+
+ if (LogHandle->Flags & ELF_LOG_HANDLE_INVALID_FOR_READ) {
+ return(STATUS_EVENTLOG_FILE_CHANGED);
+ }
+
+ //
+ // This condition is TRUE iff a backup operator has opened the security
+ // log. In this case deny access, since backup operators are allowed
+ // only backup operation on the security log.
+ //
+
+ if (LogHandle->GrantedAccess & ELF_LOGFILE_BACKUP)
+ {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ Request.Pkt.ReadPkt = &ReadPkt; // Set up read packet in request packet
+
+ //
+ // Find the matching module structure
+ //
+ Module = FindModuleStrucFromAtom (LogHandle->Atom);
+
+ //
+ // Only continue if the module was found
+ //
+
+ if (Module != NULL) {
+
+ //
+ // Fill in the request packet
+ //
+ Request.Module = Module;
+ Request.Flags = 0;
+ Request.LogFile = Module->LogFile;
+ Request.Command = ELF_COMMAND_READ;
+ Request.Status = STATUS_SUCCESS;
+ Request.Pkt.ReadPkt->MinimumBytesNeeded = *MinNumberOfBytesNeeded;
+ Request.Pkt.ReadPkt->BufferSize = NumberOfBytesToRead;
+ Request.Pkt.ReadPkt->Buffer = (PVOID)Buffer;
+ Request.Pkt.ReadPkt->ReadFlags = ReadFlags;
+ Request.Pkt.ReadPkt->RecordNumber = RecordNumber;
+ Request.Pkt.ReadPkt->LastSeekPos = LogHandle->SeekBytePos;
+ Request.Pkt.ReadPkt->LastSeekRecord = LogHandle->SeekRecordPos;
+ Request.Pkt.ReadPkt->Flags = Flags; // Indicate UNICODE or ANSI
+
+ //
+ // Pass along whether the last read was in a forward or backward
+ // direction (affects how we treat being at EOF). Then reset the
+ // bit in the handle depending on what this read is.
+ //
+
+ if (LogHandle->Flags & ELF_LOG_HANDLE_LAST_READ_FORWARD) {
+ Request.Pkt.ReadPkt->Flags |= ELF_LAST_READ_FORWARD;
+ }
+
+ if (ReadFlags & EVENTLOG_FORWARDS_READ) {
+ LogHandle->Flags |= ELF_LOG_HANDLE_LAST_READ_FORWARD;
+ }
+ else {
+ LogHandle->Flags &= ~(ELF_LOG_HANDLE_LAST_READ_FORWARD);
+ }
+
+
+ //
+ // Perform the operation
+ //
+ ElfPerformRequest( &Request );
+
+ //
+ // Update current seek positions
+ //
+ LogHandle->SeekRecordPos = Request.Pkt.ReadPkt->LastSeekRecord;
+ LogHandle->SeekBytePos = Request.Pkt.ReadPkt->LastSeekPos;
+
+ //
+ // Set up return values
+ //
+ *NumberOfBytesRead = Request.Pkt.ReadPkt->BytesRead;
+ *MinNumberOfBytesNeeded = Request.Pkt.ReadPkt->MinimumBytesNeeded;
+
+ Status = Request.Status;
+
+ } else {
+ Status = STATUS_INVALID_HANDLE;
+ //
+ // Set the NumberOfBytesNeeded to zero since there are no bytes to
+ // transfer.
+ //
+ *NumberOfBytesRead = 0;
+ *MinNumberOfBytesNeeded = 0;
+ }
+
+ return (Status);
+}
+
+
+NTSTATUS
+ElfrReadELW (
+ IN IELF_HANDLE LogHandle,
+ IN ULONG ReadFlags,
+ IN ULONG RecordNumber,
+ IN ULONG NumberOfBytesToRead,
+ IN PBYTE Buffer,
+ OUT PULONG NumberOfBytesRead,
+ OUT PULONG MinNumberOfBytesNeeded
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrReadELW API.
+
+Arguments:
+
+
+
+Return Value:
+
+ Returns an NTSTATUS code, NumberOfBytesRead if the read was successful
+ and MinNumberOfBytesNeeded if the buffer was not big enough.
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // Call the worker with the UNICODE flag
+ //
+
+ return(w_ElfrReadEL (
+ ELF_IREAD_UNICODE,
+ LogHandle,
+ ReadFlags,
+ RecordNumber,
+ NumberOfBytesToRead,
+ Buffer,
+ NumberOfBytesRead,
+ MinNumberOfBytesNeeded
+ ));
+
+}
+
+
+
+NTSTATUS
+ElfrReportEventW (
+ IN IELF_HANDLE LogHandle,
+ IN ULONG EventTime,
+ IN USHORT EventType,
+ IN USHORT EventCategory OPTIONAL,
+ IN ULONG EventID,
+ IN USHORT NumStrings,
+ IN ULONG DataSize,
+ IN PRPC_UNICODE_STRING ComputerName,
+ IN PRPC_SID UserSid,
+ IN PRPC_UNICODE_STRING *Strings,
+ IN PBYTE Data,
+ IN USHORT Flags,
+ IN OUT PULONG RecordNumber OPTIONAL,
+ IN OUT PULONG TimeWritten OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrReportEventW API.
+
+Arguments:
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status;
+ PLOGMODULE Module;
+ ELF_REQUEST_RECORD Request;
+ WRITE_PKT WritePkt;
+
+ ULONG RecordLength;
+ ULONG StringOffset, DataOffset;
+ ULONG StringsSize;
+ USHORT i;
+ PVOID EventBuffer;
+ PEVENTLOGRECORD EventLogRecord;
+ PWSTR ReplaceStrings, SrcString;
+ PBYTE BinaryData;
+ PUNICODE_STRING UComputerName;
+ PWSTR UModuleName;
+ ULONG PadSize;
+ ULONG UserSidLength = 0; // Init to zero
+ ULONG UserSidOffset;
+ ULONG ModuleNameLen, ComputerNameLen; // Length in bytes
+ ULONG zero = 0; // For pad bytes
+ LARGE_INTEGER Time;
+ ULONG LogTimeWritten;
+
+ //
+ // These are for Security Auditing to use for paired events. This will
+ // not be implemented in Product 1
+ //
+
+ UNREFERENCED_PARAMETER(RecordNumber);
+ UNREFERENCED_PARAMETER(TimeWritten);
+
+ //
+ // This condition is TRUE iff a backup operator has opened the security
+ // log. In this case deny access, since backup operators are allowed
+ // only backup operation on the security log.
+ //
+
+ if (LogHandle->GrantedAccess & ELF_LOGFILE_BACKUP)
+ {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ //
+ // Make sure the SID passed in is valid
+ //
+
+ if (ARGUMENT_PRESENT(UserSid)) {
+ if (!IsValidSid(UserSid)) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+ }
+
+ //
+ // Make sure strings are null terminated on this side of the RPC call
+ // (RPC doesn't pass the null terminator)
+
+ for (i = 0; i < NumStrings; i++ ) {
+ if (Strings[i]) {
+ if (Strings[i]->MaximumLength >= Strings[i]->Length +
+ sizeof(WCHAR)) {
+ Strings[i]->Buffer[Strings[i]->Length / sizeof(WCHAR)]
+ = L'\0';
+ }
+ }
+
+ }
+
+ //
+ // Can't write to a backup log
+ //
+
+ if (LogHandle->Flags & ELF_LOG_HANDLE_BACKUP_LOG) {
+ return(STATUS_INVALID_HANDLE);
+ }
+
+ //
+ // Make sure they didn't pass in a null pointer for the data, but tell
+ // me there was something there (I still think RPC should protect me from
+ // this!)
+ //
+
+ if (!Data && DataSize != 0) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+
+ UComputerName = (PUNICODE_STRING)ComputerName;
+ UModuleName = LogHandle->Name;
+
+ Request.Pkt.WritePkt = &WritePkt; // Set up write packet in request packet
+ Request.Flags = 0;
+
+ //
+ // Find the matching module structure
+ //
+
+ Module = FindModuleStrucFromAtom (LogHandle->Atom);
+
+ if (Module != NULL) {
+
+ //
+ // Generate any additional information needed in the record.
+ //
+ // Info that we have Info to generate
+ // ----------------- ----------------
+ // Modulename UserSidLength
+ // EventType Length
+ // EventID StringOffset
+ // NumStrings DataOffset
+ // Strings PadBytes
+ // DataLength LogTimeWritten
+ // Data
+ // UserSidOffset
+ // UserSid
+ // ComputerName
+ // TimeGenerated
+ //
+
+ // LogTimeWritten
+ // We need to generate a time when the log is written. This
+ // gets written in the log so that we can use it to test the
+ // retention period when wrapping the file.
+ //
+
+ NtQuerySystemTime(&Time);
+ RtlTimeToSecondsSince1970(
+ &Time,
+ &LogTimeWritten
+ );
+
+
+ //
+ // USERSIDLENTGH
+ //
+
+ if (UserSid) {
+ UserSidLength = RtlLengthSid ((PSID)UserSid);
+ }
+
+ //
+ // USERSIDOFFSET
+ //
+ // Extract the lengths from the STRING structure, and take care of
+ // the trailing NULLs.
+ //
+
+ ModuleNameLen = (wcslen(UModuleName) + 1)
+ * sizeof (WCHAR);
+ ComputerNameLen = UComputerName->Length + sizeof(WCHAR);
+
+ UserSidOffset = sizeof(EVENTLOGRECORD)
+ + ModuleNameLen
+ + ComputerNameLen;
+
+ //
+ // STRING OFFSET:
+ //
+
+ StringOffset = UserSidOffset + UserSidLength;
+
+
+ //
+ // Calculate the length of strings so that we can see how
+ // much space is needed for that.
+ //
+
+ StringsSize = 0;
+ for (i=0; i<NumStrings; i++) {
+ if (!Strings[i] || Strings[i]->MaximumLength == 0) {
+ StringsSize += sizeof(WCHAR);
+ }
+ else {
+ StringsSize += Strings[i]->Length + sizeof(WCHAR);
+ }
+ }
+
+ //
+ // DATA OFFSET:
+ //
+
+ DataOffset = StringOffset + StringsSize;
+
+ //
+ // Determine how big a buffer is needed for the eventlog record.
+ //
+
+ RecordLength = DataOffset
+ + DataSize
+ + sizeof(RecordLength); // Size excluding pad bytes
+
+ //
+ // Determine how many pad bytes are needed to align to a DWORD
+ // boundary.
+ //
+
+ PadSize = sizeof(ULONG) - (RecordLength % sizeof(ULONG));
+
+ RecordLength += PadSize; // True size needed
+
+ //
+ // Allocate the buffer for the Eventlog record
+ //
+
+ EventBuffer = ElfpAllocateBuffer(RecordLength);
+
+ if (EventBuffer != NULL) {
+
+ //
+ // Fill up the event record
+ //
+
+ EventLogRecord = (PEVENTLOGRECORD)EventBuffer;
+
+ EventLogRecord->Length = RecordLength;
+ EventLogRecord->TimeGenerated = EventTime;
+ EventLogRecord->Reserved = ELF_LOG_FILE_SIGNATURE;
+ EventLogRecord->TimeWritten = LogTimeWritten;
+ EventLogRecord->EventID = EventID;
+ EventLogRecord->EventType = EventType;
+ EventLogRecord->EventCategory = EventCategory;
+ EventLogRecord->ReservedFlags = Flags;
+ EventLogRecord->ClosingRecordNumber = 0;
+ EventLogRecord->NumStrings = NumStrings;
+ EventLogRecord->StringOffset = StringOffset;
+ EventLogRecord->DataLength = DataSize;
+ EventLogRecord->DataOffset = DataOffset;
+ EventLogRecord->UserSidLength = UserSidLength;
+ EventLogRecord->UserSidOffset = UserSidOffset;
+
+ //
+ // Fill in the variable-length fields
+ //
+
+ //
+ // STRINGS
+ //
+
+ ReplaceStrings = (PWSTR) ( (ULONG)EventLogRecord
+ + (ULONG)StringOffset
+ );
+
+ for (i=0; i < NumStrings; i++) {
+ if (!Strings[i] || Strings[i]->MaximumLength == 0) {
+ *ReplaceStrings = L'\0';
+ ReplaceStrings++;
+ }
+ else {
+ SrcString = (PWSTR)Strings[i]->Buffer;
+ RtlMoveMemory(ReplaceStrings, SrcString,
+ Strings[i]->Length);
+ ReplaceStrings[Strings[i]->Length / sizeof(WCHAR)] =
+ L'\0';
+ ReplaceStrings = (PWSTR)((PBYTE) ReplaceStrings
+ + Strings[i]->Length + sizeof(WCHAR));
+ }
+ }
+
+ //
+ // MODULENAME
+ //
+
+ BinaryData = (PBYTE) EventLogRecord + sizeof(EVENTLOGRECORD);
+ RtlMoveMemory (BinaryData,
+ UModuleName,
+ ModuleNameLen);
+
+ //
+ // COMPUTERNAME
+ //
+
+ ReplaceStrings = (LPWSTR) (BinaryData + ModuleNameLen);
+
+ RtlMoveMemory (ReplaceStrings,
+ UComputerName->Buffer,
+ UComputerName->Length);
+
+ ReplaceStrings[UComputerName->Length / sizeof(WCHAR)] = L'\0';
+
+ //
+ // USERSID
+ //
+
+ BinaryData = (PBYTE) ReplaceStrings + ComputerNameLen;
+
+ ASSERT (BinaryData
+ == ((PBYTE) EventLogRecord) + UserSidOffset);
+
+ RtlMoveMemory (BinaryData,
+ UserSid,
+ UserSidLength);
+
+ //
+ // BINARY DATA
+ //
+
+ BinaryData = (PBYTE) ((ULONG)EventLogRecord + DataOffset);
+ if (Data) {
+ RtlMoveMemory (BinaryData, Data, DataSize);
+ }
+
+ //
+ // PAD - Fill with zeros
+ //
+
+ BinaryData = (PBYTE) ((ULONG)BinaryData + DataSize);
+ RtlMoveMemory (BinaryData, &zero, PadSize);
+
+ //
+ // LENGTH at end of record
+ //
+
+ BinaryData = (PBYTE)((ULONG)BinaryData + PadSize);// Point after pad bytes
+ ((PULONG)BinaryData)[0] = RecordLength;
+
+ //
+ // Make sure we are in the right place
+ //
+
+ ASSERT ((ULONG)BinaryData
+ == (RecordLength + (ULONG)EventLogRecord) - sizeof(ULONG));
+
+ //
+ // Set up request packet.
+ // Link event log record into the request structure.
+ //
+
+ Request.Module = Module;
+ Request.LogFile = Request.Module->LogFile;
+ Request.Command = ELF_COMMAND_WRITE;
+ Request.Pkt.WritePkt->Buffer = (PVOID)EventBuffer;
+ Request.Pkt.WritePkt->Datasize = RecordLength;
+
+ //
+ // Perform the operation
+ //
+
+ ElfPerformRequest( &Request );
+
+ //
+ // Free up the buffer
+ //
+
+ ElfpFreeBuffer(EventBuffer);
+
+ Status = Request.Status; // Set status of WRITE
+
+ } else {
+ Status = STATUS_NO_MEMORY;
+ }
+
+ } else {
+ Status = STATUS_INVALID_HANDLE;
+ }
+
+ return (Status);
+}
+
+
+
+//
+// ANSI APIs
+//
+
+NTSTATUS
+ElfrClearELFA (
+ IN IELF_HANDLE LogHandle,
+ IN PRPC_STRING BackupFileName
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrClearELFA API.
+
+Arguments:
+
+ LogHandle - The context-handle for this module's call.
+
+ BackupFileName - Name of the file to back up the current log file.
+ NULL implies not to back up the file.
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING BackupFileNameU;
+
+ //
+ // Convert the BackupFileName to a UNICODE STRING and call the
+ // UNICODE API to do the work.
+ //
+ Status = RtlAnsiStringToUnicodeString (
+ (PUNICODE_STRING)&BackupFileNameU,
+ (PANSI_STRING)BackupFileName,
+ TRUE
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = ElfrClearELFW (
+ LogHandle,
+ (PRPC_UNICODE_STRING)&BackupFileNameU
+ );
+
+ RtlFreeUnicodeString (&BackupFileNameU);
+ }
+
+ return (Status);
+
+}
+
+
+
+NTSTATUS
+ElfrBackupELFA (
+ IN IELF_HANDLE LogHandle,
+ IN PRPC_STRING BackupFileName
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrBackupELFA API.
+
+Arguments:
+
+ LogHandle - The context-handle for this module's call.
+
+ BackupFileName - Name of the file to back up the current log file.
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING BackupFileNameU;
+
+ //
+ // Convert the BackupFileName to a UNICODE STRING and call the
+ // UNICODE API to do the work.
+ //
+ Status = RtlAnsiStringToUnicodeString (
+ (PUNICODE_STRING)&BackupFileNameU,
+ (PANSI_STRING)BackupFileName,
+ TRUE
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = ElfrBackupELFW (
+ LogHandle,
+ (PRPC_UNICODE_STRING)&BackupFileNameU
+ );
+
+ RtlFreeUnicodeString (&BackupFileNameU);
+ }
+
+ return (Status);
+
+}
+
+
+NTSTATUS
+ElfrRegisterEventSourceA (
+ IN EVENTLOG_HANDLE_A UNCServerName,
+ IN PRPC_STRING ModuleName,
+ IN PRPC_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrRegisterEventSourceA API.
+ This routine allocates a structure for the context handle, finds
+ the matching module name and fills in the data. It returns the
+ pointer to the handle structure.
+
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ ModuleName - Name of the module that is making this call.
+
+ RegModuleName - Not used.
+
+ MajorVersion/MinorVersion - The version of the client.
+
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+Note:
+
+ For now, just call ElfrOpenELA.
+
+
+--*/
+{
+
+ NTSTATUS Status;
+ PLOGMODULE Module;
+ UNICODE_STRING ModuleNameU;
+
+ Status = RtlAnsiStringToUnicodeString (
+ (PUNICODE_STRING) &ModuleNameU,
+ (PANSI_STRING) ModuleName,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ Module = GetModuleStruc ((PUNICODE_STRING) & ModuleNameU);
+
+ RtlFreeUnicodeString (& ModuleNameU);
+
+ return(ElfpOpenELA(UNCServerName, ModuleName, RegModuleName,
+ MajorVersion, MinorVersion, LogHandle, ELF_LOGFILE_WRITE));
+
+}
+
+NTSTATUS
+ElfrOpenELA (
+ IN EVENTLOG_HANDLE_A UNCServerName,
+ IN PRPC_STRING ModuleName,
+ IN PRPC_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrOpenEL API.
+ This routine allocates a structure for the context handle, finds
+ the matching module name and fills in the data. It returns the
+ pointer to the handle structure.
+
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ ModuleName - Name of the module that is making this call.
+
+ RegModuleName - Name of module to use to determine the log file.
+
+ MajorVersion/MinorVersion - The version of the client.
+
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+
+--*/
+{
+ return (ElfpOpenELA (
+ UNCServerName,
+ ModuleName,
+ RegModuleName,
+ MajorVersion,
+ MinorVersion,
+ LogHandle,
+ ELF_LOGFILE_READ));
+}
+
+NTSTATUS
+ElfpOpenELA (
+ IN EVENTLOG_HANDLE_A UNCServerName,
+ IN PRPC_STRING ModuleName,
+ IN PRPC_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle,
+ IN ULONG DesiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ Looks alot loke ElfrOpenELA, only this also takes a DesiredAccess parameter.
+
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ ModuleName - Name of the module that is making this call.
+
+ RegModuleName - Name of module to use to determine the log file.
+
+ MajorVersion/MinorVersion - The version of the client.
+
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+--*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING ModuleNameU;
+
+ //
+ // Convert the ModuleName and RegModulename to UNICODE STRINGs and call
+ // the UNICODE API to do the work.
+ //
+
+ Status = RtlAnsiStringToUnicodeString (
+ (PUNICODE_STRING)&ModuleNameU,
+ (PANSI_STRING)ModuleName,
+ TRUE
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // We *KNOW* that the UNCServerName is not used
+ // by ElfpOpenELW so we save ourselves some work
+ // and just pass in a NULL.
+ //
+ Status = ElfpOpenELW (
+ (EVENTLOG_HANDLE_W) NULL,
+ (PRPC_UNICODE_STRING)&ModuleNameU,
+ NULL,
+ MajorVersion,
+ MinorVersion,
+ LogHandle,
+ DesiredAccess
+ );
+
+ RtlFreeUnicodeString(&ModuleNameU);
+ }
+
+ return (Status);
+ UNREFERENCED_PARAMETER(UNCServerName);
+}
+
+
+
+
+
+NTSTATUS
+ElfrOpenBELA (
+ IN EVENTLOG_HANDLE_A UNCServerName,
+ IN PRPC_STRING FileName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrOpenBEL API.
+ This routine allocates a structure for the context handle, finds
+ the matching module name and fills in the data. It returns the
+ pointer to the handle structure.
+
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ FileName - Filename of the logfile
+
+ MajorVersion/MinorVersion - The version of the client.
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+
+--*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING FileNameU;
+
+ //
+ // Convert the FileName to a UNICODE STRINGs and call
+ // the UNICODE API to do the work.
+ //
+
+ Status = RtlAnsiStringToUnicodeString (
+ (PUNICODE_STRING) &FileNameU,
+ (PANSI_STRING) FileName,
+ TRUE
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // We *KNOW* that the UNCServerName is not used
+ // by ElfrOpenELW so we save ourselves some work
+ // and just pass in a NULL.
+ //
+
+ Status = ElfrOpenBELW (
+ (EVENTLOG_HANDLE_W) NULL,
+ (PRPC_UNICODE_STRING)&FileNameU,
+ MajorVersion,
+ MinorVersion,
+ LogHandle
+ );
+
+ RtlFreeUnicodeString(&FileNameU);
+ }
+
+ return (Status);
+ UNREFERENCED_PARAMETER(UNCServerName);
+
+}
+
+
+
+NTSTATUS
+ElfrReadELA (
+ IN IELF_HANDLE LogHandle,
+ IN ULONG ReadFlags,
+ IN ULONG RecordNumber,
+ IN ULONG NumberOfBytesToRead,
+ IN PBYTE Buffer,
+ OUT PULONG NumberOfBytesRead,
+ OUT PULONG MinNumberOfBytesNeeded
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrReadEL API.
+
+Arguments:
+
+
+
+Return Value:
+
+ Returns an NTSTATUS code, NumberOfBytesRead if the read was successful
+ and MinNumberOfBytesNeeded if the buffer was not big enough.
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // Call the worker with the UNICODE flag
+ //
+
+ return(w_ElfrReadEL (
+ ELF_IREAD_ANSI,
+ LogHandle,
+ ReadFlags,
+ RecordNumber,
+ NumberOfBytesToRead,
+ Buffer,
+ NumberOfBytesRead,
+ MinNumberOfBytesNeeded
+ ));
+}
+
+
+
+
+NTSTATUS
+ConvertStringArrayToUnicode (
+ PUNICODE_STRING *pUStringArray,
+ PANSI_STRING *Strings,
+ USHORT NumStrings
+ )
+/*++
+
+Routine Description:
+
+ This routine takes an array of STRINGs and generates an array of
+ PUNICODE_STRINGs. The destination array has already been allocated
+ by the caller, but the structures for the UNICODE_STRINGs will need
+ to be allocated by this routine.
+
+Arguments:
+
+ pUStringArray - Array of PUNICODE_STRINGs.
+ Strings - Array of PANSI_STRINGs.
+ NumStrings - Number of elements in the arrays.
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ USHORT i;
+
+ //
+ // For each string passed in, allocate a UNICODE_STRING buffer
+ // and set it to the UNICODE equivalent of the string passed in.
+ //
+ for (i=0; i<NumStrings; i++) {
+
+ if (Strings[i]) {
+
+ pUStringArray[i] = ElfpAllocateBuffer (sizeof (UNICODE_STRING));
+
+ if (pUStringArray[i]) {
+
+ Status = RtlAnsiStringToUnicodeString (
+ pUStringArray[i],
+ (PANSI_STRING)Strings[i],
+ TRUE
+ );
+ } else {
+ Status = STATUS_NO_MEMORY;
+ }
+ } else {
+ pUStringArray[i] = NULL;
+ }
+ if (!NT_SUCCESS(Status))
+ break; // Jump out of loop and return status
+ }
+
+ //
+ // Free any allocations on failure.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+ FreePUStringArray(pUStringArray, (USHORT)(i + 1));
+ }
+
+ return (Status);
+}
+
+
+
+VOID
+FreePUStringArray (
+ PUNICODE_STRING *pUStringArray,
+ USHORT NumStrings
+ )
+/*++
+
+Routine Description:
+
+ This routine takes the PUNICODE_STRING array that was filled in by
+ ConvertStringArrayToUnicode and frees the buffer portion of
+ each unicode string and then the UNICODE structure itseld. It handles
+ the case where the array may not have been filled completely due
+ to insufficient memory.
+
+Arguments:
+
+ pUStringArray - Array of PUNICODE_STRINGs.
+ NumStrings - Number of elements in the array.
+
+Return Value:
+
+ NONE.
+
+--*/
+{
+ USHORT i;
+
+ for (i=0; i<NumStrings; i++) {
+
+ if (pUStringArray[i]) {
+
+ if (pUStringArray[i]->Buffer) {
+ RtlFreeUnicodeString (pUStringArray[i]); // Free the string buffer
+
+ ElfpFreeBuffer (pUStringArray[i]); // Free the structure itself
+ }
+ }
+ }
+}
+
+
+
+NTSTATUS
+ElfrReportEventA (
+ IN IELF_HANDLE LogHandle,
+ IN ULONG Time,
+ IN USHORT EventType,
+ IN USHORT EventCategory OPTIONAL,
+ IN ULONG EventID,
+ IN USHORT NumStrings,
+ IN ULONG DataSize,
+ IN PRPC_STRING ComputerName,
+ IN PRPC_SID UserSid,
+ IN PRPC_STRING *Strings,
+ IN PBYTE Data,
+ IN USHORT Flags,
+ IN OUT PULONG RecordNumber OPTIONAL,
+ IN OUT PULONG TimeWritten OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrReportEventA API.
+
+Arguments:
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING ComputerNameU;
+ PUNICODE_STRING *pUStringArray;
+
+ //
+ // Convert the ComputerName to a UNICODE STRING and call the
+ // UNICODE API.
+ //
+
+ Status = RtlAnsiStringToUnicodeString (
+ (PUNICODE_STRING)&ComputerNameU,
+ (PANSI_STRING)ComputerName,
+ TRUE
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ if (NumStrings) {
+
+ pUStringArray = ElfpAllocateBuffer (
+ NumStrings * sizeof (PUNICODE_STRING)
+ );
+
+ //
+ // Convert the array of STRINGs to an array of UNICODE-STRINGs
+ // before calling the unicode API.
+ // We can just use the array of Strings passed in since we
+ // don't need to use it anywhere else.
+ //
+ Status = ConvertStringArrayToUnicode (
+ pUStringArray,
+ (PANSI_STRING *)Strings,
+ NumStrings
+ );
+
+ } else {
+ pUStringArray = NULL; // No strings passed in
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = ElfrReportEventW (
+ LogHandle,
+ Time,
+ EventType,
+ EventCategory,
+ EventID,
+ NumStrings,
+ DataSize,
+ (PRPC_UNICODE_STRING)&ComputerNameU,
+ UserSid,
+ (PRPC_UNICODE_STRING*)pUStringArray,
+ Data,
+ Flags, // Flags - paired event
+ RecordNumber,// RecordNumber | support. not in
+ TimeWritten // TimeWritten _ product 1
+ );
+
+ FreePUStringArray (pUStringArray, NumStrings);
+
+ }
+
+ RtlFreeUnicodeString (&ComputerNameU);
+ }
+ if (pUStringArray)
+ ElfpFreeBuffer (pUStringArray);
+
+ return (Status);
+}
diff --git a/private/eventlog/server/elfcfg.h b/private/eventlog/server/elfcfg.h
new file mode 100644
index 000000000..da8adae47
--- /dev/null
+++ b/private/eventlog/server/elfcfg.h
@@ -0,0 +1,66 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ elfcfg.h
+
+Abstract:
+
+ This file contains default settings for the eventlog service.
+
+Author:
+
+ Rajen Shah (rajens) 16-Aug-1991
+
+Revision History:
+
+--*/
+
+#ifndef _EVENTDEFAULTS_
+#define _EVENTDEFAULTS_
+
+//
+// Default for the Application log file
+//
+//
+
+#define ELF_DEFAULT_MODULE_NAME ELF_APPLICATION_MODULE_NAME
+#define ELF_APPLICATION_DEFAULT_LOG_FILE L"\\SystemRoot\\system32\\config\\appevent.evt"
+#define ELF_SYSTEM_DEFAULT_LOG_FILE L"\\SystemRoot\\system32\\config\\sysevent.evt"
+#define ELF_SECURITY_DEFAULT_LOG_FILE L"\\SystemRoot\\system32\\config\\secevent.evt"
+#define ELF_DEFAULT_MAX_FILE_SIZE 512*1024
+#define ELF_DEFAULT_RETENTION_PERIOD 1*24*3600
+
+#define ELF_GUEST_ACCESS_UNRESTRICTED 0
+#define ELF_GUEST_ACCESS_RESTRICTED 1
+
+//
+// Maximum size for the buffer that will read the key values from the
+// registry.
+//
+
+#define ELF_MAX_REG_KEY_INFO_SIZE 200
+
+//
+// String defines for the pre-defined nodes in the registry
+// These are used to get to the appropriate nodes.
+//
+
+#define REG_EVENTLOG_NODE_PATH \
+ L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Eventlog"
+
+//
+// String defines for the values for each of the configured pieces of
+// information in the eventlog\logfiles node. These exist per logfile.
+//
+//
+
+#define VALUE_FILENAME L"File"
+#define VALUE_MAXSIZE L"Maxsize"
+#define VALUE_RETENTION L"Retention"
+#define VALUE_RESTRICT_GUEST_ACCESS L"RestrictGuestAccess"
+#define VALUE_DEBUG L"DBFlags"
+
+#endif // ifndef _EVENTDEFAULTS_
diff --git a/private/eventlog/server/elfdata.c b/private/eventlog/server/elfdata.c
new file mode 100644
index 000000000..197d577a1
--- /dev/null
+++ b/private/eventlog/server/elfdata.c
@@ -0,0 +1,218 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ DATA.C
+
+Abstract:
+
+ Thie file contains all the global data elements of the eventlog service.
+
+Author:
+
+ Rajen Shah (rajens) 10-Jul-1991
+
+[Environment:]
+
+ User Mode - Win32, except for NTSTATUS returned by some functions.
+
+Revision History:
+
+ 10-Jul-1991 RajenS
+ created
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <elfcfg.h>
+
+//
+// Debug flag used to control ElfDbgPrint
+//
+DWORD ElfDebug = 0;
+
+//
+// Handles used for the LPC port.
+//
+HANDLE ElfConnectionPortHandle;
+HANDLE ElfCommunicationPortHandle;
+
+// The heads of various linked lists
+//
+LIST_ENTRY LogFilesHead; // Log files
+
+RTL_CRITICAL_SECTION LogFileCritSec; // Accessing log files
+
+LIST_ENTRY LogModuleHead; // Modules registered for logging
+
+RTL_CRITICAL_SECTION LogModuleCritSec; // Accessing log files
+
+LIST_ENTRY LogHandleListHead; // Context-handles for log handles
+
+RTL_CRITICAL_SECTION LogHandleCritSec; // Accessing log handles
+
+LIST_ENTRY QueuedEventListHead; // Deferred events to write
+
+RTL_CRITICAL_SECTION QueuedEventCritSec; // Accessing the deferred events
+
+LIST_ENTRY QueuedMessageListHead; // Deferred messagebox
+
+RTL_CRITICAL_SECTION QueuedMessageCritSec; // Accessing the deferred mb's
+
+HANDLE ElfDoneEvent; // Event to wait for service termination
+
+//
+// Service-related global data
+//
+
+SERVICE_STATUS_HANDLE ElfServiceStatusHandle;
+
+LPWSTR wname_Eventlogsvc = L"EVENTLOG"; // UNICODE name
+CHAR name_Eventlogsvc[] = "EVENTLOG"; // ASCII name
+
+//
+// The following resource is used to serialize access to the resources
+// of the Eventlog service at the highest level. It is used to make sure
+// that the threads that write/read/clear the log file(s) do not step over
+// the threads that monitor the registry and deal with service control
+// operations.
+//
+// The threads that operate on the log file(s) have Shared access to the
+// resource, since they are further serialized on the file that they are
+// working on.
+//
+// The threads that will modify the internal data structures, or the state
+// of the service, need Exclusive access to the resource so that we can
+// control access to the data structures and log files.
+//
+
+RTL_RESOURCE GlobalElfResource;
+
+//
+// This is used by the Backup API to signify which 4K block of the log it's
+// currently reading. This is used to prevent a writer from overwriting this
+// block while it is reading it. The event is used to let a writer block if
+// it was going to overwrite the current backup block, and get pulsed when
+// the backup thread moves to the next block.
+
+PVOID ElfBackupPointer;
+HANDLE ElfBackupEvent;
+
+//
+// Handle for the LPC thread
+//
+HANDLE LPCThreadHandle;
+
+//
+// Handle for the MessageBox thread
+//
+HANDLE MBThreadHandle;
+
+//
+// Handle and ID for the registry monitor thread
+//
+HANDLE RegistryThreadHandle=NULL;
+DWORD RegistryThreadId;
+
+//
+// Bitmask of things that have been allocated and/or started by the
+// service. When the service terminates, this is what needs to be
+// cleaned.
+//
+ULONG EventFlags; // Keep track of what is allocated
+
+//
+// Record used to indicate the end of the event records in the file.
+//
+ELF_EOF_RECORD EOFRecord = {ELFEOFRECORDSIZE,
+ 0x11111111,
+ 0x22222222,
+ 0x33333333,
+ 0x44444444,
+ FILEHEADERBUFSIZE,
+ FILEHEADERBUFSIZE,
+ 1,
+ 1,
+ ELFEOFRECORDSIZE
+ };
+
+//
+// Default module to use if no match is found, APPLICATION
+//
+
+PLOGMODULE ElfDefaultLogModule;
+
+//
+// Module for the eventlog service itself
+//
+
+PLOGMODULE ElfModule;
+
+//
+// Handle (key) to the event log node in the registry.
+// This is set up by Elfmain().
+//
+
+HANDLE hEventLogNode = NULL; // Initialize to NULL
+
+//
+// Used to create a unigue module name for backup logs
+//
+
+DWORD BackupModuleNumber;
+
+//
+// NT well-known SIDs
+//
+PSVCS_GLOBAL_DATA ElfGlobalData;
+
+//
+// Global anonymous logon sid - used in log ACL's. The only SID allocated
+// specifically by the eventlog service, all others are passed in from
+// the service controller in ElfGlobalData.
+//
+
+PSID AnonymousLogonSid = NULL;
+
+//
+// The local computer name. Used when we generate events ourself.
+//
+
+LPWSTR LocalComputerName = NULL;
+ULONG ComputerNameLength;
+
+//
+// Shutdown Flag
+//
+BOOL EventlogShutdown = FALSE;
+
+HANDLE ElfGlobalSvcRefHandle=NULL;
+
+//
+// This is the string used in the title bar of the Message Box
+// used to display log full messages.
+// GlobalMessageBoxTitle will either point to the default string, or
+// to the string allocated in the format Message function.
+//
+WCHAR DefaultMessageBoxTitle[]=L"Eventlog Service";
+LPWSTR GlobalAllocatedMsgTitle=NULL;
+LPWSTR GlobalMessageBoxTitle=DefaultMessageBoxTitle;
+
+#ifdef _CAIRO_
+
+//
+// The eventlog service links to ALERTSYS.DLL by hand (eventlog.c) after
+// eventlog initialization, since this dll's initialization code requires
+// a running eventlog service.
+//
+
+HINSTANCE ghAlertSysDll = NULL;
+PREPORTALERT gpfReportAlert = NULL;
+
+#endif // _CAIRO_
diff --git a/private/eventlog/server/elfdef.h b/private/eventlog/server/elfdef.h
new file mode 100644
index 000000000..81370a5c6
--- /dev/null
+++ b/private/eventlog/server/elfdef.h
@@ -0,0 +1,478 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ elfdef.h
+
+Abstract:
+
+ This file contains defines for the eventlog service.
+
+Author:
+
+ Rajen Shah (rajens) 1-Jul-1991
+
+Revision History:
+
+--*/
+
+#ifndef _EVENTDEF_
+#define _EVENTDEF_
+
+//
+// Logfile object specific access type
+//
+#define ELF_LOGFILE_READ 0x0001
+#define ELF_LOGFILE_WRITE 0x0002
+#define ELF_LOGFILE_CLEAR 0x0004
+#define ELF_LOGFILE_START 0x0008
+#define ELF_LOGFILE_STOP 0x000C
+#define ELF_LOGFILE_CONFIGURE 0x0010
+#define ELF_LOGFILE_BACKUP 0x0020 // Set iff a backup operator
+ // opens the security log -
+ // this overrides all other
+ // flags.
+
+#define ELF_LOGFILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
+ ELF_LOGFILE_READ | \
+ ELF_LOGFILE_WRITE | \
+ ELF_LOGFILE_CLEAR | \
+ ELF_LOGFILE_START | \
+ ELF_LOGFILE_STOP | \
+ ELF_LOGFILE_CONFIGURE)
+
+//
+// Three types of logfiles are defined from a security perspective:
+//
+// ELF_LOGFILE_SECURITY - Only Admins/LocalSystem can RW these files
+// ELF_LOGFILE_SYSTEM - Only Admins/LocalSystem can W these files
+// ELF_LOGFILE_APPLICATION - World can R/W these files
+//
+// System and Security will be SECURE, Application will be NON_SECURE
+//
+
+#define ELF_LOGFILE_SECURITY 0x0000
+#define ELF_LOGFILE_SYSTEM 0x0001
+#define ELF_LOGFILE_APPLICATION 0x0002
+
+//
+// Macro to convert a given file size into one that is "acceptable" for
+// eventlogging. It basically truncates it to a 64K boundary making sure
+// that it is as least 64K
+//
+
+#define ELFFILESIZE(x) ((x & 0xFFFF0000) ? (x & 0xFFFF0000) : 65536)
+
+//
+// Macro for debug prints
+//
+
+#if DBG
+#define ElfDbgPrint(x) if (ElfDebug) DbgPrint x
+#define ElfDbgPrintNC(x) DbgPrint x
+#else
+#define ElfDbgPrint(x)
+#define ElfDbgPrintNC(x)
+#endif
+
+//
+// The largest possible buffer we would need to hold an admin alert
+// information. This primarily depends on the number and length of the
+// replacement strings that would be passed with the message ID.
+//
+
+#define ELF_ADMIN_ALERT_BUFFER_SIZE 256
+
+//
+// Timeout defines.
+//
+
+#define INFINITE_WAIT_TIME -1 // Wait time for events
+#define ELF_GLOBAL_RESOURCE_WAIT 2000 // 2-second timeout for global resource
+
+//
+// Signature placed before each event record in a file. Is used to
+// validate where we are in a file.
+//
+
+#define ELF_RECORD_SIGNATURE 0x654c6652 // ASCII for eLfR
+
+//
+// Size by which to grow a log file until it reaches the max size
+//
+
+#define ELF_DEFAULT_LOG_SIZE 65536
+
+//
+// Bits for whether to take the global resource exclusively or shared.
+//
+
+#define ELF_GLOBAL_SHARED 0x0001
+#define ELF_GLOBAL_EXCLUSIVE 0x0002
+
+//
+// Flag bits to keep track of what resources have been allocated at INIT time
+//
+
+#define ELF_INIT_LOGHANDLE_CRIT_SEC 0x0001
+#define ELF_INIT_GLOBAL_RESOURCE 0x0002
+#define ELF_STARTED_LPC_THREAD 0x0004
+#define ELF_STARTED_REGISTRY_MONITOR 0x0008
+#define ELF_STARTED_RPC_SERVER 0x0010
+#define ELF_INIT_LOGFILE_CRIT_SEC 0x0020
+#define ELF_INIT_WELL_KNOWN_SIDS 0x0040
+#define ELF_INIT_QUEUED_EVENT_CRIT_SEC 0x0080
+#define ELF_INIT_QUEUED_MESSAGE_CRIT_SEC 0x0100
+
+//
+// Security objects
+//
+
+
+#define EVENTLOG_SUBSYSTEM_NAME L"EVENTLOG SERVICE"
+#define LOG_OBJECT_TYPE_NAME L"EVENTLOG LOGFILE OBJECT"
+
+//
+// Structure containing information on each log file
+//
+// ActualMaxFileSize and ConfigMaxFileSize are stored in BYTEs.
+// ActualMaxFileSize is the actual size of the file on the disk.
+// ConfigMaxFileSize is the configured size of the file, which may not
+// be the same as the actual size of the file.
+//
+// CurrentRecordNumber is the next absolute record number to write
+//
+// OldestRecordNumber is the next one to get overwritten
+//
+// Retention time is stored as the number of seconds.
+//
+// BaseAddress points to the physical beginning of the file.
+//
+// ViewSize is ALWAYS the size of the file in bytes.
+//
+// For the Flag bits, see the ELF_LOGFILE_HEADER_xxxx bits defined below.
+//
+
+typedef struct _LOGFILE {
+ LIST_ENTRY FileList;
+ LIST_ENTRY Notifiees; // List of ChangeNotify recipients
+ PUNICODE_STRING LogFileName; // Full path name of log file
+ PUNICODE_STRING LogModuleName; // Name of default module for this log
+ ULONG RefCount; // Number of modules using this file
+ ULONG Flags; // Autowrap, dirty, etc. - See bits below
+ ULONG ConfigMaxFileSize; // Max it can be
+ ULONG ActualMaxFileSize; // How big it is now
+ ULONG NextClearMaxFileSize; // Can't be shrunk on the fly
+ ULONG CurrentRecordNumber;// The next one to be created
+ ULONG OldestRecordNumber; // The next one to overwrite
+ ULONG Retention; // Max. Retention time
+ ULONG NextClearRetention; // they shrank the file when they set this
+ HANDLE FileHandle; // Handle to open file
+ HANDLE SectionHandle; // Memory mapped section handle
+ PVOID BaseAddress; // Map view base address
+ ULONG ViewSize; // Mapped view size
+ ULONG BeginRecord; // Offset of first log record
+ ULONG EndRecord; // Offset of byte after last log record
+ PSECURITY_DESCRIPTOR Sd; // User security object for this log7
+ RTL_RESOURCE Resource;
+} LOGFILE, *PLOGFILE;
+
+//
+// Structure containing information on each module that is registered to
+// log events.
+//
+
+typedef struct _LOGMODULE {
+ LIST_ENTRY ModuleList;
+ PWSTR ModuleName; // Name of module
+ ATOM ModuleAtom; // Atom identifying this module
+ PLOGFILE LogFile; // Log file for this module
+#ifdef _CAIRO_
+ SHORT AlertCategory; // Cairo alert category filter
+ SHORT AlertSeverity; // Cairo alert severity filter
+#endif // _CAIRO_
+} LOGMODULE, *PLOGMODULE;
+
+//
+// Command codes put in the request packets.
+//
+
+#define ELF_COMMAND_READ 1
+#define ELF_COMMAND_WRITE 2
+#define ELF_COMMAND_CLEAR 3
+#define ELF_COMMAND_BACKUP 4
+#define ELF_COMMAND_WRITE_QUEUED 5
+
+//
+// Structures that contain the operation-specific information.
+//
+
+typedef struct _WRITE_PKT {
+ DWORD Datasize; // Size of data in the buffer
+ PVOID Buffer; // Contains filled event log record
+} WRITE_PKT, *PWRITE_PKT;
+
+
+//
+// The following flag bits are used in the READ_PKT Flag field.
+//
+
+#define ELF_IREAD_UNICODE 0x0001
+#define ELF_IREAD_ANSI 0x0002
+#define ELF_LAST_READ_FORWARD 0x0004
+
+typedef struct _READ_PKT {
+ ULONG Flags; // UNICODE or ANSI
+ ULONG BufferSize; // Bytes to read
+ PVOID Buffer; // User's buffer
+ ULONG ReadFlags; // Sequential? Forwards? Random-access? Backwards?
+ ULONG RecordNumber; // Where to start the READ
+ ULONG MinimumBytesNeeded; // For return info if buffer too small
+ ULONG LastSeekPos; // Last seek position in terms of bytes
+ ULONG LastSeekRecord; // Last seek position in terms of records
+ ULONG BytesRead; // Bytes read - for return to caller
+ ULONG RecordsRead;
+} READ_PKT, *PREAD_PKT;
+
+typedef struct _CLEAR_PKT {
+ PUNICODE_STRING BackupFileName; // File to back up current log file (or NULL)
+} CLEAR_PKT, *PCLEAR_PKT;
+
+typedef struct _BACKUP_PKT {
+ PUNICODE_STRING BackupFileName; // File to back up current log file (or NULL)
+} BACKUP_PKT, *PBACKUP_PKT;
+
+//
+// Flags used in the ELF_REQUEST_RECORD
+//
+
+#define ELF_FORCE_OVERWRITE 0x01 // Ignore retention period for this write
+
+//
+// Structure for the packet that contains all the information needed
+// to perform the request.
+//
+
+typedef struct _ELF_REQUEST_RECORD {
+ USHORT Flags;
+ NTSTATUS Status; // To return status of operation
+ PLOGFILE LogFile; // File on which to operate
+ PLOGMODULE Module; // Information on module
+ USHORT Command; // Operation to be performed
+ union {
+ PWRITE_PKT WritePkt;
+ PREAD_PKT ReadPkt;
+ PCLEAR_PKT ClearPkt;
+ PBACKUP_PKT BackupPkt;
+ } Pkt;
+} ELF_REQUEST_RECORD, *PELF_REQUEST_RECORD;
+
+typedef struct _ELF_ALERT_RECORD {
+ DWORD TimeOut;
+ DWORD MessageId;
+ DWORD NumberOfStrings;
+ // array of UNICODE_STRINGs NumberOfStringsLong
+ // each string
+} ELF_ALERT_RECORD, * PELF_ALERT_RECORD;
+
+typedef struct _ELF_MESSAGE_RECORD {
+ DWORD MessageId;
+ DWORD NumberOfStrings;
+ // UNICODE null terminated strings
+} ELF_MESSAGE_RECORD, * PELF_MESSAGE_RECORD;
+
+//
+// Record for the linked list of deferred events (these are raised by the
+// eventlog service itself for writing once the current operation is complete
+//
+
+typedef struct _ELF_QUEUED_EVENT {
+ LIST_ENTRY Next;
+ enum _ELF_QUEUED_EVENT_TYPE {
+ Event,
+ Alert,
+ Message
+ } Type;
+ union _ELF_QUEUED_EVENT_DATA {
+ ELF_REQUEST_RECORD Request;
+ ELF_ALERT_RECORD Alert;
+ ELF_MESSAGE_RECORD Message;
+ } Event;
+} ELF_QUEUED_EVENT, *PELF_QUEUED_EVENT;
+
+//
+// Structure containing information on callers of ElfChangeNotify
+//
+
+typedef struct _NOTIFIEE {
+ LIST_ENTRY Next;
+ IELF_HANDLE Handle;
+ HANDLE Event;
+} NOTIFIEE, *PNOTIFIEE;
+
+
+//
+// Structure that describes the header that is at the beginning of the
+// log files.
+//
+// To see if there are any records in the file, one must subtract the
+// EndOffset from the StartOffset (allowing for the file having wrapped
+// around) and check for a difference of greater than 1.
+//
+// The header size is stored at the beginning and end so that it looks
+// just like any other event log record (the lengths do at any rate).
+//
+
+typedef struct _ELF_LOGFILE_HEADER {
+ ULONG HeaderSize; // Size of this header
+ ULONG Signature; // Signature field
+ ULONG MajorVersion;
+ ULONG MinorVersion;
+ ULONG StartOffset; // Where the first record is located
+ ULONG EndOffset; // The end of the last record + 1
+ ULONG CurrentRecordNumber; // The next record to create
+ ULONG OldestRecordNumber; // The next record to overwrite
+ ULONG MaxSize; // Max. size when file was created
+ ULONG Flags; // DIRTY, etc.
+ ULONG Retention; // Last Retention period.
+ ULONG EndHeaderSize; // Size of this header
+} ELF_LOGFILE_HEADER, *PELF_LOGFILE_HEADER;
+
+#define FILEHEADERBUFSIZE sizeof(ELF_LOGFILE_HEADER)
+#define ELF_LOG_FILE_SIGNATURE 0x654c664c // ASCII for eLfL
+
+//
+// The following flag bits are used in ELF_LOGFILE_HEADER and in the
+// LOGFILE structures' Flag fields.
+//
+
+#define ELF_LOGFILE_HEADER_DIRTY 0x0001 // File has been written to
+#define ELF_LOGFILE_HEADER_WRAP 0x0002 // The file has wrapped
+#define ELF_LOGFILE_LOGFULL_WRITTEN 0x0004 // Written logfull record
+#define ELF_LOGFILE_ARCHIVE_SET 0x0008 // Archive bit flag
+
+
+//
+// Structure that defines the record that identifies the end of the
+// circular log file.
+// This record is used to identify where the last record in the circular
+// buffer is located.
+//
+// NOTE: It is *essential* that this record is of a size that a "normal"
+// event log record can never have. There is code that relies on
+// this fact to detect an "EOF" record.
+//
+// Care must be taken to not disturb the first part of this record. It
+// is used to identify an EOF record. ELFEOFUNIQUEPART must be the
+// number of bytes that are constant.
+//
+
+typedef struct _ELF_EOF_RECORD {
+ ULONG RecordSizeBeginning;
+ ULONG One;
+ ULONG Two;
+ ULONG Three;
+ ULONG Four;
+ ULONG BeginRecord;
+ ULONG EndRecord;
+ ULONG CurrentRecordNumber;
+ ULONG OldestRecordNumber;
+ ULONG RecordSizeEnd;
+} ELF_EOF_RECORD, *PELF_EOF_RECORD;
+
+#define ELFEOFRECORDSIZE sizeof (ELF_EOF_RECORD)
+
+//
+// The following constant is how much of the EOF record is constant, and can
+// be used to identify an EOF record
+//
+
+#define ELFEOFUNIQUEPART 5 * sizeof(ULONG)
+
+//
+// This is used to fill the end of a log record so that the fixed portion
+// of a log record doesn't split the end of the file. It must be less than
+// the minimum size of any valid record
+//
+
+#define ELF_SKIP_DWORD sizeof(ELF_EOF_RECORD) - 1
+
+
+//
+// Time for the sender of a start or stop request to the Eventlog
+// service to wait (in milliseconds) before checking on the
+// Eventlog service again to see if it is done
+//
+
+#define ELF_WAIT_HINT_TIME 20000 // 20 seconds
+
+
+//
+// Flags used by ElfpCloseLogFile
+//
+
+#define ELF_LOG_CLOSE_NORMAL 0x0000
+#define ELF_LOG_CLOSE_FORCE 0x0001
+#define ELF_LOG_CLOSE_BACKUP 0x0002
+
+//
+// Structure used to store information read from the registry
+//
+
+typedef struct _LOG_FILE_INFO {
+ PUNICODE_STRING LogFileName;
+ ULONG MaxFileSize;
+ ULONG Retention;
+ ULONG GuestAccessRestriction;
+} LOG_FILE_INFO, *PLOG_FILE_INFO;
+
+//
+// DEBUG stuff.
+//
+
+//
+// This signature is placed in the context handle for debug purposes only,
+// to track down a bug in freeing the structures.
+//
+
+#define ELF_CONTEXTHANDLE_SIGN 0x654c6648 // ASCII for eLfH
+
+//
+// The different file open (or create) options are based on the type of file.
+// The types, and their meanings are:
+//
+// ElfNormalLog Normal log file, opened for cached io
+// ElfSecurityLog Audit logs, opened for write-thru
+// ElfBackupLog Not an active log file, opened read only, cached
+//
+
+typedef enum _ELF_LOG_TYPE {
+ ElfNormalLog,
+ ElfSecurityLog,
+ ElfBackupLog
+} ELF_LOG_TYPE, *PELF_LOG_TYPE;
+
+
+//
+// Eventlog States (used as return codes)
+//
+
+#define UPDATE_ONLY 0 // no change in state - just send current status.
+#define STARTING 1 // the messenger is initializing.
+#define RUNNING 2 // initialization completed normally - now running
+#define STOPPING 3 // uninstall pending
+#define STOPPED 4 // uninstalled
+#define PAUSED 5 // Paused
+#define PAUSING 6 // In the process of pausing
+#define CONTINUING 7 // In the process of continuing
+
+//
+// Forced Shutdown PendingCodes
+//
+#define PENDING TRUE
+#define IMMEDIATE FALSE
+
+
+#endif // ifndef _EVENTDEF_
diff --git a/private/eventlog/server/elfextrn.h b/private/eventlog/server/elfextrn.h
new file mode 100644
index 000000000..ddf0f1fcd
--- /dev/null
+++ b/private/eventlog/server/elfextrn.h
@@ -0,0 +1,99 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ elfextrn.h
+
+Abstract:
+
+ This file contains all the externs for the global variables.
+
+Author:
+
+ Rajen Shah (rajens) 10-Jul-1991
+
+Revision History:
+
+--*/
+
+extern DWORD ElfDebug;
+extern HANDLE ElfConnectionPortHandle;
+extern HANDLE ElfCommunicationPortHandle;
+
+extern PWSTR Computername;
+
+extern LIST_ENTRY LogFilesHead; // Log files
+extern LIST_ENTRY LogModuleHead; // Modules registered for logging
+extern LIST_ENTRY LogHandleListHead; // Context-handles for log handles
+extern LIST_ENTRY QueuedEventListHead; // Deferred events
+extern LIST_ENTRY QueuedMessageListHead; //Deferred Messagebox
+
+extern RTL_CRITICAL_SECTION LogFileCritSec;
+extern RTL_CRITICAL_SECTION LogModuleCritSec;
+extern RTL_CRITICAL_SECTION LogHandleCritSec;
+extern RTL_CRITICAL_SECTION QueuedEventCritSec;
+extern RTL_CRITICAL_SECTION QueuedMessageCritSec;
+
+extern SERVICE_STATUS ElfServiceStatus;
+extern SERVICE_STATUS_HANDLE ElfServiceStatusHandle;
+extern HANDLE ElfDoneEvent;
+
+extern LPWSTR wname_Eventlogsvc;
+extern CHAR name_Eventlogsvc[];
+
+extern RTL_RESOURCE GlobalElfResource;
+
+extern PSID AnonymousLogonSid;
+
+extern PVOID ElfBackupPointer;
+extern HANDLE ElfBackupEvent;
+
+extern HANDLE LPCThreadHandle;
+
+extern HANDLE MBThreadHandle;
+
+extern HANDLE RegistryThreadHandle;
+extern DWORD RegistryThreadId;
+
+extern ULONG EventFlags;
+
+extern ELF_EOF_RECORD EOFRecord;
+
+extern PLOGMODULE ElfDefaultLogModule;
+
+extern PLOGMODULE ElfModule;
+
+extern HANDLE hEventLogNode;
+
+extern DWORD BackupModuleNumber;
+
+extern PSVCS_GLOBAL_DATA ElfGlobalData; // WellKnownSids
+
+
+extern LPWSTR LocalComputerName;
+extern ULONG ComputerNameLength;
+
+extern BOOL EventlogShutdown;
+
+extern HANDLE ElfGlobalSvcRefHandle;
+
+extern WCHAR DefaultMessageBoxTitle[];
+extern LPWSTR GlobalAllocatedMsgTitle;
+extern LPWSTR GlobalMessageBoxTitle;
+
+#ifdef _CAIRO_
+
+//
+// The eventlog service links to ALERTSYS.DLL by hand (eventlog.c) after
+// eventlog initialization, since this dll's initialization code requires
+// a running eventlog service.
+//
+
+typedef LONG (*PREPORTALERT)(PCALERTREPORTRECORD, DWORD);
+
+extern HINSTANCE ghAlertSysDll;
+extern PREPORTALERT gpfReportAlert;
+
+#endif // _CAIRO_
diff --git a/private/eventlog/server/elflpc.c b/private/eventlog/server/elflpc.c
new file mode 100644
index 000000000..36ad8bb83
--- /dev/null
+++ b/private/eventlog/server/elflpc.c
@@ -0,0 +1,774 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ ELFLPC.C
+
+Abstract:
+
+ This file contains the routines that deal with the LPC port in the
+ eventlog service.
+
+Author:
+
+ Rajen Shah (rajens) 10-Jul-1991
+
+Revision History:
+
+
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <ntiolog.h> // For IO_ERROR_LOG_[MESSAGE/PACKET]
+#include <elflpc.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <elfextrn.h> // Computername
+
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint prototype
+#include <ntdef.h>
+#include <ntstatus.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windef.h>
+#include <winbase.h> // LocalAlloc
+#include <lmcons.h>
+#include <string.h>
+#include <lmerr.h>
+
+NTSTATUS
+SetUpLPCPort ()
+
+
+/*++
+
+Routine Description:
+
+ This routine sets up the LPC port for the service.
+
+
+Arguments:
+
+
+
+Return Value:
+
+
+
+Note:
+
+
+--*/
+{
+
+ NTSTATUS status;
+ UNICODE_STRING unicodePortName;
+ OBJECT_ATTRIBUTES objectAttributes;
+ PORT_MESSAGE connectionRequest;
+
+
+ ElfDbgPrint (("[ELF] Set up LPC port\n"));
+
+ // Initialize the handles to zero so that we can determine what to do
+ // if we need to clean up.
+
+ ElfConnectionPortHandle = NULL;
+ ElfCommunicationPortHandle = NULL;
+
+ //
+ // Create the LPC port.
+ //
+ RtlInitUnicodeString( &unicodePortName, ELF_PORT_NAME_U );
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &unicodePortName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = NtCreatePort(
+ &ElfConnectionPortHandle,
+ &objectAttributes,
+ 0,
+ ELF_PORT_MAX_MESSAGE_LENGTH,
+ ELF_PORT_MAX_MESSAGE_LENGTH * 32
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ ElfDbgPrintNC(( "[ELF] Port not created\n" ));
+ goto exit;
+ }
+
+ //
+ // Start listening for the system thread's connection to the port. Note
+ // that it is OK if it happens to call NtConnectPort first--it will
+ // simply block until this call to NtListenPort occurs.
+ //
+ ElfDbgPrint(( "[ELF] Listening to port.\n" ));
+
+ connectionRequest.u1.s1.TotalLength = sizeof(connectionRequest);
+ connectionRequest.u1.s1.DataLength = (CSHORT)0;
+ status = NtListenPort(
+ ElfConnectionPortHandle,
+ &connectionRequest
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ ElfDbgPrintNC(( "[ELF] NtListenPort failed: %X\n", status ));
+ goto exit;
+ }
+
+ //
+ // The system thread has initiated the connection. Accept the connection.
+ //
+ // BUGBUG We need some security check here.
+ //
+ ElfDbgPrint(( "[ELF] Accepting connection to port.\n" ));
+
+ status = NtAcceptConnectPort(
+ &ElfCommunicationPortHandle,
+ NULL, // PortContext
+ &connectionRequest,
+ TRUE, // AcceptConnection
+ NULL, // ServerView
+ NULL // ClientView
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ ElfDbgPrintNC(( "[ELF] NtAcceptConnectPort failed: %X\n", status ));
+ goto exit;
+ }
+
+
+ //
+ // Complete the connection to the port, thereby releasing the system
+ // thread waiting in NtConnectPort.
+ //
+
+ ElfDbgPrint(( "[ELF] Completing connection to port.\n" ));
+
+ status = NtCompleteConnectPort( ElfCommunicationPortHandle );
+
+ if ( !NT_SUCCESS(status) ) {
+ ElfDbgPrintNC(( "[ELF] NtCompleteConnectPort failed: %X\n",
+ status ));
+ goto exit;
+ }
+
+exit:
+
+ //
+ // Close open handles and shut down if necessary.
+ //
+
+ if ( !NT_SUCCESS(status) && ElfConnectionPortHandle != NULL ) {
+ NtClose( ElfConnectionPortHandle );
+ }
+
+ if ( !NT_SUCCESS(status) && ElfCommunicationPortHandle != NULL ) {
+ NtClose( ElfConnectionPortHandle );
+ }
+
+ return(status);
+
+}
+
+
+
+NTSTATUS
+ElfProcessLPCPacket ( PIO_ERROR_LOG_MESSAGE pIoErrorLogMessage
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the packet received from the LPC port and processes it.
+ The only thing that comes in thru the LPC port are packets generated by
+ device drivers and written to this port by the IO Subsystem. The packet is
+ an IO_ERROR_LOG_MESSAGE. The logfile will be system, the module name will
+ be the driver that generated the packet, the SID will always be NULL and
+ there will always be one string, which will be the device name.
+
+ It extracts the information from the LPC packet, and then calls the
+ common routine to do the work of formatting the data into
+ an event record and writing it out to the log file.
+
+
+Arguments:
+
+ pIoErrorLogMessage - Pointer to the data portion of the packet just
+ received through the LPC port.
+
+
+Return Value:
+
+ Status of this operation.
+
+
+--*/
+
+{
+ static PLOGMODULE SystemModule = NULL;
+
+ NTSTATUS status;
+ ELF_REQUEST_RECORD Request;
+ WRITE_PKT WritePkt;
+
+ UNICODE_STRING SystemString;
+ ULONG RecordLength;
+ PEVENTLOGRECORD EventLogRecord;
+ LPWSTR DestinationString, SourceString;
+ PBYTE BinaryData;
+ ULONG PadSize;
+ LARGE_INTEGER Time;
+ ULONG TimeWritten;
+ PULONG pEndLength;
+ ULONG i = 0;
+ PWCHAR pwch;
+ PWCHAR pwStart;
+ PWCHAR pwEnd;
+ ULONG StringLength;
+
+ try {
+
+ //
+ // Validate the packet, First make sure there are the correct
+ // number of NULL terminated strings, and remember the
+ // total number of bytes to copy
+ //
+
+ pwStart = pwch = (PWCHAR) ((PBYTE) pIoErrorLogMessage +
+ pIoErrorLogMessage->EntryData.StringOffset);
+
+ pwEnd = (PWCHAR) ((PBYTE) pIoErrorLogMessage +
+ pIoErrorLogMessage->Size);
+
+ while (pwch < pwEnd &&
+ i < pIoErrorLogMessage->EntryData.NumberOfStrings) {
+ if (*pwch == L'\0') {
+ i++;
+ }
+ pwch++;
+ }
+ StringLength = (pwch - pwStart) * sizeof(WCHAR);
+
+ //
+ // Now make sure everything in the packet is true
+ //
+
+ if ((i != pIoErrorLogMessage->EntryData.NumberOfStrings)
+
+ ||
+
+ (pIoErrorLogMessage->DriverNameOffset +
+ pIoErrorLogMessage->DriverNameLength >=
+ pIoErrorLogMessage->Size)
+
+ ||
+
+
+ (pIoErrorLogMessage->EntryData.StringOffset >=
+ pIoErrorLogMessage->Size)
+
+ ||
+
+ (FIELD_OFFSET(IO_ERROR_LOG_MESSAGE, EntryData) +
+ FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) +
+ pIoErrorLogMessage->EntryData.DumpDataSize >=
+ pIoErrorLogMessage->Size)) {
+
+ //
+ // It's a bad packet, log it and return
+ //
+
+ ElfDbgPrintNC(("[ELF] Bad packet from LPC port\n"));
+ ElfpCreateElfEvent(EVENT_BadDriverPacket,
+ EVENTLOG_ERROR_TYPE,
+ 0, // EventCategory
+ 0, // NumberOfStrings
+ NULL, // Strings
+ pIoErrorLogMessage, // Data
+ pIoErrorLogMessage->Size, // Datalength
+ 0 // flags
+ );
+ return(STATUS_UNSUCCESSFUL);
+ }
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+
+ //
+ // It's a bad packet, log it and return
+ //
+
+ ElfDbgPrintNC(("[ELF] Bad packet from LPC port\n"));
+ ElfpCreateElfEvent(EVENT_BadDriverPacket,
+ EVENTLOG_ERROR_TYPE,
+ 0, // EventCategory
+ 0, // NumberOfStrings
+ NULL, // Strings
+ pIoErrorLogMessage, // Data
+ pIoErrorLogMessage->Size, // Datalength
+ 0 // flags
+ );
+ return(STATUS_UNSUCCESSFUL);
+
+ }
+
+ //
+ // We're going to need this everytime, so just get it once
+ //
+
+ if (!SystemModule) {
+
+ //
+ // Get the system module to log driver events
+ //
+
+ RtlInitUnicodeString(&SystemString, ELF_SYSTEM_MODULE_NAME);
+ SystemModule = GetModuleStruc (&SystemString);
+ ASSERT(SystemModule); // GetModuleStruc never returns NULL
+
+ }
+
+ //
+ // The packet should be an IO_ERROR_LOG_MESSAGE
+ //
+
+ ASSERT(pIoErrorLogMessage->Type == IO_TYPE_ERROR_MESSAGE);
+
+ //
+ // Set up write packet in request packet
+ //
+
+ Request.Pkt.WritePkt = &WritePkt;
+ Request.Flags = 0;
+
+ //
+ // Generate any additional information needed in the record.
+ //
+
+ // TIMEWRITTEN
+ // We need to generate a time when the log is written. This
+ // gets written in the log so that we can use it to test the
+ // retention period when wrapping the file.
+ //
+
+ NtQuerySystemTime(&Time);
+ RtlTimeToSecondsSince1970(
+ &Time,
+ &TimeWritten
+ );
+
+ //
+ // Determine how big a buffer is needed for the eventlog record.
+ //
+
+ RecordLength = sizeof(EVENTLOGRECORD)
+ + ComputerNameLength // computername
+ + 2 * sizeof(WCHAR) // term's
+ + pIoErrorLogMessage->Size
+ - FIELD_OFFSET(IO_ERROR_LOG_MESSAGE, EntryData)
+ + sizeof(RecordLength); // final len
+
+ //
+ // Determine how many pad bytes are needed to align to a DWORD
+ // boundary.
+ //
+
+ PadSize = sizeof(ULONG) - (RecordLength % sizeof(ULONG));
+
+ RecordLength += PadSize; // True size needed
+
+ //
+ // Allocate the buffer for the Eventlog record
+ //
+
+ EventLogRecord = (PEVENTLOGRECORD) ElfpAllocateBuffer(RecordLength);
+
+ if (EventLogRecord != (PEVENTLOGRECORD) NULL) {
+
+ //
+ // Fill up the event record
+ //
+
+ EventLogRecord->Length = RecordLength;
+ RtlTimeToSecondsSince1970(
+ &pIoErrorLogMessage->TimeStamp,
+ &EventLogRecord->TimeGenerated
+ );
+ EventLogRecord->Reserved = ELF_LOG_FILE_SIGNATURE;
+ EventLogRecord->TimeWritten = TimeWritten;
+ EventLogRecord->EventID = pIoErrorLogMessage->EntryData.ErrorCode;
+
+ // set EventType based on the high order nibble of
+ // pIoErrorLogMessage->EntryData.ErrorCode
+
+ if (NT_INFORMATION(pIoErrorLogMessage->EntryData.ErrorCode)) {
+
+ EventLogRecord->EventType = EVENTLOG_INFORMATION_TYPE;
+
+ }
+ else if (NT_WARNING(pIoErrorLogMessage->EntryData.ErrorCode)) {
+
+ EventLogRecord->EventType = EVENTLOG_WARNING_TYPE;
+
+ }
+ else if (NT_ERROR(pIoErrorLogMessage->EntryData.ErrorCode)) {
+
+ EventLogRecord->EventType = EVENTLOG_ERROR_TYPE;
+
+ }
+ else {
+
+ //
+ // Unknown, set to error
+ //
+
+ EventLogRecord->EventType = EVENTLOG_ERROR_TYPE;
+
+ }
+
+ EventLogRecord->NumStrings =
+ pIoErrorLogMessage->EntryData.NumberOfStrings;
+ EventLogRecord->EventCategory =
+ pIoErrorLogMessage->EntryData.EventCategory;
+ EventLogRecord->StringOffset = sizeof(EVENTLOGRECORD) +
+ pIoErrorLogMessage->DriverNameLength + ComputerNameLength;
+ EventLogRecord->DataLength =
+ FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) +
+ pIoErrorLogMessage->EntryData.DumpDataSize;
+ EventLogRecord->DataOffset = EventLogRecord->StringOffset +
+ StringLength;
+ EventLogRecord->UserSidLength = 0;
+ EventLogRecord->UserSidOffset = 0;
+
+ //
+ // Fill in the variable-length fields
+
+ // MODULENAME
+ //
+ // Use the driver name as the module name, since it's location is
+ // described by an offset from the start of the IO_ERROR_LOG_MESSAGE
+ // turn it into a pointer
+ //
+
+ DestinationString = (LPWSTR)((LPBYTE)EventLogRecord +
+ sizeof(EVENTLOGRECORD));
+ SourceString = (LPWSTR)((LPBYTE) pIoErrorLogMessage +
+ pIoErrorLogMessage->DriverNameOffset);
+
+ RtlMoveMemory(DestinationString, SourceString,
+ pIoErrorLogMessage->DriverNameLength);
+
+ //
+ // Make sure it's NULL terminated
+ //
+
+ DestinationString = (LPWSTR)((LPBYTE) DestinationString +
+ pIoErrorLogMessage->DriverNameLength) - 1;
+ if (*DestinationString != L'\0') {
+ *(++DestinationString) = L'\0';
+ }
+
+ DestinationString++;
+
+ // COMPUTERNAME
+ //
+
+ RtlMoveMemory(DestinationString, LocalComputerName,
+ ComputerNameLength);
+
+ //
+ // Make sure it's NULL terminated
+ //
+
+ DestinationString = (LPWSTR)((LPBYTE) DestinationString +
+ ComputerNameLength) - 1;
+ if (*DestinationString != L'\0') {
+ *(++DestinationString) = L'\0';
+ }
+
+ DestinationString++;
+
+ // STRING
+ //
+
+ SourceString = pwStart;
+
+ RtlMoveMemory(DestinationString, SourceString, StringLength);
+
+ DestinationString += (StringLength / 2);
+
+ // BINARY DATA
+ //
+ BinaryData = (LPBYTE) DestinationString;
+
+
+ RtlMoveMemory (BinaryData, & pIoErrorLogMessage->EntryData,
+ FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) +
+ pIoErrorLogMessage->EntryData.DumpDataSize);
+
+ // LENGTH at end of record
+ //
+
+ pEndLength = (PULONG)((LPBYTE) EventLogRecord + RecordLength -
+ sizeof(ULONG));
+ *pEndLength = RecordLength;
+
+ //
+ // Set up request packet.
+ // Link event log record into the request structure.
+ //
+
+ Request.Module = SystemModule;
+ Request.LogFile = Request.Module->LogFile;
+ Request.Command = ELF_COMMAND_WRITE;
+ Request.Pkt.WritePkt->Buffer = (PVOID)EventLogRecord;
+ Request.Pkt.WritePkt->Datasize = RecordLength;
+
+ //
+ // Perform the operation
+ //
+
+ ElfPerformRequest( &Request );
+
+ //
+ // Free up the buffer
+ //
+
+ ElfpFreeBuffer(EventLogRecord );
+
+ status = Request.Status; // Set status of WRITE
+
+ } else {
+ status = STATUS_NO_MEMORY;
+ }
+
+ return (status);
+}
+
+
+
+NTSTATUS
+ElfProcessLPCCalls (
+ )
+
+/*++
+
+Routine Description:
+
+ This routine waits for messages to come through the LPC port to
+ the system thread. When one does, it calls the appropriate routine to
+ handle the API, then replies to the system thread indicating that the
+ call has completed if the message was a request, if it was a datagram,
+ it just waits for the next message.
+
+Arguments:
+
+
+Return Value:
+
+--*/
+
+{
+ NTSTATUS status;
+
+ BOOL SendReply = FALSE;
+
+ ELF_REPLY_MESSAGE replyMessage;
+ PELFIOPORTMSG receiveMessage;
+
+ //
+ // Loop dispatching API requests.
+ //
+
+ receiveMessage = ElfpAllocateBuffer(ELF_PORT_MAX_MESSAGE_LENGTH +
+ sizeof(PORT_MESSAGE));
+ if (!receiveMessage) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ while ( TRUE ) {
+
+ //
+ // On the first call to NtReplyWaitReceivePort, don't send a
+ // reply since there's nobody to reply to. However, on subsequent
+ // calls the reply to the message from the prior time if that message
+ // wasn't a LPC_DATAGRAM
+ //
+
+ status = NtReplyWaitReceivePort(
+ ElfConnectionPortHandle,
+ NULL, // PortContext
+ (PPORT_MESSAGE)( SendReply ? &replyMessage : NULL),
+ (PPORT_MESSAGE) receiveMessage
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ ElfDbgPrintNC(( "[ELF] ElfProcessLPCCalls: NtReplyWaitReceivePort failed: %X\n",
+ status ));
+ return status;
+ }
+
+ ElfDbgPrint(( "[ELF] ElfProcessLPCCalls: received message\n" ));
+
+ //
+ // Take the record received and perform the operation. Strip off
+ // the PortMessage and just send the packet
+ //
+
+
+ //
+ // Set up the response message to be sent on the next call to
+ // NtReplyWaitReceivePort if this wasn't a datagram.
+ // 'status' contains the status to return from this call.
+ // Only process messages that are LPC_REQUEST or LPC_DATAGRAM
+ //
+
+ if (receiveMessage->PortMessage.u2.s2.Type == LPC_REQUEST) {
+ status = ElfProcessLPCPacket (& receiveMessage->IoErrorLogMessage);
+
+ replyMessage.PortMessage.u1.s1.DataLength =
+ sizeof(replyMessage) - sizeof(PORT_MESSAGE);
+ replyMessage.PortMessage.u1.s1.TotalLength = sizeof(replyMessage);
+ replyMessage.PortMessage.u2.ZeroInit = 0;
+ replyMessage.PortMessage.ClientId =
+ receiveMessage->PortMessage.ClientId;
+ replyMessage.PortMessage.MessageId =
+ receiveMessage->PortMessage.MessageId;
+ replyMessage.Status = status;
+
+ SendReply = TRUE;
+ }
+ else if (receiveMessage->PortMessage.u2.s2.Type == LPC_DATAGRAM) {
+ status = ElfProcessLPCPacket (& receiveMessage->IoErrorLogMessage);
+ SendReply = FALSE;
+ }
+ else {
+ //
+ // We received a message type we didn't expect, probably due to
+ // error. BUGBUG - write an event
+ //
+
+ ElfDbgPrintNC(("[ELF] Unexpected message type received on LPC port\n"));
+ ElfDbgPrintNC(("[ELF] Messaage type = %d\n",
+ receiveMessage->PortMessage.u2.s2.Type));
+
+ }
+ }
+
+} // ElfProcessLPCCalls
+
+
+
+DWORD
+MainLPCThread (
+ LPVOID LPCThreadParm
+ )
+
+/*++
+
+Routine Description:
+
+ This is the main thread that monitors the LPC port from the I/O system.
+ It takes care of creating the LPC port, and waiting for input, which
+ it then transforms into the right operation on the event log.
+
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ ElfDbgPrint(( "[ELF] Inside LPC thread\n" ));
+
+ Status = SetUpLPCPort();
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Loop forever. This thread will be killed when the service terminates.
+ //
+ while (TRUE) {
+
+ Status = ElfProcessLPCCalls ();
+
+ }
+
+ }
+ ElfDbgPrintNC (("[ELF] Error from SetUpLPCPort. Status = %lx\n", Status));
+
+ return (Status);
+
+ UNREFERENCED_PARAMETER ( LPCThreadParm );
+}
+
+
+
+BOOL
+StartLPCThread ()
+
+/*++
+
+Routine Description:
+
+ This routine starts up the thread that monitors the LPC port.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ TRUE if thread creation succeeded, FALSE otherwise.
+
+Note:
+
+
+--*/
+{
+ DWORD error;
+ DWORD ThreadId;
+
+ ElfDbgPrint(( "[ELF] Start up the LPC thread\n" ));
+
+ //
+ // Start up the actual thread.
+ //
+
+ LPCThreadHandle = CreateThread(
+ NULL, // lpThreadAttributes
+ 4096, // dwStackSize
+ MainLPCThread, // lpStartAddress
+ NULL, // lpParameter
+ 0L, // dwCreationFlags
+ &ThreadId // lpThreadId
+ );
+
+ if ( LPCThreadHandle == NULL ) {
+ error = GetLastError();
+ ElfDbgPrintNC(( "[ELF]: LPCThread - CreateThread failed: %ld\n", error ));
+ return (FALSE);
+ }
+ return (TRUE);
+}
diff --git a/private/eventlog/server/elflpc.h b/private/eventlog/server/elflpc.h
new file mode 100644
index 000000000..e11f526cc
--- /dev/null
+++ b/private/eventlog/server/elflpc.h
@@ -0,0 +1,54 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ elflpc.h
+
+Abstract:
+
+ This file contains defines for LPC for the eventlog service
+
+Author:
+
+ Rajen Shah (rajens) 10-Jul-1991
+
+Revision History:
+
+--*/
+
+#ifndef _ELFLPC_
+#define _ELFLPC_
+
+// Name of the LPC port for kernel objects to communicate with the eventlog
+// service
+#define ELF_PORT_NAME "\\ErrorLogPort"
+#define ELF_PORT_NAME_U L"\\ErrorLogPort"
+
+//
+// Max size of data sent to the eventlogging service through the LPC port.
+//
+
+#define ELF_PORT_MAX_MESSAGE_LENGTH IO_ERROR_LOG_MESSAGE_LENGTH
+
+//
+// Structure that is passed in from the system thread to the LPC port
+//
+
+typedef struct _ELFIOPORTMSG {
+ PORT_MESSAGE PortMessage;
+ IO_ERROR_LOG_MESSAGE IoErrorLogMessage;
+} ELFIOPORTMSG, *PELFIOPORTMSG;
+
+//
+// Structure for the message as a reply from the eventlogging service to
+// the LPC client.
+//
+
+typedef struct _ELF_REPLY_MESSAGE {
+ PORT_MESSAGE PortMessage;
+ NTSTATUS Status;
+} ELF_REPLY_MESSAGE, *PELF_REPLY_MESSAGE;
+
+#endif // ifndef _ELFLPC_
diff --git a/private/eventlog/server/elfproto.h b/private/eventlog/server/elfproto.h
new file mode 100644
index 000000000..a6a28fa42
--- /dev/null
+++ b/private/eventlog/server/elfproto.h
@@ -0,0 +1,384 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ elfproto.h
+
+Abstract:
+
+ This file contains the prototypes for the Eventlog service.
+
+Author:
+
+ Rajen Shah (rajens) 12-Aug-1991
+
+Revision History:
+
+--*/
+
+#ifndef _ELFPROTO_
+#define _ELFPROTO
+
+//
+// Other prototypes
+//
+PVOID
+ElfpAllocateBuffer (ULONG size);
+
+VOID
+ElfpFreeBuffer (PVOID BufPtr);
+
+VOID
+ElfPerformRequest( PELF_REQUEST_RECORD Request);
+
+
+PLOGMODULE
+GetModuleStruc( PUNICODE_STRING ModuleName );
+
+PLOGMODULE
+FindModuleStrucFromAtom ( ATOM Atom );
+
+
+VOID
+ElfControlResponse(
+ DWORD);
+
+VOID
+ElfAnnounceServiceStatus();
+
+VOID
+Elfmain (
+ DWORD argc,
+ LPWSTR argv[]
+ );
+
+VOID
+ElfPrepareForPause();
+
+VOID
+ElfPrepareForContinue();
+
+VOID
+IELF_HANDLE_rundown(
+ IELF_HANDLE ElfHandle
+ );
+
+VOID
+LinkContextHandle(
+ IELF_HANDLE LogHandle
+ );
+
+VOID
+UnlinkContextHandle (
+ IELF_HANDLE LogHandle
+ );
+
+VOID
+LinkLogModule (
+ PLOGMODULE pLogModule,
+ ANSI_STRING * pModuleNameA
+ );
+
+VOID
+UnlinkLogModule (
+ PLOGMODULE pLogModule
+ );
+
+VOID
+LinkLogFile (
+ PLOGFILE pLogFile
+ );
+
+VOID
+UnlinkLogFile (
+ PLOGFILE pLogFile
+ );
+
+VOID
+GetGlobalResource (DWORD Type
+ );
+
+VOID
+ReleaseGlobalResource();
+
+NTSTATUS
+SetUpDataStruct (
+ PUNICODE_STRING LogFileName,
+ ULONG MaxFileSize,
+ ULONG Retention,
+ ULONG GuestAccessRestriction,
+ PUNICODE_STRING ModuleName,
+ HANDLE hLogFile,
+ ELF_LOG_TYPE LogType
+ );
+
+NTSTATUS
+SetUpModules (
+ HANDLE hLogFile,
+ PLOGFILE pLogFile,
+ BOOLEAN bAllowDupes
+ );
+
+BOOL
+StartLPCThread ();
+
+VOID
+StopLPCThread ();
+
+BOOL
+ElfStartRegistryMonitor ();
+
+VOID
+StopRegistryMonitor ();
+
+NTSTATUS
+ElfImpersonateClient(
+ VOID
+ );
+
+NTSTATUS
+ElfRevertToSelf(
+ VOID
+ );
+
+NTSTATUS
+ReadRegistryInfo (
+ HANDLE hLogFiles,
+ PUNICODE_STRING SubKeyName,
+ PLOG_FILE_INFO LogFileInfo
+ );
+
+NTSTATUS
+ElfOpenLogFile (
+ PLOGFILE pLogFile,
+ ELF_LOG_TYPE LogType
+ );
+
+NTSTATUS
+ElfpCloseLogFile (
+ PLOGFILE pLogFile,
+ DWORD Flags
+ );
+
+BOOL
+ValidFilePos (
+ PVOID Position,
+ PVOID BeginningRecord,
+ PVOID EndingRecord,
+ PVOID PhysicalEOF,
+ PVOID BaseAddress
+ );
+
+VOID
+ElfpCleanUp (
+ ULONG EventFlags
+ );
+
+NTSTATUS
+ElfpCopyFile (
+ IN HANDLE SourceHandle,
+ IN PUNICODE_STRING TargetFileName
+ );
+
+VOID
+FreeModuleAndLogFileStructs (VOID);
+
+NTSTATUS
+ElfpFlushFiles (VOID);
+
+
+VOID
+InvalidateContextHandlesForLogFile (
+ PLOGFILE pLogFile
+ );
+
+VOID
+FixContextHandlesForRecord (
+ DWORD RecordOffset,
+ DWORD NewRecordOffset
+ );
+
+PLOGFILE
+FindLogFileFromName (
+ PUNICODE_STRING LogFileName
+ );
+
+BOOL
+SendAdminAlert (
+ ULONG MessageID,
+ ULONG NumStrings,
+ UNICODE_STRING *pStrings
+ );
+
+PVOID
+NextRecordPosition (
+ ULONG ReadFlags,
+ PVOID CurrPosition,
+ ULONG CurrRecordLength,
+ PVOID BeginRecord,
+ PVOID EndRecord,
+ PVOID PhysicalEOF,
+ PVOID PhysStart
+ );
+
+VOID
+NotifyChange (
+ PLOGFILE pLogFile
+ );
+
+
+VOID
+WriteQueuedEvents (
+ );
+
+VOID
+FlushQueuedEvents (
+ );
+
+VOID
+PerformWriteRequest ( PELF_REQUEST_RECORD Request
+ );
+
+NTSTATUS
+ElfpCreateLogFileObject(
+ PLOGFILE LogFile,
+ DWORD Type,
+ ULONG GuestAccessRestriction
+ );
+
+VOID
+ElfpDeleteLogFileObject(
+ PLOGFILE LogFile
+ );
+
+VOID
+ElfpCloseAudit(
+ IN LPWSTR SubsystemName,
+ IN IELF_HANDLE ContextHandle
+ );
+
+NTSTATUS
+ElfpAccessCheckAndAudit(
+ IN LPWSTR SubsystemName,
+ IN LPWSTR ObjectTypeName,
+ IN LPWSTR ObjectName,
+ IN OUT IELF_HANDLE ContextHandle,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN ACCESS_MASK DesiredAccess,
+ IN PGENERIC_MAPPING GenericMapping,
+ IN BOOL ForSecurityLog
+ );
+
+NTSTATUS
+ElfCreateWellKnownSids(
+ VOID
+ );
+
+VOID
+ElfFreeWellKnownSids(
+ VOID
+ );
+
+NTSTATUS
+ElfCreateAndSetSD(
+ IN PRTL_ACE_DATA AceData,
+ IN ULONG AceCount,
+ IN PSID OwnerSid OPTIONAL,
+ IN PSID GroupSid OPTIONAL,
+ OUT PSECURITY_DESCRIPTOR *NewDescriptor
+ );
+
+NTSTATUS
+ElfCreateUserSecurityObject(
+ IN PRTL_ACE_DATA AceData,
+ IN ULONG AceCount,
+ IN PSID OwnerSid,
+ IN PSID GroupSid,
+ IN BOOLEAN IsDirectoryObject,
+ IN PGENERIC_MAPPING GenericMapping,
+ OUT PSECURITY_DESCRIPTOR *NewDescriptor
+ );
+
+VOID
+ElfpCreateElfEvent(
+ IN ULONG EventId,
+ IN USHORT EventType,
+ IN USHORT EventCategory,
+ IN USHORT NumStrings,
+ IN LPWSTR * Strings,
+ IN LPVOID Data,
+ IN ULONG DataSize,
+ IN USHORT Flags
+ );
+
+
+VOID
+ElfpCreateQueuedAlert(
+ DWORD MessageId,
+ DWORD NumberOfStrings,
+ LPWSTR Strings[]
+ );
+
+VOID
+ElfpCreateQueuedMessage(
+ DWORD MessageId,
+ DWORD NumberOfStrings,
+ LPWSTR Strings[]
+ );
+
+DWORD
+ElfStatusUpdate(
+ IN DWORD NewState
+ );
+
+DWORD
+GetElState (
+ VOID
+ );
+
+VOID
+ElfpGenerateLogClearedEvent(
+ IELF_HANDLE LogHandle
+ );
+
+VOID
+ElInitStatus(
+ VOID
+ );
+
+VOID
+ElCleanupStatus(
+ VOID
+ );
+
+DWORD
+ElfBeginForcedShutdown(
+ IN BOOL PendingCode,
+ IN DWORD ExitCode,
+ IN DWORD ServiceSpecificCode
+ );
+
+#ifdef _CAIRO_
+
+BOOL
+GetSourceAlertFilterFromRegistry(
+ HANDLE hKeyLogFile,
+ UNICODE_STRING * pswszSourceName,
+ SHORT * psCategoryFilter,
+ SHORT * psSeverityFilter);
+
+BOOL
+TestFilter(
+ WORD NTEventType,
+ SHORT sAlertSeverity);
+
+HRESULT
+RaiseCairoAlert(
+ PLOGMODULE pLogModule,
+ EVENTLOGRECORD * pEventLogRecord);
+
+#endif // _CAIRO_
+
+#endif // ifndef _ELFPROTO_
diff --git a/private/eventlog/server/elfrpc.c b/private/eventlog/server/elfrpc.c
new file mode 100644
index 000000000..0aa37dbeb
--- /dev/null
+++ b/private/eventlog/server/elfrpc.c
@@ -0,0 +1,216 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ ELFRPC.C
+
+Abstract:
+
+ This file contains the routines that handle the RPC calls to the
+ Eventlog service via the Elf APIs.
+
+Author:
+
+ Rajen Shah (rajens) 16-Jul-1991
+
+Revision History:
+
+ 15-Feb-1995 MarkBl
+ Unlink the ElfHandle *prior* to unlinking the module. Otherwise,
+ if another thread happens to coincidentally be in the routine,
+ FindModuleStrucFromAtom, it's not going to get a hit for the
+ module atom.
+
+ 18-May-1994 Danl
+ IELF_HANDLE_rundown: If the eventlog has been shutdown, then
+ we want to skip the code in this routine because most of the
+ resources will have been free'd.
+
+ 31-Jan-1994 Danl
+ IELF_HANDLE_rundown: Notifiee structure was being free'd and then
+ referenced when it's handle was removed from the list. Now this
+ is fixed so it advances to the next Notifiee in the list BEFORE the
+ buffer is free'd.
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+
+
+
+extern DWORD ElState;
+
+
+VOID
+IELF_HANDLE_rundown(
+ IELF_HANDLE ElfHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by the server RPC runtime to run down a
+ Context Handle and to free any allocated data. It also does all
+ the work for ElfrCloseEL.
+
+ It has to undo whatever was done in ElfrOpenEventLog in terms of
+ allocating memory.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+--*/
+
+{
+ PLOGMODULE pModule;
+ NTSTATUS Status;
+ PNOTIFIEE Notifiee;
+ PNOTIFIEE NotifieeToDelete;
+
+ //
+ // Generate an Audit if neccessary
+ //
+ ElfpCloseAudit(L"EventLog",ElfHandle);
+
+ //
+ // If the eventlog service is stopped or in the process of
+ // stopping, then we just want to ignore this rundown and return.
+ //
+ // Note, we don't bother calling GetElState() because that uses
+ // a critical section which may not exist anymore is the
+ // eventlog service has been shutdown.
+ //
+ // The eventlog isn't designed to be shutdown (except when the
+ // system is shutdown), so it isn't real good at cleaning up
+ // its resources.
+ //
+ if (ElState == STOPPING || ElState == STOPPED) {
+ return;
+ }
+
+ ElfDbgPrint(( "[ELF] Run down context handle - 0x%lx\n", ElfHandle ));
+
+ if (ElfHandle->Signature != ELF_CONTEXTHANDLE_SIGN) {
+ ElfDbgPrint (("[ELF] Invalid context handle in rundown routine.\n"));
+ return;
+ }
+
+ pModule = FindModuleStrucFromAtom(ElfHandle->Atom);
+
+ //
+ // This shouldn't ever happen. It means that a handle got created,
+ // and it's module went away without the handle getting closed.
+ //
+
+ if (!pModule) {
+ ElfDbgPrint(("[ELF] - Could not find module for Atom %d on close\n",
+ ElfHandle->Atom));
+ return;
+ }
+
+ UnlinkContextHandle (ElfHandle); // Unlink it from the linked list
+
+ //
+ // If this handle was for a backup module, then we need to
+ // close the file and clean up the data structures. The standard logs
+ // (application, system and security) are never freed.
+ //
+
+ if (ElfHandle->Flags & ELF_LOG_HANDLE_BACKUP_LOG) {
+
+ Status = ElfpCloseLogFile (pModule->LogFile, ELF_LOG_CLOSE_BACKUP);
+
+ UnlinkLogModule(pModule);
+ ElfpFreeBuffer(pModule->ModuleName);
+
+ // What about DECREMENTING RefCount??? -Danl 2/9/94
+
+ if (pModule->LogFile->RefCount == 0) {
+ UnlinkLogFile(pModule->LogFile);
+ RtlDeleteResource ( &pModule->LogFile->Resource );
+ RtlDeleteSecurityObject(&pModule->LogFile->Sd);
+ ElfpFreeBuffer (pModule->LogFile->LogFileName);
+ ElfpFreeBuffer (pModule->LogFile);
+ }
+ ElfpFreeBuffer(pModule);
+
+ }
+ else {
+
+ //
+ // See if this handle had a ElfChangeNotify outstanding, and if so,
+ // remove it from the list. ElfChangeNotify can't be called with a
+ // handle to a backup file.
+
+ //
+ // Get exclusive access to the log file. This will ensure no one
+ // else is accessing the file.
+ //
+
+ RtlAcquireResourceExclusive (
+ &pModule->LogFile->Resource,
+ TRUE // Wait until available
+ );
+
+
+ //
+ // Walk the linked list and remove any entries for this handle
+ //
+
+ Notifiee = CONTAINING_RECORD (
+ pModule->LogFile->Notifiees.Flink,
+ struct _NOTIFIEE,
+ Next
+ );
+
+
+ while (Notifiee->Next.Flink != pModule->LogFile->Notifiees.Flink) {
+
+ //
+ // If it's for this handle, remove it from the list
+ //
+
+ if (Notifiee->Handle == ElfHandle) {
+
+ RemoveEntryList(&Notifiee->Next);
+ NtClose(Notifiee->Event);
+ NotifieeToDelete = Notifiee;
+
+ Notifiee = CONTAINING_RECORD(
+ Notifiee->Next.Flink,
+ struct _NOTIFIEE,
+ Next);
+
+ ElfpFreeBuffer(NotifieeToDelete);
+ }
+ else {
+
+ Notifiee = CONTAINING_RECORD (
+ Notifiee->Next.Flink,
+ struct _NOTIFIEE,
+ Next
+ );
+ }
+ }
+
+ //
+ // Free the resource
+ //
+
+ RtlReleaseResource ( &pModule->LogFile->Resource );
+ }
+
+ ElfpFreeBuffer (ElfHandle); // Free the context-handle structure
+
+ return;
+}
diff --git a/private/eventlog/server/elfsec.c b/private/eventlog/server/elfsec.c
new file mode 100644
index 000000000..5d042cb38
--- /dev/null
+++ b/private/eventlog/server/elfsec.c
@@ -0,0 +1,920 @@
+
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ elfsec.c
+
+
+Author:
+
+ Dan Hinsley (danhi) 28-Mar-1992
+
+Environment:
+
+ Calls NT native APIs.
+
+Revision History:
+
+ 27-Oct-1993 danl
+ Make Eventlog service a DLL and attach it to services.exe.
+ Removed functions that create well-known SIDs. This information
+ is now passed into the Elfmain as a Global data structure containing
+ all well-known SIDs.
+ 28-Mar-1992 danhi
+ created - based on scsec.c in svcctrl by ritaw
+ 03-Mar-1995 markbl
+ Added guest & anonymous logon log access restriction feature.
+
+--*/
+
+#include <eventp.h>
+#include <elfcfg.h>
+
+#define PRIVILEGE_BUF_SIZE 512
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+DWORD
+ElfpGetPrivilege(
+ IN DWORD numPrivileges,
+ IN PULONG pulPrivileges
+ );
+
+DWORD
+ElfpReleasePrivilege(
+ VOID
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Structure that describes the mapping of generic access rights to //
+// object specific access rights for a LogFile object. //
+// //
+//-------------------------------------------------------------------//
+
+static GENERIC_MAPPING LogFileObjectMapping = {
+
+ STANDARD_RIGHTS_READ | // Generic read
+ ELF_LOGFILE_READ,
+
+ STANDARD_RIGHTS_WRITE | // Generic write
+ ELF_LOGFILE_WRITE,
+
+ STANDARD_RIGHTS_EXECUTE | // Generic execute
+ ELF_LOGFILE_START |
+ ELF_LOGFILE_STOP |
+ ELF_LOGFILE_CONFIGURE,
+
+ ELF_LOGFILE_ALL_ACCESS // Generic all
+ };
+
+
+//-------------------------------------------------------------------//
+// //
+// Functions //
+// //
+//-------------------------------------------------------------------//
+
+NTSTATUS
+ElfpCreateLogFileObject(
+ PLOGFILE LogFile,
+ DWORD Type,
+ ULONG GuestAccessRestriction
+ )
+/*++
+
+Routine Description:
+
+ This function creates the security descriptor which represents
+ an active log file.
+
+Arguments:
+
+ LogFile - pointer the the LOGFILE structure for this logfile
+
+Return Value:
+
+
+--*/
+{
+ NTSTATUS Status;
+ DWORD NumberOfAcesToUse;
+
+#define ELF_LOGFILE_OBJECT_ACES 10 // Number of ACEs in this DACL
+
+ RTL_ACE_DATA AceData[ELF_LOGFILE_OBJECT_ACES] = {
+
+ {ACCESS_DENIED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_ALL_ACCESS, &AnonymousLogonSid},
+
+ {ACCESS_DENIED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_ALL_ACCESS, &(ElfGlobalData->AliasGuestsSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_ALL_ACCESS, &(ElfGlobalData->LocalSystemSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_READ | ELF_LOGFILE_CLEAR, &(ElfGlobalData->AliasAdminsSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_BACKUP, &(ElfGlobalData->AliasBackupOpsSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_READ | ELF_LOGFILE_CLEAR, &(ElfGlobalData->AliasSystemOpsSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_READ, &(ElfGlobalData->WorldSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_WRITE, &(ElfGlobalData->AliasAdminsSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_WRITE, &(ElfGlobalData->AliasSystemOpsSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_WRITE, &(ElfGlobalData->WorldSid)}
+ };
+
+ PRTL_ACE_DATA pAceData = NULL;
+
+ //
+ // NON_SECURE logfiles let anyone read/write to them, secure ones
+ // only let admins/local system do this. so for secure files we just
+ // don't use the last ACE
+ //
+ // Adjust the ACL start based on the passed GuestAccessRestriction flag.
+ // The first two aces deny all log access to guests and/or anonymous
+ // logons. The flag, GuestAccessRestriction, indicates that these two
+ // deny access aces should be applied. Note that the deny aces and the
+ // GuestAccessRestriction flag are not applicable to the security log,
+ // since users and anonymous logons, by default, do not have access.
+ //
+
+ switch (Type) {
+
+ case ELF_LOGFILE_SECURITY:
+ pAceData = AceData + 2; // Deny ACEs *not* applicable
+ NumberOfAcesToUse = 3;
+ break;
+
+ case ELF_LOGFILE_SYSTEM:
+ if (GuestAccessRestriction == ELF_GUEST_ACCESS_RESTRICTED) {
+ pAceData = AceData; // Deny ACEs *applicable*
+ NumberOfAcesToUse = 8;
+ }
+ else {
+ pAceData = AceData + 2; // Deny ACEs *not* applicable
+ NumberOfAcesToUse = 6;
+ }
+ break;
+
+ case ELF_LOGFILE_APPLICATION:
+ if (GuestAccessRestriction == ELF_GUEST_ACCESS_RESTRICTED) {
+ pAceData = AceData; // Deny ACEs *applicable*
+ NumberOfAcesToUse = 10;
+ }
+ else {
+ pAceData = AceData + 2; // Deny ACEs *not* applicable
+ NumberOfAcesToUse = 8;
+ }
+ break;
+
+ }
+ Status = RtlCreateUserSecurityObject(
+ pAceData,
+ NumberOfAcesToUse,
+ NULL, // Owner
+ NULL, // Group
+ TRUE, // IsDirectoryObject
+ &LogFileObjectMapping,
+ &LogFile->Sd
+ );
+
+ if (! NT_SUCCESS(Status)) {
+ ElfDbgPrintNC((
+ "[ELF] ElfpCreateLogFileObject: ElfCreateUserSecurityObject "
+ "failed - %X\n", Status));
+ }
+
+ return (Status);
+}
+
+
+VOID
+ElfpDeleteLogFileObject(
+ PLOGFILE LogFile
+ )
+/*++
+
+Routine Description:
+
+ This function deletes the self-relative security descriptor which
+ represents an eventlog logfile object.
+
+Arguments:
+
+ LogFile - pointer the the LOGFILE structure for this logfile
+
+Return Value:
+
+ None.
+
+--*/
+{
+ (void) RtlDeleteSecurityObject(&LogFile->Sd);
+}
+
+
+NTSTATUS
+ElfpAccessCheckAndAudit(
+ IN LPWSTR SubsystemName,
+ IN LPWSTR ObjectTypeName,
+ IN LPWSTR ObjectName,
+ IN OUT IELF_HANDLE ContextHandle,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN ACCESS_MASK DesiredAccess,
+ IN PGENERIC_MAPPING GenericMapping,
+ IN BOOL ForSecurityLog
+ )
+/*++
+
+Routine Description:
+
+ This function impersonates the caller so that it can perform access
+ validation using NtAccessCheckAndAuditAlarm; and reverts back to
+ itself before returning.
+
+Arguments:
+
+ SubsystemName - Supplies a name string identifying the subsystem
+ calling this routine.
+
+ ObjectTypeName - Supplies the name of the type of the object being
+ accessed.
+
+ ObjectName - Supplies the name of the object being accessed.
+
+ ContextHandle - Supplies the context handle to the object. On return, the
+ granted access is written to the AccessGranted field of this structure
+ if this call succeeds.
+
+ SecurityDescriptor - A pointer to the Security Descriptor against which
+ acccess is to be checked.
+
+ DesiredAccess - Supplies desired acccess mask. This mask must have been
+ previously mapped to contain no generic accesses.
+
+ GenericMapping - Supplies a pointer to the generic mapping associated
+ with this object type.
+
+ ForSecurityLog - TRUE if the access check is for the security log.
+ This is a special case that may require a privilege check.
+
+Return Value:
+
+ NT status mapped to Win32 errors.
+
+--*/
+{
+
+ NTSTATUS Status;
+ RPC_STATUS RpcStatus;
+
+ UNICODE_STRING Subsystem;
+ UNICODE_STRING ObjectType;
+ UNICODE_STRING Object;
+
+ BOOLEAN GenerateOnClose=FALSE;
+ NTSTATUS AccessStatus;
+ ACCESS_MASK GrantedAccess = 0;
+ HANDLE ClientToken = NULL;
+ PPRIVILEGE_SET pPrivilegeSet;
+ ULONG PrivilegeSetLength;
+ LUID luid = {0};
+ BOOL fResult=FALSE;
+ ULONG privileges[1];
+
+
+ GenericMapping = &LogFileObjectMapping;
+
+ PrivilegeSetLength = sizeof(PRIVILEGE_SET) + (sizeof(LUID_AND_ATTRIBUTES)*10);
+ pPrivilegeSet = (PPRIVILEGE_SET)LocalAlloc(LMEM_FIXED, PrivilegeSetLength);
+
+ if (pPrivilegeSet == NULL) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: LocalAlloc Failed %d\n",
+ GetLastError()));
+ return(GetLastError());
+ }
+
+ RtlInitUnicodeString(&Subsystem, SubsystemName);
+ RtlInitUnicodeString(&ObjectType, ObjectTypeName);
+ RtlInitUnicodeString(&Object, ObjectName);
+
+ if ((RpcStatus = RpcImpersonateClient(NULL)) != RPC_S_OK) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: Failed to impersonate "
+ "client %08lx\n", RpcStatus));
+
+ LocalFree(pPrivilegeSet);
+ return RpcStatus;
+ }
+
+ //
+ // Get a token handle for the client
+ //
+ Status = NtOpenThreadToken (
+ NtCurrentThread(),
+ TOKEN_QUERY, // DesiredAccess
+ TRUE, // OpenAsSelf
+ &ClientToken);
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: NtOpenThreadToken Failed: "
+ "0x%lx\n",Status));
+
+ goto CleanExit;
+ }
+
+ //
+ // We want to see if we can get the desired access, and if we do
+ // then we also want all our other accesses granted.
+ // MAXIMUM_ALLOWED gives us this.
+ //
+ DesiredAccess |= MAXIMUM_ALLOWED;
+
+ Status = NtAccessCheck(
+ SecurityDescriptor,
+ ClientToken,
+ DesiredAccess,
+ GenericMapping,
+ pPrivilegeSet,
+ &PrivilegeSetLength,
+ &GrantedAccess,
+ &AccessStatus
+ );
+
+ if (! NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: Error calling "
+ "NtAccessCheck %08lx\n", Status));
+ goto CleanExit;
+ }
+
+ if (AccessStatus != STATUS_SUCCESS) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: Access status is %08lx\n",
+ AccessStatus));
+
+ //
+ // MarkBl 1/30/95 : Modified this code a bit to give backup operators
+ // the ability to open the security log for purposes
+ // of backup.
+ //
+
+ if ((AccessStatus == STATUS_ACCESS_DENIED) && (ForSecurityLog))
+ {
+ //
+ // MarkBl 1/30/95 : First, evalutate the existing code (performed
+ // for read or clear access), since its
+ // privilege check is more rigorous than mine.
+ //
+
+ Status = STATUS_ACCESS_DENIED;
+
+ if (!(DesiredAccess & ELF_LOGFILE_WRITE)) {
+
+ //
+ // If read or clear access to the security log is desired,
+ // then we will see if this user passes the privilege check.
+ //
+ //
+ // Do Privilege Check for SeSecurityPrivilege
+ // (SE_SECURITY_NAME).
+ //
+ // MarkBl 1/30/95 : Modified code to fall through on error
+ // instead of the jump to 'CleanExit'.
+ //
+
+ if (LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &luid)) {
+
+ pPrivilegeSet->PrivilegeCount = 1;
+ pPrivilegeSet->Control = PRIVILEGE_SET_ALL_NECESSARY;
+ pPrivilegeSet->Privilege[0].Luid = luid;
+ pPrivilegeSet->Privilege[0].Attributes =
+ SE_PRIVILEGE_ENABLED;
+
+ if (PrivilegeCheck(
+ ClientToken,
+ pPrivilegeSet,
+ &fResult)) {
+ //
+ // If the privilege is enabled, then add READ and
+ // CLEAR to the granted access mask.
+ //
+ if (fResult) {
+ GrantedAccess |= (ELF_LOGFILE_READ |
+ ELF_LOGFILE_CLEAR);
+ Status = STATUS_SUCCESS;
+ }
+ }
+ else {
+ //
+ // Check Failed, Now what?
+ //
+ Status = GetLastError();
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: " \
+ "PrivilegeCheck failed. status is " \
+ "%d\n",Status));
+ }
+ }
+ else {
+ Status = GetLastError();
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: " \
+ "LookupPrivilegeValue failed. status " \
+ "is %d\n",Status));
+ }
+ }
+
+ //
+ // MarkBl 1/30/95 : Finally, my code. If this user has backup
+ // privilege, let the open succeed.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &luid)) {
+
+ pPrivilegeSet->PrivilegeCount = 1;
+ pPrivilegeSet->Control = PRIVILEGE_SET_ALL_NECESSARY;
+ pPrivilegeSet->Privilege[0].Luid = luid;
+ pPrivilegeSet->Privilege[0].Attributes =
+ SE_PRIVILEGE_ENABLED;
+
+ if (PrivilegeCheck(
+ ClientToken,
+ pPrivilegeSet,
+ &fResult)) {
+ if (fResult) {
+ GrantedAccess |= ELF_LOGFILE_BACKUP;
+ Status = STATUS_SUCCESS;
+ }
+ }
+ else {
+ Status = GetLastError();
+ goto CleanExit;
+ }
+ }
+ else {
+ Status = GetLastError();
+ goto CleanExit;
+ }
+ }
+ }
+ else {
+ Status = AccessStatus;
+ }
+ }
+
+
+ //
+ // Revert to Self
+ //
+ if ((RpcStatus = RpcRevertToSelf()) != RPC_S_OK) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: Fail to revert to "
+ "self %08lx\n", RpcStatus));
+ //
+ // We don't return the error status here because we don't want
+ // to write over the other status that is being returned.
+ //
+
+ }
+
+ //
+ // Get SeAuditPrivilege so I can call NtOpenObjectAuditAlarm.
+ // If any of this stuff fails, I don't want the status to overwrite the
+ // status that I got back from the access and privilege checks.
+ //
+
+ privileges[0] = SE_AUDIT_PRIVILEGE;
+ AccessStatus = ElfpGetPrivilege( 1, privileges);
+
+ if (!NT_SUCCESS(AccessStatus)) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: ElfpGetPrivilege "
+ "(Enable) failed. Status is 0x%lx\n",AccessStatus));
+ }
+
+ //
+ // Call the Audit Alarm function.
+ //
+ GenerateOnClose = FALSE;
+ AccessStatus = NtOpenObjectAuditAlarm (
+ &Subsystem,
+ (PVOID)ContextHandle,
+ &ObjectType,
+ &Object,
+ SecurityDescriptor,
+ ClientToken, // Handle ClientToken
+ DesiredAccess,
+ GrantedAccess,
+ pPrivilegeSet, // PPRIVLEGE_SET
+ FALSE, // BOOLEAN ObjectCreation,
+ TRUE, // BOOLEAN AccessGranted,
+ &GenerateOnClose
+ );
+
+ if (!NT_SUCCESS(AccessStatus)) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: NtOpenObjectAuditAlarm "
+ "failed. status is 0x%lx\n",AccessStatus));
+ }
+ else {
+ if (GenerateOnClose) {
+ ContextHandle->Flags |= ELF_LOG_HANDLE_GENERATE_ON_CLOSE;
+ }
+ }
+
+ //
+ // Update the GrantedAccess in the context handle.
+ //
+ ContextHandle->GrantedAccess = GrantedAccess;
+
+ NtClose(ClientToken);
+
+ ElfpReleasePrivilege();
+
+ LocalFree(pPrivilegeSet);
+
+ return(Status);
+
+CleanExit:
+ //
+ // Revert to Self
+ //
+ if ((RpcStatus = RpcRevertToSelf()) != RPC_S_OK) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: Fail to revert to "
+ "self %08lx\n", RpcStatus));
+ //
+ // We don't return the error status here because we don't want
+ // to write over the other status that is being returned.
+ //
+
+ }
+ if (ClientToken != NULL) {
+ NtClose(ClientToken);
+ }
+
+ LocalFree(pPrivilegeSet);
+
+ return(Status);
+}
+
+VOID
+ElfpCloseAudit(
+ IN LPWSTR SubsystemName,
+ IN IELF_HANDLE ContextHandle
+ )
+
+/*++
+
+Routine Description:
+
+ If the GenerateOnClose flag in the ContextHandle is set, then this function
+ calls NtCloseAuditAlarm in order to generate a close audit for this handle.
+
+Arguments:
+
+ ContextHandle - This is a pointer to an ELF_HANDLE structure. This is the
+ handle that is being closed.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UNICODE_STRING Subsystem;
+ NTSTATUS Status;
+ NTSTATUS AccessStatus;
+ ULONG privileges[1];
+
+ RtlInitUnicodeString(&Subsystem, SubsystemName);
+
+ if (ContextHandle->Flags & ELF_LOG_HANDLE_GENERATE_ON_CLOSE) {
+ BOOLEAN WasEnabled = FALSE;
+
+ //
+ // Get Audit Privilege
+ //
+ privileges[0] = SE_AUDIT_PRIVILEGE;
+ AccessStatus = ElfpGetPrivilege( 1, privileges);
+
+ if (!NT_SUCCESS(AccessStatus)) {
+ ElfDbgPrint(("[ELF] ElfpCloseAudit: ElfpGetPrivilege "
+ "(Enable) failed. Status is 0x%lx\n",AccessStatus));
+ }
+
+ //
+ // Generate the Audit.
+ //
+ Status = NtCloseObjectAuditAlarm(
+ &Subsystem,
+ ContextHandle,
+ TRUE);
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] ElfpCloseAudit: NtCloseObjectAuditAlarm Failed: "
+ "0x%lx\n",Status));
+ }
+
+ ContextHandle->Flags &= (~ELF_LOG_HANDLE_GENERATE_ON_CLOSE);
+
+ ElfpReleasePrivilege();
+
+#ifdef REMOVE
+ //
+ // Release Audit Privilege
+ //
+ Status = RtlAdjustPrivilege(
+ SE_AUDIT_PRIVILEGE,
+ FALSE, // Disable
+ FALSE, // Use Process's token
+ &WasEnabled);
+
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] ElfpCloseAudit: RtlAdjustPrivilege "
+ "(Disable) failed. Status is 0x%lx\n",Status));
+ }
+#endif
+
+ }
+ return;
+}
+DWORD
+ElfpGetPrivilege(
+ IN DWORD numPrivileges,
+ IN PULONG pulPrivileges
+ )
+/*++
+
+Routine Description:
+
+ This function alters the privilege level for the current thread.
+
+ It does this by duplicating the token for the current thread, and then
+ applying the new privileges to that new token, then the current thread
+ impersonates with that new token.
+
+ Privileges can be relinquished by calling ElfpReleasePrivilege().
+
+Arguments:
+
+ numPrivileges - This is a count of the number of privileges in the
+ array of privileges.
+
+ pulPrivileges - This is a pointer to the array of privileges that are
+ desired. This is an array of ULONGs.
+
+Return Value:
+
+ NO_ERROR - If the operation was completely successful.
+
+ Otherwise, it returns mapped return codes from the various NT
+ functions that are called.
+
+--*/
+{
+ DWORD status;
+ NTSTATUS ntStatus;
+ HANDLE ourToken;
+ HANDLE newToken;
+ OBJECT_ATTRIBUTES Obja;
+ SECURITY_QUALITY_OF_SERVICE SecurityQofS;
+ ULONG bufLen;
+ ULONG returnLen;
+ PTOKEN_PRIVILEGES pPreviousState;
+ PTOKEN_PRIVILEGES pTokenPrivilege = NULL;
+ DWORD i;
+
+ //
+ // Initialize the Privileges Structure
+ //
+ pTokenPrivilege = (PTOKEN_PRIVILEGES) LocalAlloc(
+ LMEM_FIXED,
+ sizeof(TOKEN_PRIVILEGES) +
+ (sizeof(LUID_AND_ATTRIBUTES) *
+ numPrivileges)
+ );
+
+ if (pTokenPrivilege == NULL) {
+ status = GetLastError();
+ ElfDbgPrint(("[ELF] ElfpGetPrivilege:LocalAlloc Failed %d\n", status));
+ return(status);
+ }
+ pTokenPrivilege->PrivilegeCount = numPrivileges;
+ for (i=0; i<numPrivileges ;i++ ) {
+ pTokenPrivilege->Privileges[i].Luid = RtlConvertLongToLuid(
+ pulPrivileges[i]);
+ pTokenPrivilege->Privileges[i].Attributes = SE_PRIVILEGE_ENABLED;
+
+ }
+
+ //
+ // Initialize Object Attribute Structure.
+ //
+ InitializeObjectAttributes(&Obja,NULL,0L,NULL,NULL);
+
+ //
+ // Initialize Security Quality Of Service Structure
+ //
+ SecurityQofS.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ SecurityQofS.ImpersonationLevel = SecurityImpersonation;
+ SecurityQofS.ContextTrackingMode = FALSE; // Snapshot client context
+ SecurityQofS.EffectiveOnly = FALSE;
+
+ Obja.SecurityQualityOfService = &SecurityQofS;
+
+ //
+ // Allocate storage for the structure that will hold the Previous State
+ // information.
+ //
+ pPreviousState = (PTOKEN_PRIVILEGES) LocalAlloc(
+ LMEM_FIXED,
+ PRIVILEGE_BUF_SIZE
+ );
+ if (pPreviousState == NULL) {
+
+ status = GetLastError();
+
+ ElfDbgPrint(("[ELF] ElfpGetPrivilege: LocalAlloc Failed %d\n",
+ status));
+
+ LocalFree(pTokenPrivilege);
+ return(status);
+
+ }
+
+ //
+ // Open our own Token
+ //
+ ntStatus = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_DUPLICATE,
+ &ourToken);
+
+ if (!NT_SUCCESS(ntStatus)) {
+ ElfDbgPrint(("[ELF] ElfpGetPrivilege: NtOpenThreadToken Failed "
+ "0x%lx" "\n", ntStatus));
+
+ LocalFree(pPreviousState);
+ LocalFree(pTokenPrivilege);
+ return(RtlNtStatusToDosError(ntStatus));
+ }
+
+ //
+ // Duplicate that Token
+ //
+ ntStatus = NtDuplicateToken(
+ ourToken,
+ TOKEN_IMPERSONATE | TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
+ &Obja,
+ FALSE, // Duplicate the entire token
+ TokenImpersonation, // TokenType
+ &newToken); // Duplicate token
+
+ if (!NT_SUCCESS(ntStatus)) {
+ ElfDbgPrint(("[ELF] ElfpGetPrivilege: NtDuplicateToken Failed "
+ "0x%lx" "\n", ntStatus));
+
+ LocalFree(pPreviousState);
+ LocalFree(pTokenPrivilege);
+ NtClose(ourToken);
+ return(RtlNtStatusToDosError(ntStatus));
+ }
+
+ //
+ // Add new privileges
+ //
+ bufLen = PRIVILEGE_BUF_SIZE;
+ ntStatus = NtAdjustPrivilegesToken(
+ newToken, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ pTokenPrivilege, // NewState
+ bufLen, // bufferSize for previous state
+ pPreviousState, // pointer to previous state info
+ &returnLen); // numBytes required for buffer.
+
+ if (ntStatus == STATUS_BUFFER_TOO_SMALL) {
+
+ LocalFree(pPreviousState);
+
+ bufLen = returnLen;
+
+ pPreviousState = (PTOKEN_PRIVILEGES) LocalAlloc(
+ LMEM_FIXED,
+ (UINT) bufLen
+ );
+
+ ntStatus = NtAdjustPrivilegesToken(
+ newToken, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ pTokenPrivilege, // NewState
+ bufLen, // bufferSize for previous state
+ pPreviousState, // pointer to previous state info
+ &returnLen); // numBytes required for buffer.
+
+ }
+ if (!NT_SUCCESS(ntStatus)) {
+ ElfDbgPrint(("[ELF] ElfpGetPrivilege: NtAdjustPrivilegesToken Failed "
+ "0x%lx" "\n", ntStatus));
+
+ LocalFree(pPreviousState);
+ LocalFree(pTokenPrivilege);
+ NtClose(ourToken);
+ NtClose(newToken);
+ return(RtlNtStatusToDosError(ntStatus));
+ }
+
+ //
+ // Begin impersonating with the new token
+ //
+ ntStatus = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ (PVOID)&newToken,
+ (ULONG)sizeof(HANDLE));
+
+ if (!NT_SUCCESS(ntStatus)) {
+ ElfDbgPrint(("[ELF] ElfpGetPrivilege: NtAdjustPrivilegesToken Failed "
+ "0x%lx" "\n", ntStatus));
+
+ LocalFree(pPreviousState);
+ LocalFree(pTokenPrivilege);
+ NtClose(ourToken);
+ NtClose(newToken);
+ return(RtlNtStatusToDosError(ntStatus));
+ }
+
+ //
+ // BUGBUG: Do I need to keep some of this around to pass to the
+ // ReleasePrivilege function?
+ //
+ LocalFree(pPreviousState);
+ LocalFree(pTokenPrivilege);
+ NtClose(ourToken);
+ NtClose(newToken);
+
+ return(NO_ERROR);
+}
+
+DWORD
+ElfpReleasePrivilege(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function relinquishes privileges obtained by calling ElfpGetPrivilege().
+
+Arguments:
+
+ none
+
+Return Value:
+
+ NO_ERROR - If the operation was completely successful.
+
+ Otherwise, it returns mapped return codes from the various NT
+ functions that are called.
+
+
+--*/
+{
+ NTSTATUS ntStatus;
+ HANDLE NewToken;
+
+
+ //
+ // BUGBUG: Do I need to Adjust the Privileges back to what they
+ // were first? (if so, I need somemore info passed to this fcn)
+ //
+
+ //
+ // Revert To Self.
+ //
+ NewToken = NULL;
+
+ ntStatus = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ (PVOID)&NewToken,
+ (ULONG)sizeof(HANDLE));
+
+ if ( !NT_SUCCESS(ntStatus) ) {
+ return(RtlNtStatusToDosError(ntStatus));
+ }
+
+
+ return(NO_ERROR);
+}
diff --git a/private/eventlog/server/elfutil.c b/private/eventlog/server/elfutil.c
new file mode 100644
index 000000000..6d46081c6
--- /dev/null
+++ b/private/eventlog/server/elfutil.c
@@ -0,0 +1,1737 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ ELFUTIL.C
+
+Abstract:
+
+ This file contains all the utility routines for the Eventlog service.
+
+Author:
+
+ Rajen Shah (rajens) 16-Jul-1991
+
+
+Revision History:
+
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <lmalert.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+
+PLOGMODULE
+FindModuleStrucFromAtom ( ATOM Atom
+ )
+
+/*++
+
+Routine Description:
+
+ This routine scans the list of module structures and finds the one
+ that matches the module atom.
+
+Arguments:
+
+ Atom contains the atom matching the module name.
+
+Return Value:
+
+ A pointer to the log module structure is returned.
+ NULL if no matching atom is found.
+
+Note:
+
+--*/
+{
+
+ PLOGMODULE ModuleStruc;
+
+ // Lock the linked list
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&LogModuleCritSec);
+
+ ModuleStruc = CONTAINING_RECORD (
+ LogModuleHead.Flink,
+ LOGMODULE,
+ ModuleList
+ );
+
+ while ((ModuleStruc->ModuleList.Flink != &LogModuleHead)
+ && (ModuleStruc->ModuleAtom != Atom)) {
+
+ ModuleStruc = CONTAINING_RECORD (
+ ModuleStruc->ModuleList.Flink,
+ LOGMODULE,
+ ModuleList
+ );
+ }
+
+ // Unlock the linked list
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&LogModuleCritSec);
+
+ return (ModuleStruc->ModuleAtom == Atom ? ModuleStruc : NULL);
+}
+
+
+
+PLOGMODULE
+GetModuleStruc ( PUNICODE_STRING ModuleName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns a pointer to the log module structure for the
+ module specified in ModuleName. If none exists, the default structure
+ for application is returned.
+
+Arguments:
+
+ ModuleName contains the name of the module.
+
+Return Value:
+
+ A pointer to the log module structure is returned.
+
+Note:
+
+
+--*/
+{
+
+ NTSTATUS Status;
+ ATOM ModuleAtom;
+ ANSI_STRING ModuleNameA;
+
+ ElfDbgPrint(("[ELF] GetModuleStruc: "));
+
+ Status = RtlUnicodeStringToAnsiString (
+ &ModuleNameA,
+ ModuleName,
+ TRUE
+ );
+
+ ASSERT (NT_SUCCESS(Status)); // This MUST NOT fail.
+
+ //
+ // Guarantee that it's NULL terminated
+ //
+
+ ModuleNameA.Buffer[ModuleNameA.Length] = '\0';
+
+ ElfDbgPrint((" Module: %Z ", &ModuleNameA));
+
+ ModuleAtom = FindAtomA( ModuleNameA.Buffer );
+
+ RtlFreeAnsiString (&ModuleNameA);
+
+ if (ModuleAtom == (ATOM)0) {
+
+ ElfDbgPrint((" No atom found. Defaulting to APPLICATION.\n"));
+ return (ElfDefaultLogModule);
+
+ } else {
+
+ ElfDbgPrint((" Atom = %d \n", ModuleAtom));
+
+ return (FindModuleStrucFromAtom( ModuleAtom ));
+
+ }
+
+}
+
+
+
+VOID
+UnlinkContextHandle (IELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine unlinks the LogHandle specified from the linked list of
+ context handles.
+ In order to protect against multiple thread/process access to the
+ list at the same time, we use a critical section.
+
+Arguments:
+
+ LogHandle points to a context handle structure.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ // Lock the linked list
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
+
+
+ // Remove this entry
+
+ RemoveEntryList (&LogHandle->Next);
+
+
+ // Unlock the linked list
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
+
+}
+
+
+
+VOID
+LinkContextHandle (IELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine links the LogHandle specified into the linked list of
+ context handles.
+ In order to protect against multiple thread/process access to the
+ list at the same time, we use a critical section.
+
+Arguments:
+
+ LogHandle points to a context handle structure.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ ASSERT(LogHandle->Signature == ELF_CONTEXTHANDLE_SIGN);
+
+ // Lock the linked list
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
+
+
+ // Place structure at the beginning of the list.
+
+ InsertHeadList (&LogHandleListHead, &LogHandle->Next);
+
+
+ // Unlock the linked list
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
+
+}
+
+
+VOID
+UnlinkQueuedEvent (PELF_QUEUED_EVENT QueuedEvent
+ )
+
+/*++
+
+Routine Description:
+
+ This routine unlinks the QueuedEvent specified from the linked list of
+ QueuedEvents.
+ In order to protect against multiple thread/process access to the
+ list at the same time, we use a critical section.
+
+Arguments:
+
+ QueuedEvent - The request to remove from the linked list
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ // Lock the linked list
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
+
+
+ // Remove this entry
+
+ RemoveEntryList (&QueuedEvent->Next);
+
+
+ // Unlock the linked list
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
+
+}
+
+
+
+VOID
+LinkQueuedEvent (PELF_QUEUED_EVENT QueuedEvent
+ )
+
+/*++
+
+Routine Description:
+
+ This routine links the QueuedEvent specified into the linked list of
+ QueuedEvents.
+ In order to protect against multiple thread/process access to the
+ list at the same time, we use a critical section.
+
+Arguments:
+
+ QueuedEvent - The request to add from the linked list
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ // Lock the linked list
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
+
+
+ // Place structure at the beginning of the list.
+
+ InsertHeadList (&QueuedEventListHead, &QueuedEvent->Next);
+
+
+ // Unlock the linked list
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
+
+}
+
+
+DWORD
+WINAPI
+ElfpSendMessage (LPVOID UnUsed
+ )
+
+/*++
+
+Routine Description:
+
+ This routines just uses MessageBox to pop up a message.
+
+ This is it's own routine so we can spin a thread to do this, in case the
+ user doesn't hit the OK button for a while.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+Note:
+
+--*/
+{
+
+ PVOID MessageBuffer;
+ HANDLE hLibrary;
+ LPWSTR * StringPointers;
+ DWORD i;
+ PELF_QUEUED_EVENT QueuedEvent;
+ PELF_QUEUED_EVENT FlushEvent;
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&QueuedMessageCritSec);
+
+ //
+ // First get a handle to the message file used for the message text
+ //
+
+ hLibrary = LoadLibraryW(L"NETMSG.DLL");
+
+ //
+ // Walk the linked list and process each element
+ //
+
+ QueuedEvent = CONTAINING_RECORD (
+ QueuedMessageListHead.Flink,
+ struct _ELF_QUEUED_EVENT,
+ Next
+ );
+
+ while (QueuedEvent->Next.Flink != QueuedMessageListHead.Flink) {
+
+ ASSERT(QueuedEvent->Type == Message);
+
+ // Unlock the linked list
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&QueuedMessageCritSec);
+
+ //
+ // Build the array of pointers to the insertion strings
+ //
+
+ StringPointers = (LPWSTR *) ElfpAllocateBuffer(
+ QueuedEvent->Event.Message.NumberOfStrings * sizeof(LPWSTR));
+
+ if (StringPointers && hLibrary) {
+
+ //
+ // Build the array of pointers to the insertion string(s)
+ //
+
+ if (QueuedEvent->Event.Message.NumberOfStrings) {
+ StringPointers[0] = (LPWSTR)
+ ((PBYTE) &QueuedEvent->Event.Message +
+ sizeof(ELF_MESSAGE_RECORD));
+
+ for (i = 1; i < QueuedEvent->Event.Message.NumberOfStrings;
+ i++) {
+ StringPointers[i] = StringPointers[i-1] +
+ wcslen(StringPointers[i-1]) + 1;
+ }
+ }
+
+ //
+ // Call FormatMessage to build the message
+ //
+
+ if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_ARGUMENT_ARRAY |
+ FORMAT_MESSAGE_FROM_HMODULE,
+ (LPVOID) hLibrary,
+ QueuedEvent->Event.Message.MessageId,
+ 0, // Language ID defaulted
+ (LPWSTR) & MessageBuffer,
+ 0, // Is this ignored if allocate_buffer?
+ (va_list *) StringPointers)) {
+
+ //
+ // Now actually display it
+ //
+
+ MessageBoxW(NULL, (LPWSTR) MessageBuffer, GlobalMessageBoxTitle,
+ MB_OK | MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_SERVICE_NOTIFICATION);
+ }
+
+ ElfpFreeBuffer(StringPointers);
+ }
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&QueuedMessageCritSec);
+
+ //
+ // Move to the next one, saving this one to delete it
+ //
+
+ FlushEvent = QueuedEvent;
+
+ QueuedEvent = CONTAINING_RECORD (
+ QueuedEvent->Next.Flink,
+ struct _ELF_QUEUED_EVENT,
+ Next
+ );
+
+ //
+ // Now remove this from the queue and free it if we successfully
+ // processed it
+ //
+
+ RemoveEntryList (&FlushEvent->Next);
+
+ }
+
+ FreeLibrary(hLibrary);
+
+ MBThreadHandle = NULL;
+
+ // Unlock the linked list
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&QueuedMessageCritSec);
+
+ return(0);
+}
+
+VOID
+LinkQueuedMessage (PELF_QUEUED_EVENT QueuedEvent
+ )
+
+/*++
+
+Routine Description:
+
+ This routine links the QueuedEvent specified into the linked list of
+ QueuedMessages. If there's not already a messagebox thread running,
+ it starts one.
+ In order to protect against multiple thread/process access to the
+ list at the same time, we use a critical section.
+
+Arguments:
+
+ QueuedEvent - The request to add from the linked list
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ DWORD ThreadId;
+
+ // Lock the linked list
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&QueuedMessageCritSec);
+
+
+ // Place structure at the end of the list.
+
+ InsertTailList (&QueuedMessageListHead, &QueuedEvent->Next);
+
+ if (!MBThreadHandle) {
+ //
+ // Since the user can just let this sit on their screen,
+ // spin a thread for this
+ //
+
+ MBThreadHandle = CreateThread(NULL, // lpThreadAttributes
+ 4096, // dwStackSize
+ ElfpSendMessage, // lpStartAddress
+ NULL, // lpParameter
+ 0L, // dwCreationFlags
+ &ThreadId // lpThreadId
+ );
+ }
+
+ // Unlock the linked list
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&QueuedMessageCritSec);
+
+}
+
+
+VOID
+NotifyChange (PLOGFILE pLogFile
+ )
+
+/*++
+
+Routine Description:
+
+ This routine runs the list of events that are registered with
+ ElfChangeNotify to be notified when a log has changed, and pulses
+ the event.
+
+ In order to protect against multiple thread/process access to the
+ list at the same time, we use an exclusive resource.
+
+Arguments:
+
+ LogHandle points to a context handle structure.
+
+Return Value:
+
+ NONE
+
+Note:
+
+--*/
+{
+
+ //
+ // How frequently will I try to pulse the events? How about every
+ // 5 seconds
+ //
+
+#define MINIMUM_PULSE_TIME 5
+
+ PNOTIFIEE Notifiee;
+ static ULONG LastPulseTime = 0;
+ LARGE_INTEGER Time;
+ ULONG CurrentTime;
+ NTSTATUS Status;
+
+ //
+ // Get exclusive access to the log file. This will ensure no one
+ // else is accessing the file.
+ //
+
+ RtlAcquireResourceExclusive (
+ &pLogFile->Resource,
+ TRUE // Wait until available
+ );
+ //
+ // See if we've done this in the last MINIMUM_PULSE_TIME seconds
+ //
+
+ Status = NtQuerySystemTime(&Time);
+ RtlTimeToSecondsSince1970(&Time, &CurrentTime);
+
+ if (NT_SUCCESS(Status)) {
+ CurrentTime = CurrentTime - MINIMUM_PULSE_TIME;
+ if (CurrentTime > LastPulseTime) {
+
+ //
+ // Remember that we pulsed
+ //
+
+ LastPulseTime = CurrentTime;
+
+ //
+ // Walk the linked list and and pulse any events
+ //
+
+ Notifiee = CONTAINING_RECORD (
+ pLogFile->Notifiees.Flink,
+ struct _NOTIFIEE,
+ Next
+ );
+
+
+ while (Notifiee->Next.Flink != pLogFile->Notifiees.Flink) {
+
+ //
+ // Pulse each event as we get to it.
+ //
+
+ NtPulseEvent(Notifiee->Event,NULL);
+
+ Notifiee = CONTAINING_RECORD (
+ Notifiee->Next.Flink,
+ struct _NOTIFIEE,
+ Next
+ );
+ }
+ }
+ }
+
+ //
+ // Free the resource
+ //
+
+ RtlReleaseResource ( &pLogFile->Resource );
+}
+
+
+
+VOID
+WriteQueuedEvents (
+ )
+
+/*++
+
+Routine Description:
+
+ This routine runs the list of queued events and writes them.
+
+ In order to protect against multiple thread/process access to the
+ list at the same time, we use an exclusive resource.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+Note:
+
+--*/
+{
+
+ PELF_QUEUED_EVENT QueuedEvent;
+ PELF_QUEUED_EVENT FlushEvent;
+ BOOLEAN bFlushEvent;
+ LARGE_INTEGER Time;
+ ULONG CurrentTime;
+ static ULONG LastAlertTried = 0;
+ static BOOLEAN LastAlertFailed = FALSE;
+
+ // Lock the linked list, you must get the System Log File Resource
+ // first, it is the higher level lock
+
+ RtlAcquireResourceExclusive (
+ &ElfModule->LogFile->Resource,
+ TRUE // Wait until available
+ );
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
+
+ //
+ // Walk the linked list and process each element
+ //
+
+ QueuedEvent = CONTAINING_RECORD (
+ QueuedEventListHead.Flink,
+ struct _ELF_QUEUED_EVENT,
+ Next
+ );
+
+
+ while (QueuedEvent->Next.Flink != QueuedEventListHead.Flink) {
+
+ //
+ // Default is to flush the event after processing
+ //
+
+ bFlushEvent = TRUE;
+
+ //
+ // Do the appropriate thing
+ //
+
+ if (QueuedEvent->Type == Event) {
+ PerformWriteRequest (&QueuedEvent->Event.Request);
+ }
+ else if (QueuedEvent->Type == Alert) {
+
+ //
+ // Don't even try to send failed alerts quicker than once a minute
+ //
+
+ NtQuerySystemTime(&Time);
+ RtlTimeToSecondsSince1970(&Time, &CurrentTime);
+
+ if (!LastAlertFailed || CurrentTime > LastAlertTried + 60) {
+ if (!SendAdminAlert(QueuedEvent->Event.Alert.MessageId,
+ QueuedEvent->Event.Alert.NumberOfStrings,
+ (PUNICODE_STRING)((PBYTE) QueuedEvent +
+ FIELD_OFFSET(ELF_QUEUED_EVENT, Event) +
+ sizeof(ELF_ALERT_RECORD)))) {
+
+ LastAlertFailed = TRUE;
+ LastAlertTried = CurrentTime;
+
+ }
+ else {
+ LastAlertFailed = FALSE;
+ LastAlertTried = CurrentTime;
+ }
+ }
+
+ //
+ // Only try to write it for 5 minutes, then give up (the
+ // alerter service may not be configured to run)
+ //
+
+ if (LastAlertFailed &&
+ QueuedEvent->Event.Alert.TimeOut > CurrentTime) {
+ bFlushEvent = FALSE;
+ }
+ }
+
+ //
+ // Move to the next one, saving this one to delete it
+ //
+
+ FlushEvent = QueuedEvent;
+
+ QueuedEvent = CONTAINING_RECORD (
+ QueuedEvent->Next.Flink,
+ struct _ELF_QUEUED_EVENT,
+ Next
+ );
+
+ //
+ // Now remove this from the queue and free it if we successfully
+ // processed it
+ //
+
+ if (bFlushEvent) {
+ UnlinkQueuedEvent(FlushEvent);
+ ElfpFreeBuffer(FlushEvent);
+ }
+ }
+
+ // Unlock the linked list
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
+ RtlReleaseResource (&ElfModule->LogFile->Resource);
+}
+
+
+VOID
+FlushQueuedEvents (
+ )
+
+/*++
+
+Routine Description:
+
+ This routine runs the list of queued events and frees them.
+
+ In order to protect against multiple thread/process access to the
+ list at the same time, we use an exclusive resource.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+Note:
+
+--*/
+{
+
+ PELF_QUEUED_EVENT QueuedEvent;
+ PELF_QUEUED_EVENT FlushEvent;
+
+ // Lock the linked list
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
+
+ //
+ // Walk the linked list and and free the memory for any events
+ //
+
+ QueuedEvent = CONTAINING_RECORD (
+ QueuedEventListHead.Flink,
+ struct _ELF_QUEUED_EVENT,
+ Next
+ );
+
+
+ while (QueuedEvent->Next.Flink != QueuedEventListHead.Flink) {
+
+ //
+ // Free each event as we get to it.
+ //
+
+ FlushEvent = QueuedEvent;
+
+ QueuedEvent = CONTAINING_RECORD (
+ QueuedEvent->Next.Flink,
+ struct _ELF_QUEUED_EVENT,
+ Next
+ );
+
+ ElfpFreeBuffer(FlushEvent);
+ }
+
+ // Unlock the linked list
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
+}
+
+
+VOID
+UnlinkLogModule (PLOGMODULE LogModule
+ )
+
+/*++
+
+Routine Description:
+
+ This routine unlinks the LogModule specified from the linked list.
+ In order to protect against multiple thread/process access to the
+ list at the same time, we use a critical section.
+
+Arguments:
+
+ LogModule points to a context handle structure.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ // Lock the linked list
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&LogModuleCritSec);
+
+
+ // Remove this entry
+
+ RemoveEntryList (&LogModule->ModuleList);
+
+
+ // Unlock the linked list
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&LogModuleCritSec);
+
+}
+
+
+
+VOID
+LinkLogModule (PLOGMODULE LogModule, ANSI_STRING * pModuleNameA
+ )
+
+/*++
+
+Routine Description:
+
+ This routine links the LogModule specified into the linked list.
+ In order to protect against multiple thread/process access to the
+ list at the same time, we use a critical section.
+
+Arguments:
+
+ LogModule points to a context handle structure.
+ ANSI LogModule name.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ // Lock the linked list
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&LogModuleCritSec);
+
+
+ // Add the atom for this module.
+
+ LogModule->ModuleAtom = AddAtomA(pModuleNameA->Buffer);
+
+ // Place structure at the beginning of the list.
+
+ InsertHeadList (&LogModuleHead, &LogModule->ModuleList);
+
+
+ // Unlock the linked list
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&LogModuleCritSec);
+
+}
+
+
+VOID
+UnlinkLogFile (PLOGFILE pLogFile
+ )
+
+/*++
+
+Routine Description:
+
+ This routine unlinks the LogFile structure specified from the linked
+ list of log files;
+ In order to protect against multiple thread/process access to the
+ list at the same time, we use a critical section.
+
+Arguments:
+
+ pLogFile points to a log file structure.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ // Lock the linked list
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&LogFileCritSec);
+
+
+ // Remove this entry
+
+ RemoveEntryList (&pLogFile->FileList);
+
+
+ // Unlock the linked list
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&LogFileCritSec);
+
+}
+
+
+
+VOID
+LinkLogFile (PLOGFILE pLogFile
+ )
+
+/*++
+
+Routine Description:
+
+ This routine links the LogFile specified into the linked list of
+ log files.
+ In order to protect against multiple thread/process access to the
+ list at the same time, we use a critical section.
+
+Arguments:
+
+ pLogFile points to a context handle structure.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ // Lock the linked list
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&LogFileCritSec);
+
+
+ // Place structure at the beginning of the list.
+
+ InsertHeadList (&LogFilesHead, &pLogFile->FileList);
+
+
+ // Unlock the linked list
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&LogFileCritSec);
+
+}
+
+
+
+VOID
+GetGlobalResource (DWORD Type
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the global resource either for shared access or
+ exclusive access depending on the value of Type. It waits forever for
+ the resource to become available.
+
+Arguments:
+
+ Type is one of ELF_GLOBAL_SHARED or ELF_GLOBAL_EXCLUSIVE.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ BOOL Acquired;
+
+ if (Type & ELF_GLOBAL_SHARED) {
+
+ Acquired = RtlAcquireResourceShared(
+ &GlobalElfResource,
+ TRUE // Wait forever
+ );
+
+ } else { // Assume EXCLUSIVE
+
+ Acquired = RtlAcquireResourceExclusive(
+ &GlobalElfResource,
+ TRUE // Wait forever
+ );
+
+ }
+ ASSERT (Acquired); // This must always be TRUE.
+
+}
+
+
+
+VOID
+ReleaseGlobalResource()
+
+/*++
+
+Routine Description:
+
+ This routine releases the global resource.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ RtlReleaseResource ( &GlobalElfResource );
+}
+
+
+
+NTSTATUS
+ElfImpersonateClient(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function calls RpcImpersonateClient to impersonate the current
+ caller of an API.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS (which is the same as RPC_STATUS)
+
+--*/
+{
+ RPC_STATUS RpcStatus;
+
+
+ if ((RpcStatus = RpcImpersonateClient(NULL)) != RPC_S_OK) {
+ ElfDbgPrintNC(("[ELF] Fail to impersonate client %lx\n", RpcStatus));
+ }
+
+ return (I_RpcMapWin32Status(RpcStatus));
+}
+
+
+
+NTSTATUS
+ElfRevertToSelf(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function calls RpcRevertToSelf to undo an impersonation.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS (which is the same as RPC_STATUS)
+
+--*/
+{
+ RPC_STATUS RpcStatus;
+
+
+ if ((RpcStatus = RpcRevertToSelf()) != RPC_S_OK) {
+ ElfDbgPrintNC(("[ELF] Fail to revert to self %lx\n", RpcStatus));
+ ASSERT(FALSE);
+ }
+
+ return (RpcStatus);
+}
+
+
+
+VOID
+InvalidateContextHandlesForLogFile (
+ PLOGFILE pLogFile
+ )
+/*++
+
+Routine Description:
+
+ This routine walks through the context handles and marks the ones
+ that point to the LogFile passed in as "invalid for read".
+
+Arguments:
+
+ Pointer to log file structure.
+
+Return Value:
+
+ NONE.
+
+Note:
+
+
+--*/
+{
+
+ IELF_HANDLE LogHandle;
+ PLOGMODULE pLogModule;
+
+ //
+ // Lock the context handle list
+ //
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
+
+ //
+ // Walk the linked list and mark any matching context handles as
+ // invalid.
+ //
+
+ LogHandle = CONTAINING_RECORD (
+ LogHandleListHead.Flink,
+ struct _IELF_HANDLE,
+ Next
+ );
+
+
+ while (LogHandle->Next.Flink != LogHandleListHead.Flink) {
+
+ pLogModule = FindModuleStrucFromAtom (LogHandle->Atom);
+
+ ASSERT(pLogModule);
+
+ if (pLogFile == pLogModule->LogFile) {
+ LogHandle->Flags |= ELF_LOG_HANDLE_INVALID_FOR_READ;
+ }
+
+ LogHandle = CONTAINING_RECORD (
+ LogHandle->Next.Flink,
+ struct _IELF_HANDLE,
+ Next
+ );
+ }
+
+ //
+ // Unlock the context handle list
+ //
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
+
+}
+
+VOID
+FixContextHandlesForRecord (
+ DWORD RecordOffset,
+ DWORD NewRecordOffset
+ )
+/*++
+
+Routine Description:
+
+ This routine makes sure that the record starting at RecordOffset isn't
+ the current record for any open handle. If it is, the handle is adjusted
+ to point to the next record.
+
+Arguments:
+
+ RecordOffset - The byte offset in the log of the record that is about
+ to be overwritten.
+ NewStartingRecord - The new location to point the handle to (this is the
+ new first record)
+
+Return Value:
+
+ NONE.
+
+Note:
+
+
+--*/
+{
+
+ IELF_HANDLE LogHandle;
+
+ //
+ // Lock the context handle list
+ //
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
+
+ //
+ // Walk the linked list and fix any matching context handles
+ //
+
+ LogHandle = CONTAINING_RECORD (
+ LogHandleListHead.Flink,
+ struct _IELF_HANDLE,
+ Next
+ );
+
+
+ while (LogHandle->Next.Flink != LogHandleListHead.Flink) {
+
+ if (LogHandle->SeekBytePos == RecordOffset) {
+ LogHandle->SeekBytePos = NewRecordOffset;
+ }
+
+ LogHandle = CONTAINING_RECORD (
+ LogHandle->Next.Flink,
+ struct _IELF_HANDLE,
+ Next
+ );
+ }
+
+ //
+ // Unlock the context handle list
+ //
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
+
+}
+
+
+PLOGFILE
+FindLogFileFromName (
+ PUNICODE_STRING pFileName
+ )
+/*++
+
+Routine Description:
+
+ This routine looks at all the log files to find one that matches
+ the name passed in.
+
+Arguments:
+
+ Pointer to name of file.
+
+Return Value:
+
+ Matching LOGFILE structure if file in use.
+
+Note:
+
+
+--*/
+{
+ PLOGFILE pLogFile;
+
+ // Lock the linked list
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&LogFileCritSec);
+
+ pLogFile = CONTAINING_RECORD(
+ LogFilesHead.Flink,
+ LOGFILE,
+ FileList
+ );
+
+ while (pLogFile->FileList.Flink != LogFilesHead.Flink) {
+ if (wcscmp (pLogFile->LogFileName->Buffer, pFileName->Buffer) == 0)
+ break;
+
+ pLogFile = CONTAINING_RECORD(
+ pLogFile->FileList.Flink,
+ LOGFILE,
+ FileList
+ );
+ }
+
+ // Unlock the linked list
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&LogFileCritSec);
+
+ if (pLogFile->FileList.Flink == LogFilesHead.Flink) {
+ return(NULL);
+ }
+ else {
+ return (pLogFile);
+ }
+}
+
+
+VOID
+ElfpCreateElfEvent(
+ IN ULONG EventId,
+ IN USHORT EventType,
+ IN USHORT EventCategory,
+ IN USHORT NumStrings,
+ IN LPWSTR * Strings,
+ IN LPVOID Data,
+ IN ULONG DataSize,
+ IN USHORT Flags
+ )
+/*++
+
+Routine Description:
+
+ This creates an request packet to write an event on behalf of the event
+ log service itself. It then queues this packet to a linked list for
+ writing later.
+
+Arguments:
+
+ The fields to use to create the event record
+
+
+Return Value:
+
+ None
+
+Note:
+
+
+--*/
+{
+
+ PELF_QUEUED_EVENT QueuedEvent;
+ PWRITE_PKT WritePkt;
+ PEVENTLOGRECORD EventLogRecord;
+ PBYTE BinaryData;
+ ULONG RecordLength;
+ ULONG StringOffset, DataOffset;
+ USHORT StringsSize;
+ USHORT i;
+ ULONG PadSize;
+ ULONG ModuleNameLen; // Length in bytes
+ ULONG zero = 0; // For pad bytes
+ LARGE_INTEGER Time;
+ ULONG LogTimeWritten;
+ PWSTR ReplaceStrings;
+
+#define ELF_MODULE_NAME L"EventLog"
+
+
+ // LogTimeWritten
+ // We need to generate a time when the log is written. This
+ // gets written in the log so that we can use it to test the
+ // retention period when wrapping the file.
+ //
+
+ NtQuerySystemTime(&Time);
+ RtlTimeToSecondsSince1970(
+ &Time,
+ &LogTimeWritten
+ );
+
+ //
+ // Figure out how big a buffer to allocate
+ //
+
+ ModuleNameLen = (wcslen(ELF_MODULE_NAME) + 1)
+ * sizeof (WCHAR);
+
+ StringOffset = sizeof(EVENTLOGRECORD)
+ + ModuleNameLen
+ + ComputerNameLength;
+
+ //
+ // Calculate the length of strings so that we can see how
+ // much space is needed for that.
+ //
+
+ StringsSize = 0;
+ for (i=0; i<NumStrings; i++) {
+ StringsSize += wcslen(Strings[i]) + 1;
+ }
+
+ //
+ // DATA OFFSET:
+ //
+
+ DataOffset = StringOffset + StringsSize * sizeof(WCHAR);
+
+ //
+ // Determine how big a buffer is needed for the queued event record.
+ //
+
+ RecordLength = sizeof(ELF_QUEUED_EVENT)
+ + sizeof(WRITE_PKT)
+ + DataOffset
+ + DataSize
+ + sizeof(RecordLength); // Size excluding pad bytes
+
+ //
+ // Determine how many pad bytes are needed to align to a DWORD
+ // boundary.
+ //
+
+ PadSize = sizeof(ULONG) - (RecordLength % sizeof(ULONG));
+
+ RecordLength += PadSize; // True size needed
+
+ //
+ // Allocate the buffer for the Eventlog record
+ //
+
+ QueuedEvent = (PELF_QUEUED_EVENT) ElfpAllocateBuffer(RecordLength);
+
+ WritePkt = (PWRITE_PKT) (QueuedEvent + 1);
+
+ if (QueuedEvent != NULL) {
+
+ //
+ // Fill up the event record
+ //
+
+
+ RecordLength -= (sizeof(ELF_QUEUED_EVENT) + sizeof(WRITE_PKT));
+ EventLogRecord = (PEVENTLOGRECORD) (WritePkt + 1);
+
+ EventLogRecord->Length = RecordLength;
+ EventLogRecord->TimeGenerated = LogTimeWritten;
+ EventLogRecord->Reserved = ELF_LOG_FILE_SIGNATURE;
+ EventLogRecord->TimeWritten = LogTimeWritten;
+ EventLogRecord->EventID = EventId;
+ EventLogRecord->EventType = EventType;
+ EventLogRecord->EventCategory = EventCategory;
+ EventLogRecord->ReservedFlags = 0;
+ EventLogRecord->ClosingRecordNumber = 0;
+ EventLogRecord->NumStrings = NumStrings;
+ EventLogRecord->StringOffset = StringOffset;
+ EventLogRecord->DataLength = DataSize;
+ EventLogRecord->DataOffset = DataOffset;
+ EventLogRecord->UserSidLength = 0;
+ EventLogRecord->UserSidOffset = StringOffset;
+
+ //
+ // Fill in the variable-length fields
+ //
+
+ //
+ // STRINGS
+ //
+
+ ReplaceStrings = (PWSTR) ((PBYTE) EventLogRecord
+ + StringOffset
+ );
+
+ for (i=0; i < NumStrings; i++) {
+ wcscpy (ReplaceStrings, Strings[i]);
+ ReplaceStrings += wcslen(Strings[i]) + 1;
+ }
+
+ //
+ // MODULENAME
+ //
+
+ BinaryData = (PBYTE) EventLogRecord + sizeof(EVENTLOGRECORD);
+ RtlMoveMemory (BinaryData,
+ ELF_MODULE_NAME,
+ ModuleNameLen);
+
+ //
+ // COMPUTERNAME
+ //
+
+ BinaryData += ModuleNameLen; // Now point to computername
+
+ RtlMoveMemory (BinaryData,
+ LocalComputerName,
+ ComputerNameLength);
+
+ //
+ // BINARY DATA
+ //
+
+ BinaryData = (PBYTE) ((PBYTE) EventLogRecord + DataOffset);
+ RtlMoveMemory (BinaryData, Data, DataSize);
+
+ //
+ // PAD - Fill with zeros
+ //
+
+ BinaryData += DataSize;
+ RtlMoveMemory (BinaryData, &zero, PadSize);
+
+ //
+ // LENGTH at end of record
+ //
+
+ BinaryData += PadSize; // Point after pad bytes
+ ((PULONG)BinaryData)[0] = RecordLength;
+
+ //
+ // Build the QueuedEvent Packet
+ //
+
+ QueuedEvent->Type = Event;
+ QueuedEvent->Event.Request.Pkt.WritePkt = WritePkt;
+ QueuedEvent->Event.Request.Module = ElfModule;
+ QueuedEvent->Event.Request.Flags = Flags;
+ QueuedEvent->Event.Request.LogFile = ElfModule->LogFile;
+ QueuedEvent->Event.Request.Command = ELF_COMMAND_WRITE;
+ QueuedEvent->Event.Request.Pkt.WritePkt->Buffer =
+ (PVOID) EventLogRecord;
+ QueuedEvent->Event.Request.Pkt.WritePkt->Datasize = RecordLength;
+
+ //
+ // Now Queue it on the linked list
+ //
+
+ LinkQueuedEvent(QueuedEvent);
+
+ }
+}
+
+
+VOID
+ElfpCreateQueuedAlert(
+ DWORD MessageId,
+ DWORD NumberOfStrings,
+ LPWSTR Strings[]
+ )
+{
+ DWORD i;
+ DWORD RecordLength;
+ PELF_QUEUED_EVENT QueuedEvent;
+ PUNICODE_STRING UnicodeStrings;
+ LPWSTR pString;
+ PBYTE ptr;
+ LARGE_INTEGER Time;
+ ULONG CurrentTime;
+
+ //
+ // Turn the input strings into UNICODE_STRINGS and figure out how
+ // big to make the buffer to allocate
+ //
+
+ RecordLength = sizeof(UNICODE_STRING) * NumberOfStrings;
+ UnicodeStrings = ElfpAllocateBuffer(RecordLength);
+ if (!UnicodeStrings) {
+ return;
+ }
+ RecordLength += FIELD_OFFSET(ELF_QUEUED_EVENT, Event) +
+ sizeof(ELF_ALERT_RECORD);
+
+ for (i = 0; i < NumberOfStrings; i++) {
+
+ RtlInitUnicodeString(&UnicodeStrings[i], Strings[i]);
+ RecordLength += UnicodeStrings[i].MaximumLength;
+
+ }
+
+ //
+ // Now allocate what will be the real queued event
+ //
+
+
+ QueuedEvent = ElfpAllocateBuffer(RecordLength);
+ if (!QueuedEvent) {
+ return;
+ }
+
+ QueuedEvent->Type = Alert;
+ QueuedEvent->Event.Alert.MessageId = MessageId;
+ QueuedEvent->Event.Alert.NumberOfStrings = NumberOfStrings;
+
+ //
+ // If we can't send the alert in 5 minutes, give up
+ //
+
+ NtQuerySystemTime(&Time);
+ RtlTimeToSecondsSince1970(&Time, &CurrentTime);
+ QueuedEvent->Event.Alert.TimeOut = CurrentTime + 300;
+
+ //
+ // Move the array of UNICODE_STRINGS into the queued event and
+ // point UnicodeStrings at it. Then fix up the Buffer pointers.
+ //
+
+ ptr = (PBYTE) QueuedEvent + FIELD_OFFSET(ELF_QUEUED_EVENT, Event) +
+ sizeof(ELF_ALERT_RECORD);
+ RtlMoveMemory(ptr, UnicodeStrings,
+ sizeof(UNICODE_STRING) * NumberOfStrings);
+ ElfpFreeBuffer(UnicodeStrings);
+ UnicodeStrings = (PUNICODE_STRING) ptr;
+
+
+ pString = (LPWSTR) (ptr + sizeof(UNICODE_STRING) * NumberOfStrings);
+
+ for (i = 0; i < NumberOfStrings; i++) {
+ RtlMoveMemory(pString, UnicodeStrings[i].Buffer,
+ UnicodeStrings[i].MaximumLength);
+ UnicodeStrings[i].Buffer = pString;
+ pString =
+ (LPWSTR) ((PBYTE) pString + UnicodeStrings[i].MaximumLength);
+ }
+
+ LinkQueuedEvent(QueuedEvent);
+}
+
+VOID
+ElfpCreateQueuedMessage(
+ DWORD MessageId,
+ DWORD NumberOfStrings,
+ LPWSTR Strings[]
+ )
+{
+ DWORD i;
+ DWORD RecordLength = 0;
+ PELF_QUEUED_EVENT QueuedEvent;
+ LPWSTR pString;
+
+ //
+ // Figure out how big to make the buffer to allocate
+ //
+
+ RecordLength = sizeof(ELF_QUEUED_EVENT);
+
+ for (i = 0; i < NumberOfStrings; i++) {
+
+ RecordLength += (wcslen(Strings[i]) + 1) * sizeof(WCHAR);
+
+ }
+
+ //
+ // Now allocate what will be the real queued event
+ //
+
+
+ QueuedEvent = ElfpAllocateBuffer(RecordLength);
+ if (!QueuedEvent) {
+ return;
+ }
+
+ QueuedEvent->Type = Message;
+ QueuedEvent->Event.Message.MessageId = MessageId;
+ QueuedEvent->Event.Message.NumberOfStrings = NumberOfStrings;
+
+ //
+ // Move the array of UNICODE strings into the queued event
+ //
+
+ pString = (LPWSTR) ((PBYTE) QueuedEvent +
+ FIELD_OFFSET(ELF_QUEUED_EVENT, Event) + sizeof(ELF_MESSAGE_RECORD));
+
+ for (i = 0; i < NumberOfStrings; i++) {
+ wcscpy(pString, Strings[i]);
+ pString += wcslen(Strings[i]) + 1;
+ }
+
+ LinkQueuedMessage(QueuedEvent);
+}
diff --git a/private/eventlog/server/eventlog.c b/private/eventlog/server/eventlog.c
new file mode 100644
index 000000000..e2a4b1058
--- /dev/null
+++ b/private/eventlog/server/eventlog.c
@@ -0,0 +1,1510 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ EVENTLOG.C
+
+Abstract:
+
+ This file contains the main routines for the NT OS/2 Event
+ Logging Service.
+
+Author:
+
+ Rajen Shah (rajens) 1-Jul-1991
+
+[Environment:]
+
+ User Mode - Win32, except for NTSTATUS returned by some functions.
+
+Revision History:
+
+ 26-Jan-1994 Danl
+ SetUpModules: Fixed memory leak where the buffers for the enumerated
+ key names were never free'd. Also fixed problem where the size of
+ the MULTI_SZ buffer used for the "Sources" key was calculated by
+ using the names in the registry, while the copying was done
+ using the names in the module list. When registry keys are deleted,
+ the module list entry is retained until the next boot. Since the
+ module list is larger, it would overwrite the MULTI_SZ buffer.
+ 1-Nov-1993 Danl
+ Make Eventlog service a DLL and attach it to services.exe.
+ Pass in GlobalData to Elfmain. This GlobalData structure contains
+ all well-known SIDs and pointers to the Rpc Server (Start & Stop)
+ routines. Get rid of the service process main function.
+ 1-Jul-1991 RajenS
+ created
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <ntrpcp.h>
+#include <elfcfg.h>
+#include <string.h>
+#include <stdlib.h> // getenv()
+#include <tstr.h> // WCSSIZE
+#include <alertmsg.h> // ALERT_ELF manifests
+#include <stdio.h> // printf
+
+#ifdef _CAIRO_
+#include <elfextrn.h>
+#endif // _CAIRO_
+
+//
+// Bit Flags used for Progress Reporting in SetupDataStruct().
+//
+#define LOGFILE_OPENED 0x00000001
+#define MODULE_LINKED 0x00000002
+#define LOGFILE_LINKED 0x00000004
+
+//
+// Local Function Prorotypes
+//
+VOID
+ElfInitMessageBoxTitle(
+ VOID
+ );
+
+
+NTSTATUS
+ElfStartRPCServer ()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status;
+
+
+ ElfDbgPrint(("[ELF] Starting RPC server.\n"));
+
+ Status = RpcpAddInterface (
+ wname_Eventlogsvc,
+ eventlog_ServerIfHandle
+ );
+
+ if (Status != NERR_Success) {
+ ElfDbgPrintNC(("[ELF] RpcpAddInterface = %u\n", Status ));
+
+ }
+ return (I_RpcMapWin32Status(Status)); // Return status to caller
+
+}
+
+
+NTSTATUS
+SetUpDataStruct (
+ PUNICODE_STRING LogFileName,
+ ULONG MaxFileSize,
+ ULONG Retention,
+ ULONG GuestAccessRestriction,
+ PUNICODE_STRING ModuleName,
+ HANDLE hLogFile,
+ ELF_LOG_TYPE LogType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets up the information for one module. It is called from
+ ElfSetUpConfigDataStructs for each module to be configured.
+
+ Module information is passed into this routine and a LOGMODULE structure
+ is created for it. If the logfile associated with this module doesn't
+ exist, a LOGFILE structure is created for it, and added to the linked
+ list of LOGFILE structures. The LOGMODULE is associated with the LOGFILE,
+ and it is added to the linked list of LOGMODULE structures. The logfile
+ is opened and mapped to memory.
+
+ Finally, at the end, this function calls SetUpModules, which looks at
+ all the subkeys in the registry under this logfile, and adds any new ones
+ to the linked list, and updates the Sources MULTI_SZ for the event viewer.
+
+Arguments:
+
+ LogFileName - Name of log file for this module. If this routine needs
+ a copy of this name it will make one, so that the caller can free
+ the name afterwards if that is desired.
+
+ MaxFileSize - Max size of the log file.
+ Retention - Max retention for the file.
+ ModuleName - Name of module that this file is associated with.
+ RegistryHandle - Handle to the root node for this LogFile's info
+ in the registry. This is used to enumerate all the
+ modules under this key.
+
+Return Value:
+
+ Pointer to Module structure that is allocated in this routine.
+ NTSTATUS
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLOGFILE pLogFile=NULL;
+ PLOGMODULE pModule=NULL;
+ ANSI_STRING ModuleNameA;
+ DWORD Type;
+ BOOL bLogFileAllocatedHere=FALSE;
+ PUNICODE_STRING SavedBackupFileName=NULL;
+ DWORD StringLength;
+ PLOGMODULE OldDefaultLogModule=NULL;
+ DWORD Progress = 0L;
+
+ //
+ // Argument check.
+ //
+
+ if ((LogFileName == NULL) ||
+ (LogFileName->Buffer == NULL) ||
+ (ModuleName == NULL))
+ {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ // If the default log file for a module is also being used by another
+ // module, then we just link that same file structure with the other
+ // module.
+ //
+ // Truncate the maximum size of the log file to a 4K boundary.
+ // This is to allow for page granularity.
+ //
+
+ pLogFile = FindLogFileFromName (LogFileName);
+
+ pModule = ElfpAllocateBuffer (sizeof (LOGMODULE) );
+ if (pModule == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ if (pLogFile == NULL) {
+
+ //--------------------------------------
+ // CREATE A NEW LOGFILE !!
+ //--------------------------------------
+ // A logfile by this name doesn't exist yet. So we will create
+ // one so that we can add the module to it.
+ //
+
+ bLogFileAllocatedHere = TRUE;
+
+ pLogFile = ElfpAllocateBuffer (sizeof (LOGFILE) );
+
+ if (pLogFile == NULL) {
+ ElfpFreeBuffer (pModule);
+ return(STATUS_NO_MEMORY);
+ }
+
+ ElfDbgPrint(("[ELF] Set up file data\n"));
+
+ //
+ // Allocate a new LogFileName that can be attached to the
+ // new pLogFile structure.
+ //
+ StringLength = LogFileName->Length + sizeof(WCHAR);
+ SavedBackupFileName = (PUNICODE_STRING) ElfpAllocateBuffer(
+ sizeof(UNICODE_STRING) + StringLength);
+
+ if (SavedBackupFileName == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto ErrorExit;
+ }
+
+ SavedBackupFileName->Buffer = (LPWSTR)((LPBYTE) SavedBackupFileName +
+ sizeof(UNICODE_STRING));
+
+ SavedBackupFileName->Length = LogFileName->Length;
+ SavedBackupFileName->MaximumLength = (USHORT) StringLength;
+ RtlMoveMemory(SavedBackupFileName->Buffer, LogFileName->Buffer,
+ LogFileName->Length);
+ SavedBackupFileName->Buffer[SavedBackupFileName->Length / sizeof(WCHAR)] =
+ L'\0';
+
+ //
+ // This is the first user - RefCount gets incrememted below
+ //
+ pLogFile->RefCount = 0;
+ pLogFile->FileHandle = NULL;
+ pLogFile->LogFileName = SavedBackupFileName;
+ pLogFile->ConfigMaxFileSize = ELFFILESIZE(MaxFileSize);
+ pLogFile->Retention = Retention;
+
+
+ //
+ // Save away the default module name for this file
+ //
+ pLogFile->LogModuleName = ElfpAllocateBuffer(
+ sizeof(UNICODE_STRING) + ModuleName->MaximumLength);
+
+ if (pLogFile->LogModuleName == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto ErrorExit;
+ }
+
+ pLogFile->LogModuleName->MaximumLength = ModuleName->MaximumLength;
+ pLogFile->LogModuleName->Buffer =
+ (LPWSTR)(pLogFile->LogModuleName + 1);
+ RtlCopyUnicodeString(pLogFile->LogModuleName, ModuleName);
+
+ InitializeListHead (&pLogFile->Notifiees);
+
+
+ pLogFile->NextClearMaxFileSize = pLogFile->ConfigMaxFileSize;
+
+ RtlInitializeResource ( &pLogFile->Resource );
+ LinkLogFile ( pLogFile ); // Link it in
+
+ Progress |= LOGFILE_LINKED;
+
+ } // endif (pLogfile == NULL)
+
+ //--------------------------------------
+ // ADD THE MODULE TO THE LOG MODULE LIST
+ //--------------------------------------
+ // Set up the module data structure for the default (which is
+ // the same as the logfile keyname).
+ //
+
+ pLogFile->RefCount++;
+ pModule->LogFile = pLogFile;
+ pModule->ModuleName = (LPWSTR) ModuleName->Buffer;
+
+ Status = RtlUnicodeStringToAnsiString (
+ &ModuleNameA,
+ ModuleName,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ pLogFile->RefCount--;
+ goto ErrorExit;
+ }
+
+ //
+ // Link the new module in.
+ //
+
+ LinkLogModule(pModule, &ModuleNameA);
+
+ RtlFreeAnsiString (&ModuleNameA);
+
+ Progress |= MODULE_LINKED;
+
+ //
+ // Open up the file and map it to memory. Impersonate the
+ // caller so we can use UNC names
+ //
+
+ if (LogType == ElfBackupLog) ElfImpersonateClient();
+
+ Status = ElfOpenLogFile (pLogFile, LogType);
+
+ if (LogType == ElfBackupLog) ElfRevertToSelf();
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF] Couldn't open %ws for module %ws\n",
+ LogFileName->Buffer, ModuleName->Buffer));
+
+ if (LogType != ElfBackupLog) {
+ ElfpCreateQueuedAlert(ALERT_ELF_LogFileNotOpened, 1,
+ &(ModuleName->Buffer));
+ }
+ pLogFile->RefCount--;
+ goto ErrorExit;
+ }
+
+ Progress |= LOGFILE_OPENED;
+ //
+ // If this is the application module, remember the pointer
+ // to use if a module doesn't have an entry in the registry
+ //
+
+ if (!_wcsicmp(ModuleName->Buffer, ELF_DEFAULT_MODULE_NAME)) {
+ OldDefaultLogModule = ElfDefaultLogModule;
+ ElfDefaultLogModule = pModule;
+ }
+
+ //
+ // Create the security descriptor for this logfile. Only
+ // the system and security modules are secured against
+ // reads and writes by world.
+ //
+
+ if (!_wcsicmp(ModuleName->Buffer, ELF_SYSTEM_MODULE_NAME)) {
+ Type = ELF_LOGFILE_SYSTEM;
+ }
+ else if (!_wcsicmp(ModuleName->Buffer, ELF_SECURITY_MODULE_NAME)) {
+ Type = ELF_LOGFILE_SECURITY;
+ }
+ else {
+ Type = ELF_LOGFILE_APPLICATION;
+ }
+
+ //
+ // Create a Security Descriptor for this Logfile
+ // (RtlDeleteSecurityObject() can be used to free
+ // pLogFile->Sd).
+ //
+ Status = ElfpCreateLogFileObject(pLogFile, Type, GuestAccessRestriction);
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrintNC(("[ELF] Could not create the security "
+ "descriptor for logfile %ws\n", ModuleName->Buffer));
+
+ pLogFile->RefCount--;
+ goto ErrorExit;
+ }
+
+ //
+ // Now that we've added the default module name, see if there are any
+ // modules configured to log to this file, and if so, create the module
+ // structures for them.
+ //
+
+ SetUpModules(hLogFile, pLogFile, FALSE);
+
+ return (STATUS_SUCCESS);
+
+ErrorExit:
+
+ if (Progress & LOGFILE_OPENED) {
+ ElfpCloseLogFile(pLogFile, ELF_LOG_CLOSE_BACKUP);
+ }
+
+ if (Progress & MODULE_LINKED) {
+ UnlinkLogModule(pModule);
+ DeleteAtom(pModule->ModuleAtom);
+ }
+
+ if (bLogFileAllocatedHere) {
+
+ if (Progress & LOGFILE_LINKED) {
+ UnlinkLogFile(pLogFile);
+ RtlDeleteResource (&pLogFile->Resource);
+ }
+ if (pLogFile->LogModuleName != NULL) {
+ ElfpFreeBuffer(pLogFile->LogModuleName);
+ }
+ if (SavedBackupFileName != NULL) {
+ ElfpFreeBuffer(SavedBackupFileName);
+ }
+ if (pLogFile != NULL) {
+ ElfpFreeBuffer(pLogFile);
+ }
+ }
+
+ ElfpFreeBuffer(pModule);
+
+ if (OldDefaultLogModule != NULL) {
+ ElfDefaultLogModule = OldDefaultLogModule;
+ }
+ return(Status);
+}
+
+NTSTATUS
+SetUpModules (
+ HANDLE hLogFile,
+ PLOGFILE pLogFile,
+ BOOLEAN bAllowDupes
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets up the information for all modules for a logfile.
+
+ The subkeys under a logfile in the eventlog portion of the registry
+ are enumerated. For each unique subkey, a LOGMODULE structure is
+ created. Each new structures is added to a linked list
+ of modules for that logfile.
+
+ If there was one or more unique subkeys, meaning the list has changed
+ since we last looked, then we go through the entire linked list of
+ log modules, and create a MULTI_SZ list of all the modules. This list
+ is stored in the Sources value for that logfile for the event viewer
+ to use.
+
+ BUGBUG:
+ NOTE: A module is never un-linked from the linked list of log modules
+ even if the registry subkey for it is removed. This should probably
+ be done sometime. It would make the eventlog more robust.
+
+Arguments:
+
+ hLogFile - Registry key for the Log File node
+ pLogFile - pointer to the log file structure
+ bAllowDupes - If true, it's ok to already have a module with the same
+ name (used when processing change notify of registry)
+
+Return Value:
+
+ NTSTATUS - If unsuccessful, it is not a fatal error.
+
+ Even if this status is unsuccessful, me may have been able
+ to store some of the new subkeys in the LogModule list. Also, we
+ may have been able to update the Sources MULTI_SZ list.
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ BYTE Buffer[ELF_MAX_REG_KEY_INFO_SIZE];
+ PKEY_NODE_INFORMATION KeyBuffer = (PKEY_NODE_INFORMATION) Buffer;
+ ULONG ActualSize;
+ PWCHAR SubKeyString;
+ UNICODE_STRING NewModule;
+ ANSI_STRING ModuleNameA;
+ PLOGMODULE pModule;
+ ULONG Index = 0;
+ ATOM Atom;
+ PWCHAR pList;
+ DWORD ListLength = 0;
+ UNICODE_STRING ListName;
+ BOOLEAN ListChanged = FALSE;
+ PLIST_ENTRY pListEntry;
+#ifdef _CAIRO_
+ SHORT sCategory;
+ SHORT sSeverity;
+#endif // _CAIRO_
+
+ //
+ // Create the module structures for all modules under this logfile. We
+ // don't actually need to open the key, since we don't use any information
+ // stored there, it's existence is all we care about here. Any data is
+ // used by the Event Viewer (or any viewing app). If this is used to
+ // setup a backup file, hLogFile is NULL since there aren't any other
+ // modules to map to this file.
+ //
+
+ while (NT_SUCCESS(Status) && hLogFile) {
+
+ Status = NtEnumerateKey(hLogFile, Index++, KeyNodeInformation,
+ KeyBuffer, ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // It turns out the Name isn't null terminated, so we need
+ // to copy it somewhere and null terminate it before we use it
+ //
+
+ SubKeyString = ElfpAllocateBuffer(KeyBuffer->NameLength +
+ sizeof(WCHAR));
+ if (!SubKeyString) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ memcpy(SubKeyString, KeyBuffer->Name, KeyBuffer->NameLength);
+ SubKeyString[KeyBuffer->NameLength / sizeof(WCHAR)] = L'\0' ;
+
+ //
+ // Add the atom for this module name
+ //
+
+ RtlInitUnicodeString(&NewModule, SubKeyString);
+
+ Status = RtlUnicodeStringToAnsiString (
+ &ModuleNameA,
+ &NewModule,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ //
+ // We can't continue, so we will leave the modules
+ // we've linked so far, and move on in an attempt to
+ // create the Sources MULTI_SZ list.
+ //
+ ElfpFreeBuffer(SubKeyString);
+ break;
+ }
+
+ Atom = FindAtomA (ModuleNameA.Buffer);
+
+ //
+ // Make sure we've not already added one by this name
+ //
+
+#ifdef _CAIRO_
+ if (pModule = FindModuleStrucFromAtom(Atom)) {
+#else
+ if (FindModuleStrucFromAtom(Atom)) {
+#endif // _CAIRO_
+
+ //
+ // We've already encountered a module by this name. If
+ // this is init time, it's a configuration error. Report
+ // it and move on. If we're processing a change notify
+ // from the registry, this is ok, so just press on
+ //
+ // ** NEW FOR CAIRO **
+ //
+ // Update the module alert category & severity values. i.e.,
+ // only upon registry change notify.
+ //
+
+ if (!bAllowDupes) {
+
+ ElfDbgPrint(("[ELF] Same module exists in two log files - "
+ "%ws\n", SubKeyString));
+ }
+
+#ifdef _CAIRO_
+ if (GetSourceAlertFilterFromRegistry(hLogFile,
+ &NewModule,
+ &sCategory,
+ &sSeverity))
+ {
+ pModule->AlertCategory = sCategory;
+ pModule->AlertSeverity = sSeverity;
+ }
+#endif // _CAIRO_
+
+ RtlFreeAnsiString (&ModuleNameA);
+ ElfpFreeBuffer(SubKeyString);
+ continue;
+
+ }
+
+ ListChanged = TRUE;
+
+ pModule = ElfpAllocateBuffer (sizeof (LOGMODULE) );
+ if (!pModule) {
+ ElfpFreeBuffer(SubKeyString);
+ return(STATUS_NO_MEMORY);
+ }
+
+ //
+ // Set up a module data structure for this module
+ //
+
+ pModule->LogFile = pLogFile;
+ pModule->ModuleName = SubKeyString;
+
+#ifdef _CAIRO_
+ if (GetSourceAlertFilterFromRegistry(hLogFile,
+ &NewModule,
+ &sCategory,
+ &sSeverity))
+ {
+ pModule->AlertCategory = sCategory;
+ pModule->AlertSeverity = sSeverity;
+ }
+ else
+ {
+ pModule->AlertCategory = pModule->AlertSeverity = 0;
+ }
+#endif // _CAIRO_
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Link the new module in.
+ //
+
+ LinkLogModule(pModule, &ModuleNameA);
+
+ RtlFreeAnsiString (&ModuleNameA);
+ }
+ }
+ }
+
+ if (Status == STATUS_NO_MORE_ENTRIES) {
+
+ //
+ // It's not required that there are configured modules for a log
+ // file.
+ //
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // If the list has changed, or if we've been called during init, and not
+ // as the result of a changenotify on the registry (bAllowDupes == FALSE)
+ // then create the sources key
+ //
+
+ if (hLogFile && (ListChanged || !bAllowDupes)) {
+
+ //
+ // Now create a MULTI_SZ entry with all the module names for eventvwr
+ //
+ // STEP 1: Calculate amount of storage needed by running thru the
+ // module list, finding any module that uses this log file.
+ //
+ pListEntry = LogModuleHead.Flink;
+ while (pListEntry != &LogModuleHead) {
+
+ pModule = CONTAINING_RECORD (pListEntry, LOGMODULE, ModuleList);
+
+ if (pModule->LogFile == pLogFile) {
+ //
+ // This one is for the log we're working on, get the
+ // size of it's name.
+ //
+ ListLength += WCSSIZE(pModule->ModuleName);
+ }
+ pListEntry = pModule->ModuleList.Flink;
+ }
+
+ //
+ // STEP 2: Allocate storage for the MULTI_SZ.
+ //
+ pList = ElfpAllocateBuffer(ListLength + sizeof(WCHAR));
+
+ //
+ // If I can't allocate the list, just press on
+ //
+
+ if (pList) {
+
+ //
+ // STEP 3: Copy all the module names for this logfile into
+ // the MULTI_SZ string.
+ //
+ SubKeyString = pList; // Save this away
+
+ pListEntry = LogModuleHead.Flink;
+
+ while (pListEntry != &LogModuleHead) {
+
+ pModule = CONTAINING_RECORD (
+ pListEntry,
+ LOGMODULE,
+ ModuleList
+ );
+
+ if (pModule->LogFile == pLogFile) {
+
+ //
+ // This one is for the log we're working on, put it in the list
+ //
+
+ wcscpy(pList, pModule->ModuleName);
+ pList += wcslen(pModule->ModuleName);
+ pList++;
+
+ }
+
+ pListEntry = pModule->ModuleList.Flink;
+
+ }
+
+ *pList = L'\0'; // The terminating NULL
+
+ RtlInitUnicodeString(&ListName, L"Sources");
+
+ Status = NtSetValueKey(hLogFile,
+ &ListName,
+ 0,
+ REG_MULTI_SZ,
+ SubKeyString,
+ ListLength + sizeof(WCHAR)
+ );
+
+ ElfpFreeBuffer(SubKeyString);
+ }
+ }
+
+ return(Status);
+
+}
+
+
+NTSTATUS
+ElfSetUpConfigDataStructs (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets up all the necessary data structures for the eventlog
+ service. It enumerates the keys in the Logfiles registry node to
+ determine what to setup.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hLogFile;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING SubKeyName;
+ PUNICODE_STRING pLogFileName = NULL;
+ PUNICODE_STRING pModuleName = NULL;
+ UNICODE_STRING EventlogModuleName;
+ ULONG Index = 0;
+ BYTE Buffer[ELF_MAX_REG_KEY_INFO_SIZE];
+ PKEY_NODE_INFORMATION KeyBuffer = (PKEY_NODE_INFORMATION) Buffer;
+ ULONG ActualSize;
+ LOG_FILE_INFO LogFileInfo;
+ PWCHAR SubKeyString;
+ LPWSTR ModuleName;
+
+ ElfDbgPrint(("[ELF] Set up config data structures\n"));
+
+ //
+ // Initialize the Atom table whose size is the maximum number of
+ // module structures possible, i.e. ELF_MAX_LOG_MODULES.
+ //
+
+ if (! (InitAtomTable ( ELF_MAX_LOG_MODULES ))) {
+ return (STATUS_UNSUCCESSFUL);
+ }
+
+ //
+ // Get a handle to the Logfiles subkey. If it doesn't exist, just use
+ // the hard-coded defaults.
+ //
+
+ if (hEventLogNode) {
+
+ //
+ // Loop thru the subkeys under Eventlog and set up each logfile
+ //
+
+ while (NT_SUCCESS(Status)) {
+
+ Status = NtEnumerateKey(hEventLogNode, Index++, KeyNodeInformation,
+ KeyBuffer, ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // It turns out the Name isn't null terminated, so we need
+ // to copy it somewhere and null terminate it before we use it
+ //
+
+ SubKeyString = ElfpAllocateBuffer(KeyBuffer->NameLength +
+ sizeof(WCHAR));
+ if (!SubKeyString) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ memcpy(SubKeyString, KeyBuffer->Name, KeyBuffer->NameLength);
+ SubKeyString[KeyBuffer->NameLength / sizeof(WCHAR)] = L'\0' ;
+
+ //
+ // Open the node for this logfile and extract the information
+ // required by SetupDataStruct, and then call it.
+ //
+
+ RtlInitUnicodeString(&SubKeyName, SubKeyString);
+
+ InitializeObjectAttributes(&ObjectAttributes,
+ &SubKeyName,
+ OBJ_CASE_INSENSITIVE,
+ hEventLogNode,
+ NULL
+ );
+
+ Status = NtOpenKey(&hLogFile, KEY_READ | KEY_SET_VALUE,
+ &ObjectAttributes);
+ if (!NT_SUCCESS(Status)) {
+ //
+ // Unclear how this could happen since I just enum'ed
+ // it, but if I can't open it, I just pretend like it
+ // wasn't there to begin with.
+ //
+
+ ElfpFreeBuffer(SubKeyString);
+ Status = STATUS_SUCCESS; // so we don't terminate the loop
+ continue;
+ }
+
+ //
+ // Get the information from the registry
+ //
+
+ Status = ReadRegistryInfo(hLogFile, &SubKeyName,
+ & LogFileInfo);
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Now set up the actual data structures. Failures are
+ // dealt with in the routine
+ //
+
+ SetUpDataStruct(LogFileInfo.LogFileName,
+ LogFileInfo.MaxFileSize,
+ LogFileInfo.Retention,
+ LogFileInfo.GuestAccessRestriction,
+ & SubKeyName,
+ hLogFile,
+ ElfNormalLog
+ );
+
+ NtClose(hLogFile);
+
+ }
+ }
+
+ }
+ } // if (hEventLogNode)
+ else {
+
+ //
+ // The information doesn't exist in the registry, set up the
+ // three default logs.
+ //
+
+ pLogFileName = ElfpAllocateBuffer(sizeof(UNICODE_STRING));
+ pModuleName = ElfpAllocateBuffer(sizeof(UNICODE_STRING));
+ if (!pLogFileName || !pModuleName) {
+ return(STATUS_NO_MEMORY);
+ }
+ RtlInitUnicodeString(pLogFileName,
+ ELF_APPLICATION_DEFAULT_LOG_FILE);
+ RtlInitUnicodeString(pModuleName, ELF_DEFAULT_MODULE_NAME);
+ SetUpDataStruct(pLogFileName,
+ ELF_DEFAULT_MAX_FILE_SIZE,
+ ELF_DEFAULT_RETENTION_PERIOD,
+ ELF_GUEST_ACCESS_UNRESTRICTED,
+ pModuleName,
+ NULL,
+ ElfNormalLog
+ );
+
+
+ pLogFileName = ElfpAllocateBuffer(sizeof(UNICODE_STRING));
+ pModuleName = ElfpAllocateBuffer(sizeof(UNICODE_STRING));
+ if (!pLogFileName || !pModuleName) {
+ return(STATUS_NO_MEMORY);
+ }
+ RtlInitUnicodeString(pLogFileName,
+ ELF_SYSTEM_DEFAULT_LOG_FILE);
+ RtlInitUnicodeString(pModuleName, ELF_SYSTEM_MODULE_NAME);
+ SetUpDataStruct(pLogFileName,
+ ELF_DEFAULT_MAX_FILE_SIZE,
+ ELF_DEFAULT_RETENTION_PERIOD,
+ ELF_GUEST_ACCESS_UNRESTRICTED,
+ pModuleName,
+ NULL,
+ ElfNormalLog
+ );
+
+
+ pLogFileName = ElfpAllocateBuffer(sizeof(UNICODE_STRING));
+ pModuleName = ElfpAllocateBuffer(sizeof(UNICODE_STRING));
+ if (!pLogFileName || !pModuleName) {
+ return(STATUS_NO_MEMORY);
+ }
+ RtlInitUnicodeString(pLogFileName,
+ ELF_SECURITY_DEFAULT_LOG_FILE);
+ RtlInitUnicodeString(pModuleName, ELF_SECURITY_MODULE_NAME);
+ SetUpDataStruct(pLogFileName,
+ ELF_DEFAULT_MAX_FILE_SIZE,
+ ELF_DEFAULT_RETENTION_PERIOD,
+ ELF_GUEST_ACCESS_UNRESTRICTED,
+ pModuleName,
+ NULL,
+ ElfNormalLog
+ );
+ }
+
+ //
+ // If we just ran out of keys, that's OK (unless there weren't any at all)
+ //
+
+ if (Status == STATUS_NO_MORE_ENTRIES && Index != 1) {
+ Status = STATUS_SUCCESS;
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Make sure we created the Application log file, since it is the
+ // default. If it wasn't created, use the first module created
+ // (this is at the tail of the list since I insert them at the
+ // head). If this happens, send an alert to the admin.
+ //
+
+ if (!ElfDefaultLogModule) {
+ ElfDbgPrintNC(("[ELF] No Logfile entry for Application module, "
+ "default will be created\n"));
+
+ if (IsListEmpty(&LogModuleHead)) {
+ //
+ // No logs were created, might as well shut down
+ //
+
+ return(STATUS_EVENTLOG_CANT_START);
+ }
+
+ ElfDefaultLogModule = CONTAINING_RECORD(LogModuleHead.Blink,
+ LOGMODULE,
+ ModuleList);
+
+ ModuleName = L"Application";
+ ElfpCreateQueuedAlert(ALERT_ELF_DefaultLogCorrupt, 1,
+ &(ElfDefaultLogModule->LogFile->LogModuleName->Buffer));
+
+ }
+
+ //
+ // Now get the Module for the Eventlog service to use. GetModuleStruc
+ // always succeeds, returning the default log if the requested one
+ // isn't configured.
+ //
+
+ RtlInitUnicodeString(&EventlogModuleName, L"eventlog");
+ ElfModule = GetModuleStruc(&EventlogModuleName);
+
+ } else {
+
+ if (pLogFileName && pModuleName) {
+ ElfDbgPrintNC(("[ELF] Failure Setting up data structs for file %ws, "
+ "Module %ws - %X\n", pLogFileName->Buffer, pModuleName->Buffer,
+ Status));
+ }
+ else {
+ ElfDbgPrintNC(("[ELF] Failure setting up data structs. No logs"
+ " defined in registry\n"));
+ }
+ }
+
+
+ return (Status);
+
+}
+
+
+VOID
+SVCS_ENTRY_POINT( // (ELF_main)
+ DWORD argc,
+ LPWSTR argv[],
+ PSVCS_GLOBAL_DATA SvcsGlobalData,
+ HANDLE SvcRefHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the main routine for the Event Logging Service.
+
+
+Arguments:
+
+ Command-line arguments.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING RootRegistryNode;
+ ULONG Win32Error = 0;
+ ELF_REQUEST_RECORD FlushRequest;
+ UNICODE_STRING ValueName;
+ BYTE Buffer[ELF_MAX_REG_KEY_INFO_SIZE];
+ PKEY_VALUE_FULL_INFORMATION ValueBuffer =
+ (PKEY_VALUE_FULL_INFORMATION) Buffer;
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+
+ UNREFERENCED_PARAMETER(argc);
+ UNREFERENCED_PARAMETER(argv);
+
+ ElfGlobalSvcRefHandle = SvcRefHandle;
+ ElfGlobalData = SvcsGlobalData;
+
+ //
+ // Initialize the list heads for the modules and log files.
+ //
+
+ InitializeListHead ( &LogFilesHead );
+ InitializeListHead ( &LogModuleHead );
+ InitializeListHead ( &QueuedEventListHead );
+ InitializeListHead ( &QueuedMessageListHead );
+
+ //
+ // Initialize to 0 so that we can clean up before exiting
+ //
+
+ EventFlags = 0;
+
+ //
+ //
+ // Tuck away the local computer name
+ //
+
+ ComputerNameLength = 0;
+ GetComputerNameW(LocalComputerName, &ComputerNameLength);
+ ComputerNameLength += sizeof(WCHAR); // account for the NULL
+ LocalComputerName = ElfpAllocateBuffer(ComputerNameLength * sizeof (WCHAR));
+ if (!LocalComputerName ||
+ !GetComputerNameW(LocalComputerName, &ComputerNameLength)) {
+ ComputerNameLength = 0;
+ }
+ ComputerNameLength = (ComputerNameLength + 1) * sizeof(WCHAR);
+
+ //
+ // Initialize the status data.
+ //
+ ElInitStatus();
+
+ // Set up control handler
+ //
+
+ ElfDbgPrint(("[ELF] Calling RegisterServiceCtrlHandler\n"));
+ if ((ElfServiceStatusHandle = RegisterServiceCtrlHandler(
+ wname_Eventlogsvc,
+ ElfControlResponse
+ )) == (SERVICE_STATUS_HANDLE) NULL) {
+
+ Win32Error = GetLastError();
+
+ //
+ // If we got an error, we need to set status to uninstalled, and end the
+ // thread.
+ //
+
+ ElfDbgPrintNC(("[ELF] RegisterServiceCtrlHandler = %u\n",Win32Error));
+ goto cleanupandexit;
+ }
+
+ // Initialize all the status fields so that subsequent calls to
+ // SetServiceStatus only need to update fields that changed.
+ //
+
+ //
+ // Notify the Service Controller for the first time that we are alive
+ // and is in a start pending state
+ //
+ // *** UPDATE STATUS ***
+ ElfStatusUpdate(STARTING);
+
+ //
+ // Get the localized title for message box popups.
+ //
+ ElfInitMessageBoxTitle();
+
+ //
+ // Set up the object that describes the root node for the eventlog service
+ //
+
+ RtlInitUnicodeString(&RootRegistryNode, REG_EVENTLOG_NODE_PATH);
+ InitializeObjectAttributes(&ObjectAttributes,
+ &RootRegistryNode,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ //
+ // If this fails, we'll just use the defaults
+ //
+
+ NtOpenKey(&hEventLogNode, KEY_READ | KEY_NOTIFY, &ObjectAttributes);
+
+ //
+ // See if there's a debug key
+ //
+
+ RtlInitUnicodeString(&ValueName, VALUE_DEBUG);
+
+ NtQueryValueKey(hEventLogNode, &ValueName,
+ KeyValueFullInformation, ValueBuffer,
+ ELF_MAX_REG_KEY_INFO_SIZE, & ElfDebug);
+
+ //
+ // Initialize a critical section for use when adding or removing
+ // LogFiles or LogModules. This must be done before we process any
+ // file information.
+ //
+
+ Status = RtlInitializeCriticalSection(
+ &LogFileCritSec
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ ElfDbgPrintNC(( "ELF log file crit sec init failed: %X\n", Status ));
+ goto cleanupandexit;
+
+ }
+ Status = RtlInitializeCriticalSection(
+ &LogModuleCritSec
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ ElfDbgPrintNC(( "ELF log file crit sec init failed: %X\n", Status ));
+ goto cleanupandexit;
+
+ }
+ EventFlags |= ELF_INIT_LOGFILE_CRIT_SEC;
+
+ Status = RtlInitializeCriticalSection(
+ &QueuedEventCritSec
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ ElfDbgPrintNC(( "ELF queued event crit sec init failed: %X\n", Status ));
+ goto cleanupandexit;
+
+ }
+
+ EventFlags |= ELF_INIT_QUEUED_EVENT_CRIT_SEC;
+
+ Status = RtlInitializeCriticalSection(
+ &QueuedMessageCritSec
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ ElfDbgPrintNC(( "ELF queued message crit sec init failed: %X\n", Status ));
+ goto cleanupandexit;
+
+ }
+
+ EventFlags |= ELF_INIT_QUEUED_MESSAGE_CRIT_SEC;
+
+ //
+ // Initialize global anonymous logon sid for use in log ACL's.
+ //
+
+ Status = RtlAllocateAndInitializeSid(
+ &NtAuthority,
+ 1,
+ SECURITY_ANONYMOUS_LOGON_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &AnonymousLogonSid);
+
+ if ( !NT_SUCCESS(Status) ) {
+ ElfDbgPrintNC(("ELF anonymous log sid creation failed: %X\n",
+ Status ));
+ goto cleanupandexit;
+ }
+
+ //
+ // Set up the data structures for the Logfiles and Modules.
+ //
+
+ Status = ElfSetUpConfigDataStructs ();
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto cleanupandexit;
+ }
+
+ //
+ // Tell service controller of that we are making progress
+ //
+ // *** UPDATE STATUS ***
+ ElfStatusUpdate(STARTING);
+
+ //
+ // Initialize a critical section for use when adding or removing
+ // context-handles (LogHandles).
+ //
+
+ Status = RtlInitializeCriticalSection(
+ &LogHandleCritSec
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ ElfDbgPrintNC(( "ELF log handle crit sec init failed: %X\n", Status ));
+ goto cleanupandexit;
+
+ }
+ EventFlags |= ELF_INIT_LOGHANDLE_CRIT_SEC;
+
+ //
+ // Initialize the context handle (log handle) list.
+ //
+
+ InitializeListHead( &LogHandleListHead );
+
+ //
+ // Initialize the Global Resource.
+ //
+
+ RtlInitializeResource ( &GlobalElfResource );
+ EventFlags |= ELF_INIT_GLOBAL_RESOURCE;
+
+ //
+ // Tell service controller of that we are making progress
+ //
+ // *** UPDATE STATUS ***
+ ElfStatusUpdate(STARTING);
+
+ //
+ // Tell service controller of that we are making progress
+ //
+ // *** UPDATE STATUS ***
+ ElfStatusUpdate(STARTING);
+
+ // Create a thread for watching the LPC port.
+ //
+
+ if (!StartLPCThread ()) {
+ Status = STATUS_UNSUCCESSFUL;
+ goto cleanupandexit;
+ }
+ EventFlags |= ELF_STARTED_LPC_THREAD;
+
+ //
+ // Tell service controller of that we are making progress
+ //
+ // *** UPDATE STATUS ***
+ ElfStatusUpdate(STARTING);
+
+#ifdef _CAIRO_
+ //
+ // The eventlog service links to ALERTSYS.DLL by hand (eventlog.c) after
+ // eventlog initialization, since this dll's initialization code requires
+ // a running eventlog service.
+ //
+ // By no means, fail to start this service if something fails here.
+ // It just won't be possible to raise NT events as Cairo alerts.
+ //
+ // BUGBUG : Should probably at least log an error.
+ // Should the service state be STARTING while this
+ // initialization is in progress?
+ //
+
+ if ((ghAlertSysDll = LoadLibrary(L"ALERTSYS.DLL")) != NULL)
+ {
+ //
+ // Get ReportAlert API address.
+ //
+
+ if ((gpfReportAlert = (PREPORTALERT)GetProcAddress(
+ (HMODULE)ghAlertSysDll,
+ "ReportAlert")) == NULL)
+ {
+ FreeLibrary(ghAlertSysDll);
+ ghAlertSysDll = NULL;
+ ElfDbgPrintNC((
+ "[ELF] ReportAlert GetProAddress failed, WIN32 error(%x)\n",
+ GetLastError()));
+ }
+ }
+ else
+ {
+ ElfDbgPrintNC((
+ "[ELF] LoadLibrary of ALERTSYS.DLL failed, WIN32 error(%x)\n",
+ GetLastError()));
+ }
+
+ //
+ // Tell service controller of that we are making progress
+ //
+ // *** UPDATE STATUS ***
+ ElfStatusUpdate(STARTING);
+
+#endif // _CAIRO_
+
+ // Create a thread for watching for changes in the registry.
+ //
+
+ if (!ElfStartRegistryMonitor ()) {
+ Status = STATUS_UNSUCCESSFUL;
+ goto cleanupandexit;
+ }
+ EventFlags |= ELF_STARTED_REGISTRY_MONITOR;
+
+ //
+ // Write out an event that says we started
+ //
+
+ ElfpCreateElfEvent(EVENT_EventlogStarted,
+ EVENTLOG_INFORMATION_TYPE,
+ 0, // EventCategory
+ 0, // NumberOfStrings
+ NULL, // Strings
+ NULL, // Data
+ 0, // Datalength
+ 0 // flags
+ );
+
+ //
+ // Write out any events that were queued up during initialization
+ //
+
+ FlushRequest.Command = ELF_COMMAND_WRITE_QUEUED;
+
+ ElfPerformRequest(&FlushRequest);
+
+ //
+ // Tell service controller of that we are making progress
+ //
+ // *** UPDATE STATUS ***
+ ElfStatusUpdate(STARTING);
+
+ //
+ // Finish setting up the RPC server
+ //
+ // NOTE: Now all RPC servers in services.exe share the same pipe name.
+ // However, in order to support communication with version 1.0 of WinNt,
+ // it is necessary for the Client Pipe name to remain the same as
+ // it was in version 1.0. Mapping to the new name is performed in
+ // the Named Pipe File System code.
+ //
+ Status = ElfGlobalData->StartRpcServer(
+ ElfGlobalData->SvcsRpcPipeName,
+ eventlog_ServerIfHandle);
+
+ if (Status != NO_ERROR) {
+ ElfDbgPrint(("[ELF]StartRpcServer Failed %d\n",Status));
+ goto cleanupandexit;
+ }
+
+ //
+ // Tell service controller of that we are making progress
+ //
+
+ if (ElfStatusUpdate(RUNNING) == RUNNING) {
+ ElfDbgPrint(("[ELF] Service Started Successfully\n"));
+ }
+
+ EventFlags |= ELF_STARTED_RPC_SERVER;
+
+ if (GetElState() == RUNNING) {
+ ElfDbgPrint(("[ELF] Service Running - main thread is returning\n"));
+ return;
+ }
+
+//
+// Come here if there is cleanup necessary.
+//
+
+cleanupandexit:
+
+ ElfDbgPrint(("[ELF] Leaving the service\n"));
+
+ if (!Win32Error) {
+ Win32Error = RtlNtStatusToDosError(Status);
+ }
+ ElfBeginForcedShutdown(PENDING,Win32Error,Status);
+
+ //
+ // If the registry monitor has been initialized, then
+ // let it do the shutdown cleanup. All we need to do
+ // here is wake it up.
+ // Otherwise, this thread will do the cleanup.
+ //
+ if (EventFlags & ELF_STARTED_REGISTRY_MONITOR) {
+ StopRegistryMonitor();
+ }
+ else {
+ ElfpCleanUp(EventFlags);
+ //
+ // We should actually return here so that the DLL gets unloaded.
+ // However, RPC has a problem in that it might still call our
+ // context rundown routine even though we unregistered our interface.
+ // So we exit thread instead. This keeps our Dll loaded.
+ //
+ ExitThread(0);
+ }
+ return;
+}
+
+VOID
+ElfInitMessageBoxTitle(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Obtains the title text for the message box used to display messages.
+ If the title is successfully obtained from the message file, then
+ that title is pointed to by GlobalAllocatedMsgTitle and
+ GlobalMessageBoxTitle. If unsuccessful, then GlobalMessageBoxTitle
+ left pointing to the DefaultMessageBoxTitle.
+
+ NOTE: If successful, a buffer is allocated by this function. The
+ pointer stored in GlobalAllocatedMsgTitle and it should be freed when
+ done with this buffer.
+
+Arguments:
+
+Return Value:
+
+ none
+
+--*/
+{
+ LPVOID hModule;
+ DWORD msgSize;
+ DWORD status=NO_ERROR;
+
+ GlobalAllocatedMsgTitle = NULL;
+
+ hModule = LoadLibrary( L"netevent.dll");
+ if ( hModule == NULL) {
+ status = GetLastError();
+ ElfDbgPrint(("LoadLibrary() fails with winError = %d\n", GetLastError()));
+ return;
+ }
+ msgSize = FormatMessageW(
+ FORMAT_MESSAGE_FROM_HMODULE | // dwFlags
+ FORMAT_MESSAGE_ARGUMENT_ARRAY |
+ FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ hModule,
+ TITLE_EventlogMessageBox, // MessageId
+ 0, // dwLanguageId
+ (LPWSTR)&GlobalAllocatedMsgTitle, // lpBuffer
+ 0, // nSize
+ NULL);
+
+ if (msgSize == 0) {
+ status = GetLastError();
+ ElfDbgPrint((ERROR,"Could not find MessageBox title in a message file %d\n",
+ status));
+ }
+ else {
+ GlobalMessageBoxTitle = GlobalAllocatedMsgTitle;
+ }
+
+ FreeLibrary(hModule);
+ return;
+}
+
+
+
diff --git a/private/eventlog/server/eventlog.def b/private/eventlog/server/eventlog.def
new file mode 100644
index 000000000..987b75f56
--- /dev/null
+++ b/private/eventlog/server/eventlog.def
@@ -0,0 +1,6 @@
+NAME eventlog.dll
+
+DESCRIPTION 'NT Event Logging Service'
+
+EXPORTS
+ ServiceEntry
diff --git a/private/eventlog/server/eventlog.rc b/private/eventlog/server/eventlog.rc
new file mode 100644
index 000000000..19b4d3553
--- /dev/null
+++ b/private/eventlog/server/eventlog.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Event Logging Service"
+#define VER_INTERNALNAME_STR "Eventlog.DLL"
+#define VER_ORIGINALFILENAME_STR "Eventlog.DLL"
+
+#include "common.ver"
+
diff --git a/private/eventlog/server/eventp.h b/private/eventlog/server/eventp.h
new file mode 100644
index 000000000..ccb968cce
--- /dev/null
+++ b/private/eventlog/server/eventp.h
@@ -0,0 +1,48 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ eventp.h
+
+Abstract:
+
+ Private include file for eventlog service
+
+Author:
+
+ Rajen Shah (rajens) 12-Jul-1991
+
+Revision History:
+
+--*/
+
+#ifndef _CAIRO_
+#define UNICODE // This service uses unicode APIs
+#endif // _CAIRO_
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntelfapi.h>
+#include <netevent.h> // Manifest constants for Events
+
+#include <windows.h>
+#include <winsvc.h>
+
+#include <lmcons.h>
+#include <lmerr.h>
+#include <rpc.h>
+#include <svcs.h> // SVCS_ENTRY_POINT, PSVCS_GLOBAL_DATA
+
+#ifdef _CAIRO_
+#include <wtypes.h>
+#endif // _CAIRO_
+
+#include <elf.h>
+
+#include <elfdef.h>
+#include <elfcommn.h>
+#include <elfproto.h>
+#include <elfextrn.h>
diff --git a/private/eventlog/server/file.c b/private/eventlog/server/file.c
new file mode 100644
index 000000000..63bc7d025
--- /dev/null
+++ b/private/eventlog/server/file.c
@@ -0,0 +1,1088 @@
+/*
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ FILE.C
+
+Abstract:
+
+ This file contains the routines that deal with file-related operations.
+
+Author:
+
+ Rajen Shah (rajens) 07-Aug-1991
+
+Revision History:
+
+ 29-Aug-1994 Danl
+ We no longer grow log files in place. So there is no need to
+ reserve the MaxConfigSize block of memory.
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <alertmsg.h> // ALERT_ELF manifests
+
+//
+// Macros
+//
+
+#define IS_EOF(Ptr, Size) \
+ (Ptr)->Length == ELFEOFRECORDSIZE && \
+ RtlCompareMemory (Ptr, &EOFRecord, Size) == Size
+
+#ifdef CORRUPTED
+
+
+BOOLEAN
+VerifyLogIntegrity(
+ PLOGFILE pLogFile
+ )
+/*++
+
+Routine Description:
+
+ This routine walks the log file to verify that it isn't corrupt
+
+
+Arguments:
+
+ A pointer to the LOGFILE structure for the log to validate.
+
+Return Value:
+
+ TRUE if log OK
+ FALSE if it is corrupt
+
+Note:
+
+
+--*/
+{
+
+ PEVENTLOGRECORD pEventLogRecord;
+ PVOID PhysicalStart;
+ PVOID PhysicalEOF;
+ PVOID BeginRecord;
+ PVOID EndRecord;
+
+ pEventLogRecord =
+ (PEVENTLOGRECORD)((PBYTE) pLogFile->BaseAddress + pLogFile->BeginRecord);
+ PhysicalStart =
+ (PVOID) ((PBYTE) pLogFile->BaseAddress + FILEHEADERBUFSIZE);
+ PhysicalEOF =
+ (PVOID) ((PBYTE) pLogFile->BaseAddress + pLogFile->ViewSize);
+ BeginRecord = (PVOID)((PBYTE) pLogFile->BaseAddress + pLogFile->BeginRecord);
+ EndRecord = (PVOID)((PBYTE) pLogFile->BaseAddress + pLogFile->EndRecord);
+
+ while(pEventLogRecord->Length != ELFEOFRECORDSIZE) {
+
+ pEventLogRecord = (PEVENTLOGRECORD) NextRecordPosition (
+ EVENTLOG_FORWARDS_READ,
+ (PVOID) pEventLogRecord,
+ pEventLogRecord->Length,
+ BeginRecord,
+ EndRecord,
+ PhysicalEOF,
+ PhysicalStart
+ );
+
+ if (!pEventLogRecord || pEventLogRecord->Length == 0) {
+
+ ElfDbgPrintNC(("[ELF] The %ws logfile is corrupt\n",
+ pLogFile->LogModuleName->Buffer));
+ return(FALSE);
+ }
+ }
+
+ return(TRUE);
+
+}
+
+#endif // CORRUPTED
+
+
+NTSTATUS
+FlushLogFile (
+ PLOGFILE pLogFile
+ )
+
+/*++
+
+Routine Description:
+
+ This routine flushes the file specified. It updates the file header,
+ and then flushes the virtual memory which causes the data to get
+ written to disk.
+
+Arguments:
+
+ pLogFile points to the log file structure.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PVOID BaseAddress;
+ ULONG RegionSize;
+ PELF_LOGFILE_HEADER pLogFileHeader;
+
+ //
+ // If the dirty bit is set, update the file header before flushing it.
+ //
+ if (pLogFile->Flags & ELF_LOGFILE_HEADER_DIRTY) {
+
+ pLogFileHeader = (PELF_LOGFILE_HEADER) pLogFile->BaseAddress;
+
+ pLogFile->Flags &= ~ELF_LOGFILE_HEADER_DIRTY; // Remove dirty bit
+ pLogFileHeader->Flags = pLogFile->Flags;
+
+ pLogFileHeader->StartOffset = pLogFile->BeginRecord;
+ pLogFileHeader->EndOffset = pLogFile->EndRecord;
+ pLogFileHeader->CurrentRecordNumber = pLogFile->CurrentRecordNumber;
+ pLogFileHeader->OldestRecordNumber = pLogFile->OldestRecordNumber;
+ }
+
+ //
+ // Flush the memory in the section that is mapped to the file.
+ //
+ BaseAddress = pLogFile->BaseAddress;
+ RegionSize = pLogFile->ViewSize;
+
+ Status = NtFlushVirtualMemory(
+ NtCurrentProcess(),
+ &BaseAddress,
+ &RegionSize,
+ &IoStatusBlock
+ );
+
+ return (Status);
+
+}
+
+
+
+NTSTATUS
+ElfpFlushFiles (
+ )
+
+/*++
+
+Routine Description:
+
+ This routine flushes all the log files and forces them on disk.
+ It is usually called in preparation for a shutdown or a pause.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+
+{
+
+ PLOGFILE pLogFile;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // Make sure that there's at least one file to flush
+ //
+
+ if (IsListEmpty (&LogFilesHead) ) {
+ return(STATUS_SUCCESS);
+ }
+
+ pLogFile
+ = (PLOGFILE)
+ CONTAINING_RECORD(LogFilesHead.Flink, LOGFILE, FileList);
+
+ //
+ // Go through this loop at least once. This ensures that the termination
+ // condition will work correctly.
+ //
+ do {
+
+ Status = FlushLogFile (pLogFile);
+
+ pLogFile = // Get next one
+ (PLOGFILE)
+ CONTAINING_RECORD(pLogFile->FileList.Flink, LOGFILE, FileList);
+
+ } while ( (pLogFile->FileList.Flink != LogFilesHead.Flink)
+ && (NT_SUCCESS(Status)) ) ;
+
+ return (Status);
+}
+
+
+
+
+NTSTATUS
+ElfpCloseLogFile (
+ PLOGFILE pLogFile,
+ DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+ This routine undoes whatever is done in ElfOpenLogFile.
+
+Arguments:
+
+ pLogFile points to the log file structure.
+
+Return Value:
+
+ NTSTATUS.
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PELF_LOGFILE_HEADER pLogFileHeader;
+ PVOID BaseAddress;
+ ULONG Size;
+
+ ElfDbgPrint(("[ELF] Closing and unmapping log file: %ws\n",
+ pLogFile->LogFileName->Buffer));
+
+#ifdef CORRUPTED
+
+ //
+ // Just for debugging a log corruption problem
+ //
+
+ if (!VerifyLogIntegrity(pLogFile)) {
+ ElfDbgPrintNC(("[ELF] Integrity check failed in ElfpCloseLogFile\n"));
+ }
+
+#endif // CORRUPTED
+
+ //
+ // If the dirty bit is set, update the file header before closing it.
+ // Check to be sure it's not a backup file that just had the dirty
+ // bit set when it was copied.
+ //
+
+ if (pLogFile->Flags & ELF_LOGFILE_HEADER_DIRTY &&
+ !(Flags & ELF_LOG_CLOSE_BACKUP)) {
+ pLogFileHeader = (PELF_LOGFILE_HEADER) pLogFile->BaseAddress;
+ pLogFileHeader->StartOffset = pLogFile->BeginRecord;
+ pLogFileHeader->EndOffset = pLogFile->EndRecord;
+ pLogFileHeader->CurrentRecordNumber = pLogFile->CurrentRecordNumber;
+ pLogFileHeader->OldestRecordNumber = pLogFile->OldestRecordNumber;
+ pLogFile->Flags &= ~(ELF_LOGFILE_HEADER_DIRTY |
+ ELF_LOGFILE_ARCHIVE_SET); // Remove dirty &
+ // archive bits
+ pLogFileHeader->Flags = pLogFile->Flags;
+ }
+
+ //
+ // Decrement the reference count, and if it goes to zero, unmap the file
+ // and close the handle. Also force the close if fForceClosed is TRUE.
+ //
+
+ if ((--pLogFile->RefCount == 0) || Flags & ELF_LOG_CLOSE_FORCE) {
+
+ //
+ // Last user has gone.
+ // Close all the views and the file and section handles. Free up
+ // any extra memory we had reserved, and unlink any structures
+ //
+
+ if (pLogFile->BaseAddress) // Unmap it if it was allocated
+ NtUnmapViewOfSection (
+ NtCurrentProcess(),
+ pLogFile->BaseAddress
+ );
+
+ if (pLogFile->SectionHandle)
+ NtClose ( pLogFile->SectionHandle );
+
+ if (pLogFile->FileHandle)
+ NtClose ( pLogFile->FileHandle );
+ }
+
+ return (Status);
+
+} // ElfpCloseLogFile
+
+
+NTSTATUS
+RevalidateLogHeader (
+ PELF_LOGFILE_HEADER pLogFileHeader,
+ PLOGFILE pLogFile
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine is called if we encounter a "dirty" log file. The
+ routine walks through the file until it finds a signature for a valid log
+ record. It then walks forward thru the file until it finds the EOF
+ record, or an invalid record. Then it walks backwards from the first
+ record it found until it finds the EOF record from the other direction.
+ It then rebuilds the header and writes it back to the log. If it can't
+ find any valid records, it rebuilds the header to reflect an empty log
+ file. If it finds a trashed file, it writes 256 bytes of the log out in
+ an event to the system log.
+
+Arguments:
+
+ pLogFileHeader points to the header of the log file.
+ pLogFile points to the log file structure.
+
+Return Value:
+
+ NTSTATUS.
+
+Note:
+
+ This is an expensive routine since it scans the entire file.
+
+ It assumes that the records are on DWORD boundaries.
+
+--*/
+{
+ PVOID Start, End;
+ PDWORD pSignature;
+ PEVENTLOGRECORD pEvent;
+ PEVENTLOGRECORD FirstRecord;
+ PEVENTLOGRECORD FirstPhysicalRecord;
+ PEVENTLOGRECORD pLastGoodRecord;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ LARGE_INTEGER ByteOffset;
+ ULONG Size;
+
+ ElfDbgPrint(("[ELF] Log file had dirty bit set, revalidating\n"));
+
+ try {
+
+ //
+ // Physical start and end of log file (skipping header)
+ //
+
+ Start = (PVOID) ((PBYTE)pLogFile->BaseAddress + FILEHEADERBUFSIZE);
+ End = (PVOID) ((PBYTE)pLogFile->BaseAddress +
+ pLogFile->ActualMaxFileSize);
+
+ //
+ // First see if the log has wrapped. The EOFRECORDSIZE is for the one
+ // case where the EOF record wraps so that it's final length just replaces
+ // the next records starting length
+ //
+
+ pEvent = (PEVENTLOGRECORD) Start;
+
+ if (pEvent->Reserved != ELF_LOG_FILE_SIGNATURE
+ || pEvent->RecordNumber != 1 || pEvent->Length == ELFEOFRECORDSIZE) {
+
+ //
+ // Log has already wrapped, go looking for the first valid record
+ //
+
+ for (pSignature = (PDWORD) Start; (PVOID)pSignature < End;
+ pSignature++) {
+
+ if (*pSignature == ELF_LOG_FILE_SIGNATURE) {
+
+ //
+ // Make sure it's really a record
+ //
+
+ pEvent = CONTAINING_RECORD(pSignature, EVENTLOGRECORD,
+ Reserved);
+ if (!ValidFilePos(pEvent, Start, End, End,
+ pLogFileHeader)) {
+ //
+ // Nope, not really, keep trying
+ //
+
+ continue;
+ }
+
+ //
+ // This is a valid record, Remember this so you can use
+ // it later
+ //
+
+ FirstPhysicalRecord = pEvent;
+
+ //
+ // Walk backwards from here (wrapping if necessary) until
+ // you hit the EOF record or an invalid record.
+ //
+
+ while (pEvent && ValidFilePos(pEvent, Start, End, End, pLogFileHeader)) {
+
+ //
+ // See if it's the EOF record
+ //
+
+ if (IS_EOF(pEvent,
+ min (ELFEOFUNIQUEPART,
+ (PBYTE) End - (PBYTE) pEvent))) {
+
+ break;
+ }
+
+ pLastGoodRecord = pEvent;
+ pEvent = NextRecordPosition (
+ EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ,
+ pEvent,
+ pEvent->Length,
+ 0,
+ 0,
+ End,
+ Start);
+
+ //
+ // Make sure we're not in an infinite loop
+ //
+
+ if (pEvent == FirstPhysicalRecord) {
+ return(STATUS_UNSUCCESSFUL);
+ }
+ }
+
+ //
+ // Found the first record, now go look for the last
+ //
+
+ FirstRecord = pLastGoodRecord;
+ break;
+ }
+ }
+
+ if (pSignature == End) {
+
+ //
+ // We couldn't find one valid record in the whole log. Give
+ // up and we'll set it to an empty log file
+ //
+
+ return(STATUS_UNSUCCESSFUL);
+ }
+ }
+ else {
+
+ //
+ // We haven't wrapped yet
+ //
+
+ FirstPhysicalRecord = FirstRecord = Start;
+ }
+
+
+ //
+ // Now read forward looking for the last good record
+ //
+
+ pEvent = FirstPhysicalRecord;
+
+ while (pEvent && ValidFilePos(pEvent, Start, End, End, pLogFileHeader)) {
+
+ //
+ // See if it's the EOF record
+ //
+
+ if (IS_EOF(pEvent, min (ELFEOFUNIQUEPART,
+ (PBYTE) End - (PBYTE) pEvent))) {
+
+ break;
+ }
+
+ pLastGoodRecord = pEvent;
+ pEvent = NextRecordPosition (
+ EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ,
+ pEvent,
+ pEvent->Length,
+ 0,
+ 0,
+ End,
+ Start);
+
+ //
+ // Make sure we're not in an infinite loop
+ //
+
+ if (pEvent == FirstPhysicalRecord) {
+ return(STATUS_UNSUCCESSFUL);
+ }
+ }
+
+ //
+ // Now we know the first record (FirstRecord) and the last record
+ // (pLastGoodRecord) so we can create the header an EOF record and
+ // write them out (EOF record gets written at pEvent)
+ //
+ // First the EOF record
+ //
+
+ EOFRecord.BeginRecord = (PBYTE) FirstRecord - (PBYTE) pLogFileHeader;
+ EOFRecord.EndRecord = (PBYTE) pEvent - (PBYTE) pLogFileHeader;
+ EOFRecord.CurrentRecordNumber =
+ pLastGoodRecord->RecordNumber + 1;
+ EOFRecord.OldestRecordNumber = FirstRecord->RecordNumber;
+
+ ByteOffset = RtlConvertUlongToLargeInteger (
+ (PBYTE) pEvent - (PBYTE) pLogFileHeader);
+ Status = NtWriteFile(
+ pLogFile->FileHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ &EOFRecord, // Buffer
+ ELFEOFRECORDSIZE, // Length
+ &ByteOffset, // Byteoffset
+ NULL); // Key
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrint(("[ELF]: Log file header write failed 0x%lx\n",
+ Status));
+ return (Status);
+ }
+
+ //
+ // Now the header
+ //
+
+ pLogFileHeader->StartOffset = (PBYTE) FirstRecord- (PBYTE) pLogFileHeader;
+ pLogFileHeader->EndOffset = (PBYTE) pEvent- (PBYTE) pLogFileHeader;
+ pLogFileHeader->CurrentRecordNumber =
+ pLastGoodRecord->RecordNumber + 1;
+ pLogFileHeader->OldestRecordNumber = FirstRecord->RecordNumber;
+ pLogFileHeader->HeaderSize = pLogFileHeader->EndHeaderSize = FILEHEADERBUFSIZE;
+ pLogFileHeader->Signature = ELF_LOG_FILE_SIGNATURE;
+ pLogFileHeader->Flags = 0;
+
+ if (pLogFileHeader->StartOffset != FILEHEADERBUFSIZE)
+ pLogFileHeader->Flags |= ELF_LOGFILE_HEADER_WRAP;
+
+ pLogFileHeader->MaxSize = pLogFile->ActualMaxFileSize;
+ pLogFileHeader->Retention = pLogFile->Retention;
+ pLogFileHeader->MajorVersion = ELF_VERSION_MAJOR;
+ pLogFileHeader->MinorVersion = ELF_VERSION_MINOR;
+
+ //
+ // Now flush this to disk to commit it
+ //
+
+ Start = pLogFile->BaseAddress;
+ Size = FILEHEADERBUFSIZE;
+
+ Status = NtFlushVirtualMemory(
+ NtCurrentProcess(),
+ &Start,
+ &Size,
+ &IoStatusBlock
+ );
+
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+ return (Status);
+
+}
+
+
+NTSTATUS
+ElfOpenLogFile (
+ PLOGFILE pLogFile,
+ ELF_LOG_TYPE LogType
+ )
+
+/*++
+
+Routine Description:
+
+ Open the log file, create it if it does not exist.
+ Create a section and map a view into the log file.
+ Write out the header to the file, if it is newly created.
+ If "dirty", update the "start" and "end" pointers by scanning
+ the file. Set AUTOWRAP if the "start" does not start right after
+ the file header.
+
+Arguments:
+
+ pLogFile points to the log file structure, with the relevant data
+ filled in.
+
+ CreateOptions are the options to be passed to NtCreateFile which
+ indicate whether to open an existing file, or to create it
+ if it does not exist.
+
+Return Value:
+
+ NTSTATUS.
+
+Note:
+
+ It is up to the caller to set the RefCount in the Logfile structure.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ LARGE_INTEGER MaximumSizeOfSection;
+ LARGE_INTEGER ByteOffset;
+ PELF_LOGFILE_HEADER pLogFileHeader;
+ FILE_STANDARD_INFORMATION FileStandardInfo;
+ ULONG IoStatusInformation;
+ ULONG FileDesiredAccess;
+ ULONG SectionDesiredAccess;
+ ULONG SectionPageProtection;
+ ULONG CreateOptions;
+ ULONG CreateDisposition;
+
+ //
+ // File header in a new file has the "Start" and "End" pointers the
+ // same since there are no records in the file.
+ //
+
+ static ELF_LOGFILE_HEADER FileHeaderBuf = {FILEHEADERBUFSIZE, // Size
+ ELF_LOG_FILE_SIGNATURE,
+ ELF_VERSION_MAJOR,
+ ELF_VERSION_MINOR,
+ FILEHEADERBUFSIZE, // Start offset
+ FILEHEADERBUFSIZE, // End offset
+ 1, // Next record #
+ 0, // Oldest record #
+ 0, // Maxsize
+ 0, // Flags
+ 0, // Retention
+ FILEHEADERBUFSIZE // Size
+ };
+
+ //
+ // Set the file open and section create options based on the type of log
+ // file this is.
+ //
+
+ switch (LogType) {
+
+ case ElfNormalLog:
+ CreateDisposition = FILE_OPEN_IF;
+ FileDesiredAccess = GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE;
+ SectionDesiredAccess = SECTION_MAP_READ | SECTION_MAP_WRITE |
+ SECTION_QUERY | SECTION_EXTEND_SIZE;
+ SectionPageProtection = PAGE_READWRITE;
+ CreateOptions = FILE_SYNCHRONOUS_IO_NONALERT;
+ break;
+
+ case ElfSecurityLog:
+ CreateDisposition = FILE_OPEN_IF;
+ FileDesiredAccess = GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE;
+ SectionDesiredAccess = SECTION_MAP_READ | SECTION_MAP_WRITE |
+ SECTION_QUERY | SECTION_EXTEND_SIZE;
+ SectionPageProtection = PAGE_READWRITE;
+ CreateOptions = FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT;
+ break;
+
+ case ElfBackupLog:
+ CreateDisposition = FILE_OPEN;
+ FileDesiredAccess = GENERIC_READ | SYNCHRONIZE;
+ SectionDesiredAccess = SECTION_MAP_READ | SECTION_QUERY;
+ SectionPageProtection = PAGE_READONLY;
+ CreateOptions = FILE_SYNCHRONOUS_IO_NONALERT;
+ break;
+
+ }
+
+ ElfDbgPrint (("[ELF] Opening and mapping %ws\n",
+ pLogFile->LogFileName->Buffer));
+
+ if (pLogFile->FileHandle != NULL) {
+
+ //
+ // The log file is already in use. Do not reopen or remap it.
+ //
+
+ ElfDbgPrint(("[ELF] Log file already in use by another module\n"));
+
+ } else {
+
+ //
+ // Initialize the logfile structure so that it is easier to clean
+ // up.
+ //
+
+ pLogFile->ActualMaxFileSize = ELF_DEFAULT_LOG_SIZE;
+ pLogFile->Flags = 0;
+ pLogFile->BaseAddress = NULL;
+ pLogFile->SectionHandle = NULL;
+
+
+ //
+ // Set up the object attributes structure for the Log File
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ pLogFile->LogFileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ //
+ // Open the Log File. Create it if it does not exist and it's not
+ // being opened as a backup file. If creating, create a file of
+ // the maximum size configured.
+ //
+
+ MaximumSizeOfSection =
+ RtlConvertUlongToLargeInteger (ELF_DEFAULT_LOG_SIZE);
+
+ Status = NtCreateFile(
+ &pLogFile->FileHandle,
+ FileDesiredAccess,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ &MaximumSizeOfSection,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ CreateDisposition,
+ CreateOptions,
+ NULL,
+ 0);
+
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] Log File Open Failed 0x%lx\n", Status));
+ goto cleanup;
+ }
+
+ //
+ // If the file already existed, get its size and use that as the
+ // actual size of the file.
+ //
+
+ IoStatusInformation = IoStatusBlock.Information; // Save it away
+
+ if (!( IoStatusInformation & FILE_CREATED )) {
+ ElfDbgPrint (("[Elf] Log file exists.\n"));
+
+ Status = NtQueryInformationFile (
+ pLogFile->FileHandle,
+ &IoStatusBlock,
+ &FileStandardInfo,
+ sizeof (FileStandardInfo),
+ FileStandardInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] QueryInformation failed 0x%lx\n", Status));
+ goto cleanup;
+ } else {
+
+
+ ElfDbgPrint(("[ELF] Use existing log file size: 0x%lx:%lx\n",
+ FileStandardInfo.EndOfFile.HighPart,
+ FileStandardInfo.EndOfFile.LowPart
+ ));
+
+ MaximumSizeOfSection.LowPart =
+ FileStandardInfo.EndOfFile.LowPart;
+ MaximumSizeOfSection.HighPart =
+ FileStandardInfo.EndOfFile.HighPart;
+
+ //
+ // Make sure that the high DWORD of the file size is ZERO.
+ //
+
+ ASSERT (MaximumSizeOfSection.HighPart == 0);
+
+ //
+ // If the filesize if 0, set it to the minimum size
+ //
+
+ if (MaximumSizeOfSection.LowPart == 0) {
+ MaximumSizeOfSection.LowPart = ELF_DEFAULT_LOG_SIZE;
+ }
+
+ //
+ // Set actual size of file
+ //
+
+ pLogFile->ActualMaxFileSize =
+ MaximumSizeOfSection.LowPart;
+
+ //
+ // If the size of the log file is reduced, a clear must
+ // happen for this to take effect
+ //
+
+ if (pLogFile->ActualMaxFileSize > pLogFile->ConfigMaxFileSize) {
+ pLogFile->ConfigMaxFileSize = pLogFile->ActualMaxFileSize;
+ }
+
+ }
+ }
+
+ //
+ // Create a section mapped to the Log File just opened
+ //
+
+ Status = NtCreateSection(
+ &pLogFile->SectionHandle,
+ SectionDesiredAccess,
+ NULL,
+ &MaximumSizeOfSection,
+ SectionPageProtection,
+ SEC_COMMIT,
+ pLogFile->FileHandle
+ );
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF] Log Mem Section Create Failed 0x%lx\n",
+ Status));
+ goto cleanup;
+ }
+
+ //
+ // Map a view of the Section into the eventlog address space
+ //
+
+ pLogFile->ViewSize = 0; // Initialize value to zero
+
+ Status = NtMapViewOfSection(
+ pLogFile->SectionHandle,
+ NtCurrentProcess(),
+ &pLogFile->BaseAddress,
+ 0,
+ 0,
+ NULL,
+ &pLogFile->ViewSize,
+ ViewUnmap,
+ 0,
+ SectionPageProtection);
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF] Log Mem Sect Map View failed 0x%lx\n",
+ Status));
+ goto cleanup;
+ }
+
+ //
+ // If the file was just created, write out the file header.
+ //
+
+ if ( IoStatusInformation & FILE_CREATED ) {
+ ElfDbgPrint(("[ELF] File was created\n"));
+JustCreated:
+ FileHeaderBuf.MaxSize = pLogFile->ActualMaxFileSize;
+ FileHeaderBuf.Flags = 0;
+ FileHeaderBuf.Retention = pLogFile->Retention;
+
+ //
+ // Copy the header into the file
+ //
+
+ ByteOffset = RtlConvertUlongToLargeInteger ( 0 );
+ Status = NtWriteFile(
+ pLogFile->FileHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ &FileHeaderBuf, // Buffer
+ FILEHEADERBUFSIZE, // Length
+ &ByteOffset, // Byteoffset
+ NULL); // Key
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrint(("[ELF]: Log file header write failed 0x%lx\n",
+ Status));
+ goto cleanup;
+ }
+
+ //
+ // Copy the "EOF" record right after the header
+ //
+
+ ByteOffset = RtlConvertUlongToLargeInteger ( FILEHEADERBUFSIZE );
+ Status = NtWriteFile(
+ pLogFile->FileHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ &EOFRecord, // Buffer
+ ELFEOFRECORDSIZE, // Length
+ &ByteOffset, // Byteoffset
+ NULL); // Key
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrint(("[ELF]: Log file header write failed 0x%lx\n",
+ Status));
+ goto cleanup;
+ }
+ }
+
+ //
+ // Check to ensure that this is a valid log file. We look at the
+ // size of the header and the signature to see if they match, as
+ // well as checking the version number.
+ //
+
+ pLogFileHeader = (PELF_LOGFILE_HEADER)(pLogFile->BaseAddress);
+
+ if ( (pLogFileHeader->HeaderSize != FILEHEADERBUFSIZE)
+ ||(pLogFileHeader->EndHeaderSize != FILEHEADERBUFSIZE)
+ ||(pLogFileHeader->Signature != ELF_LOG_FILE_SIGNATURE)
+ ||(pLogFileHeader->MajorVersion != ELF_VERSION_MAJOR)
+ ||(pLogFileHeader->MinorVersion != ELF_VERSION_MINOR)) {
+
+ //
+ // This file is corrupt, reset it to an empty log, unless
+ // it's being opened as a backup file, if it is, fail the
+ // open
+ //
+
+ ElfDbgPrint(("[ELF] Invalid file header found\n"));
+
+ if (LogType == ElfBackupLog) {
+
+ Status = STATUS_EVENTLOG_FILE_CORRUPT;
+ goto cleanup;
+ }
+ else {
+ ElfpCreateQueuedAlert(ALERT_ELF_LogFileCorrupt, 1,
+ &pLogFile->LogModuleName->Buffer);
+ //
+ // Treat it like it was just created
+ //
+
+ goto JustCreated;
+ }
+ }
+ else {
+
+ //
+ // If the "dirty" bit is set in the file header, then we need to
+ // revalidate the BeginRecord and EndRecord fields since we did not
+ // get a chance to write them out before the system was rebooted.
+ // If the dirty bit is set and it's a backup file, just fail the
+ // open.
+ //
+
+ if (pLogFileHeader->Flags & ELF_LOGFILE_HEADER_DIRTY) {
+
+ if (LogType == ElfBackupLog) {
+
+ Status = STATUS_EVENTLOG_FILE_CORRUPT;
+ goto cleanup;
+ }
+ else {
+ Status = RevalidateLogHeader (pLogFileHeader, pLogFile);
+ }
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Set the beginning and end record positions in our
+ // data structure as well as the wrap flag if appropriate.
+ //
+
+ pLogFile->EndRecord = pLogFileHeader->EndOffset;
+ pLogFile->BeginRecord = pLogFileHeader->StartOffset;
+ if (pLogFileHeader->Flags & ELF_LOGFILE_HEADER_WRAP) {
+ pLogFile->Flags |= ELF_LOGFILE_HEADER_WRAP;
+ }
+
+ ElfDbgPrint(("[ELF] BeginRecord: 0x%lx EndRecord: 0x%lx \n",
+ pLogFile->BeginRecord, pLogFile->EndRecord));
+ }
+ else {
+
+ //
+ // Couldn't validate the file, treat it like it was just
+ // created (turn it into an empty file)
+ //
+
+ goto JustCreated;
+ }
+
+#ifdef CORRUPTED
+
+ //
+ // Just for debugging a log corruption problem
+ //
+
+ if (!VerifyLogIntegrity(pLogFile)) {
+ ElfDbgPrintNC(("[ELF] Integrity check failed in ElfOpenLogFile\n"));
+ }
+#endif // CORRUPTED
+
+ }
+
+ //
+ // Fill in the first and last record number values in the LogFile
+ // data structure.
+ //
+
+ pLogFile->CurrentRecordNumber = pLogFileHeader->CurrentRecordNumber;
+ pLogFile->OldestRecordNumber = pLogFileHeader->OldestRecordNumber;
+
+ }
+
+ return (Status);
+
+cleanup:
+
+ // Clean up anything that got allocated
+
+ if (pLogFile->ViewSize) {
+ NtUnmapViewOfSection(NtCurrentProcess(), pLogFile->BaseAddress);
+ }
+
+ if (pLogFile->SectionHandle) {
+ NtClose(pLogFile->SectionHandle);
+ }
+
+ if (pLogFile->FileHandle) {
+ NtClose (pLogFile->FileHandle);
+ }
+
+ return (Status);
+
+}
diff --git a/private/eventlog/server/logclear.c b/private/eventlog/server/logclear.c
new file mode 100644
index 000000000..a59d86748
--- /dev/null
+++ b/private/eventlog/server/logclear.c
@@ -0,0 +1,621 @@
+/*++
+
+Copyright (c) 1991-1994 Microsoft Corporation
+
+Module Name:
+
+ LOGCLEAR.C
+
+Abstract:
+
+ Contains functions used to log an event indicating who cleared the log.
+ This is only called after the security log has been cleared.
+
+Author:
+
+ Dan Lafferty (danl) 01-July-1994
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 01-July-1994 danl & robertre
+ Created - Rob supplied the code which I fitted into the eventlog.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <stdio.h>
+#include <msaudite.h>
+#include <eventp.h>
+#include <tstr.h>
+
+#define NUM_STRINGS 6
+
+//
+// Globals
+//
+ PUNICODE_STRING pGlobalComputerNameU = NULL;
+ PANSI_STRING pGlobalComputerNameA = NULL;
+
+//
+// LOCAL FUNCTION PROTOTYPES
+//
+BOOL
+GetUserInfo(
+ IN HANDLE Token,
+ OUT LPWSTR *UserName,
+ OUT LPWSTR *DomainName,
+ OUT LPWSTR *AuthenticationId,
+ OUT PSID *UserSid
+ );
+
+NTSTATUS
+ElfpGetComputerName (
+ IN LPSTR *ComputerNamePtr
+ );
+
+PUNICODE_STRING
+TmpGetComputerNameW (VOID);
+
+VOID
+w_GetComputerName ( );
+
+
+VOID
+ElfpGenerateLogClearedEvent(
+ IELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function generates an event indicating that the log was cleared.
+
+Arguments:
+
+ LogHandle - This is a handle to the log that the event is to be placed in.
+ It is intended that this function only be called when the SecurityLog
+ has been cleared. So it is expected the LogHandle will always be
+ a handle to the security log.
+
+Return Value:
+
+ NONE - Either it works or it doesn't. If it doesn't, there isn't much
+ we can do about it.
+
+--*/
+{
+ LPWSTR UserName=NULL;
+ LPWSTR DomainName=NULL;
+ LPWSTR AuthenticationId=NULL;
+ LPWSTR ClientUserName=NULL;
+ LPWSTR ClientDomainName=NULL;
+ LPWSTR ClientAuthenticationId=NULL;
+ PSID UserSid=NULL;
+ PSID ClientSid=NULL;
+ DWORD i;
+ BOOL Result;
+ HANDLE Token;
+ PUNICODE_STRING StringPtrArray[NUM_STRINGS];
+ UNICODE_STRING StringArray[NUM_STRINGS];
+ NTSTATUS Status;
+ LARGE_INTEGER Time;
+ ULONG EventTime;
+ PUNICODE_STRING pComputerNameU;
+
+ Result = OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &Token );
+
+ if (!Result) {
+ ElfDbgPrint(("OpenProcessToken failed, error = %d",GetLastError()));
+ return;
+ }
+
+ Result = GetUserInfo(
+ Token,
+ &UserName,
+ &DomainName,
+ &AuthenticationId,
+ &UserSid );
+
+ CloseHandle( Token );
+
+ if (!Result) {
+ ElfDbgPrint(("1st GetUserInfo ret'd %d\n",GetLastError()));
+ return;
+ }
+
+ ElfDbgPrint(("\nGetUserInfo ret'd \nUserName = %ws, "
+ "\nDomainName = %ws, \nAuthenticationId = %ws\n",
+ UserName, DomainName, AuthenticationId ));
+
+ Result = OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, FALSE, &Token );
+
+ if (Result) {
+
+ Result = GetUserInfo(
+ Token,
+ &ClientUserName,
+ &ClientDomainName,
+ &ClientAuthenticationId,
+ &ClientSid );
+
+ CloseHandle( Token );
+
+ if (!Result) {
+ ElfDbgPrint(("2nd GetUserInfo ret'd %d\n",GetLastError()));
+ goto CleanExit;
+ }
+
+ } else {
+
+ //
+ // Either got access denied or we're not impersonating.
+ // Use dash strings either way.
+ //
+
+ ClientUserName = L"-";
+ ClientDomainName = L"-";
+ ClientAuthenticationId = L"-";
+ }
+
+ ElfDbgPrint(("\nGetUserInfo ret'd \nUserName = %ws, "
+ "\nDomainName = %ws, \nAuthenticationId = %ws\n",
+ ClientUserName, ClientDomainName, ClientAuthenticationId ));
+
+
+ RtlInitUnicodeString( &StringArray[0], UserName );
+ RtlInitUnicodeString( &StringArray[1], DomainName );
+ RtlInitUnicodeString( &StringArray[2], AuthenticationId );
+ RtlInitUnicodeString( &StringArray[3], ClientUserName );
+ RtlInitUnicodeString( &StringArray[4], ClientDomainName );
+ RtlInitUnicodeString( &StringArray[5], ClientAuthenticationId );
+
+ //
+ // Create an array of pointers to UNICODE_STRINGs.
+ //
+ for (i=0; i<NUM_STRINGS; i++) {
+ StringPtrArray[i] = &StringArray[i];
+ }
+
+ //
+ // Generate the time of the event. This is done on the client side
+ // since that is where the event occurred.
+ //
+ NtQuerySystemTime(&Time);
+ RtlTimeToSecondsSince1970(&Time,
+ &EventTime
+ );
+ //
+ // Generate the ComputerName of the client.
+ // We have to do this in the client side since this call may be
+ // remoted to another server and we would not necessarily have
+ // the computer name there.
+ //
+ pComputerNameU = TmpGetComputerNameW();
+
+ Status = ElfrReportEventW (
+ LogHandle, // Log Handle
+ EventTime, // Time
+ EVENTLOG_AUDIT_SUCCESS, // Event Type
+ (USHORT)SE_CATEGID_SYSTEM, // Event Category
+ SE_AUDITID_AUDIT_LOG_CLEARED, // EventID
+ NUM_STRINGS, // NumStrings
+ 0, // DataSize
+ pComputerNameU, // pComputerNameU
+ UserSid, // UserSid
+ StringPtrArray, // *Strings
+ NULL, // Data
+ 0, // Flags
+ NULL, // RecordNumber
+ NULL // TimeWritten
+ );
+
+CleanExit:
+ //
+ // We only come down this path if we know for sure that these
+ // first three have been allocated.
+ //
+ ElfpFreeBuffer(UserName);
+ ElfpFreeBuffer(DomainName);
+ ElfpFreeBuffer(AuthenticationId);
+
+ if (UserSid != NULL) {
+ ElfpFreeBuffer(UserSid);
+ }
+ if (ClientUserName != NULL) {
+ ElfpFreeBuffer(ClientUserName);
+ }
+ if (ClientDomainName != NULL) {
+ ElfpFreeBuffer(ClientDomainName);
+ }
+ if (ClientAuthenticationId != NULL) {
+ ElfpFreeBuffer(ClientAuthenticationId);
+ }
+ if (ClientSid != NULL) {
+ ElfpFreeBuffer(ClientSid);
+ }
+}
+
+BOOL
+GetUserInfo(
+ IN HANDLE Token,
+ OUT LPWSTR *UserName,
+ OUT LPWSTR *DomainName,
+ OUT LPWSTR *AuthenticationId,
+ OUT PSID *UserSid
+ )
+/*++
+
+Routine Description:
+
+ This function gathers information about the user identified with the
+ token.
+
+Arguments:
+ Token - This token identifies the entity for which we are gathering
+ information.
+ UserName - This is the entity's user name.
+ DomainName - This is the entity's domain name.
+ AuthenticationId - This is the entity's Authentication ID.
+ UserSid - This is the entity's SID.
+
+NOTE:
+ Memory is allocated by this routine for UserName, DomainName,
+ AuthenticationId, and UserSid. It is the caller's responsibility to
+ free this memory.
+
+Return Value:
+ TRUE - If the operation was successful. It is possible that
+ UserSid did not get allocated in the successful case. Therefore,
+ the caller should test prior to freeing.
+ FALSE - If unsuccessful. No memory is allocated in this case.
+
+
+--*/
+{
+ PTOKEN_USER Buffer=NULL;
+// WCHAR User[256];
+ LPWSTR Domain = NULL;
+ LPWSTR AccountName = NULL;
+ SID_NAME_USE Use;
+ BOOL Result;
+ DWORD RequiredLength;
+ DWORD AccountNameSize;
+ DWORD DomainNameSize;
+ TOKEN_STATISTICS Statistics;
+ WCHAR LogonIdString[256];
+ DWORD Status=ERROR_SUCCESS;
+
+
+ *UserSid = NULL;
+
+ Result = GetTokenInformation ( Token, TokenUser, NULL, 0, &RequiredLength );
+
+ if (!Result) {
+
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+
+ Buffer = ElfpAllocateBuffer((RequiredLength+1)*sizeof(WCHAR) );
+
+ Result = GetTokenInformation ( Token,
+ TokenUser,
+ Buffer,
+ RequiredLength,
+ &RequiredLength
+ );
+
+ if (!Result) {
+ ElfDbgPrint(("2nd GetTokenInformation failed, "
+ "error = %d\n",GetLastError()));
+ return( FALSE );
+ }
+
+ } else {
+
+ DbgPrint("1st GetTokenInformation failed, error = %d\n",GetLastError());
+ return( FALSE );
+ }
+ }
+
+
+ if (!Result) {
+ goto ErrorCleanup;
+ }
+
+ AccountNameSize = 0;
+ DomainNameSize = 0;
+
+ Result = LookupAccountSidW( L"",
+ Buffer->User.Sid,
+ NULL,
+ &AccountNameSize,
+ NULL,
+ &DomainNameSize,
+ &Use
+ );
+
+ if (!Result) {
+
+ if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
+
+ AccountName = ElfpAllocateBuffer((AccountNameSize+1)*sizeof(WCHAR) );
+ Domain = ElfpAllocateBuffer((DomainNameSize+1)*sizeof(WCHAR) );
+
+ if ( AccountName == NULL ) {
+ ElfDbgPrint(("LocalAlloc failed allocating %d bytes, "
+ "error = %d\n",AccountNameSize,GetLastError()));
+ goto ErrorCleanup;
+ }
+
+ if ( Domain == NULL ) {
+ ElfDbgPrint(("LocalAlloc failed allocating %d bytes, "
+ "error = %d\n",DomainNameSize,GetLastError()));
+ goto ErrorCleanup;
+ }
+
+ Result = LookupAccountSidW( L"",
+ Buffer->User.Sid,
+ AccountName,
+ &AccountNameSize,
+ Domain,
+ &DomainNameSize,
+ &Use
+ );
+ if (!Result) {
+
+ ElfDbgPrint(("2nd LookupAccountSid failed, "
+ "error = %d\n",GetLastError()));
+ goto ErrorCleanup;
+ }
+
+ } else {
+
+ ElfDbgPrint(("1st LookupAccountSid failed, "
+ "error = %d\n",GetLastError()));
+ goto ErrorCleanup;
+ }
+
+ } else {
+
+ ElfDbgPrint(("LookupAccountSid succeeded unexpectedly\n"));
+ goto ErrorCleanup;
+ }
+
+ ElfDbgPrint(("Name = %ws\\%ws\n",Domain,AccountName));
+
+ Result = GetTokenInformation ( Token, TokenStatistics, &Statistics, sizeof( Statistics ), &RequiredLength );
+
+ if (!Result) {
+ ElfDbgPrint(("GetTokenInformation failed, error = %d\n",GetLastError()));
+ goto ErrorCleanup;
+ }
+
+ swprintf(LogonIdString, L"(0x%X,0x%X)",Statistics.AuthenticationId.HighPart, Statistics.AuthenticationId.LowPart );
+ ElfDbgPrint(("LogonIdString = %ws\n",LogonIdString));
+
+ *AuthenticationId = ElfpAllocateBuffer(WCSSIZE(LogonIdString));
+ if (*AuthenticationId == NULL) {
+ ElfDbgPrint(("[ELF]GetUserInfo: Failed to allocate buffer "
+ "for AuthenticationId %d\n",GetLastError()));
+ goto ErrorCleanup;
+ }
+ wcscpy(*AuthenticationId, LogonIdString);
+
+ //
+ // Return accumulated information
+ //
+
+ *UserSid = ElfpAllocateBuffer(GetLengthSid( Buffer->User.Sid ) );
+
+ Result = CopySid( GetLengthSid( Buffer->User.Sid ), *UserSid, Buffer->User.Sid );
+
+ ElfpFreeBuffer(Buffer);
+
+ *DomainName = Domain;
+ *UserName = AccountName;
+
+ return( TRUE );
+
+ErrorCleanup:
+
+ if (Buffer != NULL) {
+ ElfpFreeBuffer( Buffer );
+ }
+
+ if (Domain != NULL) {
+ ElfpFreeBuffer( Domain );
+ }
+
+ if (AccountName != NULL) {
+ ElfpFreeBuffer( AccountName );
+ }
+
+ if (*UserSid != NULL) {
+ ElfpFreeBuffer( *UserSid );
+ }
+
+ if (*AuthenticationId != NULL) {
+ ElfpFreeBuffer( *AuthenticationId );
+ }
+
+ return( FALSE );
+}
+
+NTSTATUS
+ElfpGetComputerName (
+ IN LPSTR *ComputerNamePtr)
+
+/*++
+
+Routine Description:
+
+ This routine obtains the computer name from a persistent database,
+ by calling the GetcomputerNameA Win32 Base API
+
+ This routine assumes the length of the computername is no greater
+ than MAX_COMPUTERNAME_LENGTH, space for which it allocates using
+ LocalAlloc. It is necessary for the user to free that space using
+ ElfpFreeBuffer when finished.
+
+Arguments:
+
+ ComputerNamePtr - This is a pointer to the location where the pointer
+ to the computer name is to be placed.
+
+Return Value:
+
+ NERR_Success - If the operation was successful.
+
+ It will return assorted Net or Win32 or NT error messages if not.
+
+--*/
+{
+ DWORD nSize = MAX_COMPUTERNAME_LENGTH + 1;
+
+ //
+ // Allocate a buffer to hold the largest possible computer name.
+ //
+
+ *ComputerNamePtr = ElfpAllocateBuffer(nSize);
+
+ if (*ComputerNamePtr == NULL) {
+ return (GetLastError());
+ }
+
+ //
+ // Get the computer name string into the locally allocated buffer
+ // by calling the Win32 GetComputerNameA API.
+ //
+
+ if (!GetComputerNameA(*ComputerNamePtr, &nSize)) {
+ ElfpFreeBuffer(*ComputerNamePtr);
+ *ComputerNamePtr = NULL;
+ return (GetLastError());
+ }
+
+ return (ERROR_SUCCESS);
+}
+
+VOID
+w_GetComputerName ( )
+
+/*++
+
+Routine Description:
+
+ This routine gets the name of the computer. It checks the global
+ variable to see if the computer name has already been determined.
+ If not, it updates that variable with the name.
+ It does this for the UNICODE and the ANSI versions.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+
+--*/
+{
+ PUNICODE_STRING pNameU=NULL;
+ PANSI_STRING pNameA=NULL;
+ LPSTR pName;
+ NTSTATUS Error;
+ NTSTATUS Status;
+
+
+ if (pGlobalComputerNameU != NULL) {
+ return;
+ }
+ pNameU = ElfpAllocateBuffer (sizeof (UNICODE_STRING));
+ pNameA = ElfpAllocateBuffer (sizeof (ANSI_STRING));
+
+ if ((pNameU != NULL) && (pNameA != NULL)) {
+
+ if ((Error = ElfpGetComputerName (&pName)) == ERROR_SUCCESS) {
+
+ //
+ // ElfpComputerName has allocated a buffer to contain the
+ // ASCII name of the computer. We use that for the ANSI
+ // string structure.
+ //
+ RtlInitAnsiString ( pNameA, pName );
+
+ } else {
+ //
+ // We could not get the computer name for some reason. Set up
+ // the golbal pointer to point to the NULL string.
+ //
+ RtlInitAnsiString ( pNameA, "\0");
+ }
+
+ //
+ // Set up the UNICODE_STRING structure.
+ //
+ Status = RtlAnsiStringToUnicodeString (
+ pNameU,
+ pNameA,
+ TRUE
+ );
+
+ //
+ // If there was no error, set the global variables.
+ // Otherwise, free the buffer allocated by ElfpGetComputerName
+ // and leave the global variables unchanged.
+ //
+ if (NT_SUCCESS(Status)) {
+
+ pGlobalComputerNameU = pNameU; // Set global variable if no error
+ pGlobalComputerNameA = pNameA; // Set global ANSI variable
+
+ } else {
+
+ ElfDbgPrint(("[ELFCLNT] GetComputerName - Error 0x%lx\n", Status));
+ ElfpFreeBuffer(pName);
+ ElfpFreeBuffer (pNameU); // Free the buffers
+ ElfpFreeBuffer (pNameA);
+ }
+
+ }
+}
+
+
+PUNICODE_STRING
+TmpGetComputerNameW ( )
+
+/*++
+
+Routine Description:
+
+ This routine gets the UNICODE name of the computer. It checks the global
+ variable to see if the computer name has already been determined.
+ If not, it calls the worker routine to do that.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ Returns a pointer to the computer name, or a NULL.
+
+
+--*/
+{
+ if (pGlobalComputerNameU == NULL) {
+ w_GetComputerName();
+ }
+ return (pGlobalComputerNameU);
+}
+
+
+
diff --git a/private/eventlog/server/memory.c b/private/eventlog/server/memory.c
new file mode 100644
index 000000000..8510786e1
--- /dev/null
+++ b/private/eventlog/server/memory.c
@@ -0,0 +1,149 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ MEMORY.C
+
+Abstract:
+
+ This file contains the routines that deal with memory management.
+
+Author:
+
+ Rajen Shah (rajens) 12-Jul-1991
+
+[Environment:]
+
+ User Mode - Win32, except for NTSTATUS returned by some functions.
+
+Revision History:
+
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+
+//
+// Implement my own tail-checking, since the system code requires a
+// debugging build
+//
+
+//#define TAIL_CHECKING
+#ifdef TAIL_CHECKING
+#define CHECK_HEAP_TAIL_SIZE 16
+#define CHECK_HEAP_TAIL_FILL 0xAB
+#endif
+
+
+PVOID
+ElfpAllocateBuffer (
+ ULONG Size
+ )
+
+/*++
+
+Routine Description:
+
+ Allocate a buffer of the given size, and return the pointer in BufPtr.
+
+
+Arguments:
+
+
+
+Return Value:
+
+ Pointer to allocated buffer (or NULL).
+
+Note:
+
+
+--*/
+{
+ PVOID BufPtr;
+
+#ifdef TAIL_CHECKING
+ //
+ // Keep the offset of the pattern (so we don't have to have internal
+ // knowledge about the granularity of the heap block) and copy a
+ // known pattern after the end of the user's block
+ //
+ BufPtr = (PVOID *) MIDL_user_allocate ( Size
+ + CHECK_HEAP_TAIL_SIZE + sizeof(DWORD));
+ *((PDWORD)BufPtr) = Size + sizeof(DWORD);
+ (PBYTE) BufPtr += sizeof(DWORD);
+ RtlFillMemory((PBYTE)BufPtr + Size,
+ CHECK_HEAP_TAIL_SIZE,
+ CHECK_HEAP_TAIL_FILL);
+#else
+
+ BufPtr = (PVOID *) MIDL_user_allocate ( Size );
+
+#endif
+
+ return (BufPtr);
+
+}
+
+
+
+VOID
+ElfpFreeBuffer (
+ PVOID BufPtr)
+
+/*++
+
+Routine Description:
+
+ Frees a buffer previously allocated by AllocateBuffer.
+
+
+Arguments:
+
+ Pointer to buffer.
+
+Return Value:
+
+ NOTHING
+
+Note:
+
+
+--*/
+{
+
+#ifdef TAIL_CHECKING
+ {
+ DWORD i;
+ PBYTE pb;
+
+ //
+ // Back up to real start of block
+ //
+
+ (PBYTE)BufPtr -= sizeof(DWORD);
+ i = *((PDWORD)BufPtr);
+ pb = (PBYTE)BufPtr + i;
+
+ for (i = 0; i < CHECK_HEAP_TAIL_SIZE ; i++, pb++) {
+ if (*pb != CHECK_HEAP_TAIL_FILL) {
+ ElfDbgPrint(("[ELF] Heap has been corrupted at 0x%x\n",
+ BufPtr));
+ // Make it access violate
+ pb = (PBYTE) 0;
+ *pb = 1;
+ }
+ }
+ }
+#endif
+
+ MIDL_user_free ( BufPtr );
+ return;
+
+}
diff --git a/private/eventlog/server/operate.c b/private/eventlog/server/operate.c
new file mode 100644
index 000000000..d8e9404c6
--- /dev/null
+++ b/private/eventlog/server/operate.c
@@ -0,0 +1,3117 @@
+/*++
+
+Copyright (c) 1990-1994 Microsoft Corporation
+
+Module Name:
+
+ OPERATE.C
+
+Abstract:
+
+ This file contains all the routines to perform operations on the
+ log files. They are called by the thread that performs requests.
+
+Author:
+
+ Rajen Shah (rajens) 16-Jul-1991
+
+Revision History:
+
+ 04-Apr-1995 MarkBl
+ Resets the file archive attribute on log write. The backup caller
+ clears it.
+ 29-Aug-1994 Danl
+ We no longer grow log files in place. Therefore, the ExtendSize
+ function will allocate a block that is as large as the old size plus
+ the size of the new block that must be added. If this allocation
+ succeeds, then it will free up the old block. If a failure occurs,
+ we continue to use the old block as if we have already grown as much
+ as we can.
+ 22-Jul-1994 Danl
+ ValidFilePos: Changed test for pEndRecordLen > PhysicalEOF
+ so that it uses >=. In the case where pEndRecordLen == PhysicalEOF,
+ we want to wrap to find the last DWORD at the beginning of the File
+ (after the header).
+
+ 08-Jul-1994 Danl
+ PerformWriteRequest: Fixed overwrite logic so that in the case where
+ a log is set up for always-overwrite, that we never go down the branch
+ that indicates the log was full. Previously, it would go down that
+ branch if the current time was less than the log time (ie. someone
+ set the clock back).
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <alertmsg.h> // ALERT_ELF manifests
+
+#define OVERWRITE_AS_NEEDED 0x00000000
+#define NEVER_OVERWRITE 0xffffffff
+
+#if DBG
+#define ELF_ERR_OUT(txt,code,pLogFile) (ElfErrorOut(txt,code,pLogFile))
+#else
+#define ELF_ERR_OUT(txt,code,pLogFile)
+#endif
+
+//
+// Prototypes
+//
+BOOL
+IsPositionWithinRange(
+ PVOID Position,
+ PVOID BeginningRecord,
+ PVOID EndingRecord);
+
+#if DBG
+VOID
+ElfErrorOut(
+ LPSTR ErrorText,
+ DWORD StatusCode,
+ PLOGFILE pLogFile);
+#endif // DBG
+
+
+VOID
+ElfExtendFile (
+ PLOGFILE pLogFile,
+ ULONG SpaceNeeded,
+ PULONG SpaceAvail
+ )
+/*++
+
+Routine Description:
+
+ This routine takes an open log file and extends the file and underlying
+ section and view if possible. If it can't be grown, it caps the file
+ at this size by setting the ConfigMaxFileSize to the Actual. It also
+ updates the SpaceAvail parm which is used in PerformWriteRequest (the
+ caller).
+
+Arguments:
+
+ pLogFile - pointer to a LOGFILE structure for the open logfile
+ ExtendAmount - How much bigger to make the file/section/view
+ SpaceAvail - Update this with how much space was added to the section
+
+Return Value:
+
+ None - If we can't extend the file, we just cap it at this size for the
+ duration of this boot. We'll try again the next time the eventlog
+ is closed and reopened.
+
+Note:
+
+ ExtendAmount should always be granular to 64K.
+
+--*/
+{
+
+ LARGE_INTEGER NewSize;
+ NTSTATUS Status;
+ PVOID BaseAddress;
+ ULONG Size;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ //
+ // Calculate how much to grow the file then extend the section by
+ // that amount. Do this in 64K chunks.
+ //
+
+ SpaceNeeded = ((SpaceNeeded - *SpaceAvail) & 0xFFFF0000) + 0x10000;
+
+ if (SpaceNeeded > (pLogFile->ConfigMaxFileSize - pLogFile->ActualMaxFileSize))
+ {
+
+ //
+ // We can't grow it by the full amount we need. Grow
+ // it to the max size and let file wrap take over.
+ // If there isn't any room to grow, then return;
+ //
+
+ SpaceNeeded = pLogFile->ConfigMaxFileSize -
+ pLogFile->ActualMaxFileSize;
+
+ if (SpaceNeeded == 0) {
+ return;
+ }
+ }
+
+ NewSize = RtlConvertUlongToLargeInteger (pLogFile->ActualMaxFileSize
+ + SpaceNeeded);
+
+ //
+ // Update the file size information, extend the section, and map the
+ // new section
+ //
+
+ Status = NtSetInformationFile(pLogFile->FileHandle,
+ &IoStatusBlock,
+ &NewSize,
+ sizeof(NewSize),
+ FileEndOfFileInformation);
+
+
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrintNC(("[EVENTLOG]ElfExtendFile: NtSetInformationFile "
+ "failed 0x%lx\n",Status));
+ goto ErrorExit;
+ }
+
+ Status = NtExtendSection(pLogFile->SectionHandle, &NewSize);
+
+ if (!NT_SUCCESS(Status)) {
+ goto ErrorExit;
+ }
+
+ //
+ // Now that the section is extended, we need to map the new section.
+ //
+
+ //
+ // Map a view of the entire section (with the extension).
+ // Allow the allocator to tell us where it is located, and
+ // what the size is.
+ //
+ BaseAddress = NULL;
+ Size = 0;
+ Status = NtMapViewOfSection(
+ pLogFile->SectionHandle,
+ NtCurrentProcess(),
+ &BaseAddress,
+ 0,
+ 0,
+ NULL,
+ &Size,
+ ViewUnmap,
+ 0,
+ PAGE_READWRITE);
+
+ if (!NT_SUCCESS(Status)) {
+ //
+ // If this fails, just exit, and we will continue with the
+ // view that we have.
+ //
+ ELF_ERR_OUT("ElfExtendFile:NtMapViewOfSection Failed",
+ Status, pLogFile);
+ goto ErrorExit;
+ }
+
+ //
+ // Unmap the old section.
+ //
+
+ Status = NtUnmapViewOfSection(
+ NtCurrentProcess(),
+ pLogFile->BaseAddress);
+
+ if (!NT_SUCCESS(Status)) {
+ ELF_ERR_OUT("ElfExtendFile:NtUnmapViewOfSection Failed",
+ Status, pLogFile);
+ }
+ pLogFile->BaseAddress = BaseAddress;
+
+ //
+ // We managed to extend the file, update the actual size
+ // and space available and press on.
+ //
+
+ pLogFile->ViewSize += SpaceNeeded;
+ pLogFile->ActualMaxFileSize += SpaceNeeded;
+ *SpaceAvail += SpaceNeeded;
+
+ //
+ // If we were wrapped, turn off the bit since now we're in a state
+ // that is unwrapped, except there may be some dead space after the
+ // header, but before the start of the first full record. This will
+ // be reclaimed when the file really wraps again.
+ //
+
+ pLogFile->Flags &= ~ELF_LOGFILE_HEADER_WRAP;
+
+ //
+ // Now flush this to disk to commit it
+ //
+
+ BaseAddress = pLogFile->BaseAddress;
+ Size = FILEHEADERBUFSIZE;
+
+ Status = NtFlushVirtualMemory(
+ NtCurrentProcess(),
+ &BaseAddress,
+ &Size,
+ &IoStatusBlock
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ ELF_ERR_OUT("ElfExtendFile:NtFlushVirtualMemory Failed",
+ Status, pLogFile);
+ }
+ return;
+
+ErrorExit:
+ //
+ // Couldn't extend the section for some reason. Just wrap the file now.
+ // Cap the file at this size, so we don't try and extend the section on
+ // every write. The next time the eventlog service is started up it
+ // will revert to the configured max again.
+ //
+ // Generate an Alert here - BUGBUG
+ //
+ ELF_ERR_OUT("ElfExtendFile:Couldn't extend the File",
+ Status, pLogFile);
+
+ pLogFile->ConfigMaxFileSize = pLogFile->ActualMaxFileSize;
+
+ return;
+
+}
+
+
+NTSTATUS
+CopyUnicodeToAnsiRecord (
+ PVOID Dest,
+ PVOID Src,
+ PVOID *NewBufPos,
+ PULONG RecordSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reads from the event log specified in the request packet.
+
+ This routine uses memory mapped I/O to access the log file. This makes
+ it much easier to move around the file.
+
+Arguments:
+
+ Dest - Points to destination buffer.
+ Src - Points to the UNICODE record.
+ NewBufPos - Gets offset in Dest buffer after record just transferred.
+ RecordSize - Size of this (ANSI) record.
+
+Return Value:
+
+ Actual size of record copied into the Dest buffer.
+
+Note:
+
+--*/
+{
+ ANSI_STRING StringA;
+ UNICODE_STRING StringU;
+ PEVENTLOGRECORD SrcRecord, DestRecord;
+ PWSTR pStringU;
+ PVOID TempPtr;
+ ULONG PadSize, i;
+ ULONG zero = 0;
+ WCHAR *SrcStrings, *DestStrings;
+ ULONG RecordLength, *pLength;
+ NTSTATUS Status;
+
+ DestRecord = (PEVENTLOGRECORD)Dest;
+ SrcRecord = (PEVENTLOGRECORD)Src;
+
+ DestRecord->TimeGenerated = SrcRecord->TimeGenerated;
+ DestRecord->Reserved = SrcRecord->Reserved;
+ DestRecord->RecordNumber = SrcRecord->RecordNumber;
+ DestRecord->TimeWritten = SrcRecord->TimeWritten;
+ DestRecord->EventID = SrcRecord->EventID;
+ DestRecord->EventType = SrcRecord->EventType;
+ DestRecord->EventCategory = SrcRecord->EventCategory;
+ DestRecord->NumStrings = SrcRecord->NumStrings;
+ DestRecord->UserSidLength = SrcRecord->UserSidLength;
+ DestRecord->DataLength = SrcRecord->DataLength;
+
+ //
+ // Convert and copy over modulename
+ //
+ pStringU = (PWSTR)((ULONG)SrcRecord + sizeof(EVENTLOGRECORD));
+
+ RtlInitUnicodeString ( &StringU, pStringU );
+
+ Status = RtlUnicodeStringToAnsiString (
+ &StringA,
+ &StringU,
+ TRUE
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ TempPtr = (PVOID)((ULONG)DestRecord + sizeof(EVENTLOGRECORD));
+
+ RtlMoveMemory ( TempPtr, StringA.Buffer, StringA.MaximumLength );
+
+ TempPtr = (PVOID)((ULONG) TempPtr + StringA.MaximumLength);
+
+ RtlFreeAnsiString(&StringA);
+
+ //
+ // Convert and copy over computername
+ //
+ // TempPtr points to location in the destination for the computername
+ //
+
+ pStringU = (PWSTR)((ULONG)pStringU + StringU.MaximumLength);
+
+ RtlInitUnicodeString ( &StringU, pStringU );
+
+ Status = RtlUnicodeStringToAnsiString (
+ &StringA,
+ &StringU,
+ TRUE
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ RtlMoveMemory ( TempPtr, StringA.Buffer, StringA.MaximumLength );
+
+ TempPtr = (PVOID)((ULONG) TempPtr + StringA.MaximumLength);
+
+ RtlFreeAnsiString(&StringA);
+
+ }
+ }
+ if (NT_SUCCESS(Status)) {
+
+ // TempPtr points to location after computername - i.e. UserSid.
+ // Before we write out the UserSid, we ensure that we pad the
+ // bytes so that the UserSid starts on a DWORD boundary.
+ //
+ PadSize = sizeof(ULONG) - (((ULONG)TempPtr-(ULONG)DestRecord) % sizeof(ULONG));
+
+ RtlMoveMemory (
+ TempPtr,
+ &zero,
+ PadSize
+ );
+
+ TempPtr = (PVOID)((ULONG)TempPtr + PadSize);
+ //
+ // Copy over the UserSid.
+ //
+ RtlMoveMemory (
+ TempPtr,
+ (PVOID)((ULONG)SrcRecord + SrcRecord->UserSidOffset),
+ SrcRecord->UserSidLength
+ );
+
+ DestRecord->UserSidOffset = (ULONG)( (ULONG)TempPtr
+ - (ULONG)DestRecord);
+ //
+ // Copy over the Strings
+ //
+ TempPtr = (PVOID)((ULONG)TempPtr + SrcRecord->UserSidLength);
+ SrcStrings = (WCHAR *) ((ULONG)SrcRecord + (ULONG)SrcRecord->StringOffset);
+ DestStrings = (WCHAR *)TempPtr;
+
+ for (i=0; i < DestRecord->NumStrings; i++) {
+
+ RtlInitUnicodeString (&StringU, SrcStrings);
+
+ RtlUnicodeStringToAnsiString (
+ &StringA,
+ &StringU,
+ TRUE
+ );
+
+ RtlMoveMemory (
+ DestStrings,
+ StringA.Buffer,
+ StringA.MaximumLength
+ );
+
+ DestStrings = (WCHAR*)( (ULONG)DestStrings
+ + (ULONG)StringA.MaximumLength);
+
+ SrcStrings = (WCHAR*)( (ULONG)SrcStrings
+ + (ULONG)StringU.MaximumLength);
+
+ RtlFreeAnsiString (&StringA);
+ }
+
+ DestRecord->StringOffset = (ULONG)TempPtr - (ULONG)DestRecord;
+
+ //
+ // Copy over the binary Data
+ //
+ // DestStrings points to the point after the last string copied.
+ //
+ TempPtr = (PVOID)DestStrings;
+ DestRecord->DataOffset = (ULONG)TempPtr - (ULONG)DestRecord;
+
+ RtlMoveMemory (
+ TempPtr,
+ (PVOID)((ULONG)SrcRecord + SrcRecord->DataOffset),
+ SrcRecord->DataLength
+ );
+
+ //
+ // Now do the pad bytes.
+ //
+ TempPtr = (PVOID)((ULONG)TempPtr + SrcRecord->DataLength);
+ PadSize = sizeof(ULONG) - (((ULONG)TempPtr-(ULONG)DestRecord) % sizeof(ULONG));
+
+ RtlMoveMemory (
+ TempPtr,
+ &zero,
+ PadSize
+ );
+
+ RecordLength = (ULONG)TempPtr + PadSize + sizeof(ULONG) - (ULONG)DestRecord;
+
+ pLength = (PULONG)((ULONG)TempPtr + PadSize);
+ *pLength = RecordLength;
+ DestRecord->Length = RecordLength;
+ ASSERT (((ULONG)DestRecord + RecordLength) == ((ULONG)pLength + sizeof(ULONG)));
+
+ //
+ // Set up return information
+ //
+ *NewBufPos = (PVOID)((ULONG)DestRecord + RecordLength);
+ *RecordSize = RecordLength;
+
+ }
+ return (Status);
+} // CopyUnicodeToAnsiRecord
+
+BOOL
+ValidFilePos (
+ PVOID Position,
+ PVOID BeginningRecord,
+ PVOID EndingRecord,
+ PVOID PhysicalEOF,
+ PVOID BaseAddress
+ )
+
+/*++
+
+Routine Description:
+
+ This routine determines whether we are pointing to a valid beginning
+ of an event record in the event log. It does this by validating
+ the signature then comparing the length at the beginning of the record to
+ the length at the end, both of which have to be at least the size of the
+ fixed length portion of an eventlog record.
+
+Arguments:
+
+ Position - Pointer to be verified.
+ BeginningRecord - Pointer to the beginning record in the file.
+ EndingRecord - Pointer to the byte after the ending record in the file.
+ PhysicalEOF - Pointer the physical end of the log.
+ BaseAddress - Pointer to the physical beginning of the log.
+
+Return Value:
+
+ TRUE if this position is valid.
+
+Note:
+
+ There is a probability of error if a record just happens to have the
+ ULONG at the current position the same as the value that number of
+ bytes further on in the record. However, this is a very slim chance.
+
+
+--*/
+{
+ PULONG pEndRecordLength;
+ BOOL fValid = FALSE;
+ PEVENTLOGRECORD pEventRecord;
+
+ try {
+ pEventRecord = (PEVENTLOGRECORD)Position;
+
+ //
+ // Verify that the pointer is within the range of BEGINNING->END
+ //
+
+ fValid = IsPositionWithinRange(Position,
+ BeginningRecord,
+ EndingRecord);
+
+ //
+ // If the offset looks OK, then examine the lengths at the beginning
+ // and end of the current record. If they don't match, then the position
+ // is invalid.
+ //
+
+ if (fValid) {
+
+ //
+ // Make sure the length is a multiple number of DWORDS
+ //
+
+ if (pEventRecord->Length & 3) {
+ fValid = FALSE;
+ }
+ else {
+ pEndRecordLength =
+ (PULONG) ((PBYTE)Position + pEventRecord->Length) - 1;
+
+ //
+ // If the file is wrapped, adjust the pointer to reflect the
+ // portion of the record that is wrapped starting after the header
+ //
+
+ if ((PVOID) pEndRecordLength >= PhysicalEOF) {
+ pEndRecordLength = (PULONG) ((PBYTE) BaseAddress +
+ ((PBYTE) pEndRecordLength - (PBYTE) PhysicalEOF) +
+ FILEHEADERBUFSIZE);
+ }
+
+ if (pEventRecord->Length == *pEndRecordLength &&
+ (pEventRecord->Length == ELFEOFRECORDSIZE ||
+ (pEventRecord->Length >= sizeof(EVENTLOGRECORD) &&
+ pEventRecord->Reserved == ELF_LOG_FILE_SIGNATURE))
+ ) {
+
+ fValid = TRUE;
+
+ } else {
+ fValid = FALSE;
+ }
+ }
+ }
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ fValid = FALSE;
+ }
+
+ return(fValid);
+}
+
+
+BOOL
+IsPositionWithinRange(
+ PVOID Position,
+ PVOID BeginningRecord,
+ PVOID EndingRecord)
+{
+ //
+ // Verify that the pointer is within the range of BEGINNING->END
+ //
+
+ if (EndingRecord > BeginningRecord) {
+ if ((Position >= BeginningRecord) && (Position <= EndingRecord))
+ return(TRUE);
+
+ } else if (EndingRecord < BeginningRecord) {
+ if ((Position >= BeginningRecord) || (Position <= EndingRecord))
+ return(TRUE);
+
+ } else {
+ return(FALSE);
+ }
+}
+
+
+PVOID
+FindStartOfNextRecord (
+ PVOID Position,
+ PVOID BeginningRecord,
+ PVOID EndingRecord,
+ PVOID PhysicalStart,
+ PVOID PhysicalEOF,
+ PVOID BaseAddress
+ )
+
+/*++
+
+Routine Description:
+
+ This routine starts at Position, and finds the beginning of the next
+ valid record, wrapping around the physical end of the file if necessary.
+
+Arguments:
+
+ Position - Pointer at which to start the search.
+ BeginningRecord - Pointer to the beginning record in the file.
+ EndingRecord - Pointer to the byte after the ending record in the file.
+ PhysicalStart - Pointer to the start of log infor (after header)
+ PhysicalEOF - Pointer the physical end of the log.
+ BaseAddress - Pointer to the physical beginning of the log.
+
+Return Value:
+
+ A pointer to the start of the next valid record. NULL if there is no
+ valid record.
+
+Note:
+
+ There is a probability of error if a record just happens to have the
+ ULONG at the current position the same as the value that number of
+ bytes further on in the record. However, this is a very slim chance.
+
+
+--*/
+{
+
+ PULONG ptr;
+ PULONG EndOfBlock;
+ PULONG EndOfFile;
+ PVOID pRecord;
+ ULONG Size;
+ BOOL StillLooking = TRUE;
+
+ //
+ // Search for a ULONG which matches a record signature
+ //
+
+ ptr = (PULONG) Position;
+ EndOfBlock = EndOfFile = (PULONG) PhysicalEOF - 1;
+
+ while (StillLooking) {
+
+ //
+ // Check to see if it is the EOF record
+ //
+
+ if (*ptr == ELFEOFRECORDSIZE) {
+
+ //
+ // Only scan up to the end of the file. Just compare up the
+ // constant information
+ //
+
+ Size = min (ELFEOFUNIQUEPART,
+ (PBYTE) PhysicalEOF - (PBYTE) ptr);
+
+ pRecord = (PVOID) CONTAINING_RECORD(ptr, ELF_EOF_RECORD,
+ RecordSizeBeginning);
+
+ if (RtlCompareMemory (
+ pRecord,
+ &EOFRecord,
+ Size) == Size) {
+ //
+ // This is the EOF record, back up to the last record
+ //
+
+ (PBYTE) pRecord -= *((PULONG) pRecord - 1);
+ if (pRecord < PhysicalStart) {
+ pRecord = (PVOID) ((PBYTE) PhysicalEOF -
+ ((PBYTE)PhysicalStart - (PBYTE)pRecord));
+ }
+
+ }
+
+ if (ValidFilePos(pRecord, BeginningRecord, EndingRecord,
+ PhysicalEOF, BaseAddress)) {
+ return(pRecord);
+ }
+ }
+
+ //
+ // Check to see if it is an event record
+ //
+
+ if (*ptr == ELF_LOG_FILE_SIGNATURE) {
+
+ //
+ // This is a signature, see if the containing record is valid
+ //
+
+ pRecord = (PVOID) CONTAINING_RECORD(ptr, EVENTLOGRECORD,
+ Reserved);
+ if (ValidFilePos(pRecord, BeginningRecord, EndingRecord,
+ PhysicalEOF, BaseAddress)) {
+ return(pRecord);
+ }
+
+ }
+
+ //
+ // Bump to the next byte and see if we're done.
+ //
+
+ ptr++;
+
+ if (ptr >= EndOfBlock) {
+
+ //
+ // Need the second test on this condition in case Position
+ // happens to equal PhysicalEOF - 1 (EndOfBlock initial value);
+ // without this, this loop would terminate prematurely.
+ //
+
+ if ((EndOfBlock == (PULONG) Position) &&
+ ((PULONG) Position != EndOfFile)) {
+
+ //
+ // This was the top half, so we're done
+ //
+
+ StillLooking = FALSE;
+ }
+ else {
+
+ //
+ // This was the bottom half, let's look in the top half
+ //
+
+ EndOfBlock = (PULONG) Position;
+ ptr = (PULONG) PhysicalStart;
+ }
+ }
+ }
+
+
+
+ //
+ // Didn't find a valid record
+ //
+
+ return(NULL);
+
+}
+
+PVOID
+NextRecordPosition (
+ ULONG ReadFlags,
+ PVOID CurrPosition,
+ ULONG CurrRecordLength,
+ PVOID BeginRecord,
+ PVOID EndRecord,
+ PVOID PhysicalEOF,
+ PVOID PhysStart
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine seeks to the beginning of the next record to be read
+ depending on the flags in the request packet.
+
+Arguments:
+
+ ReadFlags - Read forwards or backwards
+ CurrPosition - Pointer to the current position.
+ CurrRecordLength - Length of the record at the last position read.
+ BeginRecord - Logical first record
+ EndRecord - Logical last record (EOF record)
+ PhysEOF - End of file
+ PhysStart - Start of file pointer (following file header).
+
+Return Value:
+
+ New position or NULL if invalid record.
+
+Note:
+
+
+--*/
+{
+
+ PVOID NewPosition;
+ ULONG Length;
+ PDWORD FillDword;
+
+ if (ReadFlags & EVENTLOG_FORWARDS_READ) {
+
+ //
+ // If we're pointing at the EOF record, just set the position to
+ // the first record
+ //
+
+ if (CurrRecordLength == ELFEOFRECORDSIZE) {
+ return(BeginRecord);
+ }
+
+ NewPosition = (PVOID) ((ULONG)CurrPosition + CurrRecordLength);
+
+ //
+ // Take care of wrapping.
+ //
+
+ if (NewPosition >= PhysicalEOF) {
+ NewPosition = (PVOID)((PBYTE)PhysStart +
+ ((PBYTE) NewPosition - (PBYTE) PhysicalEOF));
+ }
+
+ //
+ // If this is a ELF_SKIP_DWORD, skip to the top of the file
+ //
+
+ if (*(PDWORD) NewPosition == ELF_SKIP_DWORD) {
+ NewPosition = PhysStart;
+ }
+
+ } else { // Reading backwards.
+
+ ASSERT (ReadFlags & EVENTLOG_BACKWARDS_READ);
+
+ if (CurrPosition == BeginRecord) {
+
+ //
+ // This is the "end of file" if we're reading backwards.
+ //
+
+ return(EndRecord);
+ }
+ else if (CurrPosition == PhysStart) {
+
+ //
+ // Flip to the bottom of the file, but skip and ELF_SKIP_DWORDs
+ //
+
+ FillDword = (PDWORD) PhysicalEOF; // last dword
+ FillDword--;
+ while (*FillDword == ELF_SKIP_DWORD) {
+ FillDword--;
+ }
+ CurrPosition = (PVOID) (FillDword + 1);
+ }
+
+ Length = *((PULONG) CurrPosition - 1);
+ if (Length < ELFEOFRECORDSIZE) { // Bogus length, must be invalid record
+ return(NULL);
+ }
+
+ NewPosition = (PVOID)((PBYTE) CurrPosition - Length);
+
+ //
+ // Take care of wrapping
+ //
+
+ if (NewPosition < PhysStart) {
+ NewPosition = (PVOID)((PBYTE) PhysicalEOF -
+ ((PBYTE) PhysStart - (PBYTE) NewPosition));
+ }
+ }
+ return (NewPosition);
+} // NextRecordPosition
+
+
+
+NTSTATUS
+SeekToStartingRecord (
+ PELF_REQUEST_RECORD Request,
+ PVOID *ReadPosition,
+ PVOID BeginRecord,
+ PVOID EndRecord,
+ PVOID PhysEOF,
+ PVOID PhysStart
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine seeks to the correct position as indicated in the
+ request packet.
+
+Arguments:
+
+ Pointer to the request packet.
+ Pointer to a pointer where the final position after the seek is returned.
+
+Return Value:
+
+ NTSTATUS and new position in file.
+
+Note:
+
+ This routine ensures that it is possible to seek to the position
+ specified in the request packet. If not, then an error is returned
+ which indicates that the file probably changed between the two
+ READ operations, or else the record offset specified is beyond the
+ end of the file.
+
+--*/
+{
+ PVOID Position;
+ ULONG RecordLen;
+ ULONG NumRecordsToSeek;
+ ULONG BytesPerRecord;
+ ULONG NumberOfRecords;
+ ULONG NumberOfBytes;
+ ULONG ReadFlags;
+
+ //
+ // If the beginning and the end are the same, then there are no
+ // entries in this file.
+ //
+ if (BeginRecord == EndRecord)
+ return (STATUS_END_OF_FILE);
+
+ //
+ // Find the last position (or the "beginning" if this is the first READ
+ // call for this handle).
+ //
+
+ if (Request->Pkt.ReadPkt->ReadFlags & EVENTLOG_SEQUENTIAL_READ) {
+
+ if (Request->Pkt.ReadPkt->ReadFlags & EVENTLOG_FORWARDS_READ) {
+
+ // If this is the first READ operation, LastSeekPosition will
+ // be zero. In that case, we set the position to the first
+ // record (in terms of time) in the file.
+ //
+ if (Request->Pkt.ReadPkt->LastSeekPos == 0) {
+
+ Position = BeginRecord;
+
+ } else {
+
+ Position = (PVOID)((PBYTE) Request->LogFile->BaseAddress
+ + Request->Pkt.ReadPkt->LastSeekPos );
+
+
+ //
+ // If we're changing the direction we're reading, skip
+ // forward one record. This is because we're pointing at
+ // the "next" record based on the last read direction
+ //
+
+ if (!(Request->Pkt.ReadPkt->Flags & ELF_LAST_READ_FORWARD)) {
+
+ Position = NextRecordPosition (
+ Request->Pkt.ReadPkt->ReadFlags,
+ Position,
+ ((PEVENTLOGRECORD) Position)->Length,
+ BeginRecord,
+ EndRecord,
+ PhysEOF,
+ PhysStart
+ );
+ }
+ else {
+ //
+ // This *really* cheesy check exists to handle the case
+ // where Position could be on an ELF_SKIP_DWORD pad
+ // dword at end of the file.
+ //
+ // NB: Must be prepared to handle an exception since
+ // a somewhat unknown pointer is dereferenced.
+ //
+ // BUGBUG: The eventlog code needs to handle exceptions
+ // in *many* more cases!
+ //
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ try {
+ if (IsPositionWithinRange(Position,
+ BeginRecord,
+ EndRecord))
+ {
+ //
+ // If this is a ELF_SKIP_DWORD, skip to the
+ // top of the file.
+ //
+
+ if (*(PDWORD) Position == ELF_SKIP_DWORD) {
+ Position = PhysStart;
+ }
+ } else {
+ //
+ // More likely the caller's handle was invalid
+ // if the position was not within range.
+ //
+
+ Status = STATUS_INVALID_HANDLE;
+ }
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = STATUS_EVENTLOG_FILE_CORRUPT;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ *ReadPosition = NULL;
+ return(Status);
+ }
+ }
+ }
+
+ } else { // READ backwards
+
+ // If this is the first READ operation, LastSeekPosition will
+ // be zero. In that case, we set the position to the last
+ // record (in terms of time) in the file.
+ //
+ if (Request->Pkt.ReadPkt->LastSeekPos == 0) {
+
+ Position = EndRecord;
+
+ //
+ // Subtract the length of the last record from the current
+ // position to get to the beginning of the record.
+ //
+ // If that moves beyond the physical beginning of the file,
+ // then we need to wrap around to the physical end of the file.
+ //
+
+ Position = (PVOID)((PBYTE)Position - *((PULONG)Position - 1));
+
+ if (Position < PhysStart) {
+ Position = (PVOID)((PBYTE)PhysEOF
+ - ((PBYTE)PhysStart - (PBYTE)Position));
+ }
+ } else {
+
+ Position = (PVOID)((PBYTE) Request->LogFile->BaseAddress
+ + Request->Pkt.ReadPkt->LastSeekPos );
+
+ //
+ // If we're changing the direction we're reading, skip
+ // forward one record. This is because we're pointing at
+ // the "next" record based on the last read direction
+ //
+
+ if (Request->Pkt.ReadPkt->Flags & ELF_LAST_READ_FORWARD) {
+
+ Position = NextRecordPosition (
+ Request->Pkt.ReadPkt->ReadFlags,
+ Position,
+ 0, // not used if reading backwards
+ BeginRecord,
+ EndRecord,
+ PhysEOF,
+ PhysStart
+ );
+ }
+ }
+ }
+
+ } else if (Request->Pkt.ReadPkt->ReadFlags & EVENTLOG_SEEK_READ) {
+
+ //
+ // Make sure the record number passed in is valid
+ //
+
+ if (Request->Pkt.ReadPkt->RecordNumber <
+ Request->LogFile->OldestRecordNumber ||
+ Request->Pkt.ReadPkt->RecordNumber >=
+ Request->LogFile->CurrentRecordNumber) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // We're seeking to an absolute record number, so use the following
+ // algorhythm:
+ //
+ // There are two defines that control the process:
+ //
+ // MAX_WALKING_DISTANCE - when we get this close to the record,
+ // we just sequentially read records till we find the right one
+ //
+ // MAX_TRIES - we'll only try this many times to get within
+ // walking distance by calculation using average record size,
+ // after this, we'll just brute force it from where we are
+ //
+ // Calculate the average number of bytes per record
+ //
+ // Based on this number seek to where the record should start
+ //
+ // Find the start of the next record in the file
+ //
+ // If it's within "walking distance" move forward sequentially
+ // to the right record
+ //
+ // If it's not, recalcuate average bytes per record for the records
+ // between the start and the current record, and repeat
+ //
+ // Have a max number of tries at this, then just walk from wherever
+ // we are to the right record
+ //
+
+#define MAX_WALKING_DISTANCE 5
+#define MAX_TRIES 5
+
+ //
+ // Calculate the average number of bytes per record
+ //
+
+ NumberOfRecords = Request->LogFile->CurrentRecordNumber -
+ Request->LogFile->OldestRecordNumber;
+ NumberOfBytes = Request->LogFile->Flags & ELF_LOGFILE_HEADER_WRAP ?
+ Request->LogFile->ActualMaxFileSize :
+ Request->LogFile->EndRecord;
+ NumberOfBytes -= FILEHEADERBUFSIZE;
+ BytesPerRecord = NumberOfBytes / NumberOfRecords;
+
+ //
+ // Calcuate the first guess as to what the offset of the desired
+ // record should be
+ //
+
+ Position = (PVOID) ((PBYTE) Request->LogFile->BaseAddress
+ + Request->LogFile->BeginRecord
+ + BytesPerRecord *
+ (Request->Pkt.ReadPkt->RecordNumber -
+ Request->LogFile->OldestRecordNumber));
+
+ //
+ // Align the position to a ULONG bountry.
+ //
+
+ Position = (PVOID) (((ULONG) Position + sizeof(ULONG) - 1) &
+ ~(sizeof(ULONG) - 1));
+
+ //
+ // Take care of file wrap
+ //
+
+ if (Position >= PhysEOF) {
+
+ Position = (PVOID)((PBYTE)PhysStart +
+ ((PBYTE) Position - (PBYTE) PhysEOF));
+ }
+ //
+ // Get to the start of the next record after position
+ //
+
+ Position = FindStartOfNextRecord(Position, BeginRecord, EndRecord,
+ PhysStart, PhysEOF, Request->LogFile->BaseAddress);
+
+ ASSERT(Position);
+
+ if (Request->Pkt.ReadPkt->RecordNumber >
+ ((PEVENTLOGRECORD) Position)->RecordNumber) {
+
+ NumRecordsToSeek = Request->Pkt.ReadPkt->RecordNumber -
+ ((PEVENTLOGRECORD) Position)->RecordNumber;
+ ReadFlags = EVENTLOG_FORWARDS_READ;
+ }
+
+ else {
+
+ NumRecordsToSeek = ((PEVENTLOGRECORD) Position)->RecordNumber -
+ Request->Pkt.ReadPkt->RecordNumber;
+ ReadFlags = EVENTLOG_BACKWARDS_READ;
+ }
+
+ ElfDbgPrint(("[ELF] Walking %d records\n", NumRecordsToSeek));
+
+ while (NumRecordsToSeek--) {
+
+ RecordLen = ((PEVENTLOGRECORD) Position)->Length;
+
+ Position = NextRecordPosition (
+ ReadFlags,
+ Position,
+ RecordLen,
+ BeginRecord,
+ EndRecord,
+ PhysEOF,
+ PhysStart
+ );
+ }
+
+ } // if SEEK_READ
+
+ *ReadPosition = Position; // This is the new seek position
+
+ if (!Position) { // The record was invalid
+ return(STATUS_EVENTLOG_FILE_CORRUPT);
+ }
+ else {
+ return (STATUS_SUCCESS);
+ }
+
+} // SeekToStartingRecord
+
+
+NTSTATUS
+ReadFromLog ( PELF_REQUEST_RECORD Request )
+
+/*++
+
+Routine Description:
+
+ This routine reads from the event log specified in the request packet.
+
+ This routine uses memory mapped I/O to access the log file. This makes
+ it much easier to move around the file.
+
+Arguments:
+
+ Pointer to the request packet.
+
+Return Value:
+
+ NTSTATUS.
+
+Note:
+
+ When we come here, we are impersonating the client. If we get a
+ fault accessing the user's buffer, the fault goes to the user,
+ not the service.
+
+--*/
+{
+ NTSTATUS Status;
+ PVOID ReadPosition; // Current read position in file
+ PVOID XferPosition; // Position from where to copy bytes
+ PVOID BufferPosition; // Current position in user's buffer
+ ULONG BytesToMove; // Number of bytes to move
+ ULONG TotalBytesRead; // Total Bytes transferred
+ ULONG TotalRecordsRead; // Total records transferred
+ ULONG BytesInBuffer; // Bytes remaining in buffer
+ ULONG RecordSize; // Size of event record
+ PVOID PhysicalEOF; // Physical end of file
+ PVOID PhysStart; // Physical start of file (after file hdr)
+ PVOID BeginRecord; // Points to first record
+ PVOID EndRecord; // Points to byte after last record
+ PVOID TempBuf, TempBufferPosition;
+ ULONG RecordBytesTransferred;
+
+ //
+ // Initialize variables.
+ //
+
+ BytesInBuffer = Request->Pkt.ReadPkt->BufferSize;
+ BufferPosition = Request->Pkt.ReadPkt->Buffer;
+ TotalBytesRead = 0;
+ TotalRecordsRead = 0;
+ PhysicalEOF = (PVOID) ((LPBYTE) Request->LogFile->BaseAddress
+ + Request->LogFile->ViewSize);
+
+ PhysStart = (PVOID) ((LPBYTE)Request->LogFile->BaseAddress
+ + FILEHEADERBUFSIZE);
+
+ BeginRecord = (PVOID) ((LPBYTE) Request->LogFile->BaseAddress
+ + Request->LogFile->BeginRecord );// Start at first record
+
+ EndRecord = (PVOID) ((LPBYTE)Request->LogFile->BaseAddress
+ + Request->LogFile->EndRecord);// Byte after end of last record
+
+ //
+ // "Seek" to the starting record depending on either the last seek
+ // position, or the starting record offset passed in.
+ //
+
+ Status = SeekToStartingRecord (
+ Request,
+ &ReadPosition,
+ BeginRecord,
+ EndRecord,
+ PhysicalEOF,
+ PhysStart
+ );
+
+ if (NT_SUCCESS (Status) ) {
+
+ //
+ // Make sure the record is valid
+ //
+
+ if (!ValidFilePos(ReadPosition,
+ BeginRecord,
+ EndRecord,
+ PhysicalEOF,
+ Request->LogFile->BaseAddress
+ ))
+ {
+
+ Request->Pkt.ReadPkt->BytesRead = 0;
+ Request->Pkt.ReadPkt->RecordsRead = 0;
+
+ return (STATUS_INVALID_HANDLE);
+
+ }
+
+ RecordSize = *((PULONG)ReadPosition);
+
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ // While there are records to be read, and more space in the buffer,
+ // keep on reading records into the buffer.
+ //
+
+ while ( (RecordSize <= BytesInBuffer)
+ && (RecordSize != ELFEOFRECORDSIZE)) {
+
+ //
+ // If we were called by an ANSI API, then we need to read the
+ // next record into a temporary buffer, process the data in
+ // that record and copy it over to the real buffer as ANSI
+ // strings (rather than UNICODE).
+ //
+
+ if (Request->Pkt.ReadPkt->Flags & ELF_IREAD_ANSI) {
+
+ TempBuf = ElfpAllocateBuffer (RecordSize);
+
+ if (TempBuf == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ TempBufferPosition = BufferPosition; // Save this away
+ BufferPosition = TempBuf; // Read into TempBuf
+ }
+
+ //
+ // If the number of bytes to the end of the file is less than the
+ // size of the record, then part of the record has wrapped to the
+ // beginning of the file - transfer the bytes piece-meal.
+ //
+ // Otherwise, transfer the whole record.
+ //
+
+ BytesToMove = min (RecordSize, (ULONG)
+ ((PBYTE)(PhysicalEOF) -
+ (PBYTE)(ReadPosition)));
+
+ XferPosition = ReadPosition;
+
+ if ( BytesToMove < RecordSize) {
+
+ //
+ // We need to copy the bytes up to the end of the file,
+ // and then wrap around and copy the remaining bytes of
+ // this record.
+ //
+
+ ASSERT (BytesToMove + TotalBytesRead <=
+ Request->Pkt.ReadPkt->BufferSize);
+ RtlMoveMemory ( BufferPosition,
+ XferPosition,
+ BytesToMove
+ );
+
+ //
+ // Advance user buffer pointer.
+ // Move read position to the beginning of the file, past the
+ // file header.
+ // Update bytes remaining to be moved for this record.
+ //
+
+ BufferPosition = (PVOID) ((PBYTE)BufferPosition
+ + BytesToMove);
+
+ XferPosition = PhysStart;
+
+ BytesToMove = RecordSize - BytesToMove; // Remaining bytes
+
+ }
+
+ //
+ // Move the remaining bytes of the record OR the full record.
+ //
+
+ ASSERT (RecordSize + TotalBytesRead <=
+ Request->Pkt.ReadPkt->BufferSize);
+ RtlMoveMemory ( BufferPosition,
+ XferPosition,
+ BytesToMove
+ );
+
+ //
+ // Update to new read positions
+ //
+
+ BufferPosition = (PVOID) ((PBYTE)BufferPosition
+ + BytesToMove);
+
+ //
+ // If we were called by an ANSI API, then we need to take the
+ // record read into TempBuf and transfer it over to the user's
+ // buffer while converting any UNICODE strings to ANSI.
+ //
+
+ if (Request->Pkt.ReadPkt->Flags & ELF_IREAD_ANSI) {
+
+ Status = CopyUnicodeToAnsiRecord (
+ TempBufferPosition,
+ TempBuf,
+ &BufferPosition,
+ &RecordBytesTransferred
+ );
+
+ // RecordBytesTransferred contains the bytes actually
+ // copied into the user's buffer.
+ // BufferPosition points to the point in the user's buffer
+ // just after this record.
+ //
+ ElfpFreeBuffer (TempBuf); // Free the temp buffer
+
+ if (!NT_SUCCESS(Status)) {
+ break; // Exit this loop
+ }
+
+ } else {
+
+ RecordBytesTransferred = RecordSize;
+ }
+
+ //
+ // Update the byte and record counts
+ //
+
+ TotalRecordsRead++;
+ TotalBytesRead += RecordBytesTransferred;
+ BytesInBuffer -= RecordBytesTransferred;
+
+ ReadPosition = NextRecordPosition (
+ Request->Pkt.ReadPkt->ReadFlags,
+ ReadPosition,
+ RecordSize,
+ BeginRecord,
+ EndRecord,
+ PhysicalEOF,
+ PhysStart
+ );
+
+ if (!ReadPosition) {
+ return(STATUS_EVENTLOG_FILE_CORRUPT);
+ }
+
+ RecordSize = *(PULONG)ReadPosition;
+
+ } // while
+
+ //
+ // If we got to the end and did not read in any records, return
+ // an error indicating that the user's buffer is too small if
+ // we're not at the EOF record, or end of file if we are.
+ //
+
+ if (TotalRecordsRead == 0) {
+ if (RecordSize == ELFEOFRECORDSIZE) {
+ Status = STATUS_END_OF_FILE;
+ }
+
+ else {
+
+ //
+ // We didn't read any records, and we're not at EOF, so
+ // the buffer was too small
+ //
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+ Request->Pkt.ReadPkt->MinimumBytesNeeded = RecordSize;
+ }
+ }
+
+ //
+ // Update the current file position.
+ //
+
+ Request->Pkt.ReadPkt->LastSeekPos =
+ (ULONG)ReadPosition
+ - (ULONG)Request->LogFile->BaseAddress;
+ Request->Pkt.ReadPkt->LastSeekRecord += TotalRecordsRead;
+
+ }
+
+ //
+ // Set the bytes read in the request packet for return to client.
+ //
+
+ Request->Pkt.ReadPkt->BytesRead = TotalBytesRead;
+ Request->Pkt.ReadPkt->RecordsRead = TotalRecordsRead;
+
+ return (Status);
+
+} // ReadFromLog
+
+
+
+
+VOID
+PerformReadRequest ( PELF_REQUEST_RECORD Request )
+
+/*++
+
+Routine Description:
+
+ This routine performs the READ request.
+ It first grabs the log file structure resource and then proceeds
+ to read from the file. If the resource is not available, it will
+ block until it is.
+
+ This routine impersonates the client in order to ensure that the correct
+ access control is uesd. If the client does not have permission to read
+ the file, the operation will fail.
+
+Arguments:
+
+ Pointer to the request packet.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ //
+ // Get shared access to the log file. This will allow multiple
+ // readers to get to the file together.
+ //
+
+ RtlAcquireResourceShared (
+ &Request->Module->LogFile->Resource,
+ TRUE // Wait until available
+ );
+
+
+ //
+ // Set status field in the request packet.
+ //
+
+ Request->Status = (NTSTATUS) ReadFromLog ( Request );
+
+ //
+ // Free the resource
+ //
+
+ RtlReleaseResource ( &Request->Module->LogFile->Resource );
+
+
+} // PerformReadRequest
+
+
+
+WCHAR wszAltDosDevices[] = L"\\DosDevices\\";
+WCHAR wszDosDevices[] = L"\\??\\";
+#define DOSDEVICES_LEN ((sizeof(wszDosDevices) / sizeof(WCHAR)) - 1)
+#define ALTDOSDEVICES_LEN ((sizeof(wszAltDosDevices) / sizeof(WCHAR)) - 1)
+
+
+VOID
+WriteToLog (
+ PLOGFILE pLogFile,
+ PVOID Buffer,
+ ULONG BufSize,
+ PULONG Destination,
+ ULONG PhysEOF,
+ ULONG PhysStart
+ )
+
+/*++
+
+Routine Description:
+
+ This routine writes the record into the log file, allowing for wrapping
+ around the end of the file.
+
+ It assumes that the caller has serialized access to the file, and has
+ ensured that there is enough space in the file for the record.
+
+Arguments:
+
+ Buffer - Pointer to the buffer containing the event record.
+ BufSize - Size of the record to be written.
+ Destination - Pointer to the destination - which is in the log file.
+ PhysEOF - Physical end of file.
+ PhysStart - Physical beginning of file (past the file header).
+
+Return Value:
+
+ NONE.
+
+Note:
+
+
+--*/
+{
+ ULONG BytesToCopy;
+ ULONG NewDestination;
+ NTSTATUS Status;
+ LARGE_INTEGER ByteOffset;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PVOID BaseAddress;
+ LPWSTR pwszLogFileName;
+
+ BytesToCopy = min (PhysEOF - *Destination, BufSize);
+
+ ByteOffset = RtlConvertUlongToLargeInteger (*Destination) ;
+ Status = NtWriteFile(
+ pLogFile->FileHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ Buffer, // Buffer
+ BytesToCopy, // Length
+ &ByteOffset, // Byteoffset
+ NULL); // Key
+
+ NewDestination = *Destination + BytesToCopy;
+
+ if (BytesToCopy != BufSize) {
+
+ //
+ // Wrap around to the beginning of the file and copy the
+ // rest of the data.
+ //
+
+ Buffer = (PVOID)((PBYTE) Buffer + BytesToCopy);
+
+ BytesToCopy = BufSize - BytesToCopy;
+
+ ByteOffset = RtlConvertUlongToLargeInteger (PhysStart);
+ Status = NtWriteFile(
+ pLogFile->FileHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ Buffer, // Buffer
+ BytesToCopy, // Length
+ &ByteOffset, // Byteoffset
+ NULL); // Key
+
+ NewDestination = PhysStart + BytesToCopy;
+
+ //
+ // Set "wrap" bit in log file structure
+ //
+
+ pLogFile->Flags |= ELF_LOGFILE_HEADER_WRAP;
+
+ //
+ // Now flush this to disk to commit it
+ //
+
+ BaseAddress = pLogFile->BaseAddress;
+ BytesToCopy = FILEHEADERBUFSIZE;
+
+ Status = NtFlushVirtualMemory(
+ NtCurrentProcess(),
+ &BaseAddress,
+ &BytesToCopy,
+ &IoStatusBlock
+ );
+ }
+
+ *Destination = NewDestination; // Return new destination
+
+ //
+ // Providing all succeeded above, if not set, set the archive file
+ // attribute on this log.
+ //
+
+ if (NT_SUCCESS(Status) && !(pLogFile->Flags & ELF_LOGFILE_ARCHIVE_SET)) {
+
+ //
+ // Advance past prefix string, '\??\' or '\DosDevices\'
+ //
+
+ if ((pLogFile->LogFileName->Length / 2) >= DOSDEVICES_LEN &&
+ !_wcsnicmp(wszDosDevices, pLogFile->LogFileName->Buffer,
+ DOSDEVICES_LEN)) {
+ pwszLogFileName = pLogFile->LogFileName->Buffer + DOSDEVICES_LEN;
+ }
+ else
+ if ((pLogFile->LogFileName->Length / 2) >= ALTDOSDEVICES_LEN &&
+ !_wcsnicmp(wszAltDosDevices, pLogFile->LogFileName->Buffer,
+ ALTDOSDEVICES_LEN)) {
+ pwszLogFileName = pLogFile->LogFileName->Buffer + ALTDOSDEVICES_LEN;
+ }
+ else {
+ pwszLogFileName = pLogFile->LogFileName->Buffer;
+ }
+
+ if (SetFileAttributes(pwszLogFileName, FILE_ATTRIBUTE_ARCHIVE)) {
+ pLogFile->Flags |= ELF_LOGFILE_ARCHIVE_SET;
+ }
+ else {
+ ElfDbgPrintNC(("[ELF] SetFileAttributes on file (%ws) failed, "
+ "WIN32 error = 0x%lx\n",
+ pwszLogFileName,
+ GetLastError()));
+ }
+ }
+
+} // WriteToLog
+
+
+
+VOID
+PerformWriteRequest ( PELF_REQUEST_RECORD Request)
+
+/*++
+
+Routine Description:
+
+ This routine writes the event log entry to the log file specified in
+ the request packet.
+ There is no need to impersonate the client since we want all clients
+ to have access to writing to the log file.
+
+ This routine does not use memory mapped I/O to access the log file. This
+ is so the changes can be immediately committed to disk if that was how
+ the log file was opened.
+
+Arguments:
+
+ Pointer to the request packet.
+
+Return Value:
+
+ NONE
+
+Note:
+
+--*/
+{
+ static ULONG LastAlertWritten = 0; // Don't Generate alerts too often
+ NTSTATUS Status;
+ ULONG WritePos; // Position to write record
+ LARGE_INTEGER Time;
+ ULONG SpaceNeeded; // Record size + "buffer" size
+ ULONG CurrentTime;
+ PEVENTLOGRECORD EventRecord;
+ ULONG RecordSize;
+ ULONG DeletedRecordOffset;
+ ULONG SpaceAvail;
+ ULONG EarliestTime;
+ PLOGFILE pLogFile; // For optimized access to structure
+ PELF_LOGFILE_HEADER pFileHeader;
+ PVOID BaseAddress;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PEVENTLOGRECORD pEventLogRecord;
+ PDWORD FillDword;
+ ULONG OverwrittenEOF = 0;
+#ifdef _CAIRO_
+ BOOL fRaiseAlert = FALSE;
+#endif // _CAIRO_
+
+ pLogFile = Request->LogFile; // Set local variable
+
+ //
+ // Get exclusive access to the log file. This will ensure no one
+ // else is accessing the file.
+ //
+
+ RtlAcquireResourceExclusive (
+ &pLogFile->Resource,
+ TRUE // Wait until available
+ );
+
+ //
+ // Put in the record number
+ //
+
+ pEventLogRecord = (PEVENTLOGRECORD) Request->Pkt.WritePkt->Buffer;
+ pEventLogRecord->RecordNumber = pLogFile->CurrentRecordNumber;
+
+ //
+ // Now, go to the end of the file and look for empty space.
+ //
+ // If there is enough space to write out the record, just
+ // write it out and update the pointers.
+ //
+ // If there isn't enough space, then we need to check if we can
+ // wrap around the file without overwriting any records that are
+ // within the time retention period.
+ // If we cannot find any room, then we have to return an error
+ // that the file is full (and alert the administrator).
+ //
+
+ RecordSize = Request->Pkt.WritePkt->Datasize;
+
+ SpaceNeeded = RecordSize + ELFEOFRECORDSIZE;
+
+ if (pLogFile->EndRecord > pLogFile->BeginRecord) {
+
+ //
+ // The current write position is after the position of the first
+ // record, then we can write up to the end of the file without
+ // worrying about overwriting existing records.
+ //
+
+ SpaceAvail = pLogFile->ActualMaxFileSize - (pLogFile->EndRecord -
+ pLogFile->BeginRecord + FILEHEADERBUFSIZE);
+
+
+ } else if (pLogFile->EndRecord == pLogFile->BeginRecord
+ && !(pLogFile->Flags & ELF_LOGFILE_HEADER_WRAP)) {
+
+ //
+ // If the write position is equal to the position of the first
+ // record, and we have't wrapped yet, then the file is "empty"
+ // and so we have room to the physical end of the file.
+ //
+
+ SpaceAvail = pLogFile->ActualMaxFileSize - FILEHEADERBUFSIZE;
+
+ } else {
+
+ //
+ // If our write position is before the position of the first record, then
+ // the file has wrapped and we need to deal with overwriting existing
+ // records in the file.
+ //
+
+ SpaceAvail = pLogFile->BeginRecord - pLogFile->EndRecord;
+
+ }
+
+ //
+ // We now have the number of bytes available to write the record
+ // WITHOUT overwriting any existing records - in SpaceAvail.
+ // If that amount is not sufficient, then we need to create more space
+ // by "deleting" existing records that are older than the retention
+ // time that was configured for this file.
+ //
+ // We check the retention time against the time when the log was
+ // written since that is consistent at the server. We cannot use the
+ // client's time since that may vary if the clients are in different
+ // time zones.
+ //
+
+ NtQuerySystemTime(&Time);
+ RtlTimeToSecondsSince1970(&Time, &CurrentTime);
+
+ EarliestTime = CurrentTime - pLogFile->Retention;
+
+ Status = STATUS_SUCCESS; // Initialize for return to caller
+
+ //
+ // Check to see if the file hasn't reached it's maximum allowable
+ // size yet, and also hasn't wrapped. If not, grow it by as much as
+ // needed, in 64K chunks.
+ //
+
+ if (pLogFile->ActualMaxFileSize < pLogFile->ConfigMaxFileSize &&
+ SpaceNeeded > SpaceAvail &&
+ !(pLogFile->Flags & ELF_LOGFILE_HEADER_WRAP))
+ {
+
+ //
+ // Extend it. This call cannot fail. If it can't extend it, it
+ // just caps it at the current size by changing
+ // pLogFile->ConfigMaxFileSize
+ //
+
+ ElfExtendFile(pLogFile, SpaceNeeded, &SpaceAvail);
+ }
+
+ //
+ // We don't want to split the fixed portion of a record across the
+ // physical end of the file, it makes it difficult when referencing
+ // these fields later (you have to check before you touch each one
+ // to make sure it's not after the physical EOF). So, if there's
+ // not enough room at the end of the file for the fixed portion,
+ // we fill it with a known byte pattern ELF_SKIP_DWORD that will
+ // be skipped if it's found at the start of a record (as long as
+ // it's less than the minimum record size, then we know it's not
+ // the start of a valid record).
+ //
+
+ if (pLogFile->ActualMaxFileSize - pLogFile->EndRecord <
+ sizeof(EVENTLOGRECORD)) {
+
+ //
+ // Save the EndRecord pointer in case we don't have the space
+ // to write another record, we'll need to rewrite the EOF where
+ // it was
+ //
+
+ OverwrittenEOF = pLogFile->EndRecord;
+
+ FillDword = (PDWORD)((PBYTE) pLogFile->BaseAddress +
+ pLogFile->EndRecord);
+ while (FillDword < (PDWORD)((LPBYTE) pLogFile->BaseAddress +
+ pLogFile->ActualMaxFileSize)) {
+ *FillDword = ELF_SKIP_DWORD;
+ FillDword++;
+ }
+
+ pLogFile->EndRecord = FILEHEADERBUFSIZE;
+ SpaceAvail = pLogFile->BeginRecord - FILEHEADERBUFSIZE;
+ pLogFile->Flags |= ELF_LOGFILE_HEADER_WRAP;
+ }
+
+ EventRecord = (PEVENTLOGRECORD)((PBYTE) pLogFile->BaseAddress +
+ pLogFile->BeginRecord);
+
+ while ( SpaceNeeded > SpaceAvail ) {
+
+ //
+ // If this logfile can be overwrite-as-needed, or if it has
+ // an overwrite time limit and the time hasn't expired, then
+ // allow the new event to overwrite an older event.
+ //
+
+ if ((pLogFile->Retention == OVERWRITE_AS_NEEDED) ||
+ ((pLogFile->Retention != NEVER_OVERWRITE) &&
+ ((EventRecord->TimeWritten < EarliestTime) ||
+ (Request->Flags & ELF_FORCE_OVERWRITE)) )) { // OK to overwrite
+
+ DeletedRecordOffset = pLogFile->BeginRecord;
+
+ pLogFile->BeginRecord += EventRecord->Length;
+
+ if (pLogFile->BeginRecord >= pLogFile->ActualMaxFileSize) {
+
+ //
+ // We're about to wrap around the end of the file. If we
+ // have room to grow it do it now, but still move the
+ // pointer to the first record to be the next one in the file.
+ //
+
+ pLogFile->BeginRecord = FILEHEADERBUFSIZE +
+ ( pLogFile->BeginRecord -
+ pLogFile->ActualMaxFileSize);
+
+ //
+ // Check to see if the file has reached it's maximum allowable
+ // size yet. If not, grow it by as much as needed, in 64K
+ // chunks.
+ //
+
+ if (pLogFile->ActualMaxFileSize < pLogFile->ConfigMaxFileSize)
+ {
+
+ //
+ // Extend it. This call cannot fail. If it can't
+ // extend it, it just caps it at the current size by
+ // changing pLogFile->ConfigMaxFileSize
+ //
+
+ ElfExtendFile(pLogFile, SpaceNeeded, &SpaceAvail);
+
+ //
+ // Since extending the file will cause it to be moved, we
+ // need to re-establish the address for the EventRecord.
+ //
+ EventRecord = (PEVENTLOGRECORD)((PBYTE) pLogFile->BaseAddress +
+ DeletedRecordOffset);
+ }
+ }
+
+ //
+ // Make sure no handle points to the record that we're getting
+ // ready to overwrite, it one does, correct it to point to the
+ // new first record.
+ //
+
+ FixContextHandlesForRecord(DeletedRecordOffset,
+ pLogFile->BeginRecord);
+
+ SpaceAvail += EventRecord->Length;
+
+ //
+ // Bump to the next record, file wrap was handled above
+ //
+
+ //
+ // If these are ELF_SKIP_DWORDs, just move past them
+ //
+
+ FillDword = (PDWORD)((PBYTE) pLogFile->BaseAddress +
+ pLogFile->BeginRecord);
+
+ if (*FillDword == ELF_SKIP_DWORD) {
+ SpaceAvail += pLogFile->ActualMaxFileSize -
+ pLogFile->BeginRecord;
+ pLogFile->BeginRecord = FILEHEADERBUFSIZE;
+ }
+
+ EventRecord = (PEVENTLOGRECORD)((PBYTE) pLogFile->BaseAddress +
+ pLogFile->BeginRecord);
+
+ } else { // All records within retention period
+
+ ElfDbgPrint(("[ELF] Log file is full\n"));
+
+ //
+ // Hang an event on the queuedevent list for later writing
+ // if we haven't just written a log full event for this log
+ //
+
+ if (!(pLogFile->Flags & ELF_LOGFILE_LOGFULL_WRITTEN)) {
+
+ //
+ // Don't generate log full events/alerts more often than
+ // once every hour
+ //
+
+ if (!LastAlertWritten ||
+ CurrentTime > LastAlertWritten + 60 * 60) {
+
+ pLogFile->Flags |= ELF_LOGFILE_LOGFULL_WRITTEN;
+
+ //
+ // Per bug #4960, it is considered a C2 security breach
+ // if a "security log full" message is posted to the
+ // system log *and* the current user's console. An admin
+ // alert is OK.
+ //
+
+ if (_wcsicmp(pLogFile->LogModuleName->Buffer,
+ ELF_SECURITY_MODULE_NAME) != 0) {
+
+ ElfpCreateElfEvent(
+ EVENT_LOG_FULL,
+ EVENTLOG_ERROR_TYPE,
+ 0, // EventCategory
+ 1, // NumberOfStrings
+ &Request->LogFile->LogModuleName->
+ Buffer, // Strings
+ NULL, // Data
+ 0, // Datalength
+ ELF_FORCE_OVERWRITE); // Overwrite if necc.
+
+ ElfpCreateQueuedMessage(ALERT_ELF_LogOverflow, 1,
+ &Request->Module->LogFile->LogModuleName->Buffer);
+ }
+
+ LastAlertWritten = CurrentTime;
+ ElfpCreateQueuedAlert(ALERT_ELF_LogOverflow, 1,
+ &Request->Module->LogFile->LogModuleName->Buffer);
+ }
+ }
+ if (OverwrittenEOF) {
+
+ //
+ // The EOF record was at the end of the physical file,
+ // and we overwrote it with ELF_SKIP_DWORDs, so we need
+ // to put it back since we're not going to be able to
+ // write a record. We also need to turn the wrap bit
+ // back off
+ //
+
+ pLogFile->Flags &= ~(ELF_LOGFILE_HEADER_WRAP);
+ pLogFile->EndRecord = OverwrittenEOF;
+ WritePos = OverwrittenEOF;
+
+ //
+ // Write out the EOF record
+ //
+
+ WriteToLog ( pLogFile,
+ (PVOID) &EOFRecord,
+ ELFEOFRECORDSIZE,
+ &WritePos,
+ pLogFile->ActualMaxFileSize,
+ FILEHEADERBUFSIZE
+ );
+ }
+
+ Status = STATUS_LOG_FILE_FULL;
+ break; // Get out of while loop
+
+ }
+ }
+
+ if (NT_SUCCESS (Status)) {
+
+ //
+ // We have enough room to write the record and the EOF record.
+ //
+
+ //
+ // Update OldestRecordNumber to reflect the records that were
+ // overwritten amd increment the CurrentRecordNumber
+ //
+ // Make sure that the log isn't empty, if it is, the oldestrecord
+ // is 1
+ //
+
+ if (pLogFile->BeginRecord == pLogFile->EndRecord) {
+ pLogFile->OldestRecordNumber = 1;
+ }
+ else {
+ pLogFile->OldestRecordNumber = EventRecord->RecordNumber;
+ }
+ pLogFile->CurrentRecordNumber++;
+
+ //
+ // If the dirty bit is not set, then this is the first time that
+ // we have written to the file since we started. In that case,
+ // set the dirty bit in the file header as well so that we will
+ // know that the contents have changed.
+ //
+
+ if ( !(pLogFile->Flags & ELF_LOGFILE_HEADER_DIRTY) ) {
+ ULONG HeaderSize;
+
+ pLogFile->Flags |= ELF_LOGFILE_HEADER_DIRTY;
+
+ pFileHeader = (PELF_LOGFILE_HEADER)(pLogFile->BaseAddress);
+ pFileHeader->Flags |= ELF_LOGFILE_HEADER_DIRTY;
+
+ //
+ // Now flush this to disk to commit it
+ //
+
+ BaseAddress = pLogFile->BaseAddress;
+ HeaderSize = FILEHEADERBUFSIZE;
+
+ Status = NtFlushVirtualMemory(
+ NtCurrentProcess(),
+ &BaseAddress,
+ &HeaderSize,
+ &IoStatusBlock
+ );
+ }
+
+ //
+ // Now write out the record
+ //
+
+ WriteToLog ( pLogFile,
+ Request->Pkt.WritePkt->Buffer,
+ RecordSize,
+ &(pLogFile->EndRecord),
+ pLogFile->ActualMaxFileSize,
+ FILEHEADERBUFSIZE
+ );
+
+ //
+ // Use a separate variable for the position,since we don't want
+ // it updated.
+ //
+
+ WritePos = pLogFile->EndRecord;
+ if (WritePos > pLogFile->ActualMaxFileSize) {
+ WritePos -= pLogFile->ActualMaxFileSize -
+ FILEHEADERBUFSIZE;
+ }
+
+ //
+ // Update the EOF record fields
+ //
+
+ EOFRecord.BeginRecord = pLogFile->BeginRecord;
+ EOFRecord.EndRecord = WritePos;
+ EOFRecord.CurrentRecordNumber = pLogFile->CurrentRecordNumber;
+ EOFRecord.OldestRecordNumber = pLogFile->OldestRecordNumber;
+
+ //
+ // Write out the EOF record
+ //
+
+ WriteToLog ( pLogFile,
+ (PVOID) &EOFRecord,
+ ELFEOFRECORDSIZE,
+ &WritePos,
+ pLogFile->ActualMaxFileSize,
+ FILEHEADERBUFSIZE
+ );
+
+
+ //
+ // If we had just written a logfull record, turn the bit off.
+ // Since we just wrote a record, technically it's not full anymore
+ //
+
+ if (!(Request->Flags & ELF_FORCE_OVERWRITE)) {
+ pLogFile->Flags &= ~(ELF_LOGFILE_LOGFULL_WRITTEN);
+ }
+
+ //
+ // See if there are any ElfChangeNotify callers to notify, and if
+ // there are, pulse their event
+ //
+
+ NotifyChange(pLogFile);
+
+#ifdef _CAIRO_
+ //
+ // ** NEW FOR CAIRO **
+ //
+ // Test if this event should be raised as a Cairo alert. Notice this
+ // test is performed within logfile resource exclusion while actual
+ // raise of the event as a Cairo alert is performed outside of
+ // it - there is no need to serialize access to the logfile while
+ // raising the event as an alert.
+ //
+ // Raise *only* events written to the System log.
+ //
+
+ if (_wcsicmp(ELF_SYSTEM_MODULE_NAME,
+ Request->LogFile->LogModuleName->Buffer) == 0)
+ {
+ fRaiseAlert = TestFilter(pEventLogRecord->EventType,
+ Request->Module->AlertSeverity);
+ }
+#endif // _CAIRO_
+ }
+
+ //
+ // Set status field in the request packet.
+ //
+
+ Request->Status = (NTSTATUS) Status;
+
+ //
+ // Free the resource
+ //
+
+ RtlReleaseResource ( &pLogFile->Resource );
+
+#ifdef _CAIRO_
+ //
+ // ** NEW FOR CAIRO **
+ //
+ // Raise the event as a Cairo alert to the local computer distributor
+ // object if the filter match test succeeded above.
+ //
+ // Note: this is done outside of log resource exclusion since raise has
+ // nothing to do with the log and its data structures.
+ //
+
+ if (fRaiseAlert)
+ {
+ //
+ // Don't care if this fails right now.
+ //
+
+ RaiseCairoAlert(Request->Module, pEventLogRecord);
+ }
+#endif // _CAIRO_
+
+} // PerformWriteRequest
+
+
+VOID
+PerformClearRequest( PELF_REQUEST_RECORD Request)
+
+/*++
+
+Routine Description:
+
+ This routine will optionally back up the log file specified, and will
+ delete it.
+
+Arguments:
+
+ Pointer to the request packet.
+
+Return Value:
+
+ NONE
+
+Note:
+
+ On the exit path, when we do some "cleanup" work, we discard the
+ status and instead return the status of the operation that is being
+ performed.
+ This is necessary since we wish to return any error condition that is
+ directly related to the clear operation. For other errors, we will
+ fail at a later stage.
+
+--*/
+{
+ NTSTATUS Status, IStatus;
+ PUNICODE_STRING FileName;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PFILE_RENAME_INFORMATION NewName = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE ClearHandle;
+ FILE_DISPOSITION_INFORMATION DeleteInfo = {TRUE};
+ ULONG FileRefCount;
+ BOOLEAN FileRenamed = FALSE;
+
+ //
+ // Get exclusive access to the log file. This will ensure no one
+ // else is accessing the file.
+ //
+
+ RtlAcquireResourceExclusive (
+ &Request->Module->LogFile->Resource,
+ TRUE // Wait until available
+ );
+
+ //
+ // We have exclusive access to the file.
+ //
+ // We force the file to be closed, and store away the ref count
+ // so that we can set it back when we reopen the file.
+ // This is a little *sleazy* but we have exclusive access to the
+ // logfile structure so we can play these games.
+ //
+
+ FileRefCount = Request->LogFile->RefCount; // Store this away
+ ElfpCloseLogFile ( Request->LogFile, ELF_LOG_CLOSE_FORCE );
+ Request->LogFile->FileHandle = NULL; // For use later
+
+ //
+ // Open the file with delete access in order to rename it.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ Request->LogFile->LogFileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ Status = NtOpenFile(&ClearHandle,
+ GENERIC_READ | DELETE | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_DELETE,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ // If the backup file name has been specified and is not NULL,
+ // then we move the current file to the new file. If that fails,
+ // then fail the whole operation.
+ //
+
+ if ( (Request->Pkt.ClearPkt->BackupFileName != NULL)
+ && (Request->Pkt.ClearPkt->BackupFileName->Length != 0)) {
+
+ FileName = Request->Pkt.ClearPkt->BackupFileName;
+
+ //
+ // Set up the rename information structure with the new name
+ //
+
+ NewName = ElfpAllocateBuffer(
+ FileName->Length + sizeof(WCHAR) + sizeof(*NewName));
+ if (NewName) {
+ RtlMoveMemory( NewName->FileName,
+ FileName->Buffer,
+ FileName->Length
+ );
+
+ //
+ // Guarantee that it's NULL terminated
+ //
+
+ NewName->FileName[FileName->Length / sizeof(WCHAR)] = L'\0';
+
+ NewName->ReplaceIfExists = FALSE;
+ NewName->RootDirectory = NULL;
+ NewName->FileNameLength = FileName->Length;
+
+ Status = NtSetInformationFile(
+ ClearHandle,
+ &IoStatusBlock,
+ NewName,
+ FileName->Length+sizeof(*NewName),
+ FileRenameInformation
+ );
+
+ if (Status == STATUS_NOT_SAME_DEVICE) {
+
+ //
+ // They want the backup file to be on a different
+ // device. We need to copy this one, and then delete
+ // it.
+ //
+
+ ElfDbgPrint(("[ELF] Copy log file\n"));
+
+ Status = ElfpCopyFile(ClearHandle, FileName);
+
+ if (NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] Deleting log file\n"));
+
+ Status = NtSetInformationFile(
+ ClearHandle,
+ &IoStatusBlock,
+ &DeleteInfo,
+ sizeof(DeleteInfo),
+ FileDispositionInformation
+ );
+
+ if ( !NT_SUCCESS (Status) ) {
+ ElfDbgPrintNC(("[ELF] Delete failed 0x%lx\n",
+ Status));
+ }
+ }
+ }
+ else if ( NT_SUCCESS (Status) ) {
+ FileRenamed = TRUE;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrintNC(("[ELF] Rename/Copy failed 0x%lx\n", Status));
+ }
+ } else {
+ Status = STATUS_NO_MEMORY;
+ }
+ } else { // No backup to done
+
+ //
+ // No backup name was specified. Just delete the log file
+ // (i.e. "clear it"). We can just delete it since we know
+ // that the first time anything is written to a log file,
+ // if that file does not exist, it is created and a header
+ // is written to it. By deleting it here, we make it cleaner
+ // to manage log files, and avoid having zero-length files all
+ // over the disk.
+ //
+
+ ElfDbgPrint(("[ELF] Deleting log file\n"));
+
+ Status = NtSetInformationFile(
+ ClearHandle,
+ &IoStatusBlock,
+ &DeleteInfo,
+ sizeof(DeleteInfo),
+ FileDispositionInformation
+ );
+
+ if ( !NT_SUCCESS (Status) ) {
+ ElfDbgPrintNC(("[ELF] Delete failed 0x%lx\n", Status));
+
+ }
+
+
+ } // Backup and/or Delete
+
+ IStatus = NtClose (ClearHandle); // Discard status
+ ASSERT (NT_SUCCESS (IStatus));
+
+ } else { // The open-for-delete failed.
+
+ ElfDbgPrintNC(("[ELF] Open-for-delete failed 0x%lx\n", Status));
+ }
+
+ //
+ // Now pick up any new size value that was set before but
+ // couldn't be used until the log was cleared (they reduced the size of
+ // the log file.)
+ //
+
+ if (NT_SUCCESS (Status)) {
+ if (Request->LogFile->NextClearMaxFileSize) {
+ Request->LogFile->ConfigMaxFileSize =
+ Request->LogFile->NextClearMaxFileSize;
+ }
+ }
+
+ //
+ // We need to recreate the file or if the file was just closed,
+ // then we reopen it.
+ //
+
+ IStatus = ElfOpenLogFile ( Request->LogFile, ElfNormalLog);
+ if (!NT_SUCCESS(IStatus)) {
+
+ //
+ // Opening the new log file failed, reopen the old log and
+ // return this error from the Api
+ //
+
+ PFILE_RENAME_INFORMATION OldName;
+ UNICODE_STRING UnicodeString;
+
+ Status = IStatus;
+
+ //
+ // There shouldn't be any way to fail unless we successfully
+ // renamed the file, and there's no recovery if that happens.
+ //
+
+ ASSERT(FileRenamed == TRUE);
+
+ //
+ // Rename the file back to the original name. Reuse ClearHandle.
+ //
+
+ RtlInitUnicodeString(&UnicodeString, NewName->FileName);
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeString,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ IStatus = NtOpenFile(&ClearHandle,
+ GENERIC_READ | DELETE | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_DELETE,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ //
+ // This can't fail, I just created it!
+ //
+
+ ASSERT(NT_SUCCESS(IStatus));
+
+ //
+ // Set up the rename information structure with the old name
+ //
+
+ OldName = ElfpAllocateBuffer(
+ Request->LogFile->LogFileName->Length + sizeof(WCHAR) +
+ sizeof(*OldName));
+
+ if (OldName) {
+ RtlMoveMemory( OldName->FileName,
+ Request->LogFile->LogFileName->Buffer,
+ Request->LogFile->LogFileName->Length
+ );
+
+
+ //
+ // Guarantee that it's NULL terminated
+ //
+
+ OldName->FileName[Request->LogFile->LogFileName->Length /
+ sizeof(WCHAR)] = L'\0';
+
+ OldName->ReplaceIfExists = FALSE;
+ OldName->RootDirectory = NULL;
+ OldName->FileNameLength = Request->LogFile->LogFileName->Length;
+
+ IStatus = NtSetInformationFile(
+ ClearHandle,
+ &IoStatusBlock,
+ OldName,
+ Request->LogFile->LogFileName->Length +
+ sizeof(*OldName) + sizeof(WCHAR),
+ FileRenameInformation
+ );
+ ASSERT(NT_SUCCESS(IStatus));
+ IStatus = NtClose(ClearHandle);
+ ASSERT(NT_SUCCESS(IStatus));
+
+ //
+ // Reopen the original file, this has to work
+ //
+
+ IStatus = ElfOpenLogFile ( Request->LogFile, ElfNormalLog);
+ ASSERT(NT_SUCCESS(IStatus));
+
+ ElfpFreeBuffer(OldName);
+ }
+ }
+
+ Request->LogFile->RefCount = FileRefCount; // Restore old value.
+
+ //
+ // Mark any open context handles that point to this file as "invalid for
+ // read". This will fail any further READ operations and force the caller
+ // to close and reopen the handle.
+ //
+
+ InvalidateContextHandlesForLogFile ( Request->LogFile );
+
+
+ //
+ // Set status field in the request packet.
+ //
+
+ Request->Status = Status;
+
+ //
+ // Free the resource
+ //
+
+ RtlReleaseResource ( &Request->Module->LogFile->Resource );
+ if (NewName) {
+ ElfpFreeBuffer (NewName);
+ }
+
+
+} // PerformClearRequest
+
+
+VOID
+PerformBackupRequest( PELF_REQUEST_RECORD Request)
+
+/*++
+
+Routine Description:
+
+ This routine will back up the log file specified.
+
+ This routine impersonates the client in order to ensure that the correct
+ access control is used.
+
+ This routine is entered with the ElfGlobalResource held in a shared
+ state and the logfile lock is acquired shared to prevent writing, but
+ allow people to still read.
+
+ This copies the file in two chunks, from the first record to the end
+ of the file, and then from the top of the file (excluding the header)
+ to the end of the EOF record.
+
+Arguments:
+
+ Pointer to the request packet.
+
+Return Value:
+
+ NONE, status is placed in the packet for later use by the API wrapper
+
+--*/
+{
+ NTSTATUS Status, IStatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ LARGE_INTEGER MaximumSizeOfSection;
+ LARGE_INTEGER Offset;
+ ULONG LastRecordNumber;
+ ULONG OldestRecordNumber;
+ HANDLE BackupHandle;
+ PBYTE StartOfCopy;
+ PBYTE EndOfCopy;
+ ULONG BytesToCopy;
+ ULONG EndRecord = FILEHEADERBUFSIZE;
+ BOOL ImpersonatingClient = FALSE;
+ ELF_LOGFILE_HEADER FileHeaderBuf = { FILEHEADERBUFSIZE, // Size
+ ELF_LOG_FILE_SIGNATURE,
+ ELF_VERSION_MAJOR,
+ ELF_VERSION_MINOR,
+ FILEHEADERBUFSIZE, // Start offset
+ FILEHEADERBUFSIZE, // End offset
+ 1, // Next record #
+ 1, // Oldest record #
+ 0, // Maxsize
+ 0, // Flags
+ 0, // Retention
+ FILEHEADERBUFSIZE // Size
+ };
+
+
+ //
+ // Get shared access to the log file. This will ensure no one
+ // else clears the file.
+ //
+
+ RtlAcquireResourceShared (
+ &Request->Module->LogFile->Resource,
+ TRUE // Wait until available
+ );
+
+ //
+ // Save away the next record number. We'll stop copying when we get to
+ // the record before this one. Also save the first record number so we
+ // can update the header and EOF record.
+ //
+
+ LastRecordNumber = Request->LogFile->CurrentRecordNumber;
+ OldestRecordNumber = Request->LogFile->OldestRecordNumber;
+
+ //
+ // Impersonate the client
+ //
+
+ Status = ElfImpersonateClient ();
+
+ if (NT_SUCCESS (Status)) {
+
+ //
+ // Keep this info so I can only revert in 1 place
+ //
+
+ ImpersonatingClient = TRUE;
+
+ //
+ // Set up the object attributes structure for the backup file
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ Request->Pkt.BackupPkt->BackupFileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ //
+ // Open the backup file. Fail if a file by this name already exists.
+ //
+
+ MaximumSizeOfSection =
+ RtlConvertUlongToLargeInteger (
+ Request->LogFile->ActualMaxFileSize);
+
+ Status = NtCreateFile(
+ &BackupHandle,
+ GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ &MaximumSizeOfSection,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_CREATE,
+ FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ 0);
+
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrintNC(("[ELF] Open of Backup file failed - %X\n", Status));
+ goto errorexit;
+ }
+
+ //
+ // Write out the header, we'll update it later
+ //
+
+ FileHeaderBuf.CurrentRecordNumber = LastRecordNumber;
+ FileHeaderBuf.OldestRecordNumber = OldestRecordNumber;
+ FileHeaderBuf.Flags = 0;
+ FileHeaderBuf.Retention = Request->LogFile->Retention;
+
+ Status = NtWriteFile(
+ BackupHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ &FileHeaderBuf, // Buffer
+ FILEHEADERBUFSIZE, // Length
+ NULL, // Byteoffset
+ NULL); // Key
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF]: Backup file header write failed %X\n",
+ Status));
+ goto errorexit;
+ }
+
+ //
+ // Scan from the end of the file skipping over ELF_SKIP_DWORDs
+ // to figure out far to copy. If we haven't wrapped, we just
+ // copy to the EndRecord offset.
+ //
+
+ if (Request->LogFile->Flags & ELF_LOGFILE_HEADER_WRAP) {
+ EndOfCopy = (PBYTE) Request->LogFile->BaseAddress
+ + Request->LogFile->ActualMaxFileSize - sizeof(DWORD);
+ while (*((PDWORD)EndOfCopy) == ELF_SKIP_DWORD) {
+ EndOfCopy -= sizeof(DWORD);
+ }
+ EndOfCopy += sizeof(DWORD);
+
+ }
+ else {
+ EndOfCopy = (PBYTE) Request->LogFile->BaseAddress +
+ Request->LogFile->EndRecord;
+ }
+
+ //
+ // Now set the start position to be the first record and
+ // calculate the number of bytes to copy
+ //
+
+ StartOfCopy = (PBYTE) Request->LogFile->BaseAddress +
+ Request->LogFile->BeginRecord;
+
+ BytesToCopy = EndOfCopy - StartOfCopy;
+ EndRecord += BytesToCopy;
+
+ Status = NtWriteFile(
+ BackupHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ StartOfCopy, // Buffer
+ BytesToCopy, // Length
+ NULL, // Byteoffset
+ NULL); // Key
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF]: Backup file 1st block write failed %X\n",
+ Status));
+ goto errorexit;
+ }
+
+ //
+ // If the file's not wrapped, we're done except for the EOF
+ // record. If the file is wrapped we have to copy the 2nd
+ // piece
+ //
+
+ if (Request->LogFile->Flags & ELF_LOGFILE_HEADER_WRAP) {
+ StartOfCopy = (PBYTE) Request->LogFile->BaseAddress +
+ FILEHEADERBUFSIZE;
+ EndOfCopy = (PBYTE) Request->LogFile->BaseAddress +
+ Request->LogFile->EndRecord;
+
+ BytesToCopy = EndOfCopy - StartOfCopy;
+ EndRecord += BytesToCopy;
+
+ Status = NtWriteFile(
+ BackupHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ StartOfCopy, // Buffer
+ BytesToCopy, // Length
+ NULL, // Byteoffset
+ NULL); // Key
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF]: Backup file 2nd block write failed %X\n",
+ Status));
+ goto errorexit;
+ }
+ }
+
+ //
+ // Write out the EOF record after updating the fields needed for
+ // recovery.
+ //
+
+ EOFRecord.BeginRecord = FILEHEADERBUFSIZE;
+ EOFRecord.EndRecord = EndRecord;
+ EOFRecord.CurrentRecordNumber = LastRecordNumber;
+ EOFRecord.OldestRecordNumber = OldestRecordNumber;
+
+ Status = NtWriteFile(
+ BackupHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ &EOFRecord, // Buffer
+ ELFEOFRECORDSIZE, // Length
+ NULL, // Byteoffset
+ NULL); // Key
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF]: Backup file EOF record write failed %X\n",
+ Status));
+ goto errorexit;
+ }
+
+ //
+ // Update the header with valid information
+ //
+
+ FileHeaderBuf.EndOffset = EndRecord;
+ FileHeaderBuf.MaxSize = EndRecord + ELFEOFRECORDSIZE;
+ Offset = RtlConvertUlongToLargeInteger (0);
+
+ Status = NtWriteFile(
+ BackupHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ &FileHeaderBuf, // Buffer
+ FILEHEADERBUFSIZE, // Length
+ &Offset, // Byteoffset
+ NULL); // Key
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF]: Backup file header rewrite failed %X\n",
+ Status));
+ goto errorexit;
+ }
+
+ //
+ // Clear the LogFile flag archive bit, assuming the caller will
+ // clear (or has cleared) this log's archive file attribute.
+ // Note: No big deal if the caller didn't clear the archive
+ // attribute.
+ //
+ // The next write to this log tests the LogFile flag archive bit.
+ // If the bit is clear, the archive file attribute is set on the
+ // log file.
+ //
+
+ Request->LogFile->Flags &= ~ELF_LOGFILE_ARCHIVE_SET;
+
+ //
+ // Close the output file
+ //
+ // BUGBUG : markbl (4/6/95) shouldn't close be done on error also?
+ //
+
+ NtClose(BackupHandle);
+
+ //
+ // Undo the impersonation.
+ //
+
+ }
+errorexit:
+
+ if (ImpersonatingClient) {
+ IStatus = ElfRevertToSelf (); // Discard status
+ ASSERT (NT_SUCCESS (IStatus));
+ }
+
+ //
+ // Set status field in the request packet.
+ //
+
+ Request->Status = Status;
+
+ //
+ // Free the resource
+ //
+
+ RtlReleaseResource ( &Request->Module->LogFile->Resource );
+
+} // PerformBackupRequest
+
+
+VOID
+ElfPerformRequest( PELF_REQUEST_RECORD Request)
+
+/*++
+
+Routine Description:
+
+ This routine takes the request packet and performs the operation
+ on the event log.
+ Before it does that, it takes the Global serialization resource
+ for a READ to prevent other threads from doing WRITE operations on
+ the resources of the service.
+
+ After it has performed the requested operation, it writes any records
+ generated by the eventlog service that have been put on the queuedevent
+ list.
+
+Arguments:
+
+ Pointer to the request packet.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ BOOL Acquired = FALSE;
+
+ //
+ // Acquire the global resource for shared access. If the resource is
+ // not immediately available (i.e. don't wait), then some other thread
+ // has it out for exclusive access.
+ //
+ // In that case, we can do one of two things:
+ //
+ // 1) Thread monitoring the registry
+ // We can wait for this thread to finish so that the
+ // operation can continue.
+ //
+ // 2) Control thread
+ // In this case, it may turn out that the service will
+ // be terminated or paused. We can examine the current
+ // status of the service and see if it is still "installed"
+ // (i.e. no "pending" state). If so, we loop around and try
+ // to get the resource again (after sleeping a bit?). We
+ // break out of the loop if the state of the service changes
+ // to PAUSED, PAUSE_PENDING, UNINSTALL_PENDING, etc. so as
+ // not to block the thread indefinitely.
+ //
+
+ while ( (GetElState() == RUNNING) && (!Acquired)) {
+
+ Acquired = RtlAcquireResourceShared(
+ &GlobalElfResource,
+ FALSE // Don't wait
+ );
+ if (!Acquired) {
+ ElfDbgPrint(("[ELF] Sleep waiting for global resource\n" ));
+ Sleep (ELF_GLOBAL_RESOURCE_WAIT);
+ }
+
+ }
+
+ // If the resource was not available and the status of the service
+ // changed to one of the "non-working" states, then we just return
+ // unsuccesful. Rpc should not allow this to happen.
+ //
+
+ if (!Acquired) {
+
+ ElfDbgPrint(("[ELF] Global resource not acquired.\n" ));
+ Request->Status = STATUS_UNSUCCESSFUL;
+
+ } else {
+
+ switch ( Request->Command ) {
+
+ case ELF_COMMAND_READ:
+ PerformReadRequest( Request );
+ break;
+
+ case ELF_COMMAND_WRITE:
+ PerformWriteRequest ( Request );
+ break;
+
+ case ELF_COMMAND_CLEAR:
+ PerformClearRequest( Request );
+ break;
+
+ case ELF_COMMAND_BACKUP:
+ PerformBackupRequest( Request );
+ break;
+ case ELF_COMMAND_WRITE_QUEUED:
+ break;
+ }
+
+ //
+ // Now run the queued event list dequeueing elements and
+ // writing them
+ //
+
+ if (!IsListEmpty(&QueuedEventListHead)) {
+
+ //
+ // There are things queued up to write, do it
+ //
+
+ WriteQueuedEvents();
+ }
+
+ //
+ // Release the global resource.
+ //
+
+ ReleaseGlobalResource();
+
+ }
+
+} // ElfPerformRequest
+
+#if DBG
+VOID
+ElfErrorOut(
+ LPSTR ErrorText,
+ DWORD StatusCode,
+ PLOGFILE pLogFile)
+{
+ DbgPrint("\n[EVENTLOG]: %s,0x%lx, \n\t%ws Log:\n"
+ "\tConfigMaxSize = 0x%lx, ActualMax = 0x%lx, BaseAddr = 0x%lx\n"
+ "\tViewSize = 0x%lx, EndRec = 0x%lx\n",
+ ErrorText,
+ StatusCode,
+ pLogFile->LogModuleName->Buffer,
+ pLogFile->ConfigMaxFileSize,
+ pLogFile->ActualMaxFileSize,
+ pLogFile->BaseAddress,
+ pLogFile->ViewSize,
+ pLogFile->EndRecord);
+
+ //DebugBreak();
+}
+#endif //DBG
diff --git a/private/eventlog/server/terminat.c b/private/eventlog/server/terminat.c
new file mode 100644
index 000000000..7a2f0d519
--- /dev/null
+++ b/private/eventlog/server/terminat.c
@@ -0,0 +1,267 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ TERMINAT.C
+
+Abstract:
+
+ This file contains all the cleanup routines for the Eventlog service.
+ These routines are called when the service is terminating.
+
+Author:
+
+ Rajen Shah (rajens) 09-Aug-1991
+
+
+Revision History:
+
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <ntrpcp.h>
+
+
+
+
+VOID
+StopLPCThread ()
+
+/*++
+
+Routine Description:
+
+ This routine stops the LPC thread and cleans up LPC-related resources.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+--*/
+
+{
+ ElfDbgPrint(( "[ELF] Stop the LPC thread\n" ));
+
+ //
+ // Close communication port handle
+ //
+
+ NtClose ( ElfCommunicationPortHandle );
+
+ //
+ // Close connection port handle
+ //
+
+ NtClose ( ElfConnectionPortHandle );
+
+ //
+ // Terminate the LPC thread.
+ //
+
+ if (!TerminateThread(LPCThreadHandle,NO_ERROR)) {
+ ElfDbgPrint(("[ELF] LPC Thread termination failed %d\n",GetLastError()));
+ }
+ CloseHandle ( LPCThreadHandle );
+
+ return;
+}
+
+
+
+
+VOID
+FreeModuleAndLogFileStructs ( )
+
+/*++
+
+Routine Description:
+
+ This routine walks the module and log file list and frees all the
+ data structures.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+Note:
+
+ The file header and ditry bits must have been dealt with before
+ this routine is called. Also, the file must have been unmapped and
+ the handle closed.
+
+--*/
+{
+
+ NTSTATUS Status;
+ PLOGMODULE pModule;
+ PLOGFILE pLogFile;
+
+ ElfDbgPrint (("[ELF] Freeing module and log file structs\n"));
+
+ //
+ // First free all the modules
+ //
+
+ while (!IsListEmpty (&LogModuleHead) ) {
+
+ pModule = (PLOGMODULE)
+ CONTAINING_RECORD(LogModuleHead.Flink, LOGMODULE, ModuleList);
+
+ UnlinkLogModule(pModule); // Remove from linked list
+
+ ElfpFreeBuffer (pModule); // Free module memory
+
+ }
+
+ //
+ // Now free all the logfiles
+ //
+
+ while (!IsListEmpty (&LogFilesHead) ) {
+
+ pLogFile = (PLOGFILE)
+ CONTAINING_RECORD(LogFilesHead.Flink, LOGFILE, FileList);
+
+ Status = ElfpCloseLogFile ( pLogFile, ELF_LOG_CLOSE_NORMAL);
+
+ UnlinkLogFile(pLogFile); // Unlink the structure
+ RtlDeleteResource ( &pLogFile->Resource );
+ ElfpFreeBuffer (pLogFile->LogFileName);
+ ElfpFreeBuffer (pLogFile);
+ }
+}
+
+
+VOID
+ElfpCleanUp (
+ ULONG EventFlags
+ )
+
+/*++
+
+Routine Description:
+
+ This routine cleans up before the service terminates. It cleans up
+ based on the parameter passed in (which indicates what has been allocated
+ and/or started.
+
+Arguments:
+
+ Bit-mask indicating what needs to be cleaned up.
+
+Return Value:
+
+ NONE
+
+Note:
+ It is expected that the RegistryMonitor has already
+ been notified of Shutdown prior to calling this routine.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+
+
+ ElfDbgPrint (("[ELF] ElfpCleanUp.\n"));
+
+ //
+ // Notify the Service Controller for the first time that we are
+ // about to stop the service.
+ //
+ // *** STATUS UPDATE ***
+ ElfStatusUpdate(STOPPING);
+
+
+ //
+ // Stop the RPC Server
+ //
+ if (EventFlags & ELF_STARTED_RPC_SERVER) {
+ ElfDbgPrint (("[ELF] Stopping the RPC Server.\n"));
+
+ status = ElfGlobalData->StopRpcServer(eventlog_ServerIfHandle);
+ if (status != NO_ERROR) {
+ ElfDbgPrint (("[ELF] Stopping RpcServer Failed %d\n",status));
+ }
+ }
+
+ //
+ // Stop the LPC thread
+ //
+ if (EventFlags & ELF_STARTED_LPC_THREAD)
+ StopLPCThread();
+
+ //
+ // Tell service controller that we are making progress
+ //
+ // *** STATUS UPDATE ***
+ ElfStatusUpdate(STOPPING);
+
+ //
+ // Flush all the log files to disk.
+ //
+ ElfDbgPrint (("[ELF] Flushing Files.\n"));
+ ElfpFlushFiles();
+
+ //
+ // Tell service controller that we are making progress
+ //
+ ElfStatusUpdate(STOPPING);
+
+ //
+ // Clean up any resources that were allocated
+ //
+ FreeModuleAndLogFileStructs();
+
+ //
+ // Free up memory
+ //
+
+ if (LocalComputerName) {
+ ElfpFreeBuffer(LocalComputerName);
+ }
+
+ //
+ // If we queued up any events, flush them
+ //
+
+ ElfDbgPrint (("[ELF] Flushing QueuedEvents.\n"));
+ FlushQueuedEvents();
+
+ //
+ // Tell service controller of that we are making progress
+ //
+ ElfStatusUpdate(STOPPING);
+
+ if (EventFlags & ELF_INIT_GLOBAL_RESOURCE)
+ RtlDeleteResource ( &GlobalElfResource );
+
+ if (EventFlags & ELF_INIT_LOGHANDLE_CRIT_SEC)
+ RtlDeleteCriticalSection((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
+
+ if (EventFlags & ELF_INIT_LOGFILE_CRIT_SEC)
+ RtlDeleteCriticalSection((PRTL_CRITICAL_SECTION)&LogFileCritSec);
+
+ if (EventFlags & ELF_INIT_QUEUED_EVENT_CRIT_SEC)
+ RtlDeleteCriticalSection((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
+
+ // *** STATUS UPDATE ***
+ ElfDbgPrint(("[ELF] Leaving the Eventlog service\n"));
+ ElfStatusUpdate(STOPPED);
+ ElCleanupStatus();
+ return;
+}