/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    adtevent.c

Abstract:

    Functions that implement audits generated by LSA itself.

Author:

    Scott Birrell       (ScottBi)      January 19, 1993

Environment:

Revision History:

--*/

#include <lsapch2.h>
#include "adtp.h"
#include "adtutil.h"



// ----------------------------------------------------------------------


//
// Lsa Global flags to indicate if we are auditing logon events.
//

BOOLEAN LsapAuditSuccessfulLogons = FALSE;
BOOLEAN LsapAuditFailedLogons = FALSE;


//
// Forwards
//
NTSTATUS
LsapAdtGetDbAttributesChangeString(
    IN LSAP_DB_ATTRIBUTE* OldAttributes,
    IN LSAP_DB_ATTRIBUTE* NewAttributes,
    IN ULONG AttributeCount,
    OUT LPWSTR* AttributeChangeString
    );


NTSTATUS
LsapAdtGenerateObjectOperationAuditEvent(
    IN LSAPR_HANDLE ObjectHandle,
    IN USHORT AuditEventType,
    IN OBJECT_OPERATION_TYPE OperationType
    )
/*++

Routine Description:

    Generates an audit entry when an operation on the object
    represented by ObjectHandle succeds/fails and if this type of
    auditing is enabled.

Arguments:

    ObjectHandle - Handle of the object being accessed

    AuditEventType - The type of audit event to be generated.
        EVENTLOG_AUDIT_SUCCESS or EVENTLOG_AUDIT_FAILURE

    OperationType - Type of operation performed on the object
        represented by ObjectHandle.

Return Value:

    NTSTATUS - Standard Nt Result Code

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    SE_ADT_PARAMETER_ARRAY AuditParameters;
    LUID ClientAuthenticationId;
    PTOKEN_USER TokenUserInformation=NULL;
    UNICODE_STRING OperationTypeName;
    LSAP_DB_HANDLE InternalHandle;
    UNICODE_STRING ObjectName;
    LUID SystemAuthId = SYSTEM_LUID;
    
    static LPCWSTR ObjectOperationNames[ObjectOperationDummyLast] = {
        L"None",
        L"Query"
    };

    LsapEnterFunc("LsapAdtGenerateObjectAcessAuditEvent");

    if (!LsapAdtIsAuditingEnabledForCategory( AuditCategoryObjectAccess,
                                              AuditEventType)) {
        goto FunctionReturn;
    }
    
    InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
    
    Status = LsapQueryClientInfo(
                 &TokenUserInformation,
                 &ClientAuthenticationId
                 );

    if ( !NT_SUCCESS( Status )) {
        goto Cleanup;
    }

    if ( RtlEqualLuid( &ClientAuthenticationId, &SystemAuthId )) {

        //
        // do not audit secret queries by the system
        //

        goto Cleanup;
    }
    
    //
    // LsarQuerySecret sometimes passes us a secret whose name will
    // be rejected by ElfReportEventW because the length parameter
    // includes the terminating NULL.
    // 
    // For example,
    //   name.Buffer        = "foo\0"
    //   name.Length        = 8
    //   name.MaximumLength = 8
    //   
    // We cannot change the input param or change the LSA code to
    // not do this, therfore we make a local copy, fix it
    // and use that instead
    //

    ObjectName = InternalHandle->PhysicalNameU;
    ObjectName.Length = (USHORT) LsapSafeWcslen( ObjectName.Buffer,
                                                 ObjectName.MaximumLength );
    
    //
    // Build an audit parameters structure.
    //
    RtlInitUnicodeString( &OperationTypeName, ObjectOperationNames[OperationType] );
    
    LsapAdtInitParametersArray(
        &AuditParameters,
        SE_CATEGID_OBJECT_ACCESS,
        SE_AUDITID_OBJECT_OPERATION,
        AuditEventType,
        11,                     // there are 11 params to init

        //
        //    User Sid
        //
        SeAdtParmTypeSid,        TokenUserInformation->User.Sid,

        //
        //    Subsystem name (if available)
        //
        SeAdtParmTypeString,     &LsapSubsystemName,

        //
        //    Operation Type
        //
        SeAdtParmTypeString,     &OperationTypeName,

        //
        //    Object Type : index of this is 3, used later
        //
        SeAdtParmTypeString,     &LsapDbObjectTypeNames[InternalHandle->
                                                       ObjectTypeId],

        //
        //    Object Name
        //
        SeAdtParmTypeString,     &ObjectName,

        //
        //    Object Handle ID
        //
        SeAdtParmTypePtr,      ObjectHandle,

        //
        // Primary Authentication information
        //
        SeAdtParmTypeLogonId,    LsapSystemLogonId,

        //
        // Clients's Authentication information
        //
        SeAdtParmTypeLogonId,    ClientAuthenticationId,

        //
        // Requested access : 3 is the index of ObjectType parameter
        //
        SeAdtParmTypeAccessMask, InternalHandle->RequestedAccess, 3,

        //
        // there are no object properties (object-type list)
        //
        SeAdtParmTypeNone,

        //
        // no additional information
        //
        SeAdtParmTypeNone
        
        );
    

    ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );

Cleanup:
    
    if (TokenUserInformation != NULL) 
    {
        LsapFreeLsaHeap( TokenUserInformation );
    }

    if (!NT_SUCCESS(Status)) {

        LsapAuditFailed( Status );
    }

FunctionReturn:
    LsapExitFunc("LsapAdtGenerateObjectAcessAuditEvent", Status);

    return Status;
}


NTSTATUS
LsapAdtGenerateLsaAuditEvent(
    IN LSAPR_HANDLE ObjectHandle,
    IN ULONG AuditEventCategory,
    IN ULONG AuditEventId,
    IN PPRIVILEGE_SET Privileges,
    IN ULONG SidCount,
    IN PSID *Sids OPTIONAL,
    IN ULONG UnicodeStringCount,
    IN PUNICODE_STRING UnicodeStrings OPTIONAL,
    IN PLSARM_POLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo OPTIONAL
    )

/*++

Routine Description:

    This function generates an Lsa-originated Audit Event.  Audit Events
    of this kind are generated as a result of Local Security Policy changes
    such as assigning/removing user rights to an account.

Arguments:

    ObjectHandle - Specifies the handle of an object in the Lsa Policy
        Database.  For global changes to policy, a handle to the
        Lsa Policy object is passed.

    AuditEventCategory - Specifies the Id of the Audit Event Category
        to which this Audit Event belongs.

    AuditEventId - Specifies the Id of the Audit Event being generated.

    LuidCount - Count of Locally Unique Ids being passed via the Luids
        parameter.  If no Locally Unique Ids are passed, this parameter must
        be set to 0.

    Luids - Pointer to array of LuidCount Locally Unique Ids and their attributes.
        The attributes are ignored.  If 0 is passed for the LuidCount
        parameter, this parameter is ignored and NULL may be specified.

    SidCount - Count of Sids being passed via the Sids parameter.  If no
        Sids are passed, this parameter must be set to 0.

    Sids - Pointer to array of SidCount Sids.  If 0 is passed for the
        SidCount parameter, this parameter is ignored and NULL may be
        specified.

    UnicodeStringCount - Count of Unicode Strings being passed via the
        UnicodeStrings parameter.  If no Unicode Strings are passed, this
        parameter must be set to 0.

    UnicodeStrings - Pointer to array of UnicodeStringCount strings.  If 0 is
        passed for the SidCount parameter, this parameter is ignored and NULL
        may be specified.

    PolicyAuditEventsInfo - Pointer to Auditing Events information structure
        containing the AuditingMode and the array of Policy Audit Event
        Information entries.  This parameter must be non-NULL if and only if
        the AuditEventCategory parameter is SE_AUDIT_POLICY_CHANGE.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    LUID ClientAuthenticationId;
    PTOKEN_USER TokenUserInformation = NULL;
    PSID ClientSid;


    UNREFERENCED_PARAMETER( ObjectHandle );

    Status = LsapQueryClientInfo(
                 &TokenUserInformation,
                 &ClientAuthenticationId
                 );

    if ( !NT_SUCCESS( Status )) {

        goto Cleanup;
    }

    ClientSid = TokenUserInformation->User.Sid;

    Status = LsapAdtGenerateLsaAuditEventWithClientSid( AuditEventCategory,
                                                        AuditEventId,
                                                        ClientSid,
                                                        ClientAuthenticationId,
                                                        Privileges,
                                                        SidCount,
                                                        Sids,
                                                        UnicodeStringCount,
                                                        UnicodeStrings,
                                                        PolicyAuditEventsInfo );



Cleanup:

    if (TokenUserInformation != NULL) 
    {
        LsapFreeLsaHeap( TokenUserInformation );
    }

    if ( !NT_SUCCESS( Status )) {

        LsapAuditFailed( Status );
    }
    
    return(Status);
}


NTSTATUS
LsapAdtGenerateLsaAuditEventWithClientSid(
    IN ULONG AuditEventCategory,
    IN ULONG AuditEventId,
    IN PSID ClientSid,
    IN LUID ClientAuthenticationId,
    IN PPRIVILEGE_SET Privileges,
    IN ULONG SidCount,
    IN PSID *Sids OPTIONAL,
    IN ULONG UnicodeStringCount,
    IN PUNICODE_STRING UnicodeStrings OPTIONAL,
    IN PLSARM_POLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo OPTIONAL
    )

/*++

Routine Description:

    This function generates an Lsa-originated Audit Event.  Audit Events
    of this kind are generated as a result of Local Security Policy changes
    such as assigning/removing user rights to an account.

Arguments:

    ObjectHandle - Specifies the handle of an object in the Lsa Policy
        Database.  For global changes to policy, a handle to the
        Lsa Policy object is passed.

    AuditEventCategory - Specifies the Id of the Audit Event Category
        to which this Audit Event belongs.

    AuditEventId - Specifies the Id of the Audit Event being generated.

    LuidCount - Count of Locally Unique Ids being passed via the Luids
        parameter.  If no Locally Unique Ids are passed, this parameter must
        be set to 0.

    Luids - Pointer to array of LuidCount Locally Unique Ids and their attributes.
        The attributes are ignored.  If 0 is passed for the LuidCount
        parameter, this parameter is ignored and NULL may be specified.

    SidCount - Count of Sids being passed via the Sids parameter.  If no
        Sids are passed, this parameter must be set to 0.

    Sids - Pointer to array of SidCount Sids.  If 0 is passed for the
        SidCount parameter, this parameter is ignored and NULL may be
        specified.

    UnicodeStringCount - Count of Unicode Strings being passed via the
        UnicodeStrings parameter.  If no Unicode Strings are passed, this
        parameter must be set to 0.

    UnicodeStrings - Pointer to array of UnicodeStringCount strings.  If 0 is
        passed for the SidCount parameter, this parameter is ignored and NULL
        may be specified.

    PolicyAuditEventsInfo - Pointer to Auditing Events information structure
        containing the AuditingMode and the array of Policy Audit Event
        Information entries.  This parameter must be non-NULL if and only if
        the AuditEventCategory parameter is SE_AUDIT_POLICY_CHANGE.
--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    UNICODE_STRING NullString = {0};

    if (NULL == UnicodeStrings)
    {
        UnicodeStrings = &NullString;
    }

    UNREFERENCED_PARAMETER( UnicodeStringCount );
    UNREFERENCED_PARAMETER( SidCount );
    
    switch ( AuditEventCategory ) {
        case SE_CATEGID_POLICY_CHANGE:
            {
                switch ( AuditEventId ) {

                    default:
                        DsysAssertMsg(FALSE, "LsapAdtGenerateLsaAuditEventWithClientSid: invalid AuditEventId");
                        break;
                        
                    case SE_AUDITID_POLICY_CHANGE:
                        {

                            LsapAdtPolicyChange(
                                (USHORT)AuditEventCategory,
                                AuditEventId,
                                EVENTLOG_AUDIT_SUCCESS,
                                ClientSid,
                                ClientAuthenticationId,
                                PolicyAuditEventsInfo
                                );
                            break;
                        }

                    case SE_AUDITID_USER_RIGHT_ASSIGNED:
                    case SE_AUDITID_USER_RIGHT_REMOVED:
                        {

                            DsysAssertMsg( SidCount == 1,
                                           "LsapAdtGenerateLsaAuditEventWithClientSid" );
                            LsapAdtUserRightAssigned(
                                (USHORT)AuditEventCategory,
                                AuditEventId,
                                EVENTLOG_AUDIT_SUCCESS,
                                ClientSid,
                                ClientAuthenticationId,
                                Sids[0],
                                Privileges
                                );
                            break;
                        }
                }


            break;

            }

        default:
            {
                DsysAssertMsg( FALSE, "LsapAdtGenerateLsaAuditEventWithClientSid: unsupported audit category" );
                return( STATUS_SUCCESS );
            }
    }


    return(Status);
}


VOID
LsapAdtUserRightAssigned(
    IN USHORT EventCategory,
    IN ULONG  EventID,
    IN USHORT EventType,
    IN PSID ClientSid,
    IN LUID CallerAuthenticationId,
    IN PSID TargetSid,
    IN PPRIVILEGE_SET Privileges
    )
/*++

Routine Description:

    Generates an audit for a user right being either assigned or removed.


Arguments:


Return Value:

    None.

--*/
{
    SE_ADT_PARAMETER_ARRAY AuditParameters;

    //
    // Build an audit parameters structure.
    //

    RtlZeroMemory (
       (PVOID) &AuditParameters,
       sizeof( AuditParameters )
       );

    AuditParameters.CategoryId = EventCategory;
    AuditParameters.AuditId = EventID;
    AuditParameters.Type = EventType;
    AuditParameters.ParameterCount = 0;

    //
    //    User Sid
    //

    LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, ClientSid );
    AuditParameters.ParameterCount++;

    //
    //    Subsystem name (if available)
    //

    LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
    AuditParameters.ParameterCount++;

    //
    // Rights
    //

    LsapSetParmTypePrivileges( AuditParameters, AuditParameters.ParameterCount, Privileges );
    AuditParameters.ParameterCount++;

    //
    // Target Sid
    //

    LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, TargetSid );
    AuditParameters.ParameterCount++;

    //
    // Caller's Authentication information
    //

    LsapSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, CallerAuthenticationId );
    AuditParameters.ParameterCount++;


    ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );

    return;
}


