/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    Accessck.c

Abstract:

    This Module implements the access check procedures.  Both NtAccessCheck
    and SeAccessCheck check to is if a user (denoted by an input token) can
    be granted the desired access rights to object protected by a security
    descriptor and an optional object owner.  Both procedures use a common
    local procedure to do the test.

--*/

#include "pch.h"

#pragma hdrstop

#include <sertlp.h>


//
//  Define the local macros and procedure for this module
//

#if DBG

extern BOOLEAN SepDumpSD;
extern BOOLEAN SepDumpToken;
BOOLEAN SepShowAccessFail;

#endif // DBG



VOID
SepUpdateParentTypeList (
    IN PIOBJECT_TYPE_LIST ObjectTypeList,
    IN ULONG ObjectTypeListLength,
    IN ULONG StartIndex
    );

typedef enum {
    UpdateRemaining,
    UpdateCurrentGranted,
    UpdateCurrentDenied
} ACCESS_MASK_FIELD_TO_UPDATE;

VOID
SepAddAccessTypeList (
    IN PIOBJECT_TYPE_LIST ObjectTypeList,
    IN ULONG ObjectTypeListLength,
    IN ULONG StartIndex,
    IN ACCESS_MASK AccessMask,
    IN ACCESS_MASK_FIELD_TO_UPDATE FieldToUpdate
    );

NTSTATUS
SeAccessCheckByType (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN PSID PrincipalSelfSid,
    IN HANDLE ClientToken,
    IN ACCESS_MASK DesiredAccess,
    IN POBJECT_TYPE_LIST ObjectTypeList OPTIONAL,
    IN ULONG ObjectTypeListLength,
    IN PGENERIC_MAPPING GenericMapping,
    OUT PPRIVILEGE_SET PrivilegeSet,
    IN OUT PULONG PrivilegeSetLength,
    OUT PACCESS_MASK GrantedAccess,
    OUT PNTSTATUS AccessStatus,
    IN BOOLEAN ReturnResultList
    );

VOID
SepMaximumAccessCheck(
    IN PTOKEN EToken,
    IN PTOKEN PrimaryToken,
    IN PACL Dacl,
    IN PSID PrincipalSelfSid,
    IN ULONG LocalTypeListLength,
    IN PIOBJECT_TYPE_LIST LocalTypeList,
    IN ULONG ObjectTypeListLength,
    IN BOOLEAN Restricted
    );

VOID
SepNormalAccessCheck(
    IN ACCESS_MASK Remaining,
    IN PTOKEN EToken,
    IN PTOKEN PrimaryToken,
    IN PACL Dacl,
    IN PSID PrincipalSelfSid,
    IN ULONG LocalTypeListLength,
    IN PIOBJECT_TYPE_LIST LocalTypeList,
    IN ULONG ObjectTypeListLength,
    IN BOOLEAN Restricted
    );

BOOLEAN
SepSidInTokenEx (
    IN PACCESS_TOKEN AToken,
    IN PSID PrincipalSelfSid,
    IN PSID Sid,
    IN BOOLEAN DenyAce,
    IN BOOLEAN Restricted
    );


#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,SeCaptureObjectTypeList)
#pragma alloc_text(PAGE,SeFreeCapturedObjectTypeList)
#pragma alloc_text(PAGE,SepUpdateParentTypeList)
#pragma alloc_text(PAGE,SepObjectInTypeList)
#pragma alloc_text(PAGE,SepAddAccessTypeList)
#pragma alloc_text(PAGE,SepSidInToken)
#pragma alloc_text(PAGE,SepSidInTokenEx)
#pragma alloc_text(PAGE,SepAccessCheck)
#pragma alloc_text(PAGE,NtAccessCheck)
#pragma alloc_text(PAGE,NtAccessCheckByType)
#pragma alloc_text(PAGE,NtAccessCheckByTypeResultList)
#pragma alloc_text(PAGE,SeAccessCheckByType)
#pragma alloc_text(PAGE,SeFreePrivileges)
#pragma alloc_text(PAGE,SeAccessCheck)
#pragma alloc_text(PAGE,SePrivilegePolicyCheck)
#pragma alloc_text(PAGE,SepTokenIsOwner)
#pragma alloc_text(PAGE,SeFastTraverseCheck)
#pragma alloc_text(PAGE,SepMaximumAccessCheck)
#pragma alloc_text(PAGE,SepNormalAccessCheck)
#pragma alloc_text(PAGE,SeMaximumAuditMask)
#endif


