/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    adtinit.c

Abstract:

    Local Security Authority - Auditing Initialization

Author:

    Scott Birrell       (ScottBi)      November 20, 1991

Environment:

Revision History:

--*/


#include <msaudite.h>
#include "lsasrvp.h"
#include "adtp.h"

NTSTATUS LsapAdtInitializeCrashOnFail( VOID );

ULONG LsapAdtInitializationPass = 0;


//
// Array of drive letter to device mappings for generating path strings.
//

DRIVE_MAPPING DriveMappingArray[MAX_DRIVE_MAPPING];


//
// Name that will be used as the default subsystem name for LSA generated events
//

UNICODE_STRING LsapSubsystemName;


//
// Special privilege values which are not normally audited,
// but generate audits when assigned to a user.  See
// LsapAdtAuditSpecialPrivileges.
//

LUID ChangeNotifyPrivilege;
LUID AuditPrivilege;
LUID CreateTokenPrivilege;
LUID AssignPrimaryTokenPrivilege;
LUID BackupPrivilege;
LUID RestorePrivilege;
LUID DebugPrivilege;


//
// Global variable indicating whether or not we are supposed
// to crash when an audit fails.
//

BOOLEAN LsapCrashOnAuditFail = FALSE;



NTSTATUS
LsapAdtInitialize(
    IN ULONG Pass
    )

