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 | |
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')
65 files changed, 24957 insertions, 0 deletions
diff --git a/private/eventlog/dirs b/private/eventlog/dirs new file mode 100644 index 000000000..ecb7e74d1 --- /dev/null +++ b/private/eventlog/dirs @@ -0,0 +1,31 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + Steve Wood (stevewo) 17-Apr-1990 + +Note: + + The 'misc' directory is temporary. Will be removed after PPC. + +!ENDIF + +DIRS=server \ + elfclnt \ + netevent \ + misc + + +OPTIONAL_DIRS=test diff --git a/private/eventlog/elf.acf b/private/eventlog/elf.acf new file mode 100644 index 000000000..678a08bd6 --- /dev/null +++ b/private/eventlog/elf.acf @@ -0,0 +1,7 @@ +[ implicit_handle( handle_t eventlog_handle )] +interface eventlog +{ +#if defined(_PPC_) +[optimize( "s" )] ElfrChangeNotify(); +#endif +} diff --git a/private/eventlog/elf.idl b/private/eventlog/elf.idl new file mode 100644 index 000000000..8685828d3 --- /dev/null +++ b/private/eventlog/elf.idl @@ -0,0 +1,317 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + ELF.IDL + +Abstract: + + Contains the Elfr APIs which are used to remote the Elf APIs to + the local or remote server via RPC. + Also contains the RPC specific data structures for these API. + +Author: + + Rajen Shah (rajens) 02-Apr-1991 + +Revision History: + + 02-Apr-1991 RajenS + created + +--*/ + +// +// Interface Attributes +// + +[ + uuid(82273FDC-E32A-18C3-3F78-827929DC23EA), + version(0.0), +#ifdef __midl + ms_union, +#endif // __midl + pointer_default(unique) +] + +// +// Interface Keyword +// + +interface eventlog + +// +// Interface Body +// + +{ + +import "imports.idl"; // import all the include files +#include "event.h" + +// +// ELF Generic Handle used to bind from client to server. +// + +typedef [handle,unique] LPWSTR EVENTLOG_HANDLE_W; +typedef [handle,unique] LPSTR EVENTLOG_HANDLE_A; + +// +// ELF RPC Context Handle (Internal definition of ELF_HANDLE) +// + +typedef [context_handle] struct _IELF_HANDLE { + LIST_ENTRY Next; + ULONG Signature; // BUGBUG - For debug only + ULONG Flags; // e.g. "invalid" + ULONG GrantedAccess; + ATOM Atom; + ULONG SeekRecordPos; + ULONG SeekBytePos; + ULONG MajorVersion; // To identify caller + ULONG MinorVersion; + ULONG NameLength; + [size_is(NameLength)] WCHAR Name[]; // Keep this as last field +} *IELF_HANDLE; +typedef IELF_HANDLE *PIELF_HANDLE; + +// +// UNICODE Function Prototypes +// +// +// Only OpenEL and CloseEL need to take PIELF_HANDLE +// parameters since they are the only ones to modify the handle. +// The others will take IELF_HANDLE parameters instead. +// + +// NOTE: +// The names of the functions are short so that when RPC +// prepends the interface name to the names, they are still +// unique. +// + +NTSTATUS +ElfrClearELFW ( + [in] IELF_HANDLE LogHandle, + [in,unique] PRPC_UNICODE_STRING BackupFileName + ); + +NTSTATUS +ElfrBackupELFW ( + [in] IELF_HANDLE LogHandle, + [in] PRPC_UNICODE_STRING BackupFileName + ); + +NTSTATUS +ElfrCloseEL ( + [in,out] PIELF_HANDLE LogHandle + ); + +NTSTATUS +ElfrDeregisterEventSource ( + [in,out] PIELF_HANDLE LogHandle + ); + +NTSTATUS +ElfrNumberOfRecords( + [in] IELF_HANDLE LogHandle, + [out] PULONG NumberOfRecords + ); + +NTSTATUS +ElfrOldestRecord( + [in] IELF_HANDLE LogHandle, + [out] PULONG OldestRecordNumber + ); + +NTSTATUS +ElfrChangeNotify( + [in] IELF_HANDLE LogHandle, + [in] RPC_CLIENT_ID ClientId, + [in] ULONG Event + ); + +// +// The ModuleName is the one passed in by the caller of the API. This is +// the one that is written to the log file. +// +// RegModuleName is generated by the client stub, and is the name that is +// used to determine the default log file name from the registry. For +// NT Product1, this will be "Application". NOTE: It is necessary to do this +// now so that we can support viewing logs from a Product 2 system in the +// future if we change our logging mechanism. +// + +NTSTATUS +ElfrOpenELW ( + [in] EVENTLOG_HANDLE_W UNCServerName, + [in] PRPC_UNICODE_STRING ModuleName, // Passed in API call + [in] PRPC_UNICODE_STRING RegModuleName, // Used for registry + [in] ULONG MajorVersion, + [in] ULONG MinorVersion, + [out] PIELF_HANDLE LogHandle + ); + +NTSTATUS +ElfrRegisterEventSourceW ( + [in] EVENTLOG_HANDLE_W UNCServerName, + [in] PRPC_UNICODE_STRING ModuleName, // Passed in API call + [in] PRPC_UNICODE_STRING RegModuleName, // Used for registry info + [in] ULONG MajorVersion, + [in] ULONG MinorVersion, + [out] PIELF_HANDLE LogHandle + ); + +NTSTATUS +ElfrOpenBELW ( + [in] EVENTLOG_HANDLE_W UNCServerName, + [in] PRPC_UNICODE_STRING BackupFileName, // Passed in API call + [in] ULONG MajorVersion, + [in] ULONG MinorVersion, + [out] PIELF_HANDLE LogHandle + ); + +// +// BUGBUG - Fix this interface once RPC supports the ability to return +// zero bytes. Then, Buffer will have a length_is(*NumberOfBytesRead) +// added to it. For now, always transfer NumberOfBytesToRead bytes! +// +// The LogHandle is an [in,out] since the server side stores information +// in it that pertains to the current seek position. +// + +NTSTATUS +ElfrReadELW ( + [in] IELF_HANDLE LogHandle, + [in] ULONG ReadFlags, + [in] ULONG RecordOffset, + [in] ULONG NumberOfBytesToRead, + [out,size_is(NumberOfBytesToRead)] PBYTE Buffer, + [out] PULONG NumberOfBytesRead, + [out] PULONG MinNumberOfBytesNeeded + ); + +NTSTATUS +ElfrReportEventW ( + [in] IELF_HANDLE LogHandle, + [in] ULONG Time, + [in] USHORT EventType, + [in] USHORT EventCategory, + [in] ULONG EventID, + [in] USHORT NumStrings, + [in] ULONG DataSize, + [in] PRPC_UNICODE_STRING ComputerName, + [in, unique] PRPC_SID UserSID, + [in, size_is(NumStrings), unique] PRPC_UNICODE_STRING Strings[*], + [in, size_is(DataSize), unique] PBYTE Data, + [in] USHORT Flags, + [in,out,unique] PULONG RecordNumber, + [in,out,unique] PULONG TimeWritten + ); + +// +// ANSI Function Prototypes +// +// +// Only OpenEL needs to take PIELF_HANDLE +// parameters since they are the only ones to modify the handle. +// The others will take IELF_HANDLE parameters instead. +// +// NOTE that there is only one api for CloseEL since there are no +// strings involved. +// + +NTSTATUS +ElfrClearELFA ( + [in] IELF_HANDLE LogHandle, + [in,unique] PRPC_STRING BackupFileName + ); + +NTSTATUS +ElfrBackupELFA ( + [in] IELF_HANDLE LogHandle, + [in] PRPC_STRING BackupFileName + ); + +// +// The ModuleName is the one passed in by the caller of the API. This is +// the one that is written to the log file. +// +// RegModuleName is generated by the client stub, and is the name that is +// used to determine the default log file name from the registry. For +// NT Product1, this will be "Application". NOTE: It is necessary to do this +// now so that we can support viewing logs from a Product 2 system in the +// future if we change our logging mechanism. +// + +NTSTATUS +ElfrOpenELA ( + [in] EVENTLOG_HANDLE_A UNCServerName, + [in] PRPC_STRING ModuleName, // Passed in API call + [in] PRPC_STRING RegModuleName, // Used for registry info + [in] ULONG MajorVersion, + [in] ULONG MinorVersion, + [out] PIELF_HANDLE LogHandle + ); + +NTSTATUS +ElfrRegisterEventSourceA ( + [in] EVENTLOG_HANDLE_A UNCServerName, + [in] PRPC_STRING ModuleName, // Passed in API call + [in] PRPC_STRING RegModuleName, // Used for registry info + [in] ULONG MajorVersion, + [in] ULONG MinorVersion, + [out] PIELF_HANDLE LogHandle + ); + +NTSTATUS +ElfrOpenBELA ( + [in] EVENTLOG_HANDLE_A UNCServerName, + [in] PRPC_STRING FileName, // Passed in API call + [in] ULONG MajorVersion, + [in] ULONG MinorVersion, + [out] PIELF_HANDLE LogHandle + ); + +// +// BUGBUG - Fix this interface once RPC supports the ability to return +// zero bytes. Then, Buffer will have a length_is(*NumberOfBytesRead) +// added to it. For now, always transfer NumberOfBytesToRead bytes! +// +// The LogHandle is an [in,out] since the server side stores information +// in it that pertains to the current seek position. +// + +NTSTATUS +ElfrReadELA ( + [in] IELF_HANDLE LogHandle, + [in] ULONG ReadFlags, + [in] ULONG RecordOffset, + [in] ULONG NumberOfBytesToRead, + [out,size_is(NumberOfBytesToRead)] PBYTE Buffer, + [out] PULONG NumberOfBytesRead, + [out] PULONG MinNumberOfBytesNeeded + ); + +NTSTATUS +ElfrReportEventA ( + [in] IELF_HANDLE LogHandle, + [in] ULONG Time, + [in] USHORT EventType, + [in] USHORT EventCategory, + [in] ULONG EventID, + [in] USHORT NumStrings, + [in] ULONG DataSize, + [in] PRPC_STRING ComputerName, + [in, unique] PRPC_SID UserSID, + [in, size_is(NumStrings), unique] PRPC_STRING Strings[*], + [in, size_is(DataSize), unique] PBYTE Data, + [in] USHORT Flags, + [in,out,unique] PULONG RecordNumber, + [in,out,unique] PULONG TimeWritten + ); + + +} diff --git a/private/eventlog/elfclnt/apistub.c b/private/eventlog/elfclnt/apistub.c new file mode 100644 index 000000000..da62f55bf --- /dev/null +++ b/private/eventlog/elfclnt/apistub.c @@ -0,0 +1,1577 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + APISTUB.C + +Abstract: + + This module contains the client ends of the Elf APIs. + + +Author: + + Rajen Shah (rajens) 29-Jul-1991 + + +Revision History: + + 29-Jul-1991 RajenS + Created + +--*/ + +#include <elfclntp.h> +#include <lmerr.h> +#include <stdlib.h> +#include <string.h> + +// +// Global data +// +PUNICODE_STRING pGlobalComputerNameU; +PANSI_STRING pGlobalComputerNameA; + + + +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; + PANSI_STRING pNameA; + LPSTR pName; + NTSTATUS Error; + NTSTATUS status; + + + pNameU = MIDL_user_allocate (sizeof (UNICODE_STRING)); + pNameA = MIDL_user_allocate (sizeof (ANSI_STRING)); + + if ((pNameU != NULL) && (pNameA != NULL)) { + + if ((Error = ElfpGetComputerName (&pName)) == NERR_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 { + + DbgPrint("[ELFCLNT] GetComputerName - Error 0x%lx\n", status); + LocalFree(pName); + MIDL_user_free (pNameU); // Free the buffers + MIDL_user_free (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); +} + + + +PANSI_STRING +TmpGetComputerNameA ( ) + +/*++ + +Routine Description: + + This routine gets the ANSI 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 (pGlobalComputerNameA == NULL) { + w_GetComputerName(); + } + return (pGlobalComputerNameA); +} + +// +// These APIs only have one interface, since they don't take or return strings +// + +NTSTATUS +ElfNumberOfRecords( + IN HANDLE LogHandle, + OUT PULONG NumberOfRecords + ) +{ + NTSTATUS status; + + // + // Make sure the output pointer is valid + // + + if (!NumberOfRecords) { + return(STATUS_INVALID_PARAMETER); + } + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + // Call service entry point + + status = ElfrNumberOfRecords ( + (IELF_HANDLE) LogHandle, + NumberOfRecords + ); + + } + RpcExcept (1) { + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + return (status); + +} + +NTSTATUS +ElfOldestRecord( + IN HANDLE LogHandle, + OUT PULONG OldestRecordNumber + ) +{ + NTSTATUS status; + + // + // + // Make sure the output pointer is valid + // + + if (!OldestRecordNumber) { + return(STATUS_INVALID_PARAMETER); + } + + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + // Call service entry point + + status = ElfrOldestRecord ( + (IELF_HANDLE) LogHandle, + OldestRecordNumber + ); + + } + RpcExcept (1) { + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + return (status); + +} + +NTSTATUS +ElfChangeNotify( + IN HANDLE LogHandle, + IN HANDLE Event + ) +{ + + NTSTATUS status; + RPC_CLIENT_ID RpcClientId; + CLIENT_ID ClientId; + + // + // Map the handles to something that RPC can understand + // + + ClientId = NtCurrentTeb()->ClientId; + RpcClientId.UniqueProcess = (ULONG) ClientId.UniqueProcess; + RpcClientId.UniqueThread = (ULONG) ClientId.UniqueThread; + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + + RpcTryExcept { + + // Call service entry point + + status = ElfrChangeNotify ( + (IELF_HANDLE) LogHandle, + RpcClientId, + (DWORD) Event + ); + + } + RpcExcept (1) { + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + return (status); + +} + +// +// UNICODE APIs +// + +NTSTATUS +ElfOpenEventLogW ( + IN PUNICODE_STRING UNCServerName, + IN PUNICODE_STRING LogName, + OUT PHANDLE LogHandle + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfOpenEventLog API. + + It creates an RPC binding for the server specified, and stores that + and additional data away. It returns a handle to the caller that can + be used to later on access the handle-specific information. + +Arguments: + + UNCServerName - Server with which to bind for subsequent operations. + + LogName - Supplies the name of the module for the logfile + to associate with this handle. + + LogHandle - Location where log handle is to be returned. + + +Return Value: + + Returns an NTSTATUS code and, if no error, a handle that can be used + for subsequent Elf API calls. + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + UNICODE_STRING RegModuleName; + EVENTLOG_HANDLE_W ServerNameString; + + // + // Make sure input & output pointers are valid + // + + if (!LogHandle || !LogName || LogName->Length == 0) { + return(STATUS_INVALID_PARAMETER); + } + + if ((UNCServerName != NULL) && (UNCServerName->Length != 0)) { + ServerNameString = UNCServerName->Buffer; + } else { + ServerNameString = NULL; + } + + RtlInitUnicodeString( &RegModuleName, UNICODE_NULL); + + // Call service via RPC. Pass in major and minor version numbers. + + *LogHandle = NULL; // Must be NULL so RPC fills it in + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + status = ElfrOpenELW( + ServerNameString, + (PRPC_UNICODE_STRING) LogName, + (PRPC_UNICODE_STRING) &RegModuleName, + ELF_VERSION_MAJOR, + ELF_VERSION_MINOR, + (PIELF_HANDLE) LogHandle + ); + + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + + return (status); +} + + +NTSTATUS +ElfRegisterEventSourceW ( + IN PUNICODE_STRING UNCServerName, + IN PUNICODE_STRING ModuleName, + OUT PHANDLE LogHandle + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfRegisterEventSource API. + + It creates an RPC binding for the server specified, and stores that + and additional data away. It returns a handle to the caller that can + be used to later on access the handle-specific information. + +Arguments: + + UNCServerName - Server with which to bind for subsequent operations. + + ModuleName - Supplies the name of the module to associate with + this handle. + + LogHandle - Location where log handle is to be returned. + + +Return Value: + + Returns an NTSTATUS code and, if no error, a handle that can be used + for subsequent Elf API calls. + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + UNICODE_STRING RegModuleName; + EVENTLOG_HANDLE_W ServerNameString; + + // + // Make sure input & output pointers are valid + // + + if (!LogHandle || !ModuleName || ModuleName->Length == 0) { + return(STATUS_INVALID_PARAMETER); + } + + if ((UNCServerName != NULL) && (UNCServerName->Length != 0)) { + ServerNameString = UNCServerName->Buffer; + } else { + ServerNameString = NULL; + } + + RtlInitUnicodeString( &RegModuleName, UNICODE_NULL); + + // Call service via RPC. Pass in major and minor version numbers. + + *LogHandle = NULL; // Must be NULL so RPC fills it in + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + status = ElfrRegisterEventSourceW( + ServerNameString, + (PRPC_UNICODE_STRING)ModuleName, + (PRPC_UNICODE_STRING)&RegModuleName, + ELF_VERSION_MAJOR, + ELF_VERSION_MINOR, + (PIELF_HANDLE) LogHandle + ); + + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + + return (status); +} + + +NTSTATUS +ElfOpenBackupEventLogW ( + IN PUNICODE_STRING UNCServerName, + IN PUNICODE_STRING BackupFileName, + OUT PHANDLE LogHandle + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfOpenBackupEventLog API. + + It creates an RPC binding for the server specified, and stores that + and additional data away. It returns a handle to the caller that can + be used to later on access the handle-specific information. + +Arguments: + + UNCServerName - Server with which to bind for subsequent operations. + + BackupFileName - Supplies the filename of the module to associate with + this handle. + + LogHandle - Location where log handle is to be returned. + + +Return Value: + + Returns an NTSTATUS code and, if no error, a handle that can be used + for subsequent Elf API calls. + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + EVENTLOG_HANDLE_W ServerNameString; + + // + // Make sure input & output pointers are valid + // + + if (!LogHandle || !BackupFileName || BackupFileName->Length == 0) { + return(STATUS_INVALID_PARAMETER); + } + + if ((UNCServerName != NULL) && (UNCServerName->Length != 0)) { + ServerNameString = UNCServerName->Buffer; + } else { + ServerNameString = NULL; + } + + // Call service via RPC. Pass in major and minor version numbers. + + *LogHandle = NULL; // Must be NULL so RPC fills it in + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + + RpcTryExcept { + + status = ElfrOpenBELW( + ServerNameString, + (PRPC_UNICODE_STRING)BackupFileName, + ELF_VERSION_MAJOR, + ELF_VERSION_MINOR, + (PIELF_HANDLE) LogHandle + ); + + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + return (status); +} + + + +NTSTATUS +ElfClearEventLogFileW ( + IN HANDLE LogHandle, + IN PUNICODE_STRING BackupFileName + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfClearEventLogFile API. + The call is passed to the eventlog service on the appropriate server + identified by LogHandle. + + +Arguments: + + LogHandle - Handle returned from a previous "Open" call. This is + used to identify the module and the server. + + 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; + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + // Call service entry point + + status = ElfrClearELFW ( + (IELF_HANDLE) LogHandle, + (PRPC_UNICODE_STRING)BackupFileName + ); + + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + return (status); + +} + + +NTSTATUS +ElfBackupEventLogFileW ( + IN HANDLE LogHandle, + IN PUNICODE_STRING BackupFileName + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfBackupEventLogFile API. + The call is passed to the eventlog service on the appropriate server + identified by LogHandle. + + +Arguments: + + LogHandle - Handle returned from a previous "Open" call. This is + used to identify the module and the server. + + BackupFileName - Name of the file to back up the current log file. + + +Return Value: + + Returns an NTSTATUS code. + + +--*/ +{ + NTSTATUS status; + + // + // Make sure input pointers are valid + // + + if (!BackupFileName || BackupFileName->Length == 0) { + return(STATUS_INVALID_PARAMETER); + } + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + // Call service entry point + + status = ElfrBackupELFW ( + (IELF_HANDLE) LogHandle, + (PRPC_UNICODE_STRING)BackupFileName + ); + + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + return (status); + +} + + +NTSTATUS +ElfCloseEventLog ( + IN HANDLE LogHandle + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfCloseEventLog API. + It closes the RPC binding, and frees any memory allocated for the + handle. + + +Arguments: + + LogHandle - Handle returned from a previous "Open" call. + + +Return Value: + + Returns an NTSTATUS code. + + +--*/ +{ + NTSTATUS status; + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + // Call server + + status = ElfrCloseEL ( + (PIELF_HANDLE) &LogHandle + ); + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + return (status); + +} + + +NTSTATUS +ElfDeregisterEventSource ( + IN HANDLE LogHandle + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfDeregisterEventSource API. + It closes the RPC binding, and frees any memory allocated for the + handle. + + +Arguments: + + LogHandle - Handle returned from a previous "Open" call. + + +Return Value: + + Returns an NTSTATUS code. + + +--*/ +{ + NTSTATUS status; + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + // Call server + + status = ElfrDeregisterEventSource ( + (PIELF_HANDLE) &LogHandle + ); + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + return (status); + +} + + + +NTSTATUS +ElfReadEventLogW ( + IN HANDLE LogHandle, + IN ULONG ReadFlags, + IN ULONG RecordNumber, + OUT PVOID Buffer, + IN ULONG NumberOfBytesToRead, + OUT PULONG NumberOfBytesRead, + OUT PULONG MinNumberOfBytesNeeded + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfReadEventLog API. + +Arguments: + + + +Return Value: + + Returns an NTSTATUS code. + + +--*/ +{ + NTSTATUS status; + ULONG FlagBits; + + // + // Make sure the output pointers are valid + // + + if (!Buffer || !NumberOfBytesRead || !MinNumberOfBytesNeeded) { + return(STATUS_INVALID_PARAMETER); + } + + // + // Ensure that the ReadFlags we got are valid. + // Make sure that one of each type of bit is set. + // + FlagBits = ReadFlags & (EVENTLOG_SEQUENTIAL_READ | EVENTLOG_SEEK_READ); + + if ((FlagBits > 2) || (FlagBits == 0)) { + return(STATUS_INVALID_PARAMETER); + } + + FlagBits = ReadFlags & (EVENTLOG_FORWARDS_READ | EVENTLOG_BACKWARDS_READ); + + if ((FlagBits > 8) || (FlagBits == 0)) { + return(STATUS_INVALID_PARAMETER); + } + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + // Call service + + status = ElfrReadELW ( + (IELF_HANDLE) LogHandle, + ReadFlags, + RecordNumber, + NumberOfBytesToRead, + Buffer, + NumberOfBytesRead, + MinNumberOfBytesNeeded + ); + + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + // Return status and bytes read/required. + + return (status); + +} + + + +NTSTATUS +ElfReportEventW ( + IN HANDLE LogHandle, + IN USHORT EventType, + IN USHORT EventCategory OPTIONAL, + IN ULONG EventID, + IN PSID UserSid, + IN USHORT NumStrings, + IN ULONG DataSize, + IN PUNICODE_STRING *Strings, + IN PVOID Data, + IN USHORT Flags, + IN OUT PULONG RecordNumber OPTIONAL, + IN OUT PULONG TimeWritten OPTIONAL + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfReportEvent API. + +Arguments: + + +Return Value: + + Returns an NTSTATUS code. + +Note: + + The last three parameters (Flags, RecordNumber and TimeWritten) are + designed to be used by Security Auditing for the implementation of + paired events (associating a file open event with the subsequent file + close). This will not be implemented in Product 1, but the API is + defined to allow easier support of this in a later release. + + +--*/ +{ + NTSTATUS status; + PUNICODE_STRING pComputerNameU; + LARGE_INTEGER Time; + ULONG EventTime; + + // + // 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(); + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + // Call service + + status = ElfrReportEventW ( + (IELF_HANDLE) LogHandle, + EventTime, + EventType, + EventCategory, + EventID, + NumStrings, + DataSize, + (PRPC_UNICODE_STRING)pComputerNameU, + UserSid, + (PRPC_UNICODE_STRING *)Strings, + Data, + Flags, + RecordNumber, + TimeWritten + ); + + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + return (status); + +} + + +// +// ANSI APIs +// + +NTSTATUS +ElfOpenEventLogA ( + IN PANSI_STRING UNCServerName, + IN PANSI_STRING LogName, + OUT PHANDLE LogHandle + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfOpenEventLog API. + + It creates an RPC binding for the server specified, and stores that + and additional data away. It returns a handle to the caller that can + be used to later on access the handle-specific information. + +Arguments: + + UNCServerName - Server with which to bind for subsequent operations. + + LogName - Supplies the name of the module for the logfile to + associate with this handle. + + LogHandle - Location where log handle is to be returned. + + +Return Value: + + Returns an NTSTATUS code and, if no error, a handle that can be used + for subsequent Elf API calls. + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + ANSI_STRING RegModuleName; + EVENTLOG_HANDLE_A ServerNameString; + + // + // Make sure input & output pointers are valid + // + + if (!LogHandle || !LogName || LogName->Length == 0) { + return(STATUS_INVALID_PARAMETER); + } + + if ((UNCServerName != NULL) && (UNCServerName->Length != 0)) { + ServerNameString = UNCServerName->Buffer; + } else { + ServerNameString = NULL; + } + + RtlInitAnsiString( &RegModuleName, ELF_APPLICATION_MODULE_NAME_ASCII ); + + if ( NT_SUCCESS (status) ) { + + // Call service via RPC. Pass in major and minor version numbers. + + *LogHandle = NULL; // Must be NULL so RPC fills it in + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + status = ElfrOpenELA ( + ServerNameString, + (PRPC_STRING) LogName, + (PRPC_STRING) &RegModuleName, + ELF_VERSION_MAJOR, + ELF_VERSION_MINOR, + (PIELF_HANDLE) LogHandle + ); + + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + + } + + return (status); +} + + +NTSTATUS +ElfRegisterEventSourceA ( + IN PANSI_STRING UNCServerName, + IN PANSI_STRING ModuleName, + OUT PHANDLE LogHandle + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfOpenEventLog API. + + It creates an RPC binding for the server specified, and stores that + and additional data away. It returns a handle to the caller that can + be used to later on access the handle-specific information. + +Arguments: + + UNCServerName - Server with which to bind for subsequent operations. + + ModuleName - Supplies the name of the module to associate with + this handle. + + LogHandle - Location where log handle is to be returned. + + +Return Value: + + Returns an NTSTATUS code and, if no error, a handle that can be used + for subsequent Elf API calls. + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + ANSI_STRING RegModuleName; + EVENTLOG_HANDLE_A ServerNameString; + + // + // Make sure input & output pointers are valid + // + + if (!LogHandle || !ModuleName || ModuleName->Length == 0) { + return(STATUS_INVALID_PARAMETER); + } + + if ((UNCServerName != NULL) && (UNCServerName->Length != 0)) { + ServerNameString = UNCServerName->Buffer; + } else { + ServerNameString = NULL; + } + + RtlInitAnsiString( &RegModuleName, ELF_APPLICATION_MODULE_NAME_ASCII ); + + if ( NT_SUCCESS (status) ) { + + // Call service via RPC. Pass in major and minor version numbers. + + *LogHandle = NULL; // Must be NULL so RPC fills it in + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + status = ElfrRegisterEventSourceA ( + ServerNameString, + (PRPC_STRING)ModuleName, + (PRPC_STRING)&RegModuleName, + ELF_VERSION_MAJOR, + ELF_VERSION_MINOR, + (PIELF_HANDLE) LogHandle + ); + + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + + } + + return (status); +} + + + +NTSTATUS +ElfOpenBackupEventLogA ( + IN PANSI_STRING UNCServerName, + IN PANSI_STRING FileName, + OUT PHANDLE LogHandle + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfOpenBackupEventLog API. + + It creates an RPC binding for the server specified, and stores that + and additional data away. It returns a handle to the caller that can + be used to later on access the handle-specific information. + +Arguments: + + UNCServerName - Server with which to bind for subsequent operations. + + FileName - Supplies the filename of the logfile to associate with + this handle. + + LogHandle - Location where log handle is to be returned. + + +Return Value: + + Returns an NTSTATUS code and, if no error, a handle that can be used + for subsequent Elf API calls. + + +--*/ +{ + EVENTLOG_HANDLE_A ServerNameString; + NTSTATUS status; + + // + // Make sure input & output pointers are valid + // + + if (!LogHandle || !FileName || FileName->Length == 0) { + return(STATUS_INVALID_PARAMETER); + } + + if ((UNCServerName != NULL) && (UNCServerName->Length != 0)) { + ServerNameString = UNCServerName->Buffer; + } else { + ServerNameString = NULL; + } + + // Call service via RPC. Pass in major and minor version numbers. + + *LogHandle = NULL; // Must be NULL so RPC fills it in + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + + RpcTryExcept { + + status = ElfrOpenBELA ( + ServerNameString, + (PRPC_STRING)FileName, + ELF_VERSION_MAJOR, + ELF_VERSION_MINOR, + (PIELF_HANDLE) LogHandle + ); + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + return (status); +} + + + +NTSTATUS +ElfClearEventLogFileA ( + IN HANDLE LogHandle, + IN PANSI_STRING BackupFileName + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfClearEventLogFile API. + The call is passed to the eventlog service on the appropriate server + identified by LogHandle. + + +Arguments: + + LogHandle - Handle returned from a previous "Open" call. This is + used to identify the module and the server. + + 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; + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + // Call service entry point + + status = ElfrClearELFA ( + (IELF_HANDLE) LogHandle, + (PRPC_STRING)BackupFileName + ); + + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + return (status); + +} + + +NTSTATUS +ElfBackupEventLogFileA ( + IN HANDLE LogHandle, + IN PANSI_STRING BackupFileName + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfBackupEventLogFile API. + The call is passed to the eventlog service on the appropriate server + identified by LogHandle. + + +Arguments: + + LogHandle - Handle returned from a previous "Open" call. This is + used to identify the module and the server. + + BackupFileName - Name of the file to back up the current log file. + + +Return Value: + + Returns an NTSTATUS code. + + +--*/ +{ + NTSTATUS status; + + // + // Make sure input pointers are valid + // + + if (!BackupFileName || BackupFileName->Length == 0) { + return(STATUS_INVALID_PARAMETER); + } + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + // Call service entry point + + status = ElfrBackupELFA ( + (IELF_HANDLE) LogHandle, + (PRPC_STRING)BackupFileName + ); + + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + return (status); + +} + + + +NTSTATUS +ElfReadEventLogA ( + IN HANDLE LogHandle, + IN ULONG ReadFlags, + IN ULONG RecordNumber, + OUT PVOID Buffer, + IN ULONG NumberOfBytesToRead, + OUT PULONG NumberOfBytesRead, + OUT PULONG MinNumberOfBytesNeeded + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfReadEventLog API. + +Arguments: + + + +Return Value: + + Returns an NTSTATUS code. + + +--*/ +{ + NTSTATUS status; + ULONG FlagBits; + + // + // Make sure the output pointers are valid + // + + if (!Buffer || !NumberOfBytesRead || !MinNumberOfBytesNeeded) { + return(STATUS_INVALID_PARAMETER); + } + + // + // Ensure that the ReadFlags we got are valid. + // Make sure that one of each type of bit is set. + // + FlagBits = ReadFlags & (EVENTLOG_SEQUENTIAL_READ | EVENTLOG_SEEK_READ); + + if ( (FlagBits == (EVENTLOG_SEQUENTIAL_READ | EVENTLOG_SEEK_READ)) + || (FlagBits == 0)) { + return(STATUS_INVALID_PARAMETER); + } + + FlagBits = ReadFlags & (EVENTLOG_FORWARDS_READ | EVENTLOG_BACKWARDS_READ); + + if ( (FlagBits == (EVENTLOG_FORWARDS_READ | EVENTLOG_BACKWARDS_READ)) + || (FlagBits == 0)) { + return(STATUS_INVALID_PARAMETER); + } + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + // Call service + + status = ElfrReadELA ( + (IELF_HANDLE) LogHandle, + ReadFlags, + RecordNumber, + NumberOfBytesToRead, + Buffer, + NumberOfBytesRead, + MinNumberOfBytesNeeded + ); + + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + // Return status and bytes read/required. + + return (status); + +} + + + +NTSTATUS +ElfReportEventA ( + IN HANDLE LogHandle, + IN USHORT EventType, + IN USHORT EventCategory OPTIONAL, + IN ULONG EventID, + IN PSID UserSid, + IN USHORT NumStrings, + IN ULONG DataSize, + IN PANSI_STRING *Strings, + IN PVOID Data, + IN USHORT Flags, + IN OUT PULONG RecordNumber OPTIONAL, + IN OUT PULONG TimeWritten OPTIONAL + ) + +/*++ + +Routine Description: + + This is the client DLL entry point for the ElfReportEvent API. + +Arguments: + + +Return Value: + + Returns an NTSTATUS code. + +Note: + + The last three parameters (Flags, RecordNumber and TimeWritten) are + designed to be used by Security Auditing for the implementation of + paired events (associating a file open event with the subsequent file + close). This will not be implemented in Product 1, but the API is + defined to allow easier support of this in a later release. + + +--*/ +{ + NTSTATUS status; + PANSI_STRING pComputerNameA; + LARGE_INTEGER Time; + ULONG EventTime; + + // + // 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. + // + pComputerNameA = TmpGetComputerNameA(); + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + // Call service + + status = ElfrReportEventA ( + (IELF_HANDLE) LogHandle, + EventTime, + EventType, + EventCategory, + EventID, + NumStrings, + DataSize, + (PRPC_STRING)pComputerNameA, + UserSid, + (PRPC_STRING*)Strings, + Data, + Flags, + RecordNumber, + TimeWritten + ); + + } + RpcExcept (1) { + + status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + + return (status); + +} diff --git a/private/eventlog/elfclnt/elfclntp.h b/private/eventlog/elfclnt/elfclntp.h new file mode 100644 index 000000000..ec0de3a67 --- /dev/null +++ b/private/eventlog/elfclnt/elfclntp.h @@ -0,0 +1,44 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + elfclntp.h + +Abstract: + + Common include file for all the client-side modules for the + event logging facility. + +Author: + + Rajen Shah (rajens) 29-Jul-1991 + + +Revision History: + + 29-Jul-1991 RajenS + Created + +--*/ + +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> + +#include <windef.h> +#include <winbase.h> +#include <rpc.h> +#include <ntrpcp.h> +#include <lmcons.h> +#include <lmerr.h> +//#include <netlib.h> + +#include <elf.h> +#include <elfcommn.h> + + +NTSTATUS +ElfpGetComputerName ( + IN LPSTR *ComputerNamePtr); diff --git a/private/eventlog/elfclnt/getconfg.c b/private/eventlog/elfclnt/getconfg.c new file mode 100644 index 000000000..a947af787 --- /dev/null +++ b/private/eventlog/elfclnt/getconfg.c @@ -0,0 +1,110 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + getconfg.c + +Abstract: + + This is a Hacked up version of getconfg.c stolen from + c:\nt\private\net\netlib. We need to make an rtl routine out + of this - something that is more globally available. + -Danl 9-3-91 + + + This module contains routines for manipulating configuration + information. The following functions available are: + + NetpGetComputerName + + Currently configuration information is kept in NT.CFG. + Later it will be kept by the configuration manager. + +Author: + + Dan Lafferty (danl) 09-Apr-1991 + +Environment: + + User Mode -Win32 (also uses nt RTL routines) + +Revision History: + + 09-Apr-1991 danl + created + +--*/ + +//#include <stdlib.h> // atol +#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 <ntlsa.h> // +#include <windef.h> +#include <winbase.h> // LocalAlloc +#include <lmcons.h> +#include <string.h> +#include <lmerr.h> + + + +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 + LocalFree 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 = LocalAlloc(LMEM_ZEROINIT, 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)) { + LocalFree(*ComputerNamePtr); + *ComputerNamePtr = NULL; + return (GetLastError()); + } + + return (NERR_Success); +} diff --git a/private/eventlog/elfclnt/makefile b/private/eventlog/elfclnt/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/eventlog/elfclnt/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/elfclnt/rpcbind.c b/private/eventlog/elfclnt/rpcbind.c new file mode 100644 index 000000000..2cf13c1eb --- /dev/null +++ b/private/eventlog/elfclnt/rpcbind.c @@ -0,0 +1,205 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + rpcbind.c + +Abstract: + + Contains the RPC bind and un-bind routines for the Eventlog + client-side APIs. + +Author: + + Rajen Shah (rajens) 30-Jul-1991 + +Revision History: + + 30-Jul-1991 RajenS + created + +--*/ + +// +// INCLUDES +// +#include <elfclntp.h> +#include <lmsvc.h> + +#define SERVICE_EVENTLOG L"EVENTLOG" + + +/****************************************************************************/ +handle_t +EVENTLOG_HANDLE_W_bind ( + EVENTLOG_HANDLE_W ServerName) + +/*++ + +Routine Description: + This routine calls a common bind routine that is shared by all services. + This routine is called from the ElfOpenEventLog API client stub when + it is necessary to bind to a server. + The binding is done to allow impersonation by the server since that is + necessary for the API calls. + +Arguments: + + ServerName - A pointer to a string containing the name of the server + to bind with. + +Return Value: + + The binding handle is returned to the stub routine. If the + binding is unsuccessful, a NULL will be returned. + +--*/ +{ + handle_t bindingHandle; + RPC_STATUS status; + + status = RpcpBindRpc ( + ServerName, + SERVICE_EVENTLOG, + NULL, + &bindingHandle); + + // DbgPrint("EVENTLOG_bind: handle=%d\n",bindingHandle); + return( bindingHandle); +} + + + +/****************************************************************************/ +void +EVENTLOG_HANDLE_W_unbind ( + EVENTLOG_HANDLE_W ServerName, + handle_t BindingHandle) + +/*++ + +Routine Description: + + This routine calls a common unbind routine that is shared by + all services. + +Arguments: + + ServerName - This is the name of the server from which to unbind. + + BindingHandle - This is the binding handle that is to be closed. + +Return Value: + + none. + +--*/ +{ + RPC_STATUS status; + + // DbgPrint("EVENTLOG_HANDLE_unbind: handle=%d\n",BindingHandle); + status = RpcpUnbindRpc ( BindingHandle); + return; + + UNREFERENCED_PARAMETER(ServerName); + +} + + +handle_t +EVENTLOG_HANDLE_A_bind ( + EVENTLOG_HANDLE_A ServerName) + +/*++ + +Routine Description: + + This routine calls EVENTLOG_HANDLE_W_bind to do the work. + +Arguments: + + ServerName - A pointer to a UNICODE string containing the name of + the server to bind with. + +Return Value: + + The binding handle is returned to the stub routine. If the + binding is unsuccessful, a NULL will be returned. + +--*/ +{ + UNICODE_STRING ServerNameU; + ANSI_STRING ServerNameA; + handle_t bindingHandle; + + // + // Convert the ANSI string to a UNICODE string before calling the + // UNICODE routine. + // + RtlInitAnsiString (&ServerNameA, (PSTR)ServerName); + + RtlAnsiStringToUnicodeString ( + &ServerNameU, + &ServerNameA, + TRUE + ); + + bindingHandle = EVENTLOG_HANDLE_W_bind( + (EVENTLOG_HANDLE_W)ServerNameU.Buffer + ); + + RtlFreeUnicodeString (&ServerNameU); + + return( bindingHandle); +} + + + +/****************************************************************************/ +void +EVENTLOG_HANDLE_A_unbind ( + EVENTLOG_HANDLE_A ServerName, + handle_t BindingHandle) + +/*++ + +Routine Description: + + This routine calls EVENTLOG_HANDLE_W_unbind. + +Arguments: + + ServerName - This is the ANSI name of the server from which to unbind. + + BindingHandle - This is the binding handle that is to be closed. + +Return Value: + + none. + +--*/ +{ + UNICODE_STRING ServerNameU; + ANSI_STRING ServerNameA; + + // + // Convert the ANSI string to a UNICODE string before calling the + // UNICODE routine. + // + RtlInitAnsiString (&ServerNameA, (PSTR)ServerName); + + RtlAnsiStringToUnicodeString ( + &ServerNameU, + &ServerNameA, + TRUE + ); + + EVENTLOG_HANDLE_W_unbind( (EVENTLOG_HANDLE_W)ServerNameU.Buffer, + BindingHandle ); + + RtlFreeUnicodeString (&ServerNameU); + + return; +} diff --git a/private/eventlog/elfclnt/sources b/private/eventlog/elfclnt/sources new file mode 100644 index 000000000..457c34587 --- /dev/null +++ b/private/eventlog/elfclnt/sources @@ -0,0 +1,59 @@ +!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 + +# +# The TARGETNAME variable is defined by the developer. It is the name of +# the target (component) that is being built by this makefile. It +# should NOT include any path or file extension information. +# + +MAJORCOMP=eventlog +MINORCOMP=client +TARGETNAME=elfapi +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=LIBRARY + +# +# The INCLUDES variable specifies any include paths that are specific to +# this source directory. Separate multiple directory paths with single +# semicolons. Relative path specifications are okay. +# + +INCLUDES=.;..;$(BASEDIR)\private\inc + +# +# The SOURCES variable is defined by the developer. It is a list of all the +# source files for this component. Each source file should be on a separate +# line using the line continuation character. This will minimize merge +# conflicts if two developers adding source files to the same component. +# + +SOURCES= \ + elf_c.c \ + apistub.c \ + rpcbind.c \ + getconfg.c + +C_DEFINES=$(C_DEFINES) -DRPC_NO_WINDOWS_H -D_ADVAPI32_ diff --git a/private/eventlog/elfcommn.h b/private/eventlog/elfcommn.h new file mode 100644 index 000000000..6c49d6bdb --- /dev/null +++ b/private/eventlog/elfcommn.h @@ -0,0 +1,75 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + elfcommn.h + +Abstract: + + Common defines for client and server. + +Author: + + Rajen Shah (rajens) 12-Aug-1991 + +Revision History: + +--*/ + +#ifndef _ELFCOMMON_ +#define _ELFCOMMON_ + +// +// Current default names of modules supported +// + +#define ELF_MAX_LOG_MODULES 256 + +#define ELF_SYSTEM_MODULE_NAME L"System" +#define ELF_APPLICATION_MODULE_NAME L"Application" +#define ELF_SECURITY_MODULE_NAME L"Security" + +#define ELF_SYSTEM_MODULE_NAME_ASCII "System" +#define ELF_APPLICATION_MODULE_NAME_ASCII "Application" +#define ELF_SECURITY_MODULE_NAME_ASCII "Security" + + +// +// Version numbers for the file header and the client +// + +#define ELF_VERSION_MAJOR 0x0001 +#define ELF_VERSION_MINOR 0x0001 + +// +// The following are definitions for the Flags field in the context handle. +// +// ELF_LOG_HANDLE_INVALID is used to indicate that the handle is no +// longer valid - i.e. the contents of the file +// or the file itself have changed. It is used for +// READs to cause the reader to "resync". +// +// ELF_LOG_HANDLE_BACKUP_LOG indicates that this was created with the +// OpenBackupEventlog API and is not an active log. +// This means we do some additional work at close time +// and we disallow clear, backup, write and +// ChangeNotify operations. +// +// ELF_LOG_HANDLE_REMOTE_HANDLE indicates that this handle was created via +// a remote RPC call. This handle cannot be used for +// ElfChangeNotify +// +// ELF_LOG_HANDLE_GENERATE_ON_CLOSE indicates that NtCloseAuditAlarm must +// be called when this handle is closed. This flag +// is set when an audit is generated on open. +// + +#define ELF_LOG_HANDLE_INVALID_FOR_READ 0x0001 +#define ELF_LOG_HANDLE_BACKUP_LOG 0x0002 +#define ELF_LOG_HANDLE_REMOTE_HANDLE 0x0004 +#define ELF_LOG_HANDLE_LAST_READ_FORWARD 0x0008 +#define ELF_LOG_HANDLE_GENERATE_ON_CLOSE 0x0010 + +#endif /* _ELFCOMMON_ */ diff --git a/private/eventlog/eltest/eltest.c b/private/eventlog/eltest/eltest.c new file mode 100644 index 000000000..67d0812e2 --- /dev/null +++ b/private/eventlog/eltest/eltest.c @@ -0,0 +1,1535 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + ELTEST.C + +Abstract: + + Test Routines for the EventLog. + +THINGS I WANT THIS TO DO... + AddReg <ServerName> <logname> <EntryName> <EventMessageFile> + <CategoryMessageFile> <CategoryCount> <ParameterMessageFile> + <TypesSupported> - Creates A Registry Entry. + + eltest addreg application mytest mf= eltest.dll cat= + + CreateMessageFile <??? Is this possible ???> + + WriteEvent <ServerName> <EventSource> <Type> <Category> <EventId> <UserSid?> + <NumStrings> <Strings> <RawData> + + ReadLog <Server> <LogFile> <ReadFlags> <RecordOffset> <bufSize> + If LogFile isn't one of the popular ones, then it could be a backup + logfile. + + GetNumEvents <Server> <LogFile> + + GetOldest <Server> <LogFile> + + Clear <Server> <LogFile> + + Backup <Server> <LogFile> <BackupFile> + + + LOOPTESTS.... + I should be able to run this test like mprtest such that it doesn't leave + the test process until told. This way we can register an event source, + then if we call WriteEvent without a specified EventSource, it will use + the stored source. Calling RegisterEventSource twice without calling + DeRegisterSource would be an error. (Or better yet, I could keep a table + of sources and handles). + + RegisterEventSource <EventSource> + DeRegisterSource <EventSource> + + + + +PROTOTYPES FOR FUNCTION.... + +BOOL +CloseEventLog ( + HANDLE hEventLog + ) +BOOL +DeregisterEventSource ( + HANDLE hEventLog + ) + +BOOL +NotifyChangeEventLog( + HANDLE hEventLog, + HANDLE hEvent + ) +BOOL +GetNumberOfEventLogRecords ( + HANDLE hEventLog, + PDWORD NumberOfRecords + ) +BOOL +GetOldestEventLogRecord ( + HANDLE hEventLog, + PDWORD OldestRecord + ) +BOOL +ClearEventLogW ( + HANDLE hEventLog, + LPCWSTR BackupFileName + ) +BOOL +BackupEventLogW ( + HANDLE hEventLog, + LPCWSTR BackupFileName + ) +HANDLE +OpenEventLogW ( + LPCWSTR UNCServerName, + LPCWSTR ModuleName + ) +HANDLE +RegisterEventSourceW ( + LPCWSTR UNCServerName, + LPCWSTR ModuleName + ) +HANDLE +OpenBackupEventLogW ( + LPCWSTR UNCServerName, + LPCWSTR FileName + ) +BOOL +ReadEventLogW ( + HANDLE hEventLog, + DWORD dwReadFlags, + DWORD dwRecordOffset, + LPVOID lpBuffer, + DWORD nNumberOfBytesToRead, + DWORD *pnBytesRead, + DWORD *pnMinNumberOfBytesNeeded + ) +BOOL +ReportEventW ( + HANDLE hEventLog, + WORD wType, + WORD wCategory OPTIONAL, + DWORD dwEventID, + PSID lpUserSid OPTIONAL, + WORD wNumStrings, + DWORD dwDataSize, + LPCWSTR *lpStrings OPTIONAL, + LPVOID lpRawData OPTIONAL + ) + + + + +Author: + + Dan Lafferty (danl) 09-March-1994 + +Environment: + + User Mode - Win32 + +Revision History: + + 09-Mar-1994 danl + created + +--*/ + +// +// INCLUDES +// +#define UNICODE 1 +#include <nt.h> // DbgPrint prototype +#include <ntrtl.h> // DbgPrint prototype +#include <nturtl.h> // needed for winbase.h + + +#include <stdlib.h> // atoi +#include <stdio.h> // printf +#include <conio.h> // getch +#include <string.h> // strcmp +#include <windows.h> // win32 typedefs +#include <tstr.h> // Unicode +#include <debugfmt.h> // FORMAT_LPTSTR + +//------------------ +// DEFINES +//------------------ +#define APPLICATION_LOG "Application" +#define SYSTEM_LOG "System" +#define SECURITY_LOG "Security" + +#define REG_APPLICATION_KEY "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\" +#define REG_SYSTEM_KEY "SYSTEM\\CurrentControlSet\\Services\\EventLog\\System\\" +#define REG_SECURITY_KEY "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Security\\" + +#define EVENT_SOURCE_NAME "tevent" +#define MSG_DLL "%SystemRoot%\\System32\\tevent.dll" + +#define VALUE_EVENT_MF TEXT("EventMessageFile") +#define VALUE_CATEGORY_MF TEXT("CategoryMessageFile") +#define VALUE_PARAMETER_MF TEXT("ParameterMessageFile") +#define VALUE_TYPES_SUPPORTED TEXT("TypesSupported") +#define VALUE_CATEGORY_COUNT TEXT("CategoryCount") + +#define TYPES_SUPPORTED (EVENTLOG_ERROR_TYPE | \ + EVENTLOG_WARNING_TYPE | \ + EVENTLOG_INFORMATION_TYPE) +//---------------------- +// GLOBALS +//---------------------- + LPTSTR ApplLogRegName=TEXT(REG_APPLICATION_KEY); + LPTSTR SysLogRegName =TEXT(REG_SYSTEM_KEY); + LPTSTR SecLogRegName =TEXT(REG_SECURITY_KEY); + LPTSTR ApplLogName = TEXT(APPLICATION_LOG); + LPTSTR SysLogName = TEXT(SYSTEM_LOG); + LPTSTR SecLogName = TEXT(SECURITY_LOG); + +//---------------------- +// FUNCTION PROTOTYPES +//---------------------- + +VOID +AddRegUsage(VOID); + +DWORD +AddSourceToRegistry( + IN LPTSTR ServerName, + IN LPTSTR LogName, + IN LPTSTR EventSourceName, + IN LPTSTR *argv, + IN DWORD argc + ); + +BOOL +ConvertToUnicode( + OUT LPWSTR *UnicodeOut, + IN LPSTR AnsiIn + ); + +DWORD +DelSourceInRegistry( + IN LPTSTR ServerName, + IN LPTSTR LogName, + IN LPTSTR EventSourceName + ); + +VOID +DisplayStatus ( + IN LPTSTR ServiceName, + IN LPTSTR DisplayName, + IN LPSERVICE_STATUS ServiceStatus + ); + +BOOL +MakeArgsUnicode ( + DWORD argc, + PCHAR argv[] + ); + +BOOL +ProcessArgs ( + LPTSTR ServerName, + DWORD argc, + LPTSTR argv[] + ); + +VOID +Usage( + VOID); + +VOID +ConfigUsage(VOID); + +VOID +CreateUsage(VOID); + +VOID +QueryUsage(VOID); + +LONG +wtol( + IN LPWSTR string + ); + +VOID +UserInputLoop( + LPTSTR ServerName + ); +DWORD +ReadLogFile( + LPTSTR ServerName, + LPTSTR LogName, + IN LPTSTR *argv, + IN DWORD argc + ); +VOID +ReadLogUsage(VOID); + +VOID +DisplayRecord( + PEVENTLOGRECORD pElRecord, + BOOL PrintTheHeader + ); + +/****************************************************************************/ +VOID _CRTAPI1 +main ( + DWORD argc, + PCHAR argvAnsi[] + ) + +/*++ + +Routine Description: + + Allows manual testing of the EVENTLOG API. + + eltest + + + +Arguments: + + + +Return Value: + + + +--*/ +{ + UCHAR i; + DWORD j; + DWORD argIndex; + LPTSTR pServerName=NULL; + LPTSTR *argv; + + if (argc <2) { + Usage(); + return; + } + + // + // Make the arguments unicode if necessary. + // +#ifdef UNICODE + if (!MakeArgsUnicode(argc, argvAnsi)) { + return; + } +#endif + + argv = (LPTSTR *)argvAnsi; + + argIndex = 1; + if (STRNCMP (argv[1], TEXT("\\\\"), 2) == 0) { + pServerName = argv[1]; + argIndex = 2; // skip over servername. + } + + // + // Check to see if we are to run in Loop Mode, or in single function + // mode. In Loop Mode, we go into a loop, and ask the user for + // input until the user decides to quit. + // + // Process Arguments: + // + // INDEX 0 1 2 3 + // EL <ServerName> <Function> <FunctionOptions...> + // + + if (STRICMP (argv[argIndex], TEXT("Loop")) == 0) { + UserInputLoop(pServerName); + } + else { + ProcessArgs(pServerName, argc-argIndex, &(argv[argIndex])); + } + + +#ifdef UNICODE + // + // Free up the unicode strings if there are any + // + for(j=0; j<argc; j++) { + LocalFree(argv[j]); + } +#endif + + return; +} + +VOID +UserInputLoop( + LPTSTR ServerName + ) + +/*++ + +Routine Description: + + This function sits in a loop, gathering input from the user, and + processing that input until the user indicates that it should stop. + The following user commands indicate that we should stop: + done + exit + stop + quit + +Arguments: + + +Return Value: + + +--*/ +{ + UCHAR i; + DWORD j; + LPTSTR *argv; + UCHAR buffer[255]; + LPSTR argvA[20]; + DWORD argc=0; + BOOL KeepGoing; + + do { + //------------------------------ + // Get input from the user + //------------------------------ + buffer[0] = 90-2; + + printf("\nwaiting for instructions... \n"); + cgets(buffer); + + if (buffer[1] > 0) { + //-------------------------------------- + // put the string in argv/argc format. + //-------------------------------------- + buffer[1]+=2; // make this an end offset + argc=0; + for (i=2,j=0; i<buffer[1]; i++,j++) { + argc++; + argvA[j] = &(buffer[i]); + while ((buffer[i] != ' ') && (buffer[i] != '\0')) { + i++; + } + buffer[i] = '\0'; + } + + //------------------------------------------ + // Make the arguments unicode if necessary. + //------------------------------------------ +#ifdef UNICODE + + if (!MakeArgsUnicode(argc, argvA)) { + return; + } + +#endif + //----------------------------------------------- + // If the first argument doesn't indicate that + // we should stop, then process the arguments. + //----------------------------------------------- + argv = (LPTSTR *)argvA; + + if((STRICMP (argv[0], TEXT("done")) == 0) || + (STRICMP (argv[0], TEXT("stop")) == 0) || + (STRICMP (argv[0], TEXT("exit")) == 0) || + (STRICMP (argv[0], TEXT("quit")) == 0)) { + KeepGoing = FALSE; + } + else { + KeepGoing = ProcessArgs(ServerName, argc, argv); + } + +#ifdef UNICODE + //----------------------------------------------- + // Free up the unicode strings if there are any + //----------------------------------------------- + for(j=0; j<argc; j++) { + LocalFree(argv[j]); + } +#endif + } + } while (KeepGoing); + + return; + +} + +/****************************************************************************/ +BOOL +ProcessArgs ( + LPTSTR ServerName, + DWORD argc, + LPTSTR argv[] + ) + +/*++ + +Routine Description: + + +Arguments: + + + +Return Value: + + + +--*/ + +{ + DWORD status; + DWORD specialFlag = FALSE; + DWORD argIndex; // index to unchecked portion of arglist. + + + argIndex = 0; + + // + // If we are adding a registry entry, the get a handle to it. + // Otherwise, get a handle to the LogFile. + // + //----------------------- + // AddSourceToRegistry + //----------------------- + if (STRICMP (argv[argIndex], TEXT("AddReg")) == 0 ) { + + // + // Must have at least "AddReg logname EntryName" + // + if (argc < (argIndex + 3)) { + AddRegUsage(); + goto CleanExit; + } + + status = AddSourceToRegistry( + ServerName, + argv[argIndex+1], // LogName + argv[argIndex+2], // SourceName + &argv[argIndex+1], + argc-(argIndex+2) + ); + } + //----------------------- + // DeleteFromRegistry + //----------------------- + else if (STRICMP (argv[argIndex], TEXT("DelReg")) == 0) { + // + // Must have at least "DelReg logname EntryName" + // + if (argc < (argIndex + 3)) { + goto CleanExit; + } + + status = DelSourceInRegistry( + ServerName, + argv[argIndex+1], // LogName + argv[argIndex+2] // SourceName + ); + + } + //----------------------- + // WriteEvent + //----------------------- + else if (STRICMP (argv[argIndex], TEXT("WriteEvent")) == 0) { + printf("In WriteEvent\n"); + if (ServerName != NULL) { + printf("ServerName = "FORMAT_LPTSTR"\n",ServerName); + } + } + //----------------------- + // ReadLog + //----------------------- + else if (STRICMP (argv[argIndex], TEXT("ReadLog")) == 0) { + printf("In ReadLog\n"); + if (ServerName != NULL) { + printf("ServerName = "FORMAT_LPTSTR"\n",ServerName); + } + // + // Must have at least "ReadLog logname" + // + if (argc < (argIndex + 2)) { + ReadLogUsage(); + goto CleanExit; + } + + status = ReadLogFile( + ServerName, // ServerName + argv[argIndex+1], // LogName + &argv[argIndex+1], // argv + argc-(argIndex+1)); // argc + } + //----------------------- + // GetNumEvents + //----------------------- + else if (STRICMP (argv[argIndex], TEXT("GetNumEvents")) == 0) { + printf("in GetNumEvents\n"); + if (ServerName != NULL) { + printf("ServerName = "FORMAT_LPTSTR"\n",ServerName); + } + } + //----------------------- + // GetOldest + //----------------------- + else if (STRICMP (argv[argIndex], TEXT("GetOldest")) == 0) { + printf("in GetOldest\n"); + if (ServerName != NULL) { + printf("ServerName = "FORMAT_LPTSTR"\n",ServerName); + } + } + //----------------------- + // ClearLog + //----------------------- + else if (STRICMP (argv[argIndex], TEXT("ClearLog")) == 0) { + printf("in ClearLog\n"); + if (ServerName != NULL) { + printf("ServerName = "FORMAT_LPTSTR"\n",ServerName); + } + } + //----------------------- + // Backup + //----------------------- + else if (STRICMP (argv[argIndex], TEXT("Backup")) == 0) { + printf("in Backup\n"); + if (ServerName != NULL) { + printf("ServerName = "FORMAT_LPTSTR"\n",ServerName); + } + } + //----------------------- + // RegisterSource + //----------------------- + else if (STRICMP (argv[argIndex], TEXT("RegisterSource")) == 0) { + printf("in RegisterSource\n"); + if (ServerName != NULL) { + printf("ServerName = "FORMAT_LPTSTR"\n",ServerName); + } + } + //----------------------- + // DeRegisterSource + //----------------------- + else if (STRICMP (argv[argIndex], TEXT("DeRegisterSource")) == 0) { + printf("in DeRegisterSource\n"); + if (ServerName != NULL) { + printf("ServerName = "FORMAT_LPTSTR"\n",ServerName); + } + } + //**************** + // Exit Program + //**************** + else if (STRICMP (argv[0], TEXT("Exit")) == 0) { + // + // THIS SHOULD CLOSE HANDLES. + // + return(FALSE); + } + else { + printf("Bad argument\n"); + Usage(); + } + +CleanExit: + + + return(TRUE); +} + + + +BOOL +MakeArgsUnicode ( + DWORD argc, + PCHAR argv[] + ) + + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +Note: + + +--*/ +{ + DWORD i; + + // + // ScConvertToUnicode allocates storage for each string. + // We will rely on process termination to free the memory. + // + for(i=0; i<argc; i++) { + + if(!ConvertToUnicode( (LPWSTR *)&(argv[i]), argv[i])) { + printf("Couldn't convert argv[%d] to unicode\n",i); + return(FALSE); + } + + + } + return(TRUE); +} + +BOOL +ConvertToUnicode( + OUT LPWSTR *UnicodeOut, + IN LPSTR AnsiIn + ) + +/*++ + +Routine Description: + + This function translates an AnsiString into a Unicode string. + A new string buffer is created by this function. If the call to + this function is successful, the caller must take responsibility for + the unicode string buffer that was allocated by this function. + The allocated buffer should be free'd with a call to LocalFree. + + NOTE: This function allocates memory for the Unicode String. + + BUGBUG: This should be changed to return either + ERROR_NOT_ENOUGH_MEMORY or ERROR_INVALID_PARAMETER + +Arguments: + + AnsiIn - This is a pointer to an ansi string that is to be converted. + + UnicodeOut - This is a pointer to a location where the pointer to the + unicode string is to be placed. + +Return Value: + + TRUE - The conversion was successful. + + FALSE - The conversion was unsuccessful. In this case a buffer for + the unicode string was not allocated. + +--*/ +{ + + NTSTATUS ntStatus; + DWORD bufSize; + UNICODE_STRING unicodeString; + ANSI_STRING ansiString; + + // + // Allocate a buffer for the unicode string. + // + + bufSize = (strlen(AnsiIn)+1) * sizeof(WCHAR); + + *UnicodeOut = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, (UINT)bufSize); + + if (*UnicodeOut == NULL) { + printf("ScConvertToUnicode:LocalAlloc Failure %ld\n",GetLastError()); + return(FALSE); + } + + // + // Initialize the string structures + // + RtlInitAnsiString( &ansiString, AnsiIn); + + unicodeString.Buffer = *UnicodeOut; + unicodeString.MaximumLength = (USHORT)bufSize; + unicodeString.Length = 0; + + // + // Call the conversion function. + // + ntStatus = RtlAnsiStringToUnicodeString ( + &unicodeString, // Destination + &ansiString, // Source + (BOOLEAN)FALSE); // Allocate the destination + + if (!NT_SUCCESS(ntStatus)) { + + printf("ScConvertToUnicode:RtlAnsiStringToUnicodeString Failure %lx\n", + ntStatus); + + return(FALSE); + } + + // + // Fill in the pointer location with the unicode string buffer pointer. + // + *UnicodeOut = unicodeString.Buffer; + + return(TRUE); + +} + +/****************************************************************************/ +VOID +DisplayStatus ( + IN LPTSTR ServiceName, + IN LPTSTR DisplayName, + IN LPSERVICE_STATUS ServiceStatus + ) + +/*++ + +Routine Description: + + Displays the service name and the service status. + + | + |SERVICE_NAME: messenger + |DISPLAY_NAME: messenger + | TYPE : WIN32 + | STATE : ACTIVE,STOPPABLE, PAUSABLE, ACCEPTS_SHUTDOWN + | EXIT_CODE : 0xC002001 + | CHECKPOINT : 0x00000001 + | WAIT_HINT : 0x00003f21 + | + +Arguments: + + ServiceName - This is a pointer to a string containing the name of + the service. + + DisplayName - This is a pointer to a string containing the display + name for the service. + + ServiceStatus - This is a pointer to a SERVICE_STATUS structure from + which information is to be displayed. + +Return Value: + + none. + +--*/ +{ + + printf("\nSERVICE_NAME: "FORMAT_LPTSTR"\n", ServiceName); + if (DisplayName != NULL) { + printf("DISPLAY_NAME: "FORMAT_LPTSTR"\n", DisplayName); + } + + printf(" TYPE : %lx ", ServiceStatus->dwServiceType); + + switch(ServiceStatus->dwServiceType){ + case SERVICE_WIN32_OWN_PROCESS: + printf("WIN32_OWN_PROCESS \n"); + break; + case SERVICE_WIN32_SHARE_PROCESS: + printf("WIN32_SHARE_PROCESS \n"); + break; + case SERVICE_WIN32: + printf("WIN32 \n"); + break; + case SERVICE_ADAPTER: + printf("ADAPTER \n"); + break; + case SERVICE_KERNEL_DRIVER: + printf("KERNEL_DRIVER \n"); + break; + case SERVICE_FILE_SYSTEM_DRIVER: + printf("FILE_SYSTEM_DRIVER \n"); + break; + case SERVICE_DRIVER: + printf("DRIVER \n"); + break; + default: + printf(" ERROR \n"); + } + + printf(" STATE : %lx ", ServiceStatus->dwCurrentState); + + switch(ServiceStatus->dwCurrentState){ + case SERVICE_STOPPED: + printf("STOPPED "); + break; + case SERVICE_START_PENDING: + printf("START_PENDING "); + break; + case SERVICE_STOP_PENDING: + printf("STOP_PENDING "); + break; + case SERVICE_RUNNING: + printf("RUNNING "); + break; + case SERVICE_CONTINUE_PENDING: + printf("CONTINUE_PENDING "); + break; + case SERVICE_PAUSE_PENDING: + printf("PAUSE_PENDING "); + break; + case SERVICE_PAUSED: + printf("PAUSED "); + break; + default: + printf(" ERROR "); + } + + // + // Print Controls Accepted Information + // + + if (ServiceStatus->dwControlsAccepted & SERVICE_ACCEPT_STOP) { + printf("\n (STOPPABLE,"); + } + else { + printf("\n (NOT_STOPPABLE,"); + } + + if (ServiceStatus->dwControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE) { + printf("PAUSABLE,"); + } + else { + printf("NOT_PAUSABLE,"); + } + + if (ServiceStatus->dwControlsAccepted & SERVICE_ACCEPT_SHUTDOWN) { + printf("ACCEPTS_SHUTDOWN)\n"); + } + else { + printf("IGNORES_SHUTDOWN)\n"); + } + + // + // Print Exit Code + // + printf(" WIN32_EXIT_CODE : %d\t(0x%lx)\n", + ServiceStatus->dwWin32ExitCode, + ServiceStatus->dwWin32ExitCode); + printf(" SERVICE_EXIT_CODE : %d\t(0x%lx)\n", + ServiceStatus->dwServiceSpecificExitCode, + ServiceStatus->dwServiceSpecificExitCode ); + + // + // Print CheckPoint & WaitHint Information + // + + printf(" CHECKPOINT : 0x%lx\n", ServiceStatus->dwCheckPoint); + printf(" WAIT_HINT : 0x%lx\n", ServiceStatus->dwWaitHint ); + + return; +} + +VOID +Usage( + VOID) +{ + printf("DESCRIPTION:\n"); + printf("\tEL is a command line program used for testing the eventlog \n"); + printf("USAGE:\n"); + printf("\tEL <ServerName> [Function] <FunctionOptions...> \n\n"); + printf("\tThe option <server> has the form \"\\\\ServerName\"\n"); + printf("\tFurther help on Functions can be obtained by typing: \"el [Function]\"\n"); + printf("\tFunctions:\n" + "\t AddReg-----------Creates a registry entry for an event source.\n" + "\t DelReg-----------Deletes a registry entry.\n" + "\t WriteEvent-------Writes an event.\n" + "\t ReadLog----------Reads from the logfile.\n" + "\t GetNumEvents-----Gets the number of events in the specified log.\n" + "\t GetOldest--------Gets the record number for the oldest record" + "\t in the log\n" + "\t ClearLog---------Clears the specified Log.\n" + "\t Backup-----------Copies the specified log to a new file.\n" + "\t RegisterSource---Registers a name for the event source.\n" + "\t The handle is stored internally.\n" + "\t DeRegisterSource-Closes handle opened with RegSource.\n" + "\t NotifyChange-----A thread is created which gets notified of EL changes.\n"); + + printf("\n"); +} + +VOID +AddRegUsage(VOID) +{ + + printf("\nAdds a subkey under one of the logfiles listed in the registry.\n"); + printf("SYNTAX: \n eltest addreg <ServerName> logfile <SubKeyName> <option1> <option2>...\n"); + printf("ADDREG OPTIONS:\n"); + printf("NOTE: The option name includes the equal sign.\n"); + + printf(" MsgFile= Name of Event Message File\n" + " CatFile= Name of Category Message File\n" + " ParamFile= Name of Parameter Message File\n" + " CatCount= Category Count\n" + " Type= <error|warning|information|AuditSuccess|AuditFailure|All>\n"); + printf("EXAMPLE:\n eltest addreg application myapp MsgFile= MyMsgs.dll" + " Type= error Type= warning\n"); + +} + +VOID +ConfigUsage(VOID) +{ + printf("Modifies a service entry in the registry and Service Database.\n"); + printf("SYNTAX: \nsc config <service> <option1> <option2>...\n"); + printf("CONFIG OPTIONS:\n"); + printf("NOTE: The option name includes the equal sign.\n" + " type= <own|share|kernel|filesys|rec|adapt|error>\n" + " start= <boot|system|auto|demand|disabled|error>\n" + " error= <normal|severe|critical|error|ignore>\n" + " binPath= <BinaryPathName>\n" + " group= <LoadOrderGroup>\n" + " tag= <yes|no>\n" + " depend= <Dependencies(space seperated)>\n" + " obj= <AccountName|ObjectName>\n" + " DisplayName= <display name>\n" + " password= <password> \n"); +} +VOID +CreateUsage(VOID) +{ + printf("Creates a service entry in the registry and Service Database.\n"); + printf("SYNTAX: \nsc create <service> <option1> <option2>...\n"); + printf("CREATE OPTIONS:\n"); + printf("NOTE: The option name includes the equal sign.\n" + " type= <own|share|kernel|filesys|rec|error>\n" + " start= <boot|system|auto|demand|disabled|error>\n" + " error= <normal|severe|critical|error|ignore>\n" + " binPath= <BinaryPathName>\n" + " group= <LoadOrderGroup>\n" + " tag= <yes|no>\n" + " depend= <Dependencies(space seperated)>\n" + " obj= <AccountName|ObjectName>\n" + " DisplayName= <display name>\n" + " password= <password> \n"); +} + +VOID +ReadLogUsage(VOID) +{ + + printf("\nReads a logfile and dumps the contents.\n"); + printf("SYNTAX: \n eltest readlog <ServerName> logfile <option1> <option2>...\n"); + printf("READLOG OPTIONS:\n"); + printf("NOTE: The option name includes the equal sign.\n"); + + printf(" ReadFlag= <fwd|back|seek|seq> (default = fwd) \n" + " RecordNum= record number where read should start (default=0)\n" + " BufSize= size of the buffer (default = 10000)\n"); + printf("EXAMPLE:\n eltest addreg application myapp MsgFile= MyMsgs.dll" + " Type= error Type= warning\n"); +} + +DWORD +AddSourceToRegistry( + IN LPTSTR ServerName, + IN LPTSTR LogName, + IN LPTSTR EventSourceName, + IN LPTSTR *argv, + IN DWORD argc + ) + +/*++ + +Routine Description: + + This function writes to the registry all the information to register + this application as an event source. + +Arguments: + + +Return Value: + + +--*/ +{ + TCHAR tempName[MAX_PATH]; + HKEY hKey; + DWORD dwStatus=NO_ERROR; + HKEY hRegistry=HKEY_LOCAL_MACHINE; + + LPTSTR EventMessageFile=NULL; + LPTSTR CategoryMessageFile=NULL; + LPTSTR ParameterMessageFile=NULL; + DWORD dwTypes=0; + DWORD dwCategoryCount=0; + DWORD i; + + // + // Look at the LogName, and generate the appropriate registry key + // path for that log. + // + if (STRICMP(LogName, ApplLogName) == 0) { + STRCPY(tempName, ApplLogRegName); + } + else if (STRICMP(LogName, SysLogName) == 0) { + STRCPY(tempName, SysLogRegName); + } + else if (STRICMP(LogName, SecLogName) == 0) { + STRCPY(tempName, SecLogRegName); + } + else { + printf("AddSourceToRegistry: Invalid LogName\n"); + return(ERROR_INVALID_PARAMETER); + } + STRCAT(tempName, EventSourceName); + + + // + // Get Variable Arguments + // + for (i=0; i<argc ;i++ ) { + if (STRICMP(argv[i], TEXT("EventMsgFile=")) == 0) { + EventMessageFile = argv[i+1]; + i++; + } + if (STRICMP(argv[i], TEXT("CategoryMsgFile=")) == 0) { + CategoryMessageFile = argv[i+1]; + i++; + } + if (STRICMP(argv[i], TEXT("ParameterMsgFile=")) == 0) { + ParameterMessageFile = argv[i+1]; + i++; + } + if (STRICMP(argv[i], TEXT("Type=")) == 0) { + //-------------------------------------------------------- + // We want to allow for several arguments of type= in the + // same line. These should cause the different arguments + // to be or'd together. + //-------------------------------------------------------- + if (STRICMP(argv[i+1],TEXT("error")) == 0) { + dwTypes |= EVENTLOG_ERROR_TYPE; + } + if (STRICMP(argv[i+1],TEXT("warning")) == 0) { + dwTypes |= EVENTLOG_WARNING_TYPE; + } + if (STRICMP(argv[i+1],TEXT("information")) == 0) { + dwTypes |= EVENTLOG_INFORMATION_TYPE; + } + if (STRICMP(argv[i+1],TEXT("AuditSuccess")) == 0) { + dwTypes |= EVENTLOG_AUDIT_SUCCESS; + } + if (STRICMP(argv[i+1],TEXT("AuditFailure")) == 0) { + dwTypes |= EVENTLOG_AUDIT_FAILURE; + } + if (STRICMP(argv[i+1],TEXT("All")) == 0) { + dwTypes |= (EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | + EVENTLOG_INFORMATION_TYPE | EVENTLOG_AUDIT_SUCCESS | + EVENTLOG_AUDIT_FAILURE); + } + else { + printf("Invalid Type\n"); + AddRegUsage(); + return(ERROR_INVALID_PARAMETER); + } + i++; + } + if (STRICMP(argv[i], TEXT("CategoryCount=")) == 0) { + dwCategoryCount = ATOL(argv[i+1]); + i++; + } + + } + + // + // Connect to the registry on the correct machine. + // + printf("Connect to Registry\n"); + dwStatus = RegConnectRegistry(ServerName, HKEY_LOCAL_MACHINE, &hRegistry); + if (dwStatus != NO_ERROR) { + printf("RegConnectRegistry Failed %d\n",GetLastError()); + return(dwStatus); + } + + // + // Create the new key for this source + // + printf("Create Key\n"); + dwStatus = RegCreateKey(hRegistry, tempName, &hKey); + if (dwStatus != ERROR_SUCCESS) { + printf("Couldn't create Source Key in registry %d\n",dwStatus); + return(dwStatus); + } + if (EventMessageFile != NULL) { + printf("Set EventMessageFile\n"); + dwStatus = RegSetValueEx( + hKey, + VALUE_EVENT_MF, + 0, + REG_EXPAND_SZ, + (LPBYTE)EventMessageFile, + STRLEN(EventMessageFile) + sizeof(TCHAR)); + + if (dwStatus != ERROR_SUCCESS) { + printf("RegSetValue (messageFile) failed %d\n",GetLastError()); + goto CleanExit; + } + } + // + // Set the Category Message File + // + if (CategoryMessageFile != NULL) { + printf("Set Category Message File\n"); + dwStatus = RegSetValueEx( + hKey, + VALUE_CATEGORY_MF, + 0, + REG_EXPAND_SZ, + (LPBYTE)CategoryMessageFile, + STRLEN(CategoryMessageFile) + sizeof(TCHAR)); + + if (dwStatus != ERROR_SUCCESS) { + printf("RegSetValue (category mf) failed %d\n",GetLastError()); + goto CleanExit; + } + } + + // + // Set the Parameter Message File + // + if (ParameterMessageFile != NULL) { + printf("Set Parameter Message File\n"); + dwStatus = RegSetValueEx( + hKey, + VALUE_PARAMETER_MF, + 0, + REG_EXPAND_SZ, + (LPBYTE)ParameterMessageFile, + STRLEN(ParameterMessageFile) + sizeof(TCHAR)); + + if (dwStatus != ERROR_SUCCESS) { + printf("RegSetValue (Parameter mf) failed %d\n",GetLastError()); + goto CleanExit; + } + } + + // + // Set the Types Supported + // + if (dwTypes != 0) { + printf("Set Types Supported\n"); + dwStatus = RegSetValueEx( + hKey, + VALUE_TYPES_SUPPORTED, + 0, + REG_DWORD, + (LPBYTE) &dwTypes, + sizeof(DWORD)); + + if (dwStatus != ERROR_SUCCESS) { + printf("RegSetValue (TypesSupported) failed %d\n",GetLastError()); + goto CleanExit; + } + + } + + // + // Set the Category Count + // + if (dwCategoryCount != 0) { + printf("Set CategoryCount\n"); + dwStatus = RegSetValueEx( + hKey, + VALUE_CATEGORY_COUNT, + 0, + REG_DWORD, + (LPBYTE) &dwCategoryCount, + sizeof(DWORD)); + + if (dwStatus != ERROR_SUCCESS) { + printf("RegSetValue (CategoryCount) failed %d\n",GetLastError()); + goto CleanExit; + } + } + dwStatus = NO_ERROR; +CleanExit: + RegCloseKey(hKey); + RegCloseKey(hRegistry); + return(dwStatus); +} + +DWORD +DelSourceInRegistry( + IN LPTSTR ServerName, + IN LPTSTR LogName, + IN LPTSTR EventSourceName + ) + +/*++ + +Routine Description: + + This function writes to the registry all the information to register + this application as an event source. + +Arguments: + + +Return Value: + + +--*/ +{ + LPTSTR tempName; + HKEY hParentKey; + BOOL status=FALSE; + DWORD dwStatus; + HKEY hRegistry=HKEY_LOCAL_MACHINE; + + + // + // Look at the LogName, and generate the appropriate registry key + // path for that log. + // + if (STRICMP(LogName, ApplLogName) == 0) { + tempName = ApplLogRegName; + } + else if (STRICMP(LogName, SysLogName) == 0) { + tempName = SysLogRegName; + } + else if (STRICMP(LogName, SecLogName) == 0) { + tempName = SecLogRegName; + } + else { + printf("AddSourceToRegistry: Invalid LogName\n"); + return(ERROR_INVALID_PARAMETER); + } + + // + // Connect to the registry on the correct machine. + // + dwStatus = RegConnectRegistry(ServerName, HKEY_LOCAL_MACHINE, &hRegistry); + if (dwStatus != NO_ERROR) { + printf("RegConnectRegistry Failed %d\n",GetLastError()); + return(status); + } + + // + // Open the Parent Key of the key we want to delete. + // + dwStatus = RegOpenKeyEx( + hRegistry, + tempName, + 0, + KEY_ALL_ACCESS, + &hParentKey); + + if (dwStatus != ERROR_SUCCESS) { + printf("Couldn't open Parent of key to be deleted. %d\n",dwStatus); + goto CleanExit; + } + // + // Delete the subkey. + // + dwStatus = RegDeleteKey(hParentKey, EventSourceName); + if (dwStatus != ERROR_SUCCESS) { + printf("Couldn't delete "FORMAT_LPTSTR" key from registry %d\n", + EventSourceName, dwStatus); + } + + RegCloseKey(hParentKey); +CleanExit: + RegCloseKey(hRegistry); + return(status); +} + +DWORD +ReadLogFile( + LPTSTR ServerName, + LPTSTR LogName, + IN LPTSTR *argv, + IN DWORD argc + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + DWORD dwReadFlag = EVENTLOG_FORWARDS_READ; + DWORD dwRecordNum = 0; + DWORD BufSize = 10000; + DWORD numBytesRead; + DWORD numBytesReqd; + LPVOID pElBuffer = NULL; + PEVENTLOGRECORD pElRecord; + BOOL PrintTheHeader; + DWORD i; + HANDLE hEventLog=NULL; + + // + // Get Variable Arguments + // + for (i=0; i<argc ;i++ ) { + if (STRICMP(argv[i], TEXT("ReadFlag=")) == 0) { + if (STRICMP(argv[i+1],TEXT("fwd")) == 0) { + dwReadFlag |= EVENTLOG_FORWARDS_READ; + } + if (STRICMP(argv[i+1],TEXT("back")) == 0) { + dwReadFlag |= EVENTLOG_BACKWARDS_READ; + } + if (STRICMP(argv[i+1],TEXT("seek")) == 0) { + dwReadFlag |= EVENTLOG_SEEK_READ; + } + if (STRICMP(argv[i+1],TEXT("seq")) == 0) { + dwReadFlag |= EVENTLOG_SEQUENTIAL_READ; + } + i++; + } + if (STRICMP(argv[i], TEXT("RecordNum=")) == 0) { + dwRecordNum = ATOL(argv[i+1]); + i++; + } + if (STRICMP(argv[i], TEXT("BufSize=")) == 0) { + BufSize = ATOL(argv[i+1]); + i++; + } + hEventLog = OpenEventLog(ServerName,LogName); + if (hEventLog == NULL) { + printf("OpenEventLog failed %d\n",GetLastError()); + return(0); + } + pElBuffer = LocalAlloc(LPTR, BufSize); + if (pElBuffer == NULL) { + printf("ReadLogFile: LocalAlloc Failed %d\n",GetLastError()); + goto CleanExit; + } + + //--------------------------------------------------------- + // Read and Display the contents of the eventlog + //--------------------------------------------------------- + PrintTheHeader = TRUE; +TryAgain: + while(ReadEventLog( + hEventLog, + dwReadFlag, + dwRecordNum, + pElBuffer, + BufSize, + &numBytesRead, + &numBytesReqd)) { + + pElRecord = (PEVENTLOGRECORD) pElBuffer; + while ((PBYTE) pElRecord < (PBYTE) pElBuffer + numBytesRead) { + // + // Print the record to the display + // + DisplayRecord(pElRecord,PrintTheHeader); + PrintTheHeader = FALSE; + // + // Move to the next event in the buffer + // + pElRecord = (PEVENTLOGRECORD)((PBYTE) pElRecord + + pElRecord->Length); + } + } + switch(GetLastError()) { + case ERROR_INSUFFICIENT_BUFFER: + // + // Increase the size of the buffer and try again + // + if (numBytesReqd > BufSize) { + LocalFree(pElBuffer); + BufSize = numBytesReqd; + pElBuffer = LocalAlloc(LPTR, BufSize); + if (!pElBuffer) { + printf("ReadLogFile: LocalAlloc Failed %d\n",GetLastError()); + } + goto TryAgain; + } + else { + printf("ReadLogFile #1: THIS SHOULD NEVER HAPPEN\n"); + } + break; + case ERROR_EVENTLOG_FILE_CHANGED: + // + // The current read position for this handle has been overwritten. + // Reopen the file and print a message to the effect that some + // records may have been missed. + // + printf("ReadLogFile: Current Read position has been overwritten\n"); + + hEventLog = OpenEventLog(ServerName,LogName); + if (hEventLog == NULL) { + printf("OpenEventLog failed %d\n",GetLastError()); + goto CleanExit; + } + goto TryAgain; + case ERROR_HANDLE_EOF: + printf("EOF\n"); + break; + default: + printf("UnknownError: %d\n",GetLastError()); + break; + } + } +CleanExit: + if (pElBuffer != NULL) { + LocalFree(pElBuffer); + } + if (hEventLog != NULL) { + CloseEventLog(hEventLog); + } + return(0); +} + +VOID +DisplayRecord( + PEVENTLOGRECORD pElRecord, + BOOL PrintTheHeader + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + if (PrintTheHeader) { + printf("RecNum/tTimeGen/tWriteTime/tEventID/tType/tNumStr/tCat/n"); + } + printf("%d/t%d/t%d/t%d/t%d/t%d/t%d\n", + pElRecord->RecordNumber, + pElRecord->TimeGenerated, + pElRecord->TimeWritten, + pElRecord->EventID, + pElRecord->EventType, + pElRecord->NumStrings, + pElRecord->EventCategory); +} + +LONG +wtol( + IN LPWSTR string + ) +{ + LONG value = 0; + + while((*string != L'\0') && + (*string >= L'0') && + ( *string <= L'9')) { + value = value * 10 + (*string - L'0'); + string++; + } + + return(value); +} + diff --git a/private/eventlog/eltest/makefile b/private/eventlog/eltest/makefile new file mode 100644 index 000000000..f0db8e4a7 --- /dev/null +++ b/private/eventlog/eltest/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS LINE!!! 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/eltest/sources b/private/eventlog/eltest/sources new file mode 100644 index 000000000..00246b41a --- /dev/null +++ b/private/eventlog/eltest/sources @@ -0,0 +1,45 @@ +!IF 0 + +Copyright (c) 1989-92 Microsoft Corporation + +Module Name: + + sources for ELTEST.EXE + +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: + + Dan Lafferty (danl) 5-May-1991 + + +Revision History: + +!ENDIF + +MAJORCOMP = private +MINORCOMP = eventlog +TARGETNAME= eltest + + +TARGETPATH=obj +TARGETTYPE=PROGRAM + +TARGETLIBS= \ + $(BASEDIR)\public\sdk\lib\*\user32.lib \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\ntdll.lib \ + +INCLUDES=.;..\..\inc + + +SOURCES= eltest.c + +UMTYPE=console + diff --git a/private/eventlog/event.h b/private/eventlog/event.h new file mode 100644 index 000000000..b735df5ca --- /dev/null +++ b/private/eventlog/event.h @@ -0,0 +1,59 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + EVENT.H + +Abstract: + + Contains the common data structures that should be put in one + place in the tree. + +Author: + + Rajen Shah (rajens) 21-Aug-1991 + +Revision History: + +--*/ + +// +// Switch to using the commonly defined (in ntdef.h) UNICODE_STRING +// Do a munge to fix this if it works ok +// + +typedef UNICODE_STRING RPC_UNICODE_STRING, *PRPC_UNICODE_STRING; + + +// +// RPC definition of the SID structure. Note the use of the [size_is()] +// qualifier to specify the number of elements in the variable size +// imbedded SubAuthorityCount array at runtime. +// +// BUGBUG - This is stolen from samrpc.idl, and should really be placed +// in a common place for all to use. +// +typedef struct _RPC_SID { + UCHAR Revision; + UCHAR SubAuthorityCount; + SID_IDENTIFIER_AUTHORITY IdentifierAuthority; + [size_is(SubAuthorityCount)] ULONG SubAuthority[*]; +} RPC_SID, *PRPC_SID, **PPRPC_SID; + +// +// ANSI counted string +// + +typedef struct _RPC_STRING { + USHORT Length; + USHORT MaximumLength; +// [size_is(MaximumLength+0), length_is(Length+1)] PCHAR Buffer; + [size_is(MaximumLength)] PCHAR Buffer; +} RPC_STRING, *PRPC_STRING, RPC_ANSI_STRING, *PRPC_ANSI_STRING; + +typedef struct _RPC_CLIENT_ID { + ULONG UniqueProcess; + ULONG UniqueThread; +} RPC_CLIENT_ID, *PRPC_CLIENT_ID; diff --git a/private/eventlog/imports.h b/private/eventlog/imports.h new file mode 100644 index 000000000..e09e76be2 --- /dev/null +++ b/private/eventlog/imports.h @@ -0,0 +1,2 @@ +#include <nt.h> +#include <windef.h> diff --git a/private/eventlog/imports.idl b/private/eventlog/imports.idl new file mode 100644 index 000000000..b7ed62591 --- /dev/null +++ b/private/eventlog/imports.idl @@ -0,0 +1,60 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + imports.idl + +Abstract: + + This file is useful for creating RPC interfaces that require the use + of types defined in other header files. The .idl file for the RPC + product should contain a line in the interface body that imports this + file. For example: + + import "imports.idl"; + + Doing this causes the MIDL generated header file to contain the + #include lines that are in this file. + + If this technique is not used, and instead the .idl file for the RPC + product simply contains #include <windef.h>, then the contents of + windef.h will be expanded in the MIDL generated header file. This + can lead to duplicate definition problems later when the RPC client + or RPC server code needs to include both the MIDL generated header file + and a file that is included in windef.h. + +Author: + + Dan Lafferty (danl) 20-Mar-1991 + +Environment: + + User Mode - Win32 - for use with the MIDL compiler + + +Revision History: + + 03-Apr-1991 danl + created + +--*/ + + +[ + uuid(12345678-1234-ABCD-EF00-9948756789AB), +#ifdef __midl + ms_union, +#endif // __midl + version(0.0) +] +interface eventlog_imports + +{ +#define MIDL_PASS +#define UNICODE + +#include "imports.h" + +} diff --git a/private/eventlog/makefil0 b/private/eventlog/makefil0 new file mode 100644 index 000000000..d9488c71e --- /dev/null +++ b/private/eventlog/makefil0 @@ -0,0 +1,48 @@ +# +# This is the MIDL compile phase of the build process. +# +# The following is where you put the name of your .idl file without +# the .idl extension: +# + +!INCLUDE $(NTMAKEENV)\makefile.plt + +ELFIDL=elf + +CLIENT_FILES=elfclnt\$(ELFIDL)_c.c $(ELFIDL).h +SERVER_FILES=server\$(ELFIDL)_s.c +LOCAL_FILES=$(ELFIDL)_c.c $(ELFIDL)_s.c + +INCS= -I$(BASEDIR)\public\sdk\inc -I$(BASEDIR)\public\sdk\inc\crt + +ELFMIDLARGS=-oldnames -error allocation -error ref -ms_ext -c_ext \ + -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) $(INCS) + +# +# Define Products and Dependencies +# + +all: $(CLIENT_FILES) $(SERVER_FILES) del_elf_sources +!IF "$(BUILDMSG)" != "" + @ech ; $(BUILDMSG) ; +!ENDIF + +clean: delete_source all + +delete_source: + -erase $(CLIENT_FILES) $(SERVER_FILES) $(LOCAL_FILES) + +# +# MIDL COMPILE +# + +$(CLIENT_FILES): $(ELFIDL).idl + midl -Oi $(ELFMIDLARGS) $(ELFIDL).idl + copy $(ELFIDL)_c.c elfclnt\$(ELFIDL)_c.c + +$(SERVER_FILES): $(ELFIDL).idl + midl -client none $(ELFMIDLARGS) $(ELFIDL).idl + copy $(ELFIDL)_s.c server\$(ELFIDL)_s.c + +del_elf_sources: + -del $(LOCAL_FILES) 1> NUL 2>NUL diff --git a/private/eventlog/misc/dirs b/private/eventlog/misc/dirs new file mode 100644 index 000000000..076ccc941 --- /dev/null +++ b/private/eventlog/misc/dirs @@ -0,0 +1,4 @@ + +DIRS= + +OPTIONAL_DIRS= diff --git a/private/eventlog/misc/logalert.c b/private/eventlog/misc/logalert.c new file mode 100644 index 000000000..2b5e5f760 --- /dev/null +++ b/private/eventlog/misc/logalert.c @@ -0,0 +1,204 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + LOGALERT.C + +Abstract: + + This file contains the routine to log a Cairo alert in the system log. + + +Author: + + Ravi Rudrappa (ravir) 18-Jan-1995 + + +Revision History: + + 18-Jan-1995 RaviR + Created + +--*/ + +#ifdef _CAIRO_ + +#include <elfclntp.h> + + +NTSTATUS +ElfLogCairoAlertInSystemLog( + IN HANDLE hLogHandle, + IN LARGE_INTEGER liEventTime, + IN USHORT usEventType, + IN USHORT usEventCategory, + IN ULONG ulEventID, + IN USHORT usNumStrings, + IN ULONG ulDataSize, + IN WCHAR *pwszComputerName, + IN WCHAR **ppwszStrings, + IN PBYTE pbData + ) +/*++ + +Routine Description: + + This routine logs the given Cairo Alert in the given log, through the + ElfrReportEventW function. + +Arguments: + + +Return Value: + + Returns an NTSTATUS code. + +Note: + + +--*/ +{ + NTSTATUS s = STATUS_SUCCESS; + ULONG ulEventTime; + PRPC_SID psidUser = NULL; + USHORT usFlags; + PUNICODE_STRING pusComputerName = NULL; + PUNICODE_STRING *ppusStrings = NULL; + ULONG i; + ULONG ulNumofAllocatedStrings = 0; + LPBYTE pbString = NULL; + + // + // parameter validation + // + + if ((hLogHandle == NULL) || + (pwszComputerName == NULL) || + ((usNumStrings > 0) && (ppwszStrings == NULL)) || + ((ulDataSize > 0) && (pbData == NULL))) + { + return STATUS_INVALID_PARAMETER; + } + + // + // user Sid + // + + psidUser = NULL; // BUGBUG: is this ok? + + // + // Map creation time + // + + RtlTimeToSecondsSince1970(&liEventTime, &ulEventTime); + + // + // Convert the array of Alert description insert strings + // to an array of UNICODE_STRINGs. + // + + if (usNumStrings > 0) + { + LPBYTE pbPtr; + // + // allocate memory in one shot. + // + + pbPtr = pbString = MIDL_user_allocate( + sizeof(PUNICODE_STRING) * usNumStrings + + sizeof(UNICODE_STRING) * usNumStrings); + + if (pbString == NULL) + { + s = STATUS_NO_MEMORY; + goto LCleanUp; + } + + ppusStrings = (PUNICODE_STRING *)pbPtr; + + pbPtr += sizeof(PUNICODE_STRING) * usNumStrings; + + // + // For each string passed in, allocate a UNICODE_STRING structure + // and set it to the matching string. + // + for (i = 0; i < usNumStrings; i++) + { + ppusStrings[i] = (PUNICODE_STRING)pbPtr; + + pbPtr += sizeof(UNICODE_STRING); + + RtlInitUnicodeString(ppusStrings[i], ppwszStrings[i]); + } + } + + // + // Map the ComputerName to UNICODE_STRING. + // + + pusComputerName = MIDL_user_allocate(sizeof(UNICODE_STRING)); + + if (pusComputerName == NULL) + { + s = STATUS_NO_MEMORY; + goto LCleanUp; + } + + RtlInitUnicodeString(pusComputerName, pwszComputerName); + + + // + // Do the RPC call with an exception handler since RPC will raise an + // exception if anything fails. It is up to us to figure out what + // to do once the exception is raised. + // + RpcTryExcept { + + // Call service + + s = ElfrReportEventW ( + (IELF_HANDLE)hLogHandle, + ulEventTime, + usEventType, + usEventCategory, + ulEventID, + usNumStrings, + ulDataSize, + (PRPC_UNICODE_STRING)pusComputerName, + psidUser, + (PRPC_UNICODE_STRING *)ppusStrings, + pbData, + 0, // Flags, + NULL, // RecordNumber, + NULL); // TimeWritten + + } + RpcExcept (1) { + + s = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept + +LCleanUp: + + // + // Free the space allocated for the inserts + // and then free the space for ComputerName. + // + if (pbString) + { + MIDL_user_free(pbString); + } + + if (pusComputerName) + { + MIDL_user_free(pusComputerName); + } + + return (s); + +} + +#endif // _CAIRO_ diff --git a/private/eventlog/netevent/dummy.c b/private/eventlog/netevent/dummy.c new file mode 100644 index 000000000..537f11ef3 --- /dev/null +++ b/private/eventlog/netevent/dummy.c @@ -0,0 +1,4 @@ +void +DummyEntryPoint(void) +{ +} diff --git a/private/eventlog/netevent/event.rc b/private/eventlog/netevent/event.rc new file mode 100644 index 000000000..658dbbd75 --- /dev/null +++ b/private/eventlog/netevent/event.rc @@ -0,0 +1,11 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Net Event Handler" +#define VER_INTERNALNAME_STR "NetEvent.Dll" +#define VER_ORIGINALFILENAME_STR "NetEvent.Dll" + +#include "common.ver" +#include "netevent.rc" diff --git a/private/eventlog/netevent/makefile b/private/eventlog/netevent/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/eventlog/netevent/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/netevent/makefile.inc b/private/eventlog/netevent/makefile.inc new file mode 100644 index 000000000..387f6df16 --- /dev/null +++ b/private/eventlog/netevent/makefile.inc @@ -0,0 +1 @@ +event.rc: netevent.rc MSG00001.bin diff --git a/private/eventlog/netevent/netevent.def b/private/eventlog/netevent/netevent.def new file mode 100644 index 000000000..5f3a626e8 --- /dev/null +++ b/private/eventlog/netevent/netevent.def @@ -0,0 +1,7 @@ +LIBRARY NETEVENT + +DESCRIPTION 'Network event messages' + +EXPORTS + + DummyEntryPoint diff --git a/private/eventlog/netevent/sources b/private/eventlog/netevent/sources new file mode 100644 index 000000000..76dbce398 --- /dev/null +++ b/private/eventlog/netevent/sources @@ -0,0 +1,20 @@ +MAJORCOMP=evenlog +MINORCOMP=netevent + +TARGETNAME=netevent +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=DYNLINK + +INCLUDES=. + +!IFNDEF DISABLE_NET_UNICODE +UNICODE=1 +NET_C_DEFINES=-DUNICODE +!ENDIF + +SOURCES=dummy.c \ + event.rc + +UMRES=obj\*\event.res + +NTTARGETFILE0=event.rc diff --git a/private/eventlog/ntsdexts/elfexts.c b/private/eventlog/ntsdexts/elfexts.c new file mode 100644 index 000000000..69fb5f25c --- /dev/null +++ b/private/eventlog/ntsdexts/elfexts.c @@ -0,0 +1,767 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + elfexts.c + +Abstract: + + This function contains the eventlog ntsd debugger extensions + +Author: + + Dan Hinsley (DanHi) 22-May-1993 + +Revision History: + +--*/ + +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <windows.h> +#include <ntsdexts.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <malloc.h> +#include <time.h> +#include <elf.h> +#include <elfdef.h> +#include <elfcommn.h> +#include <elfproto.h> +#include <svcs.h> +#include <elfextrn.h> + +//#define DbgPrint(_x_) (lpOutputRoutine) _x_ +#define DbgPrint(_x_) +#define MAX_NAME 256 +#define printf (lpOutputRoutine) +#define GET_DATA(DebugeeAddr, LocalAddr, Length) \ + Status = ReadProcessMemory( \ + GlobalhCurrentProcess, \ + (LPVOID)DebugeeAddr, \ + LocalAddr, \ + Length, \ + NULL \ + ); + +PNTSD_OUTPUT_ROUTINE lpOutputRoutine; +PNTSD_GET_EXPRESSION lpGetExpressionRoutine; +PNTSD_CHECK_CONTROL_C lpCheckControlCRoutine; + +HANDLE GlobalhCurrentProcess; +BOOL Status; + +// +// Initialize the global function pointers +// + +VOID +InitFunctionPointers( + HANDLE hCurrentProcess, + PNTSD_EXTENSION_APIS lpExtensionApis + ) +{ + // + // Load these to speed access if we haven't already + // + + if (!lpOutputRoutine) { + lpOutputRoutine = lpExtensionApis->lpOutputRoutine; + lpGetExpressionRoutine = lpExtensionApis->lpGetExpressionRoutine; + lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine; + } + + // + // Stick this in a global + // + + GlobalhCurrentProcess = hCurrentProcess; +} + +LPWSTR +GetUnicodeString( + PUNICODE_STRING pUnicodeString + ) +{ + DWORD Pointer; + UNICODE_STRING UnicodeString; + + GET_DATA(pUnicodeString, &UnicodeString, sizeof(UNICODE_STRING)) + Pointer = (DWORD) UnicodeString.Buffer; + UnicodeString.Buffer = (LPWSTR) LocalAlloc(LMEM_ZEROINIT, + UnicodeString.Length + sizeof(WCHAR)); + GET_DATA(Pointer, UnicodeString.Buffer, UnicodeString.Length) + + return(UnicodeString.Buffer); +} + +DWORD +GetLogFileAddress( + LPSTR LogFileName, + PLOGFILE LogFile + ) +{ + ANSI_STRING AnsiString; + UNICODE_STRING UnicodeString; + DWORD Pointer; + DWORD LogFileAnchor; + LPWSTR ModuleName; + + // + // Convert the string to UNICODE + // + + RtlInitAnsiString(&AnsiString, LogFileName); + RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE); + + // + // Walk the logfile list looking for a match + // + + LogFileAnchor = (lpGetExpressionRoutine)("LogFilesHead"); + + GET_DATA(LogFileAnchor, &Pointer, sizeof(DWORD)) + + while (Pointer != LogFileAnchor) { + GET_DATA(Pointer, LogFile, sizeof(LOGFILE)) + ModuleName = GetUnicodeString(LogFile->LogModuleName); + if (!wcsicmp(ModuleName, UnicodeString.Buffer)) { + break; + } + LocalFree(ModuleName); + Pointer = (DWORD) LogFile->FileList.Flink; + } + + RtlFreeUnicodeString(&UnicodeString); + + if (Pointer == LogFileAnchor) { + return(0); + } + else { + LocalFree(ModuleName); + return(Pointer); + } +} + +// +// Dump an individual record +// + +DWORD +DumpRecord( + DWORD Record, + DWORD RecordNumber, + DWORD StartOfFile, + DWORD EndOfFile + ) +{ + DWORD BufferLen; + PCHAR TimeBuffer; + PEVENTLOGRECORD EventLogRecord; + LPWSTR Module; + LPWSTR Computer; + DWORD FirstPiece = 0; + + GET_DATA(Record, &BufferLen, sizeof(DWORD)) + + // + // See if it's a ELF_SKIP_DWORD, and if it is, return the top of the + // file + // + + if (BufferLen == ELF_SKIP_DWORD) { + return(StartOfFile + sizeof(ELF_LOGFILE_HEADER)); + } + + // + // See if it's the EOF record + // + + if (BufferLen == ELFEOFRECORDSIZE) { + return(0); + } + + BufferLen += sizeof(DWORD); // get room for length of next record + EventLogRecord = (PEVENTLOGRECORD) LocalAlloc(LMEM_ZEROINIT, BufferLen); + + // + // If the record wraps, grab it piecemeal + // + + if (EndOfFile && BufferLen + Record > EndOfFile) { + FirstPiece = EndOfFile - Record; + GET_DATA(Record, EventLogRecord, FirstPiece); + GET_DATA((StartOfFile + sizeof(ELF_LOGFILE_HEADER)), + ((PBYTE) EventLogRecord + FirstPiece), BufferLen - FirstPiece) + } + else { + GET_DATA(Record, EventLogRecord, BufferLen) + } + + // + // If it's greater than the starting record, print it out + // + + if (EventLogRecord->RecordNumber >= RecordNumber) { + printf("\nRecord %d is %d [0x%X] bytes long starting at 0x%X\n", + EventLogRecord->RecordNumber, EventLogRecord->Length, + EventLogRecord->Length, Record); + Module = (LPWSTR)(EventLogRecord+1); + Computer = (LPWSTR)((PBYTE) Module + ((wcslen(Module) + 1) * sizeof(WCHAR))); + printf("\tGenerated by %ws from system %ws\n", Module, Computer); + + TimeBuffer = ctime((time_t *)&(EventLogRecord->TimeGenerated)); + if (TimeBuffer) { + printf("\tGenerated at %s", TimeBuffer); + } + else { + printf("\tGenerated time field is blank\n"); + } + TimeBuffer = ctime((time_t *)&(EventLogRecord->TimeWritten)); + if (TimeBuffer) { + printf("\tWritten at %s", TimeBuffer); + } + else { + printf("\tTime written field is blank\n"); + } + + printf("\tEvent Id = %d\n", EventLogRecord->EventID); + printf("\tEventType = "); + switch (EventLogRecord->EventType) { + case EVENTLOG_SUCCESS: + printf("Success\n"); + break; + case EVENTLOG_ERROR_TYPE: + printf("Error\n"); + break; + case EVENTLOG_WARNING_TYPE: + printf("Warning\n"); + break; + case EVENTLOG_INFORMATION_TYPE: + printf("Information\n"); + break; + case EVENTLOG_AUDIT_SUCCESS: + printf("Audit Success\n"); + break; + case EVENTLOG_AUDIT_FAILURE: + printf("Audit Failure\n"); + break; + default: + printf("Invalid value 0x%X\n", EventLogRecord->EventType); + } + printf("\t%d strings at offset 0x%X\n", EventLogRecord->NumStrings, + EventLogRecord->StringOffset); + printf("\t%d bytes of data at offset 0x%X\n", EventLogRecord->DataLength, + EventLogRecord->DataOffset); + } + + if (FirstPiece) { + Record = StartOfFile + sizeof(ELF_LOGFILE_HEADER) + BufferLen - + FirstPiece; + } + else { + Record += EventLogRecord->Length; + } + + LocalFree(EventLogRecord); + return(Record); +} + +// +// Dump a record, or all records, or n records +// + +VOID +record( + HANDLE hCurrentProcess, + HANDLE hCurrentThread, + DWORD dwCurrentPc, + PNTSD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString + ) +{ + DWORD Pointer; + LOGFILE LogFile; + DWORD StartOfFile; + DWORD EndOfFile = 0; + DWORD RecordNumber = 0; + + InitFunctionPointers(hCurrentProcess, lpExtensionApis); + + // + // Evaluate the argument string to get the address of + // the record to dump. + // + + if (lpArgumentString && *lpArgumentString) { + if (*lpArgumentString == '.') { + if (GetLogFileAddress(lpArgumentString+1, &LogFile) == 0) { + printf("Logfile %s not found\n", lpArgumentString+1); + return; + } + Pointer = ((DWORD) (LogFile.BaseAddress)) + LogFile.BeginRecord; + } + else if (*lpArgumentString == '#') { + RecordNumber = atoi(lpArgumentString + 1); + printf("Dumping records starting at record #%d\n", RecordNumber); + lpArgumentString = NULL; + } + else if (*lpArgumentString) { + Pointer = (lpGetExpressionRoutine)(lpArgumentString); + } + else { + printf("Invalid lead character 0x%02X\n", *lpArgumentString); + return; + } + } + + if (!lpArgumentString || *lpArgumentString) { + if (GetLogFileAddress("system", &LogFile) == 0) { + printf("System Logfile not found\n"); + return; + } + Pointer = ((DWORD) (LogFile.BaseAddress)) + LogFile.BeginRecord; + } + + StartOfFile = (DWORD) LogFile.BaseAddress; + EndOfFile = (DWORD) LogFile.BaseAddress + LogFile.ActualMaxFileSize; + + // + // Dump records starting wherever they told us to + // + + while (Pointer < EndOfFile && Pointer && !(lpCheckControlCRoutine)()) { + Pointer = DumpRecord(Pointer, RecordNumber, StartOfFile, EndOfFile); + } + + + return; +} + +// +// Dump a single LogModule structure if it matches MatchName (NULL matches +// all) +// + +PLIST_ENTRY +DumpLogModule( + HANDLE hCurrentProcess, + DWORD pLogModule, + LPWSTR MatchName + ) +{ + LOGMODULE LogModule; + WCHAR ModuleName[MAX_NAME / sizeof(WCHAR)]; + + GET_DATA(pLogModule, &LogModule, sizeof(LogModule)) + GET_DATA(LogModule.ModuleName, &ModuleName, MAX_NAME) + + if (!MatchName || !wcsicmp(MatchName, ModuleName)) { + printf("\tModule Name %ws\n", ModuleName); + printf("\tModule Atom 0x%X\n", LogModule.ModuleAtom); + printf("\tPointer to LogFile 0x%X\n", LogModule.LogFile); + } + + return (LogModule.ModuleList.Flink); +} + +// +// Dump selected, or all, LogModule structures +// + +VOID +logmodule( + HANDLE hCurrentProcess, + HANDLE hCurrentThread, + DWORD dwCurrentPc, + PNTSD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString + ) +{ + DWORD pLogModule; + DWORD LogModuleAnchor; + LPWSTR wArgumentString = NULL; + ANSI_STRING AnsiString; + UNICODE_STRING UnicodeString; + + InitFunctionPointers(hCurrentProcess, lpExtensionApis); + UnicodeString.Buffer = NULL; + + // + // Evaluate the argument string to get the address of + // the logmodule to dump. If no parm, dump them all. + // + + if (lpArgumentString && *lpArgumentString == '.') { + lpArgumentString++; + RtlInitAnsiString(&AnsiString, lpArgumentString); + RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE); + } + else if (lpArgumentString && *lpArgumentString) { + pLogModule = (lpGetExpressionRoutine)(lpArgumentString); + DumpLogModule(hCurrentProcess, pLogModule, NULL); + return; + } + + LogModuleAnchor = (lpGetExpressionRoutine)("LogModuleHead"); + + GET_DATA(LogModuleAnchor, &pLogModule, sizeof(DWORD)) + + while (pLogModule != LogModuleAnchor && !(lpCheckControlCRoutine)()) { + pLogModule = + (DWORD) DumpLogModule(hCurrentProcess, pLogModule, + UnicodeString.Buffer); + if (!UnicodeString.Buffer) { + printf("\n"); + } + } + if (UnicodeString.Buffer) { + RtlFreeUnicodeString(&UnicodeString); + } + + return; +} + +// +// Dump a single LogFile structure if it matches MatchName (NULL matches +// all) +// + +PLIST_ENTRY +DumpLogFile( + HANDLE hCurrentProcess, + DWORD pLogFile, + LPWSTR MatchName + ) +{ + LOGFILE LogFile; + LPWSTR UnicodeName; + + // + // Get the fixed part of the structure + // + + GET_DATA(pLogFile, &LogFile, sizeof(LogFile)) + + // + // Get the Default module name + // + + UnicodeName = GetUnicodeString(LogFile.LogModuleName); + + // + // See if we're just looking for a particular one. If we are and + // this isn't it, bail out. + // + + if (MatchName && wcsicmp(MatchName, UnicodeName)) { + LocalFree(UnicodeName); + return (LogFile.FileList.Flink); + } + + // + // Otherwise print it out + // + + printf("%ws", UnicodeName); + LocalFree(UnicodeName); + + // + // Now the file name of this logfile + // + + UnicodeName = GetUnicodeString(LogFile.LogFileName); + printf(" : %ws\n", UnicodeName); + LocalFree(UnicodeName); + + if (LogFile.Notifiees.Flink == LogFile.Notifiees.Blink) { + printf("\tNo active ChangeNotifies on this log\n"); + } + else { + printf("\tActive Change Notify! Dump of this list not implemented\n"); + } + + printf("\tReference Count: %d\n\tFlags: ", LogFile.RefCount); + if (LogFile.Flags == 0) { + printf("No flags set "); + } + else { + if (LogFile.Flags & ELF_LOGFILE_HEADER_DIRTY) { + printf("Dirty "); + } + if (LogFile.Flags & ELF_LOGFILE_HEADER_WRAP) { + printf("Wrapped "); + } + if (LogFile.Flags & ELF_LOGFILE_LOGFULL_WRITTEN) { + printf("Logfull Written "); + } + } + printf("\n"); + + printf("\tMax Files Sizes [Cfg:Curr:Next] 0x%X : 0x%X : 0x%X\n", + LogFile.ConfigMaxFileSize, LogFile.ActualMaxFileSize, + LogFile.NextClearMaxFileSize); + + printf("\tRecord Numbers [Oldest:Curr] %d : %d\n", + LogFile.OldestRecordNumber, LogFile.CurrentRecordNumber); + + printf("\tRetention period in days: %d\n", LogFile.Retention / 86400); + + printf("\tBase Address: 0x%X\n", LogFile.BaseAddress); + + printf("\tView size: 0x%X\n", LogFile.ViewSize); + + printf("\tOffset of beginning record: 0x%X\n", LogFile.BeginRecord); + + printf("\tOffset of ending record: 0x%X\n", LogFile.EndRecord); + + return (LogFile.FileList.Flink); +} + +// +// Dump selected, or all, LogFile structures +// + +VOID +logfile( + HANDLE hCurrentProcess, + HANDLE hCurrentThread, + DWORD dwCurrentPc, + PNTSD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString + ) +{ + DWORD pLogFile; + DWORD LogFileAnchor; + LPWSTR wArgumentString = NULL; + ANSI_STRING AnsiString; + UNICODE_STRING UnicodeString; + BOOL AllocateString = FALSE; + + InitFunctionPointers(hCurrentProcess, lpExtensionApis); + UnicodeString.Buffer = NULL; + + // + // Evaluate the argument string to get the address of + // the logfile to dump. If no parm, dump them all. + // + + if (lpArgumentString && *lpArgumentString) { + if(*lpArgumentString == '.') { + lpArgumentString++; + RtlInitAnsiString(&AnsiString, lpArgumentString); + RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE); + } + else { + pLogFile = (lpGetExpressionRoutine)(lpArgumentString); + DumpLogFile(hCurrentProcess, pLogFile, NULL); + return; + } + } + + LogFileAnchor = (lpGetExpressionRoutine)("LogFilesHead"); + + GET_DATA(LogFileAnchor, &pLogFile, sizeof(DWORD)) + + while (pLogFile != LogFileAnchor) { + pLogFile = + (DWORD) DumpLogFile(hCurrentProcess, pLogFile, + UnicodeString.Buffer); + if (!UnicodeString.Buffer) { + printf("\n"); + } + } + + if (UnicodeString.Buffer) { + RtlFreeUnicodeString(&UnicodeString); + } + + return; +} + +// +// Dump a request packet structure +// + +VOID +request( + HANDLE hCurrentProcess, + HANDLE hCurrentThread, + DWORD dwCurrentPc, + PNTSD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString + ) +{ + ELF_REQUEST_RECORD Request; + DWORD Pointer; + DWORD RecordSize; + WRITE_PKT WritePkt; + READ_PKT ReadPkt; + CLEAR_PKT ClearPkt; + BACKUP_PKT BackupPkt; + LPWSTR FileName; + CHAR Address[18]; + + InitFunctionPointers(hCurrentProcess, lpExtensionApis); + + // + // Evaluate the argument string to get the address of + // the request packet to dump. + // + + if (lpArgumentString && *lpArgumentString) { + Pointer = (lpGetExpressionRoutine)(lpArgumentString); + } + else { + printf("Must supply a request packet address\n"); + return; + } + + GET_DATA(Pointer, &Request, sizeof(ELF_REQUEST_RECORD)) + + switch (Request.Command ) { + case ELF_COMMAND_READ: + printf("\nRead packet\n"); + GET_DATA(Request.Pkt.ReadPkt, &ReadPkt, sizeof(READ_PKT)) + printf("\tLast Seek Position = %d\n", ReadPkt.LastSeekPos); + printf("\tLast Seek Record = %d\n", ReadPkt.LastSeekRecord); + printf("\tStart at record number %d\n", ReadPkt.RecordNumber); + printf("\tRead %d bytes into buffer at 0x%X\n", + ReadPkt.BufferSize, ReadPkt.Buffer); + if (ReadPkt.Flags & ELF_IREAD_UNICODE) { + printf("\tReturn in ANSI\n"); + } + else { + printf("\tReturn in UNICODE\n"); + } + printf("\tRead flags: "); + if (ReadPkt.ReadFlags & EVENTLOG_SEQUENTIAL_READ) { + printf("Sequential "); + } + if (ReadPkt.ReadFlags & EVENTLOG_SEEK_READ) { + printf("Seek "); + } + if (ReadPkt.ReadFlags & EVENTLOG_FORWARDS_READ) { + printf("Forward "); + } + if (ReadPkt.ReadFlags & EVENTLOG_BACKWARDS_READ) { + printf("Backwards "); + } + printf("\n"); + break; + + case ELF_COMMAND_WRITE: + printf("\nWrite packet\n"); + if (Request.Flags == ELF_FORCE_OVERWRITE) { + printf("with ELF_FORCE_OVERWRITE enabled\n"); + } + else { + printf("\n"); + } + GET_DATA(Request.Pkt.WritePkt, &WritePkt, sizeof(WRITE_PKT)) + RecordSize = (WritePkt.Datasize); + DumpRecord((DWORD)WritePkt.Buffer, 0, 0, 0); + break; + + case ELF_COMMAND_CLEAR: + printf("\nClear packet\n"); + GET_DATA(Request.Pkt.ClearPkt, &ClearPkt, sizeof(CLEAR_PKT)) + FileName = GetUnicodeString(ClearPkt.BackupFileName); + printf("Backup filename = %ws\n", FileName); + LocalFree(FileName); + break; + + case ELF_COMMAND_BACKUP: + printf("\nBackup packet\n"); + GET_DATA(Request.Pkt.BackupPkt, &BackupPkt, sizeof(BACKUP_PKT)) + FileName = GetUnicodeString(BackupPkt.BackupFileName); + printf("Backup filename = %ws\n", FileName); + LocalFree(FileName); + break; + + case ELF_COMMAND_WRITE_QUEUED: + printf("\nQueued Write packet\n"); + if (Request.Flags == ELF_FORCE_OVERWRITE) { + printf("with ELF_FORCE_OVERWRITE enabled\n"); + } + else { + printf("\n"); + } + printf("NtStatus = 0x%X\n", Request.Status); + break; + + default: + printf("\nInvalid packet\n"); + } + + printf("\nLogFile for this packet:\n\n"); + itoa((DWORD) Request.LogFile, Address, 16); + logfile(hCurrentProcess, hCurrentThread, dwCurrentPc, lpExtensionApis, + Address); + + printf("\nLogModule for this packet:\n\n"); + itoa((DWORD)Request.Module, Address, 16); + logmodule(hCurrentProcess, hCurrentThread, dwCurrentPc, lpExtensionApis, + Address); + + return; +} + +// +// Online help +// + +VOID +help( + HANDLE hCurrentProcess, + HANDLE hCurrentThread, + DWORD dwCurrentPc, + PNTSD_EXTENSION_APIS lpExtensionApis, + LPSTR lpArgumentString + ) +{ + InitFunctionPointers(hCurrentProcess, lpExtensionApis); + + printf("\nEventlog NTSD Extensions\n"); + + if (!lpArgumentString || *lpArgumentString == '\0' || + *lpArgumentString == '\n' || *lpArgumentString == '\r') { + printf("\tlogmodule - dump a logmodule structure\n"); + printf("\tlogfile - dump a logfile structure\n"); + printf("\trequest - dump a request record\n"); + printf("\trecord - dump a eventlog record\n"); + printf("\n\tEnter help <cmd> for detailed help on a command\n"); + } + else { + if (!stricmp(lpArgumentString, "logmodule")) { + printf("\tlogmodule <arg>, where <arg> can be one of:\n"); + printf("\t\tno argument - dump all logmodule structures\n"); + printf("\t\taddress - dump the logmodule at specified address\n"); + printf("\t\t.string - dump the logmodule with name string\n"); + } + else if (!stricmp(lpArgumentString, "logfile")) { + printf("\tlogfile <arg>, where <arg> can be one of:\n"); + printf("\t\tno argument - dump all logfile structures\n"); + printf("\t\taddress - dump the logfile at specified address\n"); + printf("\t\t.string - dump the logfile with name string\n"); + } + else if (!stricmp(lpArgumentString, "record")) { + printf("\trecord <arg>, where <arg> can be one of:\n"); + printf("\t\tno argument - dump all records in system log\n"); + printf("\t\taddress - dump records starting at specified address\n"); + printf("\t\t.string - dump all records in the <string> log\n"); + printf("\t\t#<nnn> - dumps records starting at nnn in system log\n"); + printf("\t\t#<nnn> .string - dumps records starting at nnn in <string> log\n"); + } + else if (!stricmp(lpArgumentString, "request")) { + printf("\trequest - dump the request record at specified address\n"); + } + else { + printf("\tInvalid command [%s]\n", lpArgumentString); + } + } +} diff --git a/private/eventlog/ntsdexts/elfexts.def b/private/eventlog/ntsdexts/elfexts.def new file mode 100644 index 000000000..bfe2048d3 --- /dev/null +++ b/private/eventlog/ntsdexts/elfexts.def @@ -0,0 +1,10 @@ +LIBRARY NTSDEXTS + +DESCRIPTION 'Eventlog Default NTSD Extensions' + +EXPORTS + help + logmodule + logfile + request + record diff --git a/private/eventlog/ntsdexts/elfexts.rc b/private/eventlog/ntsdexts/elfexts.rc new file mode 100644 index 000000000..e351edd4b --- /dev/null +++ b/private/eventlog/ntsdexts/elfexts.rc @@ -0,0 +1,9 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Eventlog Symbolic Debugger Extensions" +#define VER_INTERNALNAME_STR "elfexts\0" + +#include "common.ver" diff --git a/private/eventlog/ntsdexts/makefile b/private/eventlog/ntsdexts/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/eventlog/ntsdexts/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/ntsdexts/sources b/private/eventlog/ntsdexts/sources new file mode 100644 index 000000000..34f6a132d --- /dev/null +++ b/private/eventlog/ntsdexts/sources @@ -0,0 +1,40 @@ +!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: + + Dan Hinsley (DanHi) 22-Mar-1993 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=eventlog +MINORCOMP=ntsdexts + +TARGETNAME=elfexts +TARGETPATH=obj +TARGETTYPE=DYNLINK +TARGETLIBS=\ + $(BASEDIR)\public\sdk\lib\*\kernel32.lib \ + $(BASEDIR)\public\sdk\lib\*\libc.lib + +INCLUDES=..;..\server;..\..\inc; +SOURCES=elfexts.c \ + elfexts.rc + +UMTYPE=console +DLLBASE=0x66100000 diff --git a/private/eventlog/readme.txt b/private/eventlog/readme.txt new file mode 100644 index 000000000..34c9b1d22 --- /dev/null +++ b/private/eventlog/readme.txt @@ -0,0 +1,103 @@ +This is a description of the contents of each file in the eventlog +project. +Ignore the test and enumsvc directories. They contain test programs. + + +eventlog +-------- +dirs The directories to build. + +elf.acf The ACF file for RPC. + +elf.h Generated by MIDL using elf.idl. + +elf.idl The IDL descriptions of the APIs and the data structures. + +elfcommn.h Common defines/declarations used by the server and the client + sides. + +event.h Data structures that should be placed in a common location in the + nt source tree. + +imports.h +imports.idl Import files used by MIDL. + +makefil0 Makefile to make the RPC client and server side files. + + +elfclnt +------- + +apistub.c The stubs for the ELF APIs. + +elf_c.c Generated by MIDL. +elf_x.c + +elfapi.def The APIs that are exported in the DLL. + +elfclntp.h Private header file. + +rpcbind.c Bind routines that are called by the RPC run times. + +makefile +sources + + +server +------ + +config.c Routines that deal with the registry and configuration for the + service. Contains the main registry monitor thread. + +control.c Routines that deal with control calls from the service controller. + +elf_s.c Generated by MIDL. +elf_y.c + +elfapi.c The server side of the APIs. + +elfcfg.h Header file for configuration related data. + +elfdata.c Global data. + +elfdebug.c DEBUG routines. These should go away in the final version. + +elfdef.h Data structure definitions and #defines used in the service. + +elfextrn.h Extern declarations of the global data. + +elflpc.c Routines that deal with the LPC port and with fielding packets + from the device driver. + +elflpc.h Header file for elflpc.c. + +elfproto.h Procedure prototypes. + +elfrpc.c RPC-related routines to clean up the context handle. + +elfutil.c Various low-level utility routines. + +eventlog.c Main entry point for service. All the initialization code. + +eventp.h Private header file for server end. + +file.c Routines to manage the log files. + +memory.c Alloc/Free. + +operate.c All the low-level routines to perform the API requests. + +terminat.c Routines for terminating the service, and cleaning up. + +makefile +sources + + +winbase - these changes will go into \nt\private\windows\base\client +------- + +base.def Define the Win32 event logging APIs. + +eventapi.c Entry points for the APIs. + +sources 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; +} diff --git a/private/eventlog/test/buildreg.dat b/private/eventlog/test/buildreg.dat new file mode 100644 index 000000000..9a74e65e5 --- /dev/null +++ b/private/eventlog/test/buildreg.dat @@ -0,0 +1,3 @@ +System,\systemroot\sysevent.log,4096,10 +Application,\systemroot\appevent.log,4096,10 +Security,\systemroot\audit.log,4096,10 diff --git a/private/eventlog/test/makefile b/private/eventlog/test/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/eventlog/test/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/test/sources b/private/eventlog/test/sources new file mode 100644 index 000000000..226e46d04 --- /dev/null +++ b/private/eventlog/test/sources @@ -0,0 +1,89 @@ +!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 + +# +# The TARGETNAME variable is defined by the developer. It is the name of +# the target (component) that is being built by this makefile. It +# should NOT include any path or file extension information. +# + +MAJORCOMP = eventlog +MINORCOMP = test +TARGETNAME= elftest + +# +# The TARGETPATH and TARGETTYPE varialbes are defined by the developer. +# The first specifies where the target is to be build. The second specifies +# the type of target (either PROGRAM, DYNLINK or LIBRARY) +# + +TARGETPATH=obj + +TARGETTYPE=PROGRAM + +TARGETLIBS= \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib + + +# +# The INCLUDES variable specifies any include paths that are specific to +# this source directory. Separate multiple directory paths with single +# semicolons. Relative path specifications are okay. +# + +INCLUDES=.;..;$(BASEDIR)\public\sdk\inc;$(BASEDIR)\private\net\inc + +# +# The SOURCES variable is defined by the developer. It is a list of all the +# source files for this component. Each source file should be on a separate +# line using the line continuation character. This will minimize merge +# conflicts if two developers adding source files to the same component. +# + +SOURCES= \ + test.c + + +C_DEFINES=-DNTOS2=1 + +UMTYPE=console + +# The following builds the server executable file. The client is built +# in the client directory. + +UMTYPE=console +UMTEST=testwin*testwina +UMLIBS=$(BASEDIR)\public\sdk\lib\*\kernel32.lib +OPTIONAL_UMTEST=wintest + +# +# Defining the NTTARGETFILES variable causes MAKEFILE.DEF to attempt to +# include .\makefile.inc immediately after it specifies the top +# level targets (all, clean and loc) and their dependencies. MAKEFILE.DEF +# also expands the value of the NTTARGETFILES variable at the end of the +# list of dependencies for the all target. Useful for specifying additional +# targets and dependencies that don't fit the general case covered by +# MAKEFILE.DEF +# diff --git a/private/eventlog/test/test.c b/private/eventlog/test/test.c new file mode 100644 index 000000000..bb996bf8d --- /dev/null +++ b/private/eventlog/test/test.c @@ -0,0 +1,1400 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + TEST.C + +Abstract: + + Test program for the eventlog service. This program calls the Elf + APIs to test out the operation of the service. + +Author: + + Rajen Shah (rajens) 05-Aug-1991 + +Revision History: + + +--*/ +/*----------------------*/ +/* INCLUDES */ +/*----------------------*/ +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <stdio.h> // printf +#include <string.h> // stricmp +#include <stdlib.h> +#include <process.h> // exit +#include <elfcommn.h> +#include <windows.h> +#include <ntiolog.h> +#include <malloc.h> + +#define READ_BUFFER_SIZE 1024*2 // Use 2K buffer + +#define SIZE_DATA_ARRAY 22 + +#define SET_OPERATION(x) \ + if (Operation != Invalid) { \ + printf("Only one operation at a time\n"); \ + Usage(); \ + } \ + Operation = x; + +// +// Global buffer used to emulate "binary data" when writing an event +// record. +// +ULONG Data[SIZE_DATA_ARRAY]; +enum _OPERATION_TYPE { + Invalid, + Clear, + Backup, + Read, + Write, + Notify, + LPC +} Operation = Invalid; +ULONG ReadFlags; +BOOL Verbose = FALSE; +ULONG NumberofRecords = 1; +ULONG DelayInMilliseconds = 0; +CHAR DefaultModuleName[] = "TESTAPP"; +PCHAR pModuleName = DefaultModuleName; +PCHAR pBackupFileName; +ANSI_STRING AnsiString; +UNICODE_STRING ServerName; +BOOL ReadingBackupFile = FALSE; +BOOL ReadingModule = FALSE; +BOOL WriteInvalidRecords = FALSE; +BOOL InvalidUser = FALSE; + +// Function prototypes + +VOID ParseParms(ULONG argc, PCHAR *argv); + +VOID +Initialize ( + VOID + ) +{ + ULONG i; + + // Initialize the values in the data buffer. + // + for (i=0; i< SIZE_DATA_ARRAY; i++) + Data[i] = i; + +} + + +VOID +Usage ( + VOID + ) +{ + printf( "usage: \n" ); + printf( "-c Clears the specified log\n"); + printf( "-b <filename> Backs up the log to file <filename>\n"); + printf( "-f <filename> Filename of backup log to use for read\n"); + printf( "-i Generate invalid SID\n"); + printf( "-l[i] nn Writes nn records thru LPC port [i ==> bad records]\n"); + printf( "-m <modulename> Module name to use for read/clear\n"); + printf( "-n Test out change notify\n"); + printf( "-rsb Reads nn event log records sequentially backwards\n"); + printf( "-rsf nn Reads nn event log records sequentially forwards\n"); + printf( "-rrb <record> Reads event log from <record> backwards\n"); + printf( "-rrf <record> Reads event log from <record> forwards\n"); + printf( "-s <servername> Name of server to remote calls to\n"); + printf( "-t nn Number of milliseconds to delay between read/write" + " (default 0)\n\tOnly used with -l switch\n"); + printf( "-w <count> Writes <count> records\n"); + exit(0); + +} // Usage + + +NTSTATUS +WriteLogEntry ( + HANDLE LogHandle, + ULONG EventID + ) +{ +#define NUM_STRINGS 2 +#define SIZE_TOKEN_BUFFER 512 + + NTSTATUS Status; + USHORT EventType, i; + ULONG DataSize; + PSID pUserSid; + PWSTR Strings[NUM_STRINGS] = {L"StringOne", L"StringTwo"}; + PUNICODE_STRING UStrings[NUM_STRINGS]; + HANDLE hProcess; + HANDLE hToken; + PTOKEN_USER pTokenUser; + DWORD SizeRequired; + + EventType = EVENTLOG_INFORMATION_TYPE; + DataSize = sizeof(ULONG) * SIZE_DATA_ARRAY; + + // + // Get the SID of the current user (process) + // + + pTokenUser = malloc(SIZE_TOKEN_BUFFER); + + if (!InvalidUser) { + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, + GetCurrentProcessId()); + if (!hProcess) { + printf("Couldn't open the process, rc = %d\n", GetLastError()); + return(STATUS_UNSUCCESSFUL); + } + + if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) { + printf("Couldn't open the token, rc = %d\n", GetLastError()); + CloseHandle(hProcess); + return(STATUS_UNSUCCESSFUL); + } + if (!pTokenUser) { + printf("Couldn't allocate buffer for TokenUser\n"); + CloseHandle(hToken); + CloseHandle(hProcess); + return(STATUS_UNSUCCESSFUL); + } + + if (!GetTokenInformation(hToken, TokenUser, pTokenUser, SIZE_TOKEN_BUFFER, + &SizeRequired)) { + printf("Couldn't get TokenUser information, rc = %d\n", + GetLastError()); + CloseHandle(hToken); + CloseHandle(hProcess); + free(pTokenUser); + return(STATUS_UNSUCCESSFUL); + } + + CloseHandle(hToken); + CloseHandle(hProcess); + } + else { + memset(pTokenUser, 0xFADE, SIZE_TOKEN_BUFFER); + pTokenUser->User.Sid = (PSID)pUserSid; + } + + pUserSid = pTokenUser->User.Sid; + + for (i=0; i< SIZE_DATA_ARRAY; i++) + Data[i] += i; + + // Allocate space for the unicode strings in the array, and + // copy over the strings from Strings[] to that array. + // + for (i=0; i<NUM_STRINGS; i++) { + + UStrings[i] = malloc(sizeof(UNICODE_STRING)); + RtlInitUnicodeString (UStrings[i], Strings[i]); + // force this to be non-null terminated + UStrings[i]->MaximumLength = UStrings[i]->Length; + } + Status = ElfReportEventW ( + LogHandle, + EventType, + 0, // category + EventID, + pUserSid, + NUM_STRINGS, + DataSize, + UStrings, + (PVOID)Data, + 0, // Flags - paired event support + NULL, // RecordNumber | not in product 1 + NULL // TimeWritten - + ); + + for (i=0; i<NUM_STRINGS; i++) + free(UStrings[i]); + + free(pTokenUser); + return (Status); +} + + +VOID +DisplayEventRecords( PVOID Buffer, + ULONG BufSize, + PULONG NumRecords) + +{ + PEVENTLOGRECORD pLogRecord; + LPWSTR pwString; + ULONG Count = 0; + ULONG Offset = 0; + ULONG i; + + pLogRecord = (PEVENTLOGRECORD) Buffer; + + while (Offset < BufSize && Count < *NumRecords) { + + printf("\nRecord # %lu\n", pLogRecord->RecordNumber); + + printf("Length: 0x%lx TimeGenerated: 0x%lx EventID: 0x%lx EventType: 0x%x\n", + pLogRecord->Length, pLogRecord->TimeGenerated, pLogRecord->EventID, + pLogRecord->EventType); + + printf("NumStrings: 0x%x StringOffset: 0x%lx UserSidLength: 0x%lx TimeWritten: 0x%lx\n", + pLogRecord->NumStrings, pLogRecord->StringOffset, + pLogRecord->UserSidLength, pLogRecord->TimeWritten); + + printf("UserSidOffset: 0x%lx DataLength: 0x%lx DataOffset: 0x%lx Category: 0x%lx\n", + pLogRecord->UserSidOffset, pLogRecord->DataLength, + pLogRecord->DataOffset, pLogRecord->EventCategory); + + // + // Print out module name + // + + pwString = (PWSTR)((LPBYTE) pLogRecord + sizeof(EVENTLOGRECORD)); + printf("ModuleName: %ws\n", pwString); + + // + // Display ComputerName + // + pwString += wcslen(pwString) + 1; + printf("ComputerName: %ws\n", pwString); + + // + // Display strings + // + + pwString = (PWSTR)((LPBYTE)pLogRecord + pLogRecord->StringOffset); + + printf("Strings: "); + for (i=0; i<pLogRecord->NumStrings; i++) { + + printf(" %ws ", pwString); + pwString += wcslen(pwString) + 1; + } + + printf("\n"); + + // + // If verbose mode, display binary data (up to 256 bytes) + // BUGBUG - this code will hit an alignment fault on mips. + // + + if (Verbose) { + PULONG pData; + PULONG pEnd; + + if (pLogRecord->DataLength < 80) { + pEnd = (PULONG)((PBYTE) pLogRecord + pLogRecord->DataOffset + + pLogRecord->DataLength); + } + else { + pEnd = (PULONG)((PBYTE) pLogRecord + pLogRecord->DataOffset + + 256); + } + + printf("Data: \n\n"); + for (pData = (PULONG)((PBYTE) pLogRecord + pLogRecord->DataOffset); + pData < pEnd; (PBYTE) pData += 32) { + + printf("\t%08x %08x %08x %08x\n", pData[0], pData[1], pData[2], + pData[3]); + } + } + + // Get next record + // + Offset += pLogRecord->Length; + + pLogRecord = (PEVENTLOGRECORD)((ULONG)Buffer + Offset); + + Count++; + + } + + *NumRecords = Count; + +} + + +NTSTATUS +ReadFromLog ( HANDLE LogHandle, + PVOID Buffer, + PULONG pBytesRead, + ULONG ReadFlag, + ULONG Record + ) +{ + NTSTATUS Status; + ULONG MinBytesNeeded; + + Status = ElfReadEventLogW ( + LogHandle, + ReadFlag, + Record, + Buffer, + READ_BUFFER_SIZE, + pBytesRead, + &MinBytesNeeded + ); + + + if (Status == STATUS_BUFFER_TOO_SMALL) + printf("Buffer too small. Need %lu bytes min\n", MinBytesNeeded); + + return (Status); +} + + +NTSTATUS +TestReadEventLog ( + ULONG Count, + ULONG ReadFlag, + ULONG Record + ) + +{ + NTSTATUS Status, IStatus; + + HANDLE LogHandle; + UNICODE_STRING ModuleNameU; + ANSI_STRING ModuleNameA; + ULONG NumRecords, BytesReturned; + PVOID Buffer; + ULONG RecordOffset; + ULONG NumberOfRecords; + ULONG OldestRecord; + + printf("Testing ElfReadEventLog API to read %lu entries\n",Count); + + Buffer = malloc (READ_BUFFER_SIZE); + + // + // Initialize the strings + // + NumRecords = Count; + RtlInitAnsiString(&ModuleNameA, pModuleName); + RtlAnsiStringToUnicodeString(&ModuleNameU, &ModuleNameA, TRUE); + ModuleNameU.MaximumLength = ModuleNameU.Length; + + // + // Open the log handle + // + + if (ReadingBackupFile) { + printf("ElfOpenBackupEventLog - "); + Status = ElfOpenBackupEventLogW ( + &ServerName, + &ModuleNameU, + &LogHandle + ); + } + else { + printf("ElfOpenEventLog - "); + Status = ElfOpenEventLogW ( + &ServerName, + &ModuleNameU, + &LogHandle + ); + } + + if (!NT_SUCCESS(Status)) { + printf("Error - 0x%lx\n", Status); + + } else { + printf("SUCCESS\n"); + + // + // Get and print record information + // + + Status = ElfNumberOfRecords(LogHandle, & NumberOfRecords); + if (NT_SUCCESS(Status)) { + Status = ElfOldestRecord(LogHandle, & OldestRecord); + } + + if (!NT_SUCCESS(Status)) { + printf("Query of record information failed with %X", Status); + return(Status); + } + + printf("\nThere are %d records in the file, %d is the oldest" + " record number\n", NumberOfRecords, OldestRecord); + + RecordOffset = Record; + + while (Count && NT_SUCCESS(Status)) { + + printf("Read %u records\n", NumRecords); + // + // Read from the log + // + Status = ReadFromLog ( LogHandle, + Buffer, + &BytesReturned, + ReadFlag, + RecordOffset + ); + if (NT_SUCCESS(Status)) { + + printf("Bytes read = 0x%lx\n", BytesReturned); + NumRecords = Count; + DisplayEventRecords(Buffer, BytesReturned, &NumRecords); + Count -= NumRecords; + } + + } + printf("\n"); + + if (!NT_SUCCESS(Status)) { + if (Status == STATUS_END_OF_FILE) { + printf("Tried to read more records than in log file\n"); + } + else { + printf ("Error - 0x%lx. Remaining count %lu\n", Status, Count); + } + } else { + printf ("SUCCESS\n"); + } + + printf("Calling ElfCloseEventLog\n"); + IStatus = ElfCloseEventLog (LogHandle); + } + + return (Status); +} + + +NTSTATUS +TestReportEvent ( + ULONG Count + ) + +{ + NTSTATUS Status, IStatus; + HANDLE LogHandle; + UNICODE_STRING ModuleNameU; + ANSI_STRING ModuleNameA; + ULONG EventID = 99; + + printf("Testing ElfReportEvent API\n"); + + // + // Initialize the strings + // + + RtlInitAnsiString(&ModuleNameA, pModuleName); + RtlAnsiStringToUnicodeString(&ModuleNameU, &ModuleNameA, TRUE); + ModuleNameU.MaximumLength = ModuleNameU.Length; + + // + // Open the log handle + // + printf("Calling ElfRegisterEventSource for WRITE %lu times - ", Count); + Status = ElfRegisterEventSourceW ( + &ServerName, + &ModuleNameU, + &LogHandle + ); + + if (!NT_SUCCESS(Status)) { + printf("Error - 0x%lx\n", Status); + + } else { + printf("SUCCESS\n"); + + while (Count && NT_SUCCESS(Status)) { + + printf("Record # %u \n", Count); + + // + // Write an entry into the log + // + Data[0] = Count; // Make data "unique" + EventID = (EventID + Count) % 100; // Vary the eventids + Status = WriteLogEntry ( LogHandle, EventID ); + Count--; + } + printf("\n"); + + if (!NT_SUCCESS(Status)) { + if (Status == STATUS_LOG_FILE_FULL) { + printf("Log Full\n"); + } + else { + printf ("Error - 0x%lx. Remaining count %lu\n", Status, Count); + } + } else { + printf ("SUCCESS\n"); + } + + printf("Calling ElfDeregisterEventSource\n"); + IStatus = ElfDeregisterEventSource (LogHandle); + } + + return (Status); +} + + +NTSTATUS +TestElfClearLogFile( + VOID + ) + +{ + NTSTATUS Status, IStatus; + HANDLE LogHandle; + UNICODE_STRING BackupU, ModuleNameU; + ANSI_STRING ModuleNameA; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE ClearHandle; + FILE_DISPOSITION_INFORMATION DeleteInfo = {TRUE}; + IO_STATUS_BLOCK IoStatusBlock; + BOOLEAN DontRetry = FALSE; + + printf("Testing ElfClearLogFile API\n"); + // + // Initialize the strings + // + RtlInitAnsiString( &ModuleNameA, pModuleName); + RtlAnsiStringToUnicodeString(&ModuleNameU, &ModuleNameA, TRUE); + ModuleNameU.MaximumLength = ModuleNameU.Length ; + + // + // Open the log handle + // + printf("Calling ElfOpenEventLog for CLEAR - "); + Status = ElfOpenEventLogW ( + &ServerName, + &ModuleNameU, + &LogHandle + ); + + if (!NT_SUCCESS(Status)) { + printf("Error - 0x%lx\n", Status); + + } else { + printf("SUCCESS\n"); + + // + // Clear the log file and back it up to "view.evt" + // + + RtlInitUnicodeString( &BackupU, + L"\\SystemRoot\\System32\\Config\\view.evt" ); + BackupU.MaximumLength = BackupU.Length; +retry: + printf("Calling ElfClearEventLogFile backing up to view.evt "); + Status = ElfClearEventLogFileW ( + LogHandle, + &BackupU + ); + + if (Status == STATUS_OBJECT_NAME_COLLISION) { + if (DontRetry) { + printf("Still can't backup to View.Evt\n"); + } + else { + printf("Failed.\nView.Evt already exists, deleting ...\n"); + + // + // Open the file with delete access + // + + InitializeObjectAttributes( + &ObjectAttributes, + &BackupU, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + Status = NtOpenFile(&ClearHandle, + GENERIC_READ | DELETE | SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_DELETE, + FILE_SYNCHRONOUS_IO_NONALERT + ); + + Status = NtSetInformationFile( + ClearHandle, + &IoStatusBlock, + &DeleteInfo, + sizeof(DeleteInfo), + FileDispositionInformation + ); + + if (NT_SUCCESS (Status) ) { + Status = NtClose (ClearHandle); // Discard status + goto retry; + } + + printf("Delete failed 0x%lx\n",Status); + Status = NtClose (ClearHandle); // Discard status + goto JustClear; + } + } + + if (!NT_SUCCESS(Status)) { + printf ("Error - 0x%lx\n", Status); + } else { + printf ("SUCCESS\n"); + } + +JustClear: + + // + // Now just clear the file without backing it up + // + printf("Calling ElfClearEventLogFile with no backup "); + Status = ElfClearEventLogFileW ( + LogHandle, + NULL + ); + + if (!NT_SUCCESS(Status)) { + printf ("Error - 0x%lx\n", Status); + } else { + printf ("SUCCESS\n"); + } + + printf("Calling ElfCloseEventLog\n"); + IStatus = ElfCloseEventLog (LogHandle); + } + + return(Status); +} + + +NTSTATUS +TestElfBackupLogFile( + VOID + ) + +{ + NTSTATUS Status, IStatus; + HANDLE LogHandle; + UNICODE_STRING BackupU, ModuleNameU; + ANSI_STRING AnsiString; + + printf("Testing ElfBackupLogFile API\n"); + + // + // Initialize the strings + // + + RtlInitAnsiString( &AnsiString, pModuleName); + RtlAnsiStringToUnicodeString(&ModuleNameU, &AnsiString, TRUE); + ModuleNameU.MaximumLength = ModuleNameU.Length ; + + // + // Open the log handle + // + + printf("Calling ElfOpenEventLog for BACKUP - "); + Status = ElfOpenEventLogW ( + &ServerName, + &ModuleNameU, + &LogHandle + ); + + if (!NT_SUCCESS(Status)) { + printf("Error - 0x%lx\n", Status); + + } else { + printf("SUCCESS\n"); + + // + // Backup the log file + // + + printf("Calling ElfBackupEventLogFile backing up to %s ", + pBackupFileName); + + RtlInitAnsiString( &AnsiString, pBackupFileName); + RtlAnsiStringToUnicodeString(&BackupU, &AnsiString, TRUE); + BackupU.MaximumLength = BackupU.Length; + + Status = ElfBackupEventLogFileW ( + LogHandle, + &BackupU + ); + + if (!NT_SUCCESS(Status)) { + printf ("Error - 0x%lx\n", Status); + } else { + printf ("SUCCESS\n"); + } + + + printf("Calling ElfCloseEventLog - "); + IStatus = ElfCloseEventLog (LogHandle); + if (NT_SUCCESS(IStatus)) { + printf("Success\n"); + } + else { + printf("Failed with code %X\n", IStatus); + } + } + + return(Status); +} + +#define DRIVER_NAME L"FLOPPY" +#define DEVICE_NAME L"A:" +#define STRING L"Test String" + +// These include the NULL terminator, but is length in chars, not bytes +#define DRIVER_NAME_LENGTH 7 +#define DEVICE_NAME_LENGTH 3 +#define STRING_LENGTH 12 + +#define NUMBER_OF_DATA_BYTES 8 + +VOID +TestLPCWrite( + DWORD NumberOfRecords, + DWORD MillisecondsToDelay + ) +{ + + HANDLE PortHandle; + UNICODE_STRING PortName; + NTSTATUS Status; + SECURITY_QUALITY_OF_SERVICE Qos; + PIO_ERROR_LOG_MESSAGE pIoErrorLogMessage; + DWORD i; + LPWSTR pDestinationString; + PPORT_MESSAGE RequestMessage; + PORT_MESSAGE ReplyMessage; + WORD DataLength; + WORD TotalLength; + INT YorN; + CHAR NumberString[8]; + ULONG MessageId = 1; + DWORD BadType = 0; + + // + // Warn the user about how this test works + // + + printf("\nThis test doesn't end! It will write a number of\n" + "records, then prompt you to write more. This is \n" + "required since it is simulating the system thread\n" + "which never shuts down it's connection\n\n" + "Do you wish to continue with this test (y or n)? "); + + YorN = getc(stdin); + + if (YorN == 'n' || YorN == 'N') { + return; + } + + // + // Initialize the SecurityQualityofService structure + // + + Qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + Qos.ImpersonationLevel = SecurityImpersonation; + Qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + Qos.EffectiveOnly = TRUE; + + // + // Connect to the LPC Port + // + + RtlInitUnicodeString( &PortName, L"\\ErrorLogPort" ); + + Status = NtConnectPort(& PortHandle, + & PortName, + & Qos, + NULL, + NULL, + NULL, + NULL, + NULL + ); + + if (!NT_SUCCESS(Status)) { + printf("Connect to the LPC port failed with RC %X\n", Status); + return; + } + + // + // Allocate the memory for the Message to send to the LPC port. It + // will contain a PORT_MESSAGE followed by an IO_ERROR_LOG_MESSAGE + // followed by Drivername and Devicename UNICODE strings + // + + DataLength = PORT_MAXIMUM_MESSAGE_LENGTH - + (sizeof(IO_ERROR_LOG_MESSAGE) + + DRIVER_NAME_LENGTH * sizeof(WCHAR) + + DEVICE_NAME_LENGTH * sizeof(WCHAR) + + STRING_LENGTH * sizeof(WCHAR)); + TotalLength = PORT_MAXIMUM_MESSAGE_LENGTH + (WORD) sizeof(PORT_MESSAGE); + + RequestMessage = (PPORT_MESSAGE) malloc(TotalLength); + if (RequestMessage == NULL) { + printf("Couldn't alloc %d bytes of memory for message\n", TotalLength); + NtClose(PortHandle); + return; + } + + pIoErrorLogMessage = (PIO_ERROR_LOG_MESSAGE) ((LPBYTE) RequestMessage + + sizeof(PORT_MESSAGE)); + + // + // Initialize the PORT_MESSAGE + // + + RequestMessage->u1.s1.DataLength = PORT_MAXIMUM_MESSAGE_LENGTH; + RequestMessage->u1.s1.TotalLength = TotalLength; + RequestMessage->u2.s2.Type = 0; + RequestMessage->u2.ZeroInit = 0; + RequestMessage->ClientId.UniqueProcess = GetCurrentProcess(); + RequestMessage->ClientId.UniqueThread = GetCurrentThread(); + RequestMessage->MessageId = 0x1234; + + // + // Initialize the IO_ERROR_LOG_MESSAGE + // + + pIoErrorLogMessage->Type = IO_TYPE_ERROR_MESSAGE; + pIoErrorLogMessage->Size = PORT_MAXIMUM_MESSAGE_LENGTH; + pIoErrorLogMessage->DriverNameLength = DRIVER_NAME_LENGTH * sizeof(WCHAR); + NtQuerySystemTime((PTIME) &pIoErrorLogMessage->TimeStamp); + pIoErrorLogMessage->DriverNameOffset = sizeof(IO_ERROR_LOG_MESSAGE) + + DataLength - sizeof(DWORD); + + pIoErrorLogMessage->EntryData.MajorFunctionCode = 1; + pIoErrorLogMessage->EntryData.RetryCount = 5; + pIoErrorLogMessage->EntryData.DumpDataSize = DataLength; + pIoErrorLogMessage->EntryData.NumberOfStrings = 2; + pIoErrorLogMessage->EntryData.StringOffset = sizeof(IO_ERROR_LOG_MESSAGE) + - sizeof(DWORD) + DataLength + + DRIVER_NAME_LENGTH * sizeof(WCHAR); + pIoErrorLogMessage->EntryData.EventCategory = 0; + pIoErrorLogMessage->EntryData.ErrorCode = 0xC0020008; + pIoErrorLogMessage->EntryData.UniqueErrorValue = 0x20008; + pIoErrorLogMessage->EntryData.FinalStatus = 0x1111; + pIoErrorLogMessage->EntryData.SequenceNumber = 1; + pIoErrorLogMessage->EntryData.IoControlCode = 0xFF; + pIoErrorLogMessage->EntryData.DeviceOffset = + RtlConvertUlongToLargeInteger(1); + + for (i = 0; i < DataLength ; i++ ) { + pIoErrorLogMessage->EntryData.DumpData[i] = i; + } + + // + // Copy the strings + // + + pDestinationString = (LPWSTR) ((LPBYTE) pIoErrorLogMessage + + sizeof(IO_ERROR_LOG_MESSAGE) + - sizeof(DWORD) + pIoErrorLogMessage->EntryData.DumpDataSize); + wcscpy(pDestinationString, DRIVER_NAME); + + pDestinationString += DRIVER_NAME_LENGTH; + wcscpy(pDestinationString, DEVICE_NAME); + + pDestinationString += DEVICE_NAME_LENGTH; + wcscpy(pDestinationString, STRING); + + // + // Write the packet as many times as requested, with delay, then ask + // if they want to write more + // + while (NumberOfRecords) { + + printf("\n\nWriting %d records\n", NumberOfRecords); + + while(NumberOfRecords--) { + printf("."); + + // + // Put in a unique message number + // + + RequestMessage->MessageId = MessageId++; + + // + // If they want invalid records, give them invalid records + // + + if (WriteInvalidRecords) { + switch (BadType++) { + case 0: + pIoErrorLogMessage->EntryData.DumpDataSize++; + break; + + case 1: + pIoErrorLogMessage->EntryData.NumberOfStrings++; + break; + + case 2: + pIoErrorLogMessage->EntryData.StringOffset++; + break; + + default: + BadType = 0; + } + } + + Status = NtRequestWaitReplyPort(PortHandle, + RequestMessage, + & ReplyMessage); + + if (!NT_SUCCESS(Status)) { + printf("Request to LPC port failed with RC %X\n", Status); + break; + } + + // + // Delay a little bit, if requested + // + + if (MillisecondsToDelay) { + Sleep(MillisecondsToDelay); + } + } + printf("\nEnter the number of records to write "); + + while (!gets(NumberString) || !(NumberOfRecords = atoi(NumberString))) { + printf("Enter the number of records to write "); + } + } + + // + // Clean up and exit + // + + Status = NtClose(PortHandle); + if (!NT_SUCCESS(Status)) { + printf("Close of Port failed with RC %X\n", Status); + } + + free(RequestMessage); + + return; + +} + + +VOID +TestChangeNotify( + VOID + ) +{ + + HANDLE Event; + UNICODE_STRING ModuleNameU; + ANSI_STRING ModuleNameA; + NTSTATUS Status; + HANDLE LogHandle; + OBJECT_ATTRIBUTES obja; + ULONG NumRecords; + ULONG BytesRead; + ULONG MinBytesNeeded; + PVOID Buffer; + ULONG OldestRecord; + ULONG NumberOfRecords; + + RtlInitAnsiString(&ModuleNameA, pModuleName); + RtlAnsiStringToUnicodeString(&ModuleNameU, &ModuleNameA, TRUE); + ModuleNameU.MaximumLength = ModuleNameU.Length ; + + Buffer = malloc (READ_BUFFER_SIZE); + ASSERT(Buffer); + + // + // Open the log handle + // + + printf("ElfOpenEventLog - "); + Status = ElfOpenEventLogW ( + &ServerName, + &ModuleNameU, + &LogHandle + ); + + if (!NT_SUCCESS(Status)) { + printf("Error - 0x%lx\n", Status); + return; + } + + printf("SUCCESS\n"); + + // + // Create the Event + // + + InitializeObjectAttributes( &obja, NULL, 0, NULL, NULL); + + Status = NtCreateEvent( + &Event, + SYNCHRONIZE | EVENT_QUERY_STATE | EVENT_MODIFY_STATE, + &obja, + SynchronizationEvent, + FALSE + ); + + ASSERT(NT_SUCCESS(Status)); + + // + // Get the read pointer to the end of the log + // + + Status = ElfOldestRecord(LogHandle, & OldestRecord); + ASSERT(NT_SUCCESS(Status)); + Status = ElfNumberOfRecords(LogHandle, & NumberOfRecords); + ASSERT(NT_SUCCESS(Status)); + OldestRecord += NumberOfRecords - 1; + + Status = ElfReadEventLogW ( + LogHandle, + EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ, + OldestRecord, + Buffer, + READ_BUFFER_SIZE, + &BytesRead, + &MinBytesNeeded + ); + + + // + // This one should hit end of file + // + + Status = ElfReadEventLogW ( + LogHandle, + EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ, + 0, + Buffer, + READ_BUFFER_SIZE, + &BytesRead, + &MinBytesNeeded + ); + + if (Status != STATUS_END_OF_FILE) { + printf("Hmmm, should have hit EOF (unless there are writes going" + " on elsewhere- %X\n", Status); + } + + // + // Call ElfChangeNotify + // + + Status = ElfChangeNotify(LogHandle, Event); + ASSERT(NT_SUCCESS(Status)); + + // + // Now loop waiting for the event to get toggled + // + + while (1) { + + Status = NtWaitForSingleObject(Event, FALSE, 0); + printf("The change notify event just got kicked\n"); + + // + // Now read the new records + // + + while(1) { + + Status = ElfReadEventLogW ( + LogHandle, + EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ, + 0, + Buffer, + READ_BUFFER_SIZE, + &BytesRead, + &MinBytesNeeded + ); + + if (Status == STATUS_END_OF_FILE) { + break; + } + + NumRecords = 0xffff; // should be plenty + DisplayEventRecords (Buffer, BytesRead, &NumRecords); + } + } +} + +VOID +_CRTAPI1 +main ( + IN SHORT argc, + IN PSZ argv[] + ) +{ + + Initialize(); // Init any data + + // + // Parse the command line + // + + ParseParms(argc, argv); + + switch (Operation) { + case Clear: + + TestElfClearLogFile(); + break; + + case Backup: + + TestElfBackupLogFile(); + break; + + case Read: + + if (ReadFlags & EVENTLOG_SEEK_READ) { + TestReadEventLog(1, ReadFlags, NumberofRecords) ; + } + else { + TestReadEventLog(NumberofRecords, ReadFlags, 0) ; + } + break; + + case Write: + + TestReportEvent (NumberofRecords); + break; + + case LPC: + TestLPCWrite(NumberofRecords, DelayInMilliseconds); + break; + + case Notify: + TestChangeNotify(); + break; + + default: + printf("Invalid switch from ParseParms %d\n", Operation); + } +} + + +VOID +ParseParms( + ULONG argc, + PCHAR *argv + ) +{ + + ULONG i; + PCHAR pch; + + for (i = 1; i < argc; i++) { /* for each argument */ + if (*(pch = argv[i]) == '-') { + while (*++pch) { + switch (*pch) { + case 'b': + + SET_OPERATION(Backup) + + // + // Get the file name for backup + // + + if (i+1 < argc) { + pBackupFileName = argv[++i]; + } + else { + Usage(); + } + break; + + case 'c': + + SET_OPERATION(Clear) + + break; + + case 'f': + if (i+1 < argc) { + pModuleName = argv[++i]; + ReadingBackupFile = TRUE; + } + else { + Usage(); + } + break; + + case '?': + case 'h': + case 'H': + Usage(); + break; + + case 'i': + InvalidUser = TRUE; + break; + + case 'l': + + SET_OPERATION(LPC); + + // + // See if they want invalid records + // + + if (*++pch == 'i') { + WriteInvalidRecords = TRUE; + } + + // + // See if they specified a number of records + // + + if (i + 1 < argc && argv[i+1][0] != '-') { + NumberofRecords = atoi(argv[++i]); + if (NumberofRecords == 0) { + Usage(); + } + } + + break; + + case 'm': + if (i+1 < argc) { + pModuleName = argv[++i]; + ReadingModule = TRUE; + } + else { + Usage(); + } + break; + + case 'n': + SET_OPERATION(Notify) + break; + + case 'r': + + SET_OPERATION(Read) + + // + // Different Read options + // + + if (*++pch == 's') { + ReadFlags |= EVENTLOG_SEQUENTIAL_READ; + } + else if (*pch == 'r') { + ReadFlags |= EVENTLOG_SEEK_READ; + } + else { + Usage(); + } + + if (*++pch == 'f') { + ReadFlags |= EVENTLOG_FORWARDS_READ; + } + else if (*pch == 'b') { + ReadFlags |= EVENTLOG_BACKWARDS_READ; + } + else { + Usage(); + } + + // + // See if they specified a number of records + // + + if (i + 1 < argc && argv[i+1][0] != '-') { + NumberofRecords = atoi(argv[++i]); + if (NumberofRecords == 0) { + Usage(); + } + } + + break; + + case 's': + if (i+1 >= argc) { + printf("Must supply a server name with -s\n"); + Usage(); + } + RtlInitAnsiString(&AnsiString, argv[++i]); + RtlAnsiStringToUnicodeString(&ServerName, &AnsiString, + TRUE); + break; + + case 't': + DelayInMilliseconds = atoi(argv[++i]); + break; + + case 'v': + Verbose = TRUE; + break; + + case 'w': + + SET_OPERATION(Write) + + // + // See if they specified a number of records + // + + if (i + 1 < argc && argv[i+1][0] != '-') { + NumberofRecords = atoi(argv[++i]); + if (NumberofRecords == 0) { + Usage(); + } + } + + break; + + default: /* Invalid options */ + printf("Invalid option %c\n\n", *pch); + Usage(); + break; + } + } + } + + // + // There aren't any non switch parms + // + + else { + Usage(); + } + } + + // + // Verify parms are correct + // + + + if ( Operation == Invalid) { + printf( "Must specify an operation\n"); + Usage( ); + } + + if (ReadingBackupFile && ReadingModule) { + printf("-m and -f are mutually exclusive\n"); + Usage(); + } + + if (ReadingBackupFile && Operation == Write) { + printf("You cannot write to a backup log file\n"); + Usage(); + } + if (DelayInMilliseconds && Operation != LPC) { + printf("\n\n-t switch is only used with -l\n\n"); + } +} + diff --git a/private/eventlog/test/testwin.c b/private/eventlog/test/testwin.c new file mode 100644 index 000000000..bea524159 --- /dev/null +++ b/private/eventlog/test/testwin.c @@ -0,0 +1,770 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + TESTWIN.C + +Abstract: + + Test program for the eventlog service. This program calls the Win + APIs to test out the operation of the service. + +Author: + + Rajen Shah (rajens) 05-Aug-1991 + +Revision History: + + +--*/ +/*----------------------*/ +/* INCLUDES */ +/*----------------------*/ +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <stdio.h> // printf +#include <string.h> // stricmp +#include <stdlib.h> +#include <windows.h> +#include <netevent.h> + +// +// Turn on NotifyChangeEventLog +// +#define TEST_NOTIFY 1 +//#define TEST_REMOTE 1 + +#define READ_BUFFER_SIZE 1024*2 // Use 2K buffer + +#define SIZE_DATA_ARRAY 65 + +// +// Global buffer used to emulate "binary data" when writing an event +// record. +// + DWORD Data[SIZE_DATA_ARRAY]; +// LPWSTR ServerName=L"\\\\danl2"; + LPWSTR ServerName=NULL; + + +VOID +Initialize ( + VOID + ) +{ + DWORD i; + + // Initialize the values in the data buffer. + // + for (i=0; i< SIZE_DATA_ARRAY; i++) + Data[i] = i; + +} + + +BOOL +Usage ( + VOID + ) +{ + printf( "usage: \n" ); + printf( "-b <filename> Tests BackupEventLog API\n"); + printf( "-c Tests ClearEventLog API\n"); + printf( "-n Tests NotifyChangeEventlog\n"); + printf( "-rsb Reads event log sequentially backwards\n"); + printf( "-rsf Reads event log sequentially forwards\n"); + printf( "-rrb <record> Reads event log from <record> backwards\n"); + printf( "-rrf <record> Reads event log from <record> forwards\n"); + printf( "-w <count> Tests ReportEvent API <count> times\n"); + return ERROR_INVALID_PARAMETER; + +} // Usage + + + +BOOL +WriteLogEntry ( HANDLE LogHandle, DWORD EventID ) + +{ +#define NUM_STRINGS 2 + + BOOL Status; + WORD EventType, i; + DWORD DataSize; + PSID pUserSid; + + PWSTR Strings[NUM_STRINGS] = {L"StringAOne", + L"StringATwo" + }; + + EventType = EVENTLOG_INFORMATION_TYPE; + pUserSid = NULL; + DataSize = sizeof(DWORD) * SIZE_DATA_ARRAY; + + for (i=0; i< SIZE_DATA_ARRAY; i++) + Data[i] += i; + + Status = ReportEventW ( + LogHandle, + EventType, + 0, // event category + EventID, + pUserSid, + NUM_STRINGS, + DataSize, + Strings, + (PVOID)Data + ); + + return (Status); +} + +DWORD +WriteLogEntryMsg ( HANDLE LogHandle, DWORD EventID ) +/* + This function requires a registry entry in the Applications section + of the Eventlog for TESTWINAPP, it will use the netevent.dll message file. +*/ + +{ +#define NUM_STRINGS 2 + + WORD EventType; + DWORD DataSize; + PSID pUserSid; + + PWSTR Strings[NUM_STRINGS]; + + Strings[0] = L"This is a BOGUS message for TEST purposes Ignore this substitution text"; + Strings[1] = L"GHOST SERVICE in the long string format - I wanted a long string to pass into this function"; + + EventType = EVENTLOG_INFORMATION_TYPE; + pUserSid = NULL; + DataSize = sizeof(DWORD) * SIZE_DATA_ARRAY; + + + if (!ReportEventW ( + LogHandle, + EventType, + 0, // event category + EVENT_SERVICE_START_FAILED_NONE, + pUserSid, + NUM_STRINGS, + 0, // DataSize + Strings, + (PVOID)NULL // Data + )) { + + printf("ReportEventW failed %d\n",GetLastError()); + return(GetLastError()); + } + return (NO_ERROR); +} + + +VOID +DisplayEventRecords( PVOID Buffer, + DWORD BufSize, + ULONG *NumRecords) + +{ + PEVENTLOGRECORD pLogRecord; + ANSI_STRING StringA; + UNICODE_STRING StringU; + PWSTR pwString; + DWORD Count = 0; + DWORD Offset = 0; + DWORD i; + + pLogRecord = (PEVENTLOGRECORD) Buffer; + + while ((DWORD)Offset < BufSize) { + + printf("\nRecord # %lu\n", ++Count); + + printf("Length: 0x%lx TimeGenerated: 0x%lx EventID: 0x%lx EventType: 0x%x\n", + pLogRecord->Length, pLogRecord->TimeGenerated, pLogRecord->EventID, + pLogRecord->EventType); + + printf("NumStrings: 0x%x StringOffset: 0x%lx UserSidLength: 0x%lx TimeWritten: 0x%lx\n", + pLogRecord->NumStrings, pLogRecord->StringOffset, + pLogRecord->UserSidLength, pLogRecord->TimeWritten); + + printf("UserSidOffset: 0x%lx DataLength: 0x%lx DataOffset: 0x%lx \n", + pLogRecord->UserSidOffset, pLogRecord->DataLength, + pLogRecord->DataOffset); + + // + // Print out module name + // + pwString = (PWSTR)((DWORD)pLogRecord + sizeof(EVENTLOGRECORD)); + RtlInitUnicodeString (&StringU, pwString); + RtlUnicodeStringToAnsiString (&StringA, &StringU, TRUE); + + printf("ModuleName: %s ", StringA.Buffer); + RtlFreeAnsiString (&StringA); + + // + // Display ComputerName + // + pwString = pwString + (wcslen(pwString) + 1); + + RtlInitUnicodeString (&StringU, pwString); + RtlUnicodeStringToAnsiString (&StringA, &StringU, TRUE); + + printf("ComputerName: %s\n",StringA.Buffer); + RtlFreeAnsiString (&StringA); + + // + // Display strings + // + pwString = (PWSTR)((DWORD)Buffer + pLogRecord->StringOffset); + + printf("\nStrings: \n"); + for (i=0; i<pLogRecord->NumStrings; i++) { + + RtlInitUnicodeString (&StringU, pwString); + RtlUnicodeStringToAnsiString (&StringA, &StringU, TRUE); + + printf(" %s \n",StringA.Buffer); + + RtlFreeAnsiString (&StringA); + + pwString = (PWSTR)((DWORD)pwString + StringU.MaximumLength); + } + + // Get next record + // + Offset += pLogRecord->Length; + + pLogRecord = (PEVENTLOGRECORD)((DWORD)Buffer + Offset); + + } + *NumRecords = Count; + +} + + +BOOL +ReadFromLog ( HANDLE LogHandle, + PVOID Buffer, + ULONG *pBytesRead, + DWORD ReadFlag, + DWORD Record + ) +{ + BOOL Status; + DWORD MinBytesNeeded; + DWORD ErrorCode; + + Status = ReadEventLogW ( + LogHandle, + ReadFlag, + Record, + Buffer, + READ_BUFFER_SIZE, + pBytesRead, + &MinBytesNeeded + ); + + + if (!Status) { + ErrorCode = GetLastError(); + if (ErrorCode == ERROR_HANDLE_EOF) { + Status = TRUE; + } + else if (ErrorCode == ERROR_NO_MORE_FILES) { + printf("Buffer too small. Need %lu bytes min\n", MinBytesNeeded); + } + else { + printf("Error from ReadEventLog %d \n", ErrorCode); + } + + } + + return (Status); +} + + + + +BOOL +TestReadEventLog (DWORD Count, DWORD ReadFlag, DWORD Record) + +{ + BOOL bStatus,IStatus; + DWORD status; + HANDLE LogHandle; + LPWSTR ModuleName; + DWORD NumRecords, BytesReturned; + PVOID Buffer; + DWORD RecordOffset; + DWORD NumberOfRecords; + DWORD OldestRecord; + + printf("Testing ReadEventLog API to read %lu entries\n",Count); + + Buffer = malloc (READ_BUFFER_SIZE); + + // + // Initialize the strings + // + NumRecords = Count; + ModuleName = L"TESTWINAPP"; + + // + // Open the log handle + // + printf("OpenEventLog - "); + LogHandle = OpenEventLogW ( + ServerName, + ModuleName + ); + + if (LogHandle == NULL) { + printf("Error - %d\n", GetLastError()); + + } else { + printf("SUCCESS\n"); + + // + // Get and print record information + // + + bStatus = GetNumberOfEventLogRecords(LogHandle, & NumberOfRecords); + if (bStatus) { + bStatus = GetOldestEventLogRecord(LogHandle, & OldestRecord); + } + + if (!bStatus) { + printf("Query of record information failed with %X", GetLastError()); + return(bStatus); + } + + printf("\nThere are %d records in the file, %d is the oldest" + " record number\n", NumberOfRecords, OldestRecord); + + RecordOffset = Record; + + printf("Reading %u records\r", Count); + + while (Count) { + + // + // Read from the log + // + bStatus = ReadFromLog ( LogHandle, + Buffer, + &BytesReturned, + ReadFlag, + RecordOffset + ); + if (bStatus) { + printf("Bytes read = 0x%lx\n", BytesReturned); + printf("Read %u records\n", NumRecords); + DisplayEventRecords(Buffer, BytesReturned, &NumRecords); + Count -= NumRecords; + RecordOffset += NumRecords; + } else { + break; + } + + if (BytesReturned == 0) + break; + } + printf("\n"); + + if (!bStatus) { + printf ("ReadFromLog Error - %d. Remaining count %lu\n", GetLastError(), + Count); + } else { + printf ("SUCCESS\n"); + } + + printf("Calling CloseEventLog\n"); + IStatus = CloseEventLog (LogHandle); + } + + return (bStatus); +} + + + +BOOL +TestWriteEventLog (DWORD Count) + +{ + DWORD Status=NO_ERROR; + BOOL IStatus; + HANDLE LogHandle; + LPWSTR ModuleName; + DWORD EventID = 99; + DWORD WriteCount; + DWORD DataNum=0; + + printf("Testing ReportEvent API\n"); + + // + // Initialize the strings + // + ModuleName = L"TESTWINAPP"; + + printf("Calling RegisterEventSource for WRITE %lu times\n", Count); + while ((Count > 0) && (Status== NO_ERROR)) { + // + // Open the log handle + // + LogHandle = RegisterEventSourceW ( + ServerName, + ModuleName + ); + + if (LogHandle == NULL) { + Status = GetLastError(); + printf("RegisterEventSource Failure - %d\n", Status); + return(Status); + + } else { + printf("Registered - "); + WriteCount = 5; + printf("Record # %u: ", Count); + + while ((WriteCount>0) && (Status==NO_ERROR)) { + + // + // Write an entry into the log + // + Data[0] = DataNum; // Make data "unique" + EventID = (EventID + DataNum) % 100; // Vary the eventids + Status = WriteLogEntryMsg( LogHandle, EventID ); + DataNum++; + WriteCount--; + + if (Status != NO_ERROR) { + printf ("WriteLogEntry Error - %d. Remaining count %lu\n",Status,Count); + } else { + printf ("%d,",WriteCount); + } + } + + IStatus = DeregisterEventSource (LogHandle); + printf(" - Deregistered\n"); + } + Count--; + } + + printf("\n"); + return (Status); +} + + + +BOOL +TestClearLogFile () + +{ + BOOL Status, IStatus; + HANDLE LogHandle; + LPWSTR ModuleName, BackupName; + + printf("Testing ClearLogFile API\n"); + // + // Initialize the strings + // + ModuleName = L"TESTWINAPP"; + + // + // Open the log handle + // + printf("Calling OpenEventLog for CLEAR - "); + LogHandle = OpenEventLogW ( + NULL, + ModuleName + ); + + if (LogHandle == NULL) { + printf("OpenEventLog Error - %d\n", GetLastError()); + + } else { + printf("SUCCESS\n"); + + // + // Clear the log file and back it up to "view.log" + // + + printf("Calling ClearEventLog backing up to view.log "); + BackupName = L"\\\\danhi386\\roote\\view.log"; + + Status = ClearEventLogW ( + LogHandle, + BackupName + ); + + if (!Status) { + printf ("ClearEventLog Error - %d\n", GetLastError()); + } else { + printf ("SUCCESS\n"); + } + + // + // Now just clear the file without backing it up + // + printf("Calling ClearEventLog with no backup "); + Status = ClearEventLogW ( + LogHandle, + NULL + ); + + if (!Status) { + printf ("ClearEventLogError - %d\n", GetLastError()); + } else { + printf ("SUCCESS\n"); + } + + printf("Calling CloseEventLog\n"); + IStatus = CloseEventLog (LogHandle); + } + + return(Status); +} + +BOOL +TestBackupLogFile (LPSTR BackupFileName) + +{ + BOOL Status, IStatus; + HANDLE LogHandle; + LPWSTR ModuleName; + ANSI_STRING AnsiString; + UNICODE_STRING UnicodeString; + + printf("Testing BackupLogFile API\n"); + // + // Initialize the strings + // + ModuleName = L"TESTWINAPP"; + + // + // Open the log handle + // + printf("Calling OpenEventLog for BACKUP - "); + LogHandle = OpenEventLogW ( + NULL, + ModuleName + ); + + if (LogHandle == NULL) { + printf("OpenEventLog Failure %d\n", GetLastError()); + + } else { + printf("OpenEventLog SUCCESS\n"); + + // + // Backup the log file to BackupFileName + // + + printf("Calling BackupEventLog backing up to %s ", BackupFileName); + + RtlInitAnsiString(&AnsiString, BackupFileName); + RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE); + + Status = BackupEventLogW (LogHandle, UnicodeString.Buffer); + + if (!Status) { + printf ("BackupEventLog failure - %d\n", GetLastError()); + } else { + printf ("SUCCESS\n"); + } + + printf("Calling CloseEventLog\n"); + IStatus = CloseEventLog (LogHandle); + } + + return(Status); +} + +VOID +NotifyThread( + HANDLE hEventLog) +{ + Sleep(30000); + printf("NotifyThread: Writing an event...\n"); + if (!WriteLogEntryMsg(hEventLog,1)) { + printf("NotifyThread: WriteLogEntryMsg failed\n"); + } + else { + printf("Event was written\n"); + } + ExitThread(NO_ERROR); +} + +VOID +TestChangeNotify( + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + HANDLE hEvent; + HANDLE hThread; + HANDLE hEventLog; + DWORD threadId; + DWORD status; + + hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); + if (hEvent == NULL) { + printf("CreateEvent Failed %d\n",GetLastError()); + return; + } +#ifdef TEST_REMOTE + hEventLog = RegisterEventSourceW(L"\\\\DANL2",L"TESTWINAPP"); +#else + hEventLog = RegisterEventSourceW(NULL,L"TESTWINAPP"); +#endif + if (hEventLog == NULL) { + printf("OpenEventLog failed %d\n",GetLastError()); + } + +#ifdef TEST_NOTIFY + + if (!NotifyChangeEventLog(hEventLog,hEvent)) { + printf("NotifyChangeEventLog failed %d\n",GetLastError()); + } +#endif // TEST_NOTIFY + + hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)NotifyThread,hEventLog,0,&threadId); + if (hThread == NULL) { + printf("CreateThread Failed %d\n",GetLastError()); + CloseHandle(hEvent); + return; + } + + CloseHandle(hThread); + + printf("Wait for event to become signaled\n"); + status = WaitForSingleObject(hEvent,INFINITE); + if (status == WAIT_OBJECT_0) { + printf("The Event was signaled\n"); + } + else { + printf("The Event was NOT signaled\n"); + } + return; +} +/****************************************************************************/ +DWORD _CRTAPI1 +main ( + IN SHORT argc, + IN PSZ argv[], + IN PSZ envp[] + ) +/*++ +* +* Routine Description: +* +* +* +* Arguments: +* +* +* +* +* Return Value: +* +* +* +--*/ +/****************************************************************************/ +{ + + DWORD ReadFlags; + + Initialize(); // Init any data + + if ( argc < 2 ) { + printf( "Not enough parameters\n" ); + return Usage( ); + } + + if ( stricmp( argv[1], "-c" ) == 0 ) { + + if ( argc < 3 ) { + return TestClearLogFile(); + } + } + else if ( stricmp( argv[1], "-b" ) == 0 ) { + + if ( argc < 3 ) { + printf("You must supply a filename to backup to\n"); + return(FALSE); + } + + return TestBackupLogFile(argv[2]); + + } else if (stricmp ( argv[1], "-rsf" ) == 0 ) { + + ReadFlags = EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ; + if ( argc < 3 ) { + return TestReadEventLog(1,ReadFlags,0 ); + } else { + return Usage(); + } + } else if (stricmp ( argv[1], "-rsb" ) == 0 ) { + + ReadFlags = EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ; + if ( argc < 3 ) { + return TestReadEventLog(1,ReadFlags,0 ); + } else { + return Usage(); + } + } else if (stricmp ( argv[1], "-n" ) == 0 ) { + TestChangeNotify(); + + } else if (stricmp ( argv[1], "-rrf" ) == 0 ) { + + ReadFlags = EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ; + if ( argc < 3 ) { + return TestReadEventLog(1,ReadFlags ,1); + } else if (argc == 3) { + return (TestReadEventLog (1, ReadFlags, atoi(argv[2]))); + } + } else if (stricmp ( argv[1], "-rrb" ) == 0 ) { + + ReadFlags = EVENTLOG_SEEK_READ | EVENTLOG_BACKWARDS_READ; + if ( argc < 3 ) { + return TestReadEventLog(1,ReadFlags, 1); + } else if (argc == 3) { + return (TestReadEventLog (1, ReadFlags, atoi(argv[2]))); + } + } else if (stricmp ( argv[1], "-w" ) == 0 ) { + + if ( argc < 3 ) { + return TestWriteEventLog(1); + } else if (argc == 3) { + return (TestWriteEventLog (atoi(argv[2]))); + } + + } else { + + return Usage(); + } + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + UNREFERENCED_PARAMETER(envp); + + +} diff --git a/private/eventlog/test/testwina.c b/private/eventlog/test/testwina.c new file mode 100644 index 000000000..efd558f57 --- /dev/null +++ b/private/eventlog/test/testwina.c @@ -0,0 +1,693 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + TESTWINA.C + +Abstract: + + Test program for the eventlog service. This program calls the Win + APIs to test out the operation of the service. + +Author: + + Rajen Shah (rajens) 05-Aug-1991 + +Revision History: + + +--*/ +/*----------------------*/ +/* INCLUDES */ +/*----------------------*/ +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <stdio.h> // printf +#include <string.h> // stricmp +#include <stdlib.h> +#include <windows.h> +#include <netevent.h> +//#include <elfcommn.h> + + +#define READ_BUFFER_SIZE 1024*2 // Use 2K buffer + +#define SIZE_DATA_ARRAY 65 + +// +// Global buffer used to emulate "binary data" when writing an event +// record. +// +DWORD Data[SIZE_DATA_ARRAY]; +BOOL bHackTestBackup = FALSE; +PCHAR pServerName = NULL; + + +VOID +Initialize ( + VOID + ) +{ + DWORD i; + + // Initialize the values in the data buffer. + // + for (i=0; i< SIZE_DATA_ARRAY; i++) + Data[i] = i; + +} + + +BOOL +Usage ( + VOID + ) +{ + printf( "usage: \n" ); + printf( "-c Tests ClearEventLog API\n"); + printf( "-b filename Tests BackupEventLog API\n"); + printf( "-rsb Reads event log sequentially backwards\n"); + printf( "-rsf Reads event log sequentially forwards\n"); + printf( "-rrb <record> Reads event log from <record> backwards\n"); + printf( "-rrf <record> Reads event log from <record> forwards\n"); + printf( "-w <count> Tests ReportEvent API <count> times\n"); + return ERROR_INVALID_PARAMETER; + +} // Usage + + + +BOOL +WriteLogEntry ( HANDLE LogHandle, DWORD EventID ) + +{ +#define NUM_STRINGS 2 +#define MAX_STRING_SIZE 32767 // Max size is FFFF/2 for ANSI strings + + BOOL Status; + WORD EventType; + DWORD i; + DWORD DataSize; + PSID pUserSid; + PCHAR BigString; + + // PSTR Strings[NUM_STRINGS] = {"StringAOne","StringATwo" }; + PSTR Strings[NUM_STRINGS]; + + Strings[0] = "StringAOne"; + + BigString = malloc(MAX_STRING_SIZE); + + for (i = 0; i < MAX_STRING_SIZE; i++) { + BigString[i] = 'A'; + } + + BigString[MAX_STRING_SIZE-1] = '\0'; + Strings[1] = BigString; + + EventType = EVENTLOG_INFORMATION_TYPE; + pUserSid = NULL; + DataSize = sizeof(DWORD) * SIZE_DATA_ARRAY; + + for (i=0; i< SIZE_DATA_ARRAY; i++) + Data[i] += i; + + Status = ReportEventA ( + LogHandle, + EventType, + 0, // event category + EventID, + pUserSid, + (WORD) NUM_STRINGS, + DataSize, + Strings, + (PVOID)Data + ); + + free(BigString); + return (Status); +} + +BOOL +WriteLogEntryMsg ( HANDLE LogHandle, DWORD EventID ) +/* + This function requires a registry entry in the Applications section + of the Eventlog for TESTWINAAPP, it will use the netevent.dll message file. +*/ +{ +#define NUM_STRINGS 2 + + BOOL Status; + WORD EventType; + DWORD DataSize; + PSID pUserSid; + PCHAR BigString; + + PSTR Strings[NUM_STRINGS]; + + Strings[0] = "This is a BOGUS message for TEST purposes Ignore this substitution text"; + Strings[1] = "GHOST SERVICE in the long string format - I wanted a long string to pass into this function"; + + EventType = EVENTLOG_INFORMATION_TYPE; + pUserSid = NULL; + DataSize = sizeof(DWORD) * SIZE_DATA_ARRAY; + + Status = ReportEventA ( + LogHandle, + EventType, + 0, // event category + EVENT_SERVICE_START_FAILED_NONE, + pUserSid, + (WORD) NUM_STRINGS, + 0, // DataSize + Strings, + (PVOID)NULL // Data + ); + + free(BigString); + return (Status); +} + + +VOID +DisplayEventRecords( PVOID Buffer, + DWORD BufSize, + ULONG *NumRecords) + +{ + PEVENTLOGRECORD pLogRecord; + PSTR pString; + DWORD Count = 0; + DWORD Offset = 0; + DWORD i; + + pLogRecord = (PEVENTLOGRECORD) Buffer; + + while ((DWORD)Offset < BufSize) { + + Count++; + + printf("\n\nRecord # %lu\n", pLogRecord->RecordNumber); + + printf("Length: 0x%lx TimeGenerated: 0x%lx EventID: 0x%lx EventType: 0x%x\n", + pLogRecord->Length, pLogRecord->TimeGenerated, pLogRecord->EventID, + pLogRecord->EventType); + + printf("NumStrings: 0x%x StringOffset: 0x%lx UserSidLength: 0x%lx TimeWritten: 0x%lx\n", + pLogRecord->NumStrings, pLogRecord->StringOffset, + pLogRecord->UserSidLength, pLogRecord->TimeWritten); + + printf("UserSidOffset: 0x%lx DataLength: 0x%lx DataOffset: 0x%lx \n", + pLogRecord->UserSidOffset, pLogRecord->DataLength, + pLogRecord->DataOffset); + + // + // Print out module name + // + pString = (PSTR)((DWORD)pLogRecord + sizeof(EVENTLOGRECORD)); + printf("ModuleName: %s ", pString); + + // + // Display ComputerName + // + pString = (PSTR)((DWORD)pString + strlen(pString) + 1); + printf("ComputerName: %s\n",pString); + + // + // Display strings + // + pString = (PSTR)((DWORD)Buffer + pLogRecord->StringOffset); + + printf("Strings: "); + for (i=0; i<pLogRecord->NumStrings; i++) { + + printf(" %s ", pString); + pString = (PSTR)((DWORD)pString + strlen(pString) + 1); + } + + // Get next record + + Offset += pLogRecord->Length; + + pLogRecord = (PEVENTLOGRECORD)((DWORD)Buffer + Offset); + + } + *NumRecords = Count; + +} + + +BOOL +ReadFromLog ( HANDLE LogHandle, + PVOID Buffer, + ULONG *pBytesRead, + DWORD ReadFlag, + DWORD Record + ) +{ + BOOL Status; + DWORD MinBytesNeeded; + DWORD ErrorCode; + + Status = ReadEventLogA ( + LogHandle, + ReadFlag, + Record, + Buffer, + READ_BUFFER_SIZE, + pBytesRead, + &MinBytesNeeded + ); + + + if (!Status) { + ErrorCode = GetLastError(); + printf("Error from ReadEventLog %d \n", ErrorCode); + if (ErrorCode == ERROR_NO_MORE_FILES) + printf("Buffer too small. Need %lu bytes min\n", MinBytesNeeded); + + } + + return (Status); +} + + + + +BOOL +TestReadEventLog (DWORD Count, DWORD ReadFlag, DWORD Record) + +{ + BOOL Status, IStatus; + + HANDLE LogHandle; + LPSTR ModuleName; + DWORD NumRecords, BytesReturned; + PVOID Buffer; + DWORD RecordOffset; + DWORD NumberOfRecords; + DWORD OldestRecord; + + printf("Testing ReadEventLog API to read %lu entries\n",Count); + + Buffer = malloc (READ_BUFFER_SIZE); + + // + // Initialize the strings + // + NumRecords = Count; + ModuleName = "TESTWINAAPP"; + + // + // Open the log handle + // + + // + // This is just a quick and dirty way to test the api to read a backup + // log, until I can fix test.c to be more general purpose. + // + + if (bHackTestBackup) { + printf("OpenBackupEventLog = "); + LogHandle = OpenBackupEventLog( + NULL, + "\\\\danhi386\\roote\\view.log" + ); + } + else { + printf("OpenEventLog - "); + LogHandle = OpenEventLog ( + pServerName, + ModuleName + ); + } + + if (LogHandle == NULL) { + printf("Error - %d\n", GetLastError()); + + } else { + printf("SUCCESS\n"); + + // + // Get and print record information + // + + Status = GetNumberOfEventLogRecords(LogHandle, & NumberOfRecords); + if (NT_SUCCESS(Status)) { + Status = GetOldestEventLogRecord(LogHandle, & OldestRecord); + } + + if (!NT_SUCCESS(Status)) { + printf("Get of record information failed with %X", Status); + return(Status); + } + + printf("\nThere are %d records in the file, %d is the oldest" + " record number\n", NumberOfRecords, OldestRecord); + + RecordOffset = Record; + + while (Count && (BytesReturned != 0)) { + + printf("Read %u records\n", NumRecords); + // + // Read from the log + // + Status = ReadFromLog ( LogHandle, + Buffer, + &BytesReturned, + ReadFlag, + RecordOffset + ); + if (Status) { + printf("Bytes read = 0x%lx\n", BytesReturned); + DisplayEventRecords(Buffer, BytesReturned, &NumRecords); + Count -= NumRecords; + RecordOffset += NumRecords; + } else { + break; + } + + } + printf("\n"); + + if (!Status) { + printf ("Error - %d. Remaining count %lu\n", GetLastError(), Count); + } else { + printf ("SUCCESS\n"); + } + + printf("Calling CloseEventLog\n"); + IStatus = CloseEventLog (LogHandle); + } + free(Buffer); + return (Status); +} + + + +BOOL +TestWriteEventLog (DWORD Count) + +{ + BOOL Status, IStatus; + HANDLE LogHandle=NULL; + LPSTR ModuleName; + DWORD EventID = 99; + DWORD WriteCount; + + printf("Testing ReportEvent API\n"); + + // + // Initialize the strings + // + ModuleName = "TESTWINAAPP"; + + // + // Open the log handle + // + while (Count && NT_SUCCESS(Status)) { + //printf("Calling RegisterEventSource for WRITE %lu times - ", Count); + LogHandle = RegisterEventSourceA ( + pServerName, + ModuleName + ); + + if (LogHandle == NULL) { + printf("Error - %d\n", GetLastError()); + + } else { + printf("Registered - "); + WriteCount = 5; + printf("Record # %u ", Count); + + while (WriteCount && NT_SUCCESS(Status)) { + + // + // Write an entry into the log + // + Data[0] = Count; // Make data "unique" + EventID = (EventID + Count) % 100; // Vary the eventids + Status = WriteLogEntryMsg ( LogHandle, EventID ); + Count--; + WriteCount--; + + if (!Status) { + printf ("Error - %d. Remaining count %lu\n", GetLastError(), Count); + } else { + printf ("%d,",WriteCount); + } + } + IStatus = DeregisterEventSource (LogHandle); + printf(" - Deregistered\n"); + } + } + + return (Status); +} + + + +BOOL +TestClearLogFile () + +{ + BOOL Status, IStatus; + HANDLE LogHandle; + LPSTR ModuleName, BackupName; + + printf("Testing ClearLogFile API\n"); + // + // Initialize the strings + // + ModuleName = "TESTWINAAPP"; + + // + // Open the log handle + // + printf("Calling OpenEventLog for CLEAR - "); + LogHandle = OpenEventLogA ( + pServerName, + ModuleName + ); + + if (!Status) { + printf("Error - %d\n", GetLastError()); + + } else { + printf("SUCCESS\n"); + + // + // Clear the log file and back it up to "view.log" + // + + printf("Calling ClearEventLog backing up to view.log "); + BackupName = "view.log"; + + Status = ClearEventLogA ( + LogHandle, + BackupName + ); + + if (!Status) { + printf ("Error - %d\n", GetLastError()); + } else { + printf ("SUCCESS\n"); + } + + // + // Now just clear the file without backing it up + // + printf("Calling ClearEventLog with no backup "); + Status = ClearEventLogA ( + LogHandle, + NULL + ); + + if (!Status) { + printf ("Error - %d\n", GetLastError()); + } else { + printf ("SUCCESS\n"); + } + + printf("Calling CloseEventLog\n"); + IStatus = CloseEventLog (LogHandle); + } + + return(Status); +} + + +BOOL +TestBackupLogFile( + LPSTR FileName + ) + +{ + HANDLE LogHandle; + + printf("Testing BackupEventLog API\n"); + + // + // Open the log handle + // + + printf("Calling ElfOpenEventLog for BACKUP - "); + LogHandle = OpenEventLogA ( + NULL, + "Application" + ); + + if (!LogHandle) { + printf("Error - %d\n", GetLastError()); + + } else { + printf("SUCCESS\n"); + + // + // Backup the log file + // + + printf("Calling BackupEventLogFile backing up to %s\n", FileName); + + if (!BackupEventLogA ( + LogHandle, + FileName + )) { + printf ("Error - %d\n", GetLastError()); + } else { + printf ("SUCCESS\n"); + } + + + printf("Calling CloseEventLog - "); + if (CloseEventLog (LogHandle)) { + printf("Success\n"); + } + else { + printf("Failed with code %d\n", GetLastError()); + } + } + + return(TRUE); +} + + +/****************************************************************************/ +BOOL +main ( + IN SHORT argc, + IN PSZ argv[], + ) +/*++ +* +* Routine Description: +* +* +* +* Arguments: +* +* +* +* +* Return Value: +* +* +* +--*/ +/****************************************************************************/ +{ + + DWORD ReadFlags; + + Initialize(); // Init any data + + // + // Just till I can replace this horrid parm parsing with my own + // + + if (getenv("REMOTE")) { + pServerName = "\\\\danhi20"; + } + + if ( argc < 2 ) { + printf( "Not enough parameters\n" ); + return Usage( ); + } + + if ( stricmp( argv[1], "-c" ) == 0 ) { + + if ( argc < 3 ) { + return TestClearLogFile(); + } + + } else if (stricmp ( argv[1], "-b" ) == 0 ) { + + if ( argc < 3 ) { + return Usage(); + } else { + return TestBackupLogFile(argv[2]); + } + + } else if (stricmp ( argv[1], "-rsf" ) == 0 ) { + + ReadFlags = EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ; + if ( argc < 3 ) { + return TestReadEventLog(1,ReadFlags,0 ); + } else { + return Usage(); + } + } else if (stricmp ( argv[1], "-xsf" ) == 0 ) { + + ReadFlags = EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ; + bHackTestBackup = TRUE; + if ( argc < 3 ) { + return TestReadEventLog(1,ReadFlags,0 ); + } else { + return Usage(); + } + } else if (stricmp ( argv[1], "-rsb" ) == 0 ) { + + ReadFlags = EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ; + if ( argc < 3 ) { + return TestReadEventLog(1,ReadFlags,0 ); + } else { + return Usage(); + } + } else if (stricmp ( argv[1], "-rrf" ) == 0 ) { + + ReadFlags = EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ; + if ( argc < 3 ) { + return TestReadEventLog(1,ReadFlags ,1); + } else if (argc == 3) { + return (TestReadEventLog (1, ReadFlags, atoi(argv[2]))); + } + } else if (stricmp ( argv[1], "-rrb" ) == 0 ) { + + ReadFlags = EVENTLOG_SEEK_READ | EVENTLOG_BACKWARDS_READ; + if ( argc < 3 ) { + return TestReadEventLog(1,ReadFlags, 1); + } else if (argc == 3) { + return (TestReadEventLog (1, ReadFlags, atoi(argv[2]))); + } + } else if (stricmp ( argv[1], "-w" ) == 0 ) { + + if ( argc < 3 ) { + return TestWriteEventLog(1); + } else if (argc == 3) { + return (TestWriteEventLog (atoi(argv[2]))); + } + + } else { + + return Usage(); + } + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + +} |