VOID
LsapAdtGenerateLsaAuditSystemAccessChange(
    IN USHORT EventCategory,
    IN ULONG  EventID,
    IN USHORT EventType,
    IN PSID ClientSid,
    IN LUID CallerAuthenticationId,
    IN PSID TargetSid,
    IN PCWSTR szSystemAccess
    )

/*++

Routine Description:

    Generates an audit for System Security Access changes.


Arguments:

    EventCategory - The category of this event
    EventID - specific ID of event
    EventType - success or failure
    ClientSid - sid of client
    CallerAuthenticationID - Logon ID of caller
    TargetSid - receives access change
    szSystemAccess - string describing which access changed

Return Value:

    None.

--*/
{
    SE_ADT_PARAMETER_ARRAY AuditParameters;
    UNICODE_STRING SystemAccessString;

    //
    // Build an audit parameters structure.
    //

    RtlZeroMemory (
       (PVOID) &AuditParameters,
       sizeof( AuditParameters )
       );

    RtlInitUnicodeString( &SystemAccessString, szSystemAccess );
    LsapAdtInitParametersArray( &AuditParameters, 
                                EventCategory, 
                                EventID, 
                                EventType, 
                                5, 
                                SeAdtParmTypeSid,         ClientSid,
                                SeAdtParmTypeString,      &LsapSubsystemName,
                                SeAdtParmTypeLogonId,     CallerAuthenticationId,
                                SeAdtParmTypeString,      &SystemAccessString,
                                SeAdtParmTypeSid,         TargetSid
                                );

    (VOID) LsapAdtWriteLog( &AuditParameters, 0 );

    return;
}




NTSTATUS
LsapAdtTrustedDomainAdd(
    IN USHORT          EventType,
    IN PUNICODE_STRING pName,
    IN PSID            pSid,
    IN ULONG           Type,
    IN ULONG           Direction,
    IN ULONG           Attributes
    )