NTSTATUS
SeCaptureObjectTypeList (
    __in_ecount_opt(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
    __in ULONG ObjectTypeListLength,
    __in KPROCESSOR_MODE RequestorMode,
    __out PIOBJECT_TYPE_LIST *CapturedObjectTypeList
)
/*++

Routine Description:

    This routine probes and captures a copy of any object type list
    that might have been provided via the ObjectTypeList argument.

    The object type list is converted to the internal form that explicitly
    specifies the hierarchical relationship between the entries.

    The object types list is validated to ensure a valid hierarchical
    relationship is represented.

Arguments:

    ObjectTypeList - The object type list from which the type list
        information is to be retrieved.

    ObjectTypeListLength - Number of elements in ObjectTypeList

    RequestorMode - Indicates the processor mode by which the access
        is being requested.

    CapturedObjectTypeList - Receives the captured type list which
        must be freed using SeFreeCapturedObjectTypeList().

Return Value:

    STATUS_SUCCESS indicates no exceptions were encountered.

    Any access violations encountered will be returned.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    ULONG i;
    PIOBJECT_TYPE_LIST LocalTypeList = NULL;

    ULONG Levels[ACCESS_MAX_LEVEL+1];

    PAGED_CODE();

    //
    //  Set default return
    //

    *CapturedObjectTypeList = NULL;

    if (RequestorMode != UserMode) {
        return STATUS_NOT_IMPLEMENTED;
    }

    try {

        if ( ObjectTypeListLength == 0 ) {

            // Drop through

        } else if ( !ARGUMENT_PRESENT(ObjectTypeList) ) {

            Status = STATUS_INVALID_PARAMETER;

        } else {

            if ( !IsValidElementCount( ObjectTypeListLength, IOBJECT_TYPE_LIST ) )
            {
                Status = STATUS_INVALID_PARAMETER ;

                //
                // No more to do, get out of the try statement:
                //

                leave ;
            }

            ProbeForRead( ObjectTypeList,
                          sizeof(OBJECT_TYPE_LIST) * ObjectTypeListLength,
                          sizeof(ULONG)
                          );

            //
            // Allocate a buffer to copy into.
            //

            LocalTypeList = ExAllocatePoolWithTag( PagedPool, sizeof(IOBJECT_TYPE_LIST) * ObjectTypeListLength, 'tOeS' );

            if ( LocalTypeList == NULL ) {
                Status = STATUS_INSUFFICIENT_RESOURCES;

            //
            // Copy the callers structure to the local structure.
            //

            } else {
                GUID * CapturedObjectType;
                for ( i=0; i<ObjectTypeListLength; i++ ) {
                    USHORT CurrentLevel;

                    //
                    // Limit ourselves
                    //
                    CurrentLevel = ObjectTypeList[i].Level;
                    if ( CurrentLevel > ACCESS_MAX_LEVEL ) {
                        Status = STATUS_INVALID_PARAMETER;
                        break;
                    }

                    //
                    // Copy data the caller passed in
                    //
                    LocalTypeList[i].Level = CurrentLevel;
                    LocalTypeList[i].Flags = 0;
                    CapturedObjectType = ObjectTypeList[i].ObjectType;
                    ProbeForReadSmallStructure(
                        CapturedObjectType,
                        sizeof(GUID),
                        sizeof(ULONG)
                        );
                    LocalTypeList[i].ObjectType = *CapturedObjectType;
                    LocalTypeList[i].Remaining = 0;
                    LocalTypeList[i].CurrentGranted = 0;
                    LocalTypeList[i].CurrentDenied = 0;

                    //
                    // Ensure that the level number is consistent with the
                    //  level number of the previous entry.
                    //

                    if ( i == 0 ) {
                        if ( CurrentLevel != 0 ) {
                            Status = STATUS_INVALID_PARAMETER;
                            break;
                        }

                    } else {

                        //
                        // The previous entry is either:
                        //  my immediate parent,
                        //  my sibling, or
                        //  the child (or grandchild, etc.) of my sibling.
                        //
                        if ( CurrentLevel > LocalTypeList[i-1].Level + 1 ) {
                            Status = STATUS_INVALID_PARAMETER;
                            break;
                        }

                        //
                        // Don't support two roots.
                        //
                        if ( CurrentLevel == 0 ) {
                            Status = STATUS_INVALID_PARAMETER;
                            break;
                        }

                    }

                    //
                    // If the above rules are maintained,
                    //  then my parent object is the last object seen that
                    //  has a level one less than mine.
                    //

                    if ( CurrentLevel == 0 ) {
                        LocalTypeList[i].ParentIndex = -1;
                    } else {
                        LocalTypeList[i].ParentIndex = Levels[CurrentLevel-1];
                    }

                    //
                    // Save this object as the last object seen at this level.
                    //

                    Levels[CurrentLevel] = i;

                }

            }

        } // end_if

    } except(EXCEPTION_EXECUTE_HANDLER) {

        Status = GetExceptionCode();

    } // end_try


    if ( NT_SUCCESS( Status ) ) {

        *CapturedObjectTypeList = LocalTypeList;

    } else {

        //
        // If we captured any proxy data, we need to free it now.
        //

        if ( LocalTypeList != NULL ) {
            ExFreePool( LocalTypeList );
        }
    }

    return Status;
}


VOID
SeFreeCapturedObjectTypeList(
    __in PVOID ObjectTypeList
    )

/*++

Routine Description:

    This routine frees the data associated with a captured ObjectTypeList
    structure.

Arguments:

    ObjectTypeList - Points to a captured object type list structure.

Return Value:

    None.

--*/

{
    PAGED_CODE();

    if ( ObjectTypeList != NULL ) {
        ExFreePool( ObjectTypeList );
    }

    return;
}




BOOLEAN
SepObjectInTypeList (
    IN GUID *ObjectType,
    IN PIOBJECT_TYPE_LIST ObjectTypeList,
    IN ULONG ObjectTypeListLength,
    OUT PULONG ReturnedIndex
)
/*++

Routine Description:

    This routine searches an ObjectTypeList to determine if the specified
    object type is in the list.

Arguments:

    ObjectType - Object Type to search for.

    ObjectTypeList - The object type list to search.

    ObjectTypeListLength - Number of elements in ObjectTypeList

    ReturnedIndex - Index to the element ObjectType was found in


Return Value:

    TRUE: ObjectType was found in list.
    FALSE: ObjectType was not found in list.

--*/

{
    ULONG Index;
    GUID *LocalObjectType;

    PAGED_CODE();

    ASSERT( sizeof(GUID) == sizeof(ULONG) * 4 );
    for ( Index=0; Index<ObjectTypeListLength; Index++ ) {

        LocalObjectType = &ObjectTypeList[Index].ObjectType;

        if  ( RtlpIsEqualGuid( ObjectType, LocalObjectType ) ) {
            *ReturnedIndex = Index;
            return TRUE;
        }
    }

    return FALSE;
}


VOID
SepUpdateParentTypeList (
    IN PIOBJECT_TYPE_LIST ObjectTypeList,
    IN ULONG ObjectTypeListLength,
    IN ULONG StartIndex
)
/*++

Routine Description:

    Update the Access fields of the parent object of the specified object.


        The "remaining" field of a parent object is the logical OR of
        the remaining field of all of its children.

        The CurrentGranted field of the parent is the collection of bits
        granted to every one of its children..

        The CurrentDenied fields of the parent is the logical OR of
        the bits denied to any of its children.

    This routine takes an index to one of the children and updates the
    remaining field of the parent (and grandparents recursively).

Arguments:

    ObjectTypeList - The object type list to update.

    ObjectTypeListLength - Number of elements in ObjectTypeList

    StartIndex - Index to the "child" element whose parents are to be updated.

Return Value:

    None.


--*/

{
    ULONG Index;
    ULONG ParentIndex;
    ULONG Level;
    ACCESS_MASK NewRemaining = 0;
    ACCESS_MASK NewCurrentGranted = 0xFFFFFFFF;
    ACCESS_MASK NewCurrentDenied = 0;

    PAGED_CODE();

    //
    // If the target node is at the root,
    //  we're all done.
    //

    if ( ObjectTypeList[StartIndex].ParentIndex == -1 ) {
        return;
    }

    //
    // Get the index to the parent that needs updating and the level of
    // the siblings.
    //

    ParentIndex = ObjectTypeList[StartIndex].ParentIndex;
    Level = ObjectTypeList[StartIndex].Level;

    //
    // Loop through all the children.
    //

    for ( Index=ParentIndex+1; Index<ObjectTypeListLength; Index++ ) {

        //
        // By definition, the children of an object are all those entries
        // immediately following the target.  The list of children (or
        // grandchildren) stops as soon as we reach an entry the has the
        // same level as the target (a sibling) or lower than the target
        // (an uncle).
        //

        if ( ObjectTypeList[Index].Level <= ObjectTypeList[ParentIndex].Level ) {
            break;
        }

        //
        // Only handle direct children of the parent.
        //

        if ( ObjectTypeList[Index].Level != Level ) {
            continue;
        }

        //
        // Compute the new bits for the parent.
        //

        NewRemaining |= ObjectTypeList[Index].Remaining;
        NewCurrentGranted &= ObjectTypeList[Index].CurrentGranted;
        NewCurrentDenied |= ObjectTypeList[Index].CurrentDenied;

    }

    //
    // If we've not changed the access to the parent,
    //  we're done.
    //

    if ( NewRemaining == ObjectTypeList[ParentIndex].Remaining &&
         NewCurrentGranted == ObjectTypeList[ParentIndex].CurrentGranted &&
        NewCurrentDenied == ObjectTypeList[ParentIndex].CurrentDenied ) {
        return;
    }


    //
    // Change the parent.
    //

    ObjectTypeList[ParentIndex].Remaining = NewRemaining;
    ObjectTypeList[ParentIndex].CurrentGranted = NewCurrentGranted;
    ObjectTypeList[ParentIndex].CurrentDenied = NewCurrentDenied;

    //
    // Go update the grand parents.
    //

    SepUpdateParentTypeList( ObjectTypeList,
                             ObjectTypeListLength,
                             ParentIndex );
}


VOID
SepAddAccessTypeList (
    IN PIOBJECT_TYPE_LIST ObjectTypeList,
    IN ULONG ObjectTypeListLength,
    IN ULONG StartIndex,
    IN ACCESS_MASK AccessMask,
    IN ACCESS_MASK_FIELD_TO_UPDATE FieldToUpdate
)
/*++

Routine Description:

    This routine grants the specified AccessMask to all of the objects that
    are descendents of the object specified by StartIndex.

    The Access fields of the parent objects are also recomputed as needed.

    For example, if an ACE granting access to a Property Set is found,
        that access is granted to all the Properties in the Property Set.

Arguments:

    ObjectTypeList - The object type list to update.

    ObjectTypeListLength - Number of elements in ObjectTypeList

    StartIndex - Index to the target element to update.

    AccessMask - Mask of access to grant to the target element and
        all of its descendents

    FieldToUpdate - Indicate which fields to update in object type list

Return Value:

    None.

--*/

{
    ULONG Index;
    ACCESS_MASK OldRemaining;
    ACCESS_MASK OldCurrentGranted;
    ACCESS_MASK OldCurrentDenied;
    BOOLEAN AvoidParent = FALSE;

    PAGED_CODE();

    //
    // Update the requested field.
    //
    // Always handle the target entry.
    //
    // If we've not actually changed the bits,
    //  early out.
    //

    switch (FieldToUpdate ) {
    case UpdateRemaining:

        OldRemaining = ObjectTypeList[StartIndex].Remaining;
        ObjectTypeList[StartIndex].Remaining = OldRemaining & ~AccessMask;

        if ( OldRemaining == ObjectTypeList[StartIndex].Remaining ) {
            return;
        }
        break;

    case UpdateCurrentGranted:

        OldCurrentGranted = ObjectTypeList[StartIndex].CurrentGranted;
        ObjectTypeList[StartIndex].CurrentGranted |=
            AccessMask & ~ObjectTypeList[StartIndex].CurrentDenied;

        if ( OldCurrentGranted == ObjectTypeList[StartIndex].CurrentGranted ) {
            //
            // We can't simply return here.
            // We have to visit our children.  Consider the case where there
            // was a previous deny ACE on a child.  That deny would have
            // propagated up the tree to this entry.  However, this allow ACE
            // needs to be added all of the children that haven't been
            // explictly denied.
            //
            AvoidParent = TRUE;
        }
        break;

    case UpdateCurrentDenied:

        OldCurrentDenied = ObjectTypeList[StartIndex].CurrentDenied;
        ObjectTypeList[StartIndex].CurrentDenied |=
            AccessMask & ~ObjectTypeList[StartIndex].CurrentGranted;

        if ( OldCurrentDenied == ObjectTypeList[StartIndex].CurrentDenied ) {
            
            //
            // We must visit the children - consider the case where we
            // are applying an ACCESS_DENIED Ace to the entire tree.  
            // Suppose a node of the tree was already DENIED the same bits
            // via an ACCESS_DENIED_OBJECT_ACE.  Those deny bits would have
            // propagated up the tree.  If we are denying the bits to this
            // node then we must visit the children.
            //

            AvoidParent = TRUE;
        }
        break;

    default:
        return;
    }


    //
    // Go update parent of the target.
    //

    if ( !AvoidParent ) {
        SepUpdateParentTypeList( ObjectTypeList,
                                 ObjectTypeListLength,
                                 StartIndex );
    }

    //
    // Loop handling all children of the target.
    //

    for ( Index=StartIndex+1; Index<ObjectTypeListLength; Index++ ) {

        //
        // By definition, the children of an object are all those entries
        // immediately following the target.  The list of children (or
        // grandchildren) stops as soon as we reach an entry the has the
        // same level as the target (a sibling) or lower than the target
        // (an uncle).
        //

        if ( ObjectTypeList[Index].Level <= ObjectTypeList[StartIndex].Level ) {
            break;
        }

        //
        // Grant access to the children
        //

        switch (FieldToUpdate) {
        case UpdateRemaining:

            ObjectTypeList[Index].Remaining &= ~AccessMask;
            break;

        case UpdateCurrentGranted:

            ObjectTypeList[Index].CurrentGranted |=
                AccessMask & ~ObjectTypeList[Index].CurrentDenied;
            break;

        case UpdateCurrentDenied:

            ObjectTypeList[Index].CurrentDenied |=
                AccessMask & ~ObjectTypeList[Index].CurrentGranted;
            break;

        default:
            return;
        }
    }
}

BOOLEAN
SepSidInToken (
    IN PACCESS_TOKEN AToken,
    IN PSID PrincipalSelfSid,
    IN PSID Sid,
    IN BOOLEAN DenyAce
    )

/*++

Routine Description:

    Checks to see if a given SID is in the given token.

    N.B. The code to compute the length of a SID and test for equality
         is duplicated from the security runtime since this is such a
         frequently used routine.

Arguments:

    Token - Pointer to the token to be examined

    PrincipalSelfSid - If the object being access checked is an object which
        represents a principal (e.g., a user object), this parameter should
        be the SID of the object.  Any ACE containing the constant
        PRINCIPAL_SELF_SID is replaced by this SID.

        The parameter should be NULL if the object does not represent a principal.


    Sid - Pointer to the SID of interest

Return Value:

    A value of TRUE indicates that the SID is in the token, FALSE
    otherwise.

--*/

{

    ULONG i;
    PISID MatchSid;
    ULONG SidLength;
    PTOKEN Token;
    PSID_AND_ATTRIBUTES TokenSid;
    ULONG UserAndGroupCount;
    USHORT TargetShort;

    C_ASSERT (FIELD_OFFSET (SID, Revision) + sizeof (((SID *)Sid)->Revision) == FIELD_OFFSET (SID, SubAuthorityCount));
    C_ASSERT (sizeof (((SID *)Sid)->Revision) + sizeof (((SID *)Sid)->SubAuthorityCount) == sizeof (USHORT));


    PAGED_CODE();

#if DBG

    SepDumpTokenInfo(AToken);

#endif

    //
    // If Sid is the constant PrincipalSelfSid,
    //  replace it with the passed in PrincipalSelfSid.
    //

    if ( PrincipalSelfSid != NULL &&
         RtlEqualSid( SePrincipalSelfSid, Sid ) ) {
        Sid = PrincipalSelfSid;
    }

    //
    // Get the length of the source SID since this only needs to be computed
    // once.
    //
    SidLength = 8 + (4 * ((PISID)Sid)->SubAuthorityCount);

    //
    // To speed up processing we compare the sub authority count and the revision at the same time.
    //
    TargetShort = *(USHORT *)&((PISID)Sid)->Revision;

    //
    // Get address of user/group array and number of user/groups.
    //

    Token = (PTOKEN)AToken;
    TokenSid = Token->UserAndGroups;
    UserAndGroupCount = Token->UserAndGroupCount;

    //
    // Scan through the user/groups and attempt to find a match with the
    // specified SID.
    //

    for (i = 0 ; i < UserAndGroupCount ; i += 1) {
        MatchSid = (PISID)TokenSid->Sid;

        //
        // If revision and sub authority count matches, then compare the SIDs
        // for equality.
        //

        if (*(USHORT *) &MatchSid->Revision == TargetShort) {
            if (RtlEqualMemory(Sid, MatchSid, SidLength)) {

                //
                // If this is the first one in the list, then it is the User,
                // and return success immediately.
                //
                // If this is not the first one, then it represents a group,
                // and we must make sure the group is currently enabled before
                // we can say that the group is "in" the token.
                //

                if ((i == 0) || (TokenSid->Attributes & SE_GROUP_ENABLED) ||
                    (DenyAce && (TokenSid->Attributes & SE_GROUP_USE_FOR_DENY_ONLY))) {
                    return TRUE;

                } else {
                    return FALSE;
                }
            }
        }

        TokenSid += 1;
    }

    return FALSE;
}

BOOLEAN
SepSidInTokenEx (
    IN PACCESS_TOKEN AToken,
    IN PSID PrincipalSelfSid,
    IN PSID Sid,
    IN BOOLEAN DenyAce,
    IN BOOLEAN Restricted
    )

/*++

Routine Description:

    Checks to see if a given restricted SID is in the given token.

    N.B. The code to compute the length of a SID and test for equality
         is duplicated from the security runtime since this is such a
         frequently used routine.

Arguments:

    Token - Pointer to the token to be examined

    PrincipalSelfSid - If the object being access checked is an object which
        represents a principal (e.g., a user object), this parameter should
        be the SID of the object.  Any ACE containing the constant
        PRINCIPAL_SELF_SID is replaced by this SID.

        The parameter should be NULL if the object does not represent a principal.


    Sid - Pointer to the SID of interest

    DenyAce - The ACE being evaluated is a DENY or ACCESS DENIED ace

    Restricted - The access check being performed uses the restricted sids.

Return Value:

    A value of TRUE indicates that the SID is in the token, FALSE
    otherwise.

--*/

{

    ULONG i;
    PISID MatchSid;
    ULONG SidLength;
    PTOKEN Token;
    PSID_AND_ATTRIBUTES TokenSid;
    ULONG UserAndGroupCount;
    USHORT TargetShort;

    C_ASSERT (FIELD_OFFSET (SID, Revision) + sizeof (((SID *)Sid)->Revision) == FIELD_OFFSET (SID, SubAuthorityCount));
    C_ASSERT (sizeof (((SID *)Sid)->Revision) + sizeof (((SID *)Sid)->SubAuthorityCount) == sizeof (USHORT));

    PAGED_CODE();

#if DBG

    SepDumpTokenInfo(AToken);

#endif

    //
    // If Sid is the constant PrincipalSelfSid,
    //  replace it with the passed in PrincipalSelfSid.
    //

    if ( PrincipalSelfSid != NULL &&
         RtlEqualSid( SePrincipalSelfSid, Sid ) ) {
        Sid = PrincipalSelfSid;
    }

    //
    // Get the length of the source SID since this only needs to be computed
    // once.
    //

    //
    // Get the length of the source SID since this only needs to be computed
    // once.
    //
    SidLength = 8 + (4 * ((PISID)Sid)->SubAuthorityCount);

    //
    // To speed up processing we compare the sub authority count and the revision at the same time.
    //
    TargetShort = *(USHORT *)&((PISID)Sid)->Revision;

    //
    // Get address of user/group array and number of user/groups.
    //

    Token = (PTOKEN)AToken;
    if (Restricted) {
        TokenSid = Token->RestrictedSids;
        UserAndGroupCount = Token->RestrictedSidCount;
    } else {
        TokenSid = Token->UserAndGroups;
        UserAndGroupCount = Token->UserAndGroupCount;
    }

    //
    // Scan through the user/groups and attempt to find a match with the
    // specified SID.
    //

    for (i = 0; i < UserAndGroupCount ; i += 1) {
        MatchSid = (PISID)TokenSid->Sid;

        //
        // If the SID revision and length matches, then compare the SIDs
        // for equality.
        //

        if (*(USHORT *) &MatchSid->Revision == TargetShort) {
            if (RtlEqualMemory(Sid, MatchSid, SidLength)) {

                //
                // If this is the first one in the list and not deny-only it
                // is not a restricted token then it is the User, and return
                // success immediately.
                //
                // If this is not the first one, then it represents a group,
                // and we must make sure the group is currently enabled before
                // we can say that the group is "in" the token.
                //

                if ((!Restricted && (i == 0) && ((TokenSid->Attributes & SE_GROUP_USE_FOR_DENY_ONLY) == 0)) ||
                    (TokenSid->Attributes & SE_GROUP_ENABLED) ||
                    (DenyAce && (TokenSid->Attributes & SE_GROUP_USE_FOR_DENY_ONLY))) {
                    return TRUE;

                } else {
                    return FALSE;
                }

            }
        }

        TokenSid += 1;
    }

    return FALSE;
}

VOID
SepMaximumAccessCheck(
    IN PTOKEN EToken,
    IN PTOKEN PrimaryToken,
    IN PACL Dacl,
    IN PSID PrincipalSelfSid,
    IN ULONG LocalTypeListLength,
    IN PIOBJECT_TYPE_LIST LocalTypeList,
    IN ULONG ObjectTypeListLength,
    IN BOOLEAN Restricted
    )
/*++

Routine Description:

    Does an access check for maximum allowed or with a result list. If the
    Restricted flag is set, it is done for a restricted token. The sids
    checked are the restricted sids, not the users and groups. The current
    granted access is stored in the Remaining access and then another access
    check is run.

Arguments:

    EToken - Effective token of caller.

    PrimaryToken - Process token of calling process

    Dacl - ACL to check

    PrincipalSelfSid - Sid to use in replacing the well-known self sid

    LocalTypeListLength - Length of list of types.

    LocalTypeList - List of types.

    ObjectTypeList - Length of caller-supplied list of object types.

Return Value:

    none

--*/

{
    ULONG i,j;
    PVOID Ace;
    ULONG AceCount;
    ULONG Index;
    ULONG ResultListIndex;

    //
    // The remaining bits are the granted bits for each object type on a
    // restricted check
    //

    if ( Restricted ) {
        for ( j=0; j<LocalTypeListLength; j++ ) {
            LocalTypeList[j].Remaining = LocalTypeList[j].CurrentGranted;
            LocalTypeList[j].CurrentGranted = 0;
        }
    }


    AceCount = Dacl->AceCount;

    //
    // granted == NUL
    // denied == NUL
    //
    //  for each ACE
    //
    //      if grant
    //          for each SID
    //              if SID match, then add all that is not denied to grant mask
    //
    //      if deny
    //          for each SID
    //              if SID match, then add all that is not granted to deny mask
    //

    for ( i = 0, Ace = FirstAce( Dacl ) ;
          i < AceCount  ;
          i++, Ace = NextAce( Ace )
        ) {

        if ( !(((PACE_HEADER)Ace)->AceFlags & INHERIT_ONLY_ACE)) {

            if ( (((PACE_HEADER)Ace)->AceType == ACCESS_ALLOWED_ACE_TYPE) ) {

                if (SepSidInTokenEx( EToken, PrincipalSelfSid, &((PACCESS_ALLOWED_ACE)Ace)->SidStart, FALSE, Restricted )) {

                    //
                    // Only grant access types from this mask that have
                    // not already been denied
                    //

                    // Optimize 'normal' case
                    if ( LocalTypeListLength == 1 ) {
                        LocalTypeList->CurrentGranted |=
                           (((PACCESS_ALLOWED_ACE)Ace)->Mask & ~LocalTypeList->CurrentDenied);
                    } else {
                       //
                       // The zeroeth object type represents the object itself.
                       //
                       SepAddAccessTypeList(
                            LocalTypeList,          // List to modify
                            LocalTypeListLength,    // Length of list
                            0,                      // Element to update
                            ((PACCESS_ALLOWED_ACE)Ace)->Mask, // Access Granted
                            UpdateCurrentGranted );
                    }
                 }


             //
             // Handle an object specific Access Allowed ACE
             //
             } else if ( (((PACE_HEADER)Ace)->AceType == ACCESS_ALLOWED_OBJECT_ACE_TYPE) ) {
                 GUID *ObjectTypeInAce;

                 //
                 // If no object type is in the ACE,
                 //  treat this as an ACCESS_ALLOWED_ACE.
                 //

                 ObjectTypeInAce = RtlObjectAceObjectType(Ace);

                 if ( ObjectTypeInAce == NULL ) {

                     if ( SepSidInTokenEx( EToken, PrincipalSelfSid, RtlObjectAceSid(Ace), FALSE, Restricted ) ) {

                         // Optimize 'normal' case
                         if ( LocalTypeListLength == 1 ) {
                             LocalTypeList->CurrentGranted |=
                                (((PACCESS_ALLOWED_OBJECT_ACE)Ace)->Mask & ~LocalTypeList->CurrentDenied);
                         } else {
                             SepAddAccessTypeList(
                                 LocalTypeList,          // List to modify
                                 LocalTypeListLength,    // Length of list
                                 0,                      // Element to update
                                 ((PACCESS_ALLOWED_OBJECT_ACE)Ace)->Mask, // Access Granted
                                 UpdateCurrentGranted );
                         }
                     }

                 //
                 // If no object type list was passed,
                 //  don't grant access to anyone.
                 //

                 } else if ( ObjectTypeListLength == 0 ) {

                     // Drop through


                //
                // If an object type is in the ACE,
                //   Find it in the LocalTypeList before using the ACE.
                //
                } else {

                     if ( SepSidInTokenEx( EToken, PrincipalSelfSid, RtlObjectAceSid(Ace), FALSE, Restricted ) ) {

                         if ( SepObjectInTypeList( ObjectTypeInAce,
                                                   LocalTypeList,
                                                   LocalTypeListLength,
                                                   &Index ) ) {
                             SepAddAccessTypeList(
                                  LocalTypeList,          // List to modify
                                  LocalTypeListLength,   // Length of list
                                  Index,                  // Element already updated
                                  ((PACCESS_ALLOWED_OBJECT_ACE)Ace)->Mask, // Access Granted
                                  UpdateCurrentGranted );
                         }
                     }
                }

             } else if ( (((PACE_HEADER)Ace)->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE) ) {

                 //
                 //  If we're impersonating, EToken is set to the Client, and if we're not,
                 //  EToken is set to the Primary.  According to the DSA architecture, if
                 //  we're asked to evaluate a compound ACE and we're not impersonating,
                 //  pretend we are impersonating ourselves.  So we can just use the EToken
                 //  for the client token, since it's already set to the right thing.
                 //


                 if ( SepSidInTokenEx(EToken, PrincipalSelfSid, RtlCompoundAceClientSid( Ace ), FALSE, Restricted) &&
                      SepSidInTokenEx(PrimaryToken, NULL, RtlCompoundAceServerSid( Ace ), FALSE, FALSE)
                    ) {

                     //
                     // Only grant access types from this mask that have
                     // not already been denied
                     //

                     // Optimize 'normal' case
                     if ( LocalTypeListLength == 1 ) {
                         LocalTypeList->CurrentGranted |=
                            (((PCOMPOUND_ACCESS_ALLOWED_ACE)Ace)->Mask & ~LocalTypeList->CurrentDenied);
                     } else {
                        //
                        // The zeroeth object type represents the object itself.
                        //
                        SepAddAccessTypeList(
                             LocalTypeList,          // List to modify
                             LocalTypeListLength,    // Length of list
                             0,                      // Element to update
                             ((PCOMPOUND_ACCESS_ALLOWED_ACE)Ace)->Mask, // Access Granted
                             UpdateCurrentGranted );
                     }

                 }


             } else if ( (((PACE_HEADER)Ace)->AceType == ACCESS_DENIED_ACE_TYPE) ) {

                 if ( SepSidInTokenEx( EToken, PrincipalSelfSid, &((PACCESS_DENIED_ACE)Ace)->SidStart, TRUE, Restricted )) {

                      //
                      // Only deny access types from this mask that have
                      // not already been granted
                      //

                     // Optimize 'normal' case
                     if ( LocalTypeListLength == 1 ) {
                         LocalTypeList->CurrentDenied |=
                             (((PACCESS_DENIED_ACE)Ace)->Mask & ~LocalTypeList->CurrentGranted);
                     } else {
                         //
                         // The zeroeth object type represents the object itself.
                         //
                         SepAddAccessTypeList(
                             LocalTypeList,          // List to modify
                             LocalTypeListLength,    // Length of list
                             0,                      // Element to update
                             ((PACCESS_DENIED_ACE)Ace)->Mask, // Access denied
                             UpdateCurrentDenied );
                    }
                 }


             //
             // Handle an object specific Access Denied ACE
             //
             } else if ( (((PACE_HEADER)Ace)->AceType == ACCESS_DENIED_OBJECT_ACE_TYPE) ) {

                 if ( SepSidInTokenEx( EToken, PrincipalSelfSid, RtlObjectAceSid(Ace), TRUE, Restricted ) ) {
                     GUID *ObjectTypeInAce;

                     //
                     // If there is no object type in the ACE,
                     //  or if the caller didn't specify an object type list,
                     //  apply this deny ACE to the entire object.
                     //

                     ObjectTypeInAce = RtlObjectAceObjectType(Ace);

                     if ( ObjectTypeInAce == NULL ) {

                         if ( LocalTypeListLength == 1 ) {
                             LocalTypeList->CurrentDenied |=
                                 (((PACCESS_DENIED_OBJECT_ACE)Ace)->Mask & ~LocalTypeList->CurrentGranted);
                         } else {

                             //
                             // The zeroeth object type represents the object itself.
                             //

                             SepAddAccessTypeList(
                                 LocalTypeList,          // List to modify
                                 LocalTypeListLength,    // Length of list
                                 0,
                                 ((PACCESS_DENIED_OBJECT_ACE)Ace)->Mask, // Access denied
                                 UpdateCurrentDenied );
                         }
                     //
                     // If no object type list was passed,
                     //  don't grant access to anyone.
                     //

                     } else if ( ObjectTypeListLength == 0 ) {

                         LocalTypeList->CurrentDenied |=
                             (((PACCESS_DENIED_OBJECT_ACE)Ace)->Mask & ~LocalTypeList->CurrentGranted);


                     //
                     // If an object type is in the ACE,
                     //   Find it in the LocalTypeList before using the ACE.
                     //

                     } else if ( SepObjectInTypeList( ObjectTypeInAce,
                                                          LocalTypeList,
                                                          LocalTypeListLength,
                                                          &Index ) ) {

                            SepAddAccessTypeList(
                                LocalTypeList,          // List to modify
                                LocalTypeListLength,    // Length of list
                                Index,                  // Element to update
                                ((PACCESS_DENIED_OBJECT_ACE)Ace)->Mask, // Access denied
                                UpdateCurrentDenied );

                    }
                }
            }
        }
    }

    //
    // If this check was performed on the restricted SIDs, then CurrentGranted
    // contains the MAXIMUM_ALLOWED access for the set of SIDs.  A check on the
    // restricted SIDs must be constrained by the check on the normal SIDs, so 
    // AND the results together now.
    //

    if ( Restricted ) {
        for ( j=0; j<LocalTypeListLength; j++ ) {
            LocalTypeList[j].CurrentGranted &= LocalTypeList[j].Remaining;
        }
    }
}

VOID
SepNormalAccessCheck(
    IN ACCESS_MASK Remaining,
    IN PTOKEN EToken,
    IN PTOKEN PrimaryToken,
    IN PACL Dacl,
    IN PSID PrincipalSelfSid,
    IN ULONG LocalTypeListLength,
    IN PIOBJECT_TYPE_LIST LocalTypeList,
    IN ULONG ObjectTypeListLength,
    IN BOOLEAN Restricted
    )
/*++

Routine Description:

    Does an access check when the caller isn't asking for MAXIMUM_ALLOWED or
    a type result list. If the Restricted flag is set, the sids checked are
    the restricted sids, not the users and groups. The Remaining field is
    reset to the original remaining value and then another access check is run.

Arguments:

    Remaining - Remaining access desired after special checks

    EToken - Effective token of caller.

    PrimaryToken - Process token of calling process

    Dacl - ACL to check

    PrincipalSelfSid - Sid to use in replacing the well-known self sid

    LocalTypeListLength - Length of list of types.

    LocalTypeList - List of types.

    ObjectTypeList - Length of caller-supplied list of object types.

    Restricted - Use the restricted sids for the access check.

Return Value:

    none

--*/
{
    ULONG i,j;
    PVOID Ace;
    ULONG AceCount;
    ULONG Index;

    AceCount = Dacl->AceCount;

    //
    // The remaining bits are "remaining" at all levels
    //

    for ( j=0; j<LocalTypeListLength; j++ ) {
        LocalTypeList[j].Remaining = Remaining;
    }

    //
    // Process the DACL handling individual access bits.
    //

    for ( i = 0, Ace = FirstAce( Dacl ) ;
          ( i < AceCount ) && ( LocalTypeList->Remaining != 0 )  ;
          i++, Ace = NextAce( Ace ) ) {

        if ( !(((PACE_HEADER)Ace)->AceFlags & INHERIT_ONLY_ACE)) {

            //
            // Handle an Access Allowed ACE
            //

            if ( (((PACE_HEADER)Ace)->AceType == ACCESS_ALLOWED_ACE_TYPE) ) {

               if ( SepSidInTokenEx( EToken, PrincipalSelfSid, &((PACCESS_ALLOWED_ACE   )Ace)->SidStart, FALSE, Restricted ) ) {

                   // Optimize 'normal' case
                   if ( LocalTypeListLength == 1 ) {
                       LocalTypeList->Remaining &= ~((PACCESS_ALLOWED_ACE)Ace)->Mask;
                   } else {
                       //
                       // The zeroeth object type represents the object itself.
                       //
                       SepAddAccessTypeList(
                            LocalTypeList,          // List to modify
                            LocalTypeListLength,    // Length of list
                            0,                      // Element to update
                            ((PACCESS_ALLOWED_ACE)Ace)->Mask, // Access Granted
                            UpdateRemaining );
                   }

               }


            //
            // Handle an object specific Access Allowed ACE
            //
            } else if ( (((PACE_HEADER)Ace)->AceType == ACCESS_ALLOWED_OBJECT_ACE_TYPE) ) {
                GUID *ObjectTypeInAce;

                //
                // If no object type is in the ACE,
                //  treat this as an ACCESS_ALLOWED_ACE.
                //

                ObjectTypeInAce = RtlObjectAceObjectType(Ace);

                if ( ObjectTypeInAce == NULL ) {

                    if ( SepSidInTokenEx( EToken, PrincipalSelfSid, RtlObjectAceSid(Ace), FALSE, Restricted ) ) {

                       // Optimize 'normal' case
                       if ( LocalTypeListLength == 1 ) {
                           LocalTypeList->Remaining &= ~((PACCESS_ALLOWED_ACE)Ace)->Mask;
                       } else {
                           SepAddAccessTypeList(
                                LocalTypeList,          // List to modify
                                LocalTypeListLength,    // Length of list
                                0,                      // Element to update
                                ((PACCESS_ALLOWED_OBJECT_ACE)Ace)->Mask, // Access Granted
                                UpdateRemaining );
                       }
                    }

                //
                // If no object type list was passed,
                //  don't grant access to anyone.
                //

                } else if ( ObjectTypeListLength == 0 ) {

                    // Drop through


               //
               // If an object type is in the ACE,
               //   Find it in the LocalTypeList before using the ACE.
               //
               } else {

                    if ( SepSidInTokenEx( EToken, PrincipalSelfSid, RtlObjectAceSid(Ace), FALSE, Restricted ) ) {

                        if ( SepObjectInTypeList( ObjectTypeInAce,
                                                  LocalTypeList,
                                                  LocalTypeListLength,
                                                  &Index ) ) {
                            SepAddAccessTypeList(
                                 LocalTypeList,          // List to modify
                                 LocalTypeListLength,   // Length of list
                                 Index,                  // Element already updated
                                 ((PACCESS_ALLOWED_OBJECT_ACE)Ace)->Mask, // Access Granted
                                 UpdateRemaining );
                        }
                    }
               }


            //
            // Handle a compound Access Allowed ACE
            //

            } else if ( (((PACE_HEADER)Ace)->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE) ) {

                //
                // See comment in MAXIMUM_ALLOWED case as to why we can use EToken here
                // for the client.
                //

                if ( SepSidInTokenEx(EToken, PrincipalSelfSid, RtlCompoundAceClientSid( Ace ), FALSE, Restricted) &&
                     SepSidInTokenEx(PrimaryToken, NULL, RtlCompoundAceServerSid( Ace ), FALSE, Restricted) ) {

                    // Optimize 'normal' case
                    if ( LocalTypeListLength == 1 ) {
                        LocalTypeList->Remaining &= ~((PCOMPOUND_ACCESS_ALLOWED_ACE)Ace)->Mask;
                    } else {
                        SepAddAccessTypeList(
                             LocalTypeList,          // List to modify
                             LocalTypeListLength,    // Length of list
                             0,                      // Element to update
                             ((PCOMPOUND_ACCESS_ALLOWED_ACE)Ace)->Mask, // Access Granted
                             UpdateRemaining );
                    }
                }



            //
            // Handle an Access Denied ACE
            //

            } else if ( (((PACE_HEADER)Ace)->AceType == ACCESS_DENIED_ACE_TYPE) ) {

                if ( SepSidInTokenEx( EToken, PrincipalSelfSid, &((PACCESS_DENIED_ACE)Ace)->SidStart, TRUE, Restricted ) ) {

                    //
                    // The zeroeth element represents the object itself.
                    //  Just check that element.
                    //
                    if (LocalTypeList->Remaining & ((PACCESS_DENIED_ACE)Ace)->Mask) {

                        break;
                    }
                }


            //
            // Handle an object specific Access Denied ACE
            //
            } else if ( (((PACE_HEADER)Ace)->AceType == ACCESS_DENIED_OBJECT_ACE_TYPE) ) {

                if ( SepSidInTokenEx( EToken, PrincipalSelfSid, RtlObjectAceSid(Ace), TRUE, Restricted ) ) {
                    GUID *ObjectTypeInAce;

                    //
                    // If there is no object type in the ACE,
                    //  or if the caller didn't specify an object type list,
                    //  apply this deny ACE to the entire object.
                    //

                    ObjectTypeInAce = RtlObjectAceObjectType(Ace);
                    if ( ObjectTypeInAce == NULL ||
                         ObjectTypeListLength == 0 ) {

                        //
                        // The zeroeth element represents the object itself.
                        //  Just check that element.
                        //
                        if (LocalTypeList->Remaining & ((PACCESS_DENIED_OBJECT_ACE)Ace)->Mask) {
                            break;
                        }

                    //
                    // Otherwise apply the deny ACE to the object specified
                    //  in the ACE.
                    //

                    } else if ( SepObjectInTypeList( ObjectTypeInAce,
                                                  LocalTypeList,
                                                  LocalTypeListLength,
                                                  &Index ) ) {

                        if (LocalTypeList[Index].Remaining & ((PACCESS_DENIED_OBJECT_ACE)Ace)->Mask) {
                            break;
                        }

                    }
               }
            }

        }
    }
}


VOID
SeMaximumAuditMask(
    IN PACL Sacl,
    IN ACCESS_MASK GrantedAccess,
    IN PACCESS_TOKEN Token,
    OUT PACCESS_MASK pAuditMask
    )
/*++

Routine Description:

    This routine takes the passed security descriptor and applies the
    "MAXIMUM_ALLOWED" algorithm to the SACL contained in the security
    descriptor if one exists.  This mask represents all the success audits
    that can occur from the passed subject context accessing the passed
    security descriptor.

    The code walks the SACL and for each SYSTEM_AUDIT_ACE found that
    matches the passed subject context, keeps a running total of the
    access bits in the ACE.  The resulting mask is then masked by the
    passed GrantedAccess mask, since we're only interested in the
    bits that the object is actually being opened for.

Arguments:

    Sacl - The Sacl to be examined.

    GrantedAccess - The access that has been granted to the object.

    Token - Supplies to effective token for the access attempt.

    pAuditMask - Returns the mask of bits to be audited (if any).

Return Value:

    None


--*/
{
    USHORT AceCount        = 0;
    PACE_HEADER Ace        = NULL;
    ACCESS_MASK AccessMask = (ACCESS_MASK)0;
    UCHAR AceFlags         = 0;

    USHORT i;

    //
    // Initialize OUT parameters
    //

    *pAuditMask = (ACCESS_MASK)0;

    //
    // Determine if there is an SACL in the security descriptor.
    // If not, nothing to do.
    //

    if (0 == (AceCount = Sacl->AceCount)) {
        return;
    }

    //
    // Iterate through the ACEs on the Sacl until either we reach
    // the end or discover that we have to take all possible actions,
    // in which case it doesn't pay to look any further
    //

    for ( i = 0, Ace = FirstAce( Sacl ) ;
          (i < AceCount) ;
          i++, Ace = NextAce( Ace ) ) {

        if ( !(((PACE_HEADER)Ace)->AceFlags & INHERIT_ONLY_ACE)) {

            if ( (((PACE_HEADER)Ace)->AceType == SYSTEM_AUDIT_ACE_TYPE) ) {

                AccessMask = ((PSYSTEM_AUDIT_ACE)Ace)->Mask;
                AceFlags   = ((PACE_HEADER)Ace)->AceFlags;

                if ((AccessMask & GrantedAccess) && (AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG)) {

                    if ( SepSidInToken( (PACCESS_TOKEN)Token, NULL, &((PSYSTEM_AUDIT_ACE)Ace)->SidStart, FALSE ) ) {

                        *pAuditMask |= (AccessMask & GrantedAccess);
                    }
                }
            }
        }
    }
}



BOOLEAN
SepAccessCheck (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN PSID PrincipalSelfSid,
    IN PTOKEN PrimaryToken,
    IN PTOKEN ClientToken OPTIONAL,
    IN ACCESS_MASK DesiredAccess,
    IN PIOBJECT_TYPE_LIST ObjectTypeList OPTIONAL,
    IN ULONG ObjectTypeListLength,
    IN PGENERIC_MAPPING GenericMapping,
    IN ACCESS_MASK PreviouslyGrantedAccess,
    IN KPROCESSOR_MODE PreviousMode,
    OUT PACCESS_MASK GrantedAccess,
    OUT PPRIVILEGE_SET *Privileges OPTIONAL,
    OUT PNTSTATUS AccessStatus,
    IN BOOLEAN ReturnResultList,
    OUT PBOOLEAN ReturnSomeAccessGranted,
    OUT PBOOLEAN ReturnSomeAccessDenied
    )

/*++

Routine Description:

    Worker routine for SeAccessCheck and NtAccessCheck.  We actually do the
    access checking here.

    Whether or not we actually evaluate the DACL is based on the following
    interaction between the SE_DACL_PRESENT bit in the security descriptor
    and the value of the DACL pointer itself.


                          SE_DACL_PRESENT

                        SET          CLEAR

                   +-------------+-------------+
                   |             |             |
             NULL  |    GRANT    |    GRANT    |
                   |     ALL     |     ALL     |
     DACL          |             |             |
     Pointer       +-------------+-------------+
                   |             |             |
            !NULL  |  EVALUATE   |    GRANT    |
                   |    ACL      |     ALL     |
                   |             |             |
                   +-------------+-------------+

Arguments:

    SecurityDescriptor - Pointer to the security descriptor from the object
        being accessed.

    PrincipalSelfSid - If the object being access checked is an object which
        represents a principal (e.g., a user object), this parameter should
        be the SID of the object.  Any ACE containing the constant
        PRINCIPAL_SELF_SID is replaced by this SID.

        The parameter should be NULL if the object does not represent a principal.

    PrimaryToken / ClientToken - Pointers to user's token objects.

    PreviousMode - Previous processor privilege mode

    TokenLocked - Boolean describing whether or not there is a read lock
        on the token.

    DesiredAccess - Access mask describing the user's desired access to the
        object.  This mask is assumed not to contain generic access types.

    ObjectTypeList - Supplies a list of GUIDs representing the object (and
        sub-objects) being accessed.  If no list is present, AccessCheckByType
        behaves identically to AccessCheck.

    ObjectTypeListLength - Specifies the number of elements in the ObjectTypeList.

    GenericMapping - Supplies a pointer to the generic mapping associated
        with this object type.

    PreviouslyGrantedAccess - Access mask indicating any access' that have
        already been granted by higher level routines

    GrantedAccess - Returns an access mask describing all granted access',
        or NULL.

    Privileges - Optionally supplies a pointer in which will be returned
        any privileges that were used for the access.  If this is null,
        it will be assumed that privilege checks have been done already.

    AccessStatus - Returns STATUS_SUCCESS or other error code to be
        propagated back to the caller

    ReturnResultList - If true, GrantedAccess and AccessStatus is actually
        an array of entries ObjectTypeListLength elements long.

    ReturnSomeAccessGranted - Returns a value of TRUE to indicate that some access'
        were granted, FALSE otherwise.

    ReturnSomeAccessDenied - Returns a value of FALSE if some of the requested
        access was not granted.  This will always be an inverse of SomeAccessGranted
        unless ReturnResultList is TRUE.  In that case,

Return Value:

    A value of TRUE indicates that some access' were granted, FALSE
    otherwise.

--*/
{
    NTSTATUS Status;
    ACCESS_MASK Remaining;
    BOOLEAN RetVal = TRUE;

    PACL Dacl;

    PVOID Ace;
    ULONG AceCount;

    ULONG i;
    ULONG j;
    ULONG Index;
    ULONG PrivilegeCount = 0;
    BOOLEAN Success = FALSE;
    BOOLEAN SystemSecurity = FALSE;
    BOOLEAN WriteOwner = FALSE;
    PTOKEN EToken;

    IOBJECT_TYPE_LIST FixedTypeList;
    PIOBJECT_TYPE_LIST LocalTypeList;
    ULONG LocalTypeListLength;
    ULONG ResultListIndex;

    PAGED_CODE();

#if DBG

    SepDumpSecurityDescriptor(
        SecurityDescriptor,
        "Input to SeAccessCheck\n"
        );

    if (ARGUMENT_PRESENT( ClientToken )) {
        SepDumpTokenInfo( ClientToken );
    }

    SepDumpTokenInfo( PrimaryToken );

#endif


    EToken = ARGUMENT_PRESENT( ClientToken ) ? ClientToken : PrimaryToken;

    //
    // Assert that there are no generic accesses in the DesiredAccess
    //

    SeAssertMappedCanonicalAccess( DesiredAccess );

    Remaining = DesiredAccess;


    //
    // Check for ACCESS_SYSTEM_SECURITY here,
    // fail if he's asking for it and doesn't have
    // the privilege.
    //

    if ( Remaining & ACCESS_SYSTEM_SECURITY ) {

        //
        // Bugcheck if we weren't given a pointer to return privileges
        // into.  Our caller was supposed to have taken care of this
        // in that case.
        //

        ASSERT( ARGUMENT_PRESENT( Privileges ));

        Success = SepSinglePrivilegeCheck (
                    SeSecurityPrivilege,
                    EToken,
                    PreviousMode
                    );

        if (!Success) {
            PreviouslyGrantedAccess = 0;
            Status = STATUS_PRIVILEGE_NOT_HELD;
            goto ReturnOneStatus;
        }

        //
        // Success, remove ACCESS_SYSTEM_SECURITY from remaining, add it
        // to PreviouslyGrantedAccess
        //

        Remaining &= ~ACCESS_SYSTEM_SECURITY;
        PreviouslyGrantedAccess |= ACCESS_SYSTEM_SECURITY;

        PrivilegeCount++;
        SystemSecurity = TRUE;

        if ( Remaining == 0 ) {
            Status = STATUS_SUCCESS;
            goto ReturnOneStatus;
        }

    }


    //
    // Get pointer to client SID's
    //

    Dacl = RtlpDaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor );

    //
    //  If the SE_DACL_PRESENT bit is not set, the object has no
    //  security, so all accesses are granted.  If he's asking for
    //  MAXIMUM_ALLOWED, return the GENERIC_ALL field from the generic
    //  mapping.
    //
    //  Also grant all access if the Dacl is NULL.
    //

    if ( !RtlpAreControlBitsSet(
             (PISECURITY_DESCRIPTOR)SecurityDescriptor,
             SE_DACL_PRESENT) || (Dacl == NULL)) {


        //
        // Restricted tokens treat a NULL dacl the same as a DACL with no
        // ACEs.
        //
#ifdef SECURE_NULL_DACLS
        if (SeTokenIsRestricted( EToken )) {
            //
            // We know that Remaining != 0 here, because we
            // know it was non-zero coming into this routine,
            // and we've checked it against 0 every time we've
            // cleared a bit.
            //

            ASSERT( Remaining != 0 );

            //
            // There are ungranted accesses.  Since there is
            // nothing in the DACL, they will not be granted.
            // If, however, the only ungranted access at this
            // point is MAXIMUM_ALLOWED, and something has been
            // granted in the PreviouslyGranted mask, return
            // what has been granted.
            //

            if ( (Remaining == MAXIMUM_ALLOWED) && (PreviouslyGrantedAccess != (ACCESS_MASK)0) ) {
                Status = STATUS_SUCCESS;
                goto ReturnOneStatus;

            } else {
                PreviouslyGrantedAccess = 0;
                Status = STATUS_ACCESS_DENIED;
                goto ReturnOneStatus;
            }
        }
#endif //SECURE_NULL_DACLS
        if (DesiredAccess & MAXIMUM_ALLOWED) {

            //
            // Give him:
            //   GenericAll
            //   Anything else he asked for
            //

            PreviouslyGrantedAccess =
                GenericMapping->GenericAll |
                (DesiredAccess | PreviouslyGrantedAccess) & ~MAXIMUM_ALLOWED;

        } else {

            PreviouslyGrantedAccess |= DesiredAccess;
        }

        Status = STATUS_SUCCESS;
        goto ReturnOneStatus;
    }

    //
    // There is security on this object.  Check to see
    // if he's asking for WRITE_OWNER, and perform the
    // privilege check if so.
    //

    if ( (Remaining & WRITE_OWNER) && ARGUMENT_PRESENT( Privileges ) ) {

        Success = SepSinglePrivilegeCheck (
                    SeTakeOwnershipPrivilege,
                    EToken,
                    PreviousMode
                    );

        if (Success) {

            //
            // Success, remove WRITE_OWNER from remaining, add it
            // to PreviouslyGrantedAccess
            //

            Remaining &= ~WRITE_OWNER;
            PreviouslyGrantedAccess |= WRITE_OWNER;

            PrivilegeCount++;
            WriteOwner = TRUE;

            if ( Remaining == 0 ) {
                Status = STATUS_SUCCESS;
                goto ReturnOneStatus;
            }
        }
    }


    //
    // If the DACL is empty,
    // deny all access immediately.
    //

    if ((AceCount = Dacl->AceCount) == 0) {

        //
        // We know that Remaining != 0 here, because we
        // know it was non-zero coming into this routine,
        // and we've checked it against 0 every time we've
        // cleared a bit.
        //

        ASSERT( Remaining != 0 );

        //
        // There are ungranted accesses.  Since there is
        // nothing in the DACL, they will not be granted.
        // If, however, the only ungranted access at this
        // point is MAXIMUM_ALLOWED, and something has been
        // granted in the PreviouslyGranted mask, return
        // what has been granted.
        //

        if ( (Remaining == MAXIMUM_ALLOWED) && (PreviouslyGrantedAccess != (ACCESS_MASK)0) ) {
            Status = STATUS_SUCCESS;
            goto ReturnOneStatus;

        } else {
            PreviouslyGrantedAccess = 0;
            Status = STATUS_ACCESS_DENIED;
            goto ReturnOneStatus;
        }
    }

    //
    // Fake out a top level ObjectType list if none is passed by the caller.
    //

    if ( ObjectTypeListLength == 0 ) {
        LocalTypeList = &FixedTypeList;
        LocalTypeListLength = 1;
        RtlZeroMemory( &FixedTypeList, sizeof(FixedTypeList) );
        FixedTypeList.ParentIndex = -1;
    } else {
        LocalTypeList = ObjectTypeList;
        LocalTypeListLength = ObjectTypeListLength;
    }

    //
    // If the caller wants the MAXIMUM_ALLOWED or the caller wants the
    //  results on all objects and subobjects, use a slower algorithm
    //  that traverses all the ACEs.
    //

    if ( (DesiredAccess & MAXIMUM_ALLOWED) != 0 ||
         ReturnResultList ) {

        //
        // Do the normal maximum-allowed access check
        //

        SepMaximumAccessCheck(
            EToken,
            PrimaryToken,
            Dacl,
            PrincipalSelfSid,
            LocalTypeListLength,
            LocalTypeList,
            ObjectTypeListLength,
            FALSE
            );

        //
        // If this is a restricted token, do the additional access check
        //

        if (SeTokenIsRestricted( EToken ) ) {
            SepMaximumAccessCheck(
                EToken,
                PrimaryToken,
                Dacl,
                PrincipalSelfSid,
                LocalTypeListLength,
                LocalTypeList,
                ObjectTypeListLength,
                TRUE
                );
        }


        //
        // If the caller wants to know the individual results of each sub-object,
        //  sub-object,
        //  break it down for him.
        //

        if ( ReturnResultList ) {
            ACCESS_MASK GrantedAccessMask;
            ACCESS_MASK RequiredAccessMask;
            BOOLEAN SomeAccessGranted = FALSE;
            BOOLEAN SomeAccessDenied = FALSE;

            //
            // Compute mask of Granted access bits to tell the caller about.
            //  If he asked for MAXIMUM_ALLOWED,
            //      tell him everything,
            //  otherwise
            //      tell him what he asked about.
            //

            if (DesiredAccess & MAXIMUM_ALLOWED) {
                GrantedAccessMask = (ACCESS_MASK) ~MAXIMUM_ALLOWED;
                RequiredAccessMask = (DesiredAccess | PreviouslyGrantedAccess) & ~MAXIMUM_ALLOWED;
            } else {
                GrantedAccessMask = DesiredAccess | PreviouslyGrantedAccess;
                RequiredAccessMask = DesiredAccess | PreviouslyGrantedAccess;
            }




            //
            // Loop computing the access granted to each object and sub-object.
            //
            for ( ResultListIndex=0;
                  ResultListIndex<LocalTypeListLength;
                  ResultListIndex++ ) {

                //
                // Return the subset of the access granted that the caller
                //  expressed interest in.
                //

                GrantedAccess[ResultListIndex] =
                    (LocalTypeList[ResultListIndex].CurrentGranted |
                     PreviouslyGrantedAccess ) &
                    GrantedAccessMask;

                //
                // If absolutely no access was granted,
                //  indicate so.
                //
                if ( GrantedAccess[ResultListIndex] == 0 ) {
                    AccessStatus[ResultListIndex] = STATUS_ACCESS_DENIED;
                    SomeAccessDenied = TRUE;
                } else {

                    //
                    // If some requested access is still missing,
                    //  the bottom line is that access is denied.
                    //
                    // Note, that ByTypeResultList actually returns the
                    // partially granted access mask even though the caller
                    // really has no access to the object.
                    //

                    if  ( ((~GrantedAccess[ResultListIndex]) & RequiredAccessMask ) != 0 ) {
                        AccessStatus[ResultListIndex] = STATUS_ACCESS_DENIED;
                        SomeAccessDenied = TRUE;
                    } else {
                        AccessStatus[ResultListIndex] = STATUS_SUCCESS;
                        SomeAccessGranted = TRUE;
                    }
                }
            }

            if ( SomeAccessGranted && PrivilegeCount != 0 ) {

                SepAssemblePrivileges(
                    PrivilegeCount,
                    SystemSecurity,
                    WriteOwner,
                    Privileges
                    );

                if ( ( Privileges != NULL ) && ( *Privileges == NULL ) ) {

                    RetVal = FALSE;
                    SomeAccessGranted = FALSE;
                    SomeAccessDenied = TRUE;

                    for ( ResultListIndex=0;
                          ResultListIndex<LocalTypeListLength;
                          ResultListIndex++ ) {

                        AccessStatus[ResultListIndex] = STATUS_NO_MEMORY;
                        GrantedAccess[ResultListIndex] = 0;
                    }

                }
            }

            if ( ARGUMENT_PRESENT(ReturnSomeAccessGranted)) {
                *ReturnSomeAccessGranted = SomeAccessGranted;
            }
            if ( ARGUMENT_PRESENT(ReturnSomeAccessDenied)) {
                *ReturnSomeAccessDenied = SomeAccessDenied;
            }

            return RetVal;

        //
        // If the caller is only interested in the access to the object itself,
        //  just summarize.
        //

        } else {

            //
            // Turn off the MAXIMUM_ALLOWED bit and whatever we found that
            // he was granted.  If the user passed in extra bits in addition
            // to MAXIMUM_ALLOWED, make sure that he was granted those access
            // types.  If not, he didn't get what he wanted, so return failure.
            //

            Remaining &= ~(MAXIMUM_ALLOWED | LocalTypeList->CurrentGranted);

            if (Remaining != 0) {

                Status = STATUS_ACCESS_DENIED;
                PreviouslyGrantedAccess = 0;
                goto ReturnOneStatus;

            }



            PreviouslyGrantedAccess |= LocalTypeList->CurrentGranted;
            Status = STATUS_SUCCESS;
            goto ReturnOneStatus;

        }

    } // if MAXIMUM_ALLOWED...


    //
    // Do the normal access check first
    //

    SepNormalAccessCheck(
        Remaining,
        EToken,
        PrimaryToken,
        Dacl,
        PrincipalSelfSid,
        LocalTypeListLength,
        LocalTypeList,
        ObjectTypeListLength,
        FALSE
        );

    if (LocalTypeList->Remaining != 0) {
        Status = STATUS_ACCESS_DENIED;
        PreviouslyGrantedAccess = 0;
        goto ReturnOneStatus;
    }

    //
    // If this is a restricted token, do the additional access check
    //

    if (SeTokenIsRestricted( EToken ) ) {
        SepNormalAccessCheck(
            Remaining,
            EToken,
            PrimaryToken,
            Dacl,
            PrincipalSelfSid,
            LocalTypeListLength,
            LocalTypeList,
            ObjectTypeListLength,
            TRUE
            );
    }


    if (LocalTypeList->Remaining != 0) {
        Status = STATUS_ACCESS_DENIED;
        PreviouslyGrantedAccess = 0;
        goto ReturnOneStatus;
    }

    Status = STATUS_SUCCESS;
    PreviouslyGrantedAccess |= DesiredAccess;

    //
    // Return a single status code to the caller.
    //

    ReturnOneStatus:
    if ( Status == STATUS_SUCCESS && PreviouslyGrantedAccess == 0 ) {
        Status = STATUS_ACCESS_DENIED;
    }

    if ( NT_SUCCESS(Status) ) {
        if ( PrivilegeCount > 0 ) {
            SepAssemblePrivileges(
                PrivilegeCount,
                SystemSecurity,
                WriteOwner,
                Privileges
                );

            if ( ( Privileges != NULL ) && ( *Privileges == NULL ) ) {
                RetVal = FALSE;
                Status = STATUS_NO_MEMORY;
                PreviouslyGrantedAccess = 0;
            }
        }
    }
    //
    // If the caller asked for a list of status',
    //  duplicate the status all over.
    //
    if ( ReturnResultList ) {
        for ( ResultListIndex=0; ResultListIndex<ObjectTypeListLength; ResultListIndex++ ) {
            AccessStatus[ResultListIndex] = Status;
            GrantedAccess[ResultListIndex] = PreviouslyGrantedAccess;
        }
    } else {
        *AccessStatus = Status;
        *GrantedAccess = PreviouslyGrantedAccess;
    }

    if ( NT_SUCCESS(Status) ) {
        if ( ARGUMENT_PRESENT(ReturnSomeAccessGranted)) {
            *ReturnSomeAccessGranted = TRUE;
        }
        if ( ARGUMENT_PRESENT(ReturnSomeAccessDenied)) {
            *ReturnSomeAccessDenied = FALSE;
        }
    } else {
        if ( ARGUMENT_PRESENT(ReturnSomeAccessGranted)) {
            *ReturnSomeAccessGranted = FALSE;
        }
        if ( ARGUMENT_PRESENT(ReturnSomeAccessDenied)) {
            *ReturnSomeAccessDenied = TRUE;
        }
    }
    return RetVal;

}




