diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/eventlog/server | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/eventlog/server')
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; +} |