/*++

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;

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
    )

/*++

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;

    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
        //

        LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, AccountName );
        AuditParameters.ParameterCount++;

        //
        //    Authenticating Authority (domain name)
        //

        LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, AuthenticatingAuthority );
        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++;

        ( 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 (RtlLargeIntegerEqualToZero( PolicyAuditLogInfo->AuditRetentionPeriod )) {

        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;

    //
    // 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 =
            RtlConvertUlongToLargeInteger(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:

    //
    // 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 );
        }
    }

    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
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;
    BOOLEAN NewValue;
    ULONG Response;

    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 = FALSE;
    
        do {

            Status = NtSetValueKey( KeyHandle,
                                    &ValueName,
                                    0,
                                    REG_NONE,
                                    &NewValue,
                                    sizeof(BOOLEAN)
                                    );
    
        } 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
                 );
                                             
}