NTSTATUS
NtAccessCheck (
    __in PSECURITY_DESCRIPTOR SecurityDescriptor,
    __in HANDLE ClientToken,
    __in ACCESS_MASK DesiredAccess,
    __in PGENERIC_MAPPING GenericMapping,
    __out_bcount(*PrivilegeSetLength) PPRIVILEGE_SET PrivilegeSet,
    __inout PULONG PrivilegeSetLength,
    __out PACCESS_MASK GrantedAccess,
    __out PNTSTATUS AccessStatus
    )


/*++

Routine Description:

    See module abstract.

Arguments:

    SecurityDescriptor - Supplies the security descriptor protecting the object
        being accessed

    ClientToken - Supplies the handle of the user's token.

    DesiredAccess - Supplies the desired access mask.

    GenericMapping - Supplies the generic mapping associated with this
        object type.

    PrivilegeSet - A pointer to a buffer that upon return will contain
        any privileges that were used to perform the access validation.
        If no privileges were used, the buffer will contain a privilege
        set consisting of zero privileges.

    PrivilegeSetLength - The size of the PrivilegeSet buffer in bytes.

    GrantedAccess - Returns an access mask describing the granted access.

    AccessStatus - Status value that may be returned indicating the
         reason why access was denied.  Routines should avoid hardcoding a
         return value of STATUS_ACCESS_DENIED so that a different value can
         be returned when mandatory access control is implemented.

Return Value:

    STATUS_SUCCESS - The attempt proceeded normally.  This does not
        mean access was granted, rather that the parameters were
        correct.

    STATUS_GENERIC_NOT_MAPPED - The DesiredAccess mask contained
        an unmapped generic access.

    STATUS_BUFFER_TOO_SMALL - The passed buffer was not large enough
        to contain the information being returned.

    STATUS_NO_IMPERSONTAION_TOKEN - The passed Token was not an impersonation
        token.

--*/