/*++

Routine Description:

    This function performs initialization of auditing within the LSA, and
    it also issues commands to the Reference Monitor to enable it to
    complete any initialization of auditing variables that is dependent
    on the content of the LSA Database.  At time of call, the main
    System Init thread is in the Reference Monitor awaiting completion
    of all LSA initialization, and the Reference Monitor Command
    Server thread is waiting for commands.

    The following steps are performed:

    o Read the Audit Event and Audit Log information from the LSA
      Database.
    o Call the Event Logging function to open the Audit Log
    o Issue a Reference Monitor command to write the Audit Event Info
      to the Reference-Monitor's in-memory database.

Arguments:

    Pass - Specifies the stage of initialization to be performed.

         Pass 1 - Initialization required before Audit Records can
             be written to the Audit Log.  Any Audit Records received
             during this time will be "cached" by the LSA and will
             be written out at Pass 2.

         Pass 2 - Write out Audit Records cached during Pass 1.

Return Value:

    NTSTATUS - Standard Nt Result Code.

        All Result Codes are generated by called routines.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    NTSTATUS SecondaryStatus = STATUS_SUCCESS;
    ULONG AuditLogInfoLength = sizeof (POLICY_AUDIT_LOG_INFO);
    ULONG AuditEventInfoLength = sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO);
    ULONG AuditFullQueryInfoLength = sizeof (POLICY_AUDIT_FULL_QUERY_INFO);
    BOOLEAN AcquiredLock = FALSE;
    UNICODE_STRING UnicodeString;
    PUNICODE_STRING Strings;
    PSID Sid = NULL;
    LSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInfo;

    Strings = &UnicodeString;

    RtlInitUnicodeString( Strings, L"System Restart");

    RtlInitUnicodeString( &LsapSubsystemName, L"Security" );

    if (Pass == 1) {

        Status = LsapAdtInitializeLogQueue();

        if (!NT_SUCCESS(Status)) {

            goto AuditInitError;
        }

        //
        // Acquire the LSA Database Lock.
        //

        Status = LsapDbAcquireLock();

        if (!NT_SUCCESS(Status)) {

             goto AuditInitError;
        }

        AcquiredLock = TRUE;

        //
        // Read the Audit Log Information from the PolAdtLg attribute of the Lsa
        // Database object.
        //

        Status = LsapDbReadAttributeObject(
                     LsapDbHandle,
                     &LsapDbNames[PolAdtLg],
                     &LsapAdtLogInformation,
                     &AuditLogInfoLength
                     );

        if (!NT_SUCCESS(Status)) {

            LsapLogError(
                "LsapAdtInitialize: Read Audit Log Info returned 0x%lx\n",
                Status
                );

            goto AuditInitError;
        }

        //
        // Query the Audit Log Full Information in the LSA Database.  Note
        // that it is too early to update a log full condition, so don't
        // try to write to the Audit Log.
        //

        Status = LsapAdtQueryAuditLogFullInfo(
                     LsapDbHandle,
                     (ULONG) 0,
                     &LsapAdtLogFullInformation
                     );

        if (!NT_SUCCESS(Status)) {

            LsapLogError(
                "LsapAdtInitialize: Update Audit Log Full Info returned 0x%lx\n",
                Status
                );

            goto AuditInitError;
        }

        //
        // Read the Audit Event Information from the AdtEvent attribute of the Lsa
        // Database object.  The information consists of the Auditing Mode and
        // the Auditing Options for each Audit Event Type.
        //

        Status = LsapDbReadAttributeObject(
                     LsapDbHandle,
                     &LsapDbNames[PolAdtEv],
                     &AuditEventsInfo,
                     &AuditEventInfoLength
                     );

        if (!NT_SUCCESS(Status)) {

            //
            // This section of code is temporary and allows an old
            // Policy Database to work with the new Audit Event Categories
            // without the need to re-install.  The Audit Event Information
            // is overwritten with the new format and all auditing is turned
            // off.
            //

            if (Status == STATUS_BUFFER_OVERFLOW) {

                KdPrint(("LsapAdtInitialize: Old Audit Event Info detected\n"
                        "Replacing with new format, all auditing disabled\n"));

                //
                // Initialize Default Event Auditing Options.  No auditing is specified
                // for any event type.
                //

                Status = LsapAdtInitializeDefaultAuditing(
                             LSAP_DB_UPDATE_POLICY_DATABASE,
                             &AuditEventsInfo
                             );

                if (!NT_SUCCESS(Status)) {

                    goto AuditInitError;
                }

            } else {

                LsapLogError(
                    "LsapAdtInitialize: Read Audit Event Info returned 0x%lx\n",
                    Status
                    );
                goto AuditInitError;
            }
        }

        //
        // Set global flags to tell us if we're supposed to be auditing
        // successful logons, failed logons, or both
        //
        //

        LsapAdtAuditingLogon( &AuditEventsInfo );

        //
        // During system initialization, we are effectively logged on as
        // system.
        //

        LsapAdtSystemRestart( &AuditEventsInfo );

        (VOID) LsapAdtInitializeCrashOnFail();

        //
        // Send a command to the Reference Monitor to write the Auditing
        // State to its in-memory data.
        //

        Status = LsapCallRm(
                     RmAuditSetCommand,
                     &AuditEventsInfo,
                     sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO),
                     NULL,
                     0
                     );

        if (!NT_SUCCESS(Status)) {

            LsapLogError("LsapAdtInitialize: LsapCallRm returned 0x%lx\n", Status);
            goto AuditInitError;
        }

        RtlCopyMemory(
            &LsapAdtEventsInformation,
            &AuditEventsInfo,
            sizeof(LSARM_POLICY_AUDIT_EVENTS_INFO)
            );


        LsapAdtInitializeDriveLetters();

        //
        // Initialize privilege values we need
        //

        ChangeNotifyPrivilege       = RtlConvertLongToLargeInteger( SE_CHANGE_NOTIFY_PRIVILEGE      );
        AuditPrivilege              = RtlConvertLongToLargeInteger( SE_AUDIT_PRIVILEGE              );
        CreateTokenPrivilege        = RtlConvertLongToLargeInteger( SE_CREATE_TOKEN_PRIVILEGE       );
        AssignPrimaryTokenPrivilege = RtlConvertLongToLargeInteger( SE_ASSIGNPRIMARYTOKEN_PRIVILEGE );
        BackupPrivilege             = RtlConvertLongToLargeInteger( SE_BACKUP_PRIVILEGE             );
        RestorePrivilege            = RtlConvertLongToLargeInteger( SE_RESTORE_PRIVILEGE            );
        DebugPrivilege              = RtlConvertLongToLargeInteger( SE_DEBUG_PRIVILEGE              );

    } else if (Pass == 2) {

        //
        // Write out any Audit Records that were cached during the
        // first stage of initialization.  The Audit Log will be opened
        // on the first write if necessary.
        //

        //
        // BUGBUG - ScottBi 8/6/92 - This action cannot be taken here
        // unless we know that the EventLog service is running.  For now,
        // an attempt is made to open the log each time an Audit Record
        // is generated, and the cache grows until a limit is reached,
        // at which point auditing is turned off and subsequent records
        // are discarded.
        //

        /*
        Status = LsapAdtWriteLog( NULL, (ULONG) 0);

        if (!NT_SUCCESS(Status)) {

            goto AuditInitError;
        }
        */
    }

AuditInitFinish:

    if (AcquiredLock) {

        LsapDbReleaseLock();
    }

    return(Status);

AuditInitError:

    //
    // If the Audit Log is full, signal the Log Full condition
    //

    if (Status == STATUS_LOG_FILE_FULL) {

        SecondaryStatus = LsapAdtSignalLogFull();
    }

    //
    // If auditing failed to initialize, output warning and disable
    // auditing.
    //

    if (Pass == 1) {

        LsapLogError(
            "LSA: Warning - Audit Initialization Pass 1 Returned 0x%lx\n"
            "     Auditing has been disabled\n",
            Status
            );

    } else {

        LsapLogError(
            "LSA: Warning - Audit Initialization Pass 2 Returned 0x%lx\n"
            "     Auditing has been disabled\n",
            Status
            );
    }

    LsapAdtEventsInformation.AuditingMode = FALSE;

    Status = LsarSetInformationPolicy(
                 LsapDbHandle,
                 PolicyAuditEventsInformation,
                 (PLSAPR_POLICY_INFORMATION) &LsapAdtEventsInformation
                 );

    goto AuditInitFinish;
}


