summaryrefslogtreecommitdiffstats
path: root/private/ntos/se/adtlog.c
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/se/adtlog.c
downloadNT4.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/ntos/se/adtlog.c')
-rw-r--r--private/ntos/se/adtlog.c791
1 files changed, 791 insertions, 0 deletions
diff --git a/private/ntos/se/adtlog.c b/private/ntos/se/adtlog.c
new file mode 100644
index 000000000..48fbbf3f7
--- /dev/null
+++ b/private/ntos/se/adtlog.c
@@ -0,0 +1,791 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ adtlog.c
+
+Abstract:
+
+ Auditing - Audit Record Queuing and Logging Routines
+
+ This file contains functions that construct Audit Records in self-
+ relative form from supplied information, enqueue/dequeue them and
+ write them to the log.
+
+Author:
+
+ Scott Birrell (ScottBi) November 8, 1991
+
+Environment:
+
+ Kernel Mode only
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntos.h>
+#include <zwapi.h>
+#include "sep.h"
+#include "adt.h"
+#include "adtp.h"
+#include "rmp.h"
+
+
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE,SepAdtCopyToLsaSharedMemory)
+#pragma alloc_text(PAGE,SepAdtLogAuditRecord)
+#pragma alloc_text(PAGE,SepAdtMarshallAuditRecord)
+#pragma alloc_text(PAGE,SepAdtSetAuditLogInformation)
+#pragma alloc_text(PAGE,SepDequeueWorkItem)
+#pragma alloc_text(PAGE,SepQueueWorkItem)
+
+#endif
+
+VOID
+SepAdtLogAuditRecord(
+ IN PSE_ADT_PARAMETER_ARRAY AuditParameters
+ )
+
+/*++
+
+Routine Description:
+
+ This function manages the logging of Audit Records. It provides the
+ single interface to the Audit Logging component from the Audit/Alarm
+ generation routines. The function constructs an Audit Record in
+ self-relative format from the information provided and appends it to
+ the Audit Record Queue, a doubly-linked list of Audit Records awaiting
+ output to the Audit Log. A dedicated thread reads this queue, writing
+ Audit Records to the Audit Log and removing them from the Audit Queue.
+
+Arguments:
+
+ AuditEventType - Specifies the type of the Audit Event described by
+ the audit information provided.
+
+ AuditInformation - Pointer to buffer containing captured auditing
+ information related to an Audit Event of type AuditEventType.
+
+Return Value:
+
+ STATUS_SUCCESS
+ STATUS_UNSUCCESSFUL - Audit record was not queued
+ STATUS_INSUFFICIENT_RESOURCES - unable to allocate heap
+
+--*/
+
+{
+ NTSTATUS Status;
+ PSEP_LSA_WORK_ITEM AuditWorkItem;
+
+ PAGED_CODE();
+
+ AuditWorkItem = ExAllocatePoolWithTag( PagedPool, sizeof( SEP_LSA_WORK_ITEM ), 'iAeS' );
+
+ if ( AuditWorkItem == NULL ) {
+
+ SepAuditFailed();
+ return;
+ }
+
+ AuditWorkItem->Tag = SepAuditRecord;
+ AuditWorkItem->CommandNumber = LsapWriteAuditMessageCommand;
+ AuditWorkItem->ReplyBuffer = NULL;
+ AuditWorkItem->ReplyBufferLength = 0;
+ AuditWorkItem->CleanupFunction = NULL;
+
+ //
+ // Build an Audit record in self-relative format from the supplied
+ // Audit Information.
+ //
+
+ Status = SepAdtMarshallAuditRecord(
+ AuditParameters,
+ (PSE_ADT_PARAMETER_ARRAY *) &AuditWorkItem->CommandParams.BaseAddress,
+ &AuditWorkItem->CommandParamsMemoryType
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Extract the length of the Audit Record. Store it as the length
+ // of the Command Parameters buffer.
+ //
+
+ AuditWorkItem->CommandParamsLength =
+ ((PSE_ADT_PARAMETER_ARRAY) AuditWorkItem->CommandParams.BaseAddress)->Length;
+
+ //
+ // If we're going to crash on a discarded audit, ignore the queue bounds
+ // check and force the item onto the queue.
+ //
+
+ if (!SepQueueWorkItem( AuditWorkItem, (BOOLEAN)(SepCrashOnAuditFail ? TRUE : FALSE) )) {
+
+ ExFreePool( AuditWorkItem->CommandParams.BaseAddress );
+ ExFreePool( AuditWorkItem );
+
+ //
+ // We failed to put the record on the queue. Take whatever action is
+ // appropriate.
+ //
+
+ SepAuditFailed();
+ }
+
+ } else {
+
+ ExFreePool( AuditWorkItem );
+ SepAuditFailed();
+ }
+}
+
+
+
+VOID
+SepAuditFailed(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Bugchecks the system due to a missed audit (optional requirement
+ for C2 compliance).
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES Obja;
+ HANDLE KeyHandle;
+ UNICODE_STRING KeyName;
+ UNICODE_STRING ValueName;
+ UCHAR NewValue;
+
+ ASSERT(sizeof(UCHAR) == sizeof(BOOLEAN));
+
+ if (!SepCrashOnAuditFail) {
+ return;
+ }
+
+ //
+ // Turn off flag in the registry that controls crashing on audit failure
+ //
+
+ RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa");
+
+ InitializeObjectAttributes( &Obja,
+ &KeyName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ do {
+
+ Status = ZwOpenKey(
+ &KeyHandle,
+ KEY_SET_VALUE,
+ &Obja
+ );
+
+ } while ((Status == STATUS_INSUFFICIENT_RESOURCES) || (Status == STATUS_NO_MEMORY));
+
+ //
+ // If the LSA key isn't there, he's got big problems. But don't crash.
+ //
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ SepCrashOnAuditFail = FALSE;
+ return;
+ }
+
+ if (!NT_SUCCESS( Status )) {
+ goto bugcheck;
+ }
+
+ RtlInitUnicodeString( &ValueName, CRASH_ON_AUDIT_FAIL_VALUE );
+
+ NewValue = LSAP_ALLOW_ADIMIN_LOGONS_ONLY;
+
+ do {
+
+ Status = ZwSetValueKey( KeyHandle,
+ &ValueName,
+ 0,
+ REG_NONE,
+ &NewValue,
+ sizeof(UCHAR)
+ );
+
+ } while ((Status == STATUS_INSUFFICIENT_RESOURCES) || (Status == STATUS_NO_MEMORY));
+ ASSERT(NT_SUCCESS(Status));
+
+ if (!NT_SUCCESS( Status )) {
+ goto bugcheck;
+ }
+
+ do {
+
+ Status = ZwFlushKey( KeyHandle );
+
+ } while ((Status == STATUS_INSUFFICIENT_RESOURCES) || (Status == STATUS_NO_MEMORY));
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // go boom.
+ //
+
+bugcheck:
+
+ KeBugCheck(AUDIT_FAILURE);
+}
+
+
+
+NTSTATUS
+SepAdtMarshallAuditRecord(
+ IN PSE_ADT_PARAMETER_ARRAY AuditParameters,
+ OUT PSE_ADT_PARAMETER_ARRAY *MarshalledAuditParameters,
+ OUT PSEP_RM_LSA_MEMORY_TYPE RecordMemoryType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will take an AuditParamters structure and create
+ a new AuditParameters structure that is suitable for sending
+ to LSA. It will be in self-relative form and allocated as
+ a single chunk of memory.
+
+Arguments:
+
+
+ AuditParameters - A filled in set of AuditParameters to be marshalled.
+
+ MarshalledAuditParameters - Returns a pointer to a block of heap memory
+ containing the passed AuditParameters in self-relative form suitable
+ for passing to LSA.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG i;
+ ULONG TotalSize = sizeof( SE_ADT_PARAMETER_ARRAY );
+ PUNICODE_STRING TargetString;
+ PCHAR Base;
+ ULONG BaseIncr;
+
+ PAGED_CODE();
+
+ //
+ // Calculate the total size required for the passed AuditParameters
+ // block. This calculation will probably be an overestimate of the
+ // amount of space needed, because data smaller that 2 dwords will
+ // be stored directly in the parameters structure, but their length
+ // will be counted here anyway. The overestimate can't be more than
+ // 24 dwords, and will never even approach that amount, so it isn't
+ // worth the time it would take to avoid it.
+ //
+
+ for (i=0; i<AuditParameters->ParameterCount; i++) {
+ TotalSize += (ULONG)LongAlign(AuditParameters->Parameters[i].Length);
+ }
+
+ //
+ // Allocate a big enough block of memory to hold everything.
+ // If it fails, quietly abort, since there isn't much else we
+ // can do.
+ //
+
+ *MarshalledAuditParameters = ExAllocatePoolWithTag( PagedPool, TotalSize, 'pAeS' );
+
+ if (*MarshalledAuditParameters == NULL) {
+
+ *RecordMemoryType = SepRmNoMemory;
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ *RecordMemoryType = SepRmPagedPoolMemory;
+
+ RtlMoveMemory (
+ *MarshalledAuditParameters,
+ AuditParameters,
+ sizeof( SE_ADT_PARAMETER_ARRAY )
+ );
+
+ (*MarshalledAuditParameters)->Length = TotalSize;
+ (*MarshalledAuditParameters)->Flags = SE_ADT_PARAMETERS_SELF_RELATIVE;
+
+ //
+ // Start walking down the list of parameters and marshall them
+ // into the target buffer.
+ //
+
+ Base = (PCHAR) ((PCHAR)(*MarshalledAuditParameters) + sizeof( SE_ADT_PARAMETER_ARRAY ));
+
+ for (i=0; i<AuditParameters->ParameterCount; i++) {
+
+
+ switch (AuditParameters->Parameters[i].Type) {
+ case SeAdtParmTypeNone:
+ case SeAdtParmTypeUlong:
+ case SeAdtParmTypeLogonId:
+ case SeAdtParmTypeNoLogonId:
+ case SeAdtParmTypeAccessMask:
+ {
+ //
+ // Nothing to do for this
+ //
+
+ break;
+
+ }
+ case SeAdtParmTypeString:
+ case SeAdtParmTypeFileSpec:
+ {
+ PUNICODE_STRING SourceString;
+ //
+ // We must copy the body of the unicode string
+ // and then copy the body of the string. Pointers
+ // must be turned into offsets.
+
+ TargetString = (PUNICODE_STRING)Base;
+
+ SourceString = AuditParameters->Parameters[i].Address;
+
+ *TargetString = *SourceString;
+
+ //
+ // Reset the data pointer in the output parameters to
+ // 'point' to the new string structure.
+ //
+
+ (*MarshalledAuditParameters)->Parameters[i].Address = Base - (ULONG)(*MarshalledAuditParameters);
+
+ Base += sizeof( UNICODE_STRING );
+
+ RtlMoveMemory( Base, SourceString->Buffer, SourceString->Length );
+
+ //
+ // Make the string buffer in the target string point to where we
+ // just copied the data.
+ //
+
+ TargetString->Buffer = (PWSTR)(Base - (ULONG)(*MarshalledAuditParameters));
+
+ BaseIncr = (ULONG)LongAlign(SourceString->Length);
+
+ Base += BaseIncr;
+
+ break;
+ }
+ case SeAdtParmTypeSid:
+ {
+ PSID TargetSid = (PSID) Base;
+ PSID SourceSid = AuditParameters->Parameters[i].Address;
+
+ //
+ // Copy the Sid into the output buffer
+ //
+
+ RtlMoveMemory( TargetSid, SourceSid, RtlLengthSid( SourceSid ) );
+
+ //
+ // Reset the 'address' of the Sid to be its offset in the
+ // buffer.
+ //
+
+ (*MarshalledAuditParameters)->Parameters[i].Address = Base - (ULONG)(*MarshalledAuditParameters);
+
+ BaseIncr = (ULONG)LongAlign(RtlLengthSid( SourceSid ));
+
+ Base += BaseIncr;
+
+ break;
+ }
+ case SeAdtParmTypePrivs:
+ {
+ PPRIVILEGE_SET TargetPrivileges = (PPRIVILEGE_SET) Base;
+ PPRIVILEGE_SET SourcePrivileges = AuditParameters->Parameters[i].Address;
+
+ RtlCopyMemory( TargetPrivileges, SourcePrivileges, SepPrivilegeSetSize( SourcePrivileges ));
+
+ (*MarshalledAuditParameters)->Parameters[i].Address = Base - (ULONG)(*MarshalledAuditParameters);
+
+ BaseIncr = (ULONG)LongAlign(SepPrivilegeSetSize( SourcePrivileges ));
+
+ Base += BaseIncr;
+
+ break;
+ }
+ default:
+ {
+ //
+ // We got passed junk, complain.
+ //
+
+ ASSERT( FALSE );
+ break;
+ }
+ }
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+VOID
+SepAdtSetAuditLogInformation(
+ IN PPOLICY_AUDIT_LOG_INFO AuditLogInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function stores Audit Log Information in the Reference Monitor's
+ in-memory database. This information contains parameters such as Audit
+ Log size etc. It is the caller's responsibility to ensure that the
+ supplied information is valid.
+
+ NOTE: After initialization, this function is only called by the LSA
+ via a Reference Monitor command. This is a necessary restriction
+ because the Audit Log Information stored in the LSA Database must
+ remain in sync
+
+Arguments:
+
+ AuditLogInformation - Pointer to Audit Log Information structure.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ //
+ // Acquire Reference Monitor Database write lock.
+ //
+
+ SepRmAcquireDbWriteLock();
+
+ //
+ // Write the information
+ //
+
+ SepAdtLogInformation = *AuditLogInformation;
+
+ //
+ // Release Reference Monitor Database write lock
+ //
+
+ SepRmReleaseDbWriteLock();
+}
+
+
+
+NTSTATUS
+SepAdtCopyToLsaSharedMemory(
+ IN HANDLE LsaProcessHandle,
+ IN PVOID Buffer,
+ IN ULONG BufferLength,
+ OUT PVOID *LsaBufferAddress
+ )
+
+/*++
+
+Routine Description:
+
+ This function allocates memory shared with the LSA and optionally copies
+ a given buffer to it.
+
+Arguments:
+
+ LsaProcessHandle - Specifies a handle to the Lsa Process.
+
+ Buffer - Pointer to the buffer to be copied.
+
+ BufferLength - Length of buffer.
+
+ LsaBufferAddress - Receives the address of the buffer valid in the
+ Lsa process context.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ Result codes returned by called routines.
+--*/
+
+{
+ NTSTATUS Status, SecondaryStatus;
+ PVOID OutputLsaBufferAddress = NULL;
+ ULONG RegionSize = BufferLength;
+
+ PAGED_CODE();
+
+ Status = ZwAllocateVirtualMemory(
+ LsaProcessHandle,
+ &OutputLsaBufferAddress,
+ 0,
+ &RegionSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CopyToLsaSharedMemoryError;
+ }
+
+ Status = ZwWriteVirtualMemory(
+ LsaProcessHandle,
+ OutputLsaBufferAddress,
+ Buffer,
+ BufferLength,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CopyToLsaSharedMemoryError;
+ }
+
+ *LsaBufferAddress = OutputLsaBufferAddress;
+ return(Status);
+
+CopyToLsaSharedMemoryError:
+
+ //
+ // If we allocated memory, free it.
+ //
+
+ if (OutputLsaBufferAddress != NULL) {
+
+ RegionSize = 0;
+
+ SecondaryStatus = ZwFreeVirtualMemory(
+ LsaProcessHandle,
+ &OutputLsaBufferAddress,
+ &RegionSize,
+ MEM_RELEASE
+ );
+
+ ASSERT(NT_SUCCESS(SecondaryStatus));
+
+ OutputLsaBufferAddress = NULL;
+ }
+
+ return(Status);
+}
+
+
+BOOLEAN
+SepQueueWorkItem(
+ IN PSEP_LSA_WORK_ITEM LsaWorkItem,
+ IN BOOLEAN ForceQueue
+ )
+
+/*++
+
+Routine Description:
+
+ Puts the passed work item on the queue to be passed to LSA,
+ and returns the state of the queue upon arrival.
+
+Arguments:
+
+ LsaWorkItem - Pointer to the work item to be queued.
+
+ ForceQueue - Indicate that this item is not to be discarded
+ due because of a full queue.
+
+Return Value:
+
+ TRUE - The item was successfully queued.
+
+ FALSE - The item was not queued and must be discarded.
+
+--*/
+
+{
+ BOOLEAN rc = TRUE;
+
+ PAGED_CODE();
+
+#if 0
+
+ DbgPrint("Queueing an audit\n");
+
+#endif
+
+ SepLockLsaQueue();
+
+ if (SepAdtDiscardingAudits && !ForceQueue) {
+
+ if (SepAdtCurrentListLength < SepAdtMinListLength) {
+
+ //
+ // We need to generate an audit saying how many audits we've
+ // discarded.
+ //
+ // Since we have the mutex protecting the Audit queue, we don't
+ // have to worry about anyone coming along and logging an
+ // audit. But *we* can, since a mutex may be acquired recursively.
+ //
+ // Since we are so protected, turn off the SepAdtDiscardingAudits
+ // flag here so that we don't come through this path again.
+ //
+
+ SepAdtDiscardingAudits = FALSE;
+
+ SepAdtGenerateDiscardAudit();
+#if 0
+ DbgPrint("Auditing resumed\n");
+#endif
+
+ //
+ // We must assume that that worked, so clear the discard count.
+ //
+
+ SepAdtCountEventsDiscarded = 0;
+
+ //
+ // Our 'audits discarded' audit is now on the queue,
+ // continue logging the one we started with.
+ //
+
+ } else {
+
+ //
+ // We are not yet below our low water mark. Toss
+ // this audit and increment the discard count.
+ //
+
+ SepAdtCountEventsDiscarded++;
+ rc = FALSE;
+ goto Exit;
+ }
+ }
+
+ if (SepAdtCurrentListLength < SepAdtMaxListLength || ForceQueue) {
+
+ InsertTailList(&SepLsaQueue, &LsaWorkItem->List);
+
+ if (++SepAdtCurrentListLength == 1) {
+
+#if 0
+ DbgPrint("Queueing a work item\n");
+#endif
+
+ ExInitializeWorkItem( &SepExWorkItem.WorkItem,
+ (PWORKER_THREAD_ROUTINE) SepRmCallLsa,
+ &SepExWorkItem
+ );
+
+ ExQueueWorkItem( &SepExWorkItem.WorkItem, DelayedWorkQueue );
+ }
+
+ } else {
+
+ //
+ // There is no room for this audit on the queue,
+ // so change our state to 'discarding' and tell
+ // the caller to toss this audit.
+ //
+
+ SepAdtDiscardingAudits = TRUE;
+
+#if 0
+ DbgPrint("Starting to discard audits\n");
+#endif
+
+ rc = FALSE;
+ }
+
+Exit:
+
+ SepUnlockLsaQueue();
+ return( rc );
+}
+
+
+
+PSEP_LSA_WORK_ITEM
+SepDequeueWorkItem(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Removes the top element of the SepLsaQueue and returns the
+ next element if there is one, NULL otherwise.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ A pointer to the next SEP_LSA_WORK_ITEM, or NULL.
+
+--*/
+
+{
+ PSEP_LSA_WORK_ITEM OldWorkQueueItem;
+
+ PAGED_CODE();
+
+ SepLockLsaQueue();
+
+ OldWorkQueueItem = (PSEP_LSA_WORK_ITEM)RemoveHeadList(&SepLsaQueue);
+ OldWorkQueueItem->List.Flink = NULL;
+ ExFreePool( OldWorkQueueItem );
+
+ SepAdtCurrentListLength--;
+
+#if 0
+ DbgPrint("Removing item\n");
+#endif
+
+ if (IsListEmpty( &SepLsaQueue )) {
+
+ SepUnlockLsaQueue();
+ return( NULL );
+ }
+
+ //
+ // We know there's something on the queue now, so we
+ // can unlock it.
+ //
+
+ SepUnlockLsaQueue();
+ return((PSEP_LSA_WORK_ITEM)(&SepLsaQueue)->Flink);
+}