/*++

Routine Description:

    Generate an audit event when a trusted domain object (TDO) is created.

Arguments:

    EventType    - EVENTLOG_AUDIT_SUCCESS or EVENTLOG_AUDIT_FAILURE

    pName        - name of the domain

    pSid         - domain SID

    Type         - TDO type

    Direction    - TDO direction

    Attributes   - TDO attributes

Return Value:

    NTSTATUS - Standard Nt Result Code

Notes:

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    LUID ClientAuthenticationId;
    PTOKEN_USER TokenUserInformation=NULL;
    SE_ADT_PARAMETER_ARRAY AuditParameters = { 0 };

    //
    //  if auditing is not enabled, return asap
    //

    if ( !LsapAdtIsAuditingEnabledForCategory( AuditCategoryPolicyChange,
                                               EventType ) )
    {
        goto Cleanup;
    }

    Status = LsapQueryClientInfo( &TokenUserInformation, &ClientAuthenticationId );

    if ( !NT_SUCCESS( Status ))
    {
        goto Cleanup;
    }

    //
    // Build an audit parameters structure.
    //
    
    LsapAdtInitParametersArray(
        &AuditParameters,
        SE_CATEGID_POLICY_CHANGE,
        SE_AUDITID_TRUSTED_DOMAIN_ADD,
        EventType,
        8,                     // there are 8 params to init

        //
        //    User Sid
        //

        SeAdtParmTypeSid,        TokenUserInformation->User.Sid,

        //
        //    Subsystem name (if available)
        //

        SeAdtParmTypeString,     &LsapSubsystemName,

        //
        //    domain name
        //

        SeAdtParmTypeString,      pName,

        //
        //    domain id 
        //

        SeAdtParmTypeSid,         pSid,

        //
        //    client auth-id
        //

        SeAdtParmTypeLogonId,     ClientAuthenticationId,
        
        //
        //    TDO type
        //

        SeAdtParmTypeUlong,       Type,

        //
        //    TDO direction
        //

        SeAdtParmTypeUlong,       Direction,

        //
        //    TDO attributes
        //

        SeAdtParmTypeUlong,       Attributes

        );
    
    Status = LsapAdtWriteLog( &AuditParameters, 0 );
        
Cleanup:

    if (TokenUserInformation != NULL) 
    {
        LsapFreeLsaHeap( TokenUserInformation );
    }

    if (!NT_SUCCESS(Status))
    {
        LsapAuditFailed( Status );
    }
    
    return Status;
}


NTSTATUS
LsapAdtTrustedDomainRem(
    IN USHORT          EventType,
    IN PUNICODE_STRING pName,
    IN PSID            pSid,
    IN PSID            pClientSid,
    IN PLUID           pClientAuthId
    )
/*++

Routine Description:

    Generate an audit event when a trusted domain object (TDO) is deleted.

Arguments:

    EventType    - EVENTLOG_AUDIT_SUCCESS or EVENTLOG_AUDIT_FAILURE

    pName        - name of the domain

    pSid         - domain SID

    pClientSid   - SID of the client who deleted the TDO
                   if NULL, it is determined from the thread token

    pClientAuthId- auth-id of the client who deleted the TDO
                   if NULL, it is determined from the thread token

Return Value:

    NTSTATUS - Standard Nt Result Code

Notes:

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    LUID ClientAuthenticationId;
    PTOKEN_USER TokenUserInformation=NULL;
    SE_ADT_PARAMETER_ARRAY AuditParameters = { 0 };


    //
    //  if auditing is not enabled, return asap
    //

    if ( !LsapAdtIsAuditingEnabledForCategory( AuditCategoryPolicyChange,
                                               EventType ) )
    {
        goto Cleanup;
    }

    if ( pClientSid == NULL )
    {
        DsysAssertMsg( pClientAuthId == NULL, "LsapAdtTrustedDomainRem" );
        
        Status = LsapQueryClientInfo( &TokenUserInformation,
                                      &ClientAuthenticationId );

        if ( !NT_SUCCESS( Status ))
        {
            goto Cleanup;
        }

        pClientSid    = TokenUserInformation->User.Sid;
        pClientAuthId = &ClientAuthenticationId;
    }
#if DBG
    else
    {
        DsysAssertMsg( pClientAuthId != NULL, "LsapAdtTrustedDomainRem" );
    }
#endif

    //
    // Build an audit parameters structure.
    //
    
    LsapAdtInitParametersArray(
        &AuditParameters,
        SE_CATEGID_POLICY_CHANGE,
        SE_AUDITID_TRUSTED_DOMAIN_REM,
        EventType,
        5,                     // there are 5 params to init

        //
        //    User Sid
        //

        SeAdtParmTypeSid,        pClientSid,

        //
        //    Subsystem name (if available)
        //

        SeAdtParmTypeString,     &LsapSubsystemName,

        //
        //    domain name
        //

        SeAdtParmTypeString,      pName,

        //
        //    domain id (SID of the root domain)
        //

        SeAdtParmTypeSid,         pSid,

        //
        //    client auth-id
        //

        SeAdtParmTypeLogonId,     *pClientAuthId
        
        );
    
    Status = LsapAdtWriteLog( &AuditParameters, 0 );
        
Cleanup:

    if (TokenUserInformation != NULL) 
    {
        LsapFreeLsaHeap( TokenUserInformation );
    }

    if (!NT_SUCCESS(Status))
    {
        LsapAuditFailed( Status );
    }
    
    return Status;
}



NTSTATUS
LsapAdtTrustedDomainMod(
    IN USHORT          EventType,
    IN PSID            pDomainSid,

    IN PUNICODE_STRING pOldName,
    IN ULONG           OldType,
    IN ULONG           OldDirection,
    IN ULONG           OldAttributes,

    IN PUNICODE_STRING pNewName,
    IN ULONG           NewType,
    IN ULONG           NewDirection,
    IN ULONG           NewAttributes
    )
/*++

Routine Description:

    Generate an audit event when a trusted domain object (TDO) is modified.
    the unmodified fields are represented by a '-' in the audit log.

Arguments:

    EventType       - EVENTLOG_AUDIT_SUCCESS or EVENTLOG_AUDIT_FAILURE

    pOldName        - old name of the domain

    pOldSid         - old domain SID

    OldType         - old TDO type

    OldDirection    - old TDO direction

    OldAttributes   - old TDO attributes

    pNewName        - new name of the domain

    pNewSid         - new domain SID

    NewType         - new TDO type

    NewDirection    - new TDO direction

    NewAttributes   - new TDO attributes

Return Value:

    NTSTATUS - Standard Nt Result Code

Notes:

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    LUID ClientAuthenticationId;
    PTOKEN_USER TokenUserInformation=NULL;
    SE_ADT_PARAMETER_ARRAY AuditParameters = { 0 };
    UNICODE_STRING TempName;

    //
    //  if auditing is not enabled, return asap
    //

    if ( !LsapAdtIsAuditingEnabledForCategory( AuditCategoryPolicyChange,
                                               EventType ) )
    {
        goto Cleanup;
    }

    Status = LsapQueryClientInfo( &TokenUserInformation, &ClientAuthenticationId );

    if ( !NT_SUCCESS( Status ))
    {
        goto Cleanup;
    }

    AuditParameters.CategoryId     = SE_CATEGID_POLICY_CHANGE;
    AuditParameters.AuditId        = SE_AUDITID_TRUSTED_DOMAIN_MOD;
    AuditParameters.Type           = EventType;
    AuditParameters.ParameterCount = 8;

    //
    //    User Sid
    //

    LsapSetParmTypeSid( AuditParameters, 0, TokenUserInformation->User.Sid );

    //
    //    Subsystem name (if available)
    //

    LsapSetParmTypeString( AuditParameters, 1, &LsapSubsystemName );


    //
    // for all subsequent fields (except the domain SID),
    // output a value only if it changed.
    //

    //
    //    domain name
    //

    if ( pOldName && pNewName &&
         !RtlEqualUnicodeString( pOldName, pNewName, TRUE ) )
    {
        LsapSetParmTypeString( AuditParameters, 2, pNewName );
    }

    //
    //    domain id
    //

    LsapSetParmTypeSid( AuditParameters, 3, pDomainSid );

    //
    //    client auth-id
    //

    LsapSetParmTypeLogonId( AuditParameters, 4, ClientAuthenticationId );

    //
    //    TDO type
    //

    if ( OldType != NewType )
    {
        LsapSetParmTypeUlong( AuditParameters, 5, NewType );
    }
    
    //
    //    TDO direction
    //

    if ( OldDirection != NewDirection )
    {
        LsapSetParmTypeUlong( AuditParameters, 6, NewDirection );
    }
    
    //
    //    TDO attributes
    //

    if ( OldAttributes != NewAttributes )
    {
        LsapSetParmTypeUlong( AuditParameters, 7, NewAttributes );
    }
    

    
    Status = LsapAdtWriteLog( &AuditParameters, 0 );
        
Cleanup:

    if (TokenUserInformation != NULL) 
    {
        LsapFreeLsaHeap( TokenUserInformation );
    }

    if (!NT_SUCCESS(Status))
    {
        LsapAuditFailed( Status );
    }
    
    return Status;
}


NTSTATUS
LsapAdtTrustedForestNamespaceCollision(
    IN LSA_FOREST_TRUST_COLLISION_RECORD_TYPE CollisionTargetType,
    IN PUNICODE_STRING pCollisionTargetName,
    IN PUNICODE_STRING pForestRootDomainName,
    IN PUNICODE_STRING pTopLevelName,
    IN PUNICODE_STRING pDnsName,
    IN PUNICODE_STRING pNetbiosName,
    IN PSID            pSid,
    IN ULONG           NewFlags
    )
/*++

Routine Description:

    This function generates the audit event that represents
    a namespace element collision.


Arguments:

    CollisionTargetType  - type of the collision target
        CollisionTdo  : indicates a collision with a namespace element of
                        another forest
        CollisionXref : indicates a collision with a domain in our forest 

    pCollisionTargetName -
        name of the collision target (TDO name or Xref name)

    pForestRootDomainName - name of other forest

    pTopLevelName - top level name  (NULL == not in conflict)

    pDnsName      - DNS domain name (this is NULL is TLN is non-NULL)

    pNetbiosName  - NetBIOS name    (NULL == not in conflict)

    pSid          - SID of domain   (NULL == not in conflict)

    NewFlags      - the new value of flags

Return Value:

    NTSTATUS - Standard Nt Result Code

Notes:

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    SE_ADT_PARAMETER_ARRAY AuditParameters = { 0 };
#if DBG
    HANDLE hToken;
#endif
    DsysAssert(( CollisionTargetType == CollisionTdo ) ||
               ( CollisionTargetType == CollisionXref));
    DsysAssert( pCollisionTargetName != NULL );
    DsysAssert( pForestRootDomainName != NULL );

#if DBG
    if ( pTopLevelName )
    {
        DsysAssert( pDnsName     == NULL );
        DsysAssert( pNetbiosName == NULL );
        DsysAssert( pSid         == NULL );
    }
    else
    {
        DsysAssert( pDnsName != NULL );

        if ( pNetbiosName != NULL )
        {
            DsysAssert( pSid == NULL );
        }

        if ( pSid != NULL )
        {
            DsysAssert( pNetbiosName == NULL );
        }
    }
#endif

    //
    //  if auditing is not enabled, return asap
    //

    if ( !LsapAdtIsAuditingEnabledForCategory( AuditCategoryPolicyChange,
                                               EVENTLOG_AUDIT_SUCCESS ) )
    {
        goto Cleanup;
    }

#if DBG
    //
    // make sure that this is called in the system context
    //

    Status = NtOpenThreadToken( NtCurrentThread(), TOKEN_QUERY, TRUE, &hToken );

    DsysAssertMsg( Status == STATUS_NO_TOKEN, "LsapAdtTrustedForestNamespaceCollision" );

    if ( NT_SUCCESS(Status) )
    {
        NtClose( hToken );
    }
    else
    {
        Status = STATUS_SUCCESS;
    }
    
#endif

    //
    // Build an audit parameters structure.
    //
    
    LsapAdtInitParametersArray(
        &AuditParameters,
        SE_CATEGID_POLICY_CHANGE,
        SE_AUDITID_NAMESPACE_COLLISION,
        EVENTLOG_AUDIT_SUCCESS,

        //
        // number of params to follow 
        //

        10,

        //
        //    User Sid
        //

        SeAdtParmTypeSid,        LsapLocalSystemSid,

        //
        //    Subsystem name (if available)
        //
        
        SeAdtParmTypeString,     &LsapSubsystemName,

        //
        //    collision target type
        //    
        //    0 == CollisionTdo
        //    1 == CollisionXref
        //    

        SeAdtParmTypeUlong,      CollisionTargetType,

        //
        //    collision target name
        //
        //    name of a TDO or cross-ref
        //    

        SeAdtParmTypeString,     pCollisionTargetName,
        
        //
        //    Name of forest involved in the collision
        //

        SeAdtParmTypeString,     pForestRootDomainName,

        //
        //    top level name
        //

        SeAdtParmTypeString,     pTopLevelName,

        //
        //    DNS name
        //

        SeAdtParmTypeString,     pDnsName,

        //
        //    NetBIOS name
        //

        SeAdtParmTypeString,     pNetbiosName,

        //
        //    SID
        //

        SeAdtParmTypeSid,        pSid,

        //
        //    new flags value
        //

        SeAdtParmTypeUlong,      NewFlags
        
        );

    Status = LsapAdtWriteLog( &AuditParameters, 0 );

Cleanup:    
    if (!NT_SUCCESS(Status))
    {
        LsapAuditFailed( Status );
    }
    
    return Status;
}


NTSTATUS
LsapAdtTrustedForestInfoEntryAddRemHelper(
    IN ULONG           EventId,
    IN USHORT          EventType,
    IN PUNICODE_STRING ForestName,
    IN PSID            pForestRootDomainSid,
    IN PLUID           pOperationId,
    IN LSA_FOREST_TRUST_RECORD_TYPE EntryType,
    IN ULONG           Flags,
    IN PUNICODE_STRING TopLevelName,
    IN PUNICODE_STRING DnsName,
    IN PUNICODE_STRING NetbiosName,
    IN PSID            pSid
    )
/*++

Routine Description:

    Helper function for generating audit event when a namespace
    element has been added to / removed from forest trust info.
    If multiple entries get added, deleted or modified
    in a single update of the forest trust information, all the generated
    audit events will have a single unique identifier called OperationID.
    This allows one to determine that the multiple generated audits are
    the result of a single operation.

Arguments:

    EventId              - SE_AUDITID_TRUSTED_FOREST_INFO_ENTRY_ADD/REM

    EventType            - EVENTLOG_AUDIT_SUCCESS or EVENTLOG_AUDIT_FAILURE

    ForestName           - name of the forest

    pForestRootDomainSid - SID of the forest

    pOperationId         - operation id (see description above)

    EntryType            - type of entry ( TLN | TLN excl. | domain info )

    Flags                - flags associated with the entry ( see ntlsa.h )

    TopLevelName         - TopLevel name
                   
    DnsName              - Dns name
                   
    NetbiosName          - Netbios name

    pSid                 - domain sid

Return Value:

    NTSTATUS - Standard Nt Result Code

Notes:

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    LUID ClientAuthenticationId;
    PTOKEN_USER TokenUserInformation=NULL;
    SE_ADT_PARAMETER_ARRAY AuditParameters = { 0 };

    //
    //  if auditing is not enabled, return asap
    //

    if ( !LsapAdtIsAuditingEnabledForCategory( AuditCategoryPolicyChange,
                                               EventType ) )
    {
        goto Cleanup;
    }

    Status = LsapQueryClientInfo( &TokenUserInformation, &ClientAuthenticationId );

    if ( !NT_SUCCESS( Status ))
    {
        goto Cleanup;
    }

    //
    // Build an audit parameters structure.
    //
    
    LsapAdtInitParametersArray(
        &AuditParameters,
        SE_CATEGID_POLICY_CHANGE,
        EventId,
        EventType,

        //
        // number of params to follow 
        //

        13,

        //
        //    User Sid
        //

        SeAdtParmTypeSid,        TokenUserInformation->User.Sid,

        //
        //    Subsystem name (if available)
        //

        SeAdtParmTypeString,     &LsapSubsystemName,

        //
        //    Forest name
        //

        SeAdtParmTypeString,     ForestName,

        //
        //    Forest SID
        //

        SeAdtParmTypeSid,        pForestRootDomainSid,

        //
        //    Operation ID
        //

        SeAdtParmTypeUlong,      pOperationId->HighPart,
        SeAdtParmTypeUlong,      pOperationId->LowPart,

        //
        //    Entry Type
        //

        SeAdtParmTypeUlong,      EntryType,

        //
        //    Flags
        //

        SeAdtParmTypeUlong,      Flags,

        //
        //    top level name
        //

        SeAdtParmTypeString,     TopLevelName,

        //
        //    DNS domain name
        //

        SeAdtParmTypeString,     DnsName,

        //
        //    NetBIOS domain name
        //

        SeAdtParmTypeString,     NetbiosName,

        //
        //    domain SID
        //

        SeAdtParmTypeSid,        pSid,

        //
        //    user info
        //

        SeAdtParmTypeLogonId,    ClientAuthenticationId
        );
    
    Status = LsapAdtWriteLog( &AuditParameters, 0 );
        
Cleanup:

    if (TokenUserInformation != NULL) 
    {
        LsapFreeLsaHeap( TokenUserInformation );
    }
    
    if (!NT_SUCCESS(Status))
    {
        LsapAuditFailed( Status );
    }
    
    return Status;
}


NTSTATUS
LsapAdtTrustedForestInfoEntryAdd(
    IN PUNICODE_STRING pForestRootDomainName,
    IN PSID            pForestRootDomainSid,
    IN PLUID           pOperationId,
    IN LSA_FOREST_TRUST_RECORD_TYPE EntryType,
    IN ULONG           Flags,
    IN PUNICODE_STRING TopLevelName,
    IN PUNICODE_STRING DnsName,
    IN PUNICODE_STRING NetbiosName,
    IN PSID            pSid
    )
{
    return LsapAdtTrustedForestInfoEntryAddRemHelper(
        SE_AUDITID_TRUSTED_FOREST_INFO_ENTRY_ADD,
        EVENTLOG_AUDIT_SUCCESS,
        pForestRootDomainName,
        pForestRootDomainSid,
        pOperationId,
        EntryType,
        Flags,
        TopLevelName,
        DnsName,
        NetbiosName,
        pSid
        );
}
     

NTSTATUS
LsapAdtTrustedForestInfoEntryRem(
    IN PUNICODE_STRING pForestRootDomainName,
    IN PSID            pForestRootDomainSid,
    IN PLUID           pOperationId,
    IN LSA_FOREST_TRUST_RECORD_TYPE EntryType,
    IN ULONG           Flags,
    IN PUNICODE_STRING TopLevelName,
    IN PUNICODE_STRING DnsName,
    IN PUNICODE_STRING NetbiosName,
    IN PSID            pSid
    )
{
    return LsapAdtTrustedForestInfoEntryAddRemHelper(
        SE_AUDITID_TRUSTED_FOREST_INFO_ENTRY_REM,
        EVENTLOG_AUDIT_SUCCESS,
        pForestRootDomainName,
        pForestRootDomainSid,
        pOperationId,
        EntryType,
        Flags,
        TopLevelName,
        DnsName,
        NetbiosName,
        pSid
        );
}
     

NTSTATUS
LsapAdtTrustedForestInfoEntryMod(
    IN PUNICODE_STRING pForestRootDomainName,
    IN PSID            pForestRootDomainSid,
    IN PLUID           pOperationId,
    IN LSA_FOREST_TRUST_RECORD_TYPE EntryType,
                                    
    IN ULONG           OldFlags,
    IN PUNICODE_STRING pOldTopLevelName,
    IN PUNICODE_STRING pOldDnsName,
    IN PUNICODE_STRING pOldNetbiosName,
    IN PSID            pOldSid,
                       
    IN ULONG           NewFlags,
    IN PUNICODE_STRING pNewTopLevelName,
    IN PUNICODE_STRING pNewDnsName,
    IN PUNICODE_STRING pNewNetbiosName,
    IN PSID            pNewSid
    )
/*++

Routine Description:

    Helper function for generating audit event when a namespace
    element in forest trust info has been modified.
    If multiple entries get added, deleted or modified
    in a single update of the forest trust information, all the generated
    audit events will have a single unique identifier called OperationID.
    This allows one to determine that the multiple generated audits are
    the result of a single operation.

Arguments:

    EventType            - EVENTLOG_AUDIT_SUCCESS or EVENTLOG_AUDIT_FAILURE

    ForestName           - name of the forest

    pForestRootDomainSid - SID of the forest

    pOperationId         - operation id (see description above)

    EntryType            - type of entry ( TLN | TLN excl. | domain info )

    OldFlags             - old flags associated with the entry ( see ntlsa.h )

    pOldTopLevelName     - old TopLevel name
                   
    pOldDnsName          - old Dns name
                   
    pOldNetbiosName      - old Netbios name

    pOldSid              - old domain sid

    NewFlags             - new flags associated with the entry ( see ntlsa.h )

    pNewTopLevelName     - new TopLevel name
                   
    pNewDnsName          - new Dns name
                   
    pNewNetbiosName      - new Netbios name

    pNewSid              - new domain sid

Return Value:

    NTSTATUS - Standard Nt Result Code

Notes:

    The unmodified fields are represented by a '-' in the audit log.


--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    LUID ClientAuthenticationId;
    PTOKEN_USER TokenUserInformation=NULL;
    SE_ADT_PARAMETER_ARRAY AuditParameters = { 0 };

    //
    //  if auditing is not enabled, return asap
    //

    if ( !LsapAdtIsAuditingEnabledForCategory( AuditCategoryPolicyChange,
                                               EVENTLOG_AUDIT_SUCCESS ) )
    {
        goto Cleanup;
    }

    Status = LsapQueryClientInfo( &TokenUserInformation, &ClientAuthenticationId );

    if ( !NT_SUCCESS( Status ))
    {
        goto Cleanup;
    }

    AuditParameters.CategoryId     = SE_CATEGID_POLICY_CHANGE;
    AuditParameters.AuditId        = SE_AUDITID_TRUSTED_FOREST_INFO_ENTRY_MOD;
    AuditParameters.Type           = EVENTLOG_AUDIT_SUCCESS;
    AuditParameters.ParameterCount = 13;

    //
    //    User Sid
    //

    LsapSetParmTypeSid( AuditParameters, 0, TokenUserInformation->User.Sid );

    //
    //    Subsystem name (if available)
    //

    LsapSetParmTypeString( AuditParameters, 1, &LsapSubsystemName );


    //
    //    forest name
    //

    LsapSetParmTypeString( AuditParameters, 2, pForestRootDomainName );

    //
    //    forest id (SID of the root domain)
    //

    LsapSetParmTypeSid( AuditParameters, 3, pForestRootDomainSid );

    //
    //    Operation ID
    //

    LsapSetParmTypeUlong( AuditParameters, 4, pOperationId->HighPart );
    LsapSetParmTypeUlong( AuditParameters, 5, pOperationId->LowPart );

    //
    //    entry type
    //

    LsapSetParmTypeUlong( AuditParameters, 6, EntryType );

    //
    // for all subsequent types, output a value only if it changed.
    //

    //
    //    Flags
    //

    if ( OldFlags != NewFlags )
    {
        LsapSetParmTypeUlong( AuditParameters, 7, NewFlags );
    }

    //
    //    top level name
    //

    if ( pOldTopLevelName && pNewTopLevelName &&
         !RtlEqualUnicodeString( pOldTopLevelName, pNewTopLevelName, TRUE ) )
    {
        LsapSetParmTypeString( AuditParameters, 8, pNewTopLevelName );
    }

    //
    //    DNS domain name
    //

    if ( pOldDnsName && pNewDnsName &&
         !RtlEqualUnicodeString( pOldDnsName, pNewDnsName, TRUE ) )
    {
        LsapSetParmTypeString( AuditParameters, 9, pNewDnsName );
    }

    //
    //    NetBIOS domain name
    //

    if ( pOldNetbiosName && pNewNetbiosName &&
         !RtlEqualUnicodeString( pOldNetbiosName, pNewNetbiosName, TRUE ) )
    {
        LsapSetParmTypeString( AuditParameters, 10, pNewNetbiosName );
    }

    //
    //    domain SID
    //

    if ( pOldSid && pNewSid && !RtlEqualSid( pOldSid, pNewSid ) )
    {
        LsapSetParmTypeSid( AuditParameters, 11, pNewSid );
    }
    
    //
    //    client auth-id
    //

    LsapSetParmTypeLogonId( AuditParameters, 12, ClientAuthenticationId );

    
    Status = LsapAdtWriteLog( &AuditParameters, 0 );
        
Cleanup:

    if (TokenUserInformation != NULL) 
    {
        LsapFreeLsaHeap( TokenUserInformation );
    }
    
    if (!NT_SUCCESS(Status))
    {
        LsapAuditFailed( Status );
    }
    
    return Status;
    
}



VOID
LsapAdtPolicyChange(
    IN USHORT EventCategory,
    IN ULONG  EventID,
    IN USHORT EventType,
    IN PSID ClientSid,
    IN LUID CallerAuthenticationId,
    IN PLSARM_POLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo
    )
/*++

Routine Description:

    Generates an audit for a policy change event.

Arguments:

    EventCategory - The category of this audit.

    EventID - The event we are auditing.

    EventType - Whether the audit is success or failure.

    ClientSid - The SID of the user performing the policy change.

    CallerAuthenticationId - The Authentication id of the user.

    PolicyAuditEventsInfo - The information to audit.


Return Value:

    None.

Note:
--*/
{
    PPOLICY_AUDIT_EVENT_OPTIONS EventAuditingOptions;
    SE_ADT_PARAMETER_ARRAY AuditParameters;
    UNICODE_STRING Enabled;
    UNICODE_STRING Disabled;
    ULONG i;

    RtlInitUnicodeString( &Enabled, L"+" );
    RtlInitUnicodeString( &Disabled, L"-" );
    EventAuditingOptions = PolicyAuditEventsInfo->EventAuditingOptions;

    //
    // Build an audit parameters structure.
    //

    RtlZeroMemory (
       (PVOID) &AuditParameters,
       sizeof( AuditParameters )
       );

    AuditParameters.CategoryId = EventCategory;
    AuditParameters.AuditId = EventID;
    AuditParameters.Type = EventType;
    AuditParameters.ParameterCount = 0;

    //
    //    User Sid
    //

    LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, ClientSid );
    AuditParameters.ParameterCount++;

    //
    //    Subsystem name (if available)
    //

    LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
    AuditParameters.ParameterCount++;

    //
    // If auditing is disabled, mark all options as disabled. Otherwise
    // mark them as the appropriate
    //

    if (PolicyAuditEventsInfo->AuditingMode) {
        for ( i=0; i<POLICY_AUDIT_EVENT_TYPE_COUNT; i++ ) {

            LsapSetParmTypeString(
                AuditParameters,
                AuditParameters.ParameterCount,
                (EventAuditingOptions[i] & POLICY_AUDIT_EVENT_SUCCESS ? &Enabled : &Disabled)
                );

            AuditParameters.ParameterCount++;

            LsapSetParmTypeString(
                AuditParameters,
                AuditParameters.ParameterCount,
                (EventAuditingOptions[i] & POLICY_AUDIT_EVENT_FAILURE ? &Enabled : &Disabled)
                );

            AuditParameters.ParameterCount++;
        }
    } else {
        //
        // Auditing is disabled - mark them all disabled.
        //

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

            LsapSetParmTypeString(
                AuditParameters,
                AuditParameters.ParameterCount,
                &Disabled
                );

            AuditParameters.ParameterCount++;

            LsapSetParmTypeString(
                AuditParameters,
                AuditParameters.ParameterCount,
                &Disabled
                );

            AuditParameters.ParameterCount++;
        }

    }

    //
    // Caller's Authentication information
    //

    LsapSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, CallerAuthenticationId );
    AuditParameters.ParameterCount++;


    ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );

    return;
}



