mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3687 lines
92 KiB
3687 lines
92 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
adtlog.c
|
|
|
|
Abstract:
|
|
|
|
Local Security Authority - Audit Log Management
|
|
|
|
Functions in this module access the Audit Log via the Event Logging
|
|
interface.
|
|
|
|
Author:
|
|
|
|
Scott Birrell (ScottBi) November 20, 1991
|
|
Robert Reichel (RobertRe) April 4, 1992
|
|
|
|
Environment:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <msaudite.h>
|
|
#include "lsasrvp.h"
|
|
#include "ausrvp.h"
|
|
#include "adtp.h"
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Private data for Audit Logs and Events //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Audit Events Information.
|
|
//
|
|
|
|
LSARM_POLICY_AUDIT_EVENTS_INFO LsapAdtEventsInformation;
|
|
|
|
//
|
|
// Audit Log Information. This must be kept in sync with the information
|
|
// in the Lsa Database.
|
|
//
|
|
|
|
POLICY_AUDIT_LOG_INFO LsapAdtLogInformation;
|
|
|
|
//
|
|
// Audit Log Full Information.
|
|
//
|
|
|
|
POLICY_AUDIT_FULL_QUERY_INFO LsapAdtLogFullInformation;
|
|
|
|
//
|
|
// Audit Log Handle (returned by Event Logger).
|
|
//
|
|
|
|
HANDLE LsapAdtLogHandle = NULL;
|
|
|
|
//
|
|
// Audit Log Full Event Handle
|
|
//
|
|
|
|
HANDLE LsapAdtLogFullEventHandle;
|
|
|
|
//
|
|
// Lsa Global flagS to indicate if we are auditing logon events.
|
|
//
|
|
|
|
BOOLEAN LsapAuditSuccessfulLogons = FALSE;
|
|
BOOLEAN LsapAuditFailedLogons = FALSE;
|
|
|
|
//
|
|
// Flag to tell us if we're supposed to crash when an audit fails.
|
|
//
|
|
|
|
BOOLEAN LsapAdtCrashOnAuditFail = FALSE;
|
|
|
|
RTL_CRITICAL_SECTION LsapAdtQueueLock;
|
|
RTL_CRITICAL_SECTION LsapAdtLogFullLock;
|
|
BOOLEAN LsapAdtSignalFullInProgress;
|
|
|
|
LSAP_ADT_LOG_QUEUE_HEAD LsapAdtLogQueue;
|
|
|
|
//
|
|
// Maintain the length of the audit queue.
|
|
// If it gets too long, start discarding audits
|
|
// so we don't suck up all of memory
|
|
//
|
|
|
|
ULONG LsapAuditQueueLength = 0;
|
|
ULONG LsapAuditQueueEventsDiscarded = 0;
|
|
|
|
#define MAX_AUDIT_QUEUE_LENGTH 500
|
|
|
|
//
|
|
// Private prototypes
|
|
//
|
|
|
|
VOID
|
|
LsapAdtAuditDiscardedAudits(
|
|
ULONG NumberOfEventsDiscarded
|
|
);
|
|
|
|
//////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS
|
|
LsapAdtWriteLogWrkr(
|
|
IN PLSA_COMMAND_MESSAGE CommandMessage,
|
|
OUT PLSA_REPLY_MESSAGE ReplyMessage
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles a command, received from the Reference Monitor via
|
|
the LPC link, to write a record to the Audit Log. It is a wrapper which
|
|
deals with any LPC unmarshalling.
|
|
|
|
Arguments:
|
|
|
|
CommandMessage - Pointer to structure containing LSA command message
|
|
information consisting of an LPC PORT_MESSAGE structure followed
|
|
by the command number (LsapWriteAuditMessageCommand). This command
|
|
contains an Audit Message Packet (TBS) as a parameter.
|
|
|
|
ReplyMessage - Pointer to structure containing LSA reply message
|
|
information consisting of an LPC PORT_MESSAGE structure followed
|
|
by the command ReturnedStatus field in which a status code from the
|
|
command will be returned.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully.
|
|
|
|
Currently, all other errors from called routines are suppressed.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG RegionSize = 0;
|
|
|
|
PSE_ADT_PARAMETER_ARRAY AuditRecord = NULL;
|
|
|
|
//
|
|
// Strict check that command is correct.
|
|
//
|
|
|
|
ASSERT( CommandMessage->CommandNumber == LsapWriteAuditMessageCommand );
|
|
|
|
//
|
|
// Obtain a pointer to the Audit Record. The Audit Record is
|
|
// either stored as immediate data within the Command Message,
|
|
// or it is stored as a buffer. In the former case, the Audit Record
|
|
// begins at CommandMessage->CommandParams and in the latter case,
|
|
// it is stored at the address located at CommandMessage->CommandParams.
|
|
//
|
|
|
|
if (CommandMessage->CommandParamsMemoryType == SepRmImmediateMemory) {
|
|
|
|
AuditRecord = (PSE_ADT_PARAMETER_ARRAY) CommandMessage->CommandParams;
|
|
|
|
} else {
|
|
|
|
AuditRecord = *((PSE_ADT_PARAMETER_ARRAY *) CommandMessage->CommandParams);
|
|
}
|
|
|
|
//
|
|
// Call worker to queue Audit Record for writing to the log.
|
|
//
|
|
|
|
Status = LsapAdtWriteLog( AuditRecord, (ULONG) 0 );
|
|
|
|
UNREFERENCED_PARAMETER(ReplyMessage); // Intentionally not referenced
|
|
|
|
//
|
|
// The status value returned from LsapAdtWriteLog() is intentionally
|
|
// ignored, since there is no meaningful action that the client
|
|
// (i.e. kernel) if this LPC call can take. If an error occurs in
|
|
// trying to append an Audit Record to the log, the LSA handles the
|
|
// error.
|
|
//
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtOpenLog(
|
|
OUT PHANDLE AuditLogHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function opens the Audit Log.
|
|
|
|
Arguments:
|
|
|
|
AuditLogHandle - Receives the Handle to the Audit Log.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
All result codes are generated by called routines.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
NTSTATUS SecondaryStatus = STATUS_SUCCESS;
|
|
UNICODE_STRING ModuleName;
|
|
|
|
RtlInitUnicodeString( &ModuleName, L"Security");
|
|
|
|
Status = ElfRegisterEventSourceW (
|
|
NULL,
|
|
&ModuleName,
|
|
AuditLogHandle
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto OpenLogError;
|
|
}
|
|
|
|
|
|
OpenLogFinish:
|
|
|
|
return(Status);
|
|
|
|
OpenLogError:
|
|
|
|
//
|
|
// Check for Log Full and signal the condition.
|
|
//
|
|
|
|
if (Status != STATUS_LOG_FILE_FULL) {
|
|
|
|
goto OpenLogFinish;
|
|
}
|
|
|
|
//
|
|
// The log is full. Deal with this condition according to
|
|
// the local policy options in effect.
|
|
//
|
|
|
|
SecondaryStatus = LsapAdtSignalLogFull();
|
|
|
|
goto OpenLogFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtQueueRecord(
|
|
IN PSE_ADT_PARAMETER_ARRAY AuditParameters,
|
|
IN ULONG Options
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Puts passed audit record on the queue to be logged.
|
|
|
|
This routine will convert the passed AuditParameters structure
|
|
into self-relative form if it is not already. It will then
|
|
allocate a buffer out of the local heap and copy the audit
|
|
information into the buffer and put it on the audit queue.
|
|
|
|
The buffer will be freed when the queue is cleared.
|
|
|
|
Arguments:
|
|
|
|
AuditRecord - Contains the information to be audited.
|
|
|
|
Options - Speciifies optional actions to be taken
|
|
|
|
LSAP_ADT_LOG_QUEUE_PREPEND - Put record on front of queue. If
|
|
not specified, the record will be appended to the queue.
|
|
This option is specified when a special audit record of the
|
|
type AuditEventLogNoLongerFull is generated, so that the
|
|
record will be written out before others in the queue. The
|
|
presence of a record of this type in the log indicates that
|
|
one or more preceding Audit Records may have been lost
|
|
tdue to the log filling up.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
STATUS_SUCCESS - The call completed successfully.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
|
|
such as memory, to allocate a buffer to contain the record.
|
|
--*/
|
|
|
|
{
|
|
ULONG AuditRecordLength;
|
|
PLSAP_ADT_QUEUED_RECORD QueuedAuditRecord;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG AllocationSize;
|
|
PSE_ADT_PARAMETER_ARRAY MarshalledAuditParameters;
|
|
BOOLEAN AcquiredLock = FALSE;
|
|
BOOLEAN FreeWhenDone = FALSE;
|
|
|
|
//
|
|
// Check to see if the list is above the maximum length.
|
|
// If it gets this high, it is more than likely that the
|
|
// eventlog service is not going to start at all, so
|
|
// start tossing audits.
|
|
//
|
|
// Don't do this if crash on audit is set.
|
|
//
|
|
|
|
if ((LsapAuditQueueLength > MAX_AUDIT_QUEUE_LENGTH) && !LsapCrashOnAuditFail) {
|
|
LsapAuditQueueEventsDiscarded++;
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
//
|
|
// Gather up all of the passed information into a single
|
|
// block that can be placed on the queue.
|
|
//
|
|
|
|
if ( AuditParameters->Flags & SE_ADT_PARAMETERS_SELF_RELATIVE ) {
|
|
|
|
MarshalledAuditParameters = AuditParameters;
|
|
|
|
} else {
|
|
|
|
Status = LsapAdtMarshallAuditRecord(
|
|
AuditParameters,
|
|
&MarshalledAuditParameters
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
|
|
goto QueueAuditRecordError;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Indicate that we're to free this structure when we're
|
|
// finished
|
|
//
|
|
|
|
FreeWhenDone = TRUE;
|
|
}
|
|
}
|
|
|
|
Status = LsapAdtAcquireLogQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto QueueAuditRecordError;
|
|
}
|
|
|
|
AcquiredLock = TRUE;
|
|
|
|
//
|
|
// Copy the now self-relative audit record into a buffer
|
|
// that can be placed on the queue.
|
|
//
|
|
|
|
AuditRecordLength = MarshalledAuditParameters->Length;
|
|
AllocationSize = AuditRecordLength + sizeof( PLSAP_ADT_QUEUED_RECORD );
|
|
|
|
QueuedAuditRecord = (PLSAP_ADT_QUEUED_RECORD)LsapAllocateLsaHeap( AllocationSize );
|
|
|
|
if ( QueuedAuditRecord == NULL ) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto QueueAuditRecordError;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
RtlCopyMemory( &QueuedAuditRecord->Buffer, MarshalledAuditParameters, AuditRecordLength );
|
|
|
|
//
|
|
// We are finished with the marshalled audit record, free it.
|
|
//
|
|
|
|
if ( FreeWhenDone ) {
|
|
LsapFreeLsaHeap( MarshalledAuditParameters );
|
|
FreeWhenDone = FALSE;
|
|
}
|
|
|
|
if (!(Options & LSAP_ADT_LOG_QUEUE_PREPEND)) {
|
|
|
|
//
|
|
// Append the new record to the back of the list. Update the
|
|
// pointer to the last record and, if the queue was empty, update
|
|
// the pointer to the first record.
|
|
//
|
|
|
|
if (LsapAdtLogQueue.LastQueuedRecord != NULL) {
|
|
|
|
LsapAdtLogQueue.LastQueuedRecord->Next = QueuedAuditRecord;
|
|
|
|
} else {
|
|
|
|
ASSERT( LsapAdtLogQueue.FirstQueuedRecord == NULL );
|
|
LsapAdtLogQueue.FirstQueuedRecord = QueuedAuditRecord;
|
|
}
|
|
|
|
LsapAdtLogQueue.LastQueuedRecord = QueuedAuditRecord;
|
|
QueuedAuditRecord->Next = NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Record is to be prepended to queue.
|
|
//
|
|
|
|
QueuedAuditRecord->Next = LsapAdtLogQueue.FirstQueuedRecord;
|
|
LsapAdtLogQueue.FirstQueuedRecord = QueuedAuditRecord;
|
|
|
|
if (LsapAdtLogQueue.LastQueuedRecord == NULL) {
|
|
|
|
LsapAdtLogQueue.LastQueuedRecord = QueuedAuditRecord;
|
|
}
|
|
}
|
|
|
|
LsapAuditQueueLength++;
|
|
|
|
QueueAuditRecordFinish:
|
|
|
|
//
|
|
// If necessary, release the Audit Queue Lock.
|
|
//
|
|
|
|
if (AcquiredLock) {
|
|
|
|
LsapAdtReleaseLogQueueLock();
|
|
}
|
|
|
|
return(Status);
|
|
|
|
QueueAuditRecordError:
|
|
|
|
if ( FreeWhenDone ) {
|
|
LsapFreeLsaHeap( MarshalledAuditParameters );
|
|
}
|
|
|
|
goto QueueAuditRecordFinish;
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapAdtAuditLogon(
|
|
IN USHORT EventCategory,
|
|
IN ULONG EventID,
|
|
IN USHORT EventType,
|
|
PUNICODE_STRING AccountName,
|
|
PUNICODE_STRING AuthenticatingAuthority,
|
|
PUNICODE_STRING Source,
|
|
PUNICODE_STRING SourceDevice,
|
|
PUNICODE_STRING PackageName,
|
|
SECURITY_LOGON_TYPE LogonType,
|
|
PSID UserSid,
|
|
LUID AuthenticationId,
|
|
NTSTATUS LogonStatus,
|
|
PUNICODE_STRING WorkstationName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generates an audit of a logon event as appropriate.
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING SpareString;
|
|
UNICODE_STRING AuthenticationIdString;
|
|
BOOLEAN FreeWhenDone = FALSE;
|
|
SE_ADT_PARAMETER_ARRAY AuditParameters;
|
|
BOOLEAN AuditingSuccess;
|
|
BOOLEAN AuditingFailure;
|
|
PSID LocalSystemSid = NULL;
|
|
UNICODE_STRING NullString;
|
|
|
|
RtlInitUnicodeString( &NullString, L"" );
|
|
|
|
RtlInitUnicodeString( &SpareString, L"Security");
|
|
|
|
AuditingFailure = (EventType == EVENTLOG_AUDIT_FAILURE) && LsapAuditFailedLogons;
|
|
AuditingSuccess = (EventType == EVENTLOG_AUDIT_SUCCESS) && LsapAuditSuccessfulLogons;
|
|
|
|
if ( AuditingFailure || AuditingSuccess ) {
|
|
|
|
//
|
|
// Build an audit parameters structure.
|
|
//
|
|
|
|
RtlZeroMemory (
|
|
(PVOID) &AuditParameters,
|
|
sizeof( AuditParameters )
|
|
);
|
|
|
|
AuditParameters.CategoryId = EventCategory;
|
|
AuditParameters.AuditId = EventID;
|
|
AuditParameters.Type = EventType;
|
|
AuditParameters.ParameterCount = 0;
|
|
|
|
//
|
|
// User Sid
|
|
//
|
|
|
|
if ( AuditingSuccess ) {
|
|
|
|
LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, UserSid );
|
|
|
|
} else {
|
|
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
|
|
Status = RtlAllocateAndInitializeSid(
|
|
&NtAuthority,
|
|
1,
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&LocalSystemSid
|
|
);
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
LsapAuditFailed();
|
|
goto Finish;
|
|
|
|
} else {
|
|
|
|
LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, LocalSystemSid );
|
|
}
|
|
}
|
|
|
|
AuditParameters.ParameterCount++;
|
|
|
|
//
|
|
// Subsystem name (if available)
|
|
//
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &SpareString );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
//
|
|
// Account name
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(AccountName)) {
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, AccountName );
|
|
} else {
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &NullString );
|
|
}
|
|
|
|
AuditParameters.ParameterCount++;
|
|
|
|
//
|
|
// Authenticating Authority (domain name)
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(AuthenticatingAuthority)) {
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, AuthenticatingAuthority );
|
|
} else {
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &NullString );
|
|
|
|
}
|
|
AuditParameters.ParameterCount++;
|
|
|
|
if ( AuditingSuccess ) {
|
|
|
|
//
|
|
// Logon Id (as a string)
|
|
//
|
|
|
|
Status = LsapAdtBuildLuidString(
|
|
&AuthenticationId,
|
|
&AuthenticationIdString,
|
|
&FreeWhenDone
|
|
);
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &AuthenticationIdString );
|
|
|
|
} else {
|
|
|
|
LsapAuditFailed();
|
|
goto Finish;
|
|
}
|
|
|
|
AuditParameters.ParameterCount++;
|
|
}
|
|
|
|
//
|
|
// Logon Type
|
|
//
|
|
|
|
LsapSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, LogonType );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
//
|
|
// Source
|
|
//
|
|
|
|
if ( ARGUMENT_PRESENT( Source )) {
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, Source );
|
|
|
|
} else {
|
|
|
|
//
|
|
// No need to do anything here, since an empty entry will turn
|
|
// into a '-' in the output
|
|
//
|
|
|
|
}
|
|
|
|
AuditParameters.ParameterCount++;
|
|
|
|
//
|
|
// Authentication Package
|
|
//
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, PackageName );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
//
|
|
// Authentication Package
|
|
//
|
|
|
|
if ( ARGUMENT_PRESENT( WorkstationName )) {
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, WorkstationName );
|
|
}
|
|
|
|
AuditParameters.ParameterCount++;
|
|
|
|
( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
|
|
|
|
Finish:
|
|
|
|
if ( LocalSystemSid != NULL ) {
|
|
LsapFreeLsaHeap( LocalSystemSid );
|
|
}
|
|
|
|
if ( FreeWhenDone ) {
|
|
LsapFreeLsaHeap( AuthenticationIdString.Buffer );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapAdtAuditLogoff(
|
|
PLSAP_LOGON_SESSION Session
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generates a logoff audit. The caller is responsible for determining
|
|
if logoff auditing is necessary.
|
|
|
|
Arguments:
|
|
|
|
Session - Points to the logon session being removed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
SE_ADT_PARAMETER_ARRAY AuditParameters;
|
|
|
|
RtlZeroMemory (
|
|
(PVOID) &AuditParameters,
|
|
sizeof( AuditParameters )
|
|
);
|
|
|
|
AuditParameters.CategoryId = SE_CATEGID_LOGON;
|
|
AuditParameters.AuditId = SE_AUDITID_LOGOFF;
|
|
AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
|
|
AuditParameters.ParameterCount = 0;
|
|
|
|
//
|
|
// User Sid
|
|
//
|
|
|
|
LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, Session->UserSid );
|
|
|
|
AuditParameters.ParameterCount++;
|
|
|
|
//
|
|
// Subsystem name
|
|
//
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
|
|
|
|
AuditParameters.ParameterCount++;
|
|
|
|
//
|
|
// Logon ID
|
|
//
|
|
|
|
LsapSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, Session->LogonId );
|
|
|
|
AuditParameters.ParameterCount++;
|
|
|
|
//
|
|
// Logon Type
|
|
//
|
|
|
|
LsapSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, Session->LogonType );
|
|
|
|
AuditParameters.ParameterCount++;
|
|
|
|
( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapAdtSystemRestart(
|
|
PLSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called during LSA initialization to generate
|
|
a system restart event.
|
|
|
|
Arguments:
|
|
|
|
AuditEventsInfo - Auditing data.
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
SE_ADT_PARAMETER_ARRAY AuditParameters;
|
|
|
|
|
|
if(!AuditEventsInfo->AuditingMode) {
|
|
|
|
return;
|
|
}
|
|
|
|
if (!((AuditEventsInfo->EventAuditingOptions)[AuditCategorySystem] & POLICY_AUDIT_EVENT_SUCCESS )) {
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Construct an audit parameters array
|
|
// for the restart event.
|
|
//
|
|
|
|
RtlZeroMemory (
|
|
(PVOID) &AuditParameters,
|
|
sizeof( AuditParameters )
|
|
);
|
|
|
|
AuditParameters.CategoryId = SE_CATEGID_SYSTEM;
|
|
AuditParameters.AuditId = SE_AUDITID_SYSTEM_RESTART;
|
|
AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
|
|
AuditParameters.ParameterCount = 0;
|
|
|
|
//
|
|
// User Sid
|
|
//
|
|
|
|
LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, LsapLocalSystemSid );
|
|
|
|
AuditParameters.ParameterCount++;
|
|
|
|
//
|
|
// Subsystem name (if available)
|
|
//
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
|
|
|
|
AuditParameters.ParameterCount++;
|
|
|
|
( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtWriteLog(
|
|
IN PSE_ADT_PARAMETER_ARRAY AuditParameters OPTIONAL,
|
|
IN ULONG Options
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function appends an Audit Record and/or the content of the
|
|
Audit Record Log Queue to the Audit Log, by calling the Event Logger.
|
|
If the Audit Log becomes full, this function signals an Audit Log
|
|
Full condition. The Audit Log will be opened if necessary.
|
|
|
|
NOTE: This function may be called during initialization before
|
|
the Event Logger service has started. In that event, any Audit
|
|
Record specified will simply be added to the queue.
|
|
|
|
Arguments:
|
|
|
|
AuditRecord - Optional pointer to an Audit Record to be written to
|
|
the Audit Log. The record will first be added to the existing queue
|
|
of records waiting to be written to the log. An attempt will then
|
|
be made to write all of the records in the queue to the log. If
|
|
NULL is specified, the existing queue will be written out.
|
|
|
|
Options - Specifies optional actions to be taken.
|
|
|
|
LSAP_ADT_LOG_QUEUE_PREPEND - Prepend record to the Audit Record
|
|
queue prior to writing to the log. If not specified, the
|
|
record will be appended to the queue.
|
|
|
|
LSAP_ADT_LOG_QUEUE_DISCARD - Discard the Audit Record Queue.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN AcquiredLock = FALSE;
|
|
BOOLEAN AuditRecordFreed = FALSE;
|
|
BOOLEAN AuditRecordUnblocked = FALSE;
|
|
BOOLEAN ShutdownSystem = FALSE;
|
|
NTSTATUS Status;
|
|
NTSTATUS SecondaryStatus;
|
|
PLSAP_ADT_QUEUED_RECORD Front, Back;
|
|
|
|
|
|
SecondaryStatus = STATUS_SUCCESS;
|
|
|
|
|
|
// //
|
|
// // If we have not reached Pass 2 Audit Initialization, we need to
|
|
// // cache the audit record.
|
|
// //
|
|
//
|
|
// if (LsapAdtInitializationPass < 2) {
|
|
//
|
|
// goto WriteLogFinish;
|
|
// }
|
|
|
|
|
|
if ( Options & LSAP_ADT_LOG_QUEUE_DISCARD ) {
|
|
|
|
//
|
|
// Flush out the queue, if there is one.
|
|
//
|
|
|
|
Front = LsapAdtLogQueue.FirstQueuedRecord;
|
|
Back = NULL;
|
|
|
|
//
|
|
// Free the records in the queue.
|
|
//
|
|
|
|
while ( Front != NULL ) {
|
|
|
|
Back = Front;
|
|
Front = Front->Next;
|
|
|
|
LsapFreeLsaHeap( Back );
|
|
LsapAuditQueueLength--;
|
|
}
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
//
|
|
// If the Audit Log is not already open, attempt to open it.
|
|
// If this open is unsuccessful because the EventLog service has not
|
|
// started, queue the Audit Record if directed to do so
|
|
// via the Options parameter. If the open is unsuccessful for any
|
|
// other reason, discard the Audit Record.
|
|
//
|
|
|
|
if ( LsapAdtLogHandle == NULL ) {
|
|
|
|
if (ARGUMENT_PRESENT( AuditParameters )) {
|
|
|
|
Status = LsapAdtQueueRecord( AuditParameters, 0 );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
goto WriteLogError;
|
|
}
|
|
}
|
|
|
|
Status = LsapAdtOpenLog(&LsapAdtLogHandle);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto WriteLogFinish;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Prepare to write out all of the records in the Audit Log Queue.
|
|
// First, we need to capture the existing queue.
|
|
//
|
|
|
|
Status = LsapAdtAcquireLogQueueLock();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto WriteLogError;
|
|
}
|
|
|
|
AcquiredLock = TRUE;
|
|
|
|
Front = LsapAdtLogQueue.FirstQueuedRecord;
|
|
Back = NULL;
|
|
|
|
//
|
|
// Write out records in the queue.
|
|
//
|
|
|
|
while ( Front != NULL ) {
|
|
|
|
AuditParameters = &Front->Buffer;
|
|
|
|
LsapAdtNormalizeAuditInfo( AuditParameters );
|
|
|
|
Status = LsapAdtDemarshallAuditInfo(
|
|
AuditParameters
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Update the Audit Log Information in the Policy Object. We
|
|
// increment the Next Audit Record serial number.
|
|
//
|
|
|
|
if (LsapAdtLogInformation.NextAuditRecordId == LSAP_ADT_MAXIMUM_RECORD_ID ) {
|
|
|
|
LsapAdtLogInformation.NextAuditRecordId = 0;
|
|
}
|
|
|
|
LsapAdtLogInformation.NextAuditRecordId++;
|
|
|
|
Back = Front;
|
|
Front = Front->Next;
|
|
|
|
LsapFreeLsaHeap( Back );
|
|
LsapAuditQueueLength--;
|
|
}
|
|
|
|
if (LsapAuditQueueEventsDiscarded > 0) {
|
|
|
|
//
|
|
// We discarded some audits. Generate an audit
|
|
// so the user knows.
|
|
//
|
|
|
|
LsapAdtAuditDiscardedAudits( LsapAuditQueueEventsDiscarded );
|
|
}
|
|
|
|
//
|
|
// Make sure we don't ever try to do this again. Note that there
|
|
// is no point keeping the remainder of the queue if the Audit Log
|
|
// has become full, because a reboot is needed to clear the full
|
|
// condition.
|
|
//
|
|
|
|
LsapAdtLogQueue.FirstQueuedRecord = NULL;
|
|
LsapAdtLogQueue.LastQueuedRecord = NULL;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto WriteLogError;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Normal case, just perform the audit
|
|
//
|
|
|
|
LsapAdtNormalizeAuditInfo( AuditParameters );
|
|
|
|
Status = LsapAdtDemarshallAuditInfo(
|
|
AuditParameters
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto WriteLogError;
|
|
}
|
|
|
|
//
|
|
// Update the Audit Log Information in the Policy Object. We
|
|
// increment the Next Audit Record serial number.
|
|
//
|
|
|
|
if (LsapAdtLogInformation.NextAuditRecordId == LSAP_ADT_MAXIMUM_RECORD_ID ) {
|
|
|
|
LsapAdtLogInformation.NextAuditRecordId = 0;
|
|
}
|
|
|
|
LsapAdtLogInformation.NextAuditRecordId++;
|
|
|
|
}
|
|
|
|
|
|
WriteLogFinish:
|
|
|
|
//
|
|
// If necessary, release the LSA Audit Log Queue Lock.
|
|
//
|
|
|
|
if (AcquiredLock) {
|
|
|
|
LsapAdtReleaseLogQueueLock();
|
|
AcquiredLock = FALSE;
|
|
}
|
|
|
|
return(Status);
|
|
|
|
WriteLogError:
|
|
|
|
//
|
|
// Take whatever action we're supposed to take when an audit attempt fails.
|
|
//
|
|
|
|
LsapAuditFailed();
|
|
|
|
//
|
|
// If the error is other than Audit Log Full, just cleanup and return
|
|
// the error.
|
|
//
|
|
|
|
if ((Status != STATUS_DISK_FULL) && (Status != STATUS_LOG_FILE_FULL)) {
|
|
|
|
goto WriteLogFinish;
|
|
}
|
|
|
|
//
|
|
// The Audit Log is full. Deal with this condition according to
|
|
// local policy in effect.
|
|
//
|
|
|
|
SecondaryStatus = LsapAdtSignalLogFull();
|
|
|
|
//
|
|
// If there are Audit Records in the cache, discard them.
|
|
//
|
|
|
|
SecondaryStatus = LsapAdtWriteLog(NULL, LSAP_ADT_LOG_QUEUE_DISCARD);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = SecondaryStatus;
|
|
}
|
|
|
|
goto WriteLogFinish;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsarClearAuditLog(
|
|
IN LSAPR_HANDLE PolicyHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function used to clear the Audit Log but has been superseded
|
|
by the Event Viewer functionality provided for this purpose. To
|
|
preserve compatibility with existing RPC interfaces, this server
|
|
stub is retained.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle to an open Policy Object.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
STATUS_NOT_IMPLEMENTED - This routine is not implemented.
|
|
--*/
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER( PolicyHandle );
|
|
return(STATUS_NOT_IMPLEMENTED);
|
|
}
|
|
|
|
|
|
#if 0
|
|
|
|
NTSTATUS
|
|
LsapAdtSetInfoLog(
|
|
IN LSAPR_HANDLE PolicyHandle,
|
|
IN PPOLICY_AUDIT_LOG_INFO PolicyAuditLogInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function updates the Audit Log Information, such as the size
|
|
of the Audit Log, Retention Period for Audit Records. The update
|
|
is done in three places, LSA's global data, the PolAdtLg attribute
|
|
of the Policy Object and the MaxSize and RetentionPeriod values of
|
|
the Audit Log's Registry Key.
|
|
|
|
If the Audit Log Full state changes from "full" to "not-full", the
|
|
Audit Record Cache will be emptied if necessary.
|
|
|
|
WARNING! The LSA Database must be locked before calling this function
|
|
and a transaction must be open.
|
|
|
|
NOTE: To change the characteristics of the Audit Log it is currently
|
|
necessary to write the values of its Registry Key directly,
|
|
because no Elf API is provided to do this.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle to the Policy Object. This handle must already
|
|
have been validated. It must either be trusted, or must specify
|
|
POLICY_AUDIT_LOG_ADMIN access.
|
|
|
|
PolicyAuditLogInfo - Pointer to Audit Log Information.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code.
|
|
|
|
All Result Codes are generated by called routines.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE AuditLogRegistryHandle = NULL;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
ULONG Type = REG_DWORD;
|
|
TIME OldRetentionPeriod = LsapAdtLogInformation.AuditRetentionPeriod;
|
|
|
|
|
|
BOOLEAN LogWasFull = LsapAdtLogFullInformation.LogIsFull;
|
|
|
|
ASSERT( LsapDbIsLocked());
|
|
|
|
Status = LsapDbWriteAttributeObject(
|
|
PolicyHandle,
|
|
&LsapDbNames[PolAdtLg],
|
|
PolicyAuditLogInfo,
|
|
sizeof(POLICY_AUDIT_LOG_INFO)
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetInfoLogError;
|
|
}
|
|
|
|
RtlMoveMemory(
|
|
&LsapAdtLogInformation,
|
|
PolicyAuditLogInfo,
|
|
sizeof( POLICY_AUDIT_LOG_INFO )
|
|
);
|
|
|
|
//
|
|
// Now open the Audit Log's Registry Key using the absolute path of its
|
|
// Registry Key.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&LsapDbNames[AuditLog],
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenKey( &AuditLogRegistryHandle, KEY_WRITE, &ObjectAttributes );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetInfoLogError;
|
|
}
|
|
|
|
//
|
|
// Now set the Audit Record Retention Period.
|
|
//
|
|
|
|
Status = NtSetValueKey(
|
|
AuditLogRegistryHandle,
|
|
&LsapDbNames[AuditRecordRetentionPeriod],
|
|
(ULONG) 0,
|
|
Type,
|
|
&PolicyAuditLogInfo->AuditRetentionPeriod,
|
|
sizeof( ULONG )
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetInfoLogError;
|
|
}
|
|
|
|
//
|
|
// If the new Retention Period is 0, the Audit Log can be marked
|
|
// as no longer being full, since the Event Logger will wrap
|
|
// indefinitely.
|
|
//
|
|
|
|
if ( PolicyAuditLogInfo->AuditRetentionPeriod.QuadPart == 0) {
|
|
|
|
if (LsapAdtLogFullInformation.LogIsFull) {
|
|
|
|
LsapAdtLogFullInformation.LogIsFull = FALSE;
|
|
|
|
Status = LsapDbWriteAttributeObject(
|
|
LsapDbHandle,
|
|
&LsapDbNames[PolAdtFL],
|
|
&LsapAdtLogFullInformation,
|
|
sizeof(POLICY_AUDIT_FULL_QUERY_INFO)
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetInfoLogError;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now set the Audit Log Maximum Size MaxSize.
|
|
//
|
|
|
|
Status = NtSetValueKey(
|
|
AuditLogRegistryHandle,
|
|
&LsapDbNames[AuditLogMaxSize],
|
|
0,
|
|
Type,
|
|
&PolicyAuditLogInfo->MaximumLogSize,
|
|
sizeof( ULONG )
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetInfoLogError;
|
|
}
|
|
|
|
//
|
|
// Update the LSA's in-memory copy of the Audit Log Information.
|
|
//
|
|
|
|
RtlMoveMemory(
|
|
&LsapAdtLogInformation,
|
|
PolicyAuditLogInfo,
|
|
sizeof( POLICY_AUDIT_LOG_INFO )
|
|
);
|
|
|
|
//
|
|
// If the Audit Log has changed from "full" to "not full", clear the
|
|
// Audit Record Cache.
|
|
//
|
|
|
|
if (LogWasFull && !LsapAdtLogFullInformation.LogIsFull) {
|
|
|
|
Status = LsapAdtLogQueuedEvents((ULONG) 0);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SetInfoLogError;
|
|
}
|
|
}
|
|
|
|
SetInfoLogFinish:
|
|
|
|
//
|
|
// If necessary, close the Audit Log Registry Key.
|
|
//
|
|
|
|
if (AuditLogRegistryHandle != NULL) {
|
|
|
|
Status = NtClose( AuditLogRegistryHandle );
|
|
|
|
AuditLogRegistryHandle = NULL;
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
goto SetInfoLogError;
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
|
|
SetInfoLogError:
|
|
|
|
goto SetInfoLogFinish;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtQueryAuditLogFullInfo(
|
|
IN PLSAPR_HANDLE PolicyHandle,
|
|
IN ULONG Options,
|
|
OUT PPOLICY_AUDIT_FULL_QUERY_INFO PolicyAuditFullQueryInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function queries the Audit Log Full status as known to the LSA
|
|
Database. If this status is "Log Full", an attempt is optionally made
|
|
to check that it is really full by trying to write a special type
|
|
of Audit Record to the Audit Log.
|
|
|
|
Arguments:
|
|
|
|
PolicyHandle - Handle to Policy Object with POLICY_AUDIT_LOG_ADMIN
|
|
and POLICY_SET_AUDIT_REQUIREMENTS access.
|
|
|
|
Options - Optional actions to be taken
|
|
|
|
LSAP_ADT_LOG_FULL_UPDATE - If the Audit Log Full information
|
|
specifies that the log is full, attempt to update this
|
|
information by actually trying to write a special type of
|
|
Audit Record to the Audit Log. If the write is successful,
|
|
update the Audit Log Full information to specify that the log
|
|
is no longer full.
|
|
|
|
|
|
PolicyAuditFullQueryInfo - Pointer to structure which will receive the
|
|
updated Audit Log Full information.
|
|
--*/
|
|
|
|
{
|
|
return( STATUS_SUCCESS );
|
|
|
|
// NTSTATUS Status = STATUS_SUCCESS;
|
|
// BOOLEAN AuditLogOpened = FALSE;
|
|
// BOOLEAN ObjectReferenced = FALSE;
|
|
// PPOLICY_AUDIT_RECORD AuditRecord = NULL;
|
|
// ULONG AuditFullQueryInfoLength = sizeof (POLICY_AUDIT_FULL_QUERY_INFO);
|
|
// BOOLEAN UpdateAttribute = FALSE;
|
|
//
|
|
// //
|
|
// // Read the Audit Log Full Information from the PolAdtFL attribute of the Lsa
|
|
// // Database object.
|
|
// //
|
|
//
|
|
// Status = LsapDbReadAttributeObject(
|
|
// LsapDbHandle,
|
|
// &LsapDbNames[PolAdtFL],
|
|
// PolicyAuditFullQueryInfo,
|
|
// &AuditFullQueryInfoLength
|
|
// );
|
|
//
|
|
// if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// //
|
|
// // The Audit Full Info was not successfully read. This may be
|
|
// // because the attribute does not exist.
|
|
// //
|
|
//
|
|
// if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
//
|
|
// LsapLogError(
|
|
// "LsapAdtUpdateAuditLogFullInfo: Read Audit Log Full Info returned 0x%lx\n",
|
|
// Status
|
|
// );
|
|
//
|
|
// goto QueryAuditLogFullInfoError;
|
|
// }
|
|
//
|
|
// //
|
|
// // Write default Audit Log Full Info to the PolAdtFL attribute
|
|
// // of the Policy Object. This is temporary, to cater for
|
|
// // existing Policy Databases.
|
|
// //
|
|
//
|
|
// PolicyAuditFullQueryInfo->LogIsFull = FALSE;
|
|
// PolicyAuditFullQueryInfo->ShutDownOnFull = FALSE;
|
|
// UpdateAttribute = TRUE;
|
|
//
|
|
// } else {
|
|
//
|
|
// //
|
|
// // The Audit Full Info was successfully read. If this info indicates
|
|
// // that the Audit Log is full, optionally challenge it by actually trying
|
|
// // to write to the log, because it may be out of date.
|
|
// //
|
|
// // * Passage of time may have caused more audit records to fall
|
|
// // outwith the Retention Period (if applicable).
|
|
// //
|
|
// // * Action taken such as clearing the Audit Log.
|
|
// //
|
|
//
|
|
// if (!((Options & LSAP_ADT_LOG_FULL_UPDATE) &&
|
|
// PolicyAuditFullQueryInfo->LogIsFull)) {
|
|
//
|
|
// goto QueryAuditLogFullInfoFinish;
|
|
// }
|
|
//
|
|
// //
|
|
// // The log is apparently full. Allocate memory for a special Audit
|
|
// // Record that we will attempt to write to the Audit Log.
|
|
// //
|
|
//
|
|
// AuditRecord = LsapAllocateLsaHeap(sizeof(POLICY_AUDIT_RECORD));
|
|
//
|
|
// if (AuditRecord == NULL) {
|
|
//
|
|
// goto QueryAuditLogFullInfoError;
|
|
// }
|
|
//
|
|
// AuditRecord->AuditRecordLength = sizeof(POLICY_AUDIT_RECORD);
|
|
// AuditRecord->AuditEventType = AuditEventLogNoLongerFull;
|
|
// AuditRecord->AuditInformationOffset = 0;
|
|
//
|
|
// //
|
|
// // Try to append the record to the log file, followed by any
|
|
// // others on the queue.
|
|
// //
|
|
//
|
|
// Status = LsapAdtWriteLog(
|
|
// AuditRecord,
|
|
// LSAP_ADT_LOG_QUEUE_PREPEND
|
|
// );
|
|
//
|
|
// if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// //
|
|
// // Unable to write to the Audit Log, possibly because it
|
|
// // is full. Whatever the reason, we will not change the
|
|
// // state of the information returned from the LSA Database.
|
|
// // Mask out any error.
|
|
// //
|
|
//
|
|
// Status = STATUS_SUCCESS;
|
|
//
|
|
// goto QueryAuditLogFullInfoFinish;
|
|
// }
|
|
//
|
|
// PolicyAuditFullQueryInfo->LogIsFull = FALSE;
|
|
// }
|
|
//
|
|
// //
|
|
// // Write the Audit Full Information to the LSA Database.
|
|
// // This involves a complete Database transaction.
|
|
// //
|
|
//
|
|
// Status = LsapDbReferenceObject(
|
|
// LsapDbHandle,
|
|
// 0,
|
|
// PolicyObject,
|
|
// LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION
|
|
// );
|
|
//
|
|
// if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// goto QueryAuditLogFullInfoError;
|
|
// }
|
|
//
|
|
// ObjectReferenced = TRUE;
|
|
//
|
|
// //
|
|
// // Write the Audit Full information
|
|
// //
|
|
//
|
|
// Status = LsapDbWriteAttributeObject(
|
|
// LsapDbHandle,
|
|
// &LsapDbNames[PolAdtFL],
|
|
// &LsapAdtLogFullInformation,
|
|
// sizeof( POLICY_AUDIT_FULL_QUERY_INFO )
|
|
// );
|
|
//
|
|
// if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// LsapLogError(
|
|
// "LsapAdtInitialize: Write Audit Log Full Info returned 0x%lx\n",
|
|
// Status
|
|
// );
|
|
//
|
|
// goto QueryAuditLogFullInfoError;
|
|
// }
|
|
//
|
|
//QueryAuditLogFullInfoFinish:
|
|
//
|
|
// //
|
|
// // Finish any outstanding transaction.
|
|
// //
|
|
//
|
|
// if (ObjectReferenced) {
|
|
//
|
|
// Status = LsapDbDereferenceObject(
|
|
// &LsapDbHandle,
|
|
// PolicyObject,
|
|
// (LSAP_DB_RELEASE_LOCK |
|
|
// LSAP_DB_FINISH_TRANSACTION),
|
|
// (SECURITY_DB_DELTA_TYPE) 0,
|
|
// Status
|
|
// );
|
|
//
|
|
// ObjectReferenced = FALSE;
|
|
//
|
|
// if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// LsapLogError(
|
|
// "LsapAdtInitialize: Dereference Policy Object returned 0x%lx\n",
|
|
// Status
|
|
// );
|
|
//
|
|
// goto QueryAuditLogFullInfoError;
|
|
// }
|
|
// }
|
|
//
|
|
// //
|
|
// // If we allocated an Audit Record, free it.
|
|
// //
|
|
//
|
|
// if (AuditRecord != NULL) {
|
|
//
|
|
// LsapFreeLsaHeap( AuditRecord );
|
|
// AuditRecord = NULL;
|
|
// }
|
|
//
|
|
// return(Status);
|
|
//
|
|
//QueryAuditLogFullInfoError:
|
|
//
|
|
// goto QueryAuditLogFullInfoFinish;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtSignalLogFull(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles an Audit Log Full condition. The local policy
|
|
is checked to see if a system shutdown is to be initiated. The
|
|
Audit Log Information is updated to reflect the log full condition.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
All result codes returned are generated by called routines.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
NTSTATUS SecondaryStatus = STATUS_SUCCESS;
|
|
BOOLEAN ShutDownSystem = FALSE;
|
|
HANDLE LsaProcessTokenHandle = NULL;
|
|
BOOLEAN LsaProcessTokenOpened = FALSE;
|
|
TOKEN_PRIVILEGES PrivilegesToBeChanged;
|
|
ULONG ReturnLength;
|
|
|
|
//
|
|
// Get the lock for LsapAdtSignalFullInProgress, which indicates
|
|
// that we are currently setting the log full flag.
|
|
//
|
|
|
|
Status = RtlEnterCriticalSection(
|
|
&LsapAdtLogFullLock
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// If we are already signalling that the log is full, don't try this
|
|
// now.
|
|
//
|
|
|
|
if (LsapAdtSignalFullInProgress) {
|
|
|
|
RtlLeaveCriticalSection(
|
|
&LsapAdtLogFullLock
|
|
);
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
LsapAdtSignalFullInProgress = TRUE;
|
|
|
|
RtlLeaveCriticalSection(
|
|
&LsapAdtLogFullLock
|
|
);
|
|
|
|
|
|
//
|
|
// Set the Audit Log Full Policy Information to reflect the log full condition.
|
|
// There is an in-memory copy and a copy in the LSA Database.
|
|
//
|
|
|
|
LsapAdtLogFullInformation.LogIsFull = TRUE;
|
|
|
|
Status = LsarSetInformationPolicy(
|
|
LsapDbHandle,
|
|
PolicyAuditFullSetInformation,
|
|
(PLSAPR_POLICY_INFORMATION) &LsapAdtLogFullInformation
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
LsapLogError(
|
|
"LsapAdtAppendLog - LsarSetInformationPolicy for Audit Log Full returned 0x%lx\n",
|
|
Status
|
|
);
|
|
|
|
goto SignalLogFullError;
|
|
}
|
|
|
|
//
|
|
// If requested, set flag so that we will shutdown the system, else
|
|
// take no action. If the system is not shut down, subsequent Audit
|
|
// Records will be lost. If the system is shut down, Audit Records
|
|
// generated after the reboot will be cached until system initialization
|
|
// is complete.
|
|
//
|
|
|
|
if (LsapAdtLogFullInformation.ShutDownOnFull) {
|
|
|
|
ShutDownSystem = TRUE;
|
|
}
|
|
|
|
// //
|
|
// // Set the Audit Log information to indicate that the Audit Log
|
|
// // is full. This will be detected on system reload by winlogon
|
|
// // by calling the LsaQueryInformationPolicy API. winlogon will
|
|
// // then permit logon only to the ADMIN account to allow the user to
|
|
// // correct the Audit Log Full condition.
|
|
// //
|
|
//
|
|
// Status = LsarSetInformationPolicy(
|
|
// LsapDbHandle,
|
|
// PolicyAuditLogInformation,
|
|
// (PLSAPR_POLICY_INFORMATION) &LsapAdtLogInformation
|
|
// );
|
|
//
|
|
// if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// LsapLogError(
|
|
// "LsapAdtAppendLog - LsarSetInformationPolicy for Audit Log returned 0x%lx\n",
|
|
// Status
|
|
// );
|
|
//
|
|
// goto SignalLogFullError;
|
|
// }
|
|
|
|
//
|
|
// Shutdown the system if necessary.
|
|
//
|
|
|
|
if (ShutDownSystem) {
|
|
|
|
//
|
|
// Since we, the LSA, are a local client of SCREG.EXE, we need
|
|
// SE_SHUTDOWN_PRIVILEGE to be enabled so that we can shutdown
|
|
// the system.
|
|
//
|
|
|
|
PrivilegesToBeChanged.PrivilegeCount = 1;
|
|
PrivilegesToBeChanged.Privileges[0].Luid =
|
|
RtlConvertUlongToLuid(SE_SHUTDOWN_PRIVILEGE);
|
|
|
|
PrivilegesToBeChanged.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
|
|
//
|
|
// Open the LSA Process Token and turn on the privilege.
|
|
//
|
|
|
|
Status = NtOpenProcessToken(
|
|
NtCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES,
|
|
&LsaProcessTokenHandle
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SignalLogFullError;
|
|
}
|
|
|
|
Status = NtAdjustPrivilegesToken(
|
|
LsaProcessTokenHandle,
|
|
FALSE,
|
|
&PrivilegesToBeChanged,
|
|
0,
|
|
NULL,
|
|
&ReturnLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SignalLogFullError;
|
|
}
|
|
|
|
Status = NtClose( LsaProcessTokenHandle );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto SignalLogFullError;
|
|
}
|
|
|
|
LsaProcessTokenHandle = NULL;
|
|
|
|
//
|
|
// Now initiate a system shutdown request.
|
|
//
|
|
|
|
if (!InitiateSystemShutdownW(
|
|
L"",
|
|
L"System shutdown initiated",
|
|
(DWORD) LSAP_ADT_LOG_FULL_SHUTDOWN_TIMEOUT,
|
|
TRUE,
|
|
TRUE
|
|
)) {
|
|
|
|
//
|
|
// BUGBUG - ScottBi - Don't know how to get the last Nt error
|
|
//
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto SignalLogFullError;
|
|
}
|
|
|
|
ShutDownSystem = FALSE;
|
|
}
|
|
|
|
SignalLogFullFinish:
|
|
|
|
|
|
//
|
|
// Indicate that we are done setting the log full flag
|
|
//
|
|
|
|
SecondaryStatus = RtlEnterCriticalSection(
|
|
&LsapAdtLogFullLock
|
|
);
|
|
|
|
//
|
|
// We can't do much about this, so assert success
|
|
//
|
|
|
|
ASSERT(NT_SUCCESS(SecondaryStatus));
|
|
|
|
LsapAdtSignalFullInProgress = FALSE;
|
|
|
|
RtlLeaveCriticalSection(
|
|
&LsapAdtLogFullLock
|
|
);
|
|
|
|
|
|
//
|
|
// If necessary, close the Lsa Process Token handle.
|
|
//
|
|
|
|
if (LsaProcessTokenHandle != NULL) {
|
|
|
|
SecondaryStatus = NtClose( LsaProcessTokenHandle );
|
|
|
|
ASSERT(NT_SUCCESS(SecondaryStatus));
|
|
}
|
|
|
|
return(Status);
|
|
|
|
SignalLogFullError:
|
|
|
|
goto SignalLogFullFinish;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtLogQueuedEvents(
|
|
IN ULONG Options
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function clears the Audit Log queue. Each Audit Record on the queue
|
|
is either written to the Audit Log and removed from the queue, or
|
|
discarded depending on the option specified.
|
|
|
|
Arguments:
|
|
|
|
Options - Specify optional actions to be taken.
|
|
|
|
LSAP_ADT_LOG_QUEUE_DISCARD - Discard the queue without writing
|
|
its contents to the Audit Log. If this flag is not specified,
|
|
the queue will be written to the log prior to discard.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully.
|
|
|
|
STATUS_LOG_FILE_FULL - The Audit Log became full.
|
|
--*/
|
|
|
|
{
|
|
return( LsapAdtWriteLog( NULL, Options ));
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtAcquireLogQueueLock(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function acquires the LSA Audit Log Queue Lock. This lock serializes
|
|
all updates to the Audit Log Queue.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = RtlEnterCriticalSection(&LsapAdtQueueLock);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapAdtReleaseLogQueueLock(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function releases the LSA Audit Log Queue Lock. This lock serializes
|
|
updates to the Audit Log Queue.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None. Any error occurring within this routine is an internal error.
|
|
|
|
--*/
|
|
|
|
{
|
|
RtlLeaveCriticalSection(&LsapAdtQueueLock);
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
// //
|
|
// //
|
|
// New auditing routines //
|
|
// //
|
|
// This code will eventually replace //
|
|
// most of the above. //
|
|
// //
|
|
// //
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtDemarshallAuditInfo(
|
|
IN PSE_ADT_PARAMETER_ARRAY AuditParameters
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will walk down a marshalled audit parameter
|
|
array and unpack it so that its information may be passed
|
|
into the event logging service.
|
|
|
|
Three parallel data structures are maintained:
|
|
|
|
StringArray - Array of Unicode string structures. This array
|
|
is used primarily as temporary storage for returned string
|
|
structures.
|
|
|
|
StringPointerArray - Array of pointers to Unicode string structures.
|
|
|
|
FreeWhenDone - Array of booleans describing how to dispose of each
|
|
of the strings pointed to by the StringPointerArray.
|
|
|
|
|
|
Note that entries in the StringPointerArray are contiguous, but that
|
|
there may be gaps in the StringArray structure. For each entry in the
|
|
StringPointerArray there will be a corresponding entry in the FreeWhenDone
|
|
array. If the entry for a particular string is TRUE, the storage for
|
|
the string buffer will be released to the process heap.
|
|
|
|
|
|
|
|
StringArray
|
|
Other strings
|
|
+----------------+
|
|
| |<-----------+ +----------------+
|
|
| | | | |<-------------------+
|
|
+----------------+ | | | |
|
|
| UNUSED | | +----------------+ |
|
|
| | | |
|
|
+----------------+ | |
|
|
| |<------+ | +----------------+ |
|
|
| | | | | |<-----------+ |
|
|
+----------------+ | | | | | |
|
|
| UNUSED | | | +----------------+ | |
|
|
| | | | | |
|
|
+----------------+ | | | |
|
|
| |<--+ | | | |
|
|
| | | | | | |
|
|
+----------------+ | | | | |
|
|
| | | | | | |
|
|
| | | | | StringPointerArray | |
|
|
.... | | | | |
|
|
| | | +----------------+ | |
|
|
| | +-----| | | |
|
|
| | +----------------+ | |
|
|
| | | |---------+ |
|
|
| | +----------------+ |
|
|
| +----------| | |
|
|
| +----------------+ |
|
|
| | |-----------------+
|
|
| +----------------+
|
|
+--------------| |
|
|
+----------------+
|
|
| |
|
|
+----------------+
|
|
| |
|
|
+----------------+
|
|
| |
|
|
....
|
|
|
|
|
|
Arguments:
|
|
|
|
AuditParameters - Receives a pointer to an audit
|
|
parameters array in self-relative form.
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG ParameterCount;
|
|
USHORT i;
|
|
PUNICODE_STRING StringPointerArray[30];
|
|
BOOLEAN FreeWhenDone[30];
|
|
UNICODE_STRING StringArray[30];
|
|
USHORT StringIndex = 0;
|
|
UNICODE_STRING DashString;
|
|
BOOLEAN FreeDash;
|
|
NTSTATUS Status;
|
|
PUNICODE_STRING SourceModule;
|
|
PSID UserSid;
|
|
|
|
|
|
Status= LsapAdtBuildDashString(
|
|
&DashString,
|
|
&FreeDash
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
return( Status );
|
|
}
|
|
|
|
ParameterCount = AuditParameters->ParameterCount;
|
|
|
|
//
|
|
// Parameter 0 will always be the user SID. Convert the
|
|
// offset to the SID into a pointer.
|
|
//
|
|
|
|
ASSERT( AuditParameters->Parameters[0].Type == SeAdtParmTypeSid );
|
|
|
|
|
|
|
|
UserSid = (PSID)AuditParameters->Parameters[0].Address;
|
|
|
|
|
|
|
|
//
|
|
// Parameter 1 will always be the Source Module (or Subsystem Name).
|
|
// Unpack this now.
|
|
//
|
|
|
|
ASSERT( AuditParameters->Parameters[1].Type == SeAdtParmTypeString );
|
|
|
|
|
|
|
|
SourceModule = (PUNICODE_STRING)AuditParameters->Parameters[1].Address;
|
|
|
|
|
|
for (i=2; i<ParameterCount; i++) {
|
|
|
|
switch ( AuditParameters->Parameters[i].Type ) {
|
|
case SeAdtParmTypeNone:
|
|
{
|
|
StringPointerArray[StringIndex] = &DashString;
|
|
|
|
FreeWhenDone[StringIndex] = FALSE;
|
|
|
|
StringIndex++;
|
|
|
|
break;
|
|
}
|
|
case SeAdtParmTypeString:
|
|
{
|
|
StringPointerArray[StringIndex] =
|
|
(PUNICODE_STRING)AuditParameters->Parameters[i].Address;
|
|
|
|
FreeWhenDone[StringIndex] = FALSE;
|
|
|
|
StringIndex++;
|
|
|
|
break;
|
|
}
|
|
case SeAdtParmTypeFileSpec:
|
|
{
|
|
//
|
|
// Same as a string, except we must attempt to replace
|
|
// device information with a drive letter.
|
|
//
|
|
|
|
StringPointerArray[StringIndex] =
|
|
(PUNICODE_STRING)AuditParameters->Parameters[i].Address;
|
|
|
|
|
|
//
|
|
// This may not do anything, in which case just audit what
|
|
// we have.
|
|
//
|
|
|
|
LsapAdtSubstituteDriveLetter( StringPointerArray[StringIndex] );
|
|
|
|
FreeWhenDone[StringIndex] = FALSE;
|
|
|
|
StringIndex++;
|
|
|
|
break;
|
|
}
|
|
case SeAdtParmTypeUlong:
|
|
{
|
|
ULONG Data;
|
|
|
|
Data = AuditParameters->Parameters[i].Data[0];
|
|
|
|
Status = LsapAdtBuildUlongString(
|
|
Data,
|
|
&StringArray[StringIndex],
|
|
&FreeWhenDone[StringIndex]
|
|
);
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
|
|
StringPointerArray[StringIndex] = &StringArray[StringIndex];
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// Couldn't allocate memory for that string,
|
|
// use the Dash string that we've already created.
|
|
//
|
|
|
|
StringPointerArray[StringIndex] = &DashString;
|
|
FreeWhenDone[StringIndex] = FALSE;
|
|
}
|
|
|
|
StringIndex++;
|
|
|
|
break;
|
|
}
|
|
case SeAdtParmTypeSid:
|
|
{
|
|
PSID Sid;
|
|
|
|
Sid = (PSID)AuditParameters->Parameters[i].Address;
|
|
|
|
Status = LsapAdtBuildSidString(
|
|
Sid,
|
|
&StringArray[StringIndex],
|
|
&FreeWhenDone[StringIndex]
|
|
);
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
|
|
StringPointerArray[StringIndex] = &StringArray[StringIndex];
|
|
|
|
} else {
|
|
|
|
//
|
|
// Couldn't allocate memory for that string,
|
|
// use the Dash string that we've already created.
|
|
//
|
|
|
|
StringPointerArray[StringIndex] = &DashString;
|
|
FreeWhenDone[StringIndex] = FALSE;
|
|
}
|
|
|
|
StringIndex++;
|
|
|
|
|
|
break;
|
|
}
|
|
case SeAdtParmTypeLogonId:
|
|
{
|
|
PLUID LogonId;
|
|
ULONG j;
|
|
|
|
LogonId = (PLUID)(&AuditParameters->Parameters[i].Data[0]);
|
|
|
|
Status = LsapAdtBuildLogonIdStrings(
|
|
LogonId,
|
|
&StringArray [ StringIndex ],
|
|
&FreeWhenDone[ StringIndex ],
|
|
&StringArray [ StringIndex + 1 ],
|
|
&FreeWhenDone[ StringIndex + 1 ],
|
|
&StringArray [ StringIndex + 2 ],
|
|
&FreeWhenDone[ StringIndex + 2 ]
|
|
);
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
|
|
for (j=0; j<3; j++) {
|
|
|
|
StringPointerArray[StringIndex] = &StringArray[StringIndex];
|
|
StringIndex++;
|
|
}
|
|
|
|
//
|
|
// Finished, break out to surrounding loop.
|
|
//
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Do nothing, fall through to the NoLogonId case
|
|
//
|
|
|
|
}
|
|
}
|
|
case SeAdtParmTypeNoLogonId:
|
|
{
|
|
ULONG j;
|
|
//
|
|
// Create three "-" strings.
|
|
//
|
|
|
|
for (j=0; j<3; j++) {
|
|
|
|
StringPointerArray[ StringIndex ] = &DashString;
|
|
FreeWhenDone[ StringIndex ] = FALSE;
|
|
StringIndex++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case SeAdtParmTypeAccessMask:
|
|
{
|
|
PUNICODE_STRING ObjectTypeName;
|
|
ULONG ObjectTypeNameIndex;
|
|
ACCESS_MASK Accesses;
|
|
|
|
ObjectTypeNameIndex = AuditParameters->Parameters[i].Data[1];
|
|
ObjectTypeName = AuditParameters->Parameters[ObjectTypeNameIndex].Address;
|
|
Accesses= AuditParameters->Parameters[i].Data[0];
|
|
|
|
//
|
|
// We can determine the index to the ObjectTypeName
|
|
// parameter since it was stored away in the Data[1]
|
|
// field of this parameter.
|
|
//
|
|
|
|
Status = LsapAdtBuildAccessesString(
|
|
SourceModule,
|
|
ObjectTypeName,
|
|
Accesses,
|
|
&StringArray [ StringIndex ],
|
|
&FreeWhenDone[ StringIndex ]
|
|
);
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
|
|
StringPointerArray[ StringIndex ] = &StringArray[ StringIndex ];
|
|
|
|
} else {
|
|
|
|
//
|
|
// That didn't work, use the Dash string instead.
|
|
//
|
|
|
|
StringPointerArray[ StringIndex ] = &DashString;
|
|
FreeWhenDone [ StringIndex ] = FALSE;
|
|
}
|
|
|
|
StringIndex++;
|
|
|
|
break;
|
|
}
|
|
case SeAdtParmTypePrivs:
|
|
{
|
|
|
|
PPRIVILEGE_SET Privileges = (PPRIVILEGE_SET)AuditParameters->Parameters[i].Address;
|
|
|
|
Status = LsapBuildPrivilegeAuditString(
|
|
Privileges,
|
|
&StringArray [ StringIndex ],
|
|
&FreeWhenDone[ StringIndex ]
|
|
);
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
|
|
StringPointerArray[ StringIndex ] = &StringArray[ StringIndex ];
|
|
|
|
} else {
|
|
|
|
//
|
|
// That didn't work, use the Dash string instead.
|
|
//
|
|
|
|
StringPointerArray[ StringIndex ] = &DashString;
|
|
FreeWhenDone [ StringIndex ] = FALSE;
|
|
}
|
|
|
|
StringIndex++;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Probably have to do this from somewhere else eventually, but for now
|
|
// do it from here.
|
|
//
|
|
|
|
|
|
|
|
Status = ElfReportEventW (
|
|
LsapAdtLogHandle,
|
|
AuditParameters->Type,
|
|
(USHORT)AuditParameters->CategoryId,
|
|
AuditParameters->AuditId,
|
|
UserSid,
|
|
StringIndex,
|
|
0,
|
|
StringPointerArray,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// cleanup
|
|
//
|
|
|
|
for (i=0; i<StringIndex; i++) {
|
|
|
|
if (FreeWhenDone[i]) {
|
|
LsapFreeLsaHeap( StringPointerArray[i]->Buffer );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we are in the middle of shutdown, we can tolerate this failure.
|
|
//
|
|
|
|
if ( (Status == RPC_NT_UNKNOWN_IF) && LsapShutdownInProgress ) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
return( Status );
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
LsapAdtNormalizeAuditInfo(
|
|
IN PSE_ADT_PARAMETER_ARRAY AuditParameters
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will walk down a marshalled audit parameter
|
|
array and turn it into an Absolute format data structure.
|
|
|
|
|
|
Arguments:
|
|
|
|
AuditParameters - Receives a pointer to an audit
|
|
parameters array in self-relative form.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG ParameterCount;
|
|
ULONG i;
|
|
ULONG Address;
|
|
PUNICODE_STRING Unicode;
|
|
|
|
|
|
if ( !(AuditParameters->Flags & SE_ADT_PARAMETERS_SELF_RELATIVE)) {
|
|
|
|
return;
|
|
}
|
|
|
|
ParameterCount = AuditParameters->ParameterCount;
|
|
|
|
for (i=0; i<ParameterCount; i++) {
|
|
|
|
switch ( AuditParameters->Parameters[i].Type ) {
|
|
case SeAdtParmTypeNone:
|
|
case SeAdtParmTypeUlong:
|
|
case SeAdtParmTypeLogonId:
|
|
case SeAdtParmTypeNoLogonId:
|
|
case SeAdtParmTypeAccessMask:
|
|
{
|
|
|
|
break;
|
|
}
|
|
case SeAdtParmTypeString:
|
|
case SeAdtParmTypeFileSpec:
|
|
{
|
|
Address = (ULONG)AuditParameters->Parameters[i].Address;
|
|
Address += (ULONG)AuditParameters;
|
|
|
|
AuditParameters->Parameters[i].Address = (PVOID)Address;
|
|
|
|
Unicode = (PUNICODE_STRING)Address;
|
|
Unicode->Buffer = (PWSTR)((PCHAR)Unicode->Buffer + (ULONG)AuditParameters);
|
|
|
|
break;
|
|
}
|
|
case SeAdtParmTypeSid:
|
|
{
|
|
PSID Sid;
|
|
|
|
Sid = (PSID) AuditParameters->Parameters[i].Address;
|
|
|
|
Sid = (PSID) ((PCHAR)Sid + (ULONG)AuditParameters);
|
|
|
|
AuditParameters->Parameters[i].Address = (PVOID)Sid;
|
|
|
|
break;
|
|
}
|
|
case SeAdtParmTypePrivs:
|
|
{
|
|
PPRIVILEGE_SET Privileges;
|
|
|
|
Privileges = (PPRIVILEGE_SET) AuditParameters->Parameters[i].Address;
|
|
|
|
Privileges = (PPRIVILEGE_SET) ((PCHAR)Privileges + (ULONG)AuditParameters);
|
|
|
|
AuditParameters->Parameters[i].Address = (PVOID)Privileges;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtMarshallAuditRecord(
|
|
IN PSE_ADT_PARAMETER_ARRAY AuditParameters,
|
|
OUT PSE_ADT_PARAMETER_ARRAY *MarshalledAuditParameters
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will take an AuditParamters structure and create
|
|
a new AuditParameters structure that is suitable for placing
|
|
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;
|
|
ULONG Size;
|
|
|
|
|
|
|
|
//
|
|
// 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++) {
|
|
Size = AuditParameters->Parameters[i].Length;
|
|
TotalSize = TotalSize + (ULONG)LongAlign( Size );
|
|
}
|
|
|
|
//
|
|
// 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 = LsapAllocateLsaHeap( TotalSize );
|
|
|
|
if (*MarshalledAuditParameters == NULL) {
|
|
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
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:
|
|
{
|
|
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 );
|
|
|
|
RtlCopyMemory( 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;
|
|
|
|
ASSERT( (ULONG)Base <= (ULONG)(*MarshalledAuditParameters) + TotalSize );
|
|
|
|
break;
|
|
}
|
|
case SeAdtParmTypeSid:
|
|
{
|
|
PSID TargetSid = (PSID) Base;
|
|
PSID SourceSid = AuditParameters->Parameters[i].Address;
|
|
|
|
//
|
|
// Copy the Sid into the output buffer
|
|
//
|
|
|
|
RtlCopyMemory( 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;
|
|
|
|
ASSERT( (ULONG)Base <= (ULONG)(*MarshalledAuditParameters) + TotalSize );
|
|
|
|
break;
|
|
}
|
|
case SeAdtParmTypePrivs:
|
|
{
|
|
PPRIVILEGE_SET TargetPrivileges = (PPRIVILEGE_SET) Base;
|
|
PPRIVILEGE_SET SourcePrivileges = AuditParameters->Parameters[i].Address;
|
|
|
|
RtlCopyMemory( TargetPrivileges, SourcePrivileges, LsapPrivilegeSetSize( SourcePrivileges ));
|
|
|
|
(*MarshalledAuditParameters)->Parameters[i].Address = Base - (ULONG)(*MarshalledAuditParameters);
|
|
|
|
BaseIncr = (ULONG)LongAlign(LsapPrivilegeSetSize( SourcePrivileges ));
|
|
|
|
Base += BaseIncr;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
//
|
|
// We got passed junk, complain.
|
|
//
|
|
|
|
ASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
LsaIAuditSamEvent(
|
|
IN NTSTATUS PassedStatus,
|
|
IN ULONG AuditId,
|
|
IN PSID DomainSid,
|
|
IN PULONG MemberRid OPTIONAL,
|
|
IN PSID MemberSid OPTIONAL,
|
|
IN PUNICODE_STRING AccountName OPTIONAL,
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PULONG AccountRid OPTIONAL,
|
|
IN PPRIVILEGE_SET Privileges OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Abstract:
|
|
|
|
This routine produces an audit record representing an account
|
|
operation.
|
|
|
|
This routine goes through the list of parameters and adds a string
|
|
representation of each (in order) to an audit message. Note that
|
|
the full complement of account audit message formats is achieved by
|
|
selecting which optional parameters to include in this call.
|
|
|
|
In addition to any parameters passed below, this routine will ALWAYS
|
|
add the impersonation client's user name, domain, and logon ID as
|
|
the LAST parameters in the audit message.
|
|
|
|
|
|
Parmeters:
|
|
|
|
AuditId - Specifies the message ID of the audit being generated.
|
|
|
|
DomainSid - This parameter results in a SID string being generated
|
|
ONLY if neither the MemberRid nor AccountRid parameters are
|
|
passed. If either of those parameters are passed, this parameter
|
|
is used as a prefix of a SID.
|
|
|
|
MemberRid - This optional parameter, if present, is added to the end of
|
|
the DomainSid parameter to produce a "Member" sid. The resultant
|
|
member SID is then used to build a sid-string which is added to the
|
|
audit message following all preceeding parameters.
|
|
This parameter supports global group membership change audits, where
|
|
member IDs are always relative to a local domain.
|
|
|
|
MemberSid - This optional parameter, if present, is converted to a
|
|
SID string and added following preceeding parameters. This parameter
|
|
is generally used for describing local group (alias) members, where
|
|
the member IDs are not relative to a local domain.
|
|
|
|
AccountName - This optional parameter, if present, is added to the audit
|
|
message without change following any preceeding parameters.
|
|
This parameter is needed for almost all account audits and does not
|
|
need localization.
|
|
|
|
DomainName - This optional parameter, if present, is added to the audit
|
|
message without change following any preceeding parameters.
|
|
This parameter is needed for almost all account audits and does not
|
|
need localization.
|
|
|
|
|
|
AccountRid - This optional parameter, if present, is added to the end of
|
|
the DomainSid parameter to produce an "Account" sid. The resultant
|
|
Account SID is then used to build a sid-string which is added to the
|
|
audit message following all preceeding parameters.
|
|
This parameter supports audits that include "New account ID" or
|
|
"Target Account ID" fields.
|
|
|
|
Privileges - The privileges passed via this optional parameter,
|
|
if present, will be converted to string format and added to the
|
|
audit message following any preceeding parameters. NOTE: the
|
|
caller is responsible for freeing the privilege_set (in fact,
|
|
it may be on the stack). ALSO NOTE: The privilege set will be
|
|
destroyed by this call (due to use of the routine used to
|
|
convert the privilege values to privilege names).
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
LUID LogonId;
|
|
PSID NewAccountSid = NULL;
|
|
PSID NewMemberSid = NULL;
|
|
PSID SidPointer;
|
|
PSID ClientSid = NULL;
|
|
PTOKEN_USER TokenUserInformation;
|
|
SE_ADT_PARAMETER_ARRAY AuditParameters;
|
|
UCHAR AccountSidBuffer[256];
|
|
UCHAR MemberSidBuffer[256];
|
|
UCHAR SubAuthorityCount;
|
|
UNICODE_STRING SubsystemName;
|
|
ULONG LengthRequired;
|
|
|
|
Status = LsapQueryClientInfo(
|
|
&TokenUserInformation,
|
|
&LogonId
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
return( Status );
|
|
}
|
|
|
|
ClientSid = TokenUserInformation->User.Sid;
|
|
|
|
RtlZeroMemory (
|
|
(PVOID) &AuditParameters,
|
|
sizeof( AuditParameters )
|
|
);
|
|
|
|
AuditParameters.CategoryId = SE_CATEGID_ACCOUNT_MANAGEMENT;
|
|
AuditParameters.AuditId = AuditId;
|
|
AuditParameters.Type = (NT_SUCCESS(PassedStatus) ? EVENTLOG_AUDIT_SUCCESS : EVENTLOG_AUDIT_FAILURE );
|
|
AuditParameters.ParameterCount = 0;
|
|
|
|
LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, ClientSid );
|
|
|
|
AuditParameters.ParameterCount++;
|
|
|
|
RtlInitUnicodeString( &SubsystemName, L"Security" );
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &SubsystemName );
|
|
|
|
AuditParameters.ParameterCount++;
|
|
|
|
if (ARGUMENT_PRESENT(MemberRid)) {
|
|
|
|
//
|
|
// Add a member SID string to the audit message
|
|
//
|
|
// Domain Sid + Member Rid = Final SID.
|
|
|
|
SubAuthorityCount = *RtlSubAuthorityCountSid( DomainSid );
|
|
|
|
if ( (LengthRequired = RtlLengthRequiredSid( SubAuthorityCount + 1 )) > 256 ) {
|
|
|
|
NewMemberSid = LsapAllocateLsaHeap( LengthRequired );
|
|
|
|
SidPointer = NewMemberSid;
|
|
|
|
} else {
|
|
|
|
SidPointer = (PSID)MemberSidBuffer;
|
|
}
|
|
|
|
Status = RtlCopySid (
|
|
LengthRequired,
|
|
SidPointer,
|
|
DomainSid
|
|
);
|
|
|
|
ASSERT( NT_SUCCESS( Status ));
|
|
|
|
*(RtlSubAuthoritySid( SidPointer, SubAuthorityCount )) = *MemberRid;
|
|
*RtlSubAuthorityCountSid( SidPointer ) = SubAuthorityCount + 1;
|
|
|
|
LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, SidPointer );
|
|
|
|
AuditParameters.ParameterCount++;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(MemberSid)) {
|
|
|
|
//
|
|
// Add a member SID string to the audit message
|
|
//
|
|
|
|
LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, MemberSid );
|
|
|
|
AuditParameters.ParameterCount++;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(AccountName)) {
|
|
|
|
//
|
|
// Add a UNICODE_STRING to the audit message
|
|
//
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, AccountName );
|
|
|
|
AuditParameters.ParameterCount++;
|
|
}
|
|
|
|
|
|
if (ARGUMENT_PRESENT(DomainName)) {
|
|
|
|
//
|
|
// Add a UNICODE_STRING to the audit message
|
|
//
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, DomainName );
|
|
|
|
AuditParameters.ParameterCount++;
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ARGUMENT_PRESENT(DomainSid) &&
|
|
!(ARGUMENT_PRESENT(MemberRid) || ARGUMENT_PRESENT(AccountRid))
|
|
) {
|
|
|
|
//
|
|
// Add the domain SID as a SID string to the audit message
|
|
//
|
|
// Just the domain SID.
|
|
//
|
|
|
|
LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, DomainSid );
|
|
|
|
AuditParameters.ParameterCount++;
|
|
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(AccountRid)) {
|
|
|
|
//
|
|
// Add a member SID string to the audit message
|
|
// Domain Sid + account Rid = final sid
|
|
//
|
|
|
|
SubAuthorityCount = *RtlSubAuthorityCountSid( DomainSid );
|
|
|
|
if ( (LengthRequired = RtlLengthRequiredSid( SubAuthorityCount + 1 )) > 256 ) {
|
|
|
|
NewAccountSid = LsapAllocateLsaHeap( LengthRequired );
|
|
|
|
SidPointer = NewMemberSid;
|
|
|
|
} else {
|
|
|
|
SidPointer = (PSID)AccountSidBuffer;
|
|
}
|
|
|
|
|
|
Status = RtlCopySid (
|
|
LengthRequired,
|
|
SidPointer,
|
|
DomainSid
|
|
);
|
|
|
|
ASSERT( NT_SUCCESS( Status ));
|
|
|
|
*(RtlSubAuthoritySid( SidPointer, SubAuthorityCount )) = *AccountRid;
|
|
*RtlSubAuthorityCountSid( SidPointer ) = SubAuthorityCount + 1;
|
|
|
|
LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, SidPointer );
|
|
|
|
AuditParameters.ParameterCount++;
|
|
}
|
|
|
|
//
|
|
// Now add the caller information
|
|
//
|
|
// Caller name
|
|
// Caller domain
|
|
// Caller logon ID
|
|
//
|
|
|
|
|
|
LsapSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, LogonId );
|
|
|
|
AuditParameters.ParameterCount++;
|
|
|
|
//
|
|
// Add any privileges
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(Privileges)) {
|
|
|
|
LsapSetParmTypePrivileges( AuditParameters, AuditParameters.ParameterCount, Privileges );
|
|
}
|
|
|
|
AuditParameters.ParameterCount++;
|
|
|
|
//
|
|
// Now write out the audit record to the audit log
|
|
//
|
|
|
|
( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
|
|
|
|
//
|
|
// And clean up any allocated memory
|
|
//
|
|
|
|
if ( NewMemberSid != NULL ) {
|
|
LsapFreeLsaHeap( NewMemberSid );
|
|
}
|
|
|
|
if ( NewAccountSid != NULL ) {
|
|
LsapFreeLsaHeap( NewAccountSid );
|
|
}
|
|
|
|
if ( TokenUserInformation != NULL ) {
|
|
LsapFreeLsaHeap( TokenUserInformation );
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapAdtAuditLogonProcessRegistration(
|
|
IN PLSAP_AU_REGISTER_CONNECT_INFO ConnectInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Audits the registration of a logon process
|
|
|
|
Arguments:
|
|
|
|
ConnectInfo - Supplies the connection information for the new
|
|
logon process.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
NTSTATUS Status;
|
|
ANSI_STRING AnsiString;
|
|
UNICODE_STRING Unicode;
|
|
PSZ LogonProcessNameBuffer;
|
|
PSID LocalSystemSid;
|
|
SE_ADT_PARAMETER_ARRAY AuditParameters;
|
|
|
|
if ( !LsapAdtEventsInformation.AuditingMode ) {
|
|
return;
|
|
}
|
|
|
|
if (!(LsapAdtEventsInformation.EventAuditingOptions[AuditCategorySystem] & POLICY_AUDIT_EVENT_SUCCESS)) {
|
|
return;
|
|
}
|
|
|
|
Status = RtlAllocateAndInitializeSid(
|
|
&NtAuthority,
|
|
1,
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&LocalSystemSid
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// Must be out of memory, not much we can do here
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Turn the name text in the ConnectInfo structure into
|
|
// something we can work with.
|
|
//
|
|
|
|
LogonProcessNameBuffer = (PSZ)LsapAllocateLsaHeap( ConnectInfo->LogonProcessNameLength+1 );
|
|
|
|
RtlCopyMemory(
|
|
LogonProcessNameBuffer,
|
|
ConnectInfo->LogonProcessName,
|
|
ConnectInfo->LogonProcessNameLength
|
|
);
|
|
|
|
LogonProcessNameBuffer[ConnectInfo->LogonProcessNameLength] = 0;
|
|
RtlInitAnsiString( &AnsiString, LogonProcessNameBuffer );
|
|
|
|
Status = RtlAnsiStringToUnicodeString( &Unicode, &AnsiString, TRUE );
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// Must be out of memory, not much we can do here
|
|
//
|
|
|
|
RtlFreeSid( LocalSystemSid );
|
|
LsapFreeLsaHeap( LogonProcessNameBuffer );
|
|
return;
|
|
}
|
|
|
|
RtlZeroMemory (
|
|
(PVOID) &AuditParameters,
|
|
sizeof( AuditParameters )
|
|
);
|
|
|
|
AuditParameters.CategoryId = SE_CATEGID_SYSTEM;
|
|
AuditParameters.AuditId = SE_AUDITID_SYSTEM_LOGON_PROC_REGISTER;
|
|
AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
|
|
AuditParameters.ParameterCount = 0;
|
|
|
|
LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, LocalSystemSid );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &Unicode );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
|
|
|
|
RtlFreeSid( LocalSystemSid );
|
|
LsapFreeLsaHeap( LogonProcessNameBuffer );
|
|
RtlFreeUnicodeString( &Unicode );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapAdtAuditPackageLoad(
|
|
PUNICODE_STRING PackageFileName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Audits the loading of an authentication package.
|
|
|
|
Arguments:
|
|
|
|
PackageFileName - The name of the package being loaded.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
NTSTATUS Status;
|
|
PSID LocalSystemSid;
|
|
SE_ADT_PARAMETER_ARRAY AuditParameters;
|
|
|
|
if ( !LsapAdtEventsInformation.AuditingMode ) {
|
|
return;
|
|
}
|
|
|
|
if (!(LsapAdtEventsInformation.EventAuditingOptions[AuditCategorySystem] & POLICY_AUDIT_EVENT_SUCCESS)) {
|
|
return;
|
|
}
|
|
|
|
Status = RtlAllocateAndInitializeSid(
|
|
&NtAuthority,
|
|
1,
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&LocalSystemSid
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// Must be out of memory, not much we can do here
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
RtlZeroMemory (
|
|
(PVOID) &AuditParameters,
|
|
sizeof( AuditParameters )
|
|
);
|
|
|
|
AuditParameters.CategoryId = SE_CATEGID_SYSTEM;
|
|
AuditParameters.AuditId = SE_AUDITID_AUTH_PACKAGE_LOAD;
|
|
AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
|
|
AuditParameters.ParameterCount = 0;
|
|
|
|
LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, LocalSystemSid );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, PackageFileName );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
|
|
|
|
RtlFreeSid( LocalSystemSid );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
LsaIAuditNotifyPackageLoad(
|
|
PUNICODE_STRING PackageFileName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Audits the loading of an notification package.
|
|
|
|
Arguments:
|
|
|
|
PackageFileName - The name of the package being loaded.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
NTSTATUS Status;
|
|
PSID LocalSystemSid;
|
|
SE_ADT_PARAMETER_ARRAY AuditParameters;
|
|
|
|
if ( !LsapAdtEventsInformation.AuditingMode ) {
|
|
return;
|
|
}
|
|
|
|
if (!(LsapAdtEventsInformation.EventAuditingOptions[AuditCategorySystem] & POLICY_AUDIT_EVENT_SUCCESS)) {
|
|
return;
|
|
}
|
|
|
|
Status = RtlAllocateAndInitializeSid(
|
|
&NtAuthority,
|
|
1,
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&LocalSystemSid
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// Must be out of memory, not much we can do here
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
RtlZeroMemory (
|
|
(PVOID) &AuditParameters,
|
|
sizeof( AuditParameters )
|
|
);
|
|
|
|
AuditParameters.CategoryId = SE_CATEGID_SYSTEM;
|
|
AuditParameters.AuditId = SE_AUDITID_NOTIFY_PACKAGE_LOAD;
|
|
AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
|
|
AuditParameters.ParameterCount = 0;
|
|
|
|
LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, LocalSystemSid );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, PackageFileName );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
|
|
|
|
RtlFreeSid( LocalSystemSid );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapAdtAuditDiscardedAudits(
|
|
ULONG NumberOfEventsDiscarded
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Audits the fact that we discarded some audits.
|
|
|
|
Arguments:
|
|
|
|
NumberOfEventsDiscarded - The number of events discarded.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
NTSTATUS Status;
|
|
PSID LocalSystemSid;
|
|
SE_ADT_PARAMETER_ARRAY AuditParameters;
|
|
|
|
if ( !LsapAdtEventsInformation.AuditingMode ) {
|
|
return;
|
|
}
|
|
|
|
if (!LsapAdtEventsInformation.EventAuditingOptions[AuditCategorySystem] & POLICY_AUDIT_EVENT_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
Status = RtlAllocateAndInitializeSid(
|
|
&NtAuthority,
|
|
1,
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&LocalSystemSid
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// Must be out of memory, not much we can do here
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
RtlZeroMemory ((PVOID) &AuditParameters, sizeof( AuditParameters ));
|
|
|
|
AuditParameters.CategoryId = SE_CATEGID_SYSTEM;
|
|
AuditParameters.AuditId = SE_AUDITID_AUDITS_DISCARDED;
|
|
AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
|
|
AuditParameters.ParameterCount = 0;
|
|
|
|
LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, LocalSystemSid );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
LsapSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, NumberOfEventsDiscarded );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
|
|
|
|
RtlFreeSid( LocalSystemSid );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
PLUID LsaFilterPrivileges[] =
|
|
{
|
|
&ChangeNotifyPrivilege,
|
|
&AuditPrivilege,
|
|
&CreateTokenPrivilege,
|
|
&AssignPrimaryTokenPrivilege,
|
|
&BackupPrivilege,
|
|
&RestorePrivilege,
|
|
&DebugPrivilege,
|
|
NULL
|
|
};
|
|
|
|
|
|
VOID
|
|
LsapAdtAuditSpecialPrivileges(
|
|
PPRIVILEGE_SET Privileges,
|
|
LUID LogonId,
|
|
PSID UserSid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Audits the assignment of special privileges at logon time.
|
|
|
|
Arguments:
|
|
|
|
Privileges - List of privileges being assigned.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PPRIVILEGE_SET Buffer;
|
|
PLUID *FilterPrivilege = NULL;
|
|
ULONG i;
|
|
SE_ADT_PARAMETER_ARRAY AuditParameters;
|
|
|
|
if ( !LsapAdtEventsInformation.AuditingMode ) {
|
|
return;
|
|
}
|
|
|
|
if (!(LsapAdtEventsInformation.EventAuditingOptions[AuditCategoryPrivilegeUse] & POLICY_AUDIT_EVENT_SUCCESS)) {
|
|
return;
|
|
}
|
|
|
|
if ( (Privileges == NULL) || (Privileges->PrivilegeCount == 0) ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We can't need any more space than what's being passed in.
|
|
//
|
|
|
|
Buffer = (PPRIVILEGE_SET)LsapAllocateLsaHeap( LsapPrivilegeSetSize( Privileges ) );
|
|
|
|
if ( Buffer == NULL ) {
|
|
return;
|
|
}
|
|
|
|
Buffer->PrivilegeCount = 0;
|
|
|
|
//
|
|
// For each privilege in the privilege set, see if it's in the filter
|
|
// list.
|
|
//
|
|
|
|
for ( i=0; i<Privileges->PrivilegeCount; i++) {
|
|
|
|
FilterPrivilege = LsaFilterPrivileges;
|
|
|
|
do {
|
|
|
|
if ( RtlEqualLuid( &Privileges->Privilege[i].Luid, *FilterPrivilege )) {
|
|
|
|
Buffer->Privilege[Buffer->PrivilegeCount].Luid = **FilterPrivilege;
|
|
Buffer->PrivilegeCount++;
|
|
}
|
|
|
|
} while ( *++FilterPrivilege != NULL );
|
|
}
|
|
|
|
if ( Buffer->PrivilegeCount == 0 ) {
|
|
LsapFreeLsaHeap( Buffer );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We matched on at least one, generate an audit.
|
|
//
|
|
|
|
RtlZeroMemory ((PVOID) &AuditParameters, sizeof( AuditParameters ));
|
|
|
|
AuditParameters.CategoryId = SE_CATEGID_PRIVILEGE_USE;
|
|
AuditParameters.AuditId = SE_AUDITID_ASSIGN_SPECIAL_PRIV;
|
|
AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
|
|
AuditParameters.ParameterCount = 0;
|
|
|
|
LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, UserSid );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
LsapSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, LogonId );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
LsapSetParmTypePrivileges( AuditParameters, AuditParameters.ParameterCount, Buffer );
|
|
AuditParameters.ParameterCount++;
|
|
|
|
( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
|
|
|
|
LsapFreeLsaHeap( Buffer );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
LsapAdtSubstituteDriveLetter(
|
|
IN OUT PUNICODE_STRING FileName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Takes a filename and replaces the device name part with a
|
|
drive letter, if possible.
|
|
|
|
The string will be edited directly in place, which means that
|
|
the Length field will be adjusted, and the Buffer contents will
|
|
be moved so that the drive letter is at the beginning of the
|
|
buffer. No memory will be allocated or freed.
|
|
|
|
Arguments:
|
|
|
|
FileName - Supplies a pointer to a unicode string containing
|
|
a filename.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
WCHAR DriveLetter;
|
|
USHORT DeviceNameLength;
|
|
PWCHAR p;
|
|
PWCHAR FilePart;
|
|
USHORT FilePartLength;
|
|
|
|
if ( LsapAdtLookupDriveLetter( FileName, &DeviceNameLength, &DriveLetter )) {
|
|
|
|
p = FileName->Buffer;
|
|
FilePart = (PWCHAR)((PCHAR)(FileName->Buffer) + DeviceNameLength);
|
|
FilePartLength = FileName->Length - DeviceNameLength;
|
|
|
|
|
|
*p = DriveLetter;
|
|
*++p = L':';
|
|
|
|
//
|
|
// THIS IS AN OVERLAPPED COPY! DO NOT USE RTLCOPYMEMORY!
|
|
//
|
|
|
|
RtlMoveMemory( ++p, FilePart, FilePartLength );
|
|
|
|
FileName->Length = FilePartLength + 2 * sizeof( WCHAR );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
LsapAdtLookupDriveLetter(
|
|
IN PUNICODE_STRING FileName,
|
|
OUT PUSHORT DeviceNameLength,
|
|
OUT PWCHAR DriveLetter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will take a file name and compare it to the
|
|
list of device names obtained during LSA initialization.
|
|
If one of the device names matches the prefix of the file
|
|
name the corresponding drive letter will be returned.
|
|
|
|
Arguments:
|
|
|
|
FileName - Supplies a unicode string containing the file
|
|
name obtained from the file system.
|
|
|
|
DeviceNameLength - If successful, returns the length of
|
|
the device name.
|
|
|
|
DriveLetter - If successful, returns the drive letter
|
|
corresponding to the device object.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE of a mapping is found, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i = 0;
|
|
PUNICODE_STRING DeviceName;
|
|
USHORT OldLength;
|
|
|
|
|
|
while (DriveMappingArray[i].DeviceName.Buffer != NULL ) {
|
|
|
|
DeviceName = &DriveMappingArray[i].DeviceName;
|
|
|
|
//
|
|
// If the device name is longer than the passed file name,
|
|
// it can't be a match.
|
|
//
|
|
|
|
if ( DeviceName->Length > FileName->Length ) {
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Temporarily truncate the file name to be the same
|
|
// length as the device name by adjusting the length field
|
|
// in its unicode string structure. Then compare them and
|
|
// see if they match.
|
|
//
|
|
// The test above ensures that this is a safe thing to
|
|
// do.
|
|
//
|
|
|
|
OldLength = FileName->Length;
|
|
FileName->Length = DeviceName->Length;
|
|
|
|
|
|
if ( RtlEqualUnicodeString( FileName, DeviceName, TRUE ) ) {
|
|
|
|
//
|
|
// We've got a match.
|
|
//
|
|
|
|
FileName->Length = OldLength;
|
|
*DriveLetter = DriveMappingArray[i].DriveLetter;
|
|
*DeviceNameLength = DeviceName->Length;
|
|
return( TRUE );
|
|
|
|
}
|
|
|
|
FileName->Length = OldLength;
|
|
i++;
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapAuditFailed(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Implements current policy of how to deal with a failed audit.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
HANDLE KeyHandle;
|
|
UNICODE_STRING KeyName;
|
|
UNICODE_STRING ValueName;
|
|
UCHAR NewValue;
|
|
ULONG Response;
|
|
|
|
ASSERT(sizeof(UCHAR) == sizeof(BOOLEAN));
|
|
|
|
if (LsapCrashOnAuditFail) {
|
|
|
|
//
|
|
// 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 = NtOpenKey(
|
|
&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) {
|
|
LsapCrashOnAuditFail = FALSE;
|
|
return;
|
|
}
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
goto bugcheck;
|
|
}
|
|
|
|
RtlInitUnicodeString( &ValueName, CRASH_ON_AUDIT_FAIL_VALUE );
|
|
|
|
NewValue = LSAP_ALLOW_ADIMIN_LOGONS_ONLY;
|
|
|
|
do {
|
|
|
|
Status = NtSetValueKey( 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 = NtFlushKey( KeyHandle );
|
|
|
|
} while ((Status == STATUS_INSUFFICIENT_RESOURCES) || (Status == STATUS_NO_MEMORY));
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
|
|
//
|
|
// go boom.
|
|
//
|
|
|
|
bugcheck:
|
|
|
|
Status = NtRaiseHardError(
|
|
STATUS_AUDIT_FAILED,
|
|
0,
|
|
0,
|
|
NULL,
|
|
OptionShutdownSystem,
|
|
&Response
|
|
);
|
|
|
|
}
|
|
|
|
|