NTSTATUS
LsapAdtInitializeDefaultAuditing(
    IN ULONG Options,
    OUT PLSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInformation
    )

/*++

Routine Description:

    This routine sets an initial default Auditing State in which auditing
    is turned off.  It is called only during initialization of the LSA
    or during the installation of its Policy Database.  The initial
    auditing state may also optionally be written to the Lsa Policy
    Database provided that the Policy Object has been created and its
    internal handle is available.

Arguments:

    Options - Specifies optional actions to be taken

        LSAP_DB_UPDATE_POLICY_DATABASE - Update the corresponding information
            in the Policy Database.  This option must only be specified
            where it is known that the Policy Object exists.

    AuditEventsInformation - Pointer to structure that will receive the Audit Event
        Information

Return Values:

    None.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    LSAP_DB_ATTRIBUTE AuditEventsAttribute;
    BOOLEAN ObjectReferenced = FALSE;

    ULONG EventAuditingOptionsLength =
        (POLICY_AUDIT_EVENT_TYPE_COUNT * sizeof(POLICY_AUDIT_EVENT_OPTIONS));

    //
    // Turn off auditing and set the count of Audit Event Types (Categories)
    //

    AuditEventsInformation->AuditingMode = FALSE;
    AuditEventsInformation->MaximumAuditEventCount = POLICY_AUDIT_EVENT_TYPE_COUNT;

    //
    // Turn off auditing for all events.
    //

    RtlZeroMemory(AuditEventsInformation->EventAuditingOptions, EventAuditingOptionsLength);


    if (Options & LSAP_DB_UPDATE_POLICY_DATABASE) {

        ASSERT(LsapPolicyHandle != NULL);

        //
        // Start a transaction on the Policy Object
        //

        Status = LsapDbReferenceObject(
                     LsapPolicyHandle,
                     (ACCESS_MASK) 0,
                     PolicyObject,
                     LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION
                     );

        if (!NT_SUCCESS(Status)) {

            goto InitializeDefaultAuditingError;
        }

        ObjectReferenced = TRUE;

        LsapDbInitializeAttribute(
            &AuditEventsAttribute,
            &LsapDbNames[PolAdtEv],
            AuditEventsInformation,
            sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO),
            FALSE
            );

        Status = LsapDbWriteAttributesObject(
                     LsapPolicyHandle,
                     &AuditEventsAttribute,
                     (ULONG) 1
                     );

        if (!NT_SUCCESS(Status)) {

            goto InitializeDefaultAuditingError;
        }
    }

InitializeDefaultAuditingFinish:

    if (ObjectReferenced) {

        Status = LsapDbDereferenceObject(
                     LsapPolicyHandle,
                     PolicyObject,
                     LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION,
                     (SECURITY_DB_DELTA_TYPE) 0,
                     Status
                     );

        ObjectReferenced = FALSE;
    }

    return(Status);

InitializeDefaultAuditingError:

    goto InitializeDefaultAuditingFinish;
}


NTSTATUS
LsapAdtInitializeLogQueue(
    )

/*++

Routine Description:

    This function initializes the Audit Log Queue.

Arguments:

    None.

Return Values:

    NTSTATUS - Standard Nt Result Code

        Currently, STATUS_SUCCESS is always returned.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;

    RtlInitializeCriticalSection(&LsapAdtQueueLock);

    LsapAdtLogQueue.FirstQueuedRecord = NULL;
    LsapAdtLogQueue.LastQueuedRecord = NULL;

    return(Status);
}






VOID
LsapAdtAuditingLogon(
    PLSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInfo
    )

/*++

Routine Description:

    Examines auditing data and determines if we are auditing
    logon events.

Arguments:


    AuditEventsInfo - Auditing data.

Return Value:

    TRUE if auditing logon, FALSE otherwise.


--*/

{

    if ( !AuditEventsInfo->AuditingMode ) {

        LsapAuditSuccessfulLogons = FALSE;
        LsapAuditFailedLogons     = FALSE;

        return;
    }

    if ( (AuditEventsInfo->EventAuditingOptions)[AuditCategoryLogon] & POLICY_AUDIT_EVENT_SUCCESS ) {

        LsapAuditSuccessfulLogons = TRUE;

    } else {

        LsapAuditSuccessfulLogons = FALSE;
    }




    if ( (AuditEventsInfo->EventAuditingOptions)[AuditCategoryLogon] & POLICY_AUDIT_EVENT_FAILURE ) {

        LsapAuditFailedLogons = TRUE;

    } else {

        LsapAuditFailedLogons = FALSE;
    }
}