NTSTATUS
LsapAdtGenerateDomainPolicyChangeAuditEvent(
    IN POLICY_DOMAIN_INFORMATION_CLASS InformationClass,
    IN USHORT AuditEventType,
    IN LSAP_DB_ATTRIBUTE* OldAttributes,
    IN LSAP_DB_ATTRIBUTE* NewAttributes,
    IN ULONG AttributeCount
    )
/*++

Routine Description:

    Generate an audit event when any of the following policies changes:
    - PolicyDomainEfsInformation
    - PolicyDomainKerberosTicketInformation

Arguments:

    InformationClass - type of policy that changed

    AuditEventType - The type of audit event to be generated.
        EVENTLOG_AUDIT_SUCCESS or EVENTLOG_AUDIT_FAILURE

    OldAttributes - pointer to array of old attributes

    NewAttributes - pointer to array of new attributes

    AttributeCount - number of attributes

Return Value:

    NTSTATUS - Standard Nt Result Code

Notes:

--*/
{
    NTSTATUS Status=STATUS_SUCCESS;
    SE_ADT_PARAMETER_ARRAY AuditParameters;
    ULONG AuditId;
    LPWSTR AttributeChanges=NULL;
    UNICODE_STRING ChangesToAttributes;
    LUID ClientAuthenticationId;
    PTOKEN_USER TokenUserInformation=NULL;

    AuditId=0;                  // to get rid of uninit var warning

    Status = LsapQueryClientInfo(
                 &TokenUserInformation,
                 &ClientAuthenticationId
                 );

    if ( !NT_SUCCESS( Status )) {
        goto Cleanup;
    }

    switch (InformationClass) {
        default:
            ASSERT(FALSE);
            goto Cleanup;
            break;
            
        case PolicyDomainEfsInformation:
            AuditId = SE_AUDITID_EFS_POLICY_CHANGE;
            break;
        
        case PolicyDomainKerberosTicketInformation:
            AuditId = SE_AUDITID_KERBEROS_POLICY_CHANGE;
            break;
    }

    Status = LsapAdtGetDbAttributesChangeString( OldAttributes,
                                                 NewAttributes,
                                                 AttributeCount,
                                                 &AttributeChanges );
    if (!NT_SUCCESS(Status)) {
        goto Cleanup;
    }

    RtlInitUnicodeString(&ChangesToAttributes, AttributeChanges);
    
    LsapAdtInitParametersArray(
        &AuditParameters,
        SE_CATEGID_POLICY_CHANGE,
        AuditId,
        AuditEventType,
        4,
        //
        //    User Sid
        //
        SeAdtParmTypeSid,     TokenUserInformation->User.Sid,

        //
        //    Subsystem name (if available)
        //
        SeAdtParmTypeString,  &LsapSubsystemName,

        //
        //    Caller's Authentication information
        //
        SeAdtParmTypeLogonId, ClientAuthenticationId,

        //
        //    Changes to attributes
        //
        SeAdtParmTypeString,  &ChangesToAttributes);

    ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );

Cleanup:

    if (TokenUserInformation != NULL) 
    {
        LsapFreeLsaHeap( TokenUserInformation );
    }
    
    LsapFreeLsaHeap( AttributeChanges );
    
    return Status;
}


void LsapAdtGetAttributeValueString(
    IN  LSAP_DB_ATTRIBUTE* Attribute,
    OUT LPWSTR ValueString)
/*++

Routine Description:

    Generate a string representation of the value of an attribute

Arguments:

    Attribute - pointer to attribute

    ValueString - receives a string representation of the value of Attribute

Return Value:

    None

Notes:

--*/
{
    if (Attribute->AttributeValue) {
        
        switch (Attribute->DbNameIndex) {

            default:
                lstrcpy(ValueString, L"unknown");
                break;

            // binary blob
            case PolEfDat:
                lstrcpy(ValueString, L"<binary data>");
                break;

                // ULONG
            case KerOpts:
                swprintf(ValueString, L"0x%x", *((ULONG*) Attribute->AttributeValue));
                break;

                // LARGE_INTEGER
            case KerMinT:
            case KerMaxT:
            case KerMaxR:
            case KerProxy:
            case KerLogoff:
                swprintf(ValueString, L"0x%I64x",
                         *((ULONGLONG*) Attribute->AttributeValue));
                break;
        }
    } else {
        lstrcpy(ValueString, L"none");
    }
}