{

    PAGED_CODE();

    return SeAccessCheckByType (
                 SecurityDescriptor,
                 NULL,      // No Principal Self sid
                 ClientToken,
                 DesiredAccess,
                 NULL,      // No ObjectType List
                 0,         // No ObjectType List
                 GenericMapping,
                 PrivilegeSet,
                 PrivilegeSetLength,
                 GrantedAccess,
                 AccessStatus,
                 FALSE );  // Return a single GrantedAccess and AccessStatus


}






NTSTATUS
NtAccessCheckByType (
    __in PSECURITY_DESCRIPTOR SecurityDescriptor,
    __in_opt PSID PrincipalSelfSid,
    __in HANDLE ClientToken,
    __in ACCESS_MASK DesiredAccess,
    __in_ecount(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
    __in ULONG ObjectTypeListLength,
    __in PGENERIC_MAPPING GenericMapping,
    __out_bcount(*PrivilegeSetLength) PPRIVILEGE_SET PrivilegeSet,
    __inout PULONG PrivilegeSetLength,
    __out PACCESS_MASK GrantedAccess,
    __out PNTSTATUS AccessStatus
    )


/*++

Routine Description:

    See module abstract.

Arguments:

    SecurityDescriptor - Supplies the security descriptor protecting the object
        being accessed

    PrincipalSelfSid - If the object being access checked is an object which
        represents a principal (e.g., a user object), this parameter should
        be the SID of the object.  Any ACE containing the constant
        PRINCIPAL_SELF_SID is replaced by this SID.

        The parameter should be NULL if the object does not represent a principal.

    ClientToken - Supplies the handle of the user's token.

    DesiredAccess - Supplies the desired access mask.

    ObjectTypeList - Supplies a list of GUIDs representing the object (and
        sub-objects) being accessed.  If no list is present, AccessCheckByType
        behaves identically to AccessCheck.

    ObjectTypeListLength - Specifies the number of elements in the ObjectTypeList.

    GenericMapping - Supplies the generic mapping associated with this
        object type.

    PrivilegeSet - A pointer to a buffer that upon return will contain
        any privileges that were used to perform the access validation.
        If no privileges were used, the buffer will contain a privilege
        set consisting of zero privileges.

    PrivilegeSetLength - The size of the PrivilegeSet buffer in bytes.

    GrantedAccess - Returns an access mask describing the granted access.

    AccessStatus - Status value that may be returned indicating the
         reason why access was denied.  Routines should avoid hardcoding a
         return value of STATUS_ACCESS_DENIED so that a different value can
         be returned when mandatory access control is implemented.

Return Value:

    STATUS_SUCCESS - The attempt proceeded normally.  This does not
        mean access was granted, rather that the parameters were
        correct.

    STATUS_GENERIC_NOT_MAPPED - The DesiredAccess mask contained
        an unmapped generic access.

    STATUS_BUFFER_TOO_SMALL - The passed buffer was not large enough
        to contain the information being returned.

    STATUS_NO_IMPERSONTAION_TOKEN - The passed Token was not an impersonation
        token.

--*/

{

    PAGED_CODE();

    return SeAccessCheckByType (
                 SecurityDescriptor,
                 PrincipalSelfSid,
                 ClientToken,
                 DesiredAccess,
                 ObjectTypeList,
                 ObjectTypeListLength,
                 GenericMapping,
                 PrivilegeSet,
                 PrivilegeSetLength,
                 GrantedAccess,
                 AccessStatus,
                 FALSE );  // Return a single GrantedAccess and AccessStatus
}





NTSTATUS
NtAccessCheckByTypeResultList (
    __in PSECURITY_DESCRIPTOR SecurityDescriptor,
    __in_opt PSID PrincipalSelfSid,
    __in HANDLE ClientToken,
    __in ACCESS_MASK DesiredAccess,
    __in_ecount(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
    __in ULONG ObjectTypeListLength,
    __in PGENERIC_MAPPING GenericMapping,
    __out_bcount(*PrivilegeSetLength) PPRIVILEGE_SET PrivilegeSet,
    __inout PULONG PrivilegeSetLength,
    __out_ecount(ObjectTypeListLength) PACCESS_MASK GrantedAccess,
    __out_ecount(ObjectTypeListLength) PNTSTATUS AccessStatus
    )


/*++

Routine Description:

    See module abstract.

Arguments:

    SecurityDescriptor - Supplies the security descriptor protecting the object
        being accessed

    PrincipalSelfSid - If the object being access checked is an object which
        represents a principal (e.g., a user object), this parameter should
        be the SID of the object.  Any ACE containing the constant
        PRINCIPAL_SELF_SID is replaced by this SID.

        The parameter should be NULL if the object does not represent a principal.

    ClientToken - Supplies the handle of the user's token.

    DesiredAccess - Supplies the desired access mask.

    ObjectTypeList - Supplies a list of GUIDs representing the object (and
        sub-objects) being accessed.  If no list is present, AccessCheckByType
        behaves identically to AccessCheck.

    ObjectTypeListLength - Specifies the number of elements in the ObjectTypeList.

    GenericMapping - Supplies the generic mapping associated with this
        object type.

    PrivilegeSet - A pointer to a buffer that upon return will contain
        any privileges that were used to perform the access validation.
        If no privileges were used, the buffer will contain a privilege
        set consisting of zero privileges.

    PrivilegeSetLength - The size of the PrivilegeSet buffer in bytes.

    GrantedAccess - Returns an access mask describing the granted access.

    AccessStatus - Status value that may be returned indicating the
         reason why access was denied.  Routines should avoid hardcoding a
         return value of STATUS_ACCESS_DENIED so that a different value can
         be returned when mandatory access control is implemented.

Return Value:

    STATUS_SUCCESS - The attempt proceeded normally.  This does not
        mean access was granted, rather that the parameters were
        correct.

    STATUS_GENERIC_NOT_MAPPED - The DesiredAccess mask contained
        an unmapped generic access.

    STATUS_BUFFER_TOO_SMALL - The passed buffer was not large enough
        to contain the information being returned.

    STATUS_NO_IMPERSONTAION_TOKEN - The passed Token was not an impersonation
        token.

--*/

{

    PAGED_CODE();

    return SeAccessCheckByType (
                 SecurityDescriptor,
                 PrincipalSelfSid,
                 ClientToken,
                 DesiredAccess,
                 ObjectTypeList,
                 ObjectTypeListLength,
                 GenericMapping,
                 PrivilegeSet,
                 PrivilegeSetLength,
                 GrantedAccess,
                 AccessStatus,
                 TRUE );  // Return an array of GrantedAccess and AccessStatus
}






NTSTATUS
SeAccessCheckByType (
    __in PSECURITY_DESCRIPTOR SecurityDescriptor,
    __in_opt PSID PrincipalSelfSid,
    __in HANDLE ClientToken,
    __in ACCESS_MASK DesiredAccess,
    __in_ecount_opt(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
    __in ULONG ObjectTypeListLength,
    __in PGENERIC_MAPPING GenericMapping,
    __out_ecount_part(*PrivilegeSetLength, *PrivilegeSetLength) PPRIVILEGE_SET PrivilegeSet,
    __inout PULONG PrivilegeSetLength,
    __out PACCESS_MASK GrantedAccess,
    __out PNTSTATUS AccessStatus,
    __in BOOLEAN ReturnResultList
    )


/*++

Routine Description:

    See module abstract.

Arguments:

    SecurityDescriptor - Supplies the security descriptor protecting the object
        being accessed

    PrincipalSelfSid - If the object being access checked is an object which
        represents a principal (e.g., a user object), this parameter should
        be the SID of the object.  Any ACE containing the constant
        PRINCIPAL_SELF_SID is replaced by this SID.

        The parameter should be NULL if the object does not represent a principal.

    ClientToken - Supplies the handle of the user's token.

    DesiredAccess - Supplies the desired access mask.

    ObjectTypeList - Supplies a list of GUIDs representing the object (and
        sub-objects) being accessed.  If no list is present, AccessCheckByType
        behaves identically to AccessCheck.

    ObjectTypeListLength - Specifies the number of elements in the ObjectTypeList.

    GenericMapping - Supplies the generic mapping associated with this
        object type.

    PrivilegeSet - A pointer to a buffer that upon return will contain
        any privileges that were used to perform the access validation.
        If no privileges were used, the buffer will contain a privilege
        set consisting of zero privileges.

    PrivilegeSetLength - The size of the PrivilegeSet buffer in bytes.

    GrantedAccess - Returns an access mask describing the granted access.

    AccessStatus - Status value that may be returned indicating the
         reason why access was denied.  Routines should avoid hardcoding a
         return value of STATUS_ACCESS_DENIED so that a different value can
         be returned when mandatory access control is implemented.

    ReturnResultList - If true, GrantedAccess and AccessStatus are actually
        arrays of entries ObjectTypeListLength elements long.

Return Value:

    STATUS_SUCCESS - The attempt proceeded normally.  This does not
        mean access was granted, rather that the parameters were
        correct.

    STATUS_GENERIC_NOT_MAPPED - The DesiredAccess mask contained
        an unmapped generic access.

    STATUS_BUFFER_TOO_SMALL - The passed buffer was not large enough
        to contain the information being returned.

    STATUS_NO_IMPERSONTAION_TOKEN - The passed Token was not an impersonation
        token.

--*/

{
    ACCESS_MASK LocalGrantedAccess;
    PACCESS_MASK LocalGrantedAccessPointer = NULL;
    NTSTATUS LocalAccessStatus;
    PNTSTATUS LocalAccessStatusPointer = NULL;
    KPROCESSOR_MODE PreviousMode;
    NTSTATUS Status = STATUS_SUCCESS;
    PTOKEN Token = NULL;
    PSECURITY_DESCRIPTOR CapturedSecurityDescriptor = NULL;
    PSID CapturedPrincipalSelfSid = NULL;
    ACCESS_MASK PreviouslyGrantedAccess = 0;
    GENERIC_MAPPING LocalGenericMapping;
    PIOBJECT_TYPE_LIST LocalObjectTypeList = NULL;
    PPRIVILEGE_SET Privileges = NULL;
    SECURITY_SUBJECT_CONTEXT SubjectContext;
    ULONG LocalPrivilegeSetLength = 0;
    ULONG ResultListIndex = 0;

    PAGED_CODE();

    PreviousMode = KeGetPreviousMode();

    if (PreviousMode == KernelMode) {
        ASSERT( !ReturnResultList );
        *AccessStatus = STATUS_SUCCESS;
        *GrantedAccess = DesiredAccess;
        return(STATUS_SUCCESS);
    }

    try {

        if ( ReturnResultList ) {

            if ( ObjectTypeListLength == 0 ) {
                Status = STATUS_INVALID_PARAMETER;
                leave ;
            }

            if ( !IsValidElementCount( ObjectTypeListLength, OBJECT_TYPE_LIST ) )
            {
                Status = STATUS_INVALID_PARAMETER ;

                leave ;
            }

            ProbeForWrite(
                AccessStatus,
                sizeof(NTSTATUS) * ObjectTypeListLength,
                sizeof(ULONG)
                );

            ProbeForWrite(
                GrantedAccess,
                sizeof(ACCESS_MASK) * ObjectTypeListLength,
                sizeof(ULONG)
                );

        } else {
            ProbeForWriteUlong((PULONG)AccessStatus);
            ProbeForWriteUlong((PULONG)GrantedAccess);
        }

        LocalPrivilegeSetLength = ProbeAndReadUlong( PrivilegeSetLength );
        ProbeForWriteUlong(
            PrivilegeSetLength
            );

        ProbeForWrite(
            PrivilegeSet,
            LocalPrivilegeSetLength,
            sizeof(ULONG)
            );

        //
        // initialize PrivilegeCount in case the caller passed in an
        // uninitialized PrivilegeSet
        //

        if ( PrivilegeSet &&
             ( LocalPrivilegeSetLength >= sizeof(PRIVILEGE_SET) )) {

            PrivilegeSet->PrivilegeCount = 0;
        }

        ProbeForReadSmallStructure(
            GenericMapping,
            sizeof(GENERIC_MAPPING),
            sizeof(ULONG)
            );

        LocalGenericMapping = *GenericMapping;

    } except (EXCEPTION_EXECUTE_HANDLER) {
        Status = GetExceptionCode();
    }
    if (!NT_SUCCESS( Status ) ) {
        return( Status );
    }

    if (DesiredAccess &
        ( GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL )) {


        Status = STATUS_GENERIC_NOT_MAPPED;
        goto Cleanup;
    }

    //
    // Obtain a pointer to the passed token
    //

    Status = ObReferenceObjectByHandle(
                 ClientToken,                  // Handle
                 (ACCESS_MASK)TOKEN_QUERY,     // DesiredAccess
                 SeTokenObjectType,           // ObjectType
                 PreviousMode,                 // AccessMode
                 (PVOID *)&Token,              // Object
                 0                             // GrantedAccess
                 );

    if (!NT_SUCCESS(Status)) {
        Token = NULL;
        goto Cleanup;
    }

    //
    // It must be an impersonation token, and at impersonation
    // level of Identification or above.
    //

    if (Token->TokenType != TokenImpersonation) {
        Status = STATUS_NO_IMPERSONATION_TOKEN;
        goto Cleanup;
    }

    if ( Token->ImpersonationLevel < SecurityIdentification ) {
        Status = STATUS_BAD_IMPERSONATION_LEVEL;
        goto Cleanup;
    }

    //
    // Capture any Object type list
    //

    Status = SeCaptureObjectTypeList( ObjectTypeList,
                                      ObjectTypeListLength,
                                      PreviousMode,
                                      &LocalObjectTypeList );

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

    //
    // Compare the DesiredAccess with the privileges in the
    // passed token, and see if we can either satisfy the requested
    // access with a privilege, or bomb out immediately because
    // we don't have a privilege we need.
    //

    Status = SePrivilegePolicyCheck(
                 &DesiredAccess,
                 &PreviouslyGrantedAccess,
                 NULL,
                 (PACCESS_TOKEN)Token,
                 &Privileges,
                 PreviousMode
                 );

    if (!NT_SUCCESS( Status )) {

        try {

            if ( ReturnResultList ) {
                for ( ResultListIndex=0; ResultListIndex<ObjectTypeListLength; ResultListIndex++ ) {
                    AccessStatus[ResultListIndex] = Status;
                    GrantedAccess[ResultListIndex] = 0;
                }

            } else {
                *AccessStatus = Status;
                *GrantedAccess = 0;
            }

            Status = STATUS_SUCCESS;

        } except(EXCEPTION_EXECUTE_HANDLER) {

            Status = GetExceptionCode();
        }

        goto Cleanup;
    }

    //
    // Make sure the passed privileges buffer is large enough for
    // whatever we have to put into it.
    //

    if (Privileges != NULL) {

        if ( ((ULONG)SepPrivilegeSetSize( Privileges )) > LocalPrivilegeSetLength ) {

            try {

                *PrivilegeSetLength = SepPrivilegeSetSize( Privileges );
                Status = STATUS_BUFFER_TOO_SMALL;

            } except ( EXCEPTION_EXECUTE_HANDLER ) {

                Status = GetExceptionCode();
            }

            SeFreePrivileges( Privileges );

            goto Cleanup;

        } else {

            try {

                RtlCopyMemory(
                    PrivilegeSet,
                    Privileges,
                    SepPrivilegeSetSize( Privileges )
                    );

            } except ( EXCEPTION_EXECUTE_HANDLER ) {

                SeFreePrivileges( Privileges );
                Status = GetExceptionCode();
                goto Cleanup;
            }

        }
        SeFreePrivileges( Privileges );

    } else {

        //
        // No privileges were used, construct an empty privilege set
        //

        if ( LocalPrivilegeSetLength < sizeof(PRIVILEGE_SET) ) {

            try {

                *PrivilegeSetLength = sizeof(PRIVILEGE_SET);
                Status = STATUS_BUFFER_TOO_SMALL;

            } except ( EXCEPTION_EXECUTE_HANDLER ) {

                Status = GetExceptionCode();
            }

            goto Cleanup;
        }

        try {

            PrivilegeSet->PrivilegeCount = 0;
            PrivilegeSet->Control = 0;

        } except ( EXCEPTION_EXECUTE_HANDLER ) {

            Status = GetExceptionCode();
            goto Cleanup;

        }

    }

    //
    // Capture the PrincipalSelfSid.
    //

    if ( PrincipalSelfSid != NULL ) {
        Status = SeCaptureSid(
                     PrincipalSelfSid,
                     PreviousMode,
                     NULL, 0,
                     PagedPool,
                     TRUE,
                     &CapturedPrincipalSelfSid );

        if (!NT_SUCCESS(Status)) {
            CapturedPrincipalSelfSid = NULL;
            goto Cleanup;
        }
    }


    //
    // Capture the passed security descriptor.
    //
    // SeCaptureSecurityDescriptor probes the input security descriptor,
    // so we don't have to
    //

    Status = SeCaptureSecurityDescriptor (
                SecurityDescriptor,
                PreviousMode,
                PagedPool,
                FALSE,
                &CapturedSecurityDescriptor
                );

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


    //
    // If there's no security descriptor, then we've been
    // called without all the parameters we need.
    // Return invalid security descriptor.
    //

    if ( CapturedSecurityDescriptor == NULL ) {
        Status = STATUS_INVALID_SECURITY_DESCR;
        goto Cleanup;
    }

    //
    // A valid security descriptor must have an owner and a group
    //

    if ( RtlpOwnerAddrSecurityDescriptor(
                (PISECURITY_DESCRIPTOR)CapturedSecurityDescriptor
                ) == NULL ||
         RtlpGroupAddrSecurityDescriptor(
                (PISECURITY_DESCRIPTOR)CapturedSecurityDescriptor
                ) == NULL ) {

        SeReleaseSecurityDescriptor (
            CapturedSecurityDescriptor,
            PreviousMode,
            FALSE
            );

        Status = STATUS_INVALID_SECURITY_DESCR;
        goto Cleanup;
    }


    SeCaptureSubjectContext( &SubjectContext );

    SepAcquireTokenReadLock( Token );

    //
    // If the user in the token is the owner of the object, we
    // must automatically grant ReadControl and WriteDac access
    // if desired.  If the DesiredAccess mask is empty after
    // these bits are turned off, we don't have to do any more
    // access checking (ref section 4, DSA ACL Arch)
    //


    if ( DesiredAccess & (WRITE_DAC | READ_CONTROL | MAXIMUM_ALLOWED) ) {

        if (SepTokenIsOwner( Token, CapturedSecurityDescriptor, TRUE )) {

            if ( DesiredAccess & MAXIMUM_ALLOWED ) {

                PreviouslyGrantedAccess |= (WRITE_DAC | READ_CONTROL);

            } else {

                PreviouslyGrantedAccess |= (DesiredAccess & (WRITE_DAC | READ_CONTROL));
            }

            DesiredAccess &= ~(WRITE_DAC | READ_CONTROL);
        }

    }

    if (DesiredAccess == 0) {

        try {


            if ( ReturnResultList ) {
                for ( ResultListIndex=0; ResultListIndex<ObjectTypeListLength; ResultListIndex++ ) {

                    //
                    // Do not allow the request to go thru if the granted access
                    // evaluated to ZERO.
                    //

                    if (PreviouslyGrantedAccess == 0) {
                        AccessStatus[ResultListIndex] = STATUS_ACCESS_DENIED;
                        GrantedAccess[ResultListIndex] = 0;
                    } else {
                        AccessStatus[ResultListIndex] = STATUS_SUCCESS;
                        GrantedAccess[ResultListIndex] = PreviouslyGrantedAccess;
                    }
                }

            } else {

                //
                // Do not allow the request to go thru if the granted access
                // evaluated to ZERO.
                //

                if (PreviouslyGrantedAccess == 0) {
                    *AccessStatus = STATUS_ACCESS_DENIED;
                    *GrantedAccess = 0;
                } else {
                    *AccessStatus = STATUS_SUCCESS;
                    *GrantedAccess = PreviouslyGrantedAccess;
                }
            }
            Status = STATUS_SUCCESS;

        } except (EXCEPTION_EXECUTE_HANDLER) {

            Status = GetExceptionCode();

        }

        SepReleaseTokenReadLock( Token );

        SeReleaseSubjectContext( &SubjectContext );

        SeReleaseSecurityDescriptor (
            CapturedSecurityDescriptor,
            PreviousMode,
            FALSE
            );

        goto Cleanup;

    }


    //
    // Finally, handle the case where we actually have to check the DACL.
    //

    if ( ReturnResultList ) {
        LocalGrantedAccessPointer =
            ExAllocatePoolWithTag( PagedPool, (sizeof(ACCESS_MASK)+sizeof(NTSTATUS)) * ObjectTypeListLength, 'aGeS' );

        if (LocalGrantedAccessPointer == NULL) {

            SepReleaseTokenReadLock( Token );

            SeReleaseSubjectContext( &SubjectContext );

            SeReleaseSecurityDescriptor (
                CapturedSecurityDescriptor,
                PreviousMode,
                FALSE
                );

            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto Cleanup;
        }
        LocalAccessStatusPointer = (PNTSTATUS)(LocalGrantedAccessPointer + ObjectTypeListLength);
    } else {
        LocalGrantedAccessPointer = &LocalGrantedAccess;
        LocalAccessStatusPointer =  &LocalAccessStatus;
    }

    //
    // This does not ask for privilege set to be returned so we can ignore
    // the return value of the call.
    //

    (VOID) SepAccessCheck (
               CapturedSecurityDescriptor,
               CapturedPrincipalSelfSid,
               SubjectContext.PrimaryToken,
               Token,
               DesiredAccess,
               LocalObjectTypeList,
               ObjectTypeListLength,
               &LocalGenericMapping,
               PreviouslyGrantedAccess,
               PreviousMode,
               LocalGrantedAccessPointer,
               NULL,
               LocalAccessStatusPointer,
               ReturnResultList,
               NULL,
               NULL );

    SepReleaseTokenReadLock( Token );

    SeReleaseSubjectContext( &SubjectContext );

    SeReleaseSecurityDescriptor (
        CapturedSecurityDescriptor,
        PreviousMode,
        FALSE
        );

    try {

        if ( ReturnResultList ) {
            for ( ResultListIndex=0; ResultListIndex<ObjectTypeListLength; ResultListIndex++ ) {
                AccessStatus[ResultListIndex] = LocalAccessStatusPointer[ResultListIndex];
                GrantedAccess[ResultListIndex] = LocalGrantedAccessPointer[ResultListIndex];
            }

        } else {
            *AccessStatus = *LocalAccessStatusPointer;
            *GrantedAccess = *LocalGrantedAccessPointer;
        }

        Status = STATUS_SUCCESS;

    } except (EXCEPTION_EXECUTE_HANDLER) {
        Status = GetExceptionCode();
    }

    if ( ReturnResultList ) {
        if ( LocalGrantedAccessPointer != NULL ) {
            ExFreePool( LocalGrantedAccessPointer );
        }
    }


    //
    // Free locally used resources.
    //
Cleanup:

    if ( Token != NULL ) {
        ObDereferenceObject( Token );
    }

    if ( LocalObjectTypeList != NULL ) {
        SeFreeCapturedObjectTypeList( LocalObjectTypeList );
    }

    if (CapturedPrincipalSelfSid != NULL) {
        SeReleaseSid( CapturedPrincipalSelfSid, PreviousMode, TRUE);
    }

    return Status;
}



VOID
SeFreePrivileges(
    __in PPRIVILEGE_SET Privileges
    )

/*++

Routine Description:

    This routine frees a privilege set returned by SeAccessCheck.

Arguments:

    Privileges - Supplies a pointer to the privilege set to be freed.

Return Value:

    None.

--*/

{
    PAGED_CODE();

    ExFreePool( Privileges );
}



BOOLEAN
SeAccessCheck (
    __in PSECURITY_DESCRIPTOR SecurityDescriptor,
    __in PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
    __in BOOLEAN SubjectContextLocked,
    __in ACCESS_MASK DesiredAccess,
    __in ACCESS_MASK PreviouslyGrantedAccess,
    __deref_out_opt PPRIVILEGE_SET *Privileges,
    __in PGENERIC_MAPPING GenericMapping,
    __in KPROCESSOR_MODE AccessMode,
    __out PACCESS_MASK GrantedAccess,
    __out PNTSTATUS AccessStatus
    )

/*++

Routine Description:

    See module abstract

    This routine MAY perform tests for the following
    privileges:

                SeTakeOwnershipPrivilege
                SeSecurityPrivilege

    depending upon the accesses being requested.

    This routine may also check to see if the subject is the owner
    of the object (to grant WRITE_DAC access).

Arguments:

    SecurityDescriptor - Supplies the security descriptor protecting the
         object being accessed

    SubjectSecurityContext - A pointer to the subject's captured security
         context

    SubjectContextLocked - Supplies a flag indicating whether or not
        the user's subject context is locked, so that it does not have
        to be locked again.

    DesiredAccess - Supplies the access mask that the user is attempting to
         acquire

    PreviouslyGrantedAccess - Supplies any accesses that the user has
        already been granted, for example, as a result of holding a
        privilege.

    Privileges - Supplies a pointer in which will be returned a privilege
        set indicating any privileges that were used as part of the
        access validation.

    GenericMapping - Supplies the generic mapping associated with this
        object type.

    AccessMode - Supplies the access mode to be used in the check

    GrantedAccess - Pointer to a returned access mask indicatating the
         granted access

    AccessStatus - Status value that may be returned indicating the
         reason why access was denied.  Routines should avoid hardcoding a
         return value of STATUS_ACCESS_DENIED so that a different value can
         be returned when mandatory access control is implemented.


Return Value:

    BOOLEAN - TRUE if access is allowed and FALSE otherwise

--*/

{
    BOOLEAN Success;

    PAGED_CODE();

    if (AccessMode == KernelMode) {

        if (DesiredAccess & MAXIMUM_ALLOWED) {

            //
            // Give him:
            //   GenericAll
            //   Anything else he asked for
            //

            *GrantedAccess = GenericMapping->GenericAll;
            *GrantedAccess |= (DesiredAccess & ~MAXIMUM_ALLOWED);
            *GrantedAccess |= PreviouslyGrantedAccess;

        } else {

            *GrantedAccess = DesiredAccess | PreviouslyGrantedAccess;
        }
        *AccessStatus = STATUS_SUCCESS;
        return(TRUE);
    }

    //
    // If the object doesn't have a security descriptor (and it's supposed
    // to), return access denied.
    //

    if ( SecurityDescriptor == NULL) {

       *AccessStatus = STATUS_ACCESS_DENIED;
       return( FALSE );

    }

    //
    // If we're impersonating a client, we have to be at impersonation level
    // of SecurityImpersonation or above.
    //

    if ( (SubjectSecurityContext->ClientToken != NULL) &&
         (SubjectSecurityContext->ImpersonationLevel < SecurityImpersonation)
       ) {
           *AccessStatus = STATUS_BAD_IMPERSONATION_LEVEL;
           return( FALSE );
    }

    if ( DesiredAccess == 0 ) {

        if ( PreviouslyGrantedAccess == 0 ) {
            *AccessStatus = STATUS_ACCESS_DENIED;
            return( FALSE );
        }

        *GrantedAccess = PreviouslyGrantedAccess;
        *AccessStatus = STATUS_SUCCESS;
        *Privileges = NULL;
        return( TRUE );

    }

    SeAssertMappedCanonicalAccess( DesiredAccess );


    //
    // If the caller did not lock the subject context for us,
    // lock it here to keep lower level routines from having
    // to lock it.
    //

    if ( !SubjectContextLocked ) {
        SeLockSubjectContext( SubjectSecurityContext );
    }

    //
    // If the user in the token is the owner of the object, we
    // must automatically grant ReadControl and WriteDac access
    // if desired.  If the DesiredAccess mask is empty after
    // these bits are turned off, we don't have to do any more
    // access checking (ref section 4, DSA ACL Arch)
    //

    if ( DesiredAccess & (WRITE_DAC | READ_CONTROL | MAXIMUM_ALLOWED) ) {

        if ( SepTokenIsOwner(
                 EffectiveToken( SubjectSecurityContext ),
                 SecurityDescriptor,
                 TRUE
                 ) ) {

            if ( DesiredAccess & MAXIMUM_ALLOWED ) {

                PreviouslyGrantedAccess |= (WRITE_DAC | READ_CONTROL);

            } else {

                PreviouslyGrantedAccess |= (DesiredAccess & (WRITE_DAC | READ_CONTROL));
            }

            DesiredAccess &= ~(WRITE_DAC | READ_CONTROL);
        }
    }

    if (DesiredAccess == 0) {

        if ( !SubjectContextLocked ) {
            SeUnlockSubjectContext( SubjectSecurityContext );
        }

        *GrantedAccess = PreviouslyGrantedAccess;
        *AccessStatus = STATUS_SUCCESS;
        return( TRUE );

    } else {

        BOOLEAN b = SepAccessCheck(
                        SecurityDescriptor,
                        NULL,   // No PrincipalSelfSid
                        SubjectSecurityContext->PrimaryToken,
                        SubjectSecurityContext->ClientToken,
                        DesiredAccess,
                        NULL,   // No object type list
                        0,      // No object type list
                        GenericMapping,
                        PreviouslyGrantedAccess,
                        AccessMode,
                        GrantedAccess,
                        Privileges,
                        AccessStatus,
                        FALSE,   // Don't return a list
                        &Success,
                        NULL
                        );
#if DBG
          if (!Success && SepShowAccessFail) {
              DbgPrint("SE: Access check failed, DesiredAccess = 0x%x\n",
                DesiredAccess);
              SepDumpSD = TRUE;
              SepDumpSecurityDescriptor(
                  SecurityDescriptor,
                  "Input to SeAccessCheck\n"
                  );
              SepDumpSD = FALSE;
              SepDumpToken = TRUE;
              SepDumpTokenInfo( EffectiveToken( SubjectSecurityContext ) );
              SepDumpToken = FALSE;
          }
#endif

        //
        // If we locked it in this routine, unlock it before we
        // leave.
        //

        if ( !SubjectContextLocked ) {
            SeUnlockSubjectContext( SubjectSecurityContext );
        }

        //
        // We return failure if any of the following is TRUE
        //   1. The user was really not granted access.
        //   2. The resource manager asked for the list of privileges used to
        //      determine granted access and we failed to allocate memory
        //      required to return these.
        //

        return( b && Success );
    }
}



NTSTATUS
SePrivilegePolicyCheck(
    __inout PACCESS_MASK RemainingDesiredAccess,
    __inout PACCESS_MASK PreviouslyGrantedAccess,
    __in_opt PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
    __in_opt PACCESS_TOKEN ExplicitToken,
    __deref_out PPRIVILEGE_SET *PrivilegeSet,
    __in KPROCESSOR_MODE PreviousMode
    )

/*++

Routine Description:

    This routine implements privilege policy by examining the bits in
    a DesiredAccess mask and adjusting them based on privilege checks.

    Currently, a request for ACCESS_SYSTEM_SECURITY may only be satisfied
    by the caller having SeSecurityPrivilege.  WRITE_OWNER may optionally
    be satisfied via SeTakeOwnershipPrivilege.

Arguments:

    RemainingDesiredAccess - The desired access for the current operation.
        Bits may be cleared in this if the subject has particular privileges.

    PreviouslyGrantedAccess - Supplies an access mask describing any
        accesses that have already been granted.  Bits may be set in
        here as a result of privilege checks.

    SubjectSecurityContext - Optionally provides the subject's security
        context.

    ExplicitToken - Optionally provides the token to be examined.

    PrivilegeSet - Supplies a pointer to a location in which will be
        returned a pointer to a privilege set.

    PreviousMode - The previous processor mode.

Return Value:

    STATUS_SUCCESS - Any access requests that could be satisfied via
        privileges were done.

    STATUS_PRIVILEGE_NOT_HELD - An access type was being requested that
        requires a privilege, and the current subject did not have the
        privilege.



--*/

{
    BOOLEAN Success;
    PTOKEN Token;
    BOOLEAN WriteOwner = FALSE;
    BOOLEAN SystemSecurity = FALSE;
    ULONG PrivilegeNumber = 0;
    ULONG PrivilegeCount = 0;
    ULONG SizeRequired;

    PAGED_CODE();

    if (ARGUMENT_PRESENT( SubjectSecurityContext )) {

        Token = (PTOKEN)EffectiveToken( SubjectSecurityContext );

    } else {

        Token = (PTOKEN)ExplicitToken;
    }


    if (*RemainingDesiredAccess & ACCESS_SYSTEM_SECURITY) {

        Success = SepSinglePrivilegeCheck (
                    SeSecurityPrivilege,
                    Token,
                    PreviousMode
                    );

        if (!Success) {

            return( STATUS_PRIVILEGE_NOT_HELD );
        }

        PrivilegeCount++;
        SystemSecurity = TRUE;

        *RemainingDesiredAccess &= ~ACCESS_SYSTEM_SECURITY;
        *PreviouslyGrantedAccess |= ACCESS_SYSTEM_SECURITY;
    }

    if (*RemainingDesiredAccess & WRITE_OWNER) {

        Success = SepSinglePrivilegeCheck (
                    SeTakeOwnershipPrivilege,
                    Token,
                    PreviousMode
                    );

        if (Success) {

            PrivilegeCount++;
            WriteOwner = TRUE;

            *RemainingDesiredAccess &= ~WRITE_OWNER;
            *PreviouslyGrantedAccess |= WRITE_OWNER;

        }
    }

    if (PrivilegeCount > 0) {
        SizeRequired = sizeof(PRIVILEGE_SET) +
                        (PrivilegeCount - ANYSIZE_ARRAY) *
                        (ULONG)sizeof(LUID_AND_ATTRIBUTES);

        *PrivilegeSet = ExAllocatePoolWithTag( PagedPool, SizeRequired, 'rPeS' );

        if ( *PrivilegeSet == NULL ) {
            return( STATUS_INSUFFICIENT_RESOURCES );
        }

        (*PrivilegeSet)->PrivilegeCount = PrivilegeCount;
        (*PrivilegeSet)->Control = 0;

        if (WriteOwner) {
            (*PrivilegeSet)->Privilege[PrivilegeNumber].Luid = SeTakeOwnershipPrivilege;
            (*PrivilegeSet)->Privilege[PrivilegeNumber].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS;
            PrivilegeNumber++;
        }

        if (SystemSecurity) {
            (*PrivilegeSet)->Privilege[PrivilegeNumber].Luid = SeSecurityPrivilege;
            (*PrivilegeSet)->Privilege[PrivilegeNumber].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS;
        }
    }

    return( STATUS_SUCCESS );
}



BOOLEAN
SepTokenIsOwner(
    IN PACCESS_TOKEN EffectiveToken,
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN BOOLEAN TokenLocked
    )

/*++

Routine Description:

    This routine will determine of the Owner of the passed security descriptor
    is in the passed token. If the token is restricted it cannot be the
    owner.


Arguments:

    Token - The token representing the current user.

    SecurityDescriptor - The security descriptor for the object being
        accessed.

    TokenLocked - A boolean describing whether the caller has taken
        a read lock for the token.


Return Value:

    TRUE - The user of the token is the owner of the object.

    FALSE - The user of the token is not the owner of the object.

--*/

{
    PSID Owner;
    BOOLEAN rc;

    PISECURITY_DESCRIPTOR ISecurityDescriptor;
    PTOKEN Token;

    PAGED_CODE();

    ISecurityDescriptor = (PISECURITY_DESCRIPTOR)SecurityDescriptor;
    Token = (PTOKEN)EffectiveToken;


    Owner = RtlpOwnerAddrSecurityDescriptor( ISecurityDescriptor );
    ASSERT( Owner != NULL );

    if (!TokenLocked) {
        SepAcquireTokenReadLock( Token );
    }

    rc = SepSidInToken( Token, NULL, Owner, FALSE );

    //
    // For restricted tokens, check the restricted sids too.
    //

    if (rc && (Token->TokenFlags & TOKEN_IS_RESTRICTED) != 0) {
        rc = SepSidInTokenEx( Token, NULL, Owner, FALSE, TRUE );

    }

    if (!TokenLocked) {
        SepReleaseTokenReadLock( Token );
    }

    return( rc );
}


#define WORLD_TRAVERSAL_INCLUDES_ANONYMOUS 1

BOOLEAN
SeFastTraverseCheck(
    __in PSECURITY_DESCRIPTOR SecurityDescriptor,
    __in PACCESS_STATE AccessState,
    __in ACCESS_MASK TraverseAccess,
    __in KPROCESSOR_MODE AccessMode
    )
/*++

Routine Description:

    This routine will perform a fast check against the DACL of the passed
    Security Descriptor to see if Traverse access would be granted. If so, no
    further access checking is necessary.

    Note that the SubjectContext for the client process does not have
    to be locked to make this call, since it does not examine any data
    structures in the Token.

    The caller has the following responsibilities:
    1. It is the job of the caller to verify AccessMode is not KernelMode!
    2. The *caller* is expected to check AccessState for
       TOKEN_HAS_TRAVERSE_PRIVILEGE if the override is applicable!

Arguments:

    SecurityDescriptor - The Security Descriptor protecting the container
        object being traversed.

    AccessState - Running security access state containing caller token
        information for operation.

    TraverseAccess - Access mask describing Traverse access for this
        object type. There must be only one bit specified in the mask.

    AccessMode - Supplies the access mode to be used in the check


Return Value:

    TRUE - if Traverse access to this container can be granted. FALSE otherwise.

--*/

{
    PACL Dacl;
    ULONG i;
    PVOID Ace;
    ULONG AceCount;
#if !WORLD_TRAVERSAL_INCLUDES_ANONYMOUS
    LOGICAL FoundWorld;
    LOGICAL FoundAnonymous;
#endif

    PAGED_CODE();

    //
    // Note that I/O calls this function even if the traverse bypass privilege
    // is set. This is done as I/O doesn't want the performance override to
    // apply to the DeviceObject->FileName boundary, as not all filesystems
    // supply file-level security.
    //

    ASSERT ( AccessMode != KernelMode );

    if (SecurityDescriptor == NULL) {
        return( FALSE );
    }

    //
    // See if there is a valid DACL in the passed Security Descriptor.
    // No DACL, no security, all is granted. Note that this function returns
    // NULL if SE_DACL_PRESENT isn't set.
    //

    Dacl = RtlpDaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor );

    //
    // If no DACL is supplied, the object has no security, so all accesses are
    // granted.
    //

    if ( Dacl == NULL ) {

        return(TRUE);
    }

    //
    // There is security on this object.  If the DACL is empty, deny all access
    // immediately
    //

    if ((AceCount = Dacl->AceCount) == 0) {

        return( FALSE );
    }

    //
    // A restricted token is a token with two lists of SIDs. Access checks are
    // done against each list, passing only if both lists grant access. The
    // second "restricting SID" list can contain *any allow* SID.
    //
    // This routine doesn't walk a restricted token's restricting-SID list. As
    // such, restricted tokens require a full access check.
    //
    if (AccessState->Flags & TOKEN_IS_RESTRICTED) {

        return FALSE;
    }

    //
    // There's stuff in the DACL, walk down the list and see
    // if both Everyone and Anonymous have been granted TraverseAccess
    //
#if !WORLD_TRAVERSAL_INCLUDES_ANONYMOUS
    FoundWorld = FALSE;
    FoundAnonymous = FALSE;
#endif

    for ( i = 0, Ace = FirstAce( Dacl ) ;
          i < AceCount  ;
          i++, Ace = NextAce( Ace )
        ) {

        if (((PACE_HEADER)Ace)->AceFlags & INHERIT_ONLY_ACE) {

            continue;
        }

        if ( (((PACE_HEADER)Ace)->AceType == ACCESS_ALLOWED_ACE_TYPE) ) {

            if ( (TraverseAccess & ((PACCESS_ALLOWED_ACE)Ace)->Mask) ) {

                if ( RtlEqualSid( SeWorldSid, &((PACCESS_ALLOWED_ACE)Ace)->SidStart ) ) {
#if WORLD_TRAVERSAL_INCLUDES_ANONYMOUS
                    return( TRUE );
                }
#else
                    if (FoundAnonymous) {

                        return( TRUE );

                    } else {

                        FoundWorld = TRUE;
                    }

                } else if ( RtlEqualSid( SeAnonymousLogonSid, &((PACCESS_ALLOWED_ACE)Ace)->SidStart ) ) {

                    if (FoundWorld) {

                        return( TRUE );

                    } else {

                        FoundAnonymous = TRUE;
                    }
                }
#endif
            }

        } else if ( (((PACE_HEADER)Ace)->AceType == ACCESS_DENIED_ACE_TYPE) ) {

            if ( (TraverseAccess & ((PACCESS_DENIED_ACE)Ace)->Mask) ) {

                //
                // This ACE might refer to a group the user belongs to
                // (or could be the user). Force a full access check.
                //

                return( FALSE );
            }
        }
    }

    return( FALSE );
}