VOID
LsapAdtInitializeDriveLetters(
    VOID
    )
/*++

Routine Description:

    Initializes an array of symbolic link to drive letter mappings
    for use by auditing code.


Arguments:

    None.

Return Value:

    None.

--*/
{
    UNICODE_STRING LinkName;
    PUNICODE_STRING DeviceName;
    OBJECT_ATTRIBUTES Obja;
    HANDLE LinkHandle;
    NTSTATUS Status;
    ULONG i;
    PWCHAR p;
    PWCHAR DeviceNameBuffer;
    ULONG MappingIndex = 0;

    RtlInitUnicodeString(&LinkName,L"\\DosDevices\\A:");


    p = (PWCHAR)LinkName.Buffer;

    //
    // Make p point to the drive letter in the LinkName string
    //

    p = p+12;



    for( i=0 ; i<26 ; i++ ){

        *p = (WCHAR)'A' + (WCHAR)i;

        InitializeObjectAttributes(
            &Obja,
            &LinkName,
            OBJ_CASE_INSENSITIVE,
            NULL,
            NULL
            );
        Status = NtOpenSymbolicLinkObject(
                    &LinkHandle,
                    SYMBOLIC_LINK_QUERY,
                    &Obja
                    );
        if (NT_SUCCESS( Status )) {

            //
            // Open succeeded, Now get the link value
            //

            DriveMappingArray[MappingIndex].DriveLetter = *p;
            DeviceName = &DriveMappingArray[MappingIndex].DeviceName;


            DeviceNameBuffer = LsapAllocateLsaHeap( MAXIMUM_FILENAME_LENGTH );


            DeviceName->Length = 0;
            DeviceName->MaximumLength = MAXIMUM_FILENAME_LENGTH;
            DeviceName->Buffer = DeviceNameBuffer;

            Status = NtQuerySymbolicLinkObject(
                        LinkHandle,
                        DeviceName,
                        NULL
                        );

            NtClose(LinkHandle);

            if ( NT_SUCCESS(Status) ) {

                MappingIndex++;

            } else {

                LsapFreeLsaHeap( DeviceNameBuffer );
                RtlInitUnicodeString( DeviceName, NULL );
            }
        }
    }
}


NTSTATUS
LsapAdtInitializeCrashOnFail(
    VOID
    )

/*++

Routine Description:

    Reads the registry to see if the user has told us to crash if an audit fails.

Arguments:

    None.

Return Value:

    STATUS_SUCCESS

--*/

{
    HANDLE KeyHandle;
    NTSTATUS Status;
    NTSTATUS TmpStatus;
    OBJECT_ATTRIBUTES Obja;
    ULONG ResultLength;
    UNICODE_STRING KeyName;
    UNICODE_STRING ValueName;
    CHAR KeyInfo[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(BOOLEAN)];
    PKEY_VALUE_PARTIAL_INFORMATION pKeyInfo;

    //
    // Check the value of the CrashOnAudit key.
    //

    RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa");

    InitializeObjectAttributes( &Obja,
                                &KeyName,
                                OBJ_CASE_INSENSITIVE,
                                NULL,
                                NULL
                                );

    Status = NtOpenKey(
                 &KeyHandle,
                 KEY_QUERY_VALUE | KEY_SET_VALUE,
                 &Obja
                 );


    if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
        LsapCrashOnAuditFail = FALSE;
        return( STATUS_SUCCESS );
    }

    RtlInitUnicodeString( &ValueName, CRASH_ON_AUDIT_FAIL_VALUE );

    Status = NtQueryValueKey(
                 KeyHandle,
                 &ValueName,
                 KeyValuePartialInformation,
                 KeyInfo,
                 sizeof(KeyInfo),
                 &ResultLength
                 );

    TmpStatus = NtClose(KeyHandle);
    ASSERT(NT_SUCCESS(TmpStatus));

    //
    // If it's not found, don't enable CrashOnFail.
    //

    if (!NT_SUCCESS( Status )) {

        LsapCrashOnAuditFail = FALSE;

    } else {

        pKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo;
        LsapCrashOnAuditFail = (BOOLEAN) *(pKeyInfo->Data);
    }

    if ( LsapCrashOnAuditFail ) {

        BOOLEAN WasEnabled;

        Status = RtlAdjustPrivilege(
                     SE_SHUTDOWN_PRIVILEGE,
                     TRUE,
                     FALSE,
                     &WasEnabled
                     );

        //
        // This had better work.
        //

        ASSERT(NT_SUCCESS(Status));

        return( Status );

    }

    return( STATUS_SUCCESS );
}