void
LsapAdtGetDbAttributeChangeString(
    IN LSAP_DB_ATTRIBUTE* OldAttribute,
    IN LSAP_DB_ATTRIBUTE* NewAttribute,
    OUT LPWSTR AttributeChangeString,   OPTIONAL
    IN OUT PULONG RequiredLength
    )
/*++

Routine Description:

    Given an old attribute and a new attribute, return 
    a string representation of the difference between the two.

    If there are no changes, RequiredLength is returned as 0
    and AttributeChangeString is left unchanged;
    otherwise if AttributeChangeString is non-NULL, the change is
    written to it as:
    <ParameterName>: <new value> (<old value>)

Arguments:

    OldAttribute - pointer to old attribute

    NewAttribute - pointer to new attribute

    AttributeChangeString - if non-NULL, receives the string representation
        of the difference between OldAttribute and NewAttribute

    RequiredLength - pointer to length of AttributeChangeString

Return Value:

    None

Notes:

--*/
{
    WCHAR  ChangeString[256];
    LPWSTR TmpString;
    ULONG ChangeStringLength=0;

    //
    // do the processing only if there is a change in value
    //
    if ((OldAttribute->AttributeValue && NewAttribute->AttributeValue &&
         (0 != memcmp(OldAttribute->AttributeValue,
                      NewAttribute->AttributeValue,
                      OldAttribute->AttributeValueLength))) ||
        (OldAttribute->AttributeValue  && !NewAttribute->AttributeValue) ||
        (!OldAttribute->AttributeValue &&  NewAttribute->AttributeValue)) {

        //
        // Parameter Name
        //
        lstrcpy(ChangeString, OldAttribute->AttributeName->Buffer);
        ChangeStringLength = OldAttribute->AttributeName->Length/sizeof(WCHAR);
        TmpString = ChangeString + ChangeStringLength;

        lstrcpy(TmpString, L": ");
        ChangeStringLength += 2;
        TmpString = ChangeString + ChangeStringLength;

        //
        // Old value
        //
        LsapAdtGetAttributeValueString( NewAttribute, TmpString );
        ChangeStringLength += lstrlen(TmpString);
        TmpString = ChangeString + ChangeStringLength;
        
        //
        // New value
        //
        lstrcpy(TmpString, L" (");
        ChangeStringLength += 2;
        TmpString = ChangeString + ChangeStringLength;

        LsapAdtGetAttributeValueString( OldAttribute, TmpString );
        ChangeStringLength += lstrlen(TmpString);
        TmpString = ChangeString + ChangeStringLength;
        
        lstrcpy(TmpString, L");  ");
        ChangeStringLength += 4;

        if (AttributeChangeString && (ChangeStringLength <= *RequiredLength)) {
            lstrcpy(AttributeChangeString, ChangeString);
        } 
    }

    *RequiredLength = ChangeStringLength;
}

NTSTATUS
LsapAdtGetDbAttributesChangeString(
    IN LSAP_DB_ATTRIBUTE* OldAttributes,
    IN LSAP_DB_ATTRIBUTE* NewAttributes,
    IN ULONG AttributeCount,
    OUT LPWSTR* AttributeChangeString
    )
/*++

Routine Description:

    Given old attributes and new attributes, return a string representation
    of the difference between old and new attributes.

    If there are no changes, "--" is returned,
    otherwise each change is written to the string as:
    <ParameterName>: <new value> (<old value>)

    This function is used for writing information about
    changes to certain policies to the audit log.
    
Arguments:

    OldAttributes - pointer to array of old attributes

    NewAttributes - pointer to array of new attributes

    AttributeCount - Number of attributes. 

    AttributeChangeString - pointer to string that receives the diff.

Return Value:

    NTSTATUS - Standard Nt Result Code

Notes:

    Memory allocated for AttributeChangeString must be freed by the
    caller using LsapFreeLsaHeap.

--*/
{
    NTSTATUS Status=STATUS_SUCCESS;
    LSAP_DB_ATTRIBUTE* OldAttribute;
    LSAP_DB_ATTRIBUTE* NewAttribute;
    ULONG TmpStringLength;
    ULONG TotalRequiredLength;
    LPWSTR TmpString;
    UINT AttributeNumber;
    USHORT n=1;
        
    OldAttribute = OldAttributes;
    NewAttribute = NewAttributes;

    TotalRequiredLength = 0;

    //
    // first find out the size of the buffer required
    //
    for (AttributeNumber = 0; AttributeNumber < AttributeCount; AttributeNumber++) {

        LsapAdtGetDbAttributeChangeString( OldAttribute, NewAttribute,
                                           NULL, &TmpStringLength );
        OldAttribute++;
        NewAttribute++;
        TotalRequiredLength += TmpStringLength;
    }

    if (!TotalRequiredLength) {
        n += 2;
    }

    *AttributeChangeString = TmpString =
        LsapAllocateLsaHeap((TotalRequiredLength+n)*sizeof(WCHAR));
    
    if ( TmpString ) {

        if (TotalRequiredLength) {
            
            //
            // Now get the actual string
            //
            OldAttribute = OldAttributes;
            NewAttribute = NewAttributes;

            for (AttributeNumber = 0;
                 AttributeNumber < AttributeCount;
                 AttributeNumber++) {

                TmpStringLength = TotalRequiredLength;
                LsapAdtGetDbAttributeChangeString( OldAttribute, NewAttribute,
                                                   TmpString, &TmpStringLength );
                TmpString += TmpStringLength;
                OldAttribute++;
                NewAttribute++;
            }
        } else {
            lstrcpy(TmpString, L"--");
        }
    } else {
        Status = STATUS_NO_MEMORY;
    }

    return Status;
}


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;
    SE_ADT_PARAMETER_ARRAY AuditParameters;

    if ( !LsapAdtEventsInformation.AuditingMode ) {
        return;
    }

    if (!(LsapAdtEventsInformation.EventAuditingOptions[AuditCategorySystem] &
          POLICY_AUDIT_EVENT_SUCCESS)) {
        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, LsapLocalSystemSid );
    AuditParameters.ParameterCount++;

    LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
    AuditParameters.ParameterCount++;

    LsapSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, NumberOfEventsDiscarded );
    AuditParameters.ParameterCount++;

    ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );

    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 ) {
        //
        // ISSUE-2000/09/26-kumarp : call LsapAuditFailed
        //
        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
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;
    SE_ADT_PARAMETER_ARRAY AuditParameters;

    if ( !LsapAdtEventsInformation.AuditingMode ) {
        return;
    }

    if (!(LsapAdtEventsInformation.EventAuditingOptions[AuditCategorySystem] & POLICY_AUDIT_EVENT_SUCCESS)) {
        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, LsapLocalSystemSid );
    AuditParameters.ParameterCount++;

    LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
    AuditParameters.ParameterCount++;

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

    ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );

    return;
}


VOID
LsapAdtAuditLogonProcessRegistration(
    IN PLSAP_AU_REGISTER_CONNECT_INFO_EX 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;
    SE_ADT_PARAMETER_ARRAY AuditParameters;

    if ( !LsapAdtEventsInformation.AuditingMode ) {
        return;
    }

    if (!(LsapAdtEventsInformation.EventAuditingOptions[AuditCategorySystem] & POLICY_AUDIT_EVENT_SUCCESS)) {
        return;
    }

    //
    // Turn the name text in the ConnectInfo structure into
    // something we can work with.
    //

    LogonProcessNameBuffer = (PSZ)LsapAllocateLsaHeap( ConnectInfo->LogonProcessNameLength+1 );

    if ( LogonProcessNameBuffer == NULL ) {

        return;
    }

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

        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, LsapLocalSystemSid );
    AuditParameters.ParameterCount++;

    LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
    AuditParameters.ParameterCount++;

    LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &Unicode );
    AuditParameters.ParameterCount++;

    ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );

    LsapFreeLsaHeap( LogonProcessNameBuffer );
    RtlFreeUnicodeString( &Unicode );

    return;
}



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


VOID
LsapAdtAuditLogon(
    IN USHORT EventCategory,
    IN ULONG  EventID,
    IN USHORT EventType,
    IN PUNICODE_STRING AccountName,
    IN PUNICODE_STRING AuthenticatingAuthority,
    IN PUNICODE_STRING Source,
    IN PUNICODE_STRING PackageName,
    IN SECURITY_LOGON_TYPE LogonType,
    IN PSID UserSid,
    IN LUID AuthenticationId,
    IN PUNICODE_STRING WorkstationName,
    IN NTSTATUS LogonStatus,
    IN NTSTATUS SubStatus,
    IN LPGUID LogonGuid                 OPTIONAL
    )

/*++

Routine Description:

    Generates an audit of a logon event as appropriate.

Arguments:



Return Value:

    None.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    UNICODE_STRING SpareString;
    UNICODE_STRING AuthenticationIdString = { 0 };
    BOOLEAN FreeWhenDone = FALSE;
    SE_ADT_PARAMETER_ARRAY AuditParameters;
    BOOLEAN AuditingSuccess;
    BOOLEAN AuditingFailure;
    PSID pSid;
    PLSAP_LOGON_SESSION pLogonSession = NULL;
    
        
    RtlInitUnicodeString( &SpareString, L"Security");

    AuditingFailure = (EventType == EVENTLOG_AUDIT_FAILURE) && LsapAuditFailedLogons;
    AuditingSuccess = (EventType == EVENTLOG_AUDIT_SUCCESS) && LsapAuditSuccessfulLogons;

    //
    // return quickly if auditing is not enabled
    //
    if ( !(AuditingFailure || AuditingSuccess) )
    {
        return;
    }
    
    //
    // Build an audit parameters structure.
    //

    RtlZeroMemory ( (PVOID) &AuditParameters, sizeof( AuditParameters ) );

    AuditParameters.CategoryId = EventCategory;
    AuditParameters.AuditId = EventID;
    AuditParameters.Type = EventType;
    AuditParameters.ParameterCount = 0;

    //
    // If this is a successful logon audit event and the caller did not
    // supply a logon GUID, extract it from the logon session.
    //
    if ( AuditingSuccess && !LogonGuid &&
         ( EventType == EVENTLOG_AUDIT_SUCCESS ) )
    {
        pLogonSession = LsapLocateLogonSession( &AuthenticationId );

        ASSERT( pLogonSession && L"LsapAdtAuditLogon: logon session not found" );
        
        if ( pLogonSession )
        {
            LogonGuid = &pLogonSession->LogonGuid;
        }
    }

#if DBG
    if ( AuditingSuccess )
    {
        DsysAssert( EventID != SE_AUDITID_DOMAIN_TRUST_INCONSISTENT );
    }
#endif
    //
    //    User Sid
    //

    pSid = AuditingSuccess ? UserSid : LsapLocalSystemSid;

    LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, pSid );

    AuditParameters.ParameterCount++;

    //
    //    Subsystem name (if available)
    //

    LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &SpareString );
    AuditParameters.ParameterCount++;

        //
        //    Account name
        //

    if ( ARGUMENT_PRESENT( AccountName ) ) {

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

    AuditParameters.ParameterCount++;

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

    if ( ARGUMENT_PRESENT( AuthenticatingAuthority ) ) {

        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 {

            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++;

        //
        // Workstation Name
        //

    if ( ARGUMENT_PRESENT( WorkstationName )) {

        LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, WorkstationName );
    }

    AuditParameters.ParameterCount++;

    if ( EventID == SE_AUDITID_UNSUCCESSFUL_LOGON ) {

        //
        // we need to supply the logon status for this event,
        // so that some information can be gleened from the log.
        //

        LsapSetParmTypeHexUlong( AuditParameters, AuditParameters.ParameterCount, LogonStatus );
        AuditParameters.ParameterCount++;
        LsapSetParmTypeHexUlong( AuditParameters, AuditParameters.ParameterCount, SubStatus );
        AuditParameters.ParameterCount++;
    }

    //
    // Logon GUID
    //

    if ( ARGUMENT_PRESENT( LogonGuid )) {

        LsapSetParmTypeGuid( AuditParameters, AuditParameters.ParameterCount, LogonGuid );
        AuditParameters.ParameterCount++;
    }


    ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );

 Finish:
    if (!NT_SUCCESS(Status))
    {
        LsapAuditFailed( Status );
    }

    if ( FreeWhenDone ) {
        LsapFreeLsaHeap( AuthenticationIdString.Buffer );
    }

    if ( pLogonSession != NULL )
    {
        LsapReleaseLogonSession( pLogonSession );
    }
}




VOID
LsapAuditLogon(
    IN NTSTATUS LogonStatus,
    IN NTSTATUS LogonSubStatus,
    IN PUNICODE_STRING AccountName,
    IN PUNICODE_STRING AuthenticatingAuthority,
    IN PUNICODE_STRING WorkstationName,
    IN PSID UserSid,                            OPTIONAL
    IN SECURITY_LOGON_TYPE LogonType,
    IN PTOKEN_SOURCE TokenSource,
    IN PLUID LogonId
    )
/*++

Routine Description/Arguments/Return value

    See header comment for LsapAuditLogonHelper

--*/
{
    LsapAuditLogonHelper(
        LogonStatus,
        LogonSubStatus,
        AccountName,
        AuthenticatingAuthority,
        WorkstationName,
        UserSid,
        LogonType,
        TokenSource,
        LogonId,
        NULL                    // no logon guid
        );
}




VOID
LsapAuditLogonHelper(
    IN NTSTATUS LogonStatus,
    IN NTSTATUS LogonSubStatus,
    IN PUNICODE_STRING AccountName,
    IN PUNICODE_STRING AuthenticatingAuthority,
    IN PUNICODE_STRING WorkstationName,
    IN PSID UserSid,                            OPTIONAL
    IN SECURITY_LOGON_TYPE LogonType,
    IN PTOKEN_SOURCE TokenSource,
    IN PLUID LogonId,
    IN LPGUID LogonGuid                         OPTIONAL
    )
/*++

Routine Description:

    Helper routine for security packages to generate a logon audit

Arguments:
    LogonStatus - Status code for the logon.

    LogonSubStatus - more detailed Status code for the logon.

    AccountName - Name of principal attempting logon.

    AuthenticatingAuthority - Authority validating the logon.

    Workstation - Machine from which the logon was attempted. For a network
        logon, this should be the client machine.

    UserSid - Sid for the logged on account.

    LogonType - Type of logon, such as Network, Interactive, Service, etc.

    TokenSource - Source for the token.

    LogonId - If the logon was successful, the logon ID for the logon session.

    LogonGuid - globally unique ID for a logon. currently this is supported
                only by the kerberos package.

Return Value:

    None.

--*/
{
    ANSI_STRING AnsiSourceContext;
    CHAR AnsiBuffer[TOKEN_SOURCE_LENGTH + 2];
    UNICODE_STRING UnicodeSourceContext;
    WCHAR UnicodeBuffer[TOKEN_SOURCE_LENGTH + 2];
    NTSTATUS Status;
    USHORT EventType;
    USHORT EventCategory;
    ULONG  EventID;
    PLSAP_SECURITY_PACKAGE SecurityPackage;
    ULONG_PTR PackageId;
    
    PackageId = GetCurrentPackageId();
    DsysAssertMsg( PackageId != SPMGR_ID, "LsapAuditLogon" );
    
    SecurityPackage = SpmpLocatePackage( PackageId );
    DsysAssertMsg( SecurityPackage != NULL, "LsapAuditLogon" );


    //
    // Audit the logon attempt.  The event type and logged information
    // will depend to some extent on the whether we failed and why.
    //

    //
    // Turn the SourceContext into something we can
    // work with.
    //

    AnsiSourceContext.Buffer = AnsiBuffer;
    AnsiSourceContext.Length = TOKEN_SOURCE_LENGTH * sizeof( CHAR );
    AnsiSourceContext.MaximumLength = (TOKEN_SOURCE_LENGTH + 2) * sizeof( CHAR );

    UnicodeSourceContext.Buffer = UnicodeBuffer;
    UnicodeSourceContext.MaximumLength = (TOKEN_SOURCE_LENGTH + 2) * sizeof( WCHAR );

    RtlCopyMemory(
        AnsiBuffer,
        TokenSource->SourceName,
        TOKEN_SOURCE_LENGTH * sizeof( CHAR )
        );

    Status = RtlAnsiStringToUnicodeString(
                 &UnicodeSourceContext,
                 &AnsiSourceContext,
                 FALSE
                 );

    if ( NT_SUCCESS( Status )) {
        
        UnicodeSourceContext.Length =
            (USHORT) LsapSafeWcslen( UnicodeSourceContext.Buffer,
                                     UnicodeSourceContext.MaximumLength );
        
    } else {

        UnicodeSourceContext.Buffer = NULL;

        //
        // we cannot fail the audit because of this but catch the
        // internal clients who supply bad source contexts
        //
        DsysAssertMsg( FALSE, "LsapAuditLogon: could not convert AnsiSourceContext to unicode" );
    }

    //
    // Assume the logon failed, reset if necessary.
    //

    EventCategory = SE_CATEGID_LOGON;
    EventType     = EVENTLOG_AUDIT_FAILURE;


    switch ( LogonStatus )
    {
        case STATUS_SUCCESS:
            {
                //
                // Use a separate event for network logons
                //

                if (( LogonType == Network ) ||
                    ( LogonType == NetworkCleartext ))
                {
                    EventID = SE_AUDITID_NETWORK_LOGON;
                }
                else
                {
                    EventID = SE_AUDITID_SUCCESSFUL_LOGON;
                }

                EventType = EVENTLOG_AUDIT_SUCCESS;
                break;
            }

        case STATUS_BAD_VALIDATION_CLASS:
            EventID = SE_AUDITID_UNSUCCESSFUL_LOGON;
            break;

        case STATUS_ACCOUNT_EXPIRED:
            EventID = SE_AUDITID_ACCOUNT_EXPIRED;
            break;

        case STATUS_NETLOGON_NOT_STARTED:
            EventID = SE_AUDITID_NETLOGON_NOT_STARTED;
            break;

        case STATUS_ACCOUNT_LOCKED_OUT:
            EventID = SE_AUDITID_ACCOUNT_LOCKED;
            break;

        case STATUS_LOGON_TYPE_NOT_GRANTED:
            EventID = SE_AUDITID_LOGON_TYPE_RESTR;
            break;

        case STATUS_PASSWORD_MUST_CHANGE:
            EventID = SE_AUDITID_PASSWORD_EXPIRED;
            break;


        case STATUS_ACCOUNT_RESTRICTION:
            {

                switch ( LogonSubStatus )
                {
                    case STATUS_PASSWORD_EXPIRED:
                        EventID = SE_AUDITID_PASSWORD_EXPIRED;
                        break;

                    case STATUS_ACCOUNT_DISABLED:
                        EventID = SE_AUDITID_ACCOUNT_DISABLED;
                        break;

                    case STATUS_INVALID_LOGON_HOURS:
                        EventID = SE_AUDITID_ACCOUNT_TIME_RESTR;
                        break;

                    case STATUS_INVALID_WORKSTATION:
                        EventID = SE_AUDITID_WORKSTATION_RESTR;
                        break;

                    default:
                        EventID = SE_AUDITID_UNKNOWN_USER_OR_PWD;
                        break;
                }
                break;
            }

        case STATUS_LOGON_FAILURE:
            {
                if ( ( LogonSubStatus == STATUS_WRONG_PASSWORD ) ||
                     ( LogonSubStatus == STATUS_NO_SUCH_USER   ) )
                {
                    EventID = SE_AUDITID_UNKNOWN_USER_OR_PWD;

                }
                else if ( LogonSubStatus == STATUS_DOMAIN_TRUST_INCONSISTENT )
                {
                    EventID = SE_AUDITID_DOMAIN_TRUST_INCONSISTENT;
                }
                else
                {
                    EventID = SE_AUDITID_UNSUCCESSFUL_LOGON;
                }
                break;
            }

        default:
            EventID = SE_AUDITID_UNSUCCESSFUL_LOGON;
            break;
    }

    LsapAdtAuditLogon( EventCategory,
                       EventID,
                       EventType,
                       AccountName,
                       AuthenticatingAuthority,
                       &UnicodeSourceContext,
                       &SecurityPackage->Name,
                       LogonType,
                       UserSid,
                       *LogonId,
                       WorkstationName,
                       LogonStatus,
                       LogonSubStatus,
                       LogonGuid
                       );

}


VOID
LsapAdtAuditLogoff(
    PLSAP_LOGON_SESSION Session
    )
/*++

Routine Description:

    Generates a logoff audit.  The caller is responsible for determining
    if logoff auditing is enabled.

Arguments:

    Session - Points to the logon session being removed.

Return Value:

    None.

--*/
{
    SE_ADT_PARAMETER_ARRAY AuditParameters;
    NTSTATUS Status;
    UNICODE_STRING usLogonId;
    BOOLEAN fFreeLogonId=FALSE;

    RtlZeroMemory ( &usLogonId, sizeof(UNICODE_STRING) );

    //
    // normally we would simply store the logon-id to be audited
    // as SeAdtParmTypeLogonId. But in this case, the logon session
    // will have gone away by the time we try to convert it
    // to a string representation in LsapAdtDemarshallAuditInfo.
    // using LsapGetLogonSessionAccountInfo.
    //
    // To avoid this, we pre-convert the logon-id here
    //

    Status = LsapAdtBuildLuidString( &Session->LogonId,
                                     &usLogonId, &fFreeLogonId );

    if ( !NT_SUCCESS(Status) )
    {
        goto Cleanup;
    }


    LsapAdtInitParametersArray(
        &AuditParameters,
        SE_CATEGID_LOGON,
        SE_AUDITID_LOGOFF,
        EVENTLOG_AUDIT_SUCCESS,
        6,                       // there are 6 params to init

        //
        //    User Sid
        //
        SeAdtParmTypeSid,        Session->UserSid,

        //
        //    Subsystem name (if available)
        //
        SeAdtParmTypeString,     &LsapSubsystemName,

        //
        //    User
        //
        SeAdtParmTypeString,     &Session->AccountName,

        //
        //    Domain
        //
        SeAdtParmTypeString,     &Session->AuthorityName,

        //
        //    LogonId
        //
        SeAdtParmTypeString,     &usLogonId,

        //
        //    Logon Type
        //
        SeAdtParmTypeUlong,      Session->LogonType );


    ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );

Cleanup:
    if (fFreeLogonId)
    {
        LsapFreeLsaHeap(usLogonId.Buffer);
    